aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlexandru Croitor <alexandru.croitor@qt.io>2019-08-15 18:40:58 +0200
committerAlexandru Croitor <alexandru.croitor@qt.io>2019-08-15 18:44:54 +0200
commit2be575b4b1e6f6e11a8dbd0b1c05c003828d2598 (patch)
tree55d2bfaff9583b3e1e9f3291f4b91f34adae1d92
parent4a5939d26d1e36efdb2b30cdee1f3672587c3fa5 (diff)
parent9e32b23a1514f367921b4a9ee25bc864a008463c (diff)
Merge remote-tracking branch 'origin/wip/qt6' into wip/cmake
-rw-r--r--dependencies.yaml7
-rw-r--r--src/imports/wavefrontmesh/qwavefrontmesh.cpp13
-rw-r--r--src/plugins/scenegraph/openvg/qsgopenvgcontext.cpp12
-rw-r--r--src/plugins/scenegraph/openvg/qsgopenvgcontext_p.h10
-rw-r--r--src/plugins/scenegraph/openvg/qsgopenvglayer.cpp8
-rw-r--r--src/plugins/scenegraph/openvg/qsgopenvglayer.h10
-rw-r--r--src/plugins/scenegraph/openvg/qsgopenvgpainternode.cpp1
-rw-r--r--src/plugins/scenegraph/openvg/qsgopenvgrenderloop.cpp4
-rw-r--r--src/qml/common/qv4staticvalue_p.h1
-rw-r--r--src/qml/compiler/qqmlirbuilder.cpp6
-rw-r--r--src/qml/compiler/qv4compilerscanfunctions_p.h2
-rw-r--r--src/qml/debugger/qqmlconfigurabledebugservice_p.h4
-rw-r--r--src/qml/doc/src/cppintegration/definetypes.qdoc4
-rw-r--r--src/qml/doc/src/qmlfunctions.qdoc97
-rw-r--r--src/qml/doc/src/qmllanguageref/syntax/propertybinding.qdoc3
-rw-r--r--src/qml/jsapi/qjsengine_p.h4
-rw-r--r--src/qml/jsruntime/qv4dateobject.cpp2
-rw-r--r--src/qml/jsruntime/qv4executableallocator.cpp4
-rw-r--r--src/qml/jsruntime/qv4executableallocator_p.h2
-rw-r--r--src/qml/jsruntime/qv4executablecompilationunit.cpp2
-rw-r--r--src/qml/jsruntime/qv4promiseobject.cpp201
-rw-r--r--src/qml/jsruntime/qv4promiseobject_p.h7
-rw-r--r--src/qml/parser/qqmljs.g8
-rw-r--r--src/qml/parser/qqmljsast_p.h4
-rw-r--r--src/qml/qml/qqml.cpp20
-rw-r--r--src/qml/qml/qqml.h9
-rw-r--r--src/qml/qml/qqmlengine.cpp13
-rw-r--r--src/qml/qml/qqmlerror.cpp5
-rw-r--r--src/qml/qml/qqmlimport.cpp17
-rw-r--r--src/qml/qml/qqmlmetatype.cpp4
-rw-r--r--src/qml/qml/qqmlmetatype_p.h4
-rw-r--r--src/qml/qml/qqmlobjectcreator.cpp6
-rw-r--r--src/qml/qml/qqmlprivate.h7
-rw-r--r--src/qml/qml/qqmlproperty.cpp12
-rw-r--r--src/qml/qml/qqmlxmlhttprequest.cpp12
-rw-r--r--src/qml/types/qqmlbind.cpp27
-rw-r--r--src/qmlmodels/configure.cmake2
-rw-r--r--src/qmlmodels/configure.json2
-rw-r--r--src/qmlmodels/qqmlmodelsmodule.cpp2
-rw-r--r--src/quick/CMakeLists.txt284
-rw-r--r--src/quick/doc/images/sg-renderloop-singlethreaded.jpgbin29515 -> 0 bytes
-rw-r--r--src/quick/doc/images/sg-renderloop-singlethreaded.pngbin0 -> 162831 bytes
-rw-r--r--src/quick/doc/images/sg-renderloop-threaded.jpgbin45262 -> 0 bytes
-rw-r--r--src/quick/doc/images/sg-renderloop-threaded.pngbin0 -> 225933 bytes
-rw-r--r--src/quick/doc/images/sg-renderloop-threaded.xml2
-rw-r--r--src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc4
-rw-r--r--src/quick/handlers/qquickhandlerpoint.cpp15
-rw-r--r--src/quick/handlers/qquickhoverhandler.cpp3
-rw-r--r--src/quick/handlers/qquickpointerdevicehandler.cpp5
-rw-r--r--src/quick/handlers/qquicksinglepointhandler.cpp3
-rw-r--r--src/quick/handlers/qquicktaphandler.cpp9
-rw-r--r--src/quick/items/qquickevents.cpp10
-rw-r--r--src/quick/items/qquickgenericshadereffect.cpp31
-rw-r--r--src/quick/items/qquickgenericshadereffect_p.h4
-rw-r--r--src/quick/items/qquickitemsmodule.cpp1
-rw-r--r--src/quick/items/qquickitemview.cpp3
-rw-r--r--src/quick/items/qquickpainteditem.cpp4
-rw-r--r--src/quick/items/qquickshadereffectmesh_p.h4
-rw-r--r--src/quick/items/qquickwindow.cpp13
-rw-r--r--src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp407
-rw-r--r--src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h49
-rw-r--r--src/quick/scenegraph/coreapi/qsgopenglvisualizer.cpp365
-rw-r--r--src/quick/scenegraph/coreapi/qsgopenglvisualizer_p.h91
-rw-r--r--src/quick/scenegraph/coreapi/qsgrenderer_p.h1
-rw-r--r--src/quick/scenegraph/coreapi/qsgrhivisualizer.cpp929
-rw-r--r--src/quick/scenegraph/coreapi/qsgrhivisualizer_p.h233
-rw-r--r--src/quick/scenegraph/qsgdefaultglyphnode.cpp2
-rw-r--r--src/quick/scenegraph/qsgdefaultglyphnode_p.cpp31
-rw-r--r--src/quick/scenegraph/qsgdefaultglyphnode_p_p.h7
-rw-r--r--src/quick/scenegraph/qsgrenderloop.cpp14
-rw-r--r--src/quick/scenegraph/qsgrhisupport.cpp11
-rw-r--r--src/quick/scenegraph/qsgrhitextureglyphcache.cpp5
-rw-r--r--src/quick/scenegraph/qsgrhitextureglyphcache_p.h3
-rw-r--r--src/quick/scenegraph/qsgthreadedrenderloop.cpp32
-rw-r--r--src/quick/scenegraph/scenegraph.pri6
-rw-r--r--src/quick/scenegraph/scenegraph.qrc3
-rwxr-xr-xsrc/quick/scenegraph/shaders_ng/compile.bat2
-rw-r--r--src/quick/scenegraph/shaders_ng/visualization.frag19
-rw-r--r--src/quick/scenegraph/shaders_ng/visualization.frag.qsbbin0 -> 1877 bytes
-rw-r--r--src/quick/scenegraph/shaders_ng/visualization.vert28
-rw-r--r--src/quick/scenegraph/shaders_ng/visualization.vert.qsbbin0 -> 2030 bytes
-rw-r--r--src/quick/scenegraph/util/qsgdefaultpainternode_p.h3
-rw-r--r--src/quick/util/qquickpath.cpp157
-rw-r--r--src/quick/util/qquickpath_p.h25
-rw-r--r--src/quickshapes/qquickshape.cpp5
-rw-r--r--tests/auto/qml/ecmascripttests/qjstest/test262runner.cpp2
-rw-r--r--tests/auto/qml/qjsengine/tst_qjsengine.cpp15
-rw-r--r--tests/auto/qml/qmllint/data/FromRootDirectParent.qml14
-rw-r--r--tests/auto/qml/qmllint/data/SignalHandler.qml10
-rw-r--r--tests/auto/qml/qmllint/data/nonSpuriousParentWarning.qml8
-rw-r--r--tests/auto/qml/qmllint/data/spuriousParentWarning.qml7
-rw-r--r--tests/auto/qml/qmllint/main.cpp39
-rw-r--r--tests/auto/qml/qmlmin/tst_qmlmin.cpp2
-rw-r--r--tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp6
-rw-r--r--tests/auto/qml/qqmldirparser/CMakeLists.txt15
-rw-r--r--tests/auto/qml/qqmldirparser/qqmldirparser.pro2
-rw-r--r--tests/auto/qml/qqmlecmascript/data/generatorFunction.qml22
-rw-r--r--tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp29
-rw-r--r--tests/auto/qml/qqmlengine/CMakeLists.txt23
-rw-r--r--tests/auto/qml/qqmlengine/qqmlengine.pro2
-rw-r--r--tests/auto/qml/qqmlengine/tst_qqmlengine.cpp54
-rw-r--r--tests/auto/qml/qqmlerror/tst_qqmlerror.cpp2
-rw-r--r--tests/auto/qml/qqmlexpression/tst_qqmlexpression.cpp7
-rw-r--r--tests/auto/qml/qqmlimport/CMakeLists.txt33
-rw-r--r--tests/auto/qml/qqmlimport/data/QTBUG-77102/imports/MyPlugin/MyComponent_0_9.qml14
-rw-r--r--tests/auto/qml/qqmlimport/data/QTBUG-77102/imports/MyPlugin/MyComponent_1_0.qml15
-rw-r--r--tests/auto/qml/qqmlimport/data/QTBUG-77102/imports/MyPlugin/MySettings_1_0.qml7
-rw-r--r--tests/auto/qml/qqmlimport/data/QTBUG-77102/imports/MyPlugin/qmldir6
-rw-r--r--tests/auto/qml/qqmlimport/data/QTBUG-77102/main.0.9.fail.qml11
-rw-r--r--tests/auto/qml/qqmlimport/data/QTBUG-77102/main.0.9.qml11
-rw-r--r--tests/auto/qml/qqmlimport/data/QTBUG-77102/main.1.0.qml11
-rw-r--r--tests/auto/qml/qqmlimport/data/QTBUG-77102/main.nonumber.qml11
-rw-r--r--tests/auto/qml/qqmlimport/qqmlimport.pro5
-rw-r--r--tests/auto/qml/qqmlimport/tst_qqmlimport.cpp50
-rw-r--r--tests/auto/qml/qqmllocale/tst_qqmllocale.cpp17
-rw-r--r--tests/auto/qml/qqmlnotifier/tst_qqmlnotifier.cpp3
-rw-r--r--tests/auto/qml/qqmlpromise/data/promisechain.qml24
-rw-r--r--tests/auto/qml/qqmlpromise/tst_qqmlpromise.cpp15
-rw-r--r--tests/auto/qml/qqmlproperty/data/nullPropertyBinding.qml22
-rw-r--r--tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp17
-rw-r--r--tests/auto/quick/pointerhandlers/multipointtoucharea_interop/data/dragParentOfMPTA.qml50
-rw-r--r--tests/auto/quick/pointerhandlers/multipointtoucharea_interop/tst_multipointtoucharea_interop.cpp43
-rw-r--r--tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp82
-rw-r--r--tests/auto/quick/pointerhandlers/qquicktaphandler/data/Button.qml2
-rw-r--r--tests/auto/quick/pointerhandlers/qquicktaphandler/tst_qquicktaphandler.cpp8
-rw-r--r--tests/auto/quick/qquickflickable/tst_qquickflickable.cpp3
-rw-r--r--tests/auto/quick/qquicklistview/data/resizeAfterComponentComplete.qml77
-rw-r--r--tests/auto/quick/qquicklistview/tst_qquicklistview.cpp16
-rw-r--r--tests/auto/quick/qquickshape/data/pathitem8.pngbin0 -> 7323 bytes
-rw-r--r--tests/auto/quick/qquickshape/data/pathitem8.qml88
-rw-r--r--tests/auto/quick/qquickshape/tst_qquickshape.cpp30
-rw-r--r--tests/auto/quick/qquicktext/BLACKLIST2
-rw-r--r--tests/manual/nodetypes_ng/MultiClipRects.qml20
-rw-r--r--tests/manual/scenegraph_lancelot/data/shape/shape_multiline.qml74
-rw-r--r--tools/qmllint/findunqualified.cpp35
-rw-r--r--tools/qmllint/findunqualified.h1
-rw-r--r--tools/qmllint/scopetree.cpp50
-rw-r--r--tools/qmllint/scopetree.h7
138 files changed, 3670 insertions, 830 deletions
diff --git a/dependencies.yaml b/dependencies.yaml
deleted file mode 100644
index ac5797762b..0000000000
--- a/dependencies.yaml
+++ /dev/null
@@ -1,7 +0,0 @@
-dependencies:
-- ../qtbase.git:
- ref: f955bd8ced8b93d142b7f755665946424c6d3644
- required: true
-- ../qtsvg.git:
- ref: 4b186657ef09634c082b03570624288cf7c1baba
- required: false
diff --git a/src/imports/wavefrontmesh/qwavefrontmesh.cpp b/src/imports/wavefrontmesh/qwavefrontmesh.cpp
index 101e6ab4b0..e973ef0103 100644
--- a/src/imports/wavefrontmesh/qwavefrontmesh.cpp
+++ b/src/imports/wavefrontmesh/qwavefrontmesh.cpp
@@ -53,19 +53,6 @@
QT_BEGIN_NAMESPACE
-static const char qt_position_attribute_name[] = "qt_Vertex";
-static const char qt_texcoord_attribute_name[] = "qt_MultiTexCoord0";
-
-const char *qtPositionAttributeName()
-{
- return qt_position_attribute_name;
-}
-
-const char *qtTexCoordAttributeName()
-{
- return qt_texcoord_attribute_name;
-}
-
class QWavefrontMeshPrivate : public QObjectPrivate
{
public:
diff --git a/src/plugins/scenegraph/openvg/qsgopenvgcontext.cpp b/src/plugins/scenegraph/openvg/qsgopenvgcontext.cpp
index 76ebb7c4ee..1736a2098e 100644
--- a/src/plugins/scenegraph/openvg/qsgopenvgcontext.cpp
+++ b/src/plugins/scenegraph/openvg/qsgopenvgcontext.cpp
@@ -68,10 +68,14 @@ QSGOpenVGRenderContext::QSGOpenVGRenderContext(QSGContext *context)
}
-void QSGOpenVGRenderContext::initialize(void *context)
+void QSGOpenVGRenderContext::initialize(const QSGRenderContext::InitParams *params)
{
- m_vgContext = static_cast<QOpenVGContext*>(context);
- QSGRenderContext::initialize(context);
+ const InitParams *vgparams = static_cast<const InitParams *>(params);
+ if (vgparams->sType != INIT_PARAMS_MAGIC)
+ qFatal("Invalid OpenVG render context parameters");
+
+ m_vgContext = vgparams->context;
+ QSGRenderContext::initialize(params);
}
void QSGOpenVGRenderContext::invalidate()
@@ -160,7 +164,7 @@ QSGInternalRectangleNode *QSGOpenVGContext::createInternalRectangleNode()
return new QSGOpenVGInternalRectangleNode();
}
-QSGInternalImageNode *QSGOpenVGContext::createInternalImageNode()
+QSGInternalImageNode *QSGOpenVGContext::createInternalImageNode(QSGRenderContext *)
{
return new QSGOpenVGInternalImageNode();
}
diff --git a/src/plugins/scenegraph/openvg/qsgopenvgcontext_p.h b/src/plugins/scenegraph/openvg/qsgopenvgcontext_p.h
index 31a1e8643f..15d0b3f344 100644
--- a/src/plugins/scenegraph/openvg/qsgopenvgcontext_p.h
+++ b/src/plugins/scenegraph/openvg/qsgopenvgcontext_p.h
@@ -57,7 +57,13 @@ class QSGOpenVGRenderContext : public QSGRenderContext, public QSGRendererInterf
public:
QSGOpenVGRenderContext(QSGContext *context);
- void initialize(void *context) override;
+ static const int INIT_PARAMS_MAGIC = 0x51E;
+ struct InitParams : public QSGRenderContext::InitParams {
+ int sType = INIT_PARAMS_MAGIC;
+ QOpenVGContext *context = nullptr;
+ };
+
+ void initialize(const QSGRenderContext::InitParams *params) override;
void invalidate() override;
void renderNextFrame(QSGRenderer *renderer, uint fboId) override;
QSGTexture *createTexture(const QImage &image, uint flags) const override;
@@ -94,7 +100,7 @@ public:
QSGLayer *createLayer(QSGRenderContext *renderContext) override;
QSurfaceFormat defaultSurfaceFormat() const override;
QSGInternalRectangleNode *createInternalRectangleNode() override;
- QSGInternalImageNode *createInternalImageNode() override;
+ QSGInternalImageNode *createInternalImageNode(QSGRenderContext *renderContext) override;
#if QT_CONFIG(quick_sprite)
QSGSpriteNode *createSpriteNode() override;
#endif
diff --git a/src/plugins/scenegraph/openvg/qsgopenvglayer.cpp b/src/plugins/scenegraph/openvg/qsgopenvglayer.cpp
index 047539d431..b03168b334 100644
--- a/src/plugins/scenegraph/openvg/qsgopenvglayer.cpp
+++ b/src/plugins/scenegraph/openvg/qsgopenvglayer.cpp
@@ -44,7 +44,8 @@
QT_BEGIN_NAMESPACE
QSGOpenVGLayer::QSGOpenVGLayer(QSGRenderContext *renderContext)
- : m_item(nullptr)
+ : QSGLayer(*(new QSGOpenVGLayerPrivate))
+ , m_item(nullptr)
, m_renderer(nullptr)
, m_device_pixel_ratio(1)
, m_mirrorHorizontal(false)
@@ -312,4 +313,9 @@ void QSGOpenVGLayer::grab()
markDirtyTexture(); // Continuously update if 'live' and 'recursive'.
}
+int QSGOpenVGLayerPrivate::comparisonKey() const
+{
+ return 0;
+}
+
QT_END_NAMESPACE
diff --git a/src/plugins/scenegraph/openvg/qsgopenvglayer.h b/src/plugins/scenegraph/openvg/qsgopenvglayer.h
index 8deedc3347..f2763463cd 100644
--- a/src/plugins/scenegraph/openvg/qsgopenvglayer.h
+++ b/src/plugins/scenegraph/openvg/qsgopenvglayer.h
@@ -42,6 +42,7 @@
#include <private/qsgadaptationlayer_p.h>
#include <private/qsgcontext_p.h>
+#include <private/qsgtexture_p.h>
#include "qopenvgcontext_p.h"
#include "qopenvgoffscreensurface.h"
@@ -50,9 +51,11 @@ QT_BEGIN_NAMESPACE
class QSGOpenVGRenderer;
class QSGOpenVGRenderContext;
+class QSGOpenVGLayerPrivate;
class QSGOpenVGLayer : public QSGLayer
{
+ Q_DECLARE_PRIVATE(QSGOpenVGLayer)
public:
QSGOpenVGLayer(QSGRenderContext *renderContext);
~QSGOpenVGLayer();
@@ -109,6 +112,13 @@ private:
QOpenVGOffscreenSurface *m_secondaryOffscreenSurface;
};
+class QSGOpenVGLayerPrivate : public QSGTexturePrivate
+{
+ Q_DECLARE_PUBLIC(QSGOpenVGLayer)
+public:
+ int comparisonKey() const override;
+};
+
QT_END_NAMESPACE
#endif // QSGOPENVGLAYER_H
diff --git a/src/plugins/scenegraph/openvg/qsgopenvgpainternode.cpp b/src/plugins/scenegraph/openvg/qsgopenvgpainternode.cpp
index fb68ebf2bc..74f30f8189 100644
--- a/src/plugins/scenegraph/openvg/qsgopenvgpainternode.cpp
+++ b/src/plugins/scenegraph/openvg/qsgopenvgpainternode.cpp
@@ -39,6 +39,7 @@
#include "qsgopenvgpainternode.h"
#include "qsgopenvgtexture.h"
+#include <private/qsgcontext_p.h>
#include <qmath.h>
#include <QtGui/QPainter>
diff --git a/src/plugins/scenegraph/openvg/qsgopenvgrenderloop.cpp b/src/plugins/scenegraph/openvg/qsgopenvgrenderloop.cpp
index c41dfd7400..94f7f76036 100644
--- a/src/plugins/scenegraph/openvg/qsgopenvgrenderloop.cpp
+++ b/src/plugins/scenegraph/openvg/qsgopenvgrenderloop.cpp
@@ -176,7 +176,9 @@ void QSGOpenVGRenderLoop::renderWindow(QQuickWindow *window)
if (vg == nullptr) {
vg = new QOpenVGContext(window);
vg->makeCurrent();
- cd->context->initialize(vg);
+ QSGOpenVGRenderContext::InitParams params;
+ params.context = vg;
+ cd->context->initialize(&params);
} else {
vg->makeCurrent();
}
diff --git a/src/qml/common/qv4staticvalue_p.h b/src/qml/common/qv4staticvalue_p.h
index 8160bbb748..0716f7ea20 100644
--- a/src/qml/common/qv4staticvalue_p.h
+++ b/src/qml/common/qv4staticvalue_p.h
@@ -51,6 +51,7 @@
//
#include <QtCore/private/qnumeric_p.h>
+#include <cstring>
#ifdef QT_NO_DEBUG
#define QV4_NEARLY_ALWAYS_INLINE Q_ALWAYS_INLINE
diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp
index aab3f8e9d6..171dc641d3 100644
--- a/src/qml/compiler/qqmlirbuilder.cpp
+++ b/src/qml/compiler/qqmlirbuilder.cpp
@@ -908,7 +908,7 @@ bool IRBuilder::visit(QQmlJS::AST::UiPublicMember *node)
bool IRBuilder::visit(QQmlJS::AST::UiSourceElement *node)
{
- if (QQmlJS::AST::FunctionDeclaration *funDecl = QQmlJS::AST::cast<QQmlJS::AST::FunctionDeclaration *>(node->sourceElement)) {
+ if (QQmlJS::AST::FunctionExpression *funDecl = node->sourceElement->asFunctionDefinition()) {
CompiledFunctionOrExpression *foe = New<CompiledFunctionOrExpression>();
foe->node = funDecl;
foe->parentNode = funDecl;
@@ -1770,7 +1770,7 @@ QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList<Compil
for (const CompiledFunctionOrExpression &f : functions) {
Q_ASSERT(f.node != document->program);
Q_ASSERT(f.parentNode && f.parentNode != document->program);
- QQmlJS::AST::FunctionDeclaration *function = QQmlJS::AST::cast<QQmlJS::AST::FunctionDeclaration*>(f.node);
+ auto function = f.node->asFunctionDefinition();
if (function) {
scan.enterQmlFunction(function);
@@ -1794,7 +1794,7 @@ QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList<Compil
QQmlJS::AST::Node *node = qmlFunction.node;
Q_ASSERT(node != document->program);
- QQmlJS::AST::FunctionDeclaration *function = QQmlJS::AST::cast<QQmlJS::AST::FunctionDeclaration*>(node);
+ QQmlJS::AST::FunctionExpression *function = node->asFunctionDefinition();
QString name;
if (function)
diff --git a/src/qml/compiler/qv4compilerscanfunctions_p.h b/src/qml/compiler/qv4compilerscanfunctions_p.h
index f67db030a2..2de80eac44 100644
--- a/src/qml/compiler/qv4compilerscanfunctions_p.h
+++ b/src/qml/compiler/qv4compilerscanfunctions_p.h
@@ -88,7 +88,7 @@ public:
const QString &name);
void leaveEnvironment();
- void enterQmlFunction(QQmlJS::AST::FunctionDeclaration *ast)
+ void enterQmlFunction(QQmlJS::AST::FunctionExpression *ast)
{ enterFunction(ast, false); }
protected:
diff --git a/src/qml/debugger/qqmlconfigurabledebugservice_p.h b/src/qml/debugger/qqmlconfigurabledebugservice_p.h
index 96ec46f475..38f41047c0 100644
--- a/src/qml/debugger/qqmlconfigurabledebugservice_p.h
+++ b/src/qml/debugger/qqmlconfigurabledebugservice_p.h
@@ -64,7 +64,7 @@ class QQmlConfigurableDebugService : public Base
{
protected:
QQmlConfigurableDebugService(float version, QObject *parent = nullptr) :
- Base(version, parent), m_configMutex(QMutex::Recursive)
+ Base(version, parent)
{
init();
}
@@ -103,7 +103,7 @@ protected:
emit Base::attachedToEngine(engine);
}
- QMutex m_configMutex;
+ QRecursiveMutex m_configMutex;
QList<QJSEngine *> m_waitingEngines;
bool m_waitingForConfiguration;
};
diff --git a/src/qml/doc/src/cppintegration/definetypes.qdoc b/src/qml/doc/src/cppintegration/definetypes.qdoc
index 41bc9fd140..2e8102bd65 100644
--- a/src/qml/doc/src/cppintegration/definetypes.qdoc
+++ b/src/qml/doc/src/cppintegration/definetypes.qdoc
@@ -471,7 +471,7 @@ will be accessible to the \e attachee:
class MessageBoardAttachedType : public QObject
{
Q_OBJECT
- Q_PROPERTY(bool expired READ expired WRITE expired NOTIFY expiredChanged)
+ Q_PROPERTY(bool expired READ expired WRITE setExpired NOTIFY expiredChanged)
public:
MessageBoardAttachedType(QObject *parent);
bool expired() const;
@@ -493,7 +493,7 @@ class MessageBoard : public QObject
{
Q_OBJECT
public:
- static MessageBoard *qmlAttachedProperties(QObject *object)
+ static MessageBoardAttachedType *qmlAttachedProperties(QObject *object)
{
return new MessageBoardAttachedType(object);
}
diff --git a/src/qml/doc/src/qmlfunctions.qdoc b/src/qml/doc/src/qmlfunctions.qdoc
index ab54b5fd1d..9c106558fd 100644
--- a/src/qml/doc/src/qmlfunctions.qdoc
+++ b/src/qml/doc/src/qmlfunctions.qdoc
@@ -487,8 +487,7 @@
A QObject singleton type may be referenced via the type name with which it was registered, and this
typename may be used as the target in a \l Connections type or otherwise used as any other type id would.
- One exception to this is that a QObject singleton type property may not be aliased (because the
- singleton type name does not identify an object within the same component as any other item).
+ One exception to this is that a QObject singleton type property may not be aliased.
\b{NOTE:} A QObject singleton type instance returned from a singleton type provider is owned by
the QML engine unless the object has explicit QQmlEngine::CppOwnership flag set.
@@ -619,6 +618,100 @@
*/
/*!
+ \fn int qmlRegisterSingletonInstance(const char *uri, int versionMajor, int
+ versionMinor, const char *typeName, QObject* cppObject)
+ \relates QQmlEngine
+ \since 5.14
+
+ This function is used to register a singleton object \a cppObject, with a
+ particular \a uri and \a typeName. Its version is a combination of \a
+ versionMajor and \a versionMinor.
+
+ Installing a singleton type into a URI allows you to provide arbitrary
+ functionality (methods and properties) to QML code without requiring
+ individual instances of the type to be instantiated by the client.
+
+ Use this function to register an object of the given type T as a singleton
+ type.
+
+ A QObject singleton type may be referenced via the type name with which it
+ was registered; in turn this type name may be used as the target in a \l
+ Connections type, or like any other type ID. However, there's one
+ exception: a QObject singleton type property can't be aliased because the
+ singleton type name does not identify an object within the same component
+ as any other item.
+
+ \note \a cppObject must outlive the QML engine in which it is used.
+ Moreover, \cppObject must have the same thread affinity as the engine. If
+ you want separate singleton instances for multiple engines, you need to use
+ \l {qmlRegisterSingletonType}. See \l{Threads and QObjects} for more
+ information about thread safety.
+
+ Usage:
+ \code
+ // First, define your QObject which provides the functionality.
+ class SingletonTypeExample : public QObject
+ {
+ Q_OBJECT
+ Q_PROPERTY(int someProperty READ someProperty WRITE setSomeProperty NOTIFY somePropertyChanged)
+
+ public:
+ explicit SingletonTypeExample(QObject* parent = nullptr) : QObject(parent) {}
+
+ Q_INVOKABLE int doSomething()
+ {
+ setSomeProperty(5);
+ return m_someProperty;
+ }
+
+ int someProperty() const { return m_someProperty; }
+ void setSomeProperty(int val) {
+ if (m_someProperty != val) {
+ m_someProperty = val;
+ emit somePropertyChanged(val);
+ }
+ }
+
+ signals:
+ void somePropertyChanged(int newValue);
+
+ private:
+ int m_someProperty = 0;
+ };
+ \endcode
+
+ \code
+ // Second, create an instance of the object
+
+ // allocate example before the engine to ensure that it outlives it
+ QScopedPointer<SingletonTypeExample> example(new SingletonTypeExample);
+ QQmlEngine engine;
+
+ // Third, register the singleton type provider with QML by calling this
+ // function in an initialization function.
+ qmlRegisterSingletonInstance("Qt.example.qobjectSingleton", 1, 0, "MyApi", example.get());
+ \endcode
+
+
+ In order to use the registered singleton type in QML, you must import the
+ URI with the corresponding version.
+ \qml
+ import QtQuick 2.0
+ import Qt.example.qobjectSingleton 1.0
+ Item {
+ id: root
+ property int someValue: MyApi.someProperty
+
+ Component.onCompleted: {
+ console.log(MyApi.doSomething())
+ }
+ }
+ \endqml
+
+ \sa qmlRegisterSingletonType
+ */
+
+/*!
\fn int qmlRegisterType(const QUrl &url, const char *uri, int versionMajor, int versionMinor, const char *qmlName);
\relates QQmlEngine
diff --git a/src/qml/doc/src/qmllanguageref/syntax/propertybinding.qdoc b/src/qml/doc/src/qmllanguageref/syntax/propertybinding.qdoc
index a5ad6af4a2..e607666886 100644
--- a/src/qml/doc/src/qmllanguageref/syntax/propertybinding.qdoc
+++ b/src/qml/doc/src/qmllanguageref/syntax/propertybinding.qdoc
@@ -128,6 +128,9 @@ loops - it could indicate that the binding is being used for more than describin
property relationships. Complex bindings can reduce code performance, readability,
and maintainability. It may be a good idea to redesign components that have
complex bindings, or at least factor the binding out into a separate function.
+As a general rule, users should not rely on the evaluation order of bindings.
+
+\sa {Positioning with Anchors}
\target qml-javascript-assignment
diff --git a/src/qml/jsapi/qjsengine_p.h b/src/qml/jsapi/qjsengine_p.h
index a77d710cff..164a70d000 100644
--- a/src/qml/jsapi/qjsengine_p.h
+++ b/src/qml/jsapi/qjsengine_p.h
@@ -74,7 +74,7 @@ public:
static const QJSEnginePrivate* get(const QJSEngine*e) { return e->d_func(); }
static QJSEnginePrivate* get(QV4::ExecutionEngine *e);
- QJSEnginePrivate() : mutex(QMutex::Recursive) {}
+ QJSEnginePrivate() = default;
~QJSEnginePrivate() override;
static void addToDebugServer(QJSEngine *q);
@@ -105,7 +105,7 @@ public:
};
// Shared by QQmlEngine
- mutable QMutex mutex;
+ mutable QRecursiveMutex mutex;
// These methods may be called from the QML loader thread
diff --git a/src/qml/jsruntime/qv4dateobject.cpp b/src/qml/jsruntime/qv4dateobject.cpp
index 1b26608bf3..bba88e5c9a 100644
--- a/src/qml/jsruntime/qv4dateobject.cpp
+++ b/src/qml/jsruntime/qv4dateobject.cpp
@@ -54,7 +54,7 @@
#include <wtf/MathExtras.h>
-#if defined(Q_OS_LINUX) && QT_CONFIG(timezone)
+#if defined(Q_OS_LINUX) && QT_CONFIG(timezone) && !defined(Q_OS_ANDROID)
/*
See QTBUG-56899. Although we don't (yet) have a proper way to reset the
system zone, the code below, that uses QTimeZone::systemTimeZone(), works
diff --git a/src/qml/jsruntime/qv4executableallocator.cpp b/src/qml/jsruntime/qv4executableallocator.cpp
index c836d121e3..7ee6f39aa2 100644
--- a/src/qml/jsruntime/qv4executableallocator.cpp
+++ b/src/qml/jsruntime/qv4executableallocator.cpp
@@ -147,9 +147,7 @@ bool ExecutableAllocator::ChunkOfPages::contains(Allocation *alloc) const
}
ExecutableAllocator::ExecutableAllocator()
- : mutex(QMutex::NonRecursive)
-{
-}
+ = default;
ExecutableAllocator::~ExecutableAllocator()
{
diff --git a/src/qml/jsruntime/qv4executableallocator_p.h b/src/qml/jsruntime/qv4executableallocator_p.h
index 013c6d7120..f98f2c7d33 100644
--- a/src/qml/jsruntime/qv4executableallocator_p.h
+++ b/src/qml/jsruntime/qv4executableallocator_p.h
@@ -130,7 +130,7 @@ public:
private:
QMultiMap<size_t, Allocation*> freeAllocations;
QMap<quintptr, ChunkOfPages*> chunks;
- mutable QMutex mutex;
+ mutable QRecursiveMutex mutex;
};
}
diff --git a/src/qml/jsruntime/qv4executablecompilationunit.cpp b/src/qml/jsruntime/qv4executablecompilationunit.cpp
index a9283cfa54..950e0b10ea 100644
--- a/src/qml/jsruntime/qv4executablecompilationunit.cpp
+++ b/src/qml/jsruntime/qv4executablecompilationunit.cpp
@@ -694,7 +694,7 @@ bool ExecutableCompilationUnit::saveToDisk(const QUrl &unitUrl, QString *errorSt
}
return CompiledData::SaveableUnitPointer(unitData()).saveToDisk<char>(
- [this, &unitUrl, errorString](const char *data, quint32 size) {
+ [&unitUrl, errorString](const char *data, quint32 size) {
return CompiledData::SaveableUnitPointer::writeDataToFile(localCacheFilePath(unitUrl), data,
size, errorString);
});
diff --git a/src/qml/jsruntime/qv4promiseobject.cpp b/src/qml/jsruntime/qv4promiseobject.cpp
index b32e227b58..27075e96a0 100644
--- a/src/qml/jsruntime/qv4promiseobject.cpp
+++ b/src/qml/jsruntime/qv4promiseobject.cpp
@@ -86,6 +86,7 @@ namespace QV4 {
namespace Promise {
const int PROMISE_REACTION_EVENT = QEvent::registerEventType();
+const int PROMISE_RESOLVE_THENABLE_EVENT = QEvent::registerEventType();
struct ReactionEvent : public QEvent
{
@@ -99,6 +100,18 @@ struct ReactionEvent : public QEvent
QV4::PersistentValue resolution;
};
+struct ResolveThenableEvent : public QEvent
+{
+ ResolveThenableEvent(ExecutionEngine *e, const PromiseObject *promise_, const Object *thenable_, const FunctionObject *then_)
+ : QEvent(QEvent::Type(PROMISE_RESOLVE_THENABLE_EVENT)), promise(e, *promise_), thenable(e, *thenable_), then(e, *then_)
+ {}
+
+ QV4::PersistentValue promise;
+ QV4::PersistentValue thenable;
+ QV4::PersistentValue then;
+};
+
+
} // namespace Promise
} // namespace QV4
QT_END_NAMESPACE
@@ -115,6 +128,11 @@ void ReactionHandler::addReaction(ExecutionEngine *e, const Value *reaction, con
QCoreApplication::postEvent(this, new ReactionEvent(e, reaction, value));
}
+void ReactionHandler::addResolveThenable(ExecutionEngine *e, const PromiseObject *promise, const Object *thenable, const FunctionObject *then)
+{
+ QCoreApplication::postEvent(this, new ResolveThenableEvent(e, promise, thenable, then));
+}
+
void ReactionHandler::customEvent(QEvent *event)
{
if (event)
@@ -122,6 +140,9 @@ void ReactionHandler::customEvent(QEvent *event)
const int type = event->type();
if (type == PROMISE_REACTION_EVENT)
executeReaction(static_cast<ReactionEvent*>(event));
+
+ if (type == PROMISE_RESOLVE_THENABLE_EVENT)
+ executeResolveThenable(static_cast<ResolveThenableEvent*>(event));
}
}
@@ -159,6 +180,7 @@ void ReactionHandler::executeReaction(ReactionEvent *event)
}
}
+
namespace {
class FunctionBuilder {
@@ -200,6 +222,26 @@ public:
}
+
+void ReactionHandler::executeResolveThenable(ResolveThenableEvent *event)
+{
+ Scope scope(event->then.engine());
+ JSCallData jsCallData(scope, 2);
+ PromiseObject *promise = event->promise.as<PromiseObject>();
+ ScopedFunctionObject resolve {scope, FunctionBuilder::makeResolveFunction(scope.engine, promise->d())};
+ ScopedFunctionObject reject {scope, FunctionBuilder::makeRejectFunction(scope.engine, promise->d())};
+ jsCallData->args[0] = resolve;
+ jsCallData.args[1] = reject;
+ jsCallData->thisObject = event->thenable.as<QV4::Object>();
+ event->then.as<const FunctionObject>()->call(jsCallData);
+ if (scope.engine->hasException) {
+ JSCallData rejectCallData(scope, 1);
+ rejectCallData->args[0] = scope.engine->catchException();
+ Scoped<RejectWrapper> reject {scope, scope.engine->memoryManager->allocate<QV4::RejectWrapper>()};
+ reject->call(rejectCallData);
+ }
+}
+
void Heap::PromiseObject::setState(PromiseObject::State state)
{
this->state = state;
@@ -363,23 +405,36 @@ void Heap::RejectWrapper::init()
Heap::FunctionObject::init();
}
+ReturnedValue PromiseCtor::virtualCall(const FunctionObject *f, const Value *, const Value *, int)
+{
+ // 25.4.3.1 Promise ( executor )
+ // 1. If NewTarget is undefined, throw a TypeError exception.
+ Scope scope(f);
+ THROW_TYPE_ERROR();
+}
ReturnedValue PromiseCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget)
{
+ // 25.4.3.1 Promise ( executor )
+
Scope scope(f);
- if (argc != 1)
+ if (argc == 0) // If there are no arguments, argument 1 will be undefined ==> thus not callable ==> type error
THROW_TYPE_ERROR();
ScopedFunctionObject executor(scope, argv[0].as<const FunctionObject>());
- if (!executor)
- THROW_TYPE_ERROR();
+ if (!executor) //2. If IsCallable(executor) is false
+ THROW_TYPE_ERROR(); // throw a TypeError exception
Scoped<PromiseObject> a(scope, scope.engine->newPromiseObject());
if (scope.engine->hasException)
return Encode::undefined();
- a->d()->state = Heap::PromiseObject::Pending;
+ a->d()->state = Heap::PromiseObject::Pending; //4. Set promise.[[PromiseState]] to "pending"
+ // 5. Set promise.[[PromiseFulfillReactions]] to a new empty List.
+ // 6. Set promise.[[PromiseRejectReactions]] to a new empty List.
+ // 7. Set promise.[[PromiseIsHandled]] to false.
+ // happens in constructor of a
ScopedFunctionObject resolve(scope, FunctionBuilder::makeResolveFunction(scope.engine, a->d()));
ScopedFunctionObject reject(scope, FunctionBuilder::makeRejectFunction(scope.engine, a->d()));
@@ -387,13 +442,15 @@ ReturnedValue PromiseCtor::virtualCallAsConstructor(const FunctionObject *f, con
JSCallData jsCallData(scope, 2);
jsCallData->args[0] = resolve;
jsCallData->args[1] = reject;
- jsCallData->thisObject = a;
+ //jsCallData->thisObject = a; VERIFY corretness, but this should be undefined (see below)
- executor->call(jsCallData);
+ executor->call(jsCallData); // 9. Let completion be Call(executor, undefined, « resolvingFunctions.[[Resolve]], resolvingFunctions.[[Reject]] »).
if (scope.engine->hasException) {
- a->d()->state = Heap::PromiseObject::Rejected;
- a->d()->resolution.set(scope.engine, Value::fromReturnedValue(scope.engine->catchException()));
+ ScopedValue exception {scope, scope.engine->catchException()};
+ JSCallData callData {scope, 1};
+ callData.args[0] = exception;
+ reject->call(callData);
}
if (newTarget)
@@ -402,42 +459,42 @@ ReturnedValue PromiseCtor::virtualCallAsConstructor(const FunctionObject *f, con
return a->asReturnedValue();
}
-ReturnedValue PromiseCtor::virtualCall(const FunctionObject *f, const Value *, const Value *, int)
-{
- Scope scope(f);
- THROW_TYPE_ERROR();
-}
ReturnedValue PromiseCtor::method_resolve(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
{
+ // 25.4.4.5Promise.resolve ( x )
Scope scope(f);
ExecutionEngine* e = scope.engine;
- if (!thisObject || !thisObject->isObject())
+ if (!thisObject || !thisObject->isObject()) // 2. If Type(C) is not Object, throw a TypeError exception
THROW_TYPE_ERROR();
- ScopedValue argument(scope);
+ ScopedValue x(scope);
if (argc < 1) {
- argument = Encode::undefined();
+ x = Encode::undefined();
} else {
- argument = argv[0];
+ x = argv[0];
}
- if (isPromise(argument) && argument->isObject()) {
+ // 3. If IsPromise(x) is true, then
+ if (isPromise(x) && x->isObject()) {
ScopedObject so(scope, thisObject);
- ScopedObject constructor(scope, argument->objectValue()->get(e->id_constructor()));
- if (so->d() == constructor->d())
- return argument->asReturnedValue();
+ // Let xConstructor be ? Get(x, "constructor").
+ ScopedObject constructor(scope, x->objectValue()->get(e->id_constructor()));
+ if (so->d() == constructor->d()) // If SameValue(xConstructor, C) is true, return x.
+ return x->asReturnedValue();
}
+ // Let promiseCapability be ? NewPromiseCapability(C).
Scoped<PromiseCapability> capability(scope, e->memoryManager->allocate<QV4::PromiseCapability>());
ScopedObject newPromise(scope, e->newPromiseObject(thisObject->as<const FunctionObject>(), capability));
if (!newPromise || !isCallable(capability->d()->resolve) || !isCallable(capability->d()->reject))
THROW_TYPE_ERROR();
+ // Perform ? Call(promiseCapability.[[Resolve]], undefined, « x »).
ScopedValue undefined(scope, Value::undefinedValue());
ScopedFunctionObject resolve(scope, capability->d()->resolve);
- resolve->call(undefined, argument, 1);
+ resolve->call(undefined, x, 1);
return newPromise.asReturnedValue();
}
@@ -447,16 +504,18 @@ ReturnedValue PromiseCtor::method_reject(const FunctionObject *f, const Value *t
Scope scope(f);
ExecutionEngine *e = scope.engine;
+ // 2. If Type(C) is not Object, throw a TypeError exception.
if (!thisObject || !thisObject->isObject())
THROW_TYPE_ERROR();
- ScopedValue argument(scope);
+ ScopedValue r(scope);
if (argc < 1) {
- argument = Encode::undefined();
+ r = Encode::undefined();
} else {
- argument = argv[0];
+ r = argv[0];
}
+ // 3. Let promiseCapability be ? NewPromiseCapability(C).
Scoped<PromiseCapability> capability(scope, e->memoryManager->allocate<QV4::PromiseCapability>());
ScopedObject newPromise(scope, e->newPromiseObject(thisObject->as<const FunctionObject>(), capability));
@@ -465,7 +524,8 @@ ReturnedValue PromiseCtor::method_reject(const FunctionObject *f, const Value *t
ScopedValue undefined(scope, Value::undefinedValue());
ScopedFunctionObject reject(scope, capability->d()->reject.as<const FunctionObject>());
- reject->call(undefined, argument, 1);
+ // Perform ? Call(promiseCapability.[[Reject]], undefined, « r »).
+ reject->call(undefined, r, 1);
return newPromise.asReturnedValue();
}
@@ -475,6 +535,7 @@ ReturnedValue PromiseCtor::method_all(const FunctionObject *f, const Value *this
Scope scope(f);
ExecutionEngine* e = scope.engine;
+ // 2. If Type(C) is not Object, throw a TypeError exception.
if (!thisObject || !thisObject->isObject())
THROW_TYPE_ERROR();
@@ -777,6 +838,7 @@ void PromisePrototype::init(ExecutionEngine *engine, Object *ctor)
ReturnedValue PromisePrototype::method_then(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
{
+ // 25.4.5.3 Promise.prototype.then
Scope scope(f);
ExecutionEngine* e = scope.engine;
@@ -804,6 +866,7 @@ ReturnedValue PromisePrototype::method_then(const FunctionObject *f, const Value
if (!constructor || scope.hasException())
THROW_TYPE_ERROR();
+ // 4. Let resultCapability be ? NewPromiseCapability(C).
ScopedObject nextPromise(scope, e->newPromiseObject(constructor, capability));
capability->d()->promise.set(scope.engine, nextPromise);
@@ -811,21 +874,25 @@ ReturnedValue PromisePrototype::method_then(const FunctionObject *f, const Value
Scoped<PromiseReaction> rejectReaction(scope, Heap::PromiseReaction::createRejectReaction(scope.engine, capability, onRejected));
ScopedValue resolution(scope, promise->d()->resolution);
- if (promise->d()->isPending()) {
+ if (promise->d()->isPending()) { // 7. If promise.[[PromiseState]] is "pending"
{
+ // Append fulfillReaction as the last element of the List that is promise.[[PromiseFulfillReactions]].
ScopedArrayObject a(scope, promise->d()->fulfillReactions);
ScopedValue newValue(scope, fulfillReaction->d());
a->push_back(newValue);
}
{
+ // Append rejectReaction as the last element of the List that is promise.[[PromiseRejectReactions]].
ScopedArrayObject a(scope, promise->d()->rejectReactions);
ScopedValue newValue(scope, rejectReaction->d());
a->push_back(newValue);
}
- } else if (promise->d()->isFulfilled()) {
- fulfillReaction->as<QV4::PromiseReaction>()->d()->triggerWithValue(e, resolution);
- } else if (promise->d()->isRejected()) {
+ } else if (promise->d()->isFulfilled()) { // 8. Else if promise.[[PromiseState]] is "fulfilled", then
+ // Perform EnqueueJob("PromiseJobs", PromiseReactionJob, « fulfillReaction, value »).
+ fulfillReaction->as<QV4::PromiseReaction>()->d()->triggerWithValue(e, resolution); // Perform EnqueueJob("PromiseJobs", PromiseReactionJob, « fulfillReaction, value »).
+ } else if (promise->d()->isRejected()) { // 9. Else
+ // Perform EnqueueJob("PromiseJobs", PromiseReactionJob, « rejectReaction, reason »).
rejectReaction->as<QV4::PromiseReaction>()->d()->triggerWithValue(e, resolution);
} else {
Q_ASSERT(false);
@@ -889,7 +956,6 @@ ReturnedValue CapabilitiesExecutorWrapper::virtualCall(const FunctionObject *f,
if (argc >= 2 && !argv[1].isUndefined())
capabilities->reject.set(scope.engine, argv[1]);
- // TODO: return?
return Encode::undefined();
}
@@ -929,27 +995,60 @@ ReturnedValue ResolveElementWrapper::virtualCall(const FunctionObject *f, const
ReturnedValue ResolveWrapper::virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
{
+ // 25.4.1.3.2 (ecmase-262/8.0)
+
Q_UNUSED(thisObject);
Scope scope(f);
const ResolveWrapper *self = static_cast<const ResolveWrapper*>(f);
Scoped<PromiseObject> promise(scope, self->d()->promise);
- if (self->d()->alreadyResolved || !promise->d()->isPending())
+ // 4. If alreadyRseolved.[[Value]] is true, return undefined
+ if (self->d()->alreadyResolved || !promise->d()->isPending()) // Why check for pending?
return Encode::undefined();
- ScopedValue value(scope);
+ // 5. Set alreadyResolved.[[Value]] to true
+ self->d()->alreadyResolved = true;
+
+ ScopedValue resolution(scope);
if (argc == 1) {
- value = argv[0];
+ resolution = argv[0];
} else {
- value = Encode::undefined();
+ resolution = Encode::undefined();
}
- self->d()->alreadyResolved = true;
- promise->d()->setState(Heap::PromiseObject::Fulfilled);
- promise->d()->resolution.set(scope.engine, value);
-
- promise->d()->triggerFullfillReactions(scope.engine);
+ if (!resolution->isObject()) { // 7 If Type(resolution) is not Object
+ // then Return FullFillPromise(promise, resolution)
+ // (FullFillPromise will return undefined, so we share the return with the other path which also returns undefined
+ promise->d()->setState(Heap::PromiseObject::Fulfilled);
+ promise->d()->resolution.set(scope.engine, resolution);
+ promise->d()->triggerFullfillReactions(scope.engine);
+ } else {
+ //PromiseObject *promise = resolution->as<PromiseObject>();
+ auto resolutionObject = resolution->as<Object>();
+ ScopedString thenName(scope, scope.engine->newIdentifier(QStringLiteral("then")));
+
+ // 8. Let then be Get(resolution, then)
+ ScopedFunctionObject thenAction { scope, resolutionObject->get(thenName)};
+ // 9. If then is an abrupt completion, then
+ if (scope.engine->hasException) {
+ // Return RecjectPromise(promise, then.[[Value]]
+ ScopedValue thenValue {scope, scope.engine->catchException()};
+ promise->d()->setState(Heap::PromiseObject::Rejected);
+ promise->d()->resolution.set(scope.engine, thenValue);
+ promise->d()->triggerRejectReactions(scope.engine);
+ } else {
+ // 10. Let thenAction be then.[[Value]]
+ if (!thenAction) { // 11. If IsCallable(thenAction) is false
+ promise->d()->setState(Heap::PromiseObject::Fulfilled);
+ promise->d()->resolution.set(scope.engine, resolution);
+ promise->d()->triggerFullfillReactions(scope.engine);
+ } else {
+ // 12. Perform EnqueueJob("PromiseJobs", PromiseResolveThenableJob, « promise, resolution, thenAction »).
+ scope.engine->getPromiseReactionHandler()->addResolveThenable(scope.engine, promise.getPointer(), resolutionObject, thenAction);
+ }
+ }
+ }
return Encode::undefined();
}
@@ -972,11 +1071,25 @@ ReturnedValue RejectWrapper::virtualCall(const FunctionObject *f, const Value *t
value = Encode::undefined();
}
- self->d()->alreadyResolved = true;
- promise->d()->setState(Heap::PromiseObject::Rejected);
- promise->d()->resolution.set(scope.engine, value);
+ if (!isPromise(value)) {
+ self->d()->alreadyResolved = true;
+ promise->d()->setState(Heap::PromiseObject::Rejected);
+ promise->d()->resolution.set(scope.engine, value);
+
+ promise->d()->triggerRejectReactions(scope.engine);
- promise->d()->triggerRejectReactions(scope.engine);
+ } else {
+ PromiseObject *promise = value->as<PromiseObject>();
+ ScopedString thenName(scope, scope.engine->newIdentifier(QStringLiteral("catch")));
+
+ ScopedFunctionObject then(scope, promise->get(thenName));
+ JSCallData jsCallData(scope, 2);
+ jsCallData->args[0] = *f;
+ jsCallData->args[1] = Encode::undefined();
+ jsCallData->thisObject = value;
+
+ then->call(jsCallData);
+ }
return Encode::undefined();
}
diff --git a/src/qml/jsruntime/qv4promiseobject_p.h b/src/qml/jsruntime/qv4promiseobject_p.h
index bce59b19a7..8a3724e07d 100644
--- a/src/qml/jsruntime/qv4promiseobject_p.h
+++ b/src/qml/jsruntime/qv4promiseobject_p.h
@@ -62,6 +62,7 @@ struct PromiseCapability;
namespace Promise {
struct ReactionEvent;
+struct ResolveThenableEvent;
class ReactionHandler : public QObject
{
@@ -69,13 +70,15 @@ class ReactionHandler : public QObject
public:
ReactionHandler(QObject *parent = nullptr);
- virtual ~ReactionHandler();
+ virtual ~ReactionHandler() override;
void addReaction(ExecutionEngine *e, const Value *reaction, const Value *value);
+ void addResolveThenable(ExecutionEngine *e, const PromiseObject *promise, const Object *thenable, const FunctionObject *then);
protected:
- void customEvent(QEvent *event);
+ void customEvent(QEvent *event) override;
void executeReaction(ReactionEvent *event);
+ void executeResolveThenable(ResolveThenableEvent *event);
};
} // Promise
diff --git a/src/qml/parser/qqmljs.g b/src/qml/parser/qqmljs.g
index 82b040232a..6c9760e472 100644
--- a/src/qml/parser/qqmljs.g
+++ b/src/qml/parser/qqmljs.g
@@ -1396,6 +1396,14 @@ UiObjectMember: FunctionDeclarationWithTypes;
} break;
./
+UiObjectMember: GeneratorExpression;
+/.
+ case $rule_number: {
+ auto node = new (pool) AST::UiSourceElement(sym(1).Node);
+ sym(1).Node = node;
+ } break;
+./
+
UiObjectMember: VariableStatement;
/.
case $rule_number: {
diff --git a/src/qml/parser/qqmljsast_p.h b/src/qml/parser/qqmljsast_p.h
index 1502298d14..39194068bf 100644
--- a/src/qml/parser/qqmljsast_p.h
+++ b/src/qml/parser/qqmljsast_p.h
@@ -3351,7 +3351,7 @@ public:
SourceLocation firstSourceLocation() const override
{
- if (FunctionDeclaration *funDecl = cast<FunctionDeclaration *>(sourceElement))
+ if (FunctionExpression *funDecl = sourceElement->asFunctionDefinition())
return funDecl->firstSourceLocation();
else if (VariableStatement *varStmt = cast<VariableStatement *>(sourceElement))
return varStmt->firstSourceLocation();
@@ -3361,7 +3361,7 @@ public:
SourceLocation lastSourceLocation() const override
{
- if (FunctionDeclaration *funDecl = cast<FunctionDeclaration *>(sourceElement))
+ if (FunctionExpression *funDecl = sourceElement->asFunctionDefinition())
return funDecl->lastSourceLocation();
else if (VariableStatement *varStmt = cast<VariableStatement *>(sourceElement))
return varStmt->lastSourceLocation();
diff --git a/src/qml/qml/qqml.cpp b/src/qml/qml/qqml.cpp
index 613816e3f7..8a50b51b5d 100644
--- a/src/qml/qml/qqml.cpp
+++ b/src/qml/qml/qqml.cpp
@@ -76,6 +76,26 @@ int qmlTypeId(const char *uri, int versionMajor, int versionMinor, const char *q
return QQmlMetaType::typeId(uri, versionMajor, versionMinor, qmlName);
}
+// From qqmlprivate.h
+QObject *QQmlPrivate::RegisterSingletonFunctor::operator()(QQmlEngine *qeng, QJSEngine *)
+{
+ if (qeng->thread() != m_object->thread()) {
+ QQmlError error;
+ error.setDescription(QLatin1String("Registered object must live in the same thread as the engine it was registered with"));
+ QQmlEnginePrivate::get(qeng)->warning(qeng, error);
+ return nullptr;
+ }
+ if (alreadyCalled) {
+ QQmlError error;
+ error.setDescription(QLatin1String("Singleton registered by registerSingletonInstance must only be accessed from one engine"));
+ QQmlEnginePrivate::get(qeng)->warning(qeng, error);
+ return nullptr;
+ }
+ alreadyCalled = true;
+ qeng->setObjectOwnership(m_object, QQmlEngine::CppOwnership);
+ return m_object;
+};
+
/*
This method is "over generalized" to allow us to (potentially) register more types of things in
the future without adding exported symbols.
diff --git a/src/qml/qml/qqml.h b/src/qml/qml/qqml.h
index 4f3bfb76b0..bfd1c88b28 100644
--- a/src/qml/qml/qqml.h
+++ b/src/qml/qml/qqml.h
@@ -667,6 +667,15 @@ inline int qmlRegisterSingletonType(const char *uri, int versionMajor, int versi
return QQmlPrivate::qmlregister(QQmlPrivate::SingletonRegistration, &api);
}
+template<typename T>
+inline auto qmlRegisterSingletonInstance(const char *uri, int versionMajor, int versionMinor,
+ const char *typeName, T *cppObject) -> typename std::enable_if<std::is_base_of<QObject, T>::value, int>::type
+{
+ QQmlPrivate::RegisterSingletonFunctor registrationFunctor;
+ registrationFunctor.m_object = cppObject;
+ return qmlRegisterSingletonType<T>(uri, versionMajor, versionMinor, typeName, registrationFunctor);
+}
+
inline int qmlRegisterSingletonType(const QUrl &url, const char *uri, int versionMajor, int versionMinor, const char *qmlName)
{
if (url.isRelative()) {
diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp
index 7ba5fc08a5..0580efe0d2 100644
--- a/src/qml/qml/qqmlengine.cpp
+++ b/src/qml/qml/qqmlengine.cpp
@@ -2454,17 +2454,20 @@ QJSValue QQmlEnginePrivate::singletonInstance<QJSValue>(const QQmlType &type)
} else if (siinfo->qobjectCallback) {
QObject *o = siinfo->qobjectCallback(q, q);
if (!o) {
- qFatal("qmlRegisterSingletonType(): \"%s\" is not available because the callback function returns a null pointer.",
- qPrintable(QString::fromUtf8(type.typeName())));
+ QQmlError error;
+ error.setMessageType(QtMsgType::QtCriticalMsg);
+ error.setDescription(QString::asprintf("qmlRegisterSingletonType(): \"%s\" is not available because the callback function returns a null pointer.",
+ qPrintable(QString::fromUtf8(type.typeName()))));
+ warning(error);
+ } else {
+ // if this object can use a property cache, create it now
+ QQmlData::ensurePropertyCache(q, o);
}
- // if this object can use a property cache, create it now
- QQmlData::ensurePropertyCache(q, o);
// even though the object is defined in C++, qmlContext(obj) and qmlEngine(obj)
// should behave identically to QML singleton types.
q->setContextForObject(o, new QQmlContext(q->rootContext(), q));
value = q->newQObject(o);
singletonInstances.insert(type, value);
-
} else if (!siinfo->url.isEmpty()) {
QQmlComponent component(q, siinfo->url, QQmlComponent::PreferSynchronous);
QObject *o = component.beginCreate(q->rootContext());
diff --git a/src/qml/qml/qqmlerror.cpp b/src/qml/qml/qqmlerror.cpp
index 739d5ce4cd..0b94ed3b49 100644
--- a/src/qml/qml/qqmlerror.cpp
+++ b/src/qml/qml/qqmlerror.cpp
@@ -38,6 +38,7 @@
****************************************************************************/
#include "qqmlerror.h"
+#include "qqmlfile.h"
#include "qqmlsourcecoordinate_p.h"
#include <private/qqmljsdiagnosticmessage_p.h>
@@ -307,8 +308,8 @@ QDebug operator<<(QDebug debug, const QQmlError &error)
QUrl url = error.url();
- if (error.line() > 0 && url.scheme() == QLatin1String("file")) {
- QString file = url.toLocalFile();
+ if (error.line() > 0 && (url.scheme() == QLatin1String("file") || url.scheme() == QLatin1String("qrc"))) {
+ QString file = QQmlFile::urlToLocalFileOrQrc(url);
QFile f(file);
if (f.open(QIODevice::ReadOnly)) {
QByteArray data = f.readAll();
diff --git a/src/qml/qml/qqmlimport.cpp b/src/qml/qml/qqmlimport.cpp
index f4cdb45aff..7c9c0da01a 100644
--- a/src/qml/qml/qqmlimport.cpp
+++ b/src/qml/qml/qqmlimport.cpp
@@ -395,9 +395,18 @@ void findCompositeSingletons(const QQmlImportNamespace &set, QList<QQmlImports::
const QQmlDirComponents &components = import->qmlDirComponents;
+ const int importMajorVersion = import->majversion;
+ const int importMinorVersion = import->minversion;
+ auto shouldSkipSingleton = [importMajorVersion, importMinorVersion](int singletonMajorVersion, int singletonMinorVersion) -> bool {
+ return importMajorVersion != -1 &&
+ (singletonMajorVersion > importMajorVersion || (singletonMajorVersion == importMajorVersion && singletonMinorVersion > importMinorVersion));
+ };
+
ConstIterator cend = components.constEnd();
for (ConstIterator cit = components.constBegin(); cit != cend; ++cit) {
if (cit->singleton && excludeBaseUrl(import->url, cit->fileName, baseUrl.toString())) {
+ if (shouldSkipSingleton(cit->majorVersion, cit->minorVersion))
+ continue;
QQmlImports::CompositeSingletonReference ref;
ref.typeName = cit->typeName;
ref.prefix = set.prefix;
@@ -408,7 +417,9 @@ void findCompositeSingletons(const QQmlImportNamespace &set, QList<QQmlImports::
}
if (QQmlTypeModule *module = QQmlMetaType::typeModule(import->uri, import->majversion)) {
- module->walkCompositeSingletons([&resultList, &set](const QQmlType &singleton) {
+ module->walkCompositeSingletons([&resultList, &set, &shouldSkipSingleton](const QQmlType &singleton) {
+ if (shouldSkipSingleton(singleton.majorVersion(), singleton.minorVersion()))
+ return;
QQmlImports::CompositeSingletonReference ref;
ref.typeName = singleton.elementName();
ref.prefix = set.prefix;
@@ -1947,7 +1958,9 @@ void QQmlImportDatabase::setImportPathList(const QStringList &paths)
if (qmlImportTrace())
qDebug().nospace() << "QQmlImportDatabase::setImportPathList: " << paths;
- fileImportPath = paths;
+ fileImportPath.clear();
+ for (auto it = paths.crbegin(); it != paths.crend(); ++it)
+ addImportPath(*it);
// Our existing cached paths may have been invalidated
clearDirCache();
diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp
index f21427ff69..2c641d3845 100644
--- a/src/qml/qml/qqmlmetatype.cpp
+++ b/src/qml/qml/qqmlmetatype.cpp
@@ -60,7 +60,7 @@ struct LockedData : private QQmlMetaTypeData
};
Q_GLOBAL_STATIC(LockedData, metaTypeData)
-Q_GLOBAL_STATIC_WITH_ARGS(QMutex, metaTypeDataLock, (QMutex::Recursive))
+Q_GLOBAL_STATIC(QRecursiveMutex, metaTypeDataLock)
class QQmlMetaTypeDataPtr
{
@@ -804,7 +804,7 @@ QQmlType QQmlMetaType::typeForUrl(const QString &urlString,
return QQmlType();
}
-QMutex *QQmlMetaType::typeRegistrationLock()
+QRecursiveMutex *QQmlMetaType::typeRegistrationLock()
{
return metaTypeDataLock();
}
diff --git a/src/qml/qml/qqmlmetatype_p.h b/src/qml/qml/qqmlmetatype_p.h
index c2535a7fd5..6c2b0bb2a6 100644
--- a/src/qml/qml/qqmlmetatype_p.h
+++ b/src/qml/qml/qqmlmetatype_p.h
@@ -58,7 +58,7 @@
QT_BEGIN_NAMESPACE
class QQmlTypeModule;
-class QMutex;
+class QRecursiveMutex;
class QQmlError;
namespace QV4 { class ExecutableCompilationUnit; }
@@ -160,7 +160,7 @@ public:
static void prependCachedUnitLookupFunction(QQmlPrivate::QmlUnitCacheLookupFunction handler);
static void removeCachedUnitLookupFunction(QQmlPrivate::QmlUnitCacheLookupFunction handler);
- static QMutex *typeRegistrationLock();
+ static QRecursiveMutex *typeRegistrationLock();
static QString prettyTypeName(const QObject *object);
diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp
index d5681b3449..f89608cd5d 100644
--- a/src/qml/qml/qqmlobjectcreator.cpp
+++ b/src/qml/qml/qqmlobjectcreator.cpp
@@ -57,6 +57,7 @@
#include <private/qqmldebugserviceinterfaces_p.h>
#include <private/qqmlscriptdata_p.h>
#include <private/qjsvalue_p.h>
+#include <private/qv4generatorobject_p.h>
#include <qtqml_tracepoints_p.h>
@@ -1135,7 +1136,10 @@ void QQmlObjectCreator::setupFunctions()
if (!property->isVMEFunction())
continue;
- function = QV4::FunctionObject::createScriptFunction(qmlContext, runtimeFunction);
+ if (runtimeFunction->isGenerator())
+ function = QV4::GeneratorFunction::create(qmlContext, runtimeFunction);
+ else
+ function = QV4::FunctionObject::createScriptFunction(qmlContext, runtimeFunction);
_vmeMetaObject->setVmeMethod(property->coreIndex(), function);
}
}
diff --git a/src/qml/qml/qqmlprivate.h b/src/qml/qml/qqmlprivate.h
index eff78195d9..e6dd5e0b16 100644
--- a/src/qml/qml/qqmlprivate.h
+++ b/src/qml/qml/qqmlprivate.h
@@ -322,6 +322,13 @@ namespace QQmlPrivate
int Q_QML_EXPORT qmlregister(RegistrationType, void *);
void Q_QML_EXPORT qmlunregister(RegistrationType, quintptr);
+ struct Q_QML_EXPORT RegisterSingletonFunctor
+ {
+ QObject *operator()(QQmlEngine *, QJSEngine *);
+
+ QObject *m_object;
+ bool alreadyCalled = false;
+ };
}
QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmlproperty.cpp b/src/qml/qml/qqmlproperty.cpp
index c8166695ba..5f57e0eca1 100644
--- a/src/qml/qml/qqmlproperty.cpp
+++ b/src/qml/qml/qqmlproperty.cpp
@@ -1216,10 +1216,18 @@ bool QQmlPropertyPrivate::write(QObject *object,
if (propertyType == variantType && !isUrl && propertyType != qMetaTypeId<QList<QUrl>>() && !property.isQList()) {
return property.writeProperty(object, const_cast<void *>(value.constData()), flags);
} else if (property.isQObject()) {
- QQmlMetaObject valMo = rawMetaObjectForType(enginePriv, variantType);
+ QVariant val = value;
+ int varType = variantType;
+ if (variantType == QMetaType::Nullptr) {
+ // This reflects the fact that you can assign a nullptr to a QObject pointer
+ // Without the change to QObjectStar, rawMetaObjectForType would not give us a QQmlMetaObject
+ varType = QMetaType::QObjectStar;
+ val = QVariant(QMetaType::QObjectStar, nullptr);
+ }
+ QQmlMetaObject valMo = rawMetaObjectForType(enginePriv, varType);
if (valMo.isNull())
return false;
- QObject *o = *static_cast<QObject *const *>(value.constData());
+ QObject *o = *static_cast<QObject *const *>(val.constData());
QQmlMetaObject propMo = rawMetaObjectForType(enginePriv, propertyType);
if (o)
diff --git a/src/qml/qml/qqmlxmlhttprequest.cpp b/src/qml/qml/qqmlxmlhttprequest.cpp
index b435f8ef66..4db0562c0e 100644
--- a/src/qml/qml/qqmlxmlhttprequest.cpp
+++ b/src/qml/qml/qqmlxmlhttprequest.cpp
@@ -1570,7 +1570,8 @@ void QQmlXMLHttpRequest::dispatchCallbackNow(Object *thisObj, bool done, bool er
if (scope.engine->hasException) {
QQmlError error = scope.engine->catchExceptionAsQmlError();
- QQmlEnginePrivate::warning(QQmlEnginePrivate::get(scope.engine->qmlEngine()), error);
+ QQmlEnginePrivate *qmlEnginePrivate = scope.engine->qmlEngine() ? QQmlEnginePrivate::get(scope.engine->qmlEngine()) : nullptr;
+ QQmlEnginePrivate::warning(qmlEnginePrivate, error);
}
};
@@ -1761,8 +1762,13 @@ ReturnedValue QQmlXMLHttpRequestCtor::method_open(const FunctionObject *b, const
// Argument 1 - URL
QUrl url = QUrl(argv[1].toQStringNoThrow());
- if (url.isRelative())
- url = scope.engine->callingQmlContext()->resolvedUrl(url);
+ if (url.isRelative()) {
+ QQmlContextData *qmlContextData = scope.engine->callingQmlContext();
+ if (qmlContextData)
+ url = qmlContextData->resolvedUrl(url);
+ else
+ url = scope.engine->resolvedUrl(url.url());
+ }
bool async = true;
// Argument 2 - async (optional)
diff --git a/src/qml/types/qqmlbind.cpp b/src/qml/types/qqmlbind.cpp
index 5067b6a02e..4b60108597 100644
--- a/src/qml/types/qqmlbind.cpp
+++ b/src/qml/types/qqmlbind.cpp
@@ -70,12 +70,8 @@ public:
, delayed(false)
, pendingEval(false)
, restoreBinding(true)
-#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
, restoreValue(false)
, restoreModeExplicit(false)
-#else
- , restoreValue(true)
-#endif
{}
~QQmlBindPrivate() { }
@@ -93,9 +89,7 @@ public:
bool pendingEval:1;
bool restoreBinding:1;
bool restoreValue:1;
-#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
bool restoreModeExplicit:1;
-#endif
void validate(QObject *binding) const;
void clearPrev();
@@ -365,8 +359,7 @@ void QQmlBind::setDelayed(bool delayed)
\li Binding.RestoreBindingOrValue The original value is always restored.
\list
- \warning The default value is Binding.RestoreBinding. This will change in
- Qt 5.15 to Binding.RestoreBindingOrValue.
+ The default value is Binding.RestoreBinding.
If you rely on any specific behavior regarding the restoration of plain
values when bindings get disabled you should migrate to explicitly set the
@@ -392,9 +385,7 @@ void QQmlBind::setRestoreMode(RestorationMode newMode)
if (newMode != restoreMode()) {
d->restoreValue = (newMode & RestoreValue);
d->restoreBinding = (newMode & RestoreBinding);
-#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
d->restoreModeExplicit = true;
-#endif
emit restoreModeChanged();
}
}
@@ -465,27 +456,11 @@ void QQmlBind::eval()
Q_ASSERT(vmemo);
vmemo->setVMEProperty(propPriv->core.coreIndex(), *d->v4Value.valueRef());
d->clearPrev();
-#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
- } else if (!d->restoreModeExplicit) {
- qmlWarning(this)
- << "Not restoring previous value because restoreMode has not been set."
- << "This behavior is deprecated."
- << "In Qt < 5.15 the default is Binding.RestoreBinding."
- << "In Qt >= 5.15 the default is Binding.RestoreBindingOrValue.";
-#endif
}
} else if (d->prevIsVariant) {
if (d->restoreValue) {
d->prop.write(d->prevValue);
d->clearPrev();
-#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
- } else if (!d->restoreModeExplicit) {
- qmlWarning(this)
- << "Not restoring previous value because restoreMode has not been set."
- << "This behavior is deprecated."
- << "In Qt < 5.15 the default is Binding.RestoreBinding."
- << "In Qt >= 5.15 the default is Binding.RestoreBindingOrValue.";
-#endif
}
}
return;
diff --git a/src/qmlmodels/configure.cmake b/src/qmlmodels/configure.cmake
index b8409f7106..43c3ac8aab 100644
--- a/src/qmlmodels/configure.cmake
+++ b/src/qmlmodels/configure.cmake
@@ -16,7 +16,7 @@
qt_feature("qml_object_model" PRIVATE
SECTION "QML"
- LABEL "QML list model"
+ LABEL "QML object model"
PURPOSE "Provides the ObjectModel and Instantiator QML types."
)
qt_feature("qml_list_model" PRIVATE
diff --git a/src/qmlmodels/configure.json b/src/qmlmodels/configure.json
index 2b4fa4f6e2..84eefed261 100644
--- a/src/qmlmodels/configure.json
+++ b/src/qmlmodels/configure.json
@@ -7,7 +7,7 @@
"features": {
"qml-object-model" : {
- "label": "QML list model",
+ "label": "QML object model",
"purpose": "Provides the ObjectModel and Instantiator QML types.",
"section": "QML",
"output": [ "privateFeature" ]
diff --git a/src/qmlmodels/qqmlmodelsmodule.cpp b/src/qmlmodels/qqmlmodelsmodule.cpp
index 5195f9baad..884b7f78b2 100644
--- a/src/qmlmodels/qqmlmodelsmodule.cpp
+++ b/src/qmlmodels/qqmlmodelsmodule.cpp
@@ -71,6 +71,8 @@ void QQmlModelsModule::defineModule()
qmlRegisterCustomType<QQmlListModel>(uri, 2, 0, "ListModel", new QQmlListModelParser);
#endif
#if QT_CONFIG(qml_delegate_model)
+ qmlRegisterType<QQmlDelegateModel>(uri, 2, 0, "VisualDataModel");
+ qmlRegisterType<QQmlDelegateModelGroup>(uri, 2, 0, "VisualDataGroup");
qmlRegisterType<QQmlDelegateModel>(uri, 2, 1, "DelegateModel");
qmlRegisterType<QQmlDelegateModelGroup>(uri, 2, 1, "DelegateModelGroup");
qmlRegisterType<QQuickPackage>(uri, 2, 0, "Package");
diff --git a/src/quick/CMakeLists.txt b/src/quick/CMakeLists.txt
index 7830a4b547..2cdcee56a1 100644
--- a/src/quick/CMakeLists.txt
+++ b/src/quick/CMakeLists.txt
@@ -185,133 +185,155 @@ add_qt_module(Quick
)
# Resources:
-add_qt_resource(Quick "scenegraph" PREFIX "/qt-project.org/scenegraph" BASE "scenegraph" FILES
- shaders/24bittextmask.frag
- shaders/24bittextmask_core.frag
- shaders/32bitcolortext.frag
- shaders/32bitcolortext_core.frag
- shaders/8bittextmask.frag
- shaders/8bittextmask_core.frag
- shaders/distancefieldoutlinetext.frag
- shaders/distancefieldoutlinetext_core.frag
- shaders/distancefieldshiftedtext.frag
- shaders/distancefieldshiftedtext.vert
- shaders/distancefieldshiftedtext_core.frag
- shaders/distancefieldshiftedtext_core.vert
- shaders/distancefieldtext.frag
- shaders/distancefieldtext.vert
- shaders/distancefieldtext_core.frag
- shaders/distancefieldtext_core.vert
- shaders/flatcolor.frag
- shaders/flatcolor.vert
- shaders/flatcolor_core.frag
- shaders/flatcolor_core.vert
- shaders/hiqsubpixeldistancefieldtext.frag
- shaders/hiqsubpixeldistancefieldtext.vert
- shaders/hiqsubpixeldistancefieldtext_core.frag
- shaders/hiqsubpixeldistancefieldtext_core.vert
- shaders/loqsubpixeldistancefieldtext.frag
- shaders/loqsubpixeldistancefieldtext.vert
- shaders/loqsubpixeldistancefieldtext_core.frag
- shaders/loqsubpixeldistancefieldtext_core.vert
- shaders/opaquetexture.frag
- shaders/opaquetexture.vert
- shaders/opaquetexture_core.frag
- shaders/opaquetexture_core.vert
- shaders/outlinedtext.frag
- shaders/outlinedtext.vert
- shaders/outlinedtext_core.frag
- shaders/outlinedtext_core.vert
- shaders/rendernode.frag
- shaders/rendernode.vert
- shaders/rendernode_core.frag
- shaders/rendernode_core.vert
- shaders/smoothcolor.frag
- shaders/smoothcolor.vert
- shaders/smoothcolor_core.frag
- shaders/smoothcolor_core.vert
- shaders/smoothtexture.frag
- shaders/smoothtexture.vert
- shaders/smoothtexture_core.frag
- shaders/smoothtexture_core.vert
- shaders/sprite.frag
- shaders/sprite.vert
- shaders/sprite_core.frag
- shaders/sprite_core.vert
- shaders/stencilclip.frag
- shaders/stencilclip.vert
- shaders/stencilclip_core.frag
- shaders/stencilclip_core.vert
- shaders/styledtext.frag
- shaders/styledtext.vert
- shaders/styledtext_core.frag
- shaders/styledtext_core.vert
- shaders/textmask.frag
- shaders/textmask.vert
- shaders/textmask_core.frag
- shaders/textmask_core.vert
- shaders/texture.frag
- shaders/texture_core.frag
- shaders/vertexcolor.frag
- shaders/vertexcolor.vert
- shaders/vertexcolor_core.frag
- shaders/vertexcolor_core.vert
- shaders/visualization.frag
- shaders/visualization.vert
- shaders_ng/24bittextmask.frag.qsb
- shaders_ng/32bitcolortext.frag.qsb
- shaders_ng/8bittextmask.frag.qsb
- shaders_ng/8bittextmask_a.frag.qsb
- shaders_ng/distancefieldoutlinetext.frag.qsb
- shaders_ng/distancefieldoutlinetext.vert.qsb
- shaders_ng/distancefieldoutlinetext_a.frag.qsb
- shaders_ng/distancefieldshiftedtext.frag.qsb
- shaders_ng/distancefieldshiftedtext.vert.qsb
- shaders_ng/distancefieldshiftedtext_a.frag.qsb
- shaders_ng/distancefieldtext.frag.qsb
- shaders_ng/distancefieldtext.vert.qsb
- shaders_ng/distancefieldtext_a.frag.qsb
- shaders_ng/flatcolor.frag.qsb
- shaders_ng/flatcolor.vert.qsb
- shaders_ng/hiqsubpixeldistancefieldtext.frag.qsb
- shaders_ng/hiqsubpixeldistancefieldtext.vert.qsb
- shaders_ng/hiqsubpixeldistancefieldtext_a.frag.qsb
- shaders_ng/loqsubpixeldistancefieldtext.frag.qsb
- shaders_ng/loqsubpixeldistancefieldtext.vert.qsb
- shaders_ng/loqsubpixeldistancefieldtext_a.frag.qsb
- shaders_ng/opaquetexture.frag.qsb
- shaders_ng/opaquetexture.vert.qsb
- shaders_ng/outlinedtext.frag.qsb
- shaders_ng/outlinedtext.vert.qsb
- shaders_ng/outlinedtext_a.frag.qsb
- shaders_ng/shadereffect.frag.qsb
- shaders_ng/shadereffect.vert.qsb
- shaders_ng/smoothcolor.frag.qsb
- shaders_ng/smoothcolor.vert.qsb
- shaders_ng/smoothtexture.frag.qsb
- shaders_ng/smoothtexture.vert.qsb
- shaders_ng/sprite.frag.qsb
- shaders_ng/sprite.vert.qsb
- shaders_ng/stencilclip.frag.qsb
- shaders_ng/stencilclip.vert.qsb
- shaders_ng/styledtext.frag.qsb
- shaders_ng/styledtext.vert.qsb
- shaders_ng/styledtext_a.frag.qsb
- shaders_ng/textmask.frag.qsb
- shaders_ng/textmask.vert.qsb
- shaders_ng/texture.frag.qsb
- shaders_ng/texture.vert.qsb
- shaders_ng/vertexcolor.frag.qsb
- shaders_ng/vertexcolor.vert.qsb)
-add_qt_resource(Quick "items" PREFIX "/qt-project.org/items" BASE "items" FILES
- shaders/shadereffect.frag
- shaders/shadereffect.vert
- shaders/shadereffect_core.frag
- shaders/shadereffect_core.vert
- shaders/shadereffectfallback.frag
- shaders/shadereffectfallback.vert
- shaders/shadereffectfallback_core.frag
- shaders/shadereffectfallback_core.vert)
+set(scenegraph_resource_files
+ "shaders/24bittextmask.frag"
+ "shaders/24bittextmask_core.frag"
+ "shaders/32bitcolortext.frag"
+ "shaders/32bitcolortext_core.frag"
+ "shaders/8bittextmask.frag"
+ "shaders/8bittextmask_core.frag"
+ "shaders/distancefieldoutlinetext.frag"
+ "shaders/distancefieldoutlinetext_core.frag"
+ "shaders/distancefieldshiftedtext.frag"
+ "shaders/distancefieldshiftedtext.vert"
+ "shaders/distancefieldshiftedtext_core.frag"
+ "shaders/distancefieldshiftedtext_core.vert"
+ "shaders/distancefieldtext.frag"
+ "shaders/distancefieldtext.vert"
+ "shaders/distancefieldtext_core.frag"
+ "shaders/distancefieldtext_core.vert"
+ "shaders/flatcolor.frag"
+ "shaders/flatcolor.vert"
+ "shaders/flatcolor_core.frag"
+ "shaders/flatcolor_core.vert"
+ "shaders/hiqsubpixeldistancefieldtext.frag"
+ "shaders/hiqsubpixeldistancefieldtext.vert"
+ "shaders/hiqsubpixeldistancefieldtext_core.frag"
+ "shaders/hiqsubpixeldistancefieldtext_core.vert"
+ "shaders/loqsubpixeldistancefieldtext.frag"
+ "shaders/loqsubpixeldistancefieldtext.vert"
+ "shaders/loqsubpixeldistancefieldtext_core.frag"
+ "shaders/loqsubpixeldistancefieldtext_core.vert"
+ "shaders/opaquetexture.frag"
+ "shaders/opaquetexture.vert"
+ "shaders/opaquetexture_core.frag"
+ "shaders/opaquetexture_core.vert"
+ "shaders/outlinedtext.frag"
+ "shaders/outlinedtext.vert"
+ "shaders/outlinedtext_core.frag"
+ "shaders/outlinedtext_core.vert"
+ "shaders/rendernode.frag"
+ "shaders/rendernode.vert"
+ "shaders/rendernode_core.frag"
+ "shaders/rendernode_core.vert"
+ "shaders/smoothcolor.frag"
+ "shaders/smoothcolor.vert"
+ "shaders/smoothcolor_core.frag"
+ "shaders/smoothcolor_core.vert"
+ "shaders/smoothtexture.frag"
+ "shaders/smoothtexture.vert"
+ "shaders/smoothtexture_core.frag"
+ "shaders/smoothtexture_core.vert"
+ "shaders/sprite.frag"
+ "shaders/sprite.vert"
+ "shaders/sprite_core.frag"
+ "shaders/sprite_core.vert"
+ "shaders/stencilclip.frag"
+ "shaders/stencilclip.vert"
+ "shaders/stencilclip_core.frag"
+ "shaders/stencilclip_core.vert"
+ "shaders/styledtext.frag"
+ "shaders/styledtext.vert"
+ "shaders/styledtext_core.frag"
+ "shaders/styledtext_core.vert"
+ "shaders/textmask.frag"
+ "shaders/textmask.vert"
+ "shaders/textmask_core.frag"
+ "shaders/textmask_core.vert"
+ "shaders/texture.frag"
+ "shaders/texture_core.frag"
+ "shaders/vertexcolor.frag"
+ "shaders/vertexcolor.vert"
+ "shaders/vertexcolor_core.frag"
+ "shaders/vertexcolor_core.vert"
+ "shaders/visualization.frag"
+ "shaders/visualization.vert"
+ "shaders_ng/24bittextmask.frag.qsb"
+ "shaders_ng/32bitcolortext.frag.qsb"
+ "shaders_ng/8bittextmask.frag.qsb"
+ "shaders_ng/8bittextmask_a.frag.qsb"
+ "shaders_ng/distancefieldoutlinetext.frag.qsb"
+ "shaders_ng/distancefieldoutlinetext.vert.qsb"
+ "shaders_ng/distancefieldoutlinetext_a.frag.qsb"
+ "shaders_ng/distancefieldshiftedtext.frag.qsb"
+ "shaders_ng/distancefieldshiftedtext.vert.qsb"
+ "shaders_ng/distancefieldshiftedtext_a.frag.qsb"
+ "shaders_ng/distancefieldtext.frag.qsb"
+ "shaders_ng/distancefieldtext.vert.qsb"
+ "shaders_ng/distancefieldtext_a.frag.qsb"
+ "shaders_ng/flatcolor.frag.qsb"
+ "shaders_ng/flatcolor.vert.qsb"
+ "shaders_ng/hiqsubpixeldistancefieldtext.frag.qsb"
+ "shaders_ng/hiqsubpixeldistancefieldtext.vert.qsb"
+ "shaders_ng/hiqsubpixeldistancefieldtext_a.frag.qsb"
+ "shaders_ng/loqsubpixeldistancefieldtext.frag.qsb"
+ "shaders_ng/loqsubpixeldistancefieldtext.vert.qsb"
+ "shaders_ng/loqsubpixeldistancefieldtext_a.frag.qsb"
+ "shaders_ng/opaquetexture.frag.qsb"
+ "shaders_ng/opaquetexture.vert.qsb"
+ "shaders_ng/outlinedtext.frag.qsb"
+ "shaders_ng/outlinedtext.vert.qsb"
+ "shaders_ng/outlinedtext_a.frag.qsb"
+ "shaders_ng/shadereffect.frag.qsb"
+ "shaders_ng/shadereffect.vert.qsb"
+ "shaders_ng/smoothcolor.frag.qsb"
+ "shaders_ng/smoothcolor.vert.qsb"
+ "shaders_ng/smoothtexture.frag.qsb"
+ "shaders_ng/smoothtexture.vert.qsb"
+ "shaders_ng/sprite.frag.qsb"
+ "shaders_ng/sprite.vert.qsb"
+ "shaders_ng/stencilclip.frag.qsb"
+ "shaders_ng/stencilclip.vert.qsb"
+ "shaders_ng/styledtext.frag.qsb"
+ "shaders_ng/styledtext.vert.qsb"
+ "shaders_ng/styledtext_a.frag.qsb"
+ "shaders_ng/textmask.frag.qsb"
+ "shaders_ng/textmask.vert.qsb"
+ "shaders_ng/texture.frag.qsb"
+ "shaders_ng/texture.vert.qsb"
+ "shaders_ng/vertexcolor.frag.qsb"
+ "shaders_ng/vertexcolor.vert.qsb"
+ "shaders_ng/visualization.frag.qsb"
+ "shaders_ng/visualization.vert.qsb"
+)
+
+add_qt_resource(Quick "scenegraph"
+ PREFIX
+ "/qt-project.org/scenegraph"
+ BASE
+ "scenegraph"
+ FILES
+ ${scenegraph_resource_files}
+)
+set(items_resource_files
+ "shaders/shadereffect.frag"
+ "shaders/shadereffect.vert"
+ "shaders/shadereffect_core.frag"
+ "shaders/shadereffect_core.vert"
+ "shaders/shadereffectfallback.frag"
+ "shaders/shadereffectfallback.vert"
+ "shaders/shadereffectfallback_core.frag"
+ "shaders/shadereffectfallback_core.vert"
+)
+
+add_qt_resource(Quick "items"
+ PREFIX
+ "/qt-project.org/items"
+ BASE
+ "items"
+ FILES
+ ${items_resource_files}
+)
if(ANDROID)
@@ -331,8 +353,10 @@ endif()
## Scopes:
#####################################################################
-#### Keys ignored in scope 2:.:.:quick.pro:QT_FEATURE_qml_network:
-# QT_PRIVATE = "network"
+extend_target(Quick CONDITION QT_FEATURE_qml_network
+ LIBRARIES
+ Qt::Network
+)
extend_target(Quick CONDITION MSVC
DEFINES
@@ -409,6 +433,8 @@ extend_target(Quick CONDITION QT_FEATURE_opengl OR QT_FEATURE_opengles2 OR QT_FE
scenegraph/compressedtexture/qsgcompressedatlastexture.cpp scenegraph/compressedtexture/qsgcompressedatlastexture_p.h
scenegraph/compressedtexture/qsgcompressedtexture.cpp scenegraph/compressedtexture/qsgcompressedtexture_p.h
scenegraph/coreapi/qsgbatchrenderer.cpp scenegraph/coreapi/qsgbatchrenderer_p.h
+ scenegraph/coreapi/qsgopenglvisualizer.cpp scenegraph/coreapi/qsgopenglvisualizer_p.h
+ scenegraph/coreapi/qsgrhivisualizer.cpp scenegraph/coreapi/qsgrhivisualizer_p.h
scenegraph/coreapi/qsgshaderrewriter.cpp
scenegraph/qsgdefaultcontext.cpp scenegraph/qsgdefaultcontext_p.h
scenegraph/qsgdefaultglyphnode.cpp scenegraph/qsgdefaultglyphnode_p.cpp scenegraph/qsgdefaultglyphnode_p.h
diff --git a/src/quick/doc/images/sg-renderloop-singlethreaded.jpg b/src/quick/doc/images/sg-renderloop-singlethreaded.jpg
deleted file mode 100644
index c6d1577138..0000000000
--- a/src/quick/doc/images/sg-renderloop-singlethreaded.jpg
+++ /dev/null
Binary files differ
diff --git a/src/quick/doc/images/sg-renderloop-singlethreaded.png b/src/quick/doc/images/sg-renderloop-singlethreaded.png
new file mode 100644
index 0000000000..ad5ce62690
--- /dev/null
+++ b/src/quick/doc/images/sg-renderloop-singlethreaded.png
Binary files differ
diff --git a/src/quick/doc/images/sg-renderloop-threaded.jpg b/src/quick/doc/images/sg-renderloop-threaded.jpg
deleted file mode 100644
index 2f7d97591b..0000000000
--- a/src/quick/doc/images/sg-renderloop-threaded.jpg
+++ /dev/null
Binary files differ
diff --git a/src/quick/doc/images/sg-renderloop-threaded.png b/src/quick/doc/images/sg-renderloop-threaded.png
new file mode 100644
index 0000000000..1b1d6c7b11
--- /dev/null
+++ b/src/quick/doc/images/sg-renderloop-threaded.png
Binary files differ
diff --git a/src/quick/doc/images/sg-renderloop-threaded.xml b/src/quick/doc/images/sg-renderloop-threaded.xml
new file mode 100644
index 0000000000..857720b93a
--- /dev/null
+++ b/src/quick/doc/images/sg-renderloop-threaded.xml
@@ -0,0 +1,2 @@
+<mxfile modified="2019-07-16T06:49:28.503Z" host="www.draw.io" agent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_4) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.1 Safari/605.1.15" etag="GcefJOKlDSi76bXNeTL1" version="10.9.7" type="device"><diagram id="HMeFProKuhY0WpDPD-Gk" name="Page-1">7Vptc5s4EP41nrn7kBsMhtofbcfNddq0ddxO7/qlI8MCuoDECeGX/vouQhgT2alvxj4mvptJHHYlgXiefdM6PWeabu4EyeJ7HkDSs61g03Nue7bdt0Y2/ik120rjWcNKEQka6EmNYkG/Q71SawsaQN6aKDlPJM3aSp8zBr5s6YgQfN2eFvKk/dSMRGAoFj5JTO0XGshYa/veqBn4HWgU60cP7VfVQErqyfpN8pgEfL2ncmY9Zyo4l9VVuplCUoJX41Kte31kdLcxAUyesuDtdDWYhvej8Gb91vs6mQfx5MONvksut/ULQ4Dvr0UuZMwjzkgya7QTwQsWQHnXPkrNnHecZ1r5F0i51WSSQnJUxTJN9ChuWGz/QMGqhT9L4Te3Fm83+4O3Wy2FnEl9076HcrX3csNHIanfjxfC17Pu6XIczInDvn5abLeWO2ffPtc4SCIikM/Mc3fEocUDTwH3h+sEJETSVXsfRJtetJvXsIMXmqDDZD23yRVJCv2k+byg/uMbCWnPGeNPkQVEwi+/Gqy2OVvHVMIiIwqSNTpum5+QJsmUJ1yotU5AYBj6qM+l4I+wN+L5Q1iGOyZWICRsnufCxK5e4Go/0YGiX/vNunE7e6R18Z7H1evODrd9ItwfeULz+DpAH3UNumOAfvf5Tbkox49lwv1HBPScQIdDH/yDQC+H7sC1LgM05sHTgN65wdmRHhhIj4MVYYiabY0ZxfxFOStRn63Kl7Stj4L7kOeURT0bH+kluMXJUuBVVF4RFqhIix+cXYEvDKzOKXINihaYoaRiwCL4y2BdFjWCpGAS8iEDdvcOx6eYN2Ejay9KSVBy7BdCQfPymHJttx21Bm7XqcIyYPw366idUNVR9j8opOwmwJ2lkPJOLKScLgspz/CrCUSUle+3ZX4sOKPfVfgznUqlIJyostLL95xR157zyqACUlqGqqrW+kKZOjSV1dYSQi5gsWMIw+C5iy4XhsHgEAlDe+l43mVIsDsPX0ODhCOVLqFMvscz/nXg3rnx2/b/aaNCYnRi2hh0mTZGhpvMqpr3Z0mjYBdLG514jtN5xKrbg6fnjQdAkMUFckYnidvpPHb1zW7UwgdWnixUK7g+bQiF+5kP7d2APuje7M2e1FGzJ6EEcV1WP+je6s3+VHXMrs/YCvp8TbJJEYYg8qvA3e3e8M1u1VHDV+2QBVKQQXAd8Hdv9mYn6nXVdKqCvM/TLAFZylUbkKvDtC8AXmIr8CkBXvf2b7YsVCnZVJp7CHt/F+WXiqpev8lVwT7GCX0v2yh86vG6PB1nWUJ9Xbta93jI+xQLIGbKriLcPj1tGhhn8IQzrSIJjRiKWCFgVkJFyQ8+MxnrgZQGgTreHLKHxmIOfQ2oZL3J/hnYf9oIdk4k37sY+WaT5GH2/nb2cCb+n9Zt/2n2jUr7cuyj2Hz9r8b2/onCmf0A</diagram><diagram id="ARaiocFhs8Yj9Ub61lwa" name="Page-2">5Zhbb9owFMc/TaTtYVIuEOARKPSidmubdp36ZuKTxMOxM8c00E8/O3EKNEyj09ZImQQi+R9fwvn5nGPH8qbp+lSgLLniGKjl2nhteSeW6zr2yFU/WtlUim8PKyEWBJtGWyEgz1D3NOqKYMj3GkrOqSTZvhhyxiCUexoSghf7zSJO92fNUAwNIQgRbaoPBMvEqI4/2hrOgMSJmXroDipDiurG5p/kCcK82JG8meVNBeeyukrXU6DaebVfZrd3PwbzydlDf3kT4Mfomkzpp2qw+Vu6vPwFAUz+8dAXrP/1+7q3XDznxWKTedgfxaaL/YToyvjr5mZFwuW5hNTyxuqzyjCSYDwgN7VbBV8xDHpox/ImRUIkBBkKtbVQC0lpiUypMUeE0imnXJR9PYxgGIVKz6XgS9ix+OEQFpGymMcCIWH9iuNvnOC8kFFLGngKUmxUPzNK3+1X45jV7NRwi+3acEdGS3aWRd9oyCzH+GXorcvVhfH6Gwg4RxK45pTkyYePnSUxaJuE2yBxen9uuT5VE08WQl3F+mqcZZSESBLOVOsrRPTPXSIA4QYb5TO5D2Df0YwzeEXFSIiSmKnbUPkYlD7RBNS0dGwMKcFYT3OQ+HZN2Hp4zqRJzI5f35uHdN6Lr3ckXv8v4E2WF9i5n82Dk/Pb4Fs2S9eXjwdSXSCR8imL9Wzqy6DQBUagFJrUv2TATi+Vfaqcp6mq2fKySmDQ1WslSm91NDjd90yTB+k10ySkRFOosuUDYWVt1vlyAREXEGxYmAjOyLMi3N206b5n2jxIppk2f1HAVKaUn9X+srswvNbDxHtrmNyCcrzodIh4rYdIr1l6VF3XdaM8dNW1RJQsoLmN6AiIXuvh0T8+PFCk9l3/QXT0Wo8OvwGl2mzVO60SR16gbLKKIhB5d1n0Ww+QwfEBUm6UA4UlA9xhJK2Hx7CBZF4dUaqiEfI0o6Df0NiIqdJhl8fSPBQArKtQ/NbjZNSAMsZPiIUaw5iRtH49MHsqT4X2teAh5Hl52Jw2T5cVuZwbfB2l9g9DSd1uX8SWtp3X2d7sJw==</diagram></mxfile>
+
diff --git a/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc b/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc
index 9383c78a42..ee6c501c71 100644
--- a/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc
+++ b/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc
@@ -210,7 +210,7 @@ and when interaction with the scene graph can happen.
The following is a simple outline of how a frame gets
composed with the threaded render loop.
-\image sg-renderloop-threaded.jpg
+\image sg-renderloop-threaded.png
\list 1
@@ -301,7 +301,7 @@ will make the code non-portable.
The following is a simplified illustration of the frame rendering
sequence in the non-threaded renderer.
-\image sg-renderloop-singlethreaded.jpg
+\image sg-renderloop-singlethreaded.png
\section2 Custom control over rendering with QQuickRenderControl
diff --git a/src/quick/handlers/qquickhandlerpoint.cpp b/src/quick/handlers/qquickhandlerpoint.cpp
index de21537f27..f3d92cf200 100644
--- a/src/quick/handlers/qquickhandlerpoint.cpp
+++ b/src/quick/handlers/qquickhandlerpoint.cpp
@@ -51,13 +51,14 @@ Q_DECLARE_LOGGING_CATEGORY(DBG_TOUCH_TARGET)
A QML representation of a QQuickEventPoint.
- It's possible to make bindings to properties of a \l SinglePointHandler's
- current point. For example:
+ It's possible to make bindings to properties of a handler's current
+ \l {SinglePointHandler::point}{point} or
+ \l {MultiPointHandler::centroid}{centroid}. For example:
\snippet pointerHandlers/dragHandlerNullTarget.qml 0
The point is kept up-to-date when the DragHandler is actively responding to
- an EventPoint; but when the point is released, or the current point is
+ an EventPoint; but after the point is released, or when the current point is
being handled by a different handler, \c position.x and \c position.y are 0.
\note This is practically identical to QtQuick::EventPoint; however an
@@ -68,7 +69,7 @@ Q_DECLARE_LOGGING_CATEGORY(DBG_TOUCH_TARGET)
handler is handling. HandlerPoint is a Q_GADGET that the handler owns.
This allows you to make lifetime bindings to its properties.
- \sa SinglePointHandler::point
+ \sa SinglePointHandler::point, MultiPointHandler::centroid
*/
QQuickHandlerPoint::QQuickHandlerPoint()
@@ -106,12 +107,6 @@ void QQuickHandlerPoint::reset(const QQuickEventPoint *point)
m_scenePressPosition = point->scenePosition();
m_pressedButtons = event->buttons();
break;
- case QQuickEventPoint::Released:
- if (event->buttons() == Qt::NoButton) {
- reset();
- return;
- }
- break;
default:
break;
}
diff --git a/src/quick/handlers/qquickhoverhandler.cpp b/src/quick/handlers/qquickhoverhandler.cpp
index 61955cad03..d7566f0cd8 100644
--- a/src/quick/handlers/qquickhoverhandler.cpp
+++ b/src/quick/handlers/qquickhoverhandler.cpp
@@ -38,6 +38,7 @@
****************************************************************************/
#include "qquickhoverhandler_p.h"
+#include <private/qquicksinglepointhandler_p_p.h>
QT_BEGIN_NAMESPACE
@@ -59,6 +60,8 @@ Q_LOGGING_CATEGORY(lcHoverHandler, "qt.quick.handler.hover")
QQuickHoverHandler::QQuickHoverHandler(QQuickItem *parent)
: QQuickSinglePointHandler(parent)
{
+ // Tell QQuickPointerDeviceHandler::wantsPointerEvent() to ignore button state
+ d_func()->acceptedButtons = Qt::NoButton;
// Rule out the touchscreen for now (can be overridden in QML in case a hover-detecting touchscreen exists)
setAcceptedDevices(static_cast<QQuickPointerDevice::DeviceType>(
static_cast<int>(QQuickPointerDevice::AllDevices) ^ static_cast<int>(QQuickPointerDevice::TouchScreen)));
diff --git a/src/quick/handlers/qquickpointerdevicehandler.cpp b/src/quick/handlers/qquickpointerdevicehandler.cpp
index 096fad2071..449d726b78 100644
--- a/src/quick/handlers/qquickpointerdevicehandler.cpp
+++ b/src/quick/handlers/qquickpointerdevicehandler.cpp
@@ -256,6 +256,11 @@ bool QQuickPointerDeviceHandler::wantsPointerEvent(QQuickPointerEvent *event)
return false;
if (d->acceptedModifiers != Qt::KeyboardModifierMask && event->modifiers() != d->acceptedModifiers)
return false;
+ // HoverHandler sets acceptedButtons to Qt::NoButton to indicate that button state is irrelevant.
+ if (event->device()->pointerType() != QQuickPointerDevice::Finger && acceptedButtons() != Qt::NoButton &&
+ (event->buttons() & acceptedButtons()) == 0 && (event->button() & acceptedButtons()) == 0
+ && !event->asPointerScrollEvent())
+ return false;
return true;
}
diff --git a/src/quick/handlers/qquicksinglepointhandler.cpp b/src/quick/handlers/qquicksinglepointhandler.cpp
index 234bc3c75a..b51f53b74f 100644
--- a/src/quick/handlers/qquicksinglepointhandler.cpp
+++ b/src/quick/handlers/qquicksinglepointhandler.cpp
@@ -74,9 +74,6 @@ bool QQuickSinglePointHandler::wantsPointerEvent(QQuickPointerEvent *event)
Q_D(QQuickSinglePointHandler);
if (!QQuickPointerDeviceHandler::wantsPointerEvent(event))
return false;
- if (event->device()->pointerType() != QQuickPointerDevice::Finger &&
- (event->buttons() & acceptedButtons()) == 0 && (event->button() & acceptedButtons()) == 0)
- return false;
if (d->pointInfo.id()) {
// We already know which one we want, so check whether it's there.
diff --git a/src/quick/handlers/qquicktaphandler.cpp b/src/quick/handlers/qquicktaphandler.cpp
index 40a4813527..255e47d73a 100644
--- a/src/quick/handlers/qquicktaphandler.cpp
+++ b/src/quick/handlers/qquicktaphandler.cpp
@@ -392,9 +392,6 @@ void QQuickTapHandler::updateTimeHeld()
from the release event about the point that was tapped:
\snippet pointerHandlers/tapHandlerOnTapped.qml 0
-
- \note At the time this signal is emitted, \l point has been reset
- (all coordinates are \c 0).
*/
/*!
@@ -406,9 +403,6 @@ void QQuickTapHandler::updateTimeHeld()
it can be tapped again; but if the time until the next tap is less,
\l tapCount will increase. The \c eventPoint signal parameter contains
information from the release event about the point that was tapped.
-
- \note At the time this signal is emitted, \l point has been reset
- (all coordinates are \c 0).
*/
/*!
@@ -422,9 +416,6 @@ void QQuickTapHandler::updateTimeHeld()
\l singleTapped, \l tapped, and \l tapCountChanged. The \c eventPoint
signal parameter contains information from the release event about the
point that was tapped.
-
- \note At the time this signal is emitted, \l point has been reset
- (all coordinates are \c 0).
*/
/*!
diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp
index 51c662cb3a..ad3a39dd71 100644
--- a/src/quick/items/qquickevents.cpp
+++ b/src/quick/items/qquickevents.cpp
@@ -913,10 +913,14 @@ void QQuickEventPoint::setGrabberPointerHandler(QQuickPointerHandler *grabber, b
passiveGrabber->onGrabChanged(grabber, OverrideGrabPassive, this);
}
}
- if (oldGrabberHandler)
+ if (oldGrabberHandler) {
oldGrabberHandler->onGrabChanged(oldGrabberHandler, (grabber ? CancelGrabExclusive : UngrabExclusive), this);
- else if (oldGrabberItem && pointerEvent()->asPointerTouchEvent())
- oldGrabberItem->touchUngrabEvent();
+ } else if (oldGrabberItem) {
+ if (pointerEvent()->asPointerTouchEvent())
+ oldGrabberItem->touchUngrabEvent();
+ else if (pointerEvent()->asPointerMouseEvent())
+ oldGrabberItem->mouseUngrabEvent();
+ }
// touchUngrabEvent() can result in the grabber being set to null (MPTA does that, for example).
// So set it again to ensure that final state is what we want.
m_exclusiveGrabber = QPointer<QObject>(grabber);
diff --git a/src/quick/items/qquickgenericshadereffect.cpp b/src/quick/items/qquickgenericshadereffect.cpp
index 1d71555849..df61ee853d 100644
--- a/src/quick/items/qquickgenericshadereffect.cpp
+++ b/src/quick/items/qquickgenericshadereffect.cpp
@@ -40,10 +40,27 @@
#include <private/qquickgenericshadereffect_p.h>
#include <private/qquickwindow_p.h>
#include <private/qquickitem_p.h>
-#include <QSignalMapper>
QT_BEGIN_NAMESPACE
+namespace {
+class IntSignalMapper : public QObject
+{
+ Q_OBJECT
+
+ int value;
+public:
+ explicit IntSignalMapper(int v)
+ : QObject(nullptr), value(v) {}
+
+public Q_SLOTS:
+ void map() { emit mapped(value); }
+
+Q_SIGNALS:
+ void mapped(int);
+};
+} // unnamed namespace
+
// The generic shader effect is used whenever on the RHI code path, or when the
// scenegraph backend indicates SupportsShaderEffectNode. This, unlike the
// monolithic and interconnected (e.g. with particles) OpenGL variant, passes
@@ -547,15 +564,10 @@ void QQuickGenericShaderEffect::updateShaderVars(Shader shaderType)
if (!mp.hasNotifySignal())
qWarning("ShaderEffect: property '%s' does not have notification method", v.name.constData());
- // Have a QSignalMapper that emits mapped() with an index+type on each property change notify signal.
+ // Have a IntSignalMapper that emits mapped() with an index+type on each property change notify signal.
auto &sm(m_signalMappers[shaderType][i]);
- if (!sm.mapper) {
-QT_WARNING_PUSH
-QT_WARNING_DISABLE_DEPRECATED
- sm.mapper = new QSignalMapper;
-QT_WARNING_POP
- sm.mapper->setMapping(m_item, i | (shaderType << 16));
- }
+ if (!sm.mapper)
+ sm.mapper = new IntSignalMapper(i | (shaderType << 16));
sm.active = true;
const QByteArray signalName = '2' + mp.notifySignal().methodSignature();
QObject::connect(m_item, signalName, sm.mapper, SLOT(map()));
@@ -665,3 +677,4 @@ void QQuickGenericShaderEffect::markGeometryDirtyAndUpdateIfSupportsAtlas()
QT_END_NAMESPACE
#include "moc_qquickgenericshadereffect_p.cpp"
+#include "qquickgenericshadereffect.moc"
diff --git a/src/quick/items/qquickgenericshadereffect_p.h b/src/quick/items/qquickgenericshadereffect_p.h
index 3f6f92921b..368c32d2f8 100644
--- a/src/quick/items/qquickgenericshadereffect_p.h
+++ b/src/quick/items/qquickgenericshadereffect_p.h
@@ -59,8 +59,6 @@
QT_BEGIN_NAMESPACE
-class QSignalMapper;
-
class Q_QUICK_PRIVATE_EXPORT QQuickGenericShaderEffect : public QObject
{
Q_OBJECT
@@ -142,7 +140,7 @@ private:
struct SignalMapper {
SignalMapper() : mapper(nullptr), active(false) { }
- QSignalMapper *mapper;
+ QObject *mapper;
bool active;
};
QVector<SignalMapper> m_signalMappers[NShader];
diff --git a/src/quick/items/qquickitemsmodule.cpp b/src/quick/items/qquickitemsmodule.cpp
index e6090e66ba..8aa259a7b6 100644
--- a/src/quick/items/qquickitemsmodule.cpp
+++ b/src/quick/items/qquickitemsmodule.cpp
@@ -227,6 +227,7 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor)
qmlRegisterType<QQuickPathSvg>("QtQuick",2,0,"PathSvg");
qmlRegisterType<QQuickPath, 14>(uri, 2, 14, "Path");
qmlRegisterType<QQuickPathPolyline>("QtQuick", 2, 14, "PathPolyline");
+ qmlRegisterType<QQuickPathMultiline>("QtQuick", 2, 14, "PathMultiline");
#endif
#if QT_CONFIG(quick_pathview)
qmlRegisterType<QQuickPathView>(uri,major,minor,"PathView");
diff --git a/src/quick/items/qquickitemview.cpp b/src/quick/items/qquickitemview.cpp
index 95f1229b92..bbfbf6244c 100644
--- a/src/quick/items/qquickitemview.cpp
+++ b/src/quick/items/qquickitemview.cpp
@@ -1852,6 +1852,9 @@ void QQuickItemViewPrivate::layout()
forceLayout = false;
if (transitioner && transitioner->canTransition(QQuickItemViewTransitioner::PopulateTransition, true)) {
+ // Give the view one more chance to refill itself,
+ // in case its size is changed such that more delegates become visible after component completed
+ refill();
for (FxViewItem *item : qAsConst(visibleItems)) {
if (!item->transitionScheduledOrRunning())
item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::PopulateTransition, true);
diff --git a/src/quick/items/qquickpainteditem.cpp b/src/quick/items/qquickpainteditem.cpp
index 57848919f3..cee73ac2e8 100644
--- a/src/quick/items/qquickpainteditem.cpp
+++ b/src/quick/items/qquickpainteditem.cpp
@@ -369,10 +369,6 @@ void QQuickPaintedItem::setTextureSize(const QSize &size)
emit textureSizeChanged();
}
-#if QT_VERSION >= 0x060000
-#warning "Remove: QQuickPaintedItem::contentsBoundingRect, contentsScale, contentsSize. Also remove them from qsgadaptationlayer_p.h and qsgdefaultpainternode.h/cpp."
-#endif
-
/*!
\obsolete
diff --git a/src/quick/items/qquickshadereffectmesh_p.h b/src/quick/items/qquickshadereffectmesh_p.h
index 62a9798e40..79e05a5f9f 100644
--- a/src/quick/items/qquickshadereffectmesh_p.h
+++ b/src/quick/items/qquickshadereffectmesh_p.h
@@ -66,8 +66,8 @@ QT_REQUIRE_CONFIG(quick_shadereffect);
QT_BEGIN_NAMESPACE
-const char *qtPositionAttributeName();
-const char *qtTexCoordAttributeName();
+Q_QUICK_PRIVATE_EXPORT const char *qtPositionAttributeName();
+Q_QUICK_PRIVATE_EXPORT const char *qtTexCoordAttributeName();
class QSGGeometry;
class QRectF;
diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp
index 30738b3db6..a208e135af 100644
--- a/src/quick/items/qquickwindow.cpp
+++ b/src/quick/items/qquickwindow.cpp
@@ -536,6 +536,13 @@ void QQuickWindowPrivate::renderSceneGraph(const QSize &size, const QSize &surfa
context->endNextRhiFrame(renderer);
else
context->endNextFrame(renderer);
+
+ if (renderer->hasCustomRenderModeWithContinuousUpdate()) {
+ // For the overdraw visualizer. This update is not urgent so avoid a
+ // direct update() call, this is only here to keep the overdraw
+ // visualization box rotating even when the scene is static.
+ QCoreApplication::postEvent(q, new QEvent(QEvent::Type(FullUpdateRequest)));
+ }
}
QQuickWindowPrivate::QQuickWindowPrivate()
@@ -3804,6 +3811,12 @@ bool QQuickWindow::isSceneGraphInitialized() const
This signal is emitted when the window receives the event \a close from
the windowing system.
+
+ On \macOs, Qt will create a menu item \c Quit if there is no menu item
+ whose text is "quit" or "exit". This menu item calls the \c QCoreApplication::quit
+ signal, not the \c QQuickWindow::closing() signal.
+
+ \sa {QMenuBar as a Global Menu Bar}
*/
/*!
diff --git a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp
index b5437ffb7e..2cd9ee689b 100644
--- a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp
+++ b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp
@@ -59,6 +59,9 @@
#include <private/qquickprofiler_p.h>
#include "qsgmaterialrhishader_p.h"
+#include "qsgopenglvisualizer_p.h"
+#include "qsgrhivisualizer_p.h"
+
#include <algorithm>
#ifndef GL_DOUBLE
@@ -145,7 +148,7 @@ static inline uint aligned(uint v, uint byteAlign)
return (v + byteAlign - 1) & ~(byteAlign - 1);
}
-static inline QRhiVertexInputAttribute::Format vertexInputFormat(const QSGGeometry::Attribute &a)
+QRhiVertexInputAttribute::Format qsg_vertexInputFormat(const QSGGeometry::Attribute &a)
{
switch (a.type) {
case QSGGeometry::FloatType:
@@ -193,7 +196,7 @@ static QRhiVertexInputLayout calculateVertexInputLayout(const QSGMaterialRhiShad
qWarning("Vertex input %d is present in material but not in shader. This is wrong.",
a.position);
}
- inputAttributes.append(QRhiVertexInputAttribute(VERTEX_BUFFER_BINDING, a.position, vertexInputFormat(a), offset));
+ inputAttributes.append(QRhiVertexInputAttribute(VERTEX_BUFFER_BINDING, a.position, qsg_vertexInputFormat(a), offset));
offset += a.tupleSize * size_of_type(a.type);
}
if (batchable) {
@@ -215,7 +218,7 @@ static QRhiVertexInputLayout calculateVertexInputLayout(const QSGMaterialRhiShad
return inputLayout;
}
-static inline QRhiCommandBuffer::IndexFormat indexFormat(const QSGGeometry *geometry)
+QRhiCommandBuffer::IndexFormat qsg_indexFormat(const QSGGeometry *geometry)
{
switch (geometry->indexType()) {
case QSGGeometry::UnsignedShortType:
@@ -230,7 +233,7 @@ static inline QRhiCommandBuffer::IndexFormat indexFormat(const QSGGeometry *geom
}
}
-static inline QRhiGraphicsPipeline::Topology gpTopology(int geomDrawMode)
+QRhiGraphicsPipeline::Topology qsg_topology(int geomDrawMode)
{
QRhiGraphicsPipeline::Topology topology = QRhiGraphicsPipeline::Triangles;
switch (geomDrawMode) {
@@ -501,8 +504,8 @@ void Updater::updateStates(QSGNode *n)
qDebug(" - forceupdate");
}
- if (Q_UNLIKELY(renderer->m_visualizeMode == Renderer::VisualizeChanges))
- renderer->visualizeChangesPrepare(sn);
+ if (Q_UNLIKELY(renderer->m_visualizer->mode() == Visualizer::VisualizeChanges))
+ renderer->m_visualizer->visualizeChangesPrepare(sn);
visitNode(sn);
}
@@ -975,7 +978,6 @@ Renderer::Renderer(QSGDefaultRenderContext *ctx)
, m_vertexUploadPool(256)
, m_indexUploadPool(64)
, m_vao(nullptr)
- , m_visualizeMode(VisualizeNothing)
{
m_rhi = m_context->rhi();
if (m_rhi) {
@@ -983,9 +985,11 @@ Renderer::Renderer(QSGDefaultRenderContext *ctx)
m_uint32IndexForRhi = !m_rhi->isFeatureSupported(QRhi::NonFourAlignedEffectiveIndexBufferOffset);
if (qEnvironmentVariableIntValue("QSG_RHI_UINT32_INDEX"))
m_uint32IndexForRhi = true;
+ m_visualizer = new RhiVisualizer(this);
} else {
initializeOpenGLFunctions();
m_uint32IndexForRhi = false;
+ m_visualizer = new OpenGLVisualizer(this);
}
setNodeUpdater(new Updater(this));
@@ -1088,6 +1092,8 @@ Renderer::~Renderer()
}
destroyGraphicsResources();
+
+ delete m_visualizer;
}
void Renderer::destroyGraphicsResources()
@@ -1104,6 +1110,8 @@ void Renderer::destroyGraphicsResources()
m_stencilClipCommon.reset();
delete m_dummyTexture;
+
+ m_visualizer->releaseResources();
}
void Renderer::releaseCachedResources()
@@ -1138,7 +1146,7 @@ void Renderer::invalidateAndRecycleBatch(Batch *b)
*/
void Renderer::map(Buffer *buffer, int byteSize, bool isIndexBuf)
{
- if (!m_context->hasBrokenIndexBufferObjects() && m_visualizeMode == VisualizeNothing) {
+ if (!m_context->hasBrokenIndexBufferObjects() && m_visualizer->mode() == Visualizer::VisualizeNothing) {
// Common case, use a shared memory pool for uploading vertex data to avoid
// excessive reevaluation
QDataBuffer<char> &pool = m_context->separateIndexBuffer() && isIndexBuf
@@ -1194,7 +1202,7 @@ void Renderer::unmap(Buffer *buffer, bool isIndexBuf)
m_resourceUpdates->updateDynamicBuffer(buffer->buf, 0, buffer->size,
QByteArray::fromRawData(buffer->data, buffer->size));
}
- if (m_visualizeMode == VisualizeNothing)
+ if (m_visualizer->mode() == Visualizer::VisualizeNothing)
buffer->data = nullptr;
} else {
if (buffer->id == 0)
@@ -1202,7 +1210,7 @@ void Renderer::unmap(Buffer *buffer, bool isIndexBuf)
GLenum target = isIndexBuf ? GL_ELEMENT_ARRAY_BUFFER : GL_ARRAY_BUFFER;
glBindBuffer(target, buffer->id);
glBufferData(target, buffer->size, buffer->data, m_bufferStrategy);
- if (!m_context->hasBrokenIndexBufferObjects() && m_visualizeMode == VisualizeNothing)
+ if (!m_context->hasBrokenIndexBufferObjects() && m_visualizer->mode() == Visualizer::VisualizeNothing)
buffer->data = nullptr;
}
}
@@ -2096,7 +2104,7 @@ void Renderer::uploadMergedElement(Element *e, int vaOffset, char **vertexData,
*indexCount += iCount;
}
-static QMatrix4x4 qsg_matrixForRoot(Node *node)
+QMatrix4x4 qsg_matrixForRoot(Node *node)
{
if (node->type() == QSGNode::TransformNodeType)
return static_cast<QSGTransformNode *>(node->sgNode)->combinedMatrix();
@@ -2830,31 +2838,31 @@ void Renderer::updateClipState(const QSGClipNode *clipList, Batch *batch) // RHI
if (firstStencilClipInBatch) {
m_stencilClipCommon.inputLayout.setBindings({ QRhiVertexInputBinding(g->sizeOfVertex()) });
- m_stencilClipCommon.inputLayout.setAttributes({ QRhiVertexInputAttribute(0, 0, vertexInputFormat(*a), 0) });
- m_stencilClipCommon.topology = gpTopology(g->drawingMode());
+ m_stencilClipCommon.inputLayout.setAttributes({ QRhiVertexInputAttribute(0, 0, qsg_vertexInputFormat(*a), 0) });
+ m_stencilClipCommon.topology = qsg_topology(g->drawingMode());
}
#ifndef QT_NO_DEBUG
else {
- if (gpTopology(g->drawingMode()) != m_stencilClipCommon.topology)
+ if (qsg_topology(g->drawingMode()) != m_stencilClipCommon.topology)
qWarning("updateClipState: Clip list entries have different primitive topologies, this is not currently supported.");
- if (vertexInputFormat(*a) != m_stencilClipCommon.inputLayout.attributes().first().format())
+ if (qsg_vertexInputFormat(*a) != m_stencilClipCommon.inputLayout.attributes().first().format())
qWarning("updateClipState: Clip list entries have different vertex input layouts, this is must not happen.");
}
#endif
drawCall.vbufOffset = aligned(vOffset, 4);
const int vertexByteSize = g->sizeOfVertex() * g->vertexCount();
- vOffset += vertexByteSize;
+ vOffset = drawCall.vbufOffset + vertexByteSize;
int indexByteSize = 0;
if (g->indexCount()) {
drawCall.ibufOffset = aligned(iOffset, 4);
indexByteSize = g->sizeOfIndex() * g->indexCount();
- iOffset += indexByteSize;
+ iOffset = drawCall.ibufOffset + indexByteSize;
}
drawCall.ubufOffset = aligned(uOffset, m_ubufAlignment);
- uOffset += StencilClipUbufSize;
+ uOffset = drawCall.ubufOffset + StencilClipUbufSize;
QMatrix4x4 matrixYUpNDC = m_current_projection_matrix;
if (clip->matrix())
@@ -2874,7 +2882,7 @@ void Renderer::updateClipState(const QSGClipNode *clipList, Batch *batch) // RHI
drawCall.vertexCount = g->vertexCount();
drawCall.indexCount = g->indexCount();
- drawCall.indexFormat = indexFormat(g);
+ drawCall.indexFormat = qsg_indexFormat(g);
batch->stencilClipState.drawCalls.add(drawCall);
}
@@ -2919,13 +2927,13 @@ void Renderer::enqueueStencilDraw(const Batch *batch) // RHI only
QRhiCommandBuffer::DynamicOffset ubufOffset(0, drawCall.ubufOffset);
if (i == 0) {
cb->setGraphicsPipeline(m_stencilClipCommon.replacePs);
- cb->setShaderResources(srb, 1, &ubufOffset);
cb->setViewport(m_pstate.viewport);
} else if (i == 1) {
cb->setGraphicsPipeline(m_stencilClipCommon.incrPs);
- cb->setShaderResources(srb, 1, &ubufOffset);
cb->setViewport(m_pstate.viewport);
}
+ // else incrPs is already bound
+ cb->setShaderResources(srb, 1, &ubufOffset);
cb->setStencilRef(drawCall.stencilRef);
const QRhiCommandBuffer::VertexInput vbufBinding(batch->stencilClipState.vbuf, drawCall.vbufOffset);
if (drawCall.indexCount) {
@@ -3255,7 +3263,7 @@ bool Renderer::ensurePipelineState(Element *e, const ShaderManager::Shader *sms)
flags |= QRhiGraphicsPipeline::UsesStencilRef;
ps->setFlags(flags);
- ps->setTopology(gpTopology(m_gstate.drawMode));
+ ps->setTopology(qsg_topology(m_gstate.drawMode));
ps->setCullMode(m_gstate.cullMode);
QRhiGraphicsPipeline::TargetBlend blend;
@@ -4089,6 +4097,9 @@ void Renderer::renderBatches()
}
}
+ if (m_visualizer->mode() != Visualizer::VisualizeNothing)
+ m_visualizer->prepareVisualize();
+
QRhiCommandBuffer *cb = commandBuffer();
cb->beginPass(renderTarget(), m_pstate.clearColor, m_pstate.dsClear, m_resourceUpdates);
m_resourceUpdates = nullptr;
@@ -4120,7 +4131,8 @@ void Renderer::renderBatches()
if (m_renderPassRecordingCallbacks.end)
m_renderPassRecordingCallbacks.end(m_renderPassRecordingCallbacks.userData);
- cb->endPass();
+ if (m_visualizer->mode() == Visualizer::VisualizeNothing)
+ cb->endPass();
}
}
@@ -4314,13 +4326,16 @@ void Renderer::render()
m_renderOrderRebuildLower = -1;
m_renderOrderRebuildUpper = -1;
- if (!m_rhi) {
- if (m_visualizeMode != VisualizeNothing)
- visualize();
+ if (m_visualizer->mode() != Visualizer::VisualizeNothing)
+ m_visualizer->visualize();
+ if (!m_rhi) {
if (m_vao)
m_vao->release();
} else {
+ if (m_visualizer->mode() != Visualizer::VisualizeNothing)
+ commandBuffer()->endPass();
+
if (m_resourceUpdates) {
m_resourceUpdates->release();
m_resourceUpdates = nullptr;
@@ -4460,311 +4475,23 @@ void Renderer::renderRenderNode(Batch *batch)
glBindFramebuffer(GL_FRAMEBUFFER, prevFbo);
}
-class VisualizeShader : public QOpenGLShaderProgram
-{
-public:
- int color;
- int matrix;
- int rotation;
- int pattern;
- int projection;
-};
-
-void Renderer::visualizeDrawGeometry(const QSGGeometry *g)
-{
- if (g->attributeCount() < 1)
- return;
- const QSGGeometry::Attribute *a = g->attributes();
- glVertexAttribPointer(0, a->tupleSize, a->type, false, g->sizeOfVertex(), g->vertexData());
- if (g->indexCount())
- glDrawElements(g->drawingMode(), g->indexCount(), g->indexType(), g->indexData());
- else
- glDrawArrays(g->drawingMode(), 0, g->vertexCount());
-
-}
-
-void Renderer::visualizeBatch(Batch *b)
-{
- VisualizeShader *shader = static_cast<VisualizeShader *>(m_shaderManager->visualizeProgram);
-
- if (b->positionAttribute != 0)
- return;
-
- QSGGeometryNode *gn = b->first->node;
- QSGGeometry *g = gn->geometry();
- const QSGGeometry::Attribute &a = g->attributes()[b->positionAttribute];
-
- glBindBuffer(GL_ARRAY_BUFFER, b->vbo.id);
-
- QMatrix4x4 matrix(m_current_projection_matrix);
- if (b->root)
- matrix = matrix * qsg_matrixForRoot(b->root);
-
- shader->setUniformValue(shader->pattern, float(b->merged ? 0 : 1));
-
- QColor color = QColor::fromHsvF((rand() & 1023) / 1023.0, 1.0, 1.0);
- float cr = color.redF();
- float cg = color.greenF();
- float cb = color.blueF();
- shader->setUniformValue(shader->color, cr, cg, cb, 1.0);
-
- if (b->merged) {
- shader->setUniformValue(shader->matrix, matrix);
- const char *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));
- glDrawElements(g->drawingMode(), set.indexCount, GL_UNSIGNED_SHORT,
- (void *)(qintptr)(dataStart + set.indices));
- }
- } else {
- Element *e = b->first;
- int offset = 0;
- while (e) {
- gn = e->node;
- g = gn->geometry();
- shader->setUniformValue(shader->matrix, matrix * *gn->matrix());
- glVertexAttribPointer(a.position, a.tupleSize, a.type, false, g->sizeOfVertex(), (void *) (qintptr) offset);
- if (g->indexCount())
- glDrawElements(g->drawingMode(), g->indexCount(), g->indexType(), g->indexData());
- else
- glDrawArrays(g->drawingMode(), 0, g->vertexCount());
- offset += g->sizeOfVertex() * g->vertexCount();
- e = e->nextInBatch;
- }
- }
-}
-
-
-
-
-void Renderer::visualizeClipping(QSGNode *node)
-{
- if (node->type() == QSGNode::ClipNodeType) {
- VisualizeShader *shader = static_cast<VisualizeShader *>(m_shaderManager->visualizeProgram);
- QSGClipNode *clipNode = static_cast<QSGClipNode *>(node);
- QMatrix4x4 matrix = m_current_projection_matrix;
- if (clipNode->matrix())
- matrix = matrix * *clipNode->matrix();
- shader->setUniformValue(shader->matrix, matrix);
- visualizeDrawGeometry(clipNode->geometry());
- }
-
- QSGNODE_TRAVERSE(node) {
- visualizeClipping(child);
- }
-}
-
-#define QSGNODE_DIRTY_PARENT (QSGNode::DirtyNodeAdded \
- | QSGNode::DirtyOpacity \
- | QSGNode::DirtyMatrix \
- | QSGNode::DirtyNodeRemoved)
-
-void Renderer::visualizeChangesPrepare(Node *n, uint parentChanges)
-{
- uint childDirty = (parentChanges | n->dirtyState) & QSGNODE_DIRTY_PARENT;
- uint selfDirty = n->dirtyState | parentChanges;
- if (n->type() == QSGNode::GeometryNodeType && selfDirty != 0)
- m_visualizeChanceSet.insert(n, selfDirty);
- SHADOWNODE_TRAVERSE(n) {
- visualizeChangesPrepare(child, childDirty);
- }
-}
-
-void Renderer::visualizeChanges(Node *n)
-{
-
- if (n->type() == QSGNode::GeometryNodeType && n->element()->batch && m_visualizeChanceSet.contains(n)) {
- uint dirty = m_visualizeChanceSet.value(n);
- bool tinted = (dirty & QSGNODE_DIRTY_PARENT) != 0;
-
- VisualizeShader *shader = static_cast<VisualizeShader *>(m_shaderManager->visualizeProgram);
- QColor color = QColor::fromHsvF((rand() & 1023) / 1023.0, 0.3, 1.0);
- float ca = 0.5;
- float cr = color.redF() * ca;
- float cg = color.greenF() * ca;
- float cb = color.blueF() * ca;
- shader->setUniformValue(shader->color, cr, cg, cb, ca);
- shader->setUniformValue(shader->pattern, float(tinted ? 0.5 : 0));
-
- QSGGeometryNode *gn = static_cast<QSGGeometryNode *>(n->sgNode);
-
- QMatrix4x4 matrix = m_current_projection_matrix;
- if (n->element()->batch->root)
- matrix = matrix * qsg_matrixForRoot(n->element()->batch->root);
- matrix = matrix * *gn->matrix();
- shader->setUniformValue(shader->matrix, matrix);
- visualizeDrawGeometry(gn->geometry());
-
- // 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 = nullptr;
- }
-
- SHADOWNODE_TRAVERSE(n) {
- visualizeChanges(child);
- }
-}
-
-void Renderer::visualizeOverdraw_helper(Node *node)
-{
- if (node->type() == QSGNode::GeometryNodeType && node->element()->batch) {
- VisualizeShader *shader = static_cast<VisualizeShader *>(m_shaderManager->visualizeProgram);
- QSGGeometryNode *gn = static_cast<QSGGeometryNode *>(node->sgNode);
-
- QMatrix4x4 matrix = m_current_projection_matrix;
- matrix(2, 2) = m_zRange;
- matrix(2, 3) = 1.0f - node->element()->order * m_zRange;
-
- if (node->element()->batch->root)
- matrix = matrix * qsg_matrixForRoot(node->element()->batch->root);
- matrix = matrix * *gn->matrix();
- shader->setUniformValue(shader->matrix, matrix);
-
- QColor color = node->element()->batch->isOpaque ? QColor::fromRgbF(0.3, 1.0, 0.3) : QColor::fromRgbF(1.0, 0.3, 0.3);
- float ca = 0.33f;
- shader->setUniformValue(shader->color, color.redF() * ca, color.greenF() * ca, color.blueF() * ca, ca);
-
- visualizeDrawGeometry(gn->geometry());
- }
-
- SHADOWNODE_TRAVERSE(node) {
- visualizeOverdraw_helper(child);
- }
-}
-
-void Renderer::visualizeOverdraw()
-{
- VisualizeShader *shader = static_cast<VisualizeShader *>(m_shaderManager->visualizeProgram);
- shader->setUniformValue(shader->color, 0.5f, 0.5f, 1.0f, 1.0f);
- shader->setUniformValue(shader->projection, 1);
-
- glBlendFunc(GL_ONE, GL_ONE);
-
- static float step = 0;
- step += static_cast<float>(M_PI * 2 / 1000.);
- if (step > M_PI * 2)
- step = 0;
- float angle = 80.0 * std::sin(step);
-
- QMatrix4x4 xrot; xrot.rotate(20, 1, 0, 0);
- QMatrix4x4 zrot; zrot.rotate(angle, 0, 0, 1);
- QMatrix4x4 tx; tx.translate(0, 0, 1);
-
- QMatrix4x4 m;
-
-// m.rotate(180, 0, 1, 0);
-
- m.translate(0, 0.5, 4);
- m.scale(2, 2, 1);
-
- m.rotate(-30, 1, 0, 0);
- m.rotate(angle, 0, 1, 0);
- m.translate(0, 0, -1);
-
- shader->setUniformValue(shader->rotation, m);
-
- float box[] = {
- // lower
- -1, 1, 0, 1, 1, 0,
- -1, 1, 0, -1, -1, 0,
- 1, 1, 0, 1, -1, 0,
- -1, -1, 0, 1, -1, 0,
-
- // upper
- -1, 1, 1, 1, 1, 1,
- -1, 1, 1, -1, -1, 1,
- 1, 1, 1, 1, -1, 1,
- -1, -1, 1, 1, -1, 1,
-
- // sides
- -1, -1, 0, -1, -1, 1,
- 1, -1, 0, 1, -1, 1,
- -1, 1, 0, -1, 1, 1,
- 1, 1, 0, 1, 1, 1
- };
- glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, box);
- glLineWidth(2);
- glDrawArrays(GL_LINES, 0, 24);
-
- visualizeOverdraw_helper(m_nodes.value(rootNode()));
-
- // Animate the view...
- QSurface *surface = m_context->openglContext()->surface();
- if (surface->surfaceClass() == QSurface::Window)
- if (QQuickWindow *window = qobject_cast<QQuickWindow *>(static_cast<QWindow *>(surface)))
- window->update();
-}
-
void Renderer::setCustomRenderMode(const QByteArray &mode)
{
- if (mode.isEmpty()) m_visualizeMode = VisualizeNothing;
- else if (mode == "clip") m_visualizeMode = VisualizeClipping;
- else if (mode == "overdraw") m_visualizeMode = VisualizeOverdraw;
- else if (mode == "batches") m_visualizeMode = VisualizeBatches;
- else if (mode == "changes") m_visualizeMode = VisualizeChanges;
+ if (mode.isEmpty())
+ m_visualizer->setMode(Visualizer::VisualizeNothing);
+ else if (mode == "clip")
+ m_visualizer->setMode(Visualizer::VisualizeClipping);
+ else if (mode == "overdraw")
+ m_visualizer->setMode(Visualizer::VisualizeOverdraw);
+ else if (mode == "batches")
+ m_visualizer->setMode(Visualizer::VisualizeBatches);
+ else if (mode == "changes")
+ m_visualizer->setMode(Visualizer::VisualizeChanges);
}
-void Renderer::visualize()
+bool Renderer::hasCustomRenderModeWithContinuousUpdate() const
{
- if (!m_shaderManager->visualizeProgram) {
- VisualizeShader *prog = new VisualizeShader();
- QSGShaderSourceBuilder::initializeProgramFromFiles(
- prog,
- QStringLiteral(":/qt-project.org/scenegraph/shaders/visualization.vert"),
- QStringLiteral(":/qt-project.org/scenegraph/shaders/visualization.frag"));
- prog->bindAttributeLocation("v", 0);
- prog->link();
- prog->bind();
- prog->color = prog->uniformLocation("color");
- prog->pattern = prog->uniformLocation("pattern");
- prog->projection = prog->uniformLocation("projection");
- prog->matrix = prog->uniformLocation("matrix");
- prog->rotation = prog->uniformLocation("rotation");
- m_shaderManager->visualizeProgram = prog;
- } else {
- m_shaderManager->visualizeProgram->bind();
- }
- VisualizeShader *shader = static_cast<VisualizeShader *>(m_shaderManager->visualizeProgram);
-
- glDisable(GL_DEPTH_TEST);
- glEnable(GL_BLEND);
- glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
- glEnableVertexAttribArray(0);
-
- // Blacken out the actual rendered content...
- float bgOpacity = 0.8f;
- if (m_visualizeMode == VisualizeBatches)
- bgOpacity = 1.0;
- float v[] = { -1, 1, 1, 1, -1, -1, 1, -1 };
- shader->setUniformValue(shader->color, 0.0f, 0.0f, 0.0f, bgOpacity);
- shader->setUniformValue(shader->matrix, QMatrix4x4());
- shader->setUniformValue(shader->rotation, QMatrix4x4());
- shader->setUniformValue(shader->pattern, 0.0f);
- shader->setUniformValue(shader->projection, false);
- glVertexAttribPointer(0, 2, GL_FLOAT, false, 0, v);
- glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
-
- if (m_visualizeMode == VisualizeBatches) {
- srand(0); // To force random colors to be roughly the same every time..
- for (int i=0; i<m_opaqueBatches.size(); ++i) visualizeBatch(m_opaqueBatches.at(i));
- for (int i=0; i<m_alphaBatches.size(); ++i) visualizeBatch(m_alphaBatches.at(i));
- } else if (m_visualizeMode == VisualizeClipping) {
- shader->setUniformValue(shader->pattern, 0.5f);
- shader->setUniformValue(shader->color, 0.2f, 0.0f, 0.0f, 0.2f);
- visualizeClipping(rootNode());
- } else if (m_visualizeMode == VisualizeChanges) {
- visualizeChanges(m_nodes.value(rootNode()));
- m_visualizeChanceSet.clear();
- } else if (m_visualizeMode == VisualizeOverdraw) {
- visualizeOverdraw();
- }
-
- // Reset state back to defaults..
- glDisable(GL_BLEND);
- glDisableVertexAttribArray(0);
- shader->release();
+ return m_visualizer->mode() == Visualizer::VisualizeOverdraw;
}
bool operator==(const GraphicsState &a, const GraphicsState &b) Q_DECL_NOTHROW
@@ -4822,8 +4549,34 @@ uint qHash(const GraphicsPipelineStateKey &k, uint seed) Q_DECL_NOTHROW
return qHash(k.state, seed) + qHash(k.sms->programRhi.program, seed) + qHash(k.rpDesc, seed);
}
-QT_END_NAMESPACE
+Visualizer::Visualizer(Renderer *renderer)
+ : m_renderer(renderer),
+ m_visualizeMode(VisualizeNothing)
+{
+}
+
+Visualizer::~Visualizer()
+{
+}
+
+#define QSGNODE_DIRTY_PARENT (QSGNode::DirtyNodeAdded \
+ | QSGNode::DirtyOpacity \
+ | QSGNode::DirtyMatrix \
+ | QSGNode::DirtyNodeRemoved)
+void Visualizer::visualizeChangesPrepare(Node *n, uint parentChanges)
+{
+ uint childDirty = (parentChanges | n->dirtyState) & QSGNODE_DIRTY_PARENT;
+ uint selfDirty = n->dirtyState | parentChanges;
+ if (n->type() == QSGNode::GeometryNodeType && selfDirty != 0)
+ m_visualizeChangeSet.insert(n, selfDirty);
+ SHADOWNODE_TRAVERSE(n) {
+ visualizeChangesPrepare(child, childDirty);
+ }
}
+} // namespace QSGBatchRenderer
+
+QT_END_NAMESPACE
+
#include "moc_qsgbatchrenderer_p.cpp"
diff --git a/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h b/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h
index 9dec203e73..ea9dab244f 100644
--- a/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h
+++ b/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h
@@ -648,7 +648,7 @@ public:
float lastOpacity;
};
- ShaderManager(QSGDefaultRenderContext *ctx) : visualizeProgram(nullptr), blitProgram(nullptr), context(ctx) { }
+ ShaderManager(QSGDefaultRenderContext *ctx) : blitProgram(nullptr), context(ctx) { }
~ShaderManager() {
qDeleteAll(rewrittenShaders);
qDeleteAll(stockShaders);
@@ -665,8 +665,6 @@ public:
Shader *prepareMaterial(QSGMaterial *material, bool enableRhiShaders = false, const QSGGeometry *geometry = nullptr);
Shader *prepareMaterialNoRewrite(QSGMaterial *material, bool enableRhiShaders = false, const QSGGeometry *geometry = nullptr);
- QOpenGLShaderProgram *visualizeProgram;
-
private:
QHash<QSGMaterialType *, Shader *> rewrittenShaders;
QHash<QSGMaterialType *, Shader *> stockShaders;
@@ -719,12 +717,9 @@ struct RenderPassState
bool scissorSet;
};
-class Q_QUICK_PRIVATE_EXPORT Renderer : public QSGRenderer, public QOpenGLFunctions
+class Visualizer
{
public:
- Renderer(QSGDefaultRenderContext *);
- ~Renderer();
-
enum VisualizeMode {
VisualizeNothing,
VisualizeBatches,
@@ -733,6 +728,30 @@ public:
VisualizeOverdraw
};
+ Visualizer(Renderer *renderer);
+ virtual ~Visualizer();
+
+ VisualizeMode mode() const { return m_visualizeMode; }
+ void setMode(VisualizeMode mode) { m_visualizeMode = mode; }
+
+ virtual void visualizeChangesPrepare(Node *n, uint parentChanges = 0);
+ virtual void prepareVisualize() = 0;
+ virtual void visualize() = 0;
+
+ virtual void releaseResources() = 0;
+
+protected:
+ Renderer *m_renderer;
+ VisualizeMode m_visualizeMode;
+ QHash<Node *, uint> m_visualizeChangeSet;
+};
+
+class Q_QUICK_PRIVATE_EXPORT Renderer : public QSGRenderer, public QOpenGLFunctions
+{
+public:
+ Renderer(QSGDefaultRenderContext *);
+ ~Renderer();
+
protected:
void nodeChanged(QSGNode *node, QSGNode::DirtyState state) override;
void render() override;
@@ -747,6 +766,8 @@ private:
};
friend class Updater;
+ friend class OpenGLVisualizer;
+ friend class RhiVisualizer;
void destroyGraphicsResources();
void map(Buffer *buffer, int size, bool isIndexBuf = false);
@@ -813,15 +834,8 @@ private:
inline Batch *newBatch();
void invalidateAndRecycleBatch(Batch *b);
- void visualize();
- void visualizeBatch(Batch *b);
- void visualizeClipping(QSGNode *node);
- void visualizeChangesPrepare(Node *n, uint parentChanges = 0);
- void visualizeChanges(Node *n);
- void visualizeOverdraw();
- void visualizeOverdraw_helper(Node *node);
- void visualizeDrawGeometry(const QSGGeometry *g);
void setCustomRenderMode(const QByteArray &mode) override;
+ bool hasCustomRenderModeWithContinuousUpdate() const override;
QSGDefaultRenderContext *m_context;
QSet<Node *> m_taggedRoots;
@@ -852,6 +866,8 @@ private:
int m_batchNodeThreshold;
int m_batchVertexThreshold;
+ Visualizer *m_visualizer;
+
// Stuff used during rendering only...
ShaderManager *m_shaderManager; // per rendercontext, shared
QSGMaterial *m_currentMaterial;
@@ -874,9 +890,6 @@ private:
// For minimal OpenGL core profile support
QOpenGLVertexArrayObject *m_vao;
- QHash<Node *, uint> m_visualizeChanceSet;
- VisualizeMode m_visualizeMode;
-
Allocator<Node, 256> m_nodeAllocator;
Allocator<Element, 64> m_elementAllocator;
diff --git a/src/quick/scenegraph/coreapi/qsgopenglvisualizer.cpp b/src/quick/scenegraph/coreapi/qsgopenglvisualizer.cpp
new file mode 100644
index 0000000000..6c2ff0b176
--- /dev/null
+++ b/src/quick/scenegraph/coreapi/qsgopenglvisualizer.cpp
@@ -0,0 +1,365 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Copyright (C) 2016 Jolla Ltd, author: <gunnar.sletta@jollamobile.com>
+** Copyright (C) 2016 Robin Burchell <robin.burchell@viroteck.net>
+** 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 "qsgopenglvisualizer_p.h"
+#include <qmath.h>
+#include <private/qsgshadersourcebuilder_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QSGBatchRenderer
+{
+
+#define QSGNODE_TRAVERSE(NODE) for (QSGNode *child = NODE->firstChild(); child; child = child->nextSibling())
+#define SHADOWNODE_TRAVERSE(NODE) for (Node *child = NODE->firstChild(); child; child = child->sibling())
+#define QSGNODE_DIRTY_PARENT (QSGNode::DirtyNodeAdded \
+ | QSGNode::DirtyOpacity \
+ | QSGNode::DirtyMatrix \
+ | QSGNode::DirtyNodeRemoved)
+
+QMatrix4x4 qsg_matrixForRoot(Node *node);
+
+class VisualizeShader : public QOpenGLShaderProgram
+{
+public:
+ int color;
+ int matrix;
+ int rotation;
+ int pattern;
+ int projection;
+};
+
+OpenGLVisualizer::OpenGLVisualizer(Renderer *renderer)
+ : Visualizer(renderer),
+ m_funcs(QOpenGLContext::currentContext()->functions()),
+ m_visualizeProgram(nullptr)
+{
+}
+
+OpenGLVisualizer::~OpenGLVisualizer()
+{
+ releaseResources();
+}
+
+void OpenGLVisualizer::releaseResources()
+{
+ delete m_visualizeProgram;
+ m_visualizeProgram = nullptr;
+}
+
+void OpenGLVisualizer::prepareVisualize()
+{
+ // nothing to do here
+}
+
+void OpenGLVisualizer::visualizeDrawGeometry(const QSGGeometry *g)
+{
+ if (g->attributeCount() < 1)
+ return;
+ const QSGGeometry::Attribute *a = g->attributes();
+ m_funcs->glVertexAttribPointer(0, a->tupleSize, a->type, false, g->sizeOfVertex(), g->vertexData());
+ if (g->indexCount())
+ m_funcs->glDrawElements(g->drawingMode(), g->indexCount(), g->indexType(), g->indexData());
+ else
+ m_funcs->glDrawArrays(g->drawingMode(), 0, g->vertexCount());
+
+}
+
+void OpenGLVisualizer::visualizeBatch(Batch *b)
+{
+ VisualizeShader *shader = static_cast<VisualizeShader *>(m_visualizeProgram);
+
+ if (b->positionAttribute != 0)
+ return;
+
+ QSGGeometryNode *gn = b->first->node;
+ QSGGeometry *g = gn->geometry();
+ const QSGGeometry::Attribute &a = g->attributes()[b->positionAttribute];
+
+ m_funcs->glBindBuffer(GL_ARRAY_BUFFER, b->vbo.id);
+
+ QMatrix4x4 matrix(m_renderer->m_current_projection_matrix);
+ if (b->root)
+ matrix = matrix * qsg_matrixForRoot(b->root);
+
+ shader->setUniformValue(shader->pattern, float(b->merged ? 0 : 1));
+
+ QColor color = QColor::fromHsvF((rand() & 1023) / 1023.0, 1.0, 1.0);
+ float cr = color.redF();
+ float cg = color.greenF();
+ float cb = color.blueF();
+ shader->setUniformValue(shader->color, cr, cg, cb, 1.0);
+
+ if (b->merged) {
+ shader->setUniformValue(shader->matrix, matrix);
+ const char *dataStart = m_renderer->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);
+ m_funcs->glVertexAttribPointer(a.position, 2, a.type, false, g->sizeOfVertex(),
+ (void *) (qintptr) (set.vertices));
+ m_funcs->glDrawElements(g->drawingMode(), set.indexCount, GL_UNSIGNED_SHORT,
+ (void *)(qintptr)(dataStart + set.indices));
+ }
+ } else {
+ Element *e = b->first;
+ int offset = 0;
+ while (e) {
+ gn = e->node;
+ g = gn->geometry();
+ shader->setUniformValue(shader->matrix, matrix * *gn->matrix());
+ m_funcs->glVertexAttribPointer(a.position, a.tupleSize, a.type, false, g->sizeOfVertex(),
+ (void *) (qintptr) offset);
+ if (g->indexCount())
+ m_funcs->glDrawElements(g->drawingMode(), g->indexCount(), g->indexType(), g->indexData());
+ else
+ m_funcs->glDrawArrays(g->drawingMode(), 0, g->vertexCount());
+ offset += g->sizeOfVertex() * g->vertexCount();
+ e = e->nextInBatch;
+ }
+ }
+}
+
+void OpenGLVisualizer::visualizeClipping(QSGNode *node)
+{
+ if (node->type() == QSGNode::ClipNodeType) {
+ VisualizeShader *shader = static_cast<VisualizeShader *>(m_visualizeProgram);
+ QSGClipNode *clipNode = static_cast<QSGClipNode *>(node);
+ QMatrix4x4 matrix = m_renderer->m_current_projection_matrix;
+ if (clipNode->matrix())
+ matrix = matrix * *clipNode->matrix();
+ shader->setUniformValue(shader->matrix, matrix);
+ visualizeDrawGeometry(clipNode->geometry());
+ }
+
+ QSGNODE_TRAVERSE(node) {
+ visualizeClipping(child);
+ }
+}
+
+void OpenGLVisualizer::visualizeChanges(Node *n)
+{
+
+ if (n->type() == QSGNode::GeometryNodeType && n->element()->batch && m_visualizeChangeSet.contains(n)) {
+ uint dirty = m_visualizeChangeSet.value(n);
+ bool tinted = (dirty & QSGNODE_DIRTY_PARENT) != 0;
+
+ VisualizeShader *shader = static_cast<VisualizeShader *>(m_visualizeProgram);
+ QColor color = QColor::fromHsvF((rand() & 1023) / 1023.0, 0.3, 1.0);
+ float ca = 0.5;
+ float cr = color.redF() * ca;
+ float cg = color.greenF() * ca;
+ float cb = color.blueF() * ca;
+ shader->setUniformValue(shader->color, cr, cg, cb, ca);
+ shader->setUniformValue(shader->pattern, float(tinted ? 0.5 : 0));
+
+ QSGGeometryNode *gn = static_cast<QSGGeometryNode *>(n->sgNode);
+
+ QMatrix4x4 matrix = m_renderer->m_current_projection_matrix;
+ if (n->element()->batch->root)
+ matrix = matrix * qsg_matrixForRoot(n->element()->batch->root);
+ matrix = matrix * *gn->matrix();
+ shader->setUniformValue(shader->matrix, matrix);
+ visualizeDrawGeometry(gn->geometry());
+
+ // 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 = nullptr;
+ }
+
+ SHADOWNODE_TRAVERSE(n) {
+ visualizeChanges(child);
+ }
+}
+
+void OpenGLVisualizer::visualizeOverdraw_helper(Node *node)
+{
+ if (node->type() == QSGNode::GeometryNodeType && node->element()->batch) {
+ VisualizeShader *shader = static_cast<VisualizeShader *>(m_visualizeProgram);
+ QSGGeometryNode *gn = static_cast<QSGGeometryNode *>(node->sgNode);
+
+ QMatrix4x4 matrix = m_renderer->m_current_projection_matrix;
+ matrix(2, 2) = m_renderer->m_zRange;
+ matrix(2, 3) = 1.0f - node->element()->order * m_renderer->m_zRange;
+
+ if (node->element()->batch->root)
+ matrix = matrix * qsg_matrixForRoot(node->element()->batch->root);
+ matrix = matrix * *gn->matrix();
+ shader->setUniformValue(shader->matrix, matrix);
+
+ QColor color = node->element()->batch->isOpaque ? QColor::fromRgbF(0.3, 1.0, 0.3) : QColor::fromRgbF(1.0, 0.3, 0.3);
+ float ca = 0.33f;
+ shader->setUniformValue(shader->color, color.redF() * ca, color.greenF() * ca, color.blueF() * ca, ca);
+
+ visualizeDrawGeometry(gn->geometry());
+ }
+
+ SHADOWNODE_TRAVERSE(node) {
+ visualizeOverdraw_helper(child);
+ }
+}
+
+void OpenGLVisualizer::visualizeOverdraw()
+{
+ VisualizeShader *shader = static_cast<VisualizeShader *>(m_visualizeProgram);
+ shader->setUniformValue(shader->color, 0.5f, 0.5f, 1.0f, 1.0f);
+ shader->setUniformValue(shader->projection, 1);
+
+ m_funcs->glBlendFunc(GL_ONE, GL_ONE);
+
+ static float step = 0;
+ step += static_cast<float>(M_PI * 2 / 1000.);
+ if (step > M_PI * 2)
+ step = 0;
+ float angle = 80.0 * std::sin(step);
+
+ QMatrix4x4 xrot; xrot.rotate(20, 1, 0, 0);
+ QMatrix4x4 zrot; zrot.rotate(angle, 0, 0, 1);
+ QMatrix4x4 tx; tx.translate(0, 0, 1);
+
+ QMatrix4x4 m;
+
+// m.rotate(180, 0, 1, 0);
+
+ m.translate(0, 0.5, 4);
+ m.scale(2, 2, 1);
+
+ m.rotate(-30, 1, 0, 0);
+ m.rotate(angle, 0, 1, 0);
+ m.translate(0, 0, -1);
+
+ shader->setUniformValue(shader->rotation, m);
+
+ float box[] = {
+ // lower
+ -1, 1, 0, 1, 1, 0,
+ -1, 1, 0, -1, -1, 0,
+ 1, 1, 0, 1, -1, 0,
+ -1, -1, 0, 1, -1, 0,
+
+ // upper
+ -1, 1, 1, 1, 1, 1,
+ -1, 1, 1, -1, -1, 1,
+ 1, 1, 1, 1, -1, 1,
+ -1, -1, 1, 1, -1, 1,
+
+ // sides
+ -1, -1, 0, -1, -1, 1,
+ 1, -1, 0, 1, -1, 1,
+ -1, 1, 0, -1, 1, 1,
+ 1, 1, 0, 1, 1, 1
+ };
+ m_funcs->glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, box);
+ m_funcs->glLineWidth(2);
+ m_funcs->glDrawArrays(GL_LINES, 0, 24);
+
+ visualizeOverdraw_helper(m_renderer->m_nodes.value(m_renderer->rootNode()));
+}
+
+void OpenGLVisualizer::visualize()
+{
+ if (m_visualizeMode == VisualizeNothing)
+ return;
+
+ if (!m_visualizeProgram) {
+ VisualizeShader *prog = new VisualizeShader();
+ QSGShaderSourceBuilder::initializeProgramFromFiles(
+ prog,
+ QStringLiteral(":/qt-project.org/scenegraph/shaders/visualization.vert"),
+ QStringLiteral(":/qt-project.org/scenegraph/shaders/visualization.frag"));
+ prog->bindAttributeLocation("v", 0);
+ prog->link();
+ prog->bind();
+ prog->color = prog->uniformLocation("color");
+ prog->pattern = prog->uniformLocation("pattern");
+ prog->projection = prog->uniformLocation("projection");
+ prog->matrix = prog->uniformLocation("matrix");
+ prog->rotation = prog->uniformLocation("rotation");
+ m_visualizeProgram = prog;
+ } else {
+ m_visualizeProgram->bind();
+ }
+ VisualizeShader *shader = static_cast<VisualizeShader *>(m_visualizeProgram);
+
+ m_funcs->glDisable(GL_DEPTH_TEST);
+ m_funcs->glEnable(GL_BLEND);
+ m_funcs->glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+ m_funcs->glEnableVertexAttribArray(0);
+
+ // Blacken out the actual rendered content...
+ float bgOpacity = 0.8f;
+ if (m_visualizeMode == VisualizeBatches)
+ bgOpacity = 1.0;
+ float v[] = { -1, 1, 1, 1, -1, -1, 1, -1 };
+ shader->setUniformValue(shader->color, 0.0f, 0.0f, 0.0f, bgOpacity);
+ shader->setUniformValue(shader->matrix, QMatrix4x4());
+ shader->setUniformValue(shader->rotation, QMatrix4x4());
+ shader->setUniformValue(shader->pattern, 0.0f);
+ shader->setUniformValue(shader->projection, false);
+ m_funcs->glVertexAttribPointer(0, 2, GL_FLOAT, false, 0, v);
+ m_funcs->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+
+ if (m_visualizeMode == VisualizeBatches) {
+ srand(0); // To force random colors to be roughly the same every time..
+ for (int i = 0; i < m_renderer->m_opaqueBatches.size(); ++i)
+ visualizeBatch(m_renderer->m_opaqueBatches.at(i));
+ for (int i = 0; i < m_renderer->m_alphaBatches.size(); ++i)
+ visualizeBatch(m_renderer->m_alphaBatches.at(i));
+ } else if (m_visualizeMode == VisualizeClipping) {
+ shader->setUniformValue(shader->pattern, 0.5f);
+ shader->setUniformValue(shader->color, 0.2f, 0.0f, 0.0f, 0.2f);
+ visualizeClipping(m_renderer->rootNode());
+ } else if (m_visualizeMode == VisualizeChanges) {
+ visualizeChanges(m_renderer->m_nodes.value(m_renderer->rootNode()));
+ m_visualizeChangeSet.clear();
+ } else if (m_visualizeMode == VisualizeOverdraw) {
+ visualizeOverdraw();
+ }
+
+ // Reset state back to defaults..
+ m_funcs->glDisable(GL_BLEND);
+ m_funcs->glDisableVertexAttribArray(0);
+ shader->release();
+}
+
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/coreapi/qsgopenglvisualizer_p.h b/src/quick/scenegraph/coreapi/qsgopenglvisualizer_p.h
new file mode 100644
index 0000000000..0b21694351
--- /dev/null
+++ b/src/quick/scenegraph/coreapi/qsgopenglvisualizer_p.h
@@ -0,0 +1,91 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2016 Jolla Ltd, author: <gunnar.sletta@jollamobile.com>
+** Copyright (C) 2016 Robin Burchell <robin.burchell@viroteck.net>
+** 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 QSGOPENGLVISUALIZER_P_H
+#define QSGOPENGLVISUALIZER_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 "qsgbatchrenderer_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QSGBatchRenderer
+{
+
+// ### Qt 6: remove
+class OpenGLVisualizer : public Visualizer
+{
+public:
+ OpenGLVisualizer(Renderer *renderer);
+ ~OpenGLVisualizer();
+
+ void prepareVisualize() override;
+ void visualize() override;
+
+ void releaseResources() override;
+
+private:
+ void visualizeBatch(Batch *b);
+ void visualizeClipping(QSGNode *node);
+ void visualizeChanges(Node *n);
+ void visualizeOverdraw();
+ void visualizeOverdraw_helper(Node *node);
+ void visualizeDrawGeometry(const QSGGeometry *g);
+
+ QOpenGLFunctions *m_funcs;
+ QOpenGLShaderProgram *m_visualizeProgram;
+};
+
+} // namespace QSGBatchRenderer
+
+QT_END_NAMESPACE
+
+#endif // QSGOPENGLVISUALIZER_P_H
diff --git a/src/quick/scenegraph/coreapi/qsgrenderer_p.h b/src/quick/scenegraph/coreapi/qsgrenderer_p.h
index 9c83ddf111..c4ed0072f6 100644
--- a/src/quick/scenegraph/coreapi/qsgrenderer_p.h
+++ b/src/quick/scenegraph/coreapi/qsgrenderer_p.h
@@ -97,6 +97,7 @@ public:
inline QSGMaterialShader::RenderState state(QSGMaterialShader::RenderState::DirtyStates dirty) const;
inline QSGMaterialRhiShader::RenderState rhiState(QSGMaterialRhiShader::RenderState::DirtyStates dirty) const;
virtual void setCustomRenderMode(const QByteArray &) { }
+ virtual bool hasCustomRenderModeWithContinuousUpdate() const { return false; }
virtual void releaseCachedResources() { }
void clearChangedFlag() { m_changed_emitted = false; }
diff --git a/src/quick/scenegraph/coreapi/qsgrhivisualizer.cpp b/src/quick/scenegraph/coreapi/qsgrhivisualizer.cpp
new file mode 100644
index 0000000000..38d4c4440f
--- /dev/null
+++ b/src/quick/scenegraph/coreapi/qsgrhivisualizer.cpp
@@ -0,0 +1,929 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Copyright (C) 2016 Jolla Ltd, author: <gunnar.sletta@jollamobile.com>
+** Copyright (C) 2016 Robin Burchell <robin.burchell@viroteck.net>
+** 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 "qsgrhivisualizer_p.h"
+#include <qmath.h>
+#include <QQuickWindow>
+#include <private/qsgmaterialrhishader_p.h>
+#include <private/qsgshadersourcebuilder_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QSGBatchRenderer
+{
+
+#define QSGNODE_TRAVERSE(NODE) for (QSGNode *child = NODE->firstChild(); child; child = child->nextSibling())
+#define SHADOWNODE_TRAVERSE(NODE) for (Node *child = NODE->firstChild(); child; child = child->sibling())
+#define QSGNODE_DIRTY_PARENT (QSGNode::DirtyNodeAdded \
+ | QSGNode::DirtyOpacity \
+ | QSGNode::DirtyMatrix \
+ | QSGNode::DirtyNodeRemoved)
+
+QMatrix4x4 qsg_matrixForRoot(Node *node);
+QRhiVertexInputAttribute::Format qsg_vertexInputFormat(const QSGGeometry::Attribute &a);
+QRhiCommandBuffer::IndexFormat qsg_indexFormat(const QSGGeometry *geometry);
+QRhiGraphicsPipeline::Topology qsg_topology(int geomDrawMode);
+
+RhiVisualizer::RhiVisualizer(Renderer *renderer)
+ : Visualizer(renderer)
+{
+}
+
+RhiVisualizer::~RhiVisualizer()
+{
+ releaseResources();
+}
+
+void RhiVisualizer::releaseResources()
+{
+ m_pipelines.releaseResources();
+
+ m_fade.releaseResources();
+
+ m_changeVis.releaseResources();
+ m_batchVis.releaseResources();
+ m_clipVis.releaseResources();
+ m_overdrawVis.releaseResources();
+}
+
+void RhiVisualizer::prepareVisualize()
+{
+ // Called before the render pass has begun (but after preparing the
+ // batches). Now is the time to put resource updates to the renderer's
+ // current m_resourceUpdates instance.
+
+ if (m_visualizeMode == VisualizeNothing)
+ return;
+
+ if (!m_vs.isValid()) {
+ m_vs = QSGMaterialRhiShaderPrivate::loadShader(
+ QLatin1String(":/qt-project.org/scenegraph/shaders_ng/visualization.vert.qsb"));
+ m_fs = QSGMaterialRhiShaderPrivate::loadShader(
+ QLatin1String(":/qt-project.org/scenegraph/shaders_ng/visualization.frag.qsb"));
+ }
+
+ m_fade.prepare(this, m_renderer->m_rhi, m_renderer->m_resourceUpdates, m_renderer->renderPassDescriptor());
+
+ const bool forceUintIndex = m_renderer->m_uint32IndexForRhi;
+
+ switch (m_visualizeMode) {
+ case VisualizeBatches:
+ m_batchVis.prepare(m_renderer->m_opaqueBatches, m_renderer->m_alphaBatches,
+ this,
+ m_renderer->m_rhi, m_renderer->m_resourceUpdates,
+ forceUintIndex);
+ break;
+ case VisualizeClipping:
+ m_clipVis.prepare(m_renderer->rootNode(), this,
+ m_renderer->m_rhi, m_renderer->m_resourceUpdates);
+ break;
+ case VisualizeChanges:
+ m_changeVis.prepare(m_renderer->m_nodes.value(m_renderer->rootNode()),
+ this,
+ m_renderer->m_rhi, m_renderer->m_resourceUpdates);
+ m_visualizeChangeSet.clear();
+ break;
+ case VisualizeOverdraw:
+ m_overdrawVis.prepare(m_renderer->m_nodes.value(m_renderer->rootNode()),
+ this,
+ m_renderer->m_rhi, m_renderer->m_resourceUpdates);
+ break;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+}
+
+void RhiVisualizer::visualize()
+{
+ if (m_visualizeMode == VisualizeNothing)
+ return;
+
+ QRhiCommandBuffer *cb = m_renderer->commandBuffer();
+ m_fade.render(cb);
+
+ switch (m_visualizeMode) {
+ case VisualizeBatches:
+ m_batchVis.render(cb);
+ break;
+ case VisualizeClipping:
+ m_clipVis.render(cb);
+ break;
+ case VisualizeChanges:
+ m_changeVis.render(cb);
+ break;
+ case VisualizeOverdraw:
+ m_overdrawVis.render(cb);
+ break;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+}
+
+void RhiVisualizer::recordDrawCalls(const QVector<DrawCall> &drawCalls,
+ QRhiCommandBuffer *cb,
+ QRhiShaderResourceBindings *srb,
+ bool blendOneOne)
+{
+ for (const DrawCall &dc : drawCalls) {
+ QRhiGraphicsPipeline *ps = m_pipelines.pipeline(this, m_renderer->m_rhi, srb, m_renderer->renderPassDescriptor(),
+ dc.vertex.topology, dc.vertex.format, dc.vertex.stride,
+ blendOneOne);
+ if (!ps)
+ continue;
+ cb->setGraphicsPipeline(ps); // no-op if same as the last one
+ QRhiCommandBuffer::DynamicOffset dynofs(0, dc.buf.ubufOffset);
+ cb->setShaderResources(srb, 1, &dynofs);
+ QRhiCommandBuffer::VertexInput vb(dc.buf.vbuf, dc.buf.vbufOffset);
+ if (dc.index.count) {
+ cb->setVertexInput(0, 1, &vb, dc.buf.ibuf, dc.buf.ibufOffset, dc.index.format);
+ cb->drawIndexed(dc.index.count);
+ } else {
+ cb->setVertexInput(0, 1, &vb);
+ cb->draw(dc.vertex.count);
+ }
+ }
+}
+
+const QRhiShaderResourceBinding::StageFlags ubufVisibility =
+ QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage;
+
+void RhiVisualizer::Fade::prepare(RhiVisualizer *visualizer,
+ QRhi *rhi, QRhiResourceUpdateBatch *u, QRhiRenderPassDescriptor *rpDesc)
+{
+ this->visualizer = visualizer;
+
+ if (!vbuf) {
+ float v[] = { -1, 1, 1, 1, -1, -1, 1, -1 };
+ vbuf = rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(v));
+ if (!vbuf->build())
+ return;
+ u->uploadStaticBuffer(vbuf, v);
+ }
+
+ if (!ubuf) {
+ ubuf = rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, DrawCall::UBUF_SIZE);
+ if (!ubuf->build())
+ return;
+ float bgOpacity = 0.8f;
+ if (visualizer->m_visualizeMode == Visualizer::VisualizeBatches)
+ bgOpacity = 1.0;
+ QMatrix4x4 ident;
+ u->updateDynamicBuffer(ubuf, 0, 64, ident.constData()); // matrix
+ u->updateDynamicBuffer(ubuf, 64, 64, ident.constData()); // rotation
+ float color[4] = { 0.0f, 0.0f, 0.0f, bgOpacity };
+ u->updateDynamicBuffer(ubuf, 128, 16, color);
+ float pattern = 0.0f;
+ u->updateDynamicBuffer(ubuf, 144, 4, &pattern);
+ qint32 projection = 0;
+ u->updateDynamicBuffer(ubuf, 148, 4, &projection);
+ }
+
+ if (!srb) {
+ srb = rhi->newShaderResourceBindings();
+ srb->setBindings({ QRhiShaderResourceBinding::uniformBuffer(0, ubufVisibility, ubuf) });
+ if (!srb->build())
+ return;
+ }
+
+ if (!ps) {
+ ps = rhi->newGraphicsPipeline();
+ ps->setTopology(QRhiGraphicsPipeline::TriangleStrip);
+ QRhiGraphicsPipeline::TargetBlend blend; // defaults to premul alpha, just what we need
+ blend.enable = true;
+ ps->setTargetBlends({ blend });
+ ps->setShaderStages({ { QRhiShaderStage::Vertex, visualizer->m_vs },
+ { QRhiShaderStage::Fragment, visualizer->m_fs } });
+ QRhiVertexInputLayout inputLayout;
+ inputLayout.setBindings({ { 2 * sizeof(float) } });
+ inputLayout.setAttributes({ { 0, 0, QRhiVertexInputAttribute::Float2, 0 } });
+ ps->setVertexInputLayout(inputLayout);
+ ps->setShaderResourceBindings(srb);
+ ps->setRenderPassDescriptor(rpDesc);
+ if (!ps->build())
+ return;
+ }
+}
+
+void RhiVisualizer::Fade::releaseResources()
+{
+ delete ps;
+ ps = nullptr;
+
+ delete srb;
+ srb = nullptr;
+
+ delete ubuf;
+ ubuf = nullptr;
+
+ delete vbuf;
+ vbuf = nullptr;
+}
+
+void RhiVisualizer::Fade::render(QRhiCommandBuffer *cb)
+{
+ cb->setGraphicsPipeline(ps);
+ cb->setViewport(visualizer->m_renderer->m_pstate.viewport);
+ cb->setShaderResources();
+ QRhiCommandBuffer::VertexInput vb(vbuf, 0);
+ cb->setVertexInput(0, 1, &vb);
+ cb->draw(4);
+}
+
+static void fillVertexIndex(RhiVisualizer::DrawCall *dc, QSGGeometry *g, bool withData, bool forceUintIndex)
+{
+ dc->vertex.topology = qsg_topology(g->drawingMode());
+ dc->vertex.format = qsg_vertexInputFormat(g->attributes()[0]);
+ dc->vertex.count = g->vertexCount();
+ dc->vertex.stride = g->sizeOfVertex();
+ if (withData)
+ dc->vertex.data = g->vertexData();
+
+ dc->index.format = forceUintIndex ? QRhiCommandBuffer::IndexUInt32 : qsg_indexFormat(g);
+ dc->index.count = g->indexCount();
+ dc->index.stride = forceUintIndex ? sizeof(quint32) : g->sizeOfIndex();
+ if (withData && g->indexCount())
+ dc->index.data = g->indexData();
+}
+
+static inline uint aligned(uint v, uint byteAlign)
+{
+ return (v + byteAlign - 1) & ~(byteAlign - 1);
+}
+
+static bool ensureBuffer(QRhi *rhi, QRhiBuffer **buf, QRhiBuffer::UsageFlags usage, int newSize)
+{
+ if (!*buf) {
+ *buf = rhi->newBuffer(QRhiBuffer::Dynamic, usage, newSize);
+ if (!(*buf)->build())
+ return false;
+ } else if ((*buf)->size() < newSize) {
+ (*buf)->setSize(newSize);
+ if (!(*buf)->build())
+ return false;
+ }
+ return true;
+}
+
+QRhiGraphicsPipeline *RhiVisualizer::PipelineCache::pipeline(RhiVisualizer *visualizer,
+ QRhi *rhi,
+ QRhiShaderResourceBindings *srb,
+ QRhiRenderPassDescriptor *rpDesc,
+ QRhiGraphicsPipeline::Topology topology,
+ QRhiVertexInputAttribute::Format vertexFormat,
+ quint32 vertexStride,
+ bool blendOneOne)
+{
+ for (int i = 0, ie = pipelines.count(); i != ie; ++i) {
+ const Pipeline &p(pipelines.at(i));
+ if (p.topology == topology && p.format == vertexFormat && p.stride == vertexStride)
+ return p.ps;
+ }
+
+ QRhiGraphicsPipeline *ps = rhi->newGraphicsPipeline();
+ ps->setTopology(topology);
+ QRhiGraphicsPipeline::TargetBlend blend; // premul alpha
+ blend.enable = true;
+ if (blendOneOne) {
+ // only for visualizing overdraw, other modes use premul alpha
+ blend.srcColor = QRhiGraphicsPipeline::One;
+ blend.dstColor = QRhiGraphicsPipeline::One;
+ blend.srcAlpha = QRhiGraphicsPipeline::One;
+ blend.dstAlpha = QRhiGraphicsPipeline::One;
+ }
+ ps->setTargetBlends({ blend });
+ ps->setShaderStages({ { QRhiShaderStage::Vertex, visualizer->m_vs },
+ { QRhiShaderStage::Fragment, visualizer->m_fs } });
+ QRhiVertexInputLayout inputLayout;
+ inputLayout.setBindings({ { vertexStride } });
+ inputLayout.setAttributes({ { 0, 0, vertexFormat, 0 } });
+ ps->setVertexInputLayout(inputLayout);
+ ps->setShaderResourceBindings(srb);
+ ps->setRenderPassDescriptor(rpDesc);
+ if (!ps->build())
+ return nullptr;
+
+ Pipeline p;
+ p.topology = topology;
+ p.format = vertexFormat;
+ p.stride = vertexStride;
+ p.ps = ps;
+ pipelines.append(p);
+
+ return ps;
+}
+
+void RhiVisualizer::PipelineCache::releaseResources()
+{
+ for (int i = 0, ie = pipelines.count(); i != ie; ++i)
+ delete pipelines.at(i).ps;
+
+ pipelines.clear();
+}
+
+void RhiVisualizer::ChangeVis::gather(Node *n)
+{
+ if (n->type() == QSGNode::GeometryNodeType && n->element()->batch && visualizer->m_visualizeChangeSet.contains(n)) {
+ const uint dirty = visualizer->m_visualizeChangeSet.value(n);
+ const bool tinted = (dirty & QSGNODE_DIRTY_PARENT) != 0;
+ const QColor color = QColor::fromHsvF((rand() & 1023) / 1023.0f, 0.3f, 1.0f);
+ const float alpha = 0.5f;
+
+ QMatrix4x4 matrix = visualizer->m_renderer->m_current_projection_matrix;
+ if (n->element()->batch->root)
+ matrix = matrix * qsg_matrixForRoot(n->element()->batch->root);
+
+ QSGGeometryNode *gn = static_cast<QSGGeometryNode *>(n->sgNode);
+ matrix = matrix * *gn->matrix();
+
+ QSGGeometry *g = gn->geometry();
+ if (g->attributeCount() >= 1) {
+ DrawCall dc;
+ memcpy(dc.uniforms.data, matrix.constData(), 64);
+ QMatrix4x4 rotation;
+ memcpy(dc.uniforms.data + 64, rotation.constData(), 64);
+ float c[4] = {
+ float(color.redF()) * alpha,
+ float(color.greenF()) * alpha,
+ float(color.blueF()) * alpha,
+ alpha
+ };
+ memcpy(dc.uniforms.data + 128, c, 16);
+ float pattern = tinted ? 0.5f : 0.0f;
+ memcpy(dc.uniforms.data + 144, &pattern, 4);
+ qint32 projection = 0;
+ memcpy(dc.uniforms.data + 148, &projection, 4);
+
+ fillVertexIndex(&dc, g, true, false);
+ drawCalls.append(dc);
+ }
+
+ // 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 = nullptr;
+ }
+
+ SHADOWNODE_TRAVERSE(n) {
+ gather(child);
+ }
+}
+
+void RhiVisualizer::ChangeVis::prepare(Node *n, RhiVisualizer *visualizer,
+ QRhi *rhi, QRhiResourceUpdateBatch *u)
+{
+ this->visualizer = visualizer;
+
+ drawCalls.clear();
+ gather(n);
+
+ if (drawCalls.isEmpty())
+ return;
+
+ const int ubufAlign = rhi->ubufAlignment();
+ int vbufOffset = 0;
+ int ibufOffset = 0;
+ int ubufOffset = 0;
+ for (RhiVisualizer::DrawCall &dc : drawCalls) {
+ dc.buf.vbufOffset = aligned(vbufOffset, 4);
+ vbufOffset = dc.buf.vbufOffset + dc.vertex.count * dc.vertex.stride;
+
+ dc.buf.ibufOffset = aligned(ibufOffset, 4);
+ ibufOffset = dc.buf.ibufOffset + dc.index.count * dc.index.stride;
+
+ dc.buf.ubufOffset = aligned(ubufOffset, ubufAlign);
+ ubufOffset = dc.buf.ubufOffset + DrawCall::UBUF_SIZE;
+ }
+
+ ensureBuffer(rhi, &vbuf, QRhiBuffer::VertexBuffer, vbufOffset);
+ if (ibufOffset)
+ ensureBuffer(rhi, &ibuf, QRhiBuffer::IndexBuffer, ibufOffset);
+ const int ubufSize = ubufOffset;
+ ensureBuffer(rhi, &ubuf, QRhiBuffer::UniformBuffer, ubufSize);
+
+ for (RhiVisualizer::DrawCall &dc : drawCalls) {
+ u->updateDynamicBuffer(vbuf, dc.buf.vbufOffset, dc.vertex.count * dc.vertex.stride, dc.vertex.data);
+ dc.buf.vbuf = vbuf;
+ if (dc.index.count) {
+ u->updateDynamicBuffer(ibuf, dc.buf.ibufOffset, dc.index.count * dc.index.stride, dc.index.data);
+ dc.buf.ibuf = ibuf;
+ }
+ u->updateDynamicBuffer(ubuf, dc.buf.ubufOffset, DrawCall::UBUF_SIZE, dc.uniforms.data);
+ }
+
+ if (!srb) {
+ srb = rhi->newShaderResourceBindings();
+ srb->setBindings({ QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(0, ubufVisibility, ubuf, DrawCall::UBUF_SIZE) });
+ if (!srb->build())
+ return;
+ }
+}
+
+void RhiVisualizer::ChangeVis::releaseResources()
+{
+ delete srb;
+ srb = nullptr;
+
+ delete ubuf;
+ ubuf = nullptr;
+
+ delete ibuf;
+ ibuf = nullptr;
+
+ delete vbuf;
+ vbuf = nullptr;
+}
+
+void RhiVisualizer::ChangeVis::render(QRhiCommandBuffer *cb)
+{
+ visualizer->recordDrawCalls(drawCalls, cb, srb);
+}
+
+void RhiVisualizer::BatchVis::gather(Batch *b)
+{
+ if (b->positionAttribute != 0)
+ return;
+
+ QMatrix4x4 matrix(visualizer->m_renderer->m_current_projection_matrix);
+ if (b->root)
+ matrix = matrix * qsg_matrixForRoot(b->root);
+
+ DrawCall dc;
+
+ QMatrix4x4 rotation;
+ memcpy(dc.uniforms.data + 64, rotation.constData(), 64);
+
+ QColor color = QColor::fromHsvF((rand() & 1023) / 1023.0, 1.0, 1.0);
+
+ float c[4] = {
+ float(color.redF()),
+ float(color.greenF()),
+ float(color.blueF()),
+ 1.0f
+ };
+ memcpy(dc.uniforms.data + 128, c, 16);
+
+ float pattern = b->merged ? 0.0f : 1.0f;
+ memcpy(dc.uniforms.data + 144, &pattern, 4);
+
+ qint32 projection = 0;
+ memcpy(dc.uniforms.data + 148, &projection, 4);
+
+ if (b->merged) {
+ memcpy(dc.uniforms.data, matrix.constData(), 64);
+
+ QSGGeometryNode *gn = b->first->node;
+ QSGGeometry *g = gn->geometry();
+
+ fillVertexIndex(&dc, g, false, forceUintIndex);
+
+ for (int ds = 0; ds < b->drawSets.size(); ++ds) {
+ const DrawSet &set = b->drawSets.at(ds);
+ dc.buf.vbuf = b->vbo.buf;
+ dc.buf.vbufOffset = set.vertices;
+ dc.buf.ibuf = b->ibo.buf;
+ dc.buf.ibufOffset = set.indices;
+ dc.index.count = set.indexCount;
+ drawCalls.append(dc);
+ }
+ } else {
+ Element *e = b->first;
+ int vOffset = 0;
+ int iOffset = 0;
+
+ while (e) {
+ QSGGeometryNode *gn = e->node;
+ QSGGeometry *g = gn->geometry();
+
+ QMatrix4x4 m = matrix * *gn->matrix();
+ memcpy(dc.uniforms.data, m.constData(), 64);
+
+ fillVertexIndex(&dc, g, false, forceUintIndex);
+
+ dc.buf.vbuf = b->vbo.buf;
+ dc.buf.vbufOffset = vOffset;
+ if (g->indexCount()) {
+ dc.buf.ibuf = b->ibo.buf;
+ dc.buf.ibufOffset = iOffset;
+ }
+
+ drawCalls.append(dc);
+
+ vOffset += dc.vertex.count * dc.vertex.stride;
+ iOffset += dc.index.count * dc.index.stride;
+
+ e = e->nextInBatch;
+ }
+ }
+}
+
+void RhiVisualizer::BatchVis::prepare(const QDataBuffer<Batch *> &opaqueBatches, const QDataBuffer<Batch *> &alphaBatches,
+ RhiVisualizer *visualizer,
+ QRhi *rhi, QRhiResourceUpdateBatch *u,
+ bool forceUintIndex)
+{
+ this->visualizer = visualizer;
+ this->forceUintIndex = forceUintIndex;
+
+ drawCalls.clear();
+
+ srand(0); // To force random colors to be roughly the same every time..
+ for (int i = 0; i < opaqueBatches.size(); ++i)
+ gather(opaqueBatches.at(i));
+ for (int i = 0; i < alphaBatches.size(); ++i)
+ gather(alphaBatches.at(i));
+
+ if (drawCalls.isEmpty())
+ return;
+
+ const int ubufAlign = rhi->ubufAlignment();
+ int ubufOffset = 0;
+ for (RhiVisualizer::DrawCall &dc : drawCalls) {
+ dc.buf.ubufOffset = aligned(ubufOffset, ubufAlign);
+ ubufOffset = dc.buf.ubufOffset + DrawCall::UBUF_SIZE;
+ }
+
+ const int ubufSize = ubufOffset;
+ ensureBuffer(rhi, &ubuf, QRhiBuffer::UniformBuffer, ubufSize);
+
+ for (RhiVisualizer::DrawCall &dc : drawCalls)
+ u->updateDynamicBuffer(ubuf, dc.buf.ubufOffset, DrawCall::UBUF_SIZE, dc.uniforms.data);
+
+ if (!srb) {
+ srb = rhi->newShaderResourceBindings();
+ srb->setBindings({ QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(0, ubufVisibility, ubuf, DrawCall::UBUF_SIZE) });
+ if (!srb->build())
+ return;
+ }
+}
+
+void RhiVisualizer::BatchVis::releaseResources()
+{
+ delete srb;
+ srb = nullptr;
+
+ delete ubuf;
+ ubuf = nullptr;
+}
+
+void RhiVisualizer::BatchVis::render(QRhiCommandBuffer *cb)
+{
+ visualizer->recordDrawCalls(drawCalls, cb, srb);
+}
+
+void RhiVisualizer::ClipVis::gather(QSGNode *node)
+{
+ if (node->type() == QSGNode::ClipNodeType) {
+ QSGClipNode *clipNode = static_cast<QSGClipNode *>(node);
+ QMatrix4x4 matrix = visualizer->m_renderer->m_current_projection_matrix;
+ if (clipNode->matrix())
+ matrix = matrix * *clipNode->matrix();
+
+ QSGGeometry *g = clipNode->geometry();
+ if (g->attributeCount() >= 1) {
+ DrawCall dc;
+ memcpy(dc.uniforms.data, matrix.constData(), 64);
+ QMatrix4x4 rotation;
+ memcpy(dc.uniforms.data + 64, rotation.constData(), 64);
+ float c[4] = { 0.2f, 0.0f, 0.0f, 0.2f };
+ memcpy(dc.uniforms.data + 128, c, 16);
+ float pattern = 0.5f;
+ memcpy(dc.uniforms.data + 144, &pattern, 4);
+ qint32 projection = 0;
+ memcpy(dc.uniforms.data + 148, &projection, 4);
+ fillVertexIndex(&dc, g, true, false);
+ drawCalls.append(dc);
+ }
+ }
+
+ QSGNODE_TRAVERSE(node) {
+ gather(child);
+ }
+}
+
+void RhiVisualizer::ClipVis::prepare(QSGNode *node, RhiVisualizer *visualizer,
+ QRhi *rhi, QRhiResourceUpdateBatch *u)
+{
+ this->visualizer = visualizer;
+
+ drawCalls.clear();
+ gather(node);
+
+ if (drawCalls.isEmpty())
+ return;
+
+ const int ubufAlign = rhi->ubufAlignment();
+ int vbufOffset = 0;
+ int ibufOffset = 0;
+ int ubufOffset = 0;
+ for (RhiVisualizer::DrawCall &dc : drawCalls) {
+ dc.buf.vbufOffset = aligned(vbufOffset, 4);
+ vbufOffset = dc.buf.vbufOffset + dc.vertex.count * dc.vertex.stride;
+
+ dc.buf.ibufOffset = aligned(ibufOffset, 4);
+ ibufOffset = dc.buf.ibufOffset + dc.index.count * dc.index.stride;
+
+ dc.buf.ubufOffset = aligned(ubufOffset, ubufAlign);
+ ubufOffset = dc.buf.ubufOffset + DrawCall::UBUF_SIZE;
+ }
+
+ ensureBuffer(rhi, &vbuf, QRhiBuffer::VertexBuffer, vbufOffset);
+ if (ibufOffset)
+ ensureBuffer(rhi, &ibuf, QRhiBuffer::IndexBuffer, ibufOffset);
+ const int ubufSize = ubufOffset;
+ ensureBuffer(rhi, &ubuf, QRhiBuffer::UniformBuffer, ubufSize);
+
+ for (RhiVisualizer::DrawCall &dc : drawCalls) {
+ u->updateDynamicBuffer(vbuf, dc.buf.vbufOffset, dc.vertex.count * dc.vertex.stride, dc.vertex.data);
+ dc.buf.vbuf = vbuf;
+ if (dc.index.count) {
+ u->updateDynamicBuffer(ibuf, dc.buf.ibufOffset, dc.index.count * dc.index.stride, dc.index.data);
+ dc.buf.ibuf = ibuf;
+ }
+ u->updateDynamicBuffer(ubuf, dc.buf.ubufOffset, DrawCall::UBUF_SIZE, dc.uniforms.data);
+ }
+
+ if (!srb) {
+ srb = rhi->newShaderResourceBindings();
+ srb->setBindings({ QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(0, ubufVisibility, ubuf, DrawCall::UBUF_SIZE) });
+ if (!srb->build())
+ return;
+ }
+}
+
+void RhiVisualizer::ClipVis::releaseResources()
+{
+ delete srb;
+ srb = nullptr;
+
+ delete ubuf;
+ ubuf = nullptr;
+
+ delete ibuf;
+ ibuf = nullptr;
+
+ delete vbuf;
+ vbuf = nullptr;
+}
+
+void RhiVisualizer::ClipVis::render(QRhiCommandBuffer *cb)
+{
+ visualizer->recordDrawCalls(drawCalls, cb, srb);
+}
+
+void RhiVisualizer::OverdrawVis::gather(Node *n)
+{
+ if (n->type() == QSGNode::GeometryNodeType && n->element()->batch) {
+ QMatrix4x4 matrix = visualizer->m_renderer->m_current_projection_matrix;
+ matrix(2, 2) = visualizer->m_renderer->m_zRange;
+ matrix(2, 3) = 1.0f - n->element()->order * visualizer->m_renderer->m_zRange;
+
+ if (n->element()->batch->root)
+ matrix = matrix * qsg_matrixForRoot(n->element()->batch->root);
+
+ QSGGeometryNode *gn = static_cast<QSGGeometryNode *>(n->sgNode);
+ matrix = matrix * *gn->matrix();
+
+ QSGGeometry *g = gn->geometry();
+ if (g->attributeCount() >= 1) {
+ DrawCall dc;
+ memcpy(dc.uniforms.data, matrix.constData(), 64);
+ memcpy(dc.uniforms.data + 64, rotation.constData(), 64);
+
+ float c[4];
+ const float ca = 0.33f;
+ if (n->element()->batch->isOpaque) {
+ c[0] = ca * 0.3f; c[1] = ca * 1.0f; c[2] = ca * 0.3f; c[3] = ca;
+ } else {
+ c[0] = ca * 1.0f; c[1] = ca * 0.3f; c[2] = ca * 0.3f; c[3] = ca;
+ }
+ memcpy(dc.uniforms.data + 128, c, 16);
+ float pattern = 0.0f;
+ memcpy(dc.uniforms.data + 144, &pattern, 4);
+ qint32 projection = 1;
+ memcpy(dc.uniforms.data + 148, &projection, 4);
+
+ fillVertexIndex(&dc, g, true, false);
+ drawCalls.append(dc);
+ }
+ }
+
+ SHADOWNODE_TRAVERSE(n) {
+ gather(child);
+ }
+}
+
+void RhiVisualizer::OverdrawVis::prepare(Node *n, RhiVisualizer *visualizer,
+ QRhi *rhi, QRhiResourceUpdateBatch *u)
+{
+ this->visualizer = visualizer;
+
+ step += float(M_PI * 2 / 1000.0);
+ if (step > float(M_PI * 2))
+ step = 0.0f;
+
+ const float yfix = rhi->isYUpInNDC() ? 1.0f : -1.0f;
+ rotation.setToIdentity();
+ rotation.translate(0.0f, 0.5f * yfix, 4.0f);
+ rotation.scale(2.0f, 2.0f, 1.0f);
+ rotation.rotate(-30.0f * yfix, 1.0f, 0.0f, 0.0f);
+ rotation.rotate(80.0f * std::sin(step), 0.0f, 1.0f, 0.0f);
+ rotation.translate(0.0f, 0.0f, -1.0f);
+
+ drawCalls.clear();
+ gather(n);
+
+ if (!box.vbuf) {
+ const float v[] = {
+ // lower
+ -1, 1, 0, 1, 1, 0,
+ -1, 1, 0, -1, -1, 0,
+ 1, 1, 0, 1, -1, 0,
+ -1, -1, 0, 1, -1, 0,
+
+ // upper
+ -1, 1, 1, 1, 1, 1,
+ -1, 1, 1, -1, -1, 1,
+ 1, 1, 1, 1, -1, 1,
+ -1, -1, 1, 1, -1, 1,
+
+ // sides
+ -1, -1, 0, -1, -1, 1,
+ 1, -1, 0, 1, -1, 1,
+ -1, 1, 0, -1, 1, 1,
+ 1, 1, 0, 1, 1, 1
+ };
+ box.vbuf = rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(v));
+ if (!box.vbuf->build())
+ return;
+ u->uploadStaticBuffer(box.vbuf, v);
+ }
+
+ if (!box.ubuf) {
+ box.ubuf = rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, DrawCall::UBUF_SIZE);
+ if (!box.ubuf->build())
+ return;
+ QMatrix4x4 ident;
+ u->updateDynamicBuffer(box.ubuf, 0, 64, ident.constData());
+ float color[4] = { 0.5f, 0.5f, 1.0f, 1.0f };
+ u->updateDynamicBuffer(box.ubuf, 128, 16, color);
+ float pattern = 0.0f;
+ u->updateDynamicBuffer(box.ubuf, 144, 4, &pattern);
+ qint32 projection = 1;
+ u->updateDynamicBuffer(box.ubuf, 148, 4, &projection);
+ }
+
+ u->updateDynamicBuffer(box.ubuf, 64, 64, rotation.constData());
+
+ if (!box.srb) {
+ box.srb = rhi->newShaderResourceBindings();
+ box.srb->setBindings({ QRhiShaderResourceBinding::uniformBuffer(0, ubufVisibility, box.ubuf) });
+ if (!box.srb->build())
+ return;
+ }
+
+ if (!box.ps) {
+ box.ps = rhi->newGraphicsPipeline();
+ box.ps->setTopology(QRhiGraphicsPipeline::Lines);
+ box.ps->setLineWidth(2); // may be be ignored (D3D, Metal), but may be used on GL and Vulkan
+ QRhiGraphicsPipeline::TargetBlend blend;
+ blend.enable = true;
+ blend.srcColor = QRhiGraphicsPipeline::One;
+ blend.dstColor = QRhiGraphicsPipeline::One;
+ blend.srcAlpha = QRhiGraphicsPipeline::One;
+ blend.dstAlpha = QRhiGraphicsPipeline::One;
+ box.ps->setTargetBlends({ blend });
+ box.ps->setShaderStages({ { QRhiShaderStage::Vertex, visualizer->m_vs },
+ { QRhiShaderStage::Fragment, visualizer->m_fs } });
+ QRhiVertexInputLayout inputLayout;
+ inputLayout.setBindings({ { 3 * sizeof(float) } });
+ inputLayout.setAttributes({ { 0, 0, QRhiVertexInputAttribute::Float3, 0 } });
+ box.ps->setVertexInputLayout(inputLayout);
+ box.ps->setShaderResourceBindings(box.srb);
+ box.ps->setRenderPassDescriptor(visualizer->m_renderer->renderPassDescriptor());
+ if (!box.ps->build())
+ return;
+ }
+
+ if (drawCalls.isEmpty())
+ return;
+
+ const int ubufAlign = rhi->ubufAlignment();
+ int vbufOffset = 0;
+ int ibufOffset = 0;
+ int ubufOffset = 0;
+ for (RhiVisualizer::DrawCall &dc : drawCalls) {
+ dc.buf.vbufOffset = aligned(vbufOffset, 4);
+ vbufOffset = dc.buf.vbufOffset + dc.vertex.count * dc.vertex.stride;
+
+ dc.buf.ibufOffset = aligned(ibufOffset, 4);
+ ibufOffset = dc.buf.ibufOffset + dc.index.count * dc.index.stride;
+
+ dc.buf.ubufOffset = aligned(ubufOffset, ubufAlign);
+ ubufOffset = dc.buf.ubufOffset + DrawCall::UBUF_SIZE;
+ }
+
+ ensureBuffer(rhi, &vbuf, QRhiBuffer::VertexBuffer, vbufOffset);
+ if (ibufOffset)
+ ensureBuffer(rhi, &ibuf, QRhiBuffer::IndexBuffer, ibufOffset);
+ const int ubufSize = ubufOffset;
+ ensureBuffer(rhi, &ubuf, QRhiBuffer::UniformBuffer, ubufSize);
+
+ for (RhiVisualizer::DrawCall &dc : drawCalls) {
+ u->updateDynamicBuffer(vbuf, dc.buf.vbufOffset, dc.vertex.count * dc.vertex.stride, dc.vertex.data);
+ dc.buf.vbuf = vbuf;
+ if (dc.index.count) {
+ u->updateDynamicBuffer(ibuf, dc.buf.ibufOffset, dc.index.count * dc.index.stride, dc.index.data);
+ dc.buf.ibuf = ibuf;
+ }
+ u->updateDynamicBuffer(ubuf, dc.buf.ubufOffset, DrawCall::UBUF_SIZE, dc.uniforms.data);
+ }
+
+ if (!srb) {
+ srb = rhi->newShaderResourceBindings();
+ srb->setBindings({ QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(0, ubufVisibility, ubuf, DrawCall::UBUF_SIZE) });
+ if (!srb->build())
+ return;
+ }
+}
+
+void RhiVisualizer::OverdrawVis::releaseResources()
+{
+ delete srb;
+ srb = nullptr;
+
+ delete ubuf;
+ ubuf = nullptr;
+
+ delete ibuf;
+ ibuf = nullptr;
+
+ delete vbuf;
+ vbuf = nullptr;
+
+ delete box.ps;
+ box.ps = nullptr;
+
+ delete box.srb;
+ box.srb = nullptr;
+
+ delete box.ubuf;
+ box.ubuf = nullptr;
+
+ delete box.vbuf;
+ box.vbuf = nullptr;
+}
+
+void RhiVisualizer::OverdrawVis::render(QRhiCommandBuffer *cb)
+{
+ cb->setGraphicsPipeline(box.ps);
+ cb->setShaderResources();
+ QRhiCommandBuffer::VertexInput vb(box.vbuf, 0);
+ cb->setVertexInput(0, 1, &vb);
+ cb->draw(24);
+
+ visualizer->recordDrawCalls(drawCalls, cb, srb, true);
+}
+
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/coreapi/qsgrhivisualizer_p.h b/src/quick/scenegraph/coreapi/qsgrhivisualizer_p.h
new file mode 100644
index 0000000000..1ce52a81f1
--- /dev/null
+++ b/src/quick/scenegraph/coreapi/qsgrhivisualizer_p.h
@@ -0,0 +1,233 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2016 Jolla Ltd, author: <gunnar.sletta@jollamobile.com>
+** Copyright (C) 2016 Robin Burchell <robin.burchell@viroteck.net>
+** 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 QSGRHIVISUALIZER_P_H
+#define QSGRHIVISUALIZER_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 "qsgbatchrenderer_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QSGBatchRenderer
+{
+
+class RhiVisualizer : public Visualizer
+{
+public:
+ RhiVisualizer(Renderer *renderer);
+ ~RhiVisualizer();
+
+ void prepareVisualize() override;
+ void visualize() override;
+
+ void releaseResources() override;
+
+ struct DrawCall
+ {
+ static const int UBUF_SIZE = 152; // visualization.vert/frag
+ struct {
+ char data[UBUF_SIZE]; // matrix, rotation, color, pattern, projection
+ } uniforms;
+ struct {
+ QRhiGraphicsPipeline::Topology topology;
+ QRhiVertexInputAttribute::Format format;
+ int count;
+ int stride;
+ const void *data; // only when using own vbuf
+ } vertex;
+ struct {
+ QRhiCommandBuffer::IndexFormat format;
+ int count;
+ int stride;
+ const void *data; // only when using own ibuf
+ } index;
+ struct {
+ QRhiBuffer *vbuf; // either same for all draw calls and owned by the *Vis, or points to a Batch.Buffer.vbo.buf
+ int vbufOffset;
+ QRhiBuffer *ibuf; // same, but for index
+ int ibufOffset;
+ int ubufOffset;
+ } buf;
+ };
+
+private:
+ QShader m_vs;
+ QShader m_fs;
+
+ void recordDrawCalls(const QVector<DrawCall> &drawCalls,
+ QRhiCommandBuffer *cb,
+ QRhiShaderResourceBindings *srb,
+ bool blendOneOne = false);
+
+ class PipelineCache {
+ public:
+ QRhiGraphicsPipeline *pipeline(RhiVisualizer *visualizer,
+ QRhi *rhi,
+ QRhiShaderResourceBindings *srb,
+ QRhiRenderPassDescriptor *rpDesc,
+ QRhiGraphicsPipeline::Topology topology,
+ QRhiVertexInputAttribute::Format vertexFormat,
+ quint32 vertexStride,
+ bool blendOneOne);
+ void releaseResources();
+ private:
+ struct Pipeline {
+ QRhiGraphicsPipeline::Topology topology;
+ QRhiVertexInputAttribute::Format format;
+ quint32 stride;
+ QRhiGraphicsPipeline *ps;
+ };
+ QVarLengthArray<Pipeline, 16> pipelines;
+ };
+
+ PipelineCache m_pipelines;
+
+ class Fade {
+ public:
+ void prepare(RhiVisualizer *visualizer,
+ QRhi *rhi, QRhiResourceUpdateBatch *u, QRhiRenderPassDescriptor *rpDesc);
+ void releaseResources();
+ void render(QRhiCommandBuffer *cb);
+ private:
+ RhiVisualizer *visualizer;
+ QRhiBuffer *vbuf = nullptr;
+ QRhiBuffer *ubuf = nullptr;
+ QRhiGraphicsPipeline *ps = nullptr;
+ QRhiShaderResourceBindings *srb = nullptr;
+ } m_fade;
+
+ class ChangeVis {
+ public:
+ void prepare(Node *n, RhiVisualizer *visualizer,
+ QRhi *rhi, QRhiResourceUpdateBatch *u);
+ void releaseResources();
+ void render(QRhiCommandBuffer *cb);
+ private:
+ void gather(Node *n);
+ RhiVisualizer *visualizer;
+ QVector<DrawCall> drawCalls;
+ QRhiBuffer *vbuf = nullptr;
+ QRhiBuffer *ibuf = nullptr;
+ QRhiBuffer *ubuf = nullptr;
+ QRhiShaderResourceBindings *srb = nullptr;
+ } m_changeVis;
+
+ class BatchVis {
+ public:
+ void prepare(const QDataBuffer<Batch *> &opaqueBatches,
+ const QDataBuffer<Batch *> &alphaBatches,
+ RhiVisualizer *visualizer,
+ QRhi *rhi, QRhiResourceUpdateBatch *u,
+ bool forceUintIndex);
+ void releaseResources();
+ void render(QRhiCommandBuffer *cb);
+ private:
+ void gather(Batch *b);
+ RhiVisualizer *visualizer;
+ bool forceUintIndex;
+ QVector<DrawCall> drawCalls;
+ QRhiBuffer *ubuf = nullptr;
+ QRhiShaderResourceBindings *srb = nullptr;
+ } m_batchVis;
+
+ class ClipVis {
+ public:
+ void prepare(QSGNode *node, RhiVisualizer *visualizer,
+ QRhi *rhi, QRhiResourceUpdateBatch *u);
+ void releaseResources();
+ void render(QRhiCommandBuffer *cb);
+ private:
+ void gather(QSGNode *node);
+ RhiVisualizer *visualizer;
+ QVector<DrawCall> drawCalls;
+ QRhiBuffer *vbuf = nullptr;
+ QRhiBuffer *ibuf = nullptr;
+ QRhiBuffer *ubuf = nullptr;
+ QRhiShaderResourceBindings *srb = nullptr;
+ } m_clipVis;
+
+ class OverdrawVis {
+ public:
+ void prepare(Node *n, RhiVisualizer *visualizer,
+ QRhi *rhi, QRhiResourceUpdateBatch *u);
+ void releaseResources();
+ void render(QRhiCommandBuffer *cb);
+ private:
+ void gather(Node *n);
+ RhiVisualizer *visualizer;
+ QVector<DrawCall> drawCalls;
+ QRhiBuffer *vbuf = nullptr;
+ QRhiBuffer *ibuf = nullptr;
+ QRhiBuffer *ubuf = nullptr;
+ QRhiShaderResourceBindings *srb = nullptr;
+ float step = 0.0f;
+ QMatrix4x4 rotation;
+ struct {
+ QRhiBuffer *vbuf = nullptr;
+ QRhiBuffer *ubuf = nullptr;
+ QRhiShaderResourceBindings *srb = nullptr;
+ QRhiGraphicsPipeline *ps = nullptr;
+ } box;
+ } m_overdrawVis;
+
+ friend class Fade;
+ friend class PipelineCache;
+ friend class ChangeVis;
+ friend class ClipVis;
+ friend class OverdrawVis;
+};
+
+} // namespace QSGBatchRenderer
+
+QT_END_NAMESPACE
+
+#endif // QSGRHIVISUALIZER_P_H
diff --git a/src/quick/scenegraph/qsgdefaultglyphnode.cpp b/src/quick/scenegraph/qsgdefaultglyphnode.cpp
index ba286b8a36..5bd5cc4891 100644
--- a/src/quick/scenegraph/qsgdefaultglyphnode.cpp
+++ b/src/quick/scenegraph/qsgdefaultglyphnode.cpp
@@ -76,7 +76,7 @@ void QSGDefaultGlyphNode::update()
QMargins margins(0, 0, 0, 0);
if (m_style == QQuickText::Normal) {
- m_material = new QSGTextMaskMaterial(m_context, font);
+ m_material = new QSGTextMaskMaterial(m_context, QVector4D(m_color.redF(), m_color.greenF(), m_color.blueF(), m_color.alphaF()), font);
} else if (m_style == QQuickText::Outline) {
QSGOutlinedTextMaterial *material = new QSGOutlinedTextMaterial(m_context, font);
material->setStyleColor(m_styleColor);
diff --git a/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp b/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp
index 8ce469b39b..db889c3102 100644
--- a/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp
+++ b/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp
@@ -687,11 +687,12 @@ public:
// ***** common material stuff
-QSGTextMaskMaterial::QSGTextMaskMaterial(QSGRenderContext *rc, const QRawFont &font, QFontEngine::GlyphFormat glyphFormat)
+QSGTextMaskMaterial::QSGTextMaskMaterial(QSGRenderContext *rc, const QVector4D &color, const QRawFont &font, QFontEngine::GlyphFormat glyphFormat)
: m_rc(qobject_cast<QSGDefaultRenderContext *>(rc))
, m_texture(nullptr)
, m_glyphCache(nullptr)
, m_font(font)
+ , m_color(color)
{
init(glyphFormat);
}
@@ -701,6 +702,19 @@ QSGTextMaskMaterial::~QSGTextMaskMaterial()
delete m_texture;
}
+void QSGTextMaskMaterial::setColor(const QVector4D &color)
+{
+ if (m_color == color)
+ return;
+
+ m_color = color;
+
+ // If it is an RGB cache, then the pen color is actually part of the cache key
+ // so it has to be updated
+ if (m_glyphCache != nullptr && m_glyphCache->glyphFormat() == QFontEngine::Format_ARGB)
+ updateCache(QFontEngine::Format_ARGB);
+}
+
void QSGTextMaskMaterial::init(QFontEngine::GlyphFormat glyphFormat)
{
Q_ASSERT(m_font.isValid());
@@ -711,6 +725,11 @@ void QSGTextMaskMaterial::init(QFontEngine::GlyphFormat glyphFormat)
Q_ASSERT(m_rc);
m_rhi = m_rc->rhi();
+ updateCache(glyphFormat);
+}
+
+void QSGTextMaskMaterial::updateCache(QFontEngine::GlyphFormat glyphFormat)
+{
// The following piece of code will read/write to the font engine's caches,
// potentially from different threads. However, this is safe because this
// code is only called from QQuickItem::updatePaintNode() which is called
@@ -745,13 +764,13 @@ void QSGTextMaskMaterial::init(QFontEngine::GlyphFormat glyphFormat)
if (!fontEngine->supportsTransformation(glyphCacheTransform))
glyphCacheTransform = QTransform();
- m_glyphCache = fontEngine->glyphCache(cacheKey, glyphFormat, glyphCacheTransform);
-
+ QColor color = glyphFormat == QFontEngine::Format_ARGB ? QColor::fromRgbF(m_color.x(), m_color.y(), m_color.z(), m_color.w()) : QColor();
+ m_glyphCache = fontEngine->glyphCache(cacheKey, glyphFormat, glyphCacheTransform, color);
if (!m_glyphCache || int(m_glyphCache->glyphFormat()) != glyphFormat) {
if (m_rhi)
- m_glyphCache = new QSGRhiTextureGlyphCache(m_rhi, glyphFormat, glyphCacheTransform);
+ m_glyphCache = new QSGRhiTextureGlyphCache(m_rhi, glyphFormat, glyphCacheTransform, color);
else
- m_glyphCache = new QOpenGLTextureGlyphCache(glyphFormat, glyphCacheTransform);
+ m_glyphCache = new QOpenGLTextureGlyphCache(glyphFormat, glyphCacheTransform, color);
fontEngine->setGlyphCache(cacheKey, m_glyphCache.data());
m_rc->registerFontengineForCleanup(fontEngine);
@@ -963,7 +982,7 @@ bool QSGTextMaskMaterial::ensureUpToDate()
QSGStyledTextMaterial::QSGStyledTextMaterial(QSGRenderContext *rc, const QRawFont &font)
- : QSGTextMaskMaterial(rc, font, QFontEngine::Format_A8)
+ : QSGTextMaskMaterial(rc, QVector4D(), font, QFontEngine::Format_A8)
{
}
diff --git a/src/quick/scenegraph/qsgdefaultglyphnode_p_p.h b/src/quick/scenegraph/qsgdefaultglyphnode_p_p.h
index cd1b331278..7d2635794d 100644
--- a/src/quick/scenegraph/qsgdefaultglyphnode_p_p.h
+++ b/src/quick/scenegraph/qsgdefaultglyphnode_p_p.h
@@ -72,15 +72,15 @@ class QSGDefaultRenderContext;
class QSGTextMaskMaterial: public QSGMaterial
{
public:
- QSGTextMaskMaterial(QSGRenderContext *rc, const QRawFont &font, QFontEngine::GlyphFormat glyphFormat = QFontEngine::Format_None);
+ QSGTextMaskMaterial(QSGRenderContext *rc, const QVector4D &color, const QRawFont &font, QFontEngine::GlyphFormat glyphFormat = QFontEngine::Format_None);
virtual ~QSGTextMaskMaterial();
QSGMaterialType *type() const override;
QSGMaterialShader *createShader() const override;
int compare(const QSGMaterial *other) const override;
- void setColor(const QColor &c) { m_color = QVector4D(c.redF(), c.greenF(), c.blueF(), c.alphaF()); }
- void setColor(const QVector4D &color) { m_color = color; }
+ void setColor(const QColor &c) { setColor(QVector4D(c.redF(), c.greenF(), c.blueF(), c.alphaF())); }
+ void setColor(const QVector4D &color);
const QVector4D &color() const { return m_color; }
QSGTexture *texture() const { return m_texture; }
@@ -98,6 +98,7 @@ public:
private:
void init(QFontEngine::GlyphFormat glyphFormat);
+ void updateCache(QFontEngine::GlyphFormat glyphFormat);
QSGDefaultRenderContext *m_rc;
QSGPlainTexture *m_texture;
diff --git a/src/quick/scenegraph/qsgrenderloop.cpp b/src/quick/scenegraph/qsgrenderloop.cpp
index d38b5649c7..ec835fe3bd 100644
--- a/src/quick/scenegraph/qsgrenderloop.cpp
+++ b/src/quick/scenegraph/qsgrenderloop.cpp
@@ -249,14 +249,6 @@ QSGRenderLoop *QSGRenderLoop::instance()
loopType = BasicRenderLoop;
switch (rhiSupport->rhiBackend()) {
- case QRhi::Vulkan:
-#if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)
- // ### to be investigated (Mesa/Gnome deadlocks on
- // resize with threaded+Vulkan (but not threaded+GL))
- loopType = BasicRenderLoop;
-#endif
- break;
-
case QRhi::D3D11:
// D3D11 is forced to 'basic' always for now. The threaded loop's model may
// not be suitable for DXGI due to the possibility of having the main
@@ -572,7 +564,7 @@ void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window)
QRhiRenderBuffer::UsedWithSwapChainOnly);
cd->swapchain->setWindow(window);
cd->swapchain->setDepthStencil(cd->depthStencilForSwapchain);
- qDebug("MSAA sample count for the swapchain is %d", rhiSampleCount);
+ qCDebug(QSG_LOG_INFO, "MSAA sample count for the swapchain is %d", rhiSampleCount);
cd->swapchain->setSampleCount(rhiSampleCount);
cd->swapchain->setFlags(flags);
cd->rpDescForSwapchain = cd->swapchain->newCompatibleRenderPassDescriptor();
@@ -637,7 +629,7 @@ void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window)
const QSize previousOutputSize = cd->swapchain->currentPixelSize();
if (previousOutputSize != effectiveOutputSize || cd->swapchainJustBecameRenderable) {
if (cd->swapchainJustBecameRenderable)
- qDebug("just became exposed");
+ qCDebug(QSG_LOG_RENDERLOOP, "just became exposed");
cd->swapchainJustBecameRenderable = false;
cd->depthStencilForSwapchain->setPixelSize(effectiveOutputSize);
@@ -648,7 +640,7 @@ void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window)
if (!cd->hasActiveSwapchain)
qWarning("Failed to build or resize swapchain");
else
- qDebug() << "rhi swapchain size" << effectiveOutputSize;
+ qCDebug(QSG_LOG_RENDERLOOP) << "rhi swapchain size" << effectiveOutputSize;
}
Q_ASSERT(rhi == cd->rhi);
diff --git a/src/quick/scenegraph/qsgrhisupport.cpp b/src/quick/scenegraph/qsgrhisupport.cpp
index 2ca66f23d5..8bae24dc76 100644
--- a/src/quick/scenegraph/qsgrhisupport.cpp
+++ b/src/quick/scenegraph/qsgrhisupport.cpp
@@ -192,8 +192,9 @@ void QSGRhiSupport::applySettings()
default:
break;
}
- qDebug("Using QRhi with backend %s\n graphics API debug/validation layers: %d\n QRhi profiling and debug markers: %d",
- backendName, m_debugLayer, m_profile);
+ qCDebug(QSG_LOG_INFO,
+ "Using QRhi with backend %s\n graphics API debug/validation layers: %d\n QRhi profiling and debug markers: %d",
+ backendName, m_debugLayer, m_profile);
}
QSGRhiSupport *QSGRhiSupport::staticInst()
@@ -527,11 +528,11 @@ void QSGRhiProfileConnection::initialize(QRhi *rhi)
int profPort = qEnvironmentVariableIntValue("QSG_RHI_PROFILE_PORT");
if (!profPort)
profPort = 30667;
- qDebug("Sending RHI profiling output to %s:%d", qPrintable(profHost), profPort);
+ qCDebug(QSG_LOG_INFO, "Sending RHI profiling output to %s:%d", qPrintable(profHost), profPort);
m_profConn.reset(new QTcpSocket);
QObject::connect(m_profConn.data(), QOverload<QAbstractSocket::SocketError>::of(&QAbstractSocket::error), m_profConn.data(),
- [this](QAbstractSocket::SocketError socketError) { qDebug(" RHI profiler error: %d (%s)",
- socketError, qPrintable(m_profConn->errorString())); });
+ [this](QAbstractSocket::SocketError socketError) { qWarning(" RHI profiler error: %d (%s)",
+ socketError, qPrintable(m_profConn->errorString())); });
m_profConn->connectToHost(profHost, profPort);
m_profConn->waitForConnected(); // blocking wait because we want to send stuff already from the init below
rhi->profiler()->setDevice(m_profConn.data());
diff --git a/src/quick/scenegraph/qsgrhitextureglyphcache.cpp b/src/quick/scenegraph/qsgrhitextureglyphcache.cpp
index f181d101c6..99761302e2 100644
--- a/src/quick/scenegraph/qsgrhitextureglyphcache.cpp
+++ b/src/quick/scenegraph/qsgrhitextureglyphcache.cpp
@@ -43,8 +43,9 @@
QT_BEGIN_NAMESPACE
-QSGRhiTextureGlyphCache::QSGRhiTextureGlyphCache(QRhi *rhi, QFontEngine::GlyphFormat format, const QTransform &matrix)
- : QImageTextureGlyphCache(format, matrix),
+QSGRhiTextureGlyphCache::QSGRhiTextureGlyphCache(QRhi *rhi, QFontEngine::GlyphFormat format, const QTransform &matrix,
+ const QColor &color)
+ : QImageTextureGlyphCache(format, matrix, color),
m_rhi(rhi)
{
// Some OpenGL implementations, for instance macOS, have issues with
diff --git a/src/quick/scenegraph/qsgrhitextureglyphcache_p.h b/src/quick/scenegraph/qsgrhitextureglyphcache_p.h
index 1533beb162..75d82de90d 100644
--- a/src/quick/scenegraph/qsgrhitextureglyphcache_p.h
+++ b/src/quick/scenegraph/qsgrhitextureglyphcache_p.h
@@ -59,7 +59,8 @@ QT_BEGIN_NAMESPACE
class QSGRhiTextureGlyphCache : public QImageTextureGlyphCache
{
public:
- QSGRhiTextureGlyphCache(QRhi *rhi, QFontEngine::GlyphFormat format, const QTransform &matrix);
+ QSGRhiTextureGlyphCache(QRhi *rhi, QFontEngine::GlyphFormat format, const QTransform &matrix,
+ const QColor &color = QColor());
~QSGRhiTextureGlyphCache();
void createTextureData(int width, int height) override;
diff --git a/src/quick/scenegraph/qsgthreadedrenderloop.cpp b/src/quick/scenegraph/qsgthreadedrenderloop.cpp
index db8e17a8e6..d1258cf903 100644
--- a/src/quick/scenegraph/qsgthreadedrenderloop.cpp
+++ b/src/quick/scenegraph/qsgthreadedrenderloop.cpp
@@ -686,10 +686,11 @@ void QSGRenderThread::syncAndRender(QImage *grabImage)
syncResultedInChanges = false;
QQuickWindowPrivate *d = QQuickWindowPrivate::get(window);
- bool repaintRequested = (pendingUpdate & RepaintRequest) || d->customRenderStage || grabImage;
- bool syncRequested = (pendingUpdate & SyncRequest) || grabImage;
- bool exposeRequested = (pendingUpdate & ExposeRequest) == ExposeRequest;
+ const bool repaintRequested = (pendingUpdate & RepaintRequest) || d->customRenderStage || grabImage;
+ const bool syncRequested = (pendingUpdate & SyncRequest) || grabImage;
+ const bool exposeRequested = (pendingUpdate & ExposeRequest) == ExposeRequest;
pendingUpdate = 0;
+ const bool grabRequested = grabImage != nullptr;
QQuickWindowPrivate *cd = QQuickWindowPrivate::get(window);
// Begin the frame before syncing -> sync is where we may invoke
@@ -708,7 +709,7 @@ void QSGRenderThread::syncAndRender(QImage *grabImage)
const QSize previousOutputSize = cd->swapchain->currentPixelSize();
if (previousOutputSize != effectiveOutputSize || cd->swapchainJustBecameRenderable) {
if (cd->swapchainJustBecameRenderable)
- qDebug("just became exposed");
+ qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "just became exposed");
cd->swapchainJustBecameRenderable = false;
cd->depthStencilForSwapchain->setPixelSize(effectiveOutputSize);
@@ -719,7 +720,7 @@ void QSGRenderThread::syncAndRender(QImage *grabImage)
if (!cd->hasActiveSwapchain)
qWarning("Failed to build or resize swapchain");
else
- qDebug() << "rhi swapchain size" << effectiveOutputSize;
+ qCDebug(QSG_LOG_RENDERLOOP) << "rhi swapchain size" << effectiveOutputSize;
}
Q_ASSERT(rhi == cd->rhi);
@@ -732,13 +733,26 @@ void QSGRenderThread::syncAndRender(QImage *grabImage)
// try again later
if (frameResult == QRhi::FrameOpDeviceLost || frameResult == QRhi::FrameOpSwapChainOutOfDate)
QCoreApplication::postEvent(window, new QEvent(QEvent::Type(QQuickWindowPrivate::FullUpdateRequest)));
+
+ // Before returning we need to ensure the same wake up logic that
+ // would have happened if beginFrame() had suceeded.
+ if (exposeRequested) {
+ qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- bailing out due to failed beginFrame, wake Gui");
+ waitCondition.wakeOne();
+ mutex.unlock();
+ } else if (syncRequested && !grabRequested) {
+ qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- bailing out due to failed beginFrame, wake Gui like sync would do");
+ mutex.lock();
+ waitCondition.wakeOne();
+ mutex.unlock();
+ }
return;
}
}
if (syncRequested) {
qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- updatePending, doing sync");
- sync(exposeRequested, grabImage != nullptr);
+ sync(exposeRequested, grabRequested);
}
#ifndef QSG_NO_RENDER_TIMING
if (profileFrames)
@@ -888,6 +902,10 @@ void QSGRenderThread::run()
QQuickProfiler::registerAnimationCallback();
while (active) {
+#ifdef Q_OS_DARWIN
+ QMacAutoReleasePool frameReleasePool;
+#endif
+
if (window) {
if (enableRhi) {
if (!rhi) {
@@ -922,7 +940,7 @@ void QSGRenderThread::run()
QRhiRenderBuffer::UsedWithSwapChainOnly);
cd->swapchain->setWindow(window);
cd->swapchain->setDepthStencil(cd->depthStencilForSwapchain);
- qDebug("MSAA sample count for the swapchain is %d", rhiSampleCount);
+ qCDebug(QSG_LOG_INFO, "MSAA sample count for the swapchain is %d", rhiSampleCount);
cd->swapchain->setSampleCount(rhiSampleCount);
cd->swapchain->setFlags(flags);
cd->rpDescForSwapchain = cd->swapchain->newCompatibleRenderPassDescriptor();
diff --git a/src/quick/scenegraph/scenegraph.pri b/src/quick/scenegraph/scenegraph.pri
index 3390d2b87a..ee9ea0f5ed 100644
--- a/src/quick/scenegraph/scenegraph.pri
+++ b/src/quick/scenegraph/scenegraph.pri
@@ -37,9 +37,13 @@ SOURCES += \
qtConfig(opengl(es1|es2)?) {
HEADERS += \
- $$PWD/coreapi/qsgbatchrenderer_p.h
+ $$PWD/coreapi/qsgbatchrenderer_p.h \
+ $$PWD/coreapi/qsgopenglvisualizer_p.h \
+ $$PWD/coreapi/qsgrhivisualizer_p.h
SOURCES += \
$$PWD/coreapi/qsgbatchrenderer.cpp \
+ $$PWD/coreapi/qsgopenglvisualizer.cpp \
+ $$PWD/coreapi/qsgrhivisualizer.cpp \
$$PWD/coreapi/qsgshaderrewriter.cpp
}
diff --git a/src/quick/scenegraph/scenegraph.qrc b/src/quick/scenegraph/scenegraph.qrc
index c7257c17a0..e409f07610 100644
--- a/src/quick/scenegraph/scenegraph.qrc
+++ b/src/quick/scenegraph/scenegraph.qrc
@@ -121,5 +121,8 @@
<file>shaders_ng/shadereffect.frag.qsb</file>
<file>shaders_ng/sprite.vert.qsb</file>
<file>shaders_ng/sprite.frag.qsb</file>
+
+ <file>shaders_ng/visualization.vert.qsb</file>
+ <file>shaders_ng/visualization.frag.qsb</file>
</qresource>
</RCC>
diff --git a/src/quick/scenegraph/shaders_ng/compile.bat b/src/quick/scenegraph/shaders_ng/compile.bat
index 8ce42f3483..a0c74c22c7 100755
--- a/src/quick/scenegraph/shaders_ng/compile.bat
+++ b/src/quick/scenegraph/shaders_ng/compile.bat
@@ -82,3 +82,5 @@ qsb -b --glsl "150,120,100 es" --hlsl 50 --msl 12 -o shadereffect.vert.qsb shade
qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o shadereffect.frag.qsb shadereffect.frag
qsb -b --glsl "150,120,100 es" --hlsl 50 --msl 12 -o sprite.vert.qsb sprite.vert
qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o sprite.frag.qsb sprite.frag
+qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o visualization.vert.qsb visualization.vert
+qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o visualization.frag.qsb visualization.frag
diff --git a/src/quick/scenegraph/shaders_ng/visualization.frag b/src/quick/scenegraph/shaders_ng/visualization.frag
new file mode 100644
index 0000000000..29f718fe5d
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/visualization.frag
@@ -0,0 +1,19 @@
+#version 440
+
+layout(location = 0) in vec2 pos;
+layout(location = 0) out vec4 fragColor;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 matrix;
+ mat4 rotation;
+ vec4 color;
+ float pattern;
+ int projection;
+} ubuf;
+
+void main(void)
+{
+ vec4 c = ubuf.color;
+ c.xyz += pow(max(sin(pos.x + pos.y), 0.0), 2.0) * ubuf.pattern * 0.25;
+ fragColor = c;
+}
diff --git a/src/quick/scenegraph/shaders_ng/visualization.frag.qsb b/src/quick/scenegraph/shaders_ng/visualization.frag.qsb
new file mode 100644
index 0000000000..eadad927dc
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/visualization.frag.qsb
Binary files differ
diff --git a/src/quick/scenegraph/shaders_ng/visualization.vert b/src/quick/scenegraph/shaders_ng/visualization.vert
new file mode 100644
index 0000000000..c29492417a
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/visualization.vert
@@ -0,0 +1,28 @@
+#version 440
+
+layout(location = 0) in vec4 v;
+layout(location = 0) out vec2 pos;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 matrix;
+ mat4 rotation;
+ vec4 color;
+ float pattern;
+ int projection;
+} ubuf;
+
+out gl_PerVertex { vec4 gl_Position; };
+
+void main()
+{
+ vec4 p = ubuf.matrix * v;
+
+ if (ubuf.projection != 0) {
+ vec4 proj = ubuf.rotation * p;
+ gl_Position = vec4(proj.x, proj.y, 0, proj.z);
+ } else {
+ gl_Position = p;
+ }
+
+ pos = v.xy * 1.37;
+}
diff --git a/src/quick/scenegraph/shaders_ng/visualization.vert.qsb b/src/quick/scenegraph/shaders_ng/visualization.vert.qsb
new file mode 100644
index 0000000000..bd89847dd3
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/visualization.vert.qsb
Binary files differ
diff --git a/src/quick/scenegraph/util/qsgdefaultpainternode_p.h b/src/quick/scenegraph/util/qsgdefaultpainternode_p.h
index a86f7397be..dc103648ff 100644
--- a/src/quick/scenegraph/util/qsgdefaultpainternode_p.h
+++ b/src/quick/scenegraph/util/qsgdefaultpainternode_p.h
@@ -159,9 +159,6 @@ private:
QSize m_textureSize;
QRect m_dirtyRect;
QColor m_fillColor;
-#if QT_VERSION >= 0x060000
-#warning "Remove m_contentsScale and assume 1 everywhere"
-#endif
qreal m_contentsScale;
bool m_dirtyContents : 1;
diff --git a/src/quick/util/qquickpath.cpp b/src/quick/util/qquickpath.cpp
index 840a8c6a2c..d246ee7910 100644
--- a/src/quick/util/qquickpath.cpp
+++ b/src/quick/util/qquickpath.cpp
@@ -109,6 +109,11 @@ QT_BEGIN_NAMESPACE
\li Yes
\li Yes
\li Yes
+ \li PathMultiLine
+ \li Yes
+ \li Yes
+ \li Yes
+ \li Yes
\row
\li PathQuad
\li Yes
@@ -246,7 +251,8 @@ bool QQuickPath::isClosed() const
A path can contain the following path objects:
\list
\li \l PathLine - a straight line to a given position.
- \li \l PathPolyline - a polyline specified as a list of normalized coordinates.
+ \li \l PathPolyline - a polyline specified as a list of coordinates.
+ \li \l PathMultiline - a list of polylines specified as a list of lists of coordinates.
\li \l PathQuad - a quadratic Bezier curve to a given position with a control point.
\li \l PathCubic - a cubic Bezier curve to a given position with two control points.
\li \l PathArc - an arc to a given position with a radius.
@@ -2416,9 +2422,14 @@ void QQuickPathPolyline::setPath(const QVariantList &path)
pathList.append(c);
}
- if (m_path != pathList) {
+ setPath(pathList);
+}
+
+void QQuickPathPolyline::setPath(const QVector<QPointF> &path)
+{
+ if (m_path != path) {
const QPointF &oldStart = start();
- m_path = pathList;
+ m_path = path;
const QPointF &newStart = start();
emit pathChanged();
if (oldStart != newStart)
@@ -2446,6 +2457,146 @@ void QQuickPathPolyline::addToPath(QPainterPath &path, const QQuickPathData &/*d
path.lineTo(m_path.at(i));
}
+
+/*!
+ \qmltype PathMultiline
+ \instantiates QQuickPathMultiline
+ \inqmlmodule QtQuick
+ \ingroup qtquick-animation-paths
+ \brief Defines a set of polylines through a list of lists of coordinates.
+ \since QtQuick 2.14
+
+ This element allows to define a list of polylines at once.
+ Each polyline in the list will be preceded by a \l{QPainterPath::moveTo}{moveTo}
+ command, effectively making each polyline a separate one.
+ The polylines in this list are supposed to be non-intersecting with each other.
+ In any case, when used in conjunction with a \l ShapePath, the containing ShapePath's
+ \l ShapePath::fillRule applies.
+ That is, with the default \c OddEvenFill and non intersecting shapes, the largest shape in the list defines an area to be filled;
+ areas where two shapes overlap are holes; areas where three shapes overlap are filled areas inside holes, etc.
+
+ The example below creates a high voltage symbol by adding each path
+ of the symbol to the list of paths.
+ The coordinates of the vertices are normalized, and through the containing shape's
+ \l scale property, the path will be rescaled together with its containing shape.
+
+ \qml
+ PathMultiline {
+ paths: [
+ [Qt.point(0.5, 0.06698),
+ Qt.point(1, 0.93301),
+ Qt.point(0, 0.93301),
+ Qt.point(0.5, 0.06698)],
+
+ [Qt.point(0.5, 0.12472),
+ Qt.point(0.95, 0.90414),
+ Qt.point(0.05, 0.90414),
+ Qt.point(0.5, 0.12472)],
+
+ [Qt.point(0.47131, 0.32986),
+ Qt.point(0.36229, 0.64789),
+ Qt.point(0.51492, 0.58590),
+ Qt.point(0.47563, 0.76014),
+ Qt.point(0.44950, 0.73590),
+ Qt.point(0.46292, 0.83392),
+ Qt.point(0.52162, 0.75190),
+ Qt.point(0.48531, 0.76230),
+ Qt.point(0.57529, 0.53189),
+ Qt.point(0.41261, 0.59189),
+ Qt.point(0.53001, 0.32786),
+ Qt.point(0.47131, 0.32986)]
+ ]
+ }
+ \endqml
+
+ \sa Path, QPainterPath::setFillRule, PathPolyline, PathQuad, PathCubic, PathArc, PathAngleArc, PathCurve, PathSvg, PathMove
+*/
+
+/*!
+ \qmlproperty point QtQuick::PathMultiline::start
+
+ This read-only property contains the beginning of the polylines.
+*/
+
+/*!
+ \qmlproperty list<list<point>> QtQuick::PathMultiline::paths
+
+ This property defines the vertices of the polylines.
+*/
+
+QQuickPathMultiline::QQuickPathMultiline(QObject *parent) : QQuickCurve(parent)
+{
+}
+
+QVariantList QQuickPathMultiline::paths() const
+{
+ QVariantList res;
+ for (int j = 0; j < m_paths.length(); ++j) {
+ const QVector<QPointF> &path = m_paths.at(j);
+ QVariantList p;
+ for (int i = 0; i < path.length(); ++i) {
+ const QPointF &c = path.at(i);
+ p.append(QVariant::fromValue(c));
+ }
+ res.append(p);
+ }
+ return res;
+}
+
+void QQuickPathMultiline::setPaths(const QVariantList &paths)
+{
+ QVector<QVector<QPointF>> pathsList;
+ for (int j = 0; j < paths.length(); ++j) {
+ if (paths.at(j).type() != QVariant::List)
+ qWarning() << "QQuickPathMultiLine::setPaths: elements in argument not of type List";
+ QVariantList path = paths.at(j).toList();
+ QVector<QPointF> l;
+ for (int i = 0; i < path.length(); ++i) {
+ const QVariant &element = path.at(i);
+ const QVariant::Type elementType = element.type();
+ if (elementType == QVariant::PointF || elementType == QVariant::Point) {
+ const QPointF c = element.toPointF();
+ l.append(c);
+ }
+ }
+ if (l.size() >= 2)
+ pathsList.append(l);
+ }
+
+ setPaths(pathsList);
+}
+
+void QQuickPathMultiline::setPaths(const QVector<QVector<QPointF>> &paths)
+{
+ if (m_paths != paths) {
+ const QPointF &oldStart = start();
+ m_paths = paths;
+ const QPointF &newStart = start();
+ emit pathsChanged();
+ if (oldStart != newStart)
+ emit startChanged();
+ emit changed();
+ }
+}
+
+QPointF QQuickPathMultiline::start() const
+{
+ if (m_paths.size())
+ return m_paths.first().first();
+ return QPointF();
+}
+
+void QQuickPathMultiline::addToPath(QPainterPath &path, const QQuickPathData &)
+{
+ if (!m_paths.size())
+ return;
+ for (const QVector<QPointF> &p: m_paths) {
+ path.moveTo(p.first());
+ for (int i = 1; i < p.size(); ++i)
+ path.lineTo(p.at(i));
+ }
+}
+
QT_END_NAMESPACE
#include "moc_qquickpath_p.cpp"
diff --git a/src/quick/util/qquickpath_p.h b/src/quick/util/qquickpath_p.h
index 998f4e3123..aa3425ff6c 100644
--- a/src/quick/util/qquickpath_p.h
+++ b/src/quick/util/qquickpath_p.h
@@ -434,6 +434,7 @@ public:
QVariantList path() const;
void setPath(const QVariantList &path);
+ void setPath(const QVector<QPointF> &path);
QPointF start() const;
void addToPath(QPainterPath &path, const QQuickPathData &data) override;
@@ -445,6 +446,30 @@ private:
QVector<QPointF> m_path;
};
+class Q_QUICK_PRIVATE_EXPORT QQuickPathMultiline : public QQuickCurve
+{
+ Q_OBJECT
+ Q_PROPERTY(QPointF start READ start NOTIFY startChanged)
+ Q_PROPERTY(QVariantList paths READ paths WRITE setPaths NOTIFY pathsChanged)
+public:
+ QQuickPathMultiline(QObject *parent=nullptr);
+
+ QVariantList paths() const;
+ void setPaths(const QVariantList &paths);
+ void setPaths(const QVector<QVector<QPointF>> &paths);
+ QPointF start() const;
+ void addToPath(QPainterPath &path, const QQuickPathData &) override;
+
+Q_SIGNALS:
+ void pathsChanged();
+ void startChanged();
+
+private:
+ QPointF absolute(const QPointF &relative) const;
+
+ QVector<QVector<QPointF>> m_paths;
+};
+
struct QQuickCachedBezier
{
QQuickCachedBezier() {}
diff --git a/src/quickshapes/qquickshape.cpp b/src/quickshapes/qquickshape.cpp
index e8779debde..04e8de5aa0 100644
--- a/src/quickshapes/qquickshape.cpp
+++ b/src/quickshapes/qquickshape.cpp
@@ -969,6 +969,11 @@ void QQuickShape::itemChange(ItemChange change, const ItemChangeData &data)
// sync may have been deferred; do it now if the item became visible
if (change == ItemVisibleHasChanged && data.boolValue)
d->_q_shapePathChanged();
+ else if (change == QQuickItem::ItemSceneChange) {
+ for (int i = 0; i < d->sp.count(); ++i)
+ QQuickShapePathPrivate::get(d->sp[i])->dirty = QQuickShapePathPrivate::DirtyAll;
+ d->_q_shapePathChanged();
+ }
QQuickItem::itemChange(change, data);
}
diff --git a/tests/auto/qml/ecmascripttests/qjstest/test262runner.cpp b/tests/auto/qml/ecmascripttests/qjstest/test262runner.cpp
index 33efcb4da4..9fe2de5368 100644
--- a/tests/auto/qml/ecmascripttests/qjstest/test262runner.cpp
+++ b/tests/auto/qml/ecmascripttests/qjstest/test262runner.cpp
@@ -597,7 +597,7 @@ void SingleTest::run()
void SingleTest::runExternalTest()
{
- auto runTest = [=] (const char *header, TestCase::Result *result) {
+ auto runTest = [this] (const char *header, TestCase::Result *result) {
QTemporaryFile tempFile;
tempFile.open();
tempFile.write(header);
diff --git a/tests/auto/qml/qjsengine/tst_qjsengine.cpp b/tests/auto/qml/qjsengine/tst_qjsengine.cpp
index e08a1cc37e..0ccb8210bc 100644
--- a/tests/auto/qml/qjsengine/tst_qjsengine.cpp
+++ b/tests/auto/qml/qjsengine/tst_qjsengine.cpp
@@ -249,6 +249,8 @@ private slots:
void interrupt_data();
void interrupt();
+ void triggerBackwardJumpWithDestructuring();
+
public:
Q_INVOKABLE QJSValue throwingCppMethod1();
Q_INVOKABLE void throwingCppMethod2();
@@ -4938,6 +4940,19 @@ void tst_QJSEngine::interrupt()
#endif
}
+void tst_QJSEngine::triggerBackwardJumpWithDestructuring()
+{
+ QJSEngine engine;
+ auto value = engine.evaluate(
+ "function makeArray(n) { return [...Array(n).keys()]; }\n"
+ "for (let i=0;i<100;++i) {\n"
+ " let arr = makeArray(20)\n"
+ " arr.sort( (a, b) => b - a )\n"
+ "}"
+ );
+ QVERIFY(!value.isError());
+}
+
QTEST_MAIN(tst_QJSEngine)
#include "tst_qjsengine.moc"
diff --git a/tests/auto/qml/qmllint/data/FromRootDirectParent.qml b/tests/auto/qml/qmllint/data/FromRootDirectParent.qml
deleted file mode 100644
index c0bfd0f26b..0000000000
--- a/tests/auto/qml/qmllint/data/FromRootDirectParent.qml
+++ /dev/null
@@ -1,14 +0,0 @@
-import QtQuick 2.0
-
-Item {
- id: root
- property int unqualified: 42
-
- Item {
- x: unqualified // user defined property from root
- }
-
- QtObject {
- property int check: x // existing property from root
- }
-}
diff --git a/tests/auto/qml/qmllint/data/SignalHandler.qml b/tests/auto/qml/qmllint/data/SignalHandler.qml
index bdf503e3dc..865277cedb 100644
--- a/tests/auto/qml/qmllint/data/SignalHandler.qml
+++ b/tests/auto/qml/qmllint/data/SignalHandler.qml
@@ -1,5 +1,13 @@
import QtQuick 2.0
MouseArea {
- onDoubleClicked: console.log(mouse);
+ onDoubleClicked: {
+ console.log(mouse);
+ // do further things
+ }
+ onClicked: console.info(mouse)
+ onPositionChanged: {
+ console.log(mouse)
+ }
+ onPressAndHold: console.warn(mouse)
}
diff --git a/tests/auto/qml/qmllint/data/nonSpuriousParentWarning.qml b/tests/auto/qml/qmllint/data/nonSpuriousParentWarning.qml
new file mode 100644
index 0000000000..a59b736929
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/nonSpuriousParentWarning.qml
@@ -0,0 +1,8 @@
+import QtQuick 2.12
+import QtQml 2.12
+
+Item {
+ QtObject {
+ property int x: parent.x
+ }
+}
diff --git a/tests/auto/qml/qmllint/data/spuriousParentWarning.qml b/tests/auto/qml/qmllint/data/spuriousParentWarning.qml
new file mode 100644
index 0000000000..1323593031
--- /dev/null
+++ b/tests/auto/qml/qmllint/data/spuriousParentWarning.qml
@@ -0,0 +1,7 @@
+import QtQuick 2.12
+
+Item {
+ Unknown {
+ property int x: parent.x
+ }
+}
diff --git a/tests/auto/qml/qmllint/main.cpp b/tests/auto/qml/qmllint/main.cpp
index 038826790b..928575bc82 100644
--- a/tests/auto/qml/qmllint/main.cpp
+++ b/tests/auto/qml/qmllint/main.cpp
@@ -40,6 +40,7 @@ private Q_SLOTS:
void test_data();
void testUnqualified();
void testUnqualified_data();
+ void testUnqualifiedNoSpuriousParentWarning();
private:
QString m_qmllintPath;
};
@@ -73,13 +74,14 @@ void TestQmllint::test_data()
void TestQmllint::testUnqualified()
{
+ auto qmlImportDir = QLibraryInfo::location(QLibraryInfo::Qml2ImportsPath);
QFETCH(QString, filename);
QFETCH(QString, warningMessage);
QFETCH(int, warningLine);
QFETCH(int, warningColumn);
filename.prepend(QStringLiteral("data/"));
QStringList args;
- args << QStringLiteral("-U") << filename;
+ args << QStringLiteral("-U") << filename << QStringLiteral("-I") << qmlImportDir;
QProcess process;
process.start(m_qmllintPath, args);
@@ -106,14 +108,41 @@ void TestQmllint::testUnqualified_data()
// access property of root object
QTest::newRow("FromRootDirect") << QStringLiteral("FromRoot.qml") << QStringLiteral("x: root.unqualified") << 9 << 16; // new property
QTest::newRow("FromRootAccess") << QStringLiteral("FromRoot.qml") << QStringLiteral("property int check: root.x") << 13 << 33; // builtin property
- // access property of root object from direct child
- QTest::newRow("FromRootDirectParentDirect") << QStringLiteral("FromRootDirectParent.qml") << QStringLiteral("x: parent.unqualified") << 8 << 12;
- QTest::newRow("FromRootDirectParentAccess") << QStringLiteral("FromRootDirectParent.qml") << QStringLiteral("property int check: parent.x") << 12 << 29;
// access injected name from signal
- QTest::newRow("SignalHandler") << QStringLiteral("SignalHandler.qml") << QStringLiteral("onDoubleClicked: function(mouse) {...") << 4 << 34;
+ QTest::newRow("SignalHandler1") << QStringLiteral("SignalHandler.qml") << QStringLiteral("onDoubleClicked: function(mouse) {...") << 5 << 21;
+ QTest::newRow("SignalHandler2") << QStringLiteral("SignalHandler.qml") << QStringLiteral("onPositionChanged: function(mouse) {...") << 10 << 21;
+ QTest::newRow("SignalHandlerShort1") << QStringLiteral("SignalHandler.qml") << QStringLiteral("onClicked: (mouse) => {...") << 8 << 29;
+ QTest::newRow("SignalHandlerShort2") << QStringLiteral("SignalHandler.qml") << QStringLiteral("onPressAndHold: (mouse) => {...") << 12 << 34;
}
+void TestQmllint::testUnqualifiedNoSpuriousParentWarning()
+{
+ auto qmlImportDir = QLibraryInfo::location(QLibraryInfo::Qml2ImportsPath);
+ {
+ QString filename = QLatin1String("spuriousParentWarning.qml");
+ filename.prepend(QStringLiteral("data/"));
+ QStringList args;
+ args << QStringLiteral("-U") << filename << QStringLiteral("-I") << qmlImportDir;
+ QProcess process;
+ process.start(m_qmllintPath, args);
+ QVERIFY(process.waitForFinished());
+ QVERIFY(process.exitStatus() == QProcess::NormalExit);
+ QVERIFY(process.exitCode() == 0);
+ }
+ {
+ QString filename = QLatin1String("nonSpuriousParentWarning.qml");
+ filename.prepend(QStringLiteral("data/"));
+ QStringList args;
+ args << QStringLiteral("-U") << filename << QStringLiteral("-I") << qmlImportDir;
+ QProcess process;
+ process.start(m_qmllintPath, args);
+ QVERIFY(process.waitForFinished());
+ QVERIFY(process.exitStatus() == QProcess::NormalExit);
+ QVERIFY(process.exitCode());
+ }
+}
+
void TestQmllint::test()
{
QFETCH(QString, filename);
diff --git a/tests/auto/qml/qmlmin/tst_qmlmin.cpp b/tests/auto/qml/qmlmin/tst_qmlmin.cpp
index 696ec66246..cae833cd60 100644
--- a/tests/auto/qml/qmlmin/tst_qmlmin.cpp
+++ b/tests/auto/qml/qmlmin/tst_qmlmin.cpp
@@ -130,6 +130,8 @@ void tst_qmlmin::initTestCase()
invalidFiles << "tests/auto/qml/qjsengine/script/com/trolltech/syntaxerror/__init__.js";
invalidFiles << "tests/auto/qml/debugger/qqmlpreview/data/broken.qml";
invalidFiles << "tests/auto/qml/qqmllanguage/data/fuzzed.2.qml";
+ // generatorFunction.qml is not invalid per se, but the minifier cannot handle yield statements
+ invalidFiles << "tests/auto/qml/qqmlecmascript/data/generatorFunction.qml";
#endif
}
diff --git a/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp b/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp
index a9c28a0911..8787a43884 100644
--- a/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp
+++ b/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp
@@ -96,6 +96,9 @@ void tst_qqmlapplicationengine::basicLoading()
// will break.
void tst_qqmlapplicationengine::testNonResolvedPath()
{
+#ifdef Q_OS_ANDROID
+ QSKIP("Android stores QML files in resources, and the path to a resource cannot be relative in this case");
+#endif
{
// NOTE NOTE NOTE! Missing testFileUrl is *WANTED* here! We want a
// non-resolved URL.
@@ -117,6 +120,9 @@ void tst_qqmlapplicationengine::testNonResolvedPath()
void tst_qqmlapplicationengine::application_data()
{
+#ifdef Q_OS_ANDROID
+ QSKIP("Cannot launch external process on Android");
+#endif
QTest::addColumn<QByteArray>("qmlFile");
QTest::addColumn<QByteArray>("expectedStdErr");
diff --git a/tests/auto/qml/qqmldirparser/CMakeLists.txt b/tests/auto/qml/qqmldirparser/CMakeLists.txt
index 21a8fa44ae..30efe97fed 100644
--- a/tests/auto/qml/qqmldirparser/CMakeLists.txt
+++ b/tests/auto/qml/qqmldirparser/CMakeLists.txt
@@ -4,6 +4,14 @@
## tst_qqmldirparser Test:
#####################################################################
+# Collect test data
+
+file(GLOB test_data_glob
+ LIST_DIRECTORIES true
+ RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
+ "data/*")
+list(APPEND test_data ${test_data_glob})
+
add_qt_test(tst_qqmldirparser
GUI
SOURCES
@@ -16,17 +24,12 @@ add_qt_test(tst_qqmldirparser
PUBLIC_LIBRARIES
Qt::Gui
Qt::Qml
+ TESTDATA ${test_data}
)
-#### Keys ignored in scope 1:.:.:qqmldirparser.pro:<TRUE>:
-# CONFIG = "testcase"
-
## Scopes:
#####################################################################
-#### Keys ignored in scope 2:.:.:qqmldirparser.pro:APPLE_OSX:
-# CONFIG = "-app_bundle"
-
extend_target(tst_qqmldirparser CONDITION ANDROID OR APPLE_IOS
DEFINES
QT_QMLTEST_DATADIR=\\\":/data\\\"
diff --git a/tests/auto/qml/qqmldirparser/qqmldirparser.pro b/tests/auto/qml/qqmldirparser/qqmldirparser.pro
index dda74b1ef9..b5373a6e8f 100644
--- a/tests/auto/qml/qqmldirparser/qqmldirparser.pro
+++ b/tests/auto/qml/qqmldirparser/qqmldirparser.pro
@@ -6,3 +6,5 @@ macx:CONFIG -= app_bundle
SOURCES += tst_qqmldirparser.cpp
include (../../shared/util.pri)
+
+TESTDATA = data/*
diff --git a/tests/auto/qml/qqmlecmascript/data/generatorFunction.qml b/tests/auto/qml/qqmlecmascript/data/generatorFunction.qml
new file mode 100644
index 0000000000..66bb642f34
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/generatorFunction.qml
@@ -0,0 +1,22 @@
+import QtQml 2.12
+
+QtObject {
+ id: root
+ property bool test1: false;
+ property bool test2: false;
+ property bool test3: false;
+ property bool done: false;
+ function *gen() {
+ yield 1
+ yield 2
+ yield 3
+ }
+
+ Component.onCompleted: {
+ let it = root.gen();
+ root.test1 = (it.next().value == 1);
+ root.test2 = (it.next().value == 2);
+ root.test3 = (it.next().value == 3);
+ root.done = it.next().done;
+ }
+}
diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
index d603ca6907..b44fe9766c 100644
--- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
+++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
@@ -233,6 +233,7 @@ private slots:
void functionAssignment_afterBinding();
void eval();
void function();
+ void topLevelGeneratorFunction();
void qtbug_10696();
void qtbug_11606();
void qtbug_11600();
@@ -2048,7 +2049,7 @@ void tst_qqmlecmascript::functionErrors()
QObject *resource = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
warning = url + QLatin1String(":16: TypeError: Property 'scarceResource' of object ScarceResourceObject(0x%1) is not a function");
- warning = warning.arg(QString::number((qintptr)resource, 16));
+ warning = warning.arg(QString::number((quintptr)resource, 16));
QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData()); // we expect a meaningful warning to be printed.
QMetaObject::invokeMethod(object, "retrieveScarceResource");
delete object;
@@ -4573,7 +4574,7 @@ void tst_qqmlecmascript::scarceResources_other()
eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
expectedWarning = varComponentTwelve.url().toString() + QLatin1String(":16: TypeError: Property 'scarceResource' of object ScarceResourceObject(0x%1) is not a function");
- expectedWarning = expectedWarning.arg(QString::number((qintptr)eo, 16));
+ expectedWarning = expectedWarning.arg(QString::number((quintptr)eo, 16));
QTest::ignoreMessage(QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed.
QMetaObject::invokeMethod(object, "retrieveScarceResource");
QVERIFY(!object->property("scarceResourceCopy").isValid()); // due to exception, assignment will NOT have occurred.
@@ -4647,7 +4648,7 @@ void tst_qqmlecmascript::scarceResources_other()
eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>());
QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
expectedWarning = variantComponentTwelve.url().toString() + QLatin1String(":16: TypeError: Property 'scarceResource' of object ScarceResourceObject(0x%1) is not a function");
- expectedWarning = expectedWarning.arg(QString::number((qintptr)eo, 16));
+ expectedWarning = expectedWarning.arg(QString::number((quintptr)eo, 16));
QTest::ignoreMessage(QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed.
QMetaObject::invokeMethod(object, "retrieveScarceResource");
QVERIFY(!object->property("scarceResourceCopy").isValid()); // due to exception, assignment will NOT have occurred.
@@ -6348,6 +6349,28 @@ void tst_qqmlecmascript::function()
delete o;
}
+// QTBUG-77096
+void tst_qqmlecmascript::topLevelGeneratorFunction()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("generatorFunction.qml"));
+
+ QScopedPointer<QObject> o {component.create()};
+ QVERIFY(o != nullptr);
+
+ // check that generator works correctly in QML
+ QCOMPARE(o->property("test1").toBool(), true);
+ QCOMPARE(o->property("test2").toBool(), true);
+ QCOMPARE(o->property("test3").toBool(), true);
+ QCOMPARE(o->property("done").toBool(), true);
+
+ // check that generator is accessible from C++
+ QVariant returnedValue;
+ QMetaObject::invokeMethod(o.get(), "gen", Q_RETURN_ARG(QVariant, returnedValue));
+ auto it = returnedValue.value<QJSValue>();
+ QCOMPARE(it.property("next").callWithInstance(it).property("value").toInt(), 1);
+}
+
// Test the "Qt.include" method
void tst_qqmlecmascript::include()
{
diff --git a/tests/auto/qml/qqmlengine/CMakeLists.txt b/tests/auto/qml/qqmlengine/CMakeLists.txt
index 5f38633234..e4c38d384c 100644
--- a/tests/auto/qml/qqmlengine/CMakeLists.txt
+++ b/tests/auto/qml/qqmlengine/CMakeLists.txt
@@ -4,6 +4,14 @@
## tst_qqmlengine Test:
#####################################################################
+# Collect test data
+
+file(GLOB test_data_glob
+ LIST_DIRECTORIES true
+ RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
+ "data/*")
+list(APPEND test_data ${test_data_glob})
+
add_qt_test(tst_qqmlengine
GUI
SOURCES
@@ -19,27 +27,26 @@ add_qt_test(tst_qqmlengine
Qt::Gui
Qt::Network
Qt::Qml
+ TESTDATA ${test_data}
)
# Resources:
+set(qmake_immediate_resource_files
+ "data/qrcurls.js"
+ "data/qrcurls.qml"
+)
+
add_qt_resource(tst_qqmlengine "qmake_immediate"
PREFIX
"/"
FILES
- data/qrcurls.js
- data/qrcurls.qml
+ ${qmake_immediate_resource_files}
)
-#### Keys ignored in scope 1:.:.:qqmlengine.pro:<TRUE>:
-# CONFIG = "testcase"
-
## Scopes:
#####################################################################
-#### Keys ignored in scope 2:.:.:qqmlengine.pro:APPLE_OSX:
-# CONFIG = "-app_bundle"
-
extend_target(tst_qqmlengine CONDITION boot2qt
DEFINES
SKIP_GCCORRUPTION_TEST
diff --git a/tests/auto/qml/qqmlengine/qqmlengine.pro b/tests/auto/qml/qqmlengine/qqmlengine.pro
index d2eb92bfd5..8946201ea0 100644
--- a/tests/auto/qml/qqmlengine/qqmlengine.pro
+++ b/tests/auto/qml/qqmlengine/qqmlengine.pro
@@ -4,6 +4,8 @@ macx:CONFIG -= app_bundle
include (../../shared/util.pri)
+TESTDATA = data/*
+
SOURCES += tst_qqmlengine.cpp
QT += core-private gui-private qml-private network testlib
diff --git a/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp b/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp
index 66d50cfe39..aae42e9ebb 100644
--- a/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp
+++ b/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp
@@ -428,7 +428,7 @@ void tst_qqmlengine::trimComponentCache()
engine.setIncubationController(&componentCache);
QQmlComponent component(&engine, testFileUrl(file));
- QVERIFY(component.isReady());
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
QScopedPointer<QObject> object(component.create());
QVERIFY(object != nullptr);
QCOMPARE(object->property("success").toBool(), true);
@@ -742,13 +742,17 @@ public:
CustomSelector(const QUrl &base):m_base(base){}
virtual QUrl intercept(const QUrl &url, QQmlAbstractUrlInterceptor::DataType d)
{
- if (url.scheme() != QStringLiteral("file"))
+ if ((url.scheme() != QStringLiteral("file") && url.scheme() != QStringLiteral("qrc"))
+ || url.path().contains("QtQml"))
return url;
if (!m_interceptionPoints.contains(d))
return url;
- if (url.path().endsWith("Test.2/qmldir"))//Special case
- return QUrl::fromLocalFile(m_base.path() + "interception/module/intercepted/qmldir");
+ if (url.path().endsWith("Test.2/qmldir")) {//Special case
+ QUrl url = m_base;
+ url.setPath(m_base.path() + "interception/module/intercepted/qmldir");
+ return url;
+ }
// Special case: with 5.10 we always add the implicit import, so we need to explicitly handle this case now
if (url.path().endsWith("intercepted/qmldir"))
return url;
@@ -836,7 +840,7 @@ void tst_qqmlengine::urlInterceptor()
QFETCH(QString, expectedAbsoluteUrl);
QQmlEngine e;
- e.addImportPath(testFileUrl("interception/imports").toLocalFile());
+ e.addImportPath(testFileUrl("interception/imports").url());
CustomSelector cs(testFileUrl(""));
cs.m_interceptionPoints = interceptionPoint;
e.setUrlInterceptor(&cs);
@@ -1033,6 +1037,46 @@ void tst_qqmlengine::singletonInstance()
{
qmlRegisterSingletonType<CppSingleton>("Qt.test",1,0,"NotAmbiguous", [](QQmlEngine* qeng, QJSEngine* jeng) -> QObject* {return CppSingleton::create(qeng, jeng);}); // test that overloads for qmlRegisterSingleton are not ambiguous
}
+ {
+ // Register QObject* directly
+ CppSingleton single;
+ int id = qmlRegisterSingletonInstance("Qt.test", 1, 0, "CppOwned",
+ &single);
+ QQmlEngine engine2;
+ CppSingleton *singlePtr = engine2.singletonInstance<CppSingleton *>(id);
+ QVERIFY(singlePtr);
+ QCOMPARE(&single, singlePtr);
+ QVERIFY(engine2.objectOwnership(singlePtr) == QQmlEngine::CppOwnership);
+ }
+
+ {
+ CppSingleton single;
+ QQmlEngine engineA;
+ QQmlEngine engineB;
+ int id = qmlRegisterSingletonInstance("Qt.test", 1, 0, "CppOwned", &single);
+ auto singlePtr = engineA.singletonInstance<CppSingleton *>(id);
+ QVERIFY(singlePtr);
+ singlePtr = engineA.singletonInstance<CppSingleton *>(id); // accessing the singleton multiple times from the same engine is fine
+ QVERIFY(singlePtr);
+ QTest::ignoreMessage(QtMsgType::QtCriticalMsg, "<Unknown File>: qmlRegisterSingletonType(): \"CppOwned\" is not available because the callback function returns a null pointer.");
+ QTest::ignoreMessage(QtMsgType::QtWarningMsg, "<Unknown File>: Singleton registered by registerSingletonInstance must only be accessed from one engine");
+ QCOMPARE(&single, singlePtr);
+ auto noSinglePtr = engineB.singletonInstance<CppSingleton *>(id);
+ QVERIFY(!noSinglePtr);
+ }
+
+ {
+ CppSingleton single;
+ QThread newThread {};
+ single.moveToThread(&newThread);
+ QCOMPARE(single.thread(), &newThread);
+ QQmlEngine engineB;
+ int id = qmlRegisterSingletonInstance("Qt.test", 1, 0, "CppOwned", &single);
+ QTest::ignoreMessage(QtMsgType::QtCriticalMsg, "<Unknown File>: qmlRegisterSingletonType(): \"CppOwned\" is not available because the callback function returns a null pointer.");
+ QTest::ignoreMessage(QtMsgType::QtWarningMsg, "<Unknown File>: Registered object must live in the same thread as the engine it was registered with");
+ auto noSinglePtr = engineB.singletonInstance<CppSingleton *>(id);
+ QVERIFY(!noSinglePtr);
+ }
{
// Invalid types
diff --git a/tests/auto/qml/qqmlerror/tst_qqmlerror.cpp b/tests/auto/qml/qqmlerror/tst_qqmlerror.cpp
index 7a7185e909..f282e60417 100644
--- a/tests/auto/qml/qqmlerror/tst_qqmlerror.cpp
+++ b/tests/auto/qml/qqmlerror/tst_qqmlerror.cpp
@@ -195,7 +195,7 @@ void tst_qqmlerror::debug()
}
{
- QUrl url(dataDirectoryUrl().resolved(QUrl("test.txt")));
+ QUrl url = testFileUrl("test.txt");
QQmlError error;
error.setUrl(url);
error.setDescription("An Error");
diff --git a/tests/auto/qml/qqmlexpression/tst_qqmlexpression.cpp b/tests/auto/qml/qqmlexpression/tst_qqmlexpression.cpp
index 1decc04ad2..0ba29d6b6a 100644
--- a/tests/auto/qml/qqmlexpression/tst_qqmlexpression.cpp
+++ b/tests/auto/qml/qqmlexpression/tst_qqmlexpression.cpp
@@ -28,6 +28,7 @@
#include <qtest.h>
#include <QtQml/qqmlengine.h>
+#include <QtQml/qqmlfile.h>
#include <QtQml/qqmlcomponent.h>
#include <QtQml/qqmlexpression.h>
#include <QtQml/qqmlscriptstring.h>
@@ -125,10 +126,12 @@ void tst_qqmlexpression::expressionFromDataComponent()
QQmlEngine engine;
QQmlComponent c(&engine);
- QUrl url = testFileUrl("expressionFromDataComponent.qml");
+ const QString fn(QLatin1String("expressionFromDataComponent.qml"));
+ QUrl url = testFileUrl(fn);
+ QString path = testFile(fn);
{
- QFile f(url.toLocalFile());
+ QFile f(path);
QVERIFY(f.open(QIODevice::ReadOnly));
c.setData(f.readAll(), url);
}
diff --git a/tests/auto/qml/qqmlimport/CMakeLists.txt b/tests/auto/qml/qqmlimport/CMakeLists.txt
index 71f71c7326..50b588cb04 100644
--- a/tests/auto/qml/qqmlimport/CMakeLists.txt
+++ b/tests/auto/qml/qqmlimport/CMakeLists.txt
@@ -4,6 +4,32 @@
## tst_qqmlimport Test:
#####################################################################
+# Collect test data
+
+file(GLOB test_data_glob
+ LIST_DIRECTORIES true
+ RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
+ "data/*")
+list(APPEND test_data ${test_data_glob})
+
+file(GLOB test_data_glob
+ LIST_DIRECTORIES true
+ RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
+ "MyPluginSupported/*")
+list(APPEND test_data ${test_data_glob})
+
+file(GLOB test_data_glob
+ LIST_DIRECTORIES true
+ RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
+ "MyPluginUnsupported/*")
+list(APPEND test_data ${test_data_glob})
+
+file(GLOB test_data_glob
+ LIST_DIRECTORIES true
+ RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
+ "FormFromQmlDir/*")
+list(APPEND test_data ${test_data_glob})
+
add_qt_test(tst_qqmlimport
GUI
SOURCES
@@ -17,17 +43,12 @@ add_qt_test(tst_qqmlimport
Qt::Gui
Qt::Qml
Qt::Quick
+ TESTDATA ${test_data}
)
-#### Keys ignored in scope 1:.:.:qqmlimport.pro:<TRUE>:
-# CONFIG = "testcase"
-
## Scopes:
#####################################################################
-#### Keys ignored in scope 2:.:.:qqmlimport.pro:APPLE_OSX:
-# CONFIG = "-app_bundle"
-
extend_target(tst_qqmlimport CONDITION ANDROID OR APPLE_IOS
DEFINES
QT_QMLTEST_DATADIR=\\\":/data\\\"
diff --git a/tests/auto/qml/qqmlimport/data/QTBUG-77102/imports/MyPlugin/MyComponent_0_9.qml b/tests/auto/qml/qqmlimport/data/QTBUG-77102/imports/MyPlugin/MyComponent_0_9.qml
new file mode 100644
index 0000000000..a63e2d121c
--- /dev/null
+++ b/tests/auto/qml/qqmlimport/data/QTBUG-77102/imports/MyPlugin/MyComponent_0_9.qml
@@ -0,0 +1,14 @@
+import QtQuick 2.0
+
+Rectangle {
+ width: 50
+ height: 50
+
+ color: "yellow"
+
+ Text {
+ anchors.centerIn: parent
+ text: "0.9"
+
+ }
+}
diff --git a/tests/auto/qml/qqmlimport/data/QTBUG-77102/imports/MyPlugin/MyComponent_1_0.qml b/tests/auto/qml/qqmlimport/data/QTBUG-77102/imports/MyPlugin/MyComponent_1_0.qml
new file mode 100644
index 0000000000..d9cff582bb
--- /dev/null
+++ b/tests/auto/qml/qqmlimport/data/QTBUG-77102/imports/MyPlugin/MyComponent_1_0.qml
@@ -0,0 +1,15 @@
+
+import QtQuick 2.0
+
+Rectangle {
+ width: 50
+ height: 50
+
+ color: "orange"
+
+ Text {
+ anchors.centerIn: parent
+ text: "1.0"
+
+ }
+}
diff --git a/tests/auto/qml/qqmlimport/data/QTBUG-77102/imports/MyPlugin/MySettings_1_0.qml b/tests/auto/qml/qqmlimport/data/QTBUG-77102/imports/MyPlugin/MySettings_1_0.qml
new file mode 100644
index 0000000000..63ad6ba1a5
--- /dev/null
+++ b/tests/auto/qml/qqmlimport/data/QTBUG-77102/imports/MyPlugin/MySettings_1_0.qml
@@ -0,0 +1,7 @@
+pragma Singleton
+
+import QtQuick 2.0
+
+Item {
+ property int baseWidth: 50
+}
diff --git a/tests/auto/qml/qqmlimport/data/QTBUG-77102/imports/MyPlugin/qmldir b/tests/auto/qml/qqmlimport/data/QTBUG-77102/imports/MyPlugin/qmldir
new file mode 100644
index 0000000000..57b8c55f81
--- /dev/null
+++ b/tests/auto/qml/qqmlimport/data/QTBUG-77102/imports/MyPlugin/qmldir
@@ -0,0 +1,6 @@
+module MyPlugin
+
+MyComponent 0.9 MyComponent_0_9.qml
+
+MyComponent 1.0 MyComponent_1_0.qml
+singleton MySettings 1.0 MySettings_1_0.qml
diff --git a/tests/auto/qml/qqmlimport/data/QTBUG-77102/main.0.9.fail.qml b/tests/auto/qml/qqmlimport/data/QTBUG-77102/main.0.9.fail.qml
new file mode 100644
index 0000000000..0fbcec607a
--- /dev/null
+++ b/tests/auto/qml/qqmlimport/data/QTBUG-77102/main.0.9.fail.qml
@@ -0,0 +1,11 @@
+import QtQuick 2.12
+import MyPlugin 0.9
+
+Item {
+ width: MySettings.baseWidth
+ height: 100
+
+ MyComponent {
+ anchors.centerIn: parent
+ }
+}
diff --git a/tests/auto/qml/qqmlimport/data/QTBUG-77102/main.0.9.qml b/tests/auto/qml/qqmlimport/data/QTBUG-77102/main.0.9.qml
new file mode 100644
index 0000000000..53fc25699c
--- /dev/null
+++ b/tests/auto/qml/qqmlimport/data/QTBUG-77102/main.0.9.qml
@@ -0,0 +1,11 @@
+import QtQuick 2.12
+import MyPlugin 0.9
+
+Item {
+ width: 100
+ height: 100
+
+ MyComponent {
+ anchors.centerIn: parent
+ }
+}
diff --git a/tests/auto/qml/qqmlimport/data/QTBUG-77102/main.1.0.qml b/tests/auto/qml/qqmlimport/data/QTBUG-77102/main.1.0.qml
new file mode 100644
index 0000000000..1f6244068b
--- /dev/null
+++ b/tests/auto/qml/qqmlimport/data/QTBUG-77102/main.1.0.qml
@@ -0,0 +1,11 @@
+import QtQuick 2.12
+import MyPlugin 1.0
+
+Item {
+ width: MySettings.baseWidth
+ height: 100
+
+ MyComponent {
+ anchors.centerIn: parent
+ }
+}
diff --git a/tests/auto/qml/qqmlimport/data/QTBUG-77102/main.nonumber.qml b/tests/auto/qml/qqmlimport/data/QTBUG-77102/main.nonumber.qml
new file mode 100644
index 0000000000..dea00c0163
--- /dev/null
+++ b/tests/auto/qml/qqmlimport/data/QTBUG-77102/main.nonumber.qml
@@ -0,0 +1,11 @@
+import QtQuick 2.12
+import "imports/MyPlugin"
+
+Item {
+ width: MySettings.baseWidth
+ height: 100
+
+ MyComponent {
+ anchors.centerIn: parent
+ }
+}
diff --git a/tests/auto/qml/qqmlimport/qqmlimport.pro b/tests/auto/qml/qqmlimport/qqmlimport.pro
index c8b0f68d9f..ba80547f4e 100644
--- a/tests/auto/qml/qqmlimport/qqmlimport.pro
+++ b/tests/auto/qml/qqmlimport/qqmlimport.pro
@@ -6,3 +6,8 @@ osx:CONFIG -= app_bundle
SOURCES += tst_qqmlimport.cpp
include (../../shared/util.pri)
+
+TESTDATA = data/* \
+ MyPluginSupported/* \
+ MyPluginUnsupported/* \
+ FormFromQmlDir/*
diff --git a/tests/auto/qml/qqmlimport/tst_qqmlimport.cpp b/tests/auto/qml/qqmlimport/tst_qqmlimport.cpp
index a3cb68fdcb..ca1e52ad2c 100644
--- a/tests/auto/qml/qqmlimport/tst_qqmlimport.cpp
+++ b/tests/auto/qml/qqmlimport/tst_qqmlimport.cpp
@@ -45,6 +45,7 @@ private slots:
void completeQmldirPaths_data();
void completeQmldirPaths();
void interceptQmldir();
+ void singletonVersionResolution();
void cleanup();
};
@@ -77,7 +78,7 @@ void tst_QQmlImport::testDesignerSupported()
QVERIFY(window->errors().isEmpty());
QString warningString("%1:30:1: module does not support the designer \"MyPluginUnsupported\" \n import MyPluginUnsupported 1.0\r \n ^ ");
-#ifndef Q_OS_WIN
+#if !defined(Q_OS_WIN) && !defined(Q_OS_ANDROID)
warningString.remove('\r');
#endif
warningString = warningString.arg(testFileUrl("testfile_unsupported.qml").toString());
@@ -129,6 +130,9 @@ void tst_QQmlImport::uiFormatLoading()
void tst_QQmlImport::importPathOrder()
{
+#ifdef Q_OS_ANDROID
+ QSKIP("QLibraryInfo::location(QLibraryInfo::Qml2ImportsPath) returns bogus path on Android, but its nevertheless unusable.");
+#endif
QStringList expectedImportPaths;
QString appDirPath = QCoreApplication::applicationDirPath();
QString qml2Imports = QLibraryInfo::location(QLibraryInfo::Qml2ImportsPath);
@@ -212,6 +216,50 @@ void tst_QQmlImport::interceptQmldir()
QVERIFY(!obj.isNull());
}
+// QTBUG-77102
+void tst_QQmlImport::singletonVersionResolution()
+{
+ QQmlEngine engine;
+ engine.addImportPath(testFile("QTBUG-77102/imports"));
+ {
+ // Singleton with higher version is simply ignored when importing lower version of plugin
+ QQmlComponent component(&engine);
+ component.loadUrl(testFileUrl("QTBUG-77102/main.0.9.qml"));
+ QVERIFY(component.isReady());
+ QScopedPointer<QObject> obj(component.create());
+ QVERIFY(!obj.isNull());
+ }
+ {
+ // but the singleton is not accessible
+ QQmlComponent component(&engine);
+ QTest::ignoreMessage(QtMsgType::QtWarningMsg, QRegularExpression {".*ReferenceError: MySettings is not defined$"} );
+ component.loadUrl(testFileUrl("QTBUG-77102/main.0.9.fail.qml"));
+ QVERIFY(component.isReady());
+ QScopedPointer<QObject> obj(component.create());
+ QVERIFY(!obj.isNull());
+ }
+ {
+ // unless a version which is high enough is imported
+ QQmlComponent component(&engine);
+ component.loadUrl(testFileUrl("QTBUG-77102/main.1.0.qml"));
+ QVERIFY(component.isReady());
+ QScopedPointer<QObject> obj(component.create());
+ QVERIFY(!obj.isNull());
+ auto item = qobject_cast<QQuickItem*>(obj.get());
+ QCOMPARE(item->width(), 50);
+ }
+ {
+ // or when there is no number because we are importing from a path
+ QQmlComponent component(&engine);
+ component.loadUrl(testFileUrl("QTBUG-77102/main.nonumber.qml"));
+ QVERIFY(component.isReady());
+ QScopedPointer<QObject> obj(component.create());
+ QVERIFY(!obj.isNull());
+ auto item = qobject_cast<QQuickItem*>(obj.get());
+ QCOMPARE(item->width(), 50);
+ }
+}
+
QTEST_MAIN(tst_QQmlImport)
diff --git a/tests/auto/qml/qqmllocale/tst_qqmllocale.cpp b/tests/auto/qml/qqmllocale/tst_qqmllocale.cpp
index cc13fb4b5f..a90749208c 100644
--- a/tests/auto/qml/qqmllocale/tst_qqmllocale.cpp
+++ b/tests/auto/qml/qqmllocale/tst_qqmllocale.cpp
@@ -32,6 +32,8 @@
#include <QtQml/qqmlcomponent.h>
#include <QtQml/qqmlcontext.h>
#include <QtCore/QDateTime>
+#include <QtCore/qscopeguard.h>
+#include <QtCore/qscopedpointer.h>
#include <qcolor.h>
#include "../../shared/util.h"
@@ -1270,13 +1272,21 @@ void tst_qqmllocale::timeZoneUpdated()
// Set the timezone to Brisbane time, AEST-10:00
setTimeZone(QByteArray("Australia/Brisbane"));
+ QScopedPointer<QObject> obj;
+ auto cleanup = qScopeGuard([&original, &obj] {
+ // Restore to original time zone
+ setTimeZone(original);
+ QMetaObject::invokeMethod(obj.data(), "resetTimeZone");
+ });
+
DateFormatter formatter;
QQmlEngine e;
e.rootContext()->setContextObject(&formatter);
QQmlComponent c(&e, testFileUrl("timeZoneUpdated.qml"));
- QScopedPointer<QObject> obj(c.create());
+ QVERIFY2(!c.isError(), qPrintable(c.errorString()));
+ obj.reset(c.create());
QVERIFY(obj);
QVERIFY(obj->property("success").toBool());
@@ -1284,11 +1294,6 @@ void tst_qqmllocale::timeZoneUpdated()
setTimeZone(QByteArray("Asia/Kolkata"));
QMetaObject::invokeMethod(obj.data(), "check");
-
- // Reset to original time
- setTimeZone(original);
- QMetaObject::invokeMethod(obj.data(), "resetTimeZone");
-
QVERIFY(obj->property("success").toBool());
}
#endif
diff --git a/tests/auto/qml/qqmlnotifier/tst_qqmlnotifier.cpp b/tests/auto/qml/qqmlnotifier/tst_qqmlnotifier.cpp
index a5332c8860..de762d66c5 100644
--- a/tests/auto/qml/qqmlnotifier/tst_qqmlnotifier.cpp
+++ b/tests/auto/qml/qqmlnotifier/tst_qqmlnotifier.cpp
@@ -342,6 +342,9 @@ void tst_qqmlnotifier::lotsOfBindings()
void tst_qqmlnotifier::deleteFromHandler()
{
+#ifdef Q_OS_ANDROID
+ QSKIP("Android seems to have problems with QProcess");
+#endif
#if !QT_CONFIG(process)
QSKIP("Need QProcess support to test qFatal.");
#else
diff --git a/tests/auto/qml/qqmlpromise/data/promisechain.qml b/tests/auto/qml/qqmlpromise/data/promisechain.qml
new file mode 100644
index 0000000000..fa1809aef0
--- /dev/null
+++ b/tests/auto/qml/qqmlpromise/data/promisechain.qml
@@ -0,0 +1,24 @@
+import QtQml 2.0
+
+QtObject {
+ property int x: 0
+ id: root;
+ Component.onCompleted: {
+ new Promise((res) => {
+ res(1)
+ })
+ .then((data) => {
+ console.debug(data)
+ return new Promise((res) => {res(2)});
+ })
+ .then((data) => {
+ console.debug(data)
+ return new Promise((res) => {res(3)});
+ })
+ .then((data) => {
+ console.debug(data);
+ root.x = 42;
+ });
+ }
+
+}
diff --git a/tests/auto/qml/qqmlpromise/tst_qqmlpromise.cpp b/tests/auto/qml/qqmlpromise/tst_qqmlpromise.cpp
index 0f4bb5cdcc..41850d0263 100644
--- a/tests/auto/qml/qqmlpromise/tst_qqmlpromise.cpp
+++ b/tests/auto/qml/qqmlpromise/tst_qqmlpromise.cpp
@@ -82,6 +82,7 @@ private slots:
void then_fulfilled_non_callable();
void then_reject_non_callable();
void then_resolve_multiple_then();
+ void promiseChain();
private:
void execute_test(QString testName);
@@ -270,6 +271,20 @@ void tst_qqmlpromise::execute_test(QString testName)
QTRY_COMPARE(object->property("wasTestSuccessful").toBool(), true);
}
+void tst_qqmlpromise::promiseChain()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("promisechain.qml"));
+ QVERIFY(component.isReady());
+ QTest::ignoreMessage(QtDebugMsg, "1");
+ QTest::ignoreMessage(QtDebugMsg, "2");
+ QTest::ignoreMessage(QtDebugMsg, "3");
+ QScopedPointer<QObject> root(component.create());
+ QVERIFY(root);
+ QTRY_VERIFY(root->property("x") == 42);
+
+}
+
QTEST_MAIN(tst_qqmlpromise)
diff --git a/tests/auto/qml/qqmlproperty/data/nullPropertyBinding.qml b/tests/auto/qml/qqmlproperty/data/nullPropertyBinding.qml
new file mode 100644
index 0000000000..4fffc7aead
--- /dev/null
+++ b/tests/auto/qml/qqmlproperty/data/nullPropertyBinding.qml
@@ -0,0 +1,22 @@
+import QtQuick 2.12
+
+Item {
+ id: root
+
+ width: 640
+ height: 480
+
+ property bool toggle: false
+ property Item bound
+ property string message: "defined"
+
+ readonly property Item item: root.toggle ? root : null
+
+ Binding { target: root; property: "bound"; value: item}
+
+ function tog() {
+ console.info(root.bound ? root.bound.message: "undefined")
+ root.toggle = !root.toggle
+ return 42;
+ }
+}
diff --git a/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp b/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp
index 04c61ec11b..67da768f73 100644
--- a/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp
+++ b/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp
@@ -144,6 +144,7 @@ private slots:
void deeplyNestedObject();
void readOnlyDynamicProperties();
void aliasToIdWithMatchingQmlFileNameOnCaseInsensitiveFileSystem();
+ void nullPropertyBinding();
void floatToStringPrecision_data();
void floatToStringPrecision();
@@ -2059,6 +2060,22 @@ void tst_qqmlproperty::aliasToIdWithMatchingQmlFileNameOnCaseInsensitiveFileSyst
QVERIFY(property.isValid());
}
+// QTBUG-77027
+void tst_qqmlproperty::nullPropertyBinding()
+{
+ const QUrl url = testFileUrl("nullPropertyBinding.qml");
+ QQmlEngine engine;
+ QQmlComponent component(&engine, url);
+ QScopedPointer<QObject> root(component.create());
+ QVERIFY(root);
+ QTest::ignoreMessage(QtMsgType::QtInfoMsg, "undefined");
+ QMetaObject::invokeMethod(root.get(), "tog");
+ QTest::ignoreMessage(QtMsgType::QtInfoMsg, "defined");
+ QMetaObject::invokeMethod(root.get(), "tog");
+ QTest::ignoreMessage(QtMsgType::QtInfoMsg, "undefined");
+ QMetaObject::invokeMethod(root.get(), "tog");
+}
+
void tst_qqmlproperty::floatToStringPrecision_data()
{
QTest::addColumn<QString>("propertyName");
diff --git a/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/data/dragParentOfMPTA.qml b/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/data/dragParentOfMPTA.qml
new file mode 100644
index 0000000000..dc7e5f6411
--- /dev/null
+++ b/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/data/dragParentOfMPTA.qml
@@ -0,0 +1,50 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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 General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** 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-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.12
+
+Item {
+ width: 640
+ height: 480
+ property alias touchpointPressed: tp1.pressed
+
+ Rectangle {
+ color: tp1.pressed ? "lightsteelblue" : drag.active ? "tomato" : "wheat"
+ width: 180
+ height: 180
+
+ DragHandler { id: drag }
+
+ MultiPointTouchArea {
+ anchors.fill: parent
+ touchPoints: [
+ TouchPoint { id: tp1 }
+ ]
+ }
+ }
+}
diff --git a/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/tst_multipointtoucharea_interop.cpp b/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/tst_multipointtoucharea_interop.cpp
index 58bc3d40b5..35fed99e8b 100644
--- a/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/tst_multipointtoucharea_interop.cpp
+++ b/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/tst_multipointtoucharea_interop.cpp
@@ -56,6 +56,7 @@ private slots:
void touchDrag();
void touchesThenPinch();
void unloadHandlerWithPassiveGrab();
+ void dragHandlerInParentStealingGrabFromItem();
private:
void createView(QScopedPointer<QQuickView> &window, const char *fileName);
@@ -300,6 +301,48 @@ void tst_MptaInterop::unloadHandlerWithPassiveGrab()
QTest::mouseRelease(window, Qt::LeftButton, 0, point); // QTBUG-73819: don't crash
}
+void tst_MptaInterop::dragHandlerInParentStealingGrabFromItem() // QTBUG-75025
+{
+ const int dragThreshold = QGuiApplication::styleHints()->startDragDistance();
+ QScopedPointer<QQuickView> windowPtr;
+ createView(windowPtr, "dragParentOfMPTA.qml");
+ QQuickView * window = windowPtr.data();
+ auto pointerEvent = QQuickWindowPrivate::get(window)->pointerEventInstance(QQuickPointerDevice::genericMouseDevice());
+
+ QPointer<QQuickPointerHandler> handler = window->rootObject()->findChild<QQuickPointerHandler*>();
+ QVERIFY(handler);
+ QQuickMultiPointTouchArea *mpta = window->rootObject()->findChild<QQuickMultiPointTouchArea*>();
+ QVERIFY(mpta);
+
+ // In QTBUG-75025 there is a QQ Controls Button; the MPTA here stands in for that,
+ // simply as an Item that grabs the mouse. The bug has nothing to do with MPTA specifically.
+
+ QPoint point(20, 20);
+ QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, point);
+ QCOMPARE(window->mouseGrabberItem(), mpta);
+ QCOMPARE(window->rootObject()->property("touchpointPressed").toBool(), true);
+
+ // Start dragging
+ // DragHandler keeps monitoring, due to its passive grab,
+ // and eventually steals the exclusive grab from MPTA
+ int dragStoleGrab = 0;
+ for (int i = 0; i < 4; ++i) {
+ point += QPoint(dragThreshold / 2, 0);
+ QTest::mouseMove(window, point);
+ if (!dragStoleGrab && pointerEvent->point(0)->exclusiveGrabber() == handler)
+ dragStoleGrab = i;
+ }
+ if (dragStoleGrab)
+ qCDebug(lcPointerTests, "DragHandler stole the grab after %d events", dragStoleGrab);
+ QVERIFY(dragStoleGrab > 1);
+ QCOMPARE(handler->active(), true);
+ QCOMPARE(window->rootObject()->property("touchpointPressed").toBool(), false);
+
+ QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, point);
+ QCOMPARE(handler->active(), false);
+ QCOMPARE(window->rootObject()->property("touchpointPressed").toBool(), false);
+}
+
QTEST_MAIN(tst_MptaInterop)
#include "tst_multipointtoucharea_interop.moc"
diff --git a/tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp b/tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp
index 7636bb38f1..66314f88a2 100644
--- a/tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp
+++ b/tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp
@@ -54,6 +54,7 @@ private slots:
void defaultPropertyValues();
void touchDrag();
+ void mouseDrag_data();
void mouseDrag();
void dragFromMargin();
void snapMode_data();
@@ -195,8 +196,22 @@ void tst_DragHandler::touchDrag()
QCOMPARE(centroidChangedSpy.count(), 5);
}
+void tst_DragHandler::mouseDrag_data()
+{
+ QTest::addColumn<Qt::MouseButtons>("acceptedButtons");
+ QTest::addColumn<Qt::MouseButtons>("dragButton");
+ QTest::newRow("left: drag") << Qt::MouseButtons(Qt::LeftButton) << Qt::MouseButtons(Qt::LeftButton);
+ QTest::newRow("right: don't drag") << Qt::MouseButtons(Qt::LeftButton) << Qt::MouseButtons(Qt::RightButton);
+ QTest::newRow("left: don't drag") << Qt::MouseButtons(Qt::RightButton | Qt::MiddleButton) << Qt::MouseButtons(Qt::LeftButton);
+ QTest::newRow("right or middle: drag") << Qt::MouseButtons(Qt::RightButton | Qt::MiddleButton) << Qt::MouseButtons(Qt::MiddleButton);
+}
+
void tst_DragHandler::mouseDrag()
{
+ QFETCH(Qt::MouseButtons, acceptedButtons);
+ QFETCH(Qt::MouseButtons, dragButton);
+ bool shouldDrag = bool(acceptedButtons & dragButton);
+
const int dragThreshold = QGuiApplication::styleHints()->startDragDistance();
QScopedPointer<QQuickView> windowPtr;
createView(windowPtr, "draggables.qml");
@@ -206,6 +221,7 @@ void tst_DragHandler::mouseDrag()
QVERIFY(ball);
QQuickDragHandler *dragHandler = ball->findChild<QQuickDragHandler*>();
QVERIFY(dragHandler);
+ dragHandler->setAcceptedButtons(acceptedButtons); // QTBUG-76875
QSignalSpy translationChangedSpy(dragHandler, SIGNAL(translationChanged()));
QSignalSpy centroidChangedSpy(dragHandler, SIGNAL(centroidChanged()));
@@ -213,45 +229,57 @@ void tst_DragHandler::mouseDrag()
QPointF ballCenter = ball->clipRect().center();
QPointF scenePressPos = ball->mapToScene(ballCenter);
QPoint p1 = scenePressPos.toPoint();
- QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1);
+ QTest::mousePress(window, static_cast<Qt::MouseButton>(int(dragButton)), Qt::NoModifier, p1);
QVERIFY(!dragHandler->active());
- QCOMPARE(dragHandler->centroid().position(), ballCenter);
- QCOMPARE(dragHandler->centroid().pressPosition(), ballCenter);
- QCOMPARE(dragHandler->centroid().scenePosition(), scenePressPos);
- QCOMPARE(dragHandler->centroid().scenePressPosition(), scenePressPos);
- QCOMPARE(dragHandler->centroid().velocity(), QVector2D());
- QCOMPARE(centroidChangedSpy.count(), 1);
+ if (shouldDrag) {
+ QCOMPARE(dragHandler->centroid().position(), ballCenter);
+ QCOMPARE(dragHandler->centroid().pressPosition(), ballCenter);
+ QCOMPARE(dragHandler->centroid().scenePosition(), scenePressPos);
+ QCOMPARE(dragHandler->centroid().scenePressPosition(), scenePressPos);
+ QCOMPARE(dragHandler->centroid().velocity(), QVector2D());
+ QCOMPARE(centroidChangedSpy.count(), 1);
+ }
p1 += QPoint(dragThreshold, 0);
QTest::mouseMove(window, p1);
- QTRY_VERIFY(dragHandler->centroid().velocity().x() > 0);
- QCOMPARE(centroidChangedSpy.count(), 2);
- QVERIFY(!dragHandler->active());
+ if (shouldDrag) {
+ QTRY_VERIFY(dragHandler->centroid().velocity().x() > 0);
+ QCOMPARE(centroidChangedSpy.count(), 2);
+ QVERIFY(!dragHandler->active());
+ }
p1 += QPoint(1, 0);
QTest::mouseMove(window, p1);
- QTRY_VERIFY(dragHandler->active());
+ if (shouldDrag)
+ QTRY_VERIFY(dragHandler->active());
+ else
+ QVERIFY(!dragHandler->active());
QCOMPARE(translationChangedSpy.count(), 0);
- QCOMPARE(centroidChangedSpy.count(), 3);
+ if (shouldDrag)
+ QCOMPARE(centroidChangedSpy.count(), 3);
QCOMPARE(dragHandler->translation().x(), 0.0);
QPointF sceneGrabPos = p1;
- QCOMPARE(dragHandler->centroid().sceneGrabPosition(), sceneGrabPos);
+ if (shouldDrag)
+ QCOMPARE(dragHandler->centroid().sceneGrabPosition(), sceneGrabPos);
p1 += QPoint(19, 0);
QTest::mouseMove(window, p1);
- QTRY_VERIFY(dragHandler->active());
- QCOMPARE(dragHandler->centroid().position(), ballCenter);
- QCOMPARE(dragHandler->centroid().pressPosition(), ballCenter);
- QCOMPARE(dragHandler->centroid().scenePosition(), ball->mapToScene(ballCenter));
- QCOMPARE(dragHandler->centroid().scenePressPosition(), scenePressPos);
- QCOMPARE(dragHandler->centroid().sceneGrabPosition(), sceneGrabPos);
- QCOMPARE(dragHandler->translation().x(), dragThreshold + 20.0);
- QCOMPARE(dragHandler->translation().y(), 0.0);
- QVERIFY(dragHandler->centroid().velocity().x() > 0);
- QCOMPARE(centroidChangedSpy.count(), 4);
- QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1);
+ QVERIFY(shouldDrag ? dragHandler->active() : !dragHandler->active());
+ if (shouldDrag) {
+ QCOMPARE(dragHandler->centroid().position(), ballCenter);
+ QCOMPARE(dragHandler->centroid().pressPosition(), ballCenter);
+ QCOMPARE(dragHandler->centroid().scenePosition(), ball->mapToScene(ballCenter));
+ QCOMPARE(dragHandler->centroid().scenePressPosition(), scenePressPos);
+ QCOMPARE(dragHandler->centroid().sceneGrabPosition(), sceneGrabPos);
+ QCOMPARE(dragHandler->translation().x(), dragThreshold + 20.0);
+ QCOMPARE(dragHandler->translation().y(), 0.0);
+ QVERIFY(dragHandler->centroid().velocity().x() > 0);
+ QCOMPARE(centroidChangedSpy.count(), 4);
+ }
+ QTest::mouseRelease(window, static_cast<Qt::MouseButton>(int(dragButton)), Qt::NoModifier, p1);
QTRY_VERIFY(!dragHandler->active());
QCOMPARE(dragHandler->centroid().pressedButtons(), Qt::NoButton);
- QCOMPARE(ball->mapToScene(ballCenter).toPoint(), p1);
- QCOMPARE(translationChangedSpy.count(), 1);
- QCOMPARE(centroidChangedSpy.count(), 5);
+ if (shouldDrag)
+ QCOMPARE(ball->mapToScene(ballCenter).toPoint(), p1);
+ QCOMPARE(translationChangedSpy.count(), shouldDrag ? 1 : 0);
+ QCOMPARE(centroidChangedSpy.count(), shouldDrag ? 5 : 0);
}
void tst_DragHandler::dragFromMargin() // QTBUG-74966
diff --git a/tests/auto/quick/pointerhandlers/qquicktaphandler/data/Button.qml b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/Button.qml
index 221e7df139..042b730799 100644
--- a/tests/auto/quick/pointerhandlers/qquicktaphandler/data/Button.qml
+++ b/tests/auto/quick/pointerhandlers/qquicktaphandler/data/Button.qml
@@ -34,6 +34,7 @@ Rectangle {
property alias pressed: tap.pressed
property bool checked: false
property alias gesturePolicy: tap.gesturePolicy
+ property point tappedPosition: Qt.point(0, 0)
signal tapped
signal canceled
@@ -51,6 +52,7 @@ Rectangle {
longPressThreshold: 100 // CI can be insanely slow, so don't demand a timely release to generate onTapped
onTapped: {
tapFlash.start()
+ root.tappedPosition = point.scenePosition
root.tapped()
}
onCanceled: root.canceled()
diff --git a/tests/auto/quick/pointerhandlers/qquicktaphandler/tst_qquicktaphandler.cpp b/tests/auto/quick/pointerhandlers/qquicktaphandler/tst_qquicktaphandler.cpp
index 33cea69147..e77ea97518 100644
--- a/tests/auto/quick/pointerhandlers/qquicktaphandler/tst_qquicktaphandler.cpp
+++ b/tests/auto/quick/pointerhandlers/qquicktaphandler/tst_qquicktaphandler.cpp
@@ -107,6 +107,8 @@ void tst_TapHandler::touchGesturePolicyDragThreshold()
QQuickItem *buttonDragThreshold = window->rootObject()->findChild<QQuickItem*>("DragThreshold");
QVERIFY(buttonDragThreshold);
+ QQuickTapHandler *tapHandler = buttonDragThreshold->findChild<QQuickTapHandler*>();
+ QVERIFY(tapHandler);
QSignalSpy dragThresholdTappedSpy(buttonDragThreshold, SIGNAL(tapped()));
// DragThreshold button stays pressed while touchpoint stays within dragThreshold, emits tapped on release
@@ -122,6 +124,8 @@ void tst_TapHandler::touchGesturePolicyDragThreshold()
QQuickTouchUtils::flush(window);
QTRY_VERIFY(!buttonDragThreshold->property("pressed").toBool());
QCOMPARE(dragThresholdTappedSpy.count(), 1);
+ QCOMPARE(buttonDragThreshold->property("tappedPosition").toPoint(), p1);
+ QCOMPARE(tapHandler->point().position(), QPointF());
// DragThreshold button is no longer pressed if touchpoint goes beyond dragThreshold
dragThresholdTappedSpy.clear();
@@ -152,6 +156,8 @@ void tst_TapHandler::mouseGesturePolicyDragThreshold()
QQuickItem *buttonDragThreshold = window->rootObject()->findChild<QQuickItem*>("DragThreshold");
QVERIFY(buttonDragThreshold);
+ QQuickTapHandler *tapHandler = buttonDragThreshold->findChild<QQuickTapHandler*>();
+ QVERIFY(tapHandler);
QSignalSpy dragThresholdTappedSpy(buttonDragThreshold, SIGNAL(tapped()));
// DragThreshold button stays pressed while mouse stays within dragThreshold, emits tapped on release
@@ -164,6 +170,8 @@ void tst_TapHandler::mouseGesturePolicyDragThreshold()
QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1);
QTRY_VERIFY(!buttonDragThreshold->property("pressed").toBool());
QTRY_COMPARE(dragThresholdTappedSpy.count(), 1);
+ QCOMPARE(buttonDragThreshold->property("tappedPosition").toPoint(), p1);
+ QCOMPARE(tapHandler->point().position(), QPointF());
// DragThreshold button is no longer pressed if mouse goes beyond dragThreshold
dragThresholdTappedSpy.clear();
diff --git a/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp b/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp
index 65a08ce87f..2314b82e8c 100644
--- a/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp
+++ b/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp
@@ -1652,6 +1652,7 @@ void tst_qquickflickable::flickTwiceUsingTouches()
QQuickViewTestUtil::moveMouseAway(window.data());
window->show();
QVERIFY(window->rootObject() != nullptr);
+ QVERIFY(QTest::qWaitForWindowActive(window.data()));
QQuickFlickable *flickable = qobject_cast<QQuickFlickable*>(window->rootObject());
QVERIFY(flickable != nullptr);
@@ -1994,6 +1995,7 @@ void tst_qquickflickable::nestedMouseAreaUsingTouch()
QQuickViewTestUtil::moveMouseAway(window.data());
window->show();
QVERIFY(window->rootObject() != nullptr);
+ QVERIFY(QTest::qWaitForWindowActive(window.data()));
QQuickFlickable *flickable = qobject_cast<QQuickFlickable*>(window->rootObject());
QVERIFY(flickable != nullptr);
@@ -2489,6 +2491,7 @@ void tst_qquickflickable::synchronousDrag()
QQuickViewTestUtil::moveMouseAway(window);
window->show();
QVERIFY(window->rootObject() != nullptr);
+ QVERIFY(QTest::qWaitForWindowActive(window));
QQuickFlickable *flickable = qobject_cast<QQuickFlickable*>(window->rootObject());
QVERIFY(flickable != nullptr);
diff --git a/tests/auto/quick/qquicklistview/data/resizeAfterComponentComplete.qml b/tests/auto/quick/qquicklistview/data/resizeAfterComponentComplete.qml
new file mode 100644
index 0000000000..851d8f9a0c
--- /dev/null
+++ b/tests/auto/quick/qquicklistview/data/resizeAfterComponentComplete.qml
@@ -0,0 +1,77 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.12
+
+ListView {
+ id: listView
+ property var lastItem
+ anchors.fill: parent
+ model: 10
+ delegate: Rectangle {
+ width: parent.width
+ height: 40
+ border.color: "lightsteelblue"
+ Text {
+ text: "Item" + (index + 1)
+ }
+ Component.onCompleted: {
+ if (index == 9)
+ listView.lastItem = this
+ }
+ }
+
+ populate: Transition {
+ NumberAnimation {
+ properties: "x,y"
+ duration: 1000
+ }
+ }
+}
diff --git a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp
index f14a6e75f6..bab2ec34f8 100644
--- a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp
+++ b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp
@@ -278,6 +278,7 @@ private slots:
void addOnCompleted();
void setPositionOnLayout();
void touchCancel();
+ void resizeAfterComponentComplete();
private:
template <class T> void items(const QUrl &source);
@@ -9020,6 +9021,21 @@ void tst_QQuickListView::touchCancel() // QTBUG-74679
QTRY_COMPARE(listview->contentY(), 500.0);
}
+void tst_QQuickListView::resizeAfterComponentComplete() // QTBUG-76487
+{
+ QScopedPointer<QQuickView> window(createView());
+ window->setSource(testFileUrl("resizeAfterComponentComplete.qml"));
+ window->resize(640, 480);
+ window->show();
+ QVERIFY(QTest::qWaitForWindowExposed(window.data()));
+
+ QObject *listView = window->rootObject();
+ QVERIFY(listView);
+
+ QObject *lastItem = qvariant_cast<QObject *>(listView->property("lastItem"));
+ QTRY_COMPARE(lastItem->property("y").toInt(), 9 * lastItem->property("height").toInt());
+}
+
QTEST_MAIN(tst_QQuickListView)
#include "tst_qquicklistview.moc"
diff --git a/tests/auto/quick/qquickshape/data/pathitem8.png b/tests/auto/quick/qquickshape/data/pathitem8.png
new file mode 100644
index 0000000000..ee585958ec
--- /dev/null
+++ b/tests/auto/quick/qquickshape/data/pathitem8.png
Binary files differ
diff --git a/tests/auto/quick/qquickshape/data/pathitem8.qml b/tests/auto/quick/qquickshape/data/pathitem8.qml
new file mode 100644
index 0000000000..9789ff90e0
--- /dev/null
+++ b/tests/auto/quick/qquickshape/data/pathitem8.qml
@@ -0,0 +1,88 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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 General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** 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-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.14
+import QtQuick.Shapes 1.14
+
+Item {
+ id: item
+ width: 200
+ height: 150
+
+ Shape {
+ vendorExtensionsEnabled: false
+ objectName: "shape"
+ id: shape
+ anchors.fill: parent
+
+ ShapePath {
+ strokeWidth: 4
+ strokeColor: "red"
+ scale: Qt.size(shape.width - 1, shape.height - 1)
+ fillGradient: LinearGradient {
+ x1: 20; y1: 20
+ x2: 180; y2: 130
+ GradientStop { position: 0; color: "blue" }
+ GradientStop { position: 0.2; color: "green" }
+ GradientStop { position: 0.4; color: "red" }
+ GradientStop { position: 0.6; color: "yellow" }
+ GradientStop { position: 1; color: "cyan" }
+ }
+ strokeStyle: ShapePath.DashLine
+ dashPattern: [ 1, 4 ]
+ PathMultiline {
+ paths: [[Qt.point(20.0 / (item.width - 1.0), 20.0 / (item.height - 1.0)),
+ Qt.point(180.0 / (item.width - 1.0), 130.0 / (item.height - 1.0)),
+ Qt.point(20.0 / (item.width - 1.0), 130.0 / (item.height - 1.0)),
+ Qt.point(20.0 / (item.width - 1.0), 20.0 / (item.height - 1.0)) ],
+ [Qt.point( 0.45 , 0.67 ),
+ Qt.point( 0.414906666468 , 0.573581858547 ),
+ Qt.point( 0.32604722665 , 0.522278837048 ),
+ Qt.point( 0.225 , 0.540096189432 ),
+ Qt.point( 0.159046106882 , 0.618696978501 ),
+ Qt.point( 0.159046106882 , 0.721303021499 ),
+ Qt.point( 0.225 , 0.799903810568 ),
+ Qt.point( 0.32604722665 , 0.817721162952 ),
+ Qt.point( 0.414906666468 , 0.766418141453 ),
+ Qt.point( 0.45 , 0.67 ),
+ ],
+ [Qt.point( 0.69 , 0.75 ),
+ Qt.point( 0.668943999881 , 0.692149115128 ),
+ Qt.point( 0.61562833599 , 0.661367302229 ),
+ Qt.point( 0.555 , 0.672057713659 ),
+ Qt.point( 0.515427664129 , 0.719218187101 ),
+ Qt.point( 0.515427664129 , 0.780781812899 ),
+ Qt.point( 0.555 , 0.827942286341 ),
+ Qt.point( 0.61562833599 , 0.838632697771 ),
+ Qt.point( 0.668943999881 , 0.807850884872 ),
+ Qt.point( 0.69 , 0.75 ),
+ ]]
+ }
+ }
+ }
+}
diff --git a/tests/auto/quick/qquickshape/tst_qquickshape.cpp b/tests/auto/quick/qquickshape/tst_qquickshape.cpp
index 174ada65a5..b3b8d2d148 100644
--- a/tests/auto/quick/qquickshape/tst_qquickshape.cpp
+++ b/tests/auto/quick/qquickshape/tst_qquickshape.cpp
@@ -59,6 +59,7 @@ private slots:
void radialGrad();
void conicalGrad();
void renderPolyline();
+ void renderMultiline();
};
tst_QQuickShape::tst_QQuickShape()
@@ -343,6 +344,35 @@ void tst_QQuickShape::renderPolyline()
QVERIFY2(res, qPrintable(errorMessage));
}
+void tst_QQuickShape::renderMultiline()
+{
+ QScopedPointer<QQuickView> window(createView());
+
+ window->setSource(testFileUrl("pathitem8.qml"));
+ window->show();
+ QVERIFY(QTest::qWaitForWindowExposed(window.data()));
+
+ if ((QGuiApplication::platformName() == QLatin1String("offscreen"))
+ || (QGuiApplication::platformName() == QLatin1String("minimal")))
+ QEXPECT_FAIL("", "Failure due to grabWindow not functional on offscreen/minimimal platforms", Abort);
+
+ QImage img = window->grabWindow();
+ QVERIFY(!img.isNull());
+
+ QImage refImg(testFileUrl("pathitem8.png").toLocalFile());
+ QVERIFY(!refImg.isNull());
+
+ QString errorMessage;
+ const QImage actualImg = img.convertToFormat(refImg.format());
+ const bool res = QQuickVisualTestUtil::compareImages(actualImg, refImg, &errorMessage);
+ if (!res) { // For visual inspection purposes.
+ QTest::qWait(5000);
+ const QString &tempLocation = QStandardPaths::writableLocation(QStandardPaths::TempLocation);
+ actualImg.save(tempLocation + QLatin1String("/pathitem8.png"));
+ }
+ QVERIFY2(res, qPrintable(errorMessage));
+}
+
QTEST_MAIN(tst_QQuickShape)
#include "tst_qquickshape.moc"
diff --git a/tests/auto/quick/qquicktext/BLACKLIST b/tests/auto/quick/qquicktext/BLACKLIST
index 6c3c3af154..594f9af3b3 100644
--- a/tests/auto/quick/qquicktext/BLACKLIST
+++ b/tests/auto/quick/qquicktext/BLACKLIST
@@ -4,3 +4,5 @@ qemu
osx-10.12
[fontSizeMode]
opensuse-42.1
+[contentSize]
+windows gcc
diff --git a/tests/manual/nodetypes_ng/MultiClipRects.qml b/tests/manual/nodetypes_ng/MultiClipRects.qml
index 793ebeae93..2d3804af21 100644
--- a/tests/manual/nodetypes_ng/MultiClipRects.qml
+++ b/tests/manual/nodetypes_ng/MultiClipRects.qml
@@ -61,8 +61,8 @@ Item {
GradientStop { position: 1; color: "black" }
}
- // Clip using scissor, 2 levels.
- // This means that the lightGreen-yellow-blue batch's clip list will have two clips.
+ // Clip using scissor, up to 2 levels. This means that the
+ // lightGreen-yellow-blue batch's clip list will have two clips.
Row {
spacing: 10
Repeater {
@@ -103,9 +103,9 @@ Item {
}
}
- // Clip using stencil, 2 levels.
- // This means that the lightGreen-yellow batch's clip list will have two clips
- // and so two stencil draw calls before drawing the actual content.
+ // Clip using stencil, up to 3 levels. This means that the
+ // lightGreen-yellow-blue batch's clip list will have three clips and
+ // so two stencil draw calls before drawing the actual content.
Row {
spacing: 10
Repeater {
@@ -133,6 +133,16 @@ Item {
x: 75
y: 75
NumberAnimation on rotation { from: 360; to: 0; duration: 5000; loops: Animation.Infinite; }
+ clip: true
+
+ Rectangle {
+ color: "blue"
+ width: 50
+ height: 50
+ x: 0
+ y: 0
+ NumberAnimation on rotation { from: 360; to: 0; duration: 5000; loops: Animation.Infinite; }
+ }
}
}
}
diff --git a/tests/manual/scenegraph_lancelot/data/shape/shape_multiline.qml b/tests/manual/scenegraph_lancelot/data/shape/shape_multiline.qml
new file mode 100644
index 0000000000..8524915bc4
--- /dev/null
+++ b/tests/manual/scenegraph_lancelot/data/shape/shape_multiline.qml
@@ -0,0 +1,74 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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 General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** 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-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.14
+import QtQuick.Shapes 1.14
+
+Item {
+ id: root
+ width: 320
+ height: 320
+
+ Shape {
+ vendorExtensionsEnabled: false
+ anchors.fill: parent
+
+ ShapePath {
+ strokeWidth: 1
+ strokeColor: "red"
+ fillColor: Qt.rgba(1,0,0,0.3)
+ scale: Qt.size(root.width - 1, root.height - 1)
+ PathMultiline {
+ paths: [
+ [Qt.point(0.5, 0.06698),
+ Qt.point(1, 0.93301),
+ Qt.point(0, 0.93301),
+ Qt.point(0.5, 0.06698)],
+
+ [Qt.point(0.5, 0.12472),
+ Qt.point(0.95, 0.90414),
+ Qt.point(0.05, 0.90414),
+ Qt.point(0.5, 0.12472)],
+
+ [Qt.point(0.47131, 0.32986),
+ Qt.point(0.36229, 0.64789),
+ Qt.point(0.51492, 0.58590),
+ Qt.point(0.47563, 0.76014),
+ Qt.point(0.44950, 0.73590),
+ Qt.point(0.46292, 0.83392),
+ Qt.point(0.52162, 0.75190),
+ Qt.point(0.48531, 0.76230),
+ Qt.point(0.57529, 0.53189),
+ Qt.point(0.41261, 0.59189),
+ Qt.point(0.53001, 0.32786),
+ Qt.point(0.47131, 0.32986)]
+ ]
+ }
+ }
+ }
+}
diff --git a/tools/qmllint/findunqualified.cpp b/tools/qmllint/findunqualified.cpp
index 3abdfcfa53..0d1cbf5823 100644
--- a/tools/qmllint/findunqualified.cpp
+++ b/tools/qmllint/findunqualified.cpp
@@ -38,6 +38,7 @@
#include <private/qqmljsast_p.h>
#include <private/qqmljslexer_p.h>
#include <private/qqmljsparser_p.h>
+#include <private/qv4codegen_p.h>
QDebug &operator<<(QDebug dbg, const QQmlJS::AST::SourceLocation &loc);
@@ -225,8 +226,7 @@ FindUnqualifiedIDVisitor::localQmlFile2FakeMetaObject(QString filePath)
break;
}
case UiObjectMember::Kind_UiEnumDeclaration: {
- auto enumDeclaration = static_cast<UiEnumDeclaration *>(initMembers->member);
- qDebug() << "enumdecl" << enumDeclaration->name;
+ // nothing to do
break;
}
case UiObjectMember::Kind_UiObjectBinding: {
@@ -235,8 +235,6 @@ FindUnqualifiedIDVisitor::localQmlFile2FakeMetaObject(QString filePath)
}
case UiObjectMember::Kind_UiObjectDefinition: {
// creates nothing accessible
- /*auto objectDefinition = static_cast<UiObjectDefinition*>(initMembers->member);
- qDebug() << "objdef" << objectDefinition->qualifiedTypeNameId->name;*/
break;
}
case UiObjectMember::Kind_UiPublicMember: {
@@ -325,7 +323,9 @@ void FindUnqualifiedIDVisitor::importExportedNames(QStringRef prefix, QString na
break;
}
} else {
- qDebug() << name << "not found";
+ m_colorOut.write(QLatin1String("warning: "), Warning);
+ m_colorOut.write(name + QLatin1String(" was not found. Did you add all import paths?\n"));
+ m_unknownImports.insert(name);
break;
}
}
@@ -503,7 +503,9 @@ bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::UiScriptBinding *uisb)
} else {
auto method = m_currentScope->methods()[signal];
for (auto const &param : method.parameterNames()) {
- m_currentScope->insertSignalIdentifier(param, method, uisb->statement->firstSourceLocation());
+ auto firstSourceLocation = uisb->statement->firstSourceLocation();
+ bool hasMultilineStatementBody = uisb->statement->lastSourceLocation().startLine > firstSourceLocation.startLine;
+ m_currentScope->insertSignalIdentifier(param, method, firstSourceLocation, hasMultilineStatementBody);
}
}
return true;
@@ -524,7 +526,7 @@ bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::IdentifierExpression *idexp)
auto name = idexp->name;
if (!m_exportedName2MetaObject.contains(name.toString())) {
m_currentScope->addIdToAccssedIfNotInParentScopes(
- { name.toString(), idexp->firstSourceLocation() });
+ { name.toString(), idexp->firstSourceLocation() }, m_unknownImports);
}
return true;
}
@@ -545,15 +547,19 @@ FindUnqualifiedIDVisitor::FindUnqualifiedIDVisitor(QStringList const &qmltypeDir
m_colorOut.insertColorMapping(Normal, ColorOutput::DefaultColor);
m_colorOut.insertColorMapping(Hint, ColorOutput::GreenForeground);
QLatin1String jsGlobVars[] = {
- QLatin1String ("Array"), QLatin1String("Boolean"), QLatin1String("Date"), QLatin1String("Function"), QLatin1String("Math"), QLatin1String("Number"), QLatin1String("Object"), QLatin1String("RegExp"), QLatin1String("String"),
- QLatin1String("Error"), QLatin1String("EvalError"), QLatin1String("RangeError"), QLatin1String("ReferenceError"), QLatin1String("SyntaxError"), QLatin1String("TypeError"), QLatin1String("URIError"),
- QLatin1String("encodeURI"), QLatin1String("encodeURIComponent"), QLatin1String("decodeURI"), QLatin1String("decodeURIComponent"), QLatin1String("escape"), QLatin1String("unescape"),
- QLatin1String("isFinite"), QLatin1String("isNanN"), QLatin1String("parseFloat"), QLatin1String("parseInt"),
- QLatin1String("eval"), QLatin1String("console"), QLatin1String("print"), QLatin1String("gc"),
+ /* Not listed on the MDN page; browser and QML extensions: */
+ // console/debug api
+ QLatin1String("console"), QLatin1String("print"),
+ // garbage collector
+ QLatin1String("gc"),
+ // i18n
QLatin1String("qsTr"), QLatin1String("qsTrId"), QLatin1String("QT_TR_NOOP"), QLatin1String("QT_TRANSLATE_NOOP"), QLatin1String("QT_TRID_NOOP"),
- QLatin1String("XMLHttpRequest"), QLatin1String("JSON"), QLatin1String("Promise"),
- QLatin1String("undefined")
+ // XMLHttpRequest
+ QLatin1String("XMLHttpRequest")
};
+ for (const char **globalName = QV4::Compiler::Codegen::s_globalNames; *globalName != nullptr; ++globalName) {
+ m_currentScope->insertJSIdentifier(QString::fromLatin1(*globalName), QQmlJS::AST::VariableScope::Const);
+ }
for (const auto& jsGlobVar: jsGlobVars)
m_currentScope->insertJSIdentifier(jsGlobVar, QQmlJS::AST::VariableScope::Const);
}
@@ -593,7 +599,6 @@ void FindUnqualifiedIDVisitor::visitFunctionExpressionHelper(QQmlJS::AST::Functi
m_currentScope->insertJSIdentifier(name, VariableScope::Const);
}
}
- // qDebug() << fexpr->firstSourceLocation() << "function expression" << fexpr->name;
QString name = fexpr->name.toString();
if (name.isEmpty())
name = "<anon>";
diff --git a/tools/qmllint/findunqualified.h b/tools/qmllint/findunqualified.h
index 937194d142..8fc8257bef 100644
--- a/tools/qmllint/findunqualified.h
+++ b/tools/qmllint/findunqualified.h
@@ -57,6 +57,7 @@ private:
QString m_rootId;
QString m_filePath;
QSet<QPair<QString, QString>> m_alreadySeenImports;
+ QSet<QString> m_unknownImports;
ColorOutput m_colorOut;
struct OutstandingConnection {QString targetName; ScopeTree *scope; QQmlJS::AST::UiObjectDefinition *uiod;};
diff --git a/tools/qmllint/scopetree.cpp b/tools/qmllint/scopetree.cpp
index b064117dd4..2eff3fa319 100644
--- a/tools/qmllint/scopetree.cpp
+++ b/tools/qmllint/scopetree.cpp
@@ -30,6 +30,8 @@
#include "qcoloroutput_p.h"
+#include <algorithm>
+
#include <QQueue>
ScopeTree::ScopeTree(ScopeType type, QString name, ScopeTree *parentScope)
@@ -66,10 +68,10 @@ void ScopeTree::insertQMLIdentifier(QString id)
m_currentScopeQMLIdentifiers.insert(id);
}
-void ScopeTree::insertSignalIdentifier(QString id, LanguageUtils::FakeMetaMethod method, QQmlJS::AST::SourceLocation loc)
+void ScopeTree::insertSignalIdentifier(QString id, LanguageUtils::FakeMetaMethod method, QQmlJS::AST::SourceLocation loc, bool hasMultilineHandlerBody)
{
Q_ASSERT(m_scopeType == ScopeType::QMLScope);
- m_injectedSignalIdentifiers.insert(id, {method, loc});
+ m_injectedSignalIdentifiers.insert(id, {method, loc, hasMultilineHandlerBody});
}
void ScopeTree::insertPropertyIdentifier(QString id)
@@ -84,8 +86,14 @@ bool ScopeTree::isIdInCurrentScope(const QString &id) const
return isIdInCurrentQMlScopes(id) || isIdInCurrentJSScopes(id);
}
-void ScopeTree::addIdToAccssedIfNotInParentScopes(const QPair<QString, QQmlJS::AST::SourceLocation> &id_loc_pair) {
- if (!isIdInCurrentScope(id_loc_pair.first)) {
+void ScopeTree::addIdToAccssedIfNotInParentScopes(const QPair<QString, QQmlJS::AST::SourceLocation> &id_loc_pair, const QSet<QString>& unknownImports) {
+ // also do not add id if it is parent
+ // parent is almost always defined valid in QML, and if we could not find a definition for the current QML component
+ // not skipping "parent" will lead to many false positives
+ // Moreover, if the top level item is Item or inherits from it, it will have a parent property to which we would point the user
+ // which makes for a very nonsensical warning
+ auto qmlScope = getCurrentQMLScope();
+ if (!isIdInCurrentScope(id_loc_pair.first) && !(id_loc_pair.first == QLatin1String("parent") && qmlScope && unknownImports.contains(qmlScope->name()))) {
m_accessedIdentifiers.push_back(id_loc_pair);
}
}
@@ -146,31 +154,37 @@ bool ScopeTree::recheckIdentifiers(const QString& code, const QHash<QString, Lan
while (parentScope && parentScope->scopeType() != ScopeType::QMLScope) {
parentScope = parentScope->m_parentScope;
}
- bool directChild = parentScope && parentScope->isVisualRootScope();
colorOut.write("Note: ", Info);
colorOut.write( idLocationPair.first + QLatin1String(" is a meber of the root element\n"), Normal );
- if (directChild) {
- colorOut.write(QLatin1String(" As the current element is a direct child of the root element,\n you can qualify the access with parent to avoid this warning:\n"), Normal);
- } else {
- colorOut.write(QLatin1String(" You can qualify the access with its id to avoid this warning:\n"), Normal);
- if (rootId == QLatin1String("<id>")) {
- colorOut.write("Note: ", Warning);
- colorOut.write(("You first have to give the root element an id\n"));
- }
+ colorOut.write(QLatin1String(" You can qualify the access with its id to avoid this warning:\n"), Normal);
+ if (rootId == QLatin1String("<id>")) {
+ colorOut.write("Note: ", Warning);
+ colorOut.write(("You first have to give the root element an id\n"));
}
colorOut.write(issueLocationWithContext.beforeText.toString(), Normal);
- colorOut.write( (directChild ? QLatin1String("parent") : rootId) + QLatin1Char('.'), Hint);
+ colorOut.write(rootId + QLatin1Char('.'), Hint);
colorOut.write(issueLocationWithContext.issueText.toString(), Normal);
colorOut.write(issueLocationWithContext.afterText + QLatin1Char('\n'), Normal);
} else if (currentScope->isIdInjectedFromSignal(idLocationPair.first)) {
auto qmlScope = currentScope->getCurrentQMLScope();
- MethodUsage methodUsage = qmlScope->m_injectedSignalIdentifiers[idLocationPair.first];
+ auto methodUsages = qmlScope->m_injectedSignalIdentifiers.values(idLocationPair.first);
+ auto location = idLocationPair.second;
+ // sort the list of signal handlers by their occurrence in the source code
+ // then, we select the first one whose location is after the unqualified id
+ // and go one step backwards to get the one which we actually need
+ std::sort(methodUsages.begin(), methodUsages.end(), [](const MethodUsage m1, const MethodUsage m2) {
+ return m1.loc.startLine < m2.loc.startLine || (m1.loc.startLine == m2.loc.startLine && m1.loc.startColumn < m2.loc.startColumn);
+ });
+ auto oneBehindIt = std::find_if(methodUsages.begin(), methodUsages.end(), [&location](MethodUsage methodUsage) {
+ return location.startLine < methodUsage.loc.startLine || (location.startLine == methodUsage.loc.startLine && location.startColumn < methodUsage.loc.startColumn);
+ });
+ auto methodUsage = *(--oneBehindIt);
colorOut.write("Note:", Info);
colorOut.write(idLocationPair.first + QString::asprintf(" is accessible in this scope because you are handling a signal at %d:%d\n", methodUsage.loc.startLine, methodUsage.loc.startColumn), Normal);
colorOut.write("Consider using a function instead\n", Normal);
IssueLocationWithContext context {code, methodUsage.loc};
colorOut.write(context.beforeText + QLatin1Char(' '));
- colorOut.write("function(", Hint);
+ colorOut.write(methodUsage.hasMultilineHandlerBody ? "function(" : "(", Hint);
const auto parameters = methodUsage.method.parameterNames();
for (int numParams = parameters.size(); numParams > 0; --numParams) {
colorOut.write(parameters.at(parameters.size() - numParams), Hint);
@@ -178,10 +192,10 @@ bool ScopeTree::recheckIdentifiers(const QString& code, const QHash<QString, Lan
colorOut.write(", ", Hint);
}
}
- colorOut.write(")", Hint);
+ colorOut.write(methodUsage.hasMultilineHandlerBody ? ")" : ") => ", Hint);
colorOut.write(" {...", Normal);
}
- colorOut.write("\n", Normal);
+ colorOut.write("\n\n\n", Normal);
}
for (auto const& childScope: currentScope->m_childScopes) {
workQueue.enqueue(childScope);
diff --git a/tools/qmllint/scopetree.h b/tools/qmllint/scopetree.h
index cb495584c1..872a509123 100644
--- a/tools/qmllint/scopetree.h
+++ b/tools/qmllint/scopetree.h
@@ -56,6 +56,7 @@ struct MethodUsage
{
LanguageUtils::FakeMetaMethod method;
QQmlJS::AST::SourceLocation loc;
+ bool hasMultilineHandlerBody;
};
class ColorOutput;
@@ -70,11 +71,11 @@ public:
void insertJSIdentifier(QString id, QQmlJS::AST::VariableScope scope);
void insertQMLIdentifier(QString id);
- void insertSignalIdentifier(QString id, LanguageUtils::FakeMetaMethod method, QQmlJS::AST::SourceLocation loc);
+ void insertSignalIdentifier(QString id, LanguageUtils::FakeMetaMethod method, QQmlJS::AST::SourceLocation loc, bool hasMultilineHandlerBody);
void insertPropertyIdentifier(QString id); // inserts property as qml identifier as well as the corresponding
bool isIdInCurrentScope(QString const &id) const;
- void addIdToAccssedIfNotInParentScopes(QPair<QString, QQmlJS::AST::SourceLocation> const& id_loc_pair);
+ void addIdToAccssedIfNotInParentScopes(QPair<QString, QQmlJS::AST::SourceLocation> const& id_loc_pair, const QSet<QString>& unknownImports);
bool isVisualRootScope() const;
QString name() const;
@@ -88,7 +89,7 @@ public:
private:
QSet<QString> m_currentScopeJSIdentifiers;
QSet<QString> m_currentScopeQMLIdentifiers;
- QHash<QString, MethodUsage> m_injectedSignalIdentifiers; // need iteration in insertion order for hints, rarely more than 4 parameters needed
+ QMultiHash<QString, MethodUsage> m_injectedSignalIdentifiers;
QMap<QString, LanguageUtils::FakeMetaMethod> m_methods;
QVector<QPair<QString, QQmlJS::AST::SourceLocation>> m_accessedIdentifiers;
QVector<ScopeTree*> m_childScopes;