aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick
diff options
context:
space:
mode:
Diffstat (limited to 'src/quick')
-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
45 files changed, 2256 insertions, 562 deletions
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() {}