diff options
Diffstat (limited to 'src/quick/scenegraph')
96 files changed, 2844 insertions, 1273 deletions
diff --git a/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer.cpp b/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer.cpp index 02cf8209d1..2e5fdbbe6b 100644 --- a/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer.cpp @@ -77,6 +77,11 @@ QSGSoftwareRenderableNode *QSGAbstractSoftwareRenderer::renderableNode(QSGNode * return m_nodes.value(node, nullptr); } +const QLinkedList<QSGSoftwareRenderableNode*> &QSGAbstractSoftwareRenderer::renderableNodes() const +{ + return m_renderableNodes; +} + void QSGAbstractSoftwareRenderer::addNodeMapping(QSGNode *node, QSGSoftwareRenderableNode *renderableNode) { m_nodes.insert(node, renderableNode); @@ -193,6 +198,12 @@ QRegion QSGAbstractSoftwareRenderer::optimizeRenderList() } } + if (m_obscuredRegion.contains(m_background->rect().toAlignedRect())) { + m_isOpaque = true; + } else { + m_isOpaque = false; + } + // Empty dirtyRegion (for second pass) m_dirtyRegion = QRegion(); m_obscuredRegion = QRegion(); @@ -227,11 +238,11 @@ void QSGAbstractSoftwareRenderer::setBackgroundColor(const QColor &color) renderableNode(m_background)->markMaterialDirty(); } -void QSGAbstractSoftwareRenderer::setBackgroundSize(const QSize &size) +void QSGAbstractSoftwareRenderer::setBackgroundRect(const QRect &rect) { - if (m_background->rect().size().toSize() == size) + if (m_background->rect().toRect() == rect) return; - m_background->setRect(0.0f, 0.0f, size.width(), size.height()); + m_background->setRect(rect); renderableNode(m_background)->markGeometryDirty(); // Invalidate the whole scene when the background is resized markDirty(); @@ -242,21 +253,21 @@ QColor QSGAbstractSoftwareRenderer::backgroundColor() return m_background->color(); } -QSize QSGAbstractSoftwareRenderer::backgroundSize() +QRect QSGAbstractSoftwareRenderer::backgroundRect() { - return m_background->rect().size().toSize(); + return m_background->rect().toRect(); } void QSGAbstractSoftwareRenderer::nodeAdded(QSGNode *node) { - qCDebug(lc2DRender) << "nodeAdded" << (void*)node; + qCDebug(lc2DRender, "nodeAdded %p", (void*)node); m_nodeUpdater->updateNodes(node); } void QSGAbstractSoftwareRenderer::nodeRemoved(QSGNode *node) { - qCDebug(lc2DRender) << "nodeRemoved" << (void*)node; + qCDebug(lc2DRender, "nodeRemoved %p", (void*)node); auto renderable = renderableNode(node); // remove mapping @@ -280,7 +291,7 @@ void QSGAbstractSoftwareRenderer::nodeRemoved(QSGNode *node) void QSGAbstractSoftwareRenderer::nodeGeometryUpdated(QSGNode *node) { - qCDebug(lc2DRender) << "nodeGeometryUpdated"; + qCDebug(lc2DRender, "nodeGeometryUpdated"); // Mark node as dirty auto renderable = renderableNode(node); @@ -293,7 +304,7 @@ void QSGAbstractSoftwareRenderer::nodeGeometryUpdated(QSGNode *node) void QSGAbstractSoftwareRenderer::nodeMaterialUpdated(QSGNode *node) { - qCDebug(lc2DRender) << "nodeMaterialUpdated"; + qCDebug(lc2DRender, "nodeMaterialUpdated"); // Mark node as dirty auto renderable = renderableNode(node); @@ -306,7 +317,7 @@ void QSGAbstractSoftwareRenderer::nodeMaterialUpdated(QSGNode *node) void QSGAbstractSoftwareRenderer::nodeMatrixUpdated(QSGNode *node) { - qCDebug(lc2DRender) << "nodeMaterialUpdated"; + qCDebug(lc2DRender, "nodeMaterialUpdated"); // Update children nodes m_nodeUpdater->updateNodes(node); @@ -314,7 +325,7 @@ void QSGAbstractSoftwareRenderer::nodeMatrixUpdated(QSGNode *node) void QSGAbstractSoftwareRenderer::nodeOpacityUpdated(QSGNode *node) { - qCDebug(lc2DRender) << "nodeOpacityUpdated"; + qCDebug(lc2DRender, "nodeOpacityUpdated"); // Update children nodes m_nodeUpdater->updateNodes(node); diff --git a/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer_p.h b/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer_p.h index 905577b92a..99204ef25e 100644 --- a/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer_p.h +++ b/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer_p.h @@ -63,7 +63,7 @@ class QSGSimpleRectNode; class QSGSoftwareRenderableNode; class QSGSoftwareRenderableNodeUpdater; -class QSGAbstractSoftwareRenderer : public QSGRenderer +class Q_QUICK_PRIVATE_EXPORT QSGAbstractSoftwareRenderer : public QSGRenderer { public: QSGAbstractSoftwareRenderer(QSGRenderContext *context); @@ -83,9 +83,12 @@ protected: QRegion optimizeRenderList(); void setBackgroundColor(const QColor &color); - void setBackgroundSize(const QSize &size); + void setBackgroundRect(const QRect &rect); QColor backgroundColor(); - QSize backgroundSize(); + QRect backgroundRect(); + // only known after calling optimizeRenderList() + bool isOpaque() const { return m_isOpaque; } + const QLinkedList<QSGSoftwareRenderableNode*> &renderableNodes() const; private: void nodeAdded(QSGNode *node); @@ -102,6 +105,7 @@ private: QRegion m_dirtyRegion; QRegion m_obscuredRegion; + bool m_isOpaque = false; QSGSoftwareRenderableNodeUpdater *m_nodeUpdater; }; diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwareadaptation.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwareadaptation.cpp index 92c02b4966..c33144344f 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwareadaptation.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwareadaptation.cpp @@ -74,6 +74,7 @@ QSGContextFactoryInterface::Flags QSGSoftwareAdaptation::flags(const QString &) QSGRenderLoop *QSGSoftwareAdaptation::createWindowManager() { +#if QT_CONFIG(thread) static bool threaded = false; static bool envChecked = false; if (!envChecked) { @@ -83,10 +84,11 @@ QSGRenderLoop *QSGSoftwareAdaptation::createWindowManager() if (threaded) return new QSGSoftwareThreadedRenderLoop; +#endif return new QSGSoftwareRenderLoop(); } -QSGSoftwareContext *QSGSoftwareAdaptation::instance = 0; +QSGSoftwareContext *QSGSoftwareAdaptation::instance = nullptr; QT_END_NAMESPACE diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwareadaptation_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwareadaptation_p.h index ffe54b5d4b..8b2a545033 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwareadaptation_p.h +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwareadaptation_p.h @@ -62,7 +62,7 @@ class QSGSoftwareContext; class QSGSoftwareAdaptation : public QSGContextPlugin { public: - QSGSoftwareAdaptation(QObject *parent = 0); + QSGSoftwareAdaptation(QObject *parent = nullptr); QStringList keys() const override; QSGContext *create(const QString &key) const override; diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext.cpp index aa850a80db..5b5bf005d8 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext.cpp @@ -205,12 +205,12 @@ QSGRendererInterface::ShaderType QSGSoftwareContext::shaderType() const QSGRendererInterface::ShaderCompilationTypes QSGSoftwareContext::shaderCompilationType() const { - return 0; + return nullptr; } QSGRendererInterface::ShaderSourceTypes QSGSoftwareContext::shaderSourceType() const { - return 0; + return nullptr; } void *QSGSoftwareContext::getResource(QQuickWindow *window, Resource resource) const diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwareglyphnode.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwareglyphnode.cpp index 21f20c66cd..dd789b78c7 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwareglyphnode.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwareglyphnode.cpp @@ -49,12 +49,40 @@ QSGSoftwareGlyphNode::QSGSoftwareGlyphNode() setGeometry(&m_geometry); } +namespace { +QRectF calculateBoundingRect(const QPointF &position, const QGlyphRun &glyphs) +{ + qreal minX = 0; + qreal minY = 0; + qreal maxX = 0; + qreal maxY = 0; + + for (int i = 0, n = qMin(glyphs.glyphIndexes().size(), glyphs.positions().size()); i < n; ++i) { + QRectF glyphRect = glyphs.rawFont().boundingRect(glyphs.glyphIndexes()[i]); + glyphRect.translate(glyphs.positions()[i]); + + if (i == 0) { + minX = glyphRect.left(); + minY = glyphRect.top(); + maxX = glyphRect.right(); + maxY = glyphRect.bottom(); + } else { + minX = qMin(glyphRect.left(), minX); + minY = qMin(glyphRect.top(), minY); + maxX = qMax(glyphRect.right(),maxX); + maxY = qMax(glyphRect.bottom(), maxY); + } + } + QRectF boundingRect(QPointF(minX, minY), QPointF(maxX, maxY)); + return boundingRect.translated(position - QPointF(0.0, glyphs.rawFont().ascent())); +} +} 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())); + m_bounding_rect = calculateBoundingRect(position, glyphs); } void QSGSoftwareGlyphNode::setColor(const QColor &color) @@ -91,8 +119,8 @@ void QSGSoftwareGlyphNode::paint(QPainter *painter) 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(); + if (painter->device()->devicePixelRatioF() > 0.0) + offset = 1.0 / painter->device()->devicePixelRatioF(); switch (m_style) { case QQuickText::Normal: break; diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalimagenode.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalimagenode.cpp index 10291b9cb5..da5d39db20 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalimagenode.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalimagenode.cpp @@ -68,6 +68,9 @@ void qDrawBorderPixmap(QPainter *painter, const QRect &targetRect, const QMargin QMargins sourceMargins = normalizedMargins(sourceMarginsIn); QMargins targetMargins = normalizedMargins(targetMarginsIn); + const qreal sourceDpr = pixmap.devicePixelRatioF(); + sourceMargins *= sourceDpr; + // source center const int sourceCenterTop = sourceRect.top() + sourceMargins.top(); const int sourceCenterLeft = sourceRect.left() + sourceMargins.left(); @@ -89,20 +92,13 @@ void qDrawBorderPixmap(QPainter *painter, const QRect &targetRect, const QMargin int columns = 3; int rows = 3; if (rules.horizontal != Qt::StretchTile && sourceCenterWidth != 0) - columns = qMax(3, 2 + qCeil(targetCenterWidth / qreal(sourceCenterWidth))); + columns = qMax(3, 2 + qCeil((targetCenterWidth * sourceDpr) / qreal(sourceCenterWidth))); if (rules.vertical != Qt::StretchTile && sourceCenterHeight != 0) - rows = qMax(3, 2 + qCeil(targetCenterHeight / qreal(sourceCenterHeight))); + rows = qMax(3, 2 + qCeil((targetCenterHeight * sourceDpr) / 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; @@ -121,7 +117,7 @@ void qDrawBorderPixmap(QPainter *painter, const QRect &targetRect, const QMargin dx = targetCenterWidth; break; case Qt::RepeatTile: - dx = sourceCenterWidth; + dx = sourceCenterWidth / sourceDpr; break; case Qt::RoundTile: dx = targetCenterWidth / qreal(columns - 2); @@ -136,7 +132,7 @@ void qDrawBorderPixmap(QPainter *painter, const QRect &targetRect, const QMargin dy = targetCenterHeight; break; case Qt::RepeatTile: - dy = sourceCenterHeight; + dy = sourceCenterHeight / sourceDpr; break; case Qt::RoundTile: dy = targetCenterHeight / qreal(rows - 2); @@ -308,9 +304,6 @@ void qDrawBorderPixmap(QPainter *painter, const QRect &targetRect, const QMargin 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 @@ -318,8 +311,9 @@ void qDrawBorderPixmap(QPainter *painter, const QRect &targetRect, const QMargin QSGSoftwareInternalImageNode::QSGSoftwareInternalImageNode() : m_innerSourceRect(0, 0, 1, 1) , m_subSourceRect(0, 0, 1, 1) - , m_texture(0) + , m_texture(nullptr) , m_mirror(false) + , m_textureIsLayer(false) , m_smooth(true) , m_tileHorizontal(false) , m_tileVertical(false) @@ -366,6 +360,7 @@ void QSGSoftwareInternalImageNode::setTexture(QSGTexture *texture) { m_texture = texture; m_cachedMirroredPixmapIsDirty = true; + m_textureIsLayer = static_cast<bool>(qobject_cast<QSGSoftwareLayer*>(texture)); markDirty(DirtyMaterial); } @@ -415,8 +410,13 @@ void QSGSoftwareInternalImageNode::setVerticalWrapMode(QSGTexture::WrapMode wrap void QSGSoftwareInternalImageNode::update() { if (m_cachedMirroredPixmapIsDirty) { - if (m_mirror) { - m_cachedMirroredPixmap = pixmap().transformed(QTransform(-1, 0, 0, 1, 0, 0)); + if (m_mirror || m_textureIsLayer) { + QTransform transform( + (m_mirror ? -1 : 1), 0, + 0 , (m_textureIsLayer ? -1 :1), + 0 , 0 + ); + m_cachedMirroredPixmap = pixmap().transformed(transform); } else { //Cleanup cached pixmap if necessary if (!m_cachedMirroredPixmap.isNull()) @@ -436,6 +436,7 @@ void QSGSoftwareInternalImageNode::preprocess() } if (doDirty) markDirty(DirtyMaterial); + m_cachedMirroredPixmapIsDirty = doDirty; } static Qt::TileRule getTileRule(qreal factor) @@ -453,8 +454,10 @@ static Qt::TileRule getTileRule(qreal factor) void QSGSoftwareInternalImageNode::paint(QPainter *painter) { painter->setRenderHint(QPainter::SmoothPixmapTransform, m_smooth); + // Disable antialiased clipping. It causes transformed tiles to have gaps. + painter->setRenderHint(QPainter::Antialiasing, false); - const QPixmap &pm = m_mirror ? m_cachedMirroredPixmap : pixmap(); + const QPixmap &pm = m_mirror || m_textureIsLayer ? m_cachedMirroredPixmap : pixmap(); if (m_innerTargetRect != m_targetRect) { // border image @@ -462,7 +465,7 @@ void QSGSoftwareInternalImageNode::paint(QPainter *painter) 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)); + margins, tilerules, QSGSoftwareHelpers::QDrawBorderPixmap::DrawingHints(nullptr)); return; } @@ -470,8 +473,7 @@ void QSGSoftwareInternalImageNode::paint(QPainter *painter) 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->setTransform(QTransform::fromScale(sx, sy), 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())); @@ -483,6 +485,7 @@ void QSGSoftwareInternalImageNode::paint(QPainter *painter) } } + QRectF QSGSoftwareInternalImageNode::rect() const { return m_targetRect; @@ -490,12 +493,13 @@ QRectF QSGSoftwareInternalImageNode::rect() const const QPixmap &QSGSoftwareInternalImageNode::pixmap() const { - if (QSGSoftwarePixmapTexture *pt = qobject_cast<QSGSoftwarePixmapTexture*>(m_texture)) { + if (QSGSoftwarePixmapTexture *pt = qobject_cast<QSGSoftwarePixmapTexture*>(m_texture)) return pt->pixmap(); - } else { - QSGSoftwareLayer *layer = qobject_cast<QSGSoftwareLayer*>(m_texture); + if (QSGSoftwareLayer *layer = qobject_cast<QSGSoftwareLayer*>(m_texture)) return layer->pixmap(); - } + Q_ASSERT(m_texture == nullptr); + static const QPixmap nullPixmap; + return nullPixmap; } QT_END_NAMESPACE diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalimagenode_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalimagenode_p.h index f21667fdf7..b80bacbaa0 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalimagenode_p.h +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalimagenode_p.h @@ -124,8 +124,8 @@ public: QRectF rect() const; -private: const QPixmap &pixmap() const; +private: QRectF m_targetRect; QRectF m_innerTargetRect; @@ -136,6 +136,7 @@ private: QPixmap m_cachedMirroredPixmap; bool m_mirror; + bool m_textureIsLayer; bool m_smooth; bool m_tileHorizontal; bool m_tileVertical; diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode.cpp index f6898b3879..f50fa00b0b 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode.cpp @@ -47,6 +47,7 @@ QT_BEGIN_NAMESPACE QSGSoftwareInternalRectangleNode::QSGSoftwareInternalRectangleNode() : m_penWidth(0) , m_radius(0) + , m_vertical(true) , m_cornerPixmapIsDirty(true) , m_devicePixelRatio(1) { @@ -120,7 +121,7 @@ void QSGSoftwareInternalRectangleNode::setGradientStops(const QGradientStops &st for (const QGradientStop &stop : qAsConst(stops)) { if (stop.first < 0.0 || stop.first > 1.0) { needsNormalization = true; - continue; + break; } } @@ -186,6 +187,15 @@ void QSGSoftwareInternalRectangleNode::setGradientStops(const QGradientStops &st markDirty(DirtyMaterial); } +void QSGSoftwareInternalRectangleNode::setGradientVertical(bool vertical) +{ + if (m_vertical != vertical) { + m_vertical = vertical; + m_cornerPixmapIsDirty = true; + markDirty(DirtyMaterial); + } +} + void QSGSoftwareInternalRectangleNode::setRadius(qreal radius) { if (m_radius != radius) { @@ -209,7 +219,7 @@ void QSGSoftwareInternalRectangleNode::update() } if (!m_stops.isEmpty()) { - QLinearGradient gradient(QPoint(0,0), QPoint(0,1)); + QLinearGradient gradient(QPoint(0,0), QPoint(m_vertical ? 0 : 1, m_vertical ? 1 : 0)); gradient.setStops(m_stops); gradient.setCoordinateMode(QGradient::ObjectBoundingMode); m_brush = QBrush(gradient); @@ -227,8 +237,8 @@ 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(); + if (!qFuzzyCompare(painter->device()->devicePixelRatioF(), m_devicePixelRatio)) { + m_devicePixelRatio = painter->device()->devicePixelRatioF(); generateCornerPixmap(); } @@ -245,7 +255,7 @@ void QSGSoftwareInternalRectangleNode::paint(QPainter *painter) } 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); + QPixmap pixmap = QPixmap(qRound(m_rect.width() * m_devicePixelRatio), qRound(m_rect.height() * m_devicePixelRatio)); pixmap.fill(Qt::transparent); pixmap.setDevicePixelRatio(m_devicePixelRatio); QPainter pixmapPainter(&pixmap); @@ -356,7 +366,7 @@ void QSGSoftwareInternalRectangleNode::paintRectangle(QPainter *painter, const Q } else { //blit 4 corners to border - int scaledRadius = radius * m_devicePixelRatio; + int scaledRadius = qRound(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)); @@ -415,8 +425,11 @@ void QSGSoftwareInternalRectangleNode::generateCornerPixmap() { //Generate new corner Pixmap int radius = qFloor(qMin(qMin(m_rect.width(), m_rect.height()) * 0.5, m_radius)); + const auto width = qRound(radius * 2 * m_devicePixelRatio); + + if (m_cornerPixmap.width() != width) + m_cornerPixmap = QPixmap(width, width); - m_cornerPixmap = QPixmap(radius * 2 * m_devicePixelRatio, radius * 2 * m_devicePixelRatio); m_cornerPixmap.setDevicePixelRatio(m_devicePixelRatio); m_cornerPixmap.fill(Qt::transparent); diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode_p.h index f363e279e1..125520de26 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode_p.h +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode_p.h @@ -69,6 +69,7 @@ public: void setPenColor(const QColor &color) override; void setPenWidth(qreal width) override; void setGradientStops(const QGradientStops &stops) override; + void setGradientVertical(bool vertical) override; void setRadius(qreal radius) override; void setAntialiasing(bool antialiasing) override { Q_UNUSED(antialiasing) } void setAligned(bool aligned) override; @@ -91,11 +92,12 @@ private: double m_radius; QPen m_pen; QBrush m_brush; + bool m_vertical; bool m_cornerPixmapIsDirty; QPixmap m_cornerPixmap; - int m_devicePixelRatio; + qreal m_devicePixelRatio; }; QT_END_NAMESPACE diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarelayer.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarelayer.cpp index 2954f591ad..70378d2950 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarelayer.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarelayer.cpp @@ -45,12 +45,12 @@ QT_BEGIN_NAMESPACE QSGSoftwareLayer::QSGSoftwareLayer(QSGRenderContext *renderContext) - : m_item(0) + : m_item(nullptr) , m_context(renderContext) - , m_renderer(0) + , m_renderer(nullptr) , m_device_pixel_ratio(1) , m_mirrorHorizontal(false) - , m_mirrorVertical(false) + , m_mirrorVertical(true) , m_live(true) , m_grab(true) , m_recursive(false) @@ -203,7 +203,7 @@ void QSGSoftwareLayer::markDirtyTexture() void QSGSoftwareLayer::invalidated() { delete m_renderer; - m_renderer = 0; + m_renderer = nullptr; } void QSGSoftwareLayer::grab() @@ -229,9 +229,6 @@ void QSGSoftwareLayer::grab() if (m_pixmap.size() != m_size) { m_pixmap = QPixmap(m_size); m_pixmap.setDevicePixelRatio(m_device_pixel_ratio); - // This fill here is wasteful, but necessary because it is the only way - // to force a QImage based pixmap to have an alpha channel. - m_pixmap.fill(Qt::transparent); } // Render texture. @@ -243,9 +240,9 @@ void QSGSoftwareLayer::grab() 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_mirrorVertical ? m_rect.bottom() * m_device_pixel_ratio : m_rect.top() * 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_mirrorVertical ? -m_rect.height() * m_device_pixel_ratio : m_rect.height() * m_device_pixel_ratio); m_renderer->setProjectionRect(mirrored); m_renderer->setClearColor(Qt::transparent); diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarepainternode.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarepainternode.cpp index 34b0cd5b72..60ae06dd94 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarepainternode.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarepainternode.cpp @@ -47,7 +47,7 @@ QSGSoftwarePainterNode::QSGSoftwarePainterNode(QQuickPaintedItem *item) : QSGPainterNode() , m_preferredRenderTarget(QQuickPaintedItem::Image) , m_item(item) - , m_texture(0) + , m_texture(nullptr) , m_dirtyContents(false) , m_opaquePainting(false) , m_linear_filtering(false) diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaprenderer.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaprenderer.cpp index ad6cf39425..bb4afc8301 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaprenderer.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaprenderer.cpp @@ -79,16 +79,9 @@ void QSGSoftwarePixmapRenderer::render(QPaintDevice *target) QElapsedTimer renderTimer; // Setup background item - setBackgroundSize(QSize(target->width(), target->height())); + setBackgroundRect(m_projectionRect.normalized()); 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(); @@ -101,6 +94,19 @@ void QSGSoftwarePixmapRenderer::render(QPaintDevice *target) optimizeRenderList(); qint64 optimizeRenderListTime = renderTimer.restart(); + if (!isOpaque() && target->devType() == QInternal::Pixmap) { + // This fill here is wasteful, but necessary because it is the only way + // to force a QImage based pixmap to have an alpha channel. + static_cast<QPixmap *>(target)->fill(Qt::transparent); + } + + 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; + QRegion paintedRegion = renderNodes(&painter); qint64 renderTime = renderTimer.elapsed(); diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes.cpp index 77d21ec042..20286a03d5 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes.cpp @@ -38,6 +38,7 @@ ****************************************************************************/ #include "qsgsoftwarepublicnodes_p.h" +#include "qsgsoftwarelayer_p.h" #include "qsgsoftwarepixmaptexture_p.h" #include "qsgsoftwareinternalimagenode_p.h" @@ -98,12 +99,17 @@ void QSGSoftwareImageNode::paint(QPainter *painter) updateCachedMirroredPixmap(); painter->setRenderHint(QPainter::SmoothPixmapTransform, (m_filtering == QSGTexture::Linear)); + // Disable antialiased clipping. It causes transformed tiles to have gaps. + painter->setRenderHint(QPainter::Antialiasing, false); if (!m_cachedPixmap.isNull()) { painter->drawPixmap(m_rect, m_cachedPixmap, m_sourceRect); } else if (QSGSoftwarePixmapTexture *pt = qobject_cast<QSGSoftwarePixmapTexture *>(m_texture)) { const QPixmap &pm = pt->pixmap(); painter->drawPixmap(m_rect, pm, m_sourceRect); + } else if (QSGSoftwareLayer *pt = qobject_cast<QSGSoftwareLayer *>(m_texture)) { + const QPixmap &pm = pt->pixmap(); + painter->drawPixmap(m_rect, pm, m_sourceRect); } else if (QSGPlainTexture *pt = qobject_cast<QSGPlainTexture *>(m_texture)) { const QImage &im = pt->image(); painter->drawImage(m_rect, im, m_sourceRect); @@ -115,7 +121,6 @@ void QSGSoftwareImageNode::updateCachedMirroredPixmap() if (m_transformMode == NoTransform) { m_cachedPixmap = QPixmap(); } else { - if (QSGSoftwarePixmapTexture *pt = qobject_cast<QSGSoftwarePixmapTexture *>(m_texture)) { QTransform mirrorTransform; if (m_transformMode.testFlag(MirrorVertically)) @@ -123,6 +128,13 @@ void QSGSoftwareImageNode::updateCachedMirroredPixmap() if (m_transformMode.testFlag(MirrorHorizontally)) mirrorTransform = mirrorTransform.scale(-1, 1); m_cachedPixmap = pt->pixmap().transformed(mirrorTransform); + } else if (QSGSoftwareLayer *pt = qobject_cast<QSGSoftwareLayer *>(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 = qobject_cast<QSGPlainTexture *>(m_texture)) { m_cachedPixmap = QPixmap::fromImage(pt->image().mirrored(m_transformMode.testFlag(MirrorHorizontally), m_transformMode.testFlag(MirrorVertically))); } else { @@ -144,10 +156,11 @@ void QSGSoftwareNinePatchNode::setTexture(QSGTexture *texture) QSGSoftwarePixmapTexture *pt = qobject_cast<QSGSoftwarePixmapTexture*>(texture); if (!pt) { qWarning() << "Image used with invalid texture format."; - return; + } else { + m_pixmap = pt->pixmap(); + markDirty(DirtyMaterial); } - m_pixmap = pt->pixmap(); - markDirty(DirtyMaterial); + delete texture; } void QSGSoftwareNinePatchNode::setBounds(const QRectF &bounds) @@ -184,11 +197,14 @@ void QSGSoftwareNinePatchNode::update() void QSGSoftwareNinePatchNode::paint(QPainter *painter) { + // Disable antialiased clipping. It causes transformed tiles to have gaps. + painter->setRenderHint(QPainter::Antialiasing, false); + 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)); + m_margins, Qt::StretchTile, QSGSoftwareHelpers::QDrawBorderPixmap::DrawingHints(nullptr)); } QRectF QSGSoftwareNinePatchNode::bounds() const diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes_p.h index 9f1913205b..114137fb55 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes_p.h +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes_p.h @@ -133,6 +133,8 @@ public: QRectF bounds() const; + bool isOpaque() const { return !m_pixmap.hasAlphaChannel(); } + private: QPixmap m_pixmap; QRectF m_bounds; diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp index 8036baf166..7fb531cca3 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp @@ -134,73 +134,58 @@ void QSGSoftwareRenderableNode::update() { // Update the Node properties m_isDirty = true; + m_isOpaque = false; QRectF boundingRect; switch (m_nodeType) { case QSGSoftwareRenderableNode::SimpleRect: - if (m_handle.simpleRectNode->color().alpha() == 255 && !m_transform.isRotating()) + if (m_handle.simpleRectNode->color().alpha() == 255) m_isOpaque = true; - else - m_isOpaque = false; boundingRect = m_handle.simpleRectNode->rect(); break; case QSGSoftwareRenderableNode::SimpleTexture: - if (!m_handle.simpleTextureNode->texture()->hasAlphaChannel() && !m_transform.isRotating()) + if (!m_handle.simpleTextureNode->texture()->hasAlphaChannel()) m_isOpaque = true; - else - m_isOpaque = false; boundingRect = m_handle.simpleTextureNode->rect(); break; case QSGSoftwareRenderableNode::Image: - // There isn't a way to tell, so assume it's not - m_isOpaque = false; + m_isOpaque = !m_handle.imageNode->pixmap().hasAlphaChannel(); boundingRect = m_handle.imageNode->rect().toRect(); break; case QSGSoftwareRenderableNode::Painter: - if (m_handle.painterNode->opaquePainting() && !m_transform.isRotating()) + if (m_handle.painterNode->opaquePainting()) m_isOpaque = true; - else - m_isOpaque = false; boundingRect = QRectF(0, 0, m_handle.painterNode->size().width(), m_handle.painterNode->size().height()); break; case QSGSoftwareRenderableNode::Rectangle: - if (m_handle.rectangleNode->isOpaque() && !m_transform.isRotating()) + if (m_handle.rectangleNode->isOpaque()) m_isOpaque = true; - else - m_isOpaque = false; boundingRect = m_handle.rectangleNode->rect(); break; case QSGSoftwareRenderableNode::Glyph: // Always has alpha - m_isOpaque = false; - boundingRect = m_handle.glpyhNode->boundingRect(); break; case QSGSoftwareRenderableNode::NinePatch: - // Difficult to tell, assume non-opaque - m_isOpaque = false; + m_isOpaque = m_handle.ninePatchNode->isOpaque(); boundingRect = m_handle.ninePatchNode->bounds(); break; case QSGSoftwareRenderableNode::SimpleRectangle: - if (m_handle.simpleRectangleNode->color().alpha() == 255 && !m_transform.isRotating()) + if (m_handle.simpleRectangleNode->color().alpha() == 255) m_isOpaque = true; - else - m_isOpaque = false; boundingRect = m_handle.simpleRectangleNode->rect(); break; case QSGSoftwareRenderableNode::SimpleImage: - if (!m_handle.simpleImageNode->texture()->hasAlphaChannel() && !m_transform.isRotating()) + if (!m_handle.simpleImageNode->texture()->hasAlphaChannel()) m_isOpaque = true; - else - m_isOpaque = false; boundingRect = m_handle.simpleImageNode->rect(); break; @@ -211,10 +196,8 @@ void QSGSoftwareRenderableNode::update() break; #endif case QSGSoftwareRenderableNode::RenderNode: - if (m_handle.renderNode->flags().testFlag(QSGRenderNode::OpaqueRendering) && !m_transform.isRotating()) + if (m_handle.renderNode->flags().testFlag(QSGRenderNode::OpaqueRendering)) m_isOpaque = true; - else - m_isOpaque = false; boundingRect = m_handle.renderNode->rect(); break; @@ -222,6 +205,9 @@ void QSGSoftwareRenderableNode::update() break; } + if (m_transform.isRotating()) + m_isOpaque = false; + const QRectF transformedRect = m_transform.mapRect(boundingRect); m_boundingRectMin = toRectMin(transformedRect); m_boundingRectMax = toRectMax(transformedRect); @@ -232,8 +218,9 @@ void QSGSoftwareRenderableNode::update() m_boundingRectMin = QRect(); m_boundingRectMax = QRect(); } else { - m_boundingRectMin = m_boundingRectMin.intersected(m_clipRegion.rects().first()); - m_boundingRectMax = m_boundingRectMax.intersected(m_clipRegion.rects().first()); + const auto rects = m_clipRegion.begin(); + m_boundingRectMin = m_boundingRectMin.intersected(rects[0]); + m_boundingRectMax = m_boundingRectMax.intersected(rects[0]); } } diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode_p.h index 8fc87db179..b20d0a1828 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode_p.h +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode_p.h @@ -72,7 +72,7 @@ class QSGSoftwareNinePatchNode; class QSGSoftwareSpriteNode; class QSGRenderNode; -class QSGSoftwareRenderableNode +class Q_QUICK_PRIVATE_EXPORT QSGSoftwareRenderableNode { public: enum NodeType { @@ -104,6 +104,7 @@ public: bool isOpaque() const { return m_isOpaque; } bool isDirty() const { return m_isDirty; } bool isDirtyRegionEmpty() const; + QSGNode *handle() const { return m_handle.node; } void setTransform(const QTransform &transform); void setClipRegion(const QRegion &clipRegion, bool hasClipRegion = true); @@ -123,6 +124,7 @@ public: private: union RenderableNodeHandle { + QSGNode *node; QSGSimpleRectNode *simpleRectNode; QSGSimpleTextureNode *simpleTextureNode; QSGSoftwareInternalImageNode *imageNode; diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater.cpp index 4937565aa9..fabecfcbb8 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater.cpp @@ -83,7 +83,7 @@ void QSGSoftwareRenderableNodeUpdater::endVisit(QSGTransformNode *) bool QSGSoftwareRenderableNodeUpdater::visit(QSGClipNode *node) { // Make sure to translate the clip rect into world coordinates - if (m_clipState.count() == 1) { + if (m_clipState.count() == 0 || (m_clipState.count() == 1 && m_clipState.top().isNull())) { m_clipState.push(m_transformState.top().map(QRegion(node->clipRect().toRect()))); m_hasClip = true; } else { @@ -97,7 +97,7 @@ bool QSGSoftwareRenderableNodeUpdater::visit(QSGClipNode *node) void QSGSoftwareRenderableNodeUpdater::endVisit(QSGClipNode *) { m_clipState.pop(); - if (m_clipState.count() == 1) + if (m_clipState.count() == 0 || (m_clipState.count() == 1 && m_clipState.top().isNull())) m_hasClip = false; } diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer.cpp index 85d04fe136..e9ed52d428 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer.cpp @@ -112,8 +112,9 @@ void QSGSoftwareRenderer::render() QElapsedTimer renderTimer; setBackgroundColor(clearColor()); - setBackgroundSize(QSize(m_paintDevice->width() / m_paintDevice->devicePixelRatio(), - m_paintDevice->height() / m_paintDevice->devicePixelRatio())); + setBackgroundRect(QRect(0, 0, + m_paintDevice->width() / m_paintDevice->devicePixelRatioF(), + m_paintDevice->height() / m_paintDevice->devicePixelRatioF())); // Build Renderlist // The renderlist is created by visiting each node in the tree and when a @@ -155,6 +156,7 @@ void QSGSoftwareRenderer::render() m_flushRegion = renderNodes(&painter); qint64 renderTime = renderTimer.elapsed(); + painter.end(); if (m_backingStore != nullptr) m_backingStore->endPaint(); diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop.cpp index 962db20cbc..f5a41410ee 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop.cpp @@ -45,6 +45,7 @@ #include <private/qquickwindow_p.h> #include <QElapsedTimer> +#include <private/qquickanimatorcontroller_p.h> #include <private/qquickprofiler_p.h> #include <private/qsgsoftwarerenderer_p.h> #include <qpa/qplatformbackingstore.h> @@ -97,8 +98,9 @@ void QSGSoftwareRenderLoop::windowDestroyed(QQuickWindow *window) if (m_windows.size() == 0) { rc->invalidate(); - QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); } + + delete d->animationController; } void QSGSoftwareRenderLoop::renderWindow(QQuickWindow *window, bool isNewExpose) @@ -149,6 +151,7 @@ void QSGSoftwareRenderLoop::renderWindow(QQuickWindow *window, bool isNewExpose) emit window->afterAnimating(); cd->syncSceneGraph(); + rc->endSync(); if (profileFrames) syncTime = renderTimer.nsecsElapsed(); @@ -195,7 +198,7 @@ void QSGSoftwareRenderLoop::renderWindow(QQuickWindow *window, bool isNewExpose) int(polishTime / 1000000), int((syncTime - polishTime) / 1000000), int((renderTime - syncTime) / 1000000), - int((swapTime - renderTime) / 10000000), + int((swapTime - renderTime) / 1000000), int(lastFrameTime.msecsTo(QTime::currentTime()))); lastFrameTime = QTime::currentTime(); } diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarespritenode.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarespritenode.cpp index ba7bbc2d11..d4e5e98d68 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarespritenode.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarespritenode.cpp @@ -123,7 +123,7 @@ void QSGSoftwareSpriteNode::paint(QPainter *painter) // 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)); + QRectF(m_sourceA * pixmap.devicePixelRatioF(), m_spriteSize * pixmap.devicePixelRatioF())); } bool QSGSoftwareSpriteNode::isOpaque() const diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop.cpp index d2186e7cf1..f8973af2fb 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop.cpp @@ -294,7 +294,7 @@ bool QSGSoftwareRenderThread::event(QEvent *e) } rc->invalidate(); QCoreApplication::processEvents(); - QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); if (wme->destroying) delete wd->animationController; } @@ -330,6 +330,7 @@ bool QSGSoftwareRenderThread::event(QEvent *e) softwareRenderer->setBackingStore(backingStore); rc->initialize(nullptr); wd->syncSceneGraph(); + rc->endSync(); wd->renderSceneGraph(wme->window->size()); *wme->image = backingStore->handle()->toImage(); } @@ -443,6 +444,7 @@ void QSGSoftwareRenderThread::sync(bool inExpose) rc->initialize(nullptr); wd->syncSceneGraph(); + rc->endSync(); if (!hadRenderer && wd->renderer) { qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - created renderer"); @@ -454,7 +456,7 @@ void QSGSoftwareRenderThread::sync(bool inExpose) // 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); + QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); } if (!inExpose) { @@ -521,7 +523,7 @@ void QSGSoftwareRenderThread::syncAndRender() // 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"; + qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - blocking for %d ms", blockTime); msleep(blockTime); } renderThrottleTimer.restart(); diff --git a/src/quick/scenegraph/adaptations/software/software.pri b/src/quick/scenegraph/adaptations/software/software.pri index de5f01cdee..278dbe7944 100644 --- a/src/quick/scenegraph/adaptations/software/software.pri +++ b/src/quick/scenegraph/adaptations/software/software.pri @@ -18,8 +18,7 @@ SOURCES += \ $$PWD/qsgsoftwarerenderlistbuilder.cpp \ $$PWD/qsgsoftwarerenderloop.cpp \ $$PWD/qsgsoftwarelayer.cpp \ - $$PWD/qsgsoftwareadaptation.cpp \ - $$PWD/qsgsoftwarethreadedrenderloop.cpp + $$PWD/qsgsoftwareadaptation.cpp HEADERS += \ $$PWD/qsgsoftwarecontext_p.h \ @@ -37,8 +36,7 @@ HEADERS += \ $$PWD/qsgsoftwarerenderlistbuilder_p.h \ $$PWD/qsgsoftwarerenderloop_p.h \ $$PWD/qsgsoftwarelayer_p.h \ - $$PWD/qsgsoftwareadaptation_p.h \ - $$PWD/qsgsoftwarethreadedrenderloop_p.h + $$PWD/qsgsoftwareadaptation_p.h qtConfig(quick-sprite) { SOURCES += \ @@ -46,3 +44,10 @@ qtConfig(quick-sprite) { HEADERS += \ $$PWD/qsgsoftwarespritenode_p.h } + +qtConfig(thread) { + SOURCES += \ + $$PWD/qsgsoftwarethreadedrenderloop.cpp + HEADERS +=\ + $$PWD/qsgsoftwarethreadedrenderloop_p.h +} diff --git a/src/quick/scenegraph/compressedtexture/qsgcompressedatlastexture.cpp b/src/quick/scenegraph/compressedtexture/qsgcompressedatlastexture.cpp new file mode 100644 index 0000000000..796bc870de --- /dev/null +++ b/src/quick/scenegraph/compressedtexture/qsgcompressedatlastexture.cpp @@ -0,0 +1,172 @@ +/**************************************************************************** +** +** Copyright (C) 2018 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 "qsgcompressedatlastexture_p.h" + +#include <QtCore/QVarLengthArray> +#include <QtCore/QElapsedTimer> +#include <QtCore/QtMath> + +#include <QtGui/QOpenGLContext> +#include <QtGui/QGuiApplication> +#include <QtGui/QScreen> +#include <QtGui/QSurface> +#include <QtGui/QWindow> +#include <QtGui/QOpenGLFunctions> +#include <QtGui/QOpenGLTexture> +#include <QDebug> + +#include <private/qqmlglobal_p.h> +#include <private/qquickprofiler_p.h> +#include <private/qsgtexture_p.h> +#include <private/qsgcompressedtexture_p.h> + +QT_BEGIN_NAMESPACE + +static QElapsedTimer qsg_renderer_timer; + +namespace QSGCompressedAtlasTexture +{ + +Atlas::Atlas(const QSize &size, uint format) + : QSGAtlasTexture::AtlasBase(size) + , m_format(format) +{ +} + +Atlas::~Atlas() +{ +} + +Texture *Atlas::create(const QByteArray &data, int dataLength, int dataOffset, const QSize &size, const QSize &paddedSize) +{ + // No need to lock, as manager already locked it. + QRect rect = m_allocator.allocate(paddedSize); + if (rect.width() > 0 && rect.height() > 0) { + Texture *t = new Texture(this, rect, data, dataLength, dataOffset, size); + m_pending_uploads << t; + return t; + } + return nullptr; +} + +void Atlas::generateTexture() +{ + QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions(); + funcs->glCompressedTexImage2D(GL_TEXTURE_2D, 0, m_format, + m_size.width(), m_size.height(), 0, + (m_size.width() * m_size.height()) / 2, + nullptr); +} + +void Atlas::uploadPendingTexture(int i) +{ + Texture *texture = static_cast<Texture*>(m_pending_uploads.at(i)); + + const QRect &r = texture->atlasSubRect(); + + QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions(); + funcs->glCompressedTexSubImage2D(GL_TEXTURE_2D, 0, + r.x(), r.y(), r.width(), r.height(), m_format, + texture->sizeInBytes(), + texture->data().constData() + texture->dataOffset()); + + qCDebug(QSG_LOG_TIME_TEXTURE).nospace() << "compressed atlastexture uploaded in: " << qsg_renderer_timer.elapsed() + << "ms (" << texture->textureSize().width() << "x" + << texture->textureSize().height() << ")"; + + // TODO: consider releasing the data (as is done in the regular atlas)? + // The advantage of keeping this data around is that it makes it much easier + // to remove the texture from the atlas +} + +Texture::Texture(Atlas *atlas, const QRect &textureRect, const QByteArray &data, int dataLength, int dataOffset, const QSize &size) + : QSGAtlasTexture::TextureBase(atlas, textureRect) + , m_nonatlas_texture(nullptr) + , m_data(data) + , m_size(size) + , m_dataLength(dataLength) + , m_dataOffset(dataOffset) +{ + float w = atlas->size().width(); + float h = atlas->size().height(); + QRect nopad = atlasSubRect(); + // offset by half-pixel to prevent bleeding when scaling + m_texture_coords_rect = QRectF((nopad.x() + .5) / w, + (nopad.y() + .5) / h, + (nopad.width() - 1.) / w, + (nopad.height() - 1.) / h); +} + +Texture::~Texture() +{ + delete m_nonatlas_texture; +} + +bool Texture::hasAlphaChannel() const +{ + return QSGCompressedTexture::formatIsOpaque(static_cast<Atlas*>(m_atlas)->format()); +} + +QSGTexture *Texture::removedFromAtlas() const +{ + if (m_nonatlas_texture) { + m_nonatlas_texture->setMipmapFiltering(mipmapFiltering()); + m_nonatlas_texture->setFiltering(filtering()); + return m_nonatlas_texture; + } + + if (!m_data.isEmpty()) { + QTextureFileData texData; + texData.setData(m_data); + texData.setSize(m_size); + texData.setGLInternalFormat(static_cast<Atlas*>(m_atlas)->format()); + texData.setDataLength(m_dataLength); + texData.setDataOffset(m_dataOffset); + m_nonatlas_texture = new QSGCompressedTexture(texData); + m_nonatlas_texture->setMipmapFiltering(mipmapFiltering()); + m_nonatlas_texture->setFiltering(filtering()); + } + + return m_nonatlas_texture; +} + +} + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/compressedtexture/qsgcompressedatlastexture_p.h b/src/quick/scenegraph/compressedtexture/qsgcompressedatlastexture_p.h new file mode 100644 index 0000000000..59e935b623 --- /dev/null +++ b/src/quick/scenegraph/compressedtexture/qsgcompressedatlastexture_p.h @@ -0,0 +1,119 @@ +/**************************************************************************** +** +** Copyright (C) 2018 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 QSGCOMPRESSEDATLASTEXTURE_P_H +#define QSGCOMPRESSEDATLASTEXTURE_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 <QtCore/QSize> + +#include <QtGui/qopengl.h> + +#include <QtQuick/QSGTexture> +#include <QtQuick/private/qsgareaallocator_p.h> +#include <QtQuick/private/qsgatlastexture_p.h> + +QT_BEGIN_NAMESPACE + +class QSGCompressedTextureFactory; + +namespace QSGCompressedAtlasTexture { + +class Texture; + +class Atlas : public QSGAtlasTexture::AtlasBase +{ +public: + Atlas(const QSize &size, uint format); + ~Atlas(); + + void generateTexture() override; + void uploadPendingTexture(int i) override; + + Texture *create(const QByteArray &data, int dataLength, int dataOffset, const QSize &size, const QSize &paddedSize); + + uint format() const { return m_format; } + +private: + uint m_format; +}; + +class Texture : public QSGAtlasTexture::TextureBase +{ + Q_OBJECT +public: + Texture(Atlas *atlas, const QRect &textureRect, const QByteArray &data, int dataLength, int dataOffset, const QSize &size); + ~Texture(); + + QSize textureSize() const override { return m_size; } + bool hasAlphaChannel() const override; + bool hasMipmaps() const override { return false; } + + QRectF normalizedTextureSubRect() const override { return m_texture_coords_rect; } + + QSGTexture *removedFromAtlas() const override; + + const QByteArray &data() const { return m_data; } + int sizeInBytes() const { return m_dataLength; } + int dataOffset() const { return m_dataOffset; } + +private: + QRectF m_texture_coords_rect; + mutable QSGTexture *m_nonatlas_texture; + QByteArray m_data; + QSize m_size; + int m_dataLength; + int m_dataOffset; +}; + +} + +QT_END_NAMESPACE + +#endif diff --git a/src/quick/scenegraph/compressedtexture/qsgcompressedtexture.cpp b/src/quick/scenegraph/compressedtexture/qsgcompressedtexture.cpp new file mode 100644 index 0000000000..d3310bc11c --- /dev/null +++ b/src/quick/scenegraph/compressedtexture/qsgcompressedtexture.cpp @@ -0,0 +1,207 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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 "qsgcompressedtexture_p.h" +#include <QOpenGLContext> +#include <QOpenGLTexture> +#include <QOpenGLFunctions> +#include <QDebug> +#include <QtQuick/private/qquickwindow_p.h> + +QT_BEGIN_NAMESPACE + +Q_LOGGING_CATEGORY(QSG_LOG_TEXTUREIO, "qt.scenegraph.textureio"); + +QSGCompressedTexture::QSGCompressedTexture(const QTextureFileData &texData) + : m_textureData(texData) +{ + m_size = m_textureData.size(); + m_hasAlpha = !formatIsOpaque(m_textureData.glInternalFormat()); +} + +QSGCompressedTexture::~QSGCompressedTexture() +{ +#if QT_CONFIG(opengl) + if (m_textureId) { + QOpenGLContext *ctx = QOpenGLContext::currentContext(); + QOpenGLFunctions *funcs = ctx ? ctx->functions() : nullptr; + if (!funcs) + return; + + funcs->glDeleteTextures(1, &m_textureId); + } +#endif +} + +int QSGCompressedTexture::textureId() const +{ +#if QT_CONFIG(opengl) + if (!m_textureId) { + QOpenGLContext *ctx = QOpenGLContext::currentContext(); + QOpenGLFunctions *funcs = ctx ? ctx->functions() : nullptr; + if (!funcs) + return 0; + + funcs->glGenTextures(1, &m_textureId); + } +#endif + return m_textureId; +} + +QSize QSGCompressedTexture::textureSize() const +{ + return m_size; +} + +bool QSGCompressedTexture::hasAlphaChannel() const +{ + return m_hasAlpha; +} + +bool QSGCompressedTexture::hasMipmaps() const +{ + return false; +} + +void QSGCompressedTexture::bind() +{ +#if QT_CONFIG(opengl) + QOpenGLContext *ctx = QOpenGLContext::currentContext(); + QOpenGLFunctions *funcs = ctx ? ctx->functions() : nullptr; + if (!funcs) + return; + + if (!textureId()) + return; + + funcs->glBindTexture(GL_TEXTURE_2D, m_textureId); + + if (m_uploaded) + return; + + if (!m_textureData.isValid()) { + qCDebug(QSG_LOG_TEXTUREIO, "Invalid texture data for %s", m_textureData.logName().constData()); + funcs->glBindTexture(GL_TEXTURE_2D, 0); + return; + } + + if (Q_UNLIKELY(QSG_LOG_TEXTUREIO().isDebugEnabled())) { + qCDebug(QSG_LOG_TEXTUREIO) << "Uploading texture" << m_textureData; + while (funcs->glGetError() != GL_NO_ERROR); + } + + funcs->glCompressedTexImage2D(GL_TEXTURE_2D, 0, m_textureData.glInternalFormat(), + m_size.width(), m_size.height(), 0, m_textureData.dataLength(), + m_textureData.data().constData() + m_textureData.dataOffset()); + + if (Q_UNLIKELY(QSG_LOG_TEXTUREIO().isDebugEnabled())) { + GLuint error = funcs->glGetError(); + if (error != GL_NO_ERROR) { + qCDebug(QSG_LOG_TEXTUREIO, "glCompressedTexImage2D failed for %s, error 0x%x", m_textureData.logName().constData(), error); + } + } + + m_textureData = QTextureFileData(); // Release this memory, not needed anymore + + updateBindOptions(true); + m_uploaded = true; +#endif // QT_CONFIG(opengl) +} + +QTextureFileData QSGCompressedTexture::textureData() const +{ + return m_textureData; +} + +bool QSGCompressedTexture::formatIsOpaque(quint32 glTextureFormat) +{ + switch (glTextureFormat) { + case QOpenGLTexture::RGB_DXT1: + case QOpenGLTexture::R_ATI1N_UNorm: + case QOpenGLTexture::R_ATI1N_SNorm: + case QOpenGLTexture::RG_ATI2N_UNorm: + case QOpenGLTexture::RG_ATI2N_SNorm: + case QOpenGLTexture::RGB_BP_UNSIGNED_FLOAT: + case QOpenGLTexture::RGB_BP_SIGNED_FLOAT: + case QOpenGLTexture::R11_EAC_UNorm: + case QOpenGLTexture::R11_EAC_SNorm: + case QOpenGLTexture::RG11_EAC_UNorm: + case QOpenGLTexture::RG11_EAC_SNorm: + case QOpenGLTexture::RGB8_ETC2: + case QOpenGLTexture::SRGB8_ETC2: + case QOpenGLTexture::RGB8_ETC1: + case QOpenGLTexture::SRGB_DXT1: + return true; + break; + default: + return false; + } +} + +QSGCompressedTextureFactory::QSGCompressedTextureFactory(const QTextureFileData &texData) + : m_textureData(texData) +{ +} + +QSGTexture *QSGCompressedTextureFactory::createTexture(QQuickWindow *window) const +{ + if (!m_textureData.isValid()) + return nullptr; + + // attempt to atlas the texture + QSGRenderContext *context = QQuickWindowPrivate::get(window)->context; + QSGTexture *t = context->compressedTextureForFactory(this); + if (t) + return t; + + return new QSGCompressedTexture(m_textureData); +} + +int QSGCompressedTextureFactory::textureByteCount() const +{ + return qMax(0, m_textureData.data().size() - m_textureData.dataOffset()); +} + +QSize QSGCompressedTextureFactory::textureSize() const +{ + return m_textureData.size(); +} + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/compressedtexture/qsgcompressedtexture_p.h b/src/quick/scenegraph/compressedtexture/qsgcompressedtexture_p.h new file mode 100644 index 0000000000..c3b58a2389 --- /dev/null +++ b/src/quick/scenegraph/compressedtexture/qsgcompressedtexture_p.h @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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 QSGCOMPRESSEDTEXTURE_P_H +#define QSGCOMPRESSEDTEXTURE_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/qtexturefiledata_p.h> +#include <QSGTexture> +#include <QtQuick/private/qsgcontext_p.h> +#include <QQuickTextureFactory> +#include <QOpenGLFunctions> + +QT_BEGIN_NAMESPACE + +class Q_QUICK_PRIVATE_EXPORT QSGCompressedTexture : public QSGTexture +{ + Q_OBJECT +public: + QSGCompressedTexture(const QTextureFileData& texData); + virtual ~QSGCompressedTexture(); + + int textureId() const override; + QSize textureSize() const override; + bool hasAlphaChannel() const override; + bool hasMipmaps() const override; + + void bind() override; + + QTextureFileData textureData() const; + + static bool formatIsOpaque(quint32 glTextureFormat); + +protected: + QTextureFileData m_textureData; + QSize m_size; + mutable uint m_textureId = 0; + bool m_hasAlpha = false; + bool m_uploaded = false; +}; + +namespace QSGAtlasTexture { + class Manager; +} + +class Q_QUICK_PRIVATE_EXPORT QSGCompressedTextureFactory : public QQuickTextureFactory +{ +public: + QSGCompressedTextureFactory(const QTextureFileData& texData); + QSGTexture *createTexture(QQuickWindow *) const override; + int textureByteCount() const override; + QSize textureSize() const override; + +protected: + QTextureFileData m_textureData; + +private: + friend class QSGAtlasTexture::Manager; +}; + +QT_END_NAMESPACE + +#endif // QSGCOMPRESSEDTEXTURE_P_H diff --git a/src/quick/scenegraph/coreapi/qsgabstractrenderer.cpp b/src/quick/scenegraph/coreapi/qsgabstractrenderer.cpp index 3d4ce24716..fddac7ed71 100644 --- a/src/quick/scenegraph/coreapi/qsgabstractrenderer.cpp +++ b/src/quick/scenegraph/coreapi/qsgabstractrenderer.cpp @@ -97,7 +97,7 @@ QT_BEGIN_NAMESPACE \internal */ QSGAbstractRendererPrivate::QSGAbstractRendererPrivate() - : m_root_node(0) + : m_root_node(nullptr) , m_clear_color(Qt::transparent) , m_clear_mode(QSGAbstractRenderer::ClearColorBuffer | QSGAbstractRenderer::ClearDepthBuffer) { diff --git a/src/quick/scenegraph/coreapi/qsgabstractrenderer.h b/src/quick/scenegraph/coreapi/qsgabstractrenderer.h index eb9e7cea7c..b9805f9db6 100644 --- a/src/quick/scenegraph/coreapi/qsgabstractrenderer.h +++ b/src/quick/scenegraph/coreapi/qsgabstractrenderer.h @@ -61,8 +61,9 @@ public: ClearStencilBuffer = 0x0004 }; Q_DECLARE_FLAGS(ClearMode, ClearModeBit) + Q_FLAG(ClearMode) - virtual ~QSGAbstractRenderer(); + ~QSGAbstractRenderer() override; void setRootNode(QSGRootNode *node); QSGRootNode *rootNode() const; @@ -90,7 +91,7 @@ Q_SIGNALS: void sceneGraphChanged(); protected: - explicit QSGAbstractRenderer(QObject *parent = Q_NULLPTR); + explicit QSGAbstractRenderer(QObject *parent = nullptr); virtual void nodeChanged(QSGNode *node, QSGNode::DirtyState state) = 0; private: diff --git a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp index 78f2c86f6c..afe3380494 100644 --- a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp +++ b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp @@ -55,6 +55,7 @@ #include <QtGui/QOpenGLFunctions_1_0> #include <QtGui/QOpenGLFunctions_3_2_Core> +#include <private/qnumeric_p.h> #include <private/qquickprofiler_p.h> #include "qsgmaterialshader_p.h" @@ -143,7 +144,7 @@ ShaderManager::Shader *ShaderManager::prepareMaterial(QSGMaterial *material) Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphContextFrame); QSGMaterialShader *s = material->createShader(); - QOpenGLContext *ctx = QOpenGLContext::currentContext(); + QOpenGLContext *ctx = context->openglContext(); QSurfaceFormat::OpenGLContextProfile profile = ctx->format().profile(); QOpenGLShaderProgram *p = s->program(); @@ -154,10 +155,10 @@ ShaderManager::Shader *ShaderManager::prepareMaterial(QSGMaterial *material) p->bindAttributeLocation(attr[i], i); } p->bindAttributeLocation("_qt_order", i); - context->compileShader(s, material, qsgShaderRewriter_insertZAttributes(s->vertexShader(), profile), 0); + context->compileShader(s, material, qsgShaderRewriter_insertZAttributes(s->vertexShader(), profile), nullptr); context->initializeShader(s); if (!p->isLinked()) - return 0; + return nullptr; shader = new Shader; shader->program = s; @@ -214,7 +215,7 @@ void ShaderManager::invalidated() qDeleteAll(rewrittenShaders); rewrittenShaders.clear(); delete blitProgram; - blitProgram = 0; + blitProgram = nullptr; } void qsg_dumpShadowRoots(BatchRootInfo *i, int indent) @@ -225,7 +226,7 @@ void qsg_dumpShadowRoots(BatchRootInfo *i, int indent) QByteArray ind(indent + extraIndent + 10, ' '); if (!i) { - qDebug() << ind.constData() << "- no info"; + qDebug("%s - no info", ind.constData()); } else { qDebug() << ind.constData() << "- parent:" << i->parentRoot << "orders" << i->firstOrder << "->" << i->lastOrder << ", avail:" << i->availableOrders; for (QSet<Node *>::const_iterator it = i->subRoots.constBegin(); @@ -279,7 +280,7 @@ Updater::Updater(Renderer *r) void Updater::updateStates(QSGNode *n) { - m_current_clip = 0; + m_current_clip = nullptr; m_added = 0; m_transformChange = 0; @@ -292,15 +293,15 @@ void Updater::updateStates(QSGNode *n) qsg_dumpShadowRoots(sn); if (Q_UNLIKELY(debug_build())) { - qDebug() << "Updater::updateStates()"; + qDebug("Updater::updateStates()"); if (sn->dirtyState & (QSGNode::DirtyNodeAdded << 16)) - qDebug() << " - nodes have been added"; + qDebug(" - nodes have been added"); if (sn->dirtyState & (QSGNode::DirtyMatrix << 16)) - qDebug() << " - transforms have changed"; + qDebug(" - transforms have changed"); if (sn->dirtyState & (QSGNode::DirtyOpacity << 16)) - qDebug() << " - opacity has changed"; + qDebug(" - opacity has changed"); if (uint(sn->dirtyState) & uint(QSGNode::DirtyForceUpdate << 16)) - qDebug() << " - forceupdate"; + qDebug(" - forceupdate"); } if (Q_UNLIKELY(renderer->m_visualizeMode == Renderer::VisualizeChanges)) @@ -346,7 +347,7 @@ void Updater::visitNode(Node *n) m_added = count; m_force_update = force; - n->dirtyState = 0; + n->dirtyState = nullptr; } void Updater::visitClipNode(Node *n) @@ -472,7 +473,7 @@ void Updater::visitGeometryNode(Node *n) if (e->root) { BatchRootInfo *info = renderer->batchRootInfo(e->root); - while (info != 0) { + while (info != nullptr) { info->availableOrders--; if (info->availableOrders < 0) { renderer->m_rebuild |= Renderer::BuildRenderLists; @@ -480,10 +481,10 @@ void Updater::visitGeometryNode(Node *n) renderer->m_rebuild |= Renderer::BuildRenderListsForTaggedRoots; renderer->m_taggedRoots << e->root; } - if (info->parentRoot != 0) + if (info->parentRoot != nullptr) info = renderer->batchRootInfo(info->parentRoot); else - info = 0; + info = nullptr; } } else { renderer->m_rebuild |= Renderer::FullRebuild; @@ -679,12 +680,12 @@ void Batch::invalidate() // the batch to do an early out.. cleanupRemovedElements(); Element *e = first; - first = 0; - root = 0; + first = nullptr; + root = nullptr; while (e) { - e->batch = 0; + e->batch = nullptr; Element *n = e->nextInBatch; - e->nextInBatch = 0; + e->nextInBatch = nullptr; e = n; } } @@ -755,7 +756,7 @@ Renderer::Renderer(QSGDefaultRenderContext *ctx) , m_alphaRenderList(64) , m_nextRenderOrder(0) , m_partialRebuild(false) - , m_partialRebuildRoot(0) + , m_partialRebuildRoot(nullptr) , m_useDepthBuffer(true) , m_opaqueBatches(16) , m_alphaBatches(16) @@ -767,17 +768,15 @@ Renderer::Renderer(QSGDefaultRenderContext *ctx) , m_zRange(0) , m_renderOrderRebuildLower(-1) , m_renderOrderRebuildUpper(-1) - , m_currentMaterial(0) - , m_currentShader(0) + , m_currentMaterial(nullptr) + , m_currentShader(nullptr) , m_currentStencilValue(0) , m_clipMatrixId(0) - , m_currentClip(0) + , m_currentClip(nullptr) , m_currentClipType(NoClip) , m_vertexUploadPool(256) -#ifdef QSG_SEPARATE_INDEX_BUFFER , m_indexUploadPool(64) -#endif - , m_vao(0) + , m_vao(nullptr) , m_visualizeMode(VisualizeNothing) { initializeOpenGLFunctions(); @@ -804,8 +803,11 @@ Renderer::Renderer(QSGDefaultRenderContext *ctx) m_batchVertexThreshold = qt_sg_envInt("QSG_RENDERER_BATCH_VERTEX_THRESHOLD", 1024); if (Q_UNLIKELY(debug_build() || debug_render())) { - qDebug() << "Batch thresholds: nodes:" << m_batchNodeThreshold << " vertices:" << m_batchVertexThreshold; - qDebug() << "Using buffer strategy:" << (m_bufferStrategy == GL_STATIC_DRAW ? "static" : (m_bufferStrategy == GL_DYNAMIC_DRAW ? "dynamic" : "stream")); + qDebug("Batch thresholds: nodes: %d vertices: %d", + m_batchNodeThreshold, m_batchVertexThreshold); + qDebug("Using buffer strategy: %s", + (m_bufferStrategy == GL_STATIC_DRAW + ? "static" : (m_bufferStrategy == GL_DYNAMIC_DRAW ? "dynamic" : "stream"))); } // If rendering with an OpenGL Core profile context, we need to create a VAO @@ -830,12 +832,11 @@ static void qsg_wipeBuffer(Buffer *buffer, QOpenGLFunctions *funcs) free(buffer->data); } -static void qsg_wipeBatch(Batch *batch, QOpenGLFunctions *funcs) +static void qsg_wipeBatch(Batch *batch, QOpenGLFunctions *funcs, bool separateIndexBuffer) { qsg_wipeBuffer(&batch->vbo, funcs); -#ifdef QSG_SEPARATE_INDEX_BUFFER - qsg_wipeBuffer(&batch->ibo, funcs); -#endif + if (separateIndexBuffer) + qsg_wipeBuffer(&batch->ibo, funcs); delete batch; } @@ -843,9 +844,13 @@ Renderer::~Renderer() { if (QOpenGLContext::currentContext()) { // Clean up batches and buffers - for (int i=0; i<m_opaqueBatches.size(); ++i) qsg_wipeBatch(m_opaqueBatches.at(i), this); - for (int i=0; i<m_alphaBatches.size(); ++i) qsg_wipeBatch(m_alphaBatches.at(i), this); - for (int i=0; i<m_batchPool.size(); ++i) qsg_wipeBatch(m_batchPool.at(i), this); + const bool separateIndexBuffer = m_context->separateIndexBuffer(); + for (int i = 0; i < m_opaqueBatches.size(); ++i) + qsg_wipeBatch(m_opaqueBatches.at(i), this, separateIndexBuffer); + for (int i = 0; i < m_alphaBatches.size(); ++i) + qsg_wipeBatch(m_alphaBatches.at(i), this, separateIndexBuffer); + for (int i = 0; i < m_batchPool.size(); ++i) + qsg_wipeBatch(m_batchPool.at(i), this, separateIndexBuffer); } for (Node *n : qAsConst(m_nodes)) @@ -885,13 +890,8 @@ void Renderer::map(Buffer *buffer, int byteSize, bool isIndexBuf) if (!m_context->hasBrokenIndexBufferObjects() && m_visualizeMode == VisualizeNothing) { // Common case, use a shared memory pool for uploading vertex data to avoid // excessive reevaluation - QDataBuffer<char> &pool = -#ifdef QSG_SEPARATE_INDEX_BUFFER - isIndexBuf ? m_indexUploadPool : m_vertexUploadPool; -#else - m_vertexUploadPool; - Q_UNUSED(isIndexBuf); -#endif + QDataBuffer<char> &pool = m_context->separateIndexBuffer() && isIndexBuf + ? m_indexUploadPool : m_vertexUploadPool; if (byteSize > pool.size()) pool.resize(byteSize); buffer->data = pool.data(); @@ -912,7 +912,7 @@ void Renderer::unmap(Buffer *buffer, bool isIndexBuf) glBufferData(target, buffer->size, buffer->data, m_bufferStrategy); if (!m_context->hasBrokenIndexBufferObjects() && m_visualizeMode == VisualizeNothing) { - buffer->data = 0; + buffer->data = nullptr; } } @@ -940,7 +940,7 @@ void Renderer::removeBatchRootFromParent(Node *childRoot) Q_ASSERT(parentInfo->subRoots.contains(childRoot)); parentInfo->subRoots.remove(childRoot); - childInfo->parentRoot = 0; + childInfo->parentRoot = nullptr; } void Renderer::registerBatchRoot(Node *subRoot, Node *parentRoot) @@ -969,9 +969,10 @@ bool Renderer::changeBatchRoot(Node *node, Node *root) void Renderer::nodeChangedBatchRoot(Node *node, Node *root) { if (node->type() == QSGNode::ClipNodeType || node->isBatchRoot) { - if (!changeBatchRoot(node, root)) - return; - node = root; + // When we reach a batchroot, we only need to update it. Its subtree + // is relative to that root, so no need to recurse further. + changeBatchRoot(node, root); + return; } else if (node->type() == QSGNode::GeometryNodeType) { // Only need to change the root as nodeChanged anyway flags a full update. Element *e = node->element(); @@ -1067,7 +1068,7 @@ void Renderer::nodeWasRemoved(Node *node) if (e) { e->removed = true; m_elementsToDelete.add(e); - e->node = 0; + e->node = nullptr; if (e->root) { BatchRootInfo *info = batchRootInfo(e->root); info->availableOrders++; @@ -1110,7 +1111,7 @@ void Renderer::nodeWasRemoved(Node *node) void Renderer::turnNodeIntoBatchRoot(Node *node) { - if (Q_UNLIKELY(debug_change())) qDebug() << " - new batch root"; + if (Q_UNLIKELY(debug_change())) qDebug(" - new batch root"); m_rebuild |= FullRebuild; node->isBatchRoot = true; node->becameBatchRoot = true; @@ -1180,7 +1181,7 @@ void Renderer::nodeChanged(QSGNode *node, QSGNode::DirtyState state) return; } if (node == rootNode()) - nodeWasAdded(node, 0); + nodeWasAdded(node, nullptr); else nodeWasAdded(node, m_nodes.value(node->parent())); } @@ -1433,7 +1434,7 @@ void Renderer::buildRenderListsForTaggedRoots() } } m_partialRebuild = false; - m_partialRebuildRoot = 0; + m_partialRebuildRoot = nullptr; m_taggedRoots.clear(); m_nextRenderOrder = qMax(m_nextRenderOrder, maxRenderOrder); @@ -1859,11 +1860,11 @@ void Renderer::uploadBatch(Batch *b) ibufferSize = unmergedIndexSize; } -#ifdef QSG_SEPARATE_INDEX_BUFFER - map(&b->ibo, ibufferSize, true); -#else - bufferSize += ibufferSize; -#endif + const bool separateIndexBuffer = m_context->separateIndexBuffer(); + if (separateIndexBuffer) + map(&b->ibo, ibufferSize, true); + else + bufferSize += ibufferSize; map(&b->vbo, bufferSize); if (Q_UNLIKELY(debug_upload())) qDebug() << " - batch" << b << " first:" << b->first << " root:" @@ -1873,22 +1874,17 @@ void Renderer::uploadBatch(Batch *b) if (b->merged) { char *vertexData = b->vbo.data; char *zData = vertexData + b->vertexCount * g->sizeOfVertex(); -#ifdef QSG_SEPARATE_INDEX_BUFFER - char *indexData = b->ibo.data; -#else - char *indexData = zData + (m_useDepthBuffer ? b->vertexCount * sizeof(float) : 0); -#endif + char *indexData = separateIndexBuffer + ? b->ibo.data + : zData + (int(m_useDepthBuffer) * b->vertexCount * sizeof(float)); quint16 iOffset = 0; e = b->first; int verticesInSet = 0; int indicesInSet = 0; b->drawSets.reset(); -#ifdef QSG_SEPARATE_INDEX_BUFFER - int drawSetIndices = 0; -#else - int drawSetIndices = indexData - vertexData; -#endif + int drawSetIndices = separateIndexBuffer ? 0 : indexData - vertexData; + const auto indexBase = separateIndexBuffer ? b->ibo.data : b->vbo.data; b->drawSets << DrawSet(0, zData - vertexData, drawSetIndices); while (e) { verticesInSet += e->node->geometry()->vertexCount(); @@ -1898,11 +1894,7 @@ void Renderer::uploadBatch(Batch *b) b->drawSets.last().indices += 1 * sizeof(quint16); b->drawSets.last().indexCount -= 2; } -#ifdef QSG_SEPARATE_INDEX_BUFFER - drawSetIndices = indexData - b->ibo.data; -#else - drawSetIndices = indexData - b->vbo.data; -#endif + drawSetIndices = indexData - indexBase; b->drawSets << DrawSet(vertexData - b->vbo.data, zData - b->vbo.data, drawSetIndices); @@ -1922,11 +1914,8 @@ void Renderer::uploadBatch(Batch *b) } } else { char *vboData = b->vbo.data; -#ifdef QSG_SEPARATE_INDEX_BUFFER - char *iboData = b->ibo.data; -#else - char *iboData = vboData + b->vertexCount * g->sizeOfVertex(); -#endif + char *iboData = separateIndexBuffer ? b->ibo.data + : vboData + b->vertexCount * g->sizeOfVertex(); Element *e = b->first; while (e) { QSGGeometry *g = e->node->geometry(); @@ -1974,12 +1963,9 @@ void Renderer::uploadBatch(Batch *b) } if (!b->drawSets.isEmpty()) { - const quint16 *id = -# ifdef QSG_SEPARATE_INDEX_BUFFER - (const quint16 *) (b->ibo.data); -# else - (const quint16 *) (b->vbo.data + b->drawSets.at(0).indices); -# endif + const quint16 *id = (const quint16 *)(separateIndexBuffer + ? b->ibo.data + : b->vbo.data + b->drawSets.at(0).indices); { QDebug iDump = qDebug(); iDump << " -- Index Data, count:" << b->indexCount; @@ -1999,9 +1985,8 @@ void Renderer::uploadBatch(Batch *b) #endif // QT_NO_DEBUG_OUTPUT unmap(&b->vbo); -#ifdef QSG_SEPARATE_INDEX_BUFFER - unmap(&b->ibo, true); -#endif + if (separateIndexBuffer) + unmap(&b->ibo, true); if (Q_UNLIKELY(debug_upload())) qDebug() << " --- vertex/index buffers unmapped, batch upload completed..."; @@ -2030,7 +2015,7 @@ Renderer::ClipType Renderer::updateStencilClip(const QSGClipNode *clip) int vboSize = 0; bool useVBO = false; - QOpenGLContext *ctx = QOpenGLContext::currentContext(); + QOpenGLContext *ctx = m_context->openglContext(); QSurfaceFormat::OpenGLContextProfile profile = ctx->format().profile(); if (!ctx->isOpenGLES() && profile == QSurfaceFormat::CoreProfile) { @@ -2139,7 +2124,7 @@ Renderer::ClipType Renderer::updateStencilClip(const QSGClipNode *clip) glBufferSubData(GL_ARRAY_BUFFER, 0, vertexByteSize, g->vertexData()); } - pointer = 0; + pointer = nullptr; } glVertexAttribPointer(0, a->tupleSize, a->type, GL_FALSE, g->sizeOfVertex(), pointer); @@ -2181,7 +2166,7 @@ void Renderer::updateClip(const QSGClipNode *clipList, const Batch *batch) m_currentClip = clipList; // updateClip sets another program, so force-reactivate our own if (m_currentShader) - setActiveShader(0, 0); + setActiveShader(nullptr, nullptr); glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); if (batch->isOpaque) @@ -2202,8 +2187,8 @@ void Renderer::updateClip(const QSGClipNode *clipList, const Batch *batch) */ void Renderer::setActiveShader(QSGMaterialShader *program, ShaderManager::Shader *shader) { - const char * const *c = m_currentProgram ? m_currentProgram->attributeNames() : 0; - const char * const *n = program ? program->attributeNames() : 0; + const char * const *c = m_currentProgram ? m_currentProgram->attributeNames() : nullptr; + const char * const *n = program ? program->attributeNames() : nullptr; int cza = m_currentShader ? m_currentShader->pos_order : -1; int nza = shader ? shader->pos_order : -1; @@ -2214,18 +2199,18 @@ void Renderer::setActiveShader(QSGMaterialShader *program, ShaderManager::Shader bool was = c; if (cza == i) { was = true; - c = 0; + c = nullptr; } else if (c && !c[i]) { // end of the attribute array names - c = 0; + c = nullptr; was = false; } bool is = n; if (nza == i) { is = true; - n = 0; + n = nullptr; } else if (n && !n[i]) { - n = 0; + n = nullptr; is = false; } @@ -2241,7 +2226,7 @@ void Renderer::setActiveShader(QSGMaterialShader *program, ShaderManager::Shader m_currentProgram->deactivate(); m_currentProgram = program; m_currentShader = shader; - m_currentMaterial = 0; + m_currentMaterial = nullptr; if (m_currentProgram) { m_currentProgram->program()->bind(); m_currentProgram->activate(); @@ -2293,12 +2278,8 @@ void Renderer::renderMergedBatch(const Batch *batch) glBindBuffer(GL_ARRAY_BUFFER, batch->vbo.id); - char *indexBase = 0; -#ifdef QSG_SEPARATE_INDEX_BUFFER - const Buffer *indexBuf = &batch->ibo; -#else - const Buffer *indexBuf = &batch->vbo; -#endif + char *indexBase = nullptr; + const Buffer *indexBuf = m_context->separateIndexBuffer() ? &batch->ibo : &batch->vbo; if (m_context->hasBrokenIndexBufferObjects()) { indexBase = indexBuf->data; glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); @@ -2317,7 +2298,7 @@ void Renderer::renderMergedBatch(const Batch *batch) setActiveShader(program, sms); m_current_opacity = gn->inheritedOpacity(); - if (sms->lastOpacity != m_current_opacity) { + if (!qFuzzyCompare(sms->lastOpacity, float(m_current_opacity))) { dirty |= QSGMaterialShader::RenderState::DirtyOpacity; sms->lastOpacity = m_current_opacity; } @@ -2326,7 +2307,7 @@ void Renderer::renderMergedBatch(const Batch *batch) #ifndef QT_NO_DEBUG if (qsg_test_and_clear_material_failure()) { - qDebug() << "QSGMaterial::updateState triggered an error (merged), batch will be skipped:"; + qDebug("QSGMaterial::updateState triggered an error (merged), batch will be skipped:"); Element *ee = e; while (ee) { qDebug() << " -" << ee->node; @@ -2389,12 +2370,9 @@ void Renderer::renderUnmergedBatch(const Batch *batch) updateClip(gn->clipList(), batch); glBindBuffer(GL_ARRAY_BUFFER, batch->vbo.id); - char *indexBase = 0; -#ifdef QSG_SEPARATE_INDEX_BUFFER - const Buffer *indexBuf = &batch->ibo; -#else - const Buffer *indexBuf = &batch->vbo; -#endif + char *indexBase = nullptr; + const auto separateIndexBuffer = m_context->separateIndexBuffer(); + const Buffer *indexBuf = separateIndexBuffer ? &batch->ibo : &batch->vbo; if (batch->indexCount) { if (m_context->hasBrokenIndexBufferObjects()) { indexBase = indexBuf->data; @@ -2423,11 +2401,9 @@ void Renderer::renderUnmergedBatch(const Batch *batch) } int vOffset = 0; -#ifdef QSG_SEPARATE_INDEX_BUFFER char *iOffset = indexBase; -#else - char *iOffset = indexBase + batch->vertexCount * gn->geometry()->sizeOfVertex(); -#endif + if (!separateIndexBuffer) + iOffset += batch->vertexCount * gn->geometry()->sizeOfVertex(); QMatrix4x4 rootMatrix = batch->root ? qsg_matrixForRoot(batch->root) : QMatrix4x4(); @@ -2447,7 +2423,7 @@ void Renderer::renderUnmergedBatch(const Batch *batch) #ifndef QT_NO_DEBUG if (qsg_test_and_clear_material_failure()) { - qDebug() << "QSGMaterial::updateState() triggered an error (unmerged), batch will be skipped:"; + qDebug("QSGMaterial::updateState() triggered an error (unmerged), batch will be skipped:"); qDebug() << " - offending node is" << e->node; QSGNodeDumper::dump(rootNode()); qFatal("Aborting: scene graph is invalid..."); @@ -2492,18 +2468,21 @@ void Renderer::updateLineWidth(QSGGeometry *g) if (g->drawingMode() == GL_LINE_STRIP || g->drawingMode() == GL_LINE_LOOP || g->drawingMode() == GL_LINES) glLineWidth(g->lineWidth()); #if !defined(QT_OPENGL_ES_2) - else if (!QOpenGLContext::currentContext()->isOpenGLES() && g->drawingMode() == GL_POINTS) { - QOpenGLFunctions_1_0 *gl1funcs = 0; - QOpenGLFunctions_3_2_Core *gl3funcs = 0; - if (QOpenGLContext::currentContext()->format().profile() == QSurfaceFormat::CoreProfile) - gl3funcs = QOpenGLContext::currentContext()->versionFunctions<QOpenGLFunctions_3_2_Core>(); - else - gl1funcs = QOpenGLContext::currentContext()->versionFunctions<QOpenGLFunctions_1_0>(); - Q_ASSERT(gl1funcs || gl3funcs); - if (gl1funcs) - gl1funcs->glPointSize(g->lineWidth()); - else - gl3funcs->glPointSize(g->lineWidth()); + else { + QOpenGLContext *ctx = m_context->openglContext(); + if (!ctx->isOpenGLES() && g->drawingMode() == GL_POINTS) { + QOpenGLFunctions_1_0 *gl1funcs = nullptr; + QOpenGLFunctions_3_2_Core *gl3funcs = nullptr; + if (ctx->format().profile() == QSurfaceFormat::CoreProfile) + gl3funcs = ctx->versionFunctions<QOpenGLFunctions_3_2_Core>(); + else + gl1funcs = ctx->versionFunctions<QOpenGLFunctions_1_0>(); + Q_ASSERT(gl1funcs || gl3funcs); + if (gl1funcs) + gl1funcs->glPointSize(g->lineWidth()); + else + gl3funcs->glPointSize(g->lineWidth()); + } } #endif } @@ -2538,10 +2517,10 @@ void Renderer::renderBatches() bindable()->clear(clearMode()); m_current_opacity = 1; - m_currentMaterial = 0; - m_currentShader = 0; - m_currentProgram = 0; - m_currentClip = 0; + m_currentMaterial = nullptr; + m_currentShader = nullptr; + m_currentProgram = nullptr; + m_currentClip = nullptr; bool renderOpaque = !debug_noopaque(); bool renderAlpha = !debug_noalpha(); @@ -2574,8 +2553,8 @@ void Renderer::renderBatches() } if (m_currentShader) - setActiveShader(0, 0); - updateStencilClip(0); + setActiveShader(nullptr, nullptr); + updateStencilClip(nullptr); glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); glDepthMask(true); @@ -2589,12 +2568,12 @@ void Renderer::deleteRemovedElements() for (int i=0; i<m_opaqueRenderList.size(); ++i) { Element **e = m_opaqueRenderList.data() + i; if (*e && (*e)->removed) - *e = 0; + *e = nullptr; } for (int i=0; i<m_alphaRenderList.size(); ++i) { Element **e = m_alphaRenderList.data() + i; if (*e && (*e)->removed) - *e = 0; + *e = nullptr; } for (int i=0; i<m_elementsToDelete.size(); ++i) { @@ -2609,6 +2588,8 @@ void Renderer::deleteRemovedElements() void Renderer::render() { + Q_ASSERT(m_context->openglContext() == QOpenGLContext::currentContext()); + if (Q_UNLIKELY(debug_dump())) { qDebug("\n"); QSGNodeDumper::dump(rootNode()); @@ -2653,12 +2634,12 @@ void Renderer::render() m_rebuild |= BuildBatches; if (Q_UNLIKELY(debug_build())) { - qDebug() << "Opaque render lists" << (complete ? "(complete)" : "(partial)") << ":"; + qDebug("Opaque render lists %s:", (complete ? "(complete)" : "(partial)")); for (int i=0; i<m_opaqueRenderList.size(); ++i) { Element *e = m_opaqueRenderList.at(i); qDebug() << " - element:" << e << " batch:" << e->batch << " node:" << e->node << " order:" << e->order; } - qDebug() << "Alpha render list:" << (complete ? "(complete)" : "(partial)") << ":"; + qDebug("Alpha render list %s:", complete ? "(complete)" : "(partial)"); for (int i=0; i<m_alphaRenderList.size(); ++i) { Element *e = m_alphaRenderList.at(i); qDebug() << " - element:" << e << " batch:" << e->batch << " node:" << e->node << " order:" << e->order; @@ -2683,7 +2664,7 @@ void Renderer::render() if (Q_UNLIKELY(debug_render())) timePrepareAlpha = timer.restart(); if (Q_UNLIKELY(debug_build())) { - qDebug() << "Opaque Batches:"; + qDebug("Opaque Batches:"); for (int i=0; i<m_opaqueBatches.size(); ++i) { Batch *b = m_opaqueBatches.at(i); qDebug() << " - Batch " << i << b << (b->needsUpload ? "upload" : "") << " root:" << b->root; @@ -2691,7 +2672,7 @@ void Renderer::render() qDebug() << " - element:" << e << " node:" << e->node << e->order; } } - qDebug() << "Alpha Batches:"; + qDebug("Alpha Batches:"); for (int i=0; i<m_alphaBatches.size(); ++i) { Batch *b = m_alphaBatches.at(i); qDebug() << " - Batch " << i << b << (b->needsUpload ? "upload" : "") << " root:" << b->root; @@ -2725,39 +2706,31 @@ void Renderer::render() if (Q_UNLIKELY(debug_render())) timeSorting = timer.restart(); int largestVBO = 0; -#ifdef QSG_SEPARATE_INDEX_BUFFER int largestIBO = 0; -#endif - if (Q_UNLIKELY(debug_upload())) qDebug() << "Uploading Opaque Batches:"; + if (Q_UNLIKELY(debug_upload())) qDebug("Uploading Opaque Batches:"); for (int i=0; i<m_opaqueBatches.size(); ++i) { Batch *b = m_opaqueBatches.at(i); largestVBO = qMax(b->vbo.size, largestVBO); -#ifdef QSG_SEPARATE_INDEX_BUFFER largestIBO = qMax(b->ibo.size, largestIBO); -#endif uploadBatch(b); } if (Q_UNLIKELY(debug_render())) timeUploadOpaque = timer.restart(); - if (Q_UNLIKELY(debug_upload())) qDebug() << "Uploading Alpha Batches:"; + if (Q_UNLIKELY(debug_upload())) qDebug("Uploading Alpha Batches:"); for (int i=0; i<m_alphaBatches.size(); ++i) { Batch *b = m_alphaBatches.at(i); uploadBatch(b); largestVBO = qMax(b->vbo.size, largestVBO); -#ifdef QSG_SEPARATE_INDEX_BUFFER largestIBO = qMax(b->ibo.size, largestIBO); -#endif } if (Q_UNLIKELY(debug_render())) timeUploadAlpha = timer.restart(); if (largestVBO * 2 < m_vertexUploadPool.size()) m_vertexUploadPool.resize(largestVBO * 2); -#ifdef QSG_SEPARATE_INDEX_BUFFER - if (largestIBO * 2 < m_indexUploadPool.size()) + if (m_context->separateIndexBuffer() && largestIBO * 2 < m_indexUploadPool.size()) m_indexUploadPool.resize(largestIBO * 2); -#endif renderBatches(); @@ -2805,11 +2778,11 @@ void Renderer::renderRenderNode(Batch *batch) Q_ASSERT(batch->first->isRenderNode); RenderNodeElement *e = (RenderNodeElement *) batch->first; - setActiveShader(0, 0); + setActiveShader(nullptr, nullptr); QSGNode *clip = e->renderNode->parent(); QSGRenderNodePrivate *rd = QSGRenderNodePrivate::get(e->renderNode); - rd->m_clip_list = 0; + rd->m_clip_list = nullptr; while (clip != rootNode()) { if (clip->type() == QSGNode::ClipNodeType) { rd->m_clip_list = static_cast<QSGClipNode *>(clip); @@ -2873,8 +2846,8 @@ void Renderer::renderRenderNode(Batch *batch) e->renderNode->render(&state); - rd->m_matrix = 0; - rd->m_clip_list = 0; + rd->m_matrix = nullptr; + rd->m_clip_list = nullptr; if (changes & QSGRenderNode::ViewportState) { QRect r = viewportRect(); @@ -2889,7 +2862,7 @@ void Renderer::renderRenderNode(Batch *batch) if (changes & (QSGRenderNode::StencilState | QSGRenderNode::ScissorState)) { glDisable(GL_SCISSOR_TEST); - m_currentClip = 0; + m_currentClip = nullptr; m_currentClipType = NoClip; } @@ -2913,6 +2886,11 @@ void Renderer::renderRenderNode(Batch *batch) glBindFramebuffer(GL_FRAMEBUFFER, prevFbo); } +void Renderer::releaseCachedResources() +{ + m_shaderManager->invalidated(); +} + class VisualizeShader : public QOpenGLShaderProgram { public: @@ -2963,14 +2941,12 @@ void Renderer::visualizeBatch(Batch *b) if (b->merged) { shader->setUniformValue(shader->matrix, matrix); + const auto &dataStart = m_context->separateIndexBuffer() ? b->ibo.data : b->vbo.data; for (int ds=0; ds<b->drawSets.size(); ++ds) { const DrawSet &set = b->drawSets.at(ds); glVertexAttribPointer(a.position, 2, a.type, false, g->sizeOfVertex(), (void *) (qintptr) (set.vertices)); -#ifdef QSG_SEPARATE_INDEX_BUFFER - glDrawElements(g->drawingMode(), set.indexCount, GL_UNSIGNED_SHORT, (void *) (qintptr) (b->ibo.data + set.indices)); -#else - glDrawElements(g->drawingMode(), set.indexCount, GL_UNSIGNED_SHORT, (void *) (qintptr) (b->vbo.data + set.indices)); -#endif + glDrawElements(g->drawingMode(), set.indexCount, GL_UNSIGNED_SHORT, + (void *)(qintptr)(dataStart + set.indices)); } } else { Element *e = b->first; @@ -3054,7 +3030,7 @@ void Renderer::visualizeChanges(Node *n) // This is because many changes don't propegate their dirty state to the // parent so the node updater will not unset these states. They are // not used for anything so, unsetting it should have no side effects. - n->dirtyState = 0; + n->dirtyState = nullptr; } SHADOWNODE_TRAVERSE(n) { @@ -3146,7 +3122,7 @@ void Renderer::visualizeOverdraw() visualizeOverdraw_helper(m_nodes.value(rootNode())); // Animate the view... - QSurface *surface = QOpenGLContext::currentContext()->surface(); + QSurface *surface = m_context->openglContext()->surface(); if (surface->surfaceClass() == QSurface::Window) if (QQuickWindow *window = qobject_cast<QQuickWindow *>(static_cast<QWindow *>(surface))) window->update(); diff --git a/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h b/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h index 2c0f8667e8..12b48c1451 100644 --- a/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h +++ b/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h @@ -125,7 +125,6 @@ template <typename Type, int PageSize> class Allocator { public: Allocator() - : m_freePage(0) { pages.push_back(new AllocatorPage<Type, PageSize>()); } @@ -209,7 +208,7 @@ public: } QVector<AllocatorPage<Type, PageSize> *> pages; - int m_freePage; + int m_freePage = 0; }; @@ -306,12 +305,7 @@ struct Buffer { struct Element { Element() - : node(0) - , batch(0) - , nextInBatch(0) - , root(0) - , order(0) - , boundsComputed(false) + : boundsComputed(false) , boundsOutsideFloatRange(false) , translateOnlyToRoot(false) , removed(false) @@ -332,14 +326,14 @@ struct Element { } void computeBounds(); - QSGGeometryNode *node; - Batch *batch; - Element *nextInBatch; - Node *root; + QSGGeometryNode *node = nullptr; + Batch *batch = nullptr; + Element *nextInBatch = nullptr; + Node *root = nullptr; Rect bounds; // in device coordinates - int order; + int order = 0; uint boundsComputed : 1; uint boundsOutsideFloatRange : 1; @@ -362,12 +356,12 @@ struct RenderNodeElement : public Element { }; struct BatchRootInfo { - BatchRootInfo() : parentRoot(0), lastOrder(-1), firstOrder(-1), availableOrders(0) { } + BatchRootInfo() {} QSet<Node *> subRoots; - Node *parentRoot; - int lastOrder; - int firstOrder; - int availableOrders; + Node *parentRoot = nullptr; + int lastOrder = -1; + int firstOrder = -1; + int availableOrders = 0; }; struct ClipBatchRootInfo : public BatchRootInfo @@ -381,14 +375,13 @@ struct DrawSet : vertices(v) , zorders(z) , indices(i) - , indexCount(0) { } - DrawSet() : vertices(0), zorders(0), indices(0), indexCount(0) {} - int vertices; - int zorders; - int indices; - int indexCount; + DrawSet() {} + int vertices = 0; + int zorders = 0; + int indices = 0; + int indexCount = 0; }; enum BatchCompatibility @@ -410,8 +403,8 @@ struct Batch // pseudo-constructor... void init() { - first = 0; - root = 0; + first = nullptr; + root = nullptr; vertexCount = 0; indexCount = 0; isOpaque = false; @@ -461,9 +454,9 @@ struct Node void append(Node *child) { Q_ASSERT(child); Q_ASSERT(!hasChild(child)); - Q_ASSERT(child->m_parent == 0); - Q_ASSERT(child->m_next == 0); - Q_ASSERT(child->m_prev == 0); + Q_ASSERT(child->m_parent == nullptr); + Q_ASSERT(child->m_next == nullptr); + Q_ASSERT(child->m_prev == nullptr); if (!m_child) { child->m_next = child; @@ -484,27 +477,27 @@ struct Node // only child.. if (child->m_next == child) { - m_child = 0; + m_child = nullptr; } else { if (m_child == child) m_child = child->m_next; child->m_next->m_prev = child->m_prev; child->m_prev->m_next = child->m_next; } - child->m_next = 0; - child->m_prev = 0; - child->setParent(0); + child->m_next = nullptr; + child->m_prev = nullptr; + child->setParent(nullptr); } Node *firstChild() const { return m_child; } Node *sibling() const { Q_ASSERT(m_parent); - return m_next == m_parent->m_child ? 0 : m_next; + return m_next == m_parent->m_child ? nullptr : m_next; } void setParent(Node *p) { - Q_ASSERT(m_parent == 0 || p == 0); + Q_ASSERT(m_parent == nullptr || p == nullptr); m_parent = p; } @@ -589,7 +582,7 @@ public: float lastOpacity; }; - ShaderManager(QSGDefaultRenderContext *ctx) : visualizeProgram(0), blitProgram(0), context(ctx) { } + ShaderManager(QSGDefaultRenderContext *ctx) : visualizeProgram(nullptr), blitProgram(nullptr), context(ctx) { } ~ShaderManager() { qDeleteAll(rewrittenShaders); qDeleteAll(stockShaders); @@ -627,8 +620,9 @@ public: }; protected: - void nodeChanged(QSGNode *node, QSGNode::DirtyState state) Q_DECL_OVERRIDE; - void render() Q_DECL_OVERRIDE; + void nodeChanged(QSGNode *node, QSGNode::DirtyState state) override; + void render() override; + void releaseCachedResources() override; private: enum ClipTypeBit @@ -697,7 +691,7 @@ private: void visualizeOverdraw(); void visualizeOverdraw_helper(Node *node); void visualizeDrawGeometry(const QSGGeometry *g); - void setCustomRenderMode(const QByteArray &mode) Q_DECL_OVERRIDE; + void setCustomRenderMode(const QByteArray &mode) override; QSGDefaultRenderContext *m_context; QSet<Node *> m_taggedRoots; @@ -762,6 +756,7 @@ Batch *Renderer::newBatch() m_batchPool.resize(size - 1); } else { b = new Batch(); + Q_ASSERT(offsetof(Batch, ibo) == sizeof(Buffer) + offsetof(Batch, vbo)); memset(&b->vbo, 0, sizeof(Buffer) * 2); // Clear VBO & IBO } b->init(); diff --git a/src/quick/scenegraph/coreapi/qsggeometry.cpp b/src/quick/scenegraph/coreapi/qsggeometry.cpp index 69a8c21ed2..dd701fba5f 100644 --- a/src/quick/scenegraph/coreapi/qsggeometry.cpp +++ b/src/quick/scenegraph/coreapi/qsggeometry.cpp @@ -115,7 +115,7 @@ const QSGGeometry::AttributeSet &QSGGeometry::defaultAttributes_ColoredPoint2D() /*! \class QSGGeometry::Attribute - \brief The QSGGeometry::Attribute describes a single vertex attribute in a QSGGeometry + \brief The QSGGeometry::Attribute describes a single vertex attribute in a QSGGeometry. \inmodule QtQuick The QSGGeometry::Attribute struct describes the attribute register position, @@ -430,9 +430,9 @@ QSGGeometry::QSGGeometry(const QSGGeometry::AttributeSet &attributes, , m_index_count(0) , m_index_type(indexType) , m_attributes(attributes) - , m_data(0) + , m_data(nullptr) , m_index_data_offset(-1) - , m_server_data(0) + , m_server_data(nullptr) , m_owns_data(false) , m_index_usage_pattern(AlwaysUploadPattern) , m_vertex_usage_pattern(AlwaysUploadPattern) @@ -529,7 +529,7 @@ QSGGeometry::~QSGGeometry() void *QSGGeometry::indexData() { return m_index_data_offset < 0 - ? 0 + ? nullptr : ((char *) m_data + m_index_data_offset); } @@ -541,7 +541,7 @@ void *QSGGeometry::indexData() const void *QSGGeometry::indexData() const { return m_index_data_offset < 0 - ? 0 + ? nullptr : ((char *) m_data + m_index_data_offset); } @@ -768,6 +768,19 @@ void QSGGeometry::updateColoredRectGeometry(QSGGeometry *g, const QRectF &rect) v[3].y = rect.bottom(); } +/*! + \enum QSGGeometry::AttributeType + + This enum identifies several attribute types. + + \value UnknownAttribute Don't care + \value PositionAttribute Position + \value ColorAttribute Color + \value TexCoordAttribute Texture coordinate + \value TexCoord1Attribute Texture coordinate 1 + \value TexCoord2Attribute Texture coordinate 2 + + */ /*! \enum QSGGeometry::DataPattern diff --git a/src/quick/scenegraph/coreapi/qsgmaterial.cpp b/src/quick/scenegraph/coreapi/qsgmaterial.cpp index 07dc87a643..8557de1b1f 100644 --- a/src/quick/scenegraph/coreapi/qsgmaterial.cpp +++ b/src/quick/scenegraph/coreapi/qsgmaterial.cpp @@ -443,7 +443,12 @@ void QSGMaterialShader::compile() otherwise returns \c false. */ +/*! + \fn bool QSGMaterialShader::RenderState::isCachedMaterialDataDirty() const + Returns \c true if the dirtyStates() contains the dirty cached material state, + otherwise returns \c false. + */ /*! \fn QSGMaterialShader::RenderState::DirtyStates QSGMaterialShader::RenderState::dirtyStates() const @@ -636,7 +641,7 @@ static void qt_print_material_count() */ QSGMaterial::QSGMaterial() - : m_flags(0) + : m_flags(nullptr) { Q_UNUSED(m_reserved); #ifndef QT_NO_DEBUG diff --git a/src/quick/scenegraph/coreapi/qsgnode.cpp b/src/quick/scenegraph/coreapi/qsgnode.cpp index e400928d4e..1976538aec 100644 --- a/src/quick/scenegraph/coreapi/qsgnode.cpp +++ b/src/quick/scenegraph/coreapi/qsgnode.cpp @@ -114,6 +114,10 @@ static void qt_print_node_count() \value DirtyOpacity The opacity of a QSGOpacityNode has changed. \value DirtySubtreeBlocked The subtree has been blocked. + \omitvalue DirtyForceUpdate + \omitvalue DirtyUsePreprocess + \omitvalue DirtyPropagationMask + \sa QSGNode::markDirty() */ @@ -135,6 +139,8 @@ static void qt_print_node_count() ownership over the opaque material and will delete it when the node is destroyed or a material is assigned. \value InternalReserved Reserved for internal use. + + \omitvalue IsVisitableNode */ /*! @@ -149,6 +155,8 @@ static void qt_print_node_count() \value OpacityNodeType The type of QSGOpacityNode \value RenderNodeType The type of QSGRenderNode + \omitvalue RootNodeType + \sa type() */ @@ -236,15 +244,8 @@ static void qt_print_node_count() * Constructs a new node */ QSGNode::QSGNode() - : m_parent(0) - , m_type(BasicNodeType) - , m_firstChild(0) - , m_lastChild(0) - , m_nextSibling(0) - , m_previousSibling(0) - , m_subtreeRenderableCount(0) - , m_nodeFlags(OwnedByParent) - , m_dirtyState(0) + : m_nodeFlags(OwnedByParent) + , m_dirtyState(nullptr) { init(); } @@ -255,15 +256,15 @@ QSGNode::QSGNode() * \internal */ QSGNode::QSGNode(NodeType type) - : m_parent(0) + : m_parent(nullptr) , m_type(type) - , m_firstChild(0) - , m_lastChild(0) - , m_nextSibling(0) - , m_previousSibling(0) + , m_firstChild(nullptr) + , m_lastChild(nullptr) + , m_nextSibling(nullptr) + , m_previousSibling(nullptr) , m_subtreeRenderableCount(type == GeometryNodeType || type == RenderNodeType ? 1 : 0) , m_nodeFlags(OwnedByParent) - , m_dirtyState(0) + , m_dirtyState(nullptr) { init(); } @@ -274,15 +275,15 @@ QSGNode::QSGNode(NodeType type) * \internal */ QSGNode::QSGNode(QSGNodePrivate &dd, NodeType type) - : m_parent(0) + : m_parent(nullptr) , m_type(type) - , m_firstChild(0) - , m_lastChild(0) - , m_nextSibling(0) - , m_previousSibling(0) + , m_firstChild(nullptr) + , m_lastChild(nullptr) + , m_nextSibling(nullptr) + , m_previousSibling(nullptr) , m_subtreeRenderableCount(type == GeometryNodeType || type == RenderNodeType ? 1 : 0) , m_nodeFlags(OwnedByParent) - , m_dirtyState(0) + , m_dirtyState(nullptr) , d_ptr(&dd) { init(); @@ -380,17 +381,17 @@ void QSGNode::destroy() { if (m_parent) { m_parent->removeChildNode(this); - Q_ASSERT(m_parent == 0); + Q_ASSERT(m_parent == nullptr); } while (m_firstChild) { QSGNode *child = m_firstChild; removeChildNode(child); - Q_ASSERT(child->m_parent == 0); + Q_ASSERT(child->m_parent == nullptr); if (child->flags() & OwnedByParent) delete child; } - Q_ASSERT(m_firstChild == 0 && m_lastChild == 0); + Q_ASSERT(m_firstChild == nullptr && m_lastChild == nullptr); } @@ -549,11 +550,11 @@ void QSGNode::removeChildNode(QSGNode *node) next->m_previousSibling = previous; else m_lastChild = previous; - node->m_previousSibling = 0; - node->m_nextSibling = 0; + node->m_previousSibling = nullptr; + node->m_nextSibling = nullptr; node->markDirty(DirtyNodeRemoved); - node->m_parent = 0; + node->m_parent = nullptr; } @@ -566,13 +567,13 @@ void QSGNode::removeAllChildNodes() while (m_firstChild) { QSGNode *node = m_firstChild; m_firstChild = node->m_nextSibling; - node->m_nextSibling = 0; + node->m_nextSibling = nullptr; if (m_firstChild) - m_firstChild->m_previousSibling = 0; + m_firstChild->m_previousSibling = nullptr; else - m_lastChild = 0; + m_lastChild = nullptr; node->markDirty(DirtyNodeRemoved); - node->m_parent = 0; + node->m_parent = nullptr; } } @@ -706,9 +707,9 @@ void qsgnode_set_description(QSGNode *node, const QString &description) */ QSGBasicGeometryNode::QSGBasicGeometryNode(NodeType type) : QSGNode(type) - , m_geometry(0) - , m_matrix(0) - , m_clip_list(0) + , m_geometry(nullptr) + , m_matrix(nullptr) + , m_clip_list(nullptr) { } @@ -718,9 +719,9 @@ QSGBasicGeometryNode::QSGBasicGeometryNode(NodeType type) */ QSGBasicGeometryNode::QSGBasicGeometryNode(QSGBasicGeometryNodePrivate &dd, NodeType type) : QSGNode(dd, type) - , m_geometry(0) - , m_matrix(0) - , m_clip_list(0) + , m_geometry(nullptr) + , m_matrix(nullptr) + , m_clip_list(nullptr) { } @@ -862,10 +863,6 @@ void QSGBasicGeometryNode::setGeometry(QSGGeometry *geometry) QSGGeometryNode::QSGGeometryNode() : QSGBasicGeometryNode(GeometryNodeType) - , m_render_order(0) - , m_material(0) - , m_opaque_material(0) - , m_opacity(1) { } @@ -876,8 +873,8 @@ QSGGeometryNode::QSGGeometryNode() QSGGeometryNode::QSGGeometryNode(QSGGeometryNodePrivate &dd) : QSGBasicGeometryNode(dd, GeometryNodeType) , m_render_order(0) - , m_material(0) - , m_opaque_material(0) + , m_material(nullptr) + , m_opaque_material(nullptr) , m_opacity(1) { } @@ -971,7 +968,7 @@ void QSGGeometryNode::setMaterial(QSGMaterial *material) delete m_material; m_material = material; #ifndef QT_NO_DEBUG - if (m_material != 0 && m_opaque_material == m_material) + if (m_material != nullptr && m_opaque_material == m_material) qWarning("QSGGeometryNode: using same material for both opaque and translucent"); #endif markDirty(DirtyMaterial); @@ -1002,7 +999,7 @@ void QSGGeometryNode::setOpaqueMaterial(QSGMaterial *material) delete m_opaque_material; m_opaque_material = material; #ifndef QT_NO_DEBUG - if (m_opaque_material != 0 && m_opaque_material == m_material) + if (m_opaque_material != nullptr && m_opaque_material == m_material) qWarning("QSGGeometryNode: using same material for both opaque and translucent"); #endif @@ -1079,6 +1076,7 @@ void QSGGeometryNode::setInheritedOpacity(qreal opacity) QSGClipNode::QSGClipNode() : QSGBasicGeometryNode(ClipNodeType) + , m_is_rectangular(false) { Q_UNUSED(m_reserved); } @@ -1114,6 +1112,8 @@ QSGClipNode::~QSGClipNode() When this hint is set and it is applicable, the clip region will be generated from clipRect() rather than geometry(). + + By default this property is \c false. */ void QSGClipNode::setIsRectangular(bool rectHint) @@ -1144,7 +1144,7 @@ void QSGClipNode::setClipRect(const QRectF &rect) /*! \class QSGTransformNode - \brief The QSGTransformNode class implements transformations in the scene graph + \brief The QSGTransformNode class implements transformations in the scene graph. \inmodule QtQuick \ingroup qtquick-scenegraph-nodes @@ -1263,7 +1263,7 @@ QSGRootNode::QSGRootNode() QSGRootNode::~QSGRootNode() { while (!m_renderers.isEmpty()) - m_renderers.constLast()->setRootNode(0); + m_renderers.constLast()->setRootNode(nullptr); destroy(); // Must call destroy() here because markDirty() casts this to QSGRootNode. } @@ -1315,8 +1315,6 @@ void QSGRootNode::notifyNodeChange(QSGNode *node, DirtyState state) */ QSGOpacityNode::QSGOpacityNode() : QSGNode(OpacityNodeType) - , m_opacity(1) - , m_combined_opacity(1) { } @@ -1582,6 +1580,7 @@ QDebug operator<<(QDebug d, const QSGRootNode *n) d << "RootNode(null)"; return d; } + QDebugStateSaver saver(d); d << "RootNode" << hex << (const void *) n << (n->isSubtreeBlocked() ? "*BLOCKED*" : ""); #ifdef QSG_RUNTIME_DESCRIPTION d << QSGNodePrivate::description(n); diff --git a/src/quick/scenegraph/coreapi/qsgnode.h b/src/quick/scenegraph/coreapi/qsgnode.h index 1467f2233d..854e284c9e 100644 --- a/src/quick/scenegraph/coreapi/qsgnode.h +++ b/src/quick/scenegraph/coreapi/qsgnode.h @@ -77,9 +77,7 @@ public: TransformNodeType, ClipNodeType, OpacityNodeType, -#ifndef qdoc RootNodeType, -#endif RenderNodeType }; @@ -96,9 +94,8 @@ public: OwnsOpaqueMaterial = 0x00040000, // Uppermost 8 bits are reserved for internal use. -#ifndef qdoc IsVisitableNode = 0x01000000 -#else +#ifdef Q_CLANG_QDOC InternalReserved = 0x01000000 #endif }; @@ -113,7 +110,6 @@ public: DirtyMaterial = 0x2000, DirtyOpacity = 0x4000, -#ifndef qdoc DirtyForceUpdate = 0x8000, DirtyUsePreprocess = UsePreprocess, @@ -122,7 +118,6 @@ public: | DirtyNodeAdded | DirtyOpacity | DirtyForceUpdate -#endif }; Q_DECLARE_FLAGS(DirtyState, DirtyStateBit) @@ -151,7 +146,7 @@ public: QT_DEPRECATED void clearDirty() { } void markDirty(DirtyState bits); - QT_DEPRECATED DirtyState dirtyState() const { return Q_NULLPTR; } + QT_DEPRECATED DirtyState dirtyState() const { return nullptr; } virtual bool isSubtreeBlocked() const; @@ -173,13 +168,13 @@ private: void init(); void destroy(); - QSGNode *m_parent; - NodeType m_type; - QSGNode *m_firstChild; - QSGNode *m_lastChild; - QSGNode *m_nextSibling; - QSGNode *m_previousSibling; - int m_subtreeRenderableCount; + QSGNode *m_parent = nullptr; + NodeType m_type = BasicNodeType; + QSGNode *m_firstChild = nullptr; + QSGNode *m_lastChild = nullptr; + QSGNode *m_nextSibling = nullptr; + QSGNode *m_previousSibling = nullptr; + int m_subtreeRenderableCount = 0; Flags m_nodeFlags; DirtyState m_dirtyState; // Obsolete, remove in Qt 6 @@ -195,7 +190,7 @@ void Q_QUICK_EXPORT qsgnode_set_description(QSGNode *node, const QString &descri class Q_QUICK_EXPORT QSGBasicGeometryNode : public QSGNode { public: - ~QSGBasicGeometryNode(); + ~QSGBasicGeometryNode() override; void setGeometry(QSGGeometry *geometry); const QSGGeometry *geometry() const { return m_geometry; } @@ -229,7 +224,7 @@ class Q_QUICK_EXPORT QSGGeometryNode : public QSGBasicGeometryNode { public: QSGGeometryNode(); - ~QSGGeometryNode(); + ~QSGGeometryNode() override; void setMaterial(QSGMaterial *material); QSGMaterial *material() const { return m_material; } @@ -251,18 +246,18 @@ protected: private: friend class QSGNodeUpdater; - int m_render_order; - QSGMaterial *m_material; - QSGMaterial *m_opaque_material; + int m_render_order = 0; + QSGMaterial *m_material = nullptr; + QSGMaterial *m_opaque_material = nullptr; - qreal m_opacity; + qreal m_opacity = 1; }; class Q_QUICK_EXPORT QSGClipNode : public QSGBasicGeometryNode { public: QSGClipNode(); - ~QSGClipNode(); + ~QSGClipNode() override; void setIsRectangular(bool rectHint); bool isRectangular() const { return m_is_rectangular; } @@ -282,7 +277,7 @@ class Q_QUICK_EXPORT QSGTransformNode : public QSGNode { public: QSGTransformNode(); - ~QSGTransformNode(); + ~QSGTransformNode() override; void setMatrix(const QMatrix4x4 &matrix); const QMatrix4x4 &matrix() const { return m_matrix; } @@ -300,7 +295,7 @@ class Q_QUICK_EXPORT QSGRootNode : public QSGNode { public: QSGRootNode(); - ~QSGRootNode(); + ~QSGRootNode() override; private: void notifyNodeChange(QSGNode *node, DirtyState state); @@ -317,7 +312,7 @@ class Q_QUICK_EXPORT QSGOpacityNode : public QSGNode { public: QSGOpacityNode(); - ~QSGOpacityNode(); + ~QSGOpacityNode() override; void setOpacity(qreal opacity); qreal opacity() const { return m_opacity; } @@ -328,8 +323,8 @@ public: bool isSubtreeBlocked() const override; private: - qreal m_opacity; - qreal m_combined_opacity; + qreal m_opacity = 1; + qreal m_combined_opacity = 1; }; class Q_QUICK_EXPORT QSGNodeVisitor { diff --git a/src/quick/scenegraph/coreapi/qsgnode_p.h b/src/quick/scenegraph/coreapi/qsgnode_p.h index 84d5477085..f81128f51a 100644 --- a/src/quick/scenegraph/coreapi/qsgnode_p.h +++ b/src/quick/scenegraph/coreapi/qsgnode_p.h @@ -78,18 +78,14 @@ public: class QSGBasicGeometryNodePrivate : public QSGNodePrivate { public: - QSGBasicGeometryNodePrivate() - : QSGNodePrivate() - {} + QSGBasicGeometryNodePrivate() {} }; class QSGGeometryNodePrivate: public QSGBasicGeometryNodePrivate { public: - QSGGeometryNodePrivate() - : QSGBasicGeometryNodePrivate() - {} + QSGGeometryNodePrivate() {} }; QT_END_NAMESPACE diff --git a/src/quick/scenegraph/coreapi/qsgnodeupdater.cpp b/src/quick/scenegraph/coreapi/qsgnodeupdater.cpp index d6d533307e..8bc9ded594 100644 --- a/src/quick/scenegraph/coreapi/qsgnodeupdater.cpp +++ b/src/quick/scenegraph/coreapi/qsgnodeupdater.cpp @@ -48,7 +48,7 @@ QT_BEGIN_NAMESPACE QSGNodeUpdater::QSGNodeUpdater() : m_combined_matrix_stack(64) , m_opacity_stack(64) - , m_current_clip(0) + , m_current_clip(nullptr) , m_force_update(0) { m_opacity_stack.add(1); @@ -60,7 +60,7 @@ QSGNodeUpdater::~QSGNodeUpdater() void QSGNodeUpdater::updateStates(QSGNode *n) { - m_current_clip = 0; + m_current_clip = nullptr; m_force_update = 0; Q_ASSERT(m_opacity_stack.size() == 1); // The one we added in the constructr... @@ -82,7 +82,7 @@ void QSGNodeUpdater::updateStates(QSGNode *n) bool QSGNodeUpdater::isNodeBlocked(QSGNode *node, QSGNode *root) const { - while (node != root && node != 0) { + while (node != root && node != nullptr) { if (node->isSubtreeBlocked()) return true; node = node->parent(); diff --git a/src/quick/scenegraph/coreapi/qsgrenderer.cpp b/src/quick/scenegraph/coreapi/qsgrenderer.cpp index 8fbd402a2f..e1ba001d2d 100644 --- a/src/quick/scenegraph/coreapi/qsgrenderer.cpp +++ b/src/quick/scenegraph/coreapi/qsgrenderer.cpp @@ -50,7 +50,9 @@ QT_BEGIN_NAMESPACE +#if QT_CONFIG(opengl) static const bool qsg_sanity_check = qEnvironmentVariableIntValue("QSG_SANITY_CHECK"); +#endif static QElapsedTimer frameTimer; static qint64 preprocessTime; @@ -130,8 +132,8 @@ QSGRenderer::QSGRenderer(QSGRenderContext *context) , m_current_determinant(1) , m_device_pixel_ratio(1) , m_context(context) - , m_node_updater(0) - , m_bindable(0) + , m_node_updater(nullptr) + , m_bindable(nullptr) , m_changed_emitted(false) , m_is_rendering(false) , m_is_preprocessing(false) @@ -141,7 +143,7 @@ QSGRenderer::QSGRenderer(QSGRenderContext *context) QSGRenderer::~QSGRenderer() { - setRootNode(0); + setRootNode(nullptr); delete m_node_updater; } @@ -247,7 +249,7 @@ void QSGRenderer::renderScene(const QSGBindable &bindable) m_is_rendering = false; m_changed_emitted = false; - m_bindable = 0; + m_bindable = nullptr; qCDebug(QSG_LOG_TIME_RENDERER, "time in renderer: total=%dms, preprocess=%d, updates=%d, binding=%d, rendering=%d", diff --git a/src/quick/scenegraph/coreapi/qsgrenderer_p.h b/src/quick/scenegraph/coreapi/qsgrenderer_p.h index 1ea2775e6f..d4ff6ea9fe 100644 --- a/src/quick/scenegraph/coreapi/qsgrenderer_p.h +++ b/src/quick/scenegraph/coreapi/qsgrenderer_p.h @@ -86,13 +86,14 @@ public: bool isMirrored() const; void renderScene(const QSGBindable &bindable); - virtual void renderScene(uint fboId = 0) Q_DECL_OVERRIDE; - virtual void nodeChanged(QSGNode *node, QSGNode::DirtyState state) Q_DECL_OVERRIDE; + void renderScene(uint fboId = 0) override; + void nodeChanged(QSGNode *node, QSGNode::DirtyState state) override; QSGNodeUpdater *nodeUpdater() const; void setNodeUpdater(QSGNodeUpdater *updater); inline QSGMaterialShader::RenderState state(QSGMaterialShader::RenderState::DirtyStates dirty) const; - virtual void setCustomRenderMode(const QByteArray &) { }; + virtual void setCustomRenderMode(const QByteArray &) { } + virtual void releaseCachedResources() { } void clearChangedFlag() { m_changed_emitted = false; } @@ -161,12 +162,12 @@ class Q_QUICK_PRIVATE_EXPORT QSGNodeDumper : public QSGNodeVisitor { public: static void dump(QSGNode *n); - QSGNodeDumper() : m_indent(0) {} + QSGNodeDumper() {} void visitNode(QSGNode *n) override; void visitChildren(QSGNode *n) override; private: - int m_indent; + int m_indent = 0; }; diff --git a/src/quick/scenegraph/coreapi/qsgrendernode.cpp b/src/quick/scenegraph/coreapi/qsgrendernode.cpp index a8954848d6..df3fa16a32 100644 --- a/src/quick/scenegraph/coreapi/qsgrendernode.cpp +++ b/src/quick/scenegraph/coreapi/qsgrendernode.cpp @@ -74,8 +74,8 @@ QSGRenderNode::~QSGRenderNode() } QSGRenderNodePrivate::QSGRenderNodePrivate() - : m_matrix(0) - , m_clip_list(0) + : m_matrix(nullptr) + , m_clip_list(nullptr) , m_opacity(1) { } @@ -119,7 +119,7 @@ QSGRenderNodePrivate::QSGRenderNodePrivate() */ QSGRenderNode::StateFlags QSGRenderNode::changedStates() const { - return 0; + return nullptr; } /*! @@ -213,6 +213,22 @@ void QSGRenderNode::releaseResources() } /*! + \enum QSGRenderNode::StateFlag + + This enum is a bit mask identifying several states. + + \value DepthState Depth + \value StencilState Stencil + \value ScissorState Scissor + \value ColorState Color + \value BlendState Blend + \value CullState Cull + \value ViewportState View poirt + \value RenderTargetState Render target + + */ + +/*! \enum QSGRenderNode::RenderingFlag Possible values for the bitmask returned from flags(). @@ -251,7 +267,7 @@ void QSGRenderNode::releaseResources() */ QSGRenderNode::RenderingFlags QSGRenderNode::flags() const { - return 0; + return nullptr; } /*! @@ -354,7 +370,7 @@ QSGRenderNode::RenderState::~RenderState() */ /*! - \fn const QRegion *QSGRenderNode::clipRegion() const + \fn const QRegion *QSGRenderNode::RenderState::clipRegion() const \return the current clip region or null for backends where clipping is implemented via stencil or scissoring. diff --git a/src/quick/scenegraph/coreapi/qsgrendernode.h b/src/quick/scenegraph/coreapi/qsgrendernode.h index f6bc40d3ee..0fb83b080c 100644 --- a/src/quick/scenegraph/coreapi/qsgrendernode.h +++ b/src/quick/scenegraph/coreapi/qsgrendernode.h @@ -80,7 +80,7 @@ public: }; QSGRenderNode(); - ~QSGRenderNode(); + ~QSGRenderNode() override; virtual StateFlags changedStates() const; virtual void render(const RenderState *state) = 0; diff --git a/src/quick/scenegraph/qsgadaptationlayer.cpp b/src/quick/scenegraph/qsgadaptationlayer.cpp index c64360f955..252e5a9c55 100644 --- a/src/quick/scenegraph/qsgadaptationlayer.cpp +++ b/src/quick/scenegraph/qsgadaptationlayer.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2018 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtQuick module of the Qt Toolkit. @@ -56,7 +56,7 @@ static QElapsedTimer qsg_render_timer; QSGDistanceFieldGlyphCache::Texture QSGDistanceFieldGlyphCache::s_emptyTexture; -QSGDistanceFieldGlyphCache::QSGDistanceFieldGlyphCache(QOpenGLContext *c, const QRawFont &font) +QSGDistanceFieldGlyphCache::QSGDistanceFieldGlyphCache(const QRawFont &font) : m_pendingGlyphs(64) { Q_ASSERT(font.isValid()); @@ -71,30 +71,32 @@ QSGDistanceFieldGlyphCache::QSGDistanceFieldGlyphCache(QOpenGLContext *c, const // 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()); -#if QT_CONFIG(opengl) - m_coreProfile = (c->format().profile() == QSurfaceFormat::CoreProfile); -#else - Q_UNUSED(c) -#endif } QSGDistanceFieldGlyphCache::~QSGDistanceFieldGlyphCache() { } +QSGDistanceFieldGlyphCache::GlyphData &QSGDistanceFieldGlyphCache::emptyData(glyph_t glyph) +{ + GlyphData gd; + gd.texture = &s_emptyTexture; + QHash<glyph_t, GlyphData>::iterator it = m_glyphsData.insert(glyph, gd); + return it.value(); +} + QSGDistanceFieldGlyphCache::GlyphData &QSGDistanceFieldGlyphCache::glyphData(glyph_t glyph) { QHash<glyph_t, GlyphData>::iterator data = m_glyphsData.find(glyph); if (data == m_glyphsData.end()) { - GlyphData gd; - gd.texture = &s_emptyTexture; + GlyphData &gd = emptyData(glyph); gd.path = m_referenceFont.pathForGlyph(glyph); // need bounding rect in base font size scale qreal scaleFactor = qreal(1) / QT_DISTANCEFIELD_SCALE(m_doubleGlyphResolution); QTransform scaleDown; scaleDown.scale(scaleFactor, scaleFactor); gd.boundingRect = scaleDown.mapRect(gd.path.boundingRect()); - data = m_glyphsData.insert(glyph, gd); + return gd; } return data.value(); } @@ -234,10 +236,8 @@ void QSGDistanceFieldGlyphCache::setGlyphsPosition(const QList<GlyphPosition> &g } if (!invalidatedGlyphs.isEmpty()) { - QLinkedList<QSGDistanceFieldGlyphConsumer *>::iterator it = m_registeredNodes.begin(); - while (it != m_registeredNodes.end()) { - (*it)->invalidateGlyphs(invalidatedGlyphs); - ++it; + for (QSGDistanceFieldGlyphConsumerList::iterator iter = m_registeredNodes.begin(); iter != m_registeredNodes.end(); ++iter) { + iter->invalidateGlyphs(invalidatedGlyphs); } } } @@ -280,10 +280,8 @@ void QSGDistanceFieldGlyphCache::setGlyphsTexture(const QVector<glyph_t> &glyphs } if (!invalidatedGlyphs.isEmpty()) { - QLinkedList<QSGDistanceFieldGlyphConsumer*>::iterator it = m_registeredNodes.begin(); - while (it != m_registeredNodes.end()) { - (*it)->invalidateGlyphs(invalidatedGlyphs); - ++it; + for (QSGDistanceFieldGlyphConsumerList::iterator iter = m_registeredNodes.begin(); iter != m_registeredNodes.end(); ++iter) { + iter->invalidateGlyphs(invalidatedGlyphs); } } } diff --git a/src/quick/scenegraph/qsgadaptationlayer_p.h b/src/quick/scenegraph/qsgadaptationlayer_p.h index ba146b884f..58ecae94e7 100644 --- a/src/quick/scenegraph/qsgadaptationlayer_p.h +++ b/src/quick/scenegraph/qsgadaptationlayer_p.h @@ -62,8 +62,8 @@ #include <QtCore/qurl.h> #include <private/qfontengine_p.h> #include <QtGui/private/qdatabuffer_p.h> -#include <private/qopenglcontext_p.h> #include <private/qdistancefield_p.h> +#include <private/qintrusivelist_p.h> // ### remove #include <QtQuick/private/qquicktext_p.h> @@ -74,7 +74,6 @@ class QSGNode; class QImage; class TextureReference; class QSGDistanceFieldGlyphNode; -class QOpenGLContext; class QSGInternalImageNode; class QSGPainterNode; class QSGInternalRectangleNode; @@ -82,6 +81,7 @@ class QSGGlyphNode; class QSGRootNode; class QSGSpriteNode; class QSGRenderNode; +class QSGRenderContext; class Q_QUICK_PRIVATE_EXPORT QSGNodeVisitorEx { @@ -136,6 +136,7 @@ public: virtual void setPenColor(const QColor &color) = 0; virtual void setPenWidth(qreal width) = 0; virtual void setGradientStops(const QGradientStops &stops) = 0; + virtual void setGradientVertical(bool vertical) = 0; virtual void setRadius(qreal radius) = 0; virtual void setAntialiasing(bool antialiasing) { Q_UNUSED(antialiasing) } virtual void setAligned(bool aligned) = 0; @@ -266,19 +267,19 @@ public: Texture // for APIs with separate texture and sampler objects }; struct InputParameter { - InputParameter() : semanticIndex(0) { } + InputParameter() {} // Semantics use the D3D keys (POSITION, TEXCOORD). // Attribute name based APIs can map based on pre-defined names. QByteArray semanticName; - int semanticIndex; + int semanticIndex = 0; }; struct Variable { - Variable() : type(Constant), offset(0), size(0), bindPoint(0) { } - VariableType type; + Variable() {} + VariableType type = Constant; QByteArray name; - uint offset; // for cbuffer members - uint size; // for cbuffer members - int bindPoint; // for textures and samplers; for register-based APIs + uint offset = 0; // for cbuffer members + uint size = 0; // for cbuffer members + int bindPoint = 0; // for textures and samplers; for register-based APIs }; QByteArray blob; // source or bytecode @@ -329,8 +330,8 @@ public: }; struct ShaderData { - ShaderData() : hasShaderCode(false) { } - bool hasShaderCode; + ShaderData() {} + bool hasShaderCode = false; QSGGuiThreadShaderEffectManager::ShaderInfo shaderInfo; QVector<VariableData> varData; }; @@ -373,7 +374,7 @@ public: HighQualitySubPixelAntialiasing }; - QSGGlyphNode() : m_ownerElement(0) {} + QSGGlyphNode() {} virtual void setGlyphs(const QPointF &position, const QGlyphRun &glyphs) = 0; virtual void setColor(const QColor &color) = 0; @@ -394,7 +395,7 @@ public: void accept(QSGNodeVisitorEx *visitor) override { if (visitor->visit(this)) visitor->visitChildren(this); visitor->endVisit(this); } protected: QRectF m_bounding_rect; - QQuickItem *m_ownerElement; + QQuickItem *m_ownerElement = nullptr; }; class Q_QUICK_PRIVATE_EXPORT QSGDistanceFieldGlyphConsumer @@ -403,12 +404,14 @@ public: virtual ~QSGDistanceFieldGlyphConsumer() {} virtual void invalidateGlyphs(const QVector<quint32> &glyphs) = 0; + QIntrusiveListNode node; }; +typedef QIntrusiveList<QSGDistanceFieldGlyphConsumer, &QSGDistanceFieldGlyphConsumer::node> QSGDistanceFieldGlyphConsumerList; class Q_QUICK_PRIVATE_EXPORT QSGDistanceFieldGlyphCache { public: - QSGDistanceFieldGlyphCache(QOpenGLContext *c, const QRawFont &font); + QSGDistanceFieldGlyphCache(const QRawFont &font); virtual ~QSGDistanceFieldGlyphCache(); struct Metrics { @@ -421,24 +424,24 @@ public: }; struct TexCoord { - qreal x; - qreal y; - qreal width; - qreal height; - qreal xMargin; - qreal yMargin; + qreal x = 0; + qreal y = 0; + qreal width = -1; + qreal height = -1; + qreal xMargin = 0; + qreal yMargin = 0; - TexCoord() : x(0), y(0), width(-1), height(-1), xMargin(0), yMargin(0) { } + TexCoord() {} bool isNull() const { return width <= 0 || height <= 0; } bool isValid() const { return width >= 0 && height >= 0; } }; struct Texture { - uint textureId; + uint textureId = 0; QSize size; - Texture() : textureId(0), size(QSize()) { } + Texture() : size(QSize()) { } bool operator == (const Texture &other) const { return textureId == other.textureId; } }; @@ -464,8 +467,8 @@ public: void update(); - void registerGlyphNode(QSGDistanceFieldGlyphConsumer *node) { m_registeredNodes.append(node); } - void unregisterGlyphNode(QSGDistanceFieldGlyphConsumer *node) { m_registeredNodes.removeOne(node); } + void registerGlyphNode(QSGDistanceFieldGlyphConsumer *node) { m_registeredNodes.insert(node); } + void unregisterGlyphNode(QSGDistanceFieldGlyphConsumer *node) { m_registeredNodes.remove(node); } virtual void registerOwnerElement(QQuickItem *ownerElement); virtual void unregisterOwnerElement(QQuickItem *ownerElement); @@ -478,13 +481,13 @@ protected: }; struct GlyphData { - Texture *texture; + Texture *texture = nullptr; TexCoord texCoord; QRectF boundingRect; QPainterPath path; - quint32 ref; + quint32 ref = 0; - GlyphData() : texture(0), ref(0) { } + GlyphData() {} }; virtual void requestGlyphs(const QSet<glyph_t> &glyphs) = 0; @@ -503,25 +506,24 @@ protected: uint textureIdForGlyph(glyph_t glyph) const; GlyphData &glyphData(glyph_t glyph); + GlyphData &emptyData(glyph_t glyph); #if defined(QSG_DISTANCEFIELD_CACHE_DEBUG) void saveTexture(GLuint textureId, int width, int height) const; #endif - inline bool isCoreProfile() const { return m_coreProfile; } + bool m_doubleGlyphResolution; -private: +protected: QRawFont m_referenceFont; - int m_glyphCount; - - bool m_doubleGlyphResolution; - bool m_coreProfile; +private: + int m_glyphCount; QList<Texture> m_textures; QHash<glyph_t, GlyphData> m_glyphsData; QDataBuffer<glyph_t> m_pendingGlyphs; QSet<glyph_t> m_populatingGlyphs; - QLinkedList<QSGDistanceFieldGlyphConsumer*> m_registeredNodes; + QSGDistanceFieldGlyphConsumerList m_registeredNodes; static Texture s_emptyTexture; }; diff --git a/src/quick/scenegraph/qsgbasicglyphnode.cpp b/src/quick/scenegraph/qsgbasicglyphnode.cpp index 38f650a82c..4559b7951c 100644 --- a/src/quick/scenegraph/qsgbasicglyphnode.cpp +++ b/src/quick/scenegraph/qsgbasicglyphnode.cpp @@ -44,7 +44,7 @@ QT_BEGIN_NAMESPACE QSGBasicGlyphNode::QSGBasicGlyphNode() : m_style(QQuickText::Normal) - , m_material(0) + , m_material(nullptr) , m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 0) { m_geometry.setDrawingMode(QSGGeometry::DrawTriangles); @@ -59,7 +59,7 @@ QSGBasicGlyphNode::~QSGBasicGlyphNode() void QSGBasicGlyphNode::setColor(const QColor &color) { m_color = color; - if (m_material != 0) { + if (m_material != nullptr) { setMaterialColor(color); markDirty(DirtyMaterial); } @@ -67,7 +67,7 @@ void QSGBasicGlyphNode::setColor(const QColor &color) void QSGBasicGlyphNode::setGlyphs(const QPointF &position, const QGlyphRun &glyphs) { - if (m_material != 0) + if (m_material != nullptr) delete m_material; m_position = position; diff --git a/src/quick/scenegraph/qsgbasicinternalimagenode.cpp b/src/quick/scenegraph/qsgbasicinternalimagenode.cpp index c8699218ba..c434563c90 100644 --- a/src/quick/scenegraph/qsgbasicinternalimagenode.cpp +++ b/src/quick/scenegraph/qsgbasicinternalimagenode.cpp @@ -189,15 +189,30 @@ namespace { struct Y { float y, ty; }; } -static inline void appendQuad(quint16 **indices, quint16 topLeft, quint16 topRight, - quint16 bottomLeft, quint16 bottomRight) +static inline void appendQuad(int indexType, void **indexData, + int topLeft, int topRight, + int bottomLeft, int bottomRight) { - *(*indices)++ = topLeft; - *(*indices)++ = bottomLeft; - *(*indices)++ = bottomRight; - *(*indices)++ = bottomRight; - *(*indices)++ = topRight; - *(*indices)++ = topLeft; + if (indexType == QSGGeometry::UnsignedIntType) { + quint32 *indices = static_cast<quint32 *>(*indexData); + *indices++ = topLeft; + *indices++ = bottomLeft; + *indices++ = bottomRight; + *indices++ = bottomRight; + *indices++ = topRight; + *indices++ = topLeft; + *indexData = indices; + } else { + Q_ASSERT(indexType == QSGGeometry::UnsignedShortType); + quint16 *indices = static_cast<quint16 *>(*indexData); + *indices++ = topLeft; + *indices++ = bottomLeft; + *indices++ = bottomRight; + *indices++ = bottomRight; + *indices++ = topRight; + *indices++ = topLeft; + *indexData = indices; + } } QSGGeometry *QSGBasicInternalImageNode::updateGeometry(const QRectF &targetRect, @@ -230,6 +245,7 @@ QSGGeometry *QSGBasicInternalImageNode::updateGeometry(const QRectF &targetRect, ++vCells; if (innerTargetRect.bottom() != targetRect.bottom()) ++vCells; + QVarLengthArray<X, 32> xData(2 * hCells); QVarLengthArray<Y, 32> yData(2 * vCells); X *xs = xData.data(); @@ -270,7 +286,7 @@ QSGGeometry *QSGBasicInternalImageNode::updateGeometry(const QRectF &targetRect, float leftPlusRight = targetRect.left() + targetRect.right(); int count = xData.size(); xs = xData.data(); - for (int i = 0; i < count >> 1; ++i) + 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; @@ -308,16 +324,29 @@ QSGGeometry *QSGBasicInternalImageNode::updateGeometry(const QRectF &targetRect, } Q_ASSERT(ys == yData.data() + yData.size()); + QSGGeometry::Type indexType = QSGGeometry::UnsignedShortType; + // We can handled up to 0xffff indices, but keep the limit lower here to + // merge better in the batch renderer. + if (hCells * vCells * 4 > 0x7fff) + indexType = QSGGeometry::UnsignedIntType; + if (antialiasing) { + if (!geometry || geometry->indexType() != indexType) { + geometry = new QSGGeometry(smoothAttributeSet(), + hCells * vCells * 4 + (hCells + vCells - 1) * 4, + hCells * vCells * 6 + (hCells + vCells) * 12, + indexType); + } else { + geometry->allocate(hCells * vCells * 4 + (hCells + vCells - 1) * 4, + hCells * vCells * 6 + (hCells + vCells) * 12); + } 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(); + void *indexData = g->indexData(); // 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 @@ -345,7 +374,7 @@ QSGGeometry *QSGBasicInternalImageNode::updateGeometry(const QRectF &targetRect, float delta = float(qAbs(targetRect.width()) < qAbs(targetRect.height()) ? targetRect.width() : targetRect.height()) * 0.5f; - quint16 index = 0; + int index = 0; ys = yData.data(); for (int j = 0; j < vCells; ++j, ys += 2) { xs = xData.data(); @@ -357,7 +386,7 @@ QSGGeometry *QSGBasicInternalImageNode::updateGeometry(const QRectF &targetRect, SmoothVertex *v = vertices + index; - quint16 topLeft = index; + int topLeft = index; for (int k = (isTop || isLeft ? 2 : 1); k--; ++v, ++index) { v->x = xs[0].x; v->u = xs[0].tx; @@ -365,7 +394,7 @@ QSGGeometry *QSGBasicInternalImageNode::updateGeometry(const QRectF &targetRect, v->v = ys[0].ty; } - quint16 topRight = index; + int topRight = index; for (int k = (isTop || isRight ? 2 : 1); k--; ++v, ++index) { v->x = xs[1].x; v->u = xs[1].tx; @@ -373,7 +402,7 @@ QSGGeometry *QSGBasicInternalImageNode::updateGeometry(const QRectF &targetRect, v->v = ys[0].ty; } - quint16 bottomLeft = index; + int bottomLeft = index; for (int k = (isBottom || isLeft ? 2 : 1); k--; ++v, ++index) { v->x = xs[0].x; v->u = xs[0].tx; @@ -381,7 +410,7 @@ QSGGeometry *QSGBasicInternalImageNode::updateGeometry(const QRectF &targetRect, v->v = ys[1].ty; } - quint16 bottomRight = index; + int bottomRight = index; for (int k = (isBottom || isRight ? 2 : 1); k--; ++v, ++index) { v->x = xs[1].x; v->u = xs[1].tx; @@ -389,45 +418,44 @@ QSGGeometry *QSGBasicInternalImageNode::updateGeometry(const QRectF &targetRect, v->v = ys[1].ty; } - appendQuad(&indices, topLeft, topRight, bottomLeft, bottomRight); + appendQuad(g->indexType(), &indexData, 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); + appendQuad(g->indexType(), &indexData, 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); + appendQuad(g->indexType(), &indexData, 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); + appendQuad(g->indexType(), &indexData, 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); + appendQuad(g->indexType(), &indexData, topRight, topRight + 1, bottomRight, bottomRight + 1); } } } Q_ASSERT(index == g->vertexCount()); - Q_ASSERT(indices - g->indexCount() == g->indexData()); } else { - if (!geometry) { + if (!geometry || geometry->indexType() != indexType) { geometry = new QSGGeometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), hCells * vCells * 4, hCells * vCells * 6, - QSGGeometry::UnsignedShortType); + indexType); } else { geometry->allocate(hCells * vCells * 4, hCells * vCells * 6); } @@ -450,10 +478,9 @@ QSGGeometry *QSGBasicInternalImageNode::updateGeometry(const QRectF &targetRect, vertices += 4; } } - - quint16 *indices = geometry->indexDataAsUShort(); + void *indexData = geometry->indexData(); for (int i = 0; i < 4 * vCells * hCells; i += 4) - appendQuad(&indices, i, i + 1, i + 2, i + 3); + appendQuad(geometry->indexType(), &indexData, i, i + 1, i + 2, i + 3); } return geometry; } @@ -484,7 +511,7 @@ void QSGBasicInternalImageNode::updateGeometry() int hTiles = ceilRight - floorLeft; int vTiles = ceilBottom - floorTop; - bool hasTiles = hTiles != 1 || vTiles != 1; + bool hasTiles = hTiles > 1 || vTiles > 1; bool fullTexture = innerSourceRect == QRectF(0, 0, 1, 1); // An image can be rendered as a single quad if: @@ -512,6 +539,10 @@ void QSGBasicInternalImageNode::updateGeometry() if (m_antialiasing) { QSGGeometry *g = geometry(); Q_ASSERT(g != &m_geometry); + if (g->indexType() != QSGGeometry::UnsignedShortType) { + setGeometry(new QSGGeometry(smoothAttributeSet(), 0)); + g = geometry(); + } g->allocate(8, 14); g->setDrawingMode(QSGGeometry::DrawTriangleStrip); SmoothVertex *vertices = reinterpret_cast<SmoothVertex *>(g->vertexData()); @@ -546,10 +577,14 @@ void QSGBasicInternalImageNode::updateGeometry() 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); + QSGGeometry *g = geometry(); + g = updateGeometry(m_targetRect, m_innerTargetRect, + sourceRect, innerSourceRect, m_subSourceRect, + g, m_mirror, m_antialiasing); + if (g != geometry()) { + setGeometry(g); + setFlag(OwnsGeometry, true); + } } } markDirty(DirtyGeometry); diff --git a/src/quick/scenegraph/qsgbasicinternalrectanglenode.cpp b/src/quick/scenegraph/qsgbasicinternalrectanglenode.cpp index df124cce35..91317ee2d7 100644 --- a/src/quick/scenegraph/qsgbasicinternalrectanglenode.cpp +++ b/src/quick/scenegraph/qsgbasicinternalrectanglenode.cpp @@ -68,19 +68,30 @@ namespace { float x, y; Color4ub color; - void set(float nx, float ny, Color4ub ncolor) + + void set(float primary, float secondary, Color4ub ncolor, bool vertical) { - x = nx; y = ny; color = ncolor; + if (vertical) { + x = secondary; y = primary; + } else { + x = primary; y = secondary; + } + color = ncolor; } }; struct SmoothVertex : public Vertex { float dx, dy; - void set(float nx, float ny, Color4ub ncolor, float ndx, float ndy) + + void set(float primary, float secondary, Color4ub ncolor, float dPrimary, float dSecondary, bool vertical) { - Vertex::set(nx, ny, ncolor); - dx = ndx; dy = ndy; + Vertex::set(primary, secondary, ncolor, vertical); + if (vertical) { + dx = dSecondary; dy = dPrimary; + } else { + dx = dPrimary; dy = dSecondary; + } } }; @@ -103,6 +114,7 @@ QSGBasicInternalRectangleNode::QSGBasicInternalRectangleNode() , m_antialiasing(false) , m_gradient_is_opaque(true) , m_dirty_geometry(false) + , m_gradient_is_vertical(true) , m_geometry(QSGGeometry::defaultAttributes_ColoredPoint2D(), 0) { setGeometry(&m_geometry); @@ -160,6 +172,15 @@ void QSGBasicInternalRectangleNode::setGradientStops(const QGradientStops &stops m_dirty_geometry = true; } +void QSGBasicInternalRectangleNode::setGradientVertical(bool vertical) +{ + if (vertical == m_gradient_is_vertical) + return; + m_gradient_is_vertical = vertical; + m_dirty_geometry = true; +} + + void QSGBasicInternalRectangleNode::setRadius(qreal radius) { if (radius == m_radius) @@ -230,12 +251,15 @@ void QSGBasicInternalRectangleNode::updateGeometry() Color4ub transparent = { 0, 0, 0, 0 }; const QGradientStops &stops = m_gradient_stops; + float length = (m_gradient_is_vertical ? height : width); + float secondaryLength = (m_gradient_is_vertical ? width : height); + int nextGradientStop = 0; - float gradientPos = penWidth / height; + float gradientPos = penWidth / length; while (nextGradientStop < stops.size() && stops.at(nextGradientStop).first <= gradientPos) ++nextGradientStop; int lastGradientStop = stops.size() - 1; - float lastGradientPos = 1.0f - penWidth / height; + float lastGradientPos = 1.0f - penWidth / length; while (lastGradientStop >= nextGradientStop && stops.at(lastGradientStop).first >= lastGradientPos) --lastGradientStop; int gradientIntersections = (lastGradientStop - nextGradientStop + 1); @@ -319,40 +343,46 @@ void QSGBasicInternalRectangleNode::updateGeometry() quint16 *indices = g->indexDataAsUShort(); quint16 index = 0; - float py = 0; // previous inner y-coordinate. - float plx = 0; // previous inner left x-coordinate. - float prx = 0; // previous inner right x-coordinate. + float pp = 0; // previous inner primary coordinate. + float pss = 0; // previous inner secondary start coordinate. + float pse = 0; // previous inner secondary end coordinate. float angle = 0.5f * float(M_PI) / segments; float cosStep = qFastCos(angle); float sinStep = qFastSin(angle); + float innerStart = (m_gradient_is_vertical ? innerRect.top() : innerRect.left()); + float innerEnd = (m_gradient_is_vertical ? innerRect.bottom() : innerRect.right()); + float innerLength = (m_gradient_is_vertical ? innerRect.height() : innerRect.width()); + float innerSecondaryStart = (m_gradient_is_vertical ? innerRect.left() : innerRect.top()); + float innerSecondaryEnd = (m_gradient_is_vertical ? innerRect.right() : innerRect.bottom()); + for (int part = 0; part < 2; ++part) { float c = 1 - part; float s = part; for (int i = 0; i <= segments; ++i) { - float y, lx, rx; + float p, ss, se; if (innerRadius > 0) { - y = (part ? innerRect.bottom() : innerRect.top()) - innerRadius * c; // current inner y-coordinate. - lx = innerRect.left() - innerRadius * s; // current inner left x-coordinate. - rx = innerRect.right() + innerRadius * s; // current inner right x-coordinate. - gradientPos = ((part ? innerRect.height() : 0) + radius - innerRadius * c) / height; + p = (part ? innerEnd : innerStart) - innerRadius * c; // current inner primary coordinate. + ss = innerSecondaryStart - innerRadius * s; // current inner secondary start coordinate. + se = innerSecondaryEnd + innerRadius * s; // current inner secondary end coordinate. + gradientPos = ((part ? innerLength : 0) + radius - innerRadius * c) / length; } else { - y = (part ? innerRect.bottom() + innerRadius : innerRect.top() - innerRadius); // current inner y-coordinate. - lx = innerRect.left() - innerRadius; // current inner left x-coordinate. - rx = innerRect.right() + innerRadius; // current inner right x-coordinate. - gradientPos = ((part ? innerRect.height() + innerRadius : -innerRadius) + radius) / height; + p = (part ? innerEnd + innerRadius : innerStart - innerRadius); // current inner primary coordinate. + ss = innerSecondaryStart - innerRadius; // current inner secondary start coordinate. + se = innerSecondaryEnd + innerRadius; // current inner secondary end coordinate. + gradientPos = ((part ? innerLength + innerRadius : -innerRadius) + radius) / length; } - float Y = (part ? innerRect.bottom() : innerRect.top()) - outerRadius * c; // current outer y-coordinate. - float lX = innerRect.left() - outerRadius * s; // current outer left x-coordinate. - float rX = innerRect.right() + outerRadius * s; // current outer right x-coordinate. + float outerEdge = (part ? innerEnd : innerStart) - outerRadius * c; // current outer primary coordinate. + float outerSecondaryStart = innerSecondaryStart - outerRadius * s; // current outer secondary start coordinate. + float outerSecondaryEnd = innerSecondaryEnd + outerRadius * s; // current outer secondary end coordinate. while (nextGradientStop <= lastGradientStop && stops.at(nextGradientStop).first <= gradientPos) { // Insert vertices at gradient stops. - float gy = (innerRect.top() - radius) + stops.at(nextGradientStop).first * height; - float t = (gy - py) / (y - py); - float glx = plx * (1 - t) + t * lx; - float grx = prx * (1 - t) + t * rx; + float gp = (innerStart - radius) + stops.at(nextGradientStop).first * length; + float t = (gp - pp) / (p - pp); + float gis = pss * (1 - t) + t * ss; // gradient inner start + float gie = pse * (1 - t) + t * se; // gradient inner end fillColor = colorToColor4ub(stops.at(nextGradientStop).second); @@ -377,23 +407,23 @@ void QSGBasicInternalRectangleNode::updateGeometry() indices[innerAATail++] = index + 3; bool lower = stops.at(nextGradientStop).first > 0.5f; - float dy = lower ? qMin(0.0f, height - gy - delta) : qMax(0.0f, delta - gy); - smoothVertices[index++].set(grx, gy, fillColor, width - grx - delta, dy); - smoothVertices[index++].set(glx, gy, fillColor, delta - glx, dy); + float dp = lower ? qMin(0.0f, length - gp - delta) : qMax(0.0f, delta - gp); + smoothVertices[index++].set(gp, gie, fillColor, dp, secondaryLength - gie - delta, m_gradient_is_vertical); + smoothVertices[index++].set(gp, gis, fillColor, dp, delta - gis, m_gradient_is_vertical); if (penWidth) { - smoothVertices[index++].set(grx, gy, borderColor, 0.49f * penWidth * s, -0.49f * penWidth * c); - smoothVertices[index++].set(glx, gy, borderColor, -0.49f * penWidth * s, -0.49f * penWidth * c); + smoothVertices[index++].set(gp, gie, borderColor, -0.49f * penWidth * c, 0.49f * penWidth * s, m_gradient_is_vertical); + smoothVertices[index++].set(gp, gis, borderColor, -0.49f * penWidth * c, -0.49f * penWidth * s, m_gradient_is_vertical); } else { - dy = lower ? delta : -delta; - smoothVertices[index++].set(grx, gy, transparent, delta, dy); - smoothVertices[index++].set(glx, gy, transparent, -delta, dy); + dp = lower ? delta : -delta; + smoothVertices[index++].set(gp, gie, transparent, dp, delta, m_gradient_is_vertical); + smoothVertices[index++].set(gp, gis, transparent, dp, -delta, m_gradient_is_vertical); } } else { - vertices[index++].set(grx, gy, fillColor); - vertices[index++].set(glx, gy, fillColor); + vertices[index++].set(gp, gie, fillColor, m_gradient_is_vertical); + vertices[index++].set(gp, gis, fillColor, m_gradient_is_vertical); if (penWidth) { - vertices[index++].set(grx, gy, borderColor); - vertices[index++].set(glx, gy, borderColor); + vertices[index++].set(gp, gie, borderColor, m_gradient_is_vertical); + vertices[index++].set(gp, gis, borderColor, m_gradient_is_vertical); } } ++nextGradientStop; @@ -430,41 +460,41 @@ void QSGBasicInternalRectangleNode::updateGeometry() indices[innerAATail++] = index + 1; indices[innerAATail++] = index + 3; - float dy = part ? qMin(0.0f, height - y - delta) : qMax(0.0f, delta - y); - smoothVertices[index++].set(rx, y, fillColor, width - rx - delta, dy); - smoothVertices[index++].set(lx, y, fillColor, delta - lx, dy); + float dp = part ? qMin(0.0f, length - p - delta) : qMax(0.0f, delta - p); + smoothVertices[index++].set(p, se, fillColor, dp, secondaryLength - se - delta, m_gradient_is_vertical); + smoothVertices[index++].set(p, ss, fillColor, dp, delta - ss, m_gradient_is_vertical); - dy = part ? delta : -delta; + dp = part ? delta : -delta; if (penWidth) { - smoothVertices[index++].set(rx, y, borderColor, 0.49f * penWidth * s, -0.49f * penWidth * c); - smoothVertices[index++].set(lx, y, borderColor, -0.49f * penWidth * s, -0.49f * penWidth * c); - smoothVertices[index++].set(rX, Y, borderColor, -0.49f * penWidth * s, 0.49f * penWidth * c); - smoothVertices[index++].set(lX, Y, borderColor, 0.49f * penWidth * s, 0.49f * penWidth * c); - smoothVertices[index++].set(rX, Y, transparent, delta, dy); - smoothVertices[index++].set(lX, Y, transparent, -delta, dy); + smoothVertices[index++].set(p, se, borderColor, -0.49f * penWidth * c, 0.49f * penWidth * s, m_gradient_is_vertical); + smoothVertices[index++].set(p, ss, borderColor, -0.49f * penWidth * c, -0.49f * penWidth * s, m_gradient_is_vertical); + smoothVertices[index++].set(outerEdge, outerSecondaryEnd, borderColor, 0.49f * penWidth * c, -0.49f * penWidth * s, m_gradient_is_vertical); + smoothVertices[index++].set(outerEdge, outerSecondaryStart, borderColor, 0.49f * penWidth * c, 0.49f * penWidth * s, m_gradient_is_vertical); + smoothVertices[index++].set(outerEdge, outerSecondaryEnd, transparent, dp, delta, m_gradient_is_vertical); + smoothVertices[index++].set(outerEdge, outerSecondaryStart, transparent, dp, -delta, m_gradient_is_vertical); indices[--outerAAHead] = index - 2; indices[--outerAAHead] = index - 4; indices[outerAATail++] = index - 3; indices[outerAATail++] = index - 1; } else { - smoothVertices[index++].set(rx, y, transparent, delta, dy); - smoothVertices[index++].set(lx, y, transparent, -delta, dy); + smoothVertices[index++].set(p, se, transparent, dp, delta, m_gradient_is_vertical); + smoothVertices[index++].set(p, ss, transparent, dp, -delta, m_gradient_is_vertical); } } else { - vertices[index++].set(rx, y, fillColor); - vertices[index++].set(lx, y, fillColor); + vertices[index++].set(p, se, fillColor, m_gradient_is_vertical); + vertices[index++].set(p, ss, fillColor, m_gradient_is_vertical); if (penWidth) { - vertices[index++].set(rx, y, borderColor); - vertices[index++].set(lx, y, borderColor); - vertices[index++].set(rX, Y, borderColor); - vertices[index++].set(lX, Y, borderColor); + vertices[index++].set(p, se, borderColor, m_gradient_is_vertical); + vertices[index++].set(p, ss, borderColor, m_gradient_is_vertical); + vertices[index++].set(outerEdge, outerSecondaryEnd, borderColor, m_gradient_is_vertical); + vertices[index++].set(outerEdge, outerSecondaryStart, borderColor, m_gradient_is_vertical); } } - py = y; - plx = lx; - prx = rx; + pp = p; + pss = ss; + pse = se; // Rotate qreal tmp = c; @@ -543,19 +573,24 @@ void QSGBasicInternalRectangleNode::updateGeometry() quint16 *indices = g->indexDataAsUShort(); quint16 index = 0; - float lx = innerRect.left(); - float rx = innerRect.right(); - float lX = outerRect.left(); - float rX = outerRect.right(); + float innerStart = (m_gradient_is_vertical ? innerRect.top() : innerRect.left()); + float innerEnd = (m_gradient_is_vertical ? innerRect.bottom() : innerRect.right()); + float outerStart = (m_gradient_is_vertical ? outerRect.top() : outerRect.left()); + float outerEnd = (m_gradient_is_vertical ? outerRect.bottom() : outerRect.right()); + + float innerSecondaryStart = (m_gradient_is_vertical ? innerRect.left() : innerRect.top()); + float innerSecondaryEnd = (m_gradient_is_vertical ? innerRect.right() : innerRect.bottom()); + float outerSecondaryStart = (m_gradient_is_vertical ? outerRect.left() : outerRect.top()); + float outerSecondaryEnd = (m_gradient_is_vertical ? outerRect.right() : outerRect.bottom()); for (int part = -1; part <= 1; part += 2) { - float y = (part == 1 ? innerRect.bottom() : innerRect.top()); - float Y = (part == 1 ? outerRect.bottom() : outerRect.top()); - gradientPos = (y - innerRect.top() + penWidth) / height; + float innerEdge = (part == 1 ? innerEnd : innerStart); + float outerEdge = (part == 1 ? outerEnd : outerStart); + gradientPos = (innerEdge - innerStart + penWidth) / length; while (nextGradientStop <= lastGradientStop && stops.at(nextGradientStop).first <= gradientPos) { // Insert vertices at gradient stops. - float gy = (innerRect.top() - penWidth) + stops.at(nextGradientStop).first * height; + float gp = (innerStart - penWidth) + stops.at(nextGradientStop).first * length; fillColor = colorToColor4ub(stops.at(nextGradientStop).second); @@ -580,22 +615,22 @@ void QSGBasicInternalRectangleNode::updateGeometry() indices[innerAATail++] = index + 3; bool lower = stops.at(nextGradientStop).first > 0.5f; - float dy = lower ? qMin(0.0f, height - gy - delta) : qMax(0.0f, delta - gy); - smoothVertices[index++].set(rx, gy, fillColor, width - rx - delta, dy); - smoothVertices[index++].set(lx, gy, fillColor, delta - lx, dy); + float dp = lower ? qMin(0.0f, length - gp - delta) : qMax(0.0f, delta - gp); + smoothVertices[index++].set(gp, innerSecondaryEnd, fillColor, dp, secondaryLength - innerSecondaryEnd - delta, m_gradient_is_vertical); + smoothVertices[index++].set(gp, innerSecondaryStart, fillColor, dp, delta - innerSecondaryStart, m_gradient_is_vertical); if (penWidth) { - smoothVertices[index++].set(rx, gy, borderColor, 0.49f * penWidth, (lower ? 0.49f : -0.49f) * penWidth); - smoothVertices[index++].set(lx, gy, borderColor, -0.49f * penWidth, (lower ? 0.49f : -0.49f) * penWidth); + smoothVertices[index++].set(gp, innerSecondaryEnd, borderColor, (lower ? 0.49f : -0.49f) * penWidth, 0.49f * penWidth, m_gradient_is_vertical); + smoothVertices[index++].set(gp, innerSecondaryStart, borderColor, (lower ? 0.49f : -0.49f) * penWidth, -0.49f * penWidth, m_gradient_is_vertical); } else { - smoothVertices[index++].set(rx, gy, transparent, delta, lower ? delta : -delta); - smoothVertices[index++].set(lx, gy, transparent, -delta, lower ? delta : -delta); + smoothVertices[index++].set(gp, innerSecondaryEnd, transparent, lower ? delta : -delta, delta, m_gradient_is_vertical); + smoothVertices[index++].set(gp, innerSecondaryStart, transparent, lower ? delta : -delta, -delta, m_gradient_is_vertical); } } else { - vertices[index++].set(rx, gy, fillColor); - vertices[index++].set(lx, gy, fillColor); + vertices[index++].set(gp, innerSecondaryEnd, fillColor, m_gradient_is_vertical); + vertices[index++].set(gp, innerSecondaryStart, fillColor, m_gradient_is_vertical); if (penWidth) { - vertices[index++].set(rx, gy, borderColor); - vertices[index++].set(lx, gy, borderColor); + vertices[index++].set(gp, innerSecondaryEnd, borderColor, m_gradient_is_vertical); + vertices[index++].set(gp, innerSecondaryStart, borderColor, m_gradient_is_vertical); } } ++nextGradientStop; @@ -632,34 +667,34 @@ void QSGBasicInternalRectangleNode::updateGeometry() indices[innerAATail++] = index + 1; indices[innerAATail++] = index + 3; - float dy = part == 1 ? qMin(0.0f, height - y - delta) : qMax(0.0f, delta - y); - smoothVertices[index++].set(rx, y, fillColor, width - rx - delta, dy); - smoothVertices[index++].set(lx, y, fillColor, delta - lx, dy); + float dp = part == 1 ? qMin(0.0f, length - innerEdge - delta) : qMax(0.0f, delta - innerEdge); + smoothVertices[index++].set(innerEdge, innerSecondaryEnd, fillColor, dp, secondaryLength - innerSecondaryEnd - delta, m_gradient_is_vertical); + smoothVertices[index++].set(innerEdge, innerSecondaryStart, fillColor, dp, delta - innerSecondaryStart, m_gradient_is_vertical); if (penWidth) { - smoothVertices[index++].set(rx, y, borderColor, 0.49f * penWidth, 0.49f * penWidth * part); - smoothVertices[index++].set(lx, y, borderColor, -0.49f * penWidth, 0.49f * penWidth * part); - smoothVertices[index++].set(rX, Y, borderColor, -0.49f * penWidth, -0.49f * penWidth * part); - smoothVertices[index++].set(lX, Y, borderColor, 0.49f * penWidth, -0.49f * penWidth * part); - smoothVertices[index++].set(rX, Y, transparent, delta, delta * part); - smoothVertices[index++].set(lX, Y, transparent, -delta, delta * part); + smoothVertices[index++].set(innerEdge, innerSecondaryEnd, borderColor, 0.49f * penWidth * part, 0.49f * penWidth, m_gradient_is_vertical); + smoothVertices[index++].set(innerEdge, innerSecondaryStart, borderColor, 0.49f * penWidth * part, -0.49f * penWidth, m_gradient_is_vertical); + smoothVertices[index++].set(outerEdge, outerSecondaryEnd, borderColor, -0.49f * penWidth * part, -0.49f * penWidth, m_gradient_is_vertical); + smoothVertices[index++].set(outerEdge, outerSecondaryStart, borderColor, -0.49f * penWidth * part, 0.49f * penWidth, m_gradient_is_vertical); + smoothVertices[index++].set(outerEdge, outerSecondaryEnd, transparent, delta * part, delta, m_gradient_is_vertical); + smoothVertices[index++].set(outerEdge, outerSecondaryStart, transparent, delta * part, -delta, m_gradient_is_vertical); indices[--outerAAHead] = index - 2; indices[--outerAAHead] = index - 4; indices[outerAATail++] = index - 3; indices[outerAATail++] = index - 1; } else { - smoothVertices[index++].set(rx, y, transparent, delta, delta * part); - smoothVertices[index++].set(lx, y, transparent, -delta, delta * part); + smoothVertices[index++].set(innerEdge, innerSecondaryEnd, transparent, delta * part, delta, m_gradient_is_vertical); + smoothVertices[index++].set(innerEdge, innerSecondaryStart, transparent, delta * part, -delta, m_gradient_is_vertical); } } else { - vertices[index++].set(rx, y, fillColor); - vertices[index++].set(lx, y, fillColor); + vertices[index++].set(innerEdge, innerSecondaryEnd, fillColor, m_gradient_is_vertical); + vertices[index++].set(innerEdge, innerSecondaryStart, fillColor, m_gradient_is_vertical); if (penWidth) { - vertices[index++].set(rx, y, borderColor); - vertices[index++].set(lx, y, borderColor); - vertices[index++].set(rX, Y, borderColor); - vertices[index++].set(lX, Y, borderColor); + vertices[index++].set(innerEdge, innerSecondaryEnd, borderColor, m_gradient_is_vertical); + vertices[index++].set(innerEdge, innerSecondaryStart, borderColor, m_gradient_is_vertical); + vertices[index++].set(outerEdge, outerSecondaryEnd, borderColor, m_gradient_is_vertical); + vertices[index++].set(outerEdge, outerSecondaryStart, borderColor, m_gradient_is_vertical); } } } diff --git a/src/quick/scenegraph/qsgbasicinternalrectanglenode_p.h b/src/quick/scenegraph/qsgbasicinternalrectanglenode_p.h index 98e53669ce..99f26b9aed 100644 --- a/src/quick/scenegraph/qsgbasicinternalrectanglenode_p.h +++ b/src/quick/scenegraph/qsgbasicinternalrectanglenode_p.h @@ -66,6 +66,7 @@ public: void setPenColor(const QColor &color) override; void setPenWidth(qreal width) override; void setGradientStops(const QGradientStops &stops) override; + void setGradientVertical(bool vertical) override; void setRadius(qreal radius) override; void setAntialiasing(bool antialiasing) override; void setAligned(bool aligned) override; @@ -90,6 +91,7 @@ protected: uint m_antialiasing : 1; uint m_gradient_is_opaque : 1; uint m_dirty_geometry : 1; + uint m_gradient_is_vertical : 1; QSGGeometry m_geometry; }; diff --git a/src/quick/scenegraph/qsgcontext.cpp b/src/quick/scenegraph/qsgcontext.cpp index d460794573..d9ed25c099 100644 --- a/src/quick/scenegraph/qsgcontext.cpp +++ b/src/quick/scenegraph/qsgcontext.cpp @@ -143,7 +143,7 @@ public: qCDebug(QSG_LOG_INFO, "Animation Driver: using walltime"); } - void start() Q_DECL_OVERRIDE + void start() override { m_time = 0; m_timer.start(); @@ -151,14 +151,14 @@ public: QAnimationDriver::start(); } - qint64 elapsed() const Q_DECL_OVERRIDE + qint64 elapsed() const override { return m_mode == VSyncMode ? qint64(m_time) : qint64(m_time) + m_wallTime.elapsed(); } - void advance() Q_DECL_OVERRIDE + void advance() override { qint64 delta = m_timer.restart(); @@ -363,13 +363,6 @@ void QSGRenderContext::registerFontengineForCleanup(QFontEngine *engine) } /*! - Factory function for texture objects. - - If \a image is a valid image, the QSGTexture::setImage function - will be called with \a image as argument. - */ - -/*! Factory function for the scene graph renderers. The renderers are used for the toplevel renderer and once for every @@ -379,7 +372,7 @@ void QSGRenderContext::registerFontengineForCleanup(QFontEngine *engine) QSGTexture *QSGRenderContext::textureForFactory(QQuickTextureFactory *factory, QQuickWindow *window) { if (!factory) - return 0; + return nullptr; m_mutex.lock(); QSGTexture *texture = m_textures.value(factory); @@ -404,6 +397,20 @@ void QSGRenderContext::textureFactoryDestroyed(QObject *o) m_mutex.unlock(); } +/*! + Return the texture corresponding to a texture factory. + + This may optionally manipulate the texture in some way; for example by returning + an atlased texture. + + This function is not a replacement for textureForFactory; both should be used + for a single texture (this might atlas, while the other might cache). +*/ +QSGTexture *QSGRenderContext::compressedTextureForFactory(const QSGCompressedTextureFactory *) const +{ + return nullptr; +} + #include "qsgcontext.moc" #include "moc_qsgcontext_p.cpp" diff --git a/src/quick/scenegraph/qsgcontext_p.h b/src/quick/scenegraph/qsgcontext_p.h index 6ff8f4a76e..6d70d7ef6b 100644 --- a/src/quick/scenegraph/qsgcontext_p.h +++ b/src/quick/scenegraph/qsgcontext_p.h @@ -78,6 +78,7 @@ class QSGMaterial; class QSGRenderLoop; class QSGLayer; class QQuickTextureFactory; +class QSGCompressedTextureFactory; class QSGContext; class QQuickPaintedItem; class QSGRendererInterface; @@ -109,8 +110,8 @@ public: MsaaAntialiasing }; - explicit QSGContext(QObject *parent = 0); - virtual ~QSGContext(); + explicit QSGContext(QObject *parent = nullptr); + ~QSGContext() override; virtual void renderContextInitialized(QSGRenderContext *renderContext); virtual void renderContextInvalidated(QSGRenderContext *renderContext); @@ -158,7 +159,7 @@ public: }; QSGRenderContext(QSGContext *context); - virtual ~QSGRenderContext(); + ~QSGRenderContext() override; QSGContext *sceneGraphContext() const { return m_sg; } virtual bool isValid() const { return true; } @@ -173,6 +174,7 @@ public: virtual QSGTexture *createTexture(const QImage &image, uint flags = CreateTexture_Alpha) const = 0; virtual QSGRenderer *createRenderer() = 0; + virtual QSGTexture *compressedTextureForFactory(const QSGCompressedTextureFactory *) const; virtual void setAttachToGraphicsContext(bool attach) { Q_UNUSED(attach); } @@ -194,7 +196,7 @@ protected: QMutex m_mutex; QHash<QQuickTextureFactory *, QSGTexture *> m_textures; QSet<QSGTexture *> m_texturesToDelete; - QHash<QRawFont, QSGDistanceFieldGlyphCache*> m_glyphCaches; + QHash<QString, QSGDistanceFieldGlyphCache *> m_glyphCaches; QSet<QFontEngine *> m_fontEnginesToClean; }; diff --git a/src/quick/scenegraph/qsgcontextplugin.cpp b/src/quick/scenegraph/qsgcontextplugin.cpp index b8b5141957..66add51c55 100644 --- a/src/quick/scenegraph/qsgcontextplugin.cpp +++ b/src/quick/scenegraph/qsgcontextplugin.cpp @@ -49,6 +49,9 @@ #include <QtQuick/private/qsgdefaultcontext_p.h> #endif +#include <QtGui/private/qguiapplication_p.h> +#include <QtGui/qpa/qplatformintegration.h> + QT_BEGIN_NAMESPACE Q_DECLARE_LOGGING_CATEGORY(QSG_LOG_INFO) @@ -70,9 +73,11 @@ Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader, struct QSGAdaptationBackendData { QSGAdaptationBackendData(); + ~QSGAdaptationBackendData(); + Q_DISABLE_COPY(QSGAdaptationBackendData) - bool tried; - QSGContextFactoryInterface *factory; + bool tried = false; + QSGContextFactoryInterface *factory = nullptr; QString name; QSGContextFactoryInterface::Flags flags; @@ -82,14 +87,17 @@ struct QSGAdaptationBackendData }; QSGAdaptationBackendData::QSGAdaptationBackendData() - : tried(false) - , factory(nullptr) - , flags(0) + : flags(nullptr) { // Fill in the table with the built-in adaptations. builtIns.append(new QSGSoftwareAdaptation); } +QSGAdaptationBackendData::~QSGAdaptationBackendData() +{ + qDeleteAll(builtIns); +} + Q_GLOBAL_STATIC(QSGAdaptationBackendData, qsg_adaptation_data) // This only works when the backend is loaded (contextFactory() was called), @@ -119,24 +127,24 @@ QSGAdaptationBackendData *contextFactory() } } - if (requestedBackend.isEmpty() && qEnvironmentVariableIsSet("QMLSCENE_DEVICE")) - requestedBackend = QString::fromLocal8Bit(qgetenv("QMLSCENE_DEVICE")); + if (requestedBackend.isEmpty()) + requestedBackend = qEnvironmentVariable("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")); + if (requestedBackend.isEmpty()) + requestedBackend = qEnvironmentVariable("QT_QUICK_BACKEND"); -#if !QT_CONFIG(opengl) - // If this is a build without OpenGL, and no backend has been set + // If this platform does not support OpenGL, and no backend has been set // default to the software renderer - if (requestedBackend.isEmpty()) + if (requestedBackend.isEmpty() + && !QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::OpenGL)) { requestedBackend = QString::fromLocal8Bit("software"); -#endif + } if (!requestedBackend.isEmpty()) { - qCDebug(QSG_LOG_INFO) << "Loading backend" << requestedBackend; + qCDebug(QSG_LOG_INFO, "Loading backend %s", qUtf8Printable(requestedBackend)); // First look for a built-in adaptation. for (QSGContextFactoryInterface *builtInBackend : qAsConst(backendData->builtIns)) { @@ -206,7 +214,7 @@ QQuickTextureFactory *QSGContext::createTextureFactoryFromImage(const QImage &im QSGAdaptationBackendData *backendData = contextFactory(); if (backendData->factory) return backendData->factory->createTextureFactoryFromImage(image); - return 0; + return nullptr; } @@ -220,7 +228,7 @@ QSGRenderLoop *QSGContext::createWindowManager() QSGAdaptationBackendData *backendData = contextFactory(); if (backendData->factory) return backendData->factory->createWindowManager(); - return 0; + return nullptr; } void QSGContext::setBackend(const QString &backend) diff --git a/src/quick/scenegraph/qsgcontextplugin_p.h b/src/quick/scenegraph/qsgcontextplugin_p.h index 5914b42809..d37d4df270 100644 --- a/src/quick/scenegraph/qsgcontextplugin_p.h +++ b/src/quick/scenegraph/qsgcontextplugin_p.h @@ -87,13 +87,13 @@ class Q_QUICK_PRIVATE_EXPORT QSGContextPlugin : public QObject, public QSGContex Q_OBJECT Q_INTERFACES(QSGContextFactoryInterface:QFactoryInterface) public: - explicit QSGContextPlugin(QObject *parent = 0); + explicit QSGContextPlugin(QObject *parent = nullptr); virtual ~QSGContextPlugin(); - virtual QStringList keys() const override = 0; + QStringList keys() const override = 0; - QQuickTextureFactory *createTextureFactoryFromImage(const QImage &) override { return 0; } - QSGRenderLoop *createWindowManager() override { return 0; } + QQuickTextureFactory *createTextureFactoryFromImage(const QImage &) override { return nullptr; } + QSGRenderLoop *createWindowManager() override { return nullptr; } }; QT_END_NAMESPACE diff --git a/src/quick/scenegraph/qsgdefaultcontext.cpp b/src/quick/scenegraph/qsgdefaultcontext.cpp index be5fec9dab..af0589e5d3 100644 --- a/src/quick/scenegraph/qsgdefaultcontext.cpp +++ b/src/quick/scenegraph/qsgdefaultcontext.cpp @@ -149,20 +149,23 @@ void QSGDefaultContext::renderContextInitialized(QSGRenderContext *renderContext 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); + qCDebug(QSG_LOG_INFO, "R/G/B/A Buffers: %d %d %d %d", format.redBufferSize(), + format.greenBufferSize(), format.blueBufferSize(), format.alphaBufferSize()); + qCDebug(QSG_LOG_INFO, "Depth Buffer: %d", format.depthBufferSize()); + qCDebug(QSG_LOG_INFO, "Stencil Buffer: %d", format.stencilBufferSize()); + qCDebug(QSG_LOG_INFO, "Samples: %d", format.samples()); + qCDebug(QSG_LOG_INFO, "GL_VENDOR: %s", (const char*)funcs->glGetString(GL_VENDOR)); + qCDebug(QSG_LOG_INFO, "GL_RENDERER: %s", + (const char*)funcs->glGetString(GL_RENDERER)); + qCDebug(QSG_LOG_INFO, "GL_VERSION: %s", (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); + qCDebug(QSG_LOG_INFO, "GL_EXTENSIONS: %s", all.constData()); + qCDebug(QSG_LOG_INFO, "Max Texture Size: %d", openglRenderContext->maxTextureSize()); + qCDebug(QSG_LOG_INFO, "Debug context: %s", + format.testOption(QSurfaceFormat::DebugContext) ? "true" : "false"); } m_mutex.unlock(); @@ -218,8 +221,14 @@ QSurfaceFormat QSGDefaultContext::defaultSurfaceFormat() const 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 (useDepth && format.depthBufferSize() == -1) + format.setDepthBufferSize(24); + else if (!useDepth) + format.setDepthBufferSize(0); + if (useStencil && format.stencilBufferSize() == -1) + format.setStencilBufferSize(8); + else if (!useStencil) + format.setStencilBufferSize(0); if (enableDebug) format.setOption(QSurfaceFormat::DebugContext); if (QQuickWindow::hasDefaultAlphaBuffer()) @@ -287,3 +296,10 @@ QSGRendererInterface::ShaderSourceTypes QSGDefaultContext::shaderSourceType() co } QT_END_NAMESPACE + +static void initResources() +{ + Q_INIT_RESOURCE(scenegraph); +} + +Q_CONSTRUCTOR_FUNCTION(initResources) diff --git a/src/quick/scenegraph/qsgdefaultcontext_p.h b/src/quick/scenegraph/qsgdefaultcontext_p.h index ab319502ef..6dfd197cf6 100644 --- a/src/quick/scenegraph/qsgdefaultcontext_p.h +++ b/src/quick/scenegraph/qsgdefaultcontext_p.h @@ -53,14 +53,14 @@ #include <QtQuick/private/qsgcontext_p.h> #include <QtQuick/private/qsgdistancefieldglyphnode_p.h> -#include "qsgrendererinterface.h" +#include <qsgrendererinterface.h> QT_BEGIN_NAMESPACE class Q_QUICK_PRIVATE_EXPORT QSGDefaultContext : public QSGContext, public QSGRendererInterface { public: - QSGDefaultContext(QObject *parent = 0); + QSGDefaultContext(QObject *parent = nullptr); ~QSGDefaultContext(); void renderContextInitialized(QSGRenderContext *renderContext) override; diff --git a/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp b/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp index ba25172d2f..8121b4559e 100644 --- a/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp +++ b/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2018 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtQuick module of the Qt Toolkit. @@ -39,12 +39,18 @@ #include "qsgdefaultdistancefieldglyphcache_p.h" +#include <QtCore/qelapsedtimer.h> +#include <QtCore/qbuffer.h> +#include <QtQml/qqmlfile.h> + #include <QtGui/private/qdistancefield_p.h> #include <QtGui/private/qopenglcontext_p.h> #include <QtQml/private/qqmlglobal_p.h> #include <qopenglfunctions.h> #include <qopenglframebufferobject.h> #include <qmath.h> +#include "qsgcontext_p.h" + #if !defined(QT_OPENGL_ES_2) #include <QtGui/qopenglfunctions_3_2_core.h> @@ -59,26 +65,34 @@ DEFINE_BOOL_CONFIG_OPTION(qsgPreferFullSizeGlyphCacheTextures, QSG_PREFER_FULLSI # define QSG_DEFAULT_DISTANCEFIELD_GLYPH_CACHE_PADDING 2 #endif -QSGDefaultDistanceFieldGlyphCache::QSGDefaultDistanceFieldGlyphCache(QOpenGLContext *c, const QRawFont &font) - : QSGDistanceFieldGlyphCache(c, font) +QSGDefaultDistanceFieldGlyphCache::QSGDefaultDistanceFieldGlyphCache(QOpenGLContext *c, + const QRawFont &font) + : QSGDistanceFieldGlyphCache(font) , m_maxTextureSize(0) , m_maxTextureCount(3) - , m_blitProgram(0) + , m_areaAllocator(nullptr) + , m_blitProgram(nullptr) , m_blitBuffer(QOpenGLBuffer::VertexBuffer) - , m_fboGuard(0) + , m_fboGuard(nullptr) , m_funcs(c->functions()) #if !defined(QT_OPENGL_ES_2) - , m_coreFuncs(0) + , m_coreFuncs(nullptr) #endif { - m_blitBuffer.create(); - m_blitBuffer.bind(); - static GLfloat buffer[16] = {-1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f, - 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f}; - m_blitBuffer.allocate(buffer, sizeof(buffer)); - m_blitBuffer.release(); - - m_areaAllocator = new QSGAreaAllocator(QSize(maxTextureSize(), m_maxTextureCount * maxTextureSize())); + if (Q_LIKELY(m_blitBuffer.create())) { + m_blitBuffer.bind(); + static const GLfloat buffer[16] = {-1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f, + 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f}; + m_blitBuffer.allocate(buffer, sizeof(buffer)); + m_blitBuffer.release(); + } else { + qWarning("Buffer creation failed"); + } + + m_coreProfile = (c->format().profile() == QSurfaceFormat::CoreProfile); + + // Load a pregenerated cache if the font contains one + loadPregeneratedCache(font); } QSGDefaultDistanceFieldGlyphCache::~QSGDefaultDistanceFieldGlyphCache() @@ -86,7 +100,7 @@ QSGDefaultDistanceFieldGlyphCache::~QSGDefaultDistanceFieldGlyphCache() for (int i = 0; i < m_textures.count(); ++i) m_funcs->glDeleteTextures(1, &m_textures[i].texture); - if (m_fboGuard != 0) + if (m_fboGuard != nullptr) m_fboGuard->free(); delete m_blitProgram; @@ -98,6 +112,9 @@ void QSGDefaultDistanceFieldGlyphCache::requestGlyphs(const QSet<glyph_t> &glyph QList<GlyphPosition> glyphPositions; QVector<glyph_t> glyphsToRender; + if (m_areaAllocator == nullptr) + m_areaAllocator = new QSGAreaAllocator(QSize(maxTextureSize(), m_maxTextureCount * maxTextureSize())); + for (QSet<glyph_t>::const_iterator it = glyphs.constBegin(); it != glyphs.constEnd() ; ++it) { glyph_t glyphIndex = *it; @@ -234,10 +251,23 @@ void QSGDefaultDistanceFieldGlyphCache::releaseGlyphs(const QSet<glyph_t> &glyph m_unusedGlyphs += glyphs; } -void QSGDefaultDistanceFieldGlyphCache::createTexture(TextureInfo *texInfo, int width, int height) +void QSGDefaultDistanceFieldGlyphCache::createTexture(TextureInfo *texInfo, + int width, + int height) { - if (useTextureResizeWorkaround() && texInfo->image.isNull()) + QByteArray zeroBuf(width * height, 0); + createTexture(texInfo, width, height, zeroBuf.constData()); +} + +void QSGDefaultDistanceFieldGlyphCache::createTexture(TextureInfo *texInfo, + int width, + int height, + const void *pixels) +{ + if (useTextureResizeWorkaround() && texInfo->image.isNull()) { texInfo->image = QDistanceField(width, height); + memcpy(texInfo->image.bits(), pixels, width * height); + } while (m_funcs->glGetError() != GL_NO_ERROR) { } @@ -258,8 +288,7 @@ void QSGDefaultDistanceFieldGlyphCache::createTexture(TextureInfo *texInfo, int const GLenum format = GL_ALPHA; #endif - QByteArray zeroBuf(width * height, 0); - m_funcs->glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, format, GL_UNSIGNED_BYTE, zeroBuf.constData()); + m_funcs->glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, format, GL_UNSIGNED_BYTE, pixels); texInfo->size = QSize(width, height); @@ -397,7 +426,7 @@ void QSGDefaultDistanceFieldGlyphCache::resizeTexture(TextureInfo *texInfo, int m_funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); #endif m_funcs->glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, oldWidth, oldHeight, 0, - GL_RGBA, GL_UNSIGNED_BYTE, NULL); + GL_RGBA, GL_UNSIGNED_BYTE, nullptr); m_funcs->glBindTexture(GL_TEXTURE_2D, 0); m_funcs->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tmp_texture, 0); @@ -517,4 +546,261 @@ int QSGDefaultDistanceFieldGlyphCache::maxTextureSize() const return m_maxTextureSize; } +namespace { + struct Qtdf { + // We need these structs to be tightly packed, but some compilers we use do not + // support #pragma pack(1), so we need to hardcode the offsets/sizes in the + // file format + enum TableSize { + HeaderSize = 14, + GlyphRecordSize = 46, + TextureRecordSize = 17 + }; + + enum Offset { + // Header + majorVersion = 0, + minorVersion = 1, + pixelSize = 2, + textureSize = 4, + flags = 8, + headerPadding = 9, + numGlyphs = 10, + + // Glyph record + glyphIndex = 0, + textureOffsetX = 4, + textureOffsetY = 8, + textureWidth = 12, + textureHeight = 16, + xMargin = 20, + yMargin = 24, + boundingRectX = 28, + boundingRectY = 32, + boundingRectWidth = 36, + boundingRectHeight = 40, + textureIndex = 44, + + // Texture record + allocatedX = 0, + allocatedY = 4, + allocatedWidth = 8, + allocatedHeight = 12, + texturePadding = 16 + + }; + + template <typename T> + static inline T fetch(const char *data, Offset offset) + { + return qFromBigEndian<T>(data + int(offset)); + } + }; +} + +bool QSGDefaultDistanceFieldGlyphCache::loadPregeneratedCache(const QRawFont &font) +{ + // The pregenerated data must be loaded first, otherwise the area allocator + // will be wrong + if (m_areaAllocator != nullptr) { + qWarning("Font cache must be loaded before cache is used"); + return false; + } + + static QElapsedTimer timer; + + bool profile = QSG_LOG_TIME_GLYPH().isDebugEnabled(); + if (profile) + timer.start(); + + QByteArray qtdfTable = font.fontTable("qtdf"); + if (qtdfTable.isEmpty()) + return false; + + typedef QHash<TextureInfo *, QVector<glyph_t> > GlyphTextureHash; + + GlyphTextureHash glyphTextures; + + if (uint(qtdfTable.size()) < Qtdf::HeaderSize) { + qWarning("Invalid qtdf table in font '%s'", + qPrintable(font.familyName())); + return false; + } + + const char *qtdfTableStart = qtdfTable.constData(); + const char *qtdfTableEnd = qtdfTableStart + qtdfTable.size(); + + int padding = 0; + int textureCount = 0; + { + quint8 majorVersion = Qtdf::fetch<quint8>(qtdfTableStart, Qtdf::majorVersion); + quint8 minorVersion = Qtdf::fetch<quint8>(qtdfTableStart, Qtdf::minorVersion); + if (majorVersion != 5 || minorVersion != 12) { + qWarning("Invalid version of qtdf table %d.%d in font '%s'", + majorVersion, + minorVersion, + qPrintable(font.familyName())); + return false; + } + + qreal pixelSize = qreal(Qtdf::fetch<quint16>(qtdfTableStart, Qtdf::pixelSize)); + m_maxTextureSize = Qtdf::fetch<quint32>(qtdfTableStart, Qtdf::textureSize); + m_doubleGlyphResolution = Qtdf::fetch<quint8>(qtdfTableStart, Qtdf::flags) == 1; + padding = Qtdf::fetch<quint8>(qtdfTableStart, Qtdf::headerPadding); + + if (pixelSize <= 0.0) { + qWarning("Invalid pixel size in '%s'", qPrintable(font.familyName())); + return false; + } + + if (m_maxTextureSize <= 0) { + qWarning("Invalid texture size in '%s'", qPrintable(font.familyName())); + return false; + } + + int systemMaxTextureSize; + m_funcs->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &systemMaxTextureSize); + + if (m_maxTextureSize > systemMaxTextureSize) { + qWarning("System maximum texture size is %d. This is lower than the value in '%s', which is %d", + systemMaxTextureSize, + qPrintable(font.familyName()), + m_maxTextureSize); + } + + if (padding != QSG_DEFAULT_DISTANCEFIELD_GLYPH_CACHE_PADDING) { + qWarning("Padding mismatch in '%s'. Font requires %d, but Qt is compiled with %d.", + qPrintable(font.familyName()), + padding, + QSG_DEFAULT_DISTANCEFIELD_GLYPH_CACHE_PADDING); + } + + m_referenceFont.setPixelSize(pixelSize); + + quint32 glyphCount = Qtdf::fetch<quint32>(qtdfTableStart, Qtdf::numGlyphs); + m_unusedGlyphs.reserve(glyphCount); + + const char *allocatorData = qtdfTableStart + Qtdf::HeaderSize; + { + m_areaAllocator = new QSGAreaAllocator(QSize(0, 0)); + allocatorData = m_areaAllocator->deserialize(allocatorData, qtdfTableEnd - allocatorData); + if (allocatorData == nullptr) + return false; + } + + if (m_areaAllocator->size().height() % m_maxTextureSize != 0) { + qWarning("Area allocator size mismatch in '%s'", qPrintable(font.familyName())); + return false; + } + + textureCount = m_areaAllocator->size().height() / m_maxTextureSize; + m_maxTextureCount = qMax(m_maxTextureCount, textureCount); + + const char *textureRecord = allocatorData; + for (int i = 0; i < textureCount; ++i, textureRecord += Qtdf::TextureRecordSize) { + if (textureRecord + Qtdf::TextureRecordSize > qtdfTableEnd) { + qWarning("qtdf table too small in font '%s'.", + qPrintable(font.familyName())); + return false; + } + + TextureInfo *tex = textureInfo(i); + tex->allocatedArea.setX(Qtdf::fetch<quint32>(textureRecord, Qtdf::allocatedX)); + tex->allocatedArea.setY(Qtdf::fetch<quint32>(textureRecord, Qtdf::allocatedY)); + tex->allocatedArea.setWidth(Qtdf::fetch<quint32>(textureRecord, Qtdf::allocatedWidth)); + tex->allocatedArea.setHeight(Qtdf::fetch<quint32>(textureRecord, Qtdf::allocatedHeight)); + tex->padding = Qtdf::fetch<quint8>(textureRecord, Qtdf::texturePadding); + } + + const char *glyphRecord = textureRecord; + for (quint32 i = 0; i < glyphCount; ++i, glyphRecord += Qtdf::GlyphRecordSize) { + if (glyphRecord + Qtdf::GlyphRecordSize > qtdfTableEnd) { + qWarning("qtdf table too small in font '%s'.", + qPrintable(font.familyName())); + return false; + } + + glyph_t glyph = Qtdf::fetch<quint32>(glyphRecord, Qtdf::glyphIndex); + m_unusedGlyphs.insert(glyph); + + GlyphData &glyphData = emptyData(glyph); + +#define FROM_FIXED_POINT(value) \ +(((qreal)value)/(qreal)65536) + + glyphData.texCoord.x = FROM_FIXED_POINT(Qtdf::fetch<quint32>(glyphRecord, Qtdf::textureOffsetX)); + glyphData.texCoord.y = FROM_FIXED_POINT(Qtdf::fetch<quint32>(glyphRecord, Qtdf::textureOffsetY)); + glyphData.texCoord.width = FROM_FIXED_POINT(Qtdf::fetch<quint32>(glyphRecord, Qtdf::textureWidth)); + glyphData.texCoord.height = FROM_FIXED_POINT(Qtdf::fetch<quint32>(glyphRecord, Qtdf::textureHeight)); + glyphData.texCoord.xMargin = FROM_FIXED_POINT(Qtdf::fetch<quint32>(glyphRecord, Qtdf::xMargin)); + glyphData.texCoord.yMargin = FROM_FIXED_POINT(Qtdf::fetch<quint32>(glyphRecord, Qtdf::yMargin)); + glyphData.boundingRect.setX(FROM_FIXED_POINT(Qtdf::fetch<qint32>(glyphRecord, Qtdf::boundingRectX))); + glyphData.boundingRect.setY(FROM_FIXED_POINT(Qtdf::fetch<qint32>(glyphRecord, Qtdf::boundingRectY))); + glyphData.boundingRect.setWidth(FROM_FIXED_POINT(Qtdf::fetch<quint32>(glyphRecord, Qtdf::boundingRectWidth))); + glyphData.boundingRect.setHeight(FROM_FIXED_POINT(Qtdf::fetch<quint32>(glyphRecord, Qtdf::boundingRectHeight))); + +#undef FROM_FIXED_POINT + + int textureIndex = Qtdf::fetch<quint16>(glyphRecord, Qtdf::textureIndex); + if (textureIndex < 0 || textureIndex >= textureCount) { + qWarning("Invalid texture index %d (texture count == %d) in '%s'", + textureIndex, + textureCount, + qPrintable(font.familyName())); + return false; + } + + + TextureInfo *texInfo = textureInfo(textureIndex); + m_glyphsTexture.insert(glyph, texInfo); + + glyphTextures[texInfo].append(glyph); + } + + GLint alignment = 4; // default value + m_funcs->glGetIntegerv(GL_UNPACK_ALIGNMENT, &alignment); + + m_funcs->glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + + const uchar *textureData = reinterpret_cast<const uchar *>(glyphRecord); + for (int i = 0; i < textureCount; ++i) { + + TextureInfo *texInfo = textureInfo(i); + + int width = texInfo->allocatedArea.width(); + int height = texInfo->allocatedArea.height(); + qint64 size = width * height; + if (reinterpret_cast<const char *>(textureData + size) > qtdfTableEnd) { + qWarning("qtdf table too small in font '%s'.", + qPrintable(font.familyName())); + return false; + } + + createTexture(texInfo, width, height, textureData); + + QVector<glyph_t> glyphs = glyphTextures.value(texInfo); + + Texture t; + t.textureId = texInfo->texture; + t.size = texInfo->size; + + setGlyphsTexture(glyphs, t); + + textureData += size; + } + + m_funcs->glPixelStorei(GL_UNPACK_ALIGNMENT, alignment); + } + + if (profile) { + quint64 now = timer.elapsed(); + qCDebug(QSG_LOG_TIME_GLYPH, + "distancefield: %d pre-generated glyphs loaded in %dms", + m_unusedGlyphs.size(), + (int) now); + } + + return true; +} + QT_END_NAMESPACE diff --git a/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache_p.h b/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache_p.h index fe365495c2..c64adddd91 100644 --- a/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache_p.h +++ b/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache_p.h @@ -85,18 +85,22 @@ public: void setMaxTextureCount(int max) { m_maxTextureCount = max; } int maxTextureCount() const { return m_maxTextureCount; } + private: + bool loadPregeneratedCache(const QRawFont &font); + inline bool isCoreProfile() const { return m_coreProfile; } + struct TextureInfo { GLuint texture; QSize size; QRect allocatedArea; QDistanceField image; - int padding; + int padding = -1; - TextureInfo(const QRect &preallocRect = QRect()) : texture(0), allocatedArea(preallocRect), padding(-1) - { } + TextureInfo(const QRect &preallocRect = QRect()) : texture(0), allocatedArea(preallocRect) { } }; + void createTexture(TextureInfo * texInfo, int width, int height, const void *pixels); void createTexture(TextureInfo * texInfo, int width, int height); void resizeTexture(TextureInfo * texInfo, int width, int height); @@ -134,6 +138,7 @@ private: mutable int m_maxTextureSize; int m_maxTextureCount; + bool m_coreProfile; QList<TextureInfo> m_textures; QHash<glyph_t, TextureInfo *> m_glyphsTexture; diff --git a/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp b/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp index edb6e92a0d..ce706d76f7 100644 --- a/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp +++ b/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp @@ -71,9 +71,9 @@ static inline QVector4D qsg_premultiply(const QVector4D &c, float globalOpacity) return QVector4D(c.x() * o, c.y() * o, c.z() * o, o); } -static inline int qsg_device_pixel_ratio(QOpenGLContext *ctx) +static inline qreal qsg_device_pixel_ratio(QOpenGLContext *ctx) { - int devicePixelRatio = 1; + qreal devicePixelRatio = 1; if (ctx->surface()->surfaceClass() == QSurface::Window) { QWindow *w = static_cast<QWindow *>(ctx->surface()); if (QQuickWindow *qw = qobject_cast<QQuickWindow *>(w)) @@ -107,7 +107,7 @@ protected: char const *const *QSGTextMaskShader::attributeNames() const { - static char const *const attr[] = { "vCoord", "tCoord", 0 }; + static char const *const attr[] = { "vCoord", "tCoord", nullptr }; return attr; } @@ -141,13 +141,13 @@ void QSGTextMaskShader::updateState(const RenderState &state, QSGMaterial *newEf { QSGTextMaskMaterial *material = static_cast<QSGTextMaskMaterial *>(newEffect); QSGTextMaskMaterial *oldMaterial = static_cast<QSGTextMaskMaterial *>(oldEffect); - Q_ASSERT(oldEffect == 0 || newEffect->type() == oldEffect->type()); + Q_ASSERT(oldEffect == nullptr || newEffect->type() == oldEffect->type()); bool updated = material->ensureUpToDate(); Q_ASSERT(material->texture()); - Q_ASSERT(oldMaterial == 0 || oldMaterial->texture()); + Q_ASSERT(oldMaterial == nullptr || oldMaterial->texture()); if (updated - || oldMaterial == 0 + || oldMaterial == nullptr || oldMaterial->texture()->textureId() != material->texture()->textureId()) { program()->setUniformValue(m_textureScale_id, QVector2D(1.0 / material->cacheTextureWidth(), 1.0 / material->cacheTextureHeight())); @@ -190,7 +190,7 @@ void QSG8BitTextMaskShader::updateState(const RenderState &state, QSGMaterial *n QSGTextMaskMaterial *material = static_cast<QSGTextMaskMaterial *>(newEffect); QSGTextMaskMaterial *oldMaterial = static_cast<QSGTextMaskMaterial *>(oldEffect); - if (oldMaterial == 0 || material->color() != oldMaterial->color() || state.isOpacityDirty()) { + if (oldMaterial == nullptr || material->color() != oldMaterial->color() || state.isOpacityDirty()) { QVector4D color = qsg_premultiply(material->color(), state.opacity()); program()->setUniformValue(m_color_id, color); } @@ -282,7 +282,7 @@ void QSG24BitTextMaskShader::updateState(const RenderState &state, QSGMaterial * QSGTextMaskMaterial *material = static_cast<QSGTextMaskMaterial *>(newEffect); QSGTextMaskMaterial *oldMaterial = static_cast<QSGTextMaskMaterial *>(oldEffect); - if (oldMaterial == 0 || material->color() != oldMaterial->color() || state.isOpacityDirty()) { + if (oldMaterial == nullptr || material->color() != oldMaterial->color() || state.isOpacityDirty()) { QVector4D color = material->color(); if (useSRGB()) color = qt_sRGB_to_linear_RGB(color); @@ -301,7 +301,7 @@ public: setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/scenegraph/shaders/32bitcolortext.frag")); } - void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) Q_DECL_OVERRIDE; + void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override; }; void QSG32BitColorTextShader::updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) @@ -310,7 +310,7 @@ void QSG32BitColorTextShader::updateState(const RenderState &state, QSGMaterial QSGTextMaskMaterial *material = static_cast<QSGTextMaskMaterial *>(newEffect); QSGTextMaskMaterial *oldMaterial = static_cast<QSGTextMaskMaterial *>(oldEffect); - if (oldMaterial == Q_NULLPTR || material->color() != oldMaterial->color() || state.isOpacityDirty()) { + if (oldMaterial == nullptr || material->color() != oldMaterial->color() || state.isOpacityDirty()) { float opacity = material->color().w() * state.opacity(); program()->setUniformValue(m_color_id, opacity); } @@ -346,20 +346,20 @@ void QSGStyledTextShader::updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) { - Q_ASSERT(oldEffect == 0 || newEffect->type() == oldEffect->type()); + Q_ASSERT(oldEffect == nullptr || newEffect->type() == oldEffect->type()); QSGStyledTextMaterial *material = static_cast<QSGStyledTextMaterial *>(newEffect); QSGStyledTextMaterial *oldMaterial = static_cast<QSGStyledTextMaterial *>(oldEffect); - if (oldMaterial == 0 || oldMaterial->styleShift() != material->styleShift()) + if (oldMaterial == nullptr || oldMaterial->styleShift() != material->styleShift()) program()->setUniformValue(m_shift_id, material->styleShift()); - if (oldMaterial == 0 || material->color() != oldMaterial->color() || state.isOpacityDirty()) { + if (oldMaterial == nullptr || material->color() != oldMaterial->color() || state.isOpacityDirty()) { QVector4D color = qsg_premultiply(material->color(), state.opacity()); program()->setUniformValue(m_color_id, color); } - if (oldMaterial == 0 || material->styleColor() != oldMaterial->styleColor() || state.isOpacityDirty()) { + if (oldMaterial == nullptr || material->styleColor() != oldMaterial->styleColor() || state.isOpacityDirty()) { QVector4D styleColor = qsg_premultiply(material->styleColor(), state.opacity()); program()->setUniformValue(m_styleColor_id, styleColor); } @@ -367,9 +367,9 @@ void QSGStyledTextShader::updateState(const RenderState &state, bool updated = material->ensureUpToDate(); Q_ASSERT(material->texture()); - Q_ASSERT(oldMaterial == 0 || oldMaterial->texture()); + Q_ASSERT(oldMaterial == nullptr || oldMaterial->texture()); if (updated - || oldMaterial == 0 + || oldMaterial == nullptr || oldMaterial->texture()->textureId() != material->texture()->textureId()) { program()->setUniformValue(m_textureScale_id, QVector2D(1.0 / material->cacheTextureWidth(), 1.0 / material->cacheTextureHeight())); @@ -400,8 +400,8 @@ public: }; QSGTextMaskMaterial::QSGTextMaskMaterial(const QRawFont &font, QFontEngine::GlyphFormat glyphFormat) - : m_texture(0) - , m_glyphCache(0) + : m_texture(nullptr) + , m_glyphCache(nullptr) , m_font(font) { init(glyphFormat); @@ -419,7 +419,7 @@ void QSGTextMaskMaterial::init(QFontEngine::GlyphFormat glyphFormat) setFlag(Blending, true); QOpenGLContext *ctx = const_cast<QOpenGLContext *>(QOpenGLContext::currentContext()); - Q_ASSERT(ctx != 0); + Q_ASSERT(ctx != 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 diff --git a/src/quick/scenegraph/qsgdefaultinternalimagenode.cpp b/src/quick/scenegraph/qsgdefaultinternalimagenode.cpp index a5a6da06a7..5dd6eaa4ca 100644 --- a/src/quick/scenegraph/qsgdefaultinternalimagenode.cpp +++ b/src/quick/scenegraph/qsgdefaultinternalimagenode.cpp @@ -83,7 +83,6 @@ QSGMaterialShader *QSGSmoothTextureMaterial::createShader() const } 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")); @@ -91,7 +90,7 @@ SmoothTextureMaterialShader::SmoothTextureMaterialShader() void SmoothTextureMaterialShader::updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) { - if (oldEffect == 0) { + if (oldEffect == nullptr) { // 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()); @@ -106,7 +105,7 @@ char const *const *SmoothTextureMaterialShader::attributeNames() const "multiTexCoord", "vertexOffset", "texCoordOffset", - 0 + nullptr }; return attributes; } @@ -171,7 +170,7 @@ void QSGDefaultInternalImageNode::updateMaterialAntialiasing() { if (m_antialiasing) { setMaterial(&m_smoothMaterial); - setOpaqueMaterial(0); + setOpaqueMaterial(nullptr); } else { setMaterial(&m_materialO); setOpaqueMaterial(&m_material); diff --git a/src/quick/scenegraph/qsgdefaultinternalrectanglenode.cpp b/src/quick/scenegraph/qsgdefaultinternalrectanglenode.cpp index e52dcaad52..fd0dcebd57 100644 --- a/src/quick/scenegraph/qsgdefaultinternalrectanglenode.cpp +++ b/src/quick/scenegraph/qsgdefaultinternalrectanglenode.cpp @@ -67,7 +67,6 @@ private: }; 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")); @@ -81,7 +80,7 @@ void SmoothColorMaterialShader::updateState(const RenderState &state, QSGMateria if (state.isMatrixDirty()) program()->setUniformValue(m_matrixLoc, state.combinedMatrix()); - if (oldEffect == 0) { + if (oldEffect == nullptr) { // 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()); @@ -94,7 +93,7 @@ char const *const *SmoothColorMaterialShader::attributeNames() const "vertex", "vertexColor", "vertexOffset", - 0 + nullptr }; return attributes; } diff --git a/src/quick/scenegraph/qsgdefaultlayer.cpp b/src/quick/scenegraph/qsgdefaultlayer.cpp index 86d74acf54..b2b123912f 100644 --- a/src/quick/scenegraph/qsgdefaultlayer.cpp +++ b/src/quick/scenegraph/qsgdefaultlayer.cpp @@ -60,7 +60,7 @@ namespace public: BindableFbo(QOpenGLFramebufferObject *fbo, QSGDepthStencilBuffer *depthStencil); virtual ~BindableFbo(); - void bind() const Q_DECL_OVERRIDE; + void bind() const override; private: QOpenGLFramebufferObject *m_fbo; QSGDepthStencilBuffer *m_depthStencil; @@ -90,15 +90,15 @@ namespace QSGDefaultLayer::QSGDefaultLayer(QSGRenderContext *context) : QSGLayer() - , m_item(0) + , m_item(nullptr) , m_device_pixel_ratio(1) , m_format(GL_RGBA) - , m_renderer(0) - , m_fbo(0) - , m_secondaryFbo(0) + , m_renderer(nullptr) + , m_fbo(nullptr) + , m_secondaryFbo(nullptr) , m_transparentTexture(0) #ifdef QSG_DEBUG_FBO_OVERLAY - , m_debugOverlay(0) + , m_debugOverlay(nullptr) #endif , m_samples(0) , m_mipmap(false) @@ -122,13 +122,13 @@ QSGDefaultLayer::~QSGDefaultLayer() void QSGDefaultLayer::invalidated() { delete m_renderer; - m_renderer = 0; + m_renderer = nullptr; delete m_fbo; delete m_secondaryFbo; - m_fbo = m_secondaryFbo = 0; + m_fbo = m_secondaryFbo = nullptr; #ifdef QSG_DEBUG_FBO_OVERLAY delete m_debugOverlay; - m_debugOverlay = 0; + m_debugOverlay = nullptr; #endif if (m_transparentTexture) { QOpenGLContext::currentContext()->functions()->glDeleteTextures(1, &m_transparentTexture); @@ -204,7 +204,7 @@ void QSGDefaultLayer::setItem(QSGNode *item) if (m_live && !m_item) { delete m_fbo; delete m_secondaryFbo; - m_fbo = m_secondaryFbo = 0; + m_fbo = m_secondaryFbo = nullptr; m_depthStencilBuffer.clear(); } @@ -228,7 +228,7 @@ void QSGDefaultLayer::setSize(const QSize &size) if (m_live && m_size.isNull()) { delete m_fbo; delete m_secondaryFbo; - m_fbo = m_secondaryFbo = 0; + m_fbo = m_secondaryFbo = nullptr; m_depthStencilBuffer.clear(); } @@ -252,7 +252,7 @@ void QSGDefaultLayer::setLive(bool live) if (m_live && (!m_item || m_size.isNull())) { delete m_fbo; delete m_secondaryFbo; - m_fbo = m_secondaryFbo = 0; + m_fbo = m_secondaryFbo = nullptr; m_depthStencilBuffer.clear(); } @@ -295,7 +295,7 @@ void QSGDefaultLayer::grab() if (!m_item || m_size.isNull()) { delete m_fbo; delete m_secondaryFbo; - m_fbo = m_secondaryFbo = 0; + m_fbo = m_secondaryFbo = nullptr; m_depthStencilBuffer.clear(); m_dirtyTexture = false; return; @@ -362,7 +362,7 @@ void QSGDefaultLayer::grab() delete m_fbo; delete m_secondaryFbo; m_fbo = new QOpenGLFramebufferObject(m_size, format); - m_secondaryFbo = 0; + m_secondaryFbo = nullptr; funcs->glBindTexture(GL_TEXTURE_2D, m_fbo->texture()); updateBindOptions(true); m_depthStencilBuffer = m_context->depthStencilBufferForFbo(m_fbo); diff --git a/src/quick/scenegraph/qsgdefaultlayer_p.h b/src/quick/scenegraph/qsgdefaultlayer_p.h index 7b09293095..06355e0c21 100644 --- a/src/quick/scenegraph/qsgdefaultlayer_p.h +++ b/src/quick/scenegraph/qsgdefaultlayer_p.h @@ -69,56 +69,56 @@ public: QSGDefaultLayer(QSGRenderContext *context); ~QSGDefaultLayer(); - bool updateTexture() Q_DECL_OVERRIDE; + bool updateTexture() override; // The item's "paint node", not effect node. QSGNode *item() const { return m_item; } - void setItem(QSGNode *item) Q_DECL_OVERRIDE; + void setItem(QSGNode *item) override; QRectF rect() const { return m_rect; } - void setRect(const QRectF &rect) Q_DECL_OVERRIDE; + void setRect(const QRectF &rect) override; QSize size() const { return m_size; } - void setSize(const QSize &size) Q_DECL_OVERRIDE; + void setSize(const QSize &size) override; - void setHasMipmaps(bool mipmap) Q_DECL_OVERRIDE; + void setHasMipmaps(bool mipmap) override; - void bind() Q_DECL_OVERRIDE; + void bind() override; - bool hasAlphaChannel() const Q_DECL_OVERRIDE; - bool hasMipmaps() const Q_DECL_OVERRIDE; - int textureId() const Q_DECL_OVERRIDE; - QSize textureSize() const Q_DECL_OVERRIDE { return m_size; } + bool hasAlphaChannel() const override; + bool hasMipmaps() const override; + int textureId() const override; + QSize textureSize() const override { return m_size; } GLenum format() const { return m_format; } - void setFormat(GLenum format) Q_DECL_OVERRIDE; + void setFormat(GLenum format) override; bool live() const { return bool(m_live); } - void setLive(bool live) Q_DECL_OVERRIDE; + void setLive(bool live) override; bool recursive() const { return bool(m_recursive); } - void setRecursive(bool recursive) Q_DECL_OVERRIDE; + void setRecursive(bool recursive) override; - void setDevicePixelRatio(qreal ratio) Q_DECL_OVERRIDE { m_device_pixel_ratio = ratio; } + void setDevicePixelRatio(qreal ratio) override { m_device_pixel_ratio = ratio; } bool mirrorHorizontal() const { return bool(m_mirrorHorizontal); } - void setMirrorHorizontal(bool mirror) Q_DECL_OVERRIDE; + void setMirrorHorizontal(bool mirror) override; bool mirrorVertical() const { return bool(m_mirrorVertical); } - void setMirrorVertical(bool mirror) Q_DECL_OVERRIDE; + void setMirrorVertical(bool mirror) override; - void scheduleUpdate() Q_DECL_OVERRIDE; + void scheduleUpdate() override; - QImage toImage() const Q_DECL_OVERRIDE; + QImage toImage() const override; - QRectF normalizedTextureSubRect() const Q_DECL_OVERRIDE; + QRectF normalizedTextureSubRect() const override; int samples() const { return m_samples; } - void setSamples(int samples) Q_DECL_OVERRIDE { m_samples = samples; } + void setSamples(int samples) override { m_samples = samples; } public Q_SLOTS: - void markDirtyTexture() Q_DECL_OVERRIDE; - void invalidated() Q_DECL_OVERRIDE; + void markDirtyTexture() override; + void invalidated() override; private: void grab(); diff --git a/src/quick/scenegraph/qsgdefaultrendercontext.cpp b/src/quick/scenegraph/qsgdefaultrendercontext.cpp index 29600ef0ca..73b79c6300 100644 --- a/src/quick/scenegraph/qsgdefaultrendercontext.cpp +++ b/src/quick/scenegraph/qsgdefaultrendercontext.cpp @@ -39,11 +39,13 @@ #include "qsgdefaultrendercontext_p.h" +#include <QtGui/QGuiApplication> #include <QtGui/QOpenGLFramebufferObject> #include <QtQuick/private/qsgbatchrenderer_p.h> #include <QtQuick/private/qsgrenderer_p.h> #include <QtQuick/private/qsgatlastexture_p.h> +#include <QtQuick/private/qsgcompressedtexture_p.h> #include <QtQuick/private/qsgdefaultdistancefieldglyphcache_p.h> QT_BEGIN_NAMESPACE @@ -156,14 +158,14 @@ void QSGDefaultRenderContext::invalidate() m_fontEnginesToClean.clear(); delete m_depthStencilManager; - m_depthStencilManager = 0; + m_depthStencilManager = nullptr; qDeleteAll(m_glyphCaches); m_glyphCaches.clear(); if (m_gl->property(QSG_RENDERCONTEXT_PROPERTY) == QVariant::fromValue(this)) m_gl->setProperty(QSG_RENDERCONTEXT_PROPERTY, QVariant()); - m_gl = 0; + m_gl = nullptr; if (m_sg) m_sg->renderContextInvalidated(this); @@ -210,7 +212,7 @@ QSharedPointer<QSGDepthStencilBuffer> QSGDefaultRenderContext::depthStencilBuffe QSGDepthStencilBufferManager *QSGDefaultRenderContext::depthStencilBufferManager() { if (!m_gl) - return 0; + return nullptr; if (!m_depthStencilManager) m_depthStencilManager = new QSGDepthStencilBufferManager(m_gl); return m_depthStencilManager; @@ -243,6 +245,14 @@ QSGRenderer *QSGDefaultRenderContext::createRenderer() return new QSGBatchRenderer::Renderer(this); } +QSGTexture *QSGDefaultRenderContext::compressedTextureForFactory(const QSGCompressedTextureFactory *factory) const +{ + // The atlas implementation is only supported from the render thread + if (openglContext() && QThread::currentThread() == openglContext()->thread()) + return m_atlasManager->create(factory); + return nullptr; +} + /*! Compile \a shader, optionally using \a vertexCode and \a fragmentCode as replacement for the source code supplied by \a shader. @@ -271,6 +281,26 @@ void QSGDefaultRenderContext::compileShader(QSGMaterialShader *shader, QSGMateri } } +QString QSGDefaultRenderContext::fontKey(const QRawFont &font) +{ + QFontEngine *fe = QRawFontPrivate::get(font)->fontEngine; + if (!fe->faceId().filename.isEmpty()) { + QByteArray keyName = fe->faceId().filename; + if (font.style() != QFont::StyleNormal) + keyName += QByteArray(" I"); + if (font.weight() != QFont::Normal) + keyName += ' ' + QByteArray::number(font.weight()); + keyName += QByteArray(" DF"); + return QString::fromUtf8(keyName); + } else { + return QString::fromLatin1("%1_%2_%3_%4") + .arg(font.familyName()) + .arg(font.styleName()) + .arg(font.weight()) + .arg(font.style()); + } +} + void QSGDefaultRenderContext::initializeShader(QSGMaterialShader *shader) { shader->program()->bind(); @@ -288,18 +318,30 @@ QSGDefaultRenderContext *QSGDefaultRenderContext::from(QOpenGLContext *context) return qobject_cast<QSGDefaultRenderContext *>(context->property(QSG_RENDERCONTEXT_PROPERTY).value<QObject *>()); } -QT_END_NAMESPACE - +bool QSGDefaultRenderContext::separateIndexBuffer() const +{ + // WebGL: A given WebGLBuffer object may only be bound to one of + // the ARRAY_BUFFER or ELEMENT_ARRAY_BUFFER target in its + // lifetime. An attempt to bind a buffer object to the other + // target will generate an INVALID_OPERATION error, and the + // current binding will remain untouched. + static const bool isWebGL = (qGuiApp->platformName().compare(QLatin1String("webgl")) == 0 + || qGuiApp->platformName().compare(QLatin1String("wasm")) == 0); + return isWebGL; +} QSGDistanceFieldGlyphCache *QSGDefaultRenderContext::distanceFieldGlyphCache(const QRawFont &font) { - QSGDistanceFieldGlyphCache *cache = m_glyphCaches.value(font, 0); + QString key = fontKey(font); + QSGDistanceFieldGlyphCache *cache = m_glyphCaches.value(key, 0); if (!cache) { cache = new QSGDefaultDistanceFieldGlyphCache(openglContext(), font); - m_glyphCaches.insert(font, cache); + m_glyphCaches.insert(key, cache); } return cache; } +QT_END_NAMESPACE + #include "moc_qsgdefaultrendercontext_p.cpp" diff --git a/src/quick/scenegraph/qsgdefaultrendercontext_p.h b/src/quick/scenegraph/qsgdefaultrendercontext_p.h index 0aed46b658..57aa4b4c90 100644 --- a/src/quick/scenegraph/qsgdefaultrendercontext_p.h +++ b/src/quick/scenegraph/qsgdefaultrendercontext_p.h @@ -84,8 +84,9 @@ public: QSGTexture *createTexture(const QImage &image, uint flags) const override; QSGRenderer *createRenderer() override; + QSGTexture *compressedTextureForFactory(const QSGCompressedTextureFactory *factory) const override; - virtual void compileShader(QSGMaterialShader *shader, QSGMaterial *material, const char *vertexCode = 0, const char *fragmentCode = 0); + virtual void compileShader(QSGMaterialShader *shader, QSGMaterial *material, const char *vertexCode = nullptr, const char *fragmentCode = nullptr); virtual void initializeShader(QSGMaterialShader *shader); void setAttachToGraphicsContext(bool attach) override; @@ -94,8 +95,11 @@ public: bool hasBrokenIndexBufferObjects() const { return m_brokenIBOs; } int maxTextureSize() const override { return m_maxTextureSize; } + bool separateIndexBuffer() const; protected: + static QString fontKey(const QRawFont &font); + QOpenGLContext *m_gl; QSGDepthStencilBufferManager *m_depthStencilManager; int m_maxTextureSize; @@ -103,8 +107,6 @@ protected: bool m_serializedRender; bool m_attachToGLContext; QSGAtlasTexture::Manager *m_atlasManager; - - }; QT_END_NAMESPACE diff --git a/src/quick/scenegraph/qsgdefaultspritenode.cpp b/src/quick/scenegraph/qsgdefaultspritenode.cpp index 5eb8fb6e08..8761d99c1f 100644 --- a/src/quick/scenegraph/qsgdefaultspritenode.cpp +++ b/src/quick/scenegraph/qsgdefaultspritenode.cpp @@ -70,26 +70,18 @@ public: return this - static_cast<const QQuickSpriteMaterial *>(other); } - QSGTexture *texture; - - float animT; - float animX1; - float animY1; - float animX2; - float animY2; - float animW; - float animH; + QSGTexture *texture = nullptr; + + float animT = 0.0f; + float animX1 = 0.0f; + float animY1 = 0.0f; + float animX2 = 0.0f; + float animY2 = 0.0f; + float animW = 1.0f; + float animH = 1.0f; }; 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); } @@ -103,13 +95,12 @@ 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 + void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *) override { QQuickSpriteMaterial *m = static_cast<QQuickSpriteMaterial *>(newEffect); m->texture->bind(); @@ -122,18 +113,18 @@ public: program()->setUniformValue(m_matrix_id, state.combinedMatrix()); } - void initialize() Q_DECL_OVERRIDE { + void initialize() 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 { + char const *const *attributeNames() const override { static const char *attr[] = { "vPos", "vTex", - 0 + nullptr }; return attr; } diff --git a/src/quick/scenegraph/qsgdistancefieldglyphnode.cpp b/src/quick/scenegraph/qsgdistancefieldglyphnode.cpp index 32eda2d142..ae6336718e 100644 --- a/src/quick/scenegraph/qsgdistancefieldglyphnode.cpp +++ b/src/quick/scenegraph/qsgdistancefieldglyphnode.cpp @@ -46,12 +46,12 @@ QT_BEGIN_NAMESPACE QSGDistanceFieldGlyphNode::QSGDistanceFieldGlyphNode(QSGRenderContext *context) : m_glyphNodeType(RootGlyphNode) , m_context(context) - , m_material(0) - , m_glyph_cache(0) + , m_material(nullptr) + , m_glyph_cache(nullptr) , m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 0) , m_style(QQuickText::Normal) , m_antialiasingMode(GrayAntialiasing) - , m_texture(0) + , m_texture(nullptr) , m_dirtyGeometry(false) , m_dirtyMaterial(false) { @@ -80,7 +80,7 @@ QSGDistanceFieldGlyphNode::~QSGDistanceFieldGlyphNode() void QSGDistanceFieldGlyphNode::setColor(const QColor &color) { m_color = color; - if (m_material != 0) { + if (m_material != nullptr) { m_material->setColor(color); markDirty(DirtyMaterial); } else { @@ -113,7 +113,7 @@ void QSGDistanceFieldGlyphNode::setGlyphs(const QPointF &position, const QGlyphR return; if (m_glyph_cache != oldCache) { - Q_ASSERT(ownerElement() != 0); + Q_ASSERT(ownerElement() != nullptr); if (oldCache) { oldCache->unregisterGlyphNode(this); oldCache->unregisterOwnerElement(ownerElement()); @@ -181,7 +181,7 @@ void QSGDistanceFieldGlyphNode::updateGeometry() // Remove previously created sub glyph nodes // We assume all the children are sub glyph nodes QSGNode *subnode = firstChild(); - QSGNode *nextNode = 0; + QSGNode *nextNode = nullptr; while (subnode) { nextNode = subnode->nextSibling(); delete subnode; diff --git a/src/quick/scenegraph/qsgdistancefieldglyphnode_p.cpp b/src/quick/scenegraph/qsgdistancefieldglyphnode_p.cpp index a67c659c99..aa58218505 100644 --- a/src/quick/scenegraph/qsgdistancefieldglyphnode_p.cpp +++ b/src/quick/scenegraph/qsgdistancefieldglyphnode_p.cpp @@ -61,37 +61,27 @@ protected: void updateColor(const QVector4D &c); void updateTextureScale(const QVector2D &ts); - float m_fontScale; - float m_matrixScale; + float m_fontScale = 1.0; + float m_matrixScale = 1.0; - int m_matrix_id; - int m_textureScale_id; - int m_alphaMin_id; - int m_alphaMax_id; - int m_color_id; + int m_matrix_id = -1; + int m_textureScale_id = -1; + int m_alphaMin_id = -1; + int m_alphaMax_id = -1; + int m_color_id = -1; QVector2D m_lastTextureScale; QVector4D m_lastColor; - float m_lastAlphaMin; - float m_lastAlphaMax; + float m_lastAlphaMin = -1; + float m_lastAlphaMax = -1; }; char const *const *QSGDistanceFieldTextMaterialShader::attributeNames() const { - static char const *const attr[] = { "vCoord", "tCoord", 0 }; + static char const *const attr[] = { "vCoord", "tCoord", nullptr }; return attr; } QSGDistanceFieldTextMaterialShader::QSGDistanceFieldTextMaterialShader() - : QSGMaterialShader(), - m_fontScale(1.0) - , m_matrixScale(1.0) - , m_matrix_id(-1) - , m_textureScale_id(-1) - , m_alphaMin_id(-1) - , m_alphaMax_id(-1) - , m_color_id(-1) - , m_lastAlphaMin(-1) - , m_lastAlphaMax(-1) { setShaderSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/qt-project.org/scenegraph/shaders/distancefieldtext.vert")); setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/scenegraph/shaders/distancefieldtext.frag")); @@ -166,13 +156,13 @@ void QSGDistanceFieldTextMaterialShader::initialize() void QSGDistanceFieldTextMaterialShader::updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) { - Q_ASSERT(oldEffect == 0 || newEffect->type() == oldEffect->type()); + Q_ASSERT(oldEffect == nullptr || newEffect->type() == oldEffect->type()); QSGDistanceFieldTextMaterial *material = static_cast<QSGDistanceFieldTextMaterial *>(newEffect); QSGDistanceFieldTextMaterial *oldMaterial = static_cast<QSGDistanceFieldTextMaterial *>(oldEffect); bool updated = material->updateTextureSize(); - if (oldMaterial == 0 + if (oldMaterial == nullptr || material->color() != oldMaterial->color() || state.isOpacityDirty()) { QVector4D color = material->color(); @@ -181,7 +171,7 @@ void QSGDistanceFieldTextMaterialShader::updateState(const RenderState &state, Q } bool updateRange = false; - if (oldMaterial == 0 + if (oldMaterial == nullptr || material->fontScale() != oldMaterial->fontScale()) { m_fontScale = material->fontScale(); updateRange = true; @@ -198,12 +188,12 @@ void QSGDistanceFieldTextMaterialShader::updateState(const RenderState &state, Q Q_ASSERT(material->glyphCache()); if (updated - || oldMaterial == 0 + || oldMaterial == nullptr || oldMaterial->texture()->textureId != material->texture()->textureId) { updateTextureScale(QVector2D(1.0 / material->textureSize().width(), 1.0 / material->textureSize().height())); - QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions(); + QOpenGLFunctions *funcs = state.context()->functions(); funcs->glBindTexture(GL_TEXTURE_2D, material->texture()->textureId); if (updated) { @@ -218,8 +208,8 @@ void QSGDistanceFieldTextMaterialShader::updateState(const RenderState &state, Q } QSGDistanceFieldTextMaterial::QSGDistanceFieldTextMaterial() - : m_glyph_cache(0) - , m_texture(0) + : m_glyph_cache(nullptr) + , m_texture(nullptr) , m_fontScale(1.0) { setFlag(Blending | RequiresDeterminant, true); @@ -288,12 +278,11 @@ public: protected: void initialize() override; - int m_styleColor_id; + int m_styleColor_id = -1; }; DistanceFieldStyledTextMaterialShader::DistanceFieldStyledTextMaterialShader() : QSGDistanceFieldTextMaterialShader() - , m_styleColor_id(-1) { } @@ -310,7 +299,7 @@ void DistanceFieldStyledTextMaterialShader::updateState(const RenderState &state QSGDistanceFieldStyledTextMaterial *material = static_cast<QSGDistanceFieldStyledTextMaterial *>(newEffect); QSGDistanceFieldStyledTextMaterial *oldMaterial = static_cast<QSGDistanceFieldStyledTextMaterial *>(oldEffect); - if (oldMaterial == 0 + if (oldMaterial == nullptr || material->styleColor() != oldMaterial->styleColor() || (state.isOpacityDirty())) { QVector4D color = material->styleColor(); @@ -358,14 +347,12 @@ protected: void updateOutlineAlphaRange(int dfRadius); - int m_outlineAlphaMax0_id; - int m_outlineAlphaMax1_id; + int m_outlineAlphaMax0_id = -1; + int m_outlineAlphaMax1_id = -1; }; DistanceFieldOutlineTextMaterialShader::DistanceFieldOutlineTextMaterialShader() : DistanceFieldStyledTextMaterialShader() - , m_outlineAlphaMax0_id(-1) - , m_outlineAlphaMax1_id(-1) { setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/scenegraph/shaders/distancefieldoutlinetext.frag")); } @@ -398,7 +385,7 @@ void DistanceFieldOutlineTextMaterialShader::updateState(const RenderState &stat QSGDistanceFieldOutlineTextMaterial *material = static_cast<QSGDistanceFieldOutlineTextMaterial *>(newEffect); QSGDistanceFieldOutlineTextMaterial *oldMaterial = static_cast<QSGDistanceFieldOutlineTextMaterial *>(oldEffect); - if (oldMaterial == 0 + if (oldMaterial == nullptr || material->fontScale() != oldMaterial->fontScale() || state.isMatrixDirty()) updateOutlineAlphaRange(material->glyphCache()->distanceFieldRadius()); @@ -438,12 +425,11 @@ protected: void updateShift(qreal fontScale, const QPointF& shift); - int m_shift_id; + int m_shift_id = -1; }; DistanceFieldShiftedStyleTextMaterialShader::DistanceFieldShiftedStyleTextMaterialShader() : DistanceFieldStyledTextMaterialShader() - , m_shift_id(-1) { setShaderSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/qt-project.org/scenegraph/shaders/distancefieldshiftedtext.vert")); setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/scenegraph/shaders/distancefieldshiftedtext.frag")); @@ -462,7 +448,7 @@ void DistanceFieldShiftedStyleTextMaterialShader::updateState(const RenderState QSGDistanceFieldShiftedStyleTextMaterial *material = static_cast<QSGDistanceFieldShiftedStyleTextMaterial *>(newEffect); QSGDistanceFieldShiftedStyleTextMaterial *oldMaterial = static_cast<QSGDistanceFieldShiftedStyleTextMaterial *>(oldEffect); - if (oldMaterial == 0 + if (oldMaterial == nullptr || oldMaterial->fontScale() != material->fontScale() || oldMaterial->shift() != material->shift() || oldMaterial->textureSize() != material->textureSize()) { @@ -516,14 +502,12 @@ public: void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override; private: - int m_fontScale_id; - int m_vecDelta_id; + int m_fontScale_id = -1; + int m_vecDelta_id = -1; }; QSGHiQSubPixelDistanceFieldTextMaterialShader::QSGHiQSubPixelDistanceFieldTextMaterialShader() : QSGDistanceFieldTextMaterialShader() - , m_fontScale_id(-1) - , m_vecDelta_id(-1) { setShaderSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/qt-project.org/scenegraph/shaders/hiqsubpixeldistancefieldtext.vert")); setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/scenegraph/shaders/hiqsubpixeldistancefieldtext.frag")); @@ -550,19 +534,19 @@ void QSGHiQSubPixelDistanceFieldTextMaterialShader::deactivate() void QSGHiQSubPixelDistanceFieldTextMaterialShader::updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) { - Q_ASSERT(oldEffect == 0 || newEffect->type() == oldEffect->type()); + Q_ASSERT(oldEffect == nullptr || newEffect->type() == oldEffect->type()); QSGDistanceFieldTextMaterial *material = static_cast<QSGDistanceFieldTextMaterial *>(newEffect); QSGDistanceFieldTextMaterial *oldMaterial = static_cast<QSGDistanceFieldTextMaterial *>(oldEffect); - if (oldMaterial == 0 || material->color() != oldMaterial->color()) { + if (oldMaterial == nullptr || material->color() != oldMaterial->color()) { QVector4D c = material->color(); state.context()->functions()->glBlendColor(c.x(), c.y(), c.z(), 1.0f); } - if (oldMaterial == 0 || material->fontScale() != oldMaterial->fontScale()) + if (oldMaterial == nullptr || material->fontScale() != oldMaterial->fontScale()) program()->setUniformValue(m_fontScale_id, GLfloat(material->fontScale())); - if (oldMaterial == 0 || state.isMatrixDirty()) { + if (oldMaterial == nullptr || state.isMatrixDirty()) { int viewportWidth = state.viewportRect().width(); QMatrix4x4 mat = state.combinedMatrix().inverted(); program()->setUniformValue(m_vecDelta_id, mat.column(0) * (qreal(2) / viewportWidth)); diff --git a/src/quick/scenegraph/qsgrenderloop.cpp b/src/quick/scenegraph/qsgrenderloop.cpp index c11b698a03..2e91bafa7c 100644 --- a/src/quick/scenegraph/qsgrenderloop.cpp +++ b/src/quick/scenegraph/qsgrenderloop.cpp @@ -56,6 +56,7 @@ #include <QtQuick/QQuickWindow> #include <QtQuick/private/qquickwindow_p.h> #include <QtQuick/private/qsgcontext_p.h> +#include <QtQuick/private/qsgrenderer_p.h> #include <private/qquickprofiler_p.h> #if QT_CONFIG(opengl) @@ -75,7 +76,7 @@ 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); #if QT_CONFIG(opengl) -/*! +/* expectations for this manager to work: - one opengl context to render multiple windows - OpenGL pipeline will not block for vsync in swap @@ -87,7 +88,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::s_instance = nullptr; QSGRenderLoop::~QSGRenderLoop() { @@ -106,11 +107,11 @@ void QSGRenderLoop::cleanup() QQuickWindowPrivate *wd = QQuickWindowPrivate::get(w); if (wd->windowManager == s_instance) { s_instance->windowDestroyed(w); - wd->windowManager = 0; + wd->windowManager = nullptr; } } delete s_instance; - s_instance = 0; + s_instance = nullptr; } /*! @@ -152,9 +153,9 @@ public: void update(QQuickWindow *window) override { maybeUpdate(window); } // identical for this implementation. void handleUpdateRequest(QQuickWindow *) override; - void releaseResources(QQuickWindow *) override { } + void releaseResources(QQuickWindow *) override; - QAnimationDriver *animationDriver() const override { return 0; } + QAnimationDriver *animationDriver() const override { return nullptr; } QSGContext *sceneGraphContext() const override; QSGRenderContext *createRenderContext(QSGContext *) const override { return rc; } @@ -220,10 +221,12 @@ QSGRenderLoop *QSGRenderLoop::instance() } switch (loopType) { +#if QT_CONFIG(thread) case ThreadedRenderLoop: qCDebug(QSG_LOG_INFO, "threaded render loop"); s_instance = new QSGThreadedRenderLoop(); break; +#endif case WindowsRenderLoop: qCDebug(QSG_LOG_INFO, "windows render loop"); s_instance = new QSGWindowsRenderLoop(); @@ -274,7 +277,7 @@ void QSGRenderLoop::handleContextCreationFailure(QQuickWindow *window, } #if QT_CONFIG(opengl) QSGGuiThreadRenderLoop::QSGGuiThreadRenderLoop() - : gl(0) + : gl(nullptr) { if (qsg_useConsistentTiming()) { QUnifiedTimer::instance(true)->setConsistentTiming(true); @@ -304,6 +307,8 @@ void QSGGuiThreadRenderLoop::hide(QQuickWindow *window) { QQuickWindowPrivate *cd = QQuickWindowPrivate::get(window); cd->fireAboutToStop(); + if (m_windows.contains(window)) + m_windows[window].updatePending = false; } void QSGGuiThreadRenderLoop::windowDestroyed(QQuickWindow *window) @@ -326,17 +331,18 @@ void QSGGuiThreadRenderLoop::windowDestroyed(QQuickWindow *window) current = gl->makeCurrent(surface); } if (Q_UNLIKELY(!current)) - qCDebug(QSG_LOG_RENDERLOOP) << "cleanup without an OpenGL context"; + qCDebug(QSG_LOG_RENDERLOOP, "cleanup without an OpenGL context"); #if QT_CONFIG(quick_shadereffect) && QT_CONFIG(opengl) - QQuickOpenGLShaderEffectMaterial::cleanupMaterialCache(); + if (current) + QQuickOpenGLShaderEffectMaterial::cleanupMaterialCache(); #endif d->cleanupNodesOnShutdown(); if (m_windows.size() == 0) { rc->invalidate(); delete gl; - gl = 0; + gl = nullptr; } else if (gl && window == gl->surface() && current) { gl->doneCurrent(); } @@ -346,11 +352,16 @@ void QSGGuiThreadRenderLoop::windowDestroyed(QQuickWindow *window) void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window) { - QQuickWindowPrivate *cd = QQuickWindowPrivate::get(window); - if (!cd->isRenderable() || !m_windows.contains(window)) + if (!m_windows.contains(window)) return; WindowData &data = const_cast<WindowData &>(m_windows[window]); + bool alsoSwap = data.updatePending; + data.updatePending = false; + + QQuickWindowPrivate *cd = QQuickWindowPrivate::get(window); + if (!cd->isRenderable()) + return; bool current = false; @@ -363,7 +374,7 @@ void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window) if (!gl->create()) { const bool isEs = gl->isOpenGLES(); delete gl; - gl = 0; + gl = nullptr; handleContextCreationFailure(window, isEs); } else { cd->fireOpenGLContextCreated(gl); @@ -377,8 +388,15 @@ void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window) current = gl->makeCurrent(window); } - bool alsoSwap = data.updatePending; - data.updatePending = false; + bool lastDirtyWindow = true; + auto i = m_windows.constBegin(); + while (i != m_windows.constEnd()) { + if (i.value().updatePending) { + lastDirtyWindow = false; + break; + } + i++; + } if (!current) return; @@ -407,6 +425,8 @@ void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window) emit window->afterAnimating(); cd->syncSceneGraph(); + if (lastDirtyWindow) + rc->endSync(); if (profileFrames) syncTime = renderTimer.nsecsElapsed(); @@ -481,7 +501,8 @@ QImage QSGGuiThreadRenderLoop::grab(QQuickWindow *window) void QSGGuiThreadRenderLoop::maybeUpdate(QQuickWindow *window) { - if (!m_windows.contains(window)) + QQuickWindowPrivate *cd = QQuickWindowPrivate::get(window); + if (!cd->isRenderable() || !m_windows.contains(window)) return; m_windows[window].updatePending = true; @@ -493,6 +514,14 @@ QSGContext *QSGGuiThreadRenderLoop::sceneGraphContext() const return sg; } +void QSGGuiThreadRenderLoop::releaseResources(QQuickWindow *w) +{ + // No full invalidation of the rendercontext, just clear some caches. + QQuickWindowPrivate *d = QQuickWindowPrivate::get(w); + if (d->renderer) + d->renderer->releaseCachedResources(); +} + void QSGGuiThreadRenderLoop::handleUpdateRequest(QQuickWindow *window) { renderWindow(window); diff --git a/src/quick/scenegraph/qsgthreadedrenderloop.cpp b/src/quick/scenegraph/qsgthreadedrenderloop.cpp index 7d77e52b5f..c18ba4226c 100644 --- a/src/quick/scenegraph/qsgthreadedrenderloop.cpp +++ b/src/quick/scenegraph/qsgthreadedrenderloop.cpp @@ -115,7 +115,7 @@ QT_BEGIN_NAMESPACE -#define QSG_RT_PAD " (RT)" +#define QSG_RT_PAD " (RT) %s" static inline int qsgrl_animation_interval() { qreal refreshRate = QGuiApplication::primaryScreen()->refreshRate(); @@ -167,7 +167,7 @@ template <typename T> T *windowFor(const QList<T> &list, QQuickWindow *window) if (t.window == window) return const_cast<T *>(&t); } - return 0; + return nullptr; } @@ -270,13 +270,13 @@ class QSGRenderThread : public QThread public: QSGRenderThread(QSGThreadedRenderLoop *w, QSGRenderContext *renderContext) : wm(w) - , gl(0) - , animatorDriver(0) + , gl(nullptr) + , animatorDriver(nullptr) , pendingUpdate(0) , sleeping(false) , syncResultedInChanges(false) , active(false) - , window(0) + , window(nullptr) , stopEventProcessing(false) { sgrc = static_cast<QSGDefaultRenderContext *>(renderContext); @@ -315,7 +315,7 @@ public: public slots: void sceneGraphChanged() { - qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "sceneGraphChanged"; + qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "sceneGraphChanged"); syncResultedInChanges = true; } @@ -358,15 +358,15 @@ bool QSGRenderThread::event(QEvent *e) switch ((int) e->type()) { case WM_Obscure: { - qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "WM_Obscure"; + qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "WM_Obscure"); Q_ASSERT(!window || window == static_cast<WMWindowEvent *>(e)->window); mutex.lock(); if (window) { QQuickWindowPrivate::get(window)->fireAboutToStop(); - qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "- window removed"; - window = 0; + qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- window removed"); + window = nullptr; } waitCondition.wakeOne(); mutex.unlock(); @@ -374,7 +374,7 @@ bool QSGRenderThread::event(QEvent *e) return true; } case WM_RequestSync: { - qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "WM_RequestSync"; + qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "WM_RequestSync"); WMSyncEvent *se = static_cast<WMSyncEvent *>(e); if (sleeping) stopEventProcessing = true; @@ -383,29 +383,36 @@ bool QSGRenderThread::event(QEvent *e) pendingUpdate |= SyncRequest; if (se->syncInExpose) { - qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "- triggered from expose"; + qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- triggered from expose"); pendingUpdate |= ExposeRequest; } if (se->forceRenderPass) { - qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "- repaint regardless"; + qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- repaint regardless"); pendingUpdate |= RepaintRequest; } return true; } case WM_TryRelease: { - qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "WM_TryRelease"; + qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "WM_TryRelease"); mutex.lock(); wm->m_lockedForSync = true; WMTryReleaseEvent *wme = static_cast<WMTryReleaseEvent *>(e); if (!window || wme->inDestructor) { - qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "- setting exit flag and invalidating OpenGL"; + qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- setting exit flag and invalidating OpenGL"); invalidateOpenGL(wme->window, wme->inDestructor, wme->fallbackSurface); active = gl; Q_ASSERT_X(!wme->inDestructor || !active, "QSGRenderThread::invalidateOpenGL()", "Thread's active state is not set to false when shutting down"); if (sleeping) stopEventProcessing = true; } else { - qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "- not releasing because window is still active"; + qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- not releasing because window is still active"); + if (window) { + QQuickWindowPrivate *d = QQuickWindowPrivate::get(window); + if (d->renderer) { + qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- requesting renderer to release cached resources"); + d->renderer->releaseCachedResources(); + } + } } waitCondition.wakeOne(); wm->m_lockedForSync = false; @@ -414,7 +421,7 @@ bool QSGRenderThread::event(QEvent *e) } case WM_Grab: { - qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "WM_Grab"; + qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "WM_Grab"); WMGrabEvent *ce = static_cast<WMGrabEvent *>(e); Q_ASSERT(ce->window); Q_ASSERT(ce->window == window || !window); @@ -422,40 +429,41 @@ bool QSGRenderThread::event(QEvent *e) if (ce->window) { gl->makeCurrent(ce->window); - qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "- sync scene graph"; + qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- sync scene graph"); QQuickWindowPrivate *d = QQuickWindowPrivate::get(ce->window); d->syncSceneGraph(); + sgrc->endSync(); - qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "- rendering scene graph"; + qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- rendering scene graph"); QQuickWindowPrivate::get(ce->window)->renderSceneGraph(ce->window->size()); - qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "- grabbing result"; + qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- grabbing result"); bool alpha = ce->window->format().alphaBufferSize() > 0 && ce->window->color().alpha() != 255; *ce->image = qt_gl_read_framebuffer(windowSize * ce->window->effectiveDevicePixelRatio(), alpha, alpha); ce->image->setDevicePixelRatio(ce->window->effectiveDevicePixelRatio()); } - qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "- waking gui to handle result"; + qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- waking gui to handle result"); waitCondition.wakeOne(); mutex.unlock(); return true; } case WM_PostJob: { - qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "WM_PostJob"; + qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "WM_PostJob"); WMJobEvent *ce = static_cast<WMJobEvent *>(e); Q_ASSERT(ce->window == window); if (window) { gl->makeCurrent(window); ce->job->run(); delete ce->job; - ce->job = 0; - qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "- job done"; + ce->job = nullptr; + qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- job done"); } return true; } case WM_RequestRepaint: - qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "WM_RequestPaint"; + qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "WM_RequestPaint"); // When GUI posts this event, it is followed by a polishAndSync, so we mustn't // exit the event loop yet. pendingUpdate |= RepaintRequest; @@ -469,13 +477,13 @@ bool QSGRenderThread::event(QEvent *e) void QSGRenderThread::invalidateOpenGL(QQuickWindow *window, bool inDestructor, QOffscreenSurface *fallback) { - qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "invalidateOpenGL()"; + qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "invalidateOpenGL()"); if (!gl) return; if (!window) { - qCWarning(QSG_LOG_RENDERLOOP()) << "QSGThreadedRenderLoop:QSGRenderThread: no window to make current..."; + qCWarning(QSG_LOG_RENDERLOOP, "QSGThreadedRenderLoop:QSGRenderThread: no window to make current..."); return; } @@ -485,20 +493,21 @@ void QSGRenderThread::invalidateOpenGL(QQuickWindow *window, bool inDestructor, bool current = gl->makeCurrent(fallback ? static_cast<QSurface *>(fallback) : static_cast<QSurface *>(window)); if (Q_UNLIKELY(!current)) { - qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "- cleanup without an OpenGL context"; + qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- cleanup without an OpenGL context"); } QQuickWindowPrivate *dd = QQuickWindowPrivate::get(window); #if QT_CONFIG(quick_shadereffect) - QQuickOpenGLShaderEffectMaterial::cleanupMaterialCache(); + if (current) + QQuickOpenGLShaderEffectMaterial::cleanupMaterialCache(); #endif // The canvas nodes must be cleaned up regardless if we are in the destructor.. if (wipeSG) { dd->cleanupNodesOnShutdown(); } else { - qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "- persistent SG, avoiding cleanup"; + qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- persistent SG, avoiding cleanup"); if (current) gl->doneCurrent(); return; @@ -506,29 +515,29 @@ void QSGRenderThread::invalidateOpenGL(QQuickWindow *window, bool inDestructor, sgrc->invalidate(); QCoreApplication::processEvents(); - QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); if (inDestructor) delete dd->animationController; if (current) gl->doneCurrent(); - qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "- invalidating scene graph"; + qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- invalidating scene graph"); if (wipeGL) { delete gl; - gl = 0; - qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "- invalidated OpenGL"; + gl = nullptr; + qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- invalidated OpenGL"); } else { - qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "- persistent GL, avoiding cleanup"; + qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- persistent GL, avoiding cleanup"); } } -/*! +/* Enters the mutex lock to make sure GUI is blocking and performs sync, then wakes GUI. */ void QSGRenderThread::sync(bool inExpose) { - qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "sync()"; + qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "sync()"); mutex.lock(); Q_ASSERT_X(wm->m_lockedForSync, "QSGRenderThread::sync()", "sync triggered on bad terms as gui is not already locked..."); @@ -546,14 +555,15 @@ void QSGRenderThread::sync(bool inExpose) } if (current) { QQuickWindowPrivate *d = QQuickWindowPrivate::get(window); - bool hadRenderer = d->renderer != 0; + bool hadRenderer = d->renderer != nullptr; // If the scene graph was touched since the last sync() make sure it sends the // changed signal. if (d->renderer) d->renderer->clearChangedFlag(); d->syncSceneGraph(); + sgrc->endSync(); if (!hadRenderer && d->renderer) { - qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "- renderer was created"; + qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- renderer was created"); syncResultedInChanges = true; connect(d->renderer, SIGNAL(sceneGraphChanged()), this, SLOT(sceneGraphChanged()), Qt::DirectConnection); } @@ -561,13 +571,13 @@ void QSGRenderThread::sync(bool inExpose) // 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); + QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); } else { - qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "- window has bad size, sync aborted"; + qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- window has bad size, sync aborted"); } if (!inExpose) { - qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "- sync complete, waking Gui"; + qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- sync complete, waking Gui"); waitCondition.wakeOne(); mutex.unlock(); } @@ -585,7 +595,7 @@ void QSGRenderThread::syncAndRender() QElapsedTimer waitTimer; waitTimer.start(); - qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "syncAndRender()"; + qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "syncAndRender()"); syncResultedInChanges = false; QQuickWindowPrivate *d = QQuickWindowPrivate::get(window); @@ -596,7 +606,7 @@ void QSGRenderThread::syncAndRender() pendingUpdate = 0; if (syncRequested) { - qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "- updatePending, doing sync"; + qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- updatePending, doing sync"); sync(exposeRequested); } #ifndef QSG_NO_RENDER_TIMING @@ -607,14 +617,14 @@ void QSGRenderThread::syncAndRender() QQuickProfiler::SceneGraphRenderLoopSync); if (!syncResultedInChanges && !repaintRequested && sgrc->isValid()) { - qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "- no changes, render aborted"; + qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- no changes, render aborted"); int waitTime = vsyncDelta - (int) waitTimer.elapsed(); if (waitTime > 0) msleep(waitTime); return; } - qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "- rendering started"; + qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- rendering started"); if (animatorDriver->isRunning()) { @@ -644,10 +654,10 @@ void QSGRenderThread::syncAndRender() } else { Q_QUICK_SG_PROFILE_SKIP(QQuickProfiler::SceneGraphRenderLoopFrame, QQuickProfiler::SceneGraphRenderLoopSync, 1); - qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "- window not ready, skipping render"; + qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- window not ready, skipping render"); } - qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "- rendering done"; + qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- rendering done"); // Though it would be more correct to put this block directly after // fireFrameSwapped in the if (current) branch above, we don't do @@ -655,7 +665,7 @@ void QSGRenderThread::syncAndRender() // has started rendering with a bad window, causing makeCurrent to // fail or if the window has a bad size. if (exposeRequested) { - qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "- wake Gui after initial expose"; + qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- wake Gui after initial expose"); waitCondition.wakeOne(); mutex.unlock(); } @@ -683,31 +693,31 @@ void QSGRenderThread::postEvent(QEvent *e) void QSGRenderThread::processEvents() { - qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "--- begin processEvents()"; + qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "--- begin processEvents()"); while (eventQueue.hasMoreEvents()) { QEvent *e = eventQueue.takeEvent(false); event(e); delete e; } - qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "--- done processEvents()"; + qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "--- done processEvents()"); } void QSGRenderThread::processEventsAndWaitForMore() { - qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "--- begin processEventsAndWaitForMore()"; + qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "--- begin processEventsAndWaitForMore()"); stopEventProcessing = false; while (!stopEventProcessing) { QEvent *e = eventQueue.takeEvent(true); event(e); delete e; } - qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "--- done processEventsAndWaitForMore()"; + qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "--- done processEventsAndWaitForMore()"); } void QSGRenderThread::run() { - qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "run()"; - animatorDriver = sgrc->sceneGraphContext()->createAnimationDriver(0); + qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "run()"); + animatorDriver = sgrc->sceneGraphContext()->createAnimationDriver(nullptr); animatorDriver->install(); if (QQmlDebugConnector::service<QQmlProfilerService>()) QQuickProfiler::registerAnimationCallback(); @@ -724,7 +734,7 @@ void QSGRenderThread::run() QCoreApplication::processEvents(); if (active && (pendingUpdate == 0 || !window)) { - qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "done drawing, sleep..."; + qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "done drawing, sleep..."); sleeping = true; processEventsAndWaitForMore(); sleeping = false; @@ -733,10 +743,10 @@ void QSGRenderThread::run() Q_ASSERT_X(!gl, "QSGRenderThread::run()", "The OpenGL context should be cleaned up before exiting the render thread..."); - qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "run() completed"; + qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "run() completed"); delete animatorDriver; - animatorDriver = 0; + animatorDriver = nullptr; sgrc->moveToThread(wm->thread()); moveToThread(wm->thread()); @@ -800,7 +810,7 @@ bool QSGThreadedRenderLoop::interleaveIncubation() const void QSGThreadedRenderLoop::animationStarted() { - qCDebug(QSG_LOG_RENDERLOOP) << "- animationStarted()"; + qCDebug(QSG_LOG_RENDERLOOP, "- animationStarted()"); startOrStopAnimationTimer(); for (int i=0; i<m_windows.size(); ++i) @@ -809,7 +819,7 @@ void QSGThreadedRenderLoop::animationStarted() void QSGThreadedRenderLoop::animationStopped() { - qCDebug(QSG_LOG_RENDERLOOP) << "- animationStopped()"; + qCDebug(QSG_LOG_RENDERLOOP, "- animationStopped()"); startOrStopAnimationTimer(); } @@ -817,7 +827,7 @@ void QSGThreadedRenderLoop::animationStopped() void QSGThreadedRenderLoop::startOrStopAnimationTimer() { int exposedWindows = 0; - const Window *theOne = 0; + const Window *theOne = nullptr; for (int i=0; i<m_windows.size(); ++i) { const Window &w = m_windows.at(i); if (w.window->isVisible() && w.window->isExposed()) { @@ -827,14 +837,14 @@ void QSGThreadedRenderLoop::startOrStopAnimationTimer() } if (m_animation_timer != 0 && (exposedWindows == 1 || !m_animation_driver->isRunning())) { - qCDebug(QSG_LOG_RENDERLOOP) << "*** Stopping animation timer"; + qCDebug(QSG_LOG_RENDERLOOP, "*** Stopping animation timer"); killTimer(m_animation_timer); m_animation_timer = 0; // If animations are running, make sure we keep on animating if (m_animation_driver->isRunning()) maybePostPolishRequest(const_cast<Window *>(theOne)); } else if (m_animation_timer == 0 && exposedWindows != 1 && m_animation_driver->isRunning()) { - qCDebug(QSG_LOG_RENDERLOOP) << "*** Starting animation timer"; + qCDebug(QSG_LOG_RENDERLOOP, "*** Starting animation timer"); m_animation_timer = startTimer(qsgrl_animation_interval()); } } @@ -861,7 +871,7 @@ void QSGThreadedRenderLoop::hide(QQuickWindow *window) } -/*! +/* If the window is first hide it, then perform a complete cleanup with releaseResources which will take down the GL context and exit the rendering thread. @@ -911,7 +921,7 @@ void QSGThreadedRenderLoop::exposureChanged(QQuickWindow *window) } } -/*! +/* Will post an event to the render thread that this window should start to render. */ @@ -921,7 +931,7 @@ void QSGThreadedRenderLoop::handleExposure(QQuickWindow *window) Window *w = windowFor(m_windows, window); if (!w) { - qCDebug(QSG_LOG_RENDERLOOP) << "- adding window to list"; + qCDebug(QSG_LOG_RENDERLOOP, "- adding window to list"); Window win; win.window = window; win.actualWindowFormat = window->format(); @@ -953,7 +963,7 @@ void QSGThreadedRenderLoop::handleExposure(QQuickWindow *window) // Start render thread if it is not running if (!w->thread->isRunning()) { - qCDebug(QSG_LOG_RENDERLOOP) << "- starting render thread"; + qCDebug(QSG_LOG_RENDERLOOP, "- starting render thread"); if (!w->thread->gl) { w->thread->gl = new QOpenGLContext(); @@ -964,7 +974,7 @@ void QSGThreadedRenderLoop::handleExposure(QQuickWindow *window) if (!w->thread->gl->create()) { const bool isEs = w->thread->gl->isOpenGLES(); delete w->thread->gl; - w->thread->gl = 0; + w->thread->gl = nullptr; handleContextCreationFailure(w->window, isEs); return; } @@ -972,7 +982,7 @@ void QSGThreadedRenderLoop::handleExposure(QQuickWindow *window) QQuickWindowPrivate::get(w->window)->fireOpenGLContextCreated(w->thread->gl); w->thread->gl->moveToThread(w->thread); - qCDebug(QSG_LOG_RENDERLOOP) << "- OpenGL context created"; + qCDebug(QSG_LOG_RENDERLOOP, "- OpenGL context created"); } QQuickAnimatorController *controller = QQuickWindowPrivate::get(w->window)->animationController; @@ -989,16 +999,16 @@ void QSGThreadedRenderLoop::handleExposure(QQuickWindow *window) qFatal("Render thread failed to start, aborting application."); } else { - qCDebug(QSG_LOG_RENDERLOOP) << "- render thread already running"; + qCDebug(QSG_LOG_RENDERLOOP, "- render thread already running"); } polishAndSync(w, true); - qCDebug(QSG_LOG_RENDERLOOP) << "- done with handleExposure()"; + qCDebug(QSG_LOG_RENDERLOOP, "- done with handleExposure()"); startOrStopAnimationTimer(); } -/*! +/* This function posts an event to the render thread to remove the window from the list of windowses to render. @@ -1020,7 +1030,7 @@ void QSGThreadedRenderLoop::handleObscurity(Window *w) void QSGThreadedRenderLoop::handleUpdateRequest(QQuickWindow *window) { - qCDebug(QSG_LOG_RENDERLOOP) << "- polish and sync update request"; + qCDebug(QSG_LOG_RENDERLOOP, "- polish and sync update request"); Window *w = windowFor(m_windows, window); if (w) polishAndSync(w); @@ -1033,7 +1043,7 @@ void QSGThreadedRenderLoop::maybeUpdate(QQuickWindow *window) maybeUpdate(w); } -/*! +/* Called whenever the QML scene has changed. Will post an event to ourselves that a sync is needed. */ @@ -1056,7 +1066,7 @@ void QSGThreadedRenderLoop::maybeUpdate(Window *w) // Call this function from the Gui thread later as startTimer cannot be // called from the render thread. if (current == w->thread) { - qCDebug(QSG_LOG_RENDERLOOP) << "- on render thread"; + qCDebug(QSG_LOG_RENDERLOOP, "- on render thread"); w->updateDuringSync = true; return; } @@ -1064,7 +1074,7 @@ void QSGThreadedRenderLoop::maybeUpdate(Window *w) maybePostPolishRequest(w); } -/*! +/* Called when the QQuickWindow should be explicitly repainted. This function can also be called on the render thread when the GUI thread is blocked to keep render thread animations alive. @@ -1096,7 +1106,7 @@ void QSGThreadedRenderLoop::releaseResources(QQuickWindow *window) releaseResources(w, false); } -/*! +/* * Release resources will post an event to the render thread to * free up the SG and GL resources and exists the render thread. */ @@ -1114,15 +1124,15 @@ void QSGThreadedRenderLoop::releaseResources(Window *w, bool inDestructor) // and the OpenGL resources. // QOffscreenSurface must be created on the GUI thread, so we // create it here and pass it on to QSGRenderThread::invalidateGL() - QOffscreenSurface *fallback = 0; + QOffscreenSurface *fallback = nullptr; if (!window->handle()) { - qCDebug(QSG_LOG_RENDERLOOP) << "- using fallback surface"; + qCDebug(QSG_LOG_RENDERLOOP, "- using fallback surface"); fallback = new QOffscreenSurface(); fallback->setFormat(w->actualWindowFormat); fallback->create(); } - qCDebug(QSG_LOG_RENDERLOOP) << "- posting release request to render thread"; + qCDebug(QSG_LOG_RENDERLOOP, "- posting release request to render thread"); w->thread->postEvent(new WMTryReleaseEvent(window, inDestructor, fallback)); w->thread->waitCondition.wait(&w->thread->mutex); delete fallback; @@ -1152,7 +1162,7 @@ void QSGThreadedRenderLoop::polishAndSync(Window *w, bool inExpose) QQuickWindow *window = w->window; if (!w->thread || !w->thread->window) { - qCDebug(QSG_LOG_RENDERLOOP) << "- not exposed, abort"; + qCDebug(QSG_LOG_RENDERLOOP, "- not exposed, abort"); return; } @@ -1161,7 +1171,7 @@ void QSGThreadedRenderLoop::polishAndSync(Window *w, bool inExpose) // The delivery of the event might have caused the window to stop rendering w = windowFor(m_windows, window); if (!w || !w->thread || !w->thread->window) { - qCDebug(QSG_LOG_RENDERLOOP) << "- removed after event flushing, abort"; + qCDebug(QSG_LOG_RENDERLOOP, "- removed after event flushing, abort"); return; } @@ -1187,13 +1197,13 @@ void QSGThreadedRenderLoop::polishAndSync(Window *w, bool inExpose) emit window->afterAnimating(); - qCDebug(QSG_LOG_RENDERLOOP) << "- lock for sync"; + qCDebug(QSG_LOG_RENDERLOOP, "- lock for sync"); w->thread->mutex.lock(); m_lockedForSync = true; w->thread->postEvent(new WMSyncEvent(window, inExpose, w->forceRenderPass)); w->forceRenderPass = false; - qCDebug(QSG_LOG_RENDERLOOP) << "- wait for sync"; + qCDebug(QSG_LOG_RENDERLOOP, "- wait for sync"); if (profileFrames) waitTime = timer.nsecsElapsed(); Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphPolishAndSync, @@ -1201,7 +1211,7 @@ void QSGThreadedRenderLoop::polishAndSync(Window *w, bool inExpose) w->thread->waitCondition.wait(&w->thread->mutex); m_lockedForSync = false; w->thread->mutex.unlock(); - qCDebug(QSG_LOG_RENDERLOOP) << "- unlock after sync"; + qCDebug(QSG_LOG_RENDERLOOP, "- unlock after sync"); if (profileFrames) syncTime = timer.nsecsElapsed(); @@ -1209,9 +1219,9 @@ void QSGThreadedRenderLoop::polishAndSync(Window *w, bool inExpose) QQuickProfiler::SceneGraphPolishAndSyncSync); if (m_animation_timer == 0 && m_animation_driver->isRunning()) { - qCDebug(QSG_LOG_RENDERLOOP) << "- advancing animations"; + qCDebug(QSG_LOG_RENDERLOOP, "- advancing animations"); m_animation_driver->advance(); - qCDebug(QSG_LOG_RENDERLOOP) << "- animations done.."; + qCDebug(QSG_LOG_RENDERLOOP, "- animations done.."); // We need to trigger another sync to keep animations running... maybePostPolishRequest(w); emit timeToIncubate(); @@ -1238,7 +1248,7 @@ bool QSGThreadedRenderLoop::event(QEvent *e) case QEvent::Timer: { QTimerEvent *te = static_cast<QTimerEvent *>(e); if (te->timerId() == m_animation_timer) { - qCDebug(QSG_LOG_RENDERLOOP) << "- ticking non-visual timer"; + qCDebug(QSG_LOG_RENDERLOOP, "- ticking non-visual timer"); m_animation_driver->advance(); emit timeToIncubate(); return true; @@ -1277,25 +1287,25 @@ QImage QSGThreadedRenderLoop::grab(QQuickWindow *window) if (!window->handle()) window->create(); - qCDebug(QSG_LOG_RENDERLOOP) << "- polishing items"; + qCDebug(QSG_LOG_RENDERLOOP, "- polishing items"); QQuickWindowPrivate *d = QQuickWindowPrivate::get(window); d->polishItems(); QImage result; w->thread->mutex.lock(); m_lockedForSync = true; - qCDebug(QSG_LOG_RENDERLOOP) << "- posting grab event"; + qCDebug(QSG_LOG_RENDERLOOP, "- posting grab event"); w->thread->postEvent(new WMGrabEvent(window, &result)); w->thread->waitCondition.wait(&w->thread->mutex); m_lockedForSync = false; w->thread->mutex.unlock(); - qCDebug(QSG_LOG_RENDERLOOP) << "- grab complete"; + qCDebug(QSG_LOG_RENDERLOOP, "- grab complete"); return result; } -/*! +/* * Posts a new job event to the render thread. * Returns true if posting succeeded. */ diff --git a/src/quick/scenegraph/qsgwindowsrenderloop.cpp b/src/quick/scenegraph/qsgwindowsrenderloop.cpp index e16f7ea966..95df700a15 100644 --- a/src/quick/scenegraph/qsgwindowsrenderloop.cpp +++ b/src/quick/scenegraph/qsgwindowsrenderloop.cpp @@ -48,6 +48,7 @@ #include <QtQuick/private/qsgcontext_p.h> #include <QtQuick/private/qquickwindow_p.h> +#include <QtQuick/private/qsgrenderer_p.h> #include <QtQuick/private/qsgdefaultrendercontext_p.h> #include <QtQuick/QQuickWindow> @@ -63,7 +64,7 @@ QT_BEGIN_NAMESPACE extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha); -#define RLDEBUG(x) qCDebug(QSG_LOG_RENDERLOOP) << x; +#define RLDEBUG(x) qCDebug(QSG_LOG_RENDERLOOP, x) static QElapsedTimer qsg_render_timer; #define QSG_LOG_TIME_SAMPLE(sampleName) \ @@ -77,7 +78,7 @@ static QElapsedTimer qsg_render_timer; QSGWindowsRenderLoop::QSGWindowsRenderLoop() - : m_gl(0) + : m_gl(nullptr) , m_sg(QSGContext::createDefaultContext()) , m_updateTimer(0) , m_animationTimer(0) @@ -116,7 +117,7 @@ QSGWindowsRenderLoop::WindowData *QSGWindowsRenderLoop::windowData(QQuickWindow if (wd.window == window) return &wd; } - return 0; + return nullptr; } void QSGWindowsRenderLoop::maybePostUpdateTimer() @@ -157,7 +158,7 @@ void QSGWindowsRenderLoop::stopped() void QSGWindowsRenderLoop::show(QQuickWindow *window) { RLDEBUG("show"); - if (windowData(window) != 0) + if (windowData(window) != nullptr) return; // This happens before the platform window is shown, but after @@ -177,7 +178,7 @@ void QSGWindowsRenderLoop::show(QQuickWindow *window) if (!created) { const bool isEs = m_gl->isOpenGLES(); delete m_gl; - m_gl = 0; + m_gl = nullptr; handleContextCreationFailure(window, isEs); return; } @@ -242,17 +243,18 @@ void QSGWindowsRenderLoop::windowDestroyed(QQuickWindow *window) current = m_gl->makeCurrent(surface); } if (Q_UNLIKELY(!current)) - qCDebug(QSG_LOG_RENDERLOOP) << "cleanup without an OpenGL context"; + RLDEBUG("cleanup without an OpenGL context"); #if QT_CONFIG(quick_shadereffect) && QT_CONFIG(opengl) - QQuickOpenGLShaderEffectMaterial::cleanupMaterialCache(); + if (current) + QQuickOpenGLShaderEffectMaterial::cleanupMaterialCache(); #endif d->cleanupNodesOnShutdown(); if (m_windows.size() == 0) { d->context->invalidate(); delete m_gl; - m_gl = 0; + m_gl = nullptr; } else if (m_gl && current) { m_gl->doneCurrent(); } @@ -271,7 +273,7 @@ bool QSGWindowsRenderLoop::anyoneShowing() const void QSGWindowsRenderLoop::exposureChanged(QQuickWindow *window) { - if (windowData(window) == 0) + if (windowData(window) == nullptr) return; if (window->isExposed() && window->isVisible()) { @@ -445,6 +447,14 @@ void QSGWindowsRenderLoop::renderWindow(QQuickWindow *window) } } + bool lastDirtyWindow = true; + for (int i=0; i<m_windows.size(); ++i) { + if ( m_windows[i].pendingUpdate) { + lastDirtyWindow = false; + break; + } + } + d->flushFrameSynchronousEvents(); // Event delivery or processing has caused the window to stop rendering. if (!windowData(window)) @@ -464,6 +474,8 @@ void QSGWindowsRenderLoop::renderWindow(QQuickWindow *window) RLDEBUG(" - syncing"); d->syncSceneGraph(); + if (lastDirtyWindow) + m_rc->endSync(); QSG_RENDER_TIMING_SAMPLE(QQuickProfiler::SceneGraphRenderLoopFrame, time_synced, QQuickProfiler::SceneGraphRenderLoopSync); @@ -493,6 +505,15 @@ void QSGWindowsRenderLoop::renderWindow(QQuickWindow *window) QQuickProfiler::SceneGraphRenderLoopSwap); } +void QSGWindowsRenderLoop::releaseResources(QQuickWindow *w) +{ + // No full invalidation of the rendercontext, just clear some caches. + RLDEBUG("releaseResources"); + QQuickWindowPrivate *d = QQuickWindowPrivate::get(w); + if (d->renderer) + d->renderer->releaseCachedResources(); +} + QT_END_NAMESPACE #include "moc_qsgwindowsrenderloop_p.cpp" diff --git a/src/quick/scenegraph/qsgwindowsrenderloop_p.h b/src/quick/scenegraph/qsgwindowsrenderloop_p.h index 1940a66af2..a1188fed8a 100644 --- a/src/quick/scenegraph/qsgwindowsrenderloop_p.h +++ b/src/quick/scenegraph/qsgwindowsrenderloop_p.h @@ -86,7 +86,7 @@ public: QSGContext *sceneGraphContext() const override { return m_sg; } QSGRenderContext *createRenderContext(QSGContext *) const override; - void releaseResources(QQuickWindow *) override { } + void releaseResources(QQuickWindow *) override; void render(); void renderWindow(QQuickWindow *window); diff --git a/src/quick/scenegraph/scenegraph.pri b/src/quick/scenegraph/scenegraph.pri index c6db3df158..ddd7fb7f4c 100644 --- a/src/quick/scenegraph/scenegraph.pri +++ b/src/quick/scenegraph/scenegraph.pri @@ -1,4 +1,3 @@ -DEFINES += QSG_SEPARATE_INDEX_BUFFER # DEFINES += QSG_DISTANCEFIELD_CACHE_DEBUG # Core API @@ -116,7 +115,6 @@ qtConfig(opengl(es1|es2)?) { $$PWD/util/qsgdefaultimagenode.cpp \ $$PWD/util/qsgdefaultninepatchnode.cpp \ $$PWD/qsgdefaultlayer.cpp \ - $$PWD/qsgthreadedrenderloop.cpp \ $$PWD/qsgwindowsrenderloop.cpp HEADERS += \ $$PWD/qsgdefaultglyphnode_p.h \ @@ -133,9 +131,15 @@ qtConfig(opengl(es1|es2)?) { $$PWD/util/qsgdefaultimagenode_p.h \ $$PWD/util/qsgdefaultninepatchnode_p.h \ $$PWD/qsgdefaultlayer_p.h \ - $$PWD/qsgthreadedrenderloop_p.h \ $$PWD/qsgwindowsrenderloop_p.h + qtConfig(thread) { + SOURCES += \ + $$PWD/qsgthreadedrenderloop.cpp + HEADERS += \ + $$PWD/qsgthreadedrenderloop_p.h + } + qtConfig(quick-sprite) { SOURCES += \ $$PWD/qsgdefaultspritenode.cpp @@ -220,3 +224,20 @@ qtConfig(opengl(es1|es2)?) { $$PWD/shaders/visualization.frag \ $$PWD/shaders/visualization.vert } + +# Compressed Texture API +HEADERS += \ + $$PWD/util/qsgtexturereader_p.h + +SOURCES += \ + $$PWD/util/qsgtexturereader.cpp + +qtConfig(opengl(es1|es2)?) { + HEADERS += \ + $$PWD/compressedtexture/qsgcompressedatlastexture_p.h \ + $$PWD/compressedtexture/qsgcompressedtexture_p.h + + SOURCES += \ + $$PWD/compressedtexture/qsgcompressedatlastexture.cpp \ + $$PWD/compressedtexture/qsgcompressedtexture.cpp +} diff --git a/src/quick/scenegraph/util/qsgareaallocator.cpp b/src/quick/scenegraph/util/qsgareaallocator.cpp index 67a9fa285a..9a8c8e333b 100644 --- a/src/quick/scenegraph/util/qsgareaallocator.cpp +++ b/src/quick/scenegraph/util/qsgareaallocator.cpp @@ -42,6 +42,9 @@ #include <QtCore/qglobal.h> #include <QtCore/qrect.h> #include <QtCore/qpoint.h> +#include <QtCore/qdatastream.h> +#include <QtCore/qstack.h> +#include <QtCore/qendian.h> QT_BEGIN_NAMESPACE @@ -72,8 +75,8 @@ struct QSGAreaAllocatorNode QSGAreaAllocatorNode::QSGAreaAllocatorNode(QSGAreaAllocatorNode *parent) : parent(parent) - , left(0) - , right(0) + , left(nullptr) + , right(nullptr) , isOccupied(false) { } @@ -86,14 +89,14 @@ QSGAreaAllocatorNode::~QSGAreaAllocatorNode() bool QSGAreaAllocatorNode::isLeaf() { - Q_ASSERT((left != 0) == (right != 0)); + Q_ASSERT((left != nullptr) == (right != nullptr)); return !left; } QSGAreaAllocator::QSGAreaAllocator(const QSize &size) : m_size(size) { - m_root = new QSGAreaAllocatorNode(0); + m_root = new QSGAreaAllocatorNode(nullptr); } QSGAreaAllocator::~QSGAreaAllocator() @@ -179,13 +182,13 @@ bool QSGAreaAllocator::deallocateInNode(const QPoint &pos, QSGAreaAllocatorNode void QSGAreaAllocator::mergeNodeWithNeighbors(QSGAreaAllocatorNode *node) { bool done = false; - QSGAreaAllocatorNode *parent = 0; - QSGAreaAllocatorNode *current = 0; + QSGAreaAllocatorNode *parent = nullptr; + QSGAreaAllocatorNode *current = nullptr; QSGAreaAllocatorNode *sibling; while (!done) { Q_ASSERT(node->isLeaf()); Q_ASSERT(!node->isOccupied); - if (node->parent == 0) + if (node->parent == nullptr) return; // No neighbours. SplitType splitType = SplitType(node->parent->splitType); @@ -238,7 +241,7 @@ void QSGAreaAllocator::mergeNodeWithNeighbors(QSGAreaAllocatorNode *node) } sibling->parent = parent->parent; *nodeRef = sibling; - parent->left = parent->right = 0; + parent->left = parent->right = nullptr; delete parent; delete neighbor; done = false; @@ -276,7 +279,7 @@ void QSGAreaAllocator::mergeNodeWithNeighbors(QSGAreaAllocatorNode *node) } sibling->parent = parent->parent; *nodeRef = sibling; - parent->left = parent->right = 0; + parent->left = parent->right = nullptr; delete parent; delete neighbor; done = false; @@ -285,4 +288,145 @@ void QSGAreaAllocator::mergeNodeWithNeighbors(QSGAreaAllocatorNode *node) } // end while(!done) } +namespace { + struct AreaAllocatorTable + { + enum TableSize { + HeaderSize = 10, + NodeSize = 9 + }; + + enum Offset { + // Header + majorVersion = 0, + minorVersion = 1, + width = 2, + height = 6, + + // Node + split = 0, + splitType = 4, + flags = 8 + }; + + enum Flags { + IsOccupied = 1, + HasLeft = 2, + HasRight = 4 + }; + + template <typename T> + static inline T fetch(const char *data, Offset offset) + { + return qFromBigEndian<T>(data + int(offset)); + } + + template <typename T> + static inline void put(char *data, Offset offset, T value) + { + qToBigEndian(value, data + int(offset)); + } + }; +} + +QByteArray QSGAreaAllocator::serialize() +{ + QVarLengthArray<QSGAreaAllocatorNode *> nodesToProcess; + + QStack<QSGAreaAllocatorNode *> nodes; + nodes.push(m_root); + while (!nodes.isEmpty()) { + QSGAreaAllocatorNode *node = nodes.pop(); + + nodesToProcess.append(node); + if (node->left != nullptr) + nodes.push(node->left); + if (node->right != nullptr) + nodes.push(node->right); + } + + QByteArray ret; + ret.resize(AreaAllocatorTable::HeaderSize + AreaAllocatorTable::NodeSize * nodesToProcess.size()); + + char *data = ret.data(); + AreaAllocatorTable::put(data, AreaAllocatorTable::majorVersion, quint8(5)); + AreaAllocatorTable::put(data, AreaAllocatorTable::minorVersion, quint8(12)); + AreaAllocatorTable::put(data, AreaAllocatorTable::width, quint32(m_size.width())); + AreaAllocatorTable::put(data, AreaAllocatorTable::height, quint32(m_size.height())); + + data += AreaAllocatorTable::HeaderSize; + for (QSGAreaAllocatorNode *node : nodesToProcess) { + AreaAllocatorTable::put(data, AreaAllocatorTable::split, qint32(node->split)); + AreaAllocatorTable::put(data, AreaAllocatorTable::splitType, quint32(node->splitType)); + + quint8 flags = + (node->isOccupied ? AreaAllocatorTable::IsOccupied : 0) + | (node->left != nullptr ? AreaAllocatorTable::HasLeft : 0) + | (node->right != nullptr ? AreaAllocatorTable::HasRight : 0); + AreaAllocatorTable::put(data, AreaAllocatorTable::flags, flags); + data += AreaAllocatorTable::NodeSize; + } + + return ret; +} + +const char *QSGAreaAllocator::deserialize(const char *data, int size) +{ + if (uint(size) < AreaAllocatorTable::HeaderSize) { + qWarning("QSGAreaAllocator::deserialize: Data not long enough to fit header"); + return nullptr; + } + + const char *end = data + size; + + quint8 majorVersion = AreaAllocatorTable::fetch<quint8>(data, AreaAllocatorTable::majorVersion); + quint8 minorVersion = AreaAllocatorTable::fetch<quint8>(data, AreaAllocatorTable::minorVersion); + if (majorVersion != 5 || minorVersion != 12) { + qWarning("Unrecognized version %d.%d of QSGAreaAllocator", + majorVersion, + minorVersion); + return nullptr; + } + + m_size = QSize(AreaAllocatorTable::fetch<quint32>(data, AreaAllocatorTable::width), + AreaAllocatorTable::fetch<quint32>(data, AreaAllocatorTable::height)); + + Q_ASSERT(m_root != nullptr); + Q_ASSERT(m_root->left == nullptr); + Q_ASSERT(m_root->right == nullptr); + + QStack<QSGAreaAllocatorNode *> nodes; + nodes.push(m_root); + + data += AreaAllocatorTable::HeaderSize; + while (!nodes.isEmpty()) { + if (data + AreaAllocatorTable::NodeSize > end) { + qWarning("QSGAreaAllocator::deseriable: Data not long enough for nodes"); + return nullptr; + } + + QSGAreaAllocatorNode *node = nodes.pop(); + + node->split = AreaAllocatorTable::fetch<qint32>(data, AreaAllocatorTable::split); + node->splitType = SplitType(AreaAllocatorTable::fetch<quint32>(data, AreaAllocatorTable::splitType)); + + quint8 flags = AreaAllocatorTable::fetch<quint8>(data, AreaAllocatorTable::flags); + node->isOccupied = flags & AreaAllocatorTable::IsOccupied; + + if (flags & AreaAllocatorTable::HasLeft) { + node->left = new QSGAreaAllocatorNode(node); + nodes.push(node->left); + } + + if (flags & AreaAllocatorTable::HasRight) { + node->right = new QSGAreaAllocatorNode(node); + nodes.push(node->right); + } + + data += AreaAllocatorTable::NodeSize; + } + + return data; +} + QT_END_NAMESPACE diff --git a/src/quick/scenegraph/util/qsgareaallocator_p.h b/src/quick/scenegraph/util/qsgareaallocator_p.h index aa40ff0a6e..300a8128c0 100644 --- a/src/quick/scenegraph/util/qsgareaallocator_p.h +++ b/src/quick/scenegraph/util/qsgareaallocator_p.h @@ -67,8 +67,12 @@ public: QRect allocate(const QSize &size); bool deallocate(const QRect &rect); - bool isEmpty() const { return m_root == 0; } + bool isEmpty() const { return m_root == nullptr; } QSize size() const { return m_size; } + + QByteArray serialize(); + const char *deserialize(const char *data, int size); + private: bool allocateInNode(const QSize &size, QPoint &result, const QRect ¤tRect, QSGAreaAllocatorNode *node); bool deallocateInNode(const QPoint &pos, QSGAreaAllocatorNode *node); diff --git a/src/quick/scenegraph/util/qsgatlastexture.cpp b/src/quick/scenegraph/util/qsgatlastexture.cpp index 22f0b13f46..921ed0c1fc 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/QOpenGLTexture> #include <QtGui/QOpenGLFunctions> #include <QtGui/QGuiApplication> #include <QtGui/QScreen> @@ -51,7 +52,10 @@ #include <QtGui/QWindow> #include <QtGui/qpa/qplatformnativeinterface.h> +#include <private/qqmlglobal_p.h> #include <private/qsgtexture_p.h> +#include <private/qsgcompressedtexture_p.h> +#include <private/qsgcompressedatlastexture_p.h> #include <private/qquickprofiler_p.h> @@ -65,11 +69,13 @@ int qt_sg_envInt(const char *name, int defaultValue); static QElapsedTimer qsg_renderer_timer; +DEFINE_BOOL_CONFIG_OPTION(qsgEnableCompressedAtlas, QSG_ENABLE_COMPRESSED_ATLAS) + namespace QSGAtlasTexture { Manager::Manager() - : m_atlas(0) + : m_atlas(nullptr) { QOpenGLContext *gl = QOpenGLContext::currentContext(); Q_ASSERT(gl); @@ -99,7 +105,8 @@ Manager::Manager() Manager::~Manager() { - Q_ASSERT(m_atlas == 0); + Q_ASSERT(m_atlas == nullptr); + Q_ASSERT(m_atlases.isEmpty()); } void Manager::invalidate() @@ -107,13 +114,21 @@ void Manager::invalidate() if (m_atlas) { m_atlas->invalidate(); m_atlas->deleteLater(); - m_atlas = 0; + m_atlas = nullptr; + } + + QHash<unsigned int, QSGCompressedAtlasTexture::Atlas*>::iterator i = m_atlases.begin(); + while (i != m_atlases.end()) { + i.value()->invalidate(); + i.value()->deleteLater(); + ++i; } + m_atlases.clear(); } QSGTexture *Manager::create(const QImage &image, bool hasAlphaChannel) { - Texture *t = 0; + Texture *t = nullptr; if (image.width() < m_atlas_size_limit && image.height() < m_atlas_size_limit) { if (!m_atlas) m_atlas = new Atlas(m_atlas_size); @@ -125,13 +140,147 @@ QSGTexture *Manager::create(const QImage &image, bool hasAlphaChannel) return t; } -Atlas::Atlas(const QSize &size) +QSGTexture *Manager::create(const QSGCompressedTextureFactory *factory) +{ + QSGTexture *t = nullptr; + if (!qsgEnableCompressedAtlas() || !factory->m_textureData.isValid()) + return t; + + // TODO: further abstract the atlas and remove this restriction + unsigned int format = factory->m_textureData.glInternalFormat(); + switch (format) { + case QOpenGLTexture::RGB8_ETC1: + case QOpenGLTexture::RGB8_ETC2: + case QOpenGLTexture::RGBA8_ETC2_EAC: + case QOpenGLTexture::RGB8_PunchThrough_Alpha1_ETC2: + break; + default: + return t; + } + + QSize size = factory->m_textureData.size(); + if (size.width() < m_atlas_size_limit && size.height() < m_atlas_size_limit) { + QHash<unsigned int, QSGCompressedAtlasTexture::Atlas*>::iterator i = m_atlases.find(format); + if (i == m_atlases.end()) + i = m_atlases.insert(format, new QSGCompressedAtlasTexture::Atlas(m_atlas_size, format)); + // must be multiple of 4 + QSize paddedSize(((size.width() + 3) / 4) * 4, ((size.height() + 3) / 4) * 4); + QByteArray data = factory->m_textureData.data(); + t = i.value()->create(data, factory->m_textureData.dataLength(), factory->m_textureData.dataOffset(), size, paddedSize); + } + return t; +} + +AtlasBase::AtlasBase(const QSize &size) : m_allocator(size) , m_texture_id(0) , m_size(size) - , m_atlas_transient_image_threshold(0) , m_allocated(false) { +} + +AtlasBase::~AtlasBase() +{ + Q_ASSERT(!m_texture_id); +} + +void AtlasBase::invalidate() +{ + if (m_texture_id && QOpenGLContext::currentContext()) + QOpenGLContext::currentContext()->functions()->glDeleteTextures(1, &m_texture_id); + m_texture_id = 0; +} + +int AtlasBase::textureId() const +{ + if (!m_texture_id) { + Q_ASSERT(QOpenGLContext::currentContext()); + QOpenGLContext::currentContext()->functions()->glGenTextures(1, &const_cast<AtlasBase *>(this)->m_texture_id); + } + + return m_texture_id; +} + +void AtlasBase::bind(QSGTexture::Filtering filtering) +{ + QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions(); + if (!m_allocated) { + m_allocated = true; + + while (funcs->glGetError() != GL_NO_ERROR) ; + + funcs->glGenTextures(1, &m_texture_id); + funcs->glBindTexture(GL_TEXTURE_2D, m_texture_id); + funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); +#if !defined(QT_OPENGL_ES_2) + if (!QOpenGLContext::currentContext()->isOpenGLES()) + funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); +#endif + generateTexture(); + + GLenum errorCode = funcs->glGetError(); + if (errorCode == GL_OUT_OF_MEMORY) { + qDebug("QSGTextureAtlas: texture atlas allocation failed, out of memory"); + funcs->glDeleteTextures(1, &m_texture_id); + m_texture_id = 0; + } else if (errorCode != GL_NO_ERROR) { + qDebug("QSGTextureAtlas: texture atlas allocation failed, code=%x", errorCode); + funcs->glDeleteTextures(1, &m_texture_id); + m_texture_id = 0; + } + } else { + funcs->glBindTexture(GL_TEXTURE_2D, m_texture_id); + } + + if (m_texture_id == 0) + return; + + // Upload all pending images.. + for (int i=0; i<m_pending_uploads.size(); ++i) { + + bool profileFrames = QSG_LOG_TIME_TEXTURE().isDebugEnabled(); + if (profileFrames) + qsg_renderer_timer.start(); + + Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphTexturePrepare); + + // Skip bind, convert, swizzle; they're irrelevant + Q_QUICK_SG_PROFILE_SKIP(QQuickProfiler::SceneGraphTexturePrepare, + QQuickProfiler::SceneGraphTexturePrepareStart, 3); + + uploadPendingTexture(i); + + Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphTexturePrepare, + QQuickProfiler::SceneGraphTexturePrepareUpload); + + // Skip mipmap; unused + Q_QUICK_SG_PROFILE_SKIP(QQuickProfiler::SceneGraphTexturePrepare, + QQuickProfiler::SceneGraphTexturePrepareUpload, 1); + Q_QUICK_SG_PROFILE_REPORT(QQuickProfiler::SceneGraphTexturePrepare, + QQuickProfiler::SceneGraphTexturePrepareMipmap); + } + + GLenum f = filtering == QSGTexture::Nearest ? GL_NEAREST : GL_LINEAR; + funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, f); + funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, f); + + m_pending_uploads.clear(); +} + +void AtlasBase::remove(TextureBase *t) +{ + QRect atlasRect = t->atlasSubRect(); + m_allocator.deallocate(atlasRect); + m_pending_uploads.removeOne(t); +} + +Atlas::Atlas(const QSize &size) + : AtlasBase(size) + , m_atlas_transient_image_threshold(0) +{ m_internalFormat = GL_RGBA; m_externalFormat = GL_BGRA; @@ -188,14 +337,6 @@ Atlas::Atlas(const QSize &size) Atlas::~Atlas() { - Q_ASSERT(!m_texture_id); -} - -void Atlas::invalidate() -{ - if (m_texture_id && QOpenGLContext::currentContext()) - QOpenGLContext::currentContext()->functions()->glDeleteTextures(1, &m_texture_id); - m_texture_id = 0; } Texture *Atlas::create(const QImage &image) @@ -207,31 +348,7 @@ Texture *Atlas::create(const QImage &image) m_pending_uploads << t; return t; } - return 0; -} - - -int Atlas::textureId() const -{ - if (!m_texture_id) { - Q_ASSERT(QOpenGLContext::currentContext()); - QOpenGLContext::currentContext()->functions()->glGenTextures(1, &const_cast<Atlas *>(this)->m_texture_id); - } - - return m_texture_id; -} - -static void swizzleBGRAToRGBA(QImage *image) -{ - const int width = image->width(); - const int height = image->height(); - uint *p = (uint *) image->bits(); - int stride = image->bytesPerLine() / 4; - for (int i = 0; i < height; ++i) { - for (int x = 0; x < width; ++x) - p[x] = ((p[x] << 16) & 0xff0000) | ((p[x] >> 16) & 0xff) | (p[x] & 0xff00ff00); - p += stride; - } + return nullptr; } void Atlas::upload(Texture *texture) @@ -265,7 +382,7 @@ void Atlas::upload(Texture *texture) } if (m_externalFormat == GL_RGBA) - swizzleBGRAToRGBA(&tmp); + tmp = std::move(tmp).convertToFormat(QImage::Format_RGBA8888_Premultiplied); QOpenGLContext::currentContext()->functions()->glTexSubImage2D(GL_TEXTURE_2D, 0, r.x(), r.y(), r.width(), r.height(), m_externalFormat, GL_UNSIGNED_BYTE, tmp.constBits()); @@ -334,126 +451,74 @@ void Atlas::uploadBgra(Texture *texture) } } -void Atlas::bind(QSGTexture::Filtering filtering) +void Atlas::generateTexture() { QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions(); - if (!m_allocated) { - m_allocated = true; - - while (funcs->glGetError() != GL_NO_ERROR) ; - - funcs->glGenTextures(1, &m_texture_id); - funcs->glBindTexture(GL_TEXTURE_2D, m_texture_id); - funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); -#if !defined(QT_OPENGL_ES_2) - if (!QOpenGLContext::currentContext()->isOpenGLES()) - funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); -#endif - funcs->glTexImage2D(GL_TEXTURE_2D, 0, m_internalFormat, m_size.width(), m_size.height(), 0, m_externalFormat, GL_UNSIGNED_BYTE, 0); + funcs->glTexImage2D(GL_TEXTURE_2D, 0, m_internalFormat, m_size.width(), m_size.height(), 0, m_externalFormat, GL_UNSIGNED_BYTE, nullptr); #if 0 - QImage pink(m_size.width(), m_size.height(), QImage::Format_ARGB32_Premultiplied); - pink.fill(0); - QPainter p(&pink); - QLinearGradient redGrad(0, 0, m_size.width(), 0); - redGrad.setColorAt(0, Qt::black); - redGrad.setColorAt(1, Qt::red); - p.fillRect(0, 0, m_size.width(), m_size.height(), redGrad); - p.setCompositionMode(QPainter::CompositionMode_Plus); - QLinearGradient blueGrad(0, 0, 0, m_size.height()); - blueGrad.setColorAt(0, Qt::black); - blueGrad.setColorAt(1, Qt::blue); - p.fillRect(0, 0, m_size.width(), m_size.height(), blueGrad); - p.end(); - - funcs->glTexImage2D(GL_TEXTURE_2D, 0, m_internalFormat, m_size.width(), m_size.height(), 0, m_externalFormat, GL_UNSIGNED_BYTE, pink.constBits()); + QImage pink(m_size.width(), m_size.height(), QImage::Format_ARGB32_Premultiplied); + pink.fill(0); + QPainter p(&pink); + QLinearGradient redGrad(0, 0, m_size.width(), 0); + redGrad.setColorAt(0, Qt::black); + redGrad.setColorAt(1, Qt::red); + p.fillRect(0, 0, m_size.width(), m_size.height(), redGrad); + p.setCompositionMode(QPainter::CompositionMode_Plus); + QLinearGradient blueGrad(0, 0, 0, m_size.height()); + blueGrad.setColorAt(0, Qt::black); + blueGrad.setColorAt(1, Qt::blue); + p.fillRect(0, 0, m_size.width(), m_size.height(), blueGrad); + p.end(); + + funcs->glTexImage2D(GL_TEXTURE_2D, 0, m_internalFormat, m_size.width(), m_size.height(), 0, m_externalFormat, GL_UNSIGNED_BYTE, pink.constBits()); #endif +} - GLenum errorCode = funcs->glGetError(); - if (errorCode == GL_OUT_OF_MEMORY) { - qDebug("QSGTextureAtlas: texture atlas allocation failed, out of memory"); - funcs->glDeleteTextures(1, &m_texture_id); - m_texture_id = 0; - } else if (errorCode != GL_NO_ERROR) { - qDebug("QSGTextureAtlas: texture atlas allocation failed, code=%x", errorCode); - funcs->glDeleteTextures(1, &m_texture_id); - m_texture_id = 0; - } +void Atlas::uploadPendingTexture(int i) +{ + Texture *t = static_cast<Texture*>(m_pending_uploads.at(i)); + if (m_externalFormat == GL_BGRA && + !m_use_bgra_fallback) { + uploadBgra(t); } else { - funcs->glBindTexture(GL_TEXTURE_2D, m_texture_id); - } - - if (m_texture_id == 0) - return; - - // Upload all pending images.. - for (int i=0; i<m_pending_uploads.size(); ++i) { - - bool profileFrames = QSG_LOG_TIME_TEXTURE().isDebugEnabled(); - if (profileFrames) - qsg_renderer_timer.start(); - - Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphTexturePrepare); - - // Skip bind, convert, swizzle; they're irrelevant - Q_QUICK_SG_PROFILE_SKIP(QQuickProfiler::SceneGraphTexturePrepare, - QQuickProfiler::SceneGraphTexturePrepareStart, 3); - - Texture *t = m_pending_uploads.at(i); - if (m_externalFormat == GL_BGRA && - !m_use_bgra_fallback) { - uploadBgra(t); - } else { - upload(t); - } - const QSize textureSize = t->textureSize(); - if (textureSize.width() > m_atlas_transient_image_threshold || - textureSize.height() > m_atlas_transient_image_threshold) - t->releaseImage(); - - qCDebug(QSG_LOG_TIME_TEXTURE).nospace() << "atlastexture uploaded in: " << qsg_renderer_timer.elapsed() - << "ms (" << t->textureSize().width() << "x" - << t->textureSize().height() << ")"; - - Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphTexturePrepare, - QQuickProfiler::SceneGraphTexturePrepareUpload); - - // Skip mipmap; unused - Q_QUICK_SG_PROFILE_SKIP(QQuickProfiler::SceneGraphTexturePrepare, - QQuickProfiler::SceneGraphTexturePrepareUpload, 1); - Q_QUICK_SG_PROFILE_REPORT(QQuickProfiler::SceneGraphTexturePrepare, - QQuickProfiler::SceneGraphTexturePrepareMipmap); + upload(t); } - - GLenum f = filtering == QSGTexture::Nearest ? GL_NEAREST : GL_LINEAR; - funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, f); - funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, f); - - m_pending_uploads.clear(); + const QSize textureSize = t->textureSize(); + if (textureSize.width() > m_atlas_transient_image_threshold || + textureSize.height() > m_atlas_transient_image_threshold) + t->releaseImage(); + + qCDebug(QSG_LOG_TIME_TEXTURE, "atlastexture uploaded in: %lldms (%dx%d)", + qsg_renderer_timer.elapsed(), + t->textureSize().width(), + t->textureSize().height()); } -void Atlas::remove(Texture *t) +TextureBase::TextureBase(AtlasBase *atlas, const QRect &textureRect) + : m_allocated_rect(textureRect) + , m_atlas(atlas) { - QRect atlasRect = t->atlasSubRect(); - m_allocator.deallocate(atlasRect); - m_pending_uploads.removeOne(t); } +TextureBase::~TextureBase() +{ + m_atlas->remove(this); +} +void TextureBase::bind() +{ + m_atlas->bind(filtering()); +} Texture::Texture(Atlas *atlas, const QRect &textureRect, const QImage &image) - : QSGTexture() - , m_allocated_rect(textureRect) + : TextureBase(atlas, textureRect) , m_image(image) - , m_atlas(atlas) - , m_nonatlas_texture(0) + , m_nonatlas_texture(nullptr) , m_has_alpha(image.hasAlphaChannel()) { - float w = atlas->size().width(); - float h = atlas->size().height(); + qreal w = atlas->size().width(); + qreal h = atlas->size().height(); QRect nopad = atlasSubRectWithoutPadding(); m_texture_coords_rect = QRectF(nopad.x() / w, nopad.y() / h, @@ -463,16 +528,10 @@ Texture::Texture(Atlas *atlas, const QRect &textureRect, const QImage &image) Texture::~Texture() { - m_atlas->remove(this); if (m_nonatlas_texture) delete m_nonatlas_texture; } -void Texture::bind() -{ - m_atlas->bind(filtering()); -} - QSGTexture *Texture::removedFromAtlas() const { if (m_nonatlas_texture) { @@ -508,7 +567,7 @@ QSGTexture *Texture::removedFromAtlas() const QRect r = atlasSubRectWithoutPadding(); // and copy atlas into our texture. while (f->glGetError() != GL_NO_ERROR) ; - f->glCopyTexImage2D(GL_TEXTURE_2D, 0, m_atlas->internalFormat(), r.x(), r.y(), r.width(), r.height(), 0); + f->glCopyTexImage2D(GL_TEXTURE_2D, 0, static_cast<Atlas*>(m_atlas)->internalFormat(), r.x(), r.y(), r.width(), r.height(), 0); // BGRA may have been rejected by some GLES implementations if (f->glGetError() != GL_NO_ERROR) f->glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, r.x(), r.y(), r.width(), r.height(), 0); diff --git a/src/quick/scenegraph/util/qsgatlastexture_p.h b/src/quick/scenegraph/util/qsgatlastexture_p.h index 3dee539547..14dc8f7958 100644 --- a/src/quick/scenegraph/util/qsgatlastexture_p.h +++ b/src/quick/scenegraph/util/qsgatlastexture_p.h @@ -61,10 +61,16 @@ QT_BEGIN_NAMESPACE +namespace QSGCompressedAtlasTexture { + class Atlas; +} +class QSGCompressedTextureFactory; + namespace QSGAtlasTexture { class Texture; +class TextureBase; class Atlas; class Manager : public QObject @@ -76,93 +82,121 @@ public: ~Manager(); QSGTexture *create(const QImage &image, bool hasAlphaChannel); + QSGTexture *create(const QSGCompressedTextureFactory *factory); void invalidate(); private: Atlas *m_atlas; + // set of atlases for different compressed formats + QHash<unsigned int, QSGCompressedAtlasTexture::Atlas*> m_atlases; QSize m_atlas_size; int m_atlas_size_limit; }; -class Atlas : public QObject +class AtlasBase : public QObject { + Q_OBJECT public: - Atlas(const QSize &size); - ~Atlas(); + AtlasBase(const QSize &size); + ~AtlasBase(); void invalidate(); int textureId() const; void bind(QSGTexture::Filtering filtering); + void remove(TextureBase *t); + + QSize size() const { return m_size; } + +protected: + virtual void generateTexture() = 0; + virtual void uploadPendingTexture(int i) = 0; + +protected: + QSGAreaAllocator m_allocator; + unsigned int m_texture_id; + QSize m_size; + QList<TextureBase *> m_pending_uploads; + +private: + bool m_allocated; +}; + +class Atlas : public AtlasBase +{ +public: + Atlas(const QSize &size); + ~Atlas(); + + void generateTexture() override; + void uploadPendingTexture(int i) override; + void upload(Texture *texture); void uploadBgra(Texture *texture); Texture *create(const QImage &image); - void remove(Texture *t); - - QSize size() const { return m_size; } uint internalFormat() const { return m_internalFormat; } uint externalFormat() const { return m_externalFormat; } private: - QSGAreaAllocator m_allocator; - unsigned int m_texture_id; - QSize m_size; - QList<Texture *> m_pending_uploads; - uint m_internalFormat; uint m_externalFormat; int m_atlas_transient_image_threshold; - uint m_allocated : 1; uint m_use_bgra_fallback: 1; - uint m_debug_overlay : 1; }; -class Texture : public QSGTexture +class TextureBase : public QSGTexture +{ + Q_OBJECT +public: + TextureBase(AtlasBase *atlas, const QRect &textureRect); + ~TextureBase(); + + int textureId() const override { return m_atlas->textureId(); } + bool isAtlasTexture() const override { return true; } + + QRect atlasSubRect() const { return m_allocated_rect; } + + void bind() override; + +protected: + QRect m_allocated_rect; + AtlasBase *m_atlas; +}; + +class Texture : public TextureBase { Q_OBJECT public: Texture(Atlas *atlas, const QRect &textureRect, const QImage &image); ~Texture(); - int textureId() const override { return m_atlas->textureId(); } QSize textureSize() const override { return atlasSubRectWithoutPadding().size(); } void setHasAlphaChannel(bool alpha) { m_has_alpha = alpha; } bool hasAlphaChannel() const override { return m_has_alpha; } bool hasMipmaps() const override { return false; } - bool isAtlasTexture() const override { return true; } QRectF normalizedTextureSubRect() const override { return m_texture_coords_rect; } QRect atlasSubRect() const { return m_allocated_rect; } QRect atlasSubRectWithoutPadding() const { return m_allocated_rect.adjusted(1, 1, -1, -1); } - bool isTexture() const { return true; } - QSGTexture *removedFromAtlas() const override; void releaseImage() { m_image = QImage(); } const QImage &image() const { return m_image; } - void bind() override; - private: - QRect m_allocated_rect; QRectF m_texture_coords_rect; - QImage m_image; - - Atlas *m_atlas; - mutable QSGPlainTexture *m_nonatlas_texture; - - uint m_has_alpha : 1; + bool m_has_alpha; }; } diff --git a/src/quick/scenegraph/util/qsgdefaultpainternode.cpp b/src/quick/scenegraph/util/qsgdefaultpainternode.cpp index 9ffd1b4b08..981ea089be 100644 --- a/src/quick/scenegraph/util/qsgdefaultpainternode.cpp +++ b/src/quick/scenegraph/util/qsgdefaultpainternode.cpp @@ -78,11 +78,11 @@ QSGDefaultPainterNode::QSGDefaultPainterNode(QQuickPaintedItem *item) , m_preferredRenderTarget(QQuickPaintedItem::Image) , m_actualRenderTarget(QQuickPaintedItem::Image) , m_item(item) - , m_fbo(0) - , m_multisampledFbo(0) + , m_fbo(nullptr) + , m_multisampledFbo(nullptr) , m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4) - , m_texture(0) - , m_gl_device(0) + , m_texture(nullptr) + , m_gl_device(nullptr) , m_fillColor(Qt::transparent) , m_contentsScale(1.0) , m_dirtyContents(false) @@ -260,8 +260,8 @@ void QSGDefaultPainterNode::updateRenderTarget() delete m_fbo; delete m_multisampledFbo; delete m_gl_device; - m_fbo = m_multisampledFbo = 0; - m_gl_device = 0; + m_fbo = m_multisampledFbo = nullptr; + m_gl_device = nullptr; } if (m_actualRenderTarget == QQuickPaintedItem::FramebufferObject || @@ -275,7 +275,7 @@ void QSGDefaultPainterNode::updateRenderTarget() delete m_fbo; delete m_multisampledFbo; - m_fbo = m_multisampledFbo = 0; + m_fbo = m_multisampledFbo = nullptr; if (m_gl_device) m_gl_device->setSize(m_fboSize); diff --git a/src/quick/scenegraph/util/qsgdepthstencilbuffer.cpp b/src/quick/scenegraph/util/qsgdepthstencilbuffer.cpp index ba0207aca8..94912778f8 100644 --- a/src/quick/scenegraph/util/qsgdepthstencilbuffer.cpp +++ b/src/quick/scenegraph/util/qsgdepthstencilbuffer.cpp @@ -43,7 +43,7 @@ QT_BEGIN_NAMESPACE QSGDepthStencilBuffer::QSGDepthStencilBuffer(QOpenGLContext *context, const Format &format) : m_functions(context) - , m_manager(0) + , m_manager(nullptr) , m_format(format) , m_depthBuffer(0) , m_stencilBuffer(0) @@ -57,20 +57,34 @@ QSGDepthStencilBuffer::~QSGDepthStencilBuffer() m_manager->m_buffers.remove(m_format); } +#ifndef GL_DEPTH_STENCIL_ATTACHMENT +#define GL_DEPTH_STENCIL_ATTACHMENT 0x821A +#endif + void QSGDepthStencilBuffer::attach() { +#ifndef Q_OS_WASM m_functions.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_depthBuffer); m_functions.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, m_stencilBuffer); +#else + m_functions.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, + GL_RENDERBUFFER, m_stencilBuffer); +#endif } void QSGDepthStencilBuffer::detach() { +#ifndef Q_OS_WASM m_functions.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0); m_functions.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 0); +#else + m_functions.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, + GL_RENDERBUFFER, 0); +#endif } #ifndef GL_DEPTH24_STENCIL8_OES @@ -81,12 +95,17 @@ void QSGDepthStencilBuffer::detach() #define GL_DEPTH_COMPONENT24_OES 0x81A6 #endif +#ifndef GL_DEPTH_STENCIL +#define GL_DEPTH_STENCIL 0x84F9 +#endif + QSGDefaultDepthStencilBuffer::QSGDefaultDepthStencilBuffer(QOpenGLContext *context, const Format &format) : QSGDepthStencilBuffer(context, format) { const GLsizei width = format.size.width(); const GLsizei height = format.size.height(); +#ifndef Q_OS_WASM if (format.attachments == (DepthAttachment | StencilAttachment) && m_functions.hasOpenGLExtension(QOpenGLExtensions::PackedDepthStencil)) { @@ -138,6 +157,12 @@ QSGDefaultDepthStencilBuffer::QSGDefaultDepthStencilBuffer(QOpenGLContext *conte m_functions.glRenderbufferStorage(GL_RENDERBUFFER, internalFormat, width, height); } } +#else + m_functions.glGenRenderbuffers(1, &m_depthBuffer); + m_functions.glBindRenderbuffer(GL_RENDERBUFFER, m_depthBuffer); + m_functions.glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_STENCIL, width, height); + m_stencilBuffer = m_depthBuffer; +#endif } QSGDefaultDepthStencilBuffer::~QSGDefaultDepthStencilBuffer() @@ -160,7 +185,7 @@ QSGDepthStencilBufferManager::~QSGDepthStencilBufferManager() for (Hash::const_iterator it = m_buffers.constBegin(), cend = m_buffers.constEnd(); it != cend; ++it) { QSGDepthStencilBuffer *buffer = it.value().data(); buffer->free(); - buffer->m_manager = 0; + buffer->m_manager = nullptr; } } @@ -174,7 +199,7 @@ QSharedPointer<QSGDepthStencilBuffer> QSGDepthStencilBufferManager::bufferForFor void QSGDepthStencilBufferManager::insertBuffer(const QSharedPointer<QSGDepthStencilBuffer> &buffer) { - Q_ASSERT(buffer->m_manager == 0); + Q_ASSERT(buffer->m_manager == nullptr); Q_ASSERT(!m_buffers.contains(buffer->m_format)); buffer->m_manager = this; m_buffers.insert(buffer->m_format, buffer.toWeakRef()); diff --git a/src/quick/scenegraph/util/qsgengine.cpp b/src/quick/scenegraph/util/qsgengine.cpp index dffe199224..91fa46033c 100644 --- a/src/quick/scenegraph/util/qsgengine.cpp +++ b/src/quick/scenegraph/util/qsgengine.cpp @@ -157,7 +157,7 @@ QSGAbstractRenderer *QSGEngine::createRenderer() const { Q_D(const QSGEngine); if (!d->sgRenderContext->isValid()) - return 0; + return nullptr; QSGRenderer *renderer = d->sgRenderContext->createRenderer(); renderer->setCustomRenderMode(qgetenv("QSG_VISUALIZE")); @@ -178,7 +178,7 @@ QSGTexture *QSGEngine::createTextureFromImage(const QImage &image, CreateTexture { Q_D(const QSGEngine); if (!d->sgRenderContext->isValid()) - return 0; + return nullptr; uint flags = 0; if (options & TextureCanUseAtlas) flags |= QSGRenderContext::CreateTexture_Atlas; if (!(options & TextureIsOpaque)) flags |= QSGRenderContext::CreateTexture_Alpha; @@ -206,7 +206,7 @@ QSGTexture *QSGEngine::createTextureFromId(uint id, const QSize &size, CreateTex texture->setTextureSize(size); return texture; } - return 0; + return nullptr; } /*! diff --git a/src/quick/scenegraph/util/qsgengine.h b/src/quick/scenegraph/util/qsgengine.h index 3c8b61852e..e48b7784ae 100644 --- a/src/quick/scenegraph/util/qsgengine.h +++ b/src/quick/scenegraph/util/qsgengine.h @@ -66,9 +66,10 @@ public: TextureIsOpaque = 0x0010 }; Q_DECLARE_FLAGS(CreateTextureOptions, CreateTextureOption) + Q_FLAG(CreateTextureOptions) - explicit QSGEngine(QObject *parent = Q_NULLPTR); - ~QSGEngine(); + explicit QSGEngine(QObject *parent = nullptr); + ~QSGEngine() override; void initialize(QOpenGLContext *context); void invalidate(); diff --git a/src/quick/scenegraph/util/qsgflatcolormaterial.cpp b/src/quick/scenegraph/util/qsgflatcolormaterial.cpp index a0c71b5340..28f6113a60 100644 --- a/src/quick/scenegraph/util/qsgflatcolormaterial.cpp +++ b/src/quick/scenegraph/util/qsgflatcolormaterial.cpp @@ -77,13 +77,13 @@ FlatColorMaterialShader::FlatColorMaterialShader() void FlatColorMaterialShader::updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) { #if QT_CONFIG(opengl) - Q_ASSERT(oldEffect == 0 || newEffect->type() == oldEffect->type()); + Q_ASSERT(oldEffect == nullptr || newEffect->type() == oldEffect->type()); QSGFlatColorMaterial *oldMaterial = static_cast<QSGFlatColorMaterial *>(oldEffect); QSGFlatColorMaterial *newMaterial = static_cast<QSGFlatColorMaterial *>(newEffect); const QColor &c = newMaterial->color(); - if (oldMaterial == 0 || c != oldMaterial->color() || state.isOpacityDirty()) { + if (oldMaterial == nullptr || c != oldMaterial->color() || state.isOpacityDirty()) { float opacity = state.opacity() * c.alphaF(); QVector4D v(c.redF() * opacity, c.greenF() * opacity, @@ -103,7 +103,7 @@ void FlatColorMaterialShader::updateState(const RenderState &state, QSGMaterial char const *const *FlatColorMaterialShader::attributeNames() const { - static char const *const attr[] = { "vCoord", 0 }; + static char const *const attr[] = { "vCoord", nullptr }; return attr; } diff --git a/src/quick/scenegraph/util/qsgimagenode.cpp b/src/quick/scenegraph/util/qsgimagenode.cpp index c03c91d1cb..b154023247 100644 --- a/src/quick/scenegraph/util/qsgimagenode.cpp +++ b/src/quick/scenegraph/util/qsgimagenode.cpp @@ -168,7 +168,7 @@ QT_BEGIN_NAMESPACE */ /*! - \fn QSGImageNode::TextureCoordinatesTransformMode textureCoordinatesTransform() const + \fn QSGImageNode::TextureCoordinatesTransformMode QSGImageNode::textureCoordinatesTransform() const Returns the mode used to generate texture coordinates for this node. */ @@ -187,6 +187,15 @@ QT_BEGIN_NAMESPACE \return \c true if the node takes ownership of the texture; otherwise \c false. */ +/*! + Updates the geometry \a g with the \a texture, the coordinates + in \a rect, and the texture coordinates from \a sourceRect. + + \a g is assumed to be a triangle strip of four vertices of type + QSGGeometry::TexturedPoint2D. + + \a texCoordMode is used for normalizing the \a sourceRect. + */ void QSGImageNode::rebuildGeometry(QSGGeometry *g, QSGTexture *texture, const QRectF &rect, diff --git a/src/quick/scenegraph/util/qsgimagenode.h b/src/quick/scenegraph/util/qsgimagenode.h index 0e053c307f..3b78f78a0e 100644 --- a/src/quick/scenegraph/util/qsgimagenode.h +++ b/src/quick/scenegraph/util/qsgimagenode.h @@ -48,7 +48,7 @@ QT_BEGIN_NAMESPACE class Q_QUICK_EXPORT QSGImageNode : public QSGGeometryNode { public: - virtual ~QSGImageNode() { } + ~QSGImageNode() override = default; virtual void setRect(const QRectF &rect) = 0; inline void setRect(qreal x, qreal y, qreal w, qreal h) { setRect(QRectF(x, y, w, h)); } diff --git a/src/quick/scenegraph/util/qsgninepatchnode.h b/src/quick/scenegraph/util/qsgninepatchnode.h index 8509cbd326..b690a50e9d 100644 --- a/src/quick/scenegraph/util/qsgninepatchnode.h +++ b/src/quick/scenegraph/util/qsgninepatchnode.h @@ -48,7 +48,7 @@ QT_BEGIN_NAMESPACE class Q_QUICK_EXPORT QSGNinePatchNode : public QSGGeometryNode { public: - virtual ~QSGNinePatchNode() { } + ~QSGNinePatchNode() override = default; virtual void setTexture(QSGTexture *texture) = 0; virtual void setBounds(const QRectF &bounds) = 0; diff --git a/src/quick/scenegraph/util/qsgrectanglenode.h b/src/quick/scenegraph/util/qsgrectanglenode.h index 8e0da1d9c7..c435dc790f 100644 --- a/src/quick/scenegraph/util/qsgrectanglenode.h +++ b/src/quick/scenegraph/util/qsgrectanglenode.h @@ -47,7 +47,7 @@ QT_BEGIN_NAMESPACE class Q_QUICK_EXPORT QSGRectangleNode : public QSGGeometryNode { public: - virtual ~QSGRectangleNode() { } + ~QSGRectangleNode() override = default; virtual void setRect(const QRectF &rect) = 0; inline void setRect(qreal x, qreal y, qreal w, qreal h) { setRect(QRectF(x, y, w, h)); } diff --git a/src/quick/scenegraph/util/qsgshadersourcebuilder.cpp b/src/quick/scenegraph/util/qsgshadersourcebuilder.cpp index e134a5d4d3..93fc213f2e 100644 --- a/src/quick/scenegraph/util/qsgshadersourcebuilder.cpp +++ b/src/quick/scenegraph/util/qsgshadersourcebuilder.cpp @@ -262,8 +262,8 @@ void QSGShaderSourceBuilder::addDefinition(const QByteArray &definition) tok.initialize(input); // First find #version, #extension's and "void main() { ... " - const char *versionPos = 0; - const char *extensionPos = 0; + const char *versionPos = nullptr; + const char *extensionPos = nullptr; bool inSingleLineComment = false; bool inMultiLineComment = false; bool foundVersionStart = false; @@ -325,8 +325,8 @@ void QSGShaderSourceBuilder::removeVersion() tok.initialize(input); // First find #version beginning and end (if present) - const char *versionStartPos = 0; - const char *versionEndPos = 0; + const char *versionStartPos = nullptr; + const char *versionEndPos = nullptr; bool inSingleLineComment = false; bool inMultiLineComment = false; bool foundVersionStart = false; @@ -361,7 +361,7 @@ void QSGShaderSourceBuilder::removeVersion() t = tok.next(); } - if (versionStartPos == 0) + if (versionStartPos == nullptr) return; // Construct a new shader string, inserting the definition diff --git a/src/quick/scenegraph/util/qsgsimplematerial.cpp b/src/quick/scenegraph/util/qsgsimplematerial.cpp index f29c58ad9e..376f7dce5c 100644 --- a/src/quick/scenegraph/util/qsgsimplematerial.cpp +++ b/src/quick/scenegraph/util/qsgsimplematerial.cpp @@ -173,17 +173,17 @@ /*! - \fn char const *const *QSGSimpleMaterialShader::attributeNames() const + \fn template <typename State> char const *const *QSGSimpleMaterialShader<State>::attributeNames() const \internal */ /*! - \fn void QSGSimpleMaterialShader::initialize() + \fn template <typename State> void QSGSimpleMaterialShader<State>::initialize() \internal */ /*! - \fn void QSGSimpleMaterialShader::resolveUniforms() + \fn template <typename State> void QSGSimpleMaterialShader<State>::resolveUniforms() Reimplement this function to resolve the location of named uniforms in the shader program. @@ -192,34 +192,34 @@ */ /*! - \fn const char *QSGSimpleMaterialShader::uniformMatrixName() const + \fn template <typename State> const char *QSGSimpleMaterialShader<State>::uniformMatrixName() const Returns the name for the transform matrix uniform of this item. The default value is \c qt_Matrix. */ /*! - \fn const char *QSGSimpleMaterialShader::uniformOpacityName() const + \fn template <typename State> const char *QSGSimpleMaterialShader<State>::uniformOpacityName() const Returns the name for the opacity uniform of this item. The default value is \c qt_Opacity. */ /*! - \fn void QSGSimpleMaterialShader::updateState(const RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) + \fn template <typename State> void QSGSimpleMaterialShader<State>::updateState(const RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) \internal */ /*! - \fn QList<QByteArray> QSGSimpleMaterialShader::attributes() const + \fn template <typename State> QList<QByteArray> QSGSimpleMaterialShader<State>::attributes() const Returns a list of names, declaring the vertex attributes in the vertex shader. */ /*! - \fn void QSGSimpleMaterialShader::updateState(const State *newState, const State *oldState) + \fn template <typename State> void QSGSimpleMaterialShader<State>::updateState(const State *newState, const State *oldState) Called whenever the state of this shader should be updated from \a oldState to \a newState, typical for each new set of diff --git a/src/quick/scenegraph/util/qsgsimplematerial.h b/src/quick/scenegraph/util/qsgsimplematerial.h index b5b8815b4a..79180ca8e2 100644 --- a/src/quick/scenegraph/util/qsgsimplematerial.h +++ b/src/quick/scenegraph/util/qsgsimplematerial.h @@ -138,7 +138,7 @@ template <typename State> class QSGSimpleMaterial : public QSGMaterial { public: -#ifndef qdoc +#ifndef Q_CLANG_QDOC QSGSimpleMaterial(const State &aState, PtrShaderCreateFunc func) : m_state(aState) , m_func(func) @@ -185,7 +185,7 @@ public: QSGSimpleMaterialComparableMaterial(PtrShaderCreateFunc func) : QSGSimpleMaterial<State>(func) {} - int compare(const QSGMaterial *other) const { + int compare(const QSGMaterial *other) const override { return QSGSimpleMaterialComparableMaterial<State>::state()->compare(static_cast<const QSGSimpleMaterialComparableMaterial<State> *>(other)->state()); } }; @@ -207,7 +207,7 @@ Q_INLINE_TEMPLATE void QSGSimpleMaterialShader<State>::updateState(const RenderS Q_UNUSED(state) #endif State *ns = static_cast<QSGSimpleMaterial<State> *>(newMaterial)->state(); - State *old = 0; + State *old = nullptr; if (oldMaterial) old = static_cast<QSGSimpleMaterial<State> *>(oldMaterial)->state(); updateState(ns, old); diff --git a/src/quick/scenegraph/util/qsgsimpletexturenode.cpp b/src/quick/scenegraph/util/qsgsimpletexturenode.cpp index 6ce37de7cb..0c49ca9aa5 100644 --- a/src/quick/scenegraph/util/qsgsimpletexturenode.cpp +++ b/src/quick/scenegraph/util/qsgsimpletexturenode.cpp @@ -47,8 +47,7 @@ class QSGSimpleTextureNodePrivate : public QSGGeometryNodePrivate { public: QSGSimpleTextureNodePrivate() - : QSGGeometryNodePrivate() - , texCoordMode(QSGSimpleTextureNode::NoTransform) + : texCoordMode(QSGSimpleTextureNode::NoTransform) , isAtlasTexture(false) , ownsTexture(false) {} diff --git a/src/quick/scenegraph/util/qsgsimpletexturenode.h b/src/quick/scenegraph/util/qsgsimpletexturenode.h index 09e4277c66..010463d3c6 100644 --- a/src/quick/scenegraph/util/qsgsimpletexturenode.h +++ b/src/quick/scenegraph/util/qsgsimpletexturenode.h @@ -52,7 +52,7 @@ class Q_QUICK_EXPORT QSGSimpleTextureNode : public QSGGeometryNode { public: QSGSimpleTextureNode(); - ~QSGSimpleTextureNode(); + ~QSGSimpleTextureNode() override; void setRect(const QRectF &rect); inline void setRect(qreal x, qreal y, qreal w, qreal h) { setRect(QRectF(x, y, w, h)); } diff --git a/src/quick/scenegraph/util/qsgtexture.cpp b/src/quick/scenegraph/util/qsgtexture.cpp index 4f11d95e70..042eee19f5 100644 --- a/src/quick/scenegraph/util/qsgtexture.cpp +++ b/src/quick/scenegraph/util/qsgtexture.cpp @@ -49,11 +49,12 @@ # include <qopenglfunctions.h> # include <QtGui/qopenglcontext.h> # include <QtGui/qopenglfunctions.h> +# include <QtGui/private/qopengltextureuploader_p.h> # include <private/qsgdefaultrendercontext_p.h> #endif #include <private/qsgmaterialshader_p.h> -#if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID) && !defined(__UCLIBC__) +#if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID) && defined(__GLIBC__) #define CAN_BACKTRACE_EXECINFO #endif @@ -89,7 +90,7 @@ static const bool qsg_leak_check = !qEnvironmentVariableIsEmpty("QML_LEAK_CHECK" QT_BEGIN_NAMESPACE -#if QT_CONFIG(opengl) +#if QT_CONFIG(opengl) && !defined(QT_NO_DEBUG) inline static bool isPowerOfTwo(int x) { // Assumption: x >= 1 @@ -388,7 +389,7 @@ QSGTexture::~QSGTexture() it to a shader that operates on the texture coordinates 0-1 instead of the texture subrect inside the atlas. - If the texture is not part of a texture atlas, this function returns 0. + If the texture is not part of a texture atlas, this function returns \nullptr. Implementations of this function are recommended to return the same instance for multiple calls to limit memory usage. @@ -399,7 +400,7 @@ QSGTexture::~QSGTexture() QSGTexture *QSGTexture::removedFromAtlas() const { Q_ASSERT_X(!isAtlasTexture(), "QSGTexture::removedFromAtlas()", "Called on a non-atlas texture"); - return 0; + return nullptr; } /*! @@ -659,17 +660,6 @@ QSGPlainTexture::~QSGPlainTexture() #endif } -void qsg_swizzleBGRAToRGBA(QImage *image) -{ - const int width = image->width(); - const int height = image->height(); - for (int i = 0; i < height; ++i) { - uint *p = (uint *) image->scanLine(i); - for (int x = 0; x < width; ++x) - p[x] = ((p[x] << 16) & 0xff0000) | ((p[x] >> 16) & 0xff) | (p[x] & 0xff00ff00); - } -} - void QSGPlainTexture::setImage(const QImage &image) { m_image = image; @@ -766,9 +756,7 @@ void QSGPlainTexture::bind() // ### TODO: check for out-of-memory situations... - QImage tmp = (m_image.format() == QImage::Format_RGB32 || m_image.format() == QImage::Format_ARGB32_Premultiplied) - ? m_image - : m_image.convertToFormat(QImage::Format_ARGB32_Premultiplied); + QOpenGLTextureUploader::BindOptions options = QOpenGLTextureUploader::PremultipliedAlphaBindOption; // Downscale the texture to fit inside the max texture limit if it is too big. // It would be better if the image was already downscaled to the right size, @@ -782,75 +770,19 @@ void QSGPlainTexture::bind() max = rc->maxTextureSize(); else funcs->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max); - if (tmp.width() > max || tmp.height() > max) { - tmp = tmp.scaled(qMin(max, tmp.width()), qMin(max, tmp.height()), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); - m_texture_size = tmp.size(); - } + + m_texture_size = m_texture_size.boundedTo(QSize(max, max)); // Scale to a power of two size if mipmapping is requested and the // texture is npot and npot textures are not properly supported. if (mipmapFiltering() != QSGTexture::None - && (!isPowerOfTwo(m_texture_size.width()) || !isPowerOfTwo(m_texture_size.height())) && !funcs->hasOpenGLFeature(QOpenGLFunctions::NPOTTextures)) { - tmp = tmp.scaled(qNextPowerOfTwo(m_texture_size.width()), qNextPowerOfTwo(m_texture_size.height()), - Qt::IgnoreAspectRatio, Qt::SmoothTransformation); - m_texture_size = tmp.size(); + options |= QOpenGLTextureUploader::PowerOfTwoBindOption; } - if (tmp.width() * 4 != tmp.bytesPerLine()) - tmp = tmp.copy(); - - qint64 convertTime = 0; - if (profileFrames) - convertTime = qsg_renderer_timer.nsecsElapsed(); - Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphTexturePrepare, - QQuickProfiler::SceneGraphTexturePrepareConvert); - updateBindOptions(m_dirty_bind_options); - GLenum externalFormat = GL_RGBA; - GLenum internalFormat = GL_RGBA; - -#if defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED) - QString *deviceName = - static_cast<QString *>(QGuiApplication::platformNativeInterface()->nativeResourceForIntegration("AndroidDeviceName")); - static bool wrongfullyReportsBgra8888Support = deviceName != 0 - && (deviceName->compare(QLatin1String("samsung SM-T211"), Qt::CaseInsensitive) == 0 - || deviceName->compare(QLatin1String("samsung SM-T210"), Qt::CaseInsensitive) == 0 - || deviceName->compare(QLatin1String("samsung SM-T215"), Qt::CaseInsensitive) == 0); -#else - static bool wrongfullyReportsBgra8888Support = false; -#endif - - if (context->hasExtension(QByteArrayLiteral("GL_EXT_bgra"))) { - externalFormat = GL_BGRA; -#ifdef QT_OPENGL_ES - internalFormat = GL_BGRA; -#else - if (context->isOpenGLES()) - internalFormat = GL_BGRA; -#endif // QT_OPENGL_ES - } else if (!wrongfullyReportsBgra8888Support - && (context->hasExtension(QByteArrayLiteral("GL_EXT_texture_format_BGRA8888")) - || context->hasExtension(QByteArrayLiteral("GL_IMG_texture_format_BGRA8888")))) { - externalFormat = GL_BGRA; - internalFormat = GL_BGRA; -#if defined(Q_OS_DARWIN) && !defined(Q_OS_OSX) - } else if (context->hasExtension(QByteArrayLiteral("GL_APPLE_texture_format_BGRA8888"))) { - externalFormat = GL_BGRA; - internalFormat = GL_RGBA; -#endif - } else { - qsg_swizzleBGRAToRGBA(&tmp); - } - - qint64 swizzleTime = 0; - if (profileFrames) - swizzleTime = qsg_renderer_timer.nsecsElapsed(); - Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphTexturePrepare, - QQuickProfiler::SceneGraphTexturePrepareSwizzle); - - funcs->glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, m_texture_size.width(), m_texture_size.height(), 0, externalFormat, GL_UNSIGNED_BYTE, tmp.constBits()); + QOpenGLTextureUploader::textureImage(GL_TEXTURE_2D, m_image, options, QSize(max, max)); qint64 uploadTime = 0; if (profileFrames) @@ -867,15 +799,11 @@ void QSGPlainTexture::bind() if (profileFrames) { mipmapTime = qsg_renderer_timer.nsecsElapsed(); qCDebug(QSG_LOG_TIME_TEXTURE, - "plain texture uploaded in: %dms (%dx%d), bind=%d, convert=%d, swizzle=%d (%s->%s), upload=%d, mipmap=%d%s", + "plain texture uploaded in: %dms (%dx%d), bind=%d, upload=%d, mipmap=%d%s", int(mipmapTime / 1000000), m_texture_size.width(), m_texture_size.height(), int(bindTime / 1000000), - int((convertTime - bindTime)/1000000), - int((swizzleTime - convertTime)/1000000), - (externalFormat == GL_BGRA ? "BGRA" : "RGBA"), - (internalFormat == GL_BGRA ? "BGRA" : "RGBA"), - int((uploadTime - swizzleTime)/1000000), + int((uploadTime - bindTime)/1000000), int((mipmapTime - uploadTime)/1000000), m_texture_size != m_image.size() ? " (scaled to GL_MAX_TEXTURE_SIZE)" : ""); } diff --git a/src/quick/scenegraph/util/qsgtexture.h b/src/quick/scenegraph/util/qsgtexture.h index 032129434e..7bd57a16e3 100644 --- a/src/quick/scenegraph/util/qsgtexture.h +++ b/src/quick/scenegraph/util/qsgtexture.h @@ -54,7 +54,7 @@ class Q_QUICK_EXPORT QSGTexture : public QObject public: QSGTexture(); - ~QSGTexture(); + ~QSGTexture() override; enum WrapMode { Repeat, diff --git a/src/quick/scenegraph/util/qsgtexture_p.h b/src/quick/scenegraph/util/qsgtexture_p.h index 52dc6db2d0..18dd5eff68 100644 --- a/src/quick/scenegraph/util/qsgtexture_p.h +++ b/src/quick/scenegraph/util/qsgtexture_p.h @@ -83,7 +83,7 @@ class Q_QUICK_PRIVATE_EXPORT QSGPlainTexture : public QSGTexture Q_OBJECT public: QSGPlainTexture(); - virtual ~QSGPlainTexture(); + ~QSGPlainTexture() override; void setOwnsTexture(bool owns) { m_owns_texture = owns; } bool ownsTexture() const { return m_owns_texture; } diff --git a/src/quick/scenegraph/util/qsgtexturematerial.cpp b/src/quick/scenegraph/util/qsgtexturematerial.cpp index fbc8f27a63..7b1d5abb26 100644 --- a/src/quick/scenegraph/util/qsgtexturematerial.cpp +++ b/src/quick/scenegraph/util/qsgtexturematerial.cpp @@ -57,7 +57,6 @@ inline static bool isPowerOfTwo(int x) QSGMaterialType QSGOpaqueTextureMaterialShader::type; QSGOpaqueTextureMaterialShader::QSGOpaqueTextureMaterialShader() - : QSGMaterialShader() { #if QT_CONFIG(opengl) setShaderSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/qt-project.org/scenegraph/shaders/opaquetexture.vert")); @@ -67,7 +66,7 @@ QSGOpaqueTextureMaterialShader::QSGOpaqueTextureMaterialShader() char const *const *QSGOpaqueTextureMaterialShader::attributeNames() const { - static char const *const attr[] = { "qt_VertexPosition", "qt_VertexTexCoord", 0 }; + static char const *const attr[] = { "qt_VertexPosition", "qt_VertexTexCoord", nullptr }; return attr; } @@ -80,7 +79,7 @@ void QSGOpaqueTextureMaterialShader::initialize() void QSGOpaqueTextureMaterialShader::updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) { - Q_ASSERT(oldEffect == 0 || newEffect->type() == oldEffect->type()); + Q_ASSERT(oldEffect == nullptr || newEffect->type() == oldEffect->type()); QSGOpaqueTextureMaterial *tx = static_cast<QSGOpaqueTextureMaterial *>(newEffect); QSGOpaqueTextureMaterial *oldTx = static_cast<QSGOpaqueTextureMaterial *>(oldEffect); @@ -112,7 +111,7 @@ void QSGOpaqueTextureMaterialShader::updateState(const RenderState &state, QSGMa t->setMipmapFiltering(tx->mipmapFiltering()); t->setAnisotropyLevel(tx->anisotropyLevel()); - if (oldTx == 0 || oldTx->texture()->textureId() != t->textureId()) + if (oldTx == nullptr || oldTx->texture()->textureId() != t->textureId()) t->bind(); else t->updateBindOptions(); @@ -169,7 +168,7 @@ void QSGOpaqueTextureMaterialShader::updateState(const RenderState &state, QSGMa */ QSGOpaqueTextureMaterial::QSGOpaqueTextureMaterial() - : m_texture(0) + : m_texture(nullptr) , m_filtering(QSGTexture::Nearest) , m_mipmap_filtering(QSGTexture::None) , m_horizontal_wrap(QSGTexture::ClampToEdge) @@ -304,7 +303,17 @@ void QSGOpaqueTextureMaterial::setTexture(QSGTexture *texture) The default vertical wrap mode is \c QSGTexture::ClampToEdge. */ +/*! + \fn void QSGOpaqueTextureMaterial::setAnisotropyLevel(QSGTexture::AnisotropyLevel level) + + Sets this material's anistropy level to \a level. +*/ + +/*! + \fn QSGTexture::AnisotropyLevel QSGOpaqueTextureMaterial::anisotropyLevel() const + Returns this material's anistropy level. +*/ /*! \internal @@ -388,7 +397,7 @@ QSGTextureMaterialShader::QSGTextureMaterialShader() void QSGTextureMaterialShader::updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) { - Q_ASSERT(oldEffect == 0 || newEffect->type() == oldEffect->type()); + Q_ASSERT(oldEffect == nullptr || newEffect->type() == oldEffect->type()); #if QT_CONFIG(opengl) if (state.isOpacityDirty()) program()->setUniformValue(m_opacity_id, state.opacity()); diff --git a/src/quick/scenegraph/util/qsgtexturematerial_p.h b/src/quick/scenegraph/util/qsgtexturematerial_p.h index 093d820801..a99e872580 100644 --- a/src/quick/scenegraph/util/qsgtexturematerial_p.h +++ b/src/quick/scenegraph/util/qsgtexturematerial_p.h @@ -72,7 +72,7 @@ protected: int m_matrix_id; }; -class QSGTextureMaterialShader : public QSGOpaqueTextureMaterialShader +class Q_QUICK_PRIVATE_EXPORT QSGTextureMaterialShader : public QSGOpaqueTextureMaterialShader { public: QSGTextureMaterialShader(); diff --git a/src/quick/scenegraph/util/qsgtexturereader.cpp b/src/quick/scenegraph/util/qsgtexturereader.cpp new file mode 100644 index 0000000000..5e12ca4035 --- /dev/null +++ b/src/quick/scenegraph/util/qsgtexturereader.cpp @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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 "qsgtexturereader_p.h" +#include <private/qtexturefilereader_p.h> + +#if QT_CONFIG(opengl) +#include <private/qsgcompressedtexture_p.h> +#endif + +QT_BEGIN_NAMESPACE + +QSGTextureReader::QSGTextureReader(QIODevice *device, const QString &fileName) +{ +#if QT_CONFIG(opengl) + m_reader = new QTextureFileReader(device, fileName); +#else + Q_UNUSED(device); + Q_UNUSED(fileName); +#endif +} + +QSGTextureReader::~QSGTextureReader() +{ + delete m_reader; +} + +QQuickTextureFactory *QSGTextureReader::read() +{ +#if QT_CONFIG(opengl) + if (!m_reader) + return nullptr; + + QTextureFileData texData = m_reader->read(); + if (!texData.isValid()) + return nullptr; + + return new QSGCompressedTextureFactory(texData); +#else + return nullptr; +#endif +} + +bool QSGTextureReader::isTexture() +{ + return m_reader ? m_reader->canRead() : false; +} + +QList<QByteArray> QSGTextureReader::supportedFileFormats() +{ + return QTextureFileReader::supportedFileFormats(); +} + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/util/qsgtexturereader_p.h b/src/quick/scenegraph/util/qsgtexturereader_p.h new file mode 100644 index 0000000000..20c17fce50 --- /dev/null +++ b/src/quick/scenegraph/util/qsgtexturereader_p.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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 QSGTEXTUREREADER_H +#define QSGTEXTUREREADER_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 <QString> +#include <QFileInfo> + +QT_BEGIN_NAMESPACE + +class QIODevice; +class QQuickTextureFactory; +class QTextureFileReader; + +class QSGTextureReader +{ +public: + QSGTextureReader(QIODevice *device, const QString &fileName = QString()); + ~QSGTextureReader(); + + QQuickTextureFactory *read(); + bool isTexture(); + + // TBD access function to params + // TBD ask for identified fmt + + static QList<QByteArray> supportedFileFormats(); + +private: + QTextureFileReader *m_reader = nullptr; +}; + +QT_END_NAMESPACE + +#endif // QSGTEXTUREREADER_H diff --git a/src/quick/scenegraph/util/qsgvertexcolormaterial.cpp b/src/quick/scenegraph/util/qsgvertexcolormaterial.cpp index 42c589b14a..cb61430e2e 100644 --- a/src/quick/scenegraph/util/qsgvertexcolormaterial.cpp +++ b/src/quick/scenegraph/util/qsgvertexcolormaterial.cpp @@ -64,7 +64,6 @@ private: QSGMaterialType QSGVertexColorMaterialShader::type; QSGVertexColorMaterialShader::QSGVertexColorMaterialShader() - : QSGMaterialShader() { #if QT_CONFIG(opengl) setShaderSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/qt-project.org/scenegraph/shaders/vertexcolor.vert")); @@ -87,7 +86,7 @@ void QSGVertexColorMaterialShader::updateState(const RenderState &state, QSGMate char const *const *QSGVertexColorMaterialShader::attributeNames() const { - static const char *const attr[] = { "vertexCoord", "vertexColor", 0 }; + static const char *const attr[] = { "vertexCoord", "vertexColor", nullptr }; return attr; } |