aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick
diff options
context:
space:
mode:
authorFrederik Gladhorn <frederik.gladhorn@theqtcompany.com>2016-08-04 13:12:57 +0200
committerFrederik Gladhorn <frederik.gladhorn@theqtcompany.com>2016-08-04 13:15:54 +0200
commit175f53860af034edf5a2dd5e9a5555376e604180 (patch)
treec3ce4eb15fc9918d7bf4b45f9ddc2d7fb8f65e60 /src/quick
parente2329c91e2999b4533a38c2b9f204af88e0e31ec (diff)
parentecf365c0b2ceeda97e42e4bc408964a9588cb6f2 (diff)
Merge branch 'dev' into wip/pointerhandler
Conflicts: src/quick/items/qquickwindow.cpp: we need the fix for QTBUG-31861 but now using QQuickPointerMouseEvent src/quick/items/qquickwindow_p.h: hover events need timestamps (e4f7ab42) tests/auto/quick/qquickwindow/tst_qquickwindow.cpp: added test for QTBUG-31861 Change-Id: Ic120513b69b318df3ba62d8174c276cbf6b7b55e
Diffstat (limited to 'src/quick')
-rw-r--r--src/quick/designer/qquickdesignercustomobjectdata.cpp4
-rw-r--r--src/quick/doc/src/concepts/modelviewsdata/cppmodels.qdoc105
-rw-r--r--src/quick/doc/src/qmltypereference.qdoc13
-rw-r--r--src/quick/items/qquickclipnode.cpp2
-rw-r--r--src/quick/items/qquickevents_p_p.h8
-rw-r--r--src/quick/items/qquickflickable.cpp4
-rw-r--r--src/quick/items/qquickitem.cpp3
-rw-r--r--src/quick/items/qquickitemanimation.cpp42
-rw-r--r--src/quick/items/qquicktextedit.cpp4
-rw-r--r--src/quick/items/qquickview.cpp3
-rw-r--r--src/quick/items/qquickwindow.cpp39
-rw-r--r--src/quick/items/qquickwindow_p.h6
-rw-r--r--src/quick/qtquick2.cpp8
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwareadaptation.cpp11
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp18
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater.cpp3
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop.cpp991
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop_p.h118
-rw-r--r--src/quick/scenegraph/adaptations/software/software.pri6
-rw-r--r--src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp8
-rw-r--r--src/quick/scenegraph/coreapi/qsgrendernode.cpp67
-rw-r--r--src/quick/scenegraph/coreapi/qsgrendernode.h10
-rw-r--r--src/quick/scenegraph/qsgthreadedrenderloop.cpp2
-rw-r--r--src/quick/scenegraph/util/qsgatlastexture.cpp4
-rw-r--r--src/quick/util/qquickanimation.cpp6
-rw-r--r--src/quick/util/qquickbehavior.cpp6
-rw-r--r--src/quick/util/qquickprofiler.cpp9
-rw-r--r--src/quick/util/qquickprofiler_p.h104
-rw-r--r--src/quick/util/qquickpropertychanges.cpp12
-rw-r--r--src/quick/util/qquicksmoothedanimation.cpp12
-rw-r--r--src/quick/util/qquickspringanimation.cpp4
-rw-r--r--src/quick/util/qquicktransitionmanager.cpp6
-rw-r--r--src/quick/util/qquickvaluetypes.cpp72
-rw-r--r--src/quick/util/qquickvaluetypes_p.h18
-rw-r--r--src/quick/util/util.pri3
35 files changed, 1590 insertions, 141 deletions
diff --git a/src/quick/designer/qquickdesignercustomobjectdata.cpp b/src/quick/designer/qquickdesignercustomobjectdata.cpp
index 42dcb08d45..3c8f4b281c 100644
--- a/src/quick/designer/qquickdesignercustomobjectdata.cpp
+++ b/src/quick/designer/qquickdesignercustomobjectdata.cpp
@@ -194,7 +194,7 @@ void QQuickDesignerCustomObjectData::doResetProperty(QQmlContext *context, const
#endif
if (qmlBinding)
qmlBinding->setTarget(property);
- QQmlPropertyPrivate::setBinding(binding, QQmlPropertyPrivate::None, QQmlPropertyPrivate::DontRemoveBinding);
+ QQmlPropertyPrivate::setBinding(binding, QQmlPropertyPrivate::None, QQmlPropertyData::DontRemoveBinding);
if (qmlBinding)
qmlBinding->update();
@@ -262,7 +262,7 @@ void QQuickDesignerCustomObjectData::setPropertyBinding(QQmlContext *context,
binding->setTarget(property);
binding->setNotifyOnValueChanged(true);
- QQmlPropertyPrivate::setBinding(binding, QQmlPropertyPrivate::None, QQmlPropertyPrivate::DontRemoveBinding);
+ QQmlPropertyPrivate::setBinding(binding, QQmlPropertyPrivate::None, QQmlPropertyData::DontRemoveBinding);
//Refcounting is taking take care of deletion
binding->update();
if (binding->hasError()) {
diff --git a/src/quick/doc/src/concepts/modelviewsdata/cppmodels.qdoc b/src/quick/doc/src/concepts/modelviewsdata/cppmodels.qdoc
index cb281a2d4a..a764402c2f 100644
--- a/src/quick/doc/src/concepts/modelviewsdata/cppmodels.qdoc
+++ b/src/quick/doc/src/concepts/modelviewsdata/cppmodels.qdoc
@@ -156,6 +156,111 @@ with list models of QAbstractItemModel type:
\li \l DelegateModel::parentModelIndex() returns a QModelIndex which can be assigned to DelegateModel::rootIndex
\endlist
+\section2 SQL Models
+
+Qt provides C++ classes that support SQL data models. These classes work
+transparently on the underlying SQL data, reducing the need to run SQL
+queries for basic SQL operations such as create, insert, or update.
+For more details about these classes, see \l{Using the SQL Model Classes}.
+
+Although the C++ classes provide complete feature sets to operate on SQL
+data, they do not provide data access to QML. So you must implement a
+C++ custom data model as a subclass of one of these classes, and expose it
+to QML either as a type or context property.
+
+\section3 Read-only Data Model
+
+The custom model must reimplement the following methods to enable read-only
+access to the data from QML:
+
+\list
+\li \l{QAbstractItemModel::}{roleNames}() to expose the role names to the
+ QML frontend. For example, the following version returns the selected
+ table's field names as role names:
+ \code
+ QHash<int, QByteArray> SqlQueryModel::roleNames() const
+ {
+ QHash<int, QByteArray> roles;
+ // record() returns an empty QSqlRecord
+ for (int i = 0; i < this->record().count(); i ++) {
+ roles.insert(Qt::UserRole + i + 1, record().fieldName(i).toUtf8());
+ }
+ return roles;
+ }
+ \endcode
+\li \l{QSqlQueryModel::}{data}() to expose SQL data to the QML frontend.
+ For example, the following implementation returns data for the given
+ model index:
+ \code
+ QVariant SqlQueryModel::data(const QModelIndex &index, int role) const
+ {
+ QVariant value;
+
+ if (index.isValid()) {
+ if (role < Qt::UserRole) {
+ value = QSqlQueryModel::data(index, role);
+ } else {
+ int columnIdx = role - Qt::UserRole - 1;
+ QModelIndex modelIndex = this->index(index.row(), columnIdx);
+ value = QSqlQueryModel::data(modelIndex, Qt::DisplayRole);
+ }
+ }
+ return value;
+ }
+ \endcode
+\endlist
+
+The QSqlQueryModel class is good enough to implement a custom read-only
+model that represents data in an SQL database. The
+\l{Qt Quick Controls 2 - Chat Tutorial}{chat tutorial} example
+demonstrates this very well by implementing a custom model to fetch the
+contact details from an SQLite database.
+
+\section3 Editable Data Model
+
+Besides the \c roleNames() and \c data(), the editable models must reimplement
+the \l{QSqlTableModel::}{setData} method to save changes to existing SQL data.
+The following version of the method checks if the given model index is valid
+and the \c role is equal to \l Qt::EditRole, before calling the parent class
+version:
+
+\code
+bool SqlEditableModel::setData(const QModelIndex &item, const QVariant &value, int role)
+{
+ if (item.isValid() && role == Qt::EditRole) {
+ QSqlTableModel::setData(item, value,role);
+ emit dataChanged(item, item);
+ return true;
+ }
+ return false;
+
+}
+\endcode
+
+\note It is important to emit the \l{QAbstractItemModel::}{dataChanged}()
+signal after saving the changes.
+
+Unlike the C++ item views such as QListView or QTableView, the \c setData()
+method must be explicitly invoked from QML whenever appropriate. For example,
+on the \l[QML]{TextField::}{editingFinished}() or \l[QML]{TextField::}{accepted}()
+signal of \l[QtQuickControls]{TextField}. Depending on the
+\l{QSqlTableModel::}{EditStrategy} used by the model, the changes are either
+queued for submission later or submitted immediately.
+
+You can also insert new data into the model by calling
+\l {QSqlTableModel::insertRecord}(). In the following example snippet,
+a QSqlRecord is populated with book details and appended to the
+model:
+
+\code
+ ...
+ QSqlRecord newRecord = record();
+ newRecord.setValue("author", "John Grisham");
+ newRecord.setValue("booktitle", "The Litigators");
+ insertRecord(rowCount(), newRecord);
+ ...
+\endcode
+
\section2 Exposing C++ Data Models to QML
The above examples use QQmlContext::setContextProperty() to set
diff --git a/src/quick/doc/src/qmltypereference.qdoc b/src/quick/doc/src/qmltypereference.qdoc
index 6e6e66e026..2406722dbc 100644
--- a/src/quick/doc/src/qmltypereference.qdoc
+++ b/src/quick/doc/src/qmltypereference.qdoc
@@ -100,9 +100,9 @@ available when you import \c QtQuick.
\li By a hexadecimal triplet or quad in the form \c "#RRGGBB" and \c "#AARRGGBB"
respectively. For example, the color red corresponds to a triplet of \c "#FF0000"
and a slightly transparent blue to a quad of \c "#800000FF".
- \li Using the \l{QtQml::Qt::rgba()}{Qt.rgba()}, \l{QtQml::Qt::hsla()}{Qt.hsla()},
- \l{QtQml::Qt::darker()}{Qt.darker()}, \l{QtQml::Qt::lighter()}{Qt.lighter()} or
- \l{QtQml::Qt::tint()}{Qt.tint()} functions.
+ \li Using the \l{QtQml::Qt::rgba()}{Qt.rgba()}, \l{QtQml::Qt::hsva()}{Qt.hsva()},
+ \l{QtQml::Qt::hsla()}{Qt.hsla()}, \l{QtQml::Qt::darker()}{Qt.darker()},
+ \l{QtQml::Qt::lighter()}{Qt.lighter()} or \l{QtQml::Qt::tint()}{Qt.tint()} functions.
\endlist
Example:
@@ -112,8 +112,11 @@ available when you import \c QtQuick.
\enddiv
\snippet qml/colors.qml colors
- Additionally, a color type has \c r, \c g, \c b and \c a properties that refer to the
- red, green, blue and alpha values of the color, respectively:
+ A color type has \c r, \c g, \c b and \c a properties that refer to the red,
+ green, blue and alpha values of the color, respectively. Additionally it has
+ \c hsvHue, \c hsvSaturation, \c hsvValue and \c hslHue, \c hslSaturation,
+ \c hslLightness properties, which allow access to color values in HSV and HSL
+ color models accordingly:
\qml
Text {
diff --git a/src/quick/items/qquickclipnode.cpp b/src/quick/items/qquickclipnode.cpp
index 1114686a9a..7c7fee4a42 100644
--- a/src/quick/items/qquickclipnode.cpp
+++ b/src/quick/items/qquickclipnode.cpp
@@ -113,7 +113,7 @@ void QQuickDefaultClipNode::updateGeometry()
}
}
- markDirty(DirtyGeometry);
setClipRect(m_rect);
+ markDirty(DirtyGeometry);
}
diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h
index 47fda2fe01..f3e6fdddff 100644
--- a/src/quick/items/qquickevents_p_p.h
+++ b/src/quick/items/qquickevents_p_p.h
@@ -284,6 +284,8 @@ private:
bool m_valid : 1;
bool m_accept : 1;
int m_reserved : 30;
+
+ Q_DISABLE_COPY(QQuickEventPoint)
};
class Q_QUICK_PRIVATE_EXPORT QQuickEventTouchPoint : public QQuickEventPoint
@@ -306,6 +308,8 @@ private:
qreal m_rotation;
qreal m_pressure;
QPointerUniqueId m_uniqueId;
+
+ Q_DISABLE_COPY(QQuickEventTouchPoint)
};
class Q_QUICK_PRIVATE_EXPORT QQuickPointerEvent : public QObject
@@ -390,6 +394,8 @@ public:
private:
QQuickEventPoint *m_mousePoint;
+
+ Q_DISABLE_COPY(QQuickPointerMouseEvent)
};
class Q_QUICK_PRIVATE_EXPORT QQuickPointerTouchEvent : public QQuickPointerEvent
@@ -418,6 +424,8 @@ public:
private:
int m_pointCount;
QVector<QQuickEventTouchPoint *> m_touchPoints;
+
+ Q_DISABLE_COPY(QQuickPointerTouchEvent)
};
// ### Qt 6: move this to qtbase, replace QTouchDevice and the enums in QTabletEvent
diff --git a/src/quick/items/qquickflickable.cpp b/src/quick/items/qquickflickable.cpp
index a09088dfed..dcba5c2d71 100644
--- a/src/quick/items/qquickflickable.cpp
+++ b/src/quick/items/qquickflickable.cpp
@@ -1590,13 +1590,13 @@ qreal QQuickFlickable::minXExtent() const
qreal QQuickFlickable::maxXExtent() const
{
Q_D(const QQuickFlickable);
- return qMin<qreal>(0, width() - vWidth() - d->hData.endMargin);
+ return qMin<qreal>(minXExtent(), width() - vWidth() - d->hData.endMargin);
}
/* returns -ve */
qreal QQuickFlickable::maxYExtent() const
{
Q_D(const QQuickFlickable);
- return qMin<qreal>(0, height() - vHeight() - d->vData.endMargin);
+ return qMin<qreal>(minYExtent(), height() - vHeight() - d->vData.endMargin);
}
void QQuickFlickable::componentComplete()
diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp
index 695454af38..84f9b0f169 100644
--- a/src/quick/items/qquickitem.cpp
+++ b/src/quick/items/qquickitem.cpp
@@ -2090,6 +2090,9 @@ void QQuickItemPrivate::updateSubFocusItem(QQuickItem *scope, bool focus)
\value ItemDevicePixelRatioHasChanged The device pixel ratio of the screen
the item is on has changed. ItemChangedData::realValue contains the new
device pixel ratio.
+
+ \value ItemAntialiasingHasChanged The antialiasing has changed. The current
+ (boolean) value can be found in QQuickItem::antialiasing.
*/
/*!
diff --git a/src/quick/items/qquickitemanimation.cpp b/src/quick/items/qquickitemanimation.cpp
index 962f410095..027c07ec07 100644
--- a/src/quick/items/qquickitemanimation.cpp
+++ b/src/quick/items/qquickitemanimation.cpp
@@ -200,6 +200,27 @@ QPointF QQuickParentAnimationPrivate::computeTransformOrigin(QQuickItem::Transfo
}
}
+struct QQuickParentAnimationData : public QAbstractAnimationAction
+{
+ QQuickParentAnimationData() : reverse(false) {}
+ ~QQuickParentAnimationData() { qDeleteAll(pc); }
+
+ QQuickStateActions actions;
+ //### reverse should probably apply on a per-action basis
+ bool reverse;
+ QList<QQuickParentChange *> pc;
+ void doAction() Q_DECL_OVERRIDE
+ {
+ for (int ii = 0; ii < actions.count(); ++ii) {
+ const QQuickStateAction &action = actions.at(ii);
+ if (reverse)
+ action.event->reverse();
+ else
+ action.event->execute();
+ }
+ }
+};
+
QAbstractAnimationJob* QQuickParentAnimation::transition(QQuickStateActions &actions,
QQmlProperties &modified,
TransitionDirection direction,
@@ -207,27 +228,6 @@ QAbstractAnimationJob* QQuickParentAnimation::transition(QQuickStateActions &act
{
Q_D(QQuickParentAnimation);
- struct QQuickParentAnimationData : public QAbstractAnimationAction
- {
- QQuickParentAnimationData() : reverse(false) {}
- ~QQuickParentAnimationData() { qDeleteAll(pc); }
-
- QQuickStateActions actions;
- //### reverse should probably apply on a per-action basis
- bool reverse;
- QList<QQuickParentChange *> pc;
- void doAction() Q_DECL_OVERRIDE
- {
- for (int ii = 0; ii < actions.count(); ++ii) {
- const QQuickStateAction &action = actions.at(ii);
- if (reverse)
- action.event->reverse();
- else
- action.event->execute();
- }
- }
- };
-
QQuickParentAnimationData *data = new QQuickParentAnimationData;
QQuickParentAnimationData *viaData = new QQuickParentAnimationData;
diff --git a/src/quick/items/qquicktextedit.cpp b/src/quick/items/qquicktextedit.cpp
index eb568c48d4..d6d9d53a3b 100644
--- a/src/quick/items/qquicktextedit.cpp
+++ b/src/quick/items/qquicktextedit.cpp
@@ -1489,8 +1489,8 @@ void QQuickTextEdit::setSelectByKeyboard(bool on)
If true, the user can use the mouse to select text in some
platform-specific way. Note that for some platforms this may
- not be an appropriate interaction (eg. may conflict with how
- the text needs to behave inside a Flickable.
+ not be an appropriate interaction; it may conflict with how
+ the text needs to behave inside a Flickable, for example.
*/
bool QQuickTextEdit::selectByMouse() const
{
diff --git a/src/quick/items/qquickview.cpp b/src/quick/items/qquickview.cpp
index 1d89c8bfc2..8b74d26576 100644
--- a/src/quick/items/qquickview.cpp
+++ b/src/quick/items/qquickview.cpp
@@ -44,9 +44,6 @@
#include "qquickitem_p.h"
#include "qquickitemchangelistener_p.h"
-#include <private/qqmldebugconnector_p.h>
-#include <private/qquickprofiler_p.h>
-#include <private/qqmldebugserviceinterfaces_p.h>
#include <private/qqmlmemoryprofiler_p.h>
#include <QtQml/qqmlengine.h>
diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp
index 024f326c3e..ae3b272e72 100644
--- a/src/quick/items/qquickwindow.cpp
+++ b/src/quick/items/qquickwindow.cpp
@@ -697,10 +697,10 @@ bool QQuickWindowPrivate::deliverTouchAsMouse(QQuickItem *item, QTouchEvent *eve
lastMousePosition = me->windowPos();
bool accepted = me->isAccepted();
- bool delivered = deliverHoverEvent(contentItem, me->windowPos(), last, me->modifiers(), accepted);
+ bool delivered = deliverHoverEvent(contentItem, me->windowPos(), last, me->modifiers(), me->timestamp(), accepted);
if (!delivered) {
//take care of any exits
- accepted = clearHover();
+ accepted = clearHover(me->timestamp());
}
me->setAccepted(accepted);
break;
@@ -1471,7 +1471,7 @@ QQuickItem *QQuickWindow::mouseGrabberItem() const
}
-bool QQuickWindowPrivate::clearHover()
+bool QQuickWindowPrivate::clearHover(ulong timestamp)
{
Q_Q(QQuickWindow);
if (hoverItems.isEmpty())
@@ -1481,7 +1481,7 @@ bool QQuickWindowPrivate::clearHover()
bool accepted = false;
foreach (QQuickItem* item, hoverItems)
- accepted = sendHoverEvent(QEvent::HoverLeave, item, pos, pos, QGuiApplication::keyboardModifiers(), true) || accepted;
+ accepted = sendHoverEvent(QEvent::HoverLeave, item, pos, pos, QGuiApplication::keyboardModifiers(), timestamp, true) || accepted;
hoverItems.clear();
return accepted;
}
@@ -1612,6 +1612,15 @@ void QQuickWindowPrivate::deliverMouseEvent(QQuickPointerMouseEvent *pointerEven
lastMousePosition = point->scenePos();
QQuickItem *grabber = point->grabber();
if (grabber) {
+ // if the update consists of changing button state, then don't accept it
+ // unless the button is one in which the item is interested
+ if (pointerEvent->button() != Qt::NoButton
+ && grabber->acceptedMouseButtons()
+ && !(grabber->acceptedMouseButtons() & pointerEvent->button())) {
+ pointerEvent->setAccepted(false);
+ return;
+ }
+
// send update
QPointF localPos = grabber->mapFromScene(lastMousePosition);
auto me = pointerEvent->asMouseEvent(localPos);
@@ -1638,12 +1647,14 @@ void QQuickWindowPrivate::deliverMouseEvent(QQuickPointerMouseEvent *pointerEven
bool QQuickWindowPrivate::sendHoverEvent(QEvent::Type type, QQuickItem *item,
const QPointF &scenePos, const QPointF &lastScenePos,
- Qt::KeyboardModifiers modifiers, bool accepted)
+ Qt::KeyboardModifiers modifiers, ulong timestamp,
+ bool accepted)
{
const QTransform transform = QQuickItemPrivate::get(item)->windowToItemTransform();
//create copy of event
QHoverEvent hoverEvent(type, transform.map(scenePos), transform.map(lastScenePos), modifiers);
+ hoverEvent.setTimestamp(timestamp);
hoverEvent.setAccepted(accepted);
QSet<QQuickItem *> hasFiltered;
@@ -1657,7 +1668,7 @@ bool QQuickWindowPrivate::sendHoverEvent(QEvent::Type type, QQuickItem *item,
}
bool QQuickWindowPrivate::deliverHoverEvent(QQuickItem *item, const QPointF &scenePos, const QPointF &lastScenePos,
- Qt::KeyboardModifiers modifiers, bool &accepted)
+ Qt::KeyboardModifiers modifiers, ulong timestamp, bool &accepted)
{
Q_Q(QQuickWindow);
QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
@@ -1675,7 +1686,7 @@ bool QQuickWindowPrivate::deliverHoverEvent(QQuickItem *item, const QPointF &sce
QQuickItem *child = children.at(ii);
if (!child->isVisible() || !child->isEnabled() || QQuickItemPrivate::get(child)->culled)
continue;
- if (deliverHoverEvent(child, scenePos, lastScenePos, modifiers, accepted))
+ if (deliverHoverEvent(child, scenePos, lastScenePos, modifiers, timestamp, accepted))
return true;
}
}
@@ -1685,7 +1696,7 @@ bool QQuickWindowPrivate::deliverHoverEvent(QQuickItem *item, const QPointF &sce
if (item->contains(p)) {
if (!hoverItems.isEmpty() && hoverItems[0] == item) {
//move
- accepted = sendHoverEvent(QEvent::HoverMove, item, scenePos, lastScenePos, modifiers, accepted);
+ accepted = sendHoverEvent(QEvent::HoverMove, item, scenePos, lastScenePos, modifiers, timestamp, accepted);
} else {
QList<QQuickItem *> itemsToHover;
QQuickItem* parent = item;
@@ -1696,12 +1707,12 @@ bool QQuickWindowPrivate::deliverHoverEvent(QQuickItem *item, const QPointF &sce
// Leaving from previous hovered items until we reach the item or one of its ancestors.
while (!hoverItems.isEmpty() && !itemsToHover.contains(hoverItems[0])) {
QQuickItem *hoverLeaveItem = hoverItems.takeFirst();
- sendHoverEvent(QEvent::HoverLeave, hoverLeaveItem, scenePos, lastScenePos, modifiers, accepted);
+ sendHoverEvent(QEvent::HoverLeave, hoverLeaveItem, scenePos, lastScenePos, modifiers, timestamp, accepted);
}
if (!hoverItems.isEmpty() && hoverItems[0] == item){//Not entering a new Item
// ### Shouldn't we send moves for the parent items as well?
- accepted = sendHoverEvent(QEvent::HoverMove, item, scenePos, lastScenePos, modifiers, accepted);
+ accepted = sendHoverEvent(QEvent::HoverMove, item, scenePos, lastScenePos, modifiers, timestamp, accepted);
} else {
// Enter items that are not entered yet.
int startIdx = -1;
@@ -1720,7 +1731,7 @@ bool QQuickWindowPrivate::deliverHoverEvent(QQuickItem *item, const QPointF &sce
// itemToHoverPrivate->window here prevents that case.
if (itemToHoverPrivate->window == q && itemToHoverPrivate->hoverEnabled) {
hoverItems.prepend(itemToHover);
- sendHoverEvent(QEvent::HoverEnter, itemToHover, scenePos, lastScenePos, modifiers, accepted);
+ sendHoverEvent(QEvent::HoverEnter, itemToHover, scenePos, lastScenePos, modifiers, timestamp, accepted);
}
}
}
@@ -2009,10 +2020,10 @@ void QQuickWindowPrivate::handleMouseEvent(QMouseEvent *event)
lastMousePosition = event->windowPos();
bool accepted = event->isAccepted();
- bool delivered = deliverHoverEvent(contentItem, event->windowPos(), last, event->modifiers(), accepted);
+ bool delivered = deliverHoverEvent(contentItem, event->windowPos(), last, event->modifiers(), event->timestamp(), accepted);
if (!delivered) {
//take care of any exits
- accepted = clearHover();
+ accepted = clearHover(event->timestamp());
}
event->setAccepted(accepted);
return;
@@ -2045,7 +2056,7 @@ void QQuickWindowPrivate::flushFrameSynchronousEvents()
// whether it has moved into a position where it is now under the cursor.
if (!q->mouseGrabberItem() && !lastMousePosition.isNull()) {
bool accepted = false;
- bool delivered = deliverHoverEvent(contentItem, lastMousePosition, lastMousePosition, QGuiApplication::keyboardModifiers(), accepted);
+ bool delivered = deliverHoverEvent(contentItem, lastMousePosition, lastMousePosition, QGuiApplication::keyboardModifiers(), 0, accepted);
if (!delivered)
clearHover(); // take care of any exits
}
diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h
index fca0f78de5..90e75fb751 100644
--- a/src/quick/items/qquickwindow_p.h
+++ b/src/quick/items/qquickwindow_p.h
@@ -174,10 +174,10 @@ public:
QVector<QQuickItem *> mergePointerTargets(const QVector<QQuickItem *> &list1, const QVector<QQuickItem *> &list2) const;
// hover delivery
- bool deliverHoverEvent(QQuickItem *, const QPointF &scenePos, const QPointF &lastScenePos, Qt::KeyboardModifiers modifiers, bool &accepted);
+ bool deliverHoverEvent(QQuickItem *, const QPointF &scenePos, const QPointF &lastScenePos, Qt::KeyboardModifiers modifiers, ulong timestamp, bool &accepted);
bool sendHoverEvent(QEvent::Type, QQuickItem *, const QPointF &scenePos, const QPointF &lastScenePos,
- Qt::KeyboardModifiers modifiers, bool accepted);
- bool clearHover();
+ Qt::KeyboardModifiers modifiers, ulong timestamp, bool accepted);
+ bool clearHover(ulong timestamp = 0);
#ifndef QT_NO_DRAGANDDROP
void deliverDragEvent(QQuickDragGrabber *, QEvent *);
diff --git a/src/quick/qtquick2.cpp b/src/quick/qtquick2.cpp
index bf5b0083e9..c36adf56ec 100644
--- a/src/quick/qtquick2.cpp
+++ b/src/quick/qtquick2.cpp
@@ -49,7 +49,6 @@
#include <private/qqmldebugstatesdelegate_p.h>
#include <private/qqmlbinding_p.h>
#include <private/qqmlcontext_p.h>
-#include <private/qquickprofiler_p.h>
#include <private/qquickapplication_p.h>
#include <QtQuick/private/qquickpropertychanges_p.h>
#include <QtQuick/private/qquickstate_p.h>
@@ -63,6 +62,12 @@ static void initResources()
QT_BEGIN_NAMESPACE
+#ifdef QT_NO_QML_DEBUGGER
+
+class QQmlQtQuick2DebugStatesDelegate : public QQmlDebugStatesDelegate {};
+
+#else
+
class QQmlQtQuick2DebugStatesDelegate : public QQmlDebugStatesDelegate
{
public:
@@ -175,6 +180,7 @@ void QQmlQtQuick2DebugStatesDelegate::resetBindingForInvalidProperty(QObject *ob
}
}
+#endif // QT_NO_QML_DEBUGGER
void QQmlQtQuick2Module::defineModule()
{
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwareadaptation.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwareadaptation.cpp
index 0e2f4f5382..144e75d3e6 100644
--- a/src/quick/scenegraph/adaptations/software/qsgsoftwareadaptation.cpp
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwareadaptation.cpp
@@ -40,6 +40,7 @@
#include "qsgsoftwareadaptation_p.h"
#include "qsgsoftwarecontext_p.h"
#include "qsgsoftwarerenderloop_p.h"
+#include "qsgsoftwarethreadedrenderloop_p.h"
#include <private/qguiapplication_p.h>
#include <qpa/qplatformintegration.h>
@@ -73,6 +74,16 @@ QSGContextFactoryInterface::Flags QSGSoftwareAdaptation::flags(const QString &)
QSGRenderLoop *QSGSoftwareAdaptation::createWindowManager()
{
+ static bool threaded = false;
+ static bool envChecked = false;
+ if (!envChecked) {
+ envChecked = true;
+ threaded = qgetenv("QSG_RENDER_LOOP") == QByteArrayLiteral("threaded");
+ }
+
+ if (threaded)
+ return new QSGSoftwareThreadedRenderLoop;
+
return new QSGSoftwareRenderLoop();
}
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp
index 032a06f946..d900688173 100644
--- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp
@@ -187,6 +187,12 @@ void QSGSoftwareRenderableNode::update()
boundingRect = m_handle.spriteNode->rect().toRect();
break;
case QSGSoftwareRenderableNode::RenderNode:
+ if (m_handle.renderNode->flags().testFlag(QSGRenderNode::OpaqueRendering) && !m_transform.isRotating())
+ m_isOpaque = true;
+ else
+ m_isOpaque = false;
+
+ boundingRect = m_handle.renderNode->rect().toRect();
break;
default:
break;
@@ -244,14 +250,20 @@ QRegion QSGSoftwareRenderableNode::renderNode(QPainter *painter, bool forceOpaqu
rd->m_opacity = m_opacity;
RenderNodeState rs;
rs.cr = m_clipRegion;
+
+ const QRect br = m_handle.renderNode->flags().testFlag(QSGRenderNode::BoundedRectRendering)
+ ? m_boundingRect :
+ QRect(0, 0, painter->device()->width(), painter->device()->height());
+
painter->save();
+ painter->setClipRegion(br, Qt::ReplaceClip);
m_handle.renderNode->render(&rs);
painter->restore();
- const QRect fullRect = QRect(0, 0, painter->device()->width(), painter->device()->height());
- m_previousDirtyRegion = fullRect;
+
+ m_previousDirtyRegion = QRegion(br);
m_isDirty = false;
m_dirtyRegion = QRegion();
- return fullRect;
+ return br;
}
}
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater.cpp
index 82f8623b74..12dbf63353 100644
--- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater.cpp
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater.cpp
@@ -218,12 +218,13 @@ void QSGSoftwareRenderableNodeUpdater::updateNodes(QSGNode *node, bool isNodeRem
m_opacityState.push(state.opacity);
m_transformState.push(state.transform);
m_clipState.push(state.clip);
-
+ m_hasClip = state.hasClip;
} else {
// There is no parent, and no previous parent, so likely a root node
m_opacityState.push(1.0f);
m_transformState.push(QTransform());
m_clipState.push(QRegion());
+ m_hasClip = false;
}
// If the node is being removed, then cleanup the state data
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop.cpp
new file mode 100644
index 0000000000..5d5485ed8f
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop.cpp
@@ -0,0 +1,991 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgsoftwarethreadedrenderloop_p.h"
+#include "qsgsoftwarecontext_p.h"
+#include "qsgsoftwarerenderer_p.h"
+
+#include <private/qsgrenderer_p.h>
+#include <private/qquickwindow_p.h>
+#include <private/qquickprofiler_p.h>
+#include <private/qquickanimatorcontroller_p.h>
+#include <private/qquickprofiler_p.h>
+#include <private/qqmldebugserviceinterfaces_p.h>
+#include <private/qqmldebugconnector_p.h>
+
+#include <qpa/qplatformbackingstore.h>
+
+#include <QtCore/QQueue>
+#include <QtCore/QElapsedTimer>
+#include <QtCore/QThread>
+#include <QtCore/QMutex>
+#include <QtCore/QWaitCondition>
+#include <QtGui/QGuiApplication>
+#include <QtGui/QBackingStore>
+#include <QtQuick/QQuickWindow>
+
+QT_BEGIN_NAMESPACE
+
+// Passed from the RL to the RT when a window is removed obscured and should be
+// removed from the render loop.
+const QEvent::Type WM_Obscure = QEvent::Type(QEvent::User + 1);
+
+// Passed from the RL to RT when GUI has been locked, waiting for sync.
+const QEvent::Type WM_RequestSync = QEvent::Type(QEvent::User + 2);
+
+// Passed by the RT to itself to trigger another render pass. This is typically
+// a result of QQuickWindow::update().
+const QEvent::Type WM_RequestRepaint = QEvent::Type(QEvent::User + 3);
+
+// Passed by the RL to the RT to maybe release resource if no windows are
+// rendering.
+const QEvent::Type WM_TryRelease = QEvent::Type(QEvent::User + 4);
+
+// Passed by the RL to the RT when a QQuickWindow::grabWindow() is called.
+const QEvent::Type WM_Grab = QEvent::Type(QEvent::User + 5);
+
+// Passed by the window when there is a render job to run.
+const QEvent::Type WM_PostJob = QEvent::Type(QEvent::User + 6);
+
+class QSGSoftwareWindowEvent : public QEvent
+{
+public:
+ QSGSoftwareWindowEvent(QQuickWindow *c, QEvent::Type type) : QEvent(type), window(c) { }
+ QQuickWindow *window;
+};
+
+class QSGSoftwareTryReleaseEvent : public QSGSoftwareWindowEvent
+{
+public:
+ QSGSoftwareTryReleaseEvent(QQuickWindow *win, bool destroy)
+ : QSGSoftwareWindowEvent(win, WM_TryRelease), destroying(destroy) { }
+ bool destroying;
+};
+
+class QSGSoftwareSyncEvent : public QSGSoftwareWindowEvent
+{
+public:
+ QSGSoftwareSyncEvent(QQuickWindow *c, bool inExpose, bool force)
+ : QSGSoftwareWindowEvent(c, WM_RequestSync)
+ , size(c->size())
+ , dpr(c->effectiveDevicePixelRatio())
+ , syncInExpose(inExpose)
+ , forceRenderPass(force) { }
+ QSize size;
+ float dpr;
+ bool syncInExpose;
+ bool forceRenderPass;
+};
+
+class QSGSoftwareGrabEvent : public QSGSoftwareWindowEvent
+{
+public:
+ QSGSoftwareGrabEvent(QQuickWindow *c, QImage *result)
+ : QSGSoftwareWindowEvent(c, WM_Grab), image(result) { }
+ QImage *image;
+};
+
+class QSGSoftwareJobEvent : public QSGSoftwareWindowEvent
+{
+public:
+ QSGSoftwareJobEvent(QQuickWindow *c, QRunnable *postedJob)
+ : QSGSoftwareWindowEvent(c, WM_PostJob), job(postedJob) { }
+ ~QSGSoftwareJobEvent() { delete job; }
+ QRunnable *job;
+};
+
+class QSGSoftwareEventQueue : public QQueue<QEvent *>
+{
+public:
+ void addEvent(QEvent *e) {
+ mutex.lock();
+ enqueue(e);
+ if (waiting)
+ condition.wakeOne();
+ mutex.unlock();
+ }
+
+ QEvent *takeEvent(bool wait) {
+ mutex.lock();
+ if (isEmpty() && wait) {
+ waiting = true;
+ condition.wait(&mutex);
+ waiting = false;
+ }
+ QEvent *e = dequeue();
+ mutex.unlock();
+ return e;
+ }
+
+ bool hasMoreEvents() {
+ mutex.lock();
+ bool has = !isEmpty();
+ mutex.unlock();
+ return has;
+ }
+
+private:
+ QMutex mutex;
+ QWaitCondition condition;
+ bool waiting = false;
+};
+
+static inline int qsgrl_animation_interval()
+{
+ const qreal refreshRate = QGuiApplication::primaryScreen() ? QGuiApplication::primaryScreen()->refreshRate() : 0;
+ return refreshRate < 1 ? 16 : int(1000 / refreshRate);
+}
+
+class QSGSoftwareRenderThread : public QThread
+{
+ Q_OBJECT
+public:
+ QSGSoftwareRenderThread(QSGSoftwareThreadedRenderLoop *rl, QSGRenderContext *renderContext)
+ : renderLoop(rl)
+ {
+ rc = static_cast<QSGSoftwareRenderContext *>(renderContext);
+ vsyncDelta = qsgrl_animation_interval();
+ }
+
+ ~QSGSoftwareRenderThread()
+ {
+ delete rc;
+ }
+
+ bool event(QEvent *e);
+ void run();
+
+ void syncAndRender();
+ void sync(bool inExpose);
+
+ void requestRepaint()
+ {
+ if (sleeping)
+ stopEventProcessing = true;
+ if (exposedWindow)
+ pendingUpdate |= RepaintRequest;
+ }
+
+ void processEventsAndWaitForMore();
+ void processEvents();
+ void postEvent(QEvent *e);
+
+ enum UpdateRequest {
+ SyncRequest = 0x01,
+ RepaintRequest = 0x02,
+ ExposeRequest = 0x04 | RepaintRequest | SyncRequest
+ };
+
+ QSGSoftwareThreadedRenderLoop *renderLoop;
+ QSGSoftwareRenderContext *rc;
+ QAnimationDriver *rtAnim = nullptr;
+ volatile bool active = false;
+ uint pendingUpdate = 0;
+ bool sleeping = false;
+ bool syncResultedInChanges = false;
+ float vsyncDelta;
+ QMutex mutex;
+ QWaitCondition waitCondition;
+ QQuickWindow *exposedWindow = nullptr;
+ QBackingStore *backingStore = nullptr;
+ bool stopEventProcessing = false;
+ QSGSoftwareEventQueue eventQueue;
+ QElapsedTimer renderThrottleTimer;
+ qint64 syncTime;
+ qint64 renderTime;
+ qint64 sinceLastTime;
+
+public slots:
+ void onSceneGraphChanged() {
+ syncResultedInChanges = true;
+ }
+};
+
+bool QSGSoftwareRenderThread::event(QEvent *e)
+{
+ switch ((int)e->type()) {
+
+ case WM_Obscure:
+ Q_ASSERT(!exposedWindow || exposedWindow == static_cast<QSGSoftwareWindowEvent *>(e)->window);
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "RT - WM_Obscure" << exposedWindow;
+ mutex.lock();
+ if (exposedWindow) {
+ QQuickWindowPrivate::get(exposedWindow)->fireAboutToStop();
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_Obscure - window removed");
+ exposedWindow = nullptr;
+ delete backingStore;
+ backingStore = nullptr;
+ }
+ waitCondition.wakeOne();
+ mutex.unlock();
+ return true;
+
+ case WM_RequestSync: {
+ QSGSoftwareSyncEvent *wme = static_cast<QSGSoftwareSyncEvent *>(e);
+ if (sleeping)
+ stopEventProcessing = true;
+ exposedWindow = wme->window;
+ if (backingStore == nullptr)
+ backingStore = new QBackingStore(exposedWindow);
+ if (backingStore->size() != exposedWindow->size())
+ backingStore->resize(exposedWindow->size());
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "RT - WM_RequestSync" << exposedWindow;
+ pendingUpdate |= SyncRequest;
+ if (wme->syncInExpose) {
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_RequestSync - triggered from expose");
+ pendingUpdate |= ExposeRequest;
+ }
+ if (wme->forceRenderPass) {
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_RequestSync - repaint regardless");
+ pendingUpdate |= RepaintRequest;
+ }
+ return true;
+ }
+
+ case WM_TryRelease: {
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_TryRelease");
+ mutex.lock();
+ renderLoop->lockedForSync = true;
+ QSGSoftwareTryReleaseEvent *wme = static_cast<QSGSoftwareTryReleaseEvent *>(e);
+ // Only when no windows are exposed anymore or we are shutting down.
+ if (!exposedWindow || wme->destroying) {
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_TryRelease - invalidating rc");
+ if (wme->window) {
+ QQuickWindowPrivate *wd = QQuickWindowPrivate::get(wme->window);
+ if (wme->destroying) {
+ // Bye bye nodes...
+ wd->cleanupNodesOnShutdown();
+ }
+ rc->invalidate();
+ QCoreApplication::processEvents();
+ QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
+ if (wme->destroying)
+ delete wd->animationController;
+ }
+ if (wme->destroying)
+ active = false;
+ if (sleeping)
+ stopEventProcessing = true;
+ } else {
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_TryRelease - not releasing because window is still active");
+ }
+ waitCondition.wakeOne();
+ renderLoop->lockedForSync = false;
+ mutex.unlock();
+ return true;
+ }
+
+ case WM_Grab: {
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_Grab");
+ QSGSoftwareGrabEvent *wme = static_cast<QSGSoftwareGrabEvent *>(e);
+ Q_ASSERT(wme->window);
+ Q_ASSERT(wme->window == exposedWindow || !exposedWindow);
+ mutex.lock();
+ if (wme->window) {
+ // Grabbing is generally done by rendering a frame and reading the
+ // color buffer contents back, without presenting, and then
+ // creating a QImage from the returned data. It is terribly
+ // inefficient since it involves a full blocking wait for the GPU.
+ // However, our hands are tied by the existing, synchronous APIs of
+ // QQuickWindow and such.
+ QQuickWindowPrivate *wd = QQuickWindowPrivate::get(wme->window);
+ auto softwareRenderer = static_cast<QSGSoftwareRenderer*>(wd->renderer);
+ if (softwareRenderer)
+ softwareRenderer->setBackingStore(backingStore);
+ rc->initialize(nullptr);
+ wd->syncSceneGraph();
+ wd->renderSceneGraph(wme->window->size());
+ *wme->image = backingStore->handle()->toImage();
+ }
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_Grab - waking gui to handle result");
+ waitCondition.wakeOne();
+ mutex.unlock();
+ return true;
+ }
+
+ case WM_PostJob: {
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_PostJob");
+ QSGSoftwareJobEvent *wme = static_cast<QSGSoftwareJobEvent *>(e);
+ Q_ASSERT(wme->window == exposedWindow);
+ if (exposedWindow) {
+ wme->job->run();
+ delete wme->job;
+ wme->job = nullptr;
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_PostJob - job done");
+ }
+ return true;
+ }
+
+ case WM_RequestRepaint:
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_RequestPaint");
+ // When GUI posts this event, it is followed by a polishAndSync, so we
+ // must not exit the event loop yet.
+ pendingUpdate |= RepaintRequest;
+ break;
+
+ default:
+ break;
+ }
+
+ return QThread::event(e);
+}
+
+void QSGSoftwareRenderThread::postEvent(QEvent *e)
+{
+ eventQueue.addEvent(e);
+}
+
+void QSGSoftwareRenderThread::processEvents()
+{
+ while (eventQueue.hasMoreEvents()) {
+ QEvent *e = eventQueue.takeEvent(false);
+ event(e);
+ delete e;
+ }
+}
+
+void QSGSoftwareRenderThread::processEventsAndWaitForMore()
+{
+ stopEventProcessing = false;
+ while (!stopEventProcessing) {
+ QEvent *e = eventQueue.takeEvent(true);
+ event(e);
+ delete e;
+ }
+}
+
+void QSGSoftwareRenderThread::run()
+{
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - run()");
+
+ rtAnim = rc->sceneGraphContext()->createAnimationDriver(nullptr);
+ rtAnim->install();
+
+ if (QQmlDebugConnector::service<QQmlProfilerService>())
+ QQuickProfiler::registerAnimationCallback();
+
+ renderThrottleTimer.start();
+
+ while (active) {
+ if (exposedWindow)
+ syncAndRender();
+
+ processEvents();
+ QCoreApplication::processEvents();
+
+ if (pendingUpdate == 0 || !exposedWindow) {
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - done drawing, sleep");
+ sleeping = true;
+ processEventsAndWaitForMore();
+ sleeping = false;
+ }
+ }
+
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - run() exiting");
+
+ delete rtAnim;
+ rtAnim = nullptr;
+
+ rc->moveToThread(renderLoop->thread());
+ moveToThread(renderLoop->thread());
+}
+
+void QSGSoftwareRenderThread::sync(bool inExpose)
+{
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - sync");
+
+ mutex.lock();
+ Q_ASSERT_X(renderLoop->lockedForSync, "QSGD3D12RenderThread::sync()", "sync triggered with gui not locked");
+
+ if (exposedWindow) {
+ QQuickWindowPrivate *wd = QQuickWindowPrivate::get(exposedWindow);
+ bool hadRenderer = wd->renderer != nullptr;
+ // If the scene graph was touched since the last sync() make sure it sends the
+ // changed signal.
+ if (wd->renderer)
+ wd->renderer->clearChangedFlag();
+
+ rc->initialize(nullptr);
+ wd->syncSceneGraph();
+
+ if (!hadRenderer && wd->renderer) {
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - created renderer");
+ syncResultedInChanges = true;
+ connect(wd->renderer, &QSGRenderer::sceneGraphChanged, this,
+ &QSGSoftwareRenderThread::onSceneGraphChanged, Qt::DirectConnection);
+ }
+
+ // Process deferred deletes now, directly after the sync as deleteLater
+ // on the GUI must now also have resulted in SG changes and the delete
+ // is a safe operation.
+ QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
+ }
+
+ if (!inExpose) {
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - sync complete, waking gui");
+ waitCondition.wakeOne();
+ mutex.unlock();
+ }
+}
+
+void QSGSoftwareRenderThread::syncAndRender()
+{
+ Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphRenderLoopFrame);
+
+ QElapsedTimer waitTimer;
+ waitTimer.start();
+
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - syncAndRender()");
+
+ syncResultedInChanges = false;
+ QQuickWindowPrivate *wd = QQuickWindowPrivate::get(exposedWindow);
+
+ const bool repaintRequested = (pendingUpdate & RepaintRequest) || wd->customRenderStage;
+ const bool syncRequested = pendingUpdate & SyncRequest;
+ const bool exposeRequested = (pendingUpdate & ExposeRequest) == ExposeRequest;
+ pendingUpdate = 0;
+
+ if (syncRequested)
+ sync(exposeRequested);
+
+ Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame);
+
+ if (!syncResultedInChanges && !repaintRequested) {
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - no changes, render aborted");
+ int waitTime = vsyncDelta - (int) waitTimer.elapsed();
+ if (waitTime > 0)
+ msleep(waitTime);
+ return;
+ }
+
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - rendering started");
+
+ if (rtAnim->isRunning()) {
+ wd->animationController->lock();
+ rtAnim->advance();
+ wd->animationController->unlock();
+ }
+
+ bool canRender = wd->renderer != nullptr;
+
+ if (canRender) {
+ auto softwareRenderer = static_cast<QSGSoftwareRenderer*>(wd->renderer);
+ if (softwareRenderer)
+ softwareRenderer->setBackingStore(backingStore);
+ wd->renderSceneGraph(exposedWindow->size());
+
+ Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame);
+
+ if (softwareRenderer && (!wd->customRenderStage || !wd->customRenderStage->swap()))
+ backingStore->flush(softwareRenderer->flushRegion());
+
+ // Since there is no V-Sync with QBackingStore, throttle rendering the refresh
+ // rate of the current screen the window is on.
+ int blockTime = vsyncDelta - (int) renderThrottleTimer.elapsed();
+ if (blockTime > 0) {
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "RT - blocking for " << blockTime << "ms";
+ msleep(blockTime);
+ }
+ renderThrottleTimer.restart();
+
+ wd->fireFrameSwapped();
+ } else {
+ Q_QUICK_SG_PROFILE_SKIP(QQuickProfiler::SceneGraphRenderLoopFrame, 1);
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - window not ready, skipping render");
+ }
+
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - rendering done");
+
+ if (exposeRequested) {
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - wake gui after initial expose");
+ waitCondition.wakeOne();
+ mutex.unlock();
+ }
+
+ Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphRenderLoopFrame);
+}
+
+template<class T> T *windowFor(const QVector<T> &list, QQuickWindow *window)
+{
+ for (const T &t : list) {
+ if (t.window == window)
+ return const_cast<T *>(&t);
+ }
+ return nullptr;
+}
+
+
+QSGSoftwareThreadedRenderLoop::QSGSoftwareThreadedRenderLoop()
+{
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "software threaded render loop constructor");
+ m_sg = new QSGSoftwareContext;
+ m_anim = m_sg->createAnimationDriver(this);
+ connect(m_anim, &QAnimationDriver::started, this, &QSGSoftwareThreadedRenderLoop::onAnimationStarted);
+ connect(m_anim, &QAnimationDriver::stopped, this, &QSGSoftwareThreadedRenderLoop::onAnimationStopped);
+ m_anim->install();
+}
+
+QSGSoftwareThreadedRenderLoop::~QSGSoftwareThreadedRenderLoop()
+{
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "software threaded render loop destructor");
+ delete m_sg;
+}
+
+void QSGSoftwareThreadedRenderLoop::show(QQuickWindow *window)
+{
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "show" << window;
+}
+
+void QSGSoftwareThreadedRenderLoop::hide(QQuickWindow *window)
+{
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "hide" << window;
+
+ if (window->isExposed())
+ handleObscurity(windowFor(m_windows, window));
+
+ releaseResources(window);
+}
+
+void QSGSoftwareThreadedRenderLoop::resize(QQuickWindow *window)
+{
+ if (!window->isExposed() || window->size().isEmpty())
+ return;
+
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "resize" << window << window->size();
+}
+
+void QSGSoftwareThreadedRenderLoop::windowDestroyed(QQuickWindow *window)
+{
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "window destroyed" << window;
+
+ WindowData *w = windowFor(m_windows, window);
+ if (!w)
+ return;
+
+ handleObscurity(w);
+ handleResourceRelease(w, true);
+
+ QSGSoftwareRenderThread *thread = w->thread;
+ while (thread->isRunning())
+ QThread::yieldCurrentThread();
+
+ Q_ASSERT(thread->thread() == QThread::currentThread());
+ delete thread;
+
+ for (int i = 0; i < m_windows.size(); ++i) {
+ if (m_windows.at(i).window == window) {
+ m_windows.removeAt(i);
+ break;
+ }
+ }
+}
+
+void QSGSoftwareThreadedRenderLoop::exposureChanged(QQuickWindow *window)
+{
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "exposure changed" << window;
+
+ if (window->isExposed()) {
+ handleExposure(window);
+ } else {
+ WindowData *w = windowFor(m_windows, window);
+ if (w)
+ handleObscurity(w);
+ }
+}
+
+QImage QSGSoftwareThreadedRenderLoop::grab(QQuickWindow *window)
+{
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "grab" << window;
+
+ WindowData *w = windowFor(m_windows, window);
+ // Have to support invisible (but created()'ed) windows as well.
+ // Unlike with GL, leaving that case for QQuickWindow to handle is not feasible.
+ const bool tempExpose = !w;
+ if (tempExpose) {
+ handleExposure(window);
+ w = windowFor(m_windows, window);
+ Q_ASSERT(w);
+ }
+
+ if (!w->thread->isRunning())
+ return QImage();
+
+ if (!window->handle())
+ window->create();
+
+ QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window);
+ wd->polishItems();
+
+ QImage result;
+ w->thread->mutex.lock();
+ lockedForSync = true;
+ w->thread->postEvent(new QSGSoftwareGrabEvent(window, &result));
+ w->thread->waitCondition.wait(&w->thread->mutex);
+ lockedForSync = false;
+ w->thread->mutex.unlock();
+
+ result.setDevicePixelRatio(window->effectiveDevicePixelRatio());
+
+ if (tempExpose)
+ handleObscurity(w);
+
+ return result;
+}
+
+void QSGSoftwareThreadedRenderLoop::update(QQuickWindow *window)
+{
+ WindowData *w = windowFor(m_windows, window);
+ if (!w)
+ return;
+
+ if (w->thread == QThread::currentThread()) {
+ w->thread->requestRepaint();
+ return;
+ }
+
+ // We set forceRenderPass because we want to make sure the QQuickWindow
+ // actually does a full render pass after the next sync.
+ w->forceRenderPass = true;
+ scheduleUpdate(w);
+}
+
+void QSGSoftwareThreadedRenderLoop::maybeUpdate(QQuickWindow *window)
+{
+ WindowData *w = windowFor(m_windows, window);
+ if (w)
+ scheduleUpdate(w);
+}
+
+void QSGSoftwareThreadedRenderLoop::handleUpdateRequest(QQuickWindow *window)
+{
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "handleUpdateRequest" << window;
+
+ WindowData *w = windowFor(m_windows, window);
+ if (w)
+ polishAndSync(w, false);
+}
+
+QAnimationDriver *QSGSoftwareThreadedRenderLoop::animationDriver() const
+{
+ return m_anim;
+}
+
+QSGContext *QSGSoftwareThreadedRenderLoop::sceneGraphContext() const
+{
+ return m_sg;
+}
+
+QSGRenderContext *QSGSoftwareThreadedRenderLoop::createRenderContext(QSGContext *) const
+{
+ return m_sg->createRenderContext();
+}
+
+void QSGSoftwareThreadedRenderLoop::releaseResources(QQuickWindow *window)
+{
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "releaseResources" << window;
+
+ WindowData *w = windowFor(m_windows, window);
+ if (w)
+ handleResourceRelease(w, false);
+}
+
+void QSGSoftwareThreadedRenderLoop::postJob(QQuickWindow *window, QRunnable *job)
+{
+ WindowData *w = windowFor(m_windows, window);
+ if (w && w->thread && w->thread->exposedWindow)
+ w->thread->postEvent(new QSGSoftwareJobEvent(window, job));
+ else
+ delete job;
+}
+
+QSurface::SurfaceType QSGSoftwareThreadedRenderLoop::windowSurfaceType() const
+{
+ return QSurface::RasterSurface;
+}
+
+bool QSGSoftwareThreadedRenderLoop::interleaveIncubation() const
+{
+ bool somethingVisible = false;
+ for (const WindowData &w : m_windows) {
+ if (w.window->isVisible() && w.window->isExposed()) {
+ somethingVisible = true;
+ break;
+ }
+ }
+ return somethingVisible && m_anim->isRunning();
+}
+
+int QSGSoftwareThreadedRenderLoop::flags() const
+{
+ return SupportsGrabWithoutExpose;
+}
+
+bool QSGSoftwareThreadedRenderLoop::event(QEvent *e)
+{
+ if (e->type() == QEvent::Timer) {
+ QTimerEvent *te = static_cast<QTimerEvent *>(e);
+ if (te->timerId() == animationTimer) {
+ m_anim->advance();
+ emit timeToIncubate();
+ return true;
+ }
+ }
+
+ return QObject::event(e);
+}
+
+void QSGSoftwareThreadedRenderLoop::onAnimationStarted()
+{
+ startOrStopAnimationTimer();
+
+ for (const WindowData &w : qAsConst(m_windows))
+ w.window->requestUpdate();
+}
+
+void QSGSoftwareThreadedRenderLoop::onAnimationStopped()
+{
+ startOrStopAnimationTimer();
+}
+
+void QSGSoftwareThreadedRenderLoop::startOrStopAnimationTimer()
+{
+ int exposedWindowCount = 0;
+ const WindowData *exposed = nullptr;
+
+ for (int i = 0; i < m_windows.size(); ++i) {
+ const WindowData &w(m_windows[i]);
+ if (w.window->isVisible() && w.window->isExposed()) {
+ ++exposedWindowCount;
+ exposed = &w;
+ }
+ }
+
+ if (animationTimer && (exposedWindowCount == 1 || !m_anim->isRunning())) {
+ killTimer(animationTimer);
+ animationTimer = 0;
+ // If animations are running, make sure we keep on animating
+ if (m_anim->isRunning())
+ exposed->window->requestUpdate();
+ } else if (!animationTimer && exposedWindowCount != 1 && m_anim->isRunning()) {
+ animationTimer = startTimer(qsgrl_animation_interval());
+ }
+}
+
+void QSGSoftwareThreadedRenderLoop::handleExposure(QQuickWindow *window)
+{
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "handleExposure" << window;
+
+ WindowData *w = windowFor(m_windows, window);
+ if (!w) {
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "adding window to list");
+ WindowData win;
+ win.window = window;
+ QSGRenderContext *rc = QQuickWindowPrivate::get(window)->context; // will transfer ownership
+ win.thread = new QSGSoftwareRenderThread(this, rc);
+ win.updateDuringSync = false;
+ win.forceRenderPass = true; // also covered by polishAndSync(inExpose=true), but doesn't hurt
+ m_windows.append(win);
+ w = &m_windows.last();
+ }
+
+ // set this early as we'll be rendering shortly anyway and this avoids
+ // special casing exposure in polishAndSync.
+ w->thread->exposedWindow = window;
+
+ if (w->window->size().isEmpty()
+ || (w->window->isTopLevel() && !w->window->geometry().intersects(w->window->screen()->availableGeometry()))) {
+#ifndef QT_NO_DEBUG
+ qWarning().noquote().nospace() << "QSGSotwareThreadedRenderLoop: expose event received for window "
+ << w->window << " with invalid geometry: " << w->window->geometry()
+ << " on " << w->window->screen();
+#endif
+ }
+
+ if (!w->window->handle())
+ w->window->create();
+
+ // Start render thread if it is not running
+ if (!w->thread->isRunning()) {
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "starting render thread");
+ // Push a few things to the render thread.
+ QQuickAnimatorController *controller = QQuickWindowPrivate::get(w->window)->animationController;
+ if (controller->thread() != w->thread)
+ controller->moveToThread(w->thread);
+ if (w->thread->thread() == QThread::currentThread()) {
+ w->thread->rc->moveToThread(w->thread);
+ w->thread->moveToThread(w->thread);
+ }
+
+ w->thread->active = true;
+ w->thread->start();
+
+ if (!w->thread->isRunning())
+ qFatal("Render thread failed to start, aborting application.");
+ }
+
+ polishAndSync(w, true);
+
+ startOrStopAnimationTimer();
+}
+
+void QSGSoftwareThreadedRenderLoop::handleObscurity(QSGSoftwareThreadedRenderLoop::WindowData *w)
+{
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "handleObscurity" << w->window;
+
+ if (w->thread->isRunning()) {
+ w->thread->mutex.lock();
+ w->thread->postEvent(new QSGSoftwareWindowEvent(w->window, WM_Obscure));
+ w->thread->waitCondition.wait(&w->thread->mutex);
+ w->thread->mutex.unlock();
+ }
+
+ startOrStopAnimationTimer();
+}
+
+void QSGSoftwareThreadedRenderLoop::scheduleUpdate(QSGSoftwareThreadedRenderLoop::WindowData *w)
+{
+ if (!QCoreApplication::instance())
+ return;
+
+ if (!w || !w->thread->isRunning())
+ return;
+
+ QThread *current = QThread::currentThread();
+ if (current != QCoreApplication::instance()->thread() && (current != w->thread || !lockedForSync)) {
+ qWarning() << "Updates can only be scheduled from GUI thread or from QQuickItem::updatePaintNode()";
+ return;
+ }
+
+ if (current == w->thread) {
+ w->updateDuringSync = true;
+ return;
+ }
+
+ w->window->requestUpdate();
+}
+
+void QSGSoftwareThreadedRenderLoop::handleResourceRelease(QSGSoftwareThreadedRenderLoop::WindowData *w, bool destroying)
+{
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "handleResourceRelease" << (destroying ? "destroying" : "hide/releaseResources") << w->window;
+
+ w->thread->mutex.lock();
+ if (w->thread->isRunning() && w->thread->active) {
+ QQuickWindow *window = w->window;
+
+ // Note that window->handle() is typically null by this time because
+ // the platform window is already destroyed. This should not be a
+ // problem for the D3D cleanup.
+
+ w->thread->postEvent(new QSGSoftwareTryReleaseEvent(window, destroying));
+ w->thread->waitCondition.wait(&w->thread->mutex);
+
+ // Avoid a shutdown race condition.
+ // If SG is invalidated and 'active' becomes false, the thread's run()
+ // method will exit. handleExposure() relies on QThread::isRunning() (because it
+ // potentially needs to start the thread again) and our mutex cannot be used to
+ // track the thread stopping, so we wait a few nanoseconds extra so the thread
+ // can exit properly.
+ if (!w->thread->active)
+ w->thread->wait();
+ }
+ w->thread->mutex.unlock();
+}
+
+void QSGSoftwareThreadedRenderLoop::polishAndSync(QSGSoftwareThreadedRenderLoop::WindowData *w, bool inExpose)
+{
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "polishAndSync" << (inExpose ? "(in expose)" : "(normal)") << w->window;
+
+ QQuickWindow *window = w->window;
+ if (!w->thread || !w->thread->exposedWindow) {
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "polishAndSync - not exposed, abort");
+ return;
+ }
+
+ // Flush pending touch events.
+ QQuickWindowPrivate::get(window)->flushFrameSynchronousEvents();
+ // The delivery of the event might have caused the window to stop rendering
+ w = windowFor(m_windows, window);
+ if (!w || !w->thread || !w->thread->exposedWindow) {
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "polishAndSync - removed after touch event flushing, abort");
+ return;
+ }
+
+ Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphPolishAndSync);
+
+ QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window);
+ wd->polishItems();
+
+ Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphPolishAndSync);
+
+ w->updateDuringSync = false;
+
+ emit window->afterAnimating();
+
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "polishAndSync - lock for sync");
+ w->thread->mutex.lock();
+ lockedForSync = true;
+ w->thread->postEvent(new QSGSoftwareSyncEvent(window, inExpose, w->forceRenderPass));
+ w->forceRenderPass = false;
+
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "polishAndSync - wait for sync");
+
+ Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphPolishAndSync);
+ w->thread->waitCondition.wait(&w->thread->mutex);
+ lockedForSync = false;
+ w->thread->mutex.unlock();
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "polishAndSync - unlock after sync");
+
+ Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphPolishAndSync);
+
+ if (!animationTimer && m_anim->isRunning()) {
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "polishAndSync - advancing animations");
+ m_anim->advance();
+ // We need to trigger another sync to keep animations running...
+ w->window->requestUpdate();
+ emit timeToIncubate();
+ } else if (w->updateDuringSync) {
+ w->window->requestUpdate();
+ }
+
+ Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphPolishAndSync);
+}
+
+#include "qsgsoftwarethreadedrenderloop.moc"
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop_p.h
new file mode 100644
index 0000000000..99993d651c
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop_p.h
@@ -0,0 +1,118 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGSOFTWARETHREADEDRENDERLOOP_H
+#define QSGSOFTWARETHREADEDRENDERLOOP_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qsgrenderloop_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QSGSoftwareRenderThread;
+class QSGSoftwareContext;
+
+class QSGSoftwareThreadedRenderLoop : public QSGRenderLoop
+{
+ Q_OBJECT
+public:
+ QSGSoftwareThreadedRenderLoop();
+ ~QSGSoftwareThreadedRenderLoop();
+
+ void show(QQuickWindow *window) override;
+ void hide(QQuickWindow *window) override;
+ void resize(QQuickWindow *window) override;
+ void windowDestroyed(QQuickWindow *window) override;
+ void exposureChanged(QQuickWindow *window) override;
+ QImage grab(QQuickWindow *window) override;
+ void update(QQuickWindow *window) override;
+ void maybeUpdate(QQuickWindow *window) override;
+ void handleUpdateRequest(QQuickWindow *window) override;
+ QAnimationDriver *animationDriver() const override;
+ QSGContext *sceneGraphContext() const override;
+ QSGRenderContext *createRenderContext(QSGContext *) const override;
+ void releaseResources(QQuickWindow *window) override;
+ void postJob(QQuickWindow *window, QRunnable *job) override;
+ QSurface::SurfaceType windowSurfaceType() const override;
+ bool interleaveIncubation() const override;
+ int flags() const override;
+
+ bool event(QEvent *e) override;
+
+public Q_SLOTS:
+ void onAnimationStarted();
+ void onAnimationStopped();
+
+private:
+ struct WindowData {
+ QQuickWindow *window;
+ QSGSoftwareRenderThread *thread;
+ uint updateDuringSync : 1;
+ uint forceRenderPass : 1;
+ };
+
+ void startOrStopAnimationTimer();
+ void handleExposure(QQuickWindow *window);
+ void handleObscurity(WindowData *w);
+ void scheduleUpdate(WindowData *w);
+ void handleResourceRelease(WindowData *w, bool destroying);
+ void polishAndSync(WindowData *w, bool inExpose);
+
+ QSGSoftwareContext *m_sg;
+ QAnimationDriver *m_anim;
+ int animationTimer = 0;
+ bool lockedForSync = false;
+ QVector<WindowData> m_windows;
+
+ friend class QSGSoftwareRenderThread;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGSOFTWARETHREADEDRENDERLOOP_H
diff --git a/src/quick/scenegraph/adaptations/software/software.pri b/src/quick/scenegraph/adaptations/software/software.pri
index 1933a37d48..97644fc36a 100644
--- a/src/quick/scenegraph/adaptations/software/software.pri
+++ b/src/quick/scenegraph/adaptations/software/software.pri
@@ -19,7 +19,8 @@ SOURCES += \
$$PWD/qsgsoftwarerenderloop.cpp \
$$PWD/qsgsoftwarelayer.cpp \
$$PWD/qsgsoftwareadaptation.cpp \
- $$PWD/qsgsoftwarespritenode.cpp
+ $$PWD/qsgsoftwarespritenode.cpp \
+ $$PWD/qsgsoftwarethreadedrenderloop.cpp
HEADERS += \
$$PWD/qsgsoftwarecontext_p.h \
@@ -38,4 +39,5 @@ HEADERS += \
$$PWD/qsgsoftwarerenderloop_p.h \
$$PWD/qsgsoftwarelayer_p.h \
$$PWD/qsgsoftwareadaptation_p.h \
- $$PWD/qsgsoftwarespritenode_p.h
+ $$PWD/qsgsoftwarespritenode_p.h \
+ $$PWD/qsgsoftwarethreadedrenderloop_p.h
diff --git a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp
index bee2015007..49bbbf0ba8 100644
--- a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp
+++ b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp
@@ -1033,11 +1033,13 @@ void Renderer::nodeWasAdded(QSGNode *node, Node *shadowParent)
m_rebuild |= FullRebuild;
} else if (node->type() == QSGNode::RenderNodeType) {
- RenderNodeElement *e = new RenderNodeElement(static_cast<QSGRenderNode *>(node));
+ QSGRenderNode *rn = static_cast<QSGRenderNode *>(node);
+ RenderNodeElement *e = new RenderNodeElement(rn);
snode->data = e;
- Q_ASSERT(!m_renderNodeElements.contains(static_cast<QSGRenderNode *>(node)));
+ Q_ASSERT(!m_renderNodeElements.contains(rn));
m_renderNodeElements.insert(e->renderNode, e);
- m_useDepthBuffer = false;
+ if (!rn->flags().testFlag(QSGRenderNode::DepthAwareRendering))
+ m_useDepthBuffer = false;
m_rebuild |= FullRebuild;
}
diff --git a/src/quick/scenegraph/coreapi/qsgrendernode.cpp b/src/quick/scenegraph/coreapi/qsgrendernode.cpp
index 365abd09e2..5915d51f2b 100644
--- a/src/quick/scenegraph/coreapi/qsgrendernode.cpp
+++ b/src/quick/scenegraph/coreapi/qsgrendernode.cpp
@@ -179,6 +179,11 @@ QSGRenderNode::StateFlags QSGRenderNode::changedStates() const
default value according to the OpenGL specification. For other APIs, see
the documentation for changedStates() for more information.
+ \note Depth writes are disabled when this function is called (for example,
+ glDepthMask(false) in case of OpenGL). Enabling depth writes can lead to
+ unexpected results, depending on the scenegraph backend in use, so nodes
+ should avoid this.
+
For APIs other than OpenGL, it will likely be necessary to query certain
API-specific resources (for example, the graphics device or the command
list/buffer to add the commands to). This is done via QSGRendererInterface.
@@ -211,6 +216,68 @@ void QSGRenderNode::releaseResources()
}
/*!
+ \enum QSGRenderNode::RenderingFlag
+
+ Possible values for the bitmask returned from flags().
+
+ \value BoundedRectRendering Indicates that the implementation of render()
+ does not render outside the area reported from rect() in item
+ coordinates. Such node implementations can lead to more efficient rendering,
+ depending on the scenegraph backend. For example, the software backend can
+ continue to use the more optimal partial update path when all render nodes
+ in the scene have this flag set.
+
+ \value DepthAwareRendering Indicates that the implementations of render()
+ conforms to scenegraph expectations by only generating a Z value of 0 in
+ scene coordinates which is then transformed by the matrices retrieved from
+ RenderState::projectionMatrix() and matrix(), as described in the notes for
+ render(). Such node implementations can lead to more efficient rendering,
+ depending on the scenegraph backend. For example, the batching OpenGL
+ renderer can continue to use a more optimal path when all render nodes in
+ the scene have this flag set.
+
+ \value OpaqueRendering Indicates that the implementation of render() writes
+ out opaque pixels for the entire area reported from rect(). By default the
+ renderers must assume that render() can also output semi or fully
+ transparent pixels. Setting this flag can improve performance in some
+ cases.
+
+ \sa render(), rect()
+ */
+
+/*!
+ \return flags describing the behavior of this render node.
+
+ The default implementation returns 0.
+
+ \sa RenderingFlag, rect()
+ */
+QSGRenderNode::RenderingFlags QSGRenderNode::flags() const
+{
+ return 0;
+}
+
+/*!
+ \return the bounding rectangle in item coordinates for the area render()
+ touches. The value is only in use when flags() includes
+ BoundedRectRendering, ignored otherwise.
+
+ Reporting the rectangle in combination with BoundedRectRendering is
+ particularly important with the \c software backend because otherwise
+ having a rendernode in the scene would trigger fullscreen updates, skipping
+ all partial update optimizations.
+
+ For rendernodes covering the entire area of a corresponding QQuickItem the
+ return value will be (0, 0, item->width(), item->height()).
+
+ \sa flags()
+*/
+QRectF QSGRenderNode::rect() const
+{
+ return QRectF();
+}
+
+/*!
\return pointer to the current model-view matrix.
*/
const QMatrix4x4 *QSGRenderNode::matrix() const
diff --git a/src/quick/scenegraph/coreapi/qsgrendernode.h b/src/quick/scenegraph/coreapi/qsgrendernode.h
index 6eb425c03b..f6bc40d3ee 100644
--- a/src/quick/scenegraph/coreapi/qsgrendernode.h
+++ b/src/quick/scenegraph/coreapi/qsgrendernode.h
@@ -61,6 +61,13 @@ public:
};
Q_DECLARE_FLAGS(StateFlags, StateFlag)
+ enum RenderingFlag {
+ BoundedRectRendering = 0x01,
+ DepthAwareRendering = 0x02,
+ OpaqueRendering = 0x04
+ };
+ Q_DECLARE_FLAGS(RenderingFlags, RenderingFlag)
+
struct Q_QUICK_EXPORT RenderState {
virtual ~RenderState();
virtual const QMatrix4x4 *projectionMatrix() const = 0;
@@ -78,6 +85,8 @@ public:
virtual StateFlags changedStates() const;
virtual void render(const RenderState *state) = 0;
virtual void releaseResources();
+ virtual RenderingFlags flags() const;
+ virtual QRectF rect() const;
const QMatrix4x4 *matrix() const;
const QSGClipNode *clipList() const;
@@ -89,6 +98,7 @@ private:
};
Q_DECLARE_OPERATORS_FOR_FLAGS(QSGRenderNode::StateFlags)
+Q_DECLARE_OPERATORS_FOR_FLAGS(QSGRenderNode::RenderingFlags)
QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/qsgthreadedrenderloop.cpp b/src/quick/scenegraph/qsgthreadedrenderloop.cpp
index 3ff32b360d..6b9c67b2bd 100644
--- a/src/quick/scenegraph/qsgthreadedrenderloop.cpp
+++ b/src/quick/scenegraph/qsgthreadedrenderloop.cpp
@@ -600,7 +600,7 @@ void QSGRenderThread::syncAndRender()
#endif
Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame);
- if (!syncResultedInChanges && !repaintRequested) {
+ if (!syncResultedInChanges && !repaintRequested && sgrc->isValid()) {
qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "- no changes, render aborted";
int waitTime = vsyncDelta - (int) waitTimer.elapsed();
if (waitTime > 0)
diff --git a/src/quick/scenegraph/util/qsgatlastexture.cpp b/src/quick/scenegraph/util/qsgatlastexture.cpp
index 098a4a666b..40c3293c7b 100644
--- a/src/quick/scenegraph/util/qsgatlastexture.cpp
+++ b/src/quick/scenegraph/util/qsgatlastexture.cpp
@@ -158,13 +158,13 @@ Atlas::Atlas(const QSize &size)
wrongfullyReportsBgra8888Support = false;
const char *ext = (const char *) QOpenGLContext::currentContext()->functions()->glGetString(GL_EXTENSIONS);
- if (!wrongfullyReportsBgra8888Support
+ if (ext && !wrongfullyReportsBgra8888Support
&& (strstr(ext, "GL_EXT_bgra")
|| strstr(ext, "GL_EXT_texture_format_BGRA8888")
|| strstr(ext, "GL_IMG_texture_format_BGRA8888"))) {
m_internalFormat = m_externalFormat = GL_BGRA;
#if defined(Q_OS_DARWIN) && !defined(Q_OS_OSX)
- } else if (strstr(ext, "GL_APPLE_texture_format_BGRA8888")) {
+ } else if (ext && strstr(ext, "GL_APPLE_texture_format_BGRA8888")) {
m_internalFormat = GL_RGBA;
m_externalFormat = GL_BGRA;
#endif // IOS || TVOS
diff --git a/src/quick/util/qquickanimation.cpp b/src/quick/util/qquickanimation.cpp
index 741a583803..d782f9309f 100644
--- a/src/quick/util/qquickanimation.cpp
+++ b/src/quick/util/qquickanimation.cpp
@@ -1202,7 +1202,7 @@ QAbstractAnimationJob* QQuickPropertyAction::transition(QQuickStateActions &acti
{
for (int ii = 0; ii < actions.count(); ++ii) {
const QQuickStateAction &action = actions.at(ii);
- QQmlPropertyPrivate::write(action.property, action.toValue, QQmlPropertyPrivate::BypassInterceptor | QQmlPropertyPrivate::DontRemoveBinding);
+ QQmlPropertyPrivate::write(action.property, action.toValue, QQmlPropertyData::BypassInterceptor | QQmlPropertyData::DontRemoveBinding);
}
}
virtual void debugAction(QDebug d, int indentLevel) const {
@@ -2535,7 +2535,7 @@ void QQuickAnimationPropertyUpdater::setValue(qreal v)
QQuickStateAction &action = actions[ii];
if (v == 1.) {
- QQmlPropertyPrivate::write(action.property, action.toValue, QQmlPropertyPrivate::BypassInterceptor | QQmlPropertyPrivate::DontRemoveBinding);
+ QQmlPropertyPrivate::write(action.property, action.toValue, QQmlPropertyData::BypassInterceptor | QQmlPropertyData::DontRemoveBinding);
} else {
if (!fromSourced && !fromDefined) {
action.fromValue = action.property.read();
@@ -2551,7 +2551,7 @@ void QQuickAnimationPropertyUpdater::setValue(qreal v)
}
}
if (interpolator)
- QQmlPropertyPrivate::write(action.property, interpolator(action.fromValue.constData(), action.toValue.constData(), v), QQmlPropertyPrivate::BypassInterceptor | QQmlPropertyPrivate::DontRemoveBinding);
+ QQmlPropertyPrivate::write(action.property, interpolator(action.fromValue.constData(), action.toValue.constData(), v), QQmlPropertyData::BypassInterceptor | QQmlPropertyData::DontRemoveBinding);
}
if (deleted)
return;
diff --git a/src/quick/util/qquickbehavior.cpp b/src/quick/util/qquickbehavior.cpp
index 147380037d..1d3ee2c4be 100644
--- a/src/quick/util/qquickbehavior.cpp
+++ b/src/quick/util/qquickbehavior.cpp
@@ -180,7 +180,7 @@ void QQuickBehavior::write(const QVariant &value)
if (!d->animation || bypass) {
if (d->animationInstance)
d->animationInstance->stop();
- QQmlPropertyPrivate::write(d->property, value, QQmlPropertyPrivate::BypassInterceptor | QQmlPropertyPrivate::DontRemoveBinding);
+ QQmlPropertyPrivate::write(d->property, value, QQmlPropertyData::BypassInterceptor | QQmlPropertyData::DontRemoveBinding);
d->targetValue = value;
return;
}
@@ -206,7 +206,7 @@ void QQuickBehavior::write(const QVariant &value)
// is needed (value has not changed). If the Behavior was already
// running, let it continue as normal to ensure correct behavior and state.
if (!behaviorActive && d->targetValue == currentValue) {
- QQmlPropertyPrivate::write(d->property, value, QQmlPropertyPrivate::BypassInterceptor | QQmlPropertyPrivate::DontRemoveBinding);
+ QQmlPropertyPrivate::write(d->property, value, QQmlPropertyData::BypassInterceptor | QQmlPropertyData::DontRemoveBinding);
return;
}
@@ -234,7 +234,7 @@ void QQuickBehavior::write(const QVariant &value)
d->blockRunningChanged = false;
}
if (!after.contains(d->property))
- QQmlPropertyPrivate::write(d->property, value, QQmlPropertyPrivate::BypassInterceptor | QQmlPropertyPrivate::DontRemoveBinding);
+ QQmlPropertyPrivate::write(d->property, value, QQmlPropertyData::BypassInterceptor | QQmlPropertyData::DontRemoveBinding);
}
void QQuickBehavior::setTarget(const QQmlProperty &property)
diff --git a/src/quick/util/qquickprofiler.cpp b/src/quick/util/qquickprofiler.cpp
index 659ffe1d84..841a1c9bcf 100644
--- a/src/quick/util/qquickprofiler.cpp
+++ b/src/quick/util/qquickprofiler.cpp
@@ -70,7 +70,7 @@ void QQuickProfiler::registerAnimationCallback()
class CallbackRegistrationHelper : public QObject {
Q_OBJECT
-public slots:
+public:
void registerAnimationTimerCallback()
{
QQuickProfiler::registerAnimationCallback();
@@ -86,7 +86,12 @@ QQuickProfiler::QQuickProfiler(QObject *parent) : QObject(parent)
m_timer.start();
CallbackRegistrationHelper *helper = new CallbackRegistrationHelper; // will delete itself
helper->moveToThread(QCoreApplication::instance()->thread());
- QMetaObject::invokeMethod(helper, "registerAnimationTimerCallback", Qt::QueuedConnection);
+
+ // Queue the signal to have the animation timer registration run in the right thread;
+ QObject signalSource;
+ connect(&signalSource, &QObject::destroyed,
+ helper, &CallbackRegistrationHelper::registerAnimationTimerCallback,
+ Qt::QueuedConnection);
}
QQuickProfiler::~QQuickProfiler()
diff --git a/src/quick/util/qquickprofiler_p.h b/src/quick/util/qquickprofiler_p.h
index f1af87f4e6..66ed212722 100644
--- a/src/quick/util/qquickprofiler_p.h
+++ b/src/quick/util/qquickprofiler_p.h
@@ -62,52 +62,22 @@
QT_BEGIN_NAMESPACE
+#ifdef QT_NO_QML_DEBUGGER
+
+#define Q_QUICK_PROFILE_IF_ENABLED(feature, Code)
+
+struct QQuickProfiler {
+ static void registerAnimationCallback() {}
+};
+
+#else
+
#define Q_QUICK_PROFILE_IF_ENABLED(feature, Code)\
if (QQuickProfiler::featuresEnabled & (1 << feature)) {\
Code;\
} else\
(void)0
-#define Q_QUICK_PROFILE(feature, Method)\
- Q_QUICK_PROFILE_IF_ENABLED(feature, QQuickProfiler::Method)
-
-#define Q_QUICK_SG_PROFILE_START(Type)\
- Q_QUICK_PROFILE_IF_ENABLED(QQuickProfiler::ProfileSceneGraph,\
- (QQuickProfiler::startSceneGraphFrame<Type>()))
-
-#define Q_QUICK_SG_PROFILE_RECORD(Type)\
- Q_QUICK_PROFILE_IF_ENABLED(QQuickProfiler::ProfileSceneGraph,\
- (QQuickProfiler::recordSceneGraphTimestamp<Type>()))
-
-#define Q_QUICK_SG_PROFILE_SKIP(Type, Skip)\
- Q_QUICK_PROFILE_IF_ENABLED(QQuickProfiler::ProfileSceneGraph,\
- (QQuickProfiler::skipSceneGraphTimestamps<Type, Skip>()))
-
-#define Q_QUICK_SG_PROFILE_START_SYNCHRONIZED(Type1, Type2)\
- Q_QUICK_PROFILE_IF_ENABLED(QQuickProfiler::ProfileSceneGraph,\
- (QQuickProfiler::startSceneGraphFrame<Type1, Type2>()))
-
-#define Q_QUICK_SG_PROFILE_SWITCH(Type1, Type2) \
- Q_QUICK_PROFILE_IF_ENABLED(QQuickProfiler::ProfileSceneGraph,\
- (QQuickProfiler::reportSceneGraphFrame<Type1, true, Type2>()))
-
-#define Q_QUICK_SG_PROFILE_REPORT(Type)\
- Q_QUICK_PROFILE_IF_ENABLED(QQuickProfiler::ProfileSceneGraph,\
- (QQuickProfiler::reportSceneGraphFrame<Type, false>()))
-
-#define Q_QUICK_SG_PROFILE_END(Type)\
- Q_QUICK_PROFILE_IF_ENABLED(QQuickProfiler::ProfileSceneGraph,\
- (QQuickProfiler::reportSceneGraphFrame<Type, true>()))
-
-#define Q_QUICK_SG_PROFILE_END_WITH_PAYLOAD(Type, Payload)\
- Q_QUICK_PROFILE_IF_ENABLED(QQuickProfiler::ProfileSceneGraph,\
- (QQuickProfiler::reportSceneGraphFrame<Type, true>(Payload)))
-
-
-#define Q_QUICK_INPUT_PROFILE(Type, DetailType, A, B)\
- Q_QUICK_PROFILE_IF_ENABLED(QQuickProfiler::ProfileInputEvents,\
- (QQuickProfiler::inputEvent<Type, DetailType>(A, B)))
-
// This struct is somewhat dangerous to use:
// You can save values either with 32 or 64 bit precision. toByteArrays will
// guess the precision from messageType. If you state the wrong messageType
@@ -319,17 +289,15 @@ public:
qint64 timestamp() { return m_timer.nsecsElapsed(); }
-
static quint64 featuresEnabled;
- static bool profilingSceneGraph()
- {
- return featuresEnabled & (1 << QQuickProfiler::ProfileSceneGraph);
- }
static void initialize(QObject *parent);
virtual ~QQuickProfiler();
+signals:
+ void dataReady(const QVector<QQuickProfilerData> &data);
+
protected:
friend class QQuickProfilerAdapter;
@@ -347,16 +315,54 @@ protected:
m_data.append(message);
}
-signals:
- void dataReady(const QVector<QQuickProfilerData> &data);
-
-protected slots:
void startProfilingImpl(quint64 features);
void stopProfilingImpl();
void reportDataImpl(bool trackLocations);
void setTimer(const QElapsedTimer &t);
};
+#endif // QT_NO_QML_DEBUGGER
+
+#define Q_QUICK_PROFILE(feature, Method)\
+ Q_QUICK_PROFILE_IF_ENABLED(feature, QQuickProfiler::Method)
+
+#define Q_QUICK_SG_PROFILE_START(Type)\
+ Q_QUICK_PROFILE_IF_ENABLED(QQuickProfiler::ProfileSceneGraph,\
+ (QQuickProfiler::startSceneGraphFrame<Type>()))
+
+#define Q_QUICK_SG_PROFILE_RECORD(Type)\
+ Q_QUICK_PROFILE_IF_ENABLED(QQuickProfiler::ProfileSceneGraph,\
+ (QQuickProfiler::recordSceneGraphTimestamp<Type>()))
+
+#define Q_QUICK_SG_PROFILE_SKIP(Type, Skip)\
+ Q_QUICK_PROFILE_IF_ENABLED(QQuickProfiler::ProfileSceneGraph,\
+ (QQuickProfiler::skipSceneGraphTimestamps<Type, Skip>()))
+
+#define Q_QUICK_SG_PROFILE_START_SYNCHRONIZED(Type1, Type2)\
+ Q_QUICK_PROFILE_IF_ENABLED(QQuickProfiler::ProfileSceneGraph,\
+ (QQuickProfiler::startSceneGraphFrame<Type1, Type2>()))
+
+#define Q_QUICK_SG_PROFILE_SWITCH(Type1, Type2) \
+ Q_QUICK_PROFILE_IF_ENABLED(QQuickProfiler::ProfileSceneGraph,\
+ (QQuickProfiler::reportSceneGraphFrame<Type1, true, Type2>()))
+
+#define Q_QUICK_SG_PROFILE_REPORT(Type)\
+ Q_QUICK_PROFILE_IF_ENABLED(QQuickProfiler::ProfileSceneGraph,\
+ (QQuickProfiler::reportSceneGraphFrame<Type, false>()))
+
+#define Q_QUICK_SG_PROFILE_END(Type)\
+ Q_QUICK_PROFILE_IF_ENABLED(QQuickProfiler::ProfileSceneGraph,\
+ (QQuickProfiler::reportSceneGraphFrame<Type, true>()))
+
+#define Q_QUICK_SG_PROFILE_END_WITH_PAYLOAD(Type, Payload)\
+ Q_QUICK_PROFILE_IF_ENABLED(QQuickProfiler::ProfileSceneGraph,\
+ (QQuickProfiler::reportSceneGraphFrame<Type, true>(Payload)))
+
+
+#define Q_QUICK_INPUT_PROFILE(Type, DetailType, A, B)\
+ Q_QUICK_PROFILE_IF_ENABLED(QQuickProfiler::ProfileInputEvents,\
+ (QQuickProfiler::inputEvent<Type, DetailType>(A, B)))
+
QT_END_NAMESPACE
#endif
diff --git a/src/quick/util/qquickpropertychanges.cpp b/src/quick/util/qquickpropertychanges.cpp
index a42ec31058..c4be68cd31 100644
--- a/src/quick/util/qquickpropertychanges.cpp
+++ b/src/quick/util/qquickpropertychanges.cpp
@@ -597,7 +597,7 @@ void QQuickPropertyChanges::changeValue(const QString &name, const QVariant &val
state()->addEntryToRevertList(action);
QQmlAbstractBinding *oldBinding = QQmlPropertyPrivate::binding(action.property);
if (oldBinding)
- oldBinding->setEnabled(false, QQmlPropertyPrivate::DontRemoveBinding | QQmlPropertyPrivate::BypassInterceptor);
+ oldBinding->setEnabled(false, QQmlPropertyData::DontRemoveBinding | QQmlPropertyData::BypassInterceptor);
d->property(name).write(value);
}
}
@@ -631,7 +631,7 @@ void QQuickPropertyChanges::changeExpression(const QString &name, const QString
&QQmlPropertyPrivate::get(prop)->core, expression, object(),
qmlContext(this));
newBinding->setTarget(prop);
- QQmlPropertyPrivate::setBinding(newBinding, QQmlPropertyPrivate::None, QQmlPropertyPrivate::DontRemoveBinding | QQmlPropertyPrivate::BypassInterceptor);
+ QQmlPropertyPrivate::setBinding(newBinding, QQmlPropertyPrivate::None, QQmlPropertyData::DontRemoveBinding | QQmlPropertyData::BypassInterceptor);
}
return;
}
@@ -644,7 +644,7 @@ void QQuickPropertyChanges::changeExpression(const QString &name, const QString
if (hadValue) {
QQmlAbstractBinding *oldBinding = QQmlPropertyPrivate::binding(d->property(name));
if (oldBinding) {
- oldBinding->setEnabled(false, QQmlPropertyPrivate::DontRemoveBinding | QQmlPropertyPrivate::BypassInterceptor);
+ oldBinding->setEnabled(false, QQmlPropertyData::DontRemoveBinding | QQmlPropertyData::BypassInterceptor);
state()->changeBindingInRevertList(object(), name, oldBinding);
}
@@ -653,7 +653,7 @@ void QQuickPropertyChanges::changeExpression(const QString &name, const QString
&QQmlPropertyPrivate::get(prop)->core, expression, object(),
qmlContext(this));
newBinding->setTarget(prop);
- QQmlPropertyPrivate::setBinding(newBinding, QQmlPropertyPrivate::None, QQmlPropertyPrivate::DontRemoveBinding | QQmlPropertyPrivate::BypassInterceptor);
+ QQmlPropertyPrivate::setBinding(newBinding, QQmlPropertyPrivate::None, QQmlPropertyData::DontRemoveBinding | QQmlPropertyData::BypassInterceptor);
} else {
QQuickStateAction action;
action.restore = restoreEntryValues();
@@ -679,9 +679,9 @@ void QQuickPropertyChanges::changeExpression(const QString &name, const QString
state()->addEntryToRevertList(action);
QQmlAbstractBinding *oldBinding = QQmlPropertyPrivate::binding(action.property);
if (oldBinding)
- oldBinding->setEnabled(false, QQmlPropertyPrivate::DontRemoveBinding | QQmlPropertyPrivate::BypassInterceptor);
+ oldBinding->setEnabled(false, QQmlPropertyData::DontRemoveBinding | QQmlPropertyData::BypassInterceptor);
- QQmlPropertyPrivate::setBinding(newBinding, QQmlPropertyPrivate::None, QQmlPropertyPrivate::DontRemoveBinding | QQmlPropertyPrivate::BypassInterceptor);
+ QQmlPropertyPrivate::setBinding(newBinding, QQmlPropertyPrivate::None, QQmlPropertyData::DontRemoveBinding | QQmlPropertyData::BypassInterceptor);
}
}
}
diff --git a/src/quick/util/qquicksmoothedanimation.cpp b/src/quick/util/qquicksmoothedanimation.cpp
index 569cb37c95..a992589040 100644
--- a/src/quick/util/qquicksmoothedanimation.cpp
+++ b/src/quick/util/qquicksmoothedanimation.cpp
@@ -254,8 +254,8 @@ void QSmoothedAnimation::updateCurrentTime(int t)
qreal value = easeFollow(time_seconds);
value *= (invert? -1.0: 1.0);
QQmlPropertyPrivate::write(target, initialValue + value,
- QQmlPropertyPrivate::BypassInterceptor
- | QQmlPropertyPrivate::DontRemoveBinding);
+ QQmlPropertyData::BypassInterceptor
+ | QQmlPropertyData::DontRemoveBinding);
}
void QSmoothedAnimation::init()
@@ -287,8 +287,8 @@ void QSmoothedAnimation::init()
break;
case QQuickSmoothedAnimation::Sync:
QQmlPropertyPrivate::write(target, to,
- QQmlPropertyPrivate::BypassInterceptor
- | QQmlPropertyPrivate::DontRemoveBinding);
+ QQmlPropertyData::BypassInterceptor
+ | QQmlPropertyData::DontRemoveBinding);
trackVelocity = 0;
stop();
return;
@@ -304,8 +304,8 @@ void QSmoothedAnimation::init()
if (!recalc()) {
QQmlPropertyPrivate::write(target, to,
- QQmlPropertyPrivate::BypassInterceptor
- | QQmlPropertyPrivate::DontRemoveBinding);
+ QQmlPropertyData::BypassInterceptor
+ | QQmlPropertyData::DontRemoveBinding);
stop();
return;
}
diff --git a/src/quick/util/qquickspringanimation.cpp b/src/quick/util/qquickspringanimation.cpp
index d2bc3b4ece..294122150a 100644
--- a/src/quick/util/qquickspringanimation.cpp
+++ b/src/quick/util/qquickspringanimation.cpp
@@ -301,8 +301,8 @@ void QSpringAnimation::updateCurrentTime(int time)
qreal old_to = to;
QQmlPropertyPrivate::write(target, currentValue,
- QQmlPropertyPrivate::BypassInterceptor |
- QQmlPropertyPrivate::DontRemoveBinding);
+ QQmlPropertyData::BypassInterceptor |
+ QQmlPropertyData::DontRemoveBinding);
if (stopped && old_to == to) { // do not stop if we got restarted
if (animationTemplate)
diff --git a/src/quick/util/qquicktransitionmanager.cpp b/src/quick/util/qquicktransitionmanager.cpp
index 55abb0a207..60f710549b 100644
--- a/src/quick/util/qquicktransitionmanager.cpp
+++ b/src/quick/util/qquicktransitionmanager.cpp
@@ -159,9 +159,9 @@ void QQuickTransitionManager::transition(const QList<QQuickStateAction> &list,
for (int ii = 0; ii < applyList.size(); ++ii) {
const QQuickStateAction &action = applyList.at(ii);
if (action.toBinding) {
- QQmlPropertyPrivate::setBinding(action.toBinding.data(), QQmlPropertyPrivate::None, QQmlPropertyPrivate::BypassInterceptor | QQmlPropertyPrivate::DontRemoveBinding);
+ QQmlPropertyPrivate::setBinding(action.toBinding.data(), QQmlPropertyPrivate::None, QQmlPropertyData::BypassInterceptor | QQmlPropertyData::DontRemoveBinding);
} else if (!action.event) {
- QQmlPropertyPrivate::write(action.property, action.toValue, QQmlPropertyPrivate::BypassInterceptor | QQmlPropertyPrivate::DontRemoveBinding);
+ QQmlPropertyPrivate::write(action.property, action.toValue, QQmlPropertyData::BypassInterceptor | QQmlPropertyData::DontRemoveBinding);
} else if (action.event->isReversable()) {
if (action.reverseEvent)
action.event->reverse();
@@ -197,7 +197,7 @@ void QQuickTransitionManager::transition(const QList<QQuickStateAction> &list,
if (action.toBinding)
QQmlPropertyPrivate::removeBinding(action.property); // Make sure this is disabled during the transition
- QQmlPropertyPrivate::write(action.property, action.fromValue, QQmlPropertyPrivate::BypassInterceptor | QQmlPropertyPrivate::DontRemoveBinding);
+ QQmlPropertyPrivate::write(action.property, action.fromValue, QQmlPropertyData::BypassInterceptor | QQmlPropertyData::DontRemoveBinding);
}
}
diff --git a/src/quick/util/qquickvaluetypes.cpp b/src/quick/util/qquickvaluetypes.cpp
index 7b9b6068bd..e673df0451 100644
--- a/src/quick/util/qquickvaluetypes.cpp
+++ b/src/quick/util/qquickvaluetypes.cpp
@@ -80,6 +80,36 @@ qreal QQuickColorValueType::a() const
return v.alphaF();
}
+qreal QQuickColorValueType::hsvHue() const
+{
+ return v.hsvHueF();
+}
+
+qreal QQuickColorValueType::hsvSaturation() const
+{
+ return v.hsvSaturationF();
+}
+
+qreal QQuickColorValueType::hsvValue() const
+{
+ return v.valueF();
+}
+
+qreal QQuickColorValueType::hslHue() const
+{
+ return v.hslHueF();
+}
+
+qreal QQuickColorValueType::hslSaturation() const
+{
+ return v.hslSaturationF();
+}
+
+qreal QQuickColorValueType::hslLightness() const
+{
+ return v.lightnessF();
+}
+
void QQuickColorValueType::setR(qreal r)
{
v.setRedF(r);
@@ -100,6 +130,48 @@ void QQuickColorValueType::setA(qreal a)
v.setAlphaF(a);
}
+void QQuickColorValueType::setHsvHue(qreal hsvHue)
+{
+ qreal hue, saturation, value, alpha;
+ v.getHsvF(&hue, &saturation, &value, &alpha);
+ v.setHsvF(hsvHue, saturation, value, alpha);
+}
+
+void QQuickColorValueType::setHsvSaturation(qreal hsvSaturation)
+{
+ qreal hue, saturation, value, alpha;
+ v.getHsvF(&hue, &saturation, &value, &alpha);
+ v.setHsvF(hue, hsvSaturation, value, alpha);
+}
+
+void QQuickColorValueType::setHsvValue(qreal hsvValue)
+{
+ qreal hue, saturation, value, alpha;
+ v.getHsvF(&hue, &saturation, &value, &alpha);
+ v.setHsvF(hue, saturation, hsvValue, alpha);
+}
+
+void QQuickColorValueType::setHslHue(qreal hslHue)
+{
+ qreal hue, saturation, lightness, alpha;
+ v.getHslF(&hue, &saturation, &lightness, &alpha);
+ v.setHslF(hslHue, saturation, lightness, alpha);
+}
+
+void QQuickColorValueType::setHslSaturation(qreal hslSaturation)
+{
+ qreal hue, saturation, lightness, alpha;
+ v.getHslF(&hue, &saturation, &lightness, &alpha);
+ v.setHslF(hue, hslSaturation, lightness, alpha);
+}
+
+void QQuickColorValueType::setHslLightness(qreal hslLightness)
+{
+ qreal hue, saturation, lightness, alpha;
+ v.getHslF(&hue, &saturation, &lightness, &alpha);
+ v.setHslF(hue, saturation, hslLightness, alpha);
+}
+
QString QQuickVector2DValueType::toString() const
{
return QString(QLatin1String("QVector2D(%1, %2)")).arg(v.x()).arg(v.y());
diff --git a/src/quick/util/qquickvaluetypes_p.h b/src/quick/util/qquickvaluetypes_p.h
index 05e954f915..4a1598ec5c 100644
--- a/src/quick/util/qquickvaluetypes_p.h
+++ b/src/quick/util/qquickvaluetypes_p.h
@@ -78,6 +78,12 @@ class QQuickColorValueType
Q_PROPERTY(qreal g READ g WRITE setG FINAL)
Q_PROPERTY(qreal b READ b WRITE setB FINAL)
Q_PROPERTY(qreal a READ a WRITE setA FINAL)
+ Q_PROPERTY(qreal hsvHue READ hsvHue WRITE setHsvHue FINAL)
+ Q_PROPERTY(qreal hsvSaturation READ hsvSaturation WRITE setHsvSaturation FINAL)
+ Q_PROPERTY(qreal hsvValue READ hsvValue WRITE setHsvValue FINAL)
+ Q_PROPERTY(qreal hslHue READ hslHue WRITE setHslHue FINAL)
+ Q_PROPERTY(qreal hslSaturation READ hslSaturation WRITE setHslSaturation FINAL)
+ Q_PROPERTY(qreal hslLightness READ hslLightness WRITE setHslLightness FINAL)
Q_GADGET
public:
Q_INVOKABLE QString toString() const;
@@ -86,10 +92,22 @@ public:
qreal g() const;
qreal b() const;
qreal a() const;
+ qreal hsvHue() const;
+ qreal hsvSaturation() const;
+ qreal hsvValue() const;
+ qreal hslHue() const;
+ qreal hslSaturation() const;
+ qreal hslLightness() const;
void setR(qreal);
void setG(qreal);
void setB(qreal);
void setA(qreal);
+ void setHsvHue(qreal);
+ void setHsvSaturation(qreal);
+ void setHsvValue(qreal);
+ void setHslHue(qreal);
+ void setHslSaturation(qreal);
+ void setHslLightness(qreal);
};
class QQuickVector2DValueType
diff --git a/src/quick/util/util.pri b/src/quick/util/util.pri
index ffb31ae75e..66792536d7 100644
--- a/src/quick/util/util.pri
+++ b/src/quick/util/util.pri
@@ -26,12 +26,13 @@ SOURCES += \
$$PWD/qquickanimator.cpp \
$$PWD/qquickanimatorjob.cpp \
$$PWD/qquickanimatorcontroller.cpp \
- $$PWD/qquickprofiler.cpp \
$$PWD/qquickfontmetrics.cpp \
$$PWD/qquicktextmetrics.cpp \
$$PWD/qquickshortcut.cpp \
$$PWD/qquickvalidator.cpp
+!contains(QT_CONFIG, no-qml-debug): SOURCES += $$PWD/qquickprofiler.cpp
+
HEADERS += \
$$PWD/qquickapplication_p.h\
$$PWD/qquickutilmodule_p.h\