aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--config.tests/d3d12/d3d12.cpp51
-rw-r--r--config.tests/d3d12/d3d12.pro4
-rw-r--r--examples/quick/quick.pro8
-rw-r--r--examples/quick/scenegraph/customgeometry/beziercurve.cpp2
-rw-r--r--examples/quick/scenegraph/rendernode/customrenderitem.cpp107
-rw-r--r--examples/quick/scenegraph/rendernode/customrenderitem.h78
-rw-r--r--examples/quick/scenegraph/rendernode/d3d12renderer.cpp229
-rw-r--r--examples/quick/scenegraph/rendernode/d3d12renderer.h77
-rw-r--r--examples/quick/scenegraph/rendernode/main.cpp58
-rw-r--r--examples/quick/scenegraph/rendernode/main.qml97
-rw-r--r--examples/quick/scenegraph/rendernode/openglrenderer.cpp133
-rw-r--r--examples/quick/scenegraph/rendernode/openglrenderer.h70
-rw-r--r--examples/quick/scenegraph/rendernode/rendernode.pro37
-rw-r--r--examples/quick/scenegraph/rendernode/rendernode.qrc5
-rw-r--r--examples/quick/scenegraph/rendernode/shader.hlsl27
-rw-r--r--examples/quick/scenegraph/scenegraph.pro23
-rw-r--r--features/hlsl_bytecode_header.prf10
-rw-r--r--qtdeclarative.pro3
-rw-r--r--src/imports/imports.pro6
-rw-r--r--src/particles/qquickcustomparticle.cpp25
-rw-r--r--src/particles/qquickcustomparticle_p.h18
-rw-r--r--src/plugins/plugins.pro2
-rw-r--r--src/plugins/scenegraph/d3d12/d3d12.json3
-rw-r--r--src/plugins/scenegraph/d3d12/d3d12.pro55
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12adaptation.cpp76
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12adaptation_p.h81
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12builtinmaterials.cpp651
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12builtinmaterials_p.h236
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12context.cpp124
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12context_p.h80
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp3075
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12engine_p.h370
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12engine_p_p.h445
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12glyphcache.cpp184
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12glyphcache_p.h88
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12glyphnode.cpp90
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12glyphnode_p.h74
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12imagenode.cpp123
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12imagenode_p.h82
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12layer.cpp363
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12layer_p.h118
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12material.cpp106
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12material_p.h125
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12painternode.cpp256
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12painternode_p.h119
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12rectanglenode.cpp72
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12rectanglenode_p.h74
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12rendercontext.cpp129
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12rendercontext_p.h83
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12renderer.cpp784
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12renderer_p.h137
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12renderloop.cpp1167
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12renderloop_p.h129
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode.cpp977
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode_p.h172
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12texture.cpp133
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12texture_p.h89
-rw-r--r--src/plugins/scenegraph/d3d12/shaders/mipmapgen.hlsl60
-rw-r--r--src/plugins/scenegraph/d3d12/shaders/shadereffectdefault.hlsl27
-rw-r--r--src/plugins/scenegraph/d3d12/shaders/shaders.pri119
-rw-r--r--src/plugins/scenegraph/d3d12/shaders/smoothcolor.hlsl64
-rw-r--r--src/plugins/scenegraph/d3d12/shaders/smoothtexture.hlsl77
-rw-r--r--src/plugins/scenegraph/d3d12/shaders/stencilclip.hlsl26
-rw-r--r--src/plugins/scenegraph/d3d12/shaders/tdr.hlsl11
-rw-r--r--src/plugins/scenegraph/d3d12/shaders/textmask.hlsl104
-rw-r--r--src/plugins/scenegraph/d3d12/shaders/texture.hlsl33
-rw-r--r--src/plugins/scenegraph/d3d12/shaders/vertexcolor.hlsl32
-rw-r--r--src/plugins/scenegraph/scenegraph.pro2
-rw-r--r--src/quick/designer/qquickdesignersupport.cpp2
-rw-r--r--src/quick/designer/qquickdesignerwindowmanager.cpp11
-rw-r--r--src/quick/designer/qquickdesignerwindowmanager_p.h7
-rw-r--r--src/quick/doc/src/concepts/visualcanvas/adaptations.qdoc86
-rw-r--r--src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc12
-rw-r--r--src/quick/doc/src/concepts/visualcanvas/topic.qdoc15
-rw-r--r--src/quick/items/context2d/qquickcanvasitem.cpp75
-rw-r--r--src/quick/items/context2d/qquickcontext2d.cpp45
-rw-r--r--src/quick/items/context2d/qquickcontext2dcommandbuffer.cpp6
-rw-r--r--src/quick/items/context2d/qquickcontext2dtexture.cpp17
-rw-r--r--src/quick/items/context2d/qquickcontext2dtexture_p.h18
-rw-r--r--src/quick/items/context2d/qquickcontext2dtile.cpp13
-rw-r--r--src/quick/items/context2d/qquickcontext2dtile_p.h9
-rw-r--r--src/quick/items/items.pri83
-rw-r--r--src/quick/items/qquickframebufferobject.cpp2
-rw-r--r--src/quick/items/qquickgenericshadereffect.cpp639
-rw-r--r--src/quick/items/qquickgenericshadereffect_p.h150
-rw-r--r--src/quick/items/qquickitem.h3
-rw-r--r--src/quick/items/qquickitem_p.h1
-rw-r--r--src/quick/items/qquickitemgrabresult.cpp4
-rw-r--r--src/quick/items/qquickitemsmodule.cpp34
-rw-r--r--src/quick/items/qquickopenglshadereffect.cpp862
-rw-r--r--src/quick/items/qquickopenglshadereffect_p.h176
-rw-r--r--src/quick/items/qquickopenglshadereffectnode.cpp (renamed from src/quick/items/qquickshadereffectnode.cpp)95
-rw-r--r--src/quick/items/qquickopenglshadereffectnode_p.h (renamed from src/quick/items/qquickshadereffectnode_p.h)44
-rw-r--r--src/quick/items/qquickpainteditem.cpp29
-rw-r--r--src/quick/items/qquickrendercontrol.cpp29
-rw-r--r--src/quick/items/qquickrendererinfo.cpp129
-rw-r--r--src/quick/items/qquickrendererinfo_p.h100
-rw-r--r--src/quick/items/qquickshadereffect.cpp1254
-rw-r--r--src/quick/items/qquickshadereffect_p.h150
-rw-r--r--src/quick/items/qquickshadereffectmesh.cpp112
-rw-r--r--src/quick/items/qquickshadereffectmesh_p.h22
-rw-r--r--src/quick/items/qquickshadereffectsource.cpp3
-rw-r--r--src/quick/items/qquickshadereffectsource_p.h9
-rw-r--r--src/quick/items/qquickspriteengine.cpp5
-rw-r--r--src/quick/items/qquicktextnodeengine.cpp2
-rw-r--r--src/quick/items/qquickwindow.cpp228
-rw-r--r--src/quick/items/qquickwindow.h11
-rw-r--r--src/quick/scenegraph/adaptations/adaptations.pri1
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer.cpp321
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer_p.h109
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwareadaptation.cpp81
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwareadaptation_p.h77
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarecontext.cpp182
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarecontext_p.h103
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwareglyphnode.cpp120
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwareglyphnode_p.h84
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwareimagenode.cpp503
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwareimagenode_p.h147
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarelayer.cpp261
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarelayer_p.h121
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwareninepatchnode.cpp109
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwareninepatchnode_p.h82
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarepainternode.cpp231
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarepainternode_p.h132
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaprenderer.cpp111
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaprenderer_p.h76
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaptexture.cpp82
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaptexture_p.h79
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarerectanglenode.cpp451
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarerectanglenode_p.h103
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp328
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode_p.h140
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater.cpp275
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater_p.h137
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer.cpp126
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer_p.h80
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarerenderlistbuilder.cpp163
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarerenderlistbuilder_p.h94
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop.cpp265
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop_p.h105
-rw-r--r--src/quick/scenegraph/adaptations/software/software.pri39
-rw-r--r--src/quick/scenegraph/coreapi/qsgabstractrenderer.h4
-rw-r--r--src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp70
-rw-r--r--src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h13
-rw-r--r--src/quick/scenegraph/coreapi/qsggeometry.cpp120
-rw-r--r--src/quick/scenegraph/coreapi/qsggeometry.h77
-rw-r--r--src/quick/scenegraph/coreapi/qsgmaterial.cpp28
-rw-r--r--src/quick/scenegraph/coreapi/qsgmaterial.h25
-rw-r--r--src/quick/scenegraph/coreapi/qsgmaterialshader_p.h2
-rw-r--r--src/quick/scenegraph/coreapi/qsgnode.cpp8
-rw-r--r--src/quick/scenegraph/coreapi/qsgnode.h5
-rw-r--r--src/quick/scenegraph/coreapi/qsgnodeupdater.cpp7
-rw-r--r--src/quick/scenegraph/coreapi/qsgrenderer.cpp29
-rw-r--r--src/quick/scenegraph/coreapi/qsgrenderer_p.h8
-rw-r--r--src/quick/scenegraph/coreapi/qsgrendererinterface.cpp128
-rw-r--r--src/quick/scenegraph/coreapi/qsgrendererinterface.h73
-rw-r--r--src/quick/scenegraph/coreapi/qsgrendernode.cpp236
-rw-r--r--src/quick/scenegraph/coreapi/qsgrendernode.h94
-rw-r--r--src/quick/scenegraph/coreapi/qsgrendernode_p.h51
-rw-r--r--src/quick/scenegraph/qsgadaptationlayer.cpp46
-rw-r--r--src/quick/scenegraph/qsgadaptationlayer_p.h151
-rw-r--r--src/quick/scenegraph/qsgbasicglyphnode.cpp95
-rw-r--r--src/quick/scenegraph/qsgbasicglyphnode_p.h92
-rw-r--r--src/quick/scenegraph/qsgbasicimagenode.cpp559
-rw-r--r--src/quick/scenegraph/qsgbasicimagenode_p.h109
-rw-r--r--src/quick/scenegraph/qsgbasicrectanglenode.cpp687
-rw-r--r--src/quick/scenegraph/qsgbasicrectanglenode_p.h99
-rw-r--r--src/quick/scenegraph/qsgcontext.cpp498
-rw-r--r--src/quick/scenegraph/qsgcontext_p.h74
-rw-r--r--src/quick/scenegraph/qsgcontextplugin.cpp154
-rw-r--r--src/quick/scenegraph/qsgcontextplugin_p.h9
-rw-r--r--src/quick/scenegraph/qsgdefaultcontext.cpp254
-rw-r--r--src/quick/scenegraph/qsgdefaultcontext_p.h93
-rw-r--r--src/quick/scenegraph/qsgdefaultglyphnode.cpp59
-rw-r--r--src/quick/scenegraph/qsgdefaultglyphnode_p.cpp3
-rw-r--r--src/quick/scenegraph/qsgdefaultglyphnode_p.h31
-rw-r--r--src/quick/scenegraph/qsgdefaultimagenode.cpp522
-rw-r--r--src/quick/scenegraph/qsgdefaultimagenode_p.h51
-rw-r--r--src/quick/scenegraph/qsgdefaultlayer.cpp8
-rw-r--r--src/quick/scenegraph/qsgdefaultlayer_p.h10
-rw-r--r--src/quick/scenegraph/qsgdefaultrectanglenode.cpp653
-rw-r--r--src/quick/scenegraph/qsgdefaultrectanglenode_p.h36
-rw-r--r--src/quick/scenegraph/qsgdefaultrendercontext.cpp302
-rw-r--r--src/quick/scenegraph/qsgdefaultrendercontext_p.h112
-rw-r--r--src/quick/scenegraph/qsgrenderloop.cpp43
-rw-r--r--src/quick/scenegraph/qsgrenderloop_p.h6
-rw-r--r--src/quick/scenegraph/qsgthreadedrenderloop.cpp9
-rw-r--r--src/quick/scenegraph/qsgwindowsrenderloop.cpp17
-rw-r--r--src/quick/scenegraph/qsgwindowsrenderloop_p.h5
-rw-r--r--src/quick/scenegraph/scenegraph.pri255
-rw-r--r--src/quick/scenegraph/util/qsgatlastexture.cpp1
-rw-r--r--src/quick/scenegraph/util/qsgatlastexture_p.h10
-rw-r--r--src/quick/scenegraph/util/qsgdefaultpainternode.cpp3
-rw-r--r--src/quick/scenegraph/util/qsgdefaultpainternode_p.h3
-rw-r--r--src/quick/scenegraph/util/qsgdistancefieldutil.cpp4
-rw-r--r--src/quick/scenegraph/util/qsgengine.cpp33
-rw-r--r--src/quick/scenegraph/util/qsgengine.h2
-rw-r--r--src/quick/scenegraph/util/qsgflatcolormaterial.cpp16
-rw-r--r--src/quick/scenegraph/util/qsgsimplematerial.h9
-rw-r--r--src/quick/scenegraph/util/qsgtexture.cpp28
-rw-r--r--src/quick/scenegraph/util/qsgtexture_p.h8
-rw-r--r--src/quick/scenegraph/util/qsgtexturematerial.cpp26
-rw-r--r--src/quick/scenegraph/util/qsgvertexcolormaterial.cpp14
-rw-r--r--src/quick/util/qquickanimator.cpp3
-rw-r--r--src/quick/util/qquickanimator_p.h6
-rw-r--r--src/quick/util/qquickanimatorjob.cpp32
-rw-r--r--src/quick/util/qquickanimatorjob_p.h7
-rw-r--r--src/quick/util/qquickutilmodule.cpp3
-rw-r--r--src/quickwidgets/qquickwidget.cpp247
-rw-r--r--src/quickwidgets/qquickwidget.h39
-rw-r--r--src/quickwidgets/qquickwidget_p.h4
-rw-r--r--src/src.pro10
-rw-r--r--tests/auto/auto.pro8
-rw-r--r--tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp5
-rw-r--r--tests/auto/qml/qmlplugindump/tst_qmlplugindump.cpp1
-rw-r--r--tests/auto/qml/qqmlenginecleanup/data/types.qml3
-rw-r--r--tests/auto/quick/examples/tst_examples.cpp12
-rw-r--r--tests/auto/quick/geometry/tst_geometry.cpp6
-rw-r--r--tests/auto/quick/nodes/tst_nodestest.cpp8
-rw-r--r--tests/auto/quick/nokeywords/tst_nokeywords.cpp2
-rw-r--r--tests/auto/quick/qquickborderimage/tst_qquickborderimage.cpp6
-rw-r--r--tests/auto/quick/qquickflickable/tst_qquickflickable.cpp2
-rw-r--r--tests/auto/quick/qquickimage/tst_qquickimage.cpp5
-rw-r--r--tests/auto/quick/qquickitem2/tst_qquickitem.cpp4
-rw-r--r--tests/auto/quick/qquickitemlayer/tst_qquickitemlayer.cpp13
-rw-r--r--tests/auto/quick/qquickpainteditem/tst_qquickpainteditem.cpp16
-rw-r--r--tests/auto/quick/qquickrendererinfo/data/basic.qml5
-rw-r--r--tests/auto/quick/qquickrendererinfo/qquickrendererinfo.pro14
-rw-r--r--tests/auto/quick/qquickrendererinfo/tst_qquickrendererinfo.cpp68
-rw-r--r--tests/auto/quick/qquickshadereffect/tst_qquickshadereffect.cpp5
-rw-r--r--tests/auto/quick/qquickwindow/tst_qquickwindow.cpp109
-rw-r--r--tests/auto/quick/quick.pro20
-rw-r--r--tests/auto/quick/rendernode/tst_rendernode.cpp17
-rw-r--r--tests/auto/quick/scenegraph/tst_scenegraph.cpp47
-rw-r--r--tests/benchmarks/benchmarks.pro2
-rw-r--r--tests/manual/nodetypes/Animators.qml180
-rw-r--r--tests/manual/nodetypes/Effects.qml187
-rw-r--r--tests/manual/nodetypes/Images.qml97
-rw-r--r--tests/manual/nodetypes/Layers.qml106
-rw-r--r--tests/manual/nodetypes/LotsOfRects.qml250
-rw-r--r--tests/manual/nodetypes/Painter.qml84
-rw-r--r--tests/manual/nodetypes/Rects.qml137
-rw-r--r--tests/manual/nodetypes/Text.qml71
-rw-r--r--tests/manual/nodetypes/face-smile.pngbin0 -> 15408 bytes
-rw-r--r--tests/manual/nodetypes/hlslcompile.bat4
-rw-r--r--tests/manual/nodetypes/main.qml81
-rw-r--r--tests/manual/nodetypes/nodetypes.cpp205
-rw-r--r--tests/manual/nodetypes/nodetypes.pro9
-rw-r--r--tests/manual/nodetypes/nodetypes.qrc20
-rw-r--r--tests/manual/nodetypes/ps_shadow1.csobin0 -> 1600 bytes
-rw-r--r--tests/manual/nodetypes/ps_shadow2.csobin0 -> 1436 bytes
-rw-r--r--tests/manual/nodetypes/ps_wobble.csobin0 -> 1272 bytes
-rw-r--r--tests/manual/nodetypes/qt.pngbin0 -> 11917 bytes
-rw-r--r--tests/manual/nodetypes/shadow.pngbin0 -> 425 bytes
-rw-r--r--tests/manual/nodetypes/shadow1.hlsl18
-rw-r--r--tests/manual/nodetypes/shadow2.hlsl22
-rw-r--r--tests/manual/nodetypes/vs_wobble.csobin0 -> 1184 bytes
-rw-r--r--tests/manual/nodetypes/wobble.hlsl32
-rw-r--r--tools/qmleasing/mainwindow.cpp1
-rw-r--r--tools/qmlscene/main.cpp8
261 files changed, 27029 insertions, 3696 deletions
diff --git a/.gitignore b/.gitignore
index e949cddb22..f33da3c8b3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -282,3 +282,5 @@ src/qml/RegExpJitTables.h
src/qml/udis86_itab.c
src/qml/udis86_itab.h
+# Generated HLSL bytecode headers
+*.hlslh
diff --git a/config.tests/d3d12/d3d12.cpp b/config.tests/d3d12/d3d12.cpp
new file mode 100644
index 0000000000..0bf90cc457
--- /dev/null
+++ b/config.tests/d3d12/d3d12.cpp
@@ -0,0 +1,51 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <d3d12.h>
+#include <dxgi1_4.h>
+#include <wrl/client.h>
+
+using namespace Microsoft::WRL;
+
+int main(int, char **)
+{
+ ID3D12Device *dev = 0;
+
+ return 0;
+}
diff --git a/config.tests/d3d12/d3d12.pro b/config.tests/d3d12/d3d12.pro
new file mode 100644
index 0000000000..24c5991a4b
--- /dev/null
+++ b/config.tests/d3d12/d3d12.pro
@@ -0,0 +1,4 @@
+SOURCES = d3d12.cpp
+CONFIG -= qt dylib
+CONFIG += console
+LIBS += -ldxgi -ld3d12
diff --git a/examples/quick/quick.pro b/examples/quick/quick.pro
index 63730e53da..1b53620c89 100644
--- a/examples/quick/quick.pro
+++ b/examples/quick/quick.pro
@@ -25,8 +25,12 @@ SUBDIRS = quick-accessibility \
imageresponseprovider \
window \
particles \
- demos \
- rendercontrol
+ demos
+
+#OpenGL Support Required
+contains(QT_CONFIG, opengl(es1|es2)?) {
+ SUBDIRS += rendercontrol
+}
# Widget dependent examples
qtHaveModule(widgets) {
diff --git a/examples/quick/scenegraph/customgeometry/beziercurve.cpp b/examples/quick/scenegraph/customgeometry/beziercurve.cpp
index 14e78f8d21..ca3c6f524b 100644
--- a/examples/quick/scenegraph/customgeometry/beziercurve.cpp
+++ b/examples/quick/scenegraph/customgeometry/beziercurve.cpp
@@ -135,7 +135,7 @@ QSGNode *BezierCurve::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
//! [4] //! [5]
geometry = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), m_segmentCount);
geometry->setLineWidth(2);
- geometry->setDrawingMode(GL_LINE_STRIP);
+ geometry->setDrawingMode(QSGGeometry::DrawLineStrip);
node->setGeometry(geometry);
node->setFlag(QSGNode::OwnsGeometry);
//! [5] //! [6]
diff --git a/examples/quick/scenegraph/rendernode/customrenderitem.cpp b/examples/quick/scenegraph/rendernode/customrenderitem.cpp
new file mode 100644
index 0000000000..dbb9ee79e8
--- /dev/null
+++ b/examples/quick/scenegraph/rendernode/customrenderitem.cpp
@@ -0,0 +1,107 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "customrenderitem.h"
+#include <QQuickWindow>
+#include <QSGRendererInterface>
+
+#include "openglrenderer.h"
+#include "d3d12renderer.h"
+
+CustomRenderNode::~CustomRenderNode()
+{
+ releaseResources();
+}
+
+void CustomRenderNode::render(const RenderState *state)
+{
+ QSGRendererInterface *ri = m_item->window()->rendererInterface();
+ if (!ri)
+ return;
+
+ if (!m_renderer) {
+ switch (ri->graphicsAPI()) {
+ case QSGRendererInterface::OpenGL:
+#ifndef QT_NO_OPENGL
+ m_renderer = new OpenGLRenderer(m_item, this);
+#endif
+ break;
+ case QSGRendererInterface::Direct3D12:
+#ifdef HAS_D3D12
+ m_renderer = new D3D12Renderer(m_item, this);
+#endif
+ break;
+ default:
+ break;
+ }
+ Q_ASSERT(m_renderer);
+ m_renderer->init();
+ }
+
+ m_renderer->render(state);
+}
+
+// No need to reimplement changedStates() since our rendering is so simple,
+// without involving any state changes.
+
+void CustomRenderNode::releaseResources()
+{
+ if (!m_renderer)
+ return;
+
+ delete m_renderer;
+ m_renderer = nullptr;
+}
+
+CustomRenderItem::CustomRenderItem(QQuickItem *parent)
+ : QQuickItem(parent)
+{
+ // Our item shows something so set the flag.
+ setFlag(ItemHasContents);
+}
+
+QSGNode *CustomRenderItem::updatePaintNode(QSGNode *node, UpdatePaintNodeData *)
+{
+ CustomRenderNode *n = static_cast<CustomRenderNode *>(node);
+ if (!node)
+ n = new CustomRenderNode(this);
+
+ return n;
+}
diff --git a/examples/quick/scenegraph/rendernode/customrenderitem.h b/examples/quick/scenegraph/rendernode/customrenderitem.h
new file mode 100644
index 0000000000..b9828adaeb
--- /dev/null
+++ b/examples/quick/scenegraph/rendernode/customrenderitem.h
@@ -0,0 +1,78 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef CUSTOMRENDERITEM_H
+#define CUSTOMRENDERITEM_H
+
+#include <QQuickItem>
+#include <QSGRenderNode>
+
+class CustomRenderer
+{
+public:
+ virtual ~CustomRenderer() { }
+ virtual void init() = 0;
+ virtual void render(const QSGRenderNode::RenderState *state) = 0;
+};
+
+class CustomRenderNode : public QSGRenderNode
+{
+public:
+ CustomRenderNode(QQuickItem *item) : m_item(item) { }
+ ~CustomRenderNode();
+
+ void render(const RenderState *state) override;
+ void releaseResources() override;
+
+private:
+ QQuickItem *m_item;
+ CustomRenderer *m_renderer = nullptr;
+};
+
+class CustomRenderItem : public QQuickItem
+{
+ Q_OBJECT
+
+public:
+ CustomRenderItem(QQuickItem *parent = nullptr);
+ QSGNode *updatePaintNode(QSGNode *node, UpdatePaintNodeData *) override;
+};
+
+#endif
diff --git a/examples/quick/scenegraph/rendernode/d3d12renderer.cpp b/examples/quick/scenegraph/rendernode/d3d12renderer.cpp
new file mode 100644
index 0000000000..a48719e5dd
--- /dev/null
+++ b/examples/quick/scenegraph/rendernode/d3d12renderer.cpp
@@ -0,0 +1,229 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "d3d12renderer.h"
+#include <QQuickWindow>
+#include <QSGRendererInterface>
+
+#ifdef HAS_D3D12
+
+#include "vs_shader.hlslh"
+#include "ps_shader.hlslh"
+
+D3D12Renderer::D3D12Renderer(QQuickItem *item, QSGRenderNode *node)
+ : m_item(item),
+ m_node(node),
+ vbPtr(nullptr),
+ cbPtr(nullptr)
+{
+}
+
+void D3D12Renderer::init()
+{
+ QSGRendererInterface *rif = m_item->window()->rendererInterface();
+ m_device = static_cast<ID3D12Device *>(rif->getResource(QSGRendererInterface::Device));
+ Q_ASSERT(m_device);
+
+ D3D12_ROOT_PARAMETER rootParameter;
+ rootParameter.ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
+ rootParameter.ShaderVisibility = D3D12_SHADER_VISIBILITY_VERTEX;
+ rootParameter.Descriptor.ShaderRegister = 0; // b0
+ rootParameter.Descriptor.RegisterSpace = 0;
+
+ D3D12_ROOT_SIGNATURE_DESC desc;
+ desc.NumParameters = 1;
+ desc.pParameters = &rootParameter;
+ desc.NumStaticSamplers = 0;
+ desc.pStaticSamplers = nullptr;
+ desc.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT;
+
+ ComPtr<ID3DBlob> signature;
+ ComPtr<ID3DBlob> error;
+ if (FAILED(D3D12SerializeRootSignature(&desc, D3D_ROOT_SIGNATURE_VERSION_1, &signature, &error))) {
+ qWarning("Failed to serialize root signature");
+ return;
+ }
+ if (FAILED(m_device->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(),
+ IID_PPV_ARGS(&rootSignature)))) {
+ qWarning("Failed to create root signature");
+ return;
+ }
+
+ D3D12_INPUT_ELEMENT_DESC inputElementDescs[] = {
+ { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
+ { "COLOR", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 8, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }
+ };
+
+ D3D12_SHADER_BYTECODE vshader;
+ vshader.pShaderBytecode = g_VS_Simple;
+ vshader.BytecodeLength = sizeof(g_VS_Simple);
+ D3D12_SHADER_BYTECODE pshader;
+ pshader.pShaderBytecode = g_PS_Simple;
+ pshader.BytecodeLength = sizeof(g_PS_Simple);
+
+ D3D12_RASTERIZER_DESC rastDesc = {};
+ rastDesc.FillMode = D3D12_FILL_MODE_SOLID;
+ rastDesc.CullMode = D3D12_CULL_MODE_BACK;
+ rastDesc.FrontCounterClockwise = TRUE; // Vertices are given CCW
+
+ // No blending, just enable color write.
+ D3D12_RENDER_TARGET_BLEND_DESC defaultRenderTargetBlendDesc = {};
+ defaultRenderTargetBlendDesc.RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_ALL;
+ D3D12_BLEND_DESC blendDesc = {};
+ blendDesc.RenderTarget[0] = defaultRenderTargetBlendDesc;
+
+ D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc = {};
+ psoDesc.InputLayout = { inputElementDescs, _countof(inputElementDescs) };
+ psoDesc.pRootSignature = rootSignature.Get();
+ psoDesc.VS = vshader;
+ psoDesc.PS = pshader;
+ psoDesc.RasterizerState = rastDesc;
+ psoDesc.BlendState = blendDesc;
+ // No depth. The correct stacking of the item is ensured by the projection matrix.
+ // Do not bother with stencil since we do not apply clipping in the
+ // example. If clipping is desired, render() needs to set a different PSO
+ // with stencil enabled whenever the RenderState indicates so.
+ psoDesc.SampleMask = UINT_MAX;
+ psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
+ psoDesc.NumRenderTargets = 1;
+ psoDesc.RTVFormats[0] = DXGI_FORMAT_R8G8B8A8_UNORM;
+ psoDesc.DSVFormat = DXGI_FORMAT_D24_UNORM_S8_UINT; // not in use due to !DepthEnable, but this would be the correct format otherwise
+ psoDesc.SampleDesc.Count = 1;
+ if (FAILED(m_device->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(&pipelineState)))) {
+ qWarning("Failed to create graphics pipeline state");
+ return;
+ }
+
+ const UINT vertexBufferSize = (2 + 3) * 3 * sizeof(float);
+
+ D3D12_HEAP_PROPERTIES heapProp = {};
+ heapProp.Type = D3D12_HEAP_TYPE_UPLOAD;
+
+ D3D12_RESOURCE_DESC bufDesc;
+ bufDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
+ bufDesc.Alignment = 0;
+ bufDesc.Width = vertexBufferSize;
+ bufDesc.Height = 1;
+ bufDesc.DepthOrArraySize = 1;
+ bufDesc.MipLevels = 1;
+ bufDesc.Format = DXGI_FORMAT_UNKNOWN;
+ bufDesc.SampleDesc.Count = 1;
+ bufDesc.SampleDesc.Quality = 0;
+ bufDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
+ bufDesc.Flags = D3D12_RESOURCE_FLAG_NONE;
+
+ if (FAILED(m_device->CreateCommittedResource(&heapProp, D3D12_HEAP_FLAG_NONE, &bufDesc,
+ D3D12_RESOURCE_STATE_GENERIC_READ, nullptr,
+ IID_PPV_ARGS(&vertexBuffer)))) {
+ qWarning("Failed to create committed resource (vertex buffer)");
+ return;
+ }
+
+ vertexBufferView.BufferLocation = vertexBuffer->GetGPUVirtualAddress();
+ vertexBufferView.StrideInBytes = (2 + 3) * sizeof(float);
+ vertexBufferView.SizeInBytes = vertexBufferSize;
+
+ bufDesc.Width = 256;
+ if (FAILED(m_device->CreateCommittedResource(&heapProp, D3D12_HEAP_FLAG_NONE, &bufDesc,
+ D3D12_RESOURCE_STATE_GENERIC_READ, nullptr,
+ IID_PPV_ARGS(&constantBuffer)))) {
+ qWarning("Failed to create committed resource (constant buffer)");
+ return;
+ }
+
+ const D3D12_RANGE readRange = { 0, 0 };
+ if (FAILED(vertexBuffer->Map(0, &readRange, reinterpret_cast<void **>(&vbPtr)))) {
+ qWarning("Map failed");
+ return;
+ }
+
+ if (FAILED(constantBuffer->Map(0, &readRange, reinterpret_cast<void **>(&cbPtr)))) {
+ qWarning("Map failed (constant buffer)");
+ return;
+ }
+}
+
+D3D12Renderer::~D3D12Renderer()
+{
+ if (vbPtr)
+ vertexBuffer->Unmap(0, nullptr);
+ if (cbPtr)
+ constantBuffer->Unmap(0, nullptr);
+}
+
+void D3D12Renderer::render(const QSGRenderNode::RenderState *state)
+{
+ QSGRendererInterface *rif = m_item->window()->rendererInterface();
+ ID3D12GraphicsCommandList *commandList = static_cast<ID3D12GraphicsCommandList *>(rif->getResource(QSGRendererInterface::CommandList));
+ Q_ASSERT(commandList);
+
+ const int msize = 16 * sizeof(float);
+ memcpy(cbPtr, m_node->matrix()->constData(), msize);
+ memcpy(cbPtr + msize, state->projectionMatrix()->constData(), msize);
+
+ const QPointF p0(m_item->width() - 1, m_item->height() - 1);
+ const QPointF p1(0, 0);
+ const QPointF p2(0, m_item->height() - 1);
+
+ float *vp = reinterpret_cast<float *>(vbPtr);
+ *vp++ = p0.x();
+ *vp++ = p0.y();
+ *vp++ = 1.0f; *vp++ = 0.0f; *vp++ = 0.0f;
+
+ *vp++ = p1.x();
+ *vp++ = p1.y();
+ *vp++ = 0.0f; *vp++ = 1.0f; *vp++ = 0.0f;
+
+ *vp++ = p2.x();
+ *vp++ = p2.y();
+ *vp++ = 0.0f; *vp++ = 0.0f; *vp++ = 1.0f;
+
+ commandList->SetPipelineState(pipelineState.Get());
+ commandList->SetGraphicsRootSignature(rootSignature.Get());
+ commandList->SetGraphicsRootConstantBufferView(0, constantBuffer->GetGPUVirtualAddress());
+ commandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
+ commandList->IASetVertexBuffers(0, 1, &vertexBufferView);
+
+ commandList->DrawInstanced(3, 1, 0, 0);
+
+ // we won't implement changedStates() since no viewport/scissor/stencil/blend related commands were added
+}
+
+#endif // HAS_D3D12
diff --git a/examples/quick/scenegraph/rendernode/d3d12renderer.h b/examples/quick/scenegraph/rendernode/d3d12renderer.h
new file mode 100644
index 0000000000..365b483422
--- /dev/null
+++ b/examples/quick/scenegraph/rendernode/d3d12renderer.h
@@ -0,0 +1,77 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef D3D12RENDERER_H
+#define D3D12RENDERER_H
+
+#include "customrenderitem.h"
+#include <qsgrendernode.h>
+
+#ifdef HAS_D3D12
+
+#include <d3d12.h>
+#include <wrl/client.h>
+
+using namespace Microsoft::WRL;
+
+class D3D12Renderer : public CustomRenderer
+{
+public:
+ D3D12Renderer(QQuickItem *item, QSGRenderNode *node);
+ ~D3D12Renderer();
+ void init() override;
+ void render(const QSGRenderNode::RenderState *state) override;
+
+private:
+ QQuickItem *m_item;
+ QSGRenderNode *m_node;
+ ID3D12Device *m_device;
+ ComPtr<ID3D12PipelineState> pipelineState;
+ ComPtr<ID3D12RootSignature> rootSignature;
+ ComPtr<ID3D12Resource> vertexBuffer;
+ ComPtr<ID3D12Resource> constantBuffer;
+ D3D12_VERTEX_BUFFER_VIEW vertexBufferView;
+ quint8 *vbPtr;
+ quint8 *cbPtr;
+};
+
+#endif // HAS_D3D12
+
+#endif
diff --git a/examples/quick/scenegraph/rendernode/main.cpp b/examples/quick/scenegraph/rendernode/main.cpp
new file mode 100644
index 0000000000..9128cdc5be
--- /dev/null
+++ b/examples/quick/scenegraph/rendernode/main.cpp
@@ -0,0 +1,58 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QGuiApplication>
+#include <QQuickView>
+#include "customrenderitem.h"
+
+int main(int argc, char **argv)
+{
+ QGuiApplication app(argc, argv);
+
+ qmlRegisterType<CustomRenderItem>("SceneGraphRendering", 2, 0, "CustomRenderItem");
+
+ QQuickView view;
+ view.setResizeMode(QQuickView::SizeRootObjectToView);
+ view.setSource(QUrl("qrc:///scenegraph/rendernode/main.qml"));
+ view.resize(1024, 768);
+ view.show();
+
+ return app.exec();
+}
diff --git a/examples/quick/scenegraph/rendernode/main.qml b/examples/quick/scenegraph/rendernode/main.qml
new file mode 100644
index 0000000000..bae77aabd3
--- /dev/null
+++ b/examples/quick/scenegraph/rendernode/main.qml
@@ -0,0 +1,97 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.8 // for RendererInfo
+import SceneGraphRendering 2.0
+
+Item {
+ Rectangle {
+ anchors.fill: parent
+ gradient: Gradient {
+ GradientStop { position: 0; color: "steelblue" }
+ GradientStop { position: 1; color: "black" }
+ }
+
+ CustomRenderItem {
+ id: renderer
+ anchors.fill: parent
+ anchors.margins: 10
+
+ transform: [
+ Rotation { id: rotation; axis.x: 0; axis.z: 0; axis.y: 1; angle: 0; origin.x: renderer.width / 2; origin.y: renderer.height / 2; },
+ Translate { id: txOut; x: -renderer.width / 2; y: -renderer.height / 2 },
+ Scale { id: scale; },
+ Translate { id: txIn; x: renderer.width / 2; y: renderer.height / 2 }
+ ]
+ }
+
+ SequentialAnimation {
+ PauseAnimation { duration: 3000 }
+ ParallelAnimation {
+ NumberAnimation { target: scale; property: "xScale"; to: 0.6; duration: 1000; easing.type: Easing.InOutBack }
+ NumberAnimation { target: scale; property: "yScale"; to: 0.6; duration: 1000; easing.type: Easing.InOutBack }
+ }
+ NumberAnimation { target: rotation; property: "angle"; to: 80; duration: 1000; easing.type: Easing.InOutCubic }
+ NumberAnimation { target: rotation; property: "angle"; to: -80; duration: 1000; easing.type: Easing.InOutCubic }
+ NumberAnimation { target: rotation; property: "angle"; to: 0; duration: 1000; easing.type: Easing.InOutCubic }
+ NumberAnimation { target: renderer; property: "opacity"; to: 0.5; duration: 1000; easing.type: Easing.InOutCubic }
+ PauseAnimation { duration: 1000 }
+ NumberAnimation { target: renderer; property: "opacity"; to: 0.8; duration: 1000; easing.type: Easing.InOutCubic }
+ ParallelAnimation {
+ NumberAnimation { target: scale; property: "xScale"; to: 1; duration: 1000; easing.type: Easing.InOutBack }
+ NumberAnimation { target: scale; property: "yScale"; to: 1; duration: 1000; easing.type: Easing.InOutBack }
+ }
+ running: true
+ loops: Animation.Infinite
+ }
+
+ Text {
+ id: label
+ anchors.bottom: renderer.bottom
+ anchors.left: renderer.left
+ anchors.right: renderer.right
+ anchors.margins: 20
+ wrapMode: Text.WordWrap
+ property int api: RendererInfo.api
+ text: "Custom rendering via the graphics API " + (api === RendererInfo.OpenGL ? "OpenGL" : (api === RendererInfo.Direct3D12 ? "Direct3D 12" : ""))
+ color: "yellow"
+ }
+ }
+}
diff --git a/examples/quick/scenegraph/rendernode/openglrenderer.cpp b/examples/quick/scenegraph/rendernode/openglrenderer.cpp
new file mode 100644
index 0000000000..e9c1be071c
--- /dev/null
+++ b/examples/quick/scenegraph/rendernode/openglrenderer.cpp
@@ -0,0 +1,133 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "openglrenderer.h"
+
+#ifndef QT_NO_OPENGL
+
+#include <QOpenGLShaderProgram>
+#include <QOpenGLBuffer>
+#include <QOpenGLFunctions>
+
+OpenGLRenderer::OpenGLRenderer(QQuickItem *item, QSGRenderNode *node)
+ : m_item(item),
+ m_node(node)
+{
+}
+
+void OpenGLRenderer::init()
+{
+ m_program = new QOpenGLShaderProgram;
+
+ static const char *vertexShaderSource =
+ "attribute highp vec4 posAttr;\n"
+ "attribute lowp vec4 colAttr;\n"
+ "varying lowp vec4 col;\n"
+ "uniform highp mat4 matrix;\n"
+ "void main() {\n"
+ " col = colAttr;\n"
+ " gl_Position = matrix * posAttr;\n"
+ "}\n";
+
+ static const char *fragmentShaderSource =
+ "varying lowp vec4 col;\n"
+ "void main() {\n"
+ " gl_FragColor = col;\n"
+ "}\n";
+
+ m_program->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShaderSource);
+ m_program->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShaderSource);
+ m_program->bindAttributeLocation("posAttr", 0);
+ m_program->bindAttributeLocation("colAttr", 1);
+ m_program->link();
+
+ m_matrixUniform = m_program->uniformLocation("matrix");
+
+ const int VERTEX_SIZE = 6 * sizeof(GLfloat);
+
+ // A fully featured renderer should also take inheritedOpacity() into account
+ // and blend, but ignore that for now.
+ static GLfloat colors[] = {
+ 1.0f, 0.0f, 0.0f,
+ 0.0f, 1.0f, 0.0f,
+ 0.0f, 0.0f, 1.0f
+ };
+
+ m_vbo = new QOpenGLBuffer;
+ m_vbo->create();
+ m_vbo->bind();
+ m_vbo->allocate(VERTEX_SIZE + sizeof(colors));
+ m_vbo->write(VERTEX_SIZE, colors, sizeof(colors));
+ m_vbo->release();
+}
+
+OpenGLRenderer::~OpenGLRenderer()
+{
+ delete m_program;
+ delete m_vbo;
+}
+
+void OpenGLRenderer::render(const QSGRenderNode::RenderState *state)
+{
+ QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions();
+
+ m_program->bind();
+ m_program->setUniformValue(m_matrixUniform, *state->projectionMatrix() * *m_node->matrix());
+
+ m_vbo->bind();
+
+ QPointF p0(m_item->width() - 1, m_item->height() - 1);
+ QPointF p1(0, 0);
+ QPointF p2(0, m_item->height() - 1);
+
+ GLfloat vertices[6] = { GLfloat(p0.x()), GLfloat(p0.y()),
+ GLfloat(p1.x()), GLfloat(p1.y()),
+ GLfloat(p2.x()), GLfloat(p2.y()) };
+ m_vbo->write(0, vertices, sizeof(vertices));
+
+ m_program->setAttributeBuffer(0, GL_FLOAT, 0, 2);
+ m_program->setAttributeBuffer(1, GL_FLOAT, sizeof(vertices), 3);
+ m_program->enableAttributeArray(0);
+ m_program->enableAttributeArray(1);
+
+ f->glDrawArrays(GL_TRIANGLES, 0, 3);
+}
+
+#endif // QT_NO_OPENGL
diff --git a/examples/quick/scenegraph/rendernode/openglrenderer.h b/examples/quick/scenegraph/rendernode/openglrenderer.h
new file mode 100644
index 0000000000..d445b35c1e
--- /dev/null
+++ b/examples/quick/scenegraph/rendernode/openglrenderer.h
@@ -0,0 +1,70 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef OPENGLRENDERER_H
+#define OPENGLRENDERER_H
+
+#include "customrenderitem.h"
+#include <qsgrendernode.h>
+
+#ifndef QT_NO_OPENGL
+
+class QOpenGLShaderProgram;
+class QOpenGLBuffer;
+
+class OpenGLRenderer : public CustomRenderer
+{
+public:
+ OpenGLRenderer(QQuickItem *item, QSGRenderNode *node);
+ ~OpenGLRenderer();
+ void init() override;
+ void render(const QSGRenderNode::RenderState *state) override;
+
+private:
+ QQuickItem *m_item;
+ QSGRenderNode *m_node;
+ QOpenGLShaderProgram *m_program = nullptr;
+ int m_matrixUniform;
+ QOpenGLBuffer *m_vbo = nullptr;
+};
+
+#endif // QT_NO_OPENGL
+
+#endif
diff --git a/examples/quick/scenegraph/rendernode/rendernode.pro b/examples/quick/scenegraph/rendernode/rendernode.pro
new file mode 100644
index 0000000000..d7ae715a7d
--- /dev/null
+++ b/examples/quick/scenegraph/rendernode/rendernode.pro
@@ -0,0 +1,37 @@
+QT += qml quick
+
+HEADERS += customrenderitem.h \
+ openglrenderer.h
+
+SOURCES += customrenderitem.cpp \
+ openglrenderer.cpp \
+ main.cpp
+
+RESOURCES += rendernode.qrc
+
+target.path = $$[QT_INSTALL_EXAMPLES]/quick/scenegraph/rendernode
+INSTALLS += target
+
+OTHER_FILES += \
+ main.qml \
+ shader.hlsl
+
+config_d3d12 {
+ DEFINES += HAS_D3D12
+ HEADERS += d3d12renderer.h
+ SOURCES += d3d12renderer.cpp
+ LIBS += -ld3d12
+
+ VSPS = shader.hlsl
+ vshader.input = VSPS
+ vshader.header = vs_shader.hlslh
+ vshader.entry = VS_Simple
+ vshader.type = vs_5_0
+ pshader.input = VSPS
+ pshader.header = ps_shader.hlslh
+ pshader.entry = PS_Simple
+ pshader.type = ps_5_0
+
+ HLSL_SHADERS = vshader pshader
+ load(hlsl_bytecode_header)
+}
diff --git a/examples/quick/scenegraph/rendernode/rendernode.qrc b/examples/quick/scenegraph/rendernode/rendernode.qrc
new file mode 100644
index 0000000000..3674baccd8
--- /dev/null
+++ b/examples/quick/scenegraph/rendernode/rendernode.qrc
@@ -0,0 +1,5 @@
+<RCC>
+ <qresource prefix="/scenegraph/rendernode">
+ <file>main.qml</file>
+ </qresource>
+</RCC>
diff --git a/examples/quick/scenegraph/rendernode/shader.hlsl b/examples/quick/scenegraph/rendernode/shader.hlsl
new file mode 100644
index 0000000000..8b9b9ff9d8
--- /dev/null
+++ b/examples/quick/scenegraph/rendernode/shader.hlsl
@@ -0,0 +1,27 @@
+cbuffer ConstantBuffer : register(b0)
+{
+ float4x4 modelview;
+ float4x4 projection;
+};
+
+struct PSInput
+{
+ float4 position : SV_POSITION;
+ float4 color : COLOR;
+};
+
+PSInput VS_Simple(float4 position : POSITION, float4 color : COLOR)
+{
+ PSInput result;
+
+ float4x4 mvp = mul(projection, modelview);
+ result.position = mul(mvp, position);
+ result.color = color;
+
+ return result;
+}
+
+float4 PS_Simple(PSInput input) : SV_TARGET
+{
+ return input.color;
+}
diff --git a/examples/quick/scenegraph/scenegraph.pro b/examples/quick/scenegraph/scenegraph.pro
index cf50cdb903..1015d7be3d 100644
--- a/examples/quick/scenegraph/scenegraph.pro
+++ b/examples/quick/scenegraph/scenegraph.pro
@@ -1,14 +1,21 @@
TEMPLATE = subdirs
+
+contains(QT_CONFIG, opengl(es1|es2)?) {
+ SUBDIRS += \
+ graph \
+ simplematerial \
+ sgengine \
+ textureinsgnode \
+ openglunderqml \
+ textureinsgnode \
+ textureinthread \
+ twotextureproviders
+}
+
SUBDIRS += \
customgeometry \
- graph \
- openglunderqml \
- sgengine \
- simplematerial \
- textureinsgnode \
- textureinthread \
- threadedanimation \
- twotextureproviders
+ rendernode \
+ threadedanimation
EXAMPLE_FILES += \
shared
diff --git a/features/hlsl_bytecode_header.prf b/features/hlsl_bytecode_header.prf
new file mode 100644
index 0000000000..0fa27a75d5
--- /dev/null
+++ b/features/hlsl_bytecode_header.prf
@@ -0,0 +1,10 @@
+for (SHADER, HLSL_SHADERS) {
+ INPUT = $$eval($${SHADER}.input)
+ fxc_$${SHADER}.input = $$INPUT
+ fxc_$${SHADER}.commands = fxc.exe /nologo /E $$eval($${SHADER}.entry) /T $$eval($${SHADER}.type) /Fh ${QMAKE_FILE_OUT} ${QMAKE_FILE_NAME}
+ fxc_$${SHADER}.output = $$eval($${SHADER}.header)
+ fxc_$${SHADER}.dependency_type = TYPE_C
+ fxc_$${SHADER}.variable_out = HEADERS
+ fxc_$${SHADER}.CONFIG += target_predeps
+ QMAKE_EXTRA_COMPILERS += fxc_$${SHADER}
+}
diff --git a/qtdeclarative.pro b/qtdeclarative.pro
index 5b94da9b69..c9df6e053b 100644
--- a/qtdeclarative.pro
+++ b/qtdeclarative.pro
@@ -1,3 +1,6 @@
+load(configure)
+qtCompileTest(d3d12)
+
CONFIG += tests_need_tools examples_need_tools
load(qt_parts)
diff --git a/src/imports/imports.pro b/src/imports/imports.pro
index 5332fb0ef2..9e3cdf3f42 100644
--- a/src/imports/imports.pro
+++ b/src/imports/imports.pro
@@ -13,9 +13,13 @@ qtHaveModule(quick) {
SUBDIRS += \
layouts \
qtquick2 \
- particles \
window \
testlib
+
+ contains(QT_CONFIG, opengl(es1|es2)?) {
+ SUBDIRS += \
+ particles
+ }
}
qtHaveModule(xmlpatterns) : SUBDIRS += xmllistmodel
diff --git a/src/particles/qquickcustomparticle.cpp b/src/particles/qquickcustomparticle.cpp
index 3cb7f6c2bf..a89b0d6a3b 100644
--- a/src/particles/qquickcustomparticle.cpp
+++ b/src/particles/qquickcustomparticle.cpp
@@ -94,6 +94,7 @@ struct PlainVertices {
QQuickCustomParticle::QQuickCustomParticle(QQuickItem* parent)
: QQuickParticlePainter(parent)
+ , m_common(this)
, m_dirtyUniforms(true)
, m_dirtyUniformValues(true)
, m_dirtyTextureProviders(true)
@@ -237,7 +238,7 @@ void QQuickCustomParticle::reset()
QSGNode *QQuickCustomParticle::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
{
- QQuickShaderEffectNode *rootNode = static_cast<QQuickShaderEffectNode *>(oldNode);
+ QQuickOpenGLShaderEffectNode *rootNode = static_cast<QQuickOpenGLShaderEffectNode *>(oldNode);
if (m_pleaseReset){
delete rootNode;//Automatically deletes children
rootNode = 0;
@@ -258,7 +259,7 @@ QSGNode *QQuickCustomParticle::updatePaintNode(QSGNode *oldNode, UpdatePaintNode
return rootNode;
}
-QQuickShaderEffectNode *QQuickCustomParticle::prepareNextFrame(QQuickShaderEffectNode *rootNode)
+QQuickOpenGLShaderEffectNode *QQuickCustomParticle::prepareNextFrame(QQuickOpenGLShaderEffectNode *rootNode)
{
if (!rootNode)
rootNode = buildCustomNodes();
@@ -269,7 +270,7 @@ QQuickShaderEffectNode *QQuickCustomParticle::prepareNextFrame(QQuickShaderEffec
if (m_dirtyProgram) {
const bool isES = QOpenGLContext::currentContext()->isOpenGLES();
- QQuickShaderEffectMaterial *material = static_cast<QQuickShaderEffectMaterial *>(rootNode->material());
+ QQuickOpenGLShaderEffectMaterial *material = static_cast<QQuickOpenGLShaderEffectMaterial *>(rootNode->material());
Q_ASSERT(material);
Key s = m_common.source;
@@ -292,7 +293,7 @@ QQuickShaderEffectNode *QQuickCustomParticle::prepareNextFrame(QQuickShaderEffec
material->setProgramSource(s);
material->attributes = m_common.attributes;
- foreach (QQuickShaderEffectNode* node, m_nodes)
+ foreach (QQuickOpenGLShaderEffectNode* node, m_nodes)
node->markDirty(QSGNode::DirtyMaterial);
m_dirtyProgram = false;
@@ -305,9 +306,9 @@ QQuickShaderEffectNode *QQuickCustomParticle::prepareNextFrame(QQuickShaderEffec
return rootNode;
}
-QQuickShaderEffectNode* QQuickCustomParticle::buildCustomNodes()
+QQuickOpenGLShaderEffectNode* QQuickCustomParticle::buildCustomNodes()
{
- typedef QHash<int, QQuickShaderEffectNode*>::const_iterator NodeHashConstIt;
+ typedef QHash<int, QQuickOpenGLShaderEffectNode*>::const_iterator NodeHashConstIt;
if (!QOpenGLContext::currentContext())
return 0;
@@ -325,14 +326,14 @@ QQuickShaderEffectNode* QQuickCustomParticle::buildCustomNodes()
if (groups().isEmpty())
return 0;
- QQuickShaderEffectNode *rootNode = 0;
- QQuickShaderEffectMaterial *material = new QQuickShaderEffectMaterial;
+ QQuickOpenGLShaderEffectNode *rootNode = 0;
+ QQuickOpenGLShaderEffectMaterial *material = new QQuickOpenGLShaderEffectMaterial;
m_dirtyProgram = true;
for (auto groupId : groupIds()) {
int count = m_system->groupData[groupId]->size();
- QQuickShaderEffectNode* node = new QQuickShaderEffectNode();
+ QQuickOpenGLShaderEffectNode* node = new QQuickOpenGLShaderEffectNode();
m_nodes.insert(groupId, node);
node->setMaterial(material);
@@ -398,7 +399,7 @@ void QQuickCustomParticle::propertyChanged(int mappedId)
}
-void QQuickCustomParticle::buildData(QQuickShaderEffectNode *rootNode)
+void QQuickCustomParticle::buildData(QQuickOpenGLShaderEffectNode *rootNode)
{
if (!rootNode)
return;
@@ -408,9 +409,9 @@ void QQuickCustomParticle::buildData(QQuickShaderEffectNode *rootNode)
m_common.uniformData[shaderType][i].value = qVariantFromValue(m_lastTime);
}
}
- m_common.updateMaterial(rootNode, static_cast<QQuickShaderEffectMaterial *>(rootNode->material()),
+ m_common.updateMaterial(rootNode, static_cast<QQuickOpenGLShaderEffectMaterial *>(rootNode->material()),
m_dirtyUniforms, true, m_dirtyTextureProviders);
- foreach (QQuickShaderEffectNode* node, m_nodes)
+ foreach (QQuickOpenGLShaderEffectNode* node, m_nodes)
node->markDirty(QSGNode::DirtyMaterial);
m_dirtyUniforms = m_dirtyUniformValues = m_dirtyTextureProviders = false;
}
diff --git a/src/particles/qquickcustomparticle_p.h b/src/particles/qquickcustomparticle_p.h
index 25a3a1197c..d9690aa96a 100644
--- a/src/particles/qquickcustomparticle_p.h
+++ b/src/particles/qquickcustomparticle_p.h
@@ -51,8 +51,8 @@
// We mean it.
//
#include "qquickparticlepainter_p.h"
-#include <private/qquickshadereffectnode_p.h>
-#include <private/qquickshadereffect_p.h>
+#include <private/qquickopenglshadereffectnode_p.h>
+#include <private/qquickopenglshadereffect_p.h>
#include <QSignalMapper>
QT_BEGIN_NAMESPACE
@@ -88,11 +88,11 @@ protected:
virtual void commit(int gIdx, int pIdx);
QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *);
- QQuickShaderEffectNode *prepareNextFrame(QQuickShaderEffectNode *rootNode);
+ QQuickOpenGLShaderEffectNode *prepareNextFrame(QQuickOpenGLShaderEffectNode *rootNode);
void reset();
void resize(int oldCount, int newCount);
virtual void componentComplete();
- QQuickShaderEffectNode *buildCustomNodes();
+ QQuickOpenGLShaderEffectNode *buildCustomNodes();
void sceneGraphInvalidated();
void itemChange(ItemChange change, const ItemChangeData &value);
@@ -102,15 +102,15 @@ private Q_SLOTS:
void propertyChanged(int mappedId);
private:
- typedef QQuickShaderEffectMaterialKey Key;
- typedef QQuickShaderEffectMaterial::UniformData UniformData;
+ typedef QQuickOpenGLShaderEffectMaterialKey Key;
+ typedef QQuickOpenGLShaderEffectMaterial::UniformData UniformData;
- void buildData(QQuickShaderEffectNode *rootNode);
+ void buildData(QQuickOpenGLShaderEffectNode *rootNode);
void updateVertexShader();
- QQuickShaderEffectCommon m_common;
+ QQuickOpenGLShaderEffectCommon m_common;
- QHash<int, QQuickShaderEffectNode*> m_nodes;
+ QHash<int, QQuickOpenGLShaderEffectNode*> m_nodes;
qreal m_lastTime;
uint m_dirtyUniforms : 1;
diff --git a/src/plugins/plugins.pro b/src/plugins/plugins.pro
index 273407c19d..11c8641e21 100644
--- a/src/plugins/plugins.pro
+++ b/src/plugins/plugins.pro
@@ -1,2 +1,4 @@
TEMPLATE = subdirs
+
!contains(QT_CONFIG, no-qml-debug):SUBDIRS += qmltooling
+SUBDIRS += scenegraph
diff --git a/src/plugins/scenegraph/d3d12/d3d12.json b/src/plugins/scenegraph/d3d12/d3d12.json
new file mode 100644
index 0000000000..c450a38556
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/d3d12.json
@@ -0,0 +1,3 @@
+{
+ "Keys": ["d3d12"]
+}
diff --git a/src/plugins/scenegraph/d3d12/d3d12.pro b/src/plugins/scenegraph/d3d12/d3d12.pro
new file mode 100644
index 0000000000..6ba18acf22
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/d3d12.pro
@@ -0,0 +1,55 @@
+TARGET = qsgd3d12backend
+
+QT += core-private gui-private qml-private quick-private
+
+PLUGIN_TYPE = scenegraph
+PLUGIN_CLASS_NAME = QSGD3D12Adaptation
+load(qt_plugin)
+
+QMAKE_TARGET_PRODUCT = "Qt Quick D3D12 Renderer (Qt $$QT_VERSION)"
+QMAKE_TARGET_DESCRIPTION = "Quick D3D12 Renderer for Qt."
+
+SOURCES += \
+ $$PWD/qsgd3d12adaptation.cpp \
+ $$PWD/qsgd3d12renderloop.cpp \
+ $$PWD/qsgd3d12renderer.cpp \
+ $$PWD/qsgd3d12context.cpp \
+ $$PWD/qsgd3d12rendercontext.cpp \
+ $$PWD/qsgd3d12rectanglenode.cpp \
+ $$PWD/qsgd3d12material.cpp \
+ $$PWD/qsgd3d12builtinmaterials.cpp \
+ $$PWD/qsgd3d12texture.cpp \
+ $$PWD/qsgd3d12imagenode.cpp \
+ $$PWD/qsgd3d12glyphnode.cpp \
+ $$PWD/qsgd3d12glyphcache.cpp \
+ $$PWD/qsgd3d12layer.cpp \
+ $$PWD/qsgd3d12shadereffectnode.cpp \
+ $$PWD/qsgd3d12painternode.cpp
+
+NO_PCH_SOURCES += \
+ $$PWD/qsgd3d12engine.cpp
+
+HEADERS += \
+ $$PWD/qsgd3d12adaptation_p.h \
+ $$PWD/qsgd3d12renderloop_p.h \
+ $$PWD/qsgd3d12renderer_p.h \
+ $$PWD/qsgd3d12context_p.h \
+ $$PWD/qsgd3d12rendercontext_p.h \
+ $$PWD/qsgd3d12engine_p.h \
+ $$PWD/qsgd3d12engine_p_p.h \
+ $$PWD/qsgd3d12rectanglenode_p.h \
+ $$PWD/qsgd3d12material_p.h \
+ $$PWD/qsgd3d12builtinmaterials_p.h \
+ $$PWD/qsgd3d12texture_p.h \
+ $$PWD/qsgd3d12imagenode_p.h \
+ $$PWD/qsgd3d12glyphnode_p.h \
+ $$PWD/qsgd3d12glyphcache_p.h \
+ $$PWD/qsgd3d12layer_p.h \
+ $$PWD/qsgd3d12shadereffectnode_p.h \
+ $$PWD/qsgd3d12painternode_p.h
+
+LIBS += -ldxgi -ld3d12 -ld3dcompiler
+
+include($$PWD/shaders/shaders.pri)
+
+OTHER_FILES += $$PWD/d3d12.json
diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12adaptation.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12adaptation.cpp
new file mode 100644
index 0000000000..2762177e5d
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12adaptation.cpp
@@ -0,0 +1,76 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgd3d12adaptation_p.h"
+#include "qsgd3d12renderloop_p.h"
+#include "qsgd3d12context_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QSGD3D12Adaptation::QSGD3D12Adaptation(QObject *parent)
+ : QSGContextPlugin(parent)
+{
+}
+
+QStringList QSGD3D12Adaptation::keys() const
+{
+ return QStringList() << QLatin1String("d3d12");
+}
+
+QSGContext *QSGD3D12Adaptation::create(const QString &) const
+{
+ if (!contextInstance)
+ contextInstance = new QSGD3D12Context;
+
+ return contextInstance;
+}
+
+QSGContextFactoryInterface::Flags QSGD3D12Adaptation::flags(const QString &) const
+{
+ return QSGContextFactoryInterface::SupportsShaderEffectNode;
+}
+
+QSGRenderLoop *QSGD3D12Adaptation::createWindowManager()
+{
+ return new QSGD3D12RenderLoop;
+}
+
+QSGD3D12Context *QSGD3D12Adaptation::contextInstance = nullptr;
+
+QT_END_NAMESPACE
diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12adaptation_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12adaptation_p.h
new file mode 100644
index 0000000000..035c3408ff
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12adaptation_p.h
@@ -0,0 +1,81 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGD3D12ADAPTATION_P_H
+#define QSGD3D12ADAPTATION_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qsgcontextplugin_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QSGD3D12Context;
+class QSGContext;
+class QSGRenderLoop;
+
+class QSGD3D12Adaptation : public QSGContextPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QSGContextFactoryInterface" FILE "d3d12.json")
+
+public:
+ QSGD3D12Adaptation(QObject *parent = 0);
+
+ QStringList keys() const override;
+ QSGContext *create(const QString &key) const override;
+ QSGContextFactoryInterface::Flags flags(const QString &key) const override;
+ QSGRenderLoop *createWindowManager() override;
+
+private:
+ static QSGD3D12Context *contextInstance;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGD3D12ADAPTATION_P_H
diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12builtinmaterials.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12builtinmaterials.cpp
new file mode 100644
index 0000000000..382a08eea9
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12builtinmaterials.cpp
@@ -0,0 +1,651 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgd3d12builtinmaterials_p.h"
+#include "qsgd3d12rendercontext_p.h"
+#include <QQuickWindow>
+#include <QtCore/qmath.h>
+#include <QtGui/private/qfixed_p.h>
+
+#include "vs_vertexcolor.hlslh"
+#include "ps_vertexcolor.hlslh"
+#include "vs_smoothcolor.hlslh"
+#include "ps_smoothcolor.hlslh"
+#include "vs_texture.hlslh"
+#include "ps_texture.hlslh"
+#include "vs_smoothtexture.hlslh"
+#include "ps_smoothtexture.hlslh"
+#include "vs_textmask.hlslh"
+#include "ps_textmask24.hlslh"
+#include "ps_textmask32.hlslh"
+#include "ps_textmask8.hlslh"
+#include "vs_styledtext.hlslh"
+#include "ps_styledtext.hlslh"
+#include "vs_outlinedtext.hlslh"
+#include "ps_outlinedtext.hlslh"
+
+QT_BEGIN_NAMESPACE
+
+// NB! In HLSL constant buffer data is packed into 4-byte boundaries and, more
+// importantly, it is packed so that it does not cross a 16-byte (float4)
+// boundary. Hence the need for padding in some cases.
+
+QSGMaterialType QSGD3D12VertexColorMaterial::mtype;
+
+QSGMaterialType *QSGD3D12VertexColorMaterial::type() const
+{
+ return &QSGD3D12VertexColorMaterial::mtype;
+}
+
+int QSGD3D12VertexColorMaterial::compare(const QSGMaterial *other) const
+{
+ Q_ASSERT(other && type() == other->type());
+ // As the vertex color material has all its state in the vertex attributes
+ // defined by the geometry, all such materials will be equal.
+ return 0;
+}
+
+static const int VERTEX_COLOR_CB_SIZE_0 = 16 * sizeof(float); // float4x4
+static const int VERTEX_COLOR_CB_SIZE_1 = sizeof(float); // float
+static const int VERTEX_COLOR_CB_SIZE = VERTEX_COLOR_CB_SIZE_0 + VERTEX_COLOR_CB_SIZE_1;
+
+int QSGD3D12VertexColorMaterial::constantBufferSize() const
+{
+ return QSGD3D12Engine::alignedConstantBufferSize(VERTEX_COLOR_CB_SIZE);
+}
+
+void QSGD3D12VertexColorMaterial::preparePipeline(QSGD3D12PipelineState *pipelineState)
+{
+ pipelineState->shaders.vs = g_VS_VertexColor;
+ pipelineState->shaders.vsSize = sizeof(g_VS_VertexColor);
+ pipelineState->shaders.ps = g_PS_VertexColor;
+ pipelineState->shaders.psSize = sizeof(g_PS_VertexColor);
+}
+
+QSGD3D12Material::UpdateResults QSGD3D12VertexColorMaterial::updatePipeline(const RenderState &state,
+ QSGD3D12PipelineState *,
+ ExtraState *,
+ quint8 *constantBuffer)
+{
+ QSGD3D12Material::UpdateResults r = 0;
+ quint8 *p = constantBuffer;
+
+ if (state.isMatrixDirty()) {
+ memcpy(p, state.combinedMatrix().constData(), VERTEX_COLOR_CB_SIZE_0);
+ r |= UpdatedConstantBuffer;
+ }
+ p += VERTEX_COLOR_CB_SIZE_0;
+
+ if (state.isOpacityDirty()) {
+ const float opacity = state.opacity();
+ memcpy(p, &opacity, VERTEX_COLOR_CB_SIZE_1);
+ r |= UpdatedConstantBuffer;
+ }
+
+ return r;
+}
+
+QSGD3D12SmoothColorMaterial::QSGD3D12SmoothColorMaterial()
+{
+ setFlag(RequiresFullMatrixExceptTranslate, true);
+ setFlag(Blending, true);
+}
+
+QSGMaterialType QSGD3D12SmoothColorMaterial::mtype;
+
+QSGMaterialType *QSGD3D12SmoothColorMaterial::type() const
+{
+ return &QSGD3D12SmoothColorMaterial::mtype;
+}
+
+int QSGD3D12SmoothColorMaterial::compare(const QSGMaterial *other) const
+{
+ Q_ASSERT(other && type() == other->type());
+ return 0;
+}
+
+static const int SMOOTH_COLOR_CB_SIZE_0 = 16 * sizeof(float); // float4x4
+static const int SMOOTH_COLOR_CB_SIZE_1 = sizeof(float); // float
+static const int SMOOTH_COLOR_CB_SIZE_2 = 2 * sizeof(float); // float2
+static const int SMOOTH_COLOR_CB_SIZE = SMOOTH_COLOR_CB_SIZE_0 + SMOOTH_COLOR_CB_SIZE_1 + SMOOTH_COLOR_CB_SIZE_2;
+
+int QSGD3D12SmoothColorMaterial::constantBufferSize() const
+{
+ return QSGD3D12Engine::alignedConstantBufferSize(SMOOTH_COLOR_CB_SIZE);
+}
+
+void QSGD3D12SmoothColorMaterial::preparePipeline(QSGD3D12PipelineState *pipelineState)
+{
+ pipelineState->shaders.vs = g_VS_SmoothColor;
+ pipelineState->shaders.vsSize = sizeof(g_VS_SmoothColor);
+ pipelineState->shaders.ps = g_PS_SmoothColor;
+ pipelineState->shaders.psSize = sizeof(g_PS_SmoothColor);
+}
+
+QSGD3D12Material::UpdateResults QSGD3D12SmoothColorMaterial::updatePipeline(const RenderState &state,
+ QSGD3D12PipelineState *,
+ ExtraState *,
+ quint8 *constantBuffer)
+{
+ QSGD3D12Material::UpdateResults r = 0;
+ quint8 *p = constantBuffer;
+
+ if (state.isMatrixDirty()) {
+ memcpy(p, state.combinedMatrix().constData(), SMOOTH_COLOR_CB_SIZE_0);
+ r |= UpdatedConstantBuffer;
+ }
+ p += SMOOTH_COLOR_CB_SIZE_0;
+
+ if (state.isOpacityDirty()) {
+ const float opacity = state.opacity();
+ memcpy(p, &opacity, SMOOTH_COLOR_CB_SIZE_1);
+ r |= UpdatedConstantBuffer;
+ }
+ p += SMOOTH_COLOR_CB_SIZE_1;
+
+ if (state.isMatrixDirty()) {
+ const QRect viewport = state.viewportRect();
+ const float v[] = { 2.0f / viewport.width(), 2.0f / viewport.height() };
+ memcpy(p, v, SMOOTH_COLOR_CB_SIZE_2);
+ r |= UpdatedConstantBuffer;
+ }
+
+ return r;
+}
+
+QSGMaterialType QSGD3D12TextureMaterial::mtype;
+
+QSGMaterialType *QSGD3D12TextureMaterial::type() const
+{
+ return &QSGD3D12TextureMaterial::mtype;
+}
+
+int QSGD3D12TextureMaterial::compare(const QSGMaterial *other) const
+{
+ Q_ASSERT(other && type() == other->type());
+ const QSGD3D12TextureMaterial *o = static_cast<const QSGD3D12TextureMaterial *>(other);
+ if (int diff = m_texture->textureId() - o->texture()->textureId())
+ return diff;
+ return int(m_filtering) - int(o->m_filtering);
+}
+
+static const int TEXTURE_CB_SIZE_0 = 16 * sizeof(float); // float4x4
+static const int TEXTURE_CB_SIZE_1 = sizeof(float); // float
+static const int TEXTURE_CB_SIZE = TEXTURE_CB_SIZE_0 + TEXTURE_CB_SIZE_1;
+
+int QSGD3D12TextureMaterial::constantBufferSize() const
+{
+ return QSGD3D12Engine::alignedConstantBufferSize(TEXTURE_CB_SIZE);
+}
+
+void QSGD3D12TextureMaterial::preparePipeline(QSGD3D12PipelineState *pipelineState)
+{
+ pipelineState->shaders.vs = g_VS_Texture;
+ pipelineState->shaders.vsSize = sizeof(g_VS_Texture);
+ pipelineState->shaders.ps = g_PS_Texture;
+ pipelineState->shaders.psSize = sizeof(g_PS_Texture);
+
+ pipelineState->shaders.rootSig.textureViews.resize(1);
+}
+
+QSGD3D12Material::UpdateResults QSGD3D12TextureMaterial::updatePipeline(const RenderState &state,
+ QSGD3D12PipelineState *pipelineState,
+ ExtraState *,
+ quint8 *constantBuffer)
+{
+ QSGD3D12Material::UpdateResults r = 0;
+ quint8 *p = constantBuffer;
+
+ if (state.isMatrixDirty()) {
+ memcpy(p, state.combinedMatrix().constData(), TEXTURE_CB_SIZE_0);
+ r |= UpdatedConstantBuffer;
+ }
+ p += TEXTURE_CB_SIZE_0;
+
+ if (state.isOpacityDirty()) {
+ const float opacity = state.opacity();
+ memcpy(p, &opacity, TEXTURE_CB_SIZE_1);
+ r |= UpdatedConstantBuffer;
+ }
+
+ Q_ASSERT(m_texture);
+ m_texture->setFiltering(m_filtering);
+ m_texture->setMipmapFiltering(m_mipmap_filtering);
+ m_texture->setHorizontalWrapMode(m_horizontal_wrap);
+ m_texture->setVerticalWrapMode(m_vertical_wrap);
+
+ QSGD3D12TextureView &tv(pipelineState->shaders.rootSig.textureViews[0]);
+ if (m_filtering == QSGTexture::Linear)
+ tv.filter = m_mipmap_filtering == QSGTexture::Linear
+ ? QSGD3D12TextureView::FilterLinear : QSGD3D12TextureView::FilterMinMagLinearMipNearest;
+ else
+ tv.filter = m_mipmap_filtering == QSGTexture::Linear
+ ? QSGD3D12TextureView::FilterMinMagNearestMipLinear : QSGD3D12TextureView::FilterNearest;
+ tv.addressModeHoriz = m_horizontal_wrap == QSGTexture::ClampToEdge ? QSGD3D12TextureView::AddressClamp : QSGD3D12TextureView::AddressWrap;
+ tv.addressModeVert = m_vertical_wrap == QSGTexture::ClampToEdge ? QSGD3D12TextureView::AddressClamp : QSGD3D12TextureView::AddressWrap;
+
+ m_texture->bind();
+
+ return r;
+}
+
+QSGD3D12SmoothTextureMaterial::QSGD3D12SmoothTextureMaterial()
+{
+ setFlag(RequiresFullMatrixExceptTranslate, true);
+ setFlag(Blending, true);
+}
+
+QSGMaterialType QSGD3D12SmoothTextureMaterial::mtype;
+
+QSGMaterialType *QSGD3D12SmoothTextureMaterial::type() const
+{
+ return &QSGD3D12SmoothTextureMaterial::mtype;
+}
+
+int QSGD3D12SmoothTextureMaterial::compare(const QSGMaterial *other) const
+{
+ Q_ASSERT(other && type() == other->type());
+ const QSGD3D12SmoothTextureMaterial *o = static_cast<const QSGD3D12SmoothTextureMaterial *>(other);
+ if (int diff = m_texture->textureId() - o->texture()->textureId())
+ return diff;
+ return int(m_filtering) - int(o->m_filtering);
+}
+
+static const int SMOOTH_TEXTURE_CB_SIZE_0 = 16 * sizeof(float); // float4x4
+static const int SMOOTH_TEXTURE_CB_SIZE_1 = sizeof(float); // float
+static const int SMOOTH_TEXTURE_CB_SIZE_2 = 2 * sizeof(float); // float2
+static const int SMOOTH_TEXTURE_CB_SIZE = SMOOTH_TEXTURE_CB_SIZE_0 + SMOOTH_TEXTURE_CB_SIZE_1 + SMOOTH_TEXTURE_CB_SIZE_2;
+
+int QSGD3D12SmoothTextureMaterial::constantBufferSize() const
+{
+ return QSGD3D12Engine::alignedConstantBufferSize(SMOOTH_TEXTURE_CB_SIZE);
+}
+
+void QSGD3D12SmoothTextureMaterial::preparePipeline(QSGD3D12PipelineState *pipelineState)
+{
+ pipelineState->shaders.vs = g_VS_SmoothTexture;
+ pipelineState->shaders.vsSize = sizeof(g_VS_SmoothTexture);
+ pipelineState->shaders.ps = g_PS_SmoothTexture;
+ pipelineState->shaders.psSize = sizeof(g_PS_SmoothTexture);
+
+ pipelineState->shaders.rootSig.textureViews.resize(1);
+}
+
+QSGD3D12Material::UpdateResults QSGD3D12SmoothTextureMaterial::updatePipeline(const RenderState &state,
+ QSGD3D12PipelineState *pipelineState,
+ ExtraState *,
+ quint8 *constantBuffer)
+{
+ QSGD3D12Material::UpdateResults r = 0;
+ quint8 *p = constantBuffer;
+
+ if (state.isMatrixDirty()) {
+ memcpy(p, state.combinedMatrix().constData(), SMOOTH_TEXTURE_CB_SIZE_0);
+ r |= UpdatedConstantBuffer;
+ }
+ p += SMOOTH_TEXTURE_CB_SIZE_0;
+
+ if (state.isOpacityDirty()) {
+ const float opacity = state.opacity();
+ memcpy(p, &opacity, SMOOTH_TEXTURE_CB_SIZE_1);
+ r |= UpdatedConstantBuffer;
+ }
+ p += SMOOTH_TEXTURE_CB_SIZE_1;
+
+ if (state.isMatrixDirty()) {
+ const QRect viewport = state.viewportRect();
+ const float v[] = { 2.0f / viewport.width(), 2.0f / viewport.height() };
+ memcpy(p, v, SMOOTH_TEXTURE_CB_SIZE_2);
+ r |= UpdatedConstantBuffer;
+ }
+
+ Q_ASSERT(m_texture);
+ m_texture->setFiltering(m_filtering);
+ m_texture->setMipmapFiltering(m_mipmap_filtering);
+ m_texture->setHorizontalWrapMode(m_horizontal_wrap);
+ m_texture->setVerticalWrapMode(m_vertical_wrap);
+
+ QSGD3D12TextureView &tv(pipelineState->shaders.rootSig.textureViews[0]);
+ if (m_filtering == QSGTexture::Linear)
+ tv.filter = m_mipmap_filtering == QSGTexture::Linear
+ ? QSGD3D12TextureView::FilterLinear : QSGD3D12TextureView::FilterMinMagLinearMipNearest;
+ else
+ tv.filter = m_mipmap_filtering == QSGTexture::Linear
+ ? QSGD3D12TextureView::FilterMinMagNearestMipLinear : QSGD3D12TextureView::FilterNearest;
+ tv.addressModeHoriz = m_horizontal_wrap == QSGTexture::ClampToEdge ? QSGD3D12TextureView::AddressClamp : QSGD3D12TextureView::AddressWrap;
+ tv.addressModeVert = m_vertical_wrap == QSGTexture::ClampToEdge ? QSGD3D12TextureView::AddressClamp : QSGD3D12TextureView::AddressWrap;
+
+ m_texture->bind();
+
+ return r;
+}
+
+static inline QVector4D qsg_premultiply(const QVector4D &c, float globalOpacity)
+{
+ float o = c.w() * globalOpacity;
+ return QVector4D(c.x() * o, c.y() * o, c.z() * o, o);
+}
+
+static inline int qsg_colorDiff(const QVector4D &a, const QVector4D &b)
+{
+ if (a.x() != b.x())
+ return a.x() > b.x() ? 1 : -1;
+ if (a.y() != b.y())
+ return a.y() > b.y() ? 1 : -1;
+ if (a.z() != b.z())
+ return a.z() > b.z() ? 1 : -1;
+ if (a.w() != b.w())
+ return a.w() > b.w() ? 1 : -1;
+ return 0;
+}
+
+QSGD3D12TextMaterial::QSGD3D12TextMaterial(StyleType styleType, QSGD3D12RenderContext *rc,
+ const QRawFont &font, QFontEngine::GlyphFormat glyphFormat)
+ : m_styleType(styleType),
+ m_font(font),
+ m_rc(rc)
+{
+ setFlag(Blending, true);
+
+ QRawFontPrivate *fontD = QRawFontPrivate::get(m_font);
+ if (QFontEngine *fontEngine = fontD->fontEngine) {
+ if (glyphFormat == QFontEngine::Format_None)
+ glyphFormat = fontEngine->glyphFormat != QFontEngine::Format_None
+ ? fontEngine->glyphFormat : QFontEngine::Format_A32;
+
+ QSGD3D12Engine *d3dengine = rc->engine();
+ const float devicePixelRatio = d3dengine->windowDevicePixelRatio();
+ QTransform glyphCacheTransform = QTransform::fromScale(devicePixelRatio, devicePixelRatio);
+ if (!fontEngine->supportsTransformation(glyphCacheTransform))
+ glyphCacheTransform = QTransform();
+
+ m_glyphCache = fontEngine->glyphCache(d3dengine, glyphFormat, glyphCacheTransform);
+ if (!m_glyphCache || int(m_glyphCache->glyphFormat()) != glyphFormat) {
+ m_glyphCache = new QSGD3D12GlyphCache(d3dengine, glyphFormat, glyphCacheTransform);
+ fontEngine->setGlyphCache(d3dengine, m_glyphCache.data());
+ rc->registerFontengineForCleanup(fontEngine);
+ }
+ }
+}
+
+QSGMaterialType QSGD3D12TextMaterial::mtype[QSGD3D12TextMaterial::NTextMaterialTypes];
+
+QSGMaterialType *QSGD3D12TextMaterial::type() const
+{
+ // Format_A32 has special blend settings and therefore two materials with
+ // the same style but different formats where one is A32 are treated as
+ // different. This way the renderer can manage the pipeline state properly.
+ const int matStyle = m_styleType * 2;
+ const int matFormat = glyphCache()->glyphFormat() != QFontEngine::Format_A32 ? 0 : 1;
+ return &QSGD3D12TextMaterial::mtype[matStyle + matFormat];
+}
+
+int QSGD3D12TextMaterial::compare(const QSGMaterial *other) const
+{
+ Q_ASSERT(other && type() == other->type());
+ const QSGD3D12TextMaterial *o = static_cast<const QSGD3D12TextMaterial *>(other);
+ if (m_styleType != o->m_styleType)
+ return m_styleType - o->m_styleType;
+ if (m_glyphCache != o->m_glyphCache)
+ return m_glyphCache.data() < o->m_glyphCache.data() ? -1 : 1;
+ if (m_styleShift != o->m_styleShift)
+ return m_styleShift.y() - o->m_styleShift.y();
+ int styleColorDiff = qsg_colorDiff(m_styleColor, o->m_styleColor);
+ if (styleColorDiff)
+ return styleColorDiff;
+ return qsg_colorDiff(m_color, o->m_color);
+}
+
+static const int TEXT_CB_SIZE_0 = 16 * sizeof(float); // float4x4 mvp
+static const int TEXT_CB_SIZE_1 = 2 * sizeof(float); // float2 textureScale
+static const int TEXT_CB_SIZE_2 = sizeof(float); // float dpr
+static const int TEXT_CB_SIZE_3 = sizeof(float); // float color
+static const int TEXT_CB_SIZE_4 = 4 * sizeof(float); // float4 colorVec
+static const int TEXT_CB_SIZE_5 = 2 * sizeof(float); // float2 shift
+static const int TEXT_CB_SIZE_5_PADDING = 2 * sizeof(float); // float2 padding (the next float4 would cross the 16-byte boundary)
+static const int TEXT_CB_SIZE_6 = 4 * sizeof(float); // float4 styleColor
+static const int TEXT_CB_SIZE = TEXT_CB_SIZE_0 + TEXT_CB_SIZE_1 + TEXT_CB_SIZE_2 + TEXT_CB_SIZE_3
+ + TEXT_CB_SIZE_4 + TEXT_CB_SIZE_5 + TEXT_CB_SIZE_5_PADDING + TEXT_CB_SIZE_6;
+
+int QSGD3D12TextMaterial::constantBufferSize() const
+{
+ return QSGD3D12Engine::alignedConstantBufferSize(TEXT_CB_SIZE);
+}
+
+void QSGD3D12TextMaterial::preparePipeline(QSGD3D12PipelineState *pipelineState)
+{
+ if (m_styleType == Normal) {
+ pipelineState->shaders.vs = g_VS_TextMask;
+ pipelineState->shaders.vsSize = sizeof(g_VS_TextMask);
+ switch (glyphCache()->glyphFormat()) {
+ case QFontEngine::Format_A32:
+ pipelineState->shaders.ps = g_PS_TextMask24;
+ pipelineState->shaders.psSize = sizeof(g_PS_TextMask24);
+ break;
+ case QFontEngine::Format_ARGB:
+ pipelineState->shaders.ps = g_PS_TextMask32;
+ pipelineState->shaders.psSize = sizeof(g_PS_TextMask32);
+ break;
+ default:
+ pipelineState->shaders.ps = g_PS_TextMask8;
+ pipelineState->shaders.psSize = sizeof(g_PS_TextMask8);
+ break;
+ }
+ } else if (m_styleType == Outlined) {
+ pipelineState->shaders.vs = g_VS_OutlinedText;
+ pipelineState->shaders.vsSize = sizeof(g_VS_OutlinedText);
+ pipelineState->shaders.ps = g_PS_OutlinedText;
+ pipelineState->shaders.psSize = sizeof(g_PS_OutlinedText);
+ } else {
+ pipelineState->shaders.vs = g_VS_StyledText;
+ pipelineState->shaders.vsSize = sizeof(g_VS_StyledText);
+ pipelineState->shaders.ps = g_PS_StyledText;
+ pipelineState->shaders.psSize = sizeof(g_PS_StyledText);
+ }
+
+ pipelineState->shaders.rootSig.textureViews.resize(1);
+}
+
+QSGD3D12Material::UpdateResults QSGD3D12TextMaterial::updatePipeline(const RenderState &state,
+ QSGD3D12PipelineState *pipelineState,
+ ExtraState *extraState,
+ quint8 *constantBuffer)
+{
+ QSGD3D12Material::UpdateResults r = 0;
+ quint8 *p = constantBuffer;
+
+ if (glyphCache()->glyphFormat() == QFontEngine::Format_A32) {
+ // can freely change the state due to the way type() works
+ pipelineState->blend = QSGD3D12PipelineState::BlendColor;
+ extraState->blendFactor = m_color;
+ r |= UpdatedBlendFactor; // must be set always as this affects the command list
+ }
+
+ if (state.isMatrixDirty()) {
+ memcpy(p, state.combinedMatrix().constData(), TEXT_CB_SIZE_0);
+ r |= UpdatedConstantBuffer;
+ }
+ p += TEXT_CB_SIZE_0;
+
+ if (state.isCachedMaterialDataDirty() || m_lastGlyphCacheSize != glyphCache()->currentSize()) {
+ m_lastGlyphCacheSize = glyphCache()->currentSize();
+ const float textureScale[2] = { 1.0f / m_lastGlyphCacheSize.width(),
+ 1.0f / m_lastGlyphCacheSize.height() };
+ memcpy(p, textureScale, TEXT_CB_SIZE_1);
+ r |= UpdatedConstantBuffer;
+ }
+ p += TEXT_CB_SIZE_1;
+
+ const float dpr = m_rc->engine()->windowDevicePixelRatio();
+ if (state.isCachedMaterialDataDirty() || m_lastDpr != dpr) {
+ m_lastDpr = dpr;
+ memcpy(p, &dpr, TEXT_CB_SIZE_2);
+ r |= UpdatedConstantBuffer;
+ }
+ p += TEXT_CB_SIZE_2;
+
+ if (state.isOpacityDirty() || m_lastColor != m_color) {
+ m_lastColor = m_color;
+ if (glyphCache()->glyphFormat() == QFontEngine::Format_A32) {
+ const QVector4D color = qsg_premultiply(m_color, state.opacity());
+ const float alpha = color.w();
+ memcpy(p, &alpha, TEXT_CB_SIZE_3);
+ } else if (glyphCache()->glyphFormat() == QFontEngine::Format_ARGB) {
+ const float opacity = m_color.w() * state.opacity();
+ memcpy(p, &opacity, TEXT_CB_SIZE_3);
+ } else {
+ const QVector4D color = qsg_premultiply(m_color, state.opacity());
+ const float f[4] = { color.x(), color.y(), color.z(), color.w() };
+ memcpy(p + TEXT_CB_SIZE_3, f, TEXT_CB_SIZE_4);
+ }
+ r |= UpdatedConstantBuffer;
+ }
+ p += TEXT_CB_SIZE_3 + TEXT_CB_SIZE_4;
+
+ if (m_styleType == Styled && (state.isCachedMaterialDataDirty() || m_lastStyleShift != m_styleShift)) {
+ m_lastStyleShift = m_styleShift;
+ const float f[2] = { m_styleShift.x(), m_styleShift.y() };
+ memcpy(p, f, TEXT_CB_SIZE_5);
+ r |= UpdatedConstantBuffer;
+ }
+ p += TEXT_CB_SIZE_5 + TEXT_CB_SIZE_5_PADDING;
+
+ if ((m_styleType == Styled || m_styleType == Outlined)
+ && (state.isOpacityDirty() || m_lastStyleColor != m_styleColor)) {
+ m_lastStyleColor = m_styleColor;
+ const QVector4D color = qsg_premultiply(m_styleColor, state.opacity());
+ const float f[4] = { color.x(), color.y(), color.z(), color.w() };
+ memcpy(p, f, TEXT_CB_SIZE_6);
+ r |= UpdatedConstantBuffer;
+ }
+
+ QSGD3D12TextureView &tv(pipelineState->shaders.rootSig.textureViews[0]);
+ tv.filter = QSGD3D12TextureView::FilterNearest;
+ tv.addressModeHoriz = QSGD3D12TextureView::AddressClamp;
+ tv.addressModeVert = QSGD3D12TextureView::AddressClamp;
+
+ glyphCache()->useTexture();
+
+ return r;
+}
+
+void QSGD3D12TextMaterial::populate(const QPointF &p,
+ const QVector<quint32> &glyphIndexes,
+ const QVector<QPointF> &glyphPositions,
+ QSGGeometry *geometry,
+ QRectF *boundingRect,
+ QPointF *baseLine,
+ const QMargins &margins)
+{
+ Q_ASSERT(m_font.isValid());
+ QVector<QFixedPoint> fixedPointPositions;
+ const int glyphPositionsSize = glyphPositions.size();
+ fixedPointPositions.reserve(glyphPositionsSize);
+ for (int i=0; i < glyphPositionsSize; ++i)
+ fixedPointPositions.append(QFixedPoint::fromPointF(glyphPositions.at(i)));
+
+ QSGD3D12GlyphCache *cache = glyphCache();
+ QRawFontPrivate *fontD = QRawFontPrivate::get(m_font);
+ cache->populate(fontD->fontEngine, glyphIndexes.size(), glyphIndexes.constData(),
+ fixedPointPositions.data());
+ cache->fillInPendingGlyphs();
+
+ int margin = fontD->fontEngine->glyphMargin(cache->glyphFormat());
+
+ float glyphCacheScaleX = cache->transform().m11();
+ float glyphCacheScaleY = cache->transform().m22();
+ float glyphCacheInverseScaleX = 1.0 / glyphCacheScaleX;
+ float glyphCacheInverseScaleY = 1.0 / glyphCacheScaleY;
+
+ Q_ASSERT(geometry->indexType() == QSGGeometry::TypeUnsignedShort);
+ geometry->allocate(glyphIndexes.size() * 4, glyphIndexes.size() * 6);
+ QVector4D *vp = reinterpret_cast<QVector4D *>(geometry->vertexDataAsTexturedPoint2D());
+ Q_ASSERT(geometry->sizeOfVertex() == sizeof(QVector4D));
+ ushort *ip = geometry->indexDataAsUShort();
+
+ QPointF position(p.x(), p.y() - m_font.ascent());
+ bool supportsSubPixelPositions = fontD->fontEngine->supportsSubPixelPositions();
+ for (int i = 0; i < glyphIndexes.size(); ++i) {
+ QFixed subPixelPosition;
+ if (supportsSubPixelPositions)
+ subPixelPosition = fontD->fontEngine->subPixelPositionForX(QFixed::fromReal(glyphPositions.at(i).x()));
+
+ QTextureGlyphCache::GlyphAndSubPixelPosition glyph(glyphIndexes.at(i), subPixelPosition);
+ const QTextureGlyphCache::Coord &c = cache->coords.value(glyph);
+
+ QPointF glyphPosition = glyphPositions.at(i) + position;
+ float x = (qFloor(glyphPosition.x() * glyphCacheScaleX) * glyphCacheInverseScaleX)
+ + (c.baseLineX * glyphCacheInverseScaleX) - margin;
+ float y = (qRound(glyphPosition.y() * glyphCacheScaleY) * glyphCacheInverseScaleY)
+ - (c.baseLineY * glyphCacheInverseScaleY) - margin;
+
+ float w = c.w * glyphCacheInverseScaleX;
+ float h = c.h * glyphCacheInverseScaleY;
+
+ *boundingRect |= QRectF(x + margin, y + margin, w, h);
+
+ float cx1 = x - margins.left();
+ float cx2 = x + w + margins.right();
+ float cy1 = y - margins.top();
+ float cy2 = y + h + margins.bottom();
+
+ float tx1 = c.x - margins.left();
+ float tx2 = c.x + c.w + margins.right();
+ float ty1 = c.y - margins.top();
+ float ty2 = c.y + c.h + margins.bottom();
+
+ if (baseLine->isNull())
+ *baseLine = glyphPosition;
+
+ vp[4 * i + 0] = QVector4D(cx1, cy1, tx1, ty1);
+ vp[4 * i + 1] = QVector4D(cx2, cy1, tx2, ty1);
+ vp[4 * i + 2] = QVector4D(cx1, cy2, tx1, ty2);
+ vp[4 * i + 3] = QVector4D(cx2, cy2, tx2, ty2);
+
+ int o = i * 4;
+ ip[6 * i + 0] = o + 0;
+ ip[6 * i + 1] = o + 2;
+ ip[6 * i + 2] = o + 3;
+ ip[6 * i + 3] = o + 3;
+ ip[6 * i + 4] = o + 1;
+ ip[6 * i + 5] = o + 0;
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12builtinmaterials_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12builtinmaterials_p.h
new file mode 100644
index 0000000000..6786d8fc06
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12builtinmaterials_p.h
@@ -0,0 +1,236 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGD3D12BUILTINMATERIALS_P_H
+#define QSGD3D12BUILTINMATERIALS_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 "qsgd3d12material_p.h"
+#include "qsgd3d12glyphcache_p.h"
+#include <private/qsgtexture_p.h>
+#include <QRawFont>
+
+QT_BEGIN_NAMESPACE
+
+class QSGD3D12RenderContext;
+
+class QSGD3D12VertexColorMaterial : public QSGD3D12Material
+{
+public:
+ QSGMaterialType *type() const override;
+ int compare(const QSGMaterial *other) const override;
+
+ int constantBufferSize() const override;
+ void preparePipeline(QSGD3D12PipelineState *pipelineState) override;
+ UpdateResults updatePipeline(const RenderState &state,
+ QSGD3D12PipelineState *pipelineState,
+ ExtraState *extraState,
+ quint8 *constantBuffer) override;
+
+private:
+ static QSGMaterialType mtype;
+};
+
+class QSGD3D12SmoothColorMaterial : public QSGD3D12Material
+{
+public:
+ QSGD3D12SmoothColorMaterial();
+ QSGMaterialType *type() const override;
+ int compare(const QSGMaterial *other) const override;
+
+ int constantBufferSize() const override;
+ void preparePipeline(QSGD3D12PipelineState *pipelineState) override;
+ UpdateResults updatePipeline(const RenderState &state,
+ QSGD3D12PipelineState *pipelineState,
+ ExtraState *extraState,
+ quint8 *constantBuffer) override;
+
+private:
+ static QSGMaterialType mtype;
+};
+
+class QSGD3D12TextureMaterial : public QSGD3D12Material
+{
+public:
+ QSGMaterialType *type() const override;
+ int compare(const QSGMaterial *other) const override;
+
+ int constantBufferSize() const override;
+ void preparePipeline(QSGD3D12PipelineState *pipelineState) override;
+ UpdateResults updatePipeline(const RenderState &state,
+ QSGD3D12PipelineState *pipelineState,
+ ExtraState *extraState,
+ quint8 *constantBuffer) override;
+
+ void setTexture(QSGTexture *texture) { m_texture = texture; }
+ QSGTexture *texture() const { return m_texture; }
+
+ void setMipmapFiltering(QSGTexture::Filtering filter) { m_mipmap_filtering = filter; }
+ QSGTexture::Filtering mipmapFiltering() const { return m_mipmap_filtering; }
+
+ void setFiltering(QSGTexture::Filtering filter) { m_filtering = filter; }
+ QSGTexture::Filtering filtering() const { return m_filtering; }
+
+ void setHorizontalWrapMode(QSGTexture::WrapMode hwrap) { m_horizontal_wrap = hwrap; }
+ QSGTexture::WrapMode horizontalWrapMode() const { return m_horizontal_wrap; }
+
+ void setVerticalWrapMode(QSGTexture::WrapMode vwrap) { m_vertical_wrap = vwrap; }
+ QSGTexture::WrapMode verticalWrapMode() const { return m_vertical_wrap; }
+
+private:
+ static QSGMaterialType mtype;
+
+ QSGTexture *m_texture = nullptr;
+ QSGTexture::Filtering m_filtering = QSGTexture::Nearest;
+ QSGTexture::Filtering m_mipmap_filtering = QSGTexture::None;
+ QSGTexture::WrapMode m_horizontal_wrap = QSGTexture::ClampToEdge;
+ QSGTexture::WrapMode m_vertical_wrap = QSGTexture::ClampToEdge;
+};
+
+class QSGD3D12SmoothTextureMaterial : public QSGD3D12Material
+{
+public:
+ QSGD3D12SmoothTextureMaterial();
+
+ QSGMaterialType *type() const override;
+ int compare(const QSGMaterial *other) const override;
+
+ int constantBufferSize() const override;
+ void preparePipeline(QSGD3D12PipelineState *pipelineState) override;
+ UpdateResults updatePipeline(const RenderState &state,
+ QSGD3D12PipelineState *pipelineState,
+ ExtraState *extraState,
+ quint8 *constantBuffer) override;
+
+ void setTexture(QSGTexture *texture) { m_texture = texture; }
+ QSGTexture *texture() const { return m_texture; }
+
+ void setMipmapFiltering(QSGTexture::Filtering filter) { m_mipmap_filtering = filter; }
+ QSGTexture::Filtering mipmapFiltering() const { return m_mipmap_filtering; }
+
+ void setFiltering(QSGTexture::Filtering filter) { m_filtering = filter; }
+ QSGTexture::Filtering filtering() const { return m_filtering; }
+
+ void setHorizontalWrapMode(QSGTexture::WrapMode hwrap) { m_horizontal_wrap = hwrap; }
+ QSGTexture::WrapMode horizontalWrapMode() const { return m_horizontal_wrap; }
+
+ void setVerticalWrapMode(QSGTexture::WrapMode vwrap) { m_vertical_wrap = vwrap; }
+ QSGTexture::WrapMode verticalWrapMode() const { return m_vertical_wrap; }
+
+private:
+ static QSGMaterialType mtype;
+
+ QSGTexture *m_texture = nullptr;
+ QSGTexture::Filtering m_filtering = QSGTexture::Nearest;
+ QSGTexture::Filtering m_mipmap_filtering = QSGTexture::None;
+ QSGTexture::WrapMode m_horizontal_wrap = QSGTexture::ClampToEdge;
+ QSGTexture::WrapMode m_vertical_wrap = QSGTexture::ClampToEdge;
+};
+
+class QSGD3D12TextMaterial : public QSGD3D12Material
+{
+public:
+ enum StyleType {
+ Normal,
+ Styled,
+ Outlined,
+
+ NStyleTypes
+ };
+ QSGD3D12TextMaterial(StyleType styleType, QSGD3D12RenderContext *rc, const QRawFont &font,
+ QFontEngine::GlyphFormat glyphFormat = QFontEngine::Format_None);
+
+ QSGMaterialType *type() const override;
+ int compare(const QSGMaterial *other) const override;
+
+ int constantBufferSize() const override;
+ void preparePipeline(QSGD3D12PipelineState *pipelineState) override;
+ UpdateResults updatePipeline(const RenderState &state,
+ QSGD3D12PipelineState *pipelineState,
+ ExtraState *extraState,
+ quint8 *constantBuffer) 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; }
+ const QVector4D &color() const { return m_color; }
+
+ void setStyleShift(const QVector2D &shift) { m_styleShift = shift; }
+ const QVector2D &styleShift() const { return m_styleShift; }
+
+ void setStyleColor(const QColor &c) { m_styleColor = QVector4D(c.redF(), c.greenF(), c.blueF(), c.alphaF()); }
+ void setStyleColor(const QVector4D &color) { m_styleColor = color; }
+ const QVector4D &styleColor() const { return m_styleColor; }
+
+ void populate(const QPointF &position,
+ const QVector<quint32> &glyphIndexes, const QVector<QPointF> &glyphPositions,
+ QSGGeometry *geometry, QRectF *boundingRect, QPointF *baseLine,
+ const QMargins &margins = QMargins(0, 0, 0, 0));
+
+ QSGD3D12GlyphCache *glyphCache() const { return static_cast<QSGD3D12GlyphCache *>(m_glyphCache.data()); }
+
+private:
+ static const int NTextMaterialTypes = NStyleTypes * 2;
+ static QSGMaterialType mtype[NTextMaterialTypes];
+ StyleType m_styleType;
+ QSGD3D12RenderContext *m_rc;
+ QVector4D m_color;
+ QVector2D m_styleShift;
+ QVector4D m_styleColor;
+ QRawFont m_font;
+ QExplicitlySharedDataPointer<QFontEngineGlyphCache> m_glyphCache;
+ QSize m_lastGlyphCacheSize;
+ float m_lastDpr = 0;
+ QVector4D m_lastColor;
+ QVector2D m_lastStyleShift;
+ QVector4D m_lastStyleColor;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGD3D12BUILTINMATERIALS_P_H
diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12context.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12context.cpp
new file mode 100644
index 0000000000..ce44bc7a38
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12context.cpp
@@ -0,0 +1,124 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgd3d12context_p.h"
+#include "qsgd3d12rendercontext_p.h"
+#include "qsgd3d12rectanglenode_p.h"
+#include "qsgd3d12imagenode_p.h"
+#include "qsgd3d12glyphnode_p.h"
+#include "qsgd3d12layer_p.h"
+#include "qsgd3d12shadereffectnode_p.h"
+#include "qsgd3d12painternode_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QSGRenderContext *QSGD3D12Context::createRenderContext()
+{
+ return new QSGD3D12RenderContext(this);
+}
+
+QSGRectangleNode *QSGD3D12Context::createRectangleNode()
+{
+ return new QSGD3D12RectangleNode;
+}
+
+QSGImageNode *QSGD3D12Context::createImageNode()
+{
+ return new QSGD3D12ImageNode;
+}
+
+QSGPainterNode *QSGD3D12Context::createPainterNode(QQuickPaintedItem *item)
+{
+ return new QSGD3D12PainterNode(item);
+}
+
+QSGGlyphNode *QSGD3D12Context::createGlyphNode(QSGRenderContext *renderContext, bool preferNativeGlyphNode)
+{
+ Q_UNUSED(preferNativeGlyphNode);
+ // ### distance field text rendering is not supported atm
+
+ QSGD3D12RenderContext *rc = static_cast<QSGD3D12RenderContext *>(renderContext);
+ return new QSGD3D12GlyphNode(rc);
+}
+
+QSGNinePatchNode *QSGD3D12Context::createNinePatchNode()
+{
+ return nullptr;
+}
+
+QSGLayer *QSGD3D12Context::createLayer(QSGRenderContext *renderContext)
+{
+ QSGD3D12RenderContext *rc = static_cast<QSGD3D12RenderContext *>(renderContext);
+ return new QSGD3D12Layer(rc);
+}
+
+QSGGuiThreadShaderEffectManager *QSGD3D12Context::createGuiThreadShaderEffectManager()
+{
+ return new QSGD3D12GuiThreadShaderEffectManager;
+}
+
+QSGShaderEffectNode *QSGD3D12Context::createShaderEffectNode(QSGRenderContext *renderContext,
+ QSGGuiThreadShaderEffectManager *mgr)
+{
+ QSGD3D12RenderContext *rc = static_cast<QSGD3D12RenderContext *>(renderContext);
+ QSGD3D12GuiThreadShaderEffectManager *dmgr = static_cast<QSGD3D12GuiThreadShaderEffectManager *>(mgr);
+ return new QSGD3D12ShaderEffectNode(rc, dmgr);
+}
+
+QSize QSGD3D12Context::minimumFBOSize() const
+{
+ return QSize(16, 16);
+}
+
+QSurfaceFormat QSGD3D12Context::defaultSurfaceFormat() const
+{
+ return QSurfaceFormat::defaultFormat();
+}
+
+QSGRendererInterface *QSGD3D12Context::rendererInterface(QSGRenderContext *renderContext)
+{
+ QSGD3D12RenderContext *rc = static_cast<QSGD3D12RenderContext *>(renderContext);
+ if (!rc->engine()) {
+ qWarning("No D3D12 engine available yet (no render thread due to window not exposed?)");
+ return nullptr;
+ }
+ return rc->engine();
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12context_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12context_p.h
new file mode 100644
index 0000000000..0564c4edac
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12context_p.h
@@ -0,0 +1,80 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGD3D12CONTEXT_P_H
+#define QSGD3D12CONTEXT_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qsgcontext_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QSGD3D12Context : public QSGContext
+{
+public:
+ QSGD3D12Context(QObject *parent = 0) : QSGContext(parent) { }
+
+ QSGRenderContext *createRenderContext() override;
+ QSGRectangleNode *createRectangleNode() override;
+ QSGImageNode *createImageNode() override;
+ QSGPainterNode *createPainterNode(QQuickPaintedItem *item) override;
+ QSGGlyphNode *createGlyphNode(QSGRenderContext *renderContext, bool preferNativeGlyphNode) override;
+ QSGNinePatchNode *createNinePatchNode() override;
+ QSGLayer *createLayer(QSGRenderContext *renderContext) override;
+ QSGGuiThreadShaderEffectManager *createGuiThreadShaderEffectManager() override;
+ QSGShaderEffectNode *createShaderEffectNode(QSGRenderContext *renderContext,
+ QSGGuiThreadShaderEffectManager *mgr) override;
+ QSize minimumFBOSize() const override;
+ QSurfaceFormat defaultSurfaceFormat() const override;
+ QSGRendererInterface *rendererInterface(QSGRenderContext *renderContext) override;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGD3D12CONTEXT_P_H
diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp
new file mode 100644
index 0000000000..4580e58edd
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp
@@ -0,0 +1,3075 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgd3d12engine_p.h"
+#include "qsgd3d12engine_p_p.h"
+#include "cs_mipmapgen.hlslh"
+#include <QString>
+#include <QColor>
+#include <qmath.h>
+#include <QtCore/private/qsimd_p.h>
+
+// Comment out to disable DeviceLossTester functionality in order to reduce
+// code size and improve startup perf a tiny bit.
+#define DEVLOSS_TEST
+
+#ifdef DEVLOSS_TEST
+#include "cs_tdr.hlslh"
+#endif
+
+QT_BEGIN_NAMESPACE
+
+// NOTE: Avoid categorized logging. It is slow.
+
+#define DECLARE_DEBUG_VAR(variable) \
+ static bool debug_ ## variable() \
+ { static bool value = qgetenv("QSG_RENDERER_DEBUG").contains(QT_STRINGIFY(variable)); return value; }
+
+DECLARE_DEBUG_VAR(render)
+
+static const int DEFAULT_SWAP_CHAIN_BUFFER_COUNT = 3;
+static const int DEFAULT_FRAME_IN_FLIGHT_COUNT = 2;
+static const int DEFAULT_WAITABLE_SWAP_CHAIN_MAX_LATENCY = 0;
+
+static const int MAX_DRAW_CALLS_PER_LIST = 128;
+
+static const int MAX_CACHED_ROOTSIG = 16;
+static const int MAX_CACHED_PSO = 64;
+
+static const int GPU_CBVSRVUAV_DESCRIPTORS = 512;
+
+static const int BUCKETS_PER_HEAP = 8; // must match freeMap
+static const int DESCRIPTORS_PER_BUCKET = 32; // the bit map (freeMap) is quint32
+static const int MAX_DESCRIPTORS_PER_HEAP = BUCKETS_PER_HEAP * DESCRIPTORS_PER_BUCKET;
+
+D3D12_CPU_DESCRIPTOR_HANDLE QSGD3D12CPUDescriptorHeapManager::allocate(D3D12_DESCRIPTOR_HEAP_TYPE type)
+{
+ D3D12_CPU_DESCRIPTOR_HANDLE h = {};
+ for (Heap &heap : m_heaps) {
+ if (heap.type == type) {
+ for (int bucket = 0; bucket < _countof(heap.freeMap); ++bucket)
+ if (heap.freeMap[bucket]) {
+ unsigned long freePos = _bit_scan_forward(heap.freeMap[bucket]);
+ heap.freeMap[bucket] &= ~(1UL << freePos);
+ if (Q_UNLIKELY(debug_render()))
+ qDebug("descriptor handle type %x reserve in bucket %d index %d", type, bucket, freePos);
+ freePos += bucket * DESCRIPTORS_PER_BUCKET;
+ h = heap.start;
+ h.ptr += freePos * heap.handleSize;
+ return h;
+ }
+ }
+ }
+
+ Heap heap;
+ heap.type = type;
+ heap.handleSize = m_handleSizes[type];
+
+ D3D12_DESCRIPTOR_HEAP_DESC heapDesc = {};
+ heapDesc.NumDescriptors = MAX_DESCRIPTORS_PER_HEAP;
+ heapDesc.Type = type;
+ // The heaps created here are _never_ shader-visible.
+
+ HRESULT hr = m_device->CreateDescriptorHeap(&heapDesc, IID_PPV_ARGS(&heap.heap));
+ if (FAILED(hr)) {
+ qWarning("Failed to create heap with type 0x%x: %x", type, hr);
+ return h;
+ }
+
+ heap.start = heap.heap->GetCPUDescriptorHandleForHeapStart();
+
+ if (Q_UNLIKELY(debug_render()))
+ qDebug("new descriptor heap, type %x, start %llu", type, heap.start.ptr);
+
+ heap.freeMap[0] = 0xFFFFFFFE;
+ for (int i = 1; i < _countof(heap.freeMap); ++i)
+ heap.freeMap[i] = 0xFFFFFFFF;
+
+ h = heap.start;
+
+ m_heaps.append(heap);
+
+ return h;
+}
+
+void QSGD3D12CPUDescriptorHeapManager::release(D3D12_CPU_DESCRIPTOR_HANDLE handle, D3D12_DESCRIPTOR_HEAP_TYPE type)
+{
+ for (Heap &heap : m_heaps) {
+ if (heap.type == type
+ && handle.ptr >= heap.start.ptr
+ && handle.ptr < heap.start.ptr + heap.handleSize * MAX_DESCRIPTORS_PER_HEAP) {
+ unsigned long pos = (handle.ptr - heap.start.ptr) / heap.handleSize;
+ const int bucket = pos / DESCRIPTORS_PER_BUCKET;
+ const int indexInBucket = pos - bucket * DESCRIPTORS_PER_BUCKET;
+ heap.freeMap[bucket] |= 1UL << indexInBucket;
+ if (Q_UNLIKELY(debug_render()))
+ qDebug("free descriptor handle type %x bucket %d index %d", type, bucket, indexInBucket);
+ return;
+ }
+ }
+ qWarning("QSGD3D12CPUDescriptorHeapManager: Attempted to release untracked descriptor handle %llu of type %d", handle.ptr, type);
+}
+
+void QSGD3D12CPUDescriptorHeapManager::initialize(ID3D12Device *device)
+{
+ m_device = device;
+
+ for (int i = 0; i < D3D12_DESCRIPTOR_HEAP_TYPE_NUM_TYPES; ++i)
+ m_handleSizes[i] = m_device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE(i));
+}
+
+void QSGD3D12CPUDescriptorHeapManager::releaseResources()
+{
+ for (Heap &heap : m_heaps)
+ heap.heap = nullptr;
+
+ m_heaps.clear();
+
+ m_device = nullptr;
+}
+
+// One device per process, one everything else (engine) per window.
+Q_GLOBAL_STATIC(QSGD3D12DeviceManager, deviceManager)
+
+static void getHardwareAdapter(IDXGIFactory1 *factory, IDXGIAdapter1 **outAdapter)
+{
+ const D3D_FEATURE_LEVEL fl = D3D_FEATURE_LEVEL_11_0;
+ ComPtr<IDXGIAdapter1> adapter;
+ DXGI_ADAPTER_DESC1 desc;
+
+ for (int adapterIndex = 0; factory->EnumAdapters1(adapterIndex, &adapter) != DXGI_ERROR_NOT_FOUND; ++adapterIndex) {
+ DXGI_ADAPTER_DESC1 desc;
+ adapter->GetDesc1(&desc);
+ const QString name = QString::fromUtf16((char16_t *) desc.Description);
+ qDebug("Adapter %d: '%s' (flags 0x%x)", adapterIndex, qPrintable(name), desc.Flags);
+ }
+
+ if (qEnvironmentVariableIsSet("QT_D3D_ADAPTER_INDEX")) {
+ const int adapterIndex = qEnvironmentVariableIntValue("QT_D3D_ADAPTER_INDEX");
+ if (SUCCEEDED(factory->EnumAdapters1(adapterIndex, &adapter))
+ && SUCCEEDED(D3D12CreateDevice(adapter.Get(), fl, _uuidof(ID3D12Device), nullptr))) {
+ adapter->GetDesc1(&desc);
+ const QString name = QString::fromUtf16((char16_t *) desc.Description);
+ qDebug("Using requested adapter '%s'", qPrintable(name));
+ *outAdapter = adapter.Detach();
+ return;
+ }
+ }
+
+ for (int adapterIndex = 0; factory->EnumAdapters1(adapterIndex, &adapter) != DXGI_ERROR_NOT_FOUND; ++adapterIndex) {
+ adapter->GetDesc1(&desc);
+ if (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE)
+ continue;
+
+ if (SUCCEEDED(D3D12CreateDevice(adapter.Get(), fl, _uuidof(ID3D12Device), nullptr))) {
+ const QString name = QString::fromUtf16((char16_t *) desc.Description);
+ qDebug("Using adapter '%s'", qPrintable(name));
+ break;
+ }
+ }
+
+ *outAdapter = adapter.Detach();
+}
+
+ID3D12Device *QSGD3D12DeviceManager::ref()
+{
+ ensureCreated();
+ m_ref.ref();
+ return m_device.Get();
+}
+
+void QSGD3D12DeviceManager::unref()
+{
+ if (!m_ref.deref()) {
+ if (Q_UNLIKELY(debug_render()))
+ qDebug("destroying d3d device");
+ m_device = nullptr;
+ m_factory = nullptr;
+ }
+}
+
+void QSGD3D12DeviceManager::deviceLossDetected()
+{
+ for (DeviceLossObserver *observer : qAsConst(m_observers))
+ observer->deviceLost();
+
+ // Nothing else to do here. All windows are expected to release their
+ // resources and call unref() in response immediately.
+}
+
+IDXGIFactory4 *QSGD3D12DeviceManager::dxgi()
+{
+ ensureCreated();
+ return m_factory.Get();
+}
+
+void QSGD3D12DeviceManager::ensureCreated()
+{
+ if (m_device)
+ return;
+
+ HRESULT hr = CreateDXGIFactory2(0, IID_PPV_ARGS(&m_factory));
+ if (FAILED(hr)) {
+ qWarning("Failed to create DXGI: 0x%x", hr);
+ return;
+ }
+
+ ComPtr<IDXGIAdapter1> adapter;
+ getHardwareAdapter(m_factory.Get(), &adapter);
+
+ bool warp = true;
+ if (adapter) {
+ HRESULT hr = D3D12CreateDevice(adapter.Get(), D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&m_device));
+ if (SUCCEEDED(hr))
+ warp = false;
+ else
+ qWarning("Failed to create device: 0x%x", hr);
+ }
+
+ if (warp) {
+ qDebug("Using WARP");
+ m_factory->EnumWarpAdapter(IID_PPV_ARGS(&adapter));
+ HRESULT hr = D3D12CreateDevice(adapter.Get(), D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&m_device));
+ if (FAILED(hr)) {
+ qWarning("Failed to create WARP device: 0x%x", hr);
+ return;
+ }
+ }
+
+ ComPtr<IDXGIAdapter3> adapter3;
+ if (SUCCEEDED(adapter.As(&adapter3))) {
+ DXGI_QUERY_VIDEO_MEMORY_INFO vidMemInfo;
+ if (SUCCEEDED(adapter3->QueryVideoMemoryInfo(0, DXGI_MEMORY_SEGMENT_GROUP_LOCAL, &vidMemInfo))) {
+ qDebug("Video memory info: LOCAL: Budget %llu KB CurrentUsage %llu KB AvailableForReservation %llu KB CurrentReservation %llu KB",
+ vidMemInfo.Budget / 1024, vidMemInfo.CurrentUsage / 1024,
+ vidMemInfo.AvailableForReservation / 1024, vidMemInfo.CurrentReservation / 1024);
+ }
+ if (SUCCEEDED(adapter3->QueryVideoMemoryInfo(0, DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL, &vidMemInfo))) {
+ qDebug("Video memory info: NON-LOCAL: Budget %llu KB CurrentUsage %llu KB AvailableForReservation %llu KB CurrentReservation %llu KB",
+ vidMemInfo.Budget / 1024, vidMemInfo.CurrentUsage / 1024,
+ vidMemInfo.AvailableForReservation / 1024, vidMemInfo.CurrentReservation / 1024);
+ }
+ }
+}
+
+void QSGD3D12DeviceManager::registerDeviceLossObserver(DeviceLossObserver *observer)
+{
+ if (!m_observers.contains(observer))
+ m_observers.append(observer);
+}
+
+QSGD3D12Engine::QSGD3D12Engine()
+{
+ d = new QSGD3D12EnginePrivate;
+}
+
+QSGD3D12Engine::~QSGD3D12Engine()
+{
+ d->waitGPU();
+ d->releaseResources();
+ delete d;
+}
+
+bool QSGD3D12Engine::attachToWindow(WId window, const QSize &size, float dpr, int surfaceFormatSamples)
+{
+ if (d->isInitialized()) {
+ qWarning("QSGD3D12Engine: Cannot attach active engine to window");
+ return false;
+ }
+
+ d->initialize(window, size, dpr, surfaceFormatSamples);
+ return d->isInitialized();
+}
+
+void QSGD3D12Engine::releaseResources()
+{
+ d->releaseResources();
+}
+
+bool QSGD3D12Engine::hasResources() const
+{
+ // An explicit releaseResources() or a device loss results in initialized == false.
+ return d->isInitialized();
+}
+
+void QSGD3D12Engine::setWindowSize(const QSize &size, float dpr)
+{
+ d->setWindowSize(size, dpr);
+}
+
+WId QSGD3D12Engine::window() const
+{
+ return d->currentWindow();
+}
+
+QSize QSGD3D12Engine::windowSize() const
+{
+ return d->currentWindowSize();
+}
+
+float QSGD3D12Engine::windowDevicePixelRatio() const
+{
+ return d->currentWindowDpr();
+}
+
+uint QSGD3D12Engine::windowSamples() const
+{
+ return d->currentWindowSamples();
+}
+
+void QSGD3D12Engine::beginFrame()
+{
+ d->beginFrame();
+}
+
+void QSGD3D12Engine::endFrame()
+{
+ d->endFrame();
+}
+
+void QSGD3D12Engine::beginLayer()
+{
+ d->beginLayer();
+}
+
+void QSGD3D12Engine::endLayer()
+{
+ d->endLayer();
+}
+
+void QSGD3D12Engine::invalidateCachedFrameState()
+{
+ d->invalidateCachedFrameState();
+}
+
+void QSGD3D12Engine::restoreFrameState(bool minimal)
+{
+ d->restoreFrameState(minimal);
+}
+
+void QSGD3D12Engine::finalizePipeline(const QSGD3D12PipelineState &pipelineState)
+{
+ d->finalizePipeline(pipelineState);
+}
+
+uint QSGD3D12Engine::genBuffer()
+{
+ return d->genBuffer();
+}
+
+void QSGD3D12Engine::releaseBuffer(uint id)
+{
+ d->releaseBuffer(id);
+}
+
+void QSGD3D12Engine::resetBuffer(uint id, const quint8 *data, int size)
+{
+ d->resetBuffer(id, data, size);
+}
+
+void QSGD3D12Engine::markBufferDirty(uint id, int offset, int size)
+{
+ d->markBufferDirty(id, offset, size);
+}
+
+void QSGD3D12Engine::queueViewport(const QRect &rect)
+{
+ d->queueViewport(rect);
+}
+
+void QSGD3D12Engine::queueScissor(const QRect &rect)
+{
+ d->queueScissor(rect);
+}
+
+void QSGD3D12Engine::queueSetRenderTarget(uint id)
+{
+ d->queueSetRenderTarget(id);
+}
+
+void QSGD3D12Engine::queueClearRenderTarget(const QColor &color)
+{
+ d->queueClearRenderTarget(color);
+}
+
+void QSGD3D12Engine::queueClearDepthStencil(float depthValue, quint8 stencilValue, ClearFlags which)
+{
+ d->queueClearDepthStencil(depthValue, stencilValue, which);
+}
+
+void QSGD3D12Engine::queueSetBlendFactor(const QVector4D &factor)
+{
+ d->queueSetBlendFactor(factor);
+}
+
+void QSGD3D12Engine::queueSetStencilRef(quint32 ref)
+{
+ d->queueSetStencilRef(ref);
+}
+
+void QSGD3D12Engine::queueDraw(const DrawParams &params)
+{
+ d->queueDraw(params);
+}
+
+void QSGD3D12Engine::present()
+{
+ d->present();
+}
+
+void QSGD3D12Engine::waitGPU()
+{
+ d->waitGPU();
+}
+
+uint QSGD3D12Engine::genTexture()
+{
+ return d->genTexture();
+}
+
+void QSGD3D12Engine::createTexture(uint id, const QSize &size, QImage::Format format, TextureCreateFlags flags)
+{
+ d->createTexture(id, size, format, flags);
+}
+
+void QSGD3D12Engine::queueTextureResize(uint id, const QSize &size)
+{
+ d->queueTextureResize(id, size);
+}
+
+void QSGD3D12Engine::queueTextureUpload(uint id, const QImage &image, const QPoint &dstPos)
+{
+ d->queueTextureUpload(id, QVector<QImage>() << image, QVector<QPoint>() << dstPos);
+}
+
+void QSGD3D12Engine::queueTextureUpload(uint id, const QVector<QImage> &images, const QVector<QPoint> &dstPos)
+{
+ d->queueTextureUpload(id, images, dstPos);
+}
+
+void QSGD3D12Engine::releaseTexture(uint id)
+{
+ d->releaseTexture(id);
+}
+
+SIZE_T QSGD3D12Engine::textureSRV(uint id) const
+{
+ return d->textureSRV(id);
+}
+
+void QSGD3D12Engine::useTexture(uint id)
+{
+ d->useTexture(id);
+}
+
+uint QSGD3D12Engine::genRenderTarget()
+{
+ return d->genRenderTarget();
+}
+
+void QSGD3D12Engine::createRenderTarget(uint id, const QSize &size, const QVector4D &clearColor, uint samples)
+{
+ d->createRenderTarget(id, size, clearColor, samples);
+}
+
+void QSGD3D12Engine::releaseRenderTarget(uint id)
+{
+ d->releaseRenderTarget(id);
+}
+
+void QSGD3D12Engine::useRenderTargetAsTexture(uint id)
+{
+ d->useRenderTargetAsTexture(id);
+}
+
+uint QSGD3D12Engine::activeRenderTarget() const
+{
+ return d->activeRenderTarget();
+}
+
+QImage QSGD3D12Engine::executeAndWaitReadbackRenderTarget(uint id)
+{
+ return d->executeAndWaitReadbackRenderTarget(id);
+}
+
+void QSGD3D12Engine::simulateDeviceLoss()
+{
+ d->simulateDeviceLoss();
+}
+
+QSGRendererInterface::GraphicsAPI QSGD3D12Engine::graphicsAPI() const
+{
+ return Direct3D12;
+}
+
+void *QSGD3D12Engine::getResource(Resource resource) const
+{
+ return d->getResource(resource);
+}
+
+static inline quint32 alignedSize(quint32 size, quint32 byteAlign)
+{
+ return (size + byteAlign - 1) & ~(byteAlign - 1);
+}
+
+quint32 QSGD3D12Engine::alignedConstantBufferSize(quint32 size)
+{
+ return alignedSize(size, D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT);
+}
+
+QSGD3D12Format QSGD3D12Engine::toDXGIFormat(QSGGeometry::Type sgtype, int tupleSize, int *size)
+{
+ QSGD3D12Format format = FmtUnknown;
+
+ static const QSGD3D12Format formatMap_ub[] = { FmtUnknown,
+ FmtUNormByte,
+ FmtUNormByte2,
+ FmtUnknown,
+ FmtUNormByte4 };
+
+ static const QSGD3D12Format formatMap_f[] = { FmtUnknown,
+ FmtFloat,
+ FmtFloat2,
+ FmtFloat3,
+ FmtFloat4 };
+
+ switch (sgtype) {
+ case QSGGeometry::TypeUnsignedByte:
+ format = formatMap_ub[tupleSize];
+ if (size)
+ *size = tupleSize;
+ break;
+ case QSGGeometry::TypeFloat:
+ format = formatMap_f[tupleSize];
+ if (size)
+ *size = sizeof(float) * tupleSize;
+ break;
+
+ case QSGGeometry::TypeUnsignedShort:
+ format = FmtUnsignedShort;
+ if (size)
+ *size = sizeof(ushort) * tupleSize;
+ break;
+ case QSGGeometry::TypeUnsignedInt:
+ format = FmtUnsignedInt;
+ if (size)
+ *size = sizeof(uint) * tupleSize;
+ break;
+
+ case QSGGeometry::TypeByte:
+ case QSGGeometry::TypeInt:
+ case QSGGeometry::TypeShort:
+ qWarning("no mapping for GL type 0x%x", sgtype);
+ break;
+
+ default:
+ qWarning("unknown GL type 0x%x", sgtype);
+ break;
+ }
+
+ return format;
+}
+
+int QSGD3D12Engine::mipMapLevels(const QSize &size)
+{
+ return ceil(log2(qMax(size.width(), size.height()))) + 1;
+}
+
+inline static bool isPowerOfTwo(int x)
+{
+ // Assumption: x >= 1
+ return x == (x & -x);
+}
+
+QSize QSGD3D12Engine::mipMapAdjustedSourceSize(const QSize &size)
+{
+ if (size.isEmpty())
+ return size;
+
+ QSize adjustedSize = size;
+
+ // ### for now only power-of-two sizes are mipmap-capable
+ if (!isPowerOfTwo(size.width()))
+ adjustedSize.setWidth(qNextPowerOfTwo(size.width()));
+ if (!isPowerOfTwo(size.height()))
+ adjustedSize.setHeight(qNextPowerOfTwo(size.height()));
+
+ return adjustedSize;
+}
+
+void QSGD3D12EnginePrivate::releaseResources()
+{
+ if (!initialized)
+ return;
+
+ mipmapper.releaseResources();
+ devLossTest.releaseResources();
+
+ frameCommandList = nullptr;
+ copyCommandList = nullptr;
+
+ copyCommandAllocator = nullptr;
+ for (int i = 0; i < frameInFlightCount; ++i) {
+ frameCommandAllocator[i] = nullptr;
+ pframeData[i].gpuCbvSrvUavHeap = nullptr;
+ delete frameFence[i];
+ }
+
+ defaultDS = nullptr;
+ for (int i = 0; i < swapChainBufferCount; ++i) {
+ backBufferRT[i] = nullptr;
+ defaultRT[i] = nullptr;
+ }
+
+ psoCache.clear();
+ rootSigCache.clear();
+ buffers.clear();
+ textures.clear();
+ renderTargets.clear();
+
+ cpuDescHeapManager.releaseResources();
+
+ commandQueue = nullptr;
+ copyCommandQueue = nullptr;
+ swapChain = nullptr;
+
+ delete presentFence;
+ textureUploadFence = nullptr;
+
+ deviceManager()->unref();
+
+ initialized = false;
+
+ // 'window' must be kept, may just be a device loss
+}
+
+void QSGD3D12EnginePrivate::initialize(WId w, const QSize &size, float dpr, int surfaceFormatSamples)
+{
+ if (initialized)
+ return;
+
+ window = w;
+ windowSize = size;
+ windowDpr = dpr;
+ windowSamples = qMax(1, surfaceFormatSamples); // may be -1 or 0, whereas windowSamples is uint and >= 1
+
+ HWND hwnd = reinterpret_cast<HWND>(w);
+
+ swapChainBufferCount = qMin(qEnvironmentVariableIntValue("QT_D3D_BUFFER_COUNT"), MAX_SWAP_CHAIN_BUFFER_COUNT);
+ if (swapChainBufferCount < 2)
+ swapChainBufferCount = DEFAULT_SWAP_CHAIN_BUFFER_COUNT;
+
+ frameInFlightCount = qMin(qEnvironmentVariableIntValue("QT_D3D_FRAME_COUNT"), MAX_FRAME_IN_FLIGHT_COUNT);
+ if (frameInFlightCount < 1)
+ frameInFlightCount = DEFAULT_FRAME_IN_FLIGHT_COUNT;
+
+ static const char *latReqEnvVar = "QT_D3D_WAITABLE_SWAP_CHAIN_MAX_LATENCY";
+ if (!qEnvironmentVariableIsSet(latReqEnvVar))
+ waitableSwapChainMaxLatency = DEFAULT_WAITABLE_SWAP_CHAIN_MAX_LATENCY;
+ else
+ waitableSwapChainMaxLatency = qBound(0, qEnvironmentVariableIntValue(latReqEnvVar), 16);
+
+ qDebug("d3d12 engine init. swap chain buffer count %d, max frames prepared without blocking %d",
+ swapChainBufferCount, frameInFlightCount);
+ if (waitableSwapChainMaxLatency)
+ qDebug("Swap chain frame latency waitable object enabled. Frame latency is %d", waitableSwapChainMaxLatency);
+
+ if (qEnvironmentVariableIntValue("QT_D3D_DEBUG") != 0) {
+ qDebug("Enabling debug layer");
+ ComPtr<ID3D12Debug> debugController;
+ if (SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(&debugController))))
+ debugController->EnableDebugLayer();
+ }
+
+ QSGD3D12DeviceManager *dev = deviceManager();
+ device = dev->ref();
+ dev->registerDeviceLossObserver(this);
+
+ D3D12_COMMAND_QUEUE_DESC queueDesc = {};
+ queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
+ if (FAILED(device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&commandQueue)))) {
+ qWarning("Failed to create command queue");
+ return;
+ }
+
+ queueDesc.Type = D3D12_COMMAND_LIST_TYPE_COPY;
+ if (FAILED(device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&copyCommandQueue)))) {
+ qWarning("Failed to create copy command queue");
+ return;
+ }
+
+ DXGI_SWAP_CHAIN_DESC swapChainDesc = {};
+ swapChainDesc.BufferCount = swapChainBufferCount;
+ swapChainDesc.BufferDesc.Width = windowSize.width() * windowDpr;
+ swapChainDesc.BufferDesc.Height = windowSize.height() * windowDpr;
+ swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
+ swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
+ swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; // D3D12 requires the flip model
+ swapChainDesc.OutputWindow = hwnd;
+ swapChainDesc.SampleDesc.Count = 1; // Flip does not support MSAA so no choice here
+ swapChainDesc.Windowed = TRUE;
+ if (waitableSwapChainMaxLatency)
+ swapChainDesc.Flags = DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT;
+
+ ComPtr<IDXGISwapChain> baseSwapChain;
+ HRESULT hr = dev->dxgi()->CreateSwapChain(commandQueue.Get(), &swapChainDesc, &baseSwapChain);
+ if (FAILED(hr)) {
+ qWarning("Failed to create swap chain: 0x%x", hr);
+ return;
+ }
+ if (FAILED(baseSwapChain.As(&swapChain))) {
+ qWarning("Failed to cast swap chain");
+ return;
+ }
+
+ dev->dxgi()->MakeWindowAssociation(hwnd, DXGI_MWA_NO_ALT_ENTER);
+
+ if (waitableSwapChainMaxLatency) {
+ if (FAILED(swapChain->SetMaximumFrameLatency(waitableSwapChainMaxLatency)))
+ qWarning("Failed to set maximum frame latency to %d", waitableSwapChainMaxLatency);
+ swapEvent = swapChain->GetFrameLatencyWaitableObject();
+ }
+
+ for (int i = 0; i < frameInFlightCount; ++i) {
+ if (FAILED(device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&frameCommandAllocator[i])))) {
+ qWarning("Failed to create command allocator");
+ return;
+ }
+ }
+
+ if (FAILED(device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_COPY, IID_PPV_ARGS(&copyCommandAllocator)))) {
+ qWarning("Failed to create copy command allocator");
+ return;
+ }
+
+ for (int i = 0; i < frameInFlightCount; ++i) {
+ if (!createCbvSrvUavHeap(i, GPU_CBVSRVUAV_DESCRIPTORS))
+ return;
+ }
+
+ cpuDescHeapManager.initialize(device);
+
+ setupDefaultRenderTargets();
+
+ if (FAILED(device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, frameCommandAllocator[0].Get(),
+ nullptr, IID_PPV_ARGS(&frameCommandList)))) {
+ qWarning("Failed to create command list");
+ return;
+ }
+ // created in recording state, close it for now
+ frameCommandList->Close();
+
+ if (FAILED(device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_COPY, copyCommandAllocator.Get(),
+ nullptr, IID_PPV_ARGS(&copyCommandList)))) {
+ qWarning("Failed to create copy command list");
+ return;
+ }
+ copyCommandList->Close();
+
+ frameIndex = 0;
+
+ presentFence = createCPUWaitableFence();
+ for (int i = 0; i < frameInFlightCount; ++i)
+ frameFence[i] = createCPUWaitableFence();
+
+ if (FAILED(device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&textureUploadFence)))) {
+ qWarning("Failed to create fence");
+ return;
+ }
+
+ psoCache.setMaxCost(MAX_CACHED_PSO);
+ rootSigCache.setMaxCost(MAX_CACHED_ROOTSIG);
+
+ if (!mipmapper.initialize(this))
+ return;
+
+ if (!devLossTest.initialize(this))
+ return;
+
+ currentRenderTarget = 0;
+
+ initialized = true;
+}
+
+bool QSGD3D12EnginePrivate::createCbvSrvUavHeap(int pframeIndex, int descriptorCount)
+{
+ D3D12_DESCRIPTOR_HEAP_DESC gpuDescHeapDesc = {};
+ gpuDescHeapDesc.NumDescriptors = descriptorCount;
+ gpuDescHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
+ gpuDescHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
+
+ if (FAILED(device->CreateDescriptorHeap(&gpuDescHeapDesc, IID_PPV_ARGS(&pframeData[pframeIndex].gpuCbvSrvUavHeap)))) {
+ qWarning("Failed to create shader-visible CBV-SRV-UAV heap");
+ return false;
+ }
+
+ pframeData[pframeIndex].gpuCbvSrvUavHeapSize = descriptorCount;
+
+ return true;
+}
+
+DXGI_SAMPLE_DESC QSGD3D12EnginePrivate::makeSampleDesc(DXGI_FORMAT format, uint samples)
+{
+ DXGI_SAMPLE_DESC sampleDesc;
+ sampleDesc.Count = 1;
+ sampleDesc.Quality = 0;
+
+ if (samples > 1) {
+ D3D12_FEATURE_DATA_MULTISAMPLE_QUALITY_LEVELS msaaInfo = {};
+ msaaInfo.Format = format;
+ msaaInfo.SampleCount = samples;
+ if (SUCCEEDED(device->CheckFeatureSupport(D3D12_FEATURE_MULTISAMPLE_QUALITY_LEVELS, &msaaInfo, sizeof(msaaInfo)))) {
+ if (msaaInfo.NumQualityLevels > 0) {
+ sampleDesc.Count = samples;
+ sampleDesc.Quality = msaaInfo.NumQualityLevels - 1;
+ } else {
+ qWarning("No quality levels for multisampling with sample count %d", samples);
+ }
+ } else {
+ qWarning("Failed to query multisample quality levels for sample count %d", samples);
+ }
+ }
+
+ return sampleDesc;
+}
+
+ID3D12Resource *QSGD3D12EnginePrivate::createColorBuffer(D3D12_CPU_DESCRIPTOR_HANDLE viewHandle, const QSize &size,
+ const QVector4D &clearColor, uint samples)
+{
+ D3D12_CLEAR_VALUE clearValue = {};
+ clearValue.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
+ clearValue.Color[0] = clearColor.x();
+ clearValue.Color[1] = clearColor.y();
+ clearValue.Color[2] = clearColor.z();
+ clearValue.Color[3] = clearColor.w();
+
+ D3D12_HEAP_PROPERTIES heapProp = {};
+ heapProp.Type = D3D12_HEAP_TYPE_DEFAULT;
+
+ D3D12_RESOURCE_DESC rtDesc = {};
+ rtDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
+ rtDesc.Width = size.width();
+ rtDesc.Height = size.height();
+ rtDesc.DepthOrArraySize = 1;
+ rtDesc.MipLevels = 1;
+ rtDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
+ rtDesc.SampleDesc = makeSampleDesc(rtDesc.Format, samples);
+ rtDesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET;
+
+ ID3D12Resource *resource = nullptr;
+ if (FAILED(device->CreateCommittedResource(&heapProp, D3D12_HEAP_FLAG_NONE, &rtDesc,
+ D3D12_RESOURCE_STATE_RENDER_TARGET, &clearValue, IID_PPV_ARGS(&resource)))) {
+ qWarning("Failed to create offscreen render target of size %dx%d", size.width(), size.height());
+ return nullptr;
+ }
+
+ device->CreateRenderTargetView(resource, nullptr, viewHandle);
+
+ return resource;
+}
+
+ID3D12Resource *QSGD3D12EnginePrivate::createDepthStencil(D3D12_CPU_DESCRIPTOR_HANDLE viewHandle, const QSize &size, uint samples)
+{
+ D3D12_CLEAR_VALUE depthClearValue = {};
+ depthClearValue.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
+ depthClearValue.DepthStencil.Depth = 1.0f;
+ depthClearValue.DepthStencil.Stencil = 0;
+
+ D3D12_HEAP_PROPERTIES heapProp = {};
+ heapProp.Type = D3D12_HEAP_TYPE_DEFAULT;
+
+ D3D12_RESOURCE_DESC bufDesc = {};
+ bufDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
+ bufDesc.Width = size.width();
+ bufDesc.Height = size.height();
+ bufDesc.DepthOrArraySize = 1;
+ bufDesc.MipLevels = 1;
+ bufDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
+ bufDesc.SampleDesc = makeSampleDesc(bufDesc.Format, samples);
+ bufDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
+ bufDesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL;
+
+ ID3D12Resource *resource = nullptr;
+ if (FAILED(device->CreateCommittedResource(&heapProp, D3D12_HEAP_FLAG_NONE, &bufDesc,
+ D3D12_RESOURCE_STATE_DEPTH_WRITE, &depthClearValue, IID_PPV_ARGS(&resource)))) {
+ qWarning("Failed to create depth-stencil buffer of size %dx%d", size.width(), size.height());
+ return nullptr;
+ }
+
+ D3D12_DEPTH_STENCIL_VIEW_DESC depthStencilDesc = {};
+ depthStencilDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
+ depthStencilDesc.ViewDimension = bufDesc.SampleDesc.Count <= 1 ? D3D12_DSV_DIMENSION_TEXTURE2D : D3D12_DSV_DIMENSION_TEXTURE2DMS;
+
+ device->CreateDepthStencilView(resource, &depthStencilDesc, viewHandle);
+
+ return resource;
+}
+
+void QSGD3D12EnginePrivate::setupDefaultRenderTargets()
+{
+ for (int i = 0; i < swapChainBufferCount; ++i) {
+ if (FAILED(swapChain->GetBuffer(i, IID_PPV_ARGS(&backBufferRT[i])))) {
+ qWarning("Failed to get buffer %d from swap chain", i);
+ return;
+ }
+ defaultRTV[i] = cpuDescHeapManager.allocate(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
+ if (windowSamples == 1) {
+ defaultRT[i] = backBufferRT[i];
+ device->CreateRenderTargetView(defaultRT[i].Get(), nullptr, defaultRTV[i]);
+ } else {
+ const QSize size(windowSize.width() * windowDpr, windowSize.height() * windowDpr);
+ const QColor cc(Qt::white); // ### what if setClearColor? non-fatal but debug layer warns...
+ const QVector4D clearColor(cc.redF(), cc.greenF(), cc.blueF(), cc.alphaF());
+ ID3D12Resource *msaaRT = createColorBuffer(defaultRTV[i], size, clearColor, windowSamples);
+ if (msaaRT)
+ defaultRT[i].Attach(msaaRT);
+ }
+ }
+
+ defaultDSV = cpuDescHeapManager.allocate(D3D12_DESCRIPTOR_HEAP_TYPE_DSV);
+ const QSize size(windowSize.width() * windowDpr, windowSize.height() * windowDpr);
+ ID3D12Resource *ds = createDepthStencil(defaultDSV, size, windowSamples);
+ if (ds)
+ defaultDS.Attach(ds);
+
+ presentFrameIndex = 0;
+}
+
+void QSGD3D12EnginePrivate::setWindowSize(const QSize &size, float dpr)
+{
+ if (!initialized || (windowSize == size && windowDpr == dpr))
+ return;
+
+ waitGPU();
+
+ windowSize = size;
+ windowDpr = dpr;
+
+ if (Q_UNLIKELY(debug_render()))
+ qDebug() << "resize" << size << dpr;
+
+ // Clear these, otherwise resizing will fail.
+ defaultDS = nullptr;
+ cpuDescHeapManager.release(defaultDSV, D3D12_DESCRIPTOR_HEAP_TYPE_DSV);
+ for (int i = 0; i < swapChainBufferCount; ++i) {
+ backBufferRT[i] = nullptr;
+ defaultRT[i] = nullptr;
+ cpuDescHeapManager.release(defaultRTV[i], D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
+ }
+
+ const int w = windowSize.width() * windowDpr;
+ const int h = windowSize.height() * windowDpr;
+ HRESULT hr = swapChain->ResizeBuffers(swapChainBufferCount, w, h, DXGI_FORMAT_R8G8B8A8_UNORM,
+ waitableSwapChainMaxLatency ? DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT : 0);
+ if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET) {
+ deviceManager()->deviceLossDetected();
+ return;
+ } else if (FAILED(hr)) {
+ qWarning("Failed to resize buffers: 0x%x", hr);
+ return;
+ }
+
+ setupDefaultRenderTargets();
+}
+
+void QSGD3D12EnginePrivate::deviceLost()
+{
+ qWarning("D3D device lost, will attempt to reinitialize");
+
+ // Release all resources. This is important because otherwise reinitialization may fail.
+ releaseResources();
+
+ // Now in uninitialized state (but 'window' is still valid). Will recreate
+ // all the resources on the next beginFrame().
+}
+
+QSGD3D12CPUWaitableFence *QSGD3D12EnginePrivate::createCPUWaitableFence() const
+{
+ QSGD3D12CPUWaitableFence *f = new QSGD3D12CPUWaitableFence;
+ HRESULT hr = device->CreateFence(f->value, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&f->fence));
+ if (FAILED(hr)) {
+ qWarning("Failed to create fence: 0x%x", hr);
+ return f;
+ }
+ f->event = CreateEvent(nullptr, FALSE, FALSE, nullptr);
+ return f;
+}
+
+void QSGD3D12EnginePrivate::waitForGPU(QSGD3D12CPUWaitableFence *f) const
+{
+ const UINT64 newValue = f->value.fetchAndAddAcquire(1) + 1;
+ commandQueue->Signal(f->fence.Get(), newValue);
+ if (f->fence->GetCompletedValue() < newValue) {
+ HRESULT hr = f->fence->SetEventOnCompletion(newValue, f->event);
+ if (FAILED(hr)) {
+ qWarning("SetEventOnCompletion failed: 0x%x", hr);
+ return;
+ }
+ WaitForSingleObject(f->event, INFINITE);
+ }
+}
+
+void QSGD3D12EnginePrivate::transitionResource(ID3D12Resource *resource, ID3D12GraphicsCommandList *commandList,
+ D3D12_RESOURCE_STATES before, D3D12_RESOURCE_STATES after) const
+{
+ D3D12_RESOURCE_BARRIER barrier;
+ barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
+ barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
+ barrier.Transition.pResource = resource;
+ barrier.Transition.StateBefore = before;
+ barrier.Transition.StateAfter = after;
+ barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
+
+ commandList->ResourceBarrier(1, &barrier);
+}
+
+void QSGD3D12EnginePrivate::resolveMultisampledTarget(ID3D12Resource *msaa,
+ ID3D12Resource *resolve,
+ D3D12_RESOURCE_STATES resolveUsage,
+ ID3D12GraphicsCommandList *commandList) const
+{
+ D3D12_RESOURCE_BARRIER barriers[2];
+ for (int i = 0; i < _countof(barriers); ++i) {
+ barriers[i].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
+ barriers[i].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
+ barriers[i].Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
+ }
+
+ barriers[0].Transition.pResource = msaa;
+ barriers[0].Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
+ barriers[0].Transition.StateAfter = D3D12_RESOURCE_STATE_RESOLVE_SOURCE;
+ barriers[1].Transition.pResource = resolve;
+ barriers[1].Transition.StateBefore = resolveUsage;
+ barriers[1].Transition.StateAfter = D3D12_RESOURCE_STATE_RESOLVE_DEST;
+ commandList->ResourceBarrier(2, barriers);
+
+ commandList->ResolveSubresource(resolve, 0, msaa, 0, DXGI_FORMAT_R8G8B8A8_UNORM);
+
+ barriers[0].Transition.pResource = msaa;
+ barriers[0].Transition.StateBefore = D3D12_RESOURCE_STATE_RESOLVE_SOURCE;
+ barriers[0].Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
+ barriers[1].Transition.pResource = resolve;
+ barriers[1].Transition.StateBefore = D3D12_RESOURCE_STATE_RESOLVE_DEST;
+ barriers[1].Transition.StateAfter = resolveUsage;
+ commandList->ResourceBarrier(2, barriers);
+}
+
+void QSGD3D12EnginePrivate::uavBarrier(ID3D12Resource *resource, ID3D12GraphicsCommandList *commandList) const
+{
+ D3D12_RESOURCE_BARRIER barrier = {};
+ barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_UAV;
+ barrier.UAV.pResource = resource;
+
+ commandList->ResourceBarrier(1, &barrier);
+}
+
+ID3D12Resource *QSGD3D12EnginePrivate::createBuffer(int size)
+{
+ ID3D12Resource *buf;
+
+ D3D12_HEAP_PROPERTIES uploadHeapProp = {};
+ uploadHeapProp.Type = D3D12_HEAP_TYPE_UPLOAD;
+
+ D3D12_RESOURCE_DESC bufDesc = {};
+ bufDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
+ bufDesc.Width = size;
+ bufDesc.Height = 1;
+ bufDesc.DepthOrArraySize = 1;
+ bufDesc.MipLevels = 1;
+ bufDesc.Format = DXGI_FORMAT_UNKNOWN;
+ bufDesc.SampleDesc.Count = 1;
+ bufDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
+
+ HRESULT hr = device->CreateCommittedResource(&uploadHeapProp, D3D12_HEAP_FLAG_NONE, &bufDesc,
+ D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&buf));
+ if (FAILED(hr))
+ qWarning("Failed to create buffer resource: 0x%x", hr);
+
+ return buf;
+}
+
+void QSGD3D12EnginePrivate::ensureBuffer(Buffer *buf)
+{
+ Buffer::InFlightData &bfd(buf->d[currentPFrameIndex]);
+ // Only enlarge, never shrink
+ const bool newBufferNeeded = bfd.buffer ? (buf->cpuDataRef.size > bfd.resourceSize) : true;
+ if (newBufferNeeded) {
+ // Round it up and overallocate a little bit so that a subsequent
+ // buffer contents rebuild with a slightly larger total size does
+ // not lead to creating a new buffer.
+ const quint32 sz = alignedSize(buf->cpuDataRef.size, 4096);
+ if (Q_UNLIKELY(debug_render()))
+ qDebug("new buffer[pf=%d] of size %d (actual data size %d)", currentPFrameIndex, sz, buf->cpuDataRef.size);
+ bfd.buffer.Attach(createBuffer(sz));
+ bfd.resourceSize = sz;
+ }
+ // Cache the actual data size in the per-in-flight-frame data as well.
+ bfd.dataSize = buf->cpuDataRef.size;
+}
+
+void QSGD3D12EnginePrivate::updateBuffer(Buffer *buf)
+{
+ if (buf->cpuDataRef.dirty.isEmpty())
+ return;
+
+ Buffer::InFlightData &bfd(buf->d[currentPFrameIndex]);
+ quint8 *p = nullptr;
+ const D3D12_RANGE readRange = { 0, 0 };
+ if (FAILED(bfd.buffer->Map(0, &readRange, reinterpret_cast<void **>(&p)))) {
+ qWarning("Map failed for buffer of size %d", buf->cpuDataRef.size);
+ return;
+ }
+ for (const auto &r : qAsConst(buf->cpuDataRef.dirty)) {
+ if (Q_UNLIKELY(debug_render()))
+ qDebug("%p o %d s %d", buf, r.first, r.second);
+ memcpy(p + r.first, buf->cpuDataRef.p + r.first, r.second);
+ }
+ bfd.buffer->Unmap(0, nullptr);
+ buf->cpuDataRef.dirty.clear();
+}
+
+void QSGD3D12EnginePrivate::ensureDevice()
+{
+ if (!initialized && window)
+ initialize(window, windowSize, windowDpr, windowSamples);
+}
+
+void QSGD3D12EnginePrivate::beginFrame()
+{
+ if (inFrame && !activeLayers)
+ qFatal("beginFrame called again without an endFrame, frame index was %d", frameIndex);
+
+ if (Q_UNLIKELY(debug_render()))
+ qDebug() << "***** begin frame, logical" << frameIndex << "present" << presentFrameIndex << "layer" << activeLayers;
+
+ if (inFrame && activeLayers) {
+ if (Q_UNLIKELY(debug_render()))
+ qDebug("frame %d already in progress", frameIndex);
+ if (!currentLayerDepth) {
+ // There are layers and the real frame preparation starts now. Prepare for present.
+ beginFrameDraw();
+ }
+ return;
+ }
+
+ inFrame = true;
+
+ // The device may have been lost. This is the point to attempt to start
+ // again from scratch. Except when it is not. Operations that can happen
+ // out of frame (e.g. textures, render targets) may trigger reinit earlier
+ // than beginFrame.
+ ensureDevice();
+
+ // Wait for a buffer to be available for Present, if the waitable event is in use.
+ if (waitableSwapChainMaxLatency)
+ WaitForSingleObject(swapEvent, INFINITE);
+
+ // Block if needed. With 2 frames in flight frame N waits for frame N - 2, but not N - 1, to finish.
+ currentPFrameIndex = frameIndex % frameInFlightCount;
+ if (frameIndex >= frameInFlightCount) {
+ ID3D12Fence *fence = frameFence[currentPFrameIndex]->fence.Get();
+ HANDLE event = frameFence[currentPFrameIndex]->event;
+ // Frame fence values start from 1, hence the +1.
+ const quint64 inFlightFenceValue = frameIndex - frameInFlightCount + 1;
+ if (fence->GetCompletedValue() < inFlightFenceValue) {
+ fence->SetEventOnCompletion(inFlightFenceValue, event);
+ WaitForSingleObject(event, INFINITE);
+ }
+ frameCommandAllocator[currentPFrameIndex]->Reset();
+ }
+
+ PersistentFrameData &pfd(pframeData[currentPFrameIndex]);
+ pfd.cbvSrvUavNextFreeDescriptorIndex = 0;
+
+ for (Buffer &b : buffers) {
+ if (b.entryInUse())
+ b.d[currentPFrameIndex].dirty.clear();
+ }
+
+ if (frameIndex >= frameInFlightCount - 1) {
+ // Now sync the buffer changes from the previous, potentially still in
+ // flight, frames. This is done by taking the ranges dirtied in those
+ // frames and adding them to the global CPU-side buffer's dirty list,
+ // as if this frame changed those ranges. (however, dirty ranges
+ // inherited this way are not added to this frame's persistent
+ // per-frame dirty list because the next frame after this one should
+ // inherit this frame's genuine changes only, the rest will come from
+ // the earlier ones)
+ for (int delta = frameInFlightCount - 1; delta >= 1; --delta) {
+ const int prevPFrameIndex = (frameIndex - delta) % frameInFlightCount;
+ PersistentFrameData &prevFrameData(pframeData[prevPFrameIndex]);
+ for (uint id : qAsConst(prevFrameData.buffersUsedInFrame)) {
+ Buffer &b(buffers[id - 1]);
+ if (b.d[currentPFrameIndex].buffer && b.d[currentPFrameIndex].dataSize == b.cpuDataRef.size) {
+ if (Q_UNLIKELY(debug_render()))
+ qDebug() << "frame" << frameIndex << "takes dirty" << b.d[prevPFrameIndex].dirty
+ << "from frame" << frameIndex - delta << "for buffer" << id;
+ for (const auto &range : qAsConst(b.d[prevPFrameIndex].dirty))
+ addDirtyRange(&b.cpuDataRef.dirty, range.first, range.second, b.cpuDataRef.size);
+ } else {
+ if (Q_UNLIKELY(debug_render()))
+ qDebug() << "frame" << frameIndex << "makes all dirty from frame" << frameIndex - delta
+ << "for buffer" << id;
+ addDirtyRange(&b.cpuDataRef.dirty, 0, b.cpuDataRef.size, b.cpuDataRef.size);
+ }
+ }
+ }
+ }
+
+ if (frameIndex >= frameInFlightCount) {
+ // Do some texture upload bookkeeping.
+ const quint64 finishedFrameIndex = frameIndex - frameInFlightCount; // we know since we just blocked for this
+ // pfd conveniently refers to the same slot that was used by that frame
+ if (!pfd.pendingTextureUploads.isEmpty()) {
+ if (Q_UNLIKELY(debug_render()))
+ qDebug("Removing texture upload data for frame %d", finishedFrameIndex);
+ for (uint id : qAsConst(pfd.pendingTextureUploads)) {
+ const int idx = id - 1;
+ Texture &t(textures[idx]);
+ // fenceValue is 0 when the previous frame cleared it, skip in
+ // this case. Skip also when fenceValue > the value it was when
+ // adding the last GPU wait - this is the case when more
+ // uploads were queued for the same texture in the meantime.
+ if (t.fenceValue && t.fenceValue == t.lastWaitFenceValue) {
+ t.fenceValue = 0;
+ t.lastWaitFenceValue = 0;
+ t.stagingBuffers.clear();
+ t.stagingHeaps.clear();
+ if (Q_UNLIKELY(debug_render()))
+ qDebug("Cleaned staging data for texture %u", id);
+ }
+ }
+ pfd.pendingTextureUploads.clear();
+ if (!pfd.pendingTextureMipMap.isEmpty()) {
+ if (Q_UNLIKELY(debug_render()))
+ qDebug() << "cleaning mipmap generation data for " << pfd.pendingTextureMipMap;
+ // no special cleanup is needed as mipmap generation uses the frame's resources
+ pfd.pendingTextureMipMap.clear();
+ }
+ bool hasPending = false;
+ for (int delta = 1; delta < frameInFlightCount; ++delta) {
+ const PersistentFrameData &prevFrameData(pframeData[(frameIndex - delta) % frameInFlightCount]);
+ if (!prevFrameData.pendingTextureUploads.isEmpty()) {
+ hasPending = true;
+ break;
+ }
+ }
+ if (!hasPending) {
+ if (Q_UNLIKELY(debug_render()))
+ qDebug("no more pending textures");
+ copyCommandAllocator->Reset();
+ }
+ }
+
+ // Do the deferred deletes.
+ if (!pfd.deleteQueue.isEmpty()) {
+ for (PersistentFrameData::DeleteQueueEntry &e : pfd.deleteQueue) {
+ e.res = nullptr;
+ e.descHeap = nullptr;
+ if (e.cpuDescriptorPtr) {
+ D3D12_CPU_DESCRIPTOR_HANDLE h = { e.cpuDescriptorPtr };
+ cpuDescHeapManager.release(h, e.descHeapType);
+ }
+ }
+ pfd.deleteQueue.clear();
+ }
+ // Deferred deletes issued outside a begin-endFrame go to the next
+ // frame's out-of-frame delete queue as these cannot be executed in the
+ // next beginFrame, only in next + frameInFlightCount. Move to the
+ // normal queue if this is the next beginFrame.
+ if (!pfd.outOfFrameDeleteQueue.isEmpty()) {
+ pfd.deleteQueue = pfd.outOfFrameDeleteQueue;
+ pfd.outOfFrameDeleteQueue.clear();
+ }
+
+ // Mark released texture, buffer, etc. slots free.
+ if (!pfd.pendingReleases.isEmpty()) {
+ for (const auto &pr : qAsConst(pfd.pendingReleases)) {
+ Q_ASSERT(pr.id);
+ if (pr.type == PersistentFrameData::PendingRelease::TypeTexture) {
+ Texture &t(textures[pr.id - 1]);
+ Q_ASSERT(t.entryInUse());
+ t.flags &= ~RenderTarget::EntryInUse; // createTexture() can now reuse this entry
+ t.texture = nullptr;
+ } else if (pr.type == PersistentFrameData::PendingRelease::TypeBuffer) {
+ Buffer &b(buffers[pr.id - 1]);
+ Q_ASSERT(b.entryInUse());
+ b.flags &= ~Buffer::EntryInUse;
+ for (int i = 0; i < frameInFlightCount; ++i)
+ b.d[i].buffer = nullptr;
+ } else {
+ qFatal("Corrupt pending release list, type %d", pr.type);
+ }
+ }
+ pfd.pendingReleases.clear();
+ }
+ if (!pfd.outOfFramePendingReleases.isEmpty()) {
+ pfd.pendingReleases = pfd.outOfFramePendingReleases;
+ pfd.outOfFramePendingReleases.clear();
+ }
+ }
+
+ pfd.buffersUsedInFrame.clear();
+
+ beginDrawCalls();
+
+ // Prepare for present if this is a frame without layers.
+ if (!activeLayers)
+ beginFrameDraw();
+}
+
+void QSGD3D12EnginePrivate::beginDrawCalls()
+{
+ frameCommandList->Reset(frameCommandAllocator[frameIndex % frameInFlightCount].Get(), nullptr);
+ commandList = frameCommandList.Get();
+ invalidateCachedFrameState();
+}
+
+void QSGD3D12EnginePrivate::invalidateCachedFrameState()
+{
+ tframeData.drawingMode = QSGGeometry::DrawingMode(-1);
+ tframeData.currentIndexBuffer = 0;
+ tframeData.drawCount = 0;
+ tframeData.lastPso = nullptr;
+ tframeData.lastRootSig = nullptr;
+ tframeData.descHeapSet = false;
+}
+
+void QSGD3D12EnginePrivate::restoreFrameState(bool minimal)
+{
+ queueSetRenderTarget(currentRenderTarget);
+ if (!minimal) {
+ queueViewport(tframeData.viewport);
+ queueScissor(tframeData.scissor);
+ queueSetBlendFactor(tframeData.blendFactor);
+ queueSetStencilRef(tframeData.stencilRef);
+ }
+ finalizePipeline(tframeData.pipelineState);
+}
+
+void QSGD3D12EnginePrivate::beginFrameDraw()
+{
+ if (windowSamples == 1)
+ transitionResource(defaultRT[presentFrameIndex % swapChainBufferCount].Get(), commandList,
+ D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_RENDER_TARGET);
+}
+
+void QSGD3D12EnginePrivate::endFrame()
+{
+ if (!inFrame)
+ qFatal("endFrame called without beginFrame, frame index %d", frameIndex);
+
+ if (Q_UNLIKELY(debug_render()))
+ qDebug("***** end frame");
+
+ endDrawCalls(true);
+
+ commandQueue->Signal(frameFence[frameIndex % frameInFlightCount]->fence.Get(), frameIndex + 1);
+ ++frameIndex;
+
+ inFrame = false;
+}
+
+void QSGD3D12EnginePrivate::endDrawCalls(bool lastInFrame)
+{
+ PersistentFrameData &pfd(pframeData[currentPFrameIndex]);
+
+ // Now is the time to sync all the changed areas in the buffers.
+ if (Q_UNLIKELY(debug_render()))
+ qDebug() << "buffers used in drawcall set" << pfd.buffersUsedInDrawCallSet;
+ for (uint id : qAsConst(pfd.buffersUsedInDrawCallSet))
+ updateBuffer(&buffers[id - 1]);
+
+ pfd.buffersUsedInFrame += pfd.buffersUsedInDrawCallSet;
+ pfd.buffersUsedInDrawCallSet.clear();
+
+ // Add a wait on the 3D queue for the relevant texture uploads on the copy queue.
+ if (!pfd.pendingTextureUploads.isEmpty()) {
+ quint64 topFenceValue = 0;
+ for (uint id : qAsConst(pfd.pendingTextureUploads)) {
+ const int idx = id - 1;
+ Texture &t(textures[idx]);
+ Q_ASSERT(t.fenceValue);
+ // skip if already added a Wait in the previous frame
+ if (t.lastWaitFenceValue == t.fenceValue)
+ continue;
+ t.lastWaitFenceValue = t.fenceValue;
+ if (t.fenceValue > topFenceValue)
+ topFenceValue = t.fenceValue;
+ if (t.mipmap())
+ pfd.pendingTextureMipMap.insert(id);
+ }
+ if (topFenceValue) {
+ if (Q_UNLIKELY(debug_render()))
+ qDebug("added wait for texture fence %llu", topFenceValue);
+ commandQueue->Wait(textureUploadFence.Get(), topFenceValue);
+ // Generate mipmaps after the wait, when necessary.
+ if (!pfd.pendingTextureMipMap.isEmpty()) {
+ if (Q_UNLIKELY(debug_render()))
+ qDebug() << "starting mipmap generation for" << pfd.pendingTextureMipMap;
+ for (uint id : qAsConst(pfd.pendingTextureMipMap))
+ mipmapper.queueGenerate(textures[id - 1]);
+ }
+ }
+ }
+
+ if (lastInFrame) {
+ // Resolve and transition the backbuffer for present, if needed.
+ const int idx = presentFrameIndex % swapChainBufferCount;
+ if (windowSamples == 1) {
+ transitionResource(defaultRT[idx].Get(), commandList,
+ D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PRESENT);
+ } else {
+ if (Q_UNLIKELY(debug_render())) {
+ const D3D12_RESOURCE_DESC desc = defaultRT[idx]->GetDesc();
+ qDebug("added resolve for multisampled render target (count %d, quality %d)",
+ desc.SampleDesc.Count, desc.SampleDesc.Quality);
+ }
+ resolveMultisampledTarget(defaultRT[idx].Get(), backBufferRT[idx].Get(),
+ D3D12_RESOURCE_STATE_PRESENT, commandList);
+ }
+
+ if (activeLayers) {
+ if (Q_UNLIKELY(debug_render()))
+ qDebug("this frame had %d layers", activeLayers);
+ activeLayers = 0;
+ }
+ }
+
+ // Go!
+ HRESULT hr = frameCommandList->Close();
+ if (FAILED(hr)) {
+ qWarning("Failed to close command list: 0x%x", hr);
+ if (hr == E_INVALIDARG)
+ qWarning("Invalid arguments. Some of the commands in the list is invalid in some way.");
+ }
+
+ ID3D12CommandList *commandLists[] = { frameCommandList.Get() };
+ commandQueue->ExecuteCommandLists(_countof(commandLists), commandLists);
+
+ commandList = nullptr;
+}
+
+void QSGD3D12EnginePrivate::beginLayer()
+{
+ if (inFrame && !activeLayers)
+ qFatal("Layer rendering cannot be started while a frame is active");
+
+ if (Q_UNLIKELY(debug_render()))
+ qDebug("===== beginLayer active %d depth %d (inFrame=%d)", activeLayers, currentLayerDepth, inFrame);
+
+ ++activeLayers;
+ ++currentLayerDepth;
+
+ // Do an early beginFrame. With multiple layers this results in
+ // beginLayer - beginFrame - endLayer - beginLayer - beginFrame - endLayer - ... - (*) beginFrame - endFrame
+ // where (*) denotes the start of the preparation of the actual, non-layer frame.
+
+ if (activeLayers == 1)
+ beginFrame();
+}
+
+void QSGD3D12EnginePrivate::endLayer()
+{
+ if (!inFrame || !activeLayers || !currentLayerDepth)
+ qFatal("Mismatched endLayer");
+
+ if (Q_UNLIKELY(debug_render()))
+ qDebug("===== endLayer active %d depth %d", activeLayers, currentLayerDepth);
+
+ --currentLayerDepth;
+
+ // Do not touch activeLayers. It remains valid until endFrame.
+}
+
+// Root signature:
+// [0] CBV - always present
+// [1] table with 1 SRV per texture (optional)
+// one static sampler per texture (optional)
+//
+// SRVs can be created freely via QSGD3D12CPUDescriptorHeapManager and stored
+// in QSGD3D12TextureView. The engine will copy them onto a dedicated,
+// shader-visible CBV-SRV-UAV heap in the correct order.
+
+void QSGD3D12EnginePrivate::finalizePipeline(const QSGD3D12PipelineState &pipelineState)
+{
+ if (!inFrame) {
+ qWarning("%s: Cannot be called outside begin/endFrame", __FUNCTION__);
+ return;
+ }
+
+ tframeData.pipelineState = pipelineState;
+
+ RootSigCacheEntry *cachedRootSig = rootSigCache[pipelineState.shaders.rootSig];
+ if (!cachedRootSig) {
+ if (Q_UNLIKELY(debug_render()))
+ qDebug("NEW ROOTSIG");
+
+ cachedRootSig = new RootSigCacheEntry;
+
+ D3D12_ROOT_PARAMETER rootParams[4];
+ int rootParamCount = 0;
+
+ rootParams[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
+ rootParams[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
+ rootParams[0].Descriptor.ShaderRegister = 0; // b0
+ rootParams[0].Descriptor.RegisterSpace = 0;
+ ++rootParamCount;
+
+ if (!pipelineState.shaders.rootSig.textureViews.isEmpty()) {
+ rootParams[rootParamCount].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
+ rootParams[rootParamCount].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
+ rootParams[rootParamCount].DescriptorTable.NumDescriptorRanges = 1;
+ D3D12_DESCRIPTOR_RANGE descRange;
+ descRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
+ descRange.NumDescriptors = pipelineState.shaders.rootSig.textureViews.count();
+ descRange.BaseShaderRegister = 0; // t0, t1, ...
+ descRange.RegisterSpace = 0;
+ descRange.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
+ rootParams[rootParamCount].DescriptorTable.pDescriptorRanges = &descRange;
+ ++rootParamCount;
+ }
+
+ Q_ASSERT(rootParamCount <= _countof(rootParams));
+ D3D12_ROOT_SIGNATURE_DESC desc;
+ desc.NumParameters = rootParamCount;
+ desc.pParameters = rootParams;
+ // Mixing up samplers and resource views in QSGD3D12TextureView means
+ // that the number of static samplers has to match the number of
+ // textures. This is not really ideal in general but works for Quick's use cases.
+ // The shaders can still choose to declare and use fewer samplers, if they want to.
+ desc.NumStaticSamplers = pipelineState.shaders.rootSig.textureViews.count();
+ D3D12_STATIC_SAMPLER_DESC staticSamplers[8];
+ int sdIdx = 0;
+ Q_ASSERT(pipelineState.shaders.rootSig.textureViews.count() <= _countof(staticSamplers));
+ for (const QSGD3D12TextureView &tv : qAsConst(pipelineState.shaders.rootSig.textureViews)) {
+ D3D12_STATIC_SAMPLER_DESC sd = {};
+ sd.Filter = D3D12_FILTER(tv.filter);
+ sd.AddressU = D3D12_TEXTURE_ADDRESS_MODE(tv.addressModeHoriz);
+ sd.AddressV = D3D12_TEXTURE_ADDRESS_MODE(tv.addressModeVert);
+ sd.AddressW = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
+ sd.MinLOD = 0.0f;
+ sd.MaxLOD = D3D12_FLOAT32_MAX;
+ sd.ShaderRegister = sdIdx; // t0, t1, ...
+ sd.ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
+ staticSamplers[sdIdx++] = sd;
+ }
+ desc.pStaticSamplers = staticSamplers;
+ desc.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT;
+
+ ComPtr<ID3DBlob> signature;
+ ComPtr<ID3DBlob> error;
+ if (FAILED(D3D12SerializeRootSignature(&desc, D3D_ROOT_SIGNATURE_VERSION_1, &signature, &error))) {
+ qWarning("Failed to serialize root signature");
+ return;
+ }
+ if (FAILED(device->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(),
+ IID_PPV_ARGS(&cachedRootSig->rootSig)))) {
+ qWarning("Failed to create root signature");
+ return;
+ }
+
+ rootSigCache.insert(pipelineState.shaders.rootSig, cachedRootSig);
+ }
+
+ PSOCacheEntry *cachedPso = psoCache[pipelineState];
+ if (!cachedPso) {
+ if (Q_UNLIKELY(debug_render()))
+ qDebug("NEW PSO");
+
+ cachedPso = new PSOCacheEntry;
+
+ D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc = {};
+
+ D3D12_INPUT_ELEMENT_DESC inputElements[8];
+ Q_ASSERT(pipelineState.inputElements.count() <= _countof(inputElements));
+ int ieIdx = 0;
+ for (const QSGD3D12InputElement &ie : pipelineState.inputElements) {
+ D3D12_INPUT_ELEMENT_DESC ieDesc = {};
+ ieDesc.SemanticName = ie.semanticName;
+ ieDesc.SemanticIndex = ie.semanticIndex;
+ ieDesc.Format = DXGI_FORMAT(ie.format);
+ ieDesc.InputSlot = ie.slot;
+ ieDesc.AlignedByteOffset = ie.offset;
+ ieDesc.InputSlotClass = D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA;
+ if (Q_UNLIKELY(debug_render()))
+ qDebug("input [%d]: %s %d 0x%x %d", ieIdx, ie.semanticName, ie.offset, ie.format, ie.slot);
+ inputElements[ieIdx++] = ieDesc;
+ }
+
+ psoDesc.InputLayout = { inputElements, UINT(ieIdx) };
+
+ psoDesc.pRootSignature = cachedRootSig->rootSig.Get();
+
+ D3D12_SHADER_BYTECODE vshader;
+ vshader.pShaderBytecode = pipelineState.shaders.vs;
+ vshader.BytecodeLength = pipelineState.shaders.vsSize;
+ D3D12_SHADER_BYTECODE pshader;
+ pshader.pShaderBytecode = pipelineState.shaders.ps;
+ pshader.BytecodeLength = pipelineState.shaders.psSize;
+
+ psoDesc.VS = vshader;
+ psoDesc.PS = pshader;
+
+ D3D12_RASTERIZER_DESC rastDesc = {};
+ rastDesc.FillMode = D3D12_FILL_MODE_SOLID;
+ rastDesc.CullMode = D3D12_CULL_MODE(pipelineState.cullMode);
+ rastDesc.FrontCounterClockwise = pipelineState.frontCCW;
+ rastDesc.DepthBias = D3D12_DEFAULT_DEPTH_BIAS;
+ rastDesc.DepthBiasClamp = D3D12_DEFAULT_DEPTH_BIAS_CLAMP;
+ rastDesc.SlopeScaledDepthBias = D3D12_DEFAULT_SLOPE_SCALED_DEPTH_BIAS;
+ rastDesc.DepthClipEnable = TRUE;
+
+ psoDesc.RasterizerState = rastDesc;
+
+ D3D12_BLEND_DESC blendDesc = {};
+ if (pipelineState.blend == QSGD3D12PipelineState::BlendNone) {
+ D3D12_RENDER_TARGET_BLEND_DESC noBlendDesc = {};
+ noBlendDesc.RenderTargetWriteMask = pipelineState.colorWrite ? D3D12_COLOR_WRITE_ENABLE_ALL : 0;
+ blendDesc.RenderTarget[0] = noBlendDesc;
+ } else if (pipelineState.blend == QSGD3D12PipelineState::BlendPremul) {
+ const D3D12_RENDER_TARGET_BLEND_DESC premulBlendDesc = {
+ TRUE, FALSE,
+ D3D12_BLEND_ONE, D3D12_BLEND_INV_SRC_ALPHA, D3D12_BLEND_OP_ADD,
+ D3D12_BLEND_ONE, D3D12_BLEND_INV_SRC_ALPHA, D3D12_BLEND_OP_ADD,
+ D3D12_LOGIC_OP_NOOP,
+ UINT8(pipelineState.colorWrite ? D3D12_COLOR_WRITE_ENABLE_ALL : 0)
+ };
+ blendDesc.RenderTarget[0] = premulBlendDesc;
+ } else if (pipelineState.blend == QSGD3D12PipelineState::BlendColor) {
+ const D3D12_RENDER_TARGET_BLEND_DESC colorBlendDesc = {
+ TRUE, FALSE,
+ D3D12_BLEND_BLEND_FACTOR, D3D12_BLEND_INV_SRC_COLOR, D3D12_BLEND_OP_ADD,
+ D3D12_BLEND_BLEND_FACTOR, D3D12_BLEND_INV_SRC_ALPHA, D3D12_BLEND_OP_ADD,
+ D3D12_LOGIC_OP_NOOP,
+ UINT8(pipelineState.colorWrite ? D3D12_COLOR_WRITE_ENABLE_ALL : 0)
+ };
+ blendDesc.RenderTarget[0] = colorBlendDesc;
+ }
+ psoDesc.BlendState = blendDesc;
+
+ psoDesc.DepthStencilState.DepthEnable = pipelineState.depthEnable;
+ psoDesc.DepthStencilState.DepthWriteMask = pipelineState.depthWrite ? D3D12_DEPTH_WRITE_MASK_ALL : D3D12_DEPTH_WRITE_MASK_ZERO;
+ psoDesc.DepthStencilState.DepthFunc = D3D12_COMPARISON_FUNC(pipelineState.depthFunc);
+
+ psoDesc.DepthStencilState.StencilEnable = pipelineState.stencilEnable;
+ psoDesc.DepthStencilState.StencilReadMask = psoDesc.DepthStencilState.StencilWriteMask = 0xFF;
+ D3D12_DEPTH_STENCILOP_DESC stencilOpDesc = {
+ D3D12_STENCIL_OP(pipelineState.stencilFailOp),
+ D3D12_STENCIL_OP(pipelineState.stencilDepthFailOp),
+ D3D12_STENCIL_OP(pipelineState.stencilPassOp),
+ D3D12_COMPARISON_FUNC(pipelineState.stencilFunc)
+ };
+ psoDesc.DepthStencilState.FrontFace = psoDesc.DepthStencilState.BackFace = stencilOpDesc;
+
+ psoDesc.SampleMask = UINT_MAX;
+ psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE(pipelineState.topologyType);
+ psoDesc.NumRenderTargets = 1;
+ psoDesc.RTVFormats[0] = DXGI_FORMAT_R8G8B8A8_UNORM;
+ psoDesc.DSVFormat = DXGI_FORMAT_D24_UNORM_S8_UINT;
+ psoDesc.SampleDesc = defaultRT[0]->GetDesc().SampleDesc;
+
+ HRESULT hr = device->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(&cachedPso->pso));
+ if (FAILED(hr)) {
+ qWarning("Failed to create graphics pipeline state");
+ return;
+ }
+
+ psoCache.insert(pipelineState, cachedPso);
+ }
+
+ if (cachedPso->pso.Get() != tframeData.lastPso) {
+ tframeData.lastPso = cachedPso->pso.Get();
+ commandList->SetPipelineState(tframeData.lastPso);
+ }
+
+ if (cachedRootSig->rootSig.Get() != tframeData.lastRootSig) {
+ tframeData.lastRootSig = cachedRootSig->rootSig.Get();
+ commandList->SetGraphicsRootSignature(tframeData.lastRootSig);
+ }
+
+ if (!pipelineState.shaders.rootSig.textureViews.isEmpty())
+ setDescriptorHeaps();
+}
+
+void QSGD3D12EnginePrivate::setDescriptorHeaps(bool force)
+{
+ if (force || !tframeData.descHeapSet) {
+ tframeData.descHeapSet = true;
+ ID3D12DescriptorHeap *heaps[] = { pframeData[currentPFrameIndex].gpuCbvSrvUavHeap.Get() };
+ commandList->SetDescriptorHeaps(_countof(heaps), heaps);
+ }
+}
+
+void QSGD3D12EnginePrivate::queueViewport(const QRect &rect)
+{
+ if (!inFrame) {
+ qWarning("%s: Cannot be called outside begin/endFrame", __FUNCTION__);
+ return;
+ }
+
+ tframeData.viewport = rect;
+ const D3D12_VIEWPORT viewport = { float(rect.x()), float(rect.y()), float(rect.width()), float(rect.height()), 0, 1 };
+ commandList->RSSetViewports(1, &viewport);
+}
+
+void QSGD3D12EnginePrivate::queueScissor(const QRect &rect)
+{
+ if (!inFrame) {
+ qWarning("%s: Cannot be called outside begin/endFrame", __FUNCTION__);
+ return;
+ }
+
+ tframeData.scissor = rect;
+ const D3D12_RECT scissorRect = { rect.x(), rect.y(), rect.x() + rect.width(), rect.y() + rect.height() };
+ commandList->RSSetScissorRects(1, &scissorRect);
+}
+
+void QSGD3D12EnginePrivate::queueSetRenderTarget(uint id)
+{
+ if (!inFrame) {
+ qWarning("%s: Cannot be called outside begin/endFrame", __FUNCTION__);
+ return;
+ }
+
+ D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle;
+ D3D12_CPU_DESCRIPTOR_HANDLE dsvHandle;
+
+ if (!id) {
+ rtvHandle = defaultRTV[presentFrameIndex % swapChainBufferCount];
+ dsvHandle = defaultDSV;
+ } else {
+ const int idx = id - 1;
+ Q_ASSERT(idx < renderTargets.count() && renderTargets[idx].entryInUse());
+ RenderTarget &rt(renderTargets[idx]);
+ rtvHandle = rt.rtv;
+ dsvHandle = rt.dsv;
+ if (!(rt.flags & RenderTarget::NeedsReadBarrier)) {
+ rt.flags |= RenderTarget::NeedsReadBarrier;
+ if (!(rt.flags & RenderTarget::Multisample))
+ transitionResource(rt.color.Get(), commandList, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE,
+ D3D12_RESOURCE_STATE_RENDER_TARGET);
+ }
+ }
+
+ commandList->OMSetRenderTargets(1, &rtvHandle, FALSE, &dsvHandle);
+
+ currentRenderTarget = id;
+}
+
+void QSGD3D12EnginePrivate::queueClearRenderTarget(const QColor &color)
+{
+ if (!inFrame) {
+ qWarning("%s: Cannot be called outside begin/endFrame", __FUNCTION__);
+ return;
+ }
+
+ const float clearColor[] = { float(color.redF()), float(color.blueF()), float(color.greenF()), float(color.alphaF()) };
+ D3D12_CPU_DESCRIPTOR_HANDLE rtv = !currentRenderTarget
+ ? defaultRTV[presentFrameIndex % swapChainBufferCount]
+ : renderTargets[currentRenderTarget - 1].rtv;
+ commandList->ClearRenderTargetView(rtv, clearColor, 0, nullptr);
+}
+
+void QSGD3D12EnginePrivate::queueClearDepthStencil(float depthValue, quint8 stencilValue, QSGD3D12Engine::ClearFlags which)
+{
+ if (!inFrame) {
+ qWarning("%s: Cannot be called outside begin/endFrame", __FUNCTION__);
+ return;
+ }
+
+ D3D12_CPU_DESCRIPTOR_HANDLE dsv = !currentRenderTarget
+ ? defaultDSV
+ : renderTargets[currentRenderTarget - 1].dsv;
+ commandList->ClearDepthStencilView(dsv, D3D12_CLEAR_FLAGS(int(which)), depthValue, stencilValue, 0, nullptr);
+}
+
+void QSGD3D12EnginePrivate::queueSetBlendFactor(const QVector4D &factor)
+{
+ if (!inFrame) {
+ qWarning("%s: Cannot be called outside begin/endFrame", __FUNCTION__);
+ return;
+ }
+
+ tframeData.blendFactor = factor;
+ const float f[4] = { factor.x(), factor.y(), factor.z(), factor.w() };
+ commandList->OMSetBlendFactor(f);
+}
+
+void QSGD3D12EnginePrivate::queueSetStencilRef(quint32 ref)
+{
+ if (!inFrame) {
+ qWarning("%s: Cannot be called outside begin/endFrame", __FUNCTION__);
+ return;
+ }
+
+ tframeData.stencilRef = ref;
+ commandList->OMSetStencilRef(ref);
+}
+
+void QSGD3D12EnginePrivate::queueDraw(const QSGD3D12Engine::DrawParams &params)
+{
+ if (!inFrame) {
+ qWarning("%s: Cannot be called outside begin/endFrame", __FUNCTION__);
+ return;
+ }
+
+ const bool skip = tframeData.scissor.isEmpty();
+
+ PersistentFrameData &pfd(pframeData[currentPFrameIndex]);
+
+ pfd.buffersUsedInDrawCallSet.insert(params.vertexBuf);
+ const int vertexBufIdx = params.vertexBuf - 1;
+ Q_ASSERT(params.vertexBuf && vertexBufIdx < buffers.count() && buffers[vertexBufIdx].entryInUse());
+ pfd.buffersUsedInDrawCallSet.insert(params.constantBuf);
+ const int constantBufIdx = params.constantBuf - 1;
+ Q_ASSERT(params.constantBuf && constantBufIdx < buffers.count() && buffers[constantBufIdx].entryInUse());
+ int indexBufIdx = -1;
+ if (params.indexBuf) {
+ pfd.buffersUsedInDrawCallSet.insert(params.indexBuf);
+ indexBufIdx = params.indexBuf - 1;
+ Q_ASSERT(indexBufIdx < buffers.count() && buffers[indexBufIdx].entryInUse());
+ }
+
+ // Ensure buffers are created but do not copy the data here, leave that to endDrawCalls().
+ ensureBuffer(&buffers[vertexBufIdx]);
+ ensureBuffer(&buffers[constantBufIdx]);
+ if (indexBufIdx >= 0)
+ ensureBuffer(&buffers[indexBufIdx]);
+
+ // Set the CBV.
+ if (!skip && params.cboOffset >= 0) {
+ ID3D12Resource *cbuf = buffers[constantBufIdx].d[currentPFrameIndex].buffer.Get();
+ if (cbuf)
+ commandList->SetGraphicsRootConstantBufferView(0, cbuf->GetGPUVirtualAddress() + params.cboOffset);
+ }
+
+ // Set up vertex and index buffers.
+ ID3D12Resource *vbuf = buffers[vertexBufIdx].d[currentPFrameIndex].buffer.Get();
+ ID3D12Resource *ibuf = indexBufIdx >= 0 && params.startIndexIndex >= 0
+ ? buffers[indexBufIdx].d[currentPFrameIndex].buffer.Get() : nullptr;
+
+ if (!skip && params.mode != tframeData.drawingMode) {
+ D3D_PRIMITIVE_TOPOLOGY topology;
+ switch (params.mode) {
+ case QSGGeometry::DrawPoints:
+ topology = D3D_PRIMITIVE_TOPOLOGY_POINTLIST;
+ break;
+ case QSGGeometry::DrawLines:
+ topology = D3D_PRIMITIVE_TOPOLOGY_LINELIST;
+ break;
+ case QSGGeometry::DrawLineStrip:
+ topology = D3D_PRIMITIVE_TOPOLOGY_LINESTRIP;
+ break;
+ case QSGGeometry::DrawTriangles:
+ topology = D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
+ break;
+ case QSGGeometry::DrawTriangleStrip:
+ topology = D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP;
+ break;
+ default:
+ qFatal("Unsupported drawing mode 0x%x", params.mode);
+ break;
+ }
+ commandList->IASetPrimitiveTopology(topology);
+ tframeData.drawingMode = params.mode;
+ }
+
+ if (!skip) {
+ D3D12_VERTEX_BUFFER_VIEW vbv;
+ vbv.BufferLocation = vbuf->GetGPUVirtualAddress() + params.vboOffset;
+ vbv.SizeInBytes = params.vboSize;
+ vbv.StrideInBytes = params.vboStride;
+
+ // must be set after the topology
+ commandList->IASetVertexBuffers(0, 1, &vbv);
+ }
+
+ if (!skip && params.startIndexIndex >= 0 && ibuf && tframeData.currentIndexBuffer != params.indexBuf) {
+ tframeData.currentIndexBuffer = params.indexBuf;
+ D3D12_INDEX_BUFFER_VIEW ibv;
+ ibv.BufferLocation = ibuf->GetGPUVirtualAddress();
+ ibv.SizeInBytes = buffers[indexBufIdx].cpuDataRef.size;
+ ibv.Format = DXGI_FORMAT(params.indexFormat);
+ commandList->IASetIndexBuffer(&ibv);
+ }
+
+ // Copy the SRVs to a drawcall-dedicated area of the shader-visible descriptor heap.
+ Q_ASSERT(tframeData.activeTextures.count() == tframeData.pipelineState.shaders.rootSig.textureViews.count());
+ if (!tframeData.activeTextures.isEmpty()) {
+ if (!skip) {
+ ensureGPUDescriptorHeap(tframeData.activeTextures.count());
+ const uint stride = cpuDescHeapManager.handleSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
+ D3D12_CPU_DESCRIPTOR_HANDLE dst = pfd.gpuCbvSrvUavHeap->GetCPUDescriptorHandleForHeapStart();
+ dst.ptr += pfd.cbvSrvUavNextFreeDescriptorIndex * stride;
+ for (const TransientFrameData::ActiveTexture &t : qAsConst(tframeData.activeTextures)) {
+ Q_ASSERT(t.id);
+ const int idx = t.id - 1;
+ const bool isTex = t.type == TransientFrameData::ActiveTexture::TypeTexture;
+ device->CopyDescriptorsSimple(1, dst, isTex ? textures[idx].srv : renderTargets[idx].srv,
+ D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
+ dst.ptr += stride;
+ }
+
+ D3D12_GPU_DESCRIPTOR_HANDLE gpuAddr = pfd.gpuCbvSrvUavHeap->GetGPUDescriptorHandleForHeapStart();
+ gpuAddr.ptr += pfd.cbvSrvUavNextFreeDescriptorIndex * stride;
+ commandList->SetGraphicsRootDescriptorTable(1, gpuAddr);
+
+ pfd.cbvSrvUavNextFreeDescriptorIndex += tframeData.activeTextures.count();
+ }
+ tframeData.activeTextures.clear();
+ }
+
+ // Add the draw call.
+ if (!skip) {
+ ++tframeData.drawCount;
+ if (params.startIndexIndex >= 0)
+ commandList->DrawIndexedInstanced(params.count, 1, params.startIndexIndex, 0, 0);
+ else
+ commandList->DrawInstanced(params.count, 1, 0, 0);
+ }
+
+ if (tframeData.drawCount == MAX_DRAW_CALLS_PER_LIST) {
+ if (Q_UNLIKELY(debug_render()))
+ qDebug("Limit of %d draw calls reached, executing command list", MAX_DRAW_CALLS_PER_LIST);
+ // submit the command list
+ endDrawCalls();
+ // start a new one
+ beginDrawCalls();
+ // prepare for the upcoming drawcalls
+ restoreFrameState();
+ }
+}
+
+void QSGD3D12EnginePrivate::ensureGPUDescriptorHeap(int cbvSrvUavDescriptorCount)
+{
+ PersistentFrameData &pfd(pframeData[currentPFrameIndex]);
+ int newSize = pfd.gpuCbvSrvUavHeapSize;
+ while (pfd.cbvSrvUavNextFreeDescriptorIndex + cbvSrvUavDescriptorCount > newSize)
+ newSize *= 2;
+ if (newSize != pfd.gpuCbvSrvUavHeapSize) {
+ if (Q_UNLIKELY(debug_render()))
+ qDebug("Out of space for SRVs, creating new CBV-SRV-UAV descriptor heap with descriptor count %d", newSize);
+ deferredDelete(pfd.gpuCbvSrvUavHeap);
+ createCbvSrvUavHeap(currentPFrameIndex, newSize);
+ setDescriptorHeaps(true);
+ pfd.cbvSrvUavNextFreeDescriptorIndex = 0;
+ }
+}
+
+void QSGD3D12EnginePrivate::present()
+{
+ if (!initialized)
+ return;
+
+ if (Q_UNLIKELY(debug_render()))
+ qDebug("--- present with vsync ---");
+
+ // This call will not block the CPU unless at least 3 buffers are queued,
+ // unless the waitable frame latency event is enabled. Then the latency of
+ // 3 is changed to whatever value desired, and blocking happens in
+ // beginFrame. If none of these hold, the fence-based wait in beginFrame
+ // throttles. Vsync (interval 1) is always enabled.
+ HRESULT hr = swapChain->Present(1, 0);
+ if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET) {
+ deviceManager()->deviceLossDetected();
+ return;
+ } else if (FAILED(hr)) {
+ qWarning("Present failed: 0x%x", hr);
+ return;
+ }
+
+ ++presentFrameIndex;
+}
+
+void QSGD3D12EnginePrivate::waitGPU()
+{
+ if (!initialized)
+ return;
+
+ if (Q_UNLIKELY(debug_render()))
+ qDebug("--- blocking wait for GPU ---");
+
+ waitForGPU(presentFence);
+}
+
+template<class T> uint newId(T *tbl)
+{
+ uint id = 0;
+ for (int i = 0; i < tbl->count(); ++i) {
+ if (!(*tbl)[i].entryInUse()) {
+ id = i + 1;
+ break;
+ }
+ }
+
+ if (!id) {
+ tbl->resize(tbl->size() + 1);
+ id = tbl->count();
+ }
+
+ (*tbl)[id - 1].flags = 0x01; // reset flags and set EntryInUse
+
+ return id;
+}
+
+template<class T> void syncEntryFlags(T *e, int flag, bool b)
+{
+ if (b)
+ e->flags |= flag;
+ else
+ e->flags &= ~flag;
+}
+
+uint QSGD3D12EnginePrivate::genBuffer()
+{
+ return newId(&buffers);
+}
+
+void QSGD3D12EnginePrivate::releaseBuffer(uint id)
+{
+ if (!id || !initialized)
+ return;
+
+ const int idx = id - 1;
+ Q_ASSERT(idx < buffers.count());
+
+ if (Q_UNLIKELY(debug_render()))
+ qDebug("releasing buffer %u", id);
+
+ Buffer &b(buffers[idx]);
+ if (!b.entryInUse())
+ return;
+
+ // Do not null out and do not mark the entry reusable yet.
+ // Do that only when the frames potentially in flight have finished for sure.
+
+ for (int i = 0; i < frameInFlightCount; ++i) {
+ if (b.d[i].buffer)
+ deferredDelete(b.d[i].buffer);
+ }
+
+ QSet<PersistentFrameData::PendingRelease> *pendingReleasesSet = inFrame
+ ? &pframeData[currentPFrameIndex].pendingReleases
+ : &pframeData[(currentPFrameIndex + 1) % frameInFlightCount].outOfFramePendingReleases;
+
+ pendingReleasesSet->insert(PersistentFrameData::PendingRelease(PersistentFrameData::PendingRelease::TypeBuffer, id));
+}
+
+void QSGD3D12EnginePrivate::resetBuffer(uint id, const quint8 *data, int size)
+{
+ if (!inFrame) {
+ qWarning("%s: Cannot be called outside begin/endFrame", __FUNCTION__);
+ return;
+ }
+
+ Q_ASSERT(id);
+ const int idx = id - 1;
+ Q_ASSERT(idx < buffers.count() && buffers[idx].entryInUse());
+ Buffer &b(buffers[idx]);
+
+ if (Q_UNLIKELY(debug_render()))
+ qDebug("reset buffer %u, size %d", id, size);
+
+ b.cpuDataRef.p = data;
+ b.cpuDataRef.size = size;
+
+ b.cpuDataRef.dirty.clear();
+ b.d[currentPFrameIndex].dirty.clear();
+
+ if (size > 0) {
+ const QPair<int, int> range = qMakePair(0, size);
+ b.cpuDataRef.dirty.append(range);
+ b.d[currentPFrameIndex].dirty.append(range);
+ }
+}
+
+void QSGD3D12EnginePrivate::addDirtyRange(DirtyList *dirty, int offset, int size, int bufferSize)
+{
+ // Bail out when the dirty list already spans the entire buffer.
+ if (!dirty->isEmpty()) {
+ if (dirty->at(0).first == 0 && dirty->at(0).second == bufferSize)
+ return;
+ }
+
+ const QPair<int, int> range = qMakePair(offset, size);
+ if (!dirty->contains(range))
+ dirty->append(range);
+}
+
+void QSGD3D12EnginePrivate::markBufferDirty(uint id, int offset, int size)
+{
+ if (!inFrame) {
+ qWarning("%s: Cannot be called outside begin/endFrame", __FUNCTION__);
+ return;
+ }
+
+ Q_ASSERT(id);
+ const int idx = id - 1;
+ Q_ASSERT(idx < buffers.count() && buffers[idx].entryInUse());
+ Buffer &b(buffers[idx]);
+
+ addDirtyRange(&b.cpuDataRef.dirty, offset, size, b.cpuDataRef.size);
+ addDirtyRange(&b.d[currentPFrameIndex].dirty, offset, size, b.cpuDataRef.size);
+}
+
+uint QSGD3D12EnginePrivate::genTexture()
+{
+ const uint id = newId(&textures);
+ textures[id - 1].fenceValue = 0;
+ return id;
+}
+
+static inline DXGI_FORMAT textureFormat(QImage::Format format, bool wantsAlpha, bool mipmap,
+ QImage::Format *imageFormat, int *bytesPerPixel)
+{
+ DXGI_FORMAT f = DXGI_FORMAT_R8G8B8A8_UNORM;
+ QImage::Format convFormat = format;
+ int bpp = 4;
+
+ if (!mipmap) {
+ switch (format) {
+ case QImage::Format_Grayscale8:
+ case QImage::Format_Indexed8:
+ case QImage::Format_Alpha8:
+ f = DXGI_FORMAT_R8_UNORM;
+ bpp = 1;
+ break;
+ case QImage::Format_RGB32:
+ f = DXGI_FORMAT_B8G8R8A8_UNORM;
+ break;
+ case QImage::Format_ARGB32:
+ f = DXGI_FORMAT_B8G8R8A8_UNORM;
+ convFormat = wantsAlpha ? QImage::Format_ARGB32_Premultiplied : QImage::Format_RGB32;
+ break;
+ case QImage::Format_ARGB32_Premultiplied:
+ f = DXGI_FORMAT_B8G8R8A8_UNORM;
+ convFormat = wantsAlpha ? format : QImage::Format_RGB32;
+ break;
+ default:
+ convFormat = wantsAlpha ? QImage::Format_RGBA8888_Premultiplied : QImage::Format_RGBX8888;
+ break;
+ }
+ } else {
+ // Mipmap generation needs unordered access and BGRA is not an option for that. Stick to RGBA.
+ convFormat = wantsAlpha ? QImage::Format_RGBA8888_Premultiplied : QImage::Format_RGBX8888;
+ }
+
+ if (imageFormat)
+ *imageFormat = convFormat;
+
+ if (bytesPerPixel)
+ *bytesPerPixel = bpp;
+
+ return f;
+}
+
+static inline QImage::Format imageFormatForTexture(DXGI_FORMAT format)
+{
+ QImage::Format f = QImage::Format_Invalid;
+
+ switch (format) {
+ case DXGI_FORMAT_R8G8B8A8_UNORM:
+ f = QImage::Format_RGBA8888_Premultiplied;
+ break;
+ case DXGI_FORMAT_B8G8R8A8_UNORM:
+ f = QImage::Format_ARGB32_Premultiplied;
+ break;
+ case DXGI_FORMAT_R8_UNORM:
+ f = QImage::Format_Grayscale8;
+ break;
+ default:
+ break;
+ }
+
+ return f;
+}
+
+void QSGD3D12EnginePrivate::createTexture(uint id, const QSize &size, QImage::Format format,
+ QSGD3D12Engine::TextureCreateFlags createFlags)
+{
+ ensureDevice();
+
+ Q_ASSERT(id);
+ const int idx = id - 1;
+ Q_ASSERT(idx < textures.count() && textures[idx].entryInUse());
+ Texture &t(textures[idx]);
+
+ syncEntryFlags(&t, Texture::Alpha, createFlags & QSGD3D12Engine::TextureWithAlpha);
+ syncEntryFlags(&t, Texture::MipMap, createFlags & QSGD3D12Engine::TextureWithMipMaps);
+
+ const QSize adjustedSize = !t.mipmap() ? size : QSGD3D12Engine::mipMapAdjustedSourceSize(size);
+
+ D3D12_HEAP_PROPERTIES defaultHeapProp = {};
+ defaultHeapProp.Type = D3D12_HEAP_TYPE_DEFAULT;
+
+ D3D12_RESOURCE_DESC textureDesc = {};
+ textureDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
+ textureDesc.Width = adjustedSize.width();
+ textureDesc.Height = adjustedSize.height();
+ textureDesc.DepthOrArraySize = 1;
+ textureDesc.MipLevels = !t.mipmap() ? 1 : QSGD3D12Engine::mipMapLevels(adjustedSize);
+ textureDesc.Format = textureFormat(format, t.alpha(), t.mipmap(), nullptr, nullptr);
+ textureDesc.SampleDesc.Count = 1;
+ textureDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
+ if (t.mipmap())
+ textureDesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
+
+ HRESULT hr = device->CreateCommittedResource(&defaultHeapProp, D3D12_HEAP_FLAG_NONE, &textureDesc,
+ D3D12_RESOURCE_STATE_COMMON, nullptr, IID_PPV_ARGS(&t.texture));
+ if (FAILED(hr)) {
+ qWarning("Failed to create texture resource: 0x%x", hr);
+ return;
+ }
+
+ t.srv = cpuDescHeapManager.allocate(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
+
+ D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
+ srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
+ srvDesc.Format = textureDesc.Format;
+ srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
+ srvDesc.Texture2D.MipLevels = textureDesc.MipLevels;
+
+ device->CreateShaderResourceView(t.texture.Get(), &srvDesc, t.srv);
+
+ if (t.mipmap()) {
+ // Mipmap generation will need an UAV for each level that needs to be generated.
+ t.mipUAVs.clear();
+ for (int level = 1; level < textureDesc.MipLevels; ++level) {
+ D3D12_UNORDERED_ACCESS_VIEW_DESC uavDesc = {};
+ uavDesc.Format = textureDesc.Format;
+ uavDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2D;
+ uavDesc.Texture2D.MipSlice = level;
+ D3D12_CPU_DESCRIPTOR_HANDLE h = cpuDescHeapManager.allocate(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
+ device->CreateUnorderedAccessView(t.texture.Get(), nullptr, &uavDesc, h);
+ t.mipUAVs.append(h);
+ }
+ }
+
+ if (Q_UNLIKELY(debug_render()))
+ qDebug("created texture %u, size %dx%d, miplevels %d", id, adjustedSize.width(), adjustedSize.height(), textureDesc.MipLevels);
+}
+
+void QSGD3D12EnginePrivate::queueTextureResize(uint id, const QSize &size)
+{
+ Q_ASSERT(id);
+ const int idx = id - 1;
+ Q_ASSERT(idx < textures.count() && textures[idx].entryInUse());
+ Texture &t(textures[idx]);
+
+ if (!t.texture) {
+ qWarning("Cannot resize non-created texture %u", id);
+ return;
+ }
+
+ if (t.mipmap()) {
+ qWarning("Cannot resize mipmapped texture %u", id);
+ return;
+ }
+
+ if (Q_UNLIKELY(debug_render()))
+ qDebug("resizing texture %u, size %dx%d", id, size.width(), size.height());
+
+ D3D12_RESOURCE_DESC textureDesc = t.texture->GetDesc();
+ textureDesc.Width = size.width();
+ textureDesc.Height = size.height();
+
+ D3D12_HEAP_PROPERTIES defaultHeapProp = {};
+ defaultHeapProp.Type = D3D12_HEAP_TYPE_DEFAULT;
+
+ ComPtr<ID3D12Resource> oldTexture = t.texture;
+ deferredDelete(t.texture);
+
+ HRESULT hr = device->CreateCommittedResource(&defaultHeapProp, D3D12_HEAP_FLAG_NONE, &textureDesc,
+ D3D12_RESOURCE_STATE_COMMON, nullptr, IID_PPV_ARGS(&t.texture));
+ if (FAILED(hr)) {
+ qWarning("Failed to create resized texture resource: 0x%x", hr);
+ return;
+ }
+
+ deferredDelete(t.srv, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
+ t.srv = cpuDescHeapManager.allocate(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
+
+ D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
+ srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
+ srvDesc.Format = textureDesc.Format;
+ srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
+ srvDesc.Texture2D.MipLevels = textureDesc.MipLevels;
+
+ device->CreateShaderResourceView(t.texture.Get(), &srvDesc, t.srv);
+
+ D3D12_TEXTURE_COPY_LOCATION dstLoc;
+ dstLoc.pResource = t.texture.Get();
+ dstLoc.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
+ dstLoc.SubresourceIndex = 0;
+
+ D3D12_TEXTURE_COPY_LOCATION srcLoc;
+ srcLoc.pResource = oldTexture.Get();
+ srcLoc.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
+ srcLoc.SubresourceIndex = 0;
+
+ copyCommandList->Reset(copyCommandAllocator.Get(), nullptr);
+
+ copyCommandList->CopyTextureRegion(&dstLoc, 0, 0, 0, &srcLoc, nullptr);
+
+ copyCommandList->Close();
+ ID3D12CommandList *commandLists[] = { copyCommandList.Get() };
+ copyCommandQueue->ExecuteCommandLists(_countof(commandLists), commandLists);
+
+ t.fenceValue = nextTextureUploadFenceValue.fetchAndAddAcquire(1) + 1;
+ copyCommandQueue->Signal(textureUploadFence.Get(), t.fenceValue);
+
+ if (Q_UNLIKELY(debug_render()))
+ qDebug("submitted old content copy for texture %u on the copy queue, fence %llu", id, t.fenceValue);
+}
+
+void QSGD3D12EnginePrivate::queueTextureUpload(uint id, const QVector<QImage> &images, const QVector<QPoint> &dstPos)
+{
+ Q_ASSERT(id);
+ Q_ASSERT(images.count() == dstPos.count());
+ if (images.isEmpty())
+ return;
+
+ const int idx = id - 1;
+ Q_ASSERT(idx < textures.count() && textures[idx].entryInUse());
+ Texture &t(textures[idx]);
+ Q_ASSERT(t.texture);
+
+ // When mipmapping is not in use, image can be smaller than the size passed
+ // to createTexture() and dstPos can specify a non-zero destination position.
+
+ if (t.mipmap() && (images.count() != 1 || dstPos.count() != 1 || !dstPos[0].isNull())) {
+ qWarning("Mipmapped textures (%u) do not support partial uploads", id);
+ return;
+ }
+
+ // Make life simpler by disallowing queuing a new mipmapped upload before the previous one finishes.
+ if (t.mipmap() && t.fenceValue) {
+ qWarning("Attempted to queue mipmapped texture upload (%u) while a previous upload is still in progress", id);
+ return;
+ }
+
+ t.fenceValue = nextTextureUploadFenceValue.fetchAndAddAcquire(1) + 1;
+
+ if (Q_UNLIKELY(debug_render()))
+ qDebug("adding upload for texture %u on the copy queue, fence %llu", id, t.fenceValue);
+
+ D3D12_RESOURCE_DESC textureDesc = t.texture->GetDesc();
+ const QSize adjustedTextureSize(textureDesc.Width, textureDesc.Height);
+
+ int totalSize = 0;
+ for (const QImage &image : images) {
+ int bytesPerPixel;
+ textureFormat(image.format(), t.alpha(), t.mipmap(), nullptr, &bytesPerPixel);
+ const int w = !t.mipmap() ? image.width() : adjustedTextureSize.width();
+ const int h = !t.mipmap() ? image.height() : adjustedTextureSize.height();
+ const int stride = alignedSize(w * bytesPerPixel, D3D12_TEXTURE_DATA_PITCH_ALIGNMENT);
+ totalSize += alignedSize(h * stride, D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT);
+ }
+
+ if (Q_UNLIKELY(debug_render()))
+ qDebug("%d sub-uploads, heap size %d bytes", images.count(), totalSize);
+
+ // Instead of individual committed resources for each upload buffer,
+ // allocate only once and use placed resources.
+ D3D12_HEAP_PROPERTIES uploadHeapProp = {};
+ uploadHeapProp.Type = D3D12_HEAP_TYPE_UPLOAD;
+ D3D12_HEAP_DESC uploadHeapDesc = {};
+ uploadHeapDesc.SizeInBytes = totalSize;
+ uploadHeapDesc.Properties = uploadHeapProp;
+ uploadHeapDesc.Flags = D3D12_HEAP_FLAG_ALLOW_ONLY_BUFFERS;
+
+ Texture::StagingHeap sheap;
+ if (FAILED(device->CreateHeap(&uploadHeapDesc, IID_PPV_ARGS(&sheap.heap)))) {
+ qWarning("Failed to create texture upload heap of size %d", totalSize);
+ return;
+ }
+ t.stagingHeaps.append(sheap);
+
+ copyCommandList->Reset(copyCommandAllocator.Get(), nullptr);
+
+ int placedOffset = 0;
+ for (int i = 0; i < images.count(); ++i) {
+ QImage::Format convFormat;
+ int bytesPerPixel;
+ textureFormat(images[i].format(), t.alpha(), t.mipmap(), &convFormat, &bytesPerPixel);
+ if (Q_UNLIKELY(debug_render() && i == 0))
+ qDebug("source image format %d, target format %d, bpp %d", images[i].format(), convFormat, bytesPerPixel);
+
+ QImage convImage = images[i].format() == convFormat ? images[i] : images[i].convertToFormat(convFormat);
+
+ if (t.mipmap() && adjustedTextureSize != convImage.size())
+ convImage = convImage.scaled(adjustedTextureSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
+
+ const int stride = alignedSize(convImage.width() * bytesPerPixel, D3D12_TEXTURE_DATA_PITCH_ALIGNMENT);
+
+ D3D12_RESOURCE_DESC bufDesc = {};
+ bufDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
+ bufDesc.Width = stride * convImage.height();
+ bufDesc.Height = 1;
+ bufDesc.DepthOrArraySize = 1;
+ bufDesc.MipLevels = 1;
+ bufDesc.Format = DXGI_FORMAT_UNKNOWN;
+ bufDesc.SampleDesc.Count = 1;
+ bufDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
+
+ Texture::StagingBuffer sbuf;
+ if (FAILED(device->CreatePlacedResource(sheap.heap.Get(), placedOffset,
+ &bufDesc, D3D12_RESOURCE_STATE_GENERIC_READ,
+ nullptr, IID_PPV_ARGS(&sbuf.buffer)))) {
+ qWarning("Failed to create texture upload buffer");
+ return;
+ }
+
+ quint8 *p = nullptr;
+ const D3D12_RANGE readRange = { 0, 0 };
+ if (FAILED(sbuf.buffer->Map(0, &readRange, reinterpret_cast<void **>(&p)))) {
+ qWarning("Map failed (texture upload buffer)");
+ return;
+ }
+ for (int y = 0, ye = convImage.height(); y < ye; ++y) {
+ memcpy(p, convImage.constScanLine(y), convImage.width() * bytesPerPixel);
+ p += stride;
+ }
+ sbuf.buffer->Unmap(0, nullptr);
+
+ D3D12_TEXTURE_COPY_LOCATION dstLoc;
+ dstLoc.pResource = t.texture.Get();
+ dstLoc.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
+ dstLoc.SubresourceIndex = 0;
+
+ D3D12_TEXTURE_COPY_LOCATION srcLoc;
+ srcLoc.pResource = sbuf.buffer.Get();
+ srcLoc.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
+ srcLoc.PlacedFootprint.Offset = 0;
+ srcLoc.PlacedFootprint.Footprint.Format = textureDesc.Format;
+ srcLoc.PlacedFootprint.Footprint.Width = convImage.width();
+ srcLoc.PlacedFootprint.Footprint.Height = convImage.height();
+ srcLoc.PlacedFootprint.Footprint.Depth = 1;
+ srcLoc.PlacedFootprint.Footprint.RowPitch = stride;
+
+ copyCommandList->CopyTextureRegion(&dstLoc, dstPos[i].x(), dstPos[i].y(), 0, &srcLoc, nullptr);
+
+ t.stagingBuffers.append(sbuf);
+ placedOffset += alignedSize(bufDesc.Width, D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT);
+ }
+
+ copyCommandList->Close();
+ ID3D12CommandList *commandLists[] = { copyCommandList.Get() };
+ copyCommandQueue->ExecuteCommandLists(_countof(commandLists), commandLists);
+ copyCommandQueue->Signal(textureUploadFence.Get(), t.fenceValue);
+}
+
+void QSGD3D12EnginePrivate::releaseTexture(uint id)
+{
+ if (!id || !initialized)
+ return;
+
+ const int idx = id - 1;
+ Q_ASSERT(idx < textures.count());
+
+ if (Q_UNLIKELY(debug_render()))
+ qDebug("releasing texture %d", id);
+
+ Texture &t(textures[idx]);
+ if (!t.entryInUse())
+ return;
+
+ if (t.texture) {
+ deferredDelete(t.texture);
+ deferredDelete(t.srv, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
+ for (D3D12_CPU_DESCRIPTOR_HANDLE h : t.mipUAVs)
+ deferredDelete(h, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
+ }
+
+ QSet<PersistentFrameData::PendingRelease> *pendingReleasesSet = inFrame
+ ? &pframeData[currentPFrameIndex].pendingReleases
+ : &pframeData[(currentPFrameIndex + 1) % frameInFlightCount].outOfFramePendingReleases;
+
+ pendingReleasesSet->insert(PersistentFrameData::PendingRelease(PersistentFrameData::PendingRelease::TypeTexture, id));
+}
+
+SIZE_T QSGD3D12EnginePrivate::textureSRV(uint id) const
+{
+ Q_ASSERT(id);
+ const int idx = id - 1;
+ Q_ASSERT(idx < textures.count() && textures[idx].entryInUse());
+ return textures[idx].srv.ptr;
+}
+
+void QSGD3D12EnginePrivate::useTexture(uint id)
+{
+ if (!inFrame) {
+ qWarning("%s: Cannot be called outside begin/endFrame", __FUNCTION__);
+ return;
+ }
+
+ Q_ASSERT(id);
+ const int idx = id - 1;
+ Q_ASSERT(idx < textures.count() && textures[idx].entryInUse());
+
+ // activeTextures is a vector because the order matters
+ tframeData.activeTextures.append(TransientFrameData::ActiveTexture(TransientFrameData::ActiveTexture::TypeTexture, id));
+
+ if (textures[idx].fenceValue)
+ pframeData[currentPFrameIndex].pendingTextureUploads.insert(id);
+}
+
+bool QSGD3D12EnginePrivate::MipMapGen::initialize(QSGD3D12EnginePrivate *enginePriv)
+{
+ engine = enginePriv;
+
+ D3D12_STATIC_SAMPLER_DESC sampler = {};
+ sampler.Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR;
+ sampler.AddressU = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
+ sampler.AddressV = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
+ sampler.AddressW = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
+ sampler.MinLOD = 0.0f;
+ sampler.MaxLOD = D3D12_FLOAT32_MAX;
+
+ D3D12_DESCRIPTOR_RANGE descRange[2];
+ descRange[0].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
+ descRange[0].NumDescriptors = 1;
+ descRange[0].BaseShaderRegister = 0; // t0
+ descRange[0].RegisterSpace = 0;
+ descRange[0].OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
+ descRange[1].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV;
+ descRange[1].NumDescriptors = 4;
+ descRange[1].BaseShaderRegister = 0; // u0..u3
+ descRange[1].RegisterSpace = 0;
+ descRange[1].OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
+
+ // Split into two to allow switching between the first and second set of UAVs later.
+ D3D12_ROOT_PARAMETER rootParameters[3];
+ rootParameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
+ rootParameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
+ rootParameters[0].DescriptorTable.NumDescriptorRanges = 1;
+ rootParameters[0].DescriptorTable.pDescriptorRanges = &descRange[0];
+
+ rootParameters[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
+ rootParameters[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
+ rootParameters[1].DescriptorTable.NumDescriptorRanges = 1;
+ rootParameters[1].DescriptorTable.pDescriptorRanges = &descRange[1];
+
+ rootParameters[2].ParameterType = D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS;
+ rootParameters[2].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
+ rootParameters[2].Constants.Num32BitValues = 4; // uint2 mip1Size, uint sampleLevel, uint totalMips
+ rootParameters[2].Constants.ShaderRegister = 0; // b0
+ rootParameters[2].Constants.RegisterSpace = 0;
+
+ D3D12_ROOT_SIGNATURE_DESC desc = {};
+ desc.NumParameters = 3;
+ desc.pParameters = rootParameters;
+ desc.NumStaticSamplers = 1;
+ desc.pStaticSamplers = &sampler;
+
+ ComPtr<ID3DBlob> signature;
+ ComPtr<ID3DBlob> error;
+ if (FAILED(D3D12SerializeRootSignature(&desc, D3D_ROOT_SIGNATURE_VERSION_1, &signature, &error))) {
+ QByteArray msg(static_cast<const char *>(error->GetBufferPointer()), error->GetBufferSize());
+ qWarning("Failed to serialize compute root signature: %s", qPrintable(msg));
+ return false;
+ }
+ if (FAILED(engine->device->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(),
+ IID_PPV_ARGS(&rootSig)))) {
+ qWarning("Failed to create compute root signature");
+ return false;
+ }
+
+ D3D12_COMPUTE_PIPELINE_STATE_DESC psoDesc = {};
+ psoDesc.pRootSignature = rootSig.Get();
+ psoDesc.CS.pShaderBytecode = g_CS_Generate4MipMaps;
+ psoDesc.CS.BytecodeLength = sizeof(g_CS_Generate4MipMaps);
+
+ if (FAILED(engine->device->CreateComputePipelineState(&psoDesc, IID_PPV_ARGS(&pipelineState)))) {
+ qWarning("Failed to create compute pipeline state");
+ return false;
+ }
+
+ return true;
+}
+
+void QSGD3D12EnginePrivate::MipMapGen::releaseResources()
+{
+ pipelineState = nullptr;
+ rootSig = nullptr;
+}
+
+// The mipmap generator is used to insert commands on the main 3D queue. It is
+// guaranteed that the queue has a wait for the base texture level upload
+// before invoking queueGenerate(). There can be any number of invocations
+// without waiting for earlier ones to finish. finished() is invoked when it is
+// known for sure that frame containing the upload and mipmap generation has
+// finished on the GPU.
+
+void QSGD3D12EnginePrivate::MipMapGen::queueGenerate(const Texture &t)
+{
+ D3D12_RESOURCE_DESC textureDesc = t.texture->GetDesc();
+
+ engine->commandList->SetPipelineState(pipelineState.Get());
+ engine->commandList->SetComputeRootSignature(rootSig.Get());
+
+ // 1 SRV + (miplevels - 1) UAVs
+ const int descriptorCount = 1 + (textureDesc.MipLevels - 1);
+
+ engine->ensureGPUDescriptorHeap(descriptorCount);
+
+ // The descriptor heap is set on the command list either because the
+ // ensure() call above resized, or, typically, due to a texture-dependent
+ // draw call earlier.
+
+ engine->transitionResource(t.texture.Get(), engine->commandList,
+ D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
+
+ QSGD3D12EnginePrivate::PersistentFrameData &pfd(engine->pframeData[engine->currentPFrameIndex]);
+
+ const uint stride = engine->cpuDescHeapManager.handleSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
+ D3D12_CPU_DESCRIPTOR_HANDLE h = pfd.gpuCbvSrvUavHeap->GetCPUDescriptorHandleForHeapStart();
+ h.ptr += pfd.cbvSrvUavNextFreeDescriptorIndex * stride;
+
+ engine->device->CopyDescriptorsSimple(1, h, t.srv, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
+ h.ptr += stride;
+
+ for (int level = 1; level < textureDesc.MipLevels; ++level, h.ptr += stride)
+ engine->device->CopyDescriptorsSimple(1, h, t.mipUAVs[level - 1], D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
+
+ D3D12_GPU_DESCRIPTOR_HANDLE gpuAddr = pfd.gpuCbvSrvUavHeap->GetGPUDescriptorHandleForHeapStart();
+ gpuAddr.ptr += pfd.cbvSrvUavNextFreeDescriptorIndex * stride;
+
+ engine->commandList->SetComputeRootDescriptorTable(0, gpuAddr);
+ gpuAddr.ptr += stride; // now points to the first UAV
+
+ for (int level = 1; level < textureDesc.MipLevels; level += 4, gpuAddr.ptr += stride * 4) {
+ engine->commandList->SetComputeRootDescriptorTable(1, gpuAddr);
+
+ QSize sz(textureDesc.Width, textureDesc.Height);
+ sz.setWidth(qMax(1, sz.width() >> level));
+ sz.setHeight(qMax(1, sz.height() >> level));
+
+ const quint32 constants[4] = { quint32(sz.width()), quint32(sz.height()),
+ quint32(level - 1),
+ quint32(textureDesc.MipLevels - 1) };
+
+ engine->commandList->SetComputeRoot32BitConstants(2, 4, constants, 0);
+ engine->commandList->Dispatch(sz.width(), sz.height(), 1);
+ engine->uavBarrier(t.texture.Get(), engine->commandList);
+ }
+
+ engine->transitionResource(t.texture.Get(), engine->commandList,
+ D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
+
+ pfd.cbvSrvUavNextFreeDescriptorIndex += descriptorCount;
+}
+
+void QSGD3D12EnginePrivate::deferredDelete(ComPtr<ID3D12Resource> res)
+{
+ PersistentFrameData::DeleteQueueEntry e;
+ e.res = res;
+ QVector<PersistentFrameData::DeleteQueueEntry> *dq = inFrame
+ ? &pframeData[currentPFrameIndex].deleteQueue
+ : &pframeData[(currentPFrameIndex + 1) % frameInFlightCount].outOfFrameDeleteQueue;
+ (*dq) << e;
+}
+
+void QSGD3D12EnginePrivate::deferredDelete(ComPtr<ID3D12DescriptorHeap> dh)
+{
+ PersistentFrameData::DeleteQueueEntry e;
+ e.descHeap = dh;
+ QVector<PersistentFrameData::DeleteQueueEntry> *dq = inFrame
+ ? &pframeData[currentPFrameIndex].deleteQueue
+ : &pframeData[(currentPFrameIndex + 1) % frameInFlightCount].outOfFrameDeleteQueue;
+ (*dq) << e;
+}
+
+void QSGD3D12EnginePrivate::deferredDelete(D3D12_CPU_DESCRIPTOR_HANDLE h, D3D12_DESCRIPTOR_HEAP_TYPE type)
+{
+ PersistentFrameData::DeleteQueueEntry e;
+ e.cpuDescriptorPtr = h.ptr;
+ e.descHeapType = type;
+ QVector<PersistentFrameData::DeleteQueueEntry> *dq = inFrame
+ ? &pframeData[currentPFrameIndex].deleteQueue
+ : &pframeData[(currentPFrameIndex + 1) % frameInFlightCount].outOfFrameDeleteQueue;
+ (*dq) << e;
+}
+
+uint QSGD3D12EnginePrivate::genRenderTarget()
+{
+ return newId(&renderTargets);
+}
+
+void QSGD3D12EnginePrivate::createRenderTarget(uint id, const QSize &size, const QVector4D &clearColor, uint samples)
+{
+ ensureDevice();
+
+ Q_ASSERT(id);
+ const int idx = id - 1;
+ Q_ASSERT(idx < renderTargets.count() && renderTargets[idx].entryInUse());
+ RenderTarget &rt(renderTargets[idx]);
+
+ rt.rtv = cpuDescHeapManager.allocate(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
+ rt.dsv = cpuDescHeapManager.allocate(D3D12_DESCRIPTOR_HEAP_TYPE_DSV);
+ rt.srv = cpuDescHeapManager.allocate(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
+
+ ID3D12Resource *res = createColorBuffer(rt.rtv, size, clearColor, samples);
+ if (res)
+ rt.color.Attach(res);
+
+ ID3D12Resource *dsres = createDepthStencil(rt.dsv, size, samples);
+ if (dsres)
+ rt.ds.Attach(dsres);
+
+ const bool multisample = rt.color->GetDesc().SampleDesc.Count > 1;
+ syncEntryFlags(&rt, RenderTarget::Multisample, multisample);
+
+ if (!multisample) {
+ device->CreateShaderResourceView(rt.color.Get(), nullptr, rt.srv);
+ } else {
+ D3D12_HEAP_PROPERTIES defaultHeapProp = {};
+ defaultHeapProp.Type = D3D12_HEAP_TYPE_DEFAULT;
+
+ D3D12_RESOURCE_DESC textureDesc = {};
+ textureDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
+ textureDesc.Width = size.width();
+ textureDesc.Height = size.height();
+ textureDesc.DepthOrArraySize = 1;
+ textureDesc.MipLevels = 1;
+ textureDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
+ textureDesc.SampleDesc.Count = 1;
+ textureDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
+
+ HRESULT hr = device->CreateCommittedResource(&defaultHeapProp, D3D12_HEAP_FLAG_NONE, &textureDesc,
+ D3D12_RESOURCE_STATE_COMMON, nullptr, IID_PPV_ARGS(&rt.colorResolve));
+ if (FAILED(hr)) {
+ qWarning("Failed to create resolve buffer: 0x%x", hr);
+ return;
+ }
+
+ device->CreateShaderResourceView(rt.colorResolve.Get(), nullptr, rt.srv);
+ }
+
+ if (Q_UNLIKELY(debug_render()))
+ qDebug("created new render target %u, size %dx%d, samples %d", id, size.width(), size.height(), samples);
+}
+
+void QSGD3D12EnginePrivate::releaseRenderTarget(uint id)
+{
+ if (!id || !initialized)
+ return;
+
+ const int idx = id - 1;
+ Q_ASSERT(idx < renderTargets.count());
+ RenderTarget &rt(renderTargets[idx]);
+ if (!rt.entryInUse())
+ return;
+
+ if (Q_UNLIKELY(debug_render()))
+ qDebug("releasing render target %u", id);
+
+ if (rt.colorResolve) {
+ deferredDelete(rt.colorResolve);
+ rt.colorResolve = nullptr;
+ }
+ if (rt.color) {
+ deferredDelete(rt.color);
+ rt.color = nullptr;
+ deferredDelete(rt.rtv, D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
+ deferredDelete(rt.srv, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
+ }
+ if (rt.ds) {
+ deferredDelete(rt.ds);
+ rt.ds = nullptr;
+ deferredDelete(rt.dsv, D3D12_DESCRIPTOR_HEAP_TYPE_DSV);
+ }
+
+ rt.flags &= ~RenderTarget::EntryInUse;
+}
+
+void QSGD3D12EnginePrivate::useRenderTargetAsTexture(uint id)
+{
+ if (!inFrame) {
+ qWarning("%s: Cannot be called outside begin/endFrame", __FUNCTION__);
+ return;
+ }
+
+ Q_ASSERT(id);
+ const int idx = id - 1;
+ Q_ASSERT(idx < renderTargets.count());
+ RenderTarget &rt(renderTargets[idx]);
+ Q_ASSERT(rt.entryInUse() && rt.color);
+
+ if (rt.flags & RenderTarget::NeedsReadBarrier) {
+ rt.flags &= ~RenderTarget::NeedsReadBarrier;
+ if (rt.flags & RenderTarget::Multisample)
+ resolveMultisampledTarget(rt.color.Get(), rt.colorResolve.Get(), D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, commandList);
+ else
+ transitionResource(rt.color.Get(), commandList, D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
+ }
+
+ tframeData.activeTextures.append(TransientFrameData::ActiveTexture::ActiveTexture(TransientFrameData::ActiveTexture::TypeRenderTarget, id));
+}
+
+QImage QSGD3D12EnginePrivate::executeAndWaitReadbackRenderTarget(uint id)
+{
+ // Readback due to QQuickWindow::grabWindow() happens outside
+ // begin-endFrame, but QQuickItemGrabResult leads to rendering a layer
+ // without a real frame afterwards and triggering readback. This has to be
+ // supported as well.
+ if (inFrame && (!activeLayers || currentLayerDepth)) {
+ qWarning("%s: Cannot be called while frame preparation is active", __FUNCTION__);
+ return QImage();
+ }
+
+ // Due to the above we insert a fake "real" frame when a layer was just rendered into.
+ if (inFrame) {
+ beginFrame();
+ endFrame();
+ }
+
+ frameCommandList->Reset(frameCommandAllocator[frameIndex % frameInFlightCount].Get(), nullptr);
+
+ D3D12_RESOURCE_STATES bstate;
+ bool needsBarrier = false;
+ ID3D12Resource *rtRes;
+ if (id == 0) {
+ const int idx = presentFrameIndex % swapChainBufferCount;
+ if (windowSamples > 1) {
+ resolveMultisampledTarget(defaultRT[idx].Get(), backBufferRT[idx].Get(),
+ D3D12_RESOURCE_STATE_COPY_SOURCE, frameCommandList.Get());
+ } else {
+ bstate = D3D12_RESOURCE_STATE_PRESENT;
+ needsBarrier = true;
+ }
+ rtRes = backBufferRT[idx].Get();
+ } else {
+ const int idx = id - 1;
+ Q_ASSERT(idx < renderTargets.count());
+ RenderTarget &rt(renderTargets[idx]);
+ Q_ASSERT(rt.entryInUse() && rt.color);
+
+ if (rt.flags & RenderTarget::Multisample) {
+ resolveMultisampledTarget(rt.color.Get(), rt.colorResolve.Get(),
+ D3D12_RESOURCE_STATE_COPY_SOURCE, frameCommandList.Get());
+ rtRes = rt.colorResolve.Get();
+ } else {
+ rtRes = rt.color.Get();
+ bstate = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
+ needsBarrier = true;
+ }
+ }
+
+ ComPtr<ID3D12Resource> readbackBuf;
+
+ D3D12_RESOURCE_DESC rtDesc = rtRes->GetDesc();
+ UINT64 textureByteSize = 0;
+ D3D12_PLACED_SUBRESOURCE_FOOTPRINT textureLayout = {};
+ device->GetCopyableFootprints(&rtDesc, 0, 1, 0, &textureLayout, nullptr, nullptr, &textureByteSize);
+
+ D3D12_HEAP_PROPERTIES heapProp = {};
+ heapProp.Type = D3D12_HEAP_TYPE_READBACK;
+
+ D3D12_RESOURCE_DESC bufDesc = {};
+ bufDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
+ bufDesc.Width = textureByteSize;
+ bufDesc.Height = 1;
+ bufDesc.DepthOrArraySize = 1;
+ bufDesc.MipLevels = 1;
+ bufDesc.Format = DXGI_FORMAT_UNKNOWN;
+ bufDesc.SampleDesc.Count = 1;
+ bufDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
+
+ if (FAILED(device->CreateCommittedResource(&heapProp, D3D12_HEAP_FLAG_NONE, &bufDesc,
+ D3D12_RESOURCE_STATE_COPY_DEST, nullptr, IID_PPV_ARGS(&readbackBuf)))) {
+ qWarning("Failed to create committed resource (readback buffer)");
+ return QImage();
+ }
+
+ D3D12_TEXTURE_COPY_LOCATION dstLoc;
+ dstLoc.pResource = readbackBuf.Get();
+ dstLoc.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
+ dstLoc.PlacedFootprint = textureLayout;
+ D3D12_TEXTURE_COPY_LOCATION srcLoc;
+ srcLoc.pResource = rtRes;
+ srcLoc.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
+ srcLoc.SubresourceIndex = 0;
+
+ ID3D12GraphicsCommandList *cl = frameCommandList.Get();
+ if (needsBarrier)
+ transitionResource(rtRes, cl, bstate, D3D12_RESOURCE_STATE_COPY_SOURCE);
+ cl->CopyTextureRegion(&dstLoc, 0, 0, 0, &srcLoc, nullptr);
+ if (needsBarrier)
+ transitionResource(rtRes, cl, D3D12_RESOURCE_STATE_COPY_SOURCE, bstate);
+
+ cl->Close();
+ ID3D12CommandList *commandLists[] = { cl };
+ commandQueue->ExecuteCommandLists(_countof(commandLists), commandLists);
+
+ QScopedPointer<QSGD3D12CPUWaitableFence> f(createCPUWaitableFence());
+ waitForGPU(f.data()); // uh oh
+
+ QImage::Format fmt = imageFormatForTexture(rtDesc.Format);
+ if (fmt == QImage::Format_Invalid) {
+ qWarning("Could not map render target format %d to a QImage format", rtDesc.Format);
+ return QImage();
+ }
+ QImage img(rtDesc.Width, rtDesc.Height, fmt);
+ quint8 *p = nullptr;
+ const D3D12_RANGE readRange = { 0, 0 };
+ if (FAILED(readbackBuf->Map(0, &readRange, reinterpret_cast<void **>(&p)))) {
+ qWarning("Mapping the readback buffer failed");
+ return QImage();
+ }
+ const int bpp = 4; // ###
+ if (id == 0) {
+ for (UINT y = 0; y < rtDesc.Height; ++y) {
+ quint8 *dst = img.scanLine(y);
+ memcpy(dst, p, rtDesc.Width * bpp);
+ p += textureLayout.Footprint.RowPitch;
+ }
+ } else {
+ for (int y = rtDesc.Height - 1; y >= 0; --y) {
+ quint8 *dst = img.scanLine(y);
+ memcpy(dst, p, rtDesc.Width * bpp);
+ p += textureLayout.Footprint.RowPitch;
+ }
+ }
+ readbackBuf->Unmap(0, nullptr);
+
+ return img;
+}
+
+void QSGD3D12EnginePrivate::simulateDeviceLoss()
+{
+ qWarning("QSGD3D12Engine: Triggering device loss via TDR");
+ devLossTest.killDevice();
+}
+
+bool QSGD3D12EnginePrivate::DeviceLossTester::initialize(QSGD3D12EnginePrivate *enginePriv)
+{
+ engine = enginePriv;
+
+#ifdef DEVLOSS_TEST
+ D3D12_DESCRIPTOR_RANGE descRange[2];
+ descRange[0].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_CBV;
+ descRange[0].NumDescriptors = 1;
+ descRange[0].BaseShaderRegister = 0;
+ descRange[0].RegisterSpace = 0;
+ descRange[0].OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
+ descRange[1].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV;
+ descRange[1].NumDescriptors = 1;
+ descRange[1].BaseShaderRegister = 0;
+ descRange[1].RegisterSpace = 0;
+ descRange[1].OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
+
+ D3D12_ROOT_PARAMETER param;
+ param.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
+ param.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
+ param.DescriptorTable.NumDescriptorRanges = 2;
+ param.DescriptorTable.pDescriptorRanges = descRange;
+
+ D3D12_ROOT_SIGNATURE_DESC desc = {};
+ desc.NumParameters = 1;
+ desc.pParameters = &param;
+
+ ComPtr<ID3DBlob> signature;
+ ComPtr<ID3DBlob> error;
+ if (FAILED(D3D12SerializeRootSignature(&desc, D3D_ROOT_SIGNATURE_VERSION_1, &signature, &error))) {
+ QByteArray msg(static_cast<const char *>(error->GetBufferPointer()), error->GetBufferSize());
+ qWarning("Failed to serialize compute root signature: %s", qPrintable(msg));
+ return false;
+ }
+ if (FAILED(engine->device->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(),
+ IID_PPV_ARGS(&computeRootSignature)))) {
+ qWarning("Failed to create compute root signature");
+ return false;
+ }
+
+ D3D12_COMPUTE_PIPELINE_STATE_DESC psoDesc = {};
+ psoDesc.pRootSignature = computeRootSignature.Get();
+ psoDesc.CS.pShaderBytecode = g_timeout;
+ psoDesc.CS.BytecodeLength = sizeof(g_timeout);
+
+ if (FAILED(engine->device->CreateComputePipelineState(&psoDesc, IID_PPV_ARGS(&computeState)))) {
+ qWarning("Failed to create compute pipeline state");
+ return false;
+ }
+#endif
+
+ return true;
+}
+
+void QSGD3D12EnginePrivate::DeviceLossTester::releaseResources()
+{
+ computeState = nullptr;
+ computeRootSignature = nullptr;
+}
+
+void QSGD3D12EnginePrivate::DeviceLossTester::killDevice()
+{
+#ifdef DEVLOSS_TEST
+ ID3D12CommandAllocator *ca = engine->frameCommandAllocator[engine->frameIndex % engine->frameInFlightCount].Get();
+ ID3D12GraphicsCommandList *cl = engine->frameCommandList.Get();
+ cl->Reset(ca, computeState.Get());
+
+ cl->SetComputeRootSignature(computeRootSignature.Get());
+ cl->Dispatch(256, 1, 1);
+
+ cl->Close();
+ ID3D12CommandList *commandLists[] = { cl };
+ engine->commandQueue->ExecuteCommandLists(_countof(commandLists), commandLists);
+
+ engine->waitGPU();
+#endif
+}
+
+void *QSGD3D12EnginePrivate::getResource(QSGRendererInterface::Resource resource) const
+{
+ switch (resource) {
+ case QSGRendererInterface::Device:
+ return device;
+ case QSGRendererInterface::CommandQueue:
+ return commandQueue.Get();
+ case QSGRendererInterface::CommandList:
+ return commandList;
+ default:
+ break;
+ }
+ return nullptr;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12engine_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12engine_p.h
new file mode 100644
index 0000000000..d4c0d0b9e3
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12engine_p.h
@@ -0,0 +1,370 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGD3D12ENGINE_P_H
+#define QSGD3D12ENGINE_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 <QWindow>
+#include <QImage>
+#include <QVector4D>
+#include <qsggeometry.h>
+#include <qsgrendererinterface.h>
+#include <qt_windows.h>
+
+QT_BEGIN_NAMESPACE
+
+// No D3D or COM headers must be pulled in here. All that has to be isolated
+// to engine_p_p.h and engine.cpp.
+
+class QSGD3D12EnginePrivate;
+
+// Shader bytecode and other strings are expected to be static so that a
+// different pointer means a different shader.
+
+enum QSGD3D12Format {
+ FmtUnknown = 0,
+
+ FmtFloat4 = 2, // DXGI_FORMAT_R32G32B32A32_FLOAT
+ FmtFloat3 = 6, // DXGI_FORMAT_R32G32B32_FLOAT
+ FmtFloat2 = 16, // DXGI_FORMAT_R32G32_FLOAT
+ FmtFloat = 41, // DXGI_FORMAT_R32_FLOAT
+
+ // glVertexAttribPointer with GL_UNSIGNED_BYTE and normalized == true maps to the UNORM formats below
+ FmtUNormByte4 = 28, // DXGI_FORMAT_R8G8B8A8_UNORM
+ FmtUNormByte2 = 49, // DXGI_FORMAT_R8G8_UNORM
+ FmtUNormByte = 61, // DXGI_FORMAT_R8_UNORM
+
+ // Index data types
+ FmtUnsignedShort = 57, // DXGI_FORMAT_R16_UINT
+ FmtUnsignedInt = 42 // DXGI_FORMAT_R32_UINT
+};
+
+struct QSGD3D12InputElement
+{
+ const char *semanticName = nullptr;
+ int semanticIndex = 0;
+ QSGD3D12Format format = FmtFloat4;
+ quint32 slot = 0;
+ quint32 offset = 0;
+
+ bool operator==(const QSGD3D12InputElement &other) const {
+ return semanticName == other.semanticName && semanticIndex == other.semanticIndex
+ && format == other.format && slot == other.slot && offset == other.offset;
+ }
+};
+
+inline uint qHash(const QSGD3D12InputElement &key, uint seed = 0)
+{
+ return qHash(key.semanticName, seed) + key.semanticIndex + key.format + key.offset;
+}
+
+struct QSGD3D12TextureView
+{
+ enum Filter {
+ FilterNearest = 0,
+ FilterLinear = 0x15,
+ FilterMinMagNearestMipLinear = 0x1,
+ FilterMinMagLinearMipNearest = 0x14
+ };
+
+ enum AddressMode {
+ AddressWrap = 1,
+ AddressClamp = 3
+ };
+
+ Filter filter = FilterLinear;
+ AddressMode addressModeHoriz = AddressClamp;
+ AddressMode addressModeVert = AddressClamp;
+
+ bool operator==(const QSGD3D12TextureView &other) const {
+ return filter == other.filter
+ && addressModeHoriz == other.addressModeHoriz
+ && addressModeVert == other.addressModeVert;
+ }
+};
+
+inline uint qHash(const QSGD3D12TextureView &key, uint seed = 0)
+{
+ Q_UNUSED(seed);
+ return key.filter + key.addressModeHoriz + key.addressModeVert;
+}
+
+struct QSGD3D12RootSignature
+{
+ QVector<QSGD3D12TextureView> textureViews;
+
+ bool operator==(const QSGD3D12RootSignature &other) const {
+ return textureViews == other.textureViews;
+ }
+};
+
+inline uint qHash(const QSGD3D12RootSignature &key, uint seed = 0)
+{
+ return qHash(key.textureViews, seed);
+}
+
+// Shader bytecode blobs and root signature-related data.
+struct QSGD3D12ShaderState
+{
+ const quint8 *vs = nullptr;
+ quint32 vsSize = 0;
+ const quint8 *ps = nullptr;
+ quint32 psSize = 0;
+
+ QSGD3D12RootSignature rootSig;
+
+ bool operator==(const QSGD3D12ShaderState &other) const {
+ return vs == other.vs && vsSize == other.vsSize
+ && ps == other.ps && psSize == other.psSize
+ && rootSig == other.rootSig;
+ }
+};
+
+inline uint qHash(const QSGD3D12ShaderState &key, uint seed = 0)
+{
+ return qHash(key.vs, seed) + key.vsSize + qHash(key.ps, seed) + key.psSize + qHash(key.rootSig, seed);
+}
+
+struct QSGD3D12PipelineState
+{
+ enum CullMode {
+ CullNone = 1,
+ CullFront,
+ CullBack
+ };
+
+ enum CompareFunc {
+ CompareNever = 1,
+ CompareLess,
+ CompareEqual,
+ CompareLessEqual,
+ CompareGreater,
+ CompareNotEqual,
+ CompareGreaterEqual,
+ CompareAlways
+ };
+
+ enum StencilOp {
+ StencilKeep = 1,
+ StencilZero,
+ StencilReplace,
+ StencilIncrSat,
+ StencilDecrSat,
+ StencilInvert,
+ StencilIncr,
+ StencilDescr
+ };
+
+ enum TopologyType {
+ TopologyTypePoint = 1,
+ TopologyTypeLine,
+ TopologyTypeTriangle
+ };
+
+ enum BlendType {
+ BlendNone,
+ BlendPremul, // == GL_ONE, GL_ONE_MINUS_SRC_ALPHA
+ BlendColor // == GL_CONSTANT_COLOR, GL_ONE_MINUS_SRC_COLOR
+ };
+
+ QSGD3D12ShaderState shaders;
+
+ QVector<QSGD3D12InputElement> inputElements;
+
+ CullMode cullMode = CullNone;
+ bool frontCCW = true;
+ bool colorWrite = true;
+ BlendType blend = BlendNone;
+ bool depthEnable = true;
+ CompareFunc depthFunc = CompareLess;
+ bool depthWrite = true;
+ bool stencilEnable = false;
+ CompareFunc stencilFunc = CompareEqual;
+ StencilOp stencilFailOp = StencilKeep;
+ StencilOp stencilDepthFailOp = StencilKeep;
+ StencilOp stencilPassOp = StencilKeep;
+ TopologyType topologyType = TopologyTypeTriangle;
+
+ bool operator==(const QSGD3D12PipelineState &other) const {
+ return shaders == other.shaders
+ && inputElements == other.inputElements
+ && cullMode == other.cullMode
+ && frontCCW == other.frontCCW
+ && colorWrite == other.colorWrite
+ && blend == other.blend
+ && depthEnable == other.depthEnable
+ && (!depthEnable || depthFunc == other.depthFunc)
+ && depthWrite == other.depthWrite
+ && stencilEnable == other.stencilEnable
+ && (!stencilEnable || stencilFunc == other.stencilFunc)
+ && (!stencilEnable || stencilFailOp == other.stencilFailOp)
+ && (!stencilEnable || stencilDepthFailOp == other.stencilDepthFailOp)
+ && (!stencilEnable || stencilPassOp == other.stencilPassOp)
+ && topologyType == other.topologyType;
+ }
+};
+
+inline uint qHash(const QSGD3D12PipelineState &key, uint seed = 0)
+{
+ return qHash(key.shaders, seed) + qHash(key.inputElements, seed)
+ + key.cullMode + key.frontCCW
+ + key.colorWrite + key.blend
+ + key.depthEnable + key.depthWrite
+ + key.stencilEnable
+ + key.topologyType;
+}
+
+class QSGD3D12Engine : public QSGRendererInterface
+{
+public:
+ QSGD3D12Engine();
+ ~QSGD3D12Engine();
+
+ bool attachToWindow(WId window, const QSize &size, float dpr, int surfaceFormatSamples);
+ void releaseResources();
+ bool hasResources() const;
+ void setWindowSize(const QSize &size, float dpr);
+ WId window() const;
+ QSize windowSize() const;
+ float windowDevicePixelRatio() const;
+ uint windowSamples() const;
+
+ void beginFrame();
+ void endFrame();
+ void beginLayer();
+ void endLayer();
+ void invalidateCachedFrameState();
+ void restoreFrameState(bool minimal = false);
+
+ uint genBuffer();
+ void releaseBuffer(uint id);
+ void resetBuffer(uint id, const quint8 *data, int size);
+ void markBufferDirty(uint id, int offset, int size);
+
+ enum ClearFlag {
+ ClearDepth = 0x1,
+ ClearStencil = 0x2
+ };
+ Q_DECLARE_FLAGS(ClearFlags, ClearFlag)
+
+ void queueViewport(const QRect &rect);
+ void queueScissor(const QRect &rect);
+ void queueSetRenderTarget(uint id = 0);
+ void queueClearRenderTarget(const QColor &color);
+ void queueClearDepthStencil(float depthValue, quint8 stencilValue, ClearFlags which);
+ void queueSetBlendFactor(const QVector4D &factor);
+ void queueSetStencilRef(quint32 ref);
+
+ void finalizePipeline(const QSGD3D12PipelineState &pipelineState);
+
+ struct DrawParams {
+ QSGGeometry::DrawingMode mode = QSGGeometry::DrawTriangles;
+ int count = 0;
+ uint vertexBuf = 0;
+ uint indexBuf = 0;
+ uint constantBuf = 0;
+ int vboOffset = 0;
+ int vboSize = 0;
+ int vboStride = 0;
+ int cboOffset = 0;
+ int startIndexIndex = -1;
+ QSGD3D12Format indexFormat = FmtUnsignedShort;
+ };
+
+ void queueDraw(const DrawParams &params);
+
+ void present();
+ void waitGPU();
+
+ static quint32 alignedConstantBufferSize(quint32 size);
+ static QSGD3D12Format toDXGIFormat(QSGGeometry::Type sgtype, int tupleSize = 1, int *size = nullptr);
+ static int mipMapLevels(const QSize &size);
+ static QSize mipMapAdjustedSourceSize(const QSize &size);
+
+ enum TextureCreateFlag {
+ TextureWithAlpha = 0x1,
+ TextureWithMipMaps = 0x2
+ };
+ Q_DECLARE_FLAGS(TextureCreateFlags, TextureCreateFlag)
+
+ uint genTexture();
+ void createTexture(uint id, const QSize &size, QImage::Format format, TextureCreateFlags flags);
+ void queueTextureResize(uint id, const QSize &size);
+ void queueTextureUpload(uint id, const QImage &image, const QPoint &dstPos = QPoint());
+ void queueTextureUpload(uint id, const QVector<QImage> &images, const QVector<QPoint> &dstPos);
+ void releaseTexture(uint id);
+ SIZE_T textureSRV(uint id) const;
+ void useTexture(uint id);
+
+ uint genRenderTarget();
+ void createRenderTarget(uint id, const QSize &size, const QVector4D &clearColor, uint samples);
+ void releaseRenderTarget(uint id);
+ void useRenderTargetAsTexture(uint id);
+ uint activeRenderTarget() const;
+
+ QImage executeAndWaitReadbackRenderTarget(uint id = 0);
+
+ void simulateDeviceLoss();
+
+ // QSGRendererInterface
+ GraphicsAPI graphicsAPI() const override;
+ void *getResource(Resource resource) const override;
+
+private:
+ QSGD3D12EnginePrivate *d;
+ Q_DISABLE_COPY(QSGD3D12Engine)
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QSGD3D12Engine::ClearFlags)
+Q_DECLARE_OPERATORS_FOR_FLAGS(QSGD3D12Engine::TextureCreateFlags)
+
+QT_END_NAMESPACE
+
+#endif // QSGD3D12ENGINE_P_H
diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12engine_p_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12engine_p_p.h
new file mode 100644
index 0000000000..d8a0166dc5
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12engine_p_p.h
@@ -0,0 +1,445 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGD3D12ENGINE_P_P_H
+#define QSGD3D12ENGINE_P_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 "qsgd3d12engine_p.h"
+#include <QCache>
+
+#include <d3d12.h>
+#include <dxgi1_4.h>
+#include <wrl/client.h>
+
+using namespace Microsoft::WRL;
+
+// No moc-related features (Q_OBJECT, signals, etc.) can be used here to due
+// moc-generated code failing to compile when combined with COM stuff.
+
+// Recommended reading before moving further: https://github.com/Microsoft/DirectXTK/wiki/ComPtr
+// Note esp. operator= vs. Attach and operator& vs. GetAddressOf
+
+// ID3D12* is never passed to Qt containers directly. Always use ComPtr and put it into a struct.
+
+QT_BEGIN_NAMESPACE
+
+class QSGD3D12CPUDescriptorHeapManager
+{
+public:
+ void initialize(ID3D12Device *device);
+
+ void releaseResources();
+
+ D3D12_CPU_DESCRIPTOR_HANDLE allocate(D3D12_DESCRIPTOR_HEAP_TYPE type);
+ void release(D3D12_CPU_DESCRIPTOR_HANDLE handle, D3D12_DESCRIPTOR_HEAP_TYPE type);
+ quint32 handleSize(D3D12_DESCRIPTOR_HEAP_TYPE type) const { return m_handleSizes[type]; }
+
+private:
+ ID3D12Device *m_device = nullptr;
+ struct Heap {
+ D3D12_DESCRIPTOR_HEAP_TYPE type;
+ ComPtr<ID3D12DescriptorHeap> heap;
+ D3D12_CPU_DESCRIPTOR_HANDLE start;
+ quint32 handleSize;
+ quint32 freeMap[8];
+ };
+ QVector<Heap> m_heaps;
+ quint32 m_handleSizes[D3D12_DESCRIPTOR_HEAP_TYPE_NUM_TYPES];
+};
+
+class QSGD3D12DeviceManager
+{
+public:
+ ID3D12Device *ref();
+ void unref();
+ void deviceLossDetected();
+ IDXGIFactory4 *dxgi();
+
+ struct DeviceLossObserver {
+ virtual void deviceLost() = 0;
+ };
+ void registerDeviceLossObserver(DeviceLossObserver *observer);
+
+private:
+ void ensureCreated();
+
+ ComPtr<ID3D12Device> m_device;
+ ComPtr<IDXGIFactory4> m_factory;
+ QAtomicInt m_ref;
+ QVector<DeviceLossObserver *> m_observers;
+};
+
+struct QSGD3D12CPUWaitableFence
+{
+ ~QSGD3D12CPUWaitableFence() {
+ if (event)
+ CloseHandle(event);
+ }
+ ComPtr<ID3D12Fence> fence;
+ HANDLE event = nullptr;
+ QAtomicInt value;
+};
+
+class QSGD3D12EnginePrivate : public QSGD3D12DeviceManager::DeviceLossObserver
+{
+public:
+ void initialize(WId w, const QSize &size, float dpr, int surfaceFormatSamples);
+ bool isInitialized() const { return initialized; }
+ void releaseResources();
+ void setWindowSize(const QSize &size, float dpr);
+ WId currentWindow() const { return window; }
+ QSize currentWindowSize() const { return windowSize; }
+ float currentWindowDpr() const { return windowDpr; }
+ uint currentWindowSamples() const { return windowSamples; }
+
+ void beginFrame();
+ void endFrame();
+ void beginLayer();
+ void endLayer();
+ void invalidateCachedFrameState();
+ void restoreFrameState(bool minimal = false);
+
+ uint genBuffer();
+ void releaseBuffer(uint id);
+ void resetBuffer(uint id, const quint8 *data, int size);
+ void markBufferDirty(uint id, int offset, int size);
+
+ void queueViewport(const QRect &rect);
+ void queueScissor(const QRect &rect);
+ void queueSetRenderTarget(uint id);
+ void queueClearRenderTarget(const QColor &color);
+ void queueClearDepthStencil(float depthValue, quint8 stencilValue, QSGD3D12Engine::ClearFlags which);
+ void queueSetBlendFactor(const QVector4D &factor);
+ void queueSetStencilRef(quint32 ref);
+
+ void finalizePipeline(const QSGD3D12PipelineState &pipelineState);
+
+ void queueDraw(const QSGD3D12Engine::DrawParams &params);
+
+ void present();
+ void waitGPU();
+
+ uint genTexture();
+ void createTexture(uint id, const QSize &size, QImage::Format format, QSGD3D12Engine::TextureCreateFlags flags);
+ void queueTextureResize(uint id, const QSize &size);
+ void queueTextureUpload(uint id, const QVector<QImage> &images, const QVector<QPoint> &dstPos);
+ void releaseTexture(uint id);
+ SIZE_T textureSRV(uint id) const;
+ void useTexture(uint id);
+
+ uint genRenderTarget();
+ void createRenderTarget(uint id, const QSize &size, const QVector4D &clearColor, uint samples);
+ void releaseRenderTarget(uint id);
+ void useRenderTargetAsTexture(uint id);
+ uint activeRenderTarget() const { return currentRenderTarget; }
+
+ QImage executeAndWaitReadbackRenderTarget(uint id);
+
+ void simulateDeviceLoss();
+
+ void *getResource(QSGRendererInterface::Resource resource) const;
+
+ // the device is intentionally hidden here. all resources have to go
+ // through the engine and, unlike with GL, cannot just be created in random
+ // places due to the need for proper tracking, managing and releasing.
+private:
+ void ensureDevice();
+ void setupDefaultRenderTargets();
+ void deviceLost() override;
+
+ bool createCbvSrvUavHeap(int pframeIndex, int descriptorCount);
+ void setDescriptorHeaps(bool force = false);
+ void ensureGPUDescriptorHeap(int cbvSrvUavDescriptorCount);
+
+ DXGI_SAMPLE_DESC makeSampleDesc(DXGI_FORMAT format, uint samples);
+ ID3D12Resource *createColorBuffer(D3D12_CPU_DESCRIPTOR_HANDLE viewHandle, const QSize &size,
+ const QVector4D &clearColor, uint samples);
+ ID3D12Resource *createDepthStencil(D3D12_CPU_DESCRIPTOR_HANDLE viewHandle, const QSize &size, uint samples);
+
+ QSGD3D12CPUWaitableFence *createCPUWaitableFence() const;
+ void waitForGPU(QSGD3D12CPUWaitableFence *f) const;
+
+ void transitionResource(ID3D12Resource *resource, ID3D12GraphicsCommandList *commandList,
+ D3D12_RESOURCE_STATES before, D3D12_RESOURCE_STATES after) const;
+ void resolveMultisampledTarget(ID3D12Resource *msaa, ID3D12Resource *resolve, D3D12_RESOURCE_STATES resolveUsage,
+ ID3D12GraphicsCommandList *commandList) const;
+ void uavBarrier(ID3D12Resource *resource, ID3D12GraphicsCommandList *commandList) const;
+
+ ID3D12Resource *createBuffer(int size);
+
+ typedef QVector<QPair<int, int> > DirtyList;
+ void addDirtyRange(DirtyList *dirty, int offset, int size, int bufferSize);
+
+ struct PersistentFrameData {
+ ComPtr<ID3D12DescriptorHeap> gpuCbvSrvUavHeap;
+ int gpuCbvSrvUavHeapSize;
+ int cbvSrvUavNextFreeDescriptorIndex;
+ QSet<uint> pendingTextureUploads;
+ QSet<uint> pendingTextureMipMap;
+ struct DeleteQueueEntry {
+ ComPtr<ID3D12Resource> res;
+ ComPtr<ID3D12DescriptorHeap> descHeap;
+ SIZE_T cpuDescriptorPtr = 0;
+ D3D12_DESCRIPTOR_HEAP_TYPE descHeapType = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
+ };
+ QVector<DeleteQueueEntry> deleteQueue;
+ QVector<DeleteQueueEntry> outOfFrameDeleteQueue;
+ QSet<uint> buffersUsedInDrawCallSet;
+ QSet<uint> buffersUsedInFrame;
+ struct PendingRelease {
+ enum Type {
+ TypeTexture,
+ TypeBuffer
+ };
+ Type type = TypeTexture;
+ uint id = 0;
+ PendingRelease(Type type, uint id) : type(type), id(id) { }
+ PendingRelease() { }
+ bool operator==(const PendingRelease &other) const { return type == other.type && id == other.id; }
+ };
+ QSet<PendingRelease> pendingReleases;
+ QSet<PendingRelease> outOfFramePendingReleases;
+ };
+ friend uint qHash(const PersistentFrameData::PendingRelease &pr, uint seed);
+
+ void deferredDelete(ComPtr<ID3D12Resource> res);
+ void deferredDelete(ComPtr<ID3D12DescriptorHeap> dh);
+ void deferredDelete(D3D12_CPU_DESCRIPTOR_HANDLE h, D3D12_DESCRIPTOR_HEAP_TYPE type);
+
+ struct Buffer;
+ void ensureBuffer(Buffer *buf);
+ void updateBuffer(Buffer *buf);
+
+ void beginDrawCalls();
+ void beginFrameDraw();
+ void endDrawCalls(bool lastInFrame = false);
+
+ static const int MAX_SWAP_CHAIN_BUFFER_COUNT = 4;
+ static const int MAX_FRAME_IN_FLIGHT_COUNT = 4;
+
+ bool initialized = false;
+ bool inFrame = false;
+ WId window = 0;
+ QSize windowSize;
+ float windowDpr;
+ uint windowSamples;
+ int swapChainBufferCount;
+ int frameInFlightCount;
+ int waitableSwapChainMaxLatency;
+ ID3D12Device *device;
+ ComPtr<ID3D12CommandQueue> commandQueue;
+ ComPtr<ID3D12CommandQueue> copyCommandQueue;
+ ComPtr<IDXGISwapChain3> swapChain;
+ HANDLE swapEvent;
+ ComPtr<ID3D12Resource> backBufferRT[MAX_SWAP_CHAIN_BUFFER_COUNT];
+ ComPtr<ID3D12Resource> defaultRT[MAX_SWAP_CHAIN_BUFFER_COUNT];
+ D3D12_CPU_DESCRIPTOR_HANDLE defaultRTV[MAX_SWAP_CHAIN_BUFFER_COUNT];
+ ComPtr<ID3D12Resource> defaultDS;
+ D3D12_CPU_DESCRIPTOR_HANDLE defaultDSV;
+ ComPtr<ID3D12CommandAllocator> frameCommandAllocator[MAX_FRAME_IN_FLIGHT_COUNT];
+ ComPtr<ID3D12CommandAllocator> copyCommandAllocator;
+ ComPtr<ID3D12GraphicsCommandList> frameCommandList;
+ ComPtr<ID3D12GraphicsCommandList> copyCommandList;
+ QSGD3D12CPUDescriptorHeapManager cpuDescHeapManager;
+ quint64 presentFrameIndex;
+ quint64 frameIndex;
+ QSGD3D12CPUWaitableFence *presentFence = nullptr;
+ QSGD3D12CPUWaitableFence *frameFence[MAX_FRAME_IN_FLIGHT_COUNT];
+
+ PersistentFrameData pframeData[MAX_FRAME_IN_FLIGHT_COUNT];
+ int currentPFrameIndex;
+ ID3D12GraphicsCommandList *commandList = nullptr;
+ int activeLayers = 0;
+ int currentLayerDepth = 0;
+
+ struct PSOCacheEntry {
+ ComPtr<ID3D12PipelineState> pso;
+ };
+ QCache<QSGD3D12PipelineState, PSOCacheEntry> psoCache;
+ struct RootSigCacheEntry {
+ ComPtr<ID3D12RootSignature> rootSig;
+ };
+ QCache<QSGD3D12RootSignature, RootSigCacheEntry> rootSigCache;
+
+ struct Texture {
+ enum Flag {
+ EntryInUse = 0x01,
+ Alpha = 0x02,
+ MipMap = 0x04
+ };
+ int flags = 0;
+ bool entryInUse() const { return flags & EntryInUse; }
+ bool alpha() const { return flags & Alpha; }
+ bool mipmap() const { return flags & MipMap; }
+ ComPtr<ID3D12Resource> texture;
+ D3D12_CPU_DESCRIPTOR_HANDLE srv;
+ quint64 fenceValue = 0;
+ quint64 lastWaitFenceValue = 0;
+ struct StagingHeap {
+ ComPtr<ID3D12Heap> heap;
+ };
+ QVector<StagingHeap> stagingHeaps;
+ struct StagingBuffer {
+ ComPtr<ID3D12Resource> buffer;
+ };
+ QVector<StagingBuffer> stagingBuffers;
+ QVector<D3D12_CPU_DESCRIPTOR_HANDLE> mipUAVs;
+ };
+
+ QVector<Texture> textures;
+ ComPtr<ID3D12Fence> textureUploadFence;
+ QAtomicInt nextTextureUploadFenceValue;
+
+ struct TransientFrameData {
+ QSGGeometry::DrawingMode drawingMode;
+ uint currentIndexBuffer;
+ struct ActiveTexture {
+ enum Type {
+ TypeTexture,
+ TypeRenderTarget
+ };
+ Type type = TypeTexture;
+ uint id = 0;
+ ActiveTexture(Type type, uint id) : type(type), id(id) { }
+ ActiveTexture() { }
+ };
+ QVector<ActiveTexture> activeTextures;
+ int drawCount;
+ ID3D12PipelineState *lastPso;
+ ID3D12RootSignature *lastRootSig;
+ bool descHeapSet;
+
+ QRect viewport;
+ QRect scissor;
+ QVector4D blendFactor = QVector4D(1, 1, 1, 1);
+ quint32 stencilRef = 1;
+ QSGD3D12PipelineState pipelineState;
+ };
+ TransientFrameData tframeData;
+
+ struct MipMapGen {
+ bool initialize(QSGD3D12EnginePrivate *enginePriv);
+ void releaseResources();
+ void queueGenerate(const Texture &t);
+
+ QSGD3D12EnginePrivate *engine;
+ ComPtr<ID3D12RootSignature> rootSig;
+ ComPtr<ID3D12PipelineState> pipelineState;
+ };
+
+ MipMapGen mipmapper;
+
+ struct RenderTarget {
+ enum Flag {
+ EntryInUse = 0x01,
+ NeedsReadBarrier = 0x02,
+ Multisample = 0x04
+ };
+ int flags = 0;
+ bool entryInUse() const { return flags & EntryInUse; }
+ ComPtr<ID3D12Resource> color;
+ ComPtr<ID3D12Resource> colorResolve;
+ D3D12_CPU_DESCRIPTOR_HANDLE rtv;
+ ComPtr<ID3D12Resource> ds;
+ D3D12_CPU_DESCRIPTOR_HANDLE dsv;
+ D3D12_CPU_DESCRIPTOR_HANDLE srv;
+ };
+
+ QVector<RenderTarget> renderTargets;
+ uint currentRenderTarget;
+
+ struct CPUBufferRef {
+ const quint8 *p = nullptr;
+ quint32 size = 0;
+ DirtyList dirty;
+ CPUBufferRef() { dirty.reserve(16); }
+ };
+
+ struct Buffer {
+ enum Flag {
+ EntryInUse = 0x01
+ };
+ int flags = 0;
+ bool entryInUse() const { return flags & EntryInUse; }
+ struct InFlightData {
+ ComPtr<ID3D12Resource> buffer;
+ DirtyList dirty;
+ quint32 dataSize = 0;
+ quint32 resourceSize = 0;
+ InFlightData() { dirty.reserve(16); }
+ };
+ InFlightData d[MAX_FRAME_IN_FLIGHT_COUNT];
+ CPUBufferRef cpuDataRef;
+ };
+
+ QVector<Buffer> buffers;
+
+ struct DeviceLossTester {
+ bool initialize(QSGD3D12EnginePrivate *enginePriv);
+ void releaseResources();
+ void killDevice();
+
+ QSGD3D12EnginePrivate *engine;
+ ComPtr<ID3D12PipelineState> computeState;
+ ComPtr<ID3D12RootSignature> computeRootSignature;
+ };
+
+ DeviceLossTester devLossTest;
+};
+
+inline uint qHash(const QSGD3D12EnginePrivate::PersistentFrameData::PendingRelease &pr, uint seed = 0)
+{
+ Q_UNUSED(seed);
+ return pr.id + pr.type;
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12glyphcache.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12glyphcache.cpp
new file mode 100644
index 0000000000..45ef202e83
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12glyphcache.cpp
@@ -0,0 +1,184 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgd3d12glyphcache_p.h"
+#include "qsgd3d12engine_p.h"
+
+QT_BEGIN_NAMESPACE
+
+// NOTE: Avoid categorized logging. It is slow.
+
+#define DECLARE_DEBUG_VAR(variable) \
+ static bool debug_ ## variable() \
+ { static bool value = qgetenv("QSG_RENDERER_DEBUG").contains(QT_STRINGIFY(variable)); return value; }
+
+DECLARE_DEBUG_VAR(render)
+
+QSGD3D12GlyphCache::QSGD3D12GlyphCache(QSGD3D12Engine *engine, QFontEngine::GlyphFormat format, const QTransform &matrix)
+ : QTextureGlyphCache(format, matrix),
+ m_engine(engine)
+{
+}
+
+QSGD3D12GlyphCache::~QSGD3D12GlyphCache()
+{
+ if (m_id)
+ m_engine->releaseTexture(m_id);
+}
+
+void QSGD3D12GlyphCache::createTextureData(int width, int height)
+{
+ width = qMax(128, width);
+ height = qMax(32, height);
+
+ m_id = m_engine->genTexture();
+ Q_ASSERT(m_id);
+
+ if (Q_UNLIKELY(debug_render()))
+ qDebug("new glyph cache texture %u of size %dx%d, fontengine format %d", m_id, width, height, m_format);
+
+ m_size = QSize(width, height);
+
+ const QImage::Format imageFormat =
+ m_format == QFontEngine::Format_A8 ? QImage::Format_Alpha8 : QImage::Format_ARGB32_Premultiplied;
+ m_engine->createTexture(m_id, m_size, imageFormat, QSGD3D12Engine::TextureWithAlpha);
+}
+
+void QSGD3D12GlyphCache::resizeTextureData(int width, int height)
+{
+ width = qMax(128, width);
+ height = qMax(32, height);
+
+ if (m_size.width() >= width && m_size.height() >= height)
+ return;
+
+ if (Q_UNLIKELY(debug_render()))
+ qDebug("glyph cache texture %u resize to %dx%d", m_id, width, height);
+
+ m_size = QSize(width, height);
+
+ m_engine->queueTextureResize(m_id, m_size);
+}
+
+void QSGD3D12GlyphCache::beginFillTexture()
+{
+ Q_ASSERT(m_glyphImages.isEmpty() && m_glyphPos.isEmpty());
+}
+
+void QSGD3D12GlyphCache::fillTexture(const Coord &c, glyph_t glyph, QFixed subPixelPosition)
+{
+ QImage mask = textureMapForGlyph(glyph, subPixelPosition);
+ const int maskWidth = mask.width();
+ const int maskHeight = mask.height();
+
+ if (mask.format() == QImage::Format_Mono) {
+ mask = mask.convertToFormat(QImage::Format_Indexed8);
+ for (int y = 0; y < maskHeight; ++y) {
+ uchar *src = mask.scanLine(y);
+ for (int x = 0; x < maskWidth; ++x)
+ src[x] = -src[x]; // convert 0 and 1 into 0 and 255
+ }
+ } else if (mask.depth() == 32) {
+ if (mask.format() == QImage::Format_RGB32) {
+ // We need to make the alpha component equal to the average of the RGB values.
+ // This is needed when drawing sub-pixel antialiased text on translucent targets.
+ for (int y = 0; y < maskHeight; ++y) {
+ QRgb *src = reinterpret_cast<QRgb *>(mask.scanLine(y));
+ for (int x = 0; x < maskWidth; ++x) {
+ const int r = qRed(src[x]);
+ const int g = qGreen(src[x]);
+ const int b = qBlue(src[x]);
+ int avg;
+ if (mask.format() == QImage::Format_RGB32)
+ avg = (r + g + b + 1) / 3; // "+1" for rounding.
+ else // Format_ARGB32_Premultiplied
+ avg = qAlpha(src[x]);
+ src[x] = qRgba(r, g, b, avg);
+ }
+ }
+ }
+ }
+
+ m_glyphImages.append(mask);
+ m_glyphPos.append(QPoint(c.x, c.y));
+}
+
+void QSGD3D12GlyphCache::endFillTexture()
+{
+ if (m_glyphImages.isEmpty())
+ return;
+
+ Q_ASSERT(m_id);
+
+ m_engine->queueTextureUpload(m_id, m_glyphImages, m_glyphPos);
+
+ // Nothing else left to do, it is up to the text material to call
+ // useTexture() which will then add the texture dependency to the frame.
+
+ m_glyphImages.clear();
+ m_glyphPos.clear();
+}
+
+int QSGD3D12GlyphCache::glyphPadding() const
+{
+ return 1;
+}
+
+int QSGD3D12GlyphCache::maxTextureWidth() const
+{
+ return 16384;
+}
+
+int QSGD3D12GlyphCache::maxTextureHeight() const
+{
+ return 16384;
+}
+
+void QSGD3D12GlyphCache::useTexture()
+{
+ if (m_id)
+ m_engine->useTexture(m_id);
+}
+
+QSize QSGD3D12GlyphCache::currentSize() const
+{
+ return m_size;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12glyphcache_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12glyphcache_p.h
new file mode 100644
index 0000000000..88d3d36f33
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12glyphcache_p.h
@@ -0,0 +1,88 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGD3D12GLYPHCACHE_P_H
+#define QSGD3D12GLYPHCACHE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtGui/private/qtextureglyphcache_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QSGD3D12Engine;
+
+class QSGD3D12GlyphCache : public QTextureGlyphCache
+{
+public:
+ QSGD3D12GlyphCache(QSGD3D12Engine *engine, QFontEngine::GlyphFormat format, const QTransform &matrix);
+ ~QSGD3D12GlyphCache();
+
+ void createTextureData(int width, int height) override;
+ void resizeTextureData(int width, int height) override;
+ void beginFillTexture() override;
+ void fillTexture(const Coord &c, glyph_t glyph, QFixed subPixelPosition) override;
+ void endFillTexture() override;
+ int glyphPadding() const override;
+ int maxTextureWidth() const override;
+ int maxTextureHeight() const override;
+
+ void useTexture();
+ QSize currentSize() const;
+
+private:
+ QSGD3D12Engine *m_engine;
+ uint m_id = 0;
+ QVector<QImage> m_glyphImages;
+ QVector<QPoint> m_glyphPos;
+ QSize m_size;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGD3D12GLYPHCACHE_P_H
diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12glyphnode.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12glyphnode.cpp
new file mode 100644
index 0000000000..e559739018
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12glyphnode.cpp
@@ -0,0 +1,90 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgd3d12glyphnode_p.h"
+#include "qsgd3d12builtinmaterials_p.h"
+
+QT_BEGIN_NAMESPACE
+
+void QSGD3D12GlyphNode::setMaterialColor(const QColor &color)
+{
+ static_cast<QSGD3D12TextMaterial *>(m_material)->setColor(color);
+}
+
+void QSGD3D12GlyphNode::update()
+{
+ QRawFont font = m_glyphs.rawFont();
+ QMargins margins(0, 0, 0, 0);
+
+ if (m_style == QQuickText::Normal) {
+ // QSGBasicGlyphNode dtor will delete
+ m_material = new QSGD3D12TextMaterial(QSGD3D12TextMaterial::Normal, m_rc, font);
+ } else if (m_style == QQuickText::Outline) {
+ QSGD3D12TextMaterial *material = new QSGD3D12TextMaterial(QSGD3D12TextMaterial::Outlined,
+ m_rc, font, QFontEngine::Format_A8);
+ material->setStyleColor(m_styleColor);
+ m_material = material;
+ margins = QMargins(1, 1, 1, 1);
+ } else {
+ QSGD3D12TextMaterial *material = new QSGD3D12TextMaterial(QSGD3D12TextMaterial::Styled,
+ m_rc, font, QFontEngine::Format_A8);
+ if (m_style == QQuickText::Sunken) {
+ material->setStyleShift(QVector2D(0, -1));
+ margins.setTop(1);
+ } else if (m_style == QQuickText::Raised) {
+ material->setStyleShift(QVector2D(0, 1));
+ margins.setBottom(1);
+ }
+ material->setStyleColor(m_styleColor);
+ m_material = material;
+ }
+
+ QSGD3D12TextMaterial *textMaterial = static_cast<QSGD3D12TextMaterial *>(m_material);
+ textMaterial->setColor(m_color);
+
+ QRectF boundingRect;
+ textMaterial->populate(m_position, m_glyphs.glyphIndexes(), m_glyphs.positions(), geometry(),
+ &boundingRect, &m_baseLine, margins);
+ setBoundingRect(boundingRect);
+
+ setMaterial(m_material);
+ markDirty(DirtyGeometry);
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12glyphnode_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12glyphnode_p.h
new file mode 100644
index 0000000000..d04a8e8777
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12glyphnode_p.h
@@ -0,0 +1,74 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGD3D12GLYPHNODE_P_H
+#define QSGD3D12GLYPHNODE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qsgbasicglyphnode_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QSGD3D12RenderContext;
+
+class QSGD3D12GlyphNode : public QSGBasicGlyphNode
+{
+public:
+ QSGD3D12GlyphNode(QSGD3D12RenderContext *rc) : m_rc(rc) { }
+
+ void setMaterialColor(const QColor &color) override;
+ void update() override;
+
+private:
+ QSGD3D12RenderContext *m_rc;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGD3D12GLYPHNODE_P_H
diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12imagenode.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12imagenode.cpp
new file mode 100644
index 0000000000..9bb360bc5e
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12imagenode.cpp
@@ -0,0 +1,123 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgd3d12imagenode_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QSGD3D12ImageNode::QSGD3D12ImageNode()
+{
+ setMaterial(&m_material);
+}
+
+void QSGD3D12ImageNode::setFiltering(QSGTexture::Filtering filtering)
+{
+ if (m_material.filtering() == filtering)
+ return;
+
+ m_material.setFiltering(filtering);
+ m_smoothMaterial.setFiltering(filtering);
+ markDirty(DirtyMaterial);
+}
+
+void QSGD3D12ImageNode::setMipmapFiltering(QSGTexture::Filtering filtering)
+{
+ if (m_material.mipmapFiltering() == filtering)
+ return;
+
+ m_material.setMipmapFiltering(filtering);
+ m_smoothMaterial.setMipmapFiltering(filtering);
+ markDirty(DirtyMaterial);
+}
+
+void QSGD3D12ImageNode::setVerticalWrapMode(QSGTexture::WrapMode wrapMode)
+{
+ if (m_material.verticalWrapMode() == wrapMode)
+ return;
+
+ m_material.setVerticalWrapMode(wrapMode);
+ m_smoothMaterial.setVerticalWrapMode(wrapMode);
+ markDirty(DirtyMaterial);
+}
+
+void QSGD3D12ImageNode::setHorizontalWrapMode(QSGTexture::WrapMode wrapMode)
+{
+ if (m_material.horizontalWrapMode() == wrapMode)
+ return;
+
+ m_material.setHorizontalWrapMode(wrapMode);
+ m_smoothMaterial.setHorizontalWrapMode(wrapMode);
+ markDirty(DirtyMaterial);
+}
+
+void QSGD3D12ImageNode::updateMaterialAntialiasing()
+{
+ if (m_antialiasing)
+ setMaterial(&m_smoothMaterial);
+ else
+ setMaterial(&m_material);
+}
+
+void QSGD3D12ImageNode::setMaterialTexture(QSGTexture *texture)
+{
+ m_material.setTexture(texture);
+ m_smoothMaterial.setTexture(texture);
+}
+
+QSGTexture *QSGD3D12ImageNode::materialTexture() const
+{
+ return m_material.texture();
+}
+
+bool QSGD3D12ImageNode::updateMaterialBlending()
+{
+ const bool alpha = m_material.flags() & QSGMaterial::Blending;
+ if (materialTexture() && alpha != materialTexture()->hasAlphaChannel()) {
+ m_material.setFlag(QSGMaterial::Blending, !alpha);
+ return true;
+ }
+ return false;
+}
+
+bool QSGD3D12ImageNode::supportsWrap(const QSize &) const
+{
+ return true;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12imagenode_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12imagenode_p.h
new file mode 100644
index 0000000000..ef4b38884a
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12imagenode_p.h
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGD3D12IMAGENODE_P_H
+#define QSGD3D12IMAGENODE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qsgbasicimagenode_p.h>
+#include "qsgd3d12builtinmaterials_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QSGD3D12ImageNode : public QSGBasicImageNode
+{
+public:
+ QSGD3D12ImageNode();
+
+ void setMipmapFiltering(QSGTexture::Filtering filtering) override;
+ void setFiltering(QSGTexture::Filtering filtering) override;
+ void setHorizontalWrapMode(QSGTexture::WrapMode wrapMode) override;
+ void setVerticalWrapMode(QSGTexture::WrapMode wrapMode) override;
+
+ void updateMaterialAntialiasing() override;
+ void setMaterialTexture(QSGTexture *texture) override;
+ QSGTexture *materialTexture() const override;
+ bool updateMaterialBlending() override;
+ bool supportsWrap(const QSize &size) const override;
+
+private:
+ QSGD3D12TextureMaterial m_material;
+ QSGD3D12SmoothTextureMaterial m_smoothMaterial;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGD3D12IMAGENODE_P_H
diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12layer.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12layer.cpp
new file mode 100644
index 0000000000..faa6f7566a
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12layer.cpp
@@ -0,0 +1,363 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgd3d12layer_p.h"
+#include "qsgd3d12rendercontext_p.h"
+#include "qsgd3d12engine_p.h"
+#include "qsgd3d12renderer_p.h"
+
+QT_BEGIN_NAMESPACE
+
+// NOTE: Avoid categorized logging. It is slow.
+
+#define DECLARE_DEBUG_VAR(variable) \
+ static bool debug_ ## variable() \
+ { static bool value = qgetenv("QSG_RENDERER_DEBUG").contains(QT_STRINGIFY(variable)); return value; }
+
+DECLARE_DEBUG_VAR(render)
+
+QSGD3D12Layer::QSGD3D12Layer(QSGD3D12RenderContext *rc)
+ : m_rc(rc)
+{
+ if (Q_UNLIKELY(debug_render()))
+ qDebug("new layer %p", this);
+}
+
+QSGD3D12Layer::~QSGD3D12Layer()
+{
+ if (Q_UNLIKELY(debug_render()))
+ qDebug("destroying layer %p", this);
+
+ cleanup();
+}
+
+// QSGTexture
+
+int QSGD3D12Layer::textureId() const
+{
+ return m_rt; // not a texture id per se but will do
+}
+
+QSize QSGD3D12Layer::textureSize() const
+{
+ return m_size;
+}
+
+bool QSGD3D12Layer::hasAlphaChannel() const
+{
+ return true;
+}
+
+bool QSGD3D12Layer::hasMipmaps() const
+{
+ // mipmapped layers are not supported for now
+ return false;
+}
+
+QRectF QSGD3D12Layer::normalizedTextureSubRect() const
+{
+ return QRectF(m_mirrorHorizontal ? 1 : 0,
+ m_mirrorVertical ? 0 : 1,
+ m_mirrorHorizontal ? -1 : 1,
+ m_mirrorVertical ? 1 : -1);
+}
+
+void QSGD3D12Layer::bind()
+{
+ if (Q_UNLIKELY(debug_render()))
+ qDebug("layer %p bind rt=%u", this, m_rt);
+
+ QSGD3D12Engine *engine = m_rc->engine();
+ Q_ASSERT(m_rt);
+
+#ifndef QT_NO_DEBUG
+ // Should not use the color buffer as a texture while it is the current render target.
+ if (!m_recursive && engine->activeRenderTarget() == m_rt && engine->windowSamples() == 1)
+ qWarning("ShaderEffectSource: \'recursive\' must be set to true when rendering recursively.");
+#endif
+
+ engine->useRenderTargetAsTexture(m_rt);
+}
+
+// QSGDynamicTexture
+
+bool QSGD3D12Layer::updateTexture()
+{
+ if (Q_UNLIKELY(debug_render()))
+ qDebug("layer %p updateTexture", this);
+
+ const bool doUpdate = (m_live || m_updateContentPending) && m_dirtyTexture;
+
+ if (doUpdate)
+ updateContent();
+
+ if (m_updateContentPending) {
+ m_updateContentPending = false;
+ emit scheduledUpdateCompleted();
+ }
+
+ return doUpdate;
+}
+
+// QSGLayer
+
+void QSGD3D12Layer::setItem(QSGNode *item)
+{
+ if (m_item == item)
+ return;
+
+ if (m_live && !item)
+ resetRenderTarget();
+
+ m_item = item;
+ markDirtyTexture();
+}
+
+void QSGD3D12Layer::setRect(const QRectF &rect)
+{
+ if (m_rect == rect)
+ return;
+
+ m_rect = rect;
+ markDirtyTexture();
+}
+
+void QSGD3D12Layer::setSize(const QSize &size)
+{
+ if (m_size == size)
+ return;
+
+ if (m_live && size.isNull())
+ resetRenderTarget();
+
+ m_size = size;
+ markDirtyTexture();
+}
+
+void QSGD3D12Layer::scheduleUpdate()
+{
+ if (m_updateContentPending)
+ return;
+
+ if (Q_UNLIKELY(debug_render()))
+ qDebug("layer %p scheduleUpdate", this);
+
+ m_updateContentPending = true;
+
+ if (m_dirtyTexture)
+ emit updateRequested();
+}
+
+QImage QSGD3D12Layer::toImage() const
+{
+ return m_rc->engine()->executeAndWaitReadbackRenderTarget(m_rt);
+}
+
+void QSGD3D12Layer::setLive(bool live)
+{
+ if (m_live == live)
+ return;
+
+ if (live && (!m_item || m_size.isNull()))
+ resetRenderTarget();
+
+ m_live = live;
+ markDirtyTexture();
+}
+
+void QSGD3D12Layer::setRecursive(bool recursive)
+{
+ m_recursive = recursive;
+}
+
+void QSGD3D12Layer::setFormat(uint format)
+{
+ Q_UNUSED(format);
+}
+
+void QSGD3D12Layer::setHasMipmaps(bool mipmap)
+{
+ // mipmapped layers are not supported for now
+ Q_UNUSED(mipmap);
+}
+
+void QSGD3D12Layer::setDevicePixelRatio(qreal ratio)
+{
+ m_dpr = ratio;
+}
+
+void QSGD3D12Layer::setMirrorHorizontal(bool mirror)
+{
+ m_mirrorHorizontal = mirror;
+}
+
+void QSGD3D12Layer::setMirrorVertical(bool mirror)
+{
+ m_mirrorVertical = mirror;
+}
+
+void QSGD3D12Layer::markDirtyTexture()
+{
+ if (Q_UNLIKELY(debug_render()))
+ qDebug("layer %p markDirtyTexture", this);
+
+ m_dirtyTexture = true;
+
+ if (m_live || m_updateContentPending)
+ emit updateRequested();
+}
+
+void QSGD3D12Layer::invalidated()
+{
+ cleanup();
+}
+
+void QSGD3D12Layer::cleanup()
+{
+ if (!m_renderer && !m_rt)
+ return;
+
+ if (Q_UNLIKELY(debug_render()))
+ qDebug("layer %p cleanup renderer=%p rt=%u", this, m_renderer, m_rt);
+
+ delete m_renderer;
+ m_renderer = nullptr;
+
+ resetRenderTarget();
+}
+
+void QSGD3D12Layer::resetRenderTarget()
+{
+ if (!m_rt)
+ return;
+
+ if (Q_UNLIKELY(debug_render()))
+ qDebug("layer %p resetRenderTarget rt=%u", this, m_rt);
+
+ m_rc->engine()->releaseRenderTarget(m_rt);
+ m_rt = 0;
+
+ if (m_secondaryRT) {
+ m_rc->engine()->releaseRenderTarget(m_secondaryRT);
+ m_secondaryRT = 0;
+ }
+}
+
+void QSGD3D12Layer::updateContent()
+{
+ if (Q_UNLIKELY(debug_render()))
+ qDebug("layer %p updateContent", this);
+
+ if (!m_item || m_size.isNull()) {
+ resetRenderTarget();
+ m_dirtyTexture = false;
+ return;
+ }
+
+ QSGNode *root = m_item;
+ while (root->firstChild() && root->type() != QSGNode::RootNodeType)
+ root = root->firstChild();
+
+ if (root->type() != QSGNode::RootNodeType)
+ return;
+
+ if (!m_renderer) {
+ m_renderer = m_rc->createRenderer();
+ static_cast<QSGD3D12Renderer *>(m_renderer)->turnToLayerRenderer();
+ connect(m_renderer, &QSGRenderer::sceneGraphChanged, this, &QSGD3D12Layer::markDirtyTexture);
+ }
+
+ m_renderer->setDevicePixelRatio(m_dpr);
+ m_renderer->setRootNode(static_cast<QSGRootNode *>(root));
+
+ QSGD3D12Engine *engine = m_rc->engine();
+ const uint sampleCount = engine->windowSamples();
+ const QVector4D clearColor;
+
+ if (!m_rt || m_rtSize != m_size) {
+ if (m_rt)
+ resetRenderTarget();
+
+ m_rt = engine->genRenderTarget();
+ m_rtSize = m_size;
+
+ if (Q_UNLIKELY(debug_render()))
+ qDebug("new render target for layer %p, size=%dx%d, samples=%d",
+ this, m_size.width(), m_size.height(), sampleCount);
+
+ engine->createRenderTarget(m_rt, m_rtSize, clearColor, sampleCount);
+
+ // For multisampling the resolving via an extra non-ms color buffer is
+ // handled internally in the engine, no need to worry about it here.
+ }
+
+ if (m_recursive && !m_secondaryRT && sampleCount == 1) {
+ m_secondaryRT = engine->genRenderTarget();
+ engine->createRenderTarget(m_secondaryRT, m_rtSize, clearColor, sampleCount);
+ }
+
+ m_dirtyTexture = false;
+
+ m_renderer->setDeviceRect(m_size);
+ m_renderer->setViewportRect(m_size);
+
+ // Note that the handling of vertical mirroring differs from OpenGL here
+ // due to y running top-bottom with D3D as opposed to bottom-top with GL.
+ // The common parts of Quick follow OpenGL so vertical mirroring is
+ // typically enabled.
+ QRectF mirrored(m_mirrorHorizontal ? m_rect.right() : m_rect.left(),
+ m_mirrorVertical ? m_rect.top() : m_rect.bottom(),
+ m_mirrorHorizontal ? -m_rect.width() : m_rect.width(),
+ m_mirrorVertical ? m_rect.height() : -m_rect.height());
+
+ m_renderer->setProjectionMatrixToRect(mirrored);
+ m_renderer->setClearColor(Qt::transparent);
+
+ if (!m_recursive || sampleCount > 1) {
+ m_renderer->renderScene(m_rt);
+ } else {
+ m_renderer->renderScene(m_secondaryRT);
+ qSwap(m_rt, m_secondaryRT);
+ }
+
+ if (m_recursive)
+ markDirtyTexture(); // Continuously update if 'live' and 'recursive'.
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12layer_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12layer_p.h
new file mode 100644
index 0000000000..f1ab580a84
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12layer_p.h
@@ -0,0 +1,118 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGD3D12LAYER_P_H
+#define QSGD3D12LAYER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qsgadaptationlayer_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QSGD3D12RenderContext;
+
+class QSGD3D12Layer : public QSGLayer
+{
+ Q_OBJECT
+
+public:
+ QSGD3D12Layer(QSGD3D12RenderContext *rc);
+ ~QSGD3D12Layer();
+
+ int textureId() const override;
+ QSize textureSize() const override;
+ bool hasAlphaChannel() const override;
+ bool hasMipmaps() const override;
+ QRectF normalizedTextureSubRect() const override;
+ void bind() override;
+
+ bool updateTexture() override;
+
+ void setItem(QSGNode *item) override;
+ void setRect(const QRectF &rect) override;
+ void setSize(const QSize &size) override;
+ void scheduleUpdate() override;
+ QImage toImage() const override;
+ void setLive(bool live) override;
+ void setRecursive(bool recursive) override;
+ void setFormat(uint format) override;
+ void setHasMipmaps(bool mipmap) override;
+ void setDevicePixelRatio(qreal ratio) override;
+ void setMirrorHorizontal(bool mirror) override;
+ void setMirrorVertical(bool mirror) override;
+
+public Q_SLOTS:
+ void markDirtyTexture() override;
+ void invalidated() override;
+
+private:
+ void cleanup();
+ void resetRenderTarget();
+ void updateContent();
+
+ QSGD3D12RenderContext *m_rc;
+ uint m_rt = 0;
+ uint m_secondaryRT = 0;
+ QSize m_rtSize;
+ QSize m_size;
+ QRectF m_rect;
+ QSGNode *m_item = nullptr;
+ QSGRenderer *m_renderer = nullptr;
+ float m_dpr = 1;
+ bool m_mirrorHorizontal = false;
+ bool m_mirrorVertical = true;
+ bool m_live = true;
+ bool m_recursive = false;
+ bool m_dirtyTexture = true;
+ bool m_updateContentPending = false;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGD3D12LAYER_P_H
diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12material.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12material.cpp
new file mode 100644
index 0000000000..3412a4b63b
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12material.cpp
@@ -0,0 +1,106 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgd3d12material_p.h"
+#include <private/qsgrenderer_p.h>
+
+QT_BEGIN_NAMESPACE
+
+QSGD3D12Material::RenderState QSGD3D12Material::makeRenderState(QSGRenderer *renderer, RenderState::DirtyStates dirty)
+{
+ RenderState rs;
+ rs.m_dirty = dirty;
+ rs.m_data = renderer;
+ return rs;
+}
+
+float QSGD3D12Material::RenderState::opacity() const
+{
+ Q_ASSERT(m_data);
+ return static_cast<const QSGRenderer *>(m_data)->currentOpacity();
+}
+
+float QSGD3D12Material::RenderState::determinant() const
+{
+ Q_ASSERT(m_data);
+ return static_cast<const QSGRenderer *>(m_data)->determinant();
+}
+
+QMatrix4x4 QSGD3D12Material::RenderState::combinedMatrix() const
+{
+ Q_ASSERT(m_data);
+ return static_cast<const QSGRenderer *>(m_data)->currentCombinedMatrix();
+}
+
+float QSGD3D12Material::RenderState::devicePixelRatio() const
+{
+ Q_ASSERT(m_data);
+ return static_cast<const QSGRenderer *>(m_data)->devicePixelRatio();
+}
+
+QMatrix4x4 QSGD3D12Material::RenderState::modelViewMatrix() const
+{
+ Q_ASSERT(m_data);
+ return static_cast<const QSGRenderer *>(m_data)->currentModelViewMatrix();
+}
+
+QMatrix4x4 QSGD3D12Material::RenderState::projectionMatrix() const
+{
+ Q_ASSERT(m_data);
+ return static_cast<const QSGRenderer *>(m_data)->currentProjectionMatrix();
+}
+
+QRect QSGD3D12Material::RenderState::viewportRect() const
+{
+ Q_ASSERT(m_data);
+ return static_cast<const QSGRenderer *>(m_data)->viewportRect();
+}
+
+QRect QSGD3D12Material::RenderState::deviceRect() const
+{
+ Q_ASSERT(m_data);
+ return static_cast<const QSGRenderer *>(m_data)->deviceRect();
+}
+
+QSGMaterialShader *QSGD3D12Material::createShader() const
+{
+ return nullptr;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12material_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12material_p.h
new file mode 100644
index 0000000000..e9af8e68a0
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12material_p.h
@@ -0,0 +1,125 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGD3D12MATERIAL_P_H
+#define QSGD3D12MATERIAL_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtQuick/qsgmaterial.h>
+#include "qsgd3d12engine_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QSGRenderer;
+
+// The D3D renderer works with QSGD3D12Material as the "base" class since
+// QSGMaterial and its GL program related bits are not suitable. Also, there is
+// no split like with QSGMaterialShader.
+
+class QSGD3D12Material : public QSGMaterial
+{
+public:
+ struct RenderState {
+ enum DirtyState {
+ DirtyMatrix = 0x0001,
+ DirtyOpacity = 0x0002,
+ DirtyCachedMaterialData = 0x0004,
+ DirtyAll = 0xFFFF
+ };
+ Q_DECLARE_FLAGS(DirtyStates, DirtyState)
+
+ DirtyStates dirtyStates() const { return m_dirty; }
+
+ bool isMatrixDirty() const { return m_dirty & DirtyMatrix; }
+ bool isOpacityDirty() const { return m_dirty & DirtyOpacity; }
+ bool isCachedMaterialDataDirty() const { return m_dirty & DirtyCachedMaterialData; }
+
+ float opacity() const;
+ QMatrix4x4 combinedMatrix() const;
+ QMatrix4x4 modelViewMatrix() const;
+ QMatrix4x4 projectionMatrix() const;
+ QRect viewportRect() const;
+ QRect deviceRect() const;
+ float determinant() const;
+ float devicePixelRatio() const;
+
+ DirtyStates m_dirty = 0;
+ void *m_data = nullptr;
+ };
+
+ struct ExtraState {
+ QVector4D blendFactor;
+ };
+
+ enum UpdateResult {
+ UpdatedShaders = 0x0001,
+ UpdatedConstantBuffer = 0x0002,
+ UpdatedBlendFactor = 0x0004
+ };
+ Q_DECLARE_FLAGS(UpdateResults, UpdateResult)
+
+ static RenderState makeRenderState(QSGRenderer *renderer, RenderState::DirtyStates dirty);
+
+ virtual int constantBufferSize() const = 0;
+ virtual void preparePipeline(QSGD3D12PipelineState *pipelineState) = 0;
+ virtual UpdateResults updatePipeline(const RenderState &state,
+ QSGD3D12PipelineState *pipelineState,
+ ExtraState *extraState,
+ quint8 *constantBuffer) = 0;
+
+private:
+ QSGMaterialShader *createShader() const override; // dummy, QSGMaterialShader is too GL dependent
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QSGD3D12Material::RenderState::DirtyStates)
+Q_DECLARE_OPERATORS_FOR_FLAGS(QSGD3D12Material::UpdateResults)
+
+QT_END_NAMESPACE
+
+#endif // QSGD3D12MATERIAL_P_H
diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12painternode.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12painternode.cpp
new file mode 100644
index 0000000000..7837c3a2d3
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12painternode.cpp
@@ -0,0 +1,256 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgd3d12painternode_p.h"
+#include "qsgd3d12rendercontext_p.h"
+#include "qsgd3d12engine_p.h"
+#include <private/qquickitem_p.h>
+#include <qmath.h>
+
+QT_BEGIN_NAMESPACE
+
+QSGD3D12PainterTexture::QSGD3D12PainterTexture(QSGD3D12Engine *engine)
+ : QSGD3D12Texture(engine)
+{
+}
+
+void QSGD3D12PainterTexture::bind()
+{
+ if (m_image.isNull()) {
+ if (!m_id) {
+ m_id = m_engine->genTexture();
+ m_engine->createTexture(m_id, QSize(16, 16), QImage::Format_RGB32, 0);
+ }
+ } else if (m_image.size() != lastSize) {
+ lastSize = m_image.size();
+ if (m_id)
+ m_engine->releaseTexture(m_id);
+ m_id = m_engine->genTexture();
+ m_engine->createTexture(m_id, m_image.size(), m_image.format(), QSGD3D12Engine::TextureWithAlpha);
+ m_engine->queueTextureUpload(m_id, m_image);
+ } else if (!dirty.isEmpty()) {
+ const int bpl = m_image.bytesPerLine();
+ const uchar *p = m_image.constBits() + dirty.y() * bpl + dirty.x() * 4;
+ QImage subImg(p, dirty.width(), dirty.height(), bpl, QImage::Format_ARGB32_Premultiplied);
+ m_engine->queueTextureUpload(m_id, subImg, dirty.topLeft());
+ }
+
+ dirty = QRect();
+
+ m_engine->useTexture(m_id);
+}
+
+QSGD3D12PainterNode::QSGD3D12PainterNode(QQuickPaintedItem *item)
+ : m_item(item),
+ m_engine(static_cast<QSGD3D12RenderContext *>(QQuickItemPrivate::get(item)->sceneGraphRenderContext())->engine()),
+ m_texture(new QSGD3D12PainterTexture(m_engine)),
+ m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4),
+ m_dirtyGeometry(false),
+ m_dirtyContents(false)
+{
+ m_material.setFlag(QSGMaterial::Blending);
+ m_material.setTexture(m_texture);
+ setMaterial(&m_material);
+ setGeometry(&m_geometry);
+}
+
+QSGD3D12PainterNode::~QSGD3D12PainterNode()
+{
+ delete m_texture;
+}
+
+void QSGD3D12PainterNode::setPreferredRenderTarget(QQuickPaintedItem::RenderTarget)
+{
+ // always QImage-based
+}
+
+void QSGD3D12PainterNode::setSize(const QSize &size)
+{
+ if (m_size == size)
+ return;
+
+ m_size = size;
+ m_dirtyGeometry = true;
+}
+
+void QSGD3D12PainterNode::setDirty(const QRect &dirtyRect)
+{
+ m_dirtyRect = dirtyRect;
+ m_dirtyContents = true;
+ markDirty(DirtyMaterial);
+}
+
+void QSGD3D12PainterNode::setOpaquePainting(bool)
+{
+ // ignored
+}
+
+void QSGD3D12PainterNode::setLinearFiltering(bool linearFiltering)
+{
+ m_material.setFiltering(linearFiltering ? QSGTexture::Linear : QSGTexture::Nearest);
+ markDirty(DirtyMaterial);
+}
+
+void QSGD3D12PainterNode::setMipmapping(bool)
+{
+ // ### not yet
+}
+
+void QSGD3D12PainterNode::setSmoothPainting(bool s)
+{
+ if (m_smoothPainting == s)
+ return;
+
+ m_smoothPainting = s;
+ m_dirtyContents = true;
+ markDirty(DirtyMaterial);
+}
+
+void QSGD3D12PainterNode::setFillColor(const QColor &c)
+{
+ if (m_fillColor == c)
+ return;
+
+ m_fillColor = c;
+ m_dirtyContents = true;
+ markDirty(DirtyMaterial);
+}
+
+void QSGD3D12PainterNode::setContentsScale(qreal s)
+{
+ if (m_contentsScale == s)
+ return;
+
+ m_contentsScale = s;
+ m_dirtyContents = true;
+ markDirty(DirtyMaterial);
+}
+
+void QSGD3D12PainterNode::setFastFBOResizing(bool)
+{
+ // nope
+}
+
+void QSGD3D12PainterNode::setTextureSize(const QSize &size)
+{
+ if (m_textureSize == size)
+ return;
+
+ m_textureSize = size;
+ m_dirtyGeometry = true;
+}
+
+QImage QSGD3D12PainterNode::toImage() const
+{
+ return *m_texture->image();
+}
+
+void QSGD3D12PainterNode::update()
+{
+ if (m_dirtyGeometry) {
+ m_dirtyGeometry = false;
+ QRectF src(0, 0, 1, 1);
+ QRectF dst(QPointF(0, 0), m_size);
+ QSGGeometry::updateTexturedRectGeometry(&m_geometry, dst, src);
+ markDirty(DirtyGeometry);
+ }
+
+ QImage *img = m_texture->image();
+ if (img->size() != m_textureSize) {
+ *img = QImage(m_textureSize, QImage::Format_ARGB32_Premultiplied);
+ img->fill(Qt::transparent);
+ m_dirtyContents = true;
+ }
+
+ if (m_dirtyContents) {
+ m_dirtyContents = false;
+ if (!img->isNull()) {
+ QRect dirtyRect = m_dirtyRect.isNull() ? QRect(QPoint(0, 0), m_size) : m_dirtyRect;
+ QPainter painter;
+ painter.begin(img);
+ if (m_smoothPainting)
+ painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform);
+
+ QRect clipRect;
+ QRect dirtyTextureRect;
+
+ if (m_contentsScale == 1) {
+ float scaleX = m_textureSize.width() / (float) m_size.width();
+ float scaleY = m_textureSize.height() / (float) m_size.height();
+ painter.scale(scaleX, scaleY);
+ clipRect = dirtyRect;
+ dirtyTextureRect = QRectF(dirtyRect.x() * scaleX,
+ dirtyRect.y() * scaleY,
+ dirtyRect.width() * scaleX,
+ dirtyRect.height() * scaleY).toAlignedRect();
+ } else {
+ painter.scale(m_contentsScale, m_contentsScale);
+ QRect sclip(qFloor(dirtyRect.x() / m_contentsScale),
+ qFloor(dirtyRect.y() / m_contentsScale),
+ qCeil(dirtyRect.width() / m_contentsScale + dirtyRect.x() / m_contentsScale
+ - qFloor(dirtyRect.x() / m_contentsScale)),
+ qCeil(dirtyRect.height() / m_contentsScale + dirtyRect.y() / m_contentsScale
+ - qFloor(dirtyRect.y() / m_contentsScale)));
+ clipRect = sclip;
+ dirtyTextureRect = dirtyRect;
+ }
+
+ // only clip if we were originally updating only a subrect
+ if (!m_dirtyRect.isNull())
+ painter.setClipRect(clipRect);
+
+ painter.setCompositionMode(QPainter::CompositionMode_Source);
+ painter.fillRect(clipRect, m_fillColor);
+ painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
+
+ m_item->paint(&painter);
+ painter.end();
+
+ m_texture->dirty = dirtyTextureRect;
+ }
+ m_dirtyRect = QRect();
+ }
+}
+
+QSGTexture *QSGD3D12PainterNode::texture() const
+{
+ return m_texture;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12painternode_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12painternode_p.h
new file mode 100644
index 0000000000..67246cc3cd
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12painternode_p.h
@@ -0,0 +1,119 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGD3D12PAINTERNODE_P_H
+#define QSGD3D12PAINTERNODE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qsgadaptationlayer_p.h>
+#include "qsgd3d12texture_p.h"
+#include "qsgd3d12builtinmaterials_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QSGD3D12Engine;
+
+class QSGD3D12PainterTexture : public QSGD3D12Texture
+{
+public:
+ QSGD3D12PainterTexture(QSGD3D12Engine *engine);
+
+ void bind() override;
+
+ QImage *image() { return &m_image; }
+
+ QRect dirty;
+
+private:
+ QSize lastSize;
+};
+
+class QSGD3D12PainterNode : public QSGPainterNode
+{
+public:
+ QSGD3D12PainterNode(QQuickPaintedItem *item);
+ ~QSGD3D12PainterNode();
+
+ void setPreferredRenderTarget(QQuickPaintedItem::RenderTarget target) override;
+ void setSize(const QSize &size) override;
+ void setDirty(const QRect &dirtyRect = QRect()) override;
+ void setOpaquePainting(bool opaque) override;
+ void setLinearFiltering(bool linearFiltering) override;
+ void setMipmapping(bool mipmapping) override;
+ void setSmoothPainting(bool s) override;
+ void setFillColor(const QColor &c) override;
+ void setContentsScale(qreal s) override;
+ void setFastFBOResizing(bool dynamic) override;
+ void setTextureSize(const QSize &size) override;
+
+ QImage toImage() const override;
+ void update() override;
+ QSGTexture *texture() const override;
+
+private:
+ QQuickPaintedItem *m_item;
+ QSGD3D12Engine *m_engine;
+ QSGD3D12PainterTexture *m_texture;
+ QSize m_size;
+ QSize m_textureSize;
+ float m_contentsScale = 1;
+ bool m_smoothPainting = false;
+ QColor m_fillColor = Qt::transparent;
+ QRect m_dirtyRect;
+
+ QSGGeometry m_geometry;
+ QSGD3D12TextureMaterial m_material;
+
+ uint m_dirtyGeometry : 1;
+ uint m_dirtyContents : 1;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGD3D12PAINTERNODE_P_H
diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12rectanglenode.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12rectanglenode.cpp
new file mode 100644
index 0000000000..7548f5cbc0
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12rectanglenode.cpp
@@ -0,0 +1,72 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgd3d12rectanglenode_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QSGD3D12RectangleNode::QSGD3D12RectangleNode()
+{
+ setMaterial(&m_material);
+}
+
+void QSGD3D12RectangleNode::updateMaterialAntialiasing()
+{
+ if (m_antialiasing)
+ setMaterial(&m_smoothMaterial);
+ else
+ setMaterial(&m_material);
+}
+
+void QSGD3D12RectangleNode::updateMaterialBlending(QSGNode::DirtyState *state)
+{
+ // smoothed material is always blended, so no change in material state
+ if (material() == &m_material) {
+ bool wasBlending = (m_material.flags() & QSGMaterial::Blending);
+ bool isBlending = (m_gradient_stops.size() > 0 && !m_gradient_is_opaque)
+ || (m_color.alpha() < 255 && m_color.alpha() != 0)
+ || (m_pen_width > 0 && m_border_color.alpha() < 255);
+ if (wasBlending != isBlending) {
+ m_material.setFlag(QSGMaterial::Blending, isBlending);
+ *state |= QSGNode::DirtyMaterial;
+ }
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12rectanglenode_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12rectanglenode_p.h
new file mode 100644
index 0000000000..95f734bc4c
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12rectanglenode_p.h
@@ -0,0 +1,74 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGD3D12RECTANGLENODE_P_H
+#define QSGD3D12RECTANGLENODE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qsgbasicrectanglenode_p.h>
+#include "qsgd3d12builtinmaterials_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QSGD3D12RectangleNode : public QSGBasicRectangleNode
+{
+public:
+ QSGD3D12RectangleNode();
+
+private:
+ void updateMaterialAntialiasing() override;
+ void updateMaterialBlending(QSGNode::DirtyState *state) override;
+
+ QSGD3D12VertexColorMaterial m_material;
+ QSGD3D12SmoothColorMaterial m_smoothMaterial;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGD3D12RECTANGLENODE_P_H
diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12rendercontext.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12rendercontext.cpp
new file mode 100644
index 0000000000..b32bfe063a
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12rendercontext.cpp
@@ -0,0 +1,129 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgd3d12rendercontext_p.h"
+#include "qsgd3d12renderer_p.h"
+#include "qsgd3d12texture_p.h"
+
+QT_BEGIN_NAMESPACE
+
+// NOTE: Avoid categorized logging. It is slow.
+
+#define DECLARE_DEBUG_VAR(variable) \
+ static bool debug_ ## variable() \
+ { static bool value = qgetenv("QSG_RENDERER_DEBUG").contains(QT_STRINGIFY(variable)); return value; }
+
+DECLARE_DEBUG_VAR(render)
+
+QSGD3D12RenderContext::QSGD3D12RenderContext(QSGContext *ctx)
+ : QSGRenderContext(ctx)
+{
+}
+
+bool QSGD3D12RenderContext::isValid() const
+{
+ // The render thread sets an engine when it starts up and resets when it
+ // quits. The rc is initialized and functional between those two points,
+ // regardless of any calls to invalidate(). See setEngine().
+ return m_engine != nullptr;
+}
+
+void QSGD3D12RenderContext::invalidate()
+{
+ if (Q_UNLIKELY(debug_render()))
+ qDebug("rendercontext invalidate engine %p, %d/%d/%d", m_engine,
+ m_texturesToDelete.count(), m_textures.count(), m_fontEnginesToClean.count());
+
+ qDeleteAll(m_texturesToDelete);
+ m_texturesToDelete.clear();
+
+ qDeleteAll(m_textures);
+ m_textures.clear();
+
+ for (QSet<QFontEngine *>::const_iterator it = m_fontEnginesToClean.constBegin(),
+ end = m_fontEnginesToClean.constEnd(); it != end; ++it) {
+ (*it)->clearGlyphCache(m_engine);
+ if (!(*it)->ref.deref())
+ delete *it;
+ }
+ m_fontEnginesToClean.clear();
+
+ m_sg->renderContextInvalidated(this);
+ emit invalidated();
+}
+
+QSGTexture *QSGD3D12RenderContext::createTexture(const QImage &image, uint flags) const
+{
+ Q_ASSERT(m_engine);
+ QSGD3D12Texture *t = new QSGD3D12Texture(m_engine);
+ t->create(image, flags);
+ return t;
+}
+
+QSGRenderer *QSGD3D12RenderContext::createRenderer()
+{
+ return new QSGD3D12Renderer(this);
+}
+
+void QSGD3D12RenderContext::renderNextFrame(QSGRenderer *renderer, uint fbo)
+{
+ static_cast<QSGD3D12Renderer *>(renderer)->renderScene(fbo);
+}
+
+void QSGD3D12RenderContext::setEngine(QSGD3D12Engine *engine)
+{
+ if (m_engine == engine)
+ return;
+
+ m_engine = engine;
+
+ if (m_engine)
+ emit initialized();
+}
+
+void QSGD3D12RenderContext::ensureInitializedEmitted()
+{
+ if (!m_pendingInitialized)
+ return;
+
+ m_pendingInitialized = false;
+ emit initialized();
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12rendercontext_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12rendercontext_p.h
new file mode 100644
index 0000000000..86a300831d
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12rendercontext_p.h
@@ -0,0 +1,83 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGD3D12RENDERCONTEXT_P_H
+#define QSGD3D12RENDERCONTEXT_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qsgcontext_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QSGD3D12Engine;
+
+class QSGD3D12RenderContext : public QSGRenderContext
+{
+public:
+ QSGD3D12RenderContext(QSGContext *ctx);
+ bool isValid() const override;
+ void invalidate() override;
+ void renderNextFrame(QSGRenderer *renderer, uint fbo) override;
+ QSGTexture *createTexture(const QImage &image, uint flags) const override;
+ QSGRenderer *createRenderer() override;
+
+ void setEngine(QSGD3D12Engine *engine);
+ QSGD3D12Engine *engine() { return m_engine; }
+
+ void ensureInitializedEmitted();
+ void setInitializedPending() { m_pendingInitialized = true; }
+
+private:
+ QSGD3D12Engine *m_engine = nullptr;
+ bool m_pendingInitialized = false;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGD3D12RENDERCONTEXT_P_H
diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12renderer.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12renderer.cpp
new file mode 100644
index 0000000000..f76927c0bd
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12renderer.cpp
@@ -0,0 +1,784 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgd3d12renderer_p.h"
+#include "qsgd3d12rendercontext_p.h"
+#include <private/qsgnodeupdater_p.h>
+#include <private/qsgrendernode_p.h>
+
+#include "vs_stencilclip.hlslh"
+#include "ps_stencilclip.hlslh"
+
+//#define I_LIKE_STENCIL
+
+QT_BEGIN_NAMESPACE
+
+#define QSGNODE_TRAVERSE(NODE) for (QSGNode *child = NODE->firstChild(); child; child = child->nextSibling())
+
+// NOTE: Avoid categorized logging. It is slow.
+
+#define DECLARE_DEBUG_VAR(variable) \
+ static bool debug_ ## variable() \
+ { static bool value = qgetenv("QSG_RENDERER_DEBUG").contains(QT_STRINGIFY(variable)); return value; }
+
+DECLARE_DEBUG_VAR(build)
+DECLARE_DEBUG_VAR(change)
+DECLARE_DEBUG_VAR(render)
+
+class DummyUpdater : public QSGNodeUpdater
+{
+public:
+ void updateState(QSGNode *) { };
+};
+
+QSGD3D12Renderer::QSGD3D12Renderer(QSGRenderContext *context)
+ : QSGRenderer(context),
+ m_renderList(16),
+ m_vboData(1024),
+ m_iboData(256),
+ m_cboData(4096)
+{
+ setNodeUpdater(new DummyUpdater);
+ m_freshPipelineState.shaders.rootSig.textureViews.reserve(4);
+}
+
+QSGD3D12Renderer::~QSGD3D12Renderer()
+{
+ if (m_engine) {
+ m_engine->releaseBuffer(m_vertexBuf);
+ m_engine->releaseBuffer(m_indexBuf);
+ m_engine->releaseBuffer(m_constantBuf);
+ }
+}
+
+void QSGD3D12Renderer::renderScene(GLuint fboId)
+{
+ m_renderTarget = fboId;
+
+ struct DummyBindable : public QSGBindable {
+ void bind() const { }
+ } bindable;
+
+ QSGRenderer::renderScene(bindable); // calls back render()
+}
+
+// Search through the node set and remove nodes that are descendants of other
+// nodes in the same set.
+static QSet<QSGNode *> qsg_removeDescendants(const QSet<QSGNode *> &nodes, QSGRootNode *root)
+{
+ QSet<QSGNode *> result = nodes;
+ for (QSGNode *node : nodes) {
+ QSGNode *n = node;
+ while (n != root) {
+ if (n != node && result.contains(n)) {
+ result.remove(node);
+ break;
+ }
+ n = n->parent();
+ }
+ }
+ return result;
+}
+
+void QSGD3D12Renderer::updateMatrices(QSGNode *node, QSGTransformNode *xform)
+{
+ if (node->isSubtreeBlocked())
+ return;
+
+ if (node->type() == QSGNode::TransformNodeType) {
+ QSGTransformNode *tn = static_cast<QSGTransformNode *>(node);
+ if (xform)
+ tn->setCombinedMatrix(xform->combinedMatrix() * tn->matrix());
+ else
+ tn->setCombinedMatrix(tn->matrix());
+ QSGNODE_TRAVERSE(node)
+ updateMatrices(child, tn);
+ } else {
+ if (node->type() == QSGNode::GeometryNodeType || node->type() == QSGNode::ClipNodeType) {
+ m_nodeDirtyMap[node] |= QSGD3D12Material::RenderState::DirtyMatrix;
+ QSGBasicGeometryNode *gnode = static_cast<QSGBasicGeometryNode *>(node);
+ const QMatrix4x4 *newMatrix = xform ? &xform->combinedMatrix() : nullptr;
+ // NB the newMatrix ptr is usually the same as before as it just
+ // references the transform node's own matrix.
+ gnode->setMatrix(newMatrix);
+ }
+ QSGNODE_TRAVERSE(node)
+ updateMatrices(child, xform);
+ }
+}
+
+void QSGD3D12Renderer::updateOpacities(QSGNode *node, float inheritedOpacity)
+{
+ if (node->isSubtreeBlocked())
+ return;
+
+ if (node->type() == QSGNode::OpacityNodeType) {
+ QSGOpacityNode *on = static_cast<QSGOpacityNode *>(node);
+ float combined = inheritedOpacity * on->opacity();
+ on->setCombinedOpacity(combined);
+ QSGNODE_TRAVERSE(node)
+ updateOpacities(child, combined);
+ } else {
+ if (node->type() == QSGNode::GeometryNodeType) {
+ m_nodeDirtyMap[node] |= QSGD3D12Material::RenderState::DirtyOpacity;
+ QSGGeometryNode *gn = static_cast<QSGGeometryNode *>(node);
+ gn->setInheritedOpacity(inheritedOpacity);
+ }
+ QSGNODE_TRAVERSE(node)
+ updateOpacities(child, inheritedOpacity);
+ }
+}
+
+void QSGD3D12Renderer::buildRenderList(QSGNode *node, QSGClipNode *clip)
+{
+ if (node->isSubtreeBlocked())
+ return;
+
+ if (node->type() == QSGNode::GeometryNodeType || node->type() == QSGNode::ClipNodeType) {
+ QSGBasicGeometryNode *gn = static_cast<QSGBasicGeometryNode *>(node);
+ QSGGeometry *g = gn->geometry();
+
+ Element e;
+ e.node = gn;
+
+ if (g->vertexCount() > 0) {
+ e.vboOffset = m_vboData.size();
+ const int vertexSize = g->sizeOfVertex() * g->vertexCount();
+ m_vboData.resize(m_vboData.size() + vertexSize);
+ memcpy(m_vboData.data() + e.vboOffset, g->vertexData(), vertexSize);
+ }
+
+ if (g->indexCount() > 0) {
+ e.iboOffset = m_iboData.size();
+ e.iboStride = g->sizeOfIndex();
+ const int indexSize = e.iboStride * g->indexCount();
+ m_iboData.resize(m_iboData.size() + indexSize);
+ memcpy(m_iboData.data() + e.iboOffset, g->indexData(), indexSize);
+ }
+
+ e.cboOffset = m_cboData.size();
+ if (node->type() == QSGNode::GeometryNodeType) {
+ QSGD3D12Material *m = static_cast<QSGD3D12Material *>(static_cast<QSGGeometryNode *>(node)->activeMaterial());
+ e.cboSize = m->constantBufferSize();
+ } else {
+ // Stencil-based clipping needs a 4x4 matrix.
+ e.cboSize = QSGD3D12Engine::alignedConstantBufferSize(16 * sizeof(float));
+ }
+ m_cboData.resize(m_cboData.size() + e.cboSize);
+
+ m_renderList.add(e);
+
+ gn->setClipList(clip);
+ if (node->type() == QSGNode::ClipNodeType)
+ clip = static_cast<QSGClipNode *>(node);
+ } else if (node->type() == QSGNode::RenderNodeType) {
+ QSGRenderNode *rn = static_cast<QSGRenderNode *>(node);
+ Element e;
+ e.node = rn;
+ m_renderList.add(e);
+ }
+
+ QSGNODE_TRAVERSE(node)
+ buildRenderList(child, clip);
+}
+
+void QSGD3D12Renderer::render()
+{
+ QSGD3D12RenderContext *rc = static_cast<QSGD3D12RenderContext *>(context());
+ m_engine = rc->engine();
+ if (!m_layerRenderer)
+ m_engine->beginFrame();
+ else
+ m_engine->beginLayer();
+
+ m_activeScissorRect = QRect();
+
+ if (m_rebuild) {
+ m_rebuild = false;
+
+ m_dirtyTransformNodes.clear();
+ m_dirtyTransformNodes.insert(rootNode());
+ m_dirtyOpacityNodes.clear();
+ m_dirtyOpacityNodes.insert(rootNode());
+
+ m_renderList.reset();
+ m_vboData.reset();
+ m_iboData.reset();
+ m_cboData.reset();
+
+ buildRenderList(rootNode(), nullptr);
+
+ if (!m_vertexBuf)
+ m_vertexBuf = m_engine->genBuffer();
+ m_engine->resetBuffer(m_vertexBuf, m_vboData.data(), m_vboData.size());
+
+ if (!m_constantBuf)
+ m_constantBuf = m_engine->genBuffer();
+ m_engine->resetBuffer(m_constantBuf, m_cboData.data(), m_cboData.size());
+
+ if (m_iboData.size()) {
+ if (!m_indexBuf)
+ m_indexBuf = m_engine->genBuffer();
+ m_engine->resetBuffer(m_indexBuf, m_iboData.data(), m_iboData.size());
+ } else if (m_indexBuf) {
+ m_engine->releaseBuffer(m_indexBuf);
+ m_indexBuf = 0;
+ }
+
+ if (Q_UNLIKELY(debug_build())) {
+ qDebug("renderList: %d elements in total", m_renderList.size());
+ for (int i = 0; i < m_renderList.size(); ++i) {
+ const Element &e = m_renderList.at(i);
+ qDebug() << " - " << e.vboOffset << e.iboOffset << e.cboOffset << e.cboSize << e.node;
+ }
+ }
+ }
+
+ const QRect devRect = deviceRect();
+ m_projectionChangedDueToDeviceSize = devRect != m_lastDeviceRect;
+ if (m_projectionChangedDueToDeviceSize)
+ m_lastDeviceRect = devRect;
+
+ if (m_dirtyTransformNodes.size()) {
+ const QSet<QSGNode *> subTreeRoots = qsg_removeDescendants(m_dirtyTransformNodes, rootNode());
+ for (QSGNode *node : subTreeRoots) {
+ // First find the parent transform so we have the accumulated
+ // matrix up until this point.
+ QSGTransformNode *xform = 0;
+ QSGNode *n = node;
+ if (n->type() == QSGNode::TransformNodeType)
+ n = node->parent();
+ while (n != rootNode() && n->type() != QSGNode::TransformNodeType)
+ n = n->parent();
+ if (n != rootNode())
+ xform = static_cast<QSGTransformNode *>(n);
+
+ // Then update in the subtree
+ updateMatrices(node, xform);
+ }
+ }
+
+ if (m_dirtyOpacityNodes.size()) {
+ const QSet<QSGNode *> subTreeRoots = qsg_removeDescendants(m_dirtyOpacityNodes, rootNode());
+ for (QSGNode *node : subTreeRoots) {
+ float opacity = 1.0f;
+ QSGNode *n = node;
+ if (n->type() == QSGNode::OpacityNodeType)
+ n = node->parent();
+ while (n != rootNode() && n->type() != QSGNode::OpacityNodeType)
+ n = n->parent();
+ if (n != rootNode())
+ opacity = static_cast<QSGOpacityNode *>(n)->combinedOpacity();
+
+ updateOpacities(node, opacity);
+ }
+ m_dirtyOpaqueElements = true;
+ }
+
+ if (m_dirtyOpaqueElements) {
+ m_dirtyOpaqueElements = false;
+ m_opaqueElements.clear();
+ m_opaqueElements.resize(m_renderList.size());
+ for (int i = 0; i < m_renderList.size(); ++i) {
+ const Element &e = m_renderList.at(i);
+ if (e.node->type() == QSGNode::GeometryNodeType) {
+ const QSGGeometryNode *gn = static_cast<QSGGeometryNode *>(e.node);
+ if (gn->inheritedOpacity() > 0.999f && ((gn->activeMaterial()->flags() & QSGMaterial::Blending) == 0))
+ m_opaqueElements.setBit(i);
+ }
+ // QSGRenderNodes are always treated as non-opaque
+ }
+ }
+
+ // Build pipeline state and draw calls.
+ renderElements();
+
+ m_dirtyTransformNodes.clear();
+ m_dirtyOpacityNodes.clear();
+ m_dirtyOpaqueElements = false;
+ m_nodeDirtyMap.clear();
+
+ // Finalize buffers and execute commands.
+ if (!m_layerRenderer)
+ m_engine->endFrame();
+ else
+ m_engine->endLayer();
+}
+
+void QSGD3D12Renderer::nodeChanged(QSGNode *node, QSGNode::DirtyState state)
+{
+ // note that with DirtyNodeRemoved the window and all the graphics engine may already be gone
+
+ if (Q_UNLIKELY(debug_change())) {
+ QDebug debug = qDebug();
+ debug << "dirty:";
+ if (state & QSGNode::DirtyGeometry)
+ debug << "Geometry";
+ if (state & QSGNode::DirtyMaterial)
+ debug << "Material";
+ if (state & QSGNode::DirtyMatrix)
+ debug << "Matrix";
+ if (state & QSGNode::DirtyNodeAdded)
+ debug << "Added";
+ if (state & QSGNode::DirtyNodeRemoved)
+ debug << "Removed";
+ if (state & QSGNode::DirtyOpacity)
+ debug << "Opacity";
+ if (state & QSGNode::DirtySubtreeBlocked)
+ debug << "SubtreeBlocked";
+ if (state & QSGNode::DirtyForceUpdate)
+ debug << "ForceUpdate";
+
+ // when removed, some parts of the node could already have been destroyed
+ // so don't debug it out.
+ if (state & QSGNode::DirtyNodeRemoved)
+ debug << (void *) node << node->type();
+ else
+ debug << node;
+ }
+
+ if (state & (QSGNode::DirtyNodeAdded
+ | QSGNode::DirtyNodeRemoved
+ | QSGNode::DirtySubtreeBlocked
+ | QSGNode::DirtyGeometry
+ | QSGNode::DirtyForceUpdate))
+ m_rebuild = true;
+
+ if (state & QSGNode::DirtyMatrix)
+ m_dirtyTransformNodes << node;
+
+ if (state & QSGNode::DirtyOpacity)
+ m_dirtyOpacityNodes << node;
+
+ if (state & QSGNode::DirtyMaterial)
+ m_dirtyOpaqueElements = true;
+
+ QSGRenderer::nodeChanged(node, state);
+}
+
+void QSGD3D12Renderer::renderElements()
+{
+ m_engine->queueSetRenderTarget(m_renderTarget);
+ m_engine->queueViewport(viewportRect());
+ m_engine->queueClearRenderTarget(clearColor());
+ m_engine->queueClearDepthStencil(1, 0, QSGD3D12Engine::ClearDepth | QSGD3D12Engine::ClearStencil);
+
+ m_pipelineState.blend = m_freshPipelineState.blend = QSGD3D12PipelineState::BlendNone;
+ m_pipelineState.depthEnable = m_freshPipelineState.depthEnable = true;
+ m_pipelineState.depthWrite = m_freshPipelineState.depthWrite = true;
+
+ // First do opaque...
+ // The algorithm is quite simple. We traverse the list back-to-front, and
+ // for every item we start a second traversal and draw all elements which
+ // have identical material. Then we clear the bit for this in the rendered
+ // list so we don't draw it again when we come to that index.
+ QBitArray rendered = m_opaqueElements;
+ for (int i = m_renderList.size() - 1; i >= 0; --i) {
+ if (rendered.testBit(i)) {
+ renderElement(i);
+ for (int j = i - 1; j >= 0; --j) {
+ if (rendered.testBit(j)) {
+ const QSGGeometryNode *gni = static_cast<QSGGeometryNode *>(m_renderList.at(i).node);
+ const QSGGeometryNode *gnj = static_cast<QSGGeometryNode *>(m_renderList.at(j).node);
+ if (gni->clipList() == gnj->clipList()
+ && gni->inheritedOpacity() == gnj->inheritedOpacity()
+ && gni->geometry()->drawingMode() == gnj->geometry()->drawingMode()
+ && gni->geometry()->attributes() == gnj->geometry()->attributes()) {
+ const QSGMaterial *ami = gni->activeMaterial();
+ const QSGMaterial *amj = gnj->activeMaterial();
+ if (ami->type() == amj->type()
+ && ami->flags() == amj->flags()
+ && ami->compare(amj) == 0) {
+ renderElement(j);
+ rendered.clearBit(j);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ m_pipelineState.blend = m_freshPipelineState.blend = QSGD3D12PipelineState::BlendPremul;
+ m_pipelineState.depthWrite = m_freshPipelineState.depthWrite = false;
+
+ // ...then the alpha ones
+ for (int i = 0; i < m_renderList.size(); ++i) {
+ if ((m_renderList.at(i).node->type() == QSGNode::GeometryNodeType && !m_opaqueElements.testBit(i))
+ || m_renderList.at(i).node->type() == QSGNode::RenderNodeType)
+ renderElement(i);
+ }
+}
+
+struct RenderNodeState : public QSGRenderNode::RenderState
+{
+ const QMatrix4x4 *projectionMatrix() const override { return m_projectionMatrix; }
+ QRect scissorRect() const { return m_scissorRect; }
+ bool scissorEnabled() const { return m_scissorEnabled; }
+ int stencilValue() const { return m_stencilValue; }
+ bool stencilEnabled() const { return m_stencilEnabled; }
+
+ const QMatrix4x4 *m_projectionMatrix;
+ QRect m_scissorRect;
+ bool m_scissorEnabled;
+ int m_stencilValue;
+ bool m_stencilEnabled;
+};
+
+void QSGD3D12Renderer::renderElement(int elementIndex)
+{
+ Element &e = m_renderList.at(elementIndex);
+ Q_ASSERT(e.node->type() == QSGNode::GeometryNodeType || e.node->type() == QSGNode::RenderNodeType);
+
+ if (e.node->type() == QSGNode::RenderNodeType) {
+ renderRenderNode(static_cast<QSGRenderNode *>(e.node), elementIndex);
+ return;
+ }
+
+ if (e.vboOffset < 0)
+ return;
+
+ Q_ASSERT(e.cboOffset >= 0);
+
+ const QSGGeometryNode *gn = static_cast<QSGGeometryNode *>(e.node);
+ if (Q_UNLIKELY(debug_render()))
+ qDebug() << "renderElement:" << elementIndex << gn << e.vboOffset << e.iboOffset << gn->inheritedOpacity() << gn->clipList();
+
+ if (gn->inheritedOpacity() < 0.001f) // pretty much invisible, don't draw it
+ return;
+
+ // Update the QSGRenderer members which the materials will access.
+ m_current_projection_matrix = projectionMatrix();
+ const float scale = 1.0 / m_renderList.size();
+ m_current_projection_matrix(2, 2) = scale;
+ m_current_projection_matrix(2, 3) = 1.0f - (elementIndex + 1) * scale;
+ m_current_model_view_matrix = gn->matrix() ? *gn->matrix() : QMatrix4x4();
+ m_current_determinant = m_current_model_view_matrix.determinant();
+ m_current_opacity = gn->inheritedOpacity();
+
+ const QSGGeometry *g = gn->geometry();
+ QSGD3D12Material *m = static_cast<QSGD3D12Material *>(gn->activeMaterial());
+
+ if (m->type() != m_lastMaterialType) {
+ m_pipelineState = m_freshPipelineState;
+ m->preparePipeline(&m_pipelineState);
+ }
+
+ QSGD3D12Material::RenderState::DirtyStates dirtyState = m_nodeDirtyMap.value(e.node);
+
+ // After a rebuild everything in the cbuffer has to be updated.
+ if (!e.cboPrepared) {
+ e.cboPrepared = true;
+ dirtyState = QSGD3D12Material::RenderState::DirtyAll;
+ }
+
+ // DirtyMatrix does not include projection matrix changes that can arise
+ // due to changing the render target's size (and there is no rebuild).
+ // Accommodate for this.
+ if (m_projectionChangedDueToDeviceSize)
+ dirtyState |= QSGD3D12Material::RenderState::DirtyMatrix;
+
+ quint8 *cboPtr = nullptr;
+ if (e.cboSize > 0)
+ cboPtr = m_cboData.data() + e.cboOffset;
+
+ if (Q_UNLIKELY(debug_render()))
+ qDebug() << "dirty state for" << e.node << "is" << dirtyState;
+
+ QSGD3D12Material::ExtraState extraState;
+ QSGD3D12Material::UpdateResults updRes = m->updatePipeline(QSGD3D12Material::makeRenderState(this, dirtyState),
+ &m_pipelineState,
+ &extraState,
+ cboPtr);
+
+ if (updRes.testFlag(QSGD3D12Material::UpdatedConstantBuffer))
+ m_engine->markBufferDirty(m_constantBuf, e.cboOffset, e.cboSize);
+
+ if (updRes.testFlag(QSGD3D12Material::UpdatedBlendFactor))
+ m_engine->queueSetBlendFactor(extraState.blendFactor);
+
+ setInputLayout(g, &m_pipelineState);
+
+ m_lastMaterialType = m->type();
+
+ setupClipping(gn->clipList(), elementIndex);
+
+ // ### Lines and points with sizes other than 1 have to be implemented in some other way. Just ignore for now.
+ if (g->drawingMode() == QSGGeometry::DrawLineStrip || g->drawingMode() == QSGGeometry::DrawLines) {
+ if (g->lineWidth() != 1.0f)
+ qWarning("QSGD3D12Renderer: Line widths other than 1 are not supported by this renderer");
+ } else if (g->drawingMode() == QSGGeometry::DrawPoints) {
+ if (g->lineWidth() != 1.0f)
+ qWarning("QSGD3D12Renderer: Point sprites are not supported by this renderer");
+ }
+
+ m_engine->finalizePipeline(m_pipelineState);
+
+ queueDrawCall(g, e);
+}
+
+void QSGD3D12Renderer::setInputLayout(const QSGGeometry *g, QSGD3D12PipelineState *pipelineState)
+{
+ pipelineState->inputElements.resize(g->attributeCount());
+ const QSGGeometry::Attribute *attrs = g->attributes();
+ quint32 offset = 0;
+ for (int i = 0; i < g->attributeCount(); ++i) {
+ QSGD3D12InputElement &ie(pipelineState->inputElements[i]);
+ static const char *semanticNames[] = { "UNKNOWN", "POSITION", "COLOR", "TEXCOORD", "TEXCOORD", "TEXCOORD" };
+ static const int semanticIndices[] = { 0, 0, 0, 0, 1, 2 };
+ Q_ASSERT(attrs[i].semantic >= 1 && attrs[i].semantic < _countof(semanticNames));
+ const int tupleSize = attrs[i].tupleSize;
+ ie.semanticName = semanticNames[attrs[i].semantic];
+ ie.semanticIndex = semanticIndices[attrs[i].semantic];
+ ie.offset = offset;
+ int bytesPerTuple = 0;
+ ie.format = QSGD3D12Engine::toDXGIFormat(QSGGeometry::Type(attrs[i].type), tupleSize, &bytesPerTuple);
+ if (ie.format == FmtUnknown)
+ qFatal("QSGD3D12Renderer: unsupported tuple size for attribute type 0x%x", attrs[i].type);
+ offset += bytesPerTuple;
+ // There is one buffer with interleaved data so the slot is always 0.
+ ie.slot = 0;
+ }
+}
+
+void QSGD3D12Renderer::queueDrawCall(const QSGGeometry *g, const QSGD3D12Renderer::Element &e)
+{
+ QSGD3D12Engine::DrawParams dp;
+ dp.mode = QSGGeometry::DrawingMode(g->drawingMode());
+ dp.vertexBuf = m_vertexBuf;
+ dp.constantBuf = m_constantBuf;
+ dp.vboOffset = e.vboOffset;
+ dp.vboSize = g->vertexCount() * g->sizeOfVertex();
+ dp.vboStride = g->sizeOfVertex();
+ dp.cboOffset = e.cboOffset;
+
+ if (e.iboOffset >= 0) {
+ const QSGGeometry::Type indexType = QSGGeometry::Type(g->indexType());
+ const QSGD3D12Format indexFormat = QSGD3D12Engine::toDXGIFormat(indexType);
+ if (indexFormat == FmtUnknown)
+ qFatal("QSGD3D12Renderer: unsupported index type 0x%x", indexType);
+ dp.count = g->indexCount();
+ dp.indexBuf = m_indexBuf;
+ dp.startIndexIndex = e.iboOffset / e.iboStride;
+ dp.indexFormat = indexFormat;
+ } else {
+ dp.count = g->vertexCount();
+ }
+
+ m_engine->queueDraw(dp);
+}
+
+void QSGD3D12Renderer::setupClipping(const QSGClipNode *clip, int elementIndex)
+{
+ const QRect devRect = deviceRect();
+ QRect scissorRect;
+ int clipTypes = 0;
+ quint32 stencilValue = 0;
+
+ while (clip) {
+ QMatrix4x4 m = projectionMatrix();
+ if (clip->matrix())
+ m *= *clip->matrix();
+
+#ifndef I_LIKE_STENCIL
+ const bool isRectangleWithNoPerspective = clip->isRectangular()
+ && qFuzzyIsNull(m(3, 0)) && qFuzzyIsNull(m(3, 1));
+ const bool noRotate = qFuzzyIsNull(m(0, 1)) && qFuzzyIsNull(m(1, 0));
+ const bool isRotate90 = qFuzzyIsNull(m(0, 0)) && qFuzzyIsNull(m(1, 1));
+
+ if (isRectangleWithNoPerspective && (noRotate || isRotate90)) {
+ QRectF bbox = clip->clipRect();
+ float invW = 1.0f / m(3, 3);
+ float fx1, fy1, fx2, fy2;
+ if (noRotate) {
+ fx1 = (bbox.left() * m(0, 0) + m(0, 3)) * invW;
+ fy1 = (bbox.bottom() * m(1, 1) + m(1, 3)) * invW;
+ fx2 = (bbox.right() * m(0, 0) + m(0, 3)) * invW;
+ fy2 = (bbox.top() * m(1, 1) + m(1, 3)) * invW;
+ } else {
+ Q_ASSERT(isRotate90);
+ fx1 = (bbox.bottom() * m(0, 1) + m(0, 3)) * invW;
+ fy1 = (bbox.left() * m(1, 0) + m(1, 3)) * invW;
+ fx2 = (bbox.top() * m(0, 1) + m(0, 3)) * invW;
+ fy2 = (bbox.right() * m(1, 0) + m(1, 3)) * invW;
+ }
+
+ if (fx1 > fx2)
+ qSwap(fx1, fx2);
+ if (fy1 > fy2)
+ qSwap(fy1, fy2);
+
+ int ix1 = qRound((fx1 + 1) * devRect.width() * 0.5f);
+ int iy1 = qRound((fy1 + 1) * devRect.height() * 0.5f);
+ int ix2 = qRound((fx2 + 1) * devRect.width() * 0.5f);
+ int iy2 = qRound((fy2 + 1) * devRect.height() * 0.5f);
+
+ if (!(clipTypes & ClipScissor)) {
+ scissorRect = QRect(ix1, devRect.height() - iy2, ix2 - ix1, iy2 - iy1);
+ clipTypes |= ClipScissor;
+ } else {
+ scissorRect &= QRect(ix1, devRect.height() - iy2, ix2 - ix1, iy2 - iy1);
+ }
+ } else
+#endif
+ {
+ clipTypes |= ClipStencil;
+ renderStencilClip(clip, elementIndex, m, stencilValue);
+ }
+
+ clip = clip->clipList();
+ }
+
+ setScissor((clipTypes & ClipScissor) ? scissorRect : viewportRect());
+
+ if (clipTypes & ClipStencil) {
+ m_pipelineState.stencilEnable = true;
+ m_engine->queueSetStencilRef(stencilValue);
+ m_currentStencilValue = stencilValue;
+ } else {
+ m_pipelineState.stencilEnable = false;
+ m_currentStencilValue = 0;
+ }
+
+ m_currentClipTypes = clipTypes;
+}
+
+void QSGD3D12Renderer::setScissor(const QRect &r)
+{
+ if (m_activeScissorRect == r)
+ return;
+
+ m_activeScissorRect = r;
+ m_engine->queueScissor(r);
+}
+
+void QSGD3D12Renderer::renderStencilClip(const QSGClipNode *clip, int elementIndex,
+ const QMatrix4x4 &m, quint32 &stencilValue)
+{
+ QSGD3D12PipelineState sps;
+ sps.shaders.vs = g_VS_StencilClip;
+ sps.shaders.vsSize = sizeof(g_VS_StencilClip);
+ sps.shaders.ps = g_PS_StencilClip;
+ sps.shaders.psSize = sizeof(g_PS_StencilClip);
+
+ m_engine->queueClearDepthStencil(1, 0, QSGD3D12Engine::ClearStencil);
+ sps.stencilEnable = true;
+ sps.colorWrite = false;
+ sps.depthWrite = false;
+
+ sps.stencilFunc = QSGD3D12PipelineState::CompareEqual;
+ sps.stencilFailOp = QSGD3D12PipelineState::StencilKeep;
+ sps.stencilDepthFailOp = QSGD3D12PipelineState::StencilKeep;
+ sps.stencilPassOp = QSGD3D12PipelineState::StencilIncr;
+
+ m_engine->queueSetStencilRef(stencilValue);
+
+ int clipIndex = elementIndex;
+ while (m_renderList.at(--clipIndex).node != clip) {
+ Q_ASSERT(clipIndex >= 0);
+ }
+ const Element &ce = m_renderList.at(clipIndex);
+ Q_ASSERT(ce.node == clip);
+
+ const QSGGeometry *g = clip->geometry();
+ Q_ASSERT(g->attributeCount() == 1);
+ Q_ASSERT(g->attributes()[0].tupleSize == 2);
+ Q_ASSERT(g->attributes()[0].type == QSGGeometry::TypeFloat);
+
+ setInputLayout(g, &sps);
+ m_engine->finalizePipeline(sps);
+
+ Q_ASSERT(ce.cboSize > 0);
+ quint8 *p = m_cboData.data() + ce.cboOffset;
+ memcpy(p, m.constData(), 16 * sizeof(float));
+ m_engine->markBufferDirty(m_constantBuf, ce.cboOffset, ce.cboSize);
+
+ queueDrawCall(g, ce);
+
+ ++stencilValue;
+}
+
+void QSGD3D12Renderer::renderRenderNode(QSGRenderNode *node, int elementIndex)
+{
+ QSGRenderNodePrivate *rd = QSGRenderNodePrivate::get(node);
+ RenderNodeState state;
+
+ setupClipping(rd->m_clip_list, elementIndex);
+
+ QMatrix4x4 pm = projectionMatrix();
+ state.m_projectionMatrix = &pm;
+ state.m_scissorEnabled = m_currentClipTypes & ClipScissor;
+ state.m_stencilEnabled = m_currentClipTypes & ClipStencil;
+ state.m_scissorRect = m_activeScissorRect;
+ state.m_stencilValue = m_currentStencilValue;
+
+ // ### rendernodes do not have the QSGBasicGeometryNode infrastructure
+ // for storing combined matrices, opacity and such, but perhaps they should.
+ QSGNode *xform = node->parent();
+ QSGNode *root = rootNode();
+ QMatrix4x4 modelview;
+ while (xform != root) {
+ if (xform->type() == QSGNode::TransformNodeType) {
+ modelview *= static_cast<QSGTransformNode *>(xform)->combinedMatrix();
+ break;
+ }
+ xform = xform->parent();
+ }
+ rd->m_matrix = &modelview;
+
+ QSGNode *opacity = node->parent();
+ rd->m_opacity = 1.0;
+ while (opacity != rootNode()) {
+ if (opacity->type() == QSGNode::OpacityNodeType) {
+ rd->m_opacity = static_cast<QSGOpacityNode *>(opacity)->combinedOpacity();
+ break;
+ }
+ opacity = opacity->parent();
+ }
+
+ node->render(&state);
+
+ m_engine->invalidateCachedFrameState();
+ // For simplicity, reset viewport, scissor, blend factor, stencil ref when
+ // any of them got changed. This will likely be rare so skip these otherwise.
+ // Render target, pipeline state, draw call related stuff will be reset always.
+ const bool restoreMinimal = node->changedStates() == 0;
+ m_engine->restoreFrameState(restoreMinimal);
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12renderer_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12renderer_p.h
new file mode 100644
index 0000000000..bed34df3a1
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12renderer_p.h
@@ -0,0 +1,137 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGD3D12RENDERER_P_H
+#define QSGD3D12RENDERER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qsgrenderer_p.h>
+#include <QtGui/private/qdatabuffer_p.h>
+#include <QtCore/qbitarray.h>
+#include "qsgd3d12engine_p.h"
+#include "qsgd3d12material_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QSGRenderNode;
+
+class QSGD3D12Renderer : public QSGRenderer
+{
+public:
+ QSGD3D12Renderer(QSGRenderContext *context);
+ ~QSGD3D12Renderer();
+
+ void renderScene(GLuint fboId) override;
+ void render() override;
+ void nodeChanged(QSGNode *node, QSGNode::DirtyState state) override;
+
+ void turnToLayerRenderer() { m_layerRenderer = true; }
+
+private:
+ void updateMatrices(QSGNode *node, QSGTransformNode *xform);
+ void updateOpacities(QSGNode *node, float inheritedOpacity);
+ void buildRenderList(QSGNode *node, QSGClipNode *clip);
+ void renderElements();
+ void renderElement(int elementIndex);
+ void setInputLayout(const QSGGeometry *g, QSGD3D12PipelineState *pipelineState);
+ void setupClipping(const QSGClipNode *clip, int elementIndex);
+ void setScissor(const QRect &r);
+ void renderStencilClip(const QSGClipNode *clip, int elementIndex, const QMatrix4x4 &m, quint32 &stencilValue);
+ void renderRenderNode(QSGRenderNode *node, int elementIndex);
+
+ struct Element {
+ QSGNode *node = nullptr;
+ qint32 vboOffset = -1;
+ qint32 iboOffset = -1;
+ quint32 iboStride = 0;
+ qint32 cboOffset = -1;
+ quint32 cboSize = 0;
+ bool cboPrepared = false;
+ };
+
+ void queueDrawCall(const QSGGeometry *g, const Element &e);
+
+ bool m_layerRenderer = false;
+ QSet<QSGNode *> m_dirtyTransformNodes;
+ QSet<QSGNode *> m_dirtyOpacityNodes;
+ QBitArray m_opaqueElements;
+ bool m_rebuild = true;
+ bool m_dirtyOpaqueElements = true;
+ QDataBuffer<quint8> m_vboData;
+ QDataBuffer<quint8> m_iboData;
+ QDataBuffer<quint8> m_cboData;
+ QDataBuffer<Element> m_renderList;
+ uint m_vertexBuf = 0;
+ uint m_indexBuf = 0;
+ uint m_constantBuf = 0;
+ QSGD3D12Engine *m_engine = nullptr;
+
+ QSGMaterialType *m_lastMaterialType = nullptr;
+ QSGD3D12PipelineState m_pipelineState;
+ QSGD3D12PipelineState m_freshPipelineState;
+
+ typedef QHash<QSGNode *, QSGD3D12Material::RenderState::DirtyStates> NodeDirtyMap;
+ NodeDirtyMap m_nodeDirtyMap;
+
+ QRect m_activeScissorRect;
+ QRect m_lastDeviceRect;
+ bool m_projectionChangedDueToDeviceSize;
+
+ uint m_renderTarget = 0;
+ quint32 m_currentStencilValue;
+ enum ClipType {
+ ClipScissor = 0x1,
+ ClipStencil = 0x2
+ };
+ int m_currentClipTypes;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGD3D12RENDERER_P_H
diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12renderloop.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12renderloop.cpp
new file mode 100644
index 0000000000..44c2744978
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12renderloop.cpp
@@ -0,0 +1,1167 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgd3d12renderloop_p.h"
+#include "qsgd3d12engine_p.h"
+#include "qsgd3d12context_p.h"
+#include "qsgd3d12rendercontext_p.h"
+#include "qsgd3d12shadereffectnode_p.h"
+#include <private/qsgrenderer_p.h>
+#include <private/qquickwindow_p.h>
+#include <private/qquickprofiler_p.h>
+#include <private/qquickanimatorcontroller_p.h>
+#include <private/qquickprofiler_p.h>
+#include <private/qqmldebugserviceinterfaces_p.h>
+#include <private/qqmldebugconnector_p.h>
+#include <QElapsedTimer>
+#include <QQueue>
+#include <QGuiApplication>
+
+QT_BEGIN_NAMESPACE
+
+// NOTE: Avoid categorized logging. It is slow.
+
+#define DECLARE_DEBUG_VAR(variable) \
+ static bool debug_ ## variable() \
+ { static bool value = qgetenv("QSG_RENDERER_DEBUG").contains(QT_STRINGIFY(variable)); return value; }
+
+DECLARE_DEBUG_VAR(loop)
+DECLARE_DEBUG_VAR(time)
+
+/*
+ The D3D render loop mostly mirrors the threaded OpenGL render loop.
+
+ There are two classes here. QSGD3D12RenderLoop and QSGD3D12RenderThread. All
+ communication between the two is based on event passing and we have a number
+ of custom events.
+
+ Render loop is per process, render thread is per window. The
+ QSGD3D12RenderContext and QSGD3D12Engine are per window as well. The former
+ is created (but not owned) by QQuickWindow. The D3D device is per process.
+
+ In this implementation, the render thread is never blocked and the GUI
+ thread will initiate a polishAndSync which will block and wait for the
+ render thread to pick it up and release the block only after the render
+ thread is done syncing. The reason for this is:
+
+ 1. Clear blocking paradigm. We only have one real "block" point
+ (polishAndSync()) and all blocking is initiated by GUI and picked up by
+ Render at specific times based on events. This makes the execution
+ deterministic.
+
+ 2. Render does not have to interact with GUI. This is done so that the
+ render thread can run its own animation system which stays alive even when
+ the GUI thread is blocked doing I/O, object instantiation, QPainter-painting
+ or any other non-trivial task.
+
+ The render thread has affinity to the GUI thread until a window is shown.
+ From that moment and until the window is destroyed, it will have affinity to
+ the render thread. (moved back at the end of run for cleanup).
+ */
+
+// Passed from the RL to the RT when a window is removed obscured and should be
+// removed from the render loop.
+const QEvent::Type WM_Obscure = QEvent::Type(QEvent::User + 1);
+
+// Passed from the RL to RT when GUI has been locked, waiting for sync.
+const QEvent::Type WM_RequestSync = QEvent::Type(QEvent::User + 2);
+
+// Passed by the RT to itself to trigger another render pass. This is typically
+// a result of QQuickWindow::update().
+const QEvent::Type WM_RequestRepaint = QEvent::Type(QEvent::User + 3);
+
+// Passed by the RL to the RT to maybe release resource if no windows are
+// rendering.
+const QEvent::Type WM_TryRelease = QEvent::Type(QEvent::User + 4);
+
+// Passed by the RL to the RT when a QQuickWindow::grabWindow() is called.
+const QEvent::Type WM_Grab = QEvent::Type(QEvent::User + 5);
+
+// Passed by the window when there is a render job to run.
+const QEvent::Type WM_PostJob = QEvent::Type(QEvent::User + 6);
+
+class QSGD3D12WindowEvent : public QEvent
+{
+public:
+ QSGD3D12WindowEvent(QQuickWindow *c, QEvent::Type type) : QEvent(type), window(c) { }
+ QQuickWindow *window;
+};
+
+class QSGD3D12TryReleaseEvent : public QSGD3D12WindowEvent
+{
+public:
+ QSGD3D12TryReleaseEvent(QQuickWindow *win, bool destroy)
+ : QSGD3D12WindowEvent(win, WM_TryRelease), destroying(destroy) { }
+ bool destroying;
+};
+
+class QSGD3D12SyncEvent : public QSGD3D12WindowEvent
+{
+public:
+ QSGD3D12SyncEvent(QQuickWindow *c, bool inExpose, bool force)
+ : QSGD3D12WindowEvent(c, WM_RequestSync)
+ , size(c->size())
+ , dpr(c->effectiveDevicePixelRatio())
+ , syncInExpose(inExpose)
+ , forceRenderPass(force) { }
+ QSize size;
+ float dpr;
+ bool syncInExpose;
+ bool forceRenderPass;
+};
+
+class QSGD3D12GrabEvent : public QSGD3D12WindowEvent
+{
+public:
+ QSGD3D12GrabEvent(QQuickWindow *c, QImage *result)
+ : QSGD3D12WindowEvent(c, WM_Grab), image(result) { }
+ QImage *image;
+};
+
+class QSGD3D12JobEvent : public QSGD3D12WindowEvent
+{
+public:
+ QSGD3D12JobEvent(QQuickWindow *c, QRunnable *postedJob)
+ : QSGD3D12WindowEvent(c, WM_PostJob), job(postedJob) { }
+ ~QSGD3D12JobEvent() { delete job; }
+ QRunnable *job;
+};
+
+class QSGD3D12EventQueue : public QQueue<QEvent *>
+{
+public:
+ void addEvent(QEvent *e) {
+ mutex.lock();
+ enqueue(e);
+ if (waiting)
+ condition.wakeOne();
+ mutex.unlock();
+ }
+
+ QEvent *takeEvent(bool wait) {
+ mutex.lock();
+ if (isEmpty() && wait) {
+ waiting = true;
+ condition.wait(&mutex);
+ waiting = false;
+ }
+ QEvent *e = dequeue();
+ mutex.unlock();
+ return e;
+ }
+
+ bool hasMoreEvents() {
+ mutex.lock();
+ bool has = !isEmpty();
+ mutex.unlock();
+ return has;
+ }
+
+private:
+ QMutex mutex;
+ QWaitCondition condition;
+ bool waiting = false;
+};
+
+static inline int qsgrl_animation_interval()
+{
+ const qreal refreshRate = QGuiApplication::primaryScreen() ? QGuiApplication::primaryScreen()->refreshRate() : 0;
+ return refreshRate < 1 ? 16 : int(1000 / refreshRate);
+}
+
+class QSGD3D12RenderThread : public QThread
+{
+ Q_OBJECT
+
+public:
+ QSGD3D12RenderThread(QSGD3D12RenderLoop *rl, QSGRenderContext *renderContext)
+ : renderLoop(rl)
+ {
+ rc = static_cast<QSGD3D12RenderContext *>(renderContext);
+ vsyncDelta = qsgrl_animation_interval();
+ }
+
+ ~QSGD3D12RenderThread()
+ {
+ delete rc;
+ }
+
+ bool event(QEvent *e);
+ void run();
+
+ void syncAndRender();
+ void sync(bool inExpose);
+
+ void requestRepaint()
+ {
+ if (sleeping)
+ stopEventProcessing = true;
+ if (exposedWindow)
+ pendingUpdate |= RepaintRequest;
+ }
+
+ void processEventsAndWaitForMore();
+ void processEvents();
+ void postEvent(QEvent *e);
+
+ enum UpdateRequest {
+ SyncRequest = 0x01,
+ RepaintRequest = 0x02,
+ ExposeRequest = 0x04 | RepaintRequest | SyncRequest
+ };
+
+ QSGD3D12Engine *engine = nullptr;
+ QSGD3D12RenderLoop *renderLoop;
+ QSGD3D12RenderContext *rc;
+ QAnimationDriver *rtAnim = nullptr;
+ volatile bool active = false;
+ uint pendingUpdate = 0;
+ bool sleeping = false;
+ bool syncResultedInChanges = false;
+ float vsyncDelta;
+ QMutex mutex;
+ QWaitCondition waitCondition;
+ QQuickWindow *exposedWindow = nullptr;
+ bool stopEventProcessing = false;
+ QSGD3D12EventQueue eventQueue;
+ QElapsedTimer threadTimer;
+ qint64 syncTime;
+ qint64 renderTime;
+ qint64 sinceLastTime;
+
+public slots:
+ void onSceneGraphChanged() {
+ syncResultedInChanges = true;
+ }
+};
+
+bool QSGD3D12RenderThread::event(QEvent *e)
+{
+ switch (e->type()) {
+
+ case WM_Obscure:
+ Q_ASSERT(!exposedWindow || exposedWindow == static_cast<QSGD3D12WindowEvent *>(e)->window);
+ if (Q_UNLIKELY(debug_loop()))
+ qDebug() << "RT - WM_Obscure" << exposedWindow;
+ mutex.lock();
+ if (exposedWindow) {
+ QQuickWindowPrivate::get(exposedWindow)->fireAboutToStop();
+ if (Q_UNLIKELY(debug_loop()))
+ qDebug("RT - WM_Obscure - window removed");
+ exposedWindow = nullptr;
+ }
+ waitCondition.wakeOne();
+ mutex.unlock();
+ return true;
+
+ case WM_RequestSync: {
+ QSGD3D12SyncEvent *wme = static_cast<QSGD3D12SyncEvent *>(e);
+ if (sleeping)
+ stopEventProcessing = true;
+ // One thread+engine for each window. However, the native window may
+ // change in some (quite artificial) cases, e.g. due to a hide -
+ // destroy - show on the QWindow.
+ bool needsWindow = !engine->window();
+ if (engine->window() && engine->window() != wme->window->winId()) {
+ if (Q_UNLIKELY(debug_loop()))
+ qDebug("RT - WM_RequestSync - native window handle changes for active engine");
+ engine->waitGPU();
+ QQuickWindowPrivate::get(wme->window)->cleanupNodesOnShutdown();
+ QSGD3D12ShaderEffectNode::cleanupMaterialTypeCache();
+ rc->invalidate();
+ engine->releaseResources();
+ needsWindow = true;
+ // Be nice and emit the rendercontext's initialized() later on at
+ // some point so that QQuickWindow::sceneGraphInitialized() behaves
+ // in a manner similar to GL.
+ rc->setInitializedPending();
+ }
+ if (needsWindow) {
+ // Must only ever get here when there is no window or releaseResources() has been called.
+ const int samples = wme->window->format().samples();
+ if (Q_UNLIKELY(debug_loop()))
+ qDebug() << "RT - WM_RequestSync - initializing D3D12 engine" << wme->window
+ << wme->size << wme->dpr << samples;
+ engine->attachToWindow(wme->window->winId(), wme->size, wme->dpr, samples);
+ }
+ exposedWindow = wme->window;
+ engine->setWindowSize(wme->size, wme->dpr);
+ if (Q_UNLIKELY(debug_loop()))
+ qDebug() << "RT - WM_RequestSync" << exposedWindow;
+ pendingUpdate |= SyncRequest;
+ if (wme->syncInExpose) {
+ if (Q_UNLIKELY(debug_loop()))
+ qDebug("RT - WM_RequestSync - triggered from expose");
+ pendingUpdate |= ExposeRequest;
+ }
+ if (wme->forceRenderPass) {
+ if (Q_UNLIKELY(debug_loop()))
+ qDebug("RT - WM_RequestSync - repaint regardless");
+ pendingUpdate |= RepaintRequest;
+ }
+ return true;
+ }
+
+ case WM_TryRelease: {
+ if (Q_UNLIKELY(debug_loop()))
+ qDebug("RT - WM_TryRelease");
+ mutex.lock();
+ renderLoop->lockedForSync = true;
+ QSGD3D12TryReleaseEvent *wme = static_cast<QSGD3D12TryReleaseEvent *>(e);
+ // Only when no windows are exposed anymore or we are shutting down.
+ if (!exposedWindow || wme->destroying) {
+ if (Q_UNLIKELY(debug_loop()))
+ qDebug("RT - WM_TryRelease - invalidating rc");
+ if (wme->window) {
+ QQuickWindowPrivate *wd = QQuickWindowPrivate::get(wme->window);
+ if (wme->destroying) {
+ // QSGNode destruction may release graphics resources in use so wait first.
+ engine->waitGPU();
+ // Bye bye nodes...
+ wd->cleanupNodesOnShutdown();
+ QSGD3D12ShaderEffectNode::cleanupMaterialTypeCache();
+ }
+ rc->invalidate();
+ QCoreApplication::processEvents();
+ QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
+ if (wme->destroying)
+ delete wd->animationController;
+ }
+ if (wme->destroying)
+ active = false;
+ if (sleeping)
+ stopEventProcessing = true;
+ } else {
+ if (Q_UNLIKELY(debug_loop()))
+ qDebug("RT - WM_TryRelease - not releasing because window is still active");
+ }
+ waitCondition.wakeOne();
+ renderLoop->lockedForSync = false;
+ mutex.unlock();
+ return true;
+ }
+
+ case WM_Grab: {
+ if (Q_UNLIKELY(debug_loop()))
+ qDebug("RT - WM_Grab");
+ QSGD3D12GrabEvent *wme = static_cast<QSGD3D12GrabEvent *>(e);
+ Q_ASSERT(wme->window);
+ Q_ASSERT(wme->window == exposedWindow || !exposedWindow);
+ mutex.lock();
+ if (wme->window) {
+ // Grabbing is generally done by rendering a frame and reading the
+ // color buffer contents back, without presenting, and then
+ // creating a QImage from the returned data. It is terribly
+ // inefficient since it involves a full blocking wait for the GPU.
+ // However, our hands are tied by the existing, synchronous APIs of
+ // QQuickWindow and such.
+ QQuickWindowPrivate *wd = QQuickWindowPrivate::get(wme->window);
+ rc->ensureInitializedEmitted();
+ wd->syncSceneGraph();
+ wd->renderSceneGraph(wme->window->size());
+ *wme->image = engine->executeAndWaitReadbackRenderTarget();
+ }
+ if (Q_UNLIKELY(debug_loop()))
+ qDebug("RT - WM_Grab - waking gui to handle result");
+ waitCondition.wakeOne();
+ mutex.unlock();
+ return true;
+ }
+
+ case WM_PostJob: {
+ if (Q_UNLIKELY(debug_loop()))
+ qDebug("RT - WM_PostJob");
+ QSGD3D12JobEvent *wme = static_cast<QSGD3D12JobEvent *>(e);
+ Q_ASSERT(wme->window == exposedWindow);
+ if (exposedWindow) {
+ wme->job->run();
+ delete wme->job;
+ wme->job = nullptr;
+ if (Q_UNLIKELY(debug_loop()))
+ qDebug("RT - WM_PostJob - job done");
+ }
+ return true;
+ }
+
+ case WM_RequestRepaint:
+ if (Q_UNLIKELY(debug_loop()))
+ qDebug("RT - WM_RequestPaint");
+ // When GUI posts this event, it is followed by a polishAndSync, so we
+ // must not exit the event loop yet.
+ pendingUpdate |= RepaintRequest;
+ break;
+
+ default:
+ break;
+ }
+
+ return QThread::event(e);
+}
+
+void QSGD3D12RenderThread::postEvent(QEvent *e)
+{
+ eventQueue.addEvent(e);
+}
+
+void QSGD3D12RenderThread::processEvents()
+{
+ while (eventQueue.hasMoreEvents()) {
+ QEvent *e = eventQueue.takeEvent(false);
+ event(e);
+ delete e;
+ }
+}
+
+void QSGD3D12RenderThread::processEventsAndWaitForMore()
+{
+ stopEventProcessing = false;
+ while (!stopEventProcessing) {
+ QEvent *e = eventQueue.takeEvent(true);
+ event(e);
+ delete e;
+ }
+}
+
+void QSGD3D12RenderThread::run()
+{
+ if (Q_UNLIKELY(debug_loop()))
+ qDebug("RT - run()");
+
+ engine = new QSGD3D12Engine;
+ rc->setEngine(engine);
+
+ rtAnim = rc->sceneGraphContext()->createAnimationDriver(nullptr);
+ rtAnim->install();
+
+ if (QQmlDebugConnector::service<QQmlProfilerService>())
+ QQuickProfiler::registerAnimationCallback();
+
+ while (active) {
+ if (exposedWindow)
+ syncAndRender();
+
+ processEvents();
+ QCoreApplication::processEvents();
+
+ if (pendingUpdate == 0 || !exposedWindow) {
+ if (Q_UNLIKELY(debug_loop()))
+ qDebug("RT - done drawing, sleep");
+ sleeping = true;
+ processEventsAndWaitForMore();
+ sleeping = false;
+ }
+ }
+
+ if (Q_UNLIKELY(debug_loop()))
+ qDebug("RT - run() exiting");
+
+ delete rtAnim;
+ rtAnim = nullptr;
+
+ rc->moveToThread(renderLoop->thread());
+ moveToThread(renderLoop->thread());
+
+ rc->setEngine(nullptr);
+ delete engine;
+ engine = nullptr;
+}
+
+void QSGD3D12RenderThread::sync(bool inExpose)
+{
+ if (Q_UNLIKELY(debug_loop()))
+ qDebug("RT - sync");
+
+ mutex.lock();
+ Q_ASSERT_X(renderLoop->lockedForSync, "QSGD3D12RenderThread::sync()", "sync triggered with gui not locked");
+
+ // Recover from device loss.
+ if (!engine->hasResources()) {
+ if (Q_UNLIKELY(debug_loop()))
+ qDebug("RT - sync - device was lost, resetting scenegraph");
+ QQuickWindowPrivate::get(exposedWindow)->cleanupNodesOnShutdown();
+ QSGD3D12ShaderEffectNode::cleanupMaterialTypeCache();
+ rc->invalidate();
+ }
+
+ if (engine->window()) {
+ QQuickWindowPrivate *wd = QQuickWindowPrivate::get(exposedWindow);
+ bool hadRenderer = wd->renderer != nullptr;
+ // If the scene graph was touched since the last sync() make sure it sends the
+ // changed signal.
+ if (wd->renderer)
+ wd->renderer->clearChangedFlag();
+
+ rc->ensureInitializedEmitted();
+ wd->syncSceneGraph();
+
+ if (!hadRenderer && wd->renderer) {
+ if (Q_UNLIKELY(debug_loop()))
+ qDebug("RT - created renderer");
+ syncResultedInChanges = true;
+ connect(wd->renderer, &QSGRenderer::sceneGraphChanged, this,
+ &QSGD3D12RenderThread::onSceneGraphChanged, Qt::DirectConnection);
+ }
+
+ // Process deferred deletes now, directly after the sync as deleteLater
+ // on the GUI must now also have resulted in SG changes and the delete
+ // is a safe operation.
+ QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
+ }
+
+ if (!inExpose) {
+ if (Q_UNLIKELY(debug_loop()))
+ qDebug("RT - sync complete, waking gui");
+ waitCondition.wakeOne();
+ mutex.unlock();
+ }
+}
+
+void QSGD3D12RenderThread::syncAndRender()
+{
+ if (Q_UNLIKELY(debug_time())) {
+ sinceLastTime = threadTimer.nsecsElapsed();
+ threadTimer.start();
+ }
+ Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphRenderLoopFrame);
+
+ QElapsedTimer waitTimer;
+ waitTimer.start();
+
+ if (Q_UNLIKELY(debug_loop()))
+ qDebug("RT - syncAndRender()");
+
+ syncResultedInChanges = false;
+ QQuickWindowPrivate *wd = QQuickWindowPrivate::get(exposedWindow);
+
+ const bool repaintRequested = (pendingUpdate & RepaintRequest) || wd->customRenderStage;
+ const bool syncRequested = pendingUpdate & SyncRequest;
+ const bool exposeRequested = (pendingUpdate & ExposeRequest) == ExposeRequest;
+ pendingUpdate = 0;
+
+ if (syncRequested)
+ sync(exposeRequested);
+
+#ifndef QSG_NO_RENDER_TIMING
+ if (Q_UNLIKELY(debug_time()))
+ syncTime = threadTimer.nsecsElapsed();
+#endif
+ Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame);
+
+ if (!syncResultedInChanges && !repaintRequested) {
+ if (Q_UNLIKELY(debug_loop()))
+ qDebug("RT - no changes, render aborted");
+ int waitTime = vsyncDelta - (int) waitTimer.elapsed();
+ if (waitTime > 0)
+ msleep(waitTime);
+ return;
+ }
+
+ if (Q_UNLIKELY(debug_loop()))
+ qDebug("RT - rendering started");
+
+ if (rtAnim->isRunning()) {
+ wd->animationController->lock();
+ rtAnim->advance();
+ wd->animationController->unlock();
+ }
+
+ bool canRender = wd->renderer != nullptr;
+ // Recover from device loss.
+ if (!engine->hasResources()) {
+ if (Q_UNLIKELY(debug_loop()))
+ qDebug("RT - syncAndRender - device was lost, posting FullUpdateRequest");
+ // Cannot do anything here because gui is not locked. Request a new
+ // sync+render round on the gui thread and let the sync handle it.
+ QCoreApplication::postEvent(exposedWindow, new QEvent(QEvent::Type(QQuickWindowPrivate::FullUpdateRequest)));
+ canRender = false;
+ }
+
+ if (canRender) {
+ wd->renderSceneGraph(engine->windowSize());
+ if (Q_UNLIKELY(debug_time()))
+ renderTime = threadTimer.nsecsElapsed();
+ Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame);
+
+ // The engine is able to have multiple frames in flight. This in effect is
+ // similar to BufferQueueingOpenGL. Provide an env var to force the
+ // traditional blocking swap behavior, just in case.
+ static bool blockOnEachFrame = qEnvironmentVariableIntValue("QT_D3D_BLOCKING_PRESENT") != 0;
+
+ if (!wd->customRenderStage || !wd->customRenderStage->swap())
+ engine->present();
+
+ if (blockOnEachFrame)
+ engine->waitGPU();
+
+ // The concept of "frame swaps" is quite misleading by default, when
+ // blockOnEachFrame is not used, but emit it for compatibility.
+ wd->fireFrameSwapped();
+ } else {
+ Q_QUICK_SG_PROFILE_SKIP(QQuickProfiler::SceneGraphRenderLoopFrame, 1);
+ if (Q_UNLIKELY(debug_loop()))
+ qDebug("RT - window not ready, skipping render");
+ }
+
+ if (Q_UNLIKELY(debug_loop()))
+ qDebug("RT - rendering done");
+
+ if (exposeRequested) {
+ if (Q_UNLIKELY(debug_loop()))
+ qDebug("RT - wake gui after initial expose");
+ waitCondition.wakeOne();
+ mutex.unlock();
+ }
+
+ if (Q_UNLIKELY(debug_time()))
+ qDebug("Frame rendered with 'd3d12' renderloop in %dms, sync=%d, render=%d, swap=%d - (on render thread)",
+ int(threadTimer.elapsed()),
+ int((syncTime/1000000)),
+ int((renderTime - syncTime) / 1000000),
+ int(threadTimer.elapsed() - renderTime / 1000000));
+
+ Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphRenderLoopFrame);
+
+ static int devLossTest = qEnvironmentVariableIntValue("QT_D3D_TEST_DEVICE_LOSS");
+ if (devLossTest > 0) {
+ static QElapsedTimer kt;
+ static bool timerRunning = false;
+ if (!timerRunning) {
+ kt.start();
+ timerRunning = true;
+ } else if (kt.elapsed() > 5000) {
+ --devLossTest;
+ kt.restart();
+ engine->simulateDeviceLoss();
+ }
+ }
+}
+
+template<class T> T *windowFor(const QVector<T> &list, QQuickWindow *window)
+{
+ for (const T &t : list) {
+ if (t.window == window)
+ return const_cast<T *>(&t);
+ }
+ return nullptr;
+}
+
+QSGD3D12RenderLoop::QSGD3D12RenderLoop()
+{
+ if (Q_UNLIKELY(debug_loop()))
+ qDebug("new d3d12 render loop ctor");
+
+ sg = new QSGD3D12Context;
+
+ anim = sg->createAnimationDriver(this);
+ connect(anim, &QAnimationDriver::started, this, &QSGD3D12RenderLoop::onAnimationStarted);
+ connect(anim, &QAnimationDriver::stopped, this, &QSGD3D12RenderLoop::onAnimationStopped);
+ anim->install();
+}
+
+QSGD3D12RenderLoop::~QSGD3D12RenderLoop()
+{
+ if (Q_UNLIKELY(debug_loop()))
+ qDebug("new d3d12 render loop dtor");
+
+ delete sg;
+}
+
+void QSGD3D12RenderLoop::show(QQuickWindow *window)
+{
+ if (Q_UNLIKELY(debug_loop()))
+ qDebug() << "show" << window;
+}
+
+void QSGD3D12RenderLoop::hide(QQuickWindow *window)
+{
+ if (Q_UNLIKELY(debug_loop()))
+ qDebug() << "hide" << window;
+
+ if (window->isExposed())
+ handleObscurity(windowFor(windows, window));
+
+ releaseResources(window);
+}
+
+void QSGD3D12RenderLoop::resize(QQuickWindow *window)
+{
+ if (!window->isExposed() || window->size().isEmpty())
+ return;
+
+ if (Q_UNLIKELY(debug_loop()))
+ qDebug() << "resize" << window << window->size();
+}
+
+void QSGD3D12RenderLoop::windowDestroyed(QQuickWindow *window)
+{
+ if (Q_UNLIKELY(debug_loop()))
+ qDebug() << "window destroyed" << window;
+
+ WindowData *w = windowFor(windows, window);
+ if (!w)
+ return;
+
+ handleObscurity(w);
+ handleResourceRelease(w, true);
+
+ QSGD3D12RenderThread *thread = w->thread;
+ while (thread->isRunning())
+ QThread::yieldCurrentThread();
+
+ Q_ASSERT(thread->thread() == QThread::currentThread());
+ delete thread;
+
+ for (int i = 0; i < windows.size(); ++i) {
+ if (windows.at(i).window == window) {
+ windows.removeAt(i);
+ break;
+ }
+ }
+}
+
+void QSGD3D12RenderLoop::exposureChanged(QQuickWindow *window)
+{
+ if (Q_UNLIKELY(debug_loop()))
+ qDebug() << "exposure changed" << window;
+
+ if (window->isExposed()) {
+ handleExposure(window);
+ } else {
+ WindowData *w = windowFor(windows, window);
+ if (w)
+ handleObscurity(w);
+ }
+}
+
+QImage QSGD3D12RenderLoop::grab(QQuickWindow *window)
+{
+ if (Q_UNLIKELY(debug_loop()))
+ qDebug() << "grab" << window;
+
+ WindowData *w = windowFor(windows, window);
+ // Have to support invisible (but created()'ed) windows as well.
+ // Unlike with GL, leaving that case for QQuickWindow to handle is not feasible.
+ const bool tempExpose = !w;
+ if (tempExpose) {
+ handleExposure(window);
+ w = windowFor(windows, window);
+ Q_ASSERT(w);
+ }
+
+ if (!w->thread->isRunning())
+ return QImage();
+
+ if (!window->handle())
+ window->create();
+
+ QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window);
+ wd->polishItems();
+
+ QImage result;
+ w->thread->mutex.lock();
+ lockedForSync = true;
+ w->thread->postEvent(new QSGD3D12GrabEvent(window, &result));
+ w->thread->waitCondition.wait(&w->thread->mutex);
+ lockedForSync = false;
+ w->thread->mutex.unlock();
+
+ result.setDevicePixelRatio(window->effectiveDevicePixelRatio());
+
+ if (tempExpose)
+ handleObscurity(w);
+
+ return result;
+}
+
+void QSGD3D12RenderLoop::update(QQuickWindow *window)
+{
+ WindowData *w = windowFor(windows, window);
+ if (!w)
+ return;
+
+ if (w->thread == QThread::currentThread()) {
+ w->thread->requestRepaint();
+ return;
+ }
+
+ // We set forceRenderPass because we want to make sure the QQuickWindow
+ // actually does a full render pass after the next sync.
+ w->forceRenderPass = true;
+ scheduleUpdate(w);
+}
+
+void QSGD3D12RenderLoop::maybeUpdate(QQuickWindow *window)
+{
+ WindowData *w = windowFor(windows, window);
+ if (w)
+ scheduleUpdate(w);
+}
+
+// called in response to window->requestUpdate()
+void QSGD3D12RenderLoop::handleUpdateRequest(QQuickWindow *window)
+{
+ if (Q_UNLIKELY(debug_loop()))
+ qDebug() << "handleUpdateRequest" << window;
+
+ WindowData *w = windowFor(windows, window);
+ if (w)
+ polishAndSync(w, false);
+}
+
+QAnimationDriver *QSGD3D12RenderLoop::animationDriver() const
+{
+ return anim;
+}
+
+QSGContext *QSGD3D12RenderLoop::sceneGraphContext() const
+{
+ return sg;
+}
+
+QSGRenderContext *QSGD3D12RenderLoop::createRenderContext(QSGContext *) const
+{
+ return sg->createRenderContext();
+}
+
+void QSGD3D12RenderLoop::releaseResources(QQuickWindow *window)
+{
+ if (Q_UNLIKELY(debug_loop()))
+ qDebug() << "releaseResources" << window;
+
+ WindowData *w = windowFor(windows, window);
+ if (w)
+ handleResourceRelease(w, false);
+}
+
+void QSGD3D12RenderLoop::postJob(QQuickWindow *window, QRunnable *job)
+{
+ WindowData *w = windowFor(windows, window);
+ if (w && w->thread && w->thread->exposedWindow)
+ w->thread->postEvent(new QSGD3D12JobEvent(window, job));
+ else
+ delete job;
+}
+
+QSurface::SurfaceType QSGD3D12RenderLoop::windowSurfaceType() const
+{
+ return QSurface::OpenGLSurface;
+}
+
+bool QSGD3D12RenderLoop::interleaveIncubation() const
+{
+ bool somethingVisible = false;
+ for (const WindowData &w : windows) {
+ if (w.window->isVisible() && w.window->isExposed()) {
+ somethingVisible = true;
+ break;
+ }
+ }
+ return somethingVisible && anim->isRunning();
+}
+
+int QSGD3D12RenderLoop::flags() const
+{
+ return SupportsGrabWithoutExpose;
+}
+
+bool QSGD3D12RenderLoop::event(QEvent *e)
+{
+ if (e->type() == QEvent::Timer) {
+ QTimerEvent *te = static_cast<QTimerEvent *>(e);
+ if (te->timerId() == animationTimer) {
+ anim->advance();
+ emit timeToIncubate();
+ return true;
+ }
+ }
+
+ return QObject::event(e);
+}
+
+void QSGD3D12RenderLoop::onAnimationStarted()
+{
+ startOrStopAnimationTimer();
+
+ for (const WindowData &w : qAsConst(windows))
+ w.window->requestUpdate();
+}
+
+void QSGD3D12RenderLoop::onAnimationStopped()
+{
+ startOrStopAnimationTimer();
+}
+
+void QSGD3D12RenderLoop::startOrStopAnimationTimer()
+{
+ int exposedWindowCount = 0;
+ const WindowData *exposed = nullptr;
+
+ for (int i = 0; i < windows.size(); ++i) {
+ const WindowData &w(windows[i]);
+ if (w.window->isVisible() && w.window->isExposed()) {
+ ++exposedWindowCount;
+ exposed = &w;
+ }
+ }
+
+ if (animationTimer && (exposedWindowCount == 1 || !anim->isRunning())) {
+ killTimer(animationTimer);
+ animationTimer = 0;
+ // If animations are running, make sure we keep on animating
+ if (anim->isRunning())
+ exposed->window->requestUpdate();
+ } else if (!animationTimer && exposedWindowCount != 1 && anim->isRunning()) {
+ animationTimer = startTimer(qsgrl_animation_interval());
+ }
+}
+
+void QSGD3D12RenderLoop::handleExposure(QQuickWindow *window)
+{
+ if (Q_UNLIKELY(debug_loop()))
+ qDebug() << "handleExposure" << window;
+
+ WindowData *w = windowFor(windows, window);
+ if (!w) {
+ if (Q_UNLIKELY(debug_loop()))
+ qDebug("adding window to list");
+ WindowData win;
+ win.window = window;
+ QSGRenderContext *rc = QQuickWindowPrivate::get(window)->context; // will transfer ownership
+ win.thread = new QSGD3D12RenderThread(this, rc);
+ win.updateDuringSync = false;
+ win.forceRenderPass = true; // also covered by polishAndSync(inExpose=true), but doesn't hurt
+ windows.append(win);
+ w = &windows.last();
+ }
+
+ // set this early as we'll be rendering shortly anyway and this avoids
+ // special casing exposure in polishAndSync.
+ w->thread->exposedWindow = window;
+
+ if (w->window->size().isEmpty()
+ || (w->window->isTopLevel() && !w->window->geometry().intersects(w->window->screen()->availableGeometry()))) {
+#ifndef QT_NO_DEBUG
+ qWarning().noquote().nospace() << "QSGD3D12RenderLoop: expose event received for window "
+ << w->window << " with invalid geometry: " << w->window->geometry()
+ << " on " << w->window->screen();
+#endif
+ }
+
+ if (!w->window->handle())
+ w->window->create();
+
+ // Start render thread if it is not running
+ if (!w->thread->isRunning()) {
+ if (Q_UNLIKELY(debug_loop()))
+ qDebug("starting render thread");
+ // Push a few things to the render thread.
+ QQuickAnimatorController *controller = QQuickWindowPrivate::get(w->window)->animationController;
+ if (controller->thread() != w->thread)
+ controller->moveToThread(w->thread);
+ if (w->thread->thread() == QThread::currentThread()) {
+ w->thread->rc->moveToThread(w->thread);
+ w->thread->moveToThread(w->thread);
+ }
+
+ w->thread->active = true;
+ w->thread->start();
+
+ if (!w->thread->isRunning())
+ qFatal("Render thread failed to start, aborting application.");
+ }
+
+ polishAndSync(w, true);
+
+ startOrStopAnimationTimer();
+}
+
+void QSGD3D12RenderLoop::handleObscurity(WindowData *w)
+{
+ if (Q_UNLIKELY(debug_loop()))
+ qDebug() << "handleObscurity" << w->window;
+
+ if (w->thread->isRunning()) {
+ w->thread->mutex.lock();
+ w->thread->postEvent(new QSGD3D12WindowEvent(w->window, WM_Obscure));
+ w->thread->waitCondition.wait(&w->thread->mutex);
+ w->thread->mutex.unlock();
+ }
+
+ startOrStopAnimationTimer();
+}
+
+void QSGD3D12RenderLoop::scheduleUpdate(WindowData *w)
+{
+ if (!QCoreApplication::instance())
+ return;
+
+ if (!w || !w->thread->isRunning())
+ return;
+
+ QThread *current = QThread::currentThread();
+ if (current != QCoreApplication::instance()->thread() && (current != w->thread || !lockedForSync)) {
+ qWarning() << "Updates can only be scheduled from GUI thread or from QQuickItem::updatePaintNode()";
+ return;
+ }
+
+ if (current == w->thread) {
+ w->updateDuringSync = true;
+ return;
+ }
+
+ w->window->requestUpdate();
+}
+
+void QSGD3D12RenderLoop::handleResourceRelease(WindowData *w, bool destroying)
+{
+ if (Q_UNLIKELY(debug_loop()))
+ qDebug() << "handleResourceRelease" << (destroying ? "destroying" : "hide/releaseResources") << w->window;
+
+ w->thread->mutex.lock();
+ if (w->thread->isRunning() && w->thread->active) {
+ QQuickWindow *window = w->window;
+
+ // Note that window->handle() is typically null by this time because
+ // the platform window is already destroyed. This should not be a
+ // problem for the D3D cleanup.
+
+ w->thread->postEvent(new QSGD3D12TryReleaseEvent(window, destroying));
+ w->thread->waitCondition.wait(&w->thread->mutex);
+
+ // Avoid a shutdown race condition.
+ // If SG is invalidated and 'active' becomes false, the thread's run()
+ // method will exit. handleExposure() relies on QThread::isRunning() (because it
+ // potentially needs to start the thread again) and our mutex cannot be used to
+ // track the thread stopping, so we wait a few nanoseconds extra so the thread
+ // can exit properly.
+ if (!w->thread->active)
+ w->thread->wait();
+ }
+ w->thread->mutex.unlock();
+}
+
+void QSGD3D12RenderLoop::polishAndSync(WindowData *w, bool inExpose)
+{
+ if (Q_UNLIKELY(debug_loop()))
+ qDebug() << "polishAndSync" << (inExpose ? "(in expose)" : "(normal)") << w->window;
+
+ QQuickWindow *window = w->window;
+ if (!w->thread || !w->thread->exposedWindow) {
+ if (Q_UNLIKELY(debug_loop()))
+ qDebug("polishAndSync - not exposed, abort");
+ return;
+ }
+
+ // Flush pending touch events.
+ QQuickWindowPrivate::get(window)->flushDelayedTouchEvent();
+ // The delivery of the event might have caused the window to stop rendering
+ w = windowFor(windows, window);
+ if (!w || !w->thread || !w->thread->exposedWindow) {
+ if (Q_UNLIKELY(debug_loop()))
+ qDebug("polishAndSync - removed after touch event flushing, abort");
+ return;
+ }
+
+ QElapsedTimer timer;
+ qint64 polishTime = 0;
+ qint64 waitTime = 0;
+ qint64 syncTime = 0;
+ if (Q_UNLIKELY(debug_time()))
+ timer.start();
+ Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphPolishAndSync);
+
+ QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window);
+ wd->polishItems();
+
+ if (Q_UNLIKELY(debug_time()))
+ polishTime = timer.nsecsElapsed();
+ Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphPolishAndSync);
+
+ w->updateDuringSync = false;
+
+ emit window->afterAnimating();
+
+ if (Q_UNLIKELY(debug_loop()))
+ qDebug("polishAndSync - lock for sync");
+ w->thread->mutex.lock();
+ lockedForSync = true;
+ w->thread->postEvent(new QSGD3D12SyncEvent(window, inExpose, w->forceRenderPass));
+ w->forceRenderPass = false;
+
+ if (Q_UNLIKELY(debug_loop()))
+ qDebug("polishAndSync - wait for sync");
+ if (Q_UNLIKELY(debug_time()))
+ waitTime = timer.nsecsElapsed();
+ Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphPolishAndSync);
+ w->thread->waitCondition.wait(&w->thread->mutex);
+ lockedForSync = false;
+ w->thread->mutex.unlock();
+ if (Q_UNLIKELY(debug_loop()))
+ qDebug("polishAndSync - unlock after sync");
+
+ if (Q_UNLIKELY(debug_time()))
+ syncTime = timer.nsecsElapsed();
+ Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphPolishAndSync);
+
+ if (!animationTimer && anim->isRunning()) {
+ if (Q_UNLIKELY(debug_loop()))
+ qDebug("polishAndSync - advancing animations");
+ anim->advance();
+ // We need to trigger another sync to keep animations running...
+ w->window->requestUpdate();
+ emit timeToIncubate();
+ } else if (w->updateDuringSync) {
+ w->window->requestUpdate();
+ }
+
+ if (Q_UNLIKELY(debug_time()))
+ qDebug().nospace()
+ << "Frame prepared with 'd3d12' renderloop"
+ << ", polish=" << (polishTime / 1000000)
+ << ", lock=" << (waitTime - polishTime) / 1000000
+ << ", blockedForSync=" << (syncTime - waitTime) / 1000000
+ << ", animations=" << (timer.nsecsElapsed() - syncTime) / 1000000
+ << " - (on gui thread) " << window;
+
+ Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphPolishAndSync);
+}
+
+#include "qsgd3d12renderloop.moc"
+
+QT_END_NAMESPACE
diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12renderloop_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12renderloop_p.h
new file mode 100644
index 0000000000..732f8dd5d2
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12renderloop_p.h
@@ -0,0 +1,129 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGD3D12RENDERLOOP_P_H
+#define QSGD3D12RENDERLOOP_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qsgrenderloop_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QSGD3D12Engine;
+class QSGD3D12Context;
+class QSGD3D12RenderContext;
+class QSGD3D12RenderThread;
+
+class QSGD3D12RenderLoop : public QSGRenderLoop
+{
+ Q_OBJECT
+
+public:
+ QSGD3D12RenderLoop();
+ ~QSGD3D12RenderLoop();
+
+ void show(QQuickWindow *window) override;
+ void hide(QQuickWindow *window) override;
+ void resize(QQuickWindow *window) override;
+
+ void windowDestroyed(QQuickWindow *window) override;
+
+ void exposureChanged(QQuickWindow *window) override;
+
+ QImage grab(QQuickWindow *window) override;
+
+ void update(QQuickWindow *window) override;
+ void maybeUpdate(QQuickWindow *window) override;
+ void handleUpdateRequest(QQuickWindow *window) override;
+
+ QAnimationDriver *animationDriver() const override;
+
+ QSGContext *sceneGraphContext() const override;
+ QSGRenderContext *createRenderContext(QSGContext *) const override;
+
+ void releaseResources(QQuickWindow *window) override;
+ void postJob(QQuickWindow *window, QRunnable *job) override;
+
+ QSurface::SurfaceType windowSurfaceType() const override;
+ bool interleaveIncubation() const override;
+ int flags() const override;
+
+ bool event(QEvent *e) override;
+
+public Q_SLOTS:
+ void onAnimationStarted();
+ void onAnimationStopped();
+
+private:
+ struct WindowData {
+ QQuickWindow *window;
+ QSGD3D12RenderThread *thread;
+ uint updateDuringSync : 1;
+ uint forceRenderPass : 1;
+ };
+
+ void startOrStopAnimationTimer();
+ void handleExposure(QQuickWindow *window);
+ void handleObscurity(WindowData *w);
+ void scheduleUpdate(WindowData *w);
+ void handleResourceRelease(WindowData *w, bool destroying);
+ void polishAndSync(WindowData *w, bool inExpose);
+
+ QSGD3D12Context *sg;
+ QAnimationDriver *anim;
+ int animationTimer = 0;
+ bool lockedForSync = false;
+ QVector<WindowData> windows;
+
+ friend class QSGD3D12RenderThread;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGD3D12RENDERLOOP_P_H
diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode.cpp
new file mode 100644
index 0000000000..b4288c2ef5
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode.cpp
@@ -0,0 +1,977 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgd3d12shadereffectnode_p.h"
+#include "qsgd3d12rendercontext_p.h"
+#include "qsgd3d12texture_p.h"
+#include "qsgd3d12engine_p.h"
+#include <QtCore/qfile.h>
+#include <QtQml/qqmlfile.h>
+#include <qsgtextureprovider.h>
+
+#include <d3d12shader.h>
+#include <d3dcompiler.h>
+
+#include "vs_shadereffectdefault.hlslh"
+#include "ps_shadereffectdefault.hlslh"
+
+QT_BEGIN_NAMESPACE
+
+// NOTE: Avoid categorized logging. It is slow.
+
+#define DECLARE_DEBUG_VAR(variable) \
+ static bool debug_ ## variable() \
+ { static bool value = qgetenv("QSG_RENDERER_DEBUG").contains(QT_STRINGIFY(variable)); return value; }
+
+DECLARE_DEBUG_VAR(render)
+
+void QSGD3D12ShaderLinker::reset(const QByteArray &vertBlob, const QByteArray &fragBlob)
+{
+ Q_ASSERT(!vertBlob.isEmpty() && !fragBlob.isEmpty());
+ vs = vertBlob;
+ fs = fragBlob;
+
+ error = false;
+
+ constantBufferSize = 0;
+ constants.clear();
+ samplers.clear();
+ textures.clear();
+ textureNameMap.clear();
+}
+
+void QSGD3D12ShaderLinker::feedVertexInput(const QSGShaderEffectNode::ShaderData &shader)
+{
+ bool foundPos = false, foundTexCoord = false;
+
+ for (const auto &ip : qAsConst(shader.shaderInfo.inputParameters)) {
+ if (ip.semanticName == QByteArrayLiteral("POSITION"))
+ foundPos = true;
+ else if (ip.semanticName == QByteArrayLiteral("TEXCOORD"))
+ foundTexCoord = true;
+ }
+
+ if (!foundPos) {
+ qWarning("ShaderEffect: No POSITION input found.");
+ error = true;
+ }
+ if (!foundTexCoord) {
+ qWarning("ShaderEffect: No TEXCOORD input found.");
+ error = true;
+ }
+
+ // Nothing else to do here, the QSGGeometry::AttributeSet decides anyway
+ // and that is already generated by QQuickShaderEffectMesh via
+ // QSGGeometry::defaultAttributes_TexturedPoint2D() and has the semantics
+ // so it will just work.
+}
+
+void QSGD3D12ShaderLinker::feedConstants(const QSGShaderEffectNode::ShaderData &shader, const QSet<int> *dirtyIndices)
+{
+ Q_ASSERT(shader.shaderInfo.variables.count() == shader.varData.count());
+ if (!dirtyIndices) {
+ constantBufferSize = qMax(constantBufferSize, shader.shaderInfo.constantDataSize);
+ for (int i = 0; i < shader.shaderInfo.variables.count(); ++i) {
+ const auto &var(shader.shaderInfo.variables.at(i));
+ if (var.type == QSGGuiThreadShaderEffectManager::ShaderInfo::Constant) {
+ const auto &vd(shader.varData.at(i));
+ Constant c;
+ c.size = var.size;
+ c.specialType = vd.specialType;
+ if (c.specialType != QSGShaderEffectNode::VariableData::SubRect) {
+ c.value = vd.value;
+ } else {
+ Q_ASSERT(var.name.startsWith(QByteArrayLiteral("qt_SubRect_")));
+ c.value = var.name.mid(11);
+ }
+ constants[var.offset] = c;
+ }
+ }
+ } else {
+ for (int idx : *dirtyIndices)
+ constants[shader.shaderInfo.variables.at(idx).offset].value = shader.varData.at(idx).value;
+ }
+}
+
+void QSGD3D12ShaderLinker::feedSamplers(const QSGShaderEffectNode::ShaderData &shader)
+{
+ for (int i = 0; i < shader.shaderInfo.variables.count(); ++i) {
+ const auto &var(shader.shaderInfo.variables.at(i));
+ if (var.type == QSGGuiThreadShaderEffectManager::ShaderInfo::Sampler) {
+ const auto &vd(shader.varData.at(i));
+ Q_ASSERT(vd.specialType == QSGShaderEffectNode::VariableData::Unused);
+ samplers.insert(var.bindPoint);
+ }
+ }
+}
+
+void QSGD3D12ShaderLinker::feedTextures(const QSGShaderEffectNode::ShaderData &shader, const QSet<int> *dirtyIndices)
+{
+ if (!dirtyIndices) {
+ for (int i = 0; i < shader.shaderInfo.variables.count(); ++i) {
+ const auto &var(shader.shaderInfo.variables.at(i));
+ const auto &vd(shader.varData.at(i));
+ if (var.type == QSGGuiThreadShaderEffectManager::ShaderInfo::Texture) {
+ Q_ASSERT(vd.specialType == QSGShaderEffectNode::VariableData::Source);
+ textures.insert(var.bindPoint, vd.value);
+ textureNameMap.insert(var.name, var.bindPoint);
+ }
+ }
+ } else {
+ for (int idx : *dirtyIndices) {
+ const auto &var(shader.shaderInfo.variables.at(idx));
+ const auto &vd(shader.varData.at(idx));
+ textures.insert(var.bindPoint, vd.value);
+ textureNameMap.insert(var.name, var.bindPoint);
+ }
+ }
+}
+
+void QSGD3D12ShaderLinker::linkTextureSubRects()
+{
+ // feedConstants stores <name> in Constant::value for subrect entries. Now
+ // that both constants and textures are known, replace the name with the
+ // texture bind point.
+ for (Constant &c : constants) {
+ if (c.specialType == QSGShaderEffectNode::VariableData::SubRect) {
+ if (c.value.type() == QMetaType::QByteArray) {
+ const QByteArray name = c.value.toByteArray();
+ if (!textureNameMap.contains(name))
+ qWarning("ShaderEffect: qt_SubRect_%s refers to unknown source texture", qPrintable(name));
+ c.value = textureNameMap[name];
+ }
+ }
+ }
+}
+
+void QSGD3D12ShaderLinker::dump()
+{
+ if (error) {
+ qDebug() << "Failed to generate program data";
+ return;
+ }
+ qDebug() << "Combined shader data" << vs.size() << fs.size() << "cbuffer size" << constantBufferSize;
+ qDebug() << " - constants" << constants;
+ qDebug() << " - samplers" << samplers;
+ qDebug() << " - textures" << textures;
+}
+
+QDebug operator<<(QDebug debug, const QSGD3D12ShaderLinker::Constant &c)
+{
+ QDebugStateSaver saver(debug);
+ debug.space();
+ debug << "size" << c.size;
+ if (c.specialType != QSGShaderEffectNode::VariableData::None)
+ debug << "special" << c.specialType;
+ else
+ debug << "value" << c.value;
+ return debug;
+}
+
+QSGD3D12ShaderEffectMaterial::QSGD3D12ShaderEffectMaterial(QSGD3D12ShaderEffectNode *node)
+ : node(node)
+{
+ setFlag(Blending | RequiresFullMatrix, true); // may be changed in sync()
+}
+
+QSGD3D12ShaderEffectMaterial::~QSGD3D12ShaderEffectMaterial()
+{
+ delete dummy;
+}
+
+struct QSGD3D12ShaderMaterialTypeCache
+{
+ QSGMaterialType *get(const QByteArray &vs, const QByteArray &fs);
+ void reset() { qDeleteAll(m_types); m_types.clear(); }
+
+ struct Key {
+ QByteArray blob[2];
+ Key() { }
+ Key(const QByteArray &vs, const QByteArray &fs) { blob[0] = vs; blob[1] = fs; }
+ bool operator==(const Key &other) const {
+ return blob[0] == other.blob[0] && blob[1] == other.blob[1];
+ }
+ };
+ QHash<Key, QSGMaterialType *> m_types;
+};
+
+uint qHash(const QSGD3D12ShaderMaterialTypeCache::Key &key, uint seed = 0)
+{
+ uint hash = seed;
+ for (int i = 0; i < 2; ++i)
+ hash = hash * 31337 + qHash(key.blob[i]);
+ return hash;
+}
+
+QSGMaterialType *QSGD3D12ShaderMaterialTypeCache::get(const QByteArray &vs, const QByteArray &fs)
+{
+ const Key k(vs, fs);
+ if (m_types.contains(k))
+ return m_types.value(k);
+
+ QSGMaterialType *t = new QSGMaterialType;
+ m_types.insert(k, t);
+ return t;
+}
+
+Q_GLOBAL_STATIC(QSGD3D12ShaderMaterialTypeCache, shaderMaterialTypeCache)
+
+void QSGD3D12ShaderEffectNode::cleanupMaterialTypeCache()
+{
+ shaderMaterialTypeCache()->reset();
+}
+
+QSGMaterialType *QSGD3D12ShaderEffectMaterial::type() const
+{
+ return mtype;
+}
+
+static bool hasAtlasTexture(const QVector<QSGTextureProvider *> &textureProviders)
+{
+ for (int i = 0; i < textureProviders.count(); ++i) {
+ QSGTextureProvider *t = textureProviders.at(i);
+ if (t && t->texture() && t->texture()->isAtlasTexture())
+ return true;
+ }
+ return false;
+}
+
+int QSGD3D12ShaderEffectMaterial::compare(const QSGMaterial *other) const
+{
+ Q_ASSERT(other && type() == other->type());
+ const QSGD3D12ShaderEffectMaterial *o = static_cast<const QSGD3D12ShaderEffectMaterial *>(other);
+
+ if (int diff = cullMode - o->cullMode)
+ return diff;
+
+ if (int diff = textureProviders.count() - o->textureProviders.count())
+ return diff;
+
+ if (linker.constants != o->linker.constants)
+ return 1;
+
+ if ((hasAtlasTexture(textureProviders) && !geometryUsesTextureSubRect)
+ || (hasAtlasTexture(o->textureProviders) && !o->geometryUsesTextureSubRect))
+ return 1;
+
+ for (int i = 0; i < textureProviders.count(); ++i) {
+ QSGTextureProvider *tp1 = textureProviders.at(i);
+ QSGTextureProvider *tp2 = o->textureProviders.at(i);
+ if (!tp1 || !tp2)
+ return tp1 == tp2 ? 0 : 1;
+ QSGTexture *t1 = tp1->texture();
+ QSGTexture *t2 = tp2->texture();
+ if (!t1 || !t2)
+ return t1 == t2 ? 0 : 1;
+ if (int diff = t1->textureId() - t2->textureId())
+ return diff;
+ }
+
+ return 0;
+}
+
+int QSGD3D12ShaderEffectMaterial::constantBufferSize() const
+{
+ return QSGD3D12Engine::alignedConstantBufferSize(linker.constantBufferSize);
+}
+
+void QSGD3D12ShaderEffectMaterial::preparePipeline(QSGD3D12PipelineState *pipelineState)
+{
+ pipelineState->shaders.vs = reinterpret_cast<const quint8 *>(linker.vs.constData());
+ pipelineState->shaders.vsSize = linker.vs.size();
+ pipelineState->shaders.ps = reinterpret_cast<const quint8 *>(linker.fs.constData());
+ pipelineState->shaders.psSize = linker.fs.size();
+
+ pipelineState->shaders.rootSig.textureViews.resize(textureProviders.count());
+}
+
+static inline QColor qsg_premultiply_color(const QColor &c)
+{
+ return QColor::fromRgbF(c.redF() * c.alphaF(), c.greenF() * c.alphaF(), c.blueF() * c.alphaF(), c.alphaF());
+}
+
+QSGD3D12Material::UpdateResults QSGD3D12ShaderEffectMaterial::updatePipeline(const RenderState &state,
+ QSGD3D12PipelineState *pipelineState,
+ ExtraState *,
+ quint8 *constantBuffer)
+{
+ QSGD3D12Material::UpdateResults r = 0;
+ quint8 *p = constantBuffer;
+
+ for (auto it = linker.constants.constBegin(), itEnd = linker.constants.constEnd(); it != itEnd; ++it) {
+ quint8 *dst = p + it.key();
+ const QSGD3D12ShaderLinker::Constant &c(it.value());
+ if (c.specialType == QSGShaderEffectNode::VariableData::Opacity) {
+ if (state.isOpacityDirty()) {
+ const float f = state.opacity();
+ Q_ASSERT(sizeof(f) == c.size);
+ memcpy(dst, &f, sizeof(f));
+ r |= UpdatedConstantBuffer;
+ }
+ } else if (c.specialType == QSGShaderEffectNode::VariableData::Matrix) {
+ if (state.isMatrixDirty()) {
+ const int sz = 16 * sizeof(float);
+ Q_ASSERT(sz == c.size);
+ memcpy(dst, state.combinedMatrix().constData(), sz);
+ r |= UpdatedConstantBuffer;
+ }
+ } else if (c.specialType == QSGShaderEffectNode::VariableData::SubRect) {
+ // float4
+ QRectF subRect(0, 0, 1, 1);
+ int srcBindPoint = c.value.toInt(); // filled in by linkTextureSubRects
+ if (QSGTexture *t = textureProviders.at(srcBindPoint)->texture())
+ subRect = t->normalizedTextureSubRect();
+ const float f[4] = { float(subRect.x()), float(subRect.y()),
+ float(subRect.width()), float(subRect.height()) };
+ Q_ASSERT(sizeof(f) == c.size);
+ memcpy(dst, f, sizeof(f));
+ } else if (c.specialType == QSGShaderEffectNode::VariableData::None) {
+ r |= UpdatedConstantBuffer;
+ switch (c.value.type()) {
+ case QMetaType::QColor: {
+ const QColor v = qsg_premultiply_color(qvariant_cast<QColor>(c.value));
+ const float f[4] = { float(v.redF()), float(v.greenF()), float(v.blueF()), float(v.alphaF()) };
+ Q_ASSERT(sizeof(f) == c.size);
+ memcpy(dst, f, sizeof(f));
+ break;
+ }
+ case QMetaType::Float: {
+ const float f = qvariant_cast<float>(c.value);
+ Q_ASSERT(sizeof(f) == c.size);
+ memcpy(dst, &f, sizeof(f));
+ break;
+ }
+ case QMetaType::Double: {
+ const float f = float(qvariant_cast<double>(c.value));
+ Q_ASSERT(sizeof(f) == c.size);
+ memcpy(dst, &f, sizeof(f));
+ break;
+ }
+ case QMetaType::Int: {
+ const int i = c.value.toInt();
+ Q_ASSERT(sizeof(i) == c.size);
+ memcpy(dst, &i, sizeof(i));
+ break;
+ }
+ case QMetaType::Bool: {
+ const bool b = c.value.toBool();
+ Q_ASSERT(sizeof(b) == c.size);
+ memcpy(dst, &b, sizeof(b));
+ break;
+ }
+ case QMetaType::QTransform: { // float3x3
+ const QTransform v = qvariant_cast<QTransform>(c.value);
+ const float m[3][3] = {
+ { float(v.m11()), float(v.m12()), float(v.m13()) },
+ { float(v.m21()), float(v.m22()), float(v.m23()) },
+ { float(v.m31()), float(v.m32()), float(v.m33()) }
+ };
+ Q_ASSERT(sizeof(m) == c.size);
+ memcpy(dst, m[0], sizeof(m));
+ break;
+ }
+ case QMetaType::QSize:
+ case QMetaType::QSizeF: { // float2
+ const QSizeF v = c.value.toSizeF();
+ const float f[2] = { float(v.width()), float(v.height()) };
+ Q_ASSERT(sizeof(f) == c.size);
+ memcpy(dst, f, sizeof(f));
+ break;
+ }
+ case QMetaType::QPoint:
+ case QMetaType::QPointF: { // float2
+ const QPointF v = c.value.toPointF();
+ const float f[2] = { float(v.x()), float(v.y()) };
+ Q_ASSERT(sizeof(f) == c.size);
+ memcpy(dst, f, sizeof(f));
+ break;
+ }
+ case QMetaType::QRect:
+ case QMetaType::QRectF: { // float4
+ const QRectF v = c.value.toRectF();
+ const float f[4] = { float(v.x()), float(v.y()), float(v.width()), float(v.height()) };
+ Q_ASSERT(sizeof(f) == c.size);
+ memcpy(dst, f, sizeof(f));
+ break;
+ }
+ case QMetaType::QVector2D: { // float2
+ const QVector2D v = qvariant_cast<QVector2D>(c.value);
+ const float f[2] = { float(v.x()), float(v.y()) };
+ Q_ASSERT(sizeof(f) == c.size);
+ memcpy(dst, f, sizeof(f));
+ break;
+ }
+ case QMetaType::QVector3D: { // float3
+ const QVector3D v = qvariant_cast<QVector3D>(c.value);
+ const float f[3] = { float(v.x()), float(v.y()), float(v.z()) };
+ Q_ASSERT(sizeof(f) == c.size);
+ memcpy(dst, f, sizeof(f));
+ break;
+ }
+ case QMetaType::QVector4D: { // float4
+ const QVector4D v = qvariant_cast<QVector4D>(c.value);
+ const float f[4] = { float(v.x()), float(v.y()), float(v.z()), float(v.w()) };
+ Q_ASSERT(sizeof(f) == c.size);
+ memcpy(dst, f, sizeof(f));
+ break;
+ }
+ case QMetaType::QQuaternion: { // float4
+ const QQuaternion v = qvariant_cast<QQuaternion>(c.value);
+ const float f[4] = { float(v.x()), float(v.y()), float(v.z()), float(v.scalar()) };
+ Q_ASSERT(sizeof(f) == c.size);
+ memcpy(dst, f, sizeof(f));
+ break;
+ }
+ case QMetaType::QMatrix4x4: { // float4x4
+ const QMatrix4x4 v = qvariant_cast<QMatrix4x4>(c.value);
+ const int sz = 16 * sizeof(float);
+ Q_ASSERT(sz == c.size);
+ memcpy(dst, v.constData(), sz);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ }
+
+ for (int i = 0; i < textureProviders.count(); ++i) {
+ QSGTextureProvider *tp = textureProviders[i];
+ QSGD3D12TextureView &tv(pipelineState->shaders.rootSig.textureViews[i]);
+ if (tp) {
+ if (QSGTexture *t = tp->texture()) {
+ if (t->isAtlasTexture() && !geometryUsesTextureSubRect) {
+ QSGTexture *newTexture = t->removedFromAtlas();
+ if (newTexture)
+ t = newTexture;
+ }
+ tv.filter = t->filtering() == QSGTexture::Linear
+ ? QSGD3D12TextureView::FilterLinear : QSGD3D12TextureView::FilterNearest;
+ tv.addressModeHoriz = t->horizontalWrapMode() == QSGTexture::ClampToEdge
+ ? QSGD3D12TextureView::AddressClamp : QSGD3D12TextureView::AddressWrap;
+ tv.addressModeVert = t->verticalWrapMode() == QSGTexture::ClampToEdge
+ ? QSGD3D12TextureView::AddressClamp : QSGD3D12TextureView::AddressWrap;
+ t->bind();
+ continue;
+ }
+ }
+ if (!dummy) {
+ dummy = new QSGD3D12Texture(node->renderContext()->engine());
+ QImage img(128, 128, QImage::Format_ARGB32_Premultiplied);
+ img.fill(0);
+ dummy->create(img, QSGRenderContext::CreateTexture_Alpha);
+ }
+ tv.filter = QSGD3D12TextureView::FilterNearest;
+ tv.addressModeHoriz = QSGD3D12TextureView::AddressWrap;
+ tv.addressModeVert = QSGD3D12TextureView::AddressWrap;
+ dummy->bind();
+ }
+
+ switch (cullMode) {
+ case QSGShaderEffectNode::FrontFaceCulling:
+ pipelineState->cullMode = QSGD3D12PipelineState::CullFront;
+ break;
+ case QSGShaderEffectNode::BackFaceCulling:
+ pipelineState->cullMode = QSGD3D12PipelineState::CullBack;
+ break;
+ default:
+ pipelineState->cullMode = QSGD3D12PipelineState::CullNone;
+ break;
+ }
+
+ return r;
+}
+
+void QSGD3D12ShaderEffectMaterial::updateTextureProviders(bool layoutChange)
+{
+ if (layoutChange) {
+ for (QSGTextureProvider *tp : textureProviders) {
+ if (tp) {
+ QObject::disconnect(tp, SIGNAL(textureChanged()), node,
+ SLOT(handleTextureChange()));
+ QObject::disconnect(tp, SIGNAL(destroyed(QObject*)), node,
+ SLOT(handleTextureProviderDestroyed(QObject*)));
+ }
+ }
+
+ textureProviders.fill(nullptr, linker.textures.count());
+ }
+
+ for (auto it = linker.textures.constBegin(), itEnd = linker.textures.constEnd(); it != itEnd; ++it) {
+ const int bindPoint = it.key();
+ // Now that the linker has merged the textures, we can switch over to a
+ // simple vector indexed by the binding point for textureProviders.
+ Q_ASSERT(bindPoint >= 0 && bindPoint < textureProviders.count());
+ QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(it.value()));
+ QSGTextureProvider *newProvider = source && source->isTextureProvider() ? source->textureProvider() : nullptr;
+ QSGTextureProvider *&activeProvider(textureProviders[bindPoint]);
+ if (newProvider != activeProvider) {
+ if (activeProvider) {
+ QObject::disconnect(activeProvider, SIGNAL(textureChanged()), node,
+ SLOT(handleTextureChange()));
+ QObject::disconnect(activeProvider, SIGNAL(destroyed(QObject*)), node,
+ SLOT(handleTextureProviderDestroyed(QObject*)));
+ }
+ if (newProvider) {
+ Q_ASSERT_X(newProvider->thread() == QThread::currentThread(),
+ "QSGD3D12ShaderEffectMaterial::updateTextureProviders",
+ "Texture provider must belong to the rendering thread");
+ QObject::connect(newProvider, SIGNAL(textureChanged()), node, SLOT(handleTextureChange()));
+ QObject::connect(newProvider, SIGNAL(destroyed(QObject*)), node,
+ SLOT(handleTextureProviderDestroyed(QObject*)));
+ } else {
+ const char *typeName = source ? source->metaObject()->className() : it.value().typeName();
+ qWarning("ShaderEffect: Texture t%d is not assigned a valid texture provider (%s).",
+ bindPoint, typeName);
+ }
+ activeProvider = newProvider;
+ }
+ }
+}
+
+QSGD3D12ShaderEffectNode::QSGD3D12ShaderEffectNode(QSGD3D12RenderContext *rc, QSGD3D12GuiThreadShaderEffectManager *mgr)
+ : QSGShaderEffectNode(mgr),
+ m_rc(rc),
+ m_mgr(mgr),
+ m_material(this)
+{
+ setFlag(UsePreprocess, true);
+ setMaterial(&m_material);
+}
+
+QRectF QSGD3D12ShaderEffectNode::updateNormalizedTextureSubRect(bool supportsAtlasTextures)
+{
+ QRectF srcRect(0, 0, 1, 1);
+ bool geometryUsesTextureSubRect = false;
+ if (supportsAtlasTextures && m_material.textureProviders.count() == 1) {
+ QSGTextureProvider *provider = m_material.textureProviders.at(0);
+ if (provider->texture()) {
+ srcRect = provider->texture()->normalizedTextureSubRect();
+ geometryUsesTextureSubRect = true;
+ }
+ }
+
+ if (m_material.geometryUsesTextureSubRect != geometryUsesTextureSubRect) {
+ m_material.geometryUsesTextureSubRect = geometryUsesTextureSubRect;
+ markDirty(QSGNode::DirtyMaterial);
+ }
+
+ return srcRect;
+}
+
+void QSGD3D12ShaderEffectNode::syncMaterial(SyncData *syncData)
+{
+ if (Q_UNLIKELY(debug_render()))
+ qDebug() << "shadereffect node sync" << syncData->dirty;
+
+ if (bool(m_material.flags() & QSGMaterial::Blending) != syncData->blending) {
+ m_material.setFlag(QSGMaterial::Blending, syncData->blending);
+ markDirty(QSGNode::DirtyMaterial);
+ }
+
+ if (m_material.cullMode != syncData->cullMode) {
+ m_material.cullMode = syncData->cullMode;
+ markDirty(QSGNode::DirtyMaterial);
+ }
+
+ if (syncData->dirty & QSGShaderEffectNode::DirtyShaders) {
+ QByteArray vertBlob, fragBlob;
+
+ m_material.hasCustomVertexShader = syncData->vertex.shader->hasShaderCode;
+ if (m_material.hasCustomVertexShader) {
+ vertBlob = syncData->vertex.shader->shaderInfo.blob;
+ } else {
+ vertBlob = QByteArray::fromRawData(reinterpret_cast<const char *>(g_VS_DefaultShaderEffect),
+ sizeof(g_VS_DefaultShaderEffect));
+ }
+
+ m_material.hasCustomFragmentShader = syncData->fragment.shader->hasShaderCode;
+ if (m_material.hasCustomFragmentShader) {
+ fragBlob = syncData->fragment.shader->shaderInfo.blob;
+ } else {
+ fragBlob = QByteArray::fromRawData(reinterpret_cast<const char *>(g_PS_DefaultShaderEffect),
+ sizeof(g_PS_DefaultShaderEffect));
+ }
+
+ m_material.mtype = shaderMaterialTypeCache()->get(vertBlob, fragBlob);
+ m_material.linker.reset(vertBlob, fragBlob);
+
+ if (m_material.hasCustomVertexShader) {
+ m_material.linker.feedVertexInput(*syncData->vertex.shader);
+ m_material.linker.feedConstants(*syncData->vertex.shader);
+ } else {
+ QSGShaderEffectNode::ShaderData defaultSD;
+ defaultSD.shaderInfo.blob = vertBlob;
+ defaultSD.shaderInfo.type = QSGGuiThreadShaderEffectManager::ShaderInfo::TypeVertex;
+
+ QSGGuiThreadShaderEffectManager::ShaderInfo::InputParameter ip;
+ ip.semanticName = QByteArrayLiteral("POSITION");
+ defaultSD.shaderInfo.inputParameters.append(ip);
+ ip.semanticName = QByteArrayLiteral("TEXCOORD");
+ defaultSD.shaderInfo.inputParameters.append(ip);
+
+ // { float4x4 qt_Matrix; float qt_Opacity; } where only the matrix is used
+ QSGGuiThreadShaderEffectManager::ShaderInfo::Variable v;
+ v.name = QByteArrayLiteral("qt_Matrix");
+ v.offset = 0;
+ v.size = 16 * sizeof(float);
+ defaultSD.shaderInfo.variables.append(v);
+ QSGShaderEffectNode::VariableData vd;
+ vd.specialType = QSGShaderEffectNode::VariableData::Matrix;
+ defaultSD.varData.append(vd);
+
+ defaultSD.shaderInfo.constantDataSize = (16 + 1) * sizeof(float);
+
+ m_material.linker.feedVertexInput(defaultSD);
+ m_material.linker.feedConstants(defaultSD);
+ }
+
+ m_material.linker.feedSamplers(*syncData->vertex.shader);
+ m_material.linker.feedTextures(*syncData->vertex.shader);
+
+ if (m_material.hasCustomFragmentShader) {
+ m_material.linker.feedConstants(*syncData->fragment.shader);
+ } else {
+ QSGShaderEffectNode::ShaderData defaultSD;
+ defaultSD.shaderInfo.blob = fragBlob;
+ defaultSD.shaderInfo.type = QSGGuiThreadShaderEffectManager::ShaderInfo::TypeFragment;
+
+ // { float4x4 qt_Matrix; float qt_Opacity; } where only the opacity is used
+ QSGGuiThreadShaderEffectManager::ShaderInfo::Variable v;
+ v.name = QByteArrayLiteral("qt_Opacity");
+ v.offset = 16 * sizeof(float);
+ v.size = sizeof(float);
+ defaultSD.shaderInfo.variables.append(v);
+ QSGShaderEffectNode::VariableData vd;
+ vd.specialType = QSGShaderEffectNode::VariableData::Opacity;
+ defaultSD.varData.append(vd);
+
+ v.name = QByteArrayLiteral("source");
+ v.bindPoint = 0;
+ v.type = QSGGuiThreadShaderEffectManager::ShaderInfo::Texture;
+ defaultSD.shaderInfo.variables.append(v);
+ vd.specialType = QSGShaderEffectNode::VariableData::Source;
+ defaultSD.varData.append(vd);
+
+ v.name = QByteArrayLiteral("sourceSampler");
+ v.bindPoint = 0;
+ v.type = QSGGuiThreadShaderEffectManager::ShaderInfo::Sampler;
+ defaultSD.shaderInfo.variables.append(v);
+ vd.specialType = QSGShaderEffectNode::VariableData::Unused;
+ defaultSD.varData.append(vd);
+
+ defaultSD.shaderInfo.constantDataSize = (16 + 1) * sizeof(float);
+
+ m_material.linker.feedConstants(defaultSD);
+ m_material.linker.feedSamplers(defaultSD);
+ m_material.linker.feedTextures(defaultSD);
+ }
+
+ // While this may seem unnecessary for the built-in shaders, the value
+ // of 'source' is still in there and we have to process it.
+ m_material.linker.feedSamplers(*syncData->fragment.shader);
+ m_material.linker.feedTextures(*syncData->fragment.shader);
+
+ m_material.linker.linkTextureSubRects();
+
+ m_material.updateTextureProviders(true);
+
+ markDirty(QSGNode::DirtyMaterial);
+
+ if (Q_UNLIKELY(debug_render()))
+ m_material.linker.dump();
+ } else {
+ if (syncData->dirty & QSGShaderEffectNode::DirtyShaderConstant) {
+ if (!syncData->vertex.dirtyConstants->isEmpty())
+ m_material.linker.feedConstants(*syncData->vertex.shader, syncData->vertex.dirtyConstants);
+ if (!syncData->fragment.dirtyConstants->isEmpty())
+ m_material.linker.feedConstants(*syncData->fragment.shader, syncData->fragment.dirtyConstants);
+ markDirty(QSGNode::DirtyMaterial);
+ if (Q_UNLIKELY(debug_render()))
+ m_material.linker.dump();
+ }
+
+ if (syncData->dirty & QSGShaderEffectNode::DirtyShaderTexture) {
+ if (!syncData->vertex.dirtyTextures->isEmpty())
+ m_material.linker.feedTextures(*syncData->vertex.shader, syncData->vertex.dirtyTextures);
+ if (!syncData->fragment.dirtyTextures->isEmpty())
+ m_material.linker.feedTextures(*syncData->fragment.shader, syncData->fragment.dirtyTextures);
+ m_material.linker.linkTextureSubRects();
+ m_material.updateTextureProviders(false);
+ markDirty(QSGNode::DirtyMaterial);
+ if (Q_UNLIKELY(debug_render()))
+ m_material.linker.dump();
+ }
+ }
+
+ if (bool(m_material.flags() & QSGMaterial::RequiresFullMatrix) != m_material.hasCustomVertexShader) {
+ m_material.setFlag(QSGMaterial::RequiresFullMatrix, m_material.hasCustomVertexShader);
+ markDirty(QSGNode::DirtyMaterial);
+ }
+}
+
+void QSGD3D12ShaderEffectNode::handleTextureChange()
+{
+ markDirty(QSGNode::DirtyMaterial);
+ emit m_mgr->textureChanged();
+}
+
+void QSGD3D12ShaderEffectNode::handleTextureProviderDestroyed(QObject *object)
+{
+ for (QSGTextureProvider *&tp : m_material.textureProviders) {
+ if (tp == object)
+ tp = nullptr;
+ }
+}
+
+void QSGD3D12ShaderEffectNode::preprocess()
+{
+ for (QSGTextureProvider *tp : m_material.textureProviders) {
+ if (tp) {
+ if (QSGDynamicTexture *texture = qobject_cast<QSGDynamicTexture *>(tp->texture()))
+ texture->updateTexture();
+ }
+ }
+}
+
+QSGGuiThreadShaderEffectManager::ShaderType QSGD3D12GuiThreadShaderEffectManager::shaderType() const
+{
+ return HLSL;
+}
+
+int QSGD3D12GuiThreadShaderEffectManager::shaderCompilationType() const
+{
+ return OfflineCompilation;
+}
+
+int QSGD3D12GuiThreadShaderEffectManager::shaderSourceType() const
+{
+ return ShaderByteCode;
+}
+
+bool QSGD3D12GuiThreadShaderEffectManager::hasSeparateSamplerAndTextureObjects() const
+{
+ return true;
+}
+
+QString QSGD3D12GuiThreadShaderEffectManager::log() const
+{
+ return QString();
+}
+
+QSGGuiThreadShaderEffectManager::Status QSGD3D12GuiThreadShaderEffectManager::status() const
+{
+ return Compiled;
+}
+
+struct RefGuard {
+ RefGuard(IUnknown *p) : p(p) { }
+ ~RefGuard() { p->Release(); }
+ IUnknown *p;
+};
+
+bool QSGD3D12GuiThreadShaderEffectManager::reflect(const QByteArray &src, ShaderInfo *result)
+{
+ const QString fn = QQmlFile::urlToLocalFileOrQrc(src);
+ QFile f(fn);
+ if (!f.open(QIODevice::ReadOnly)) {
+ qWarning("ShaderEffect: Failed to read %s", qPrintable(fn));
+ return false;
+ }
+ result->blob = f.readAll();
+ f.close();
+
+ ID3D12ShaderReflection *reflector;
+ HRESULT hr = D3DReflect(result->blob.constData(), result->blob.size(), IID_PPV_ARGS(&reflector));
+ if (FAILED(hr)) {
+ qWarning("D3D shader reflection failed: 0x%x", hr);
+ return false;
+ }
+ RefGuard rg(reflector);
+
+ D3D12_SHADER_DESC shaderDesc;
+ reflector->GetDesc(&shaderDesc);
+
+ const uint progType = (shaderDesc.Version & 0xFFFF0000) >> 16;
+ const uint major = (shaderDesc.Version & 0x000000F0) >> 4;
+ const uint minor = (shaderDesc.Version & 0x0000000F);
+
+ switch (progType) {
+ case D3D12_SHVER_VERTEX_SHADER:
+ result->type = ShaderInfo::TypeVertex;
+ break;
+ case D3D12_SHVER_PIXEL_SHADER:
+ result->type = ShaderInfo::TypeFragment;
+ break;
+ default:
+ result->type = ShaderInfo::TypeOther;
+ qWarning("D3D shader is of unknown type 0x%x", shaderDesc.Version);
+ return false;
+ }
+
+ if (major < 5) {
+ qWarning("D3D shader model version %u.%u is too low", major, minor);
+ return false;
+ }
+
+ const int ieCount = shaderDesc.InputParameters;
+ const int cbufferCount = shaderDesc.ConstantBuffers;
+ const int boundResCount = shaderDesc.BoundResources;
+
+ result->constantDataSize = 0;
+
+ if (ieCount < 1) {
+ qWarning("Invalid shader: Not enough input parameters (%d)", ieCount);
+ return false;
+ }
+ if (cbufferCount < 1) {
+ qWarning("Invalid shader: Shader has no constant buffers");
+ return false;
+ }
+ if (boundResCount < 1) {
+ qWarning("Invalid shader: No resources bound. Expected to have at least a constant buffer bound.");
+ return false;
+ }
+
+ if (Q_UNLIKELY(debug_render()))
+ qDebug("Shader reflection size %d type %d v%u.%u input elems %d cbuffers %d boundres %d",
+ result->blob.size(), result->type, major, minor, ieCount, cbufferCount, boundResCount);
+
+ for (int i = 0; i < ieCount; ++i) {
+ D3D12_SIGNATURE_PARAMETER_DESC desc;
+ if (FAILED(reflector->GetInputParameterDesc(i, &desc))) {
+ qWarning("D3D reflection: Failed to query input parameter %d", i);
+ return false;
+ }
+ if (desc.SystemValueType != D3D_NAME_UNDEFINED)
+ continue;
+ ShaderInfo::InputParameter param;
+ param.semanticName = QByteArray(desc.SemanticName);
+ param.semanticIndex = desc.SemanticIndex;
+ result->inputParameters.append(param);
+ }
+
+ for (int i = 0; i < boundResCount; ++i) {
+ D3D12_SHADER_INPUT_BIND_DESC desc;
+ if (FAILED(reflector->GetResourceBindingDesc(i, &desc))) {
+ qWarning("D3D reflection: Failed to query resource binding %d", i);
+ continue;
+ }
+ bool gotCBuffer = false;
+ if (desc.Type == D3D_SIT_CBUFFER) {
+ ID3D12ShaderReflectionConstantBuffer *cbuf = reflector->GetConstantBufferByName(desc.Name);
+ D3D12_SHADER_BUFFER_DESC bufDesc;
+ if (FAILED(cbuf->GetDesc(&bufDesc))) {
+ qWarning("D3D reflection: Failed to query constant buffer description");
+ continue;
+ }
+ if (gotCBuffer) {
+ qWarning("D3D reflection: Found more than one constant buffers. Only the first one is used.");
+ continue;
+ }
+ gotCBuffer = true;
+ result->constantDataSize = bufDesc.Size;
+ for (uint cbIdx = 0; cbIdx < bufDesc.Variables; ++cbIdx) {
+ ID3D12ShaderReflectionVariable *cvar = cbuf->GetVariableByIndex(cbIdx);
+ D3D12_SHADER_VARIABLE_DESC varDesc;
+ if (FAILED(cvar->GetDesc(&varDesc))) {
+ qWarning("D3D reflection: Failed to query constant buffer variable %d", cbIdx);
+ return false;
+ }
+ // we report the full size of the buffer but only return variables that are actually used by this shader
+ if (!(varDesc.uFlags & D3D_SVF_USED))
+ continue;
+ ShaderInfo::Variable v;
+ v.type = ShaderInfo::Constant;
+ v.name = QByteArray(varDesc.Name);
+ v.offset = varDesc.StartOffset;
+ v.size = varDesc.Size;
+ result->variables.append(v);
+ }
+ } else if (desc.Type == D3D_SIT_TEXTURE) {
+ if (desc.Dimension != D3D_SRV_DIMENSION_TEXTURE2D) {
+ qWarning("D3D reflection: Texture %s is not a 2D texture, ignoring.", qPrintable(desc.Name));
+ continue;
+ }
+ if (desc.NumSamples != (UINT) -1) {
+ qWarning("D3D reflection: Texture %s is multisample (%u), ignoring.", qPrintable(desc.Name), desc.NumSamples);
+ continue;
+ }
+ if (desc.BindCount != 1) {
+ qWarning("D3D reflection: Texture %s is an array, ignoring.", qPrintable(desc.Name));
+ continue;
+ }
+ if (desc.Space != 0) {
+ qWarning("D3D reflection: Texture %s is not using register space 0, ignoring.", qPrintable(desc.Name));
+ continue;
+ }
+ ShaderInfo::Variable v;
+ v.type = ShaderInfo::Texture;
+ v.name = QByteArray(desc.Name);
+ v.bindPoint = desc.BindPoint;
+ result->variables.append(v);
+ } else if (desc.Type == D3D_SIT_SAMPLER) {
+ if (desc.BindCount != 1) {
+ qWarning("D3D reflection: Sampler %s is an array, ignoring.", qPrintable(desc.Name));
+ continue;
+ }
+ if (desc.Space != 0) {
+ qWarning("D3D reflection: Sampler %s is not using register space 0, ignoring.", qPrintable(desc.Name));
+ continue;
+ }
+ ShaderInfo::Variable v;
+ v.type = ShaderInfo::Sampler;
+ v.name = QByteArray(desc.Name);
+ v.bindPoint = desc.BindPoint;
+ result->variables.append(v);
+ } else {
+ qWarning("D3D reflection: Resource binding %d has an unknown type of %d and will be ignored.", i, desc.Type);
+ continue;
+ }
+ }
+
+ if (Q_UNLIKELY(debug_render())) {
+ qDebug() << "Input:" << result->inputParameters;
+ qDebug() << "Variables:" << result->variables << "cbuffer size" << result->constantDataSize;
+ }
+
+ return true;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode_p.h
new file mode 100644
index 0000000000..c8994a78dd
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode_p.h
@@ -0,0 +1,172 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGD3D12SHADEREFFECTNODE_P_H
+#define QSGD3D12SHADEREFFECTNODE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qsgadaptationlayer_p.h>
+#include "qsgd3d12material_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QSGD3D12RenderContext;
+class QSGD3D12GuiThreadShaderEffectManager;
+class QSGD3D12ShaderEffectNode;
+class QSGD3D12Texture;
+
+class QSGD3D12ShaderLinker
+{
+public:
+ void reset(const QByteArray &vertBlob, const QByteArray &fragBlob);
+
+ void feedVertexInput(const QSGShaderEffectNode::ShaderData &shader);
+ void feedConstants(const QSGShaderEffectNode::ShaderData &shader, const QSet<int> *dirtyIndices = nullptr);
+ void feedSamplers(const QSGShaderEffectNode::ShaderData &shader);
+ void feedTextures(const QSGShaderEffectNode::ShaderData &shader, const QSet<int> *dirtyIndices = nullptr);
+ void linkTextureSubRects();
+
+ void dump();
+
+ struct Constant {
+ uint size;
+ QSGShaderEffectNode::VariableData::SpecialType specialType;
+ QVariant value;
+ bool operator==(const Constant &other) const {
+ return size == other.size && specialType == other.specialType
+ && (specialType == QSGShaderEffectNode::VariableData::None ? value == other.value : true);
+ }
+ };
+
+ bool error;
+ QByteArray vs;
+ QByteArray fs;
+ uint constantBufferSize;
+ QHash<uint, Constant> constants; // offset -> Constant
+ QSet<int> samplers; // bindpoint
+ QHash<int, QVariant> textures; // bindpoint -> value (source ref)
+ QHash<QByteArray, int> textureNameMap; // name -> bindpoint
+};
+
+QDebug operator<<(QDebug debug, const QSGD3D12ShaderLinker::Constant &c);
+
+class QSGD3D12ShaderEffectMaterial : public QSGD3D12Material
+{
+public:
+ QSGD3D12ShaderEffectMaterial(QSGD3D12ShaderEffectNode *node);
+ ~QSGD3D12ShaderEffectMaterial();
+
+ QSGMaterialType *type() const override;
+ int compare(const QSGMaterial *other) const override;
+
+ int constantBufferSize() const override;
+ void preparePipeline(QSGD3D12PipelineState *pipelineState) override;
+ UpdateResults updatePipeline(const RenderState &state,
+ QSGD3D12PipelineState *pipelineState,
+ ExtraState *extraState,
+ quint8 *constantBuffer) override;
+
+ void updateTextureProviders(bool layoutChange);
+
+ QSGD3D12ShaderEffectNode *node;
+ bool valid = false;
+ QSGShaderEffectNode::CullMode cullMode = QSGShaderEffectNode::NoCulling;
+ bool hasCustomVertexShader = false;
+ bool hasCustomFragmentShader = false;
+ QSGD3D12ShaderLinker linker;
+ QSGMaterialType *mtype = nullptr;
+ QVector<QSGTextureProvider *> textureProviders;
+ QSGD3D12Texture *dummy = nullptr;
+ bool geometryUsesTextureSubRect = false;
+};
+
+class QSGD3D12ShaderEffectNode : public QObject, public QSGShaderEffectNode
+{
+ Q_OBJECT
+
+public:
+ QSGD3D12ShaderEffectNode(QSGD3D12RenderContext *rc, QSGD3D12GuiThreadShaderEffectManager *mgr);
+
+ QRectF updateNormalizedTextureSubRect(bool supportsAtlasTextures) override;
+ void syncMaterial(SyncData *syncData) override;
+
+ static void cleanupMaterialTypeCache();
+
+ void preprocess() override;
+
+ QSGD3D12RenderContext *renderContext() { return m_rc; }
+
+private Q_SLOTS:
+ void handleTextureChange();
+ void handleTextureProviderDestroyed(QObject *object);
+
+private:
+ QSGD3D12RenderContext *m_rc;
+ QSGD3D12GuiThreadShaderEffectManager *m_mgr;
+ QSGD3D12ShaderEffectMaterial m_material;
+};
+
+class QSGD3D12GuiThreadShaderEffectManager : public QSGGuiThreadShaderEffectManager
+{
+public:
+ ShaderType shaderType() const override;
+ int shaderCompilationType() const override;
+ int shaderSourceType() const override;
+
+ bool hasSeparateSamplerAndTextureObjects() const override;
+
+ QString log() const override;
+ Status status() const override;
+
+ bool reflect(const QByteArray &src, ShaderInfo *result) override;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGD3D12SHADEREFFECTNODE_P_H
diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12texture.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12texture.cpp
new file mode 100644
index 0000000000..ce0c0e53dd
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12texture.cpp
@@ -0,0 +1,133 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgd3d12texture_p.h"
+#include "qsgd3d12engine_p.h"
+#include <private/qsgcontext_p.h>
+
+QT_BEGIN_NAMESPACE
+
+void QSGD3D12Texture::create(const QImage &image, uint flags)
+{
+ // ### atlas?
+
+ const bool alphaRequest = flags & QSGRenderContext::CreateTexture_Alpha;
+ m_alphaWanted = alphaRequest && image.hasAlphaChannel();
+
+ m_image = image;
+
+ m_id = m_engine->genTexture();
+ Q_ASSERT(m_id);
+
+ // We could kick off the texture creation and the async upload right here.
+ // Unfortunately we cannot tell at this stage if mipmaps will be enabled
+ // via an Image element's mipmap property...so defer to bind().
+ m_createPending = true;
+}
+
+QSGD3D12Texture::~QSGD3D12Texture()
+{
+ if (m_id)
+ m_engine->releaseTexture(m_id);
+}
+
+int QSGD3D12Texture::textureId() const
+{
+ return m_id;
+}
+
+QSize QSGD3D12Texture::textureSize() const
+{
+ return m_image.size();
+}
+
+bool QSGD3D12Texture::hasAlphaChannel() const
+{
+ return m_alphaWanted;
+}
+
+bool QSGD3D12Texture::hasMipmaps() const
+{
+ return mipmapFiltering() != QSGTexture::None;
+}
+
+QRectF QSGD3D12Texture::normalizedTextureSubRect() const
+{
+ return QRectF(0, 0, 1, 1);
+}
+
+void QSGD3D12Texture::bind()
+{
+ // Called when the texture material updates the pipeline state.
+
+ if (!m_createPending && hasMipmaps() != m_createdWithMipMaps) {
+ m_engine->releaseTexture(m_id);
+ m_id = m_engine->genTexture();
+ Q_ASSERT(m_id);
+ m_createPending = true;
+ }
+
+ if (m_createPending) {
+ m_createPending = false;
+
+ QSGD3D12Engine::TextureCreateFlags createFlags = 0;
+ if (m_alphaWanted)
+ createFlags |= QSGD3D12Engine::TextureWithAlpha;
+
+ m_createdWithMipMaps = hasMipmaps();
+ if (m_createdWithMipMaps)
+ createFlags |= QSGD3D12Engine::TextureWithMipMaps;
+
+ m_engine->createTexture(m_id, m_image.size(), m_image.format(), createFlags);
+ m_engine->queueTextureUpload(m_id, m_image);
+ }
+
+ // Here we know that the texture is going to be used in the current frame
+ // by the next draw call. Notify the engine so that it can wait for
+ // possible pending uploads and set up the pipeline accordingly.
+ m_engine->useTexture(m_id);
+}
+
+SIZE_T QSGD3D12Texture::srv() const
+{
+ Q_ASSERT(m_id);
+ return m_engine->textureSRV(m_id);
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12texture_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12texture_p.h
new file mode 100644
index 0000000000..06013d4bea
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12texture_p.h
@@ -0,0 +1,89 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGD3D12TEXTURE_P_H
+#define QSGD3D12TEXTURE_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 <qsgtexture.h>
+#include <basetsd.h>
+
+QT_BEGIN_NAMESPACE
+
+class QSGD3D12Engine;
+
+class QSGD3D12Texture : public QSGTexture
+{
+public:
+ QSGD3D12Texture(QSGD3D12Engine *engine) : m_engine(engine) { }
+ ~QSGD3D12Texture();
+
+ void create(const QImage &image, uint flags);
+
+ int textureId() const override;
+ QSize textureSize() const override;
+ bool hasAlphaChannel() const override;
+ bool hasMipmaps() const override;
+ QRectF normalizedTextureSubRect() const override;
+ void bind() override;
+
+ SIZE_T srv() const;
+
+protected:
+ QSGD3D12Engine *m_engine;
+ QImage m_image;
+ bool m_createPending = false;
+ bool m_createdWithMipMaps = false;
+ uint m_id = 0;
+ bool m_alphaWanted = false;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/scenegraph/d3d12/shaders/mipmapgen.hlsl b/src/plugins/scenegraph/d3d12/shaders/mipmapgen.hlsl
new file mode 100644
index 0000000000..6793b534b0
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/shaders/mipmapgen.hlsl
@@ -0,0 +1,60 @@
+static const uint GROUP_DIM = 8; // 2 ^ (out_mip_count - 1)
+
+Texture2D tex : register(t0);
+SamplerState samp : register(s0);
+
+cbuffer ConstantBuffer : register(b0)
+{
+ uint2 mip1Size;
+ uint sampleLevel;
+ uint totalMips;
+}
+
+RWTexture2D<float4> mip1 : register(u0);
+RWTexture2D<float4> mip2 : register(u1);
+RWTexture2D<float4> mip3 : register(u2);
+RWTexture2D<float4> mip4 : register(u3);
+
+groupshared float4 groupColor[GROUP_DIM][GROUP_DIM];
+
+[numthreads(GROUP_DIM, GROUP_DIM, 1)]
+void CS_Generate4MipMaps(uint3 localId: SV_GroupThreadId, uint3 globalId: SV_DispatchThreadID)
+{
+ const float2 coord = float2(1.0f / float(mip1Size.x), 1.0f / float(mip1Size.y)) * (globalId.xy + 0.5);
+ float4 c = tex.SampleLevel(samp, coord, sampleLevel);
+
+ mip1[globalId.xy] = c;
+ groupColor[localId.y][localId.x] = c;
+
+ if (sampleLevel + 1 >= totalMips)
+ return;
+
+ GroupMemoryBarrierWithGroupSync();
+
+ if ((localId.x & 1) == 0 && (localId.y & 1) == 0) {
+ c = (c + groupColor[localId.y][localId.x + 1] + groupColor[localId.y + 1][localId.x] + groupColor[localId.y + 1][localId.x + 1]) / 4.0;
+ mip2[globalId.xy / 2] = c;
+ groupColor[localId.y][localId.x] = c;
+ }
+
+ if (sampleLevel + 2 >= totalMips)
+ return;
+
+ GroupMemoryBarrierWithGroupSync();
+
+ if ((localId.x & 3) == 0 && (localId.y & 3) == 0) {
+ c = (c + groupColor[localId.y][localId.x + 2] + groupColor[localId.y + 2][localId.x] + groupColor[localId.y + 2][localId.x + 2]) / 4.0;
+ mip3[globalId.xy / 4] = c;
+ groupColor[localId.y][localId.x] = c;
+ }
+
+ if (sampleLevel + 3 >= totalMips)
+ return;
+
+ GroupMemoryBarrierWithGroupSync();
+
+ if ((localId.x & 7) == 0 && (localId.y & 7) == 0) {
+ c = (c + groupColor[localId.y][localId.x + 3] + groupColor[localId.y + 3][localId.x] + groupColor[localId.y + 3][localId.x + 3]) / 4.0;
+ mip4[globalId.xy / 8] = c;
+ }
+}
diff --git a/src/plugins/scenegraph/d3d12/shaders/shadereffectdefault.hlsl b/src/plugins/scenegraph/d3d12/shaders/shadereffectdefault.hlsl
new file mode 100644
index 0000000000..94672d6267
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/shaders/shadereffectdefault.hlsl
@@ -0,0 +1,27 @@
+cbuffer ConstantBuffer : register(b0)
+{
+ float4x4 qt_Matrix;
+ float qt_Opacity;
+};
+
+struct PSInput
+{
+ float4 position : SV_POSITION;
+ float2 coord : TEXCOORD0;
+};
+
+Texture2D source : register(t0);
+SamplerState sourceSampler : register(s0);
+
+PSInput VS_DefaultShaderEffect(float4 position : POSITION, float2 coord : TEXCOORD0)
+{
+ PSInput result;
+ result.position = mul(qt_Matrix, position);
+ result.coord = coord;
+ return result;
+}
+
+float4 PS_DefaultShaderEffect(PSInput input) : SV_TARGET
+{
+ return source.Sample(sourceSampler, input.coord) * qt_Opacity;
+}
diff --git a/src/plugins/scenegraph/d3d12/shaders/shaders.pri b/src/plugins/scenegraph/d3d12/shaders/shaders.pri
new file mode 100644
index 0000000000..41bac334de
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/shaders/shaders.pri
@@ -0,0 +1,119 @@
+vertexcolor_VSPS = $$PWD/vertexcolor.hlsl
+vertexcolor_vshader.input = vertexcolor_VSPS
+vertexcolor_vshader.header = vs_vertexcolor.hlslh
+vertexcolor_vshader.entry = VS_VertexColor
+vertexcolor_vshader.type = vs_5_0
+vertexcolor_pshader.input = vertexcolor_VSPS
+vertexcolor_pshader.header = ps_vertexcolor.hlslh
+vertexcolor_pshader.entry = PS_VertexColor
+vertexcolor_pshader.type = ps_5_0
+
+stencilclip_VSPS = $$PWD/stencilclip.hlsl
+stencilclip_vshader.input = stencilclip_VSPS
+stencilclip_vshader.header = vs_stencilclip.hlslh
+stencilclip_vshader.entry = VS_StencilClip
+stencilclip_vshader.type = vs_5_0
+stencilclip_pshader.input = stencilclip_VSPS
+stencilclip_pshader.header = ps_stencilclip.hlslh
+stencilclip_pshader.entry = PS_StencilClip
+stencilclip_pshader.type = ps_5_0
+
+smoothcolor_VSPS = $$PWD/smoothcolor.hlsl
+smoothcolor_vshader.input = smoothcolor_VSPS
+smoothcolor_vshader.header = vs_smoothcolor.hlslh
+smoothcolor_vshader.entry = VS_SmoothColor
+smoothcolor_vshader.type = vs_5_0
+smoothcolor_pshader.input = smoothcolor_VSPS
+smoothcolor_pshader.header = ps_smoothcolor.hlslh
+smoothcolor_pshader.entry = PS_SmoothColor
+smoothcolor_pshader.type = ps_5_0
+
+texture_VSPS = $$PWD/texture.hlsl
+texture_vshader.input = texture_VSPS
+texture_vshader.header = vs_texture.hlslh
+texture_vshader.entry = VS_Texture
+texture_vshader.type = vs_5_0
+texture_pshader.input = texture_VSPS
+texture_pshader.header = ps_texture.hlslh
+texture_pshader.entry = PS_Texture
+texture_pshader.type = ps_5_0
+
+smoothtexture_VSPS = $$PWD/smoothtexture.hlsl
+smoothtexture_vshader.input = smoothtexture_VSPS
+smoothtexture_vshader.header = vs_smoothtexture.hlslh
+smoothtexture_vshader.entry = VS_SmoothTexture
+smoothtexture_vshader.type = vs_5_0
+smoothtexture_pshader.input = smoothtexture_VSPS
+smoothtexture_pshader.header = ps_smoothtexture.hlslh
+smoothtexture_pshader.entry = PS_SmoothTexture
+smoothtexture_pshader.type = ps_5_0
+
+mipmapgen_CS = $$PWD/mipmapgen.hlsl
+mipmapgen_cshader.input = mipmapgen_CS
+mipmapgen_cshader.header = cs_mipmapgen.hlslh
+mipmapgen_cshader.entry = CS_Generate4MipMaps
+mipmapgen_cshader.type = cs_5_0
+
+textmask_VSPS = $$PWD/textmask.hlsl
+textmask_vshader.input = textmask_VSPS
+textmask_vshader.header = vs_textmask.hlslh
+textmask_vshader.entry = VS_TextMask
+textmask_vshader.type = vs_5_0
+textmask_pshader24.input = textmask_VSPS
+textmask_pshader24.header = ps_textmask24.hlslh
+textmask_pshader24.entry = PS_TextMask24
+textmask_pshader24.type = ps_5_0
+textmask_pshader32.input = textmask_VSPS
+textmask_pshader32.header = ps_textmask32.hlslh
+textmask_pshader32.entry = PS_TextMask32
+textmask_pshader32.type = ps_5_0
+textmask_pshader8.input = textmask_VSPS
+textmask_pshader8.header = ps_textmask8.hlslh
+textmask_pshader8.entry = PS_TextMask8
+textmask_pshader8.type = ps_5_0
+styledtext_vshader.input = textmask_VSPS
+styledtext_vshader.header = vs_styledtext.hlslh
+styledtext_vshader.entry = VS_StyledText
+styledtext_vshader.type = vs_5_0
+styledtext_pshader.input = textmask_VSPS
+styledtext_pshader.header = ps_styledtext.hlslh
+styledtext_pshader.entry = PS_StyledText
+styledtext_pshader.type = ps_5_0
+outlinedtext_vshader.input = textmask_VSPS
+outlinedtext_vshader.header = vs_outlinedtext.hlslh
+outlinedtext_vshader.entry = VS_OutlinedText
+outlinedtext_vshader.type = vs_5_0
+outlinedtext_pshader.input = textmask_VSPS
+outlinedtext_pshader.header = ps_outlinedtext.hlslh
+outlinedtext_pshader.entry = PS_OutlinedText
+outlinedtext_pshader.type = ps_5_0
+
+shadereffectdefault_VSPS = $$PWD/shadereffectdefault.hlsl
+shadereffectdefault_vshader.input = shadereffectdefault_VSPS
+shadereffectdefault_vshader.header = vs_shadereffectdefault.hlslh
+shadereffectdefault_vshader.entry = VS_DefaultShaderEffect
+shadereffectdefault_vshader.type = vs_5_0
+shadereffectdefault_pshader.input = shadereffectdefault_VSPS
+shadereffectdefault_pshader.header = ps_shadereffectdefault.hlslh
+shadereffectdefault_pshader.entry = PS_DefaultShaderEffect
+shadereffectdefault_pshader.type = ps_5_0
+
+tdr_CS = $$PWD/tdr.hlsl
+tdr_cshader.input = tdr_CS
+tdr_cshader.header = cs_tdr.hlslh
+tdr_cshader.entry = timeout
+tdr_cshader.type = cs_5_0
+
+HLSL_SHADERS = \
+ vertexcolor_vshader vertexcolor_pshader \
+ stencilclip_vshader stencilclip_pshader \
+ smoothcolor_vshader smoothcolor_pshader \
+ texture_vshader texture_pshader \
+ smoothtexture_vshader smoothtexture_pshader \
+ mipmapgen_cshader \
+ textmask_vshader textmask_pshader24 textmask_pshader32 textmask_pshader8 \
+ styledtext_vshader styledtext_pshader outlinedtext_vshader outlinedtext_pshader \
+ shadereffectdefault_vshader shadereffectdefault_pshader \
+ tdr_cshader
+
+load(hlsl_bytecode_header)
diff --git a/src/plugins/scenegraph/d3d12/shaders/smoothcolor.hlsl b/src/plugins/scenegraph/d3d12/shaders/smoothcolor.hlsl
new file mode 100644
index 0000000000..4f69eea60f
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/shaders/smoothcolor.hlsl
@@ -0,0 +1,64 @@
+struct VSInput
+{
+ float4 position : POSITION;
+ float4 color : COLOR;
+ float2 offset : TEXCOORD0;
+};
+
+cbuffer ConstantBuffer : register(b0)
+{
+ float4x4 mvp;
+ float opacity;
+ float2 pixelSize;
+};
+
+struct PSInput
+{
+ float4 position : SV_POSITION;
+ float4 color : COLOR;
+};
+
+PSInput VS_SmoothColor(VSInput input)
+{
+ PSInput result;
+
+ float4 pos = mul(mvp, input.position);
+
+ if (input.offset.x != 0.0) {
+ // In HLSL matrix packing is column-major by default (which is good) but the math is row-major (unlike GLSL).
+ float4 delta = float4(mvp._11, mvp._21, mvp._31, mvp._41) * input.offset.x;
+ float2 dir = delta.xy * pos.w - pos.xy * delta.w;
+ float2 ndir = 0.5 * pixelSize * normalize(dir / pixelSize);
+ dir -= ndir * delta.w * pos.w;
+ float numerator = dot(dir, ndir * pos.w * pos.w);
+ float scale = 0.0;
+ if (numerator < 0.0)
+ scale = 1.0;
+ else
+ scale = min(1.0, numerator / dot(dir, dir));
+ pos += scale * delta;
+ }
+
+ if (input.offset.y != 0.0) {
+ float4 delta = float4(mvp._12, mvp._22, mvp._32, mvp._42) * input.offset.y;
+ float2 dir = delta.xy * pos.w - pos.xy * delta.w;
+ float2 ndir = 0.5 * pixelSize * normalize(dir / pixelSize);
+ dir -= ndir * delta.w * pos.w;
+ float numerator = dot(dir, ndir * pos.w * pos.w);
+ float scale = 0.0;
+ if (numerator < 0.0)
+ scale = 1.0;
+ else
+ scale = min(1.0, numerator / dot(dir, dir));
+ pos += scale * delta;
+ }
+
+ result.position = pos;
+ result.color = input.color * opacity;
+ return result;
+}
+
+float4 PS_SmoothColor(PSInput input) : SV_TARGET
+{
+ return input.color;
+}
diff --git a/src/plugins/scenegraph/d3d12/shaders/smoothtexture.hlsl b/src/plugins/scenegraph/d3d12/shaders/smoothtexture.hlsl
new file mode 100644
index 0000000000..05b1c6e9d4
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/shaders/smoothtexture.hlsl
@@ -0,0 +1,77 @@
+struct VSInput
+{
+ float4 position : POSITION;
+ float2 coord : TEXCOORD0;
+ float2 offset : TEXCOORD1;
+ float2 coordOffset : TEXCOORD2;
+};
+
+cbuffer ConstantBuffer : register(b0)
+{
+ float4x4 mvp;
+ float opacity;
+ float2 pixelSize;
+};
+
+struct PSInput
+{
+ float4 position : SV_POSITION;
+ float2 coord : TEXCOORD0;
+ float vertexOpacity : TEXCOORD3;
+};
+
+Texture2D tex : register(t0);
+SamplerState samp : register(s0);
+
+PSInput VS_SmoothTexture(VSInput input)
+{
+ PSInput result;
+
+ float4 pos = mul(mvp, input.position);
+ float2 coord = input.coord;
+
+ if (input.offset.x != 0.0) {
+ // In HLSL matrix packing is column-major by default (which is good) but the math is row-major (unlike GLSL).
+ float4 delta = float4(mvp._11, mvp._21, mvp._31, mvp._41) * input.offset.x;
+ float2 dir = delta.xy * pos.w - pos.xy * delta.w;
+ float2 ndir = 0.5 * pixelSize * normalize(dir / pixelSize);
+ dir -= ndir * delta.w * pos.w;
+ float numerator = dot(dir, ndir * pos.w * pos.w);
+ float scale = 0.0;
+ if (numerator < 0.0)
+ scale = 1.0;
+ else
+ scale = min(1.0, numerator / dot(dir, dir));
+ pos += scale * delta;
+ coord.x += scale * input.coordOffset.x;
+ }
+
+ if (input.offset.y != 0.0) {
+ float4 delta = float4(mvp._12, mvp._22, mvp._32, mvp._42) * input.offset.y;
+ float2 dir = delta.xy * pos.w - pos.xy * delta.w;
+ float2 ndir = 0.5 * pixelSize * normalize(dir / pixelSize);
+ dir -= ndir * delta.w * pos.w;
+ float numerator = dot(dir, ndir * pos.w * pos.w);
+ float scale = 0.0;
+ if (numerator < 0.0)
+ scale = 1.0;
+ else
+ scale = min(1.0, numerator / dot(dir, dir));
+ pos += scale * delta;
+ coord.y += scale * input.coordOffset.y;
+ }
+
+ if ((input.offset.x != 0.0 || input.offset.y != 0.0) && (input.coordOffset.x == 0.0 && input.coordOffset.y == 0.0))
+ result.vertexOpacity = 0.0;
+ else
+ result.vertexOpacity = opacity;
+
+ result.position = pos;
+ result.coord = coord;
+ return result;
+}
+
+float4 PS_SmoothTexture(PSInput input) : SV_TARGET
+{
+ return tex.Sample(samp, input.coord) * input.vertexOpacity;
+}
diff --git a/src/plugins/scenegraph/d3d12/shaders/stencilclip.hlsl b/src/plugins/scenegraph/d3d12/shaders/stencilclip.hlsl
new file mode 100644
index 0000000000..9aff84d261
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/shaders/stencilclip.hlsl
@@ -0,0 +1,26 @@
+struct VSInput
+{
+ float4 position : POSITION;
+};
+
+cbuffer ConstantBuffer : register(b0)
+{
+ float4x4 mvp;
+};
+
+struct PSInput
+{
+ float4 position : SV_POSITION;
+};
+
+PSInput VS_StencilClip(VSInput input)
+{
+ PSInput result;
+ result.position = mul(mvp, input.position);
+ return result;
+}
+
+float4 PS_StencilClip(PSInput input) : SV_TARGET
+{
+ return float4(0.81, 0.83, 0.12, 1.0); // Trolltech green ftw!
+}
diff --git a/src/plugins/scenegraph/d3d12/shaders/tdr.hlsl b/src/plugins/scenegraph/d3d12/shaders/tdr.hlsl
new file mode 100644
index 0000000000..f32d4fbace
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/shaders/tdr.hlsl
@@ -0,0 +1,11 @@
+// http://gamedev.stackexchange.com/questions/108141/how-can-i-test-dxgi-error-device-removed-error-handling
+
+RWBuffer<uint> uav;
+cbuffer ConstantBuffer { uint zero; }
+
+[numthreads(256, 1, 1)]
+void timeout(uint3 id: SV_DispatchThreadID)
+{
+ while (zero == 0)
+ uav[id.x] = zero;
+}
diff --git a/src/plugins/scenegraph/d3d12/shaders/textmask.hlsl b/src/plugins/scenegraph/d3d12/shaders/textmask.hlsl
new file mode 100644
index 0000000000..f9d92e8ee9
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/shaders/textmask.hlsl
@@ -0,0 +1,104 @@
+struct VSInput
+{
+ float4 position : POSITION;
+ float2 coord : TEXCOORD0;
+};
+
+cbuffer ConstantBuffer : register(b0)
+{
+ float4x4 mvp;
+ float2 textureScale;
+ float dpr;
+ float color; // for TextMask24 and 32
+ float4 colorVec; // for TextMask8 and Styled and Outlined
+ float2 shift; // for Styled
+ float4 styleColor; // for Styled and Outlined
+};
+
+struct PSInput
+{
+ float4 position : SV_POSITION;
+ float2 coord : TEXCOORD0;
+};
+
+Texture2D tex : register(t0);
+SamplerState samp : register(s0);
+
+PSInput VS_TextMask(VSInput input)
+{
+ PSInput result;
+ result.position = mul(mvp, floor(input.position * dpr + 0.5) / dpr);
+ result.coord = input.coord * textureScale;
+ return result;
+}
+
+float4 PS_TextMask24(PSInput input) : SV_TARGET
+{
+ float4 glyph = tex.Sample(samp, input.coord);
+ return float4(glyph.rgb * color, glyph.a);
+}
+
+float4 PS_TextMask32(PSInput input) : SV_TARGET
+{
+ return tex.Sample(samp, input.coord) * color;
+}
+
+float4 PS_TextMask8(PSInput input) : SV_TARGET
+{
+ return colorVec * tex.Sample(samp, input.coord).r;
+}
+
+struct StyledPSInput
+{
+ float4 position : SV_POSITION;
+ float2 coord : TEXCOORD0;
+ float2 shiftedCoord : TEXCOORD1;
+};
+
+StyledPSInput VS_StyledText(VSInput input)
+{
+ StyledPSInput result;
+ result.position = mul(mvp, floor(input.position * dpr + 0.5) / dpr);
+ result.coord = input.coord * textureScale;
+ result.shiftedCoord = (input.coord - shift) * textureScale;
+ return result;
+}
+
+float4 PS_StyledText(StyledPSInput input) : SV_TARGET
+{
+ float glyph = tex.Sample(samp, input.coord).r;
+ float style = clamp(tex.Sample(samp, input.shiftedCoord).r - glyph, 0.0, 1.0);
+ return style * styleColor + glyph * colorVec;
+}
+
+struct OutlinedPSInput
+{
+ float4 position : SV_POSITION;
+ float2 coord : TEXCOORD0;
+ float2 coordUp : TEXCOORD1;
+ float2 coordDown : TEXCOORD2;
+ float2 coordLeft : TEXCOORD3;
+ float2 coordRight : TEXCOORD4;
+};
+
+OutlinedPSInput VS_OutlinedText(VSInput input)
+{
+ OutlinedPSInput result;
+ result.position = mul(mvp, floor(input.position * dpr + 0.5) / dpr);
+ result.coord = input.coord * textureScale;
+ result.coordUp = (input.coord - float2(0.0, -1.0)) * textureScale;
+ result.coordDown = (input.coord - float2(0.0, 1.0)) * textureScale;
+ result.coordLeft = (input.coord - float2(-1.0, 0.0)) * textureScale;
+ result.coordRight = (input.coord - float2(1.0, 0.0)) * textureScale;
+ return result;
+}
+
+float4 PS_OutlinedText(OutlinedPSInput input) : SV_TARGET
+{
+ float glyph = tex.Sample(samp, input.coord).r;
+ float outline = clamp(clamp(tex.Sample(samp, input.coordUp).r
+ + tex.Sample(samp, input.coordDown).r
+ + tex.Sample(samp, input.coordLeft).r
+ + tex.Sample(samp, input.coordRight).r, 0.0, 1.0) - glyph, 0.0, 1.0);
+ return outline * styleColor + glyph * colorVec;
+}
diff --git a/src/plugins/scenegraph/d3d12/shaders/texture.hlsl b/src/plugins/scenegraph/d3d12/shaders/texture.hlsl
new file mode 100644
index 0000000000..1ae6579e8d
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/shaders/texture.hlsl
@@ -0,0 +1,33 @@
+struct VSInput
+{
+ float4 position : POSITION;
+ float2 coord : TEXCOORD0;
+};
+
+cbuffer ConstantBuffer : register(b0)
+{
+ float4x4 mvp;
+ float opacity;
+};
+
+struct PSInput
+{
+ float4 position : SV_POSITION;
+ float2 coord : TEXCOORD0;
+};
+
+Texture2D tex : register(t0);
+SamplerState samp : register(s0);
+
+PSInput VS_Texture(VSInput input)
+{
+ PSInput result;
+ result.position = mul(mvp, input.position);
+ result.coord = input.coord;
+ return result;
+}
+
+float4 PS_Texture(PSInput input) : SV_TARGET
+{
+ return tex.Sample(samp, input.coord) * opacity;
+}
diff --git a/src/plugins/scenegraph/d3d12/shaders/vertexcolor.hlsl b/src/plugins/scenegraph/d3d12/shaders/vertexcolor.hlsl
new file mode 100644
index 0000000000..a0569bb5c1
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/shaders/vertexcolor.hlsl
@@ -0,0 +1,32 @@
+struct VSInput
+{
+ float4 position : POSITION;
+ float4 color : COLOR;
+};
+
+cbuffer ConstantBuffer : register(b0)
+{
+ float4x4 mvp;
+ float opacity;
+};
+
+struct PSInput
+{
+ float4 position : SV_POSITION;
+ float4 color : COLOR;
+};
+
+PSInput VS_VertexColor(VSInput input)
+{
+ PSInput result;
+
+ result.position = mul(mvp, input.position);
+ result.color = input.color * opacity;
+
+ return result;
+}
+
+float4 PS_VertexColor(PSInput input) : SV_TARGET
+{
+ return input.color;
+}
diff --git a/src/plugins/scenegraph/scenegraph.pro b/src/plugins/scenegraph/scenegraph.pro
new file mode 100644
index 0000000000..2cf05165c4
--- /dev/null
+++ b/src/plugins/scenegraph/scenegraph.pro
@@ -0,0 +1,2 @@
+TEMPLATE = subdirs
+config_d3d12: SUBDIRS += d3d12
diff --git a/src/quick/designer/qquickdesignersupport.cpp b/src/quick/designer/qquickdesignersupport.cpp
index 78ed89a107..f063cd3a81 100644
--- a/src/quick/designer/qquickdesignersupport.cpp
+++ b/src/quick/designer/qquickdesignersupport.cpp
@@ -92,6 +92,7 @@ void QQuickDesignerSupport::refFromEffectItem(QQuickItem *referencedItem, bool h
texture->setRect(referencedItem->boundingRect());
texture->setSize(referencedItem->boundingRect().size().toSize());
texture->setRecursive(true);
+#ifndef QT_NO_OPENGL
#ifndef QT_OPENGL_ES
if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL)
texture->setFormat(GL_RGBA8);
@@ -100,6 +101,7 @@ void QQuickDesignerSupport::refFromEffectItem(QQuickItem *referencedItem, bool h
#else
texture->setFormat(GL_RGBA);
#endif
+#endif
texture->setHasMipmaps(false);
m_itemTextureHash.insert(referencedItem, texture);
diff --git a/src/quick/designer/qquickdesignerwindowmanager.cpp b/src/quick/designer/qquickdesignerwindowmanager.cpp
index efa3bcb51a..2d37db08e7 100644
--- a/src/quick/designer/qquickdesignerwindowmanager.cpp
+++ b/src/quick/designer/qquickdesignerwindowmanager.cpp
@@ -39,8 +39,9 @@
#include "qquickdesignerwindowmanager_p.h"
#include "private/qquickwindow_p.h"
-#include <QtGui/QOpenGLContext>
-
+#ifndef QT_NO_OPENGL
+# include <QtQuick/private/qsgdefaultrendercontext_p.h>
+#endif
#include <QtQuick/QQuickWindow>
QT_BEGIN_NAMESPACE
@@ -48,7 +49,7 @@ QT_BEGIN_NAMESPACE
QQuickDesignerWindowManager::QQuickDesignerWindowManager()
: m_sgContext(QSGContext::createDefaultContext())
{
- m_renderContext.reset(new QSGRenderContext(m_sgContext.data()));
+ m_renderContext.reset(m_sgContext.data()->createRenderContext());
}
void QQuickDesignerWindowManager::show(QQuickWindow *window)
@@ -66,6 +67,7 @@ void QQuickDesignerWindowManager::windowDestroyed(QQuickWindow *)
void QQuickDesignerWindowManager::makeOpenGLContext(QQuickWindow *window)
{
+#ifndef QT_NO_OPENGL
if (!m_openGlContext) {
m_openGlContext.reset(new QOpenGLContext());
m_openGlContext->setFormat(window->requestedFormat());
@@ -76,6 +78,9 @@ void QQuickDesignerWindowManager::makeOpenGLContext(QQuickWindow *window)
} else {
m_openGlContext->makeCurrent(window);
}
+#else
+ Q_UNUSED(window)
+#endif
}
void QQuickDesignerWindowManager::exposureChanged(QQuickWindow *)
diff --git a/src/quick/designer/qquickdesignerwindowmanager_p.h b/src/quick/designer/qquickdesignerwindowmanager_p.h
index 8af54c117f..a50f8aa49f 100644
--- a/src/quick/designer/qquickdesignerwindowmanager_p.h
+++ b/src/quick/designer/qquickdesignerwindowmanager_p.h
@@ -57,6 +57,10 @@
#include <private/qtquickglobal_p.h>
#include <QtQuick/private/qsgcontext_p.h>
+#ifndef QT_NO_OPENGL
+# include <QtGui/QOpenGLContext>
+#endif
+
QT_BEGIN_NAMESPACE
@@ -64,7 +68,6 @@ class QQuickWindow;
class QSGContext;
class QSGRenderContext;
class QAnimationDriver;
-class QOpenGLContext;
class QQuickDesignerWindowManager : public QSGRenderLoop
{
@@ -94,7 +97,9 @@ public:
static void createOpenGLContext(QQuickWindow *window);
private:
+#ifndef QT_NO_OPENGL
QScopedPointer<QOpenGLContext> m_openGlContext;
+#endif
QScopedPointer<QSGContext> m_sgContext;
QScopedPointer<QSGRenderContext> m_renderContext;
};
diff --git a/src/quick/doc/src/concepts/visualcanvas/adaptations.qdoc b/src/quick/doc/src/concepts/visualcanvas/adaptations.qdoc
new file mode 100644
index 0000000000..d7d2fea281
--- /dev/null
+++ b/src/quick/doc/src/concepts/visualcanvas/adaptations.qdoc
@@ -0,0 +1,86 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** 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 http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: http://www.gnu.org/copyleft/fdl.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+\title Scene Graph Adaptations
+\page qtquick-visualcanvas-adaptations.html
+
+\section1 Scene Graph Adaptations in Qt Quick
+
+Originally Qt Quick only had one available renderer for parsing the
+scene graph and rendering the results to a render target. This renderer
+is now the default OpenGL Renderer which supports rendering either using
+the OpenGL ES 2.0 or OpenGL 2.0 APIs. The Qt Quick APIs are designed
+with the assumption that these two APIs are always available. It is
+however possible now to use other graphics API's to render Qt Quick
+scenes using the scene graph APIs.
+
+\section1 OpenGL ES 2.0 and OpenGL 2.0 Adaptation
+
+The default adaptation capable of providing the full Qt Quick 2 feature
+set is the OpenGL adaptation. All of the details of the OpenGL
+adpatation can are available here
+\l{qtquick-visualcanvas-scenegraph-renderer.html}{OpenGL Adaptation}
+
+\section1 Software Adaptation
+
+The Software adaptation is an alternative renderer for \l {Qt Quick} 2 that uses the Raster
+paint engine to render the contents of the scene graph instead of OpenGL.
+As a result of not using OpenGL to render the scene graph, some features
+and optimizations are no longer available. Most Qt Quick 2 applications
+will run without modification though any attempts to use unsupported
+features will be ignored. By using the Software adpatation it is possible to run Qt
+Quick 2 applications on hardware and platforms that do not have OpenGL
+support.
+
+The Software adaptation was previously known as the Qt Quick 2D Renderer.
+
+\section2 Shader Effects
+ShaderEffect components in QtQuick 2 can not be rendered by the Software adptation.
+
+\section2 Qt Graphical Effects Module
+\l {Qt Graphical Effects} uses ShaderEffect items to render effects. If you use
+graphical effects from this module, then you should not hide the source
+item so that the original item can still be rendered.
+
+\section2 Particle Effects
+It is not possible to render particle effects with the Software adaptation. Whenever
+possible, remove particles completely from the scene. Otherwise they will still
+require some processing, even though they are not visible.
+
+\section2 Sprites
+The Sprite item depends on OpenGL functions and will not be visible.
+
+\section2 Rendering Text
+The text rendering with the Software adaptation is based on software
+rasterization and does not respond as well to transformations such as scaling
+as when using OpenGL. The quality is similar to choosing \l [QML] {Text::renderType}
+{Text.NativeRendering} with \l [QML] {Text} items.
+
+\section1 Direct3D 12 (experimental)
+
+*/
diff --git a/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc b/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc
index 443d189f58..ea62603f06 100644
--- a/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc
+++ b/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc
@@ -31,9 +31,10 @@
\section1 The Scene Graph in Qt Quick
-Qt Quick 2 makes use of a dedicated scene graph based on OpenGL ES 2.0
-or OpenGL 2.0 for its rendering. Using a scene graph for graphics
-rather than the traditional imperative painting systems (QPainter and
+Qt Quick 2 makes use of a dedicated scene graph based and a series of
+adpatations of which the default uses OpenGL ES 2.0 or OpenGL 2.0 for
+its rendering. Using a scene graph for graphics rather than the
+traditional imperative painting systems (QPainter and
similar), means the scene to be rendered can be retained between
frames and the complete set of primitives to render is known before
rendering starts. This opens up for a number of optimizations, such as
@@ -62,6 +63,11 @@ independently of the state of the items. On many platforms, the scene
graph will even be rendered on a dedicated render thread while the GUI
thread is preparing the next frame's state.
+\note Much of the information listed on this page is specific to the
+default OpenGL adaptation of the Qt Quick Scene graph. For more information
+about the different scene graph adaptations see
+\l{qtquick-visualcanvas-adaptations.html}{Scene Graph Adaptations}.
+
\section1 Qt Quick Scene Graph Structure
diff --git a/src/quick/doc/src/concepts/visualcanvas/topic.qdoc b/src/quick/doc/src/concepts/visualcanvas/topic.qdoc
index 30fba97474..168c616d06 100644
--- a/src/quick/doc/src/concepts/visualcanvas/topic.qdoc
+++ b/src/quick/doc/src/concepts/visualcanvas/topic.qdoc
@@ -55,12 +55,13 @@ See the documentation about the \l{qtquick-visualcanvas-visualparent.html}
\section1 Scene Graph
-Modern computer systems and devices use OpenGL to draw graphics. Qt Quick
-requires OpenGL and it is used to display applications developed with
-Qt Quick in QML. In particular, Qt Quick defines a scene graph which is then
-rendered. See the documentation about the
-\l{qtquick-visualcanvas-scenegraph.html}{Scene Graph} for in-depth
-information about the concept of a scene graph and why it is beneficial, and
-about the scene graph implementation provided by Qt Quick.
+Modern computer systems and devices use graphics processing units or GPUs to
+render graphics. Qt Quick can leverage this graphics hardware by using graphics
+APIs like OpenGL. The default graphics adpatation for Qt Quick requires OpenGL and
+it is used to display applications developed with Qt Quick in QML. In particular,
+Qt Quick defines a scene graph which is then rendered. See the documentation about the
+\l{qtquick-visualcanvas-scenegraph.html}{Scene Graph} for in-depth information about
+the concept of a scene graph and why it is beneficial, and about the scene graph
+adaptations provided by Qt Quick.
*/
diff --git a/src/quick/items/context2d/qquickcanvasitem.cpp b/src/quick/items/context2d/qquickcanvasitem.cpp
index 4abcc722d1..7ffb715346 100644
--- a/src/quick/items/context2d/qquickcanvasitem.cpp
+++ b/src/quick/items/context2d/qquickcanvasitem.cpp
@@ -43,7 +43,7 @@
#include <private/qquickcanvascontext_p.h>
#include <private/qquickcontext2d_p.h>
#include <private/qquickcontext2dtexture_p.h>
-#include <qsgsimpletexturenode.h>
+#include <private/qsgadaptationlayer_p.h>
#include <QtQuick/private/qquickpixmapcache_p.h>
#include <QtGui/QGuiApplication>
@@ -59,24 +59,11 @@
QT_BEGIN_NAMESPACE
-class QQuickCanvasNode : public QSGSimpleTextureNode
-{
-public:
- QQuickCanvasNode() {
- qsgnode_set_description(this, QStringLiteral("canvasnode"));
- setOwnsTexture(false);
- }
-
- ~QQuickCanvasNode() {
- delete texture();
- }
-};
-
class QQuickCanvasTextureProvider : public QSGTextureProvider
{
public:
- QQuickCanvasNode *node;
- QSGTexture *texture() const Q_DECL_OVERRIDE { return node ? node->texture() : 0; }
+ QSGTexture *tex;
+ QSGTexture *texture() const Q_DECL_OVERRIDE { return tex; }
void fireTextureChanged() { emit textureChanged(); }
};
@@ -187,7 +174,8 @@ public:
QUrl baseUrl;
QMap<int, QV4::PersistentValue> animationCallbacks;
mutable QQuickCanvasTextureProvider *textureProvider;
- QQuickCanvasNode *node;
+ QSGImageNode *node;
+ QSGTexture *nodeTexture;
};
QQuickCanvasItemPrivate::QQuickCanvasItemPrivate()
@@ -203,6 +191,7 @@ QQuickCanvasItemPrivate::QQuickCanvasItemPrivate()
, renderStrategy(QQuickCanvasItem::Immediate)
, textureProvider(0)
, node(0)
+ , nodeTexture(0)
{
implicitAntialiasing = true;
}
@@ -256,17 +245,18 @@ QQuickCanvasItemPrivate::~QQuickCanvasItemPrivate()
The Canvas item supports two render targets: \c Canvas.Image and
\c Canvas.FramebufferObject.
- The \c Canvas.Image render target is a \a QImage object. This render
- target supports background thread rendering, allowing complex or long
- running painting to be executed without blocking the UI.
+ The \c Canvas.Image render target is a \a QImage object. This render target
+ supports background thread rendering, allowing complex or long running
+ painting to be executed without blocking the UI. This is the only render
+ target that is supported by all Qt Quick backends.
The Canvas.FramebufferObject render target utilizes OpenGL hardware
acceleration rather than rendering into system memory, which in many cases
- results in faster rendering. Canvas.FramebufferObject relies on the
- OpenGL extensions \c GL_EXT_framebuffer_multisample and
- \c GL_EXT_framebuffer_blit for antialiasing. It will also use more
- graphics memory when rendering strategy is anything other than
- Canvas.Cooperative.
+ results in faster rendering. Canvas.FramebufferObject relies on the OpenGL
+ extensions \c GL_EXT_framebuffer_multisample and \c GL_EXT_framebuffer_blit
+ for antialiasing. It will also use more graphics memory when rendering
+ strategy is anything other than Canvas.Cooperative. Framebuffer objects may
+ not be available with Qt Quick backends other than OpenGL.
The default render target is Canvas.Image and the default renderStrategy is
Canvas.Immediate.
@@ -301,7 +291,14 @@ QQuickCanvasItemPrivate::~QQuickCanvasItemPrivate()
and can be used directly in \l {ShaderEffect}{ShaderEffects} and other
classes that consume texture providers.
- \sa Context2D
+ \note In general large canvases, frequent updates, and animation should be
+ avoided with the Canvas.Image render target. This is because with
+ accelerated graphics APIs each update will lead to a texture upload. Also,
+ if possible, prefer QQuickPaintedItem and implement drawing in C++ via
+ QPainter instead of the more expensive and likely less performing
+ JavaScript and Context2D approach.
+
+ \sa Context2D QQuickPaintedItem
*/
QQuickCanvasItem::QQuickCanvasItem(QQuickItem *parent)
@@ -735,16 +732,16 @@ QSGNode *QQuickCanvasItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData
if (!d->context || d->canvasWindow.size().isEmpty()) {
if (d->textureProvider) {
- d->textureProvider->node = 0;
+ d->textureProvider->tex = 0;
d->textureProvider->fireTextureChanged();
}
delete oldNode;
return 0;
}
- QQuickCanvasNode *node = static_cast<QQuickCanvasNode*>(oldNode);
+ QSGImageNode *node = static_cast<QSGImageNode *>(oldNode);
if (!node) {
- node = new QQuickCanvasNode();
+ node = QQuickWindowPrivate::get(window())->context->sceneGraphContext()->createImageNode();
d->node = node;
}
@@ -761,22 +758,27 @@ QSGNode *QQuickCanvasItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData
QQuickContext2D *ctx = qobject_cast<QQuickContext2D *>(d->context);
QQuickContext2DTexture *factory = ctx->texture();
- QSGTexture *texture = factory->textureForNextFrame(node->texture(), window());
+ QSGTexture *texture = factory->textureForNextFrame(d->nodeTexture, window());
if (!texture) {
delete node;
d->node = 0;
+ delete d->nodeTexture;
+ d->nodeTexture = 0;
if (d->textureProvider) {
- d->textureProvider->node = 0;
+ d->textureProvider->tex = 0;
d->textureProvider->fireTextureChanged();
}
return 0;
}
+ d->nodeTexture = texture;
node->setTexture(texture);
- node->setRect(QRectF(QPoint(0, 0), d->canvasWindow.size()));
+ node->setTargetRect(QRectF(QPoint(0, 0), d->canvasWindow.size()));
+ node->setInnerTargetRect(QRectF(QPoint(0, 0), d->canvasWindow.size()));
+ node->update();
if (d->textureProvider) {
- d->textureProvider->node = node;
+ d->textureProvider->tex = d->nodeTexture;
d->textureProvider->fireTextureChanged();
}
return node;
@@ -796,14 +798,17 @@ QSGTextureProvider *QQuickCanvasItem::textureProvider() const
return QQuickItem::textureProvider();
Q_D(const QQuickCanvasItem);
+#ifndef QT_NO_OPENGL
QQuickWindow *w = window();
- if (!w || !w->openglContext() || QThread::currentThread() != w->openglContext()->thread()) {
+ if (!w || !w->isSceneGraphInitialized()
+ || QThread::currentThread() != QQuickWindowPrivate::get(w)->context->thread()) {
qWarning("QQuickCanvasItem::textureProvider: can only be queried on the rendering thread of an exposed window");
return 0;
}
+#endif
if (!d->textureProvider)
d->textureProvider = new QQuickCanvasTextureProvider;
- d->textureProvider->node = d->node;
+ d->textureProvider->tex = d->nodeTexture;
return d->textureProvider;
}
diff --git a/src/quick/items/context2d/qquickcontext2d.cpp b/src/quick/items/context2d/qquickcontext2d.cpp
index b2117d3eb9..93ca8e9358 100644
--- a/src/quick/items/context2d/qquickcontext2d.cpp
+++ b/src/quick/items/context2d/qquickcontext2d.cpp
@@ -43,6 +43,7 @@
#include <private/qquickcontext2dtexture_p.h>
#include <private/qquickitem_p.h>
#include <QtQuick/private/qquickshadereffectsource_p.h>
+#include <qsgrendererinterface.h>
#include <QtQuick/private/qsgcontext_p.h>
#include <private/qquicksvgparser_p.h>
@@ -74,6 +75,10 @@
#include <private/qguiapplication_p.h>
#include <qpa/qplatformintegration.h>
+#ifndef QT_NO_OPENGL
+# include <private/qsgdefaultrendercontext_p.h>
+#endif
+
#include <cmath>
#if defined(Q_OS_QNX) || defined(Q_OS_ANDROID)
#include <ctype.h>
@@ -3980,10 +3985,12 @@ public:
~QQuickContext2DThreadCleanup()
{
+#ifndef QT_NO_OPENGL
context->makeCurrent(surface);
delete texture;
context->doneCurrent();
delete context;
+#endif
surface->deleteLater();
}
@@ -4019,6 +4026,7 @@ QQuickContext2D::~QQuickContext2D()
delete m_buffer;
if (m_renderTarget == QQuickCanvasItem::FramebufferObject) {
+#ifndef QT_NO_OPENGL
if (m_renderStrategy == QQuickCanvasItem::Immediate && m_glContext) {
Q_ASSERT(QThread::currentThread() == m_glContext->thread());
m_glContext->makeCurrent(m_surface.data());
@@ -4039,6 +4047,7 @@ QQuickContext2D::~QQuickContext2D()
m_texture->deleteLater();
}
}
+#endif
} else {
// Image based does not have GL resources, but must still be deleted
// on its designated thread after it has completed whatever it might
@@ -4064,8 +4073,6 @@ void QQuickContext2D::init(QQuickCanvasItem *canvasItem, const QVariantMap &args
m_canvas = canvasItem;
m_renderTarget = canvasItem->renderTarget();
-
- QQuickWindow *window = canvasItem->window();
m_renderStrategy = canvasItem->renderStrategy();
#ifdef Q_OS_WIN
@@ -4084,12 +4091,24 @@ void QQuickContext2D::init(QQuickCanvasItem *canvasItem, const QVariantMap &args
m_renderTarget = QQuickCanvasItem::Image;
}
+ // Disable Framebuffer Object based rendering when not running with OpenGL
+ if (m_renderTarget == QQuickCanvasItem::FramebufferObject) {
+ QSGRendererInterface *rif = canvasItem->window()->rendererInterface();
+ if (rif && rif->graphicsAPI() != QSGRendererInterface::OpenGL)
+ m_renderTarget = QQuickCanvasItem::Image;
+ }
+
switch (m_renderTarget) {
case QQuickCanvasItem::Image:
m_texture = new QQuickContext2DImageTexture;
break;
case QQuickCanvasItem::FramebufferObject:
+#ifndef QT_NO_OPENGL
m_texture = new QQuickContext2DFBOTexture;
+#else
+ // It shouldn't be possible to use a FramebufferObject without OpenGL
+ m_texture = nullptr;
+#endif
break;
}
@@ -4103,18 +4122,27 @@ void QQuickContext2D::init(QQuickCanvasItem *canvasItem, const QVariantMap &args
m_thread = QThread::currentThread();
QThread *renderThread = m_thread;
- QThread *sceneGraphThread = window->openglContext() ? window->openglContext()->thread() : 0;
+#ifndef QT_NO_OPENGL
+ QQuickWindow *window = canvasItem->window();
+ QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window);
+ QThread *sceneGraphThread = wd->context->thread();
if (m_renderStrategy == QQuickCanvasItem::Threaded)
renderThread = QQuickContext2DRenderThread::instance(qmlEngine(canvasItem));
else if (m_renderStrategy == QQuickCanvasItem::Cooperative)
renderThread = sceneGraphThread;
+#else
+ if (m_renderStrategy == QQuickCanvasItem::Threaded)
+ renderThread = QQuickContext2DRenderThread::instance(qmlEngine(canvasItem));
+#endif
+
if (renderThread && renderThread != QThread::currentThread())
m_texture->moveToThread(renderThread);
-
+#ifndef QT_NO_OPENGL
if (m_renderTarget == QQuickCanvasItem::FramebufferObject && renderThread != sceneGraphThread) {
- QOpenGLContext *cc = QQuickWindowPrivate::get(window)->context->openglContext();
+ auto openglRenderContext = static_cast<const QSGDefaultRenderContext *>(QQuickWindowPrivate::get(window)->context);
+ QOpenGLContext *cc = openglRenderContext->openglContext();
m_surface.reset(new QOffscreenSurface);
m_surface->setFormat(window->format());
m_surface->create();
@@ -4125,7 +4153,7 @@ void QQuickContext2D::init(QQuickCanvasItem *canvasItem, const QVariantMap &args
m_glContext->moveToThread(renderThread);
m_texture->initializeOpenGL(m_glContext, m_surface.data());
}
-
+#endif
connect(m_texture, SIGNAL(textureChanged()), SIGNAL(textureChanged()));
reset();
@@ -4172,6 +4200,7 @@ QImage QQuickContext2D::toImage(const QRectF& bounds)
flush();
m_texture->grabImage(bounds);
} else {
+#ifndef QT_NO_OPENGL
QQuickWindow *window = m_canvas->window();
QOpenGLContext *ctx = window ? window->openglContext() : 0;
if (ctx && ctx->isValid()) {
@@ -4187,6 +4216,10 @@ QImage QQuickContext2D::toImage(const QRectF& bounds)
qWarning() << "Cannot read pixels from canvas before opengl context is valid";
return QImage();
}
+#else
+ flush();
+ m_texture->grabImage(bounds);
+#endif
}
} else if (m_renderStrategy == QQuickCanvasItem::Cooperative) {
qWarning() << "Pixel readback is not supported in Cooperative mode, please try Threaded or Immediate mode";
diff --git a/src/quick/items/context2d/qquickcontext2dcommandbuffer.cpp b/src/quick/items/context2d/qquickcontext2dcommandbuffer.cpp
index 58b374e696..8d659040b3 100644
--- a/src/quick/items/context2d/qquickcontext2dcommandbuffer.cpp
+++ b/src/quick/items/context2d/qquickcontext2dcommandbuffer.cpp
@@ -42,9 +42,11 @@
#include <qqml.h>
#include <QtCore/QMutex>
#include <QtQuick/qsgtexture.h>
-#include <QtGui/QOpenGLContext>
#include <QtGui/QPaintEngine>
-#include <QtGui/private/qopenglpaintengine_p.h>
+#ifndef QT_NO_OPENGL
+# include <QtGui/QOpenGLContext>
+# include <QtGui/private/qopenglpaintengine_p.h>
+#endif
#define HAS_SHADOW(offsetX, offsetY, blur, color) (color.isValid() && color.alpha() && (blur || offsetX || offsetY))
diff --git a/src/quick/items/context2d/qquickcontext2dtexture.cpp b/src/quick/items/context2d/qquickcontext2dtexture.cpp
index f3513f447a..ddecf7c3d4 100644
--- a/src/quick/items/context2d/qquickcontext2dtexture.cpp
+++ b/src/quick/items/context2d/qquickcontext2dtexture.cpp
@@ -44,14 +44,16 @@
#include <QtQuick/private/qsgtexture_p.h>
#include "qquickcontext2dcommandbuffer_p.h"
#include <QOpenGLPaintDevice>
-
+#ifndef QT_NO_OPENGL
#include <QOpenGLFramebufferObject>
#include <QOpenGLFramebufferObjectFormat>
+#include <QOpenGLFunctions>
+#endif
#include <QtCore/QThread>
#include <QtGui/QGuiApplication>
QT_BEGIN_NAMESPACE
-
+#ifndef QT_NO_OPENGL
#define QT_MINIMUM_FBO_SIZE 64
static inline int qt_next_power_of_two(int v)
@@ -85,10 +87,12 @@ struct GLAcquireContext {
}
QOpenGLContext *ctx;
};
-
+#endif
QQuickContext2DTexture::QQuickContext2DTexture()
: m_context(0)
+#ifndef QT_NO_OPENGL
, m_gl(0)
+#endif
, m_surface(0)
, m_item(0)
, m_canvasWindowChanged(false)
@@ -250,9 +254,9 @@ void QQuickContext2DTexture::paint(QQuickContext2DCommandBuffer *ccb)
return;
}
QQuickContext2D::mutex.unlock();
-
+#ifndef QT_NO_OPENGL
GLAcquireContext currentContext(m_gl, m_surface);
-
+#endif
if (!m_tiledCanvas) {
paintWithoutTiles(ccb);
delete ccb;
@@ -379,7 +383,7 @@ bool QQuickContext2DTexture::event(QEvent *e)
}
return QObject::event(e);
}
-
+#ifndef QT_NO_OPENGL
static inline QSize npotAdjustedSize(const QSize &size)
{
static bool checked = false;
@@ -646,6 +650,7 @@ void QQuickContext2DFBOTexture::endPainting()
m_fbo->bindDefault();
}
+#endif
QQuickContext2DImageTexture::QQuickContext2DImageTexture()
: QQuickContext2DTexture()
diff --git a/src/quick/items/context2d/qquickcontext2dtexture_p.h b/src/quick/items/context2d/qquickcontext2dtexture_p.h
index edc7283283..ed38382892 100644
--- a/src/quick/items/context2d/qquickcontext2dtexture_p.h
+++ b/src/quick/items/context2d/qquickcontext2dtexture_p.h
@@ -54,10 +54,10 @@
#include <QtQuick/qsgtexture.h>
#include "qquickcanvasitem_p.h"
#include "qquickcontext2d_p.h"
-
-#include <QOpenGLContext>
-#include <QOpenGLFramebufferObject>
-
+#ifndef QT_NO_OPENGL
+# include <QOpenGLContext>
+# include <QOpenGLFramebufferObject>
+#endif
#include <QtCore/QMutex>
#include <QtCore/QWaitCondition>
#include <QtCore/QThread>
@@ -121,11 +121,12 @@ public:
// Called during sync() on the scene graph thread while GUI is blocked.
virtual QSGTexture *textureForNextFrame(QSGTexture *lastFrame, QQuickWindow *window) = 0;
bool event(QEvent *e);
-
+#ifndef QT_NO_OPENGL
void initializeOpenGL(QOpenGLContext *gl, QOffscreenSurface *s) {
m_gl = gl;
m_surface = s;
}
+#endif
Q_SIGNALS:
void textureChanged();
@@ -152,8 +153,9 @@ protected:
QList<QQuickContext2DTile*> m_tiles;
QQuickContext2D *m_context;
-
+#ifndef QT_NO_OPENGL
QOpenGLContext *m_gl;
+#endif
QSurface *m_surface;
QQuickContext2D::State m_state;
@@ -174,7 +176,7 @@ protected:
uint m_painting : 1;
uint m_onCustomThread : 1; // Not GUI and not SGRender
};
-
+#ifndef QT_NO_OPENGL
class QQuickContext2DFBOTexture : public QQuickContext2DTexture
{
Q_OBJECT
@@ -209,7 +211,7 @@ private:
GLuint m_displayTextures[2];
int m_displayTexture;
};
-
+#endif
class QSGPlainTexture;
class QQuickContext2DImageTexture : public QQuickContext2DTexture
{
diff --git a/src/quick/items/context2d/qquickcontext2dtile.cpp b/src/quick/items/context2d/qquickcontext2dtile.cpp
index a1503762de..95b6c9d961 100644
--- a/src/quick/items/context2d/qquickcontext2dtile.cpp
+++ b/src/quick/items/context2d/qquickcontext2dtile.cpp
@@ -38,10 +38,11 @@
****************************************************************************/
#include "qquickcontext2dtile_p.h"
-
-#include <QOpenGLFramebufferObject>
-#include <QOpenGLFramebufferObjectFormat>
-#include <QOpenGLPaintDevice>
+#ifndef QT_NO_OPENGL
+# include <QOpenGLFramebufferObject>
+# include <QOpenGLFramebufferObjectFormat>
+# include <QOpenGLPaintDevice>
+#endif
QT_BEGIN_NAMESPACE
@@ -96,7 +97,7 @@ QPainter* QQuickContext2DTile::createPainter(bool smooth, bool antialiasing)
return 0;
}
-
+#ifndef QT_NO_OPENGL
QQuickContext2DFBOTile::QQuickContext2DFBOTile()
: QQuickContext2DTile()
, m_fbo(0)
@@ -146,7 +147,7 @@ void QQuickContext2DFBOTile::setRect(const QRect& r)
m_fbo = new QOpenGLFramebufferObject(r.size(), format);
}
}
-
+#endif
QQuickContext2DImageTile::QQuickContext2DImageTile()
: QQuickContext2DTile()
diff --git a/src/quick/items/context2d/qquickcontext2dtile_p.h b/src/quick/items/context2d/qquickcontext2dtile_p.h
index d9be2023c4..a87202daae 100644
--- a/src/quick/items/context2d/qquickcontext2dtile_p.h
+++ b/src/quick/items/context2d/qquickcontext2dtile_p.h
@@ -52,8 +52,9 @@
//
#include "qquickcontext2d_p.h"
-#include <QOpenGLFramebufferObject>
-
+#ifndef QT_NO_OPENGL
+# include <QOpenGLFramebufferObject>
+#endif
QT_BEGIN_NAMESPACE
class QQuickContext2DTexture;
@@ -82,7 +83,7 @@ protected:
QPainter m_painter;
};
-
+#ifndef QT_NO_OPENGL
class QQuickContext2DFBOTile : public QQuickContext2DTile
{
public:
@@ -99,7 +100,7 @@ private:
QOpenGLFramebufferObject *m_fbo;
};
-
+#endif
class QQuickContext2DImageTile : public QQuickContext2DTile
{
public:
diff --git a/src/quick/items/items.pri b/src/quick/items/items.pri
index ab966b6ccc..1c17e7e34d 100644
--- a/src/quick/items/items.pri
+++ b/src/quick/items/items.pri
@@ -63,10 +63,6 @@ HEADERS += \
$$PWD/qquickstateoperations_p.h \
$$PWD/qquickimplicitsizeitem_p.h \
$$PWD/qquickimplicitsizeitem_p_p.h \
- $$PWD/qquickspriteengine_p.h \
- $$PWD/qquicksprite_p.h \
- $$PWD/qquickspritesequence_p.h \
- $$PWD/qquickanimatedsprite_p.h \
$$PWD/qquickdrag_p.h \
$$PWD/qquickdroparea_p.h \
$$PWD/qquickmultipointtoucharea_p.h \
@@ -76,11 +72,13 @@ HEADERS += \
$$PWD/qquickscreen_p.h \
$$PWD/qquickwindowattached_p.h \
$$PWD/qquickwindowmodule_p.h \
- $$PWD/qquickframebufferobject.h \
- $$PWD/qquickitemgrabresult.h \
+ $$PWD/qquickshadereffectsource_p.h \
+ $$PWD/qquickshadereffectmesh_p.h \
+ $$PWD/qquickshadereffect_p.h \
+ $$PWD/qquickgenericshadereffect_p.h \
$$PWD/qquickrendercontrol.h \
$$PWD/qquickrendercontrol_p.h \
- $$PWD/qquickopenglinfo_p.h
+ $$PWD/qquickrendererinfo_p.h
SOURCES += \
$$PWD/qquickevents.cpp \
@@ -120,10 +118,6 @@ SOURCES += \
$$PWD/qquickitemanimation.cpp \
$$PWD/qquickstateoperations.cpp \
$$PWD/qquickimplicitsizeitem.cpp \
- $$PWD/qquickspriteengine.cpp \
- $$PWD/qquicksprite.cpp \
- $$PWD/qquickspritesequence.cpp \
- $$PWD/qquickanimatedsprite.cpp \
$$PWD/qquickaccessibleattached.cpp \
$$PWD/qquickdrag.cpp \
$$PWD/qquickdroparea.cpp \
@@ -133,36 +127,51 @@ SOURCES += \
$$PWD/qquickwindowmodule.cpp \
$$PWD/qquickscreen.cpp \
$$PWD/qquickwindowattached.cpp \
- $$PWD/qquickframebufferobject.cpp \
- $$PWD/qquickitemgrabresult.cpp \
+ $$PWD/qquickshadereffectsource.cpp \
+ $$PWD/qquickshadereffectmesh.cpp \
+ $$PWD/qquickshadereffect.cpp \
+ $$PWD/qquickgenericshadereffect.cpp \
$$PWD/qquickrendercontrol.cpp \
- $$PWD/qquickopenglinfo.cpp
+ $$PWD/qquickrendererinfo.cpp
-SOURCES += \
- $$PWD/qquickshadereffect.cpp \
- $$PWD/qquickshadereffectmesh.cpp \
- $$PWD/qquickshadereffectnode.cpp \
- $$PWD/qquickshadereffectsource.cpp \
+# Items that depend on OpenGL Renderer
+contains(QT_CONFIG, opengl(es1|es2)?) {
+ SOURCES += \
+ $$PWD/qquickopenglinfo.cpp \
+ $$PWD/qquickopenglshadereffect.cpp \
+ $$PWD/qquickopenglshadereffectnode.cpp \
+ $$PWD/qquickframebufferobject.cpp \
+ $$PWD/qquickspriteengine.cpp \
+ $$PWD/qquicksprite.cpp \
+ $$PWD/qquickspritesequence.cpp \
+ $$PWD/qquickanimatedsprite.cpp \
+ $$PWD/qquickitemgrabresult.cpp
-HEADERS += \
- $$PWD/qquickshadereffect_p.h \
- $$PWD/qquickshadereffectmesh_p.h \
- $$PWD/qquickshadereffectnode_p.h \
- $$PWD/qquickshadereffectsource_p.h \
+ HEADERS += \
+ $$PWD/qquickopenglinfo_p.h \
+ $$PWD/qquickspriteengine_p.h \
+ $$PWD/qquicksprite_p.h \
+ $$PWD/qquickspritesequence_p.h \
+ $$PWD/qquickanimatedsprite_p.h \
+ $$PWD/qquickopenglshadereffect_p.h \
+ $$PWD/qquickopenglshadereffectnode_p.h \
+ $$PWD/qquickframebufferobject.h \
+ $$PWD/qquickitemgrabresult.h
-OTHER_FILES += \
- $$PWD/shaders/sprite.vert \
- $$PWD/shaders/sprite.frag \
- $$PWD/shaders/shadereffect.vert \
- $$PWD/shaders/shadereffect.frag \
- $$PWD/shaders/shadereffectfallback.vert \
- $$PWD/shaders/shadereffectfallback.frag \
- $$PWD/shaders/sprite_core.vert \
- $$PWD/shaders/sprite_core.frag \
- $$PWD/shaders/shadereffect_core.vert \
- $$PWD/shaders/shadereffect_core.frag \
- $$PWD/shaders/shadereffectfallback_core.vert \
- $$PWD/shaders/shadereffectfallback_core.frag
+ OTHER_FILES += \
+ $$PWD/shaders/sprite.vert \
+ $$PWD/shaders/sprite.frag \
+ $$PWD/shaders/shadereffect.vert \
+ $$PWD/shaders/shadereffect.frag \
+ $$PWD/shaders/shadereffectfallback.vert \
+ $$PWD/shaders/shadereffectfallback.frag \
+ $$PWD/shaders/sprite_core.vert \
+ $$PWD/shaders/sprite_core.frag \
+ $$PWD/shaders/shadereffect_core.vert \
+ $$PWD/shaders/shadereffect_core.frag \
+ $$PWD/shaders/shadereffectfallback_core.vert \
+ $$PWD/shaders/shadereffectfallback_core.frag
+}
RESOURCES += \
$$PWD/items.qrc
diff --git a/src/quick/items/qquickframebufferobject.cpp b/src/quick/items/qquickframebufferobject.cpp
index 0356b72e1d..d8147a48a5 100644
--- a/src/quick/items/qquickframebufferobject.cpp
+++ b/src/quick/items/qquickframebufferobject.cpp
@@ -40,7 +40,7 @@
#include "qquickframebufferobject.h"
#include <QtGui/QOpenGLFramebufferObject>
-
+#include <QtGui/QOpenGLFunctions>
#include <private/qquickitem_p.h>
#include <QSGSimpleTextureNode>
diff --git a/src/quick/items/qquickgenericshadereffect.cpp b/src/quick/items/qquickgenericshadereffect.cpp
new file mode 100644
index 0000000000..cad3520ed1
--- /dev/null
+++ b/src/quick/items/qquickgenericshadereffect.cpp
@@ -0,0 +1,639 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qquickgenericshadereffect_p.h>
+#include <private/qquickwindow_p.h>
+#include <private/qquickitem_p.h>
+#include <QSignalMapper>
+
+QT_BEGIN_NAMESPACE
+
+// The generic shader effect is used when the scenegraph backend indicates
+// SupportsShaderEffectNode. This, unlike the monolithic and interconnected (e.g.
+// with particles) OpenGL variant, passes most of the work to a scenegraph node
+// created via the adaptation layer, thus allowing different implementation in
+// the backends.
+
+QQuickGenericShaderEffect::QQuickGenericShaderEffect(QQuickShaderEffect *item, QObject *parent)
+ : QObject(parent)
+ , m_item(item)
+ , m_meshResolution(1, 1)
+ , m_mesh(nullptr)
+ , m_cullMode(QQuickShaderEffect::NoCulling)
+ , m_blending(true)
+ , m_supportsAtlasTextures(false)
+ , m_mgr(nullptr)
+ , m_dirty(0)
+{
+ connect(m_item, SIGNAL(windowChanged(QQuickWindow*)), this, SLOT(itemWindowChanged(QQuickWindow*)));
+}
+
+QQuickGenericShaderEffect::~QQuickGenericShaderEffect()
+{
+ for (int i = 0; i < NShader; ++i) {
+ disconnectSignals(Shader(i));
+ for (const auto &sm : qAsConst(m_signalMappers[i]))
+ delete sm.mapper;
+ }
+
+ delete m_mgr;
+}
+
+void QQuickGenericShaderEffect::setFragmentShader(const QByteArray &src)
+{
+ // Compare the actual values since they are often just filenames.
+ // Optimizing by comparing constData() is a bad idea since seemingly static
+ // strings in QML may in fact have different addresses when a binding
+ // triggers assigning the "same" value to the property.
+ if (m_fragShader == src)
+ return;
+
+ m_fragShader = src;
+ m_dirty |= QSGShaderEffectNode::DirtyShaders;
+
+ if (m_item->isComponentComplete())
+ updateShader(Fragment, src);
+
+ m_item->update();
+ emit m_item->fragmentShaderChanged();
+}
+
+void QQuickGenericShaderEffect::setVertexShader(const QByteArray &src)
+{
+ if (m_vertShader == src)
+ return;
+
+ m_vertShader = src;
+ m_dirty |= QSGShaderEffectNode::DirtyShaders;
+
+ if (m_item->isComponentComplete())
+ updateShader(Vertex, src);
+
+ m_item->update();
+ emit m_item->vertexShaderChanged();
+}
+
+void QQuickGenericShaderEffect::setBlending(bool enable)
+{
+ if (m_blending == enable)
+ return;
+
+ m_blending = enable;
+ m_item->update();
+ emit m_item->blendingChanged();
+}
+
+QVariant QQuickGenericShaderEffect::mesh() const
+{
+ return m_mesh ? qVariantFromValue(static_cast<QObject *>(m_mesh))
+ : qVariantFromValue(m_meshResolution);
+}
+
+void QQuickGenericShaderEffect::setMesh(const QVariant &mesh)
+{
+ QQuickShaderEffectMesh *newMesh = qobject_cast<QQuickShaderEffectMesh *>(qvariant_cast<QObject *>(mesh));
+ if (newMesh && newMesh == m_mesh)
+ return;
+
+ if (m_mesh)
+ disconnect(m_mesh, SIGNAL(geometryChanged()), this, 0);
+
+ m_mesh = newMesh;
+
+ if (m_mesh) {
+ connect(m_mesh, SIGNAL(geometryChanged()), this, SLOT(markGeometryDirtyAndUpdate()));
+ } else {
+ if (mesh.canConvert<QSize>()) {
+ m_meshResolution = mesh.toSize();
+ } else {
+ QList<QByteArray> res = mesh.toByteArray().split('x');
+ bool ok = res.size() == 2;
+ if (ok) {
+ int w = res.at(0).toInt(&ok);
+ if (ok) {
+ int h = res.at(1).toInt(&ok);
+ if (ok)
+ m_meshResolution = QSize(w, h);
+ }
+ }
+ if (!ok)
+ qWarning("ShaderEffect: mesh property must be a size or an object deriving from QQuickShaderEffectMesh");
+ }
+ m_defaultMesh.setResolution(m_meshResolution);
+ }
+
+ m_dirty |= QSGShaderEffectNode::DirtyShaderMesh;
+ m_item->update();
+
+ emit m_item->meshChanged();
+}
+
+void QQuickGenericShaderEffect::setCullMode(QQuickShaderEffect::CullMode face)
+{
+ if (m_cullMode == face)
+ return;
+
+ m_cullMode = face;
+ m_item->update();
+ emit m_item->cullModeChanged();
+}
+
+void QQuickGenericShaderEffect::setSupportsAtlasTextures(bool supports)
+{
+ if (m_supportsAtlasTextures == supports)
+ return;
+
+ m_supportsAtlasTextures = supports;
+ markGeometryDirtyAndUpdate();
+ emit m_item->supportsAtlasTexturesChanged();
+}
+
+QString QQuickGenericShaderEffect::log() const
+{
+ QSGGuiThreadShaderEffectManager *mgr = shaderEffectManager();
+ if (!mgr)
+ return QString();
+
+ return mgr->log();
+}
+
+QQuickShaderEffect::Status QQuickGenericShaderEffect::status() const
+{
+ QSGGuiThreadShaderEffectManager *mgr = shaderEffectManager();
+ if (!mgr)
+ return QQuickShaderEffect::Uncompiled;
+
+ return QQuickShaderEffect::Status(mgr->status());
+}
+
+QQuickShaderEffect::ShaderType QQuickGenericShaderEffect::shaderType() const
+{
+ QSGGuiThreadShaderEffectManager *mgr = shaderEffectManager();
+ if (!mgr)
+ return QQuickShaderEffect::ShaderType(0);
+
+ return QQuickShaderEffect::ShaderType(mgr->shaderType());
+}
+
+QQuickShaderEffect::ShaderCompilationType QQuickGenericShaderEffect::shaderCompilationType() const
+{
+ QSGGuiThreadShaderEffectManager *mgr = shaderEffectManager();
+ if (!mgr)
+ return QQuickShaderEffect::ShaderCompilationType(0);
+
+ return QQuickShaderEffect::ShaderCompilationType(mgr->shaderCompilationType());
+}
+
+QQuickShaderEffect::ShaderSourceType QQuickGenericShaderEffect::shaderSourceType() const
+{
+ QSGGuiThreadShaderEffectManager *mgr = shaderEffectManager();
+ if (!mgr)
+ return QQuickShaderEffect::ShaderSourceType(0);
+
+ return QQuickShaderEffect::ShaderSourceType(mgr->shaderSourceType());
+}
+
+void QQuickGenericShaderEffect::handleEvent(QEvent *event)
+{
+ if (event->type() == QEvent::DynamicPropertyChange) {
+ QDynamicPropertyChangeEvent *e = static_cast<QDynamicPropertyChangeEvent *>(event);
+ for (int shaderType = 0; shaderType < NShader; ++shaderType) {
+ const auto &vars(m_shaders[shaderType].shaderInfo.variables);
+ for (int idx = 0; idx < vars.count(); ++idx) {
+ if (vars[idx].name == e->propertyName()) {
+ propertyChanged((shaderType << 16) | idx);
+ break;
+ }
+ }
+ }
+ }
+}
+
+void QQuickGenericShaderEffect::handleGeometryChanged(const QRectF &, const QRectF &)
+{
+ m_dirty |= QSGShaderEffectNode::DirtyShaderGeometry;
+}
+
+QSGNode *QQuickGenericShaderEffect::handleUpdatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *)
+{
+ QSGShaderEffectNode *node = static_cast<QSGShaderEffectNode *>(oldNode);
+
+ if (m_item->width() <= 0 || m_item->height() <= 0) {
+ delete node;
+ return nullptr;
+ }
+
+ // The manager should be already created on the gui thread. Just take that instance.
+ QSGGuiThreadShaderEffectManager *mgr = shaderEffectManager();
+ if (!mgr) {
+ delete node;
+ return nullptr;
+ }
+
+ if (!node) {
+ QSGRenderContext *rc = QQuickWindowPrivate::get(m_item->window())->context;
+ node = rc->sceneGraphContext()->createShaderEffectNode(rc, mgr);
+ m_dirty = QSGShaderEffectNode::DirtyShaderAll;
+ }
+
+ QSGShaderEffectNode::SyncData sd;
+ sd.dirty = m_dirty;
+ sd.cullMode = QSGShaderEffectNode::CullMode(m_cullMode);
+ sd.blending = m_blending;
+ sd.vertex.shader = &m_shaders[Vertex];
+ sd.vertex.dirtyConstants = &m_dirtyConstants[Vertex];
+ sd.vertex.dirtyTextures = &m_dirtyTextures[Vertex];
+ sd.fragment.shader = &m_shaders[Fragment];
+ sd.fragment.dirtyConstants = &m_dirtyConstants[Fragment];
+ sd.fragment.dirtyTextures = &m_dirtyTextures[Fragment];
+ node->syncMaterial(&sd);
+
+ if (m_dirty & QSGShaderEffectNode::DirtyShaderMesh) {
+ node->setGeometry(nullptr);
+ m_dirty &= ~QSGShaderEffectNode::DirtyShaderMesh;
+ m_dirty |= QSGShaderEffectNode::DirtyShaderGeometry;
+ }
+
+ if (m_dirty & QSGShaderEffectNode::DirtyShaderGeometry) {
+ const QRectF rect(0, 0, m_item->width(), m_item->height());
+ QQuickShaderEffectMesh *mesh = m_mesh ? m_mesh : &m_defaultMesh;
+ QSGGeometry *geometry = node->geometry();
+
+ const QRectF srcRect = node->updateNormalizedTextureSubRect(m_supportsAtlasTextures);
+ geometry = mesh->updateGeometry(geometry, 2, 0, srcRect, rect);
+
+ node->setFlag(QSGNode::OwnsGeometry, false);
+ node->setGeometry(geometry);
+ node->setFlag(QSGNode::OwnsGeometry, true);
+
+ m_dirty &= ~QSGShaderEffectNode::DirtyShaderGeometry;
+ }
+
+ m_dirty = 0;
+ for (int i = 0; i < NShader; ++i) {
+ m_dirtyConstants[i].clear();
+ m_dirtyTextures[i].clear();
+ }
+
+ return node;
+}
+
+void QQuickGenericShaderEffect::handleComponentComplete()
+{
+ updateShader(Vertex, m_vertShader);
+ updateShader(Fragment, m_fragShader);
+}
+
+void QQuickGenericShaderEffect::handleItemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value)
+{
+ // Move the window ref.
+ if (change == QQuickItem::ItemSceneChange) {
+ for (int shaderType = 0; shaderType < NShader; ++shaderType) {
+ for (const auto &vd : qAsConst(m_shaders[shaderType].varData)) {
+ if (vd.specialType == QSGShaderEffectNode::VariableData::Source) {
+ QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(vd.value));
+ if (source) {
+ if (value.window)
+ QQuickItemPrivate::get(source)->refWindow(value.window);
+ else
+ QQuickItemPrivate::get(source)->derefWindow();
+ }
+ }
+ }
+ }
+ }
+}
+
+QSGGuiThreadShaderEffectManager *QQuickGenericShaderEffect::shaderEffectManager() const
+{
+ if (!m_mgr) {
+ // return null if this is not the gui thread and not already created
+ if (QThread::currentThread() != m_item->thread())
+ return m_mgr;
+ // need a window and a rendercontext (i.e. the scenegraph backend is ready)
+ QQuickWindow *w = m_item->window();
+ if (w && w->isSceneGraphInitialized()) {
+ m_mgr = QQuickWindowPrivate::get(w)->context->sceneGraphContext()->createGuiThreadShaderEffectManager();
+ if (m_mgr) {
+ connect(m_mgr, SIGNAL(logAndStatusChanged()), m_item, SIGNAL(logChanged()));
+ connect(m_mgr, SIGNAL(logAndStatusChanged()), m_item, SIGNAL(statusChanged()));
+ connect(m_mgr, SIGNAL(textureChanged()), this, SLOT(markGeometryDirtyAndUpdateIfSupportsAtlas()));
+ }
+ } else if (!w) {
+ // Wait until itemWindowChanged() gets called. Return null for now.
+ } else {
+ // Have window, but no scenegraph -> ensure the signal is connected. Return null for now.
+ const_cast<QQuickGenericShaderEffect *>(this)->itemWindowChanged(w);
+ }
+ }
+
+ return m_mgr;
+}
+
+void QQuickGenericShaderEffect::itemWindowChanged(QQuickWindow *w)
+{
+ if (w) {
+ if (w->isSceneGraphInitialized())
+ backendChanged();
+ else
+ connect(w, SIGNAL(sceneGraphInitialized()), this, SLOT(backendChanged()), Qt::UniqueConnection);
+ }
+}
+
+void QQuickGenericShaderEffect::backendChanged()
+{
+ disconnect(m_item->window(), SIGNAL(sceneGraphInitialized()), this, SLOT(backendChanged()));
+ emit m_item->logChanged();
+ emit m_item->statusChanged();
+ emit m_item->shaderTypeChanged();
+ emit m_item->shaderCompilationTypeChanged();
+ emit m_item->shaderSourceTypeChanged();
+}
+
+void QQuickGenericShaderEffect::disconnectSignals(Shader shaderType)
+{
+ for (auto &sm : m_signalMappers[shaderType]) {
+ if (sm.active) {
+ sm.active = false;
+ QObject::disconnect(m_item, nullptr, sm.mapper, SLOT(map()));
+ QObject::disconnect(sm.mapper, SIGNAL(mapped(int)), this, SLOT(propertyChanged(int)));
+ }
+ }
+ for (const auto &vd : qAsConst(m_shaders[shaderType].varData)) {
+ if (vd.specialType == QSGShaderEffectNode::VariableData::Source) {
+ QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(vd.value));
+ if (source) {
+ if (m_item->window())
+ QQuickItemPrivate::get(source)->derefWindow();
+ QObject::disconnect(source, SIGNAL(destroyed(QObject*)), this, SLOT(sourceDestroyed(QObject*)));
+ }
+ }
+ }
+}
+
+struct ReflectCache
+{
+ bool contains(const QByteArray &key) const
+ {
+ return m_reflectCache.contains(key);
+ }
+
+ QSGGuiThreadShaderEffectManager::ShaderInfo value(const QByteArray &key) const
+ {
+ return m_reflectCache.value(key);
+ }
+
+ void insert(const QByteArray &key, const QSGGuiThreadShaderEffectManager::ShaderInfo &value)
+ {
+ m_reflectCache.insert(key, value);
+ }
+
+ QHash<QByteArray, QSGGuiThreadShaderEffectManager::ShaderInfo> m_reflectCache;
+};
+
+Q_GLOBAL_STATIC(ReflectCache, reflectCache)
+
+void QQuickGenericShaderEffect::updateShader(Shader shaderType, const QByteArray &src)
+{
+ QSGGuiThreadShaderEffectManager *mgr = shaderEffectManager();
+ if (!mgr)
+ return;
+
+ const bool texturesSeparate = mgr->hasSeparateSamplerAndTextureObjects();
+
+ disconnectSignals(shaderType);
+
+ m_shaders[shaderType].shaderInfo = QSGGuiThreadShaderEffectManager::ShaderInfo();
+ m_shaders[shaderType].varData.clear();
+
+ if (!src.isEmpty()) {
+ // Figure out what input parameters and variables are used in the shader.
+ // For file-based shader source/bytecode this is where the data is pulled
+ // in from the file.
+ if (reflectCache()->contains(src)) {
+ m_shaders[shaderType].shaderInfo = reflectCache()->value(src);
+ } else {
+ QSGGuiThreadShaderEffectManager::ShaderInfo shaderInfo;
+ if (!mgr->reflect(src, &shaderInfo)) {
+ qWarning("ShaderEffect: shader reflection failed for %s", src.constData());
+ m_shaders[shaderType].hasShaderCode = false;
+ return;
+ }
+ m_shaders[shaderType].shaderInfo = shaderInfo;
+ reflectCache()->insert(src, shaderInfo);
+ }
+ m_shaders[shaderType].hasShaderCode = true;
+ } else {
+ m_shaders[shaderType].hasShaderCode = false;
+ if (shaderType == Fragment) {
+ // With built-in shaders hasShaderCode is set to false and all
+ // metadata is empty, as it is left up to the node to provide a
+ // built-in default shader and its metadata. However, in case of
+ // the built-in fragment shader the value for 'source' has to be
+ // provided and monitored like with an application-provided shader.
+ QSGGuiThreadShaderEffectManager::ShaderInfo::Variable v;
+ v.name = QByteArrayLiteral("source");
+ v.bindPoint = 0;
+ v.type = texturesSeparate ? QSGGuiThreadShaderEffectManager::ShaderInfo::Texture
+ : QSGGuiThreadShaderEffectManager::ShaderInfo::Sampler;
+ m_shaders[shaderType].shaderInfo.variables.append(v);
+ }
+ }
+
+ const int varCount = m_shaders[shaderType].shaderInfo.variables.count();
+ m_shaders[shaderType].varData.resize(varCount);
+
+ // Reuse signal mappers as much as possible since the mapping is based on
+ // the index and shader type which are both constant.
+ if (m_signalMappers[shaderType].count() < varCount)
+ m_signalMappers[shaderType].resize(varCount);
+
+ // Hook up the signals to get notified about changes for properties that
+ // correspond to variables in the shader. Store also the values.
+ for (int i = 0; i < varCount; ++i) {
+ const auto &v(m_shaders[shaderType].shaderInfo.variables.at(i));
+ QSGShaderEffectNode::VariableData &vd(m_shaders[shaderType].varData[i]);
+ const bool isSpecial = v.name.startsWith("qt_"); // special names not mapped to properties
+ if (isSpecial) {
+ if (v.name == QByteArrayLiteral("qt_Opacity"))
+ vd.specialType = QSGShaderEffectNode::VariableData::Opacity;
+ else if (v.name == QByteArrayLiteral("qt_Matrix"))
+ vd.specialType = QSGShaderEffectNode::VariableData::Matrix;
+ else if (v.name.startsWith("qt_SubRect_"))
+ vd.specialType = QSGShaderEffectNode::VariableData::SubRect;
+ continue;
+ }
+
+ // The value of a property corresponding to a sampler is the source
+ // item ref, unless there are separate texture objects in which case
+ // the sampler is ignored (here).
+ if (v.type == QSGGuiThreadShaderEffectManager::ShaderInfo::Sampler) {
+ if (texturesSeparate) {
+ vd.specialType = QSGShaderEffectNode::VariableData::Unused;
+ continue;
+ } else {
+ vd.specialType = QSGShaderEffectNode::VariableData::Source;
+ }
+ } else if (v.type == QSGGuiThreadShaderEffectManager::ShaderInfo::Texture) {
+ Q_ASSERT(texturesSeparate);
+ vd.specialType = QSGShaderEffectNode::VariableData::Source;
+ } else {
+ vd.specialType = QSGShaderEffectNode::VariableData::None;
+ }
+
+ // Find the property on the ShaderEffect item.
+ const int propIdx = m_item->metaObject()->indexOfProperty(v.name.constData());
+ if (propIdx >= 0) {
+ QMetaProperty mp = m_item->metaObject()->property(propIdx);
+ 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.
+ auto &sm(m_signalMappers[shaderType][i]);
+ if (!sm.mapper) {
+ sm.mapper = new QSignalMapper;
+ sm.mapper->setMapping(m_item, i | (shaderType << 16));
+ }
+ sm.active = true;
+ const QByteArray signalName = '2' + mp.notifySignal().methodSignature();
+ QObject::connect(m_item, signalName, sm.mapper, SLOT(map()));
+ QObject::connect(sm.mapper, SIGNAL(mapped(int)), this, SLOT(propertyChanged(int)));
+ } else {
+ // Do not warn for dynamic properties.
+ if (!m_item->property(v.name.constData()).isValid())
+ qWarning("ShaderEffect: '%s' does not have a matching property!", v.name.constData());
+ }
+
+ vd.value = m_item->property(v.name.constData());
+
+ if (vd.specialType == QSGShaderEffectNode::VariableData::Source) {
+ QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(vd.value));
+ if (source) {
+ if (m_item->window())
+ QQuickItemPrivate::get(source)->refWindow(m_item->window());
+ QObject::connect(source, SIGNAL(destroyed(QObject*)), this, SLOT(sourceDestroyed(QObject*)));
+ }
+ }
+ }
+}
+
+bool QQuickGenericShaderEffect::sourceIsUnique(QQuickItem *source, Shader typeToSkip, int indexToSkip) const
+{
+ for (int shaderType = 0; shaderType < NShader; ++shaderType) {
+ for (int idx = 0; idx < m_shaders[shaderType].varData.count(); ++idx) {
+ if (shaderType != typeToSkip || idx != indexToSkip) {
+ const auto &vd(m_shaders[shaderType].varData[idx]);
+ if (vd.specialType == QSGShaderEffectNode::VariableData::Source && qvariant_cast<QObject *>(vd.value) == source)
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+void QQuickGenericShaderEffect::propertyChanged(int mappedId)
+{
+ const Shader type = Shader(mappedId >> 16);
+ const int idx = mappedId & 0xFFFF;
+ const auto &v(m_shaders[type].shaderInfo.variables[idx]);
+ auto &vd(m_shaders[type].varData[idx]);
+
+ if (vd.specialType == QSGShaderEffectNode::VariableData::Source) {
+ QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(vd.value));
+ if (source) {
+ if (m_item->window())
+ QQuickItemPrivate::get(source)->derefWindow();
+ // QObject::disconnect() will disconnect all matching connections.
+ // If the same source has been attached to two separate
+ // textures/samplers, then changing one of them would trigger both
+ // to be disconnected. So check first.
+ if (sourceIsUnique(source, type, idx))
+ QObject::disconnect(source, SIGNAL(destroyed(QObject*)), this, SLOT(sourceDestroyed(QObject*)));
+ }
+
+ vd.value = m_item->property(v.name.constData());
+
+ source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(vd.value));
+ if (source) {
+ // 'source' needs a window to get a scene graph node. It usually gets one through its
+ // parent, but if the source item is "inline" rather than a reference -- i.e.
+ // "property variant source: Image { }" instead of "property variant source: foo" -- it
+ // will not get a parent. In those cases, 'source' should get the window from 'item'.
+ if (m_item->window())
+ QQuickItemPrivate::get(source)->refWindow(m_item->window());
+ QObject::connect(source, SIGNAL(destroyed(QObject*)), this, SLOT(sourceDestroyed(QObject*)));
+ }
+
+ m_dirty |= QSGShaderEffectNode::DirtyShaderTexture;
+ m_dirtyTextures[type].insert(idx);
+
+ } else {
+ vd.value = m_item->property(v.name.constData());
+ m_dirty |= QSGShaderEffectNode::DirtyShaderConstant;
+ m_dirtyConstants[type].insert(idx);
+ }
+
+ m_item->update();
+}
+
+void QQuickGenericShaderEffect::sourceDestroyed(QObject *object)
+{
+ for (int shaderType = 0; shaderType < NShader; ++shaderType) {
+ for (auto &vd : m_shaders[shaderType].varData) {
+ if (vd.specialType == QSGShaderEffectNode::VariableData::Source && vd.value.canConvert<QObject *>()) {
+ if (qvariant_cast<QObject *>(vd.value) == object)
+ vd.value = QVariant();
+ }
+ }
+ }
+}
+
+void QQuickGenericShaderEffect::markGeometryDirtyAndUpdate()
+{
+ m_dirty |= QSGShaderEffectNode::DirtyShaderGeometry;
+ m_item->update();
+}
+
+void QQuickGenericShaderEffect::markGeometryDirtyAndUpdateIfSupportsAtlas()
+{
+ if (m_supportsAtlasTextures)
+ markGeometryDirtyAndUpdate();
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/items/qquickgenericshadereffect_p.h b/src/quick/items/qquickgenericshadereffect_p.h
new file mode 100644
index 0000000000..2af7d1156a
--- /dev/null
+++ b/src/quick/items/qquickgenericshadereffect_p.h
@@ -0,0 +1,150 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKGENERICSHADEREFFECT_P_H
+#define QQUICKGENERICSHADEREFFECT_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtQuick/qquickitem.h>
+#include <private/qtquickglobal_p.h>
+#include <private/qsgadaptationlayer_p.h>
+#include "qquickshadereffect_p.h"
+#include "qquickshadereffectmesh_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QSignalMapper;
+
+class Q_QUICK_PRIVATE_EXPORT QQuickGenericShaderEffect : public QObject
+{
+ Q_OBJECT
+
+public:
+ QQuickGenericShaderEffect(QQuickShaderEffect *item, QObject *parent = 0);
+ ~QQuickGenericShaderEffect();
+
+ QByteArray fragmentShader() const { return m_fragShader; }
+ void setFragmentShader(const QByteArray &src);
+
+ QByteArray vertexShader() const { return m_vertShader; }
+ void setVertexShader(const QByteArray &src);
+
+ bool blending() const { return m_blending; }
+ void setBlending(bool enable);
+
+ QVariant mesh() const;
+ void setMesh(const QVariant &mesh);
+
+ QQuickShaderEffect::CullMode cullMode() const { return m_cullMode; }
+ void setCullMode(QQuickShaderEffect::CullMode face);
+
+ QString log() const;
+ QQuickShaderEffect::Status status() const;
+
+ bool supportsAtlasTextures() const { return m_supportsAtlasTextures; }
+ void setSupportsAtlasTextures(bool supports);
+
+ QQuickShaderEffect::ShaderType shaderType() const;
+ QQuickShaderEffect::ShaderCompilationType shaderCompilationType() const;
+ QQuickShaderEffect::ShaderSourceType shaderSourceType() const;
+
+ void handleEvent(QEvent *);
+ void handleGeometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry);
+ QSGNode *handleUpdatePaintNode(QSGNode *, QQuickItem::UpdatePaintNodeData *);
+ void handleComponentComplete();
+ void handleItemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value);
+
+private slots:
+ void propertyChanged(int mappedId);
+ void sourceDestroyed(QObject *object);
+ void markGeometryDirtyAndUpdate();
+ void markGeometryDirtyAndUpdateIfSupportsAtlas();
+ void itemWindowChanged(QQuickWindow *w);
+ void backendChanged();
+
+private:
+ QSGGuiThreadShaderEffectManager *shaderEffectManager() const;
+
+ enum Shader {
+ Vertex,
+ Fragment,
+
+ NShader
+ };
+ void updateShader(Shader which, const QByteArray &src);
+ void disconnectSignals(Shader which);
+ bool sourceIsUnique(QQuickItem *source, Shader typeToSkip, int indexToSkip) const;
+
+ QQuickShaderEffect *m_item;
+ QSize m_meshResolution;
+ QQuickShaderEffectMesh *m_mesh;
+ QQuickGridMesh m_defaultMesh;
+ QQuickShaderEffect::CullMode m_cullMode;
+ bool m_blending;
+ bool m_supportsAtlasTextures;
+ mutable QSGGuiThreadShaderEffectManager *m_mgr;
+ QByteArray m_fragShader;
+ QByteArray m_vertShader;
+
+ QSGShaderEffectNode::ShaderData m_shaders[NShader];
+ QSGShaderEffectNode::DirtyShaderFlags m_dirty;
+ QSet<int> m_dirtyConstants[NShader];
+ QSet<int> m_dirtyTextures[NShader];
+
+ struct SignalMapper {
+ SignalMapper() : mapper(nullptr), active(false) { }
+ QSignalMapper *mapper;
+ bool active;
+ };
+ QVector<SignalMapper> m_signalMappers[NShader];
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKGENERICSHADEREFFECT_P_H
diff --git a/src/quick/items/qquickitem.h b/src/quick/items/qquickitem.h
index c5c17615ee..4fd65fbb01 100644
--- a/src/quick/items/qquickitem.h
+++ b/src/quick/items/qquickitem.h
@@ -311,10 +311,11 @@ public:
bool keepTouchGrab() const;
void setKeepTouchGrab(bool);
+#ifndef QT_NO_OPENGL
// implemented in qquickitemgrabresult.cpp
Q_REVISION(2) Q_INVOKABLE bool grabToImage(const QJSValue &callback, const QSize &targetSize = QSize());
QSharedPointer<QQuickItemGrabResult> grabToImage(const QSize &targetSize = QSize());
-
+#endif
Q_INVOKABLE virtual bool contains(const QPointF &point) const;
QTransform itemTransform(QQuickItem *, bool *) const;
diff --git a/src/quick/items/qquickitem_p.h b/src/quick/items/qquickitem_p.h
index 854cbd769b..846063a58b 100644
--- a/src/quick/items/qquickitem_p.h
+++ b/src/quick/items/qquickitem_p.h
@@ -77,7 +77,6 @@
#include <QtCore/qelapsedtimer.h>
#include <QtQuick/private/qquickshadereffectsource_p.h>
-#include <QtQuick/private/qquickshadereffect_p.h>
QT_BEGIN_NAMESPACE
diff --git a/src/quick/items/qquickitemgrabresult.cpp b/src/quick/items/qquickitemgrabresult.cpp
index d05ecc9076..807a2ae304 100644
--- a/src/quick/items/qquickitemgrabresult.cpp
+++ b/src/quick/items/qquickitemgrabresult.cpp
@@ -48,6 +48,7 @@
#include <private/qquickpixmapcache_p.h>
#include <private/qquickitem_p.h>
#include <private/qsgcontext_p.h>
+#include <private/qsgdefaultrendercontext_p.h>
QT_BEGIN_NAMESPACE
@@ -240,8 +241,7 @@ void QQuickItemGrabResult::render()
return;
d->texture->setRect(QRectF(0, d->itemSize.height(), d->itemSize.width(), -d->itemSize.height()));
- QSGContext *sg = QSGRenderContext::from(QOpenGLContext::currentContext())->sceneGraphContext();
- const QSize minSize = sg->minimumFBOSize();
+ const QSize minSize = QQuickWindowPrivate::get(d->window.data())->context->sceneGraphContext()->minimumFBOSize();
d->texture->setSize(QSize(qMax(minSize.width(), d->textureSize.width()),
qMax(minSize.height(), d->textureSize.height())));
d->texture->scheduleUpdate();
diff --git a/src/quick/items/qquickitemsmodule.cpp b/src/quick/items/qquickitemsmodule.cpp
index 0b413b76b6..3ccbf4b140 100644
--- a/src/quick/items/qquickitemsmodule.cpp
+++ b/src/quick/items/qquickitemsmodule.cpp
@@ -41,7 +41,6 @@
#include "qquickitem.h"
#include "qquickitem_p.h"
-#include "qquickitemgrabresult.h"
#include "qquickevents_p_p.h"
#include "qquickrectangle_p.h"
#include "qquickfocusscope_p.h"
@@ -70,18 +69,23 @@
#include "qquicktranslate_p.h"
#include "qquickstateoperations_p.h"
#include "qquickitemanimation_p.h"
-#include <private/qquickshadereffect_p.h>
#include <QtQuick/private/qquickshadereffectsource_p.h>
//#include <private/qquickpincharea_p.h>
#include <QtQuick/private/qquickcanvasitem_p.h>
#include <QtQuick/private/qquickcontext2d_p.h>
-#include "qquicksprite_p.h"
-#include "qquickspritesequence_p.h"
-#include "qquickanimatedsprite_p.h"
+#ifndef QT_NO_OPENGL
+# include "qquickitemgrabresult.h"
+# include "qquicksprite_p.h"
+# include "qquickspritesequence_p.h"
+# include "qquickanimatedsprite_p.h"
+# include "qquickopenglinfo_p.h"
+#endif
+#include "qquickrendererinfo_p.h"
+#include "qquickshadereffect_p.h"
+#include "qquickshadereffectmesh_p.h"
#include "qquickdrag_p.h"
#include "qquickdroparea_p.h"
#include "qquickmultipointtoucharea_p.h"
-#include "qquickopenglinfo_p.h"
#include <private/qqmlmetatype_p.h>
#include <QtQuick/private/qquickaccessibleattached_p.h>
@@ -178,8 +182,9 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor)
qmlRegisterType<QQuickTextInput,2>(uri,2,2,"TextInput");
qmlRegisterType<QQuickTextInput,3>(uri,2,4,"TextInput");
qmlRegisterType<QQuickViewSection>(uri,major,minor,"ViewSection");
-
+#ifndef QT_NO_OPENGL
qmlRegisterType<QQuickItemGrabResult>();
+#endif
qmlRegisterType<QQuickItemLayer>();
qmlRegisterType<QQuickAnchors>();
qmlRegisterType<QQuickKeyEvent>();
@@ -207,20 +212,19 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor)
qmlRegisterType<QQuickPinch>(uri,major,minor,"Pinch");
qmlRegisterType<QQuickPinchEvent>();
- qmlRegisterType<QQuickShaderEffect>("QtQuick", 2, 0, "ShaderEffect");
qmlRegisterType<QQuickShaderEffectSource>("QtQuick", 2, 0, "ShaderEffectSource");
qmlRegisterUncreatableType<QQuickShaderEffectMesh>("QtQuick", 2, 0, "ShaderEffectMesh", QQuickShaderEffectMesh::tr("Cannot create instance of abstract class ShaderEffectMesh."));
qmlRegisterType<QQuickGridMesh>("QtQuick", 2, 0, "GridMesh");
- qmlRegisterType<QQuickBorderImageMesh>("QtQuick", 2, 8, "BorderImageMesh");
+ qmlRegisterType<QQuickShaderEffect>("QtQuick", 2, 0, "ShaderEffect");
qmlRegisterUncreatableType<QQuickPaintedItem>("QtQuick", 2, 0, "PaintedItem", QQuickPaintedItem::tr("Cannot create instance of abstract class PaintedItem"));
qmlRegisterType<QQuickCanvasItem>("QtQuick", 2, 0, "Canvas");
-
+#ifndef QT_NO_OPENGL
qmlRegisterType<QQuickSprite>("QtQuick", 2, 0, "Sprite");
qmlRegisterType<QQuickAnimatedSprite>("QtQuick", 2, 0, "AnimatedSprite");
qmlRegisterType<QQuickSpriteSequence>("QtQuick", 2, 0, "SpriteSequence");
-
+#endif
qmlRegisterType<QQuickParentChange>(uri, major, minor,"ParentChange");
qmlRegisterType<QQuickAnchorChanges>(uri, major, minor,"AnchorChanges");
qmlRegisterType<QQuickAnchorSet>();
@@ -263,8 +267,10 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor)
qmlRegisterType<QQuickListView, 2>(uri, 2, 4, "ListView");
qmlRegisterType<QQuickMouseArea, 1>(uri, 2, 4, "MouseArea");
qmlRegisterType<QQuickShaderEffect, 1>(uri, 2, 4, "ShaderEffect");
- qmlRegisterUncreatableType<QQuickOpenGLInfo>(uri, 2, 4,"OpenGLInfo", QQuickOpenGLInfo::tr("OpenGLInfo is only available via attached properties"));
+#ifndef QT_NO_OPENGL
+ qmlRegisterUncreatableType<QQuickOpenGLInfo>(uri, 2, 4,"OpenGLInfo", QQuickOpenGLInfo::tr("OpenGLInfo is only available via attached properties"));
+#endif
qmlRegisterType<QQuickPinchArea, 1>(uri, 2, 5,"PinchArea");
qmlRegisterType<QQuickImage, 2>(uri, 2, 5,"Image");
qmlRegisterType<QQuickMouseArea, 2>(uri, 2, 5, "MouseArea");
@@ -289,6 +295,10 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor)
qmlRegisterType<QQuickPathView, 7>(uri, 2, 7, "PathView");
qmlRegisterUncreatableType<QQuickMouseEvent, 7>(uri, 2, 7, nullptr, QQuickMouseEvent::tr("MouseEvent is only available within handlers in MouseArea"));
+
+ qmlRegisterType<QQuickShaderEffect, 2>(uri, 2, 8, "ShaderEffect");
+ qmlRegisterUncreatableType<QQuickRendererInfo>(uri, 2, 8,"RendererInfo", QQuickRendererInfo::tr("RendererInfo is only available via attached properties"));
+ qmlRegisterType<QQuickBorderImageMesh>("QtQuick", 2, 8, "BorderImageMesh");
}
static void initResources()
diff --git a/src/quick/items/qquickopenglshadereffect.cpp b/src/quick/items/qquickopenglshadereffect.cpp
new file mode 100644
index 0000000000..312721ed04
--- /dev/null
+++ b/src/quick/items/qquickopenglshadereffect.cpp
@@ -0,0 +1,862 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qquickopenglshadereffect_p.h>
+
+#include <QtQuick/qsgmaterial.h>
+#include <QtQuick/private/qsgshadersourcebuilder_p.h>
+#include "qquickitem_p.h"
+
+#include <QtQuick/private/qsgcontext_p.h>
+#include <QtQuick/qsgtextureprovider.h>
+#include "qquickwindow.h"
+
+#include "qquickimage_p.h"
+#include "qquickshadereffectsource_p.h"
+#include "qquickshadereffectmesh_p.h"
+
+#include <QtCore/qsignalmapper.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace {
+
+ enum VariableQualifier {
+ AttributeQualifier,
+ UniformQualifier
+ };
+
+ inline bool qt_isalpha(char c)
+ {
+ char ch = c | 0x20;
+ return (ch >= 'a' && ch <= 'z') || c == '_';
+ }
+
+ inline bool qt_isalnum(char c)
+ {
+ return qt_isalpha(c) || (c >= '0' && c <= '9');
+ }
+
+ inline bool qt_isspace(char c)
+ {
+ return c == ' ' || (c >= 0x09 && c <= 0x0d);
+ }
+
+ // Returns -1 if not found, returns index to first character after the name if found.
+ int qt_search_for_variable(const char *s, int length, int index, VariableQualifier &decl,
+ int &typeIndex, int &typeLength,
+ int &nameIndex, int &nameLength,
+ QQuickOpenGLShaderEffectCommon::Key::ShaderType shaderType)
+ {
+ enum Identifier {
+ QualifierIdentifier, // Base state
+ PrecisionIdentifier,
+ TypeIdentifier,
+ NameIdentifier
+ };
+ Identifier expected = QualifierIdentifier;
+ bool compilerDirectiveExpected = index == 0;
+
+ while (index < length) {
+ // Skip whitespace.
+ while (qt_isspace(s[index])) {
+ compilerDirectiveExpected |= s[index] == '\n';
+ ++index;
+ }
+
+ if (qt_isalpha(s[index])) {
+ // Read identifier.
+ int idIndex = index;
+ ++index;
+ while (qt_isalnum(s[index]))
+ ++index;
+ int idLength = index - idIndex;
+
+ const int attrLen = sizeof("attribute") - 1;
+ const int inLen = sizeof("in") - 1;
+ const int uniLen = sizeof("uniform") - 1;
+ const int loLen = sizeof("lowp") - 1;
+ const int medLen = sizeof("mediump") - 1;
+ const int hiLen = sizeof("highp") - 1;
+
+ switch (expected) {
+ case QualifierIdentifier:
+ if (idLength == attrLen && qstrncmp("attribute", s + idIndex, attrLen) == 0) {
+ decl = AttributeQualifier;
+ expected = PrecisionIdentifier;
+ } else if (shaderType == QQuickOpenGLShaderEffectCommon::Key::VertexShader
+ && idLength == inLen && qstrncmp("in", s + idIndex, inLen) == 0) {
+ decl = AttributeQualifier;
+ expected = PrecisionIdentifier;
+ } else if (idLength == uniLen && qstrncmp("uniform", s + idIndex, uniLen) == 0) {
+ decl = UniformQualifier;
+ expected = PrecisionIdentifier;
+ }
+ break;
+ case PrecisionIdentifier:
+ if ((idLength == loLen && qstrncmp("lowp", s + idIndex, loLen) == 0)
+ || (idLength == medLen && qstrncmp("mediump", s + idIndex, medLen) == 0)
+ || (idLength == hiLen && qstrncmp("highp", s + idIndex, hiLen) == 0))
+ {
+ expected = TypeIdentifier;
+ break;
+ }
+ // Fall through.
+ case TypeIdentifier:
+ typeIndex = idIndex;
+ typeLength = idLength;
+ expected = NameIdentifier;
+ break;
+ case NameIdentifier:
+ nameIndex = idIndex;
+ nameLength = idLength;
+ return index; // Attribute or uniform declaration found. Return result.
+ default:
+ break;
+ }
+ } else if (s[index] == '#' && compilerDirectiveExpected) {
+ // Skip compiler directives.
+ ++index;
+ while (index < length && (s[index] != '\n' || s[index - 1] == '\\'))
+ ++index;
+ } else if (s[index] == '/' && s[index + 1] == '/') {
+ // Skip comments.
+ index += 2;
+ while (index < length && s[index] != '\n')
+ ++index;
+ } else if (s[index] == '/' && s[index + 1] == '*') {
+ // Skip comments.
+ index += 2;
+ while (index < length && (s[index] != '*' || s[index + 1] != '/'))
+ ++index;
+ if (index < length)
+ index += 2; // Skip star-slash.
+ } else {
+ expected = QualifierIdentifier;
+ ++index;
+ }
+ compilerDirectiveExpected = false;
+ }
+ return -1;
+ }
+}
+
+
+
+QQuickOpenGLShaderEffectCommon::~QQuickOpenGLShaderEffectCommon()
+{
+ for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType)
+ qDeleteAll(signalMappers[shaderType]);
+}
+
+void QQuickOpenGLShaderEffectCommon::disconnectPropertySignals(QQuickItem *item, Key::ShaderType shaderType)
+{
+ for (int i = 0; i < uniformData[shaderType].size(); ++i) {
+ if (signalMappers[shaderType].at(i) == 0)
+ continue;
+ const UniformData &d = uniformData[shaderType].at(i);
+ QSignalMapper *mapper = signalMappers[shaderType].at(i);
+ QObject::disconnect(item, 0, mapper, SLOT(map()));
+ QObject::disconnect(mapper, SIGNAL(mapped(int)), host, SLOT(propertyChanged(int)));
+ if (d.specialType == UniformData::Sampler) {
+ QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value));
+ if (source) {
+ if (item->window())
+ QQuickItemPrivate::get(source)->derefWindow();
+ QObject::disconnect(source, SIGNAL(destroyed(QObject*)), host, SLOT(sourceDestroyed(QObject*)));
+ }
+ }
+ }
+}
+
+void QQuickOpenGLShaderEffectCommon::connectPropertySignals(QQuickItem *item, Key::ShaderType shaderType)
+{
+ for (int i = 0; i < uniformData[shaderType].size(); ++i) {
+ if (signalMappers[shaderType].at(i) == 0)
+ continue;
+ const UniformData &d = uniformData[shaderType].at(i);
+ int pi = item->metaObject()->indexOfProperty(d.name.constData());
+ if (pi >= 0) {
+ QMetaProperty mp = item->metaObject()->property(pi);
+ if (!mp.hasNotifySignal())
+ qWarning("QQuickOpenGLShaderEffect: property '%s' does not have notification method!", d.name.constData());
+ const QByteArray signalName = '2' + mp.notifySignal().methodSignature();
+ QSignalMapper *mapper = signalMappers[shaderType].at(i);
+ QObject::connect(item, signalName, mapper, SLOT(map()));
+ QObject::connect(mapper, SIGNAL(mapped(int)), host, SLOT(propertyChanged(int)));
+ } else {
+ // If the source is set via a dynamic property, like the layer is, then we need this
+ // check to disable the warning.
+ if (!item->property(d.name.constData()).isValid())
+ qWarning("QQuickOpenGLShaderEffect: '%s' does not have a matching property!", d.name.constData());
+ }
+
+ if (d.specialType == UniformData::Sampler) {
+ QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value));
+ if (source) {
+ if (item->window())
+ QQuickItemPrivate::get(source)->refWindow(item->window());
+ QObject::connect(source, SIGNAL(destroyed(QObject*)), host, SLOT(sourceDestroyed(QObject*)));
+ }
+ }
+ }
+}
+
+void QQuickOpenGLShaderEffectCommon::updateParseLog(bool ignoreAttributes)
+{
+ parseLog.clear();
+ if (!ignoreAttributes) {
+ if (!attributes.contains(qtPositionAttributeName())) {
+ parseLog += QLatin1String("Warning: Missing reference to \'");
+ parseLog += QLatin1String(qtPositionAttributeName());
+ parseLog += QLatin1String("\'.\n");
+ }
+ if (!attributes.contains(qtTexCoordAttributeName())) {
+ parseLog += QLatin1String("Warning: Missing reference to \'");
+ parseLog += QLatin1String(qtTexCoordAttributeName());
+ parseLog += QLatin1String("\'.\n");
+ }
+ }
+ bool respectsMatrix = false;
+ bool respectsOpacity = false;
+ for (int i = 0; i < uniformData[Key::VertexShader].size(); ++i)
+ respectsMatrix |= uniformData[Key::VertexShader].at(i).specialType == UniformData::Matrix;
+ for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) {
+ for (int i = 0; i < uniformData[shaderType].size(); ++i)
+ respectsOpacity |= uniformData[shaderType].at(i).specialType == UniformData::Opacity;
+ }
+ if (!respectsMatrix)
+ parseLog += QLatin1String("Warning: Vertex shader is missing reference to \'qt_Matrix\'.\n");
+ if (!respectsOpacity)
+ parseLog += QLatin1String("Warning: Shaders are missing reference to \'qt_Opacity\'.\n");
+}
+
+void QQuickOpenGLShaderEffectCommon::lookThroughShaderCode(QQuickItem *item, Key::ShaderType shaderType, const QByteArray &code)
+{
+ int index = 0;
+ int typeIndex = -1;
+ int typeLength = 0;
+ int nameIndex = -1;
+ int nameLength = 0;
+ const char *s = code.constData();
+ VariableQualifier decl = AttributeQualifier;
+ while ((index = qt_search_for_variable(s, code.size(), index, decl, typeIndex, typeLength,
+ nameIndex, nameLength, shaderType)) != -1)
+ {
+ if (decl == AttributeQualifier) {
+ if (shaderType == Key::VertexShader)
+ attributes.append(QByteArray(s + nameIndex, nameLength));
+ } else {
+ Q_ASSERT(decl == UniformQualifier);
+
+ const int sampLen = sizeof("sampler2D") - 1;
+ const int opLen = sizeof("qt_Opacity") - 1;
+ const int matLen = sizeof("qt_Matrix") - 1;
+ const int srLen = sizeof("qt_SubRect_") - 1;
+
+ UniformData d;
+ QSignalMapper *mapper = 0;
+ d.name = QByteArray(s + nameIndex, nameLength);
+ if (nameLength == opLen && qstrncmp("qt_Opacity", s + nameIndex, opLen) == 0) {
+ d.specialType = UniformData::Opacity;
+ } else if (nameLength == matLen && qstrncmp("qt_Matrix", s + nameIndex, matLen) == 0) {
+ d.specialType = UniformData::Matrix;
+ } else if (nameLength > srLen && qstrncmp("qt_SubRect_", s + nameIndex, srLen) == 0) {
+ d.specialType = UniformData::SubRect;
+ } else {
+ mapper = new QSignalMapper;
+ mapper->setMapping(item, uniformData[shaderType].size() | (shaderType << 16));
+ d.value = item->property(d.name.constData());
+ bool sampler = typeLength == sampLen && qstrncmp("sampler2D", s + typeIndex, sampLen) == 0;
+ d.specialType = sampler ? UniformData::Sampler : UniformData::None;
+ }
+ uniformData[shaderType].append(d);
+ signalMappers[shaderType].append(mapper);
+ }
+ }
+}
+
+void QQuickOpenGLShaderEffectCommon::updateShader(QQuickItem *item, Key::ShaderType shaderType)
+{
+ disconnectPropertySignals(item, shaderType);
+ qDeleteAll(signalMappers[shaderType]);
+ uniformData[shaderType].clear();
+ signalMappers[shaderType].clear();
+ if (shaderType == Key::VertexShader)
+ attributes.clear();
+
+ const QByteArray &code = source.sourceCode[shaderType];
+ if (code.isEmpty()) {
+ // Optimize for default code.
+ if (shaderType == Key::VertexShader) {
+ attributes.append(QByteArray(qtPositionAttributeName()));
+ attributes.append(QByteArray(qtTexCoordAttributeName()));
+ UniformData d;
+ d.name = "qt_Matrix";
+ d.specialType = UniformData::Matrix;
+ uniformData[Key::VertexShader].append(d);
+ signalMappers[Key::VertexShader].append(0);
+ } else if (shaderType == Key::FragmentShader) {
+ UniformData d;
+ d.name = "qt_Opacity";
+ d.specialType = UniformData::Opacity;
+ uniformData[Key::FragmentShader].append(d);
+ signalMappers[Key::FragmentShader].append(0);
+ QSignalMapper *mapper = new QSignalMapper;
+ mapper->setMapping(item, 1 | (Key::FragmentShader << 16));
+ const char *sourceName = "source";
+ d.name = sourceName;
+ d.value = item->property(sourceName);
+ d.specialType = UniformData::Sampler;
+ uniformData[Key::FragmentShader].append(d);
+ signalMappers[Key::FragmentShader].append(mapper);
+ }
+ } else {
+ lookThroughShaderCode(item, shaderType, code);
+ }
+
+ connectPropertySignals(item, shaderType);
+}
+
+void QQuickOpenGLShaderEffectCommon::updateMaterial(QQuickOpenGLShaderEffectNode *node,
+ QQuickOpenGLShaderEffectMaterial *material,
+ bool updateUniforms, bool updateUniformValues,
+ bool updateTextureProviders)
+{
+ if (updateUniforms) {
+ for (int i = 0; i < material->textureProviders.size(); ++i) {
+ QSGTextureProvider *t = material->textureProviders.at(i);
+ if (t) {
+ QObject::disconnect(t, SIGNAL(textureChanged()), node, SLOT(markDirtyTexture()));
+ QObject::disconnect(t, SIGNAL(destroyed(QObject*)), node, SLOT(textureProviderDestroyed(QObject*)));
+ }
+ }
+
+ // First make room in the textureProviders array. Set to proper value further down.
+ int textureProviderCount = 0;
+ for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) {
+ for (int i = 0; i < uniformData[shaderType].size(); ++i) {
+ if (uniformData[shaderType].at(i).specialType == UniformData::Sampler)
+ ++textureProviderCount;
+ }
+ material->uniforms[shaderType] = uniformData[shaderType];
+ }
+ material->textureProviders.fill(0, textureProviderCount);
+ updateUniformValues = false;
+ updateTextureProviders = true;
+ }
+
+ if (updateUniformValues) {
+ for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) {
+ Q_ASSERT(uniformData[shaderType].size() == material->uniforms[shaderType].size());
+ for (int i = 0; i < uniformData[shaderType].size(); ++i)
+ material->uniforms[shaderType][i].value = uniformData[shaderType].at(i).value;
+ }
+ }
+
+ if (updateTextureProviders) {
+ int index = 0;
+ for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) {
+ for (int i = 0; i < uniformData[shaderType].size(); ++i) {
+ const UniformData &d = uniformData[shaderType].at(i);
+ if (d.specialType != UniformData::Sampler)
+ continue;
+ QSGTextureProvider *oldProvider = material->textureProviders.at(index);
+ QSGTextureProvider *newProvider = 0;
+ QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value));
+ if (source && source->isTextureProvider())
+ newProvider = source->textureProvider();
+ if (newProvider != oldProvider) {
+ if (oldProvider) {
+ QObject::disconnect(oldProvider, SIGNAL(textureChanged()), node, SLOT(markDirtyTexture()));
+ QObject::disconnect(oldProvider, SIGNAL(destroyed(QObject*)), node, SLOT(textureProviderDestroyed(QObject*)));
+ }
+ if (newProvider) {
+ Q_ASSERT_X(newProvider->thread() == QThread::currentThread(),
+ "QQuickOpenGLShaderEffect::updatePaintNode",
+ "Texture provider must belong to the rendering thread");
+ QObject::connect(newProvider, SIGNAL(textureChanged()), node, SLOT(markDirtyTexture()));
+ QObject::connect(newProvider, SIGNAL(destroyed(QObject*)), node, SLOT(textureProviderDestroyed(QObject*)));
+ } else {
+ const char *typeName = source ? source->metaObject()->className() : d.value.typeName();
+ qWarning("ShaderEffect: Property '%s' is not assigned a valid texture provider (%s).",
+ d.name.constData(), typeName);
+ }
+ material->textureProviders[index] = newProvider;
+ }
+ ++index;
+ }
+ }
+ Q_ASSERT(index == material->textureProviders.size());
+ }
+}
+
+void QQuickOpenGLShaderEffectCommon::updateWindow(QQuickWindow *window)
+{
+ // See comment in QQuickOpenGLShaderEffectCommon::propertyChanged().
+ if (window) {
+ for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) {
+ for (int i = 0; i < uniformData[shaderType].size(); ++i) {
+ const UniformData &d = uniformData[shaderType].at(i);
+ if (d.specialType == UniformData::Sampler) {
+ QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value));
+ if (source)
+ QQuickItemPrivate::get(source)->refWindow(window);
+ }
+ }
+ }
+ } else {
+ for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) {
+ for (int i = 0; i < uniformData[shaderType].size(); ++i) {
+ const UniformData &d = uniformData[shaderType].at(i);
+ if (d.specialType == UniformData::Sampler) {
+ QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value));
+ if (source)
+ QQuickItemPrivate::get(source)->derefWindow();
+ }
+ }
+ }
+ }
+}
+
+void QQuickOpenGLShaderEffectCommon::sourceDestroyed(QObject *object)
+{
+ for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) {
+ for (int i = 0; i < uniformData[shaderType].size(); ++i) {
+ UniformData &d = uniformData[shaderType][i];
+ if (d.specialType == UniformData::Sampler && d.value.canConvert<QObject *>()) {
+ if (qvariant_cast<QObject *>(d.value) == object)
+ d.value = QVariant();
+ }
+ }
+ }
+}
+
+static bool qquick_uniqueInUniformData(QQuickItem *source, const QVector<QQuickOpenGLShaderEffectMaterial::UniformData> *uniformData, int typeToSkip, int indexToSkip)
+{
+ for (int s=0; s<QQuickOpenGLShaderEffectMaterialKey::ShaderTypeCount; ++s) {
+ for (int i=0; i<uniformData[s].size(); ++i) {
+ if (s == typeToSkip && i == indexToSkip)
+ continue;
+ const QQuickOpenGLShaderEffectMaterial::UniformData &d = uniformData[s][i];
+ if (d.specialType == QQuickOpenGLShaderEffectMaterial::UniformData::Sampler && qvariant_cast<QObject *>(d.value) == source)
+ return false;
+ }
+ }
+ return true;
+}
+
+void QQuickOpenGLShaderEffectCommon::propertyChanged(QQuickItem *item, int mappedId,
+ bool *textureProviderChanged)
+{
+ Key::ShaderType shaderType = Key::ShaderType(mappedId >> 16);
+ int index = mappedId & 0xffff;
+ UniformData &d = uniformData[shaderType][index];
+ if (d.specialType == UniformData::Sampler) {
+ QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value));
+ if (source) {
+ if (item->window())
+ QQuickItemPrivate::get(source)->derefWindow();
+
+ // QObject::disconnect() will disconnect all matching connections. If the same
+ // source has been attached to two separate samplers, then changing one of them
+ // would trigger both to be disconnected. Without the connection we'll end up
+ // with a dangling pointer in the uniformData.
+ if (qquick_uniqueInUniformData(source, uniformData, shaderType, index))
+ QObject::disconnect(source, SIGNAL(destroyed(QObject*)), host, SLOT(sourceDestroyed(QObject*)));
+ }
+
+ d.value = item->property(d.name.constData());
+
+ source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value));
+ if (source) {
+ // 'source' needs a window to get a scene graph node. It usually gets one through its
+ // parent, but if the source item is "inline" rather than a reference -- i.e.
+ // "property variant source: Image { }" instead of "property variant source: foo" -- it
+ // will not get a parent. In those cases, 'source' should get the window from 'item'.
+ if (item->window())
+ QQuickItemPrivate::get(source)->refWindow(item->window());
+ QObject::connect(source, SIGNAL(destroyed(QObject*)), host, SLOT(sourceDestroyed(QObject*)));
+ }
+ if (textureProviderChanged)
+ *textureProviderChanged = true;
+ } else {
+ d.value = item->property(d.name.constData());
+ if (textureProviderChanged)
+ *textureProviderChanged = false;
+ }
+}
+
+QQuickOpenGLShaderEffect::QQuickOpenGLShaderEffect(QQuickShaderEffect *item, QObject *parent)
+ : QObject(parent)
+ , m_item(item)
+ , m_meshResolution(1, 1)
+ , m_mesh(0)
+ , m_cullMode(QQuickShaderEffect::NoCulling)
+ , m_status(QQuickShaderEffect::Uncompiled)
+ , m_common(this)
+ , m_blending(true)
+ , m_dirtyUniforms(true)
+ , m_dirtyUniformValues(true)
+ , m_dirtyTextureProviders(true)
+ , m_dirtyProgram(true)
+ , m_dirtyParseLog(true)
+ , m_dirtyMesh(true)
+ , m_dirtyGeometry(true)
+ , m_customVertexShader(false)
+ , m_supportsAtlasTextures(false)
+{
+}
+
+QQuickOpenGLShaderEffect::~QQuickOpenGLShaderEffect()
+{
+ for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType)
+ m_common.disconnectPropertySignals(m_item, Key::ShaderType(shaderType));
+}
+
+void QQuickOpenGLShaderEffect::setFragmentShader(const QByteArray &code)
+{
+ if (m_common.source.sourceCode[Key::FragmentShader].constData() == code.constData())
+ return;
+ m_common.source.sourceCode[Key::FragmentShader] = code;
+ m_dirtyProgram = true;
+ m_dirtyParseLog = true;
+
+ if (m_item->isComponentComplete())
+ m_common.updateShader(m_item, Key::FragmentShader);
+
+ m_item->update();
+ if (m_status != QQuickShaderEffect::Uncompiled) {
+ m_status = QQuickShaderEffect::Uncompiled;
+ emit m_item->statusChanged();
+ }
+ emit m_item->fragmentShaderChanged();
+}
+
+void QQuickOpenGLShaderEffect::setVertexShader(const QByteArray &code)
+{
+ if (m_common.source.sourceCode[Key::VertexShader].constData() == code.constData())
+ return;
+ m_common.source.sourceCode[Key::VertexShader] = code;
+ m_dirtyProgram = true;
+ m_dirtyParseLog = true;
+ m_customVertexShader = true;
+
+ if (m_item->isComponentComplete())
+ m_common.updateShader(m_item, Key::VertexShader);
+
+ m_item->update();
+ if (m_status != QQuickShaderEffect::Uncompiled) {
+ m_status = QQuickShaderEffect::Uncompiled;
+ emit m_item->statusChanged();
+ }
+ emit m_item->vertexShaderChanged();
+}
+
+void QQuickOpenGLShaderEffect::setBlending(bool enable)
+{
+ if (blending() == enable)
+ return;
+
+ m_blending = enable;
+ m_item->update();
+
+ emit m_item->blendingChanged();
+}
+
+QVariant QQuickOpenGLShaderEffect::mesh() const
+{
+ return m_mesh ? qVariantFromValue(static_cast<QObject *>(m_mesh))
+ : qVariantFromValue(m_meshResolution);
+}
+
+void QQuickOpenGLShaderEffect::setMesh(const QVariant &mesh)
+{
+ QQuickShaderEffectMesh *newMesh = qobject_cast<QQuickShaderEffectMesh *>(qvariant_cast<QObject *>(mesh));
+ if (newMesh && newMesh == m_mesh)
+ return;
+ if (m_mesh)
+ disconnect(m_mesh, SIGNAL(geometryChanged()), this, 0);
+ m_mesh = newMesh;
+ if (m_mesh) {
+ connect(m_mesh, SIGNAL(geometryChanged()), this, SLOT(updateGeometry()));
+ } else {
+ if (mesh.canConvert<QSize>()) {
+ m_meshResolution = mesh.toSize();
+ } else {
+ QList<QByteArray> res = mesh.toByteArray().split('x');
+ bool ok = res.size() == 2;
+ if (ok) {
+ int w = res.at(0).toInt(&ok);
+ if (ok) {
+ int h = res.at(1).toInt(&ok);
+ if (ok)
+ m_meshResolution = QSize(w, h);
+ }
+ }
+ if (!ok)
+ qWarning("ShaderEffect: mesh property must be size or object deriving from QQuickShaderEffectMesh.");
+ }
+ m_defaultMesh.setResolution(m_meshResolution);
+ }
+
+ m_dirtyMesh = true;
+ m_dirtyParseLog = true;
+ m_item->update();
+ emit m_item->meshChanged();
+}
+
+void QQuickOpenGLShaderEffect::setCullMode(QQuickShaderEffect::CullMode face)
+{
+ if (face == m_cullMode)
+ return;
+ m_cullMode = face;
+ m_item->update();
+ emit m_item->cullModeChanged();
+}
+
+void QQuickOpenGLShaderEffect::setSupportsAtlasTextures(bool supports)
+{
+ if (supports == m_supportsAtlasTextures)
+ return;
+ m_supportsAtlasTextures = supports;
+ updateGeometry();
+ emit m_item->supportsAtlasTexturesChanged();
+}
+
+QString QQuickOpenGLShaderEffect::parseLog()
+{
+ if (m_dirtyParseLog) {
+ m_common.updateParseLog(m_mesh != 0);
+ m_dirtyParseLog = false;
+ }
+ return m_common.parseLog;
+}
+
+void QQuickOpenGLShaderEffect::handleEvent(QEvent *event)
+{
+ if (event->type() == QEvent::DynamicPropertyChange) {
+ QDynamicPropertyChangeEvent *e = static_cast<QDynamicPropertyChangeEvent *>(event);
+ for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) {
+ for (int i = 0; i < m_common.uniformData[shaderType].size(); ++i) {
+ if (m_common.uniformData[shaderType].at(i).name == e->propertyName()) {
+ bool textureProviderChanged;
+ m_common.propertyChanged(m_item, (shaderType << 16) | i, &textureProviderChanged);
+ m_dirtyTextureProviders |= textureProviderChanged;
+ m_dirtyUniformValues = true;
+ m_item->update();
+ }
+ }
+ }
+ }
+}
+
+void QQuickOpenGLShaderEffect::updateGeometry()
+{
+ m_dirtyGeometry = true;
+ m_item->update();
+}
+
+void QQuickOpenGLShaderEffect::updateGeometryIfAtlased()
+{
+ if (m_supportsAtlasTextures)
+ updateGeometry();
+}
+
+void QQuickOpenGLShaderEffect::updateLogAndStatus(const QString &log, int status)
+{
+ m_log = parseLog() + log;
+ m_status = QQuickShaderEffect::Status(status);
+ emit m_item->logChanged();
+ emit m_item->statusChanged();
+}
+
+void QQuickOpenGLShaderEffect::sourceDestroyed(QObject *object)
+{
+ m_common.sourceDestroyed(object);
+}
+
+void QQuickOpenGLShaderEffect::propertyChanged(int mappedId)
+{
+ bool textureProviderChanged;
+ m_common.propertyChanged(m_item, mappedId, &textureProviderChanged);
+ m_dirtyTextureProviders |= textureProviderChanged;
+ m_dirtyUniformValues = true;
+ m_item->update();
+}
+
+void QQuickOpenGLShaderEffect::handleGeometryChanged(const QRectF &, const QRectF &)
+{
+ m_dirtyGeometry = true;
+}
+
+QSGNode *QQuickOpenGLShaderEffect::handleUpdatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *)
+{
+ QQuickOpenGLShaderEffectNode *node = static_cast<QQuickOpenGLShaderEffectNode *>(oldNode);
+
+ // In the case of zero-size or a bad vertex shader, don't try to create a node...
+ if (m_common.attributes.isEmpty() || m_item->width() <= 0 || m_item->height() <= 0) {
+ if (node)
+ delete node;
+ return 0;
+ }
+
+ if (!node) {
+ node = new QQuickOpenGLShaderEffectNode;
+ node->setMaterial(new QQuickOpenGLShaderEffectMaterial(node));
+ node->setFlag(QSGNode::OwnsMaterial, true);
+ m_dirtyProgram = true;
+ m_dirtyUniforms = true;
+ m_dirtyGeometry = true;
+ connect(node, SIGNAL(logAndStatusChanged(QString,int)), this, SLOT(updateLogAndStatus(QString,int)));
+ connect(node, &QQuickOpenGLShaderEffectNode::dirtyTexture,
+ this, &QQuickOpenGLShaderEffect::updateGeometryIfAtlased);
+ }
+
+ QQuickOpenGLShaderEffectMaterial *material = static_cast<QQuickOpenGLShaderEffectMaterial *>(node->material());
+
+ // Update blending
+ if (bool(material->flags() & QSGMaterial::Blending) != m_blending) {
+ material->setFlag(QSGMaterial::Blending, m_blending);
+ node->markDirty(QSGNode::DirtyMaterial);
+ }
+
+ if (int(material->cullMode) != int(m_cullMode)) {
+ material->cullMode = QQuickShaderEffect::CullMode(m_cullMode);
+ node->markDirty(QSGNode::DirtyMaterial);
+ }
+
+ if (m_dirtyProgram) {
+ Key s = m_common.source;
+ QSGShaderSourceBuilder builder;
+ if (s.sourceCode[Key::FragmentShader].isEmpty()) {
+ builder.appendSourceFile(QStringLiteral(":/qt-project.org/items/shaders/shadereffect.frag"));
+ s.sourceCode[Key::FragmentShader] = builder.source();
+ builder.clear();
+ }
+ if (s.sourceCode[Key::VertexShader].isEmpty()) {
+ builder.appendSourceFile(QStringLiteral(":/qt-project.org/items/shaders/shadereffect.vert"));
+ s.sourceCode[Key::VertexShader] = builder.source();
+ }
+
+ material->setProgramSource(s);
+ material->attributes = m_common.attributes;
+ node->markDirty(QSGNode::DirtyMaterial);
+ m_dirtyProgram = false;
+ m_dirtyUniforms = true;
+ }
+
+ if (m_dirtyUniforms || m_dirtyUniformValues || m_dirtyTextureProviders) {
+ m_common.updateMaterial(node, material, m_dirtyUniforms, m_dirtyUniformValues,
+ m_dirtyTextureProviders);
+ node->markDirty(QSGNode::DirtyMaterial);
+ m_dirtyUniforms = m_dirtyUniformValues = m_dirtyTextureProviders = false;
+ }
+
+ QRectF srcRect(0, 0, 1, 1);
+ bool geometryUsesTextureSubRect = false;
+ if (m_supportsAtlasTextures && material->textureProviders.size() == 1) {
+ QSGTextureProvider *provider = material->textureProviders.at(0);
+ if (provider->texture()) {
+ srcRect = provider->texture()->normalizedTextureSubRect();
+ geometryUsesTextureSubRect = true;
+ }
+ }
+
+ if (bool(material->flags() & QSGMaterial::RequiresFullMatrix) != m_customVertexShader) {
+ material->setFlag(QSGMaterial::RequiresFullMatrix, m_customVertexShader);
+ node->markDirty(QSGNode::DirtyMaterial);
+ }
+
+ if (material->geometryUsesTextureSubRect != geometryUsesTextureSubRect) {
+ material->geometryUsesTextureSubRect = geometryUsesTextureSubRect;
+ node->markDirty(QSGNode::DirtyMaterial);
+ }
+
+ if (m_dirtyMesh) {
+ node->setGeometry(0);
+ m_dirtyMesh = false;
+ m_dirtyGeometry = true;
+ }
+
+ if (m_dirtyGeometry) {
+ node->setFlag(QSGNode::OwnsGeometry, false);
+ QSGGeometry *geometry = node->geometry();
+ QRectF rect(0, 0, m_item->width(), m_item->height());
+ QQuickShaderEffectMesh *mesh = m_mesh ? m_mesh : &m_defaultMesh;
+
+ int posIndex = 0;
+ if (!mesh->validateAttributes(m_common.attributes, &posIndex)) {
+ QString log = mesh->log();
+ if (!log.isNull()) {
+ m_log = parseLog();
+ m_log += QLatin1String("*** Mesh ***\n");
+ m_log += log;
+ m_status = QQuickShaderEffect::Error;
+ emit m_item->logChanged();
+ emit m_item->statusChanged();
+ }
+ delete node;
+ return 0;
+ }
+
+ geometry = mesh->updateGeometry(geometry, m_common.attributes.count(), posIndex, srcRect, rect);
+
+ node->setGeometry(geometry);
+ node->setFlag(QSGNode::OwnsGeometry, true);
+
+ m_dirtyGeometry = false;
+ }
+
+ return node;
+}
+
+void QQuickOpenGLShaderEffect::handleComponentComplete()
+{
+ m_common.updateShader(m_item, Key::VertexShader);
+ m_common.updateShader(m_item, Key::FragmentShader);
+}
+
+void QQuickOpenGLShaderEffect::handleItemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value)
+{
+ if (change == QQuickItem::ItemSceneChange)
+ m_common.updateWindow(value.window);
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/items/qquickopenglshadereffect_p.h b/src/quick/items/qquickopenglshadereffect_p.h
new file mode 100644
index 0000000000..0e54813443
--- /dev/null
+++ b/src/quick/items/qquickopenglshadereffect_p.h
@@ -0,0 +1,176 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKOPENGLSHADEREFFECT_P_H
+#define QQUICKOPENGLSHADEREFFECT_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtQuick/qquickitem.h>
+
+#include <QtQuick/qsgmaterial.h>
+#include <private/qtquickglobal_p.h>
+#include <private/qsgadaptationlayer_p.h>
+#include <private/qquickopenglshadereffectnode_p.h>
+#include "qquickshadereffect_p.h"
+#include "qquickshadereffectmesh_p.h"
+
+#include <QtCore/qpointer.h>
+
+QT_BEGIN_NAMESPACE
+
+class QSGContext;
+class QSignalMapper;
+class QQuickOpenGLCustomMaterialShader;
+
+// Common class for QQuickOpenGLShaderEffect and QQuickCustomParticle.
+struct Q_QUICK_PRIVATE_EXPORT QQuickOpenGLShaderEffectCommon
+{
+ typedef QQuickOpenGLShaderEffectMaterialKey Key;
+ typedef QQuickOpenGLShaderEffectMaterial::UniformData UniformData;
+
+ QQuickOpenGLShaderEffectCommon(QObject *host) : host(host) { }
+ ~QQuickOpenGLShaderEffectCommon();
+ void disconnectPropertySignals(QQuickItem *item, Key::ShaderType shaderType);
+ void connectPropertySignals(QQuickItem *item, Key::ShaderType shaderType);
+ void updateParseLog(bool ignoreAttributes);
+ void lookThroughShaderCode(QQuickItem *item, Key::ShaderType shaderType, const QByteArray &code);
+ void updateShader(QQuickItem *item, Key::ShaderType shaderType);
+ void updateMaterial(QQuickOpenGLShaderEffectNode *node, QQuickOpenGLShaderEffectMaterial *material,
+ bool updateUniforms, bool updateUniformValues, bool updateTextureProviders);
+ void updateWindow(QQuickWindow *window);
+
+ // Called by slots in QQuickOpenGLShaderEffect:
+ void sourceDestroyed(QObject *object);
+ void propertyChanged(QQuickItem *item, int mappedId, bool *textureProviderChanged);
+
+ QObject *host;
+ Key source;
+ QVector<QByteArray> attributes;
+ QVector<UniformData> uniformData[Key::ShaderTypeCount];
+ QVector<QSignalMapper *> signalMappers[Key::ShaderTypeCount];
+ QString parseLog;
+};
+
+
+class Q_QUICK_PRIVATE_EXPORT QQuickOpenGLShaderEffect : public QObject
+{
+ Q_OBJECT
+
+public:
+ QQuickOpenGLShaderEffect(QQuickShaderEffect *item, QObject *parent = 0);
+ ~QQuickOpenGLShaderEffect();
+
+ QByteArray fragmentShader() const { return m_common.source.sourceCode[Key::FragmentShader]; }
+ void setFragmentShader(const QByteArray &code);
+
+ QByteArray vertexShader() const { return m_common.source.sourceCode[Key::VertexShader]; }
+ void setVertexShader(const QByteArray &code);
+
+ bool blending() const { return m_blending; }
+ void setBlending(bool enable);
+
+ QVariant mesh() const;
+ void setMesh(const QVariant &mesh);
+
+ QQuickShaderEffect::CullMode cullMode() const { return m_cullMode; }
+ void setCullMode(QQuickShaderEffect::CullMode face);
+
+ QString log() const { return m_log; }
+ QQuickShaderEffect::Status status() const { return m_status; }
+
+ bool supportsAtlasTextures() const { return m_supportsAtlasTextures; }
+ void setSupportsAtlasTextures(bool supports);
+
+ QString parseLog();
+
+ void handleEvent(QEvent *);
+ void handleGeometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry);
+ QSGNode *handleUpdatePaintNode(QSGNode *, QQuickItem::UpdatePaintNodeData *);
+ void handleComponentComplete();
+ void handleItemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value);
+
+private Q_SLOTS:
+ void updateGeometry();
+ void updateGeometryIfAtlased();
+ void updateLogAndStatus(const QString &log, int status);
+ void sourceDestroyed(QObject *object);
+ void propertyChanged(int mappedId);
+
+private:
+ friend class QQuickCustomMaterialShader;
+ friend class QQuickOpenGLShaderEffectNode;
+
+ typedef QQuickOpenGLShaderEffectMaterialKey Key;
+ typedef QQuickOpenGLShaderEffectMaterial::UniformData UniformData;
+
+ QQuickShaderEffect *m_item;
+ QSize m_meshResolution;
+ QQuickShaderEffectMesh *m_mesh;
+ QQuickGridMesh m_defaultMesh;
+ QQuickShaderEffect::CullMode m_cullMode;
+ QString m_log;
+ QQuickShaderEffect::Status m_status;
+
+ QQuickOpenGLShaderEffectCommon m_common;
+
+ uint m_blending : 1;
+ uint m_dirtyUniforms : 1;
+ uint m_dirtyUniformValues : 1;
+ uint m_dirtyTextureProviders : 1;
+ uint m_dirtyProgram : 1;
+ uint m_dirtyParseLog : 1;
+ uint m_dirtyMesh : 1;
+ uint m_dirtyGeometry : 1;
+ uint m_customVertexShader : 1;
+ uint m_supportsAtlasTextures : 1;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKOPENGLSHADEREFFECT_P_H
diff --git a/src/quick/items/qquickshadereffectnode.cpp b/src/quick/items/qquickopenglshadereffectnode.cpp
index 246a713dca..c4dd8d5c63 100644
--- a/src/quick/items/qquickshadereffectnode.cpp
+++ b/src/quick/items/qquickopenglshadereffectnode.cpp
@@ -37,14 +37,15 @@
**
****************************************************************************/
-#include <private/qquickshadereffectnode_p.h>
+#include <private/qquickopenglshadereffectnode_p.h>
-#include "qquickshadereffect_p.h"
+#include "qquickopenglshadereffect_p.h"
#include <QtQuick/qsgtextureprovider.h>
#include <QtQuick/private/qsgrenderer_p.h>
#include <QtQuick/private/qsgshadersourcebuilder_p.h>
#include <QtQuick/private/qsgtexture_p.h>
#include <QtCore/qmutex.h>
+#include <QtGui/qopenglfunctions.h>
QT_BEGIN_NAMESPACE
@@ -61,29 +62,29 @@ static bool hasAtlasTexture(const QVector<QSGTextureProvider *> &textureProvider
class QQuickCustomMaterialShader : public QSGMaterialShader
{
public:
- QQuickCustomMaterialShader(const QQuickShaderEffectMaterialKey &key, const QVector<QByteArray> &attributes);
+ QQuickCustomMaterialShader(const QQuickOpenGLShaderEffectMaterialKey &key, const QVector<QByteArray> &attributes);
void deactivate() Q_DECL_OVERRIDE;
void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) Q_DECL_OVERRIDE;
char const *const *attributeNames() const Q_DECL_OVERRIDE;
protected:
- friend class QQuickShaderEffectNode;
+ friend class QQuickOpenGLShaderEffectNode;
void compile() Q_DECL_OVERRIDE;
const char *vertexShader() const Q_DECL_OVERRIDE;
const char *fragmentShader() const Q_DECL_OVERRIDE;
- const QQuickShaderEffectMaterialKey m_key;
+ const QQuickOpenGLShaderEffectMaterialKey m_key;
QVector<QByteArray> m_attributes;
QVector<const char *> m_attributeNames;
QString m_log;
bool m_compiled;
- QVector<int> m_uniformLocs[QQuickShaderEffectMaterialKey::ShaderTypeCount];
+ QVector<int> m_uniformLocs[QQuickOpenGLShaderEffectMaterialKey::ShaderTypeCount];
uint m_initialized : 1;
};
-QQuickCustomMaterialShader::QQuickCustomMaterialShader(const QQuickShaderEffectMaterialKey &key, const QVector<QByteArray> &attributes)
+QQuickCustomMaterialShader::QQuickCustomMaterialShader(const QQuickOpenGLShaderEffectMaterialKey &key, const QVector<QByteArray> &attributes)
: m_key(key)
, m_attributes(attributes)
, m_compiled(false)
@@ -104,11 +105,11 @@ void QQuickCustomMaterialShader::deactivate()
void QQuickCustomMaterialShader::updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect)
{
- typedef QQuickShaderEffectMaterial::UniformData UniformData;
+ typedef QQuickOpenGLShaderEffectMaterial::UniformData UniformData;
Q_ASSERT(newEffect != 0);
- QQuickShaderEffectMaterial *material = static_cast<QQuickShaderEffectMaterial *>(newEffect);
+ QQuickOpenGLShaderEffectMaterial *material = static_cast<QQuickOpenGLShaderEffectMaterial *>(newEffect);
if (!material->m_emittedLogChanged && material->m_node) {
material->m_emittedLogChanged = true;
emit material->m_node->logAndStatusChanged(m_log, m_compiled ? QQuickShaderEffect::Compiled
@@ -117,7 +118,7 @@ void QQuickCustomMaterialShader::updateState(const RenderState &state, QSGMateri
int textureProviderIndex = 0;
if (!m_initialized) {
- for (int shaderType = 0; shaderType < QQuickShaderEffectMaterialKey::ShaderTypeCount; ++shaderType) {
+ for (int shaderType = 0; shaderType < QQuickOpenGLShaderEffectMaterialKey::ShaderTypeCount; ++shaderType) {
Q_ASSERT(m_uniformLocs[shaderType].isEmpty());
m_uniformLocs[shaderType].reserve(material->uniforms[shaderType].size());
for (int i = 0; i < material->uniforms[shaderType].size(); ++i) {
@@ -138,7 +139,7 @@ void QQuickCustomMaterialShader::updateState(const RenderState &state, QSGMateri
}
QOpenGLFunctions *functions = state.context()->functions();
- for (int shaderType = 0; shaderType < QQuickShaderEffectMaterialKey::ShaderTypeCount; ++shaderType) {
+ for (int shaderType = 0; shaderType < QQuickOpenGLShaderEffectMaterialKey::ShaderTypeCount; ++shaderType) {
for (int i = 0; i < material->uniforms[shaderType].size(); ++i) {
const UniformData &d = material->uniforms[shaderType].at(i);
int loc = m_uniformLocs[shaderType].at(i);
@@ -230,14 +231,14 @@ void QQuickCustomMaterialShader::updateState(const RenderState &state, QSGMateri
}
functions->glActiveTexture(GL_TEXTURE0);
- const QQuickShaderEffectMaterial *oldMaterial = static_cast<const QQuickShaderEffectMaterial *>(oldEffect);
+ const QQuickOpenGLShaderEffectMaterial *oldMaterial = static_cast<const QQuickOpenGLShaderEffectMaterial *>(oldEffect);
if (oldEffect == 0 || material->cullMode != oldMaterial->cullMode) {
switch (material->cullMode) {
- case QQuickShaderEffectMaterial::FrontFaceCulling:
+ case QQuickShaderEffect::FrontFaceCulling:
functions->glEnable(GL_CULL_FACE);
functions->glCullFace(GL_FRONT);
break;
- case QQuickShaderEffectMaterial::BackFaceCulling:
+ case QQuickShaderEffect::BackFaceCulling:
functions->glEnable(GL_CULL_FACE);
functions->glCullFace(GL_BACK);
break;
@@ -322,16 +323,16 @@ void QQuickCustomMaterialShader::compile()
const char *QQuickCustomMaterialShader::vertexShader() const
{
- return m_key.sourceCode[QQuickShaderEffectMaterialKey::VertexShader].constData();
+ return m_key.sourceCode[QQuickOpenGLShaderEffectMaterialKey::VertexShader].constData();
}
const char *QQuickCustomMaterialShader::fragmentShader() const
{
- return m_key.sourceCode[QQuickShaderEffectMaterialKey::FragmentShader].constData();
+ return m_key.sourceCode[QQuickOpenGLShaderEffectMaterialKey::FragmentShader].constData();
}
-bool QQuickShaderEffectMaterialKey::operator == (const QQuickShaderEffectMaterialKey &other) const
+bool QQuickOpenGLShaderEffectMaterialKey::operator == (const QQuickOpenGLShaderEffectMaterialKey &other) const
{
for (int shaderType = 0; shaderType < ShaderTypeCount; ++shaderType) {
if (sourceCode[shaderType] != other.sourceCode[shaderType])
@@ -340,39 +341,39 @@ bool QQuickShaderEffectMaterialKey::operator == (const QQuickShaderEffectMateria
return true;
}
-bool QQuickShaderEffectMaterialKey::operator != (const QQuickShaderEffectMaterialKey &other) const
+bool QQuickOpenGLShaderEffectMaterialKey::operator != (const QQuickOpenGLShaderEffectMaterialKey &other) const
{
return !(*this == other);
}
-uint qHash(const QQuickShaderEffectMaterialKey &key)
+uint qHash(const QQuickOpenGLShaderEffectMaterialKey &key)
{
uint hash = 1;
- typedef QQuickShaderEffectMaterialKey Key;
+ typedef QQuickOpenGLShaderEffectMaterialKey Key;
for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType)
hash = hash * 31337 + qHash(key.sourceCode[shaderType]);
return hash;
}
-class QQuickShaderEffectMaterialCache : public QObject
+class QQuickOpenGLShaderEffectMaterialCache : public QObject
{
Q_OBJECT
public:
- static QQuickShaderEffectMaterialCache *get(bool create = true) {
+ static QQuickOpenGLShaderEffectMaterialCache *get(bool create = true) {
QOpenGLContext *ctx = QOpenGLContext::currentContext();
- QQuickShaderEffectMaterialCache *me = ctx->findChild<QQuickShaderEffectMaterialCache *>(QStringLiteral("__qt_ShaderEffectCache"), Qt::FindDirectChildrenOnly);
+ QQuickOpenGLShaderEffectMaterialCache *me = ctx->findChild<QQuickOpenGLShaderEffectMaterialCache *>(QStringLiteral("__qt_ShaderEffectCache"), Qt::FindDirectChildrenOnly);
if (!me && create) {
- me = new QQuickShaderEffectMaterialCache();
+ me = new QQuickOpenGLShaderEffectMaterialCache();
me->setObjectName(QStringLiteral("__qt_ShaderEffectCache"));
me->setParent(ctx);
}
return me;
}
- QHash<QQuickShaderEffectMaterialKey, QSGMaterialType *> cache;
+ QHash<QQuickOpenGLShaderEffectMaterialKey, QSGMaterialType *> cache;
};
-QQuickShaderEffectMaterial::QQuickShaderEffectMaterial(QQuickShaderEffectNode *node)
- : cullMode(NoCulling)
+QQuickOpenGLShaderEffectMaterial::QQuickOpenGLShaderEffectMaterial(QQuickOpenGLShaderEffectNode *node)
+ : cullMode(QQuickShaderEffect::NoCulling)
, geometryUsesTextureSubRect(false)
, m_node(node)
, m_emittedLogChanged(false)
@@ -380,17 +381,17 @@ QQuickShaderEffectMaterial::QQuickShaderEffectMaterial(QQuickShaderEffectNode *n
setFlag(Blending | RequiresFullMatrix, true);
}
-QSGMaterialType *QQuickShaderEffectMaterial::type() const
+QSGMaterialType *QQuickOpenGLShaderEffectMaterial::type() const
{
return m_type;
}
-QSGMaterialShader *QQuickShaderEffectMaterial::createShader() const
+QSGMaterialShader *QQuickOpenGLShaderEffectMaterial::createShader() const
{
return new QQuickCustomMaterialShader(m_source, attributes);
}
-bool QQuickShaderEffectMaterial::UniformData::operator == (const UniformData &other) const
+bool QQuickOpenGLShaderEffectMaterial::UniformData::operator == (const UniformData &other) const
{
if (specialType != other.specialType)
return false;
@@ -407,14 +408,14 @@ bool QQuickShaderEffectMaterial::UniformData::operator == (const UniformData &ot
}
}
-int QQuickShaderEffectMaterial::compare(const QSGMaterial *o) const
+int QQuickOpenGLShaderEffectMaterial::compare(const QSGMaterial *o) const
{
- const QQuickShaderEffectMaterial *other = static_cast<const QQuickShaderEffectMaterial *>(o);
+ const QQuickOpenGLShaderEffectMaterial *other = static_cast<const QQuickOpenGLShaderEffectMaterial *>(o);
if ((hasAtlasTexture(textureProviders) && !geometryUsesTextureSubRect) || (hasAtlasTexture(other->textureProviders) && !other->geometryUsesTextureSubRect))
return 1;
if (cullMode != other->cullMode)
return 1;
- for (int shaderType = 0; shaderType < QQuickShaderEffectMaterialKey::ShaderTypeCount; ++shaderType) {
+ for (int shaderType = 0; shaderType < QQuickOpenGLShaderEffectMaterialKey::ShaderTypeCount; ++shaderType) {
if (uniforms[shaderType] != other->uniforms[shaderType])
return 1;
}
@@ -438,12 +439,12 @@ int QQuickShaderEffectMaterial::compare(const QSGMaterial *o) const
return 0;
}
-void QQuickShaderEffectMaterial::setProgramSource(const QQuickShaderEffectMaterialKey &source)
+void QQuickOpenGLShaderEffectMaterial::setProgramSource(const QQuickOpenGLShaderEffectMaterialKey &source)
{
m_source = source;
m_emittedLogChanged = false;
- QQuickShaderEffectMaterialCache *cache = QQuickShaderEffectMaterialCache::get();
+ QQuickOpenGLShaderEffectMaterialCache *cache = QQuickOpenGLShaderEffectMaterialCache::get();
m_type = cache->cache.value(m_source);
if (!m_type) {
m_type = new QSGMaterialType();
@@ -451,16 +452,16 @@ void QQuickShaderEffectMaterial::setProgramSource(const QQuickShaderEffectMateri
}
}
-void QQuickShaderEffectMaterial::cleanupMaterialCache()
+void QQuickOpenGLShaderEffectMaterial::cleanupMaterialCache()
{
- QQuickShaderEffectMaterialCache *cache = QQuickShaderEffectMaterialCache::get(false);
+ QQuickOpenGLShaderEffectMaterialCache *cache = QQuickOpenGLShaderEffectMaterialCache::get(false);
if (cache) {
qDeleteAll(cache->cache.values());
delete cache;
}
}
-void QQuickShaderEffectMaterial::updateTextures() const
+void QQuickOpenGLShaderEffectMaterial::updateTextures() const
{
for (int i = 0; i < textureProviders.size(); ++i) {
if (QSGTextureProvider *provider = textureProviders.at(i)) {
@@ -470,7 +471,7 @@ void QQuickShaderEffectMaterial::updateTextures() const
}
}
-void QQuickShaderEffectMaterial::invalidateTextureProvider(QSGTextureProvider *provider)
+void QQuickOpenGLShaderEffectMaterial::invalidateTextureProvider(QSGTextureProvider *provider)
{
for (int i = 0; i < textureProviders.size(); ++i) {
if (provider == textureProviders.at(i))
@@ -479,7 +480,7 @@ void QQuickShaderEffectMaterial::invalidateTextureProvider(QSGTextureProvider *p
}
-QQuickShaderEffectNode::QQuickShaderEffectNode()
+QQuickOpenGLShaderEffectNode::QQuickOpenGLShaderEffectNode()
{
QSGNode::setFlag(UsePreprocess, true);
@@ -488,28 +489,28 @@ QQuickShaderEffectNode::QQuickShaderEffectNode()
#endif
}
-QQuickShaderEffectNode::~QQuickShaderEffectNode()
+QQuickOpenGLShaderEffectNode::~QQuickOpenGLShaderEffectNode()
{
}
-void QQuickShaderEffectNode::markDirtyTexture()
+void QQuickOpenGLShaderEffectNode::markDirtyTexture()
{
markDirty(DirtyMaterial);
Q_EMIT dirtyTexture();
}
-void QQuickShaderEffectNode::textureProviderDestroyed(QObject *object)
+void QQuickOpenGLShaderEffectNode::textureProviderDestroyed(QObject *object)
{
Q_ASSERT(material());
- static_cast<QQuickShaderEffectMaterial *>(material())->invalidateTextureProvider(static_cast<QSGTextureProvider *>(object));
+ static_cast<QQuickOpenGLShaderEffectMaterial *>(material())->invalidateTextureProvider(static_cast<QSGTextureProvider *>(object));
}
-void QQuickShaderEffectNode::preprocess()
+void QQuickOpenGLShaderEffectNode::preprocess()
{
Q_ASSERT(material());
- static_cast<QQuickShaderEffectMaterial *>(material())->updateTextures();
+ static_cast<QQuickOpenGLShaderEffectMaterial *>(material())->updateTextures();
}
-#include "qquickshadereffectnode.moc"
+#include "qquickopenglshadereffectnode.moc"
QT_END_NAMESPACE
diff --git a/src/quick/items/qquickshadereffectnode_p.h b/src/quick/items/qquickopenglshadereffectnode_p.h
index ff32f4e290..adf5ef730b 100644
--- a/src/quick/items/qquickshadereffectnode_p.h
+++ b/src/quick/items/qquickopenglshadereffectnode_p.h
@@ -37,8 +37,8 @@
**
****************************************************************************/
-#ifndef QQUICKSHADEREFFECTNODE_P_H
-#define QQUICKSHADEREFFECTNODE_P_H
+#ifndef QQUICKOPENGLSHADEREFFECTNODE_P_H
+#define QQUICKOPENGLSHADEREFFECTNODE_P_H
//
// W A R N I N G
@@ -56,13 +56,14 @@
#include <QtQuick/qsgtextureprovider.h>
#include <QtQuick/qquickitem.h>
#include <private/qtquickglobal_p.h>
+#include <private/qquickshadereffect_p.h>
#include <QtCore/qsharedpointer.h>
#include <QtCore/qpointer.h>
QT_BEGIN_NAMESPACE
-struct QQuickShaderEffectMaterialKey {
+struct QQuickOpenGLShaderEffectMaterialKey {
enum ShaderType
{
VertexShader,
@@ -72,15 +73,15 @@ struct QQuickShaderEffectMaterialKey {
QByteArray sourceCode[ShaderTypeCount];
- bool operator == (const QQuickShaderEffectMaterialKey &other) const;
- bool operator != (const QQuickShaderEffectMaterialKey &other) const;
+ bool operator == (const QQuickOpenGLShaderEffectMaterialKey &other) const;
+ bool operator != (const QQuickOpenGLShaderEffectMaterialKey &other) const;
};
-uint qHash(const QQuickShaderEffectMaterialKey &key);
+uint qHash(const QQuickOpenGLShaderEffectMaterialKey &key);
class QQuickCustomMaterialShader;
-class QQuickShaderEffectNode;
-class Q_QUICK_PRIVATE_EXPORT QQuickShaderEffectMaterial : public QSGMaterial
+class QQuickOpenGLShaderEffectNode;
+class Q_QUICK_PRIVATE_EXPORT QQuickOpenGLShaderEffectMaterial : public QSGMaterial
{
public:
struct UniformData
@@ -94,25 +95,18 @@ public:
bool operator == (const UniformData &other) const;
};
- enum CullMode
- {
- NoCulling,
- BackFaceCulling,
- FrontFaceCulling
- };
-
- explicit QQuickShaderEffectMaterial(QQuickShaderEffectNode *node = 0);
+ explicit QQuickOpenGLShaderEffectMaterial(QQuickOpenGLShaderEffectNode *node = 0);
QSGMaterialType *type() const Q_DECL_OVERRIDE;
QSGMaterialShader *createShader() const Q_DECL_OVERRIDE;
int compare(const QSGMaterial *other) const Q_DECL_OVERRIDE;
QVector<QByteArray> attributes;
- QVector<UniformData> uniforms[QQuickShaderEffectMaterialKey::ShaderTypeCount];
+ QVector<UniformData> uniforms[QQuickOpenGLShaderEffectMaterialKey::ShaderTypeCount];
QVector<QSGTextureProvider *> textureProviders;
- CullMode cullMode;
+ QQuickShaderEffect::CullMode cullMode;
bool geometryUsesTextureSubRect;
- void setProgramSource(const QQuickShaderEffectMaterialKey &source);
+ void setProgramSource(const QQuickOpenGLShaderEffectMaterialKey &source);
void updateTextures() const;
void invalidateTextureProvider(QSGTextureProvider *provider);
@@ -127,21 +121,21 @@ protected:
// type. The type is cleaned up in cleanupMaterialCache() which is called
// when the GL context is shut down.
QSGMaterialType *m_type;
- QQuickShaderEffectMaterialKey m_source;
+ QQuickOpenGLShaderEffectMaterialKey m_source;
- QQuickShaderEffectNode *m_node;
+ QQuickOpenGLShaderEffectNode *m_node;
bool m_emittedLogChanged;
};
class QSGShaderEffectMesh;
-class Q_QUICK_PRIVATE_EXPORT QQuickShaderEffectNode : public QObject, public QSGGeometryNode
+class Q_QUICK_PRIVATE_EXPORT QQuickOpenGLShaderEffectNode : public QObject, public QSGGeometryNode
{
Q_OBJECT
public:
- QQuickShaderEffectNode();
- virtual ~QQuickShaderEffectNode();
+ QQuickOpenGLShaderEffectNode();
+ virtual ~QQuickOpenGLShaderEffectNode();
void preprocess() Q_DECL_OVERRIDE;
@@ -156,4 +150,4 @@ private Q_SLOTS:
QT_END_NAMESPACE
-#endif // QQUICKSHADEREFFECTNODE_P_H
+#endif // QQUICKOPENGLSHADEREFFECTNODE_P_H
diff --git a/src/quick/items/qquickpainteditem.cpp b/src/quick/items/qquickpainteditem.cpp
index 75919d0791..d21eb93dbf 100644
--- a/src/quick/items/qquickpainteditem.cpp
+++ b/src/quick/items/qquickpainteditem.cpp
@@ -63,13 +63,14 @@ public:
\inmodule QtQuick
- The QQuickPaintedItem makes it possible to use the QPainter API with the QML Scene Graph.
- It sets up a textured rectangle in the Scene Graph and uses a QPainter to paint
- onto the texture. The render target can be either a QImage or a QOpenGLFramebufferObject.
- When the render target is a QImage, QPainter first renders into the image then
- the content is uploaded to the texture.
- When a QOpenGLFramebufferObject is used, QPainter paints directly onto the texture.
- Call update() to trigger a repaint.
+ The QQuickPaintedItem makes it possible to use the QPainter API with the
+ QML Scene Graph. It sets up a textured rectangle in the Scene Graph and
+ uses a QPainter to paint onto the texture. The render target can be either
+ a QImage or, when OpenGL is in use, a QOpenGLFramebufferObject. When the
+ render target is a QImage, QPainter first renders into the image then the
+ content is uploaded to the texture. When a QOpenGLFramebufferObject is
+ used, QPainter paints directly onto the texture. Call update() to trigger a
+ repaint.
To enable QPainter to do anti-aliased rendering, use setAntialiasing().
@@ -78,6 +79,10 @@ public:
public function: paint(), which implements the actual painting. The
painting will be inside the rectangle spanning from 0,0 to
width(),height().
+
+ \note It important to understand the performance implications such items
+ can incur. See QQuickPaintedItem::RenderTarget and
+ QQuickPaintedItem::renderTarget.
*/
/*!
@@ -172,8 +177,6 @@ QQuickPaintedItem::~QQuickPaintedItem()
is processed by the QML Scene Graph when the next frame is rendered. The item will only be
redrawn if it is visible.
- Note that calling this function will trigger a repaint of the whole scene.
-
\sa paint()
*/
void QQuickPaintedItem::update(const QRect &rect)
@@ -499,6 +502,12 @@ void QQuickPaintedItem::setFillColor(const QColor &c)
the QQuickPaintedItem::FramebufferObject render target if the item gets resized often.
By default, the render target is QQuickPaintedItem::Image.
+
+ \note Some Qt Quick backends may not support all render target options. For
+ example, it is likely that non-OpenGL backends will lack support for
+ QQuickPaintedItem::FramebufferObject and
+ QQuickPaintedItem::InvertedYFramebufferObject. Requesting these will then
+ be ignored.
*/
QQuickPaintedItem::RenderTarget QQuickPaintedItem::renderTarget() const
{
@@ -652,11 +661,13 @@ QSGTextureProvider *QQuickPaintedItem::textureProvider() const
return QQuickItem::textureProvider();
Q_D(const QQuickPaintedItem);
+#ifndef QT_NO_OPENGL
QQuickWindow *w = window();
if (!w || !w->openglContext() || QThread::currentThread() != w->openglContext()->thread()) {
qWarning("QQuickPaintedItem::textureProvider: can only be queried on the rendering thread of an exposed window");
return 0;
}
+#endif
if (!d->textureProvider)
d->textureProvider = new QQuickPaintedItemTextureProvider();
d->textureProvider->node = d->node;
diff --git a/src/quick/items/qquickrendercontrol.cpp b/src/quick/items/qquickrendercontrol.cpp
index e36df53d38..ec9a104812 100644
--- a/src/quick/items/qquickrendercontrol.cpp
+++ b/src/quick/items/qquickrendercontrol.cpp
@@ -44,7 +44,11 @@
#include <QtCore/QTime>
#include <QtQuick/private/qquickanimatorcontroller_p.h>
-#include <QtGui/QOpenGLContext>
+#ifndef QT_NO_OPENGL
+# include <QtGui/QOpenGLContext>
+# include <QtQuick/private/qsgdefaultrendercontext_p.h>
+# include <QtQuick/private/qquickopenglshadereffectnode_p.h>
+#endif
#include <QtGui/private/qguiapplication_p.h>
#include <qpa/qplatformintegration.h>
@@ -54,12 +58,11 @@
#include <QtQuick/private/qquickwindow_p.h>
#include <QtCore/private/qobject_p.h>
-#include <private/qquickshadereffectnode_p.h>
QT_BEGIN_NAMESPACE
-
+#ifndef QT_NO_OPENGL
extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha);
-
+#endif
/*!
\class QQuickRenderControl
@@ -134,7 +137,7 @@ QQuickRenderControlPrivate::QQuickRenderControlPrivate()
qAddPostRoutine(cleanup);
sg = QSGContext::createDefaultContext();
}
- rc = new QSGRenderContext(sg);
+ rc = sg->createRenderContext();
}
void QQuickRenderControlPrivate::cleanup()
@@ -183,7 +186,9 @@ void QQuickRenderControlPrivate::windowDestroyed()
delete QQuickWindowPrivate::get(window)->animationController;
QQuickWindowPrivate::get(window)->animationController = 0;
- QQuickShaderEffectMaterial::cleanupMaterialCache();
+#ifndef QT_NO_OPENGL
+ QQuickOpenGLShaderEffectMaterial::cleanupMaterialCache();
+#endif
window = 0;
}
@@ -213,8 +218,9 @@ void QQuickRenderControl::prepareThread(QThread *targetThread)
*/
void QQuickRenderControl::initialize(QOpenGLContext *gl)
{
- Q_D(QQuickRenderControl);
+ Q_D(QQuickRenderControl);
+#ifndef QT_NO_OPENGL
if (!d->window) {
qWarning("QQuickRenderControl::initialize called with no associated window");
return;
@@ -229,9 +235,10 @@ void QQuickRenderControl::initialize(QOpenGLContext *gl)
// It cannot be done here since the surface to use may not be the
// surface belonging to window. In fact window may not have a native
// window/surface at all.
-
d->rc->initialize(gl);
-
+#else
+ Q_UNUSED(gl)
+#endif
d->initialized = true;
}
@@ -363,7 +370,11 @@ QImage QQuickRenderControl::grab()
return QImage();
render();
+#ifndef QT_NO_OPENGL
QImage grabContent = qt_gl_read_framebuffer(d->window->size() * d->window->effectiveDevicePixelRatio(), false, false);
+#else
+ QImage grabContent = d->window->grabWindow();
+#endif
return grabContent;
}
diff --git a/src/quick/items/qquickrendererinfo.cpp b/src/quick/items/qquickrendererinfo.cpp
new file mode 100644
index 0000000000..e41f4ebbae
--- /dev/null
+++ b/src/quick/items/qquickrendererinfo.cpp
@@ -0,0 +1,129 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickrendererinfo_p.h"
+#include "qquickwindow.h"
+#include "qquickitem.h"
+#include <qsgrendererinterface.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \qmltype RendererInfo
+ \instantiates QQuickRendererInfo
+ \inqmlmodule QtQuick
+ \ingroup qtquick-effects
+ \since 5.8
+ \since QtQuick 2.8
+ \brief Provides information about the used Qt Quick backend
+
+ The RendererInfo attached type provides information about the scenegraph
+ backend used to render the contents of the associated window.
+
+ If the item to which the properties are attached is not currently
+ associated with any window, the properties are set to default values. When
+ the associated window changes, the properties will update.
+
+ \sa OpenGLInfo, ShaderEffect
+ */
+
+QQuickRendererInfo::QQuickRendererInfo(QQuickItem *item)
+ : QObject(item)
+ , m_window(0)
+ , m_api(Unknown)
+{
+ connect(item, SIGNAL(windowChanged(QQuickWindow*)), this, SLOT(setWindow(QQuickWindow*)));
+ setWindow(item->window());
+}
+
+QQuickRendererInfo *QQuickRendererInfo::qmlAttachedProperties(QObject *object)
+{
+ if (QQuickItem *item = qobject_cast<QQuickItem *>(object))
+ return new QQuickRendererInfo(item);
+
+ return nullptr;
+}
+
+/*!
+ \qmlproperty enumeration QtQuick::RendererInfo::api
+
+ This property describes the graphics API that is currently in use.
+
+ The possible values are:
+ \list
+ \li RendererInfo.Unknown - the default value when no active scenegraph is associated with the item
+ \li RendererInfo.Software - Qt Quick's software renderer based on QPainter with the raster paint engine
+ \li RendererInfo.OpenGL - OpenGL or OpenGL ES
+ \li RendererInfo.Direct3D12 - Direct3D 12
+ \endlist
+ */
+
+void QQuickRendererInfo::updateInfo()
+{
+ GraphicsAPI newAPI = Unknown;
+
+ if (m_window && m_window->isSceneGraphInitialized()) {
+ QSGRendererInterface *rif = m_window->rendererInterface();
+ if (rif)
+ newAPI = GraphicsAPI(rif->graphicsAPI()); // this function is safe to call on the gui/main thread too
+ }
+
+ if (m_api != newAPI) {
+ m_api = newAPI;
+ emit apiChanged();
+ }
+}
+
+void QQuickRendererInfo::setWindow(QQuickWindow *window)
+{
+ if (m_window != window) {
+ if (m_window) {
+ disconnect(m_window, SIGNAL(sceneGraphInitialized()), this, SLOT(updateInfo()));
+ disconnect(m_window, SIGNAL(sceneGraphInvalidated()), this, SLOT(updateInfo()));
+ }
+ if (window) {
+ connect(window, SIGNAL(sceneGraphInitialized()), this, SLOT(updateInfo()));
+ connect(window, SIGNAL(sceneGraphInvalidated()), this, SLOT(updateInfo()));
+ }
+ m_window = window;
+ }
+ updateInfo();
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/items/qquickrendererinfo_p.h b/src/quick/items/qquickrendererinfo_p.h
new file mode 100644
index 0000000000..d76f4010e0
--- /dev/null
+++ b/src/quick/items/qquickrendererinfo_p.h
@@ -0,0 +1,100 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKRENDERERINFO_P_H
+#define QQUICKRENDERERINFO_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qobject.h>
+#include <QtCore/qpointer.h>
+#include <QtQml/qqml.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQuickItem;
+class QQuickWindow;
+
+class QQuickRendererInfo : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(GraphicsAPI api READ api NOTIFY apiChanged FINAL)
+
+public:
+ // must match QSGRendererInterface
+ enum GraphicsAPI {
+ Unknown,
+ Software,
+ OpenGL,
+ Direct3D12
+ };
+ Q_ENUM(GraphicsAPI)
+
+ QQuickRendererInfo(QQuickItem *item = 0);
+
+ static QQuickRendererInfo *qmlAttachedProperties(QObject *object);
+
+ GraphicsAPI api() const { return m_api; }
+
+Q_SIGNALS:
+ void apiChanged();
+
+private Q_SLOTS:
+ void updateInfo();
+ void setWindow(QQuickWindow *window);
+
+private:
+ QPointer<QQuickWindow> m_window;
+ GraphicsAPI m_api;
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPEINFO(QQuickRendererInfo, QML_HAS_ATTACHED_PROPERTIES)
+
+#endif // QQUICKRENDERERINFO_P_H
diff --git a/src/quick/items/qquickshadereffect.cpp b/src/quick/items/qquickshadereffect.cpp
index d5c0cc8180..1fbc9c201f 100644
--- a/src/quick/items/qquickshadereffect.cpp
+++ b/src/quick/items/qquickshadereffect.cpp
@@ -38,506 +38,14 @@
****************************************************************************/
#include <private/qquickshadereffect_p.h>
-#include <private/qquickshadereffectnode_p.h>
-
-#include <QtQuick/qsgmaterial.h>
-#include <QtQuick/private/qsgshadersourcebuilder_p.h>
-#include "qquickitem_p.h"
-
-#include <QtQuick/private/qsgcontext_p.h>
-#include <QtQuick/qsgtextureprovider.h>
-#include "qquickwindow.h"
-
-#include "qquickimage_p.h"
-#include "qquickshadereffectsource_p.h"
-
-#include <QtCore/qsignalmapper.h>
-#include <QtGui/qopenglframebufferobject.h>
+#include <private/qsgcontextplugin_p.h>
+#ifndef QT_NO_OPENGL
+#include <private/qquickopenglshadereffect_p.h>
+#endif
+#include <private/qquickgenericshadereffect_p.h>
QT_BEGIN_NAMESPACE
-static const char qt_position_attribute_name[] = "qt_Vertex";
-static const char qt_texcoord_attribute_name[] = "qt_MultiTexCoord0";
-
-const char *qtPositionAttributeName()
-{
- return qt_position_attribute_name;
-}
-
-const char *qtTexCoordAttributeName()
-{
- return qt_texcoord_attribute_name;
-}
-
-namespace {
-
- enum VariableQualifier {
- AttributeQualifier,
- UniformQualifier
- };
-
- inline bool qt_isalpha(char c)
- {
- char ch = c | 0x20;
- return (ch >= 'a' && ch <= 'z') || c == '_';
- }
-
- inline bool qt_isalnum(char c)
- {
- return qt_isalpha(c) || (c >= '0' && c <= '9');
- }
-
- inline bool qt_isspace(char c)
- {
- return c == ' ' || (c >= 0x09 && c <= 0x0d);
- }
-
- // Returns -1 if not found, returns index to first character after the name if found.
- int qt_search_for_variable(const char *s, int length, int index, VariableQualifier &decl,
- int &typeIndex, int &typeLength,
- int &nameIndex, int &nameLength,
- QQuickShaderEffectCommon::Key::ShaderType shaderType)
- {
- enum Identifier {
- QualifierIdentifier, // Base state
- PrecisionIdentifier,
- TypeIdentifier,
- NameIdentifier
- };
- Identifier expected = QualifierIdentifier;
- bool compilerDirectiveExpected = index == 0;
-
- while (index < length) {
- // Skip whitespace.
- while (qt_isspace(s[index])) {
- compilerDirectiveExpected |= s[index] == '\n';
- ++index;
- }
-
- if (qt_isalpha(s[index])) {
- // Read identifier.
- int idIndex = index;
- ++index;
- while (qt_isalnum(s[index]))
- ++index;
- int idLength = index - idIndex;
-
- const int attrLen = sizeof("attribute") - 1;
- const int inLen = sizeof("in") - 1;
- const int uniLen = sizeof("uniform") - 1;
- const int loLen = sizeof("lowp") - 1;
- const int medLen = sizeof("mediump") - 1;
- const int hiLen = sizeof("highp") - 1;
-
- switch (expected) {
- case QualifierIdentifier:
- if (idLength == attrLen && qstrncmp("attribute", s + idIndex, attrLen) == 0) {
- decl = AttributeQualifier;
- expected = PrecisionIdentifier;
- } else if (shaderType == QQuickShaderEffectCommon::Key::VertexShader
- && idLength == inLen && qstrncmp("in", s + idIndex, inLen) == 0) {
- decl = AttributeQualifier;
- expected = PrecisionIdentifier;
- } else if (idLength == uniLen && qstrncmp("uniform", s + idIndex, uniLen) == 0) {
- decl = UniformQualifier;
- expected = PrecisionIdentifier;
- }
- break;
- case PrecisionIdentifier:
- if ((idLength == loLen && qstrncmp("lowp", s + idIndex, loLen) == 0)
- || (idLength == medLen && qstrncmp("mediump", s + idIndex, medLen) == 0)
- || (idLength == hiLen && qstrncmp("highp", s + idIndex, hiLen) == 0))
- {
- expected = TypeIdentifier;
- break;
- }
- // Fall through.
- case TypeIdentifier:
- typeIndex = idIndex;
- typeLength = idLength;
- expected = NameIdentifier;
- break;
- case NameIdentifier:
- nameIndex = idIndex;
- nameLength = idLength;
- return index; // Attribute or uniform declaration found. Return result.
- default:
- break;
- }
- } else if (s[index] == '#' && compilerDirectiveExpected) {
- // Skip compiler directives.
- ++index;
- while (index < length && (s[index] != '\n' || s[index - 1] == '\\'))
- ++index;
- } else if (s[index] == '/' && s[index + 1] == '/') {
- // Skip comments.
- index += 2;
- while (index < length && s[index] != '\n')
- ++index;
- } else if (s[index] == '/' && s[index + 1] == '*') {
- // Skip comments.
- index += 2;
- while (index < length && (s[index] != '*' || s[index + 1] != '/'))
- ++index;
- if (index < length)
- index += 2; // Skip star-slash.
- } else {
- expected = QualifierIdentifier;
- ++index;
- }
- compilerDirectiveExpected = false;
- }
- return -1;
- }
-}
-
-
-
-QQuickShaderEffectCommon::~QQuickShaderEffectCommon()
-{
- for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType)
- qDeleteAll(signalMappers[shaderType]);
-}
-
-void QQuickShaderEffectCommon::disconnectPropertySignals(QQuickItem *item, Key::ShaderType shaderType)
-{
- for (int i = 0; i < uniformData[shaderType].size(); ++i) {
- if (signalMappers[shaderType].at(i) == 0)
- continue;
- const UniformData &d = uniformData[shaderType].at(i);
- QSignalMapper *mapper = signalMappers[shaderType].at(i);
- QObject::disconnect(item, 0, mapper, SLOT(map()));
- QObject::disconnect(mapper, SIGNAL(mapped(int)), item, SLOT(propertyChanged(int)));
- if (d.specialType == UniformData::Sampler) {
- QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value));
- if (source) {
- if (item->window())
- QQuickItemPrivate::get(source)->derefWindow();
- QObject::disconnect(source, SIGNAL(destroyed(QObject*)), item, SLOT(sourceDestroyed(QObject*)));
- }
- }
- }
-}
-
-void QQuickShaderEffectCommon::connectPropertySignals(QQuickItem *item, Key::ShaderType shaderType)
-{
- for (int i = 0; i < uniformData[shaderType].size(); ++i) {
- if (signalMappers[shaderType].at(i) == 0)
- continue;
- const UniformData &d = uniformData[shaderType].at(i);
- int pi = item->metaObject()->indexOfProperty(d.name.constData());
- if (pi >= 0) {
- QMetaProperty mp = item->metaObject()->property(pi);
- if (!mp.hasNotifySignal())
- qWarning("QQuickShaderEffect: property '%s' does not have notification method!", d.name.constData());
- const QByteArray signalName = '2' + mp.notifySignal().methodSignature();
- QSignalMapper *mapper = signalMappers[shaderType].at(i);
- QObject::connect(item, signalName, mapper, SLOT(map()));
- QObject::connect(mapper, SIGNAL(mapped(int)), item, SLOT(propertyChanged(int)));
- } else {
- // If the source is set via a dynamic property, like the layer is, then we need this
- // check to disable the warning.
- if (!item->property(d.name.constData()).isValid())
- qWarning("QQuickShaderEffect: '%s' does not have a matching property!", d.name.constData());
- }
-
- if (d.specialType == UniformData::Sampler) {
- QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value));
- if (source) {
- if (item->window())
- QQuickItemPrivate::get(source)->refWindow(item->window());
- QObject::connect(source, SIGNAL(destroyed(QObject*)), item, SLOT(sourceDestroyed(QObject*)));
- }
- }
- }
-}
-
-void QQuickShaderEffectCommon::updateParseLog(bool ignoreAttributes)
-{
- parseLog.clear();
- if (!ignoreAttributes) {
- if (!attributes.contains(qt_position_attribute_name)) {
- parseLog += QLatin1String("Warning: Missing reference to \'");
- parseLog += QLatin1String(qt_position_attribute_name);
- parseLog += QLatin1String("\'.\n");
- }
- if (!attributes.contains(qt_texcoord_attribute_name)) {
- parseLog += QLatin1String("Warning: Missing reference to \'");
- parseLog += QLatin1String(qt_texcoord_attribute_name);
- parseLog += QLatin1String("\'.\n");
- }
- }
- bool respectsMatrix = false;
- bool respectsOpacity = false;
- for (int i = 0; i < uniformData[Key::VertexShader].size(); ++i)
- respectsMatrix |= uniformData[Key::VertexShader].at(i).specialType == UniformData::Matrix;
- for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) {
- for (int i = 0; i < uniformData[shaderType].size(); ++i)
- respectsOpacity |= uniformData[shaderType].at(i).specialType == UniformData::Opacity;
- }
- if (!respectsMatrix)
- parseLog += QLatin1String("Warning: Vertex shader is missing reference to \'qt_Matrix\'.\n");
- if (!respectsOpacity)
- parseLog += QLatin1String("Warning: Shaders are missing reference to \'qt_Opacity\'.\n");
-}
-
-void QQuickShaderEffectCommon::lookThroughShaderCode(QQuickItem *item, Key::ShaderType shaderType, const QByteArray &code)
-{
- int index = 0;
- int typeIndex = -1;
- int typeLength = 0;
- int nameIndex = -1;
- int nameLength = 0;
- const char *s = code.constData();
- VariableQualifier decl = AttributeQualifier;
- while ((index = qt_search_for_variable(s, code.size(), index, decl, typeIndex, typeLength,
- nameIndex, nameLength, shaderType)) != -1)
- {
- if (decl == AttributeQualifier) {
- if (shaderType == Key::VertexShader)
- attributes.append(QByteArray(s + nameIndex, nameLength));
- } else {
- Q_ASSERT(decl == UniformQualifier);
-
- const int sampLen = sizeof("sampler2D") - 1;
- const int opLen = sizeof("qt_Opacity") - 1;
- const int matLen = sizeof("qt_Matrix") - 1;
- const int srLen = sizeof("qt_SubRect_") - 1;
-
- UniformData d;
- QSignalMapper *mapper = 0;
- d.name = QByteArray(s + nameIndex, nameLength);
- if (nameLength == opLen && qstrncmp("qt_Opacity", s + nameIndex, opLen) == 0) {
- d.specialType = UniformData::Opacity;
- } else if (nameLength == matLen && qstrncmp("qt_Matrix", s + nameIndex, matLen) == 0) {
- d.specialType = UniformData::Matrix;
- } else if (nameLength > srLen && qstrncmp("qt_SubRect_", s + nameIndex, srLen) == 0) {
- d.specialType = UniformData::SubRect;
- } else {
- mapper = new QSignalMapper;
- mapper->setMapping(item, uniformData[shaderType].size() | (shaderType << 16));
- d.value = item->property(d.name.constData());
- bool sampler = typeLength == sampLen && qstrncmp("sampler2D", s + typeIndex, sampLen) == 0;
- d.specialType = sampler ? UniformData::Sampler : UniformData::None;
- }
- uniformData[shaderType].append(d);
- signalMappers[shaderType].append(mapper);
- }
- }
-}
-
-void QQuickShaderEffectCommon::updateShader(QQuickItem *item, Key::ShaderType shaderType)
-{
- disconnectPropertySignals(item, shaderType);
- qDeleteAll(signalMappers[shaderType]);
- uniformData[shaderType].clear();
- signalMappers[shaderType].clear();
- if (shaderType == Key::VertexShader)
- attributes.clear();
-
- const QByteArray &code = source.sourceCode[shaderType];
- if (code.isEmpty()) {
- // Optimize for default code.
- if (shaderType == Key::VertexShader) {
- attributes.append(QByteArray(qt_position_attribute_name));
- attributes.append(QByteArray(qt_texcoord_attribute_name));
- UniformData d;
- d.name = "qt_Matrix";
- d.specialType = UniformData::Matrix;
- uniformData[Key::VertexShader].append(d);
- signalMappers[Key::VertexShader].append(0);
- } else if (shaderType == Key::FragmentShader) {
- UniformData d;
- d.name = "qt_Opacity";
- d.specialType = UniformData::Opacity;
- uniformData[Key::FragmentShader].append(d);
- signalMappers[Key::FragmentShader].append(0);
- QSignalMapper *mapper = new QSignalMapper;
- mapper->setMapping(item, 1 | (Key::FragmentShader << 16));
- const char *sourceName = "source";
- d.name = sourceName;
- d.value = item->property(sourceName);
- d.specialType = UniformData::Sampler;
- uniformData[Key::FragmentShader].append(d);
- signalMappers[Key::FragmentShader].append(mapper);
- }
- } else {
- lookThroughShaderCode(item, shaderType, code);
- }
-
- connectPropertySignals(item, shaderType);
-}
-
-void QQuickShaderEffectCommon::updateMaterial(QQuickShaderEffectNode *node,
- QQuickShaderEffectMaterial *material,
- bool updateUniforms, bool updateUniformValues,
- bool updateTextureProviders)
-{
- if (updateUniforms) {
- for (int i = 0; i < material->textureProviders.size(); ++i) {
- QSGTextureProvider *t = material->textureProviders.at(i);
- if (t) {
- QObject::disconnect(t, SIGNAL(textureChanged()), node, SLOT(markDirtyTexture()));
- QObject::disconnect(t, SIGNAL(destroyed(QObject*)), node, SLOT(textureProviderDestroyed(QObject*)));
- }
- }
-
- // First make room in the textureProviders array. Set to proper value further down.
- int textureProviderCount = 0;
- for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) {
- for (int i = 0; i < uniformData[shaderType].size(); ++i) {
- if (uniformData[shaderType].at(i).specialType == UniformData::Sampler)
- ++textureProviderCount;
- }
- material->uniforms[shaderType] = uniformData[shaderType];
- }
- material->textureProviders.fill(0, textureProviderCount);
- updateUniformValues = false;
- updateTextureProviders = true;
- }
-
- if (updateUniformValues) {
- for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) {
- Q_ASSERT(uniformData[shaderType].size() == material->uniforms[shaderType].size());
- for (int i = 0; i < uniformData[shaderType].size(); ++i)
- material->uniforms[shaderType][i].value = uniformData[shaderType].at(i).value;
- }
- }
-
- if (updateTextureProviders) {
- int index = 0;
- for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) {
- for (int i = 0; i < uniformData[shaderType].size(); ++i) {
- const UniformData &d = uniformData[shaderType].at(i);
- if (d.specialType != UniformData::Sampler)
- continue;
- QSGTextureProvider *oldProvider = material->textureProviders.at(index);
- QSGTextureProvider *newProvider = 0;
- QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value));
- if (source && source->isTextureProvider())
- newProvider = source->textureProvider();
- if (newProvider != oldProvider) {
- if (oldProvider) {
- QObject::disconnect(oldProvider, SIGNAL(textureChanged()), node, SLOT(markDirtyTexture()));
- QObject::disconnect(oldProvider, SIGNAL(destroyed(QObject*)), node, SLOT(textureProviderDestroyed(QObject*)));
- }
- if (newProvider) {
- Q_ASSERT_X(newProvider->thread() == QThread::currentThread(),
- "QQuickShaderEffect::updatePaintNode",
- "Texture provider must belong to the rendering thread");
- QObject::connect(newProvider, SIGNAL(textureChanged()), node, SLOT(markDirtyTexture()));
- QObject::connect(newProvider, SIGNAL(destroyed(QObject*)), node, SLOT(textureProviderDestroyed(QObject*)));
- } else {
- const char *typeName = source ? source->metaObject()->className() : d.value.typeName();
- qWarning("ShaderEffect: Property '%s' is not assigned a valid texture provider (%s).",
- d.name.constData(), typeName);
- }
- material->textureProviders[index] = newProvider;
- }
- ++index;
- }
- }
- Q_ASSERT(index == material->textureProviders.size());
- }
-}
-
-void QQuickShaderEffectCommon::updateWindow(QQuickWindow *window)
-{
- // See comment in QQuickShaderEffectCommon::propertyChanged().
- if (window) {
- for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) {
- for (int i = 0; i < uniformData[shaderType].size(); ++i) {
- const UniformData &d = uniformData[shaderType].at(i);
- if (d.specialType == UniformData::Sampler) {
- QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value));
- if (source)
- QQuickItemPrivate::get(source)->refWindow(window);
- }
- }
- }
- } else {
- for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) {
- for (int i = 0; i < uniformData[shaderType].size(); ++i) {
- const UniformData &d = uniformData[shaderType].at(i);
- if (d.specialType == UniformData::Sampler) {
- QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value));
- if (source)
- QQuickItemPrivate::get(source)->derefWindow();
- }
- }
- }
- }
-}
-
-void QQuickShaderEffectCommon::sourceDestroyed(QObject *object)
-{
- for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) {
- for (int i = 0; i < uniformData[shaderType].size(); ++i) {
- UniformData &d = uniformData[shaderType][i];
- if (d.specialType == UniformData::Sampler && d.value.canConvert<QObject *>()) {
- if (qvariant_cast<QObject *>(d.value) == object)
- d.value = QVariant();
- }
- }
- }
-}
-
-static bool qquick_uniqueInUniformData(QQuickItem *source, const QVector<QQuickShaderEffectMaterial::UniformData> *uniformData, int typeToSkip, int indexToSkip)
-{
- for (int s=0; s<QQuickShaderEffectMaterialKey::ShaderTypeCount; ++s) {
- for (int i=0; i<uniformData[s].size(); ++i) {
- if (s == typeToSkip && i == indexToSkip)
- continue;
- const QQuickShaderEffectMaterial::UniformData &d = uniformData[s][i];
- if (d.specialType == QQuickShaderEffectMaterial::UniformData::Sampler && qvariant_cast<QObject *>(d.value) == source)
- return false;
- }
- }
- return true;
-}
-
-void QQuickShaderEffectCommon::propertyChanged(QQuickItem *item, int mappedId,
- bool *textureProviderChanged)
-{
- Key::ShaderType shaderType = Key::ShaderType(mappedId >> 16);
- int index = mappedId & 0xffff;
- UniformData &d = uniformData[shaderType][index];
- if (d.specialType == UniformData::Sampler) {
- QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value));
- if (source) {
- if (item->window())
- QQuickItemPrivate::get(source)->derefWindow();
-
- // QObject::disconnect() will disconnect all matching connections. If the same
- // source has been attached to two separate samplers, then changing one of them
- // would trigger both to be disconnected. Without the connection we'll end up
- // with a dangling pointer in the uniformData.
- if (qquick_uniqueInUniformData(source, uniformData, shaderType, index))
- QObject::disconnect(source, SIGNAL(destroyed(QObject*)), item, SLOT(sourceDestroyed(QObject*)));
- }
-
- d.value = item->property(d.name.constData());
-
- source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value));
- if (source) {
- // 'source' needs a window to get a scene graph node. It usually gets one through its
- // parent, but if the source item is "inline" rather than a reference -- i.e.
- // "property variant source: Image { }" instead of "property variant source: foo" -- it
- // will not get a parent. In those cases, 'source' should get the window from 'item'.
- if (item->window())
- QQuickItemPrivate::get(source)->refWindow(item->window());
- QObject::connect(source, SIGNAL(destroyed(QObject*)), item, SLOT(sourceDestroyed(QObject*)));
- }
- if (textureProviderChanged)
- *textureProviderChanged = true;
- } else {
- d.value = item->property(d.name.constData());
- if (textureProviderChanged)
- *textureProviderChanged = false;
- }
-}
-
-
/*!
\qmltype ShaderEffect
\instantiates QQuickShaderEffect
@@ -546,11 +54,18 @@ void QQuickShaderEffectCommon::propertyChanged(QQuickItem *item, int mappedId,
\ingroup qtquick-effects
\brief Applies custom shaders to a rectangle
- The ShaderEffect type applies a custom OpenGL
- \l{vertexShader}{vertex} and \l{fragmentShader}{fragment} shader to a
+ The ShaderEffect type applies a custom
+ \l{vertexShader}{vertex} and \l{fragmentShader}{fragment (pixel)} shader to a
rectangle. It allows you to write effects such as drop shadow, blur,
colorize and page curl directly in QML.
+ \note Depending on the Qt Quick scenegraph backend in use, the ShaderEffect
+ type may not be supported (for example, with the software backend), or may
+ use a different shading language with rules and expectations different from
+ OpenGL and GLSL.
+
+ \section1 OpenGL and GLSL
+
There are two types of input to the \l vertexShader:
uniform variables and attributes. Some are predefined:
\list
@@ -650,9 +165,167 @@ void QQuickShaderEffectCommon::propertyChanged(QQuickItem *item, int mappedId,
\endqml
\endtable
- By default, the ShaderEffect consists of four vertices, one for each
- corner. For non-linear vertex transformations, like page curl, you can
- specify a fine grid of vertices by specifying a \l mesh resolution.
+ \note Scene Graph textures have origin in the top-left corner rather than
+ bottom-left which is common in OpenGL.
+
+ For information about the GLSL version being used, see \l QtQuick::OpenGLInfo.
+
+ \section1 Direct3D and HLSL
+
+ Direct3D backends provide ShaderEffect support with HLSL. The Direct3D 12
+ backend requires using at least Shader Model 5.0 both for vertex and pixel
+ shaders. When necessary, the \l shaderType property can be used to decide
+ at runtime what kind of value to assign to \l fragmentShader or
+ \l vertexShader.
+
+ All concepts described above for OpenGL and GLSL apply to Direct3D and HLSL
+ as well. There are however a number of notable practical differences, which
+ are the following:
+
+ Instead of uniforms, HLSL shaders are expected to use a single constant
+ buffer, assigned to register \c b0. The special names \c qt_Matrix,
+ \c qt_Opacity, and \c qt_SubRect_<name> function the same way as with GLSL.
+ All other members of the buffer are expected to map to properties in the
+ ShaderEffect item.
+
+ \note The buffer layout must be compatible for both shaders. This means
+ that application-provided shaders must make sure \c qt_Matrix and
+ \c qt_Opacity are included in the buffer, starting at offset 0, when custom
+ code is provided for one type of shader only, leading to ShaderEffect
+ providing the other shader. This is due to ShaderEffect's built-in shader code
+ declaring a constant buffer containing \c{float4x4 qt_Matrix; float qt_Opacity;}.
+
+ Unlike GLSL's attributes, no names are used for vertex input elements.
+ Therefore qt_Vertex and qt_MultiTexCoord0 are not relevant. Instead, the
+ standard Direct3D semantics, \c POSITION and \c TEXCOORD (or \c TEXCOORD0)
+ are used for identifying the correct input layout.
+
+ Unlike GLSL's samplers, texture and sampler objects are separate in HLSL.
+ Shaders are expected to expect 2D, non-array, non-multisample textures.
+ Both the texture and sampler binding points are expected to be sequential
+ and start from 0 (meaning registers \c{t0, t1, ...}, and \c{s0, s1, ...},
+ respectively). Unlike with OpenGL, samplers are not mapped to Qt Quick item
+ properties and therefore the name of the sampler is not relevant. Instead,
+ it is the textures that map to properties referencing \l Image or
+ \l ShaderEffectSource items.
+
+ Unlike with OpenGL, runtime compilation of shader source code may not be
+ supported. Backends for modern APIs are likely to prefer offline
+ compilation and shipping pre-compiled bytecode with applications instead of
+ inlined shader source strings. See the \l shaderSourceType and
+ \l shaderCompilationType properties.
+
+ \table 70%
+ \row
+ \li \image declarative-shadereffectitem.png
+ \li \qml
+ import QtQuick 2.8
+
+ Rectangle {
+ width: 200; height: 100
+ Row {
+ Image { id: img;
+ sourceSize { width: 100; height: 100 } source: "qt-logo.png" }
+ ShaderEffect {
+ width: 100; height: 100
+ property variant src: img
+ fragmentShader: "qrc:/effect_ps.cso"
+ }
+ }
+ }
+ \endqml
+ \row
+ \li where \c effect_ps.cso is the compiled bytecode for the following HLSL shader:
+ \code
+ cbuffer ConstantBuffer : register(b0)
+ {
+ float4x4 qt_Matrix;
+ float qt_Opacity;
+ };
+ Texture2D src : register(t0);
+ SamplerState srcSampler : register(s0);
+ float4 ExamplePixelShader(float4 position : SV_POSITION, float2 coord : TEXCOORD0) : SV_TARGET
+ {
+ float4 tex = src.Sample(srcSampler, coord);
+ float3 col = dot(tex.rgb, float3(0.344, 0.5, 0.156));
+ return float4(col, tex.a) * qt_Opacity;
+ }
+ \endcode
+ \endtable
+
+ The above is equivalent to the OpenGL example presented earlier. The vertex
+ shader is provided implicitly by ShaderEffect. Note that the output of the
+ pixel shader is using premultiplied alpha and that \c qt_Matrix is present
+ in the constant buffer at offset 0, even though the pixel shader does not
+ use the value.
+
+ Some effects will want to provide a vertex shader as well. Below is a
+ similar effect with both the vertex and fragment shader provided by the
+ application. This time the colorization factor is provided by the QML item
+ instead of hardcoding it in the shader. This can allow, among others,
+ animating the value using QML's and Qt Quick's standard facilities.
+
+ \table 70%
+ \row
+ \li \image declarative-shadereffectitem.png
+ \li \qml
+ import QtQuick 2.8
+
+ Rectangle {
+ width: 200; height: 100
+ Row {
+ Image { id: img;
+ sourceSize { width: 100; height: 100 } source: "qt-logo.png" }
+ ShaderEffect {
+ width: 100; height: 100
+ property variant src: img
+ property variant color: Qt.vector3d(0.344, 0.5, 0.156)
+ vertexShader: "qrc:/effect_vs.cso"
+ fragmentShader: "qrc:/effect_ps.cso"
+ }
+ }
+ }
+ \endqml
+ \row
+ \li where \c effect_vs.cso and \c effect_ps.cso are the compiled bytecode
+ for \c ExampleVertexShader and \c ExamplePixelShader. The source code is
+ presented as one snippet here, the shaders can however be placed in
+ separate source files as well.
+ \code
+ cbuffer ConstantBuffer : register(b0)
+ {
+ float4x4 qt_Matrix;
+ float qt_Opacity;
+ float3 color;
+ };
+ Texture2D src : register(t0);
+ SamplerState srcSampler : register(s0);
+ struct PSInput
+ {
+ float4 position : SV_POSITION;
+ float2 coord : TEXCOORD0;
+ };
+ PSInput ExampleVertexShader(float4 position : POSITION, float2 coord : TEXCOORD0)
+ {
+ PSInput result;
+ result.position = mul(qt_Matrix, position);
+ result.coord = coord;
+ return result;
+ }
+ float4 ExamplePixelShader(PSInput input) : SV_TARGET
+ {
+ float4 tex = src.Sample(srcSampler, coord);
+ float3 col = dot(tex.rgb, color);
+ return float4(col, tex.a) * qt_Opacity;
+ }
+ \endcode
+ \endtable
+
+ \note With OpenGL the \c y coordinate runs from bottom to top whereas with
+ Direct 3D it goes top to bottom. For shader effect sources Qt Quick hides
+ the difference by treating QtQuick::ShaderEffectSource::textureMirroring as
+ appropriate, meaning texture coordinates in HLSL version of the shaders
+ will not need any adjustments compared to the equivalent GLSL code.
\section1 ShaderEffect and Item Layers
@@ -675,97 +348,117 @@ void QQuickShaderEffectCommon::propertyChanged(QQuickItem *item, int mappedId,
\li \snippet qml/opacitymask.qml 1
\endtable
- The \l {Qt Graphical Effects} module contains several ready-made effects
- for using with Qt Quick applications.
+ \section1 Other notes
- \note Scene Graph textures have origin in the top-left corner rather than
- bottom-left which is common in OpenGL.
+ By default, the ShaderEffect consists of four vertices, one for each
+ corner. For non-linear vertex transformations, like page curl, you can
+ specify a fine grid of vertices by specifying a \l mesh resolution.
- For information about the GLSL version being used, see \l QtQuick::OpenGLInfo.
+ The \l {Qt Graphical Effects} module contains several ready-made effects
+ for using with Qt Quick applications.
\sa {Item Layers}
*/
+QSGContextFactoryInterface::Flags qsg_backend_flags();
+
QQuickShaderEffect::QQuickShaderEffect(QQuickItem *parent)
- : QQuickItem(parent)
- , m_meshResolution(1, 1)
- , m_mesh(0)
- , m_cullMode(NoCulling)
- , m_status(Uncompiled)
- , m_blending(true)
- , m_dirtyUniforms(true)
- , m_dirtyUniformValues(true)
- , m_dirtyTextureProviders(true)
- , m_dirtyProgram(true)
- , m_dirtyParseLog(true)
- , m_dirtyMesh(true)
- , m_dirtyGeometry(true)
- , m_customVertexShader(false)
- , m_supportsAtlasTextures(false)
+ : QQuickItem(parent),
+#ifndef QT_NO_OPENGL
+ m_glImpl(nullptr),
+#endif
+ m_impl(nullptr)
{
setFlag(QQuickItem::ItemHasContents);
-}
-QQuickShaderEffect::~QQuickShaderEffect()
-{
- for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType)
- m_common.disconnectPropertySignals(this, Key::ShaderType(shaderType));
+#ifndef QT_NO_OPENGL
+ if (!qsg_backend_flags().testFlag(QSGContextFactoryInterface::SupportsShaderEffectNode))
+ m_glImpl = new QQuickOpenGLShaderEffect(this, this);
+
+ if (!m_glImpl)
+#endif
+ m_impl = new QQuickGenericShaderEffect(this, this);
}
/*!
\qmlproperty string QtQuick::ShaderEffect::fragmentShader
- This property holds the fragment shader's GLSL source code.
- The default shader expects the texture coordinate to be passed from the
- vertex shader as "varying highp vec2 qt_TexCoord0", and it samples from a
- sampler2D named "source".
+ This property holds the fragment (pixel) shader's source code or a
+ reference to the pre-compiled bytecode. Some APIs, like OpenGL, always
+ support runtime compilation and therefore the traditional Qt Quick way of
+ inlining shader source strings is functional. Qt Quick backends for other
+ APIs may however limit support to pre-compiled bytecode like SPIR-V or D3D
+ shader bytecode. There the string is simply a filename, which may be a file
+ in the filesystem or bundled with the executable via Qt's resource system.
+
+ With GLSL the default shader expects the texture coordinate to be passed
+ from the vertex shader as \c{varying highp vec2 qt_TexCoord0}, and it
+ samples from a sampler2D named \c source. With HLSL the texture is named
+ \c source, while the vertex shader is expected to provide
+ \c{float2 coord : TEXCOORD0} in its output in addition to
+ \c{float4 position : SV_POSITION} (names can differ since linking is done
+ based on the semantics).
+
+ \sa vertexShader, shaderType, shaderCompilationType, shaderSourceType
*/
+QByteArray QQuickShaderEffect::fragmentShader() const
+{
+#ifndef QT_NO_OPENGL
+ if (m_glImpl)
+ return m_glImpl->fragmentShader();
+#endif
+ return m_impl->fragmentShader();
+}
+
void QQuickShaderEffect::setFragmentShader(const QByteArray &code)
{
- if (m_common.source.sourceCode[Key::FragmentShader].constData() == code.constData())
+#ifndef QT_NO_OPENGL
+ if (m_glImpl) {
+ m_glImpl->setFragmentShader(code);
return;
- m_common.source.sourceCode[Key::FragmentShader] = code;
- m_dirtyProgram = true;
- m_dirtyParseLog = true;
-
- if (isComponentComplete())
- m_common.updateShader(this, Key::FragmentShader);
-
- update();
- if (m_status != Uncompiled) {
- m_status = Uncompiled;
- emit statusChanged();
}
- emit fragmentShaderChanged();
+#endif
+ m_impl->setFragmentShader(code);
}
/*!
\qmlproperty string QtQuick::ShaderEffect::vertexShader
- This property holds the vertex shader's GLSL source code.
- The default shader passes the texture coordinate along to the fragment
- shader as "varying highp vec2 qt_TexCoord0".
+ This property holds the vertex shader's source code or a reference to the
+ pre-compiled bytecode. Some APIs, like OpenGL, always support runtime
+ compilation and therefore the traditional Qt Quick way of inlining shader
+ source strings is functional. Qt Quick backends for other APIs may however
+ limit support to pre-compiled bytecode like SPIR-V or D3D shader bytecode.
+ There the string is simply a filename, which may be a file in the
+ filesystem or bundled with the executable via Qt's resource system.
+
+ With GLSL the default shader passes the texture coordinate along to the
+ fragment shader as \c{varying highp vec2 qt_TexCoord0}. With HLSL it is
+ enough to use the standard \c TEXCOORD0 semantic, for example
+ \c{float2 coord : TEXCOORD0}.
+
+ \sa fragmentShader, shaderType, shaderCompilationType, shaderSourceType
*/
+QByteArray QQuickShaderEffect::vertexShader() const
+{
+#ifndef QT_NO_OPENGL
+ if (m_glImpl)
+ return m_glImpl->vertexShader();
+#endif
+ return m_impl->vertexShader();
+}
+
void QQuickShaderEffect::setVertexShader(const QByteArray &code)
{
- if (m_common.source.sourceCode[Key::VertexShader].constData() == code.constData())
+#ifndef QT_NO_OPENGL
+ if (m_glImpl) {
+ m_glImpl->setVertexShader(code);
return;
- m_common.source.sourceCode[Key::VertexShader] = code;
- m_dirtyProgram = true;
- m_dirtyParseLog = true;
- m_customVertexShader = true;
-
- if (isComponentComplete())
- m_common.updateShader(this, Key::VertexShader);
-
- update();
- if (m_status != Uncompiled) {
- m_status = Uncompiled;
- emit statusChanged();
}
- emit vertexShaderChanged();
+#endif
+ m_impl->setVertexShader(code);
}
/*!
@@ -777,15 +470,24 @@ void QQuickShaderEffect::setVertexShader(const QByteArray &code)
property to false when blending is not needed. The default value is true.
*/
+bool QQuickShaderEffect::blending() const
+{
+#ifndef QT_NO_OPENGL
+ if (m_glImpl)
+ return m_glImpl->blending();
+#endif
+ return m_impl->blending();
+}
+
void QQuickShaderEffect::setBlending(bool enable)
{
- if (blending() == enable)
+#ifndef QT_NO_OPENGL
+ if (m_glImpl) {
+ m_glImpl->setBlending(enable);
return;
-
- m_blending = enable;
- update();
-
- emit blendingChanged();
+ }
+#endif
+ m_impl->setBlending(enable);
}
/*!
@@ -803,44 +505,22 @@ void QQuickShaderEffect::setBlending(bool enable)
QVariant QQuickShaderEffect::mesh() const
{
- return m_mesh ? qVariantFromValue(static_cast<QObject *>(m_mesh))
- : qVariantFromValue(m_meshResolution);
+#ifndef QT_NO_OPENGL
+ if (m_glImpl)
+ return m_glImpl->mesh();
+#endif
+ return m_impl->mesh();
}
void QQuickShaderEffect::setMesh(const QVariant &mesh)
{
- QQuickShaderEffectMesh *newMesh = qobject_cast<QQuickShaderEffectMesh *>(qvariant_cast<QObject *>(mesh));
- if (newMesh && newMesh == m_mesh)
+#ifndef QT_NO_OPENGL
+ if (m_glImpl) {
+ m_glImpl->setMesh(mesh);
return;
- if (m_mesh)
- disconnect(m_mesh, SIGNAL(geometryChanged()), this, 0);
- m_mesh = newMesh;
- if (m_mesh) {
- connect(m_mesh, SIGNAL(geometryChanged()), this, SLOT(updateGeometry()));
- } else {
- if (mesh.canConvert<QSize>()) {
- m_meshResolution = mesh.toSize();
- } else {
- QList<QByteArray> res = mesh.toByteArray().split('x');
- bool ok = res.size() == 2;
- if (ok) {
- int w = res.at(0).toInt(&ok);
- if (ok) {
- int h = res.at(1).toInt(&ok);
- if (ok)
- m_meshResolution = QSize(w, h);
- }
- }
- if (!ok)
- qWarning("ShaderEffect: mesh property must be size or object deriving from QQuickShaderEffectMesh.");
- }
- m_defaultMesh.setResolution(m_meshResolution);
}
-
- m_dirtyMesh = true;
- m_dirtyParseLog = true;
- update();
- emit meshChanged();
+#endif
+ m_impl->setMesh(mesh);
}
/*!
@@ -857,13 +537,24 @@ void QQuickShaderEffect::setMesh(const QVariant &mesh)
The default is NoCulling.
*/
+QQuickShaderEffect::CullMode QQuickShaderEffect::cullMode() const
+{
+#ifndef QT_NO_OPENGL
+ if (m_glImpl)
+ return m_glImpl->cullMode();
+#endif
+ return m_impl->cullMode();
+}
+
void QQuickShaderEffect::setCullMode(CullMode face)
{
- if (face == m_cullMode)
+#ifndef QT_NO_OPENGL
+ if (m_glImpl) {
+ m_glImpl->setCullMode(face);
return;
- m_cullMode = face;
- update();
- emit cullModeChanged();
+ }
+#endif
+ return m_impl->setCullMode(face);
}
/*!
@@ -887,41 +578,24 @@ void QQuickShaderEffect::setCullMode(CullMode face)
\since QtQuick 2.4
*/
-void QQuickShaderEffect::setSupportsAtlasTextures(bool supports)
-{
- if (supports == m_supportsAtlasTextures)
- return;
- m_supportsAtlasTextures = supports;
- updateGeometry();
- emit supportsAtlasTexturesChanged();
-}
-
-QString QQuickShaderEffect::parseLog()
+bool QQuickShaderEffect::supportsAtlasTextures() const
{
- if (m_dirtyParseLog) {
- m_common.updateParseLog(m_mesh != 0);
- m_dirtyParseLog = false;
- }
- return m_common.parseLog;
+#ifndef QT_NO_OPENGL
+ if (m_glImpl)
+ return m_glImpl->supportsAtlasTextures();
+#endif
+ return m_impl->supportsAtlasTextures();
}
-bool QQuickShaderEffect::event(QEvent *event)
+void QQuickShaderEffect::setSupportsAtlasTextures(bool supports)
{
- if (event->type() == QEvent::DynamicPropertyChange) {
- QDynamicPropertyChangeEvent *e = static_cast<QDynamicPropertyChangeEvent *>(event);
- for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) {
- for (int i = 0; i < m_common.uniformData[shaderType].size(); ++i) {
- if (m_common.uniformData[shaderType].at(i).name == e->propertyName()) {
- bool textureProviderChanged;
- m_common.propertyChanged(this, (shaderType << 16) | i, &textureProviderChanged);
- m_dirtyTextureProviders |= textureProviderChanged;
- m_dirtyUniformValues = true;
- update();
- }
- }
- }
+#ifndef QT_NO_OPENGL
+ if (m_glImpl) {
+ m_glImpl->setSupportsAtlasTextures(supports);
+ return;
}
- return QQuickItem::event(event);
+#endif
+ m_impl->setSupportsAtlasTextures(supports);
}
/*!
@@ -935,9 +609,17 @@ bool QQuickShaderEffect::event(QEvent *event)
\li ShaderEffect.Error - the shader program failed to compile or link.
\endlist
- When setting the fragment or vertex shader source code, the status will become Uncompiled.
- The first time the ShaderEffect is rendered with new shader source code, the shaders are
- compiled and linked, and the status is updated to Compiled or Error.
+ When setting the fragment or vertex shader source code, the status will
+ become Uncompiled. The first time the ShaderEffect is rendered with new
+ shader source code, the shaders are compiled and linked, and the status is
+ updated to Compiled or Error.
+
+ When runtime compilation is not in use and the shader properties refer to
+ files with bytecode, the status is always Compiled. The contents of the
+ shader is not examined (apart from basic reflection to discover vertex
+ input elements and constant buffer data) until later in the rendering
+ pipeline so potential errors (like layout or root signature mismatches)
+ will only be detected at a later point.
\sa log
*/
@@ -945,185 +627,215 @@ bool QQuickShaderEffect::event(QEvent *event)
/*!
\qmlproperty string QtQuick::ShaderEffect::log
- This property holds a log of warnings and errors from the latest attempt at compiling and
- linking the OpenGL shader program. It is updated at the same time \l status is set to Compiled
- or Error.
+ This property holds a log of warnings and errors from the latest attempt at
+ compiling and linking the OpenGL shader program. It is updated at the same
+ time \l status is set to Compiled or Error.
\sa status
*/
-void QQuickShaderEffect::updateGeometry()
+QString QQuickShaderEffect::log() const
{
- m_dirtyGeometry = true;
- update();
+#ifndef QT_NO_OPENGL
+ if (m_glImpl)
+ return m_glImpl->log();
+#endif
+ return m_impl->log();
}
-void QQuickShaderEffect::updateGeometryIfAtlased()
+QQuickShaderEffect::Status QQuickShaderEffect::status() const
{
- if (m_supportsAtlasTextures)
- updateGeometry();
+#ifndef QT_NO_OPENGL
+ if (m_glImpl)
+ return m_glImpl->status();
+#endif
+ return m_impl->status();
}
-void QQuickShaderEffect::updateLogAndStatus(const QString &log, int status)
-{
- m_log = parseLog() + log;
- m_status = Status(status);
- emit logChanged();
- emit statusChanged();
-}
+/*!
+ \qmlproperty enumeration QtQuick::ShaderEffect::shaderType
-void QQuickShaderEffect::sourceDestroyed(QObject *object)
-{
- m_common.sourceDestroyed(object);
-}
+ This property contains the shading language supported by the current Qt
+ Quick backend the application is using.
+ \list
+ \li ShaderEffect.UnknownShadingLanguage - Not yet known due to no window and scenegraph associated
+ \li ShaderEffect.GLSL - GLSL or GLSL ES
+ \li ShaderEffect.HLSL - HLSL
+ \endlist
-void QQuickShaderEffect::propertyChanged(int mappedId)
-{
- bool textureProviderChanged;
- m_common.propertyChanged(this, mappedId, &textureProviderChanged);
- m_dirtyTextureProviders |= textureProviderChanged;
- m_dirtyUniformValues = true;
- update();
-}
+ \note The value is only up-to-date once the item is associated with a
+ window and the window's scenegraph has initialized. Bindings relying on the
+ value have to keep this in mind since the value may change from
+ ShaderEffect.UnknownShadingLanguage to the actual value after component
+ initialization is complete. This is particularly relevant for ShaderEffect
+ items inside ShaderEffectSource items set as property values.
-void QQuickShaderEffect::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
+ \since 5.8
+ \since QtQuick 2.8
+
+ \sa shaderCompilationType, shaderSourceType, vertexShader, fragmentShader
+*/
+
+QQuickShaderEffect::ShaderType QQuickShaderEffect::shaderType() const
{
- m_dirtyGeometry = true;
- QQuickItem::geometryChanged(newGeometry, oldGeometry);
+#ifndef QT_NO_OPENGL
+ if (m_glImpl)
+ return GLSL;
+#endif
+ return m_impl->shaderType();
}
-QSGNode *QQuickShaderEffect::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
-{
- QQuickShaderEffectNode *node = static_cast<QQuickShaderEffectNode *>(oldNode);
+/*!
+ \qmlproperty enumeration QtQuick::ShaderEffect::shaderCompilationType
- // In the case of zero-size or a bad vertex shader, don't try to create a node...
- if (m_common.attributes.isEmpty() || width() <= 0 || height() <= 0) {
- if (node)
- delete node;
- return 0;
- }
+ This property contains a bitmask of the shader compilation approaches
+ supported by the current Qt Quick backend the application is using.
- if (!node) {
- node = new QQuickShaderEffectNode;
- node->setMaterial(new QQuickShaderEffectMaterial(node));
- node->setFlag(QSGNode::OwnsMaterial, true);
- m_dirtyProgram = true;
- m_dirtyUniforms = true;
- m_dirtyGeometry = true;
- connect(node, SIGNAL(logAndStatusChanged(QString,int)), this, SLOT(updateLogAndStatus(QString,int)));
- connect(node, &QQuickShaderEffectNode::dirtyTexture,
- this, &QQuickShaderEffect::updateGeometryIfAtlased);
- }
+ \list
+ \li ShaderEffect.RuntimeCompilation
+ \li ShaderEffect.OfflineCompilation
+ \endlist
- QQuickShaderEffectMaterial *material = static_cast<QQuickShaderEffectMaterial *>(node->material());
+ With OpenGL the value is ShaderEffect.RuntimeCompilation, which corresponds
+ to the traditional way of using ShaderEffect. Non-OpenGL backends are
+ expected to focus more on ShaderEffect.OfflineCompilation, however.
- // Update blending
- if (bool(material->flags() & QSGMaterial::Blending) != m_blending) {
- material->setFlag(QSGMaterial::Blending, m_blending);
- node->markDirty(QSGNode::DirtyMaterial);
- }
+ \note The value is only up-to-date once the item is associated with a
+ window and the window's scenegraph has initialized. Bindings relying on the
+ value have to keep this in mind since the value may change from \c 0 to the
+ actual bitmask after component initialization is complete. This is
+ particularly relevant for ShaderEffect items inside ShaderEffectSource
+ items set as property values.
- if (int(material->cullMode) != int(m_cullMode)) {
- material->cullMode = QQuickShaderEffectMaterial::CullMode(m_cullMode);
- node->markDirty(QSGNode::DirtyMaterial);
- }
+ \since 5.8
+ \since QtQuick 2.8
- if (m_dirtyProgram) {
- Key s = m_common.source;
- QSGShaderSourceBuilder builder;
- if (s.sourceCode[Key::FragmentShader].isEmpty()) {
- builder.appendSourceFile(QStringLiteral(":/qt-project.org/items/shaders/shadereffect.frag"));
- s.sourceCode[Key::FragmentShader] = builder.source();
- builder.clear();
- }
- if (s.sourceCode[Key::VertexShader].isEmpty()) {
- builder.appendSourceFile(QStringLiteral(":/qt-project.org/items/shaders/shadereffect.vert"));
- s.sourceCode[Key::VertexShader] = builder.source();
- }
+ \sa shaderType, shaderSourceType, vertexShader, fragmentShader
+*/
- material->setProgramSource(s);
- material->attributes = m_common.attributes;
- node->markDirty(QSGNode::DirtyMaterial);
- m_dirtyProgram = false;
- m_dirtyUniforms = true;
- }
+QQuickShaderEffect::ShaderCompilationType QQuickShaderEffect::shaderCompilationType() const
+{
+#ifndef QT_NO_OPENGL
+ if (m_glImpl)
+ return RuntimeCompilation;
+#endif
+ return m_impl->shaderCompilationType();
+}
- if (m_dirtyUniforms || m_dirtyUniformValues || m_dirtyTextureProviders) {
- m_common.updateMaterial(node, material, m_dirtyUniforms, m_dirtyUniformValues,
- m_dirtyTextureProviders);
- node->markDirty(QSGNode::DirtyMaterial);
- m_dirtyUniforms = m_dirtyUniformValues = m_dirtyTextureProviders = false;
- }
+/*!
+ \qmlproperty enumeration QtQuick::ShaderEffect::shaderSourceType
- QRectF srcRect(0, 0, 1, 1);
- bool geometryUsesTextureSubRect = false;
- if (m_supportsAtlasTextures && material->textureProviders.size() == 1) {
- QSGTextureProvider *provider = material->textureProviders.at(0);
- if (provider->texture()) {
- srcRect = provider->texture()->normalizedTextureSubRect();
- geometryUsesTextureSubRect = true;
- }
- }
+ This property contains a bitmask of the supported ways of providing shader
+ sources.
- if (bool(material->flags() & QSGMaterial::RequiresFullMatrix) != m_customVertexShader) {
- material->setFlag(QSGMaterial::RequiresFullMatrix, m_customVertexShader);
- node->markDirty(QSGNode::DirtyMaterial);
- }
+ \list
+ \li ShaderEffect.ShaderSourceString
+ \li ShaderEffect.ShaderSourceFile
+ \li ShaderEffect.ShaderByteCode
+ \endlist
- if (material->geometryUsesTextureSubRect != geometryUsesTextureSubRect) {
- material->geometryUsesTextureSubRect = geometryUsesTextureSubRect;
- node->markDirty(QSGNode::DirtyMaterial);
- }
+ With OpenGL the value is ShaderEffect.ShaderSourceString, which corresponds
+ to the traditional way of inlining GLSL source code into QML. Other,
+ non-OpenGL Qt Quick backends may however decide not to support inlined
+ shader sources, or even shader sources at all. In this case shaders are
+ expected to be pre-compiled into formats like SPIR-V or D3D shader
+ bytecode.
- if (m_dirtyMesh) {
- node->setGeometry(0);
- m_dirtyMesh = false;
- m_dirtyGeometry = true;
- }
+ \note The value is only up-to-date once the item is associated with a
+ window and the window's scenegraph has initialized. Bindings relying on the
+ value have to keep this in mind since the value may change from \c 0 to the
+ actual bitmask after component initialization is complete. This is
+ particularly relevant for ShaderEffect items inside ShaderEffectSource
+ items set as property values.
- if (m_dirtyGeometry) {
- node->setFlag(QSGNode::OwnsGeometry, false);
- QSGGeometry *geometry = node->geometry();
- QRectF rect(0, 0, width(), height());
- QQuickShaderEffectMesh *mesh = m_mesh ? m_mesh : &m_defaultMesh;
-
- geometry = mesh->updateGeometry(geometry, m_common.attributes, srcRect, rect);
- if (!geometry) {
- QString log = mesh->log();
- if (!log.isNull()) {
- m_log = parseLog();
- m_log += QLatin1String("*** Mesh ***\n");
- m_log += log;
- m_status = Error;
- emit logChanged();
- emit statusChanged();
- }
- delete node;
- return 0;
- }
+ \since 5.8
+ \since QtQuick 2.8
- node->setGeometry(geometry);
- node->setFlag(QSGNode::OwnsGeometry, true);
+ \sa shaderType, shaderCompilationType, vertexShader, fragmentShader
+*/
- m_dirtyGeometry = false;
+QQuickShaderEffect::ShaderSourceType QQuickShaderEffect::shaderSourceType() const
+{
+#ifndef QT_NO_OPENGL
+ if (m_glImpl)
+ return ShaderSourceString;
+#endif
+ return m_impl->shaderSourceType();
+}
+
+bool QQuickShaderEffect::event(QEvent *e)
+{
+#ifndef QT_NO_OPENGL
+ if (m_glImpl) {
+ m_glImpl->handleEvent(e);
+ return QQuickItem::event(e);
}
+#endif
+ m_impl->handleEvent(e);
+ return QQuickItem::event(e);
+}
- return node;
+void QQuickShaderEffect::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
+{
+#ifndef QT_NO_OPENGL
+ if (m_glImpl) {
+ m_glImpl->handleGeometryChanged(newGeometry, oldGeometry);
+ QQuickItem::geometryChanged(newGeometry, oldGeometry);
+ return;
+ }
+#endif
+ m_impl->handleGeometryChanged(newGeometry, oldGeometry);
+ QQuickItem::geometryChanged(newGeometry, oldGeometry);
+}
+
+QSGNode *QQuickShaderEffect::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *updatePaintNodeData)
+{
+#ifndef QT_NO_OPENGL
+ if (m_glImpl)
+ return m_glImpl->handleUpdatePaintNode(oldNode, updatePaintNodeData);
+#endif
+ return m_impl->handleUpdatePaintNode(oldNode, updatePaintNodeData);
}
void QQuickShaderEffect::componentComplete()
{
- m_common.updateShader(this, Key::VertexShader);
- m_common.updateShader(this, Key::FragmentShader);
+#ifndef QT_NO_OPENGL
+ if (m_glImpl) {
+ m_glImpl->handleComponentComplete();
+ QQuickItem::componentComplete();
+ return;
+ }
+#endif
+ m_impl->handleComponentComplete();
QQuickItem::componentComplete();
}
void QQuickShaderEffect::itemChange(ItemChange change, const ItemChangeData &value)
{
- if (change == QQuickItem::ItemSceneChange)
- m_common.updateWindow(value.window);
+#ifndef QT_NO_OPENGL
+ if (m_glImpl) {
+ m_glImpl->handleItemChange(change, value);
+ QQuickItem::itemChange(change, value);
+ return;
+ }
+#endif
+ m_impl->handleItemChange(change, value);
QQuickItem::itemChange(change, value);
}
+bool QQuickShaderEffect::isComponentComplete() const
+{
+ return QQuickItem::isComponentComplete();
+}
+
+QString QQuickShaderEffect::parseLog() // for OpenGL-based autotests
+{
+#ifndef QT_NO_OPENGL
+ if (m_glImpl)
+ return m_glImpl->parseLog();
+#endif
+ return QString();
+}
+
QT_END_NAMESPACE
diff --git a/src/quick/items/qquickshadereffect_p.h b/src/quick/items/qquickshadereffect_p.h
index fb266d4c44..0410514c36 100644
--- a/src/quick/items/qquickshadereffect_p.h
+++ b/src/quick/items/qquickshadereffect_p.h
@@ -52,51 +52,12 @@
//
#include <QtQuick/qquickitem.h>
-
-#include <QtQuick/qsgmaterial.h>
#include <private/qtquickglobal_p.h>
-#include <private/qsgadaptationlayer_p.h>
-#include <private/qquickshadereffectnode_p.h>
-#include "qquickshadereffectmesh_p.h"
-
-#include <QtCore/qpointer.h>
QT_BEGIN_NAMESPACE
-const char *qtPositionAttributeName();
-const char *qtTexCoordAttributeName();
-
-class QSGContext;
-class QSignalMapper;
-class QQuickCustomMaterialShader;
-
-// Common class for QQuickShaderEffect and QQuickCustomParticle.
-struct Q_QUICK_PRIVATE_EXPORT QQuickShaderEffectCommon
-{
- typedef QQuickShaderEffectMaterialKey Key;
- typedef QQuickShaderEffectMaterial::UniformData UniformData;
-
- ~QQuickShaderEffectCommon();
- void disconnectPropertySignals(QQuickItem *item, Key::ShaderType shaderType);
- void connectPropertySignals(QQuickItem *item, Key::ShaderType shaderType);
- void updateParseLog(bool ignoreAttributes);
- void lookThroughShaderCode(QQuickItem *item, Key::ShaderType shaderType, const QByteArray &code);
- void updateShader(QQuickItem *item, Key::ShaderType shaderType);
- void updateMaterial(QQuickShaderEffectNode *node, QQuickShaderEffectMaterial *material,
- bool updateUniforms, bool updateUniformValues, bool updateTextureProviders);
- void updateWindow(QQuickWindow *window);
-
- // Called by slots in QQuickShaderEffect:
- void sourceDestroyed(QObject *object);
- void propertyChanged(QQuickItem *item, int mappedId, bool *textureProviderChanged);
-
- Key source;
- QVector<QByteArray> attributes;
- QVector<UniformData> uniformData[Key::ShaderTypeCount];
- QVector<QSignalMapper *> signalMappers[Key::ShaderTypeCount];
- QString parseLog;
-};
-
+class QQuickOpenGLShaderEffect;
+class QQuickGenericShaderEffect;
class Q_QUICK_PRIVATE_EXPORT QQuickShaderEffect : public QQuickItem
{
@@ -109,51 +70,74 @@ class Q_QUICK_PRIVATE_EXPORT QQuickShaderEffect : public QQuickItem
Q_PROPERTY(QString log READ log NOTIFY logChanged)
Q_PROPERTY(Status status READ status NOTIFY statusChanged)
Q_PROPERTY(bool supportsAtlasTextures READ supportsAtlasTextures WRITE setSupportsAtlasTextures NOTIFY supportsAtlasTexturesChanged REVISION 1)
+ Q_PROPERTY(ShaderType shaderType READ shaderType NOTIFY shaderTypeChanged REVISION 2)
+ Q_PROPERTY(ShaderCompilationType shaderCompilationType READ shaderCompilationType NOTIFY shaderCompilationTypeChanged REVISION 2)
+ Q_PROPERTY(ShaderSourceType shaderSourceType READ shaderSourceType NOTIFY shaderSourceTypeChanged REVISION 2)
public:
- enum CullMode
- {
- NoCulling = QQuickShaderEffectMaterial::NoCulling,
- BackFaceCulling = QQuickShaderEffectMaterial::BackFaceCulling,
- FrontFaceCulling = QQuickShaderEffectMaterial::FrontFaceCulling
+ enum CullMode {
+ NoCulling,
+ BackFaceCulling,
+ FrontFaceCulling
};
Q_ENUM(CullMode)
- enum Status
- {
+ enum Status {
Compiled,
Uncompiled,
Error
};
Q_ENUM(Status)
+ enum ShaderType {
+ UnknownShadingLanguage,
+ GLSL,
+ HLSL
+ };
+ Q_ENUM(ShaderType)
+
+ enum ShaderCompilationType {
+ RuntimeCompilation = 0x01,
+ OfflineCompilation = 0x02
+ };
+ Q_ENUM(ShaderCompilationType)
+
+ enum ShaderSourceType {
+ ShaderSourceString = 0x01,
+ ShaderSourceFile = 0x02,
+ ShaderByteCode = 0x04
+ };
+ Q_ENUM(ShaderSourceType)
+
QQuickShaderEffect(QQuickItem *parent = 0);
- ~QQuickShaderEffect();
- QByteArray fragmentShader() const { return m_common.source.sourceCode[Key::FragmentShader]; }
+ QByteArray fragmentShader() const;
void setFragmentShader(const QByteArray &code);
- QByteArray vertexShader() const { return m_common.source.sourceCode[Key::VertexShader]; }
+ QByteArray vertexShader() const;
void setVertexShader(const QByteArray &code);
- bool blending() const { return m_blending; }
+ bool blending() const;
void setBlending(bool enable);
QVariant mesh() const;
void setMesh(const QVariant &mesh);
- CullMode cullMode() const { return m_cullMode; }
+ CullMode cullMode() const;
void setCullMode(CullMode face);
- QString log() const { return m_log; }
- Status status() const { return m_status; }
-
- bool supportsAtlasTextures() const { return m_supportsAtlasTextures; }
+ bool supportsAtlasTextures() const;
void setSupportsAtlasTextures(bool supports);
- QString parseLog();
+ QString log() const;
+ Status status() const;
- bool event(QEvent *) Q_DECL_OVERRIDE;
+ ShaderType shaderType() const;
+ ShaderCompilationType shaderCompilationType() const;
+ ShaderSourceType shaderSourceType() const;
+
+ bool isComponentComplete() const;
+ QString parseLog();
Q_SIGNALS:
void fragmentShaderChanged();
@@ -164,46 +148,22 @@ Q_SIGNALS:
void logChanged();
void statusChanged();
void supportsAtlasTexturesChanged();
+ void shaderTypeChanged();
+ void shaderCompilationTypeChanged();
+ void shaderSourceTypeChanged();
protected:
- void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) Q_DECL_OVERRIDE;
- QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *) Q_DECL_OVERRIDE;
- void componentComplete() Q_DECL_OVERRIDE;
- void itemChange(ItemChange change, const ItemChangeData &value) Q_DECL_OVERRIDE;
-
-private Q_SLOTS:
- void updateGeometry();
- void updateGeometryIfAtlased();
- void updateLogAndStatus(const QString &log, int status);
- void sourceDestroyed(QObject *object);
- void propertyChanged(int mappedId);
+ bool event(QEvent *e) override;
+ void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) override;
+ QSGNode *updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *updatePaintNodeData) override;
+ void componentComplete() override;
+ void itemChange(ItemChange change, const ItemChangeData &value) override;
private:
- friend class QQuickCustomMaterialShader;
- friend class QQuickShaderEffectNode;
-
- typedef QQuickShaderEffectMaterialKey Key;
- typedef QQuickShaderEffectMaterial::UniformData UniformData;
-
- QSize m_meshResolution;
- QQuickShaderEffectMesh *m_mesh;
- QQuickGridMesh m_defaultMesh;
- CullMode m_cullMode;
- QString m_log;
- Status m_status;
-
- QQuickShaderEffectCommon m_common;
-
- uint m_blending : 1;
- uint m_dirtyUniforms : 1;
- uint m_dirtyUniformValues : 1;
- uint m_dirtyTextureProviders : 1;
- uint m_dirtyProgram : 1;
- uint m_dirtyParseLog : 1;
- uint m_dirtyMesh : 1;
- uint m_dirtyGeometry : 1;
- uint m_customVertexShader : 1;
- uint m_supportsAtlasTextures : 1;
+#ifndef QT_NO_OPENGL
+ QQuickOpenGLShaderEffect *m_glImpl;
+#endif
+ QQuickGenericShaderEffect *m_impl;
};
QT_END_NAMESPACE
diff --git a/src/quick/items/qquickshadereffectmesh.cpp b/src/quick/items/qquickshadereffectmesh.cpp
index 842befac37..f5cc19c877 100644
--- a/src/quick/items/qquickshadereffectmesh.cpp
+++ b/src/quick/items/qquickshadereffectmesh.cpp
@@ -42,10 +42,23 @@
#include "qquickshadereffect_p.h"
#include "qquickscalegrid_p_p.h"
#include "qquickborderimage_p_p.h"
-#include <QtQuick/private/qsgdefaultimagenode_p.h>
+#include <QtQuick/private/qsgbasicimagenode_p.h>
QT_BEGIN_NAMESPACE
+static const char qt_position_attribute_name[] = "qt_Vertex";
+static const char qt_texcoord_attribute_name[] = "qt_MultiTexCoord0";
+
+const char *qtPositionAttributeName()
+{
+ return qt_position_attribute_name;
+}
+
+const char *qtTexCoordAttributeName()
+{
+ return qt_texcoord_attribute_name;
+}
+
QQuickShaderEffectMesh::QQuickShaderEffectMesh(QObject *parent)
: QObject(parent)
{
@@ -70,54 +83,64 @@ QQuickGridMesh::QQuickGridMesh(QObject *parent)
{
}
-QSGGeometry *QQuickGridMesh::updateGeometry(QSGGeometry *geometry, const QVector<QByteArray> &attributes, const QRectF &srcRect, const QRectF &dstRect)
+bool QQuickGridMesh::validateAttributes(const QVector<QByteArray> &attributes, int *posIndex)
{
- int vmesh = m_resolution.height();
- int hmesh = m_resolution.width();
- int attrCount = attributes.count();
-
+ const int attrCount = attributes.count();
int positionIndex = attributes.indexOf(qtPositionAttributeName());
int texCoordIndex = attributes.indexOf(qtTexCoordAttributeName());
- if (!geometry) {
- switch (attrCount) {
- case 0:
- m_log = QLatin1String("Error: No attributes specified.");
- return 0;
- case 1:
- if (positionIndex != 0) {
+ switch (attrCount) {
+ case 0:
+ m_log = QLatin1String("Error: No attributes specified.");
+ return false;
+ case 1:
+ if (positionIndex != 0) {
+ m_log = QLatin1String("Error: Missing \'");
+ m_log += QLatin1String(qtPositionAttributeName());
+ m_log += QLatin1String("\' attribute.\n");
+ return false;
+ }
+ break;
+ case 2:
+ if (positionIndex == -1 || texCoordIndex == -1) {
+ m_log.clear();
+ if (positionIndex == -1) {
m_log = QLatin1String("Error: Missing \'");
m_log += QLatin1String(qtPositionAttributeName());
m_log += QLatin1String("\' attribute.\n");
- return 0;
}
- break;
- case 2:
- if (positionIndex == -1 || texCoordIndex == -1) {
- m_log.clear();
- if (positionIndex == -1) {
- m_log = QLatin1String("Error: Missing \'");
- m_log += QLatin1String(qtPositionAttributeName());
- m_log += QLatin1String("\' attribute.\n");
- }
- if (texCoordIndex == -1) {
- m_log += QLatin1String("Error: Missing \'");
- m_log += QLatin1String(qtTexCoordAttributeName());
- m_log += QLatin1String("\' attribute.\n");
- }
- return 0;
+ if (texCoordIndex == -1) {
+ m_log += QLatin1String("Error: Missing \'");
+ m_log += QLatin1String(qtTexCoordAttributeName());
+ m_log += QLatin1String("\' attribute.\n");
}
- break;
- default:
- m_log = QLatin1String("Error: Too many attributes specified.");
- return 0;
+ return false;
}
+ break;
+ default:
+ m_log = QLatin1String("Error: Too many attributes specified.");
+ return false;
+ }
+
+ if (posIndex)
+ *posIndex = positionIndex;
+
+ return true;
+}
+
+QSGGeometry *QQuickGridMesh::updateGeometry(QSGGeometry *geometry, int attrCount, int posIndex,
+ const QRectF &srcRect, const QRectF &dstRect)
+{
+ int vmesh = m_resolution.height();
+ int hmesh = m_resolution.width();
+ if (!geometry) {
+ Q_ASSERT(attrCount == 1 || attrCount == 2);
geometry = new QSGGeometry(attrCount == 1
? QSGGeometry::defaultAttributes_Point2D()
: QSGGeometry::defaultAttributes_TexturedPoint2D(),
(vmesh + 1) * (hmesh + 1), vmesh * 2 * (hmesh + 2),
- GL_UNSIGNED_SHORT);
+ QSGGeometry::TypeUnsignedShort);
} else {
geometry->allocate((vmesh + 1) * (hmesh + 1), vmesh * 2 * (hmesh + 2));
@@ -132,7 +155,7 @@ QSGGeometry *QQuickGridMesh::updateGeometry(QSGGeometry *geometry, const QVector
for (int ix = 0; ix <= hmesh; ++ix) {
float fx = ix / float(hmesh);
for (int ia = 0; ia < attrCount; ++ia) {
- if (ia == positionIndex) {
+ if (ia == posIndex) {
vdata->x = float(dstRect.left()) + fx * float(dstRect.width());
vdata->y = y;
++vdata;
@@ -285,14 +308,26 @@ QQuickBorderImageMesh::QQuickBorderImageMesh(QObject *parent)
{
}
-QSGGeometry *QQuickBorderImageMesh::updateGeometry(QSGGeometry *geometry, const QVector<QByteArray> &/*attributes*/, const QRectF &srcRect, const QRectF &rect)
+bool QQuickBorderImageMesh::validateAttributes(const QVector<QByteArray> &attributes, int *posIndex)
{
+ Q_UNUSED(attributes);
+ Q_UNUSED(posIndex);
+ return true;
+}
+
+QSGGeometry *QQuickBorderImageMesh::updateGeometry(QSGGeometry *geometry, int attrCount, int posIndex,
+ const QRectF &srcRect, const QRectF &rect)
+{
+ Q_UNUSED(attrCount);
+ Q_UNUSED(posIndex);
+
QRectF innerSourceRect;
QRectF targetRect;
QRectF innerTargetRect;
QRectF subSourceRect;
- QQuickBorderImagePrivate::calculateRects(m_border, m_size, rect.size(), m_horizontalTileMode, m_verticalTileMode, 1, &targetRect, &innerTargetRect, &innerSourceRect, &subSourceRect);
+ QQuickBorderImagePrivate::calculateRects(m_border, m_size, rect.size(), m_horizontalTileMode, m_verticalTileMode,
+ 1, &targetRect, &innerTargetRect, &innerSourceRect, &subSourceRect);
QRectF sourceRect = srcRect;
QRectF modifiedInnerSourceRect(sourceRect.x() + innerSourceRect.x() * sourceRect.width(),
@@ -300,7 +335,8 @@ QSGGeometry *QQuickBorderImageMesh::updateGeometry(QSGGeometry *geometry, const
innerSourceRect.width() * sourceRect.width(),
innerSourceRect.height() * sourceRect.height());
- geometry = QSGDefaultImageNode::updateGeometry(targetRect, innerTargetRect, sourceRect, modifiedInnerSourceRect, subSourceRect, geometry);
+ geometry = QSGBasicImageNode::updateGeometry(targetRect, innerTargetRect, sourceRect,
+ modifiedInnerSourceRect, subSourceRect, geometry);
return geometry;
}
diff --git a/src/quick/items/qquickshadereffectmesh_p.h b/src/quick/items/qquickshadereffectmesh_p.h
index 34d3908140..c5f1d19866 100644
--- a/src/quick/items/qquickshadereffectmesh_p.h
+++ b/src/quick/items/qquickshadereffectmesh_p.h
@@ -43,8 +43,8 @@
#include <QtGui/qcolor.h>
#include <QtCore/qobject.h>
#include <QtCore/qsize.h>
-#include <QtCore/qvariant.h>
-#include <QtGui/qopenglfunctions.h>
+#include <QtCore/qvector.h>
+#include <QtCore/qbytearray.h>
#ifndef QQUICKSHADEREFFECTMESH_P_H
#define QQUICKSHADEREFFECTMESH_P_H
@@ -62,6 +62,9 @@
QT_BEGIN_NAMESPACE
+const char *qtPositionAttributeName();
+const char *qtTexCoordAttributeName();
+
class QSGGeometry;
class QRectF;
@@ -70,8 +73,10 @@ class QQuickShaderEffectMesh : public QObject
Q_OBJECT
public:
QQuickShaderEffectMesh(QObject *parent = 0);
- // If 'geometry' != 0, 'attributes' is the same as last time the function was called.
- virtual QSGGeometry *updateGeometry(QSGGeometry *geometry, const QVector<QByteArray> &attributes, const QRectF &srcRect, const QRectF &rect) = 0;
+ virtual bool validateAttributes(const QVector<QByteArray> &attributes, int *posIndex) = 0;
+ // If 'geometry' != 0, 'attrCount' is the same as last time the function was called.
+ virtual QSGGeometry *updateGeometry(QSGGeometry *geometry, int attrCount, int posIndex,
+ const QRectF &srcRect, const QRectF &rect) = 0;
// If updateGeometry() fails, the reason should appear in the log.
virtual QString log() const { return QString(); }
@@ -86,7 +91,9 @@ class QQuickGridMesh : public QQuickShaderEffectMesh
Q_PROPERTY(QSize resolution READ resolution WRITE setResolution NOTIFY resolutionChanged)
public:
QQuickGridMesh(QObject *parent = 0);
- QSGGeometry *updateGeometry(QSGGeometry *geometry, const QVector<QByteArray> &attributes, const QRectF &srcRect, const QRectF &rect) Q_DECL_OVERRIDE;
+ bool validateAttributes(const QVector<QByteArray> &attributes, int *posIndex) Q_DECL_OVERRIDE;
+ QSGGeometry *updateGeometry(QSGGeometry *geometry, int attrCount, int posIndex,
+ const QRectF &srcRect, const QRectF &rect) Q_DECL_OVERRIDE;
QString log() const Q_DECL_OVERRIDE { return m_log; }
void setResolution(const QSize &res);
@@ -111,7 +118,10 @@ class QQuickBorderImageMesh : public QQuickShaderEffectMesh
Q_PROPERTY(TileMode verticalTileMode READ verticalTileMode WRITE setVerticalTileMode NOTIFY verticalTileModeChanged)
public:
QQuickBorderImageMesh(QObject *parent = 0);
- QSGGeometry *updateGeometry(QSGGeometry *geometry, const QVector<QByteArray> &attributes, const QRectF &srcRect, const QRectF &rect) Q_DECL_OVERRIDE;
+
+ bool validateAttributes(const QVector<QByteArray> &attributes, int *posIndex) override;
+ QSGGeometry *updateGeometry(QSGGeometry *geometry, int attrCount, int posIndex,
+ const QRectF &srcRect, const QRectF &rect) override;
QQuickScaleGrid *border();
diff --git a/src/quick/items/qquickshadereffectsource.cpp b/src/quick/items/qquickshadereffectsource.cpp
index a8cf6155a0..338e4dc3a7 100644
--- a/src/quick/items/qquickshadereffectsource.cpp
+++ b/src/quick/items/qquickshadereffectsource.cpp
@@ -45,7 +45,6 @@
#include <QtQuick/private/qsgrenderer_p.h>
#include <qsgsimplerectnode.h>
-#include "qopenglframebufferobject.h"
#include "qmath.h"
#include <QtQuick/private/qsgtexture_p.h>
#include <QtCore/QRunnable>
@@ -680,7 +679,7 @@ QSGNode *QQuickShaderEffectSource::updatePaintNode(QSGNode *oldNode, UpdatePaint
m_texture->setDevicePixelRatio(d->window->effectiveDevicePixelRatio());
m_texture->setSize(textureSize);
m_texture->setRecursive(m_recursive);
- m_texture->setFormat(GLenum(m_format));
+ m_texture->setFormat(m_format);
m_texture->setHasMipmaps(m_mipmap);
m_texture->setMirrorHorizontal(m_textureMirroring & MirrorHorizontally);
m_texture->setMirrorVertical(m_textureMirroring & MirrorVertically);
diff --git a/src/quick/items/qquickshadereffectsource_p.h b/src/quick/items/qquickshadereffectsource_p.h
index 92b04f4ca7..5d7ed2ad2d 100644
--- a/src/quick/items/qquickshadereffectsource_p.h
+++ b/src/quick/items/qquickshadereffectsource_p.h
@@ -93,12 +93,19 @@ public:
Repeat
};
Q_ENUM(WrapMode)
-
+#ifndef QT_NO_OPENGL
enum Format {
Alpha = GL_ALPHA,
RGB = GL_RGB,
RGBA = GL_RGBA
};
+#else
+ enum Format {
+ Alpha = 0x1906,
+ RGB,
+ RGBA
+ };
+#endif
Q_ENUM(Format)
enum TextureMirroring {
diff --git a/src/quick/items/qquickspriteengine.cpp b/src/quick/items/qquickspriteengine.cpp
index 6de613c5b0..c82966cf6b 100644
--- a/src/quick/items/qquickspriteengine.cpp
+++ b/src/quick/items/qquickspriteengine.cpp
@@ -390,12 +390,15 @@ QImage QQuickSpriteEngine::assembledImage()
m_maxFrames = 0;
m_imageStateCount = 0;
int maxSize = 0;
-
+#ifndef QT_NO_OPENGL
//If there is no current OpenGL Context
if (!QOpenGLContext::currentContext())
return QImage();
QOpenGLContext::currentContext()->functions()->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxSize);
+#else
+ maxSize = 2048;
+#endif
#ifdef SPRITE_IMAGE_DEBUG
qDebug() << "MAX TEXTURE SIZE" << maxSize;
#endif
diff --git a/src/quick/items/qquicktextnodeengine.cpp b/src/quick/items/qquicktextnodeengine.cpp
index 7b600adf53..98da3ca777 100644
--- a/src/quick/items/qquicktextnodeengine.cpp
+++ b/src/quick/items/qquicktextnodeengine.cpp
@@ -737,7 +737,7 @@ void QQuickTextNodeEngine::mergeProcessedNodes(QList<BinaryTreeNode *> *regularN
BinaryTreeNode *otherNode = nodes.at(j);
glyphIndexes += otherNode->glyphRun.glyphIndexes();
primaryNode->ranges += otherNode->ranges;
- glyphBoundingRect = glyphBoundingRect.united(otherNode->glyphRun.boundingRect());
+ glyphBoundingRect = glyphBoundingRect.united(otherNode->boundingRect);
QVector<QPointF> otherPositions = otherNode->glyphRun.positions();
for (int k = 0; k < otherPositions.size(); ++k)
diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp
index 48e5b54016..cdde780932 100644
--- a/src/quick/items/qquickwindow.cpp
+++ b/src/quick/items/qquickwindow.cpp
@@ -74,8 +74,11 @@
#include <private/qqmlmemoryprofiler_p.h>
#include <private/qqmldebugserviceinterfaces_p.h>
#include <private/qqmldebugconnector_p.h>
-
-#include <private/qopenglvertexarrayobject_p.h>
+#ifndef QT_NO_OPENGL
+# include <private/qopenglvertexarrayobject_p.h>
+# include <private/qsgdefaultrendercontext_p.h>
+#endif
+#include <qsgrendererinterface.h>
QT_BEGIN_NAMESPACE
@@ -1103,8 +1106,8 @@ void QQuickWindowPrivate::cleanup(QSGNode *n)
\section1 Rendering
- QQuickWindow uses a scene graph on top of OpenGL to
- render. This scene graph is disconnected from the QML scene and
+ QQuickWindow uses a scene graph to represent what needs to be rendered.
+ This scene graph is disconnected from the QML scene and
potentially lives in another thread, depending on the platform
implementation. Since the rendering scene graph lives
independently from the QML scene, it can also be completely
@@ -1118,9 +1121,9 @@ void QQuickWindowPrivate::cleanup(QSGNode *n)
\section2 Integration with OpenGL
- It is possible to integrate OpenGL calls directly into the
- QQuickWindow using the same OpenGL context as the Qt Quick Scene
- Graph. This is done by connecting to the
+ When using the default OpenGL adaptation, it is possible to integrate
+ OpenGL calls directly into the QQuickWindow using the same OpenGL
+ context as the Qt Quick Scene Graph. This is done by connecting to the
QQuickWindow::beforeRendering() or QQuickWindow::afterRendering()
signal.
@@ -1133,10 +1136,10 @@ void QQuickWindowPrivate::cleanup(QSGNode *n)
When a QQuickWindow instance is deliberately hidden with hide() or
setVisible(false), it will stop rendering and its scene graph and
- OpenGL context might be released. The sceneGraphInvalidated()
+ graphics context might be released. The sceneGraphInvalidated()
signal will be emitted when this happens.
- \warning It is crucial that OpenGL operations and interaction with
+ \warning It is crucial that graphics operations and interaction with
the scene graph happens exclusively on the rendering thread,
primarily during the updatePaintNode() phase.
@@ -1152,8 +1155,9 @@ void QQuickWindowPrivate::cleanup(QSGNode *n)
required to aggressively release these resources. The
releaseResources() can be used to force the clean up of certain
resources. Calling releaseResources() may result in the entire
- scene graph and its OpenGL context being deleted. The
- sceneGraphInvalidated() signal will be emitted when this happens.
+ scene graph and in the case of the OpenGL adaptation the associated
+ context will be deleted. The sceneGraphInvalidated() signal will be
+ emitted when this happens.
\note All classes with QSG prefix should be used solely on the scene graph's
rendering thread. See \l {Scene Graph and Rendering} for more information.
@@ -1287,6 +1291,9 @@ void QQuickWindow::releaseResources()
The OpenGL context is still released when the last QQuickWindow is
deleted.
+ \note This only has an effect when using the default OpenGL scene
+ graph adaptation.
+
\sa setPersistentSceneGraph(),
QOpenGLContext::aboutToBeDestroyed(), sceneGraphInitialized()
*/
@@ -1304,7 +1311,8 @@ void QQuickWindow::setPersistentOpenGLContext(bool persistent)
lifetime of the QQuickWindow.
\note This is a hint. When and how this happens is implementation
- specific.
+ specific. It also only has an effect when using the default OpenGL
+ scene graph adaptation
*/
bool QQuickWindow::isPersistentOpenGLContext() const
@@ -3162,10 +3170,10 @@ void QQuickWindow::maybeUpdate()
void QQuickWindow::cleanupSceneGraph()
{
Q_D(QQuickWindow);
-
+#ifndef QT_NO_OPENGL
delete d->vaoHelper;
d->vaoHelper = 0;
-
+#endif
if (!d->renderer)
return;
@@ -3188,17 +3196,30 @@ void QQuickWindow::setTransientParent_helper(QQuickWindow *window)
}
/*!
- Returns the opengl context used for rendering.
+ Returns the OpenGL context used for rendering.
- If the scene graph is not ready, this function will return 0.
+ If the scene graph is not ready, or the scene graph is not using OpenGL,
+ this function will return null.
+
+ \note If using a scene graph adaptation other than OpenGL this
+ function will return nullptr.
\sa sceneGraphInitialized(), sceneGraphInvalidated()
*/
QOpenGLContext *QQuickWindow::openglContext() const
{
+#ifndef QT_NO_OPENGL
Q_D(const QQuickWindow);
- return d->context ? d->context->openglContext() : 0;
+ if (d->context && d->context->isValid()) {
+ QSGRendererInterface *rif = d->context->sceneGraphContext()->rendererInterface(d->context);
+ if (rif && rif->graphicsAPI() == QSGRendererInterface::OpenGL) {
+ auto openglRenderContext = static_cast<const QSGDefaultRenderContext *>(d->context);
+ return openglRenderContext->openglContext();
+ }
+ }
+#endif
+ return nullptr;
}
/*!
@@ -3234,14 +3255,14 @@ bool QQuickWindow::isSceneGraphInitialized() const
This signal is emitted when the scene graph has been invalidated.
- This signal implies that the opengl rendering context used
+ This signal implies that the graphics rendering context used
has been invalidated and all user resources tied to that context
should be released.
- The OpenGL context of this window will be bound when this function
- is called. The only exception is if the native OpenGL has been
- destroyed outside Qt's control, for instance through
- EGL_CONTEXT_LOST.
+ In the case of the default OpenGL adaptation the context of this
+ window will be bound when this function is called. The only exception
+ is if the native OpenGL has been destroyed outside Qt's control,
+ for instance through EGL_CONTEXT_LOST.
This signal will be emitted from the scene graph rendering thread.
*/
@@ -3252,7 +3273,7 @@ bool QQuickWindow::isSceneGraphInitialized() const
This signal is emitted when an \a error occurred during scene graph initialization.
Applications should connect to this signal if they wish to handle errors,
- like OpenGL context creation failures, in a custom way. When no slot is
+ like graphics context creation failures, in a custom way. When no slot is
connected to the signal, the behavior will be different: Quick will print
the \a message, or show a message box, and terminate the application.
@@ -3314,13 +3335,17 @@ bool QQuickWindow::isSceneGraphInitialized() const
The corresponding handler is \c onClosing.
*/
-
+#ifndef QT_NO_OPENGL
/*!
Sets the render target for this window to be \a fbo.
The specified fbo must be created in the context of the window
or one that shares with it.
+ \note
+ This function only has an effect when using the default OpenGL scene
+ graph adaptation.
+
\warning
This function can only be called from the thread doing
the rendering.
@@ -3343,7 +3368,7 @@ void QQuickWindow::setRenderTarget(QOpenGLFramebufferObject *fbo)
d->renderTargetSize = QSize();
}
}
-
+#endif
/*!
\overload
@@ -3353,6 +3378,10 @@ void QQuickWindow::setRenderTarget(QOpenGLFramebufferObject *fbo)
The specified FBO must be created in the context of the window
or one that shares with it.
+ \note
+ This function only has an effect when using the default OpenGL scene
+ graph adaptation.
+
\warning
This function can only be called from the thread doing
the rendering.
@@ -3394,19 +3423,23 @@ QSize QQuickWindow::renderTargetSize() const
-
+#ifndef QT_NO_OPENGL
/*!
Returns the render target for this window.
The default is to render to the surface of the window, in which
case the render target is 0.
+
+ \note
+ This function will return nullptr when not using the OpenGL scene
+ graph adaptation.
*/
QOpenGLFramebufferObject *QQuickWindow::renderTarget() const
{
Q_D(const QQuickWindow);
return d->renderTarget;
}
-
+#endif
/*!
Grabs the contents of the window and returns it as an image.
@@ -3423,33 +3456,42 @@ QOpenGLFramebufferObject *QQuickWindow::renderTarget() const
QImage QQuickWindow::grabWindow()
{
Q_D(QQuickWindow);
- if (!isVisible() && !d->context->openglContext()) {
- if (!handle() || !size().isValid()) {
- qWarning("QQuickWindow::grabWindow: window must be created and have a valid size");
- return QImage();
- }
+ if (!isVisible() && !d->renderControl) {
+ if (d->windowManager && (d->windowManager->flags() & QSGRenderLoop::SupportsGrabWithoutExpose))
+ return d->windowManager->grab(this);
+ }
- QOpenGLContext context;
- context.setFormat(requestedFormat());
- context.setShareContext(qt_gl_global_share_context());
- context.create();
- context.makeCurrent(this);
- d->context->initialize(&context);
+#ifndef QT_NO_OPENGL
+ if (!isVisible() && !d->renderControl) {
+ auto openglRenderContext = static_cast<QSGDefaultRenderContext *>(d->context);
+ if (!openglRenderContext->openglContext()) {
+ if (!handle() || !size().isValid()) {
+ qWarning("QQuickWindow::grabWindow: window must be created and have a valid size");
+ return QImage();
+ }
- d->polishItems();
- d->syncSceneGraph();
- d->renderSceneGraph(size());
+ QOpenGLContext context;
+ context.setFormat(requestedFormat());
+ context.setShareContext(qt_gl_global_share_context());
+ context.create();
+ context.makeCurrent(this);
+ d->context->initialize(&context);
- bool alpha = format().alphaBufferSize() > 0 && color().alpha() < 255;
- QImage image = qt_gl_read_framebuffer(size() * effectiveDevicePixelRatio(), alpha, alpha);
- d->cleanupNodesOnShutdown();
- d->context->invalidate();
- context.doneCurrent();
+ d->polishItems();
+ d->syncSceneGraph();
+ d->renderSceneGraph(size());
- return image;
- }
+ bool alpha = format().alphaBufferSize() > 0 && color().alpha() < 255;
+ QImage image = qt_gl_read_framebuffer(size() * effectiveDevicePixelRatio(), alpha, alpha);
+ d->cleanupNodesOnShutdown();
+ d->context->invalidate();
+ context.doneCurrent();
+ return image;
+ }
+ }
+#endif
if (d->renderControl)
return d->renderControl->grab();
else if (d->windowManager)
@@ -3491,7 +3533,7 @@ QQmlIncubationController *QQuickWindow::incubationController() const
mipmapping enabled.
\value TextureOwnsGLTexture The texture object owns the texture id and
- will delete the GL texture when the texture object is deleted.
+ will delete the OpenGL texture when the texture object is deleted.
\value TextureCanUseAtlas The image can be uploaded into a texture atlas.
@@ -3506,7 +3548,7 @@ QQmlIncubationController *QQuickWindow::incubationController() const
This enum describes the error in a sceneGraphError() signal.
- \value ContextNotAvailable OpenGL context creation failed. This typically means that
+ \value ContextNotAvailable graphics context creation failed. This typically means that
no suitable OpenGL implementation was found, for example because no graphics drivers
are installed and so no OpenGL 2 support is present. On mobile and embedded boards
that use OpenGL ES such an error is likely to indicate issues in the windowing system
@@ -3523,7 +3565,7 @@ QQmlIncubationController *QQuickWindow::incubationController() const
This signal can be used to do any preparation required before calls to
QQuickItem::updatePaintNode().
- The GL context used for rendering the scene graph will be bound at this point.
+ The OpenGL context used for rendering the scene graph will be bound at this point.
\warning This signal is emitted from the scene graph rendering thread. If your
slot function needs to finish before execution continues, you must make sure that
@@ -3544,15 +3586,16 @@ QQmlIncubationController *QQuickWindow::incubationController() const
This signal can be used to do preparation required after calls to
QQuickItem::updatePaintNode(), while the GUI thread is still locked.
- The GL context used for rendering the scene graph will be bound at this point.
+ The graphics context used for rendering the scene graph will be bound at this point.
\warning This signal is emitted from the scene graph rendering thread. If your
slot function needs to finish before execution continues, you must make sure that
the connection is direct (see Qt::ConnectionType).
- \warning Make very sure that a signal handler for afterSynchronizing leaves the GL
- context in the same state as it was when the signal handler was entered. Failing to
- do so can result in the scene not rendering properly.
+ \warning When using the OpenGL adaptation, make sure that a signal handler for
+ afterSynchronizing leaves the OpenGL context in the same state as it was when the
+ signal handler was entered. Failing to do so can result in the scene not rendering
+ properly.
\since 5.3
\sa resetOpenGLState()
@@ -3564,16 +3607,16 @@ QQmlIncubationController *QQuickWindow::incubationController() const
This signal is emitted before the scene starts rendering.
Combined with the modes for clearing the background, this option
- can be used to paint using raw GL under QML content.
+ can be used to paint using raw OpenGL under QML content.
- The GL context used for rendering the scene graph will be bound
+ The OpenGL context used for rendering the scene graph will be bound
at this point.
\warning This signal is emitted from the scene graph rendering thread. If your
slot function needs to finish before execution continues, you must make sure that
the connection is direct (see Qt::ConnectionType).
- \warning Make very sure that a signal handler for beforeRendering leaves the GL
+ \warning Make very sure that a signal handler for beforeRendering leaves the OpenGL
context in the same state as it was when the signal handler was entered. Failing to
do so can result in the scene not rendering properly.
@@ -3585,16 +3628,16 @@ QQmlIncubationController *QQuickWindow::incubationController() const
This signal is emitted after the scene has completed rendering, before swapbuffers is called.
- This signal can be used to paint using raw GL on top of QML content,
+ This signal can be used to paint using raw OpenGL on top of QML content,
or to do screen scraping of the current frame buffer.
- The GL context used for rendering the scene graph will be bound at this point.
+ The OpenGL context used for rendering the scene graph will be bound at this point.
\warning This signal is emitted from the scene graph rendering thread. If your
slot function needs to finish before execution continues, you must make sure that
the connection is direct (see Qt::ConnectionType).
- \warning Make very sure that a signal handler for afterRendering() leaves the GL
+ \warning Make very sure that a signal handler for afterRendering() leaves the OpenGL
context in the same state as it was when the signal handler was entered. Failing to
do so can result in the scene not rendering properly.
@@ -3629,6 +3672,10 @@ QQmlIncubationController *QQuickWindow::incubationController() const
until after the QQuickWindow::sceneGraphInitialize() has been
emitted.
+ \note
+ This signal will only be emmited when using the default OpenGL scene
+ graph adaptation.
+
\since 5.3
*/
@@ -3641,15 +3688,15 @@ QQmlIncubationController *QQuickWindow::incubationController() const
Applications may use this signal to release resources, but should be
prepared to reinstantiated them again fast. The scene graph and the
- OpenGL context are not released at this time.
+ graphics context are not released at this time.
\warning This signal is emitted from the scene graph rendering thread. If your
slot function needs to finish before execution continues, you must make sure that
the connection is direct (see Qt::ConnectionType).
- \warning Make very sure that a signal handler for sceneGraphAboutToStop() leaves the GL
- context in the same state as it was when the signal handler was entered. Failing to
- do so can result in the scene not rendering properly.
+ \warning Make very sure that a signal handler for sceneGraphAboutToStop() leaves the
+ graphics context in the same state as it was when the signal handler was entered.
+ Failing to do so can result in the scene not rendering properly.
\sa sceneGraphInvalidated(), resetOpenGLState()
\since 5.3
@@ -3660,7 +3707,7 @@ QQmlIncubationController *QQuickWindow::incubationController() const
Sets whether the scene graph rendering of QML should clear the color buffer
before it starts rendering to \a enabled.
- By disabling clearing of the color buffer, it is possible to do GL painting
+ By disabling clearing of the color buffer, it is possible to render OpengGL content
under the scene graph.
The color buffer is cleared by default.
@@ -3701,7 +3748,8 @@ QSGTexture *QQuickWindow::createTextureFromImage(const QImage &image) const
alpha channel, the corresponding texture will have an alpha channel.
The caller of the function is responsible for deleting the returned texture.
- The actual GL texture will be deleted when the texture object is deleted.
+ For example whe using the OpenGL adaptation the actual OpenGL texture will
+ be deleted when the texture object is deleted.
When \a options contains TextureCanUseAtlas, the engine may put the image
into a texture atlas. Textures in an atlas need to rely on
@@ -3718,9 +3766,9 @@ QSGTexture *QQuickWindow::createTextureFromImage(const QImage &image) const
texture which can use mipmap filtering. Mipmapped textures can not be in
an atlas.
- The returned texture will be using \c GL_TEXTURE_2D as texture target and
- \c GL_RGBA as internal format. Reimplement QSGTexture to create textures
- with different parameters.
+ When using the OpenGL adaptation, the returned texture will be using
+ \c GL_TEXTURE_2D as texture target and \c GL_RGBA as internal format.
+ Reimplement QSGTexture to create textures with different parameters.
\warning This function will return 0 if the scene graph has not yet been
initialized.
@@ -3739,7 +3787,7 @@ QSGTexture *QQuickWindow::createTextureFromImage(const QImage &image) const
QSGTexture *QQuickWindow::createTextureFromImage(const QImage &image, CreateTextureOptions options) const
{
Q_D(const QQuickWindow);
- if (!d->context)
+ if (!isSceneGraphInitialized()) // check both for d->context and d->context->isValid()
return 0;
uint flags = 0;
if (options & TextureCanUseAtlas) flags |= QSGRenderContext::CreateTexture_Atlas;
@@ -3751,7 +3799,7 @@ QSGTexture *QQuickWindow::createTextureFromImage(const QImage &image, CreateText
/*!
- Creates a new QSGTexture object from an existing GL texture \a id and \a size.
+ Creates a new QSGTexture object from an existing OpenGL texture \a id and \a size.
The caller of the function is responsible for deleting the returned texture.
@@ -3762,15 +3810,18 @@ QSGTexture *QQuickWindow::createTextureFromImage(const QImage &image, CreateText
Use \a options to customize the texture attributes. The TextureUsesAtlas
option is ignored.
- \warning This function will return 0 if the scenegraph has not yet been
- initialized.
+ \warning This function will return null if the scenegraph has not yet been
+ initialized or OpenGL is not in use.
+
+ \note This function only has an effect when using the default OpenGL scene graph
+ adpation.
\sa sceneGraphInitialized(), QSGTexture
*/
QSGTexture *QQuickWindow::createTextureFromId(uint id, const QSize &size, CreateTextureOptions options) const
{
- Q_D(const QQuickWindow);
- if (d->context && d->context->openglContext()) {
+#ifndef QT_NO_OPENGL
+ if (openglContext()) {
QSGPlainTexture *texture = new QSGPlainTexture();
texture->setTextureId(id);
texture->setHasAlphaChannel(options & TextureHasAlphaChannel);
@@ -3778,6 +3829,11 @@ QSGTexture *QQuickWindow::createTextureFromId(uint id, const QSize &size, Create
texture->setTextureSize(size);
return texture;
}
+#else
+ Q_UNUSED(id)
+ Q_UNUSED(size)
+ Q_UNUSED(options)
+#endif
return 0;
}
@@ -3847,7 +3903,7 @@ void QQuickWindow::setDefaultAlphaBuffer(bool useAlpha)
{
QQuickWindowPrivate::defaultAlphaBuffer = useAlpha;
}
-
+#ifndef QT_NO_OPENGL
/*!
\since 5.2
@@ -3865,6 +3921,9 @@ void QQuickWindow::setDefaultAlphaBuffer(bool useAlpha)
buffer. The depth and stencil buffer might be clobbered by the scene
graph renderer. Clear these manually on demand.
+ \note This function only has an effect when using the default OpenGL scene graph
+ adpation.
+
\sa QQuickWindow::beforeRendering()
*/
void QQuickWindow::resetOpenGLState()
@@ -3919,7 +3978,7 @@ void QQuickWindow::resetOpenGLState()
QOpenGLFramebufferObject::bindDefault();
}
-
+#endif
/*!
\qmlproperty string Window::title
@@ -4358,6 +4417,19 @@ qreal QQuickWindow::effectiveDevicePixelRatio() const
return w ? w->devicePixelRatio() : devicePixelRatio();
}
+/*!
+ Returns the current renderer interface if there is one. Otherwise null is returned.
+
+ \sa QSGRenderNode, QSGRendererInterface, isSceneGraphInitialized()
+
+ \since 5.8
+ */
+QSGRendererInterface *QQuickWindow::rendererInterface() const
+{
+ Q_D(const QQuickWindow);
+ return isSceneGraphInitialized() ? d->context->sceneGraphContext()->rendererInterface(d->context) : nullptr;
+}
+
#include "moc_qquickwindow.cpp"
QT_END_NAMESPACE
diff --git a/src/quick/items/qquickwindow.h b/src/quick/items/qquickwindow.h
index 1024147bb4..977b4956b2 100644
--- a/src/quick/items/qquickwindow.h
+++ b/src/quick/items/qquickwindow.h
@@ -60,6 +60,7 @@ class QQmlIncubationController;
class QInputMethodEvent;
class QQuickCloseEvent;
class QQuickRenderControl;
+class QSGRendererInterface;
class Q_QUICK_EXPORT QQuickWindow : public QWindow
{
@@ -110,16 +111,16 @@ public:
bool sendEvent(QQuickItem *, QEvent *);
QImage grabWindow();
-
+#ifndef QT_NO_OPENGL
void setRenderTarget(QOpenGLFramebufferObject *fbo);
QOpenGLFramebufferObject *renderTarget() const;
-
+#endif
void setRenderTarget(uint fboId, const QSize &size);
uint renderTargetId() const;
QSize renderTargetSize() const;
-
+#ifndef QT_NO_OPENGL
void resetOpenGLState();
-
+#endif
QQmlIncubationController *incubationController() const;
#ifndef QT_NO_ACCESSIBILITY
@@ -153,6 +154,8 @@ public:
qreal effectiveDevicePixelRatio() const;
+ QSGRendererInterface *rendererInterface() const;
+
Q_SIGNALS:
void frameSwapped();
Q_REVISION(2) void openglContextCreated(QOpenGLContext *context);
diff --git a/src/quick/scenegraph/adaptations/adaptations.pri b/src/quick/scenegraph/adaptations/adaptations.pri
new file mode 100644
index 0000000000..40fa739e15
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/adaptations.pri
@@ -0,0 +1 @@
+include(software/software.pri)
diff --git a/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer.cpp b/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer.cpp
new file mode 100644
index 0000000000..eb0e26462a
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer.cpp
@@ -0,0 +1,321 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgabstractsoftwarerenderer_p.h"
+
+#include "qsgsoftwarerenderablenodeupdater_p.h"
+#include "qsgsoftwarerenderlistbuilder_p.h"
+#include "qsgsoftwarecontext_p.h"
+#include "qsgsoftwarerenderablenode_p.h"
+
+#include <QtCore/QLoggingCategory>
+#include <QtGui/QWindow>
+#include <QtQuick/QSGSimpleRectNode>
+
+Q_LOGGING_CATEGORY(lc2DRender, "qt.scenegraph.softwarecontext.abstractrenderer")
+
+QT_BEGIN_NAMESPACE
+
+QSGAbstractSoftwareRenderer::QSGAbstractSoftwareRenderer(QSGRenderContext *context)
+ : QSGRenderer(context)
+ , m_background(new QSGSimpleRectNode)
+ , m_nodeUpdater(new QSGSoftwareRenderableNodeUpdater(this))
+{
+ // Setup special background node
+ auto backgroundRenderable = new QSGSoftwareRenderableNode(QSGSoftwareRenderableNode::SimpleRect, m_background);
+ addNodeMapping(m_background, backgroundRenderable);
+}
+
+QSGAbstractSoftwareRenderer::~QSGAbstractSoftwareRenderer()
+{
+ // Cleanup RenderableNodes
+ delete m_background;
+
+ for (QSGSoftwareRenderableNode *node : m_nodes.values()) {
+ delete node;
+ }
+
+ delete m_nodeUpdater;
+}
+
+QSGSoftwareRenderableNode *QSGAbstractSoftwareRenderer::renderableNode(QSGNode *node) const
+{
+ return m_nodes.value(node, nullptr);
+}
+
+void QSGAbstractSoftwareRenderer::addNodeMapping(QSGNode *node, QSGSoftwareRenderableNode *renderableNode)
+{
+ m_nodes.insert(node, renderableNode);
+}
+
+void QSGAbstractSoftwareRenderer::appendRenderableNode(QSGSoftwareRenderableNode *node)
+{
+ m_renderableNodes.append(node);
+}
+
+void QSGAbstractSoftwareRenderer::nodeChanged(QSGNode *node, QSGNode::DirtyState state)
+{
+ if (state & QSGNode::DirtyGeometry) {
+ nodeGeometryUpdated(node);
+ }
+ if (state & QSGNode::DirtyMaterial) {
+ nodeMaterialUpdated(node);
+ }
+ if (state & QSGNode::DirtyMatrix) {
+ nodeMatrixUpdated(node);
+ }
+ if (state & QSGNode::DirtyNodeAdded) {
+ nodeAdded(node);
+ }
+ if (state & QSGNode::DirtyNodeRemoved) {
+ nodeRemoved(node);
+ }
+ if (state & QSGNode::DirtyOpacity) {
+ nodeOpacityUpdated(node);
+ }
+ if (state & QSGNode::DirtySubtreeBlocked) {
+ m_nodeUpdater->updateNodes(node);
+ }
+ if (state & QSGNode::DirtyForceUpdate) {
+ m_nodeUpdater->updateNodes(node);
+ }
+ QSGRenderer::nodeChanged(node, state);
+}
+
+QRegion QSGAbstractSoftwareRenderer::renderNodes(QPainter *painter)
+{
+ QRegion dirtyRegion;
+ // If there are no nodes, do nothing
+ if (m_renderableNodes.isEmpty())
+ return dirtyRegion;
+
+ auto iterator = m_renderableNodes.begin();
+ // First node is the background and needs to painted without blending
+ auto backgroundNode = *iterator;
+ dirtyRegion += backgroundNode->renderNode(painter, /*force opaque painting*/ true);
+ iterator++;
+
+ for (; iterator != m_renderableNodes.end(); ++iterator) {
+ auto node = *iterator;
+ dirtyRegion += node->renderNode(painter);
+ }
+
+ return dirtyRegion;
+}
+
+void QSGAbstractSoftwareRenderer::buildRenderList()
+{
+ // Clear the previous renderlist
+ m_renderableNodes.clear();
+ // Add the background renderable (always first)
+ m_renderableNodes.append(renderableNode(m_background));
+ // Build the renderlist
+ QSGSoftwareRenderListBuilder(this).visitChildren(rootNode());
+}
+
+void QSGAbstractSoftwareRenderer::optimizeRenderList()
+{
+ // Iterate through the renderlist from front to back
+ // Objective is to update the dirty status and rects.
+ for (auto i = m_renderableNodes.rbegin(); i != m_renderableNodes.rend(); ++i) {
+ auto node = *i;
+ if (!m_dirtyRegion.isEmpty()) {
+ // See if the current dirty regions apply to the current node
+ node->addDirtyRegion(m_dirtyRegion, true);
+ }
+
+ if (!m_obscuredRegion.isEmpty()) {
+ // Don't try to paint things that are covered by opaque objects
+ node->subtractDirtyRegion(m_obscuredRegion);
+ }
+
+ // Keep up with obscured regions
+ if (node->isOpaque()) {
+ m_obscuredRegion += QRegion(node->boundingRect());
+ }
+
+ if (node->isDirty()) {
+ // Don't paint things outside of the rendering area
+ if (!m_background->rect().toRect().contains(node->boundingRect(), /*proper*/ true)) {
+ // Some part(s) of node is(are) outside of the rendering area
+ QRegion renderArea(m_background->rect().toRect());
+ QRegion outsideRegions = node->dirtyRegion().subtracted(renderArea);
+ if (!outsideRegions.isEmpty())
+ node->subtractDirtyRegion(outsideRegions);
+ }
+
+ // Get the dirty region's to pass to the next nodes
+ if (node->isOpaque()) {
+ // if isOpaque, subtract node's dirty rect from m_dirtyRegion
+ m_dirtyRegion -= node->dirtyRegion();
+ } else {
+ // if isAlpha, add node's dirty rect to m_dirtyRegion
+ m_dirtyRegion += node->dirtyRegion();
+ }
+ // if previousDirtyRegion has content outside of boundingRect add to m_dirtyRegion
+ QRegion prevDirty = node->previousDirtyRegion();
+ if (!prevDirty.isNull())
+ m_dirtyRegion += prevDirty;
+ }
+ }
+
+ // Empty dirtyRegion (for second pass)
+ m_dirtyRegion = QRegion();
+ m_obscuredRegion = QRegion();
+
+ // Iterate through the renderlist from back to front
+ // Objective is to make sure all non-opaque items are painted when an item under them is dirty
+ for (auto j = m_renderableNodes.begin(); j != m_renderableNodes.end(); ++j) {
+ auto node = *j;
+
+ if (!node->isOpaque() && !m_dirtyRegion.isEmpty()) {
+ // Only blended nodes need to be updated
+ node->addDirtyRegion(m_dirtyRegion, true);
+ }
+
+ m_dirtyRegion += node->dirtyRegion();
+ }
+
+ // Empty dirtyRegion
+ m_dirtyRegion = QRegion();
+ m_obscuredRegion = QRegion();
+}
+
+void QSGAbstractSoftwareRenderer::setBackgroundColor(const QColor &color)
+{
+ if (m_background->color() == color)
+ return;
+ m_background->setColor(color);
+ renderableNode(m_background)->markMaterialDirty();
+}
+
+void QSGAbstractSoftwareRenderer::setBackgroundSize(const QSize &size)
+{
+ if (m_background->rect().size().toSize() == size)
+ return;
+ m_background->setRect(0.0f, 0.0f, size.width(), size.height());
+ renderableNode(m_background)->markGeometryDirty();
+ // Invalidate the whole scene when the background is resized
+ m_dirtyRegion = QRegion(m_background->rect().toRect());
+}
+
+QColor QSGAbstractSoftwareRenderer::backgroundColor()
+{
+ return m_background->color();
+}
+
+QSize QSGAbstractSoftwareRenderer::backgroundSize()
+{
+ return m_background->rect().size().toSize();
+}
+
+void QSGAbstractSoftwareRenderer::nodeAdded(QSGNode *node)
+{
+ qCDebug(lc2DRender) << "nodeAdded" << (void*)node;
+
+ m_nodeUpdater->updateNodes(node);
+}
+
+void QSGAbstractSoftwareRenderer::nodeRemoved(QSGNode *node)
+{
+ qCDebug(lc2DRender) << "nodeRemoved" << (void*)node;
+
+ auto renderable = renderableNode(node);
+ // remove mapping
+ if (renderable != nullptr) {
+ // Need to mark this region dirty in the other nodes
+ QRegion dirtyRegion = renderable->previousDirtyRegion(true);
+ if (dirtyRegion.isEmpty())
+ dirtyRegion = renderable->boundingRect();
+ m_dirtyRegion += dirtyRegion;
+ m_nodes.remove(node);
+ delete renderable;
+ }
+
+ // Remove all children nodes as well
+ for (QSGNode *child = node->firstChild(); child; child = child->nextSibling()) {
+ nodeRemoved(child);
+ }
+
+ m_nodeUpdater->updateNodes(node, true);
+}
+
+void QSGAbstractSoftwareRenderer::nodeGeometryUpdated(QSGNode *node)
+{
+ qCDebug(lc2DRender) << "nodeGeometryUpdated";
+
+ // Mark node as dirty
+ auto renderable = renderableNode(node);
+ if (renderable != nullptr) {
+ renderable->markGeometryDirty();
+ } else {
+ m_nodeUpdater->updateNodes(node);
+ }
+}
+
+void QSGAbstractSoftwareRenderer::nodeMaterialUpdated(QSGNode *node)
+{
+ qCDebug(lc2DRender) << "nodeMaterialUpdated";
+
+ // Mark node as dirty
+ auto renderable = renderableNode(node);
+ if (renderable != nullptr) {
+ renderable->markMaterialDirty();
+ } else {
+ m_nodeUpdater->updateNodes(node);
+ }
+}
+
+void QSGAbstractSoftwareRenderer::nodeMatrixUpdated(QSGNode *node)
+{
+ qCDebug(lc2DRender) << "nodeMaterialUpdated";
+
+ // Update children nodes
+ m_nodeUpdater->updateNodes(node);
+}
+
+void QSGAbstractSoftwareRenderer::nodeOpacityUpdated(QSGNode *node)
+{
+ qCDebug(lc2DRender) << "nodeOpacityUpdated";
+
+ // Update children nodes
+ m_nodeUpdater->updateNodes(node);
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer_p.h b/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer_p.h
new file mode 100644
index 0000000000..a2e953f40d
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer_p.h
@@ -0,0 +1,109 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGABSTRACTSOFTWARERENDERER_H
+#define QSGABSTRACTSOFTWARERENDERER_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qsgrenderer_p.h>
+
+#include <QtCore/QHash>
+#include <QtCore/QLinkedList>
+
+QT_BEGIN_NAMESPACE
+
+class QSGSimpleRectNode;
+
+class QSGSoftwareRenderableNode;
+class QSGSoftwareRenderableNodeUpdater;
+
+class QSGAbstractSoftwareRenderer : public QSGRenderer
+{
+public:
+ QSGAbstractSoftwareRenderer(QSGRenderContext *context);
+ virtual ~QSGAbstractSoftwareRenderer();
+
+ QSGSoftwareRenderableNode *renderableNode(QSGNode *node) const;
+ void addNodeMapping(QSGNode *node, QSGSoftwareRenderableNode *renderableNode);
+ void appendRenderableNode(QSGSoftwareRenderableNode *node);
+
+ void nodeChanged(QSGNode *node, QSGNode::DirtyState state) override;
+
+protected:
+ QRegion renderNodes(QPainter *painter);
+ void buildRenderList();
+ void optimizeRenderList();
+
+ void setBackgroundColor(const QColor &color);
+ void setBackgroundSize(const QSize &size);
+ QColor backgroundColor();
+ QSize backgroundSize();
+
+private:
+ void nodeAdded(QSGNode *node);
+ void nodeRemoved(QSGNode *node);
+ void nodeGeometryUpdated(QSGNode *node);
+ void nodeMaterialUpdated(QSGNode *node);
+ void nodeMatrixUpdated(QSGNode *node);
+ void nodeOpacityUpdated(QSGNode *node);
+
+ QHash<QSGNode*, QSGSoftwareRenderableNode*> m_nodes;
+ QLinkedList<QSGSoftwareRenderableNode*> m_renderableNodes;
+
+ QSGSimpleRectNode *m_background;
+
+ QRegion m_dirtyRegion;
+ QRegion m_obscuredRegion;
+
+ QSGSoftwareRenderableNodeUpdater *m_nodeUpdater;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGABSTRACTSOFTWARERENDERER_H
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwareadaptation.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwareadaptation.cpp
new file mode 100644
index 0000000000..0e2f4f5382
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwareadaptation.cpp
@@ -0,0 +1,81 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgsoftwareadaptation_p.h"
+#include "qsgsoftwarecontext_p.h"
+#include "qsgsoftwarerenderloop_p.h"
+
+#include <private/qguiapplication_p.h>
+#include <qpa/qplatformintegration.h>
+
+QT_BEGIN_NAMESPACE
+
+QSGSoftwareAdaptation::QSGSoftwareAdaptation(QObject *parent)
+ : QSGContextPlugin(parent)
+{
+}
+
+QStringList QSGSoftwareAdaptation::keys() const
+{
+ return QStringList() << QLatin1String("software");
+}
+
+QSGContext *QSGSoftwareAdaptation::create(const QString &) const
+{
+ if (!instance)
+ instance = new QSGSoftwareContext();
+ return instance;
+}
+
+QSGContextFactoryInterface::Flags QSGSoftwareAdaptation::flags(const QString &) const
+{
+ // Claim we support adaptable shader effects, then return null for the
+ // shader effect node. The result is shader effects not being rendered,
+ // with the application working fine in all other respects.
+ return QSGContextFactoryInterface::SupportsShaderEffectNode;
+}
+
+QSGRenderLoop *QSGSoftwareAdaptation::createWindowManager()
+{
+ return new QSGSoftwareRenderLoop();
+}
+
+QSGSoftwareContext *QSGSoftwareAdaptation::instance = 0;
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwareadaptation_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwareadaptation_p.h
new file mode 100644
index 0000000000..ffe54b5d4b
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwareadaptation_p.h
@@ -0,0 +1,77 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef PLUGINMAIN_H
+#define PLUGINMAIN_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qsgcontextplugin_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QSGContext;
+class QSGRenderLoop;
+class QSGSoftwareContext;
+
+class QSGSoftwareAdaptation : public QSGContextPlugin
+{
+public:
+ QSGSoftwareAdaptation(QObject *parent = 0);
+
+ QStringList keys() const override;
+ QSGContext *create(const QString &key) const override;
+ QSGContextFactoryInterface::Flags flags(const QString &key) const override;
+ QSGRenderLoop *createWindowManager() override;
+private:
+ static QSGSoftwareContext *instance;
+};
+
+QT_END_NAMESPACE
+
+#endif // PLUGINMAIN_H
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext.cpp
new file mode 100644
index 0000000000..a9dbd7e325
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext.cpp
@@ -0,0 +1,182 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgsoftwarecontext_p.h"
+
+#include "qsgsoftwarerectanglenode_p.h"
+#include "qsgsoftwareimagenode_p.h"
+#include "qsgsoftwarepainternode_p.h"
+#include "qsgsoftwarepixmaptexture_p.h"
+#include "qsgsoftwareglyphnode_p.h"
+#include "qsgsoftwareninepatchnode_p.h"
+#include "qsgsoftwarelayer_p.h"
+#include "qsgsoftwarerenderer_p.h"
+
+#include <QtCore/QCoreApplication>
+#include <QtCore/QElapsedTimer>
+
+#include <QtGui/QWindow>
+
+#include <QtQuick/QSGFlatColorMaterial>
+#include <QtQuick/QSGVertexColorMaterial>
+#include <QtQuick/QSGOpaqueTextureMaterial>
+#include <QtQuick/QSGTextureMaterial>
+
+#ifndef QSG_NO_RENDERER_TIMING
+static bool qsg_render_timing = !qgetenv("QSG_RENDER_TIMING").isEmpty();
+#endif
+
+// Used for very high-level info about the renderering and gl context
+// Includes GL_VERSION, type of render loop, atlas size, etc.
+Q_LOGGING_CATEGORY(QSG_RASTER_LOG_INFO, "qt.scenegraph.info")
+
+// Used to debug the renderloop logic. Primarily useful for platform integrators
+// and when investigating the render loop logic.
+Q_LOGGING_CATEGORY(QSG_RASTER_LOG_RENDERLOOP, "qt.scenegraph.renderloop")
+
+// GLSL shader compilation
+Q_LOGGING_CATEGORY(QSG_RASTER_LOG_TIME_COMPILATION, "qt.scenegraph.time.compilation")
+
+// polish, animations, sync, render and swap in the render loop
+Q_LOGGING_CATEGORY(QSG_RASTER_LOG_TIME_RENDERLOOP, "qt.scenegraph.time.renderloop")
+
+// Texture uploads and swizzling
+Q_LOGGING_CATEGORY(QSG_RASTER_LOG_TIME_TEXTURE, "qt.scenegraph.time.texture")
+
+// Glyph preparation (only for distance fields atm)
+Q_LOGGING_CATEGORY(QSG_RASTER_LOG_TIME_GLYPH, "qt.scenegraph.time.glyph")
+
+// Timing inside the renderer base class
+Q_LOGGING_CATEGORY(QSG_RASTER_LOG_TIME_RENDERER, "qt.scenegraph.time.renderer")
+
+QT_BEGIN_NAMESPACE
+
+QSGSoftwareRenderContext::QSGSoftwareRenderContext(QSGContext *ctx)
+ : QSGRenderContext(ctx)
+ , m_initialized(false)
+{
+}
+QSGSoftwareContext::QSGSoftwareContext(QObject *parent)
+ : QSGContext(parent)
+{
+}
+
+QSGRectangleNode *QSGSoftwareContext::createRectangleNode()
+{
+ return new QSGSoftwareRectangleNode();
+}
+
+QSGImageNode *QSGSoftwareContext::createImageNode()
+{
+ return new QSGSoftwareImageNode();
+}
+
+QSGPainterNode *QSGSoftwareContext::createPainterNode(QQuickPaintedItem *item)
+{
+ return new QSGSoftwarePainterNode(item);
+}
+
+QSGGlyphNode *QSGSoftwareContext::createGlyphNode(QSGRenderContext *rc, bool preferNativeGlyphNode)
+{
+ Q_UNUSED(rc);
+ Q_UNUSED(preferNativeGlyphNode);
+ return new QSGSoftwareGlyphNode();
+}
+
+QSGNinePatchNode *QSGSoftwareContext::createNinePatchNode()
+{
+ return new QSGSoftwareNinePatchNode();
+}
+
+QSGLayer *QSGSoftwareContext::createLayer(QSGRenderContext *renderContext)
+{
+ return new QSGSoftwareLayer(renderContext);
+}
+
+QSurfaceFormat QSGSoftwareContext::defaultSurfaceFormat() const
+{
+ QSurfaceFormat format = QSurfaceFormat::defaultFormat();
+ format.setRenderableType(QSurfaceFormat::DefaultRenderableType);
+ format.setMajorVersion(0);
+ format.setMinorVersion(0);
+ return format;
+}
+
+void QSGSoftwareRenderContext::initializeIfNeeded()
+{
+ if (m_initialized)
+ return;
+ m_initialized = true;
+ emit initialized();
+}
+
+void QSGSoftwareRenderContext::invalidate()
+{
+ QSGRenderContext::invalidate();
+}
+
+QSGTexture *QSGSoftwareRenderContext::createTexture(const QImage &image, uint flags) const
+{
+ Q_UNUSED(flags)
+ return new QSGSoftwarePixmapTexture(image);
+}
+
+QSGRenderer *QSGSoftwareRenderContext::createRenderer()
+{
+ return new QSGSoftwareRenderer(this);
+}
+
+
+void QSGSoftwareRenderContext::renderNextFrame(QSGRenderer *renderer, uint fbo)
+{
+ renderer->renderScene(fbo);
+}
+
+QSGRendererInterface *QSGSoftwareContext::rendererInterface(QSGRenderContext *renderContext)
+{
+ Q_UNUSED(renderContext);
+ return this;
+}
+
+QSGRendererInterface::GraphicsAPI QSGSoftwareContext::graphicsAPI() const
+{
+ return Software;
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext_p.h
new file mode 100644
index 0000000000..12568706d4
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext_p.h
@@ -0,0 +1,103 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGSOFTWARECONTEXT_H
+#define QSGSOFTWARECONTEXT_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qsgcontext_p.h>
+#include <private/qsgadaptationlayer_p.h>
+#include "qsgrendererinterface.h"
+
+Q_DECLARE_LOGGING_CATEGORY(QSG_RASTER_LOG_TIME_RENDERLOOP)
+Q_DECLARE_LOGGING_CATEGORY(QSG_RASTER_LOG_TIME_COMPILATION)
+Q_DECLARE_LOGGING_CATEGORY(QSG_RASTER_LOG_TIME_TEXTURE)
+Q_DECLARE_LOGGING_CATEGORY(QSG_RASTER_LOG_TIME_GLYPH)
+Q_DECLARE_LOGGING_CATEGORY(QSG_RASTER_LOG_TIME_RENDERER)
+Q_DECLARE_LOGGING_CATEGORY(QSG_RASTER_LOG_INFO)
+Q_DECLARE_LOGGING_CATEGORY(QSG_RASTER_LOG_RENDERLOOP)
+
+QT_BEGIN_NAMESPACE
+
+class QSGSoftwareRenderContext : public QSGRenderContext
+{
+ Q_OBJECT
+public:
+ QSGSoftwareRenderContext(QSGContext *ctx);
+ void initializeIfNeeded();
+ void invalidate() override;
+ void renderNextFrame(QSGRenderer *renderer, uint fbo) override;
+ QSGTexture *createTexture(const QImage &image, uint flags = CreateTexture_Alpha) const override;
+ QSGRenderer *createRenderer() override;
+
+ bool m_initialized;
+};
+
+class QSGSoftwareContext : public QSGContext, public QSGRendererInterface
+{
+ Q_OBJECT
+public:
+ explicit QSGSoftwareContext(QObject *parent = nullptr);
+
+ QSGRenderContext *createRenderContext() override { return new QSGSoftwareRenderContext(this); }
+ QSGRectangleNode *createRectangleNode() override;
+ QSGImageNode *createImageNode() override;
+ QSGPainterNode *createPainterNode(QQuickPaintedItem *item) override;
+ QSGGlyphNode *createGlyphNode(QSGRenderContext *rc, bool preferNativeGlyphNode) override;
+ QSGNinePatchNode *createNinePatchNode() override;
+ QSGLayer *createLayer(QSGRenderContext *renderContext) override;
+ QSurfaceFormat defaultSurfaceFormat() const override;
+ QSGRendererInterface *rendererInterface(QSGRenderContext *renderContext) override;
+
+ GraphicsAPI graphicsAPI() const override;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGSOFTWARECONTEXT_H
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwareglyphnode.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwareglyphnode.cpp
new file mode 100644
index 0000000000..21f20c66cd
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwareglyphnode.cpp
@@ -0,0 +1,120 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgsoftwareglyphnode_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QSGSoftwareGlyphNode::QSGSoftwareGlyphNode()
+ : m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 0)
+ , m_style(QQuickText::Normal)
+{
+ setMaterial((QSGMaterial*)1);
+ setGeometry(&m_geometry);
+}
+
+
+void QSGSoftwareGlyphNode::setGlyphs(const QPointF &position, const QGlyphRun &glyphs)
+{
+ m_position = position;
+ m_glyphRun = glyphs;
+ m_bounding_rect = glyphs.boundingRect().translated(m_position - QPointF(0.0, glyphs.rawFont().ascent()));
+}
+
+void QSGSoftwareGlyphNode::setColor(const QColor &color)
+{
+ m_color = color;
+}
+
+void QSGSoftwareGlyphNode::setStyle(QQuickText::TextStyle style)
+{
+ m_style = style;
+}
+
+void QSGSoftwareGlyphNode::setStyleColor(const QColor &color)
+{
+ m_styleColor = color;
+}
+
+QPointF QSGSoftwareGlyphNode::baseLine() const
+{
+ return QPointF();
+}
+
+void QSGSoftwareGlyphNode::setPreferredAntialiasingMode(QSGGlyphNode::AntialiasingMode)
+{
+}
+
+void QSGSoftwareGlyphNode::update()
+{
+}
+
+void QSGSoftwareGlyphNode::paint(QPainter *painter)
+{
+ painter->setBrush(QBrush());
+ QPointF pos = m_position - QPointF(0, m_glyphRun.rawFont().ascent());
+
+ qreal offset = 1.0;
+ if (painter->device()->devicePixelRatio() != 0)
+ offset = 1.0 / painter->device()->devicePixelRatio();
+
+ switch (m_style) {
+ case QQuickText::Normal: break;
+ case QQuickText::Outline:
+ painter->setPen(m_styleColor);
+ painter->drawGlyphRun(pos + QPointF(0, offset), m_glyphRun);
+ painter->drawGlyphRun(pos + QPointF(0, -offset), m_glyphRun);
+ painter->drawGlyphRun(pos + QPointF(offset, 0), m_glyphRun);
+ painter->drawGlyphRun(pos + QPointF(-offset, 0), m_glyphRun);
+ break;
+ case QQuickText::Raised:
+ painter->setPen(m_styleColor);
+ painter->drawGlyphRun(pos + QPointF(0, offset), m_glyphRun);
+ break;
+ case QQuickText::Sunken:
+ painter->setPen(m_styleColor);
+ painter->drawGlyphRun(pos + QPointF(0, -offset), m_glyphRun);
+ break;
+ }
+
+ painter->setPen(m_color);
+ painter->drawGlyphRun(pos, m_glyphRun);
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwareglyphnode_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwareglyphnode_p.h
new file mode 100644
index 0000000000..559b156bf3
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwareglyphnode_p.h
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGSOFTWAREGLYPHNODE_H
+#define QSGSOFTWAREGLYPHNODE_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qsgadaptationlayer_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QSGSoftwareGlyphNode : public QSGGlyphNode
+{
+public:
+ QSGSoftwareGlyphNode();
+
+ void setGlyphs(const QPointF &position, const QGlyphRun &glyphs) override;
+ void setColor(const QColor &color) override;
+ void setStyle(QQuickText::TextStyle style) override;
+ void setStyleColor(const QColor &color) override;
+ QPointF baseLine() const override;
+ void setPreferredAntialiasingMode(AntialiasingMode) override;
+ void update() override;
+
+ void paint(QPainter *painter);
+
+private:
+ QPointF m_position;
+ QGlyphRun m_glyphRun;
+ QColor m_color;
+ QSGGeometry m_geometry;
+ QQuickText::TextStyle m_style;
+ QColor m_styleColor;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGSOFTWAREGLYPHNODE_H
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwareimagenode.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwareimagenode.cpp
new file mode 100644
index 0000000000..7dadc1d3d4
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwareimagenode.cpp
@@ -0,0 +1,503 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgsoftwareimagenode_p.h"
+
+#include "qsgsoftwarepixmaptexture_p.h"
+#include "qsgsoftwarelayer_p.h"
+#include <QPainter>
+#include <qmath.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QSGSoftwareHelpers {
+// Helper from widgets/styles/qdrawutil.cpp
+
+static inline QMargins normalizedMargins(const QMargins &m)
+{
+ return QMargins(qMax(m.left(), 0), qMax(m.top(), 0), qMax(m.right(), 0), qMax(m.bottom(), 0));
+}
+
+void qDrawBorderPixmap(QPainter *painter, const QRect &targetRect, const QMargins &targetMarginsIn,
+ const QPixmap &pixmap, const QRect &sourceRect, const QMargins &sourceMarginsIn,
+ const QTileRules &rules, QDrawBorderPixmap::DrawingHints hints)
+{
+ QPainter::PixmapFragment d;
+ d.opacity = 1.0;
+ d.rotation = 0.0;
+
+ QPixmapFragmentsArray opaqueData;
+ QPixmapFragmentsArray translucentData;
+
+ QMargins sourceMargins = normalizedMargins(sourceMarginsIn);
+ QMargins targetMargins = normalizedMargins(targetMarginsIn);
+
+ // source center
+ const int sourceCenterTop = sourceRect.top() + sourceMargins.top();
+ const int sourceCenterLeft = sourceRect.left() + sourceMargins.left();
+ const int sourceCenterBottom = sourceRect.bottom() - sourceMargins.bottom() + 1;
+ const int sourceCenterRight = sourceRect.right() - sourceMargins.right() + 1;
+ const int sourceCenterWidth = sourceCenterRight - sourceCenterLeft;
+ const int sourceCenterHeight = sourceCenterBottom - sourceCenterTop;
+ // target center
+ const int targetCenterTop = targetRect.top() + targetMargins.top();
+ const int targetCenterLeft = targetRect.left() + targetMargins.left();
+ const int targetCenterBottom = targetRect.bottom() - targetMargins.bottom() + 1;
+ const int targetCenterRight = targetRect.right() - targetMargins.right() + 1;
+ const int targetCenterWidth = targetCenterRight - targetCenterLeft;
+ const int targetCenterHeight = targetCenterBottom - targetCenterTop;
+
+ QVarLengthArray<qreal, 16> xTarget; // x-coordinates of target rectangles
+ QVarLengthArray<qreal, 16> yTarget; // y-coordinates of target rectangles
+
+ int columns = 3;
+ int rows = 3;
+ if (rules.horizontal != Qt::StretchTile && sourceCenterWidth != 0)
+ columns = qMax(3, 2 + qCeil(targetCenterWidth / qreal(sourceCenterWidth)));
+ if (rules.vertical != Qt::StretchTile && sourceCenterHeight != 0)
+ rows = qMax(3, 2 + qCeil(targetCenterHeight / qreal(sourceCenterHeight)));
+
+ xTarget.resize(columns + 1);
+ yTarget.resize(rows + 1);
+
+ bool oldAA = painter->testRenderHint(QPainter::Antialiasing);
+ if (painter->paintEngine()->type() != QPaintEngine::OpenGL
+ && painter->paintEngine()->type() != QPaintEngine::OpenGL2
+ && oldAA && painter->combinedTransform().type() != QTransform::TxNone) {
+ painter->setRenderHint(QPainter::Antialiasing, false);
+ }
+
+ xTarget[0] = targetRect.left();
+ xTarget[1] = targetCenterLeft;
+ xTarget[columns - 1] = targetCenterRight;
+ xTarget[columns] = targetRect.left() + targetRect.width();
+
+ yTarget[0] = targetRect.top();
+ yTarget[1] = targetCenterTop;
+ yTarget[rows - 1] = targetCenterBottom;
+ yTarget[rows] = targetRect.top() + targetRect.height();
+
+ qreal dx = targetCenterWidth;
+ qreal dy = targetCenterHeight;
+
+ switch (rules.horizontal) {
+ case Qt::StretchTile:
+ dx = targetCenterWidth;
+ break;
+ case Qt::RepeatTile:
+ dx = sourceCenterWidth;
+ break;
+ case Qt::RoundTile:
+ dx = targetCenterWidth / qreal(columns - 2);
+ break;
+ }
+
+ for (int i = 2; i < columns - 1; ++i)
+ xTarget[i] = xTarget[i - 1] + dx;
+
+ switch (rules.vertical) {
+ case Qt::StretchTile:
+ dy = targetCenterHeight;
+ break;
+ case Qt::RepeatTile:
+ dy = sourceCenterHeight;
+ break;
+ case Qt::RoundTile:
+ dy = targetCenterHeight / qreal(rows - 2);
+ break;
+ }
+
+ for (int i = 2; i < rows - 1; ++i)
+ yTarget[i] = yTarget[i - 1] + dy;
+
+ // corners
+ if (targetMargins.top() > 0 && targetMargins.left() > 0 && sourceMargins.top() > 0 && sourceMargins.left() > 0) { // top left
+ d.x = (0.5 * (xTarget[1] + xTarget[0]));
+ d.y = (0.5 * (yTarget[1] + yTarget[0]));
+ d.sourceLeft = sourceRect.left();
+ d.sourceTop = sourceRect.top();
+ d.width = sourceMargins.left();
+ d.height = sourceMargins.top();
+ d.scaleX = qreal(xTarget[1] - xTarget[0]) / d.width;
+ d.scaleY = qreal(yTarget[1] - yTarget[0]) / d.height;
+ if (hints & QDrawBorderPixmap::OpaqueTopLeft)
+ opaqueData.append(d);
+ else
+ translucentData.append(d);
+ }
+ if (targetMargins.top() > 0 && targetMargins.right() > 0 && sourceMargins.top() > 0 && sourceMargins.right() > 0) { // top right
+ d.x = (0.5 * (xTarget[columns] + xTarget[columns - 1]));
+ d.y = (0.5 * (yTarget[1] + yTarget[0]));
+ d.sourceLeft = sourceCenterRight;
+ d.sourceTop = sourceRect.top();
+ d.width = sourceMargins.right();
+ d.height = sourceMargins.top();
+ d.scaleX = qreal(xTarget[columns] - xTarget[columns - 1]) / d.width;
+ d.scaleY = qreal(yTarget[1] - yTarget[0]) / d.height;
+ if (hints & QDrawBorderPixmap::OpaqueTopRight)
+ opaqueData.append(d);
+ else
+ translucentData.append(d);
+ }
+ if (targetMargins.bottom() > 0 && targetMargins.left() > 0 && sourceMargins.bottom() > 0 && sourceMargins.left() > 0) { // bottom left
+ d.x = (0.5 * (xTarget[1] + xTarget[0]));
+ d.y =(0.5 * (yTarget[rows] + yTarget[rows - 1]));
+ d.sourceLeft = sourceRect.left();
+ d.sourceTop = sourceCenterBottom;
+ d.width = sourceMargins.left();
+ d.height = sourceMargins.bottom();
+ d.scaleX = qreal(xTarget[1] - xTarget[0]) / d.width;
+ d.scaleY = qreal(yTarget[rows] - yTarget[rows - 1]) / d.height;
+ if (hints & QDrawBorderPixmap::OpaqueBottomLeft)
+ opaqueData.append(d);
+ else
+ translucentData.append(d);
+ }
+ if (targetMargins.bottom() > 0 && targetMargins.right() > 0 && sourceMargins.bottom() > 0 && sourceMargins.right() > 0) { // bottom right
+ d.x = (0.5 * (xTarget[columns] + xTarget[columns - 1]));
+ d.y = (0.5 * (yTarget[rows] + yTarget[rows - 1]));
+ d.sourceLeft = sourceCenterRight;
+ d.sourceTop = sourceCenterBottom;
+ d.width = sourceMargins.right();
+ d.height = sourceMargins.bottom();
+ d.scaleX = qreal(xTarget[columns] - xTarget[columns - 1]) / d.width;
+ d.scaleY = qreal(yTarget[rows] - yTarget[rows - 1]) / d.height;
+ if (hints & QDrawBorderPixmap::OpaqueBottomRight)
+ opaqueData.append(d);
+ else
+ translucentData.append(d);
+ }
+
+ // horizontal edges
+ if (targetCenterWidth > 0 && sourceCenterWidth > 0) {
+ if (targetMargins.top() > 0 && sourceMargins.top() > 0) { // top
+ QPixmapFragmentsArray &data = hints & QDrawBorderPixmap::OpaqueTop ? opaqueData : translucentData;
+ d.sourceLeft = sourceCenterLeft;
+ d.sourceTop = sourceRect.top();
+ d.width = sourceCenterWidth;
+ d.height = sourceMargins.top();
+ d.y = (0.5 * (yTarget[1] + yTarget[0]));
+ d.scaleX = dx / d.width;
+ d.scaleY = qreal(yTarget[1] - yTarget[0]) / d.height;
+ for (int i = 1; i < columns - 1; ++i) {
+ d.x = (0.5 * (xTarget[i + 1] + xTarget[i]));
+ data.append(d);
+ }
+ if (rules.horizontal == Qt::RepeatTile)
+ data[data.size() - 1].width = ((xTarget[columns - 1] - xTarget[columns - 2]) / d.scaleX);
+ }
+ if (targetMargins.bottom() > 0 && sourceMargins.bottom() > 0) { // bottom
+ QPixmapFragmentsArray &data = hints & QDrawBorderPixmap::OpaqueBottom ? opaqueData : translucentData;
+ d.sourceLeft = sourceCenterLeft;
+ d.sourceTop = sourceCenterBottom;
+ d.width = sourceCenterWidth;
+ d.height = sourceMargins.bottom();
+ d.y = (0.5 * (yTarget[rows] + yTarget[rows - 1]));
+ d.scaleX = dx / d.width;
+ d.scaleY = qreal(yTarget[rows] - yTarget[rows - 1]) / d.height;
+ for (int i = 1; i < columns - 1; ++i) {
+ d.x = (0.5 * (xTarget[i + 1] + xTarget[i]));
+ data.append(d);
+ }
+ if (rules.horizontal == Qt::RepeatTile)
+ data[data.size() - 1].width = ((xTarget[columns - 1] - xTarget[columns - 2]) / d.scaleX);
+ }
+ }
+
+ // vertical edges
+ if (targetCenterHeight > 0 && sourceCenterHeight > 0) {
+ if (targetMargins.left() > 0 && sourceMargins.left() > 0) { // left
+ QPixmapFragmentsArray &data = hints & QDrawBorderPixmap::OpaqueLeft ? opaqueData : translucentData;
+ d.sourceLeft = sourceRect.left();
+ d.sourceTop = sourceCenterTop;
+ d.width = sourceMargins.left();
+ d.height = sourceCenterHeight;
+ d.x = (0.5 * (xTarget[1] + xTarget[0]));
+ d.scaleX = qreal(xTarget[1] - xTarget[0]) / d.width;
+ d.scaleY = dy / d.height;
+ for (int i = 1; i < rows - 1; ++i) {
+ d.y = (0.5 * (yTarget[i + 1] + yTarget[i]));
+ data.append(d);
+ }
+ if (rules.vertical == Qt::RepeatTile)
+ data[data.size() - 1].height = ((yTarget[rows - 1] - yTarget[rows - 2]) / d.scaleY);
+ }
+ if (targetMargins.right() > 0 && sourceMargins.right() > 0) { // right
+ QPixmapFragmentsArray &data = hints & QDrawBorderPixmap::OpaqueRight ? opaqueData : translucentData;
+ d.sourceLeft = sourceCenterRight;
+ d.sourceTop = sourceCenterTop;
+ d.width = sourceMargins.right();
+ d.height = sourceCenterHeight;
+ d.x = (0.5 * (xTarget[columns] + xTarget[columns - 1]));
+ d.scaleX = qreal(xTarget[columns] - xTarget[columns - 1]) / d.width;
+ d.scaleY = dy / d.height;
+ for (int i = 1; i < rows - 1; ++i) {
+ d.y = (0.5 * (yTarget[i + 1] + yTarget[i]));
+ data.append(d);
+ }
+ if (rules.vertical == Qt::RepeatTile)
+ data[data.size() - 1].height = ((yTarget[rows - 1] - yTarget[rows - 2]) / d.scaleY);
+ }
+ }
+
+ // center
+ if (targetCenterWidth > 0 && targetCenterHeight > 0 && sourceCenterWidth > 0 && sourceCenterHeight > 0) {
+ QPixmapFragmentsArray &data = hints & QDrawBorderPixmap::OpaqueCenter ? opaqueData : translucentData;
+ d.sourceLeft = sourceCenterLeft;
+ d.sourceTop = sourceCenterTop;
+ d.width = sourceCenterWidth;
+ d.height = sourceCenterHeight;
+ d.scaleX = dx / d.width;
+ d.scaleY = dy / d.height;
+
+ qreal repeatWidth = (xTarget[columns - 1] - xTarget[columns - 2]) / d.scaleX;
+ qreal repeatHeight = (yTarget[rows - 1] - yTarget[rows - 2]) / d.scaleY;
+
+ for (int j = 1; j < rows - 1; ++j) {
+ d.y = (0.5 * (yTarget[j + 1] + yTarget[j]));
+ for (int i = 1; i < columns - 1; ++i) {
+ d.x = (0.5 * (xTarget[i + 1] + xTarget[i]));
+ data.append(d);
+ }
+ if (rules.horizontal == Qt::RepeatTile)
+ data[data.size() - 1].width = repeatWidth;
+ }
+ if (rules.vertical == Qt::RepeatTile) {
+ for (int i = 1; i < columns - 1; ++i)
+ data[data.size() - i].height = repeatHeight;
+ }
+ }
+
+ if (opaqueData.size())
+ painter->drawPixmapFragments(opaqueData.data(), opaqueData.size(), pixmap, QPainter::OpaqueHint);
+ if (translucentData.size())
+ painter->drawPixmapFragments(translucentData.data(), translucentData.size(), pixmap);
+
+ if (oldAA)
+ painter->setRenderHint(QPainter::Antialiasing, true);
+}
+
+} // QSGSoftwareHelpers namespace
+
+QSGSoftwareImageNode::QSGSoftwareImageNode()
+ : m_innerSourceRect(0, 0, 1, 1)
+ , m_subSourceRect(0, 0, 1, 1)
+ , m_texture(0)
+ , m_mirror(false)
+ , m_smooth(true)
+ , m_tileHorizontal(false)
+ , m_tileVertical(false)
+ , m_cachedMirroredPixmapIsDirty(false)
+{
+ setMaterial((QSGMaterial*)1);
+ setGeometry((QSGGeometry*)1);
+}
+
+
+void QSGSoftwareImageNode::setTargetRect(const QRectF &rect)
+{
+ if (rect == m_targetRect)
+ return;
+ m_targetRect = rect;
+ markDirty(DirtyGeometry);
+}
+
+void QSGSoftwareImageNode::setInnerTargetRect(const QRectF &rect)
+{
+ if (rect == m_innerTargetRect)
+ return;
+ m_innerTargetRect = rect;
+ markDirty(DirtyGeometry);
+}
+
+void QSGSoftwareImageNode::setInnerSourceRect(const QRectF &rect)
+{
+ if (rect == m_innerSourceRect)
+ return;
+ m_innerSourceRect = rect;
+ markDirty(DirtyGeometry);
+}
+
+void QSGSoftwareImageNode::setSubSourceRect(const QRectF &rect)
+{
+ if (rect == m_subSourceRect)
+ return;
+ m_subSourceRect = rect;
+ markDirty(DirtyGeometry);
+}
+
+void QSGSoftwareImageNode::setTexture(QSGTexture *texture)
+{
+ if (m_texture != texture) {
+ m_texture = texture;
+ m_cachedMirroredPixmapIsDirty = true;
+ markDirty(DirtyMaterial);
+ }
+}
+
+void QSGSoftwareImageNode::setMirror(bool mirror)
+{
+ if (m_mirror != mirror) {
+ m_mirror = mirror;
+ m_cachedMirroredPixmapIsDirty = true;
+ markDirty(DirtyMaterial);
+ }
+}
+
+void QSGSoftwareImageNode::setMipmapFiltering(QSGTexture::Filtering /*filtering*/)
+{
+}
+
+void QSGSoftwareImageNode::setFiltering(QSGTexture::Filtering filtering)
+{
+ bool smooth = (filtering == QSGTexture::Linear);
+ if (smooth == m_smooth)
+ return;
+
+ m_smooth = smooth;
+ markDirty(DirtyMaterial);
+}
+
+void QSGSoftwareImageNode::setHorizontalWrapMode(QSGTexture::WrapMode wrapMode)
+{
+ bool tileHorizontal = (wrapMode == QSGTexture::Repeat);
+ if (tileHorizontal == m_tileHorizontal)
+ return;
+
+ m_tileHorizontal = tileHorizontal;
+ markDirty(DirtyMaterial);
+}
+
+void QSGSoftwareImageNode::setVerticalWrapMode(QSGTexture::WrapMode wrapMode)
+{
+ bool tileVertical = (wrapMode == QSGTexture::Repeat);
+ if (tileVertical == m_tileVertical)
+ return;
+
+ m_tileVertical = (wrapMode == QSGTexture::Repeat);
+ markDirty(DirtyMaterial);
+}
+
+void QSGSoftwareImageNode::update()
+{
+ if (m_cachedMirroredPixmapIsDirty) {
+ if (m_mirror) {
+ m_cachedMirroredPixmap = pixmap().transformed(QTransform(-1, 0, 0, 1, 0, 0));
+ } else {
+ //Cleanup cached pixmap if necessary
+ if (!m_cachedMirroredPixmap.isNull())
+ m_cachedMirroredPixmap = QPixmap();
+ }
+ m_cachedMirroredPixmapIsDirty = false;
+ }
+}
+
+void QSGSoftwareImageNode::preprocess()
+{
+ bool doDirty = false;
+ QSGLayer *t = qobject_cast<QSGLayer *>(m_texture);
+ if (t) {
+ doDirty = t->updateTexture();
+ markDirty(DirtyGeometry);
+ }
+ if (doDirty)
+ markDirty(DirtyMaterial);
+}
+
+static Qt::TileRule getTileRule(qreal factor)
+{
+ int ifactor = qRound(factor);
+ if (qFuzzyCompare(factor, ifactor )) {
+ if (ifactor == 1 || ifactor == 0)
+ return Qt::StretchTile;
+ return Qt::RoundTile;
+ }
+ return Qt::RepeatTile;
+}
+
+
+void QSGSoftwareImageNode::paint(QPainter *painter)
+{
+ painter->setRenderHint(QPainter::SmoothPixmapTransform, m_smooth);
+
+ const QPixmap &pm = m_mirror ? m_cachedMirroredPixmap : pixmap();
+
+ if (m_innerTargetRect != m_targetRect) {
+ // border image
+ QMargins margins(m_innerTargetRect.left() - m_targetRect.left(), m_innerTargetRect.top() - m_targetRect.top(),
+ m_targetRect.right() - m_innerTargetRect.right(), m_targetRect.bottom() - m_innerTargetRect.bottom());
+ QSGSoftwareHelpers::QTileRules tilerules(getTileRule(m_subSourceRect.width()), getTileRule(m_subSourceRect.height()));
+ QSGSoftwareHelpers::qDrawBorderPixmap(painter, m_targetRect.toRect(), margins, pm, QRect(0, 0, pm.width(), pm.height()),
+ margins, tilerules, QSGSoftwareHelpers::QDrawBorderPixmap::DrawingHints(0));
+ return;
+ }
+
+ if (m_tileHorizontal || m_tileVertical) {
+ painter->save();
+ qreal sx = m_targetRect.width()/(m_subSourceRect.width()*pm.width());
+ qreal sy = m_targetRect.height()/(m_subSourceRect.height()*pm.height());
+ QMatrix transform(sx, 0, 0, sy, 0, 0);
+ painter->setMatrix(transform, true);
+ painter->drawTiledPixmap(QRectF(m_targetRect.x()/sx, m_targetRect.y()/sy, m_targetRect.width()/sx, m_targetRect.height()/sy),
+ pm,
+ QPointF(m_subSourceRect.left()*pm.width(), m_subSourceRect.top()*pm.height()));
+ painter->restore();
+ } else {
+ QRectF sr(m_subSourceRect.left()*pm.width(), m_subSourceRect.top()*pm.height(),
+ m_subSourceRect.width()*pm.width(), m_subSourceRect.height()*pm.height());
+ painter->drawPixmap(m_targetRect, pm, sr);
+ }
+}
+
+QRectF QSGSoftwareImageNode::rect() const
+{
+ return m_targetRect;
+}
+
+const QPixmap &QSGSoftwareImageNode::pixmap() const
+{
+ if (QSGSoftwarePixmapTexture *pt = qobject_cast<QSGSoftwarePixmapTexture*>(m_texture)) {
+ return pt->pixmap();
+ } else {
+ QSGSoftwareLayer *layer = qobject_cast<QSGSoftwareLayer*>(m_texture);
+ return layer->pixmap();
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwareimagenode_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwareimagenode_p.h
new file mode 100644
index 0000000000..e42f616757
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwareimagenode_p.h
@@ -0,0 +1,147 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGSOFTWAREIMAGENODE_H
+#define QSGSOFTWAREIMAGENODE_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qsgadaptationlayer_p.h>
+#include <private/qsgtexturematerial_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QSGSoftwareHelpers {
+
+typedef QVarLengthArray<QPainter::PixmapFragment, 16> QPixmapFragmentsArray;
+
+struct QTileRules
+{
+ inline QTileRules(Qt::TileRule horizontalRule, Qt::TileRule verticalRule)
+ : horizontal(horizontalRule), vertical(verticalRule) {}
+ inline QTileRules(Qt::TileRule rule = Qt::StretchTile)
+ : horizontal(rule), vertical(rule) {}
+ Qt::TileRule horizontal;
+ Qt::TileRule vertical;
+};
+
+#ifndef Q_QDOC
+// For internal use only.
+namespace QDrawBorderPixmap
+{
+ enum DrawingHint
+ {
+ OpaqueTopLeft = 0x0001,
+ OpaqueTop = 0x0002,
+ OpaqueTopRight = 0x0004,
+ OpaqueLeft = 0x0008,
+ OpaqueCenter = 0x0010,
+ OpaqueRight = 0x0020,
+ OpaqueBottomLeft = 0x0040,
+ OpaqueBottom = 0x0080,
+ OpaqueBottomRight = 0x0100,
+ OpaqueCorners = OpaqueTopLeft | OpaqueTopRight | OpaqueBottomLeft | OpaqueBottomRight,
+ OpaqueEdges = OpaqueTop | OpaqueLeft | OpaqueRight | OpaqueBottom,
+ OpaqueFrame = OpaqueCorners | OpaqueEdges,
+ OpaqueAll = OpaqueCenter | OpaqueFrame
+ };
+
+ Q_DECLARE_FLAGS(DrawingHints, DrawingHint)
+}
+#endif
+
+void qDrawBorderPixmap(QPainter *painter, const QRect &targetRect, const QMargins &targetMargins,
+ const QPixmap &pixmap, const QRect &sourceRect,const QMargins &sourceMargins,
+ const QTileRules &rules, QDrawBorderPixmap::DrawingHints hints);
+
+} // QSGSoftwareHelpers namespace
+
+class QSGSoftwareImageNode : public QSGImageNode
+{
+public:
+ QSGSoftwareImageNode();
+
+ void setTargetRect(const QRectF &rect) override;
+ void setInnerTargetRect(const QRectF &rect) override;
+ void setInnerSourceRect(const QRectF &rect) override;
+ void setSubSourceRect(const QRectF &rect) override;
+ void setTexture(QSGTexture *texture) override;
+ void setMirror(bool mirror) override;
+ void setMipmapFiltering(QSGTexture::Filtering filtering) override;
+ void setFiltering(QSGTexture::Filtering filtering) override;
+ void setHorizontalWrapMode(QSGTexture::WrapMode wrapMode) override;
+ void setVerticalWrapMode(QSGTexture::WrapMode wrapMode) override;
+ void update() override;
+
+ void preprocess() override;
+
+ void paint(QPainter *painter);
+
+ QRectF rect() const;
+
+private:
+ const QPixmap &pixmap() const;
+
+ QRectF m_targetRect;
+ QRectF m_innerTargetRect;
+ QRectF m_innerSourceRect;
+ QRectF m_subSourceRect;
+
+ QSGTexture *m_texture;
+ QPixmap m_cachedMirroredPixmap;
+
+ bool m_mirror;
+ bool m_smooth;
+ bool m_tileHorizontal;
+ bool m_tileVertical;
+ bool m_cachedMirroredPixmapIsDirty;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGSOFTWAREIMAGENODE_H
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarelayer.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarelayer.cpp
new file mode 100644
index 0000000000..7020283898
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarelayer.cpp
@@ -0,0 +1,261 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgsoftwarelayer_p.h"
+
+#include "qsgsoftwarecontext_p.h"
+#include "qsgsoftwarepixmaprenderer_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QSGSoftwareLayer::QSGSoftwareLayer(QSGRenderContext *renderContext)
+ : m_item(0)
+ , m_context(renderContext)
+ , m_renderer(0)
+ , m_device_pixel_ratio(1)
+ , m_mirrorHorizontal(false)
+ , m_mirrorVertical(false)
+ , m_live(true)
+ , m_grab(true)
+ , m_recursive(false)
+ , m_dirtyTexture(true)
+{
+
+}
+
+QSGSoftwareLayer::~QSGSoftwareLayer()
+{
+ invalidated();
+}
+
+int QSGSoftwareLayer::textureId() const
+{
+ return 0;
+}
+
+QSize QSGSoftwareLayer::textureSize() const
+{
+ return m_pixmap.size();
+}
+
+bool QSGSoftwareLayer::hasAlphaChannel() const
+{
+ return m_pixmap.hasAlphaChannel();
+}
+
+bool QSGSoftwareLayer::hasMipmaps() const
+{
+ return false;
+}
+
+void QSGSoftwareLayer::bind()
+{
+}
+
+bool QSGSoftwareLayer::updateTexture()
+{
+ bool doGrab = (m_live || m_grab) && m_dirtyTexture;
+ if (doGrab)
+ grab();
+ if (m_grab)
+ emit scheduledUpdateCompleted();
+ m_grab = false;
+ return doGrab;
+}
+
+void QSGSoftwareLayer::setItem(QSGNode *item)
+{
+ if (item == m_item)
+ return;
+ m_item = item;
+
+ if (m_live && !m_item)
+ m_pixmap = QPixmap();
+
+ markDirtyTexture();
+}
+
+void QSGSoftwareLayer::setRect(const QRectF &rect)
+{
+ if (rect == m_rect)
+ return;
+ m_rect = rect;
+ markDirtyTexture();
+}
+
+void QSGSoftwareLayer::setSize(const QSize &size)
+{
+ if (size == m_size)
+ return;
+ m_size = size;
+
+ if (m_live && m_size.isNull())
+ m_pixmap = QPixmap();
+
+ markDirtyTexture();
+}
+
+void QSGSoftwareLayer::scheduleUpdate()
+{
+ if (m_grab)
+ return;
+ m_grab = true;
+ if (m_dirtyTexture) {
+ emit updateRequested();
+ }
+}
+
+QImage QSGSoftwareLayer::toImage() const
+{
+ return m_pixmap.toImage();
+}
+
+void QSGSoftwareLayer::setLive(bool live)
+{
+ if (live == m_live)
+ return;
+ m_live = live;
+
+ if (m_live && (!m_item || m_size.isNull()))
+ m_pixmap = QPixmap();
+
+ markDirtyTexture();
+}
+
+void QSGSoftwareLayer::setRecursive(bool recursive)
+{
+ m_recursive = recursive;
+}
+
+void QSGSoftwareLayer::setFormat(uint)
+{
+}
+
+void QSGSoftwareLayer::setHasMipmaps(bool)
+{
+}
+
+void QSGSoftwareLayer::setDevicePixelRatio(qreal ratio)
+{
+ m_device_pixel_ratio = ratio;
+}
+
+void QSGSoftwareLayer::setMirrorHorizontal(bool mirror)
+{
+ if (m_mirrorHorizontal == mirror)
+ return;
+ m_mirrorHorizontal = mirror;
+ markDirtyTexture();
+}
+
+void QSGSoftwareLayer::setMirrorVertical(bool mirror)
+{
+ if (m_mirrorVertical == mirror)
+ return;
+ m_mirrorVertical = mirror;
+ markDirtyTexture();
+}
+
+void QSGSoftwareLayer::markDirtyTexture()
+{
+ m_dirtyTexture = true;
+ if (m_live || m_grab) {
+ emit updateRequested();
+ }
+}
+
+void QSGSoftwareLayer::invalidated()
+{
+ delete m_renderer;
+ m_renderer = 0;
+}
+
+void QSGSoftwareLayer::grab()
+{
+ if (!m_item || m_size.isNull()) {
+ m_pixmap = QPixmap();
+ m_dirtyTexture = false;
+ return;
+ }
+ QSGNode *root = m_item;
+ while (root->firstChild() && root->type() != QSGNode::RootNodeType)
+ root = root->firstChild();
+ if (root->type() != QSGNode::RootNodeType)
+ return;
+
+ if (!m_renderer) {
+ m_renderer = new QSGSoftwarePixmapRenderer(m_context);
+ connect(m_renderer, SIGNAL(sceneGraphChanged()), this, SLOT(markDirtyTexture()));
+ }
+ m_renderer->setDevicePixelRatio(m_device_pixel_ratio);
+ m_renderer->setRootNode(static_cast<QSGRootNode *>(root));
+
+ if (m_pixmap.size() != m_size) {
+ m_pixmap = QPixmap(m_size);
+ m_pixmap.setDevicePixelRatio(m_device_pixel_ratio);
+ // This fill here is wasteful, but necessary because it is the only way
+ // to force a QImage based pixmap to have an alpha channel.
+ m_pixmap.fill(Qt::transparent);
+ }
+
+ // Render texture.
+ root->markDirty(QSGNode::DirtyForceUpdate); // Force matrix, clip and opacity update.
+ m_renderer->nodeChanged(root, QSGNode::DirtyForceUpdate); // Force render list update.
+
+ m_dirtyTexture = false;
+
+ m_renderer->setDeviceRect(m_size);
+ m_renderer->setViewportRect(m_size);
+ QRect mirrored(m_mirrorHorizontal ? m_rect.right() * m_device_pixel_ratio : m_rect.left() * m_device_pixel_ratio,
+ m_mirrorVertical ? m_rect.top() * m_device_pixel_ratio : m_rect.bottom() * m_device_pixel_ratio,
+ m_mirrorHorizontal ? -m_rect.width() * m_device_pixel_ratio : m_rect.width() * m_device_pixel_ratio,
+ m_mirrorVertical ? m_rect.height() * m_device_pixel_ratio : -m_rect.height() * m_device_pixel_ratio);
+ m_renderer->setProjectionRect(mirrored);
+ m_renderer->setClearColor(Qt::transparent);
+
+ m_renderer->renderScene();
+ m_renderer->render(&m_pixmap);
+
+ root->markDirty(QSGNode::DirtyForceUpdate); // Force matrix, clip, opacity and render list update.
+
+ if (m_recursive)
+ markDirtyTexture(); // Continuously update if 'live' and 'recursive'.
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarelayer_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarelayer_p.h
new file mode 100644
index 0000000000..d3f13e40b1
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarelayer_p.h
@@ -0,0 +1,121 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGSOFTWARELAYER_H
+#define QSGSOFTWARELAYER_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qsgadaptationlayer_p.h>
+#include <private/qsgcontext_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QSGSoftwarePixmapRenderer;
+
+class QSGSoftwareLayer : public QSGLayer
+{
+ Q_OBJECT
+public:
+ QSGSoftwareLayer(QSGRenderContext *renderContext);
+ ~QSGSoftwareLayer();
+
+ const QPixmap &pixmap() const { return m_pixmap; }
+
+ // QSGTexture interface
+public:
+ int textureId() const override;
+ QSize textureSize() const override;
+ bool hasAlphaChannel() const override;
+ bool hasMipmaps() const override;
+ void bind() override;
+
+ // QSGDynamicTexture interface
+public:
+ bool updateTexture() override;
+
+ // QSGLayer interface
+public:
+ void setItem(QSGNode *item) override;
+ void setRect(const QRectF &rect) override;
+ void setSize(const QSize &size) override;
+ void scheduleUpdate() override;
+ QImage toImage() const override;
+ void setLive(bool live) override;
+ void setRecursive(bool recursive) override;
+ void setFormat(uint) override;
+ void setHasMipmaps(bool) override;
+ void setDevicePixelRatio(qreal ratio) override;
+ void setMirrorHorizontal(bool mirror) override;
+ void setMirrorVertical(bool mirror) override;
+
+public slots:
+ void markDirtyTexture() override;
+ void invalidated() override;
+
+private:
+ void grab();
+
+ QSGNode *m_item;
+ QSGRenderContext *m_context;
+ QSGSoftwarePixmapRenderer *m_renderer;
+ QRectF m_rect;
+ QSize m_size;
+ QPixmap m_pixmap;
+ qreal m_device_pixel_ratio;
+ bool m_mirrorHorizontal;
+ bool m_mirrorVertical;
+ bool m_live;
+ bool m_grab;
+ bool m_recursive;
+ bool m_dirtyTexture;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGSOFTWARELAYER_H
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwareninepatchnode.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwareninepatchnode.cpp
new file mode 100644
index 0000000000..36ff1f2229
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwareninepatchnode.cpp
@@ -0,0 +1,109 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgsoftwareninepatchnode_p.h"
+#include "qsgsoftwarepixmaptexture_p.h"
+#include "qsgsoftwareimagenode_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QSGSoftwareNinePatchNode::QSGSoftwareNinePatchNode()
+{
+ setMaterial((QSGMaterial*)1);
+ setGeometry((QSGGeometry*)1);
+}
+
+void QSGSoftwareNinePatchNode::setTexture(QSGTexture *texture)
+{
+ QSGSoftwarePixmapTexture *pt = qobject_cast<QSGSoftwarePixmapTexture*>(texture);
+ if (!pt) {
+ qWarning() << "Image used with invalid texture format.";
+ return;
+ }
+ m_pixmap = pt->pixmap();
+ markDirty(DirtyMaterial);
+}
+
+void QSGSoftwareNinePatchNode::setBounds(const QRectF &bounds)
+{
+ if (m_bounds == bounds)
+ return;
+
+ m_bounds = bounds;
+ markDirty(DirtyGeometry);
+}
+
+void QSGSoftwareNinePatchNode::setDevicePixelRatio(qreal ratio)
+{
+ if (m_pixelRatio == ratio)
+ return;
+
+ m_pixelRatio = ratio;
+ markDirty(DirtyGeometry);
+}
+
+void QSGSoftwareNinePatchNode::setPadding(qreal left, qreal top, qreal right, qreal bottom)
+{
+ QMargins margins(qRound(left), qRound(top), qRound(right), qRound(bottom));
+ if (m_margins == margins)
+ return;
+
+ m_margins = QMargins(qRound(left), qRound(top), qRound(right), qRound(bottom));
+ markDirty(DirtyGeometry);
+}
+
+void QSGSoftwareNinePatchNode::update()
+{
+}
+
+void QSGSoftwareNinePatchNode::paint(QPainter *painter)
+{
+ if (m_margins.isNull())
+ painter->drawPixmap(m_bounds, m_pixmap, QRectF(0, 0, m_pixmap.width(), m_pixmap.height()));
+ else
+ QSGSoftwareHelpers::qDrawBorderPixmap(painter, m_bounds.toRect(), m_margins, m_pixmap, QRect(0, 0, m_pixmap.width(), m_pixmap.height()),
+ m_margins, Qt::StretchTile, QSGSoftwareHelpers::QDrawBorderPixmap::DrawingHints(0));
+}
+
+QRectF QSGSoftwareNinePatchNode::bounds() const
+{
+ return m_bounds;
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwareninepatchnode_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwareninepatchnode_p.h
new file mode 100644
index 0000000000..62559c933c
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwareninepatchnode_p.h
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGSOFTWARENINEPATCHNODE_H
+#define QSGSOFTWARENINEPATCHNODE_H
+
+#include <private/qsgadaptationlayer_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.
+//
+
+QT_BEGIN_NAMESPACE
+
+class QSGSoftwareNinePatchNode : public QSGNinePatchNode
+{
+public:
+ QSGSoftwareNinePatchNode();
+
+ void setTexture(QSGTexture *texture) override;
+ void setBounds(const QRectF &bounds) override;
+ void setDevicePixelRatio(qreal ratio) override;
+ void setPadding(qreal left, qreal top, qreal right, qreal bottom) override;
+ void update() override;
+
+ void paint(QPainter *painter);
+
+ QRectF bounds() const;
+
+private:
+ QPixmap m_pixmap;
+ QRectF m_bounds;
+ qreal m_pixelRatio;
+ QMargins m_margins;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGSOFTWARENINEPATCHNODE_H
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarepainternode.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarepainternode.cpp
new file mode 100644
index 0000000000..34b0cd5b72
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarepainternode.cpp
@@ -0,0 +1,231 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgsoftwarepainternode_p.h"
+#include "qsgsoftwarepixmaptexture_p.h"
+#include <qmath.h>
+
+QT_BEGIN_NAMESPACE
+
+QSGSoftwarePainterNode::QSGSoftwarePainterNode(QQuickPaintedItem *item)
+ : QSGPainterNode()
+ , m_preferredRenderTarget(QQuickPaintedItem::Image)
+ , m_item(item)
+ , m_texture(0)
+ , m_dirtyContents(false)
+ , m_opaquePainting(false)
+ , m_linear_filtering(false)
+ , m_mipmapping(false)
+ , m_smoothPainting(false)
+ , m_fastFBOResizing(false)
+ , m_fillColor(Qt::transparent)
+ , m_contentsScale(1.0)
+ , m_dirtyGeometry(false)
+{
+ setMaterial((QSGMaterial*)1);
+ setGeometry((QSGGeometry*)1);
+}
+
+QSGSoftwarePainterNode::~QSGSoftwarePainterNode()
+{
+ delete m_texture;
+}
+
+void QSGSoftwarePainterNode::setPreferredRenderTarget(QQuickPaintedItem::RenderTarget target)
+{
+ if (m_preferredRenderTarget == target)
+ return;
+
+ m_preferredRenderTarget = target;
+}
+
+void QSGSoftwarePainterNode::setSize(const QSize &size)
+{
+ if (size == m_size)
+ return;
+
+ m_size = size;
+
+ m_dirtyGeometry = true;
+}
+
+void QSGSoftwarePainterNode::setDirty(const QRect &dirtyRect)
+{
+ m_dirtyContents = true;
+ m_dirtyRect = dirtyRect;
+ markDirty(DirtyMaterial);
+}
+
+void QSGSoftwarePainterNode::setOpaquePainting(bool opaque)
+{
+ if (opaque == m_opaquePainting)
+ return;
+
+ m_opaquePainting = opaque;
+}
+
+void QSGSoftwarePainterNode::setLinearFiltering(bool linearFiltering)
+{
+ if (linearFiltering == m_linear_filtering)
+ return;
+
+ m_linear_filtering = linearFiltering;
+}
+
+void QSGSoftwarePainterNode::setMipmapping(bool mipmapping)
+{
+ if (mipmapping == m_mipmapping)
+ return;
+
+ m_mipmapping = mipmapping;
+}
+
+void QSGSoftwarePainterNode::setSmoothPainting(bool s)
+{
+ if (s == m_smoothPainting)
+ return;
+
+ m_smoothPainting = s;
+}
+
+void QSGSoftwarePainterNode::setFillColor(const QColor &c)
+{
+ if (c == m_fillColor)
+ return;
+
+ m_fillColor = c;
+ markDirty(DirtyMaterial);
+}
+
+void QSGSoftwarePainterNode::setContentsScale(qreal s)
+{
+ if (s == m_contentsScale)
+ return;
+
+ m_contentsScale = s;
+ markDirty(DirtyMaterial);
+}
+
+void QSGSoftwarePainterNode::setFastFBOResizing(bool dynamic)
+{
+ m_fastFBOResizing = dynamic;
+}
+
+QImage QSGSoftwarePainterNode::toImage() const
+{
+ return m_pixmap.toImage();
+}
+
+void QSGSoftwarePainterNode::update()
+{
+ if (m_dirtyGeometry) {
+ m_pixmap = QPixmap(m_textureSize);
+ if (!m_opaquePainting)
+ m_pixmap.fill(Qt::transparent);
+
+ if (m_texture)
+ delete m_texture;
+ m_texture = new QSGSoftwarePixmapTexture(m_pixmap);
+ }
+
+ if (m_dirtyContents)
+ paint();
+
+ m_dirtyGeometry = false;
+ m_dirtyContents = false;
+}
+
+void QSGSoftwarePainterNode::paint(QPainter *painter)
+{
+ painter->drawPixmap(0, 0, m_size.width(), m_size.height(), m_pixmap);
+}
+
+void QSGSoftwarePainterNode::paint()
+{
+ QRect dirtyRect = m_dirtyRect.isNull() ? QRect(0, 0, m_size.width(), m_size.height()) : m_dirtyRect;
+
+ QPainter painter;
+
+ painter.begin(&m_pixmap);
+ if (m_smoothPainting) {
+ painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform);
+ }
+
+ QRect clipRect;
+
+ if (m_contentsScale == 1) {
+ qreal scaleX = m_textureSize.width() / (qreal) m_size.width();
+ qreal scaleY = m_textureSize.height() / (qreal) m_size.height();
+ painter.scale(scaleX, scaleY);
+ clipRect = dirtyRect;
+ } else {
+ painter.scale(m_contentsScale, m_contentsScale);
+
+ QRect sclip(qFloor(dirtyRect.x()/m_contentsScale),
+ qFloor(dirtyRect.y()/m_contentsScale),
+ qCeil(dirtyRect.width()/m_contentsScale+dirtyRect.x()/m_contentsScale-qFloor(dirtyRect.x()/m_contentsScale)),
+ qCeil(dirtyRect.height()/m_contentsScale+dirtyRect.y()/m_contentsScale-qFloor(dirtyRect.y()/m_contentsScale)));
+
+ clipRect = sclip;
+ }
+
+ if (!m_dirtyRect.isNull())
+ painter.setClipRect(clipRect);
+
+ painter.setCompositionMode(QPainter::CompositionMode_Source);
+ painter.fillRect(clipRect, m_fillColor);
+ painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
+
+ m_item->paint(&painter);
+ painter.end();
+
+ m_dirtyRect = QRect();
+}
+
+
+void QSGSoftwarePainterNode::setTextureSize(const QSize &size)
+{
+ if (size == m_textureSize)
+ return;
+
+ m_textureSize = size;
+ m_dirtyGeometry = true;
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarepainternode_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarepainternode_p.h
new file mode 100644
index 0000000000..67b45354a2
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarepainternode_p.h
@@ -0,0 +1,132 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGSOFTWAREPAINTERNODE_H
+#define QSGSOFTWAREPAINTERNODE_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qsgadaptationlayer_p.h>
+#include <QtQuick/qquickpainteditem.h>
+
+#include <QtGui/QPixmap>
+
+QT_BEGIN_NAMESPACE
+
+class QSGSoftwarePainterNode : public QSGPainterNode
+{
+public:
+ QSGSoftwarePainterNode(QQuickPaintedItem *item);
+ ~QSGSoftwarePainterNode();
+
+ void setPreferredRenderTarget(QQuickPaintedItem::RenderTarget target) override;
+
+ void setSize(const QSize &size) override;
+ QSize size() const { return m_size; }
+
+ void setDirty(const QRect &dirtyRect = QRect()) override;
+
+ void setOpaquePainting(bool opaque) override;
+ bool opaquePainting() const { return m_opaquePainting; }
+
+ void setLinearFiltering(bool linearFiltering) override;
+ bool linearFiltering() const { return m_linear_filtering; }
+
+ void setMipmapping(bool mipmapping) override;
+ bool mipmapping() const { return m_mipmapping; }
+
+ void setSmoothPainting(bool s) override;
+ bool smoothPainting() const { return m_smoothPainting; }
+
+ void setFillColor(const QColor &c) override;
+ QColor fillColor() const { return m_fillColor; }
+
+ void setContentsScale(qreal s) override;
+ qreal contentsScale() const { return m_contentsScale; }
+
+ void setFastFBOResizing(bool dynamic) override;
+ bool fastFBOResizing() const { return m_fastFBOResizing; }
+
+ QImage toImage() const override;
+ void update() override;
+ QSGTexture *texture() const override { return m_texture; }
+
+ void paint(QPainter *painter);
+
+ void paint();
+
+ void setTextureSize(const QSize &size) override;
+ QSize textureSize() const { return m_textureSize; }
+
+private:
+
+ QQuickPaintedItem::RenderTarget m_preferredRenderTarget;
+
+ QQuickPaintedItem *m_item;
+
+ QPixmap m_pixmap;
+ QSGTexture *m_texture;
+
+ QSize m_size;
+ bool m_dirtyContents;
+ QRect m_dirtyRect;
+ bool m_opaquePainting;
+ bool m_linear_filtering;
+ bool m_mipmapping;
+ bool m_smoothPainting;
+ bool m_fastFBOResizing;
+ QColor m_fillColor;
+ qreal m_contentsScale;
+ QSize m_textureSize;
+
+ bool m_dirtyGeometry;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGSOFTWAREPAINTERNODE_H
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaprenderer.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaprenderer.cpp
new file mode 100644
index 0000000000..304106a84d
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaprenderer.cpp
@@ -0,0 +1,111 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgsoftwarepixmaprenderer_p.h"
+
+#include <QtQuick/QSGSimpleRectNode>
+
+#include <QElapsedTimer>
+
+Q_LOGGING_CATEGORY(lcPixmapRenderer, "qt.scenegraph.softwarecontext.pixmapRenderer")
+
+QT_BEGIN_NAMESPACE
+
+QSGSoftwarePixmapRenderer::QSGSoftwarePixmapRenderer(QSGRenderContext *context)
+ : QSGAbstractSoftwareRenderer(context)
+{
+
+}
+
+QSGSoftwarePixmapRenderer::~QSGSoftwarePixmapRenderer()
+{
+
+}
+
+void QSGSoftwarePixmapRenderer::renderScene(uint)
+{
+ class B : public QSGBindable
+ {
+ public:
+ void bind() const { }
+ } bindable;
+ QSGRenderer::renderScene(bindable);
+}
+
+void QSGSoftwarePixmapRenderer::render()
+{
+
+}
+
+void QSGSoftwarePixmapRenderer::render(QPaintDevice *target)
+{
+ QElapsedTimer renderTimer;
+
+ // Setup background item
+ setBackgroundSize(QSize(target->width(), target->height()));
+ setBackgroundColor(clearColor());
+
+ QPainter painter(target);
+ painter.setRenderHint(QPainter::Antialiasing);
+ painter.setWindow(m_projectionRect);
+
+ renderTimer.start();
+ buildRenderList();
+ qint64 buildRenderListTime = renderTimer.restart();
+
+ // Optimize Renderlist
+ // Right now there is an assumption that when possible the same pixmap will
+ // be reused. So we can treat it like a backing store in that we can assume
+ // that when the pixmap is not being resized, the data can be reused. What is
+ // different though is that everything should be marked as dirty on a resize.
+ optimizeRenderList();
+ qint64 optimizeRenderListTime = renderTimer.restart();
+
+ QRegion paintedRegion = renderNodes(&painter);
+ qint64 renderTime = renderTimer.elapsed();
+
+ qCDebug(lcPixmapRenderer) << "pixmapRender" << paintedRegion << buildRenderListTime << optimizeRenderListTime << renderTime;
+}
+
+void QSGSoftwarePixmapRenderer::setProjectionRect(const QRect &projectionRect)
+{
+ m_projectionRect = projectionRect;
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaprenderer_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaprenderer_p.h
new file mode 100644
index 0000000000..3b4fb304e7
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaprenderer_p.h
@@ -0,0 +1,76 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGSOFTWAREPIXMAPRENDERER_H
+#define QSGSOFTWAREPIXMAPRENDERER_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qsgabstractsoftwarerenderer_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QSGSoftwarePixmapRenderer : public QSGAbstractSoftwareRenderer
+{
+public:
+ QSGSoftwarePixmapRenderer(QSGRenderContext *context);
+ virtual ~QSGSoftwarePixmapRenderer();
+
+ void renderScene(uint fboId = 0) final;
+ void render() final;
+
+ void render(QPaintDevice *target);
+ void setProjectionRect(const QRect &projectionRect);
+
+private:
+ QRect m_projectionRect;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGSOFTWAREPIXMAPRENDERER_H
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaptexture.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaptexture.cpp
new file mode 100644
index 0000000000..e04c400af3
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaptexture.cpp
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgsoftwarepixmaptexture_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QSGSoftwarePixmapTexture::QSGSoftwarePixmapTexture(const QImage &image)
+ // Prevent pixmap format conversion to reduce memory consumption
+ // and surprises in calling code. (See QTBUG-47328)
+ : m_pixmap(QPixmap::fromImage(image, Qt::NoFormatConversion))
+{
+}
+
+QSGSoftwarePixmapTexture::QSGSoftwarePixmapTexture(const QPixmap &pixmap)
+ : m_pixmap(pixmap)
+{
+}
+
+
+int QSGSoftwarePixmapTexture::textureId() const
+{
+ return 0;
+}
+
+QSize QSGSoftwarePixmapTexture::textureSize() const
+{
+ return m_pixmap.size();
+}
+
+bool QSGSoftwarePixmapTexture::hasAlphaChannel() const
+{
+ return m_pixmap.hasAlphaChannel();
+}
+
+bool QSGSoftwarePixmapTexture::hasMipmaps() const
+{
+ return false;
+}
+
+void QSGSoftwarePixmapTexture::bind()
+{
+ Q_UNREACHABLE();
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaptexture_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaptexture_p.h
new file mode 100644
index 0000000000..01bfe19471
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaptexture_p.h
@@ -0,0 +1,79 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGSOFTWAREPIXMAPTEXTURE_H
+#define QSGSOFTWAREPIXMAPTEXTURE_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qsgtexture_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QSGSoftwarePixmapTexture : public QSGTexture
+{
+ Q_OBJECT
+public:
+ QSGSoftwarePixmapTexture(const QImage &image);
+ QSGSoftwarePixmapTexture(const QPixmap &pixmap);
+
+ int textureId() const override;
+ QSize textureSize() const override;
+ bool hasAlphaChannel() const override;
+ bool hasMipmaps() const override;
+ void bind() override;
+
+ const QPixmap &pixmap() const { return m_pixmap; }
+
+private:
+ QPixmap m_pixmap;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGSOFTWAREPIXMAPTEXTURE_H
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerectanglenode.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarerectanglenode.cpp
new file mode 100644
index 0000000000..7672b14371
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerectanglenode.cpp
@@ -0,0 +1,451 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgsoftwarerectanglenode_p.h"
+#include <qmath.h>
+
+#include <QtGui/QPainter>
+
+QT_BEGIN_NAMESPACE
+
+QSGSoftwareRectangleNode::QSGSoftwareRectangleNode()
+ : m_penWidth(0)
+ , m_radius(0)
+ , m_cornerPixmapIsDirty(true)
+ , m_devicePixelRatio(1)
+{
+ m_pen.setJoinStyle(Qt::MiterJoin);
+ m_pen.setMiterLimit(0);
+ setMaterial((QSGMaterial*)1);
+ setGeometry((QSGGeometry*)1);
+}
+
+void QSGSoftwareRectangleNode::setRect(const QRectF &rect)
+{
+ QRect alignedRect = rect.toAlignedRect();
+ if (m_rect != alignedRect) {
+ m_rect = alignedRect;
+ markDirty(DirtyMaterial);
+ }
+}
+
+void QSGSoftwareRectangleNode::setColor(const QColor &color)
+{
+ if (m_color != color) {
+ m_color = color;
+ m_cornerPixmapIsDirty = true;
+ markDirty(DirtyMaterial);
+ }
+}
+
+void QSGSoftwareRectangleNode::setPenColor(const QColor &color)
+{
+ if (m_penColor != color) {
+ m_penColor = color;
+ m_cornerPixmapIsDirty = true;
+ markDirty(DirtyMaterial);
+ }
+}
+
+void QSGSoftwareRectangleNode::setPenWidth(qreal width)
+{
+ if (m_penWidth != width) {
+ m_penWidth = width;
+ m_cornerPixmapIsDirty = true;
+ markDirty(DirtyMaterial);
+ }
+}
+
+//Move first stop by pos relative to seconds
+static QGradientStop interpolateStop(const QGradientStop &firstStop, const QGradientStop &secondStop, double newPos)
+{
+ double distance = secondStop.first - firstStop.first;
+ double distanceDelta = newPos - firstStop.first;
+ double modifierValue = distanceDelta / distance;
+ int redDelta = (secondStop.second.red() - firstStop.second.red()) * modifierValue;
+ int greenDelta = (secondStop.second.green() - firstStop.second.green()) * modifierValue;
+ int blueDelta = (secondStop.second.blue() - firstStop.second.blue()) * modifierValue;
+ int alphaDelta = (secondStop.second.alpha() - firstStop.second.alpha()) * modifierValue;
+
+ QGradientStop newStop;
+ newStop.first = newPos;
+ newStop.second = QColor(firstStop.second.red() + redDelta,
+ firstStop.second.green() + greenDelta,
+ firstStop.second.blue() + blueDelta,
+ firstStop.second.alpha() + alphaDelta);
+
+ return newStop;
+}
+
+void QSGSoftwareRectangleNode::setGradientStops(const QGradientStops &stops)
+{
+ //normalize stops
+ bool needsNormalization = false;
+ foreach (const QGradientStop &stop, stops) {
+ if (stop.first < 0.0 || stop.first > 1.0) {
+ needsNormalization = true;
+ continue;
+ }
+ }
+
+ if (needsNormalization) {
+ QGradientStops normalizedStops;
+ if (stops.count() == 1) {
+ //If there is only one stop, then the position does not matter
+ //It is just treated as a color
+ QGradientStop stop = stops.at(0);
+ stop.first = 0.0;
+ normalizedStops.append(stop);
+ } else {
+ //Clip stops to only the first below 0.0 and above 1.0
+ int below = -1;
+ int above = -1;
+ QVector<int> between;
+ for (int i = 0; i < stops.count(); ++i) {
+ if (stops.at(i).first < 0.0) {
+ below = i;
+ } else if (stops.at(i).first > 1.0) {
+ above = i;
+ break;
+ } else {
+ between.append(i);
+ }
+ }
+
+ //Interpoloate new color values for above and below
+ if (below != -1 ) {
+ //If there are more than one stops left, interpolate
+ if (below + 1 < stops.count()) {
+ normalizedStops.append(interpolateStop(stops.at(below), stops.at(below + 1), 0.0));
+ } else {
+ QGradientStop singleStop;
+ singleStop.first = 0.0;
+ singleStop.second = stops.at(below).second;
+ normalizedStops.append(singleStop);
+ }
+ }
+
+ for (int i = 0; i < between.count(); ++i)
+ normalizedStops.append(stops.at(between.at(i)));
+
+ if (above != -1) {
+ //If there stops before above, interpolate
+ if (above >= 1) {
+ normalizedStops.append(interpolateStop(stops.at(above), stops.at(above - 1), 1.0));
+ } else {
+ QGradientStop singleStop;
+ singleStop.first = 1.0;
+ singleStop.second = stops.at(above).second;
+ normalizedStops.append(singleStop);
+ }
+ }
+ }
+
+ m_stops = normalizedStops;
+
+ } else {
+ m_stops = stops;
+ }
+ m_cornerPixmapIsDirty = true;
+ markDirty(DirtyMaterial);
+}
+
+void QSGSoftwareRectangleNode::setRadius(qreal radius)
+{
+ if (m_radius != radius) {
+ m_radius = radius;
+ m_cornerPixmapIsDirty = true;
+ markDirty(DirtyMaterial);
+ }
+}
+
+void QSGSoftwareRectangleNode::setAligned(bool /*aligned*/)
+{
+}
+
+void QSGSoftwareRectangleNode::update()
+{
+ if (!m_penWidth || m_penColor == Qt::transparent) {
+ m_pen = Qt::NoPen;
+ } else {
+ m_pen = QPen(m_penColor);
+ m_pen.setWidthF(m_penWidth);
+ }
+
+ if (!m_stops.isEmpty()) {
+ QLinearGradient gradient(QPoint(0,0), QPoint(0,1));
+ gradient.setStops(m_stops);
+ gradient.setCoordinateMode(QGradient::ObjectBoundingMode);
+ m_brush = QBrush(gradient);
+ } else {
+ m_brush = QBrush(m_color);
+ }
+
+ if (m_cornerPixmapIsDirty) {
+ generateCornerPixmap();
+ m_cornerPixmapIsDirty = false;
+ }
+}
+
+void QSGSoftwareRectangleNode::paint(QPainter *painter)
+{
+ //We can only check for a device pixel ratio change when we know what
+ //paint device is being used.
+ if (painter->device()->devicePixelRatio() != m_devicePixelRatio) {
+ m_devicePixelRatio = painter->device()->devicePixelRatio();
+ generateCornerPixmap();
+ }
+
+ if (painter->transform().isRotating()) {
+ //Rotated rectangles lose the benefits of direct rendering, and have poor rendering
+ //quality when using only blits and fills.
+
+ if (m_radius == 0 && m_penWidth == 0) {
+ //Non-Rounded Rects without borders (fall back to drawRect)
+ //Most common case
+ painter->setPen(Qt::NoPen);
+ painter->setBrush(m_brush);
+ painter->drawRect(m_rect);
+ } else {
+ //Rounded Rects and Rects with Borders
+ //Avoids broken behaviors of QPainter::drawRect/roundedRect
+ QPixmap pixmap = QPixmap(m_rect.width() * m_devicePixelRatio, m_rect.height() * m_devicePixelRatio);
+ pixmap.fill(Qt::transparent);
+ pixmap.setDevicePixelRatio(m_devicePixelRatio);
+ QPainter pixmapPainter(&pixmap);
+ paintRectangle(&pixmapPainter, QRect(0, 0, m_rect.width(), m_rect.height()));
+
+ QPainter::RenderHints previousRenderHints = painter->renderHints();
+ painter->setRenderHint(QPainter::SmoothPixmapTransform, true);
+ painter->drawPixmap(m_rect, pixmap);
+ painter->setRenderHints(previousRenderHints);
+ }
+
+
+ } else {
+ //Paint directly
+ paintRectangle(painter, m_rect);
+ }
+
+}
+
+bool QSGSoftwareRectangleNode::isOpaque() const
+{
+ if (m_radius > 0.0f)
+ return false;
+ if (m_color.alpha() < 255)
+ return false;
+ if (m_penWidth > 0.0f && m_penColor.alpha() < 255)
+ return false;
+ if (m_stops.count() > 0) {
+ foreach (QGradientStop stop, m_stops) {
+ if (stop.second.alpha() < 255)
+ return false;
+ }
+ }
+
+ return true;
+}
+
+QRectF QSGSoftwareRectangleNode::rect() const
+{
+ //TODO: double check that this is correct.
+ return m_rect;
+}
+
+void QSGSoftwareRectangleNode::paintRectangle(QPainter *painter, const QRect &rect)
+{
+ //Radius should never exceeds half of the width or half of the height
+ int radius = qFloor(qMin(qMin(rect.width(), rect.height()) * 0.5, m_radius));
+
+ QPainter::RenderHints previousRenderHints = painter->renderHints();
+ painter->setRenderHint(QPainter::Antialiasing, false);
+
+ if (m_penWidth > 0) {
+ //Fill border Rects
+
+ //Borders can not be more than half the height/width of a rect
+ double borderWidth = qMin(m_penWidth, rect.width() * 0.5);
+ double borderHeight = qMin(m_penWidth, rect.height() * 0.5);
+
+
+
+ if (borderWidth > radius) {
+ //4 Rects
+ QRectF borderTopOutside(QPointF(rect.x() + radius, rect.y()),
+ QPointF(rect.x() + rect.width() - radius, rect.y() + radius));
+ QRectF borderTopInside(QPointF(rect.x() + borderWidth, rect.y() + radius),
+ QPointF(rect.x() + rect.width() - borderWidth, rect.y() + borderHeight));
+ QRectF borderBottomOutside(QPointF(rect.x() + radius, rect.y() + rect.height() - radius),
+ QPointF(rect.x() + rect.width() - radius, rect.y() + rect.height()));
+ QRectF borderBottomInside(QPointF(rect.x() + borderWidth, rect.y() + rect.height() - borderHeight),
+ QPointF(rect.x() + rect.width() - borderWidth, rect.y() + rect.height() - radius));
+
+ if (borderTopOutside.isValid())
+ painter->fillRect(borderTopOutside, m_penColor);
+ if (borderTopInside.isValid())
+ painter->fillRect(borderTopInside, m_penColor);
+ if (borderBottomOutside.isValid())
+ painter->fillRect(borderBottomOutside, m_penColor);
+ if (borderBottomInside.isValid())
+ painter->fillRect(borderBottomInside, m_penColor);
+
+ } else {
+ //2 Rects
+ QRectF borderTop(QPointF(rect.x() + radius, rect.y()),
+ QPointF(rect.x() + rect.width() - radius, rect.y() + borderHeight));
+ QRectF borderBottom(QPointF(rect.x() + radius, rect.y() + rect.height() - borderHeight),
+ QPointF(rect.x() + rect.width() - radius, rect.y() + rect.height()));
+ if (borderTop.isValid())
+ painter->fillRect(borderTop, m_penColor);
+ if (borderBottom.isValid())
+ painter->fillRect(borderBottom, m_penColor);
+ }
+ QRectF borderLeft(QPointF(rect.x(), rect.y() + radius),
+ QPointF(rect.x() + borderWidth, rect.y() + rect.height() - radius));
+ QRectF borderRight(QPointF(rect.x() + rect.width() - borderWidth, rect.y() + radius),
+ QPointF(rect.x() + rect.width(), rect.y() + rect.height() - radius));
+ if (borderLeft.isValid())
+ painter->fillRect(borderLeft, m_penColor);
+ if (borderRight.isValid())
+ painter->fillRect(borderRight, m_penColor);
+ }
+
+
+ if (radius > 0) {
+
+ if (radius * 2 >= rect.width() && radius * 2 >= rect.height()) {
+ //Blit whole pixmap for circles
+ painter->drawPixmap(rect, m_cornerPixmap, m_cornerPixmap.rect());
+ } else {
+
+ //blit 4 corners to border
+ int scaledRadius = radius * m_devicePixelRatio;
+ QRectF topLeftCorner(QPointF(rect.x(), rect.y()),
+ QPointF(rect.x() + radius, rect.y() + radius));
+ painter->drawPixmap(topLeftCorner, m_cornerPixmap, QRectF(0, 0, scaledRadius, scaledRadius));
+ QRectF topRightCorner(QPointF(rect.x() + rect.width() - radius, rect.y()),
+ QPointF(rect.x() + rect.width(), rect.y() + radius));
+ painter->drawPixmap(topRightCorner, m_cornerPixmap, QRectF(scaledRadius, 0, scaledRadius, scaledRadius));
+ QRectF bottomLeftCorner(QPointF(rect.x(), rect.y() + rect.height() - radius),
+ QPointF(rect.x() + radius, rect.y() + rect.height()));
+ painter->drawPixmap(bottomLeftCorner, m_cornerPixmap, QRectF(0, scaledRadius, scaledRadius, scaledRadius));
+ QRectF bottomRightCorner(QPointF(rect.x() + rect.width() - radius, rect.y() + rect.height() - radius),
+ QPointF(rect.x() + rect.width(), rect.y() + rect.height()));
+ painter->drawPixmap(bottomRightCorner, m_cornerPixmap, QRectF(scaledRadius, scaledRadius, scaledRadius, scaledRadius));
+
+ }
+
+ }
+
+ QRectF brushRect = QRectF(rect).marginsRemoved(QMarginsF(m_penWidth, m_penWidth, m_penWidth, m_penWidth));
+ if (brushRect.width() < 0)
+ brushRect.setWidth(0);
+ if (brushRect.height() < 0)
+ brushRect.setHeight(0);
+ double innerRectRadius = qMax(0.0, radius - m_penWidth);
+
+ //If not completely transparent or has a gradient
+ if (m_color.alpha() > 0 || !m_stops.empty()) {
+ if (innerRectRadius > 0) {
+ //Rounded Rect
+ if (m_stops.empty()) {
+ //Rounded Rects without gradient need 3 blits
+ QRectF centerRect(QPointF(brushRect.x() + innerRectRadius, brushRect.y()),
+ QPointF(brushRect.x() + brushRect.width() - innerRectRadius, brushRect.y() + brushRect.height()));
+ painter->fillRect(centerRect, m_color);
+ QRectF leftRect(QPointF(brushRect.x(), brushRect.y() + innerRectRadius),
+ QPointF(brushRect.x() + innerRectRadius, brushRect.y() + brushRect.height() - innerRectRadius));
+ painter->fillRect(leftRect, m_color);
+ QRectF rightRect(QPointF(brushRect.x() + brushRect.width() - innerRectRadius, brushRect.y() + innerRectRadius),
+ QPointF(brushRect.x() + brushRect.width(), brushRect.y() + brushRect.height() - innerRectRadius));
+ painter->fillRect(rightRect, m_color);
+ } else {
+ //Rounded Rect with gradient (slow)
+ painter->setPen(Qt::NoPen);
+ painter->setBrush(m_brush);
+ painter->drawRoundedRect(brushRect, innerRectRadius, innerRectRadius);
+ }
+ } else {
+ //non-rounded rects only need 1 blit
+ painter->fillRect(brushRect, m_brush);
+ }
+ }
+
+ painter->setRenderHints(previousRenderHints);
+}
+
+void QSGSoftwareRectangleNode::generateCornerPixmap()
+{
+ //Generate new corner Pixmap
+ int radius = qFloor(qMin(qMin(m_rect.width(), m_rect.height()) * 0.5, m_radius));
+
+ m_cornerPixmap = QPixmap(radius * 2 * m_devicePixelRatio, radius * 2 * m_devicePixelRatio);
+ m_cornerPixmap.setDevicePixelRatio(m_devicePixelRatio);
+ m_cornerPixmap.fill(Qt::transparent);
+
+ if (radius > 0) {
+ QPainter cornerPainter(&m_cornerPixmap);
+ cornerPainter.setRenderHint(QPainter::Antialiasing);
+ cornerPainter.setCompositionMode(QPainter::CompositionMode_Source);
+
+ //Paint outer cicle
+ if (m_penWidth > 0) {
+ cornerPainter.setPen(Qt::NoPen);
+ cornerPainter.setBrush(m_penColor);
+ cornerPainter.drawRoundedRect(QRectF(0, 0, radius * 2, radius *2), radius, radius);
+ }
+
+ //Paint inner circle
+ if (radius > m_penWidth) {
+ cornerPainter.setPen(Qt::NoPen);
+ if (m_stops.isEmpty())
+ cornerPainter.setBrush(m_brush);
+ else
+ cornerPainter.setBrush(Qt::transparent);
+
+ QMarginsF adjustmentMargins(m_penWidth, m_penWidth, m_penWidth, m_penWidth);
+ QRectF cornerCircleRect = QRectF(0, 0, radius * 2, radius * 2).marginsRemoved(adjustmentMargins);
+ cornerPainter.drawRoundedRect(cornerCircleRect, radius, radius);
+ }
+ cornerPainter.end();
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerectanglenode_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarerectanglenode_p.h
new file mode 100644
index 0000000000..9cc0823325
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerectanglenode_p.h
@@ -0,0 +1,103 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGSOFTWARERECTANGLENODE_H
+#define QSGSOFTWARERECTANGLENODE_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qsgadaptationlayer_p.h>
+
+#include <QPen>
+#include <QBrush>
+#include <QPixmap>
+
+QT_BEGIN_NAMESPACE
+
+class QSGSoftwareRectangleNode : public QSGRectangleNode
+{
+public:
+ QSGSoftwareRectangleNode();
+
+ void setRect(const QRectF &rect) override;
+ void setColor(const QColor &color) override;
+ void setPenColor(const QColor &color) override;
+ void setPenWidth(qreal width) override;
+ void setGradientStops(const QGradientStops &stops) override;
+ void setRadius(qreal radius) override;
+ void setAntialiasing(bool antialiasing) override { Q_UNUSED(antialiasing) }
+ void setAligned(bool aligned) override;
+
+ void update() override;
+
+ void paint(QPainter *);
+
+ bool isOpaque() const;
+ QRectF rect() const;
+private:
+ void paintRectangle(QPainter *painter, const QRect &rect);
+ void generateCornerPixmap();
+
+ QRect m_rect;
+ QColor m_color;
+ QColor m_penColor;
+ double m_penWidth;
+ QGradientStops m_stops;
+ double m_radius;
+ QPen m_pen;
+ QBrush m_brush;
+
+ bool m_cornerPixmapIsDirty;
+ QPixmap m_cornerPixmap;
+
+ int m_devicePixelRatio;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGSOFTWARERECTANGLENODE_H
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp
new file mode 100644
index 0000000000..063242c63b
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp
@@ -0,0 +1,328 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgsoftwarerenderablenode_p.h"
+
+#include "qsgsoftwareimagenode_p.h"
+#include "qsgsoftwarerectanglenode_p.h"
+#include "qsgsoftwareglyphnode_p.h"
+#include "qsgsoftwareninepatchnode_p.h"
+#include "qsgsoftwarepainternode_p.h"
+#include "qsgsoftwarepixmaptexture_p.h"
+
+#include <QtQuick/QSGSimpleRectNode>
+#include <QtQuick/qsgsimpletexturenode.h>
+#include <private/qsgtexture_p.h>
+
+Q_LOGGING_CATEGORY(lcRenderable, "qt.scenegraph.softwarecontext.renderable")
+
+QT_BEGIN_NAMESPACE
+
+QSGSoftwareRenderableNode::QSGSoftwareRenderableNode(NodeType type, QSGNode *node)
+ : m_nodeType(type)
+ , m_isOpaque(true)
+ , m_isDirty(true)
+ , m_opacity(1.0f)
+{
+ switch (m_nodeType) {
+ case QSGSoftwareRenderableNode::SimpleRect:
+ m_handle.simpleRectNode = static_cast<QSGSimpleRectNode*>(node);
+ break;
+ case QSGSoftwareRenderableNode::SimpleTexture:
+ m_handle.simpleTextureNode = static_cast<QSGSimpleTextureNode*>(node);
+ break;
+ case QSGSoftwareRenderableNode::Image:
+ m_handle.imageNode = static_cast<QSGSoftwareImageNode*>(node);
+ break;
+ case QSGSoftwareRenderableNode::Painter:
+ m_handle.painterNode = static_cast<QSGSoftwarePainterNode*>(node);
+ break;
+ case QSGSoftwareRenderableNode::Rectangle:
+ m_handle.rectangleNode = static_cast<QSGSoftwareRectangleNode*>(node);
+ break;
+ case QSGSoftwareRenderableNode::Glyph:
+ m_handle.glpyhNode = static_cast<QSGSoftwareGlyphNode*>(node);
+ break;
+ case QSGSoftwareRenderableNode::NinePatch:
+ m_handle.ninePatchNode = static_cast<QSGSoftwareNinePatchNode*>(node);
+ break;
+ case QSGSoftwareRenderableNode::Invalid:
+ m_handle.simpleRectNode = nullptr;
+ break;
+ }
+}
+
+QSGSoftwareRenderableNode::~QSGSoftwareRenderableNode()
+{
+
+}
+
+void QSGSoftwareRenderableNode::update()
+{
+ // Update the Node properties
+ m_isDirty = true;
+
+ QRect boundingRect;
+
+ switch (m_nodeType) {
+ case QSGSoftwareRenderableNode::SimpleRect:
+ if (m_handle.simpleRectNode->color().alpha() == 255 && !m_transform.isRotating())
+ m_isOpaque = true;
+ else
+ m_isOpaque = false;
+
+ boundingRect = m_handle.simpleRectNode->rect().toRect();
+ break;
+ case QSGSoftwareRenderableNode::SimpleTexture:
+ if (!m_handle.simpleTextureNode->texture()->hasAlphaChannel() && !m_transform.isRotating())
+ m_isOpaque = true;
+ else
+ m_isOpaque = false;
+
+ boundingRect = m_handle.simpleTextureNode->rect().toRect();
+ break;
+ case QSGSoftwareRenderableNode::Image:
+ // There isn't a way to tell, so assume it's not
+ m_isOpaque = false;
+
+ boundingRect = m_handle.imageNode->rect().toRect();
+ break;
+ case QSGSoftwareRenderableNode::Painter:
+ if (m_handle.painterNode->opaquePainting() && !m_transform.isRotating())
+ m_isOpaque = true;
+ else
+ m_isOpaque = false;
+
+ boundingRect = QRect(0, 0, m_handle.painterNode->size().width(), m_handle.painterNode->size().height());
+ break;
+ case QSGSoftwareRenderableNode::Rectangle:
+ if (m_handle.rectangleNode->isOpaque() && !m_transform.isRotating())
+ m_isOpaque = true;
+ else
+ m_isOpaque = false;
+
+ boundingRect = m_handle.rectangleNode->rect().toRect();
+ break;
+ case QSGSoftwareRenderableNode::Glyph:
+ // Always has alpha
+ m_isOpaque = false;
+
+ boundingRect = m_handle.glpyhNode->boundingRect().toAlignedRect();
+ break;
+ case QSGSoftwareRenderableNode::NinePatch:
+ // Difficult to tell, assume non-opaque
+ m_isOpaque = false;
+
+ boundingRect = m_handle.ninePatchNode->bounds().toRect();
+ break;
+ default:
+ break;
+ }
+
+ m_boundingRect = m_transform.mapRect(boundingRect);
+
+ if (m_clipRegion.rectCount() == 1) {
+ m_boundingRect = m_boundingRect.intersected(m_clipRegion.rects().first());
+ }
+
+ // Overrides
+ if (m_opacity < 1.0f)
+ m_isOpaque = false;
+
+ m_dirtyRegion = QRegion(m_boundingRect);
+}
+
+QRegion QSGSoftwareRenderableNode::renderNode(QPainter *painter, bool forceOpaquePainting)
+{
+ Q_ASSERT(painter);
+
+ // Check for don't paint conditions
+ if (!m_isDirty || qFuzzyIsNull(m_opacity) || m_dirtyRegion.isEmpty()) {
+ m_isDirty = false;
+ m_dirtyRegion = QRegion();
+ return QRegion();
+ }
+
+ painter->save();
+ painter->setOpacity(m_opacity);
+
+ // Set clipRegion to m_dirtyRegion (in world coordinates)
+ // as m_dirtyRegion already accounts for clipRegion
+ painter->setClipRegion(m_dirtyRegion, Qt::ReplaceClip);
+ if (m_clipRegion.rectCount() > 1)
+ painter->setClipRegion(m_clipRegion, Qt::IntersectClip);
+
+ painter->setTransform(m_transform, false); //precalculated worldTransform
+ if (forceOpaquePainting || m_isOpaque)
+ painter->setCompositionMode(QPainter::CompositionMode_Source);
+
+ switch (m_nodeType) {
+ case QSGSoftwareRenderableNode::SimpleRect:
+ painter->fillRect(m_handle.simpleRectNode->rect(), m_handle.simpleRectNode->color());
+ break;
+ case QSGSoftwareRenderableNode::SimpleTexture:
+ {
+ QSGTexture *texture = m_handle.simpleTextureNode->texture();
+ if (QSGSoftwarePixmapTexture *pt = dynamic_cast<QSGSoftwarePixmapTexture *>(texture)) {
+ const QPixmap &pm = pt->pixmap();
+ painter->drawPixmap(m_handle.simpleTextureNode->rect(), pm, QRectF(0, 0, pm.width(), pm.height()));
+ } else if (QSGPlainTexture *pt = dynamic_cast<QSGPlainTexture *>(texture)) {
+ const QImage &im = pt->image();
+ painter->drawImage(m_handle.simpleTextureNode->rect(), im, QRectF(0, 0, im.width(), im.height()));
+ }
+ }
+ break;
+ case QSGSoftwareRenderableNode::Image:
+ m_handle.imageNode->paint(painter);
+ break;
+ case QSGSoftwareRenderableNode::Painter:
+ m_handle.painterNode->paint(painter);
+ break;
+ case QSGSoftwareRenderableNode::Rectangle:
+ m_handle.rectangleNode->paint(painter);
+ break;
+ case QSGSoftwareRenderableNode::Glyph:
+ m_handle.glpyhNode->paint(painter);
+ break;
+ case QSGSoftwareRenderableNode::NinePatch:
+ m_handle.ninePatchNode->paint(painter);
+ break;
+ default:
+ break;
+ }
+
+ painter->restore();
+
+ QRegion areaToBeFlushed = m_dirtyRegion;
+ m_previousDirtyRegion = QRegion(m_boundingRect);
+ m_isDirty = false;
+ m_dirtyRegion = QRegion();
+
+ return areaToBeFlushed;
+}
+
+QRect QSGSoftwareRenderableNode::boundingRect() const
+{
+ // This returns the bounding area of a renderable node in world coordinates
+ return m_boundingRect;
+}
+
+bool QSGSoftwareRenderableNode::isDirtyRegionEmpty() const
+{
+ return m_dirtyRegion.isEmpty();
+}
+
+void QSGSoftwareRenderableNode::setTransform(const QTransform &transform)
+{
+ if (m_transform == transform)
+ return;
+ m_transform = transform;
+ update();
+}
+
+void QSGSoftwareRenderableNode::setClipRegion(const QRegion &clipRect)
+{
+ if (m_clipRegion == clipRect)
+ return;
+
+ m_clipRegion = clipRect;
+ update();
+}
+
+void QSGSoftwareRenderableNode::setOpacity(float opacity)
+{
+ if (qFuzzyCompare(m_opacity, opacity))
+ return;
+
+ m_opacity = opacity;
+ update();
+}
+
+void QSGSoftwareRenderableNode::markGeometryDirty()
+{
+ update();
+}
+
+void QSGSoftwareRenderableNode::markMaterialDirty()
+{
+ update();
+}
+
+void QSGSoftwareRenderableNode::addDirtyRegion(const QRegion &dirtyRegion, bool forceDirty)
+{
+ // Check if the dirty region applys to this node
+ QRegion prev = m_dirtyRegion;
+ if (dirtyRegion.intersects(boundingRect())) {
+ if (forceDirty)
+ m_isDirty = true;
+ m_dirtyRegion += dirtyRegion.intersected(boundingRect());
+ }
+ qCDebug(lcRenderable) << "addDirtyRegion: " << dirtyRegion << "old dirtyRegion: " << prev << "new dirtyRegion: " << m_dirtyRegion;
+}
+
+void QSGSoftwareRenderableNode::subtractDirtyRegion(const QRegion &dirtyRegion)
+{
+ QRegion prev = m_dirtyRegion;
+ if (m_isDirty) {
+ // Check if this rect concerns us
+ if (dirtyRegion.intersects(QRegion(boundingRect()))) {
+ m_dirtyRegion -= dirtyRegion;
+ if (m_dirtyRegion.isEmpty())
+ m_isDirty = false;
+ }
+ }
+ qCDebug(lcRenderable) << "subtractDirtyRegion: " << dirtyRegion << "old dirtyRegion" << prev << "new dirtyRegion: " << m_dirtyRegion;
+}
+
+QRegion QSGSoftwareRenderableNode::previousDirtyRegion(bool wasRemoved) const
+{
+ // When removing a node, the boundingRect shouldn't be subtracted
+ // because a deleted node has no valid boundingRect
+ if (wasRemoved)
+ return m_previousDirtyRegion;
+
+ return m_previousDirtyRegion.subtracted(QRegion(m_boundingRect));
+}
+
+QRegion QSGSoftwareRenderableNode::dirtyRegion() const
+{
+ return m_dirtyRegion;
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode_p.h
new file mode 100644
index 0000000000..9a5e0a5683
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode_p.h
@@ -0,0 +1,140 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGSOFTWARERENDERABLENODE_H
+#define QSGSOFTWARERENDERABLENODE_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtGui/QRegion>
+#include <QtCore/QRect>
+#include <QtGui/QTransform>
+
+QT_BEGIN_NAMESPACE
+
+class QSGNode;
+class QSGSimpleRectNode;
+class QSGSimpleTextureNode;
+class QSGSoftwareImageNode;
+class QSGSoftwarePainterNode;
+class QSGSoftwareRectangleNode;
+class QSGSoftwareGlyphNode;
+class QSGSoftwareNinePatchNode;
+
+class QSGSoftwareRenderableNode
+{
+public:
+ enum NodeType {
+ Invalid = -1,
+ SimpleRect,
+ SimpleTexture,
+ Image,
+ Painter,
+ Rectangle,
+ Glyph,
+ NinePatch
+ };
+
+ QSGSoftwareRenderableNode(NodeType type, QSGNode *node);
+ ~QSGSoftwareRenderableNode();
+
+ void update();
+
+ QRegion renderNode(QPainter *painter, bool forceOpaquePainting = false);
+ QRect boundingRect() const;
+ NodeType type() const { return m_nodeType; }
+ bool isOpaque() const { return m_isOpaque; }
+ bool isDirty() const { return m_isDirty; }
+ bool isDirtyRegionEmpty() const;
+
+ void setTransform(const QTransform &transform);
+ void setClipRegion(const QRegion &clipRegion);
+ void setOpacity(float opacity);
+ QTransform transform() const { return m_transform; }
+ QRegion clipRegion() const { return m_clipRegion; }
+ float opacity() const { return m_opacity; }
+
+ void markGeometryDirty();
+ void markMaterialDirty();
+
+ void addDirtyRegion(const QRegion &dirtyRegion, bool forceDirty = true);
+ void subtractDirtyRegion(const QRegion &dirtyRegion);
+
+ QRegion previousDirtyRegion(bool wasRemoved = false) const;
+ QRegion dirtyRegion() const;
+
+private:
+ union RenderableNodeHandle {
+ QSGSimpleRectNode *simpleRectNode;
+ QSGSimpleTextureNode *simpleTextureNode;
+ QSGSoftwareImageNode *imageNode;
+ QSGSoftwarePainterNode *painterNode;
+ QSGSoftwareRectangleNode *rectangleNode;
+ QSGSoftwareGlyphNode *glpyhNode;
+ QSGSoftwareNinePatchNode *ninePatchNode;
+ };
+
+ const NodeType m_nodeType;
+ RenderableNodeHandle m_handle;
+
+ bool m_isOpaque;
+
+ bool m_isDirty;
+ QRegion m_dirtyRegion;
+ QRegion m_previousDirtyRegion;
+
+ QTransform m_transform;
+ QRegion m_clipRegion;
+ float m_opacity;
+
+ QRect m_boundingRect;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGSOFTWARERENDERABLENODE_H
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater.cpp
new file mode 100644
index 0000000000..3d5fe21f7c
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater.cpp
@@ -0,0 +1,275 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgsoftwarerenderablenodeupdater_p.h"
+
+#include "qsgabstractsoftwarerenderer_p.h"
+#include "qsgsoftwareimagenode_p.h"
+#include "qsgsoftwarerectanglenode_p.h"
+#include "qsgsoftwareglyphnode_p.h"
+#include "qsgsoftwareninepatchnode_p.h"
+#include "qsgsoftwarepainternode_p.h"
+#include "qsgsoftwarepixmaptexture_p.h"
+
+#include <QtQuick/QSGSimpleRectNode>
+#include <QtQuick/qsgsimpletexturenode.h>
+
+QT_BEGIN_NAMESPACE
+
+QSGSoftwareRenderableNodeUpdater::QSGSoftwareRenderableNodeUpdater(QSGAbstractSoftwareRenderer *renderer)
+ : m_renderer(renderer)
+{
+ m_opacityState.push(1.0f);
+ // Invalid RectF by default for no clip
+ m_clipState.push(QRegion());
+ m_transformState.push(QTransform());
+}
+
+QSGSoftwareRenderableNodeUpdater::~QSGSoftwareRenderableNodeUpdater()
+{
+
+}
+
+bool QSGSoftwareRenderableNodeUpdater::visit(QSGTransformNode *node)
+{
+ m_transformState.push(node->matrix().toTransform() * m_transformState.top());
+ m_stateMap[node] = currentState(node);
+ return true;
+}
+
+void QSGSoftwareRenderableNodeUpdater::endVisit(QSGTransformNode *)
+{
+ m_transformState.pop();
+}
+
+bool QSGSoftwareRenderableNodeUpdater::visit(QSGClipNode *node)
+{
+ // Make sure to translate the clip rect into world coordinates
+ if (m_clipState.top().isEmpty()) {
+ m_clipState.push(m_transformState.top().map(QRegion(node->clipRect().toRect())));
+ } else
+ m_clipState.push(m_transformState.top().map(QRegion(node->clipRect().toRect()).intersected(m_clipState.top())));
+ m_stateMap[node] = currentState(node);
+ return true;
+}
+
+void QSGSoftwareRenderableNodeUpdater::endVisit(QSGClipNode *)
+{
+ m_clipState.pop();
+}
+
+bool QSGSoftwareRenderableNodeUpdater::visit(QSGGeometryNode *node)
+{
+ if (QSGSimpleRectNode *rectNode = dynamic_cast<QSGSimpleRectNode *>(node)) {
+ return updateRenderableNode(QSGSoftwareRenderableNode::SimpleRect, rectNode);
+ } else if (QSGSimpleTextureNode *tn = dynamic_cast<QSGSimpleTextureNode *>(node)) {
+ return updateRenderableNode(QSGSoftwareRenderableNode::SimpleTexture, tn);
+ } else {
+ // We dont know, so skip
+ return false;
+ }
+}
+
+void QSGSoftwareRenderableNodeUpdater::endVisit(QSGGeometryNode *)
+{
+}
+
+bool QSGSoftwareRenderableNodeUpdater::visit(QSGOpacityNode *node)
+{
+ m_opacityState.push(m_opacityState.top() * node->opacity());
+ m_stateMap[node] = currentState(node);
+ return true;
+}
+
+void QSGSoftwareRenderableNodeUpdater::endVisit(QSGOpacityNode *)
+{
+ m_opacityState.pop();
+}
+
+bool QSGSoftwareRenderableNodeUpdater::visit(QSGImageNode *node)
+{
+ return updateRenderableNode(QSGSoftwareRenderableNode::Image, node);
+}
+
+void QSGSoftwareRenderableNodeUpdater::endVisit(QSGImageNode *)
+{
+}
+
+bool QSGSoftwareRenderableNodeUpdater::visit(QSGPainterNode *node)
+{
+ return updateRenderableNode(QSGSoftwareRenderableNode::Painter, node);
+}
+
+void QSGSoftwareRenderableNodeUpdater::endVisit(QSGPainterNode *)
+{
+}
+
+bool QSGSoftwareRenderableNodeUpdater::visit(QSGRectangleNode *node)
+{
+ return updateRenderableNode(QSGSoftwareRenderableNode::Rectangle, node);
+}
+
+void QSGSoftwareRenderableNodeUpdater::endVisit(QSGRectangleNode *)
+{
+}
+
+bool QSGSoftwareRenderableNodeUpdater::visit(QSGGlyphNode *node)
+{
+ return updateRenderableNode(QSGSoftwareRenderableNode::Glyph, node);
+}
+
+void QSGSoftwareRenderableNodeUpdater::endVisit(QSGGlyphNode *)
+{
+}
+
+bool QSGSoftwareRenderableNodeUpdater::visit(QSGNinePatchNode *node)
+{
+ return updateRenderableNode(QSGSoftwareRenderableNode::NinePatch, node);
+}
+
+void QSGSoftwareRenderableNodeUpdater::endVisit(QSGNinePatchNode *)
+{
+}
+
+bool QSGSoftwareRenderableNodeUpdater::visit(QSGRootNode *node)
+{
+ m_stateMap[node] = currentState(node);
+ return true;
+}
+
+void QSGSoftwareRenderableNodeUpdater::endVisit(QSGRootNode *)
+{
+}
+
+void QSGSoftwareRenderableNodeUpdater::updateNodes(QSGNode *node, bool isNodeRemoved)
+{
+ m_opacityState.clear();
+ m_clipState.clear();
+ m_transformState.clear();
+
+ auto parentNode = node->parent();
+ // If the node was deleted, it will have no parent
+ // check if the state map has the previous parent
+ if ((!parentNode || isNodeRemoved ) && m_stateMap.contains(node))
+ parentNode = m_stateMap[node].parent;
+
+ // If we find a parent, use its state for updating the new children
+ if (parentNode && m_stateMap.contains(parentNode)) {
+ auto state = m_stateMap[parentNode];
+ m_opacityState.push(state.opacity);
+ m_transformState.push(state.transform);
+ m_clipState.push(state.clip);
+
+ } else {
+ // There is no parent, and no previous parent, so likely a root node
+ m_opacityState.push(1.0f);
+ m_transformState.push(QTransform());
+ m_clipState.push(QRegion());
+ }
+
+ // If the node is being removed, then cleanup the state data
+ // Then just visit the children without visiting the now removed node
+ if (isNodeRemoved) {
+ m_stateMap.remove(node);
+ return;
+ }
+
+ // Visit the current node itself first
+ switch (node->type()) {
+ case QSGNode::ClipNodeType: {
+ QSGClipNode *c = static_cast<QSGClipNode*>(node);
+ if (visit(c))
+ visitChildren(c);
+ endVisit(c);
+ break;
+ }
+ case QSGNode::TransformNodeType: {
+ QSGTransformNode *c = static_cast<QSGTransformNode*>(node);
+ if (visit(c))
+ visitChildren(c);
+ endVisit(c);
+ break;
+ }
+ case QSGNode::OpacityNodeType: {
+ QSGOpacityNode *c = static_cast<QSGOpacityNode*>(node);
+ if (visit(c))
+ visitChildren(c);
+ endVisit(c);
+ break;
+ }
+ case QSGNode::GeometryNodeType: {
+ if (node->flags() & QSGNode::IsVisitableNode) {
+ QSGVisitableNode *v = static_cast<QSGVisitableNode*>(node);
+ v->accept(this);
+ } else {
+ QSGGeometryNode *c = static_cast<QSGGeometryNode*>(node);
+ if (visit(c))
+ visitChildren(c);
+ endVisit(c);
+ }
+ break;
+ }
+ case QSGNode::RootNodeType: {
+ QSGRootNode *root = static_cast<QSGRootNode*>(node);
+ if (visit(root))
+ visitChildren(root);
+ endVisit(root);
+ break;
+ }
+ case QSGNode::BasicNodeType: {
+ visitChildren(node);
+ break;
+ }
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+}
+
+QSGSoftwareRenderableNodeUpdater::NodeState QSGSoftwareRenderableNodeUpdater::currentState(QSGNode *node) const
+{
+ NodeState state;
+ state.opacity = m_opacityState.top();
+ state.clip = m_clipState.top();
+ state.transform = m_transformState.top();
+ state.parent = node->parent();
+ return state;
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater_p.h
new file mode 100644
index 0000000000..562d15769e
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater_p.h
@@ -0,0 +1,137 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGSOFTWARERENDERABLENODEUPDATER_H
+#define QSGSOFTWARERENDERABLENODEUPDATER_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qsgsoftwarerenderablenode_p.h"
+#include "qsgabstractsoftwarerenderer_p.h"
+
+#include <private/qsgadaptationlayer_p.h>
+
+#include <QTransform>
+#include <QStack>
+#include <QRectF>
+
+QT_BEGIN_NAMESPACE
+
+class QSGSoftwareRenderableNodeUpdater : public QSGNodeVisitorEx
+{
+public:
+ QSGSoftwareRenderableNodeUpdater(QSGAbstractSoftwareRenderer *renderer);
+ virtual ~QSGSoftwareRenderableNodeUpdater();
+
+ bool visit(QSGTransformNode *) override;
+ void endVisit(QSGTransformNode *) override;
+ bool visit(QSGClipNode *) override;
+ void endVisit(QSGClipNode *) override;
+ bool visit(QSGGeometryNode *) override;
+ void endVisit(QSGGeometryNode *) override;
+ bool visit(QSGOpacityNode *) override;
+ void endVisit(QSGOpacityNode *) override;
+ bool visit(QSGImageNode *) override;
+ void endVisit(QSGImageNode *) override;
+ bool visit(QSGPainterNode *) override;
+ void endVisit(QSGPainterNode *) override;
+ bool visit(QSGRectangleNode *) override;
+ void endVisit(QSGRectangleNode *) override;
+ bool visit(QSGGlyphNode *) override;
+ void endVisit(QSGGlyphNode *) override;
+ bool visit(QSGNinePatchNode *) override;
+ void endVisit(QSGNinePatchNode *) override;
+ bool visit(QSGRootNode *) override;
+ void endVisit(QSGRootNode *) override;
+
+ void updateNodes(QSGNode *node, bool isNodeRemoved = false);
+
+private:
+ struct NodeState {
+ float opacity;
+ QRegion clip;
+ QTransform transform;
+ QSGNode *parent;
+ };
+
+ NodeState currentState(QSGNode *node) const;
+
+ template<class NODE>
+ bool updateRenderableNode(QSGSoftwareRenderableNode::NodeType type, NODE *node);
+
+ QSGAbstractSoftwareRenderer *m_renderer;
+ QStack<float> m_opacityState;
+ QStack<QRegion> m_clipState;
+ QStack<QTransform> m_transformState;
+ QHash<QSGNode*,NodeState> m_stateMap;
+};
+
+template<class NODE>
+bool QSGSoftwareRenderableNodeUpdater::updateRenderableNode(QSGSoftwareRenderableNode::NodeType type, NODE *node)
+{
+ //Check if we already know about node
+ auto renderableNode = m_renderer->renderableNode(node);
+ if (renderableNode == nullptr) {
+ renderableNode = new QSGSoftwareRenderableNode(type, node);
+ m_renderer->addNodeMapping(node, renderableNode);
+ }
+
+ //Update the node
+ renderableNode->setTransform(m_transformState.top());
+ renderableNode->setOpacity(m_opacityState.top());
+ renderableNode->setClipRegion(m_clipState.top());
+
+ renderableNode->update();
+ m_stateMap[node] = currentState(node);
+
+ return true;
+}
+
+QT_END_NAMESPACE
+
+#endif // QSGSOFTWARERENDERABLENODEUPDATER_H
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer.cpp
new file mode 100644
index 0000000000..ea00de7a66
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer.cpp
@@ -0,0 +1,126 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgsoftwarerenderer_p.h"
+
+#include "qsgsoftwarerenderablenodeupdater_p.h"
+#include "qsgsoftwarerenderlistbuilder_p.h"
+#include "qsgsoftwarecontext_p.h"
+#include "qsgsoftwarerenderablenode_p.h"
+
+#include <QtGui/QPaintDevice>
+#include <QElapsedTimer>
+
+Q_LOGGING_CATEGORY(lcRenderer, "qt.scenegraph.softwarecontext.renderer")
+
+QT_BEGIN_NAMESPACE
+
+QSGSoftwareRenderer::QSGSoftwareRenderer(QSGRenderContext *context)
+ : QSGAbstractSoftwareRenderer(context)
+{
+}
+
+QSGSoftwareRenderer::~QSGSoftwareRenderer()
+{
+}
+
+void QSGSoftwareRenderer::setCurrentPaintDevice(QPaintDevice *device)
+{
+ m_paintDevice = device;
+}
+
+QRegion QSGSoftwareRenderer::flushRegion() const
+{
+ return m_flushRegion;
+}
+
+void QSGSoftwareRenderer::renderScene(uint)
+{
+ class B : public QSGBindable
+ {
+ public:
+ void bind() const { }
+ } bindable;
+ QSGRenderer::renderScene(bindable);
+}
+
+void QSGSoftwareRenderer::render()
+{
+ if (!m_paintDevice)
+ return;
+
+ QElapsedTimer renderTimer;
+
+ setBackgroundColor(clearColor());
+ setBackgroundSize(QSize(m_paintDevice->width() / m_paintDevice->devicePixelRatio(),
+ m_paintDevice->height() / m_paintDevice->devicePixelRatio()));
+
+ QPainter painter(m_paintDevice);
+ painter.setRenderHint(QPainter::Antialiasing);
+
+ // Build Renderlist
+ // The renderlist is created by visiting each node in the tree and when a
+ // renderable node is reach, we find the coorosponding RenderableNode object
+ // and append it to the renderlist. At this point the RenderableNode object
+ // should not need any further updating so it is just a matter of appending
+ // RenderableNodes
+ renderTimer.start();
+ buildRenderList();
+ qint64 buildRenderListTime = renderTimer.restart();
+
+ // Optimize Renderlist
+ // This is a pass through the renderlist to determine what actually needs to
+ // be painted. Without this pass the renderlist will simply render each item
+ // from back to front, with a high potential for overdraw. It would also lead
+ // to the entire window being flushed every frame. The objective of the
+ // optimization pass is to only paint dirty nodes that are not occuluded. A
+ // side effect of this is that additional nodes may need to be marked dirty to
+ // force a repaint. It is also important that any item that needs to be
+ // repainted only paints what is needed, via the use of clip regions.
+ optimizeRenderList();
+ qint64 optimizeRenderListTime = renderTimer.restart();
+
+ // Render the contents Renderlist
+ m_flushRegion = renderNodes(&painter);
+ qint64 renderTime = renderTimer.elapsed();
+
+ qCDebug(lcRenderer) << "render" << m_flushRegion << buildRenderListTime << optimizeRenderListTime << renderTime;
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer_p.h
new file mode 100644
index 0000000000..e2b8bcddca
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer_p.h
@@ -0,0 +1,80 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGSOFTWARERENDERER_H
+#define QSGSOFTWARERENDERER_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qsgabstractsoftwarerenderer_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QPaintDevice;
+
+class Q_QUICK_PRIVATE_EXPORT QSGSoftwareRenderer : public QSGAbstractSoftwareRenderer
+{
+public:
+ QSGSoftwareRenderer(QSGRenderContext *context);
+ virtual ~QSGSoftwareRenderer();
+
+ void setCurrentPaintDevice(QPaintDevice *device);
+ QRegion flushRegion() const;
+
+protected:
+ void renderScene(uint fboId = 0) final;
+ void render() final;
+
+private:
+ QPaintDevice* m_paintDevice;
+ QRegion m_flushRegion;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGSOFTWARERENDERER_H
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderlistbuilder.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderlistbuilder.cpp
new file mode 100644
index 0000000000..af81ff61c3
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderlistbuilder.cpp
@@ -0,0 +1,163 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgsoftwarerenderlistbuilder_p.h"
+
+#include "qsgsoftwarerenderablenode_p.h"
+#include "qsgabstractsoftwarerenderer_p.h"
+#include "qsgsoftwareimagenode_p.h"
+#include "qsgsoftwarerectanglenode_p.h"
+#include "qsgsoftwareglyphnode_p.h"
+#include "qsgsoftwareninepatchnode_p.h"
+#include "qsgsoftwarepainternode_p.h"
+#include "qsgsoftwarepixmaptexture_p.h"
+
+#include <QtQuick/QSGSimpleRectNode>
+#include <QtQuick/qsgsimpletexturenode.h>
+
+QT_BEGIN_NAMESPACE
+
+QSGSoftwareRenderListBuilder::QSGSoftwareRenderListBuilder(QSGAbstractSoftwareRenderer *renderer)
+ : m_renderer(renderer)
+{
+
+}
+
+bool QSGSoftwareRenderListBuilder::visit(QSGTransformNode *)
+{
+ return true;
+}
+
+void QSGSoftwareRenderListBuilder::endVisit(QSGTransformNode *)
+{
+}
+
+bool QSGSoftwareRenderListBuilder::visit(QSGClipNode *)
+{
+ return true;
+}
+
+void QSGSoftwareRenderListBuilder::endVisit(QSGClipNode *)
+{
+}
+
+bool QSGSoftwareRenderListBuilder::visit(QSGGeometryNode *node)
+{
+ return addRenderableNode(node);
+}
+
+void QSGSoftwareRenderListBuilder::endVisit(QSGGeometryNode *)
+{
+}
+
+bool QSGSoftwareRenderListBuilder::visit(QSGOpacityNode *)
+{
+ return true;
+}
+
+void QSGSoftwareRenderListBuilder::endVisit(QSGOpacityNode *)
+{
+}
+
+bool QSGSoftwareRenderListBuilder::visit(QSGImageNode *node)
+{
+ return addRenderableNode(node);
+}
+
+void QSGSoftwareRenderListBuilder::endVisit(QSGImageNode *)
+{
+}
+
+bool QSGSoftwareRenderListBuilder::visit(QSGPainterNode *node)
+{
+ return addRenderableNode(node);
+}
+
+void QSGSoftwareRenderListBuilder::endVisit(QSGPainterNode *)
+{
+}
+
+bool QSGSoftwareRenderListBuilder::visit(QSGRectangleNode *node)
+{
+ return addRenderableNode(node);
+}
+
+void QSGSoftwareRenderListBuilder::endVisit(QSGRectangleNode *)
+{
+}
+
+bool QSGSoftwareRenderListBuilder::visit(QSGGlyphNode *node)
+{
+ return addRenderableNode(node);
+}
+
+void QSGSoftwareRenderListBuilder::endVisit(QSGGlyphNode *)
+{
+}
+
+bool QSGSoftwareRenderListBuilder::visit(QSGNinePatchNode *node)
+{
+ return addRenderableNode(node);
+}
+
+void QSGSoftwareRenderListBuilder::endVisit(QSGNinePatchNode *)
+{
+}
+
+bool QSGSoftwareRenderListBuilder::visit(QSGRootNode *)
+{
+ return true;
+}
+
+void QSGSoftwareRenderListBuilder::endVisit(QSGRootNode *)
+{
+}
+
+bool QSGSoftwareRenderListBuilder::addRenderableNode(QSGNode *node)
+{
+ auto renderableNode = m_renderer->renderableNode(node);
+ if (renderableNode == nullptr) {
+ // Not a node we can render
+ return false;
+ }
+ m_renderer->appendRenderableNode(renderableNode);
+ return true;
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderlistbuilder_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderlistbuilder_p.h
new file mode 100644
index 0000000000..94b563564d
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderlistbuilder_p.h
@@ -0,0 +1,94 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGSOFTWARERENDERLISTBUILDER_H
+#define QSGSOFTWARERENDERLISTBUILDER_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qsgadaptationlayer_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QSGAbstractSoftwareRenderer;
+
+class QSGSoftwareRenderListBuilder : public QSGNodeVisitorEx
+{
+public:
+ QSGSoftwareRenderListBuilder(QSGAbstractSoftwareRenderer *renderer);
+
+ bool visit(QSGTransformNode *) override;
+ void endVisit(QSGTransformNode *) override;
+ bool visit(QSGClipNode *) override;
+ void endVisit(QSGClipNode *) override;
+ bool visit(QSGGeometryNode *) override;
+ void endVisit(QSGGeometryNode *) override;
+ bool visit(QSGOpacityNode *) override;
+ void endVisit(QSGOpacityNode *) override;
+ bool visit(QSGImageNode *) override;
+ void endVisit(QSGImageNode *) override;
+ bool visit(QSGPainterNode *) override;
+ void endVisit(QSGPainterNode *) override;
+ bool visit(QSGRectangleNode *) override;
+ void endVisit(QSGRectangleNode *) override;
+ bool visit(QSGGlyphNode *) override;
+ void endVisit(QSGGlyphNode *) override;
+ bool visit(QSGNinePatchNode *) override;
+ void endVisit(QSGNinePatchNode *) override;
+ bool visit(QSGRootNode *) override;
+ void endVisit(QSGRootNode *) override;
+
+private:
+ bool addRenderableNode(QSGNode *node);
+
+ QSGAbstractSoftwareRenderer *m_renderer;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGSOFTWARERENDERLISTBUILDER_H
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop.cpp
new file mode 100644
index 0000000000..bfcc99c4c2
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop.cpp
@@ -0,0 +1,265 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgsoftwarerenderloop_p.h"
+
+#include "qsgsoftwarecontext_p.h"
+
+#include <QtCore/QCoreApplication>
+
+#include <private/qquickwindow_p.h>
+#include <QElapsedTimer>
+#include <private/qquickprofiler_p.h>
+#include <private/qsgsoftwarerenderer_p.h>
+#include <qpa/qplatformbackingstore.h>
+
+#include <QtGui/QBackingStore>
+
+QT_BEGIN_NAMESPACE
+
+QSGSoftwareRenderLoop::QSGSoftwareRenderLoop()
+{
+ sg = new QSGSoftwareContext();
+ rc = sg->createRenderContext();
+}
+
+QSGSoftwareRenderLoop::~QSGSoftwareRenderLoop()
+{
+ delete rc;
+ delete sg;
+}
+
+void QSGSoftwareRenderLoop::show(QQuickWindow *window)
+{
+ WindowData data;
+ data.updatePending = false;
+ data.grabOnly = false;
+ m_windows[window] = data;
+
+ if (m_backingStores[window] == nullptr) {
+ m_backingStores[window] = new QBackingStore(window);
+ }
+
+ maybeUpdate(window);
+}
+
+void QSGSoftwareRenderLoop::hide(QQuickWindow *window)
+{
+ QQuickWindowPrivate *cd = QQuickWindowPrivate::get(window);
+ cd->fireAboutToStop();
+}
+
+void QSGSoftwareRenderLoop::windowDestroyed(QQuickWindow *window)
+{
+ m_windows.remove(window);
+ delete m_backingStores[window];
+ m_backingStores.remove(window);
+ hide(window);
+
+ QQuickWindowPrivate *d = QQuickWindowPrivate::get(window);
+ d->cleanupNodesOnShutdown();
+
+ if (m_windows.size() == 0) {
+ rc->invalidate();
+ QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
+ }
+}
+
+void QSGSoftwareRenderLoop::renderWindow(QQuickWindow *window)
+{
+ QQuickWindowPrivate *cd = QQuickWindowPrivate::get(window);
+ if (!m_windows.contains(window))
+ return;
+
+ WindowData &data = const_cast<WindowData &>(m_windows[window]);
+
+ //If were not in grabOnly mode, dont render a non-renderable window
+ if (!data.grabOnly && !cd->isRenderable())
+ return;
+
+ //Resize the backing store if necessary
+ if (m_backingStores[window]->size() != window->size()) {
+ m_backingStores[window]->resize(window->size());
+ }
+
+ // ### create QPainter and set up pointer to current window/painter
+ QSGSoftwareRenderContext *ctx = static_cast<QSGSoftwareRenderContext*>(cd->context);
+ ctx->initializeIfNeeded();
+
+ bool alsoSwap = data.updatePending;
+ data.updatePending = false;
+
+ if (!data.grabOnly) {
+ cd->flushDelayedTouchEvent();
+ // Event delivery/processing triggered the window to be deleted or stop rendering.
+ if (!m_windows.contains(window))
+ return;
+ }
+ QElapsedTimer renderTimer;
+ qint64 renderTime = 0, syncTime = 0, polishTime = 0;
+ bool profileFrames = QSG_RASTER_LOG_TIME_RENDERLOOP().isDebugEnabled();
+ if (profileFrames)
+ renderTimer.start();
+ Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphPolishFrame);
+
+ cd->polishItems();
+
+ if (profileFrames)
+ polishTime = renderTimer.nsecsElapsed();
+ Q_QUICK_SG_PROFILE_SWITCH(QQuickProfiler::SceneGraphPolishFrame,
+ QQuickProfiler::SceneGraphRenderLoopFrame);
+
+ emit window->afterAnimating();
+
+ cd->syncSceneGraph();
+
+ if (profileFrames)
+ syncTime = renderTimer.nsecsElapsed();
+ Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame);
+
+ //Tell the renderer about the windows backing store
+ auto softwareRenderer = static_cast<QSGSoftwareRenderer*>(cd->renderer);
+ if (softwareRenderer)
+ softwareRenderer->setCurrentPaintDevice(m_backingStores[window]->paintDevice());
+
+ m_backingStores[window]->beginPaint(QRect(0, 0, window->width(), window->height()));
+ cd->renderSceneGraph(window->size());
+ m_backingStores[window]->endPaint();
+
+ if (profileFrames)
+ renderTime = renderTimer.nsecsElapsed();
+ Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame);
+
+ if (data.grabOnly) {
+ grabContent = m_backingStores[window]->handle()->toImage();
+ data.grabOnly = false;
+ }
+
+ if (alsoSwap && window->isVisible()) {
+ //Flush backingstore to window
+ m_backingStores[window]->flush(softwareRenderer->flushRegion());
+ cd->fireFrameSwapped();
+ }
+
+ qint64 swapTime = 0;
+ if (profileFrames)
+ swapTime = renderTimer.nsecsElapsed();
+ Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphRenderLoopFrame);
+
+ if (QSG_RASTER_LOG_TIME_RENDERLOOP().isDebugEnabled()) {
+ static QTime lastFrameTime = QTime::currentTime();
+ qCDebug(QSG_RASTER_LOG_TIME_RENDERLOOP,
+ "Frame rendered with 'basic' renderloop in %dms, polish=%d, sync=%d, render=%d, swap=%d, frameDelta=%d",
+ int(swapTime / 1000000),
+ int(polishTime / 1000000),
+ int((syncTime - polishTime) / 1000000),
+ int((renderTime - syncTime) / 1000000),
+ int((swapTime - renderTime) / 10000000),
+ int(lastFrameTime.msecsTo(QTime::currentTime())));
+ lastFrameTime = QTime::currentTime();
+ }
+
+ // Might have been set during syncSceneGraph()
+ if (data.updatePending)
+ maybeUpdate(window);
+}
+
+void QSGSoftwareRenderLoop::exposureChanged(QQuickWindow *window)
+{
+ if (window->isExposed()) {
+ m_windows[window].updatePending = true;
+ renderWindow(window);
+ }
+}
+
+QImage QSGSoftwareRenderLoop::grab(QQuickWindow *window)
+{
+ //If the window was never shown, create a new backing store
+ if (!m_backingStores.contains(window)) {
+ m_backingStores[window] = new QBackingStore(window);
+ // Call create on window to make sure platform window is created
+ window->create();
+ }
+
+ //If there is no WindowData, add one
+ if (!m_windows.contains(window)) {
+ WindowData data;
+ data.updatePending = false;
+ m_windows[window] = data;
+ }
+
+ m_windows[window].grabOnly = true;
+
+ renderWindow(window);
+
+ QImage grabbed = grabContent;
+ grabContent = QImage();
+ return grabbed;
+}
+
+
+
+void QSGSoftwareRenderLoop::maybeUpdate(QQuickWindow *window)
+{
+ if (!m_windows.contains(window))
+ return;
+
+ m_windows[window].updatePending = true;
+ window->requestUpdate();
+}
+
+QSurface::SurfaceType QSGSoftwareRenderLoop::windowSurfaceType() const
+{
+ return QSurface::RasterSurface;
+}
+
+
+
+QSGContext *QSGSoftwareRenderLoop::sceneGraphContext() const
+{
+ return sg;
+}
+
+
+void QSGSoftwareRenderLoop::handleUpdateRequest(QQuickWindow *window)
+{
+ renderWindow(window);
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop_p.h
new file mode 100644
index 0000000000..02dcf4eefa
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop_p.h
@@ -0,0 +1,105 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGSOFTWARERENDERLOOP_H
+#define QSGSOFTWARERENDERLOOP_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qsgrenderloop_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QBackingStore;
+
+class QSGSoftwareRenderLoop : public QSGRenderLoop
+{
+ Q_OBJECT
+public:
+ QSGSoftwareRenderLoop();
+ ~QSGSoftwareRenderLoop();
+
+ void show(QQuickWindow *window) override;
+ void hide(QQuickWindow *window) override;
+
+ void windowDestroyed(QQuickWindow *window) override;
+
+ void renderWindow(QQuickWindow *window);
+ void exposureChanged(QQuickWindow *window) override;
+ QImage grab(QQuickWindow *window) override;
+
+ void maybeUpdate(QQuickWindow *window) override;
+ void update(QQuickWindow *window) override { maybeUpdate(window); } // identical for this implementation.
+ void handleUpdateRequest(QQuickWindow *) override;
+
+ void releaseResources(QQuickWindow *) override { }
+
+ QSurface::SurfaceType windowSurfaceType() const override;
+
+ QAnimationDriver *animationDriver() const override { return 0; }
+
+ QSGContext *sceneGraphContext() const override;
+ QSGRenderContext *createRenderContext(QSGContext *) const override { return rc; }
+
+ struct WindowData {
+ bool updatePending : 1;
+ bool grabOnly : 1;
+ };
+
+ QHash<QQuickWindow *, WindowData> m_windows;
+ QHash<QQuickWindow *, QBackingStore *> m_backingStores;
+
+ QSGContext *sg;
+ QSGRenderContext *rc;
+
+ QImage grabContent;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGSOFTWARERENDERLOOP_H
diff --git a/src/quick/scenegraph/adaptations/software/software.pri b/src/quick/scenegraph/adaptations/software/software.pri
new file mode 100644
index 0000000000..b8cdbc4a25
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/software.pri
@@ -0,0 +1,39 @@
+QT += gui-private core-private qml-private
+
+#DEFINES += QTQUICK2D_DEBUG_FLUSH
+
+SOURCES += \
+ $$PWD/qsgsoftwarecontext.cpp \
+ $$PWD/qsgabstractsoftwarerenderer.cpp \
+ $$PWD/qsgsoftwareglyphnode.cpp \
+ $$PWD/qsgsoftwareimagenode.cpp \
+ $$PWD/qsgsoftwareninepatchnode.cpp \
+ $$PWD/qsgsoftwarepainternode.cpp \
+ $$PWD/qsgsoftwarerectanglenode.cpp \
+ $$PWD/qsgsoftwarepixmaprenderer.cpp \
+ $$PWD/qsgsoftwarepixmaptexture.cpp \
+ $$PWD/qsgsoftwarerenderablenode.cpp \
+ $$PWD/qsgsoftwarerenderablenodeupdater.cpp \
+ $$PWD/qsgsoftwarerenderer.cpp \
+ $$PWD/qsgsoftwarerenderlistbuilder.cpp \
+ $$PWD/qsgsoftwarerenderloop.cpp \
+ $$PWD/qsgsoftwarelayer.cpp \
+ $$PWD/qsgsoftwareadaptation.cpp
+
+HEADERS += \
+ $$PWD/qsgsoftwarecontext_p.h \
+ $$PWD/qsgabstractsoftwarerenderer_p.h \
+ $$PWD/qsgsoftwareglyphnode_p.h \
+ $$PWD/qsgsoftwareimagenode_p.h \
+ $$PWD/qsgsoftwareninepatchnode_p.h \
+ $$PWD/qsgsoftwarepainternode_p.h \
+ $$PWD/qsgsoftwarepixmaprenderer_p.h \
+ $$PWD/qsgsoftwarepixmaptexture_p.h \
+ $$PWD/qsgsoftwarerectanglenode_p.h \
+ $$PWD/qsgsoftwarerenderablenode_p.h \
+ $$PWD/qsgsoftwarerenderablenodeupdater_p.h \
+ $$PWD/qsgsoftwarerenderer_p.h \
+ $$PWD/qsgsoftwarerenderlistbuilder_p.h \
+ $$PWD/qsgsoftwarerenderloop_p.h \
+ $$PWD/qsgsoftwarelayer_p.h \
+ $$PWD/qsgsoftwareadaptation_p.h
diff --git a/src/quick/scenegraph/coreapi/qsgabstractrenderer.h b/src/quick/scenegraph/coreapi/qsgabstractrenderer.h
index d0a22c8c10..eb9e7cea7c 100644
--- a/src/quick/scenegraph/coreapi/qsgabstractrenderer.h
+++ b/src/quick/scenegraph/coreapi/qsgabstractrenderer.h
@@ -42,6 +42,10 @@
#include <QtQuick/qsgnode.h>
+#ifndef GLuint
+#define GLuint uint
+#endif
+
QT_BEGIN_NAMESPACE
class QSGAbstractRendererPrivate;
diff --git a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp
index d91004fbee..5af79747a7 100644
--- a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp
+++ b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp
@@ -154,8 +154,8 @@ ShaderManager::Shader *ShaderManager::prepareMaterial(QSGMaterial *material)
p->bindAttributeLocation(attr[i], i);
}
p->bindAttributeLocation("_qt_order", i);
- context->compile(s, material, qsgShaderRewriter_insertZAttributes(s->vertexShader(), profile), 0);
- context->initialize(s);
+ context->compileShader(s, material, qsgShaderRewriter_insertZAttributes(s->vertexShader(), profile), 0);
+ context->initializeShader(s);
if (!p->isLinked())
return 0;
@@ -188,8 +188,8 @@ ShaderManager::Shader *ShaderManager::prepareMaterialNoRewrite(QSGMaterial *mate
Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphContextFrame);
QSGMaterialShader *s = static_cast<QSGMaterialShader *>(material->createShader());
- context->compile(s, material);
- context->initialize(s);
+ context->compileShader(s, material);
+ context->initializeShader(s);
shader = new Shader();
shader->program = s;
@@ -746,8 +746,9 @@ static int qsg_countNodesInBatches(const QDataBuffer<Batch *> &batches)
return sum;
}
-Renderer::Renderer(QSGRenderContext *ctx)
+Renderer::Renderer(QSGDefaultRenderContext *ctx)
: QSGRenderer(ctx)
+ , m_context(ctx)
, m_opaqueRenderList(64)
, m_alphaRenderList(64)
, m_nextRenderOrder(0)
@@ -807,7 +808,7 @@ Renderer::Renderer(QSGRenderContext *ctx)
// If rendering with an OpenGL Core profile context, we need to create a VAO
// to hold our vertex specification state.
- if (context()->openglContext()->format().profile() == QSurfaceFormat::CoreProfile) {
+ if (m_context->openglContext()->format().profile() == QSurfaceFormat::CoreProfile) {
m_vao = new QOpenGLVertexArrayObject(this);
m_vao->create();
}
@@ -1101,7 +1102,7 @@ void Renderer::nodeWasRemoved(Node *node)
if (m_renderNodeElements.isEmpty()) {
static bool useDepth = qEnvironmentVariableIsEmpty("QSG_NO_DEPTH_BUFFER");
- m_useDepthBuffer = useDepth && context()->openglContext()->format().depthBufferSize() > 0;
+ m_useDepthBuffer = useDepth && m_context->openglContext()->format().depthBufferSize() > 0;
}
}
}
@@ -2760,6 +2761,21 @@ void Renderer::render()
m_vao->release();
}
+struct RenderNodeState : public QSGRenderNode::RenderState
+{
+ const QMatrix4x4 *projectionMatrix() const override { return m_projectionMatrix; }
+ QRect scissorRect() const { return m_scissorRect; }
+ bool scissorEnabled() const { return m_scissorEnabled; }
+ int stencilValue() const { return m_stencilValue; }
+ bool stencilEnabled() const { return m_stencilEnabled; }
+
+ const QMatrix4x4 *m_projectionMatrix;
+ QRect m_scissorRect;
+ bool m_scissorEnabled;
+ int m_stencilValue;
+ bool m_stencilEnabled;
+};
+
void Renderer::renderRenderNode(Batch *batch)
{
if (Q_UNLIKELY(debug_render()))
@@ -2771,24 +2787,25 @@ void Renderer::renderRenderNode(Batch *batch)
setActiveShader(0, 0);
QSGNode *clip = e->renderNode->parent();
- e->renderNode->m_clip_list = 0;
+ QSGRenderNodePrivate *rd = QSGRenderNodePrivate::get(e->renderNode);
+ rd->m_clip_list = 0;
while (clip != rootNode()) {
if (clip->type() == QSGNode::ClipNodeType) {
- e->renderNode->m_clip_list = static_cast<QSGClipNode *>(clip);
+ rd->m_clip_list = static_cast<QSGClipNode *>(clip);
break;
}
clip = clip->parent();
}
- updateClip(e->renderNode->m_clip_list, batch);
+ updateClip(rd->m_clip_list, batch);
- QSGRenderNode::RenderState state;
+ RenderNodeState state;
QMatrix4x4 pm = projectionMatrix();
- state.projectionMatrix = &pm;
- state.scissorEnabled = m_currentClipType & ScissorClip;
- state.stencilEnabled = m_currentClipType & StencilClip;
- state.scissorRect = m_currentScissorRect;
- state.stencilValue = m_currentStencilValue;
+ state.m_projectionMatrix = &pm;
+ state.m_scissorEnabled = m_currentClipType & ScissorClip;
+ state.m_stencilEnabled = m_currentClipType & StencilClip;
+ state.m_scissorRect = m_currentScissorRect;
+ state.m_stencilValue = m_currentStencilValue;
QSGNode *xform = e->renderNode->parent();
QMatrix4x4 matrix;
@@ -2804,13 +2821,13 @@ void Renderer::renderRenderNode(Batch *batch)
}
xform = xform->parent();
}
- e->renderNode->m_matrix = &matrix;
+ rd->m_matrix = &matrix;
QSGNode *opacity = e->renderNode->parent();
- e->renderNode->m_opacity = 1.0;
+ rd->m_opacity = 1.0;
while (opacity != rootNode()) {
if (opacity->type() == QSGNode::OpacityNodeType) {
- e->renderNode->m_opacity = static_cast<QSGOpacityNode *>(opacity)->combinedOpacity();
+ rd->m_opacity = static_cast<QSGOpacityNode *>(opacity)->combinedOpacity();
break;
}
opacity = opacity->parent();
@@ -2822,12 +2839,17 @@ void Renderer::renderRenderNode(Batch *batch)
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
- e->renderNode->render(state);
+ QSGRenderNode::StateFlags changes = e->renderNode->changedStates();
- e->renderNode->m_matrix = 0;
- e->renderNode->m_clip_list = 0;
+ GLuint prevFbo = 0;
+ if (changes & QSGRenderNode::RenderTargetState)
+ glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint *) &prevFbo);
+
+ e->renderNode->render(&state);
+
+ rd->m_matrix = 0;
+ rd->m_clip_list = 0;
- QSGRenderNode::StateFlags changes = e->renderNode->changedStates();
if (changes & QSGRenderNode::ViewportState) {
QRect r = viewportRect();
glViewport(r.x(), deviceRect().bottom() - r.bottom(), r.width(), r.height());
@@ -2861,6 +2883,8 @@ void Renderer::renderRenderNode(Batch *batch)
glDisable(GL_CULL_FACE);
}
+ if (changes & QSGRenderNode::RenderTargetState)
+ glBindFramebuffer(GL_FRAMEBUFFER, prevFbo);
}
class VisualizeShader : public QOpenGLShaderProgram
diff --git a/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h b/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h
index 8bf4a13af6..01e517e65b 100644
--- a/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h
+++ b/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h
@@ -54,13 +54,15 @@
//
#include <private/qsgrenderer_p.h>
+#include <private/qsgdefaultrendercontext_p.h>
#include <private/qsgnodeupdater_p.h>
-#include <private/qdatabuffer_p.h>
-
#include <private/qsgrendernode_p.h>
+#include <private/qdatabuffer_p.h>
#include <QtCore/QBitArray>
+#include <QtGui/QOpenGLFunctions>
+
QT_BEGIN_NAMESPACE
class QOpenGLVertexArrayObject;
@@ -527,7 +529,7 @@ public:
float lastOpacity;
};
- ShaderManager(QSGRenderContext *ctx) : visualizeProgram(0), blitProgram(0), context(ctx) { }
+ ShaderManager(QSGDefaultRenderContext *ctx) : visualizeProgram(0), blitProgram(0), context(ctx) { }
~ShaderManager() {
qDeleteAll(rewrittenShaders);
qDeleteAll(stockShaders);
@@ -547,13 +549,13 @@ private:
QHash<QSGMaterialType *, Shader *> stockShaders;
QOpenGLShaderProgram *blitProgram;
- QSGRenderContext *context;
+ QSGDefaultRenderContext *context;
};
class Q_QUICK_PRIVATE_EXPORT Renderer : public QSGRenderer, public QOpenGLFunctions
{
public:
- Renderer(QSGRenderContext *);
+ Renderer(QSGDefaultRenderContext *);
~Renderer();
enum VisualizeMode {
@@ -637,6 +639,7 @@ private:
void visualizeDrawGeometry(const QSGGeometry *g);
void setCustomRenderMode(const QByteArray &mode) Q_DECL_OVERRIDE;
+ QSGDefaultRenderContext *m_context;
QSet<Node *> m_taggedRoots;
QDataBuffer<Element *> m_opaqueRenderList;
QDataBuffer<Element *> m_alphaRenderList;
diff --git a/src/quick/scenegraph/coreapi/qsggeometry.cpp b/src/quick/scenegraph/coreapi/qsggeometry.cpp
index 5012f6a31b..7ec72a6e10 100644
--- a/src/quick/scenegraph/coreapi/qsggeometry.cpp
+++ b/src/quick/scenegraph/coreapi/qsggeometry.cpp
@@ -39,10 +39,11 @@
#include "qsggeometry.h"
#include "qsggeometry_p.h"
-
-#include <qopenglcontext.h>
-#include <qopenglfunctions.h>
-#include <private/qopenglextensions_p.h>
+#ifndef QT_NO_OPENGL
+# include <qopenglcontext.h>
+# include <qopenglfunctions.h>
+# include <private/qopenglextensions_p.h>
+#endif
#ifdef Q_OS_QNX
#include <malloc.h>
@@ -53,10 +54,21 @@ QT_BEGIN_NAMESPACE
QSGGeometry::Attribute QSGGeometry::Attribute::create(int attributeIndex, int tupleSize, int primitiveType, bool isPrimitive)
{
- Attribute a = { attributeIndex, tupleSize, primitiveType, isPrimitive, 0 };
+ Attribute a = { attributeIndex, tupleSize, primitiveType, isPrimitive, UNKNOWN, 0 };
return a;
}
+QSGGeometry::Attribute QSGGeometry::Attribute::createWithSemantic(int pos, int tupleSize, int type, Semantic semantic)
+{
+ Attribute a;
+ a.position = pos;
+ a.tupleSize = tupleSize;
+ a.type = type;
+ a.isVertexCoordinate = semantic == POSITION;
+ a.semantic = semantic;
+ a.reserved = 0;
+ return a;
+}
/*!
Convenience function which returns attributes to be used for 2D solid
@@ -66,7 +78,7 @@ QSGGeometry::Attribute QSGGeometry::Attribute::create(int attributeIndex, int tu
const QSGGeometry::AttributeSet &QSGGeometry::defaultAttributes_Point2D()
{
static Attribute data[] = {
- QSGGeometry::Attribute::create(0, 2, GL_FLOAT, true)
+ Attribute::createWithSemantic(0, 2, TypeFloat, Attribute::POSITION)
};
static AttributeSet attrs = { 1, sizeof(float) * 2, data };
return attrs;
@@ -79,8 +91,8 @@ const QSGGeometry::AttributeSet &QSGGeometry::defaultAttributes_Point2D()
const QSGGeometry::AttributeSet &QSGGeometry::defaultAttributes_TexturedPoint2D()
{
static Attribute data[] = {
- QSGGeometry::Attribute::create(0, 2, GL_FLOAT, true),
- QSGGeometry::Attribute::create(1, 2, GL_FLOAT)
+ Attribute::createWithSemantic(0, 2, TypeFloat, Attribute::POSITION),
+ Attribute::createWithSemantic(1, 2, TypeFloat, Attribute::TEXCOORD)
};
static AttributeSet attrs = { 2, sizeof(float) * 4, data };
return attrs;
@@ -93,8 +105,8 @@ const QSGGeometry::AttributeSet &QSGGeometry::defaultAttributes_TexturedPoint2D(
const QSGGeometry::AttributeSet &QSGGeometry::defaultAttributes_ColoredPoint2D()
{
static Attribute data[] = {
- QSGGeometry::Attribute::create(0, 2, GL_FLOAT, true),
- QSGGeometry::Attribute::create(1, 4, GL_UNSIGNED_BYTE)
+ Attribute::createWithSemantic(0, 2, TypeFloat, Attribute::POSITION),
+ Attribute::createWithSemantic(1, 4, TypeUnsignedByte, Attribute::COLOR)
};
static AttributeSet attrs = { 2, 2 * sizeof(float) + 4 * sizeof(char), data };
return attrs;
@@ -206,9 +218,9 @@ const QSGGeometry::AttributeSet &QSGGeometry::defaultAttributes_ColoredPoint2D()
The QSGGeometry class stores the geometry of the primitives
rendered with the scene graph. It contains vertex data and
optionally index data. The mode used to draw the geometry is
- specified with setDrawingMode(), which maps directly to the OpenGL
+ specified with setDrawingMode(), which maps directly to the graphics API's
drawing mode, such as \c GL_TRIANGLE_STRIP, \c GL_TRIANGLES, or
- \c GL_POINTS.
+ \c GL_POINTS in case of OpenGL.
Vertices can be as simple as points defined by x and y values or
can be more complex where each vertex contains a normal, texture
@@ -394,7 +406,7 @@ QSGGeometry::QSGGeometry(const QSGGeometry::AttributeSet &attributes,
int vertexCount,
int indexCount,
int indexType)
- : m_drawing_mode(GL_TRIANGLE_STRIP)
+ : m_drawing_mode(DrawTriangleStrip)
, m_vertex_count(0)
, m_index_count(0)
, m_index_type(indexType)
@@ -410,21 +422,20 @@ QSGGeometry::QSGGeometry(const QSGGeometry::AttributeSet &attributes,
Q_UNUSED(m_reserved_bits);
Q_ASSERT(m_attributes.count > 0);
Q_ASSERT(m_attributes.stride > 0);
-
+#ifndef QT_NO_OPENGL
Q_ASSERT_X(indexType != GL_UNSIGNED_INT
|| static_cast<QOpenGLExtensions *>(QOpenGLContext::currentContext()->functions())
->hasOpenGLExtension(QOpenGLExtensions::ElementIndexUint),
"QSGGeometry::QSGGeometry",
"GL_UNSIGNED_INT is not supported, geometry will not render"
);
-
- if (indexType != GL_UNSIGNED_BYTE
- && indexType != GL_UNSIGNED_SHORT
- && indexType != GL_UNSIGNED_INT) {
+#endif
+ if (indexType != TypeUnsignedByte
+ && indexType != TypeUnsignedShort
+ && indexType != TypeUnsignedInt) {
qFatal("QSGGeometry: Unsupported index type, %x.\n", indexType);
}
-
// Because allocate reads m_vertex_count, m_index_count and m_owns_data, these
// need to be set before calling allocate...
allocate(vertexCount, indexCount);
@@ -516,23 +527,63 @@ const void *QSGGeometry::indexData() const
}
/*!
+ \enum QSGGeometry::DrawingMode
+
+ The values correspond to OpenGL enum values like \c GL_POINTS, \c GL_LINES,
+ etc. QSGGeometry provies its own type in order to be able to provide the
+ same API with non-OpenGL backends as well.
+
+ \value DrawPoints
+ \value DrawLines
+ \value DrawLineLoop
+ \value DrawLineStrip
+ \value DrawTriangles
+ \value DrawTriangleStrip
+ \value DrawTriangleFan
+ */
+
+/*!
+ \enum QSGGeometry::Type
+
+ The values correspond to OpenGL type constants like \c GL_BYTE, \c
+ GL_UNSIGNED_BYTE, etc. QSGGeometry provies its own type in order to be able
+ to provide the same API with non-OpenGL backends as well.
+
+ \value TypeByte
+ \value TypeUnsignedByte
+ \value TypeShort
+ \value TypeUnsignedShort
+ \value TypeInt
+ \value TypeUnsignedInt
+ \value TypeFloat
+ */
+
+/*!
Sets the \a mode to be used for drawing this geometry.
- The default value is \c GL_TRIANGLE_STRIP.
+ The default value is QSGGeometry::DrawTriangleStrip.
+
+ \sa DrawingMode
*/
-void QSGGeometry::setDrawingMode(GLenum mode)
+void QSGGeometry::setDrawingMode(unsigned int mode)
{
m_drawing_mode = mode;
}
/*!
- Gets the current line or point width or to be used for this geometry. This property
- only applies to line width when the drawingMode is \c GL_LINES, \c GL_LINE_STRIP, or
- \c GL_LINE_LOOP. For desktop OpenGL, it also applies to point size when the drawingMode
- is \c GL_POINTS.
+ Gets the current line or point width or to be used for this geometry. This
+ property only applies to line width when the drawingMode is DrawLines,
+ DarwLineStrip, or DrawLineLoop. For desktop OpenGL, it also applies to
+ point size when the drawingMode is DrawPoints.
The default value is \c 1.0
+ \note When not using OpenGL, support for point and line drawing may be
+ limited. For example, some APIs do not support point sprites and so setting
+ a size other than 1 is not possible. Some backends may be able implement
+ support via geometry shaders, but this is not guaranteed to be always
+ available.
+
\sa setLineWidth(), drawingMode()
*/
float QSGGeometry::lineWidth() const
@@ -541,14 +592,15 @@ float QSGGeometry::lineWidth() const
}
/*!
- Sets the line or point width to be used for this geometry to \a width. This property
- only applies to line width when the drawingMode is \c GL_LINES, \c GL_LINE_STRIP, or
- \c GL_LINE_LOOP. For Desktop OpenGL, it also applies to point size when the drawingMode
- is \c GL_POINTS.
+ Sets the line or point width to be used for this geometry to \a width. This
+ property only applies to line width when the drawingMode is DrawLines,
+ DrawLineStrip, or DrawLineLoop. For Desktop OpenGL, it also applies to
+ point size when the drawingMode is DrawPoints.
- \note How line width and point size are treated is implementation dependent: The application
- should not rely on these, but rather create triangles or similar to draw areas. On OpenGL ES,
- line width support is limited and point size is unsupported.
+ \note How line width and point size are treated is implementation
+ dependent: The application should not rely on these, but rather create
+ triangles or similar to draw areas. On OpenGL ES, line width support is
+ limited and point size is unsupported.
\sa lineWidth(), drawingMode()
*/
@@ -601,8 +653,8 @@ void QSGGeometry::allocate(int vertexCount, int indexCount)
m_index_data_offset = -1;
m_owns_data = false;
} else {
- Q_ASSERT(m_index_type == GL_UNSIGNED_INT || m_index_type == GL_UNSIGNED_SHORT);
- int indexByteSize = indexCount * (m_index_type == GL_UNSIGNED_SHORT ? sizeof(quint16) : sizeof(quint32));
+ Q_ASSERT(m_index_type == TypeUnsignedInt || m_index_type == TypeUnsignedShort);
+ int indexByteSize = indexCount * (m_index_type == TypeUnsignedShort ? sizeof(quint16) : sizeof(quint32));
m_data = (void *) malloc(vertexByteSize + indexByteSize);
m_index_data_offset = vertexByteSize;
m_owns_data = true;
diff --git a/src/quick/scenegraph/coreapi/qsggeometry.h b/src/quick/scenegraph/coreapi/qsggeometry.h
index 5773b6abd1..1f54b7d81b 100644
--- a/src/quick/scenegraph/coreapi/qsggeometry.h
+++ b/src/quick/scenegraph/coreapi/qsggeometry.h
@@ -54,15 +54,27 @@ public:
struct Q_QUICK_EXPORT Attribute
{
+ enum Semantic {
+ UNKNOWN,
+ POSITION,
+ COLOR,
+ TEXCOORD,
+ TEXCOORD1,
+ TEXCOORD2
+ };
+
int position;
int tupleSize;
int type;
uint isVertexCoordinate : 1;
- uint reserved : 31;
+ Semantic semantic : 4;
+
+ uint reserved : 27;
static Attribute create(int pos, int tupleSize, int primitiveType, bool isPosition = false);
+ static Attribute createWithSemantic(int pos, int tupleSize, int type, Semantic semantic);
};
struct AttributeSet {
@@ -104,14 +116,37 @@ public:
StaticPattern = 3
};
+ // Equivalents to GL_* drawing modes.
+ enum DrawingMode {
+ DrawPoints = 0x0000,
+ DrawLines = 0x0001,
+ DrawLineLoop = 0x0002,
+ DrawLineStrip = 0x0003,
+ DrawTriangles = 0x0004,
+ DrawTriangleStrip = 0x0005,
+ DrawTriangleFan = 0x0006
+ };
+
+ // Equivalents to GL_BYTE and similar type constants.
+ enum Type {
+ TypeByte = 0x1400,
+ TypeUnsignedByte = 0x1401,
+ TypeShort = 0x1402,
+ TypeUnsignedShort = 0x1403,
+ TypeInt = 0x1404,
+ TypeUnsignedInt = 0x1405,
+ TypeFloat = 0x1406
+ };
+
QSGGeometry(const QSGGeometry::AttributeSet &attribs,
int vertexCount,
int indexCount = 0,
- int indexType = GL_UNSIGNED_SHORT);
+ int indexType = TypeUnsignedShort);
virtual ~QSGGeometry();
- void setDrawingMode(GLenum mode);
- inline GLenum drawingMode() const { return m_drawing_mode; }
+ // must use unsigned int to be compatible with the old GLenum to keep BC
+ void setDrawingMode(unsigned int mode);
+ inline unsigned int drawingMode() const { return m_drawing_mode; }
void allocate(int vertexCount, int indexCount = 0);
@@ -187,25 +222,25 @@ private:
inline uint *QSGGeometry::indexDataAsUInt()
{
- Q_ASSERT(m_index_type == GL_UNSIGNED_INT);
+ Q_ASSERT(m_index_type == TypeUnsignedInt);
return static_cast<uint *>(indexData());
}
inline quint16 *QSGGeometry::indexDataAsUShort()
{
- Q_ASSERT(m_index_type == GL_UNSIGNED_SHORT);
+ Q_ASSERT(m_index_type == TypeUnsignedShort);
return static_cast<quint16 *>(indexData());
}
inline const uint *QSGGeometry::indexDataAsUInt() const
{
- Q_ASSERT(m_index_type == GL_UNSIGNED_INT);
+ Q_ASSERT(m_index_type == TypeUnsignedInt);
return static_cast<const uint *>(indexData());
}
inline const quint16 *QSGGeometry::indexDataAsUShort() const
{
- Q_ASSERT(m_index_type == GL_UNSIGNED_SHORT);
+ Q_ASSERT(m_index_type == TypeUnsignedShort);
return static_cast<const quint16 *>(indexData());
}
@@ -214,7 +249,7 @@ inline QSGGeometry::Point2D *QSGGeometry::vertexDataAsPoint2D()
Q_ASSERT(m_attributes.count == 1);
Q_ASSERT(m_attributes.stride == 2 * sizeof(float));
Q_ASSERT(m_attributes.attributes[0].tupleSize == 2);
- Q_ASSERT(m_attributes.attributes[0].type == GL_FLOAT);
+ Q_ASSERT(m_attributes.attributes[0].type == TypeFloat);
Q_ASSERT(m_attributes.attributes[0].position == 0);
return static_cast<Point2D *>(m_data);
}
@@ -225,10 +260,10 @@ inline QSGGeometry::TexturedPoint2D *QSGGeometry::vertexDataAsTexturedPoint2D()
Q_ASSERT(m_attributes.stride == 4 * sizeof(float));
Q_ASSERT(m_attributes.attributes[0].position == 0);
Q_ASSERT(m_attributes.attributes[0].tupleSize == 2);
- Q_ASSERT(m_attributes.attributes[0].type == GL_FLOAT);
+ Q_ASSERT(m_attributes.attributes[0].type == TypeFloat);
Q_ASSERT(m_attributes.attributes[1].position == 1);
Q_ASSERT(m_attributes.attributes[1].tupleSize == 2);
- Q_ASSERT(m_attributes.attributes[1].type == GL_FLOAT);
+ Q_ASSERT(m_attributes.attributes[1].type == TypeFloat);
return static_cast<TexturedPoint2D *>(m_data);
}
@@ -238,10 +273,10 @@ inline QSGGeometry::ColoredPoint2D *QSGGeometry::vertexDataAsColoredPoint2D()
Q_ASSERT(m_attributes.stride == 2 * sizeof(float) + 4 * sizeof(char));
Q_ASSERT(m_attributes.attributes[0].position == 0);
Q_ASSERT(m_attributes.attributes[0].tupleSize == 2);
- Q_ASSERT(m_attributes.attributes[0].type == GL_FLOAT);
+ Q_ASSERT(m_attributes.attributes[0].type == TypeFloat);
Q_ASSERT(m_attributes.attributes[1].position == 1);
Q_ASSERT(m_attributes.attributes[1].tupleSize == 4);
- Q_ASSERT(m_attributes.attributes[1].type == GL_UNSIGNED_BYTE);
+ Q_ASSERT(m_attributes.attributes[1].type == TypeUnsignedByte);
return static_cast<ColoredPoint2D *>(m_data);
}
@@ -250,7 +285,7 @@ inline const QSGGeometry::Point2D *QSGGeometry::vertexDataAsPoint2D() const
Q_ASSERT(m_attributes.count == 1);
Q_ASSERT(m_attributes.stride == 2 * sizeof(float));
Q_ASSERT(m_attributes.attributes[0].tupleSize == 2);
- Q_ASSERT(m_attributes.attributes[0].type == GL_FLOAT);
+ Q_ASSERT(m_attributes.attributes[0].type == TypeFloat);
Q_ASSERT(m_attributes.attributes[0].position == 0);
return static_cast<const Point2D *>(m_data);
}
@@ -261,10 +296,10 @@ inline const QSGGeometry::TexturedPoint2D *QSGGeometry::vertexDataAsTexturedPoin
Q_ASSERT(m_attributes.stride == 4 * sizeof(float));
Q_ASSERT(m_attributes.attributes[0].position == 0);
Q_ASSERT(m_attributes.attributes[0].tupleSize == 2);
- Q_ASSERT(m_attributes.attributes[0].type == GL_FLOAT);
+ Q_ASSERT(m_attributes.attributes[0].type == TypeFloat);
Q_ASSERT(m_attributes.attributes[1].position == 1);
Q_ASSERT(m_attributes.attributes[1].tupleSize == 2);
- Q_ASSERT(m_attributes.attributes[1].type == GL_FLOAT);
+ Q_ASSERT(m_attributes.attributes[1].type == TypeFloat);
return static_cast<const TexturedPoint2D *>(m_data);
}
@@ -274,18 +309,18 @@ inline const QSGGeometry::ColoredPoint2D *QSGGeometry::vertexDataAsColoredPoint2
Q_ASSERT(m_attributes.stride == 2 * sizeof(float) + 4 * sizeof(char));
Q_ASSERT(m_attributes.attributes[0].position == 0);
Q_ASSERT(m_attributes.attributes[0].tupleSize == 2);
- Q_ASSERT(m_attributes.attributes[0].type == GL_FLOAT);
+ Q_ASSERT(m_attributes.attributes[0].type == TypeFloat);
Q_ASSERT(m_attributes.attributes[1].position == 1);
Q_ASSERT(m_attributes.attributes[1].tupleSize == 4);
- Q_ASSERT(m_attributes.attributes[1].type == GL_UNSIGNED_BYTE);
+ Q_ASSERT(m_attributes.attributes[1].type == TypeUnsignedByte);
return static_cast<const ColoredPoint2D *>(m_data);
}
int QSGGeometry::sizeOfIndex() const
{
- if (m_index_type == GL_UNSIGNED_SHORT) return 2;
- else if (m_index_type == GL_UNSIGNED_BYTE) return 1;
- else if (m_index_type == GL_UNSIGNED_INT) return 4;
+ if (m_index_type == TypeUnsignedShort) return 2;
+ else if (m_index_type == TypeUnsignedByte) return 1;
+ else if (m_index_type == TypeUnsignedInt) return 4;
return 0;
}
diff --git a/src/quick/scenegraph/coreapi/qsgmaterial.cpp b/src/quick/scenegraph/coreapi/qsgmaterial.cpp
index ceb53d0d14..42a4c4abd3 100644
--- a/src/quick/scenegraph/coreapi/qsgmaterial.cpp
+++ b/src/quick/scenegraph/coreapi/qsgmaterial.cpp
@@ -40,7 +40,13 @@
#include "qsgmaterial.h"
#include "qsgrenderer_p.h"
#include "qsgmaterialshader_p.h"
-#include <private/qsgshadersourcebuilder_p.h>
+#ifndef QT_NO_OPENGL
+# include <private/qsgshadersourcebuilder_p.h>
+# include <private/qsgdefaultcontext_p.h>
+# include <private/qsgdefaultrendercontext_p.h>
+# include <QtGui/QOpenGLFunctions>
+# include <QtGui/QOpenGLContext>
+#endif
QT_BEGIN_NAMESPACE
@@ -58,7 +64,7 @@ void qsg_set_material_failure()
qsg_material_failure = true;
}
#endif
-
+#ifndef QT_NO_OPENGL
const char *QSGMaterialShaderPrivate::loadShaderSource(QOpenGLShader::ShaderType type) const
{
QStringList files = m_sourceFiles[type];
@@ -68,6 +74,7 @@ const char *QSGMaterialShaderPrivate::loadShaderSource(QOpenGLShader::ShaderType
m_sources[type] = builder.source();
return m_sources[type].constData();
}
+#endif
#ifndef QT_NO_DEBUG
static const bool qsg_leak_check = !qEnvironmentVariableIsEmpty("QML_LEAK_CHECK");
@@ -220,7 +227,7 @@ QSGMaterialShader::~QSGMaterialShader()
defines the attribute register position in the vertex shader.
*/
-
+#ifndef QT_NO_OPENGL
/*!
\fn const char *QSGMaterialShader::vertexShader() const
@@ -256,7 +263,7 @@ const char *QSGMaterialShader::fragmentShader() const
Returns the shader program used by this QSGMaterialShader.
*/
-
+#endif
/*!
\fn void QSGMaterialShader::initialize()
@@ -313,6 +320,7 @@ void QSGMaterialShader::updateState(const RenderState & /* state */, QSGMaterial
{
}
+#ifndef QT_NO_OPENGL
/*!
Sets the GLSL source file for the shader stage \a type to \a sourceFile. The
default implementation of the vertexShader() and fragmentShader() functions
@@ -388,7 +396,7 @@ void QSGMaterialShader::compile()
}
}
-
+#endif
/*!
\class QSGMaterialShader::RenderState
@@ -542,7 +550,7 @@ QRect QSGMaterialShader::RenderState::deviceRect() const
return static_cast<const QSGRenderer *>(m_data)->deviceRect();
}
-
+#ifndef QT_NO_OPENGL
/*!
Returns the QOpenGLContext that is being used for rendering
@@ -550,9 +558,15 @@ QRect QSGMaterialShader::RenderState::deviceRect() const
QOpenGLContext *QSGMaterialShader::RenderState::context() const
{
- return static_cast<const QSGRenderer *>(m_data)->context()->openglContext();
+ // Only the QSGDefaultRenderContext will have an OpenGL Context to query
+ auto openGLRenderContext = static_cast<const QSGDefaultRenderContext *>(static_cast<const QSGRenderer *>(m_data)->context());
+ if (openGLRenderContext != nullptr)
+ return openGLRenderContext->openglContext();
+ else
+ return nullptr;
}
+#endif
#ifndef QT_NO_DEBUG
static int qt_material_count = 0;
diff --git a/src/quick/scenegraph/coreapi/qsgmaterial.h b/src/quick/scenegraph/coreapi/qsgmaterial.h
index 0a6a340092..13d15a089e 100644
--- a/src/quick/scenegraph/coreapi/qsgmaterial.h
+++ b/src/quick/scenegraph/coreapi/qsgmaterial.h
@@ -41,7 +41,11 @@
#define QSGMATERIAL_H
#include <QtQuick/qtquickglobal.h>
-#include <QtGui/qopenglshaderprogram.h>
+#ifndef QT_NO_OPENGL
+# include <QtGui/qopenglshaderprogram.h>
+#endif
+#include <QtGui/QMatrix4x4>
+#include <QtCore/QRect>
QT_BEGIN_NAMESPACE
@@ -77,9 +81,9 @@ public:
QRect deviceRect() const;
float determinant() const;
float devicePixelRatio() const;
-
+#ifndef QT_NO_OPENGL
QOpenGLContext *context() const;
-
+#endif
private:
friend class QSGRenderer;
DirtyStates m_dirty;
@@ -94,27 +98,30 @@ public:
// First time a material is used, oldMaterial is null.
virtual void updateState(const RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial);
virtual char const *const *attributeNames() const = 0; // Array must end with null.
-
+#ifndef QT_NO_OPENGL
inline QOpenGLShaderProgram *program() { return &m_program; }
-
+#endif
protected:
Q_DECLARE_PRIVATE(QSGMaterialShader)
QSGMaterialShader(QSGMaterialShaderPrivate &dd);
- friend class QSGRenderContext;
+ friend class QSGDefaultRenderContext;
friend class QSGBatchRenderer::ShaderManager;
-
+#ifndef QT_NO_OPENGL
void setShaderSourceFile(QOpenGLShader::ShaderType type, const QString &sourceFile);
void setShaderSourceFiles(QOpenGLShader::ShaderType type, const QStringList &sourceFiles);
virtual void compile();
+#endif
virtual void initialize() { }
-
+#ifndef QT_NO_OPENGL
virtual const char *vertexShader() const;
virtual const char *fragmentShader() const;
-
+#endif
private:
+#ifndef QT_NO_OPENGL
QOpenGLShaderProgram m_program;
+#endif
QScopedPointer<QSGMaterialShaderPrivate> d_ptr;
};
diff --git a/src/quick/scenegraph/coreapi/qsgmaterialshader_p.h b/src/quick/scenegraph/coreapi/qsgmaterialshader_p.h
index 291c0cc57c..0dbce010db 100644
--- a/src/quick/scenegraph/coreapi/qsgmaterialshader_p.h
+++ b/src/quick/scenegraph/coreapi/qsgmaterialshader_p.h
@@ -59,10 +59,12 @@ QT_BEGIN_NAMESPACE
class Q_QUICK_PRIVATE_EXPORT QSGMaterialShaderPrivate
{
public:
+#ifndef QT_NO_OPENGL
const char *loadShaderSource(QOpenGLShader::ShaderType type) const;
QHash<QOpenGLShader::ShaderType, QStringList> m_sourceFiles;
mutable QHash<QOpenGLShader::ShaderType, QByteArray> m_sources;
+#endif
};
#ifndef QT_NO_DEBUG
diff --git a/src/quick/scenegraph/coreapi/qsgnode.cpp b/src/quick/scenegraph/coreapi/qsgnode.cpp
index 223c555f3e..02c0414bc4 100644
--- a/src/quick/scenegraph/coreapi/qsgnode.cpp
+++ b/src/quick/scenegraph/coreapi/qsgnode.cpp
@@ -1472,15 +1472,15 @@ QDebug operator<<(QDebug d, const QSGGeometryNode *n)
} else {
switch (g->drawingMode()) {
- case GL_TRIANGLE_STRIP: d << "strip"; break;
- case GL_TRIANGLE_FAN: d << "fan"; break;
- case GL_TRIANGLES: d << "triangles"; break;
+ case QSGGeometry::DrawTriangleStrip: d << "strip"; break;
+ case QSGGeometry::DrawTriangleFan: d << "fan"; break;
+ case QSGGeometry::DrawTriangles: d << "triangles"; break;
default: break;
}
d << "#V:" << g->vertexCount() << "#I:" << g->indexCount();
- if (g->attributeCount() > 0 && g->attributes()->type == GL_FLOAT) {
+ if (g->attributeCount() > 0 && g->attributes()->type == QSGGeometry::TypeFloat) {
float x1 = 1e10, x2 = -1e10, y1=1e10, y2=-1e10;
int stride = g->sizeOfVertex();
for (int i = 0; i < g->vertexCount(); ++i) {
diff --git a/src/quick/scenegraph/coreapi/qsgnode.h b/src/quick/scenegraph/coreapi/qsgnode.h
index 9d14ae04df..627fe6e27f 100644
--- a/src/quick/scenegraph/coreapi/qsgnode.h
+++ b/src/quick/scenegraph/coreapi/qsgnode.h
@@ -79,8 +79,8 @@ public:
OpacityNodeType,
#ifndef qdoc
RootNodeType,
- RenderNodeType
#endif
+ RenderNodeType
};
enum Flag {
@@ -204,6 +204,9 @@ public:
const QMatrix4x4 *matrix() const { return m_matrix; }
const QSGClipNode *clipList() const { return m_clip_list; }
+ void setMatrix(const QMatrix4x4 *m) { m_matrix = m; }
+ void setClipList(const QSGClipNode *c) { m_clip_list = c; }
+
protected:
QSGBasicGeometryNode(NodeType type);
QSGBasicGeometryNode(QSGBasicGeometryNodePrivate &dd, NodeType type);
diff --git a/src/quick/scenegraph/coreapi/qsgnodeupdater.cpp b/src/quick/scenegraph/coreapi/qsgnodeupdater.cpp
index 372ffce9d6..d6d533307e 100644
--- a/src/quick/scenegraph/coreapi/qsgnodeupdater.cpp
+++ b/src/quick/scenegraph/coreapi/qsgnodeupdater.cpp
@@ -175,9 +175,10 @@ void QSGNodeUpdater::enterRenderNode(QSGRenderNode *r)
qDebug() << "enter render:" << r;
#endif
- r->m_matrix = m_combined_matrix_stack.isEmpty() ? 0 : m_combined_matrix_stack.last();
- r->m_clip_list = m_current_clip;
- r->setInheritedOpacity(m_opacity_stack.last());
+ QSGRenderNodePrivate *rd = QSGRenderNodePrivate::get(r);
+ rd->m_matrix = m_combined_matrix_stack.isEmpty() ? 0 : m_combined_matrix_stack.last();
+ rd->m_clip_list = m_current_clip;
+ rd->m_opacity = m_opacity_stack.last();
}
void QSGNodeUpdater::leaveRenderNode(QSGRenderNode *r)
diff --git a/src/quick/scenegraph/coreapi/qsgrenderer.cpp b/src/quick/scenegraph/coreapi/qsgrenderer.cpp
index ace10661c0..220e6ab212 100644
--- a/src/quick/scenegraph/coreapi/qsgrenderer.cpp
+++ b/src/quick/scenegraph/coreapi/qsgrenderer.cpp
@@ -39,11 +39,15 @@
#include "qsgrenderer_p.h"
#include "qsgnodeupdater_p.h"
-
-#include <qopenglframebufferobject.h>
-
+#ifndef QT_NO_OPENGL
+# include <QtGui/QOpenGLFramebufferObject>
+# include <QtGui/QOpenGLContext>
+# include <QtGui/QOpenGLFunctions>
+#endif
#include <private/qquickprofiler_p.h>
+#include <QtCore/QElapsedTimer>
+
QT_BEGIN_NAMESPACE
static const bool qsg_sanity_check = qEnvironmentVariableIntValue("QSG_SANITY_CHECK");
@@ -63,19 +67,25 @@ int qt_sg_envInt(const char *name, int defaultValue)
void QSGBindable::clear(QSGAbstractRenderer::ClearMode mode) const
{
+#ifndef QT_NO_OPENGL
GLuint bits = 0;
if (mode & QSGAbstractRenderer::ClearColorBuffer) bits |= GL_COLOR_BUFFER_BIT;
if (mode & QSGAbstractRenderer::ClearDepthBuffer) bits |= GL_DEPTH_BUFFER_BIT;
if (mode & QSGAbstractRenderer::ClearStencilBuffer) bits |= GL_STENCIL_BUFFER_BIT;
QOpenGLContext::currentContext()->functions()->glClear(bits);
+#else
+ Q_UNUSED(mode)
+#endif
}
// Reactivate the color buffer after switching to the stencil.
void QSGBindable::reactivate() const
{
+#ifndef QT_NO_OPENGL
QOpenGLContext::currentContext()->functions()->glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+#endif
}
-
+#ifndef QT_NO_OPENGL
QSGBindableFboId::QSGBindableFboId(GLuint id)
: m_id(id)
{
@@ -86,7 +96,7 @@ void QSGBindableFboId::bind() const
{
QOpenGLContext::currentContext()->functions()->glBindFramebuffer(GL_FRAMEBUFFER, m_id);
}
-
+#endif
/*!
\class QSGRenderer
\brief The renderer class is the abstract baseclass use for rendering the
@@ -169,8 +179,9 @@ bool QSGRenderer::isMirrored() const
return matrix(0, 0) * matrix(1, 1) - matrix(0, 1) * matrix(1, 0) > 0;
}
-void QSGRenderer::renderScene(GLuint fboId)
+void QSGRenderer::renderScene(uint fboId)
{
+#ifndef QT_NO_OPENGL
if (fboId) {
QSGBindableFboId bindable(fboId);
renderScene(bindable);
@@ -182,7 +193,11 @@ void QSGRenderer::renderScene(GLuint fboId)
} bindable;
renderScene(bindable);
}
+#else
+ Q_UNUSED(fboId)
+#endif
}
+
void QSGRenderer::renderScene(const QSGBindable &bindable)
{
if (!rootNode())
@@ -207,6 +222,7 @@ void QSGRenderer::renderScene(const QSGBindable &bindable)
bindTime = frameTimer.nsecsElapsed();
Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRendererFrame);
+#ifndef QT_NO_OPENGL
// Sanity check that attribute registers are disabled
if (qsg_sanity_check) {
GLint count = 0;
@@ -219,6 +235,7 @@ void QSGRenderer::renderScene(const QSGBindable &bindable)
}
}
}
+#endif
render();
if (profileFrames)
diff --git a/src/quick/scenegraph/coreapi/qsgrenderer_p.h b/src/quick/scenegraph/coreapi/qsgrenderer_p.h
index 5c7a32c161..94b78a85b4 100644
--- a/src/quick/scenegraph/coreapi/qsgrenderer_p.h
+++ b/src/quick/scenegraph/coreapi/qsgrenderer_p.h
@@ -86,14 +86,12 @@ public:
bool isMirrored() const;
void renderScene(const QSGBindable &bindable);
- virtual void renderScene(GLuint fboId = 0) Q_DECL_OVERRIDE;
+ virtual void renderScene(uint fboId = 0) Q_DECL_OVERRIDE;
virtual void nodeChanged(QSGNode *node, QSGNode::DirtyState state) Q_DECL_OVERRIDE;
QSGNodeUpdater *nodeUpdater() const;
void setNodeUpdater(QSGNodeUpdater *updater);
-
inline QSGMaterialShader::RenderState state(QSGMaterialShader::RenderState::DirtyStates dirty) const;
-
virtual void setCustomRenderMode(const QByteArray &) { };
void clearChangedFlag() { m_changed_emitted = false; }
@@ -135,7 +133,7 @@ public:
virtual void clear(QSGAbstractRenderer::ClearMode mode) const;
virtual void reactivate() const;
};
-
+#ifndef QT_NO_OPENGL
class QSGBindableFboId : public QSGBindable
{
public:
@@ -144,7 +142,7 @@ public:
private:
GLuint m_id;
};
-
+#endif
QSGMaterialShader::RenderState QSGRenderer::state(QSGMaterialShader::RenderState::DirtyStates dirty) const
diff --git a/src/quick/scenegraph/coreapi/qsgrendererinterface.cpp b/src/quick/scenegraph/coreapi/qsgrendererinterface.cpp
new file mode 100644
index 0000000000..b680dbe3d5
--- /dev/null
+++ b/src/quick/scenegraph/coreapi/qsgrendererinterface.cpp
@@ -0,0 +1,128 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgrendererinterface.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QSGRendererInterface
+ \brief An interface providing access to some of the graphics API specific internals
+ of the scenegraph.
+ \inmodule QtQuick
+ \since 5.8
+
+ Renderer interfaces allow accessing graphics API specific functionality in
+ the scenegraph. Such internals are not typically exposed. However, when
+ integrating custom rendering via QSGRenderNode for example, it may become
+ necessary to query certain values, for instance the graphics device (e.g.
+ the Direct3D or Vulkan device) that is used by the scenegraph.
+
+ \note QSGRendererInterface is only available after the scenegraph is
+ initialized. Additionally, there may be backend-specific limitations on
+ when the functions can be called. The only way that is guaranteed to
+ succeed is calling them when the rendering of a node (i.e. the preparation
+ of the command list for the next frame) is active. In practice this
+ typically means QSGRenderNode::render().
+ */
+
+/*!
+ \enum QSGRenderNode::GraphicsAPI
+ \value Unknown An unknown graphics API is in use
+ \value Software The Qt Quick 2D Renderer is in use
+ \value OpenGL OpenGL ES 2.0 or higher
+ \value Direct3D12 Direct3D 12
+ \value Vulkan Vulkan
+ \value Metal Metal
+ */
+
+/*!
+ \enum QSGRenderNode::Resource
+ \value Device The graphics device
+ \value CommandQueue The graphics command queue used by the scenergaph
+ \value CommandList The command list or buffer used by the scenegraph
+ */
+
+QSGRendererInterface::~QSGRendererInterface()
+{
+}
+
+/*!
+ \fn QSGRenderNode::GraphicsAPI QSGRenderNode::graphicsAPI() const
+
+ Returns the graphics API that is in use by the Qt Quick scenegraph.
+
+ \note This function can be called on any thread. However, the renderer
+ interface's lifetime may be tied to the render thread and therefore calling
+ this function from other threads during the process of application shutdown
+ or QQuickWindow closing is likely to become invalid.
+ */
+
+/*!
+ Queries a graphics \a resource. Returns null when the resource in question is
+ not supported or not available.
+
+ When successful, the returned pointer is either a direct pointer to an
+ interface (and can be cast, for example, to \c{ID3D12Device *}) or a
+ pointer to an opaque handle that needs to be dereferenced first (for
+ example, \c{VkDevice dev = *static_cast<VkDevice *>(result)}). The latter
+ is necessary since such handles may have sizes different from a pointer.
+
+ \note This function must only be called on the render thread.
+ */
+void *QSGRendererInterface::getResource(Resource resource) const
+{
+ Q_UNUSED(resource);
+ return nullptr;
+}
+
+/*!
+ Queries a graphics resource. \a resource is a backend-specific key. This
+ allows supporting any future resources that are not listed in the
+ Resource enum.
+
+ \note This function must only be called on the render thread.
+ */
+void *QSGRendererInterface::getResource(const char *resource) const
+{
+ Q_UNUSED(resource);
+ return nullptr;
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/coreapi/qsgrendererinterface.h b/src/quick/scenegraph/coreapi/qsgrendererinterface.h
new file mode 100644
index 0000000000..a34b4b3c26
--- /dev/null
+++ b/src/quick/scenegraph/coreapi/qsgrendererinterface.h
@@ -0,0 +1,73 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGRENDERERINTERFACE_H
+#define QSGRENDERERINTERFACE_H
+
+#include <QtQuick/qsgnode.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_QUICK_EXPORT QSGRendererInterface
+{
+public:
+ enum GraphicsAPI {
+ Unknown,
+ Software,
+ OpenGL,
+ Direct3D12
+ };
+
+ enum Resource {
+ Device,
+ CommandQueue,
+ CommandList
+ };
+
+ virtual ~QSGRendererInterface();
+
+ virtual GraphicsAPI graphicsAPI() const = 0;
+
+ virtual void *getResource(Resource resource) const;
+ virtual void *getResource(const char *resource) const;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/quick/scenegraph/coreapi/qsgrendernode.cpp b/src/quick/scenegraph/coreapi/qsgrendernode.cpp
index 1a38f6495e..6c61b35aa1 100644
--- a/src/quick/scenegraph/coreapi/qsgrendernode.cpp
+++ b/src/quick/scenegraph/coreapi/qsgrendernode.cpp
@@ -37,28 +37,51 @@
**
****************************************************************************/
+#include "qsgrendernode.h"
#include "qsgrendernode_p.h"
QT_BEGIN_NAMESPACE
+/*!
+ \class QSGRenderNode
+ \brief The QSGMaterialShader class represents a set of custom rendering commands
+ targeting the graphics API that is in use by the scenegraph.
+ \inmodule QtQuick
+ \since 5.8
+ */
+
QSGRenderNode::QSGRenderNode()
- : QSGNode(RenderNodeType)
- , m_matrix(0)
- , m_clip_list(0)
- , m_opacity(1)
+ : QSGNode(RenderNodeType),
+ d(new QSGRenderNodePrivate)
{
}
-void QSGRenderNode::setInheritedOpacity(qreal opacity)
+/*!
+ Destructs the render node. Derived classes are expected to perform cleanup
+ similar to releaseResources() in here.
+
+ When a low-level graphics API is in use, the scenegraph will make sure
+ there is a CPU-side wait for the GPU to complete all work submitted to the
+ scenegraph's graphics command queue before the scenegraph's nodes are
+ deleted. Therefore there is no need to issue additional waits here, unless
+ the render() implementation is using additional command queues.
+
+ \sa releaseResources()
+ */
+QSGRenderNode::~QSGRenderNode()
{
- Q_ASSERT(opacity >= 0 && opacity <= 1);
- m_opacity = opacity;
+ delete d;
}
-/*!
- \fn QSGRenderNode::StateFlags QSGRenderNode::changedStates()
+QSGRenderNodePrivate::QSGRenderNodePrivate()
+ : m_matrix(0)
+ , m_clip_list(0)
+ , m_opacity(1)
+{
+}
- This function should return a mask where each bit represents OpenGL states changed by
+/*!
+ This function should return a mask where each bit represents graphics states changed by
the \l render() function:
\list
\li DepthState - depth write mask, depth test enabled, depth comparison function
@@ -69,30 +92,72 @@ void QSGRenderNode::setInheritedOpacity(qreal opacity)
\li BlendState - blend enabled, blend function
\li CullState - front face, cull face enabled
\li ViewportState - viewport
+ \li RenderTargetState - render target
\endlist
- The function is called by the renderer so it can reset the OpenGL states after rendering this
- node.
+ The function is called by the renderer so it can reset the states after
+ rendering this node. This makes the implementation of render() simpler
+ since it does not have to query and restore these states.
- \internal
+ The default implementation returns 0, meaning no relevant state was changed
+ in render().
+
+ With APIs other than OpenGL the relevant states are only those that are set
+ via the command list (for example, OMSetRenderTargets, RSSetViewports,
+ RSSetScissorRects, OMSetBlendFactor, OMSetStencilRef in case of D3D12), and
+ only when such commands were added to the scenegraph's command list queried
+ via the QSGRendererInterface::CommandList resource enum. States set in
+ pipeline state objects do not need to be reported here. Similarly, draw
+ call related settings (root signature, descriptor heaps, etc.) are always
+ set again by the scenegraph so render() can freely change them.
+
+ \note This function may be called before render().
*/
+QSGRenderNode::StateFlags QSGRenderNode::changedStates() const
+{
+ return 0;
+}
/*!
- \fn void QSGRenderNode::render(const RenderState &state)
+ \fn void QSGRenderNode::render(const RenderState *state)
- This function is called by the renderer and should paint this node with OpenGL commands.
+ This function is called by the renderer and should paint this node with
+ directly invoking commands in the graphics API (OpenGL, Direct3D, etc.)
+ currently in use.
- The states necessary for clipping has already been set before the function is called.
- The clip is a combination of a stencil clip and scissor clip. Information about the clip is
- found in \a state.
+ The states necessary for clipping has already been set before the function
+ is called. The clip is a combination of a stencil clip and scissor clip.
+ Information about the clip is found in \a state.
+
+ \note This means that setting viewport, scissor rectangle, stencil
+ reference value, and similar is not necessary in render() since the
+ corresponding commands are on the command list (or, in case of OpenGL, the
+ context) already. However, for APIs other than OpenGL stencil-based
+ clipping will need enabling stencil testing in the pipeline state that is
+ used by render().
The effective opacity can be retrieved with \l inheritedOpacity().
- The projection matrix is available through \a state, while the model-view matrix can be
- fetched with \l matrix(). The combined matrix is then the projection matrix times the
- model-view matrix.
+ The projection matrix is available through \a state, while the model-view
+ matrix can be fetched with \l matrix(). The combined matrix is then the
+ projection matrix times the model-view matrix. The correct stacking of the
+ items in the scene is ensured by the projection matrix.
+
+ When using the provided matrices, the coordinate system for vertex data
+ follows the usual QQuickItem conventions: top-left is (0, 0), bottom-right
+ is the corresponding QQuickItem's width() and height() minus one. For
+ example, assuming a two float (x-y) per vertex coordinate layout, a
+ triangle covering half of the item can be specified as (width - 1, height - 1),
+ (0, 0), (0, height - 1) using counter-clockwise direction.
+
+ \note QSGRenderNode is provided as a means to implement custom 2D or 2.5D
+ Qt Quick items. It is not intended for integrating true 3D content into the
+ Qt Quick scene. That use case is better supported by
+ QQuickFramebufferObject, QQuickWindow::beforeRendering(), or the
+ equivalents of those for APIs other than OpenGL.
- The following states are set before this function is called:
+ For OpenGL the following states are set on the render thread's context
+ before this function is called:
\list
\li glDepthMask(false)
\li glDisable(GL_DEPTH_TEST)
@@ -107,14 +172,129 @@ void QSGRenderNode::setInheritedOpacity(qreal opacity)
\li glDisable(GL_CULL_FACE)
\endlist
- States that are not listed above, but are included in \l StateFlags, can have arbitrary
- values.
+ States that are not listed above, but are included in \l StateFlags, can
+ have arbitrary values.
- \l changedStates() should return which states this function has changed. If a state is not
- covered by \l StateFlags, the state should be set to the default value according to the
- OpenGL specification.
+ \l changedStates() should return which states this function changes. If a
+ state is not covered by \l StateFlags, the state should be set to the
+ default value according to the OpenGL specification. For other APIs, see
+ the documentation for changedStates() for more information.
- \internal
+ For APIs other than OpenGL, it will likely be necessary to query certain
+ API-specific resources (for example, the graphics device or the command
+ list/buffer to add the commands to). This is done via QSGRendererInterface.
+
+ \sa QSGRendererInterface, QQuickWindow::rendererInterface()
*/
+/*!
+ This function is called when all custom graphics resources allocated by
+ this node have to be freed immediately. In case the node does not directly
+ allocate graphics resources (buffers, textures, render targets, fences,
+ etc.) through the graphics API that is in use, there is nothing to do here.
+
+ Failing to release all custom resources can lead to incorrect behavior in
+ graphics device loss scenarios on some systems since subsequent
+ reinitialization of the graphics system may fail.
+
+ \note Some scenegraph backends may choose not to call this function.
+ Therefore it is expected that QSGRenderNode implementations perform cleanup
+ both in their destructor and in releaseResources().
+
+ Unlike with the destructor, it is expected that render() can reinitialize
+ all resources it needs when called after a call to releaseResources().
+
+ With OpenGL, the scenegraph's OpenGL context will be current both when
+ calling the destructor and this function.
+ */
+void QSGRenderNode::releaseResources()
+{
+}
+
+/*!
+ \return pointer to the current model-view matrix.
+ */
+const QMatrix4x4 *QSGRenderNode::matrix() const
+{
+ return d->m_matrix;
+}
+
+/*!
+ \return the current clip list.
+ */
+const QSGClipNode *QSGRenderNode::clipList() const
+{
+ return d->m_clip_list;
+}
+
+/*!
+ \return the current effective opacity.
+ */
+qreal QSGRenderNode::inheritedOpacity() const
+{
+ return d->m_opacity;
+}
+
+QSGRenderNode::RenderState::~RenderState()
+{
+}
+
+/*!
+ \fn const QMatrix4x4 *QSGRenderNode::RenderState::projectionMatrix() const
+
+ \return pointer to the current projection matrix.
+
+ The model-view matrix can be retrieved with QSGRenderNode::matrix().
+ Typically \c{projection * modelview} is the matrix that is then used in the
+ vertex shader to transform the vertices.
+ */
+
+/*!
+ \fn const QMatrix4x4 *QSGRenderNode::RenderState::scissorRect() const
+
+ \return the current scissor rectangle when clipping is active.
+
+ \note Be aware of the differences between graphics APIs: for some the
+ scissor rect is only active when scissoring is enabled (for example,
+ OpenGL), while for others the scissor rect is equal to the viewport rect
+ when there is no need to scissor away anything (for example, Direct3D 12).
+ */
+
+/*!
+ \fn const QMatrix4x4 *QSGRenderNode::RenderState::scissorEnabled() const
+
+ \return the current state of scissoring.
+
+ \note Only relevant for graphics APIs that have a dedicated on/off state of
+ scissoring.
+ */
+
+/*!
+ \fn const QMatrix4x4 *QSGRenderNode::RenderState::stencilValue() const
+
+ \return the current stencil reference value when clipping is active.
+ */
+
+/*!
+ \fn const QMatrix4x4 *QSGRenderNode::RenderState::stencilEnabled() const
+
+ \return the current state of stencil testing.
+
+ \note With graphics APIs where stencil testing is enabled in pipeline state
+ objects, instead of individual state-setting commands, it is up to the
+ implementation of render() to enable stencil testing with operations
+ \c KEEP, comparison function \c EQUAL, and a read and write mask of \c 0xFF.
+ */
+
+/*!
+ \return pointer to a \a state value.
+
+ Reserved for future use.
+ */
+void *QSGRenderNode::RenderState::get(const char *state) const
+{
+ Q_UNUSED(state);
+ return nullptr;
+}
+
QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/coreapi/qsgrendernode.h b/src/quick/scenegraph/coreapi/qsgrendernode.h
new file mode 100644
index 0000000000..17569f8c59
--- /dev/null
+++ b/src/quick/scenegraph/coreapi/qsgrendernode.h
@@ -0,0 +1,94 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGRENDERNODE_H
+#define QSGRENDERNODE_H
+
+#include <QtQuick/qsgnode.h>
+
+QT_BEGIN_NAMESPACE
+
+class QSGRenderNodePrivate;
+
+class Q_QUICK_EXPORT QSGRenderNode : public QSGNode
+{
+public:
+ enum StateFlag {
+ DepthState = 0x01,
+ StencilState = 0x02,
+ ScissorState = 0x04,
+ ColorState = 0x08,
+ BlendState = 0x10,
+ CullState = 0x20,
+ ViewportState = 0x40,
+ RenderTargetState = 0x80
+ };
+ Q_DECLARE_FLAGS(StateFlags, StateFlag)
+
+ struct Q_QUICK_EXPORT RenderState {
+ virtual ~RenderState();
+ virtual const QMatrix4x4 *projectionMatrix() const = 0;
+ virtual QRect scissorRect() const = 0;
+ virtual bool scissorEnabled() const = 0;
+ virtual int stencilValue() const = 0;
+ virtual bool stencilEnabled() const = 0;
+ virtual void *get(const char *state) const;
+ };
+
+ QSGRenderNode();
+ ~QSGRenderNode();
+
+ virtual StateFlags changedStates() const;
+ virtual void render(const RenderState *state) = 0;
+ virtual void releaseResources();
+
+ const QMatrix4x4 *matrix() const;
+ const QSGClipNode *clipList() const;
+ qreal inheritedOpacity() const;
+
+private:
+ QSGRenderNodePrivate *d;
+ friend class QSGRenderNodePrivate;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QSGRenderNode::StateFlags)
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/quick/scenegraph/coreapi/qsgrendernode_p.h b/src/quick/scenegraph/coreapi/qsgrendernode_p.h
index 8659b0e62c..5c42e55689 100644
--- a/src/quick/scenegraph/coreapi/qsgrendernode_p.h
+++ b/src/quick/scenegraph/coreapi/qsgrendernode_p.h
@@ -51,64 +51,23 @@
// We mean it.
//
-#include "qsgnode.h"
-#include <private/qtquickglobal_p.h>
+#include <QtQuick/qsgnode.h>
+#include <QtQuick/qsgrendernode.h>
QT_BEGIN_NAMESPACE
-namespace QSGBatchRenderer {
- class Renderer;
-}
-
-class Q_QUICK_PRIVATE_EXPORT QSGRenderNode : public QSGNode
+class QSGRenderNodePrivate
{
public:
- enum StateFlag
- {
- DepthState = 0x01, // depth mask, depth test enable, depth func, clear depth
- StencilState = 0x02, // stencil mask, stencil test enable, stencil op, stencil func, clear stencil
- ScissorState = 0x04, // scissor enable, scissor test enable
- ColorState = 0x08, // clear color, color mask
- BlendState = 0x10, // blend enable, blend func
- CullState = 0x20, // front face, cull face enable
- ViewportState = 0x40 // viewport
- };
- Q_DECLARE_FLAGS(StateFlags, StateFlag)
-
- struct RenderState
- {
- // The model-view matrix can be retrieved with matrix().
- // The opacity can be retrieved with inheritedOpacity().
- const QMatrix4x4 *projectionMatrix;
- QRect scissorRect;
- int stencilValue;
-
- bool stencilEnabled;
- bool scissorEnabled;
- };
-
- QSGRenderNode();
+ QSGRenderNodePrivate();
- virtual StateFlags changedStates() = 0;
- virtual void render(const RenderState &state) = 0;
-
- const QMatrix4x4 *matrix() const { return m_matrix; }
- const QSGClipNode *clipList() const { return m_clip_list; }
-
- void setInheritedOpacity(qreal opacity);
- qreal inheritedOpacity() const { return m_opacity; }
-
-private:
- friend class QSGNodeUpdater;
- friend class QSGBatchRenderer::Renderer;
+ static QSGRenderNodePrivate *get(QSGRenderNode *node) { return node->d; }
const QMatrix4x4 *m_matrix;
const QSGClipNode *m_clip_list;
qreal m_opacity;
};
-Q_DECLARE_OPERATORS_FOR_FLAGS(QSGRenderNode::StateFlags)
-
QT_END_NAMESPACE
#endif
diff --git a/src/quick/scenegraph/qsgadaptationlayer.cpp b/src/quick/scenegraph/qsgadaptationlayer.cpp
index eb6a846a81..50986e2528 100644
--- a/src/quick/scenegraph/qsgadaptationlayer.cpp
+++ b/src/quick/scenegraph/qsgadaptationlayer.cpp
@@ -72,8 +72,11 @@ QSGDistanceFieldGlyphCache::QSGDistanceFieldGlyphCache(QSGDistanceFieldGlyphCach
// this allows us to call pathForGlyph once and reuse the result.
m_referenceFont.setPixelSize(QT_DISTANCEFIELD_BASEFONTSIZE(m_doubleGlyphResolution) * QT_DISTANCEFIELD_SCALE(m_doubleGlyphResolution));
Q_ASSERT(m_referenceFont.isValid());
-
+#ifndef QT_NO_OPENGL
m_coreProfile = (c->format().profile() == QSurfaceFormat::CoreProfile);
+#else
+ Q_UNUSED(c)
+#endif
}
QSGDistanceFieldGlyphCache::~QSGDistanceFieldGlyphCache()
@@ -291,7 +294,7 @@ void QSGDistanceFieldGlyphCache::markGlyphsToRender(const QVector<glyph_t> &glyp
m_pendingGlyphs.add(glyphs.at(i));
}
-void QSGDistanceFieldGlyphCache::updateTexture(GLuint oldTex, GLuint newTex, const QSize &newTexSize)
+void QSGDistanceFieldGlyphCache::updateTexture(uint oldTex, uint newTex, const QSize &newTexSize)
{
int count = m_textures.count();
for (int i = 0; i < count; ++i) {
@@ -516,4 +519,43 @@ void QSGNodeVisitorEx::visitChildren(QSGNode *node)
}
}
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug debug, const QSGGuiThreadShaderEffectManager::ShaderInfo::InputParameter &p)
+{
+ QDebugStateSaver saver(debug);
+ debug.space();
+ debug << p.semanticName << "semindex" << p.semanticIndex;
+ return debug;
+}
+
+QDebug operator<<(QDebug debug, const QSGGuiThreadShaderEffectManager::ShaderInfo::Variable &v)
+{
+ QDebugStateSaver saver(debug);
+ debug.space();
+ debug << v.name;
+ switch (v.type) {
+ case QSGGuiThreadShaderEffectManager::ShaderInfo::Constant:
+ debug << "cvar" << "offset" << v.offset << "size" << v.size;
+ break;
+ case QSGGuiThreadShaderEffectManager::ShaderInfo::Sampler:
+ debug << "sampler" << "bindpoint" << v.bindPoint;
+ break;
+ case QSGGuiThreadShaderEffectManager::ShaderInfo::Texture:
+ debug << "texture" << "bindpoint" << v.bindPoint;
+ break;
+ default:
+ break;
+ }
+ return debug;
+}
+
+QDebug operator<<(QDebug debug, const QSGShaderEffectNode::VariableData &vd)
+{
+ QDebugStateSaver saver(debug);
+ debug.space();
+ debug << vd.specialType;
+ return debug;
+}
+#endif
+
QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/qsgadaptationlayer_p.h b/src/quick/scenegraph/qsgadaptationlayer_p.h
index 8579b0a57b..0df42c2aa8 100644
--- a/src/quick/scenegraph/qsgadaptationlayer_p.h
+++ b/src/quick/scenegraph/qsgadaptationlayer_p.h
@@ -210,7 +210,7 @@ public:
virtual QImage toImage() const = 0;
virtual void setLive(bool live) = 0;
virtual void setRecursive(bool recursive) = 0;
- virtual void setFormat(GLenum format) = 0;
+ virtual void setFormat(uint format) = 0;
virtual void setHasMipmaps(bool mipmap) = 0;
virtual void setDevicePixelRatio(qreal ratio) = 0;
virtual void setMirrorHorizontal(bool mirror) = 0;
@@ -223,6 +223,149 @@ Q_SIGNALS:
void scheduledUpdateCompleted();
};
+class Q_QUICK_PRIVATE_EXPORT QSGGuiThreadShaderEffectManager : public QObject
+{
+ Q_OBJECT
+
+public:
+ // Enum values must match ShaderEffect.
+ enum ShaderType {
+ UnknownShadingLanguage,
+ GLSL,
+ HLSL
+ };
+ enum ShaderCompilationType {
+ RuntimeCompilation = 0x01,
+ OfflineCompilation = 0x02
+ };
+ enum ShaderSourceType {
+ ShaderSourceString = 0x01,
+ ShaderSourceFile = 0x02,
+ ShaderByteCode = 0x04
+ };
+ enum Status {
+ Compiled,
+ Uncompiled,
+ Error
+ };
+
+ virtual ShaderType shaderType() const = 0;
+ virtual int shaderCompilationType() const = 0;
+ virtual int shaderSourceType() const = 0;
+
+ virtual bool hasSeparateSamplerAndTextureObjects() const = 0;
+
+ virtual QString log() const = 0;
+ virtual Status status() const = 0;
+
+ struct ShaderInfo {
+ enum Type {
+ TypeVertex,
+ TypeFragment,
+ TypeOther
+ };
+ enum VariableType {
+ Constant, // cbuffer members or uniforms
+ Sampler,
+ Texture // for APIs with separate texture and sampler objects
+ };
+ struct InputParameter {
+ InputParameter() : semanticIndex(0) { }
+ // Semantics use the D3D keys (POSITION, TEXCOORD).
+ // Attribute name based APIs can map based on pre-defined names.
+ QByteArray semanticName;
+ int semanticIndex;
+ };
+ struct Variable {
+ Variable() : type(Constant), offset(0), size(0), bindPoint(0) { }
+ VariableType type;
+ QByteArray name;
+ uint offset; // for cbuffer members
+ uint size; // for cbuffer members
+ int bindPoint; // for textures and samplers; for register-based APIs
+ };
+
+ QByteArray blob; // source or bytecode
+ Type type;
+ QVector<InputParameter> inputParameters;
+ QVector<Variable> variables;
+ uint constantDataSize;
+ };
+
+ virtual bool reflect(const QByteArray &src, ShaderInfo *result) = 0;
+
+Q_SIGNALS:
+ void textureChanged();
+ void logAndStatusChanged();
+};
+
+#ifndef QT_NO_DEBUG_STREAM
+Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug debug, const QSGGuiThreadShaderEffectManager::ShaderInfo::InputParameter &p);
+Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug debug, const QSGGuiThreadShaderEffectManager::ShaderInfo::Variable &v);
+#endif
+
+class Q_QUICK_PRIVATE_EXPORT QSGShaderEffectNode : public QSGVisitableNode
+{
+public:
+ enum DirtyShaderFlag {
+ DirtyShaders = 0x01,
+ DirtyShaderConstant = 0x02,
+ DirtyShaderTexture = 0x04,
+ DirtyShaderGeometry = 0x08,
+ DirtyShaderMesh = 0x10,
+
+ DirtyShaderAll = 0xFF
+ };
+ Q_DECLARE_FLAGS(DirtyShaderFlags, DirtyShaderFlag)
+
+ enum CullMode { // must match ShaderEffect
+ NoCulling,
+ BackFaceCulling,
+ FrontFaceCulling
+ };
+
+ struct VariableData {
+ enum SpecialType { None, Unused, Source, SubRect, Opacity, Matrix };
+
+ QVariant value;
+ SpecialType specialType;
+ };
+
+ struct ShaderData {
+ ShaderData() : hasShaderCode(false) { }
+ bool hasShaderCode;
+ QSGGuiThreadShaderEffectManager::ShaderInfo shaderInfo;
+ QVector<VariableData> varData;
+ };
+
+ struct SyncData {
+ DirtyShaderFlags dirty;
+ CullMode cullMode;
+ bool blending;
+ struct ShaderSyncData {
+ const ShaderData *shader;
+ const QSet<int> *dirtyConstants;
+ const QSet<int> *dirtyTextures;
+ };
+ ShaderSyncData vertex;
+ ShaderSyncData fragment;
+ };
+
+ // Each ShaderEffect item has one node (render thread) and one manager (gui thread).
+ QSGShaderEffectNode(QSGGuiThreadShaderEffectManager *) { }
+
+ virtual QRectF updateNormalizedTextureSubRect(bool supportsAtlasTextures) = 0;
+ virtual void syncMaterial(SyncData *syncData) = 0;
+
+ void accept(QSGNodeVisitorEx *visitor) override { if (visitor->visit(this)) visitor->visitChildren(this); visitor->endVisit(this); }
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QSGShaderEffectNode::DirtyShaderFlags)
+
+#ifndef QT_NO_DEBUG_STREAM
+Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug debug, const QSGShaderEffectNode::VariableData &vd);
+#endif
+
class Q_QUICK_PRIVATE_EXPORT QSGGlyphNode : public QSGVisitableNode
{
public:
@@ -295,7 +438,7 @@ public:
};
struct Texture {
- GLuint textureId;
+ uint textureId;
QSize size;
Texture() : textureId(0), size(QSize()) { }
@@ -359,10 +502,10 @@ protected:
void markGlyphsToRender(const QVector<glyph_t> &glyphs);
inline void removeGlyph(glyph_t glyph);
- void updateTexture(GLuint oldTex, GLuint newTex, const QSize &newTexSize);
+ void updateTexture(uint oldTex, uint newTex, const QSize &newTexSize);
inline bool containsGlyph(glyph_t glyph);
- GLuint textureIdForGlyph(glyph_t glyph) const;
+ uint textureIdForGlyph(glyph_t glyph) const;
GlyphData &glyphData(glyph_t glyph);
diff --git a/src/quick/scenegraph/qsgbasicglyphnode.cpp b/src/quick/scenegraph/qsgbasicglyphnode.cpp
new file mode 100644
index 0000000000..38f650a82c
--- /dev/null
+++ b/src/quick/scenegraph/qsgbasicglyphnode.cpp
@@ -0,0 +1,95 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgbasicglyphnode_p.h"
+#include <qsgmaterial.h> // just so that we can safely do delete m_material in the dtor
+
+QT_BEGIN_NAMESPACE
+
+QSGBasicGlyphNode::QSGBasicGlyphNode()
+ : m_style(QQuickText::Normal)
+ , m_material(0)
+ , m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 0)
+{
+ m_geometry.setDrawingMode(QSGGeometry::DrawTriangles);
+ setGeometry(&m_geometry);
+}
+
+QSGBasicGlyphNode::~QSGBasicGlyphNode()
+{
+ delete m_material;
+}
+
+void QSGBasicGlyphNode::setColor(const QColor &color)
+{
+ m_color = color;
+ if (m_material != 0) {
+ setMaterialColor(color);
+ markDirty(DirtyMaterial);
+ }
+}
+
+void QSGBasicGlyphNode::setGlyphs(const QPointF &position, const QGlyphRun &glyphs)
+{
+ if (m_material != 0)
+ delete m_material;
+
+ m_position = position;
+ m_glyphs = glyphs;
+
+#ifdef QSG_RUNTIME_DESCRIPTION
+ qsgnode_set_description(this, QLatin1String("glyphs"));
+#endif
+}
+
+void QSGBasicGlyphNode::setStyle(QQuickText::TextStyle style)
+{
+ if (m_style == style)
+ return;
+ m_style = style;
+}
+
+void QSGBasicGlyphNode::setStyleColor(const QColor &color)
+{
+ if (m_styleColor == color)
+ return;
+ m_styleColor = color;
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/qsgbasicglyphnode_p.h b/src/quick/scenegraph/qsgbasicglyphnode_p.h
new file mode 100644
index 0000000000..1d09367ea5
--- /dev/null
+++ b/src/quick/scenegraph/qsgbasicglyphnode_p.h
@@ -0,0 +1,92 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGBASICGLYPHNODE_P_H
+#define QSGBASICGLYPHNODE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qsgadaptationlayer_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QSGMaterial;
+
+class Q_QUICK_PRIVATE_EXPORT QSGBasicGlyphNode: public QSGGlyphNode
+{
+public:
+ QSGBasicGlyphNode();
+ virtual ~QSGBasicGlyphNode();
+
+ virtual QPointF baseLine() const { return m_baseLine; }
+ virtual void setGlyphs(const QPointF &position, const QGlyphRun &glyphs);
+ virtual void setColor(const QColor &color);
+
+ virtual void setPreferredAntialiasingMode(AntialiasingMode) { }
+ virtual void setStyle(QQuickText::TextStyle);
+ virtual void setStyleColor(const QColor &);
+
+ virtual void setMaterialColor(const QColor &color) = 0;
+ virtual void update() = 0;
+
+protected:
+ QGlyphRun m_glyphs;
+ QPointF m_position;
+ QColor m_color;
+ QQuickText::TextStyle m_style;
+ QColor m_styleColor;
+
+ QPointF m_baseLine;
+ QSGMaterial *m_material;
+
+ QSGGeometry m_geometry;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/quick/scenegraph/qsgbasicimagenode.cpp b/src/quick/scenegraph/qsgbasicimagenode.cpp
new file mode 100644
index 0000000000..24077cc947
--- /dev/null
+++ b/src/quick/scenegraph/qsgbasicimagenode.cpp
@@ -0,0 +1,559 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgbasicimagenode_p.h"
+
+#include <QtCore/qvarlengtharray.h>
+#include <QtCore/qmath.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace
+{
+ struct SmoothVertex
+ {
+ float x, y, u, v;
+ float dx, dy, du, dv;
+ };
+
+ const QSGGeometry::AttributeSet &smoothAttributeSet()
+ {
+ static QSGGeometry::Attribute data[] = {
+ QSGGeometry::Attribute::createWithSemantic(0, 2, QSGGeometry::TypeFloat, QSGGeometry::Attribute::POSITION),
+ QSGGeometry::Attribute::createWithSemantic(1, 2, QSGGeometry::TypeFloat, QSGGeometry::Attribute::TEXCOORD),
+ QSGGeometry::Attribute::createWithSemantic(2, 2, QSGGeometry::TypeFloat, QSGGeometry::Attribute::TEXCOORD1),
+ QSGGeometry::Attribute::createWithSemantic(3, 2, QSGGeometry::TypeFloat, QSGGeometry::Attribute::TEXCOORD2)
+ };
+ static QSGGeometry::AttributeSet attrs = { 4, sizeof(SmoothVertex), data };
+ return attrs;
+ }
+}
+
+QSGBasicImageNode::QSGBasicImageNode()
+ : m_innerSourceRect(0, 0, 1, 1)
+ , m_subSourceRect(0, 0, 1, 1)
+ , m_antialiasing(false)
+ , m_mirror(false)
+ , m_dirtyGeometry(false)
+ , m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4)
+ , m_dynamicTexture(nullptr)
+{
+ setGeometry(&m_geometry);
+
+#ifdef QSG_RUNTIME_DESCRIPTION
+ qsgnode_set_description(this, QLatin1String("image"));
+#endif
+}
+
+void QSGBasicImageNode::setTargetRect(const QRectF &rect)
+{
+ if (rect == m_targetRect)
+ return;
+ m_targetRect = rect;
+ m_dirtyGeometry = true;
+}
+
+void QSGBasicImageNode::setInnerTargetRect(const QRectF &rect)
+{
+ if (rect == m_innerTargetRect)
+ return;
+ m_innerTargetRect = rect;
+ m_dirtyGeometry = true;
+}
+
+void QSGBasicImageNode::setInnerSourceRect(const QRectF &rect)
+{
+ if (rect == m_innerSourceRect)
+ return;
+ m_innerSourceRect = rect;
+ m_dirtyGeometry = true;
+}
+
+void QSGBasicImageNode::setSubSourceRect(const QRectF &rect)
+{
+ if (rect == m_subSourceRect)
+ return;
+ m_subSourceRect = rect;
+ m_dirtyGeometry = true;
+}
+
+void QSGBasicImageNode::setTexture(QSGTexture *texture)
+{
+ Q_ASSERT(texture);
+
+ setMaterialTexture(texture);
+ updateMaterialBlending();
+
+ markDirty(DirtyMaterial);
+
+ // Because the texture can be a different part of the atlas, we need to update it...
+ m_dirtyGeometry = true;
+}
+
+void QSGBasicImageNode::setAntialiasing(bool antialiasing)
+{
+ if (antialiasing == m_antialiasing)
+ return;
+ m_antialiasing = antialiasing;
+ if (m_antialiasing) {
+ setGeometry(new QSGGeometry(smoothAttributeSet(), 0));
+ setFlag(OwnsGeometry, true);
+ } else {
+ setGeometry(&m_geometry);
+ setFlag(OwnsGeometry, false);
+ }
+ updateMaterialAntialiasing();
+ m_dirtyGeometry = true;
+}
+
+void QSGBasicImageNode::setMirror(bool mirror)
+{
+ if (mirror == m_mirror)
+ return;
+ m_mirror = mirror;
+ m_dirtyGeometry = true;
+}
+
+
+void QSGBasicImageNode::update()
+{
+ if (m_dirtyGeometry)
+ updateGeometry();
+}
+
+void QSGBasicImageNode::preprocess()
+{
+ bool doDirty = false;
+ QSGDynamicTexture *t = qobject_cast<QSGDynamicTexture *>(materialTexture());
+ if (t) {
+ doDirty = t->updateTexture();
+ if (doDirty) {
+ // The geometry may need updating. This is expensive however, so do
+ // it only when something relevant has changed.
+ if (t != m_dynamicTexture
+ || t->textureSize() != m_dynamicTextureSize
+ || t->normalizedTextureSubRect() != m_dynamicTextureSubRect) {
+ updateGeometry();
+ m_dynamicTextureSize = t->textureSize();
+ m_dynamicTextureSubRect = t->normalizedTextureSubRect();
+ }
+ }
+ }
+ m_dynamicTexture = t;
+
+ if (updateMaterialBlending())
+ doDirty = true;
+
+ if (doDirty)
+ markDirty(DirtyMaterial);
+}
+
+namespace {
+ struct X { float x, tx; };
+ struct Y { float y, ty; };
+}
+
+static inline void appendQuad(quint16 **indices, quint16 topLeft, quint16 topRight,
+ quint16 bottomLeft, quint16 bottomRight)
+{
+ *(*indices)++ = topLeft;
+ *(*indices)++ = bottomLeft;
+ *(*indices)++ = bottomRight;
+ *(*indices)++ = bottomRight;
+ *(*indices)++ = topRight;
+ *(*indices)++ = topLeft;
+}
+
+QSGGeometry *QSGBasicImageNode::updateGeometry(const QRectF &targetRect,
+ const QRectF &innerTargetRect,
+ const QRectF &sourceRect,
+ const QRectF &innerSourceRect,
+ const QRectF &subSourceRect,
+ QSGGeometry *geometry,
+ bool mirror,
+ bool antialiasing)
+{
+ int floorLeft = qFloor(subSourceRect.left());
+ int ceilRight = qCeil(subSourceRect.right());
+ int floorTop = qFloor(subSourceRect.top());
+ int ceilBottom = qCeil(subSourceRect.bottom());
+ int hTiles = ceilRight - floorLeft;
+ int vTiles = ceilBottom - floorTop;
+
+ int hCells = hTiles;
+ int vCells = vTiles;
+ if (innerTargetRect.width() == 0)
+ hCells = 0;
+ if (innerTargetRect.left() != targetRect.left())
+ ++hCells;
+ if (innerTargetRect.right() != targetRect.right())
+ ++hCells;
+ if (innerTargetRect.height() == 0)
+ vCells = 0;
+ if (innerTargetRect.top() != targetRect.top())
+ ++vCells;
+ if (innerTargetRect.bottom() != targetRect.bottom())
+ ++vCells;
+ QVarLengthArray<X, 32> xData(2 * hCells);
+ QVarLengthArray<Y, 32> yData(2 * vCells);
+ X *xs = xData.data();
+ Y *ys = yData.data();
+
+ if (innerTargetRect.left() != targetRect.left()) {
+ xs[0].x = targetRect.left();
+ xs[0].tx = sourceRect.left();
+ xs[1].x = innerTargetRect.left();
+ xs[1].tx = innerSourceRect.left();
+ xs += 2;
+ }
+ if (innerTargetRect.width() != 0) {
+ xs[0].x = innerTargetRect.left();
+ xs[0].tx = innerSourceRect.x() + (subSourceRect.left() - floorLeft) * innerSourceRect.width();
+ ++xs;
+ float b = innerTargetRect.width() / subSourceRect.width();
+ float a = innerTargetRect.x() - subSourceRect.x() * b;
+ for (int i = floorLeft + 1; i <= ceilRight - 1; ++i) {
+ xs[0].x = xs[1].x = a + b * i;
+ xs[0].tx = innerSourceRect.right();
+ xs[1].tx = innerSourceRect.left();
+ xs += 2;
+ }
+ xs[0].x = innerTargetRect.right();
+ xs[0].tx = innerSourceRect.x() + (subSourceRect.right() - ceilRight + 1) * innerSourceRect.width();
+ ++xs;
+ }
+ if (innerTargetRect.right() != targetRect.right()) {
+ xs[0].x = innerTargetRect.right();
+ xs[0].tx = innerSourceRect.right();
+ xs[1].x = targetRect.right();
+ xs[1].tx = sourceRect.right();
+ xs += 2;
+ }
+ Q_ASSERT(xs == xData.data() + xData.size());
+ if (mirror) {
+ float leftPlusRight = targetRect.left() + targetRect.right();
+ int count = xData.size();
+ xs = xData.data();
+ for (int i = 0; i < count >> 1; ++i)
+ qSwap(xs[i], xs[count - 1 - i]);
+ for (int i = 0; i < count; ++i)
+ xs[i].x = leftPlusRight - xs[i].x;
+ }
+
+ if (innerTargetRect.top() != targetRect.top()) {
+ ys[0].y = targetRect.top();
+ ys[0].ty = sourceRect.top();
+ ys[1].y = innerTargetRect.top();
+ ys[1].ty = innerSourceRect.top();
+ ys += 2;
+ }
+ if (innerTargetRect.height() != 0) {
+ ys[0].y = innerTargetRect.top();
+ ys[0].ty = innerSourceRect.y() + (subSourceRect.top() - floorTop) * innerSourceRect.height();
+ ++ys;
+ float b = innerTargetRect.height() / subSourceRect.height();
+ float a = innerTargetRect.y() - subSourceRect.y() * b;
+ for (int i = floorTop + 1; i <= ceilBottom - 1; ++i) {
+ ys[0].y = ys[1].y = a + b * i;
+ ys[0].ty = innerSourceRect.bottom();
+ ys[1].ty = innerSourceRect.top();
+ ys += 2;
+ }
+ ys[0].y = innerTargetRect.bottom();
+ ys[0].ty = innerSourceRect.y() + (subSourceRect.bottom() - ceilBottom + 1) * innerSourceRect.height();
+ ++ys;
+ }
+ if (innerTargetRect.bottom() != targetRect.bottom()) {
+ ys[0].y = innerTargetRect.bottom();
+ ys[0].ty = innerSourceRect.bottom();
+ ys[1].y = targetRect.bottom();
+ ys[1].ty = sourceRect.bottom();
+ ys += 2;
+ }
+ Q_ASSERT(ys == yData.data() + yData.size());
+
+ if (antialiasing) {
+ QSGGeometry *g = geometry;
+ Q_ASSERT(g);
+
+ g->allocate(hCells * vCells * 4 + (hCells + vCells - 1) * 4,
+ hCells * vCells * 6 + (hCells + vCells) * 12);
+ g->setDrawingMode(QSGGeometry::DrawTriangles);
+ SmoothVertex *vertices = reinterpret_cast<SmoothVertex *>(g->vertexData());
+ memset(vertices, 0, g->vertexCount() * g->sizeOfVertex());
+ quint16 *indices = g->indexDataAsUShort();
+
+ // The deltas are how much the fuzziness can reach into the image.
+ // Only the border vertices are moved by the vertex shader, so the fuzziness
+ // can't reach further into the image than the closest interior vertices.
+ float leftDx = xData.at(1).x - xData.at(0).x;
+ float rightDx = xData.at(xData.size() - 1).x - xData.at(xData.size() - 2).x;
+ float topDy = yData.at(1).y - yData.at(0).y;
+ float bottomDy = yData.at(yData.size() - 1).y - yData.at(yData.size() - 2).y;
+
+ float leftDu = xData.at(1).tx - xData.at(0).tx;
+ float rightDu = xData.at(xData.size() - 1).tx - xData.at(xData.size() - 2).tx;
+ float topDv = yData.at(1).ty - yData.at(0).ty;
+ float bottomDv = yData.at(yData.size() - 1).ty - yData.at(yData.size() - 2).ty;
+
+ if (hCells == 1) {
+ leftDx = rightDx *= 0.5f;
+ leftDu = rightDu *= 0.5f;
+ }
+ if (vCells == 1) {
+ topDy = bottomDy *= 0.5f;
+ topDv = bottomDv *= 0.5f;
+ }
+
+ // This delta is how much the fuzziness can reach out from the image.
+ float delta = float(qAbs(targetRect.width()) < qAbs(targetRect.height())
+ ? targetRect.width() : targetRect.height()) * 0.5f;
+
+ quint16 index = 0;
+ ys = yData.data();
+ for (int j = 0; j < vCells; ++j, ys += 2) {
+ xs = xData.data();
+ bool isTop = j == 0;
+ bool isBottom = j == vCells - 1;
+ for (int i = 0; i < hCells; ++i, xs += 2) {
+ bool isLeft = i == 0;
+ bool isRight = i == hCells - 1;
+
+ SmoothVertex *v = vertices + index;
+
+ quint16 topLeft = index;
+ for (int k = (isTop || isLeft ? 2 : 1); k--; ++v, ++index) {
+ v->x = xs[0].x;
+ v->u = xs[0].tx;
+ v->y = ys[0].y;
+ v->v = ys[0].ty;
+ }
+
+ quint16 topRight = index;
+ for (int k = (isTop || isRight ? 2 : 1); k--; ++v, ++index) {
+ v->x = xs[1].x;
+ v->u = xs[1].tx;
+ v->y = ys[0].y;
+ v->v = ys[0].ty;
+ }
+
+ quint16 bottomLeft = index;
+ for (int k = (isBottom || isLeft ? 2 : 1); k--; ++v, ++index) {
+ v->x = xs[0].x;
+ v->u = xs[0].tx;
+ v->y = ys[1].y;
+ v->v = ys[1].ty;
+ }
+
+ quint16 bottomRight = index;
+ for (int k = (isBottom || isRight ? 2 : 1); k--; ++v, ++index) {
+ v->x = xs[1].x;
+ v->u = xs[1].tx;
+ v->y = ys[1].y;
+ v->v = ys[1].ty;
+ }
+
+ appendQuad(&indices, topLeft, topRight, bottomLeft, bottomRight);
+
+ if (isTop) {
+ vertices[topLeft].dy = vertices[topRight].dy = topDy;
+ vertices[topLeft].dv = vertices[topRight].dv = topDv;
+ vertices[topLeft + 1].dy = vertices[topRight + 1].dy = -delta;
+ appendQuad(&indices, topLeft + 1, topRight + 1, topLeft, topRight);
+ }
+
+ if (isBottom) {
+ vertices[bottomLeft].dy = vertices[bottomRight].dy = -bottomDy;
+ vertices[bottomLeft].dv = vertices[bottomRight].dv = -bottomDv;
+ vertices[bottomLeft + 1].dy = vertices[bottomRight + 1].dy = delta;
+ appendQuad(&indices, bottomLeft, bottomRight, bottomLeft + 1, bottomRight + 1);
+ }
+
+ if (isLeft) {
+ vertices[topLeft].dx = vertices[bottomLeft].dx = leftDx;
+ vertices[topLeft].du = vertices[bottomLeft].du = leftDu;
+ vertices[topLeft + 1].dx = vertices[bottomLeft + 1].dx = -delta;
+ appendQuad(&indices, topLeft + 1, topLeft, bottomLeft + 1, bottomLeft);
+ }
+
+ if (isRight) {
+ vertices[topRight].dx = vertices[bottomRight].dx = -rightDx;
+ vertices[topRight].du = vertices[bottomRight].du = -rightDu;
+ vertices[topRight + 1].dx = vertices[bottomRight + 1].dx = delta;
+ appendQuad(&indices, topRight, topRight + 1, bottomRight, bottomRight + 1);
+ }
+ }
+ }
+
+ Q_ASSERT(index == g->vertexCount());
+ Q_ASSERT(indices - g->indexCount() == g->indexData());
+ } else {
+ if (!geometry) {
+ geometry = new QSGGeometry(QSGGeometry::defaultAttributes_TexturedPoint2D(),
+ hCells * vCells * 4, hCells * vCells * 6,
+ QSGGeometry::TypeUnsignedShort);
+ } else {
+ geometry->allocate(hCells * vCells * 4, hCells * vCells * 6);
+ }
+ geometry->setDrawingMode(QSGGeometry::DrawTriangles);
+ QSGGeometry::TexturedPoint2D *vertices = geometry->vertexDataAsTexturedPoint2D();
+ ys = yData.data();
+ for (int j = 0; j < vCells; ++j, ys += 2) {
+ xs = xData.data();
+ for (int i = 0; i < hCells; ++i, xs += 2) {
+ vertices[0].x = vertices[2].x = xs[0].x;
+ vertices[0].tx = vertices[2].tx = xs[0].tx;
+ vertices[1].x = vertices[3].x = xs[1].x;
+ vertices[1].tx = vertices[3].tx = xs[1].tx;
+
+ vertices[0].y = vertices[1].y = ys[0].y;
+ vertices[0].ty = vertices[1].ty = ys[0].ty;
+ vertices[2].y = vertices[3].y = ys[1].y;
+ vertices[2].ty = vertices[3].ty = ys[1].ty;
+
+ vertices += 4;
+ }
+ }
+
+ quint16 *indices = geometry->indexDataAsUShort();
+ for (int i = 0; i < 4 * vCells * hCells; i += 4)
+ appendQuad(&indices, i, i + 1, i + 2, i + 3);
+ }
+ return geometry;
+}
+
+void QSGBasicImageNode::updateGeometry()
+{
+ Q_ASSERT(!m_targetRect.isEmpty());
+ const QSGTexture *t = materialTexture();
+ if (!t) {
+ QSGGeometry *g = geometry();
+ g->allocate(4);
+ g->setDrawingMode(QSGGeometry::DrawTriangleStrip);
+ memset(g->vertexData(), 0, g->sizeOfVertex() * 4);
+ } else {
+ QRectF sourceRect = t->normalizedTextureSubRect();
+
+ QRectF innerSourceRect(sourceRect.x() + m_innerSourceRect.x() * sourceRect.width(),
+ sourceRect.y() + m_innerSourceRect.y() * sourceRect.height(),
+ m_innerSourceRect.width() * sourceRect.width(),
+ m_innerSourceRect.height() * sourceRect.height());
+
+ bool hasMargins = m_targetRect != m_innerTargetRect;
+
+ int floorLeft = qFloor(m_subSourceRect.left());
+ int ceilRight = qCeil(m_subSourceRect.right());
+ int floorTop = qFloor(m_subSourceRect.top());
+ int ceilBottom = qCeil(m_subSourceRect.bottom());
+ int hTiles = ceilRight - floorLeft;
+ int vTiles = ceilBottom - floorTop;
+
+ bool hasTiles = hTiles != 1 || vTiles != 1;
+ bool fullTexture = innerSourceRect == QRectF(0, 0, 1, 1);
+
+ // An image can be rendered as a single quad if:
+ // - There are no margins, and either:
+ // - the image isn't repeated
+ // - the source rectangle fills the entire texture so that texture wrapping can be used,
+ // and NPOT is supported
+ if (!hasMargins && (!hasTiles || (fullTexture && supportsWrap(t->textureSize())))) {
+ QRectF sr;
+ if (!fullTexture) {
+ sr = QRectF(innerSourceRect.x() + (m_subSourceRect.left() - floorLeft) * innerSourceRect.width(),
+ innerSourceRect.y() + (m_subSourceRect.top() - floorTop) * innerSourceRect.height(),
+ m_subSourceRect.width() * innerSourceRect.width(),
+ m_subSourceRect.height() * innerSourceRect.height());
+ } else {
+ sr = QRectF(m_subSourceRect.left() - floorLeft, m_subSourceRect.top() - floorTop,
+ m_subSourceRect.width(), m_subSourceRect.height());
+ }
+ if (m_mirror) {
+ qreal oldLeft = sr.left();
+ sr.setLeft(sr.right());
+ sr.setRight(oldLeft);
+ }
+
+ if (m_antialiasing) {
+ QSGGeometry *g = geometry();
+ Q_ASSERT(g != &m_geometry);
+ g->allocate(8, 14);
+ g->setDrawingMode(QSGGeometry::DrawTriangleStrip);
+ SmoothVertex *vertices = reinterpret_cast<SmoothVertex *>(g->vertexData());
+ float delta = float(qAbs(m_targetRect.width()) < qAbs(m_targetRect.height())
+ ? m_targetRect.width() : m_targetRect.height()) * 0.5f;
+ float sx = float(sr.width() / m_targetRect.width());
+ float sy = float(sr.height() / m_targetRect.height());
+ for (int d = -1; d <= 1; d += 2) {
+ for (int j = 0; j < 2; ++j) {
+ for (int i = 0; i < 2; ++i, ++vertices) {
+ vertices->x = m_targetRect.x() + i * m_targetRect.width();
+ vertices->y = m_targetRect.y() + j * m_targetRect.height();
+ vertices->u = sr.x() + i * sr.width();
+ vertices->v = sr.y() + j * sr.height();
+ vertices->dx = (i == 0 ? delta : -delta) * d;
+ vertices->dy = (j == 0 ? delta : -delta) * d;
+ vertices->du = (d < 0 ? 0 : vertices->dx * sx);
+ vertices->dv = (d < 0 ? 0 : vertices->dy * sy);
+ }
+ }
+ }
+ Q_ASSERT(vertices - g->vertexCount() == g->vertexData());
+ static const quint16 indices[] = {
+ 0, 4, 1, 5, 3, 7, 2, 6, 0, 4,
+ 4, 6, 5, 7
+ };
+ Q_ASSERT(g->sizeOfIndex() * g->indexCount() == sizeof(indices));
+ memcpy(g->indexDataAsUShort(), indices, sizeof(indices));
+ } else {
+ m_geometry.allocate(4);
+ m_geometry.setDrawingMode(QSGGeometry::DrawTriangleStrip);
+ QSGGeometry::updateTexturedRectGeometry(&m_geometry, m_targetRect, sr);
+ }
+ } else {
+ QSGGeometry *g = m_antialiasing ? geometry() : &m_geometry;
+ updateGeometry(m_targetRect, m_innerTargetRect,
+ sourceRect, innerSourceRect, m_subSourceRect,
+ g, m_mirror, m_antialiasing);
+ }
+ }
+ markDirty(DirtyGeometry);
+ m_dirtyGeometry = false;
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/qsgbasicimagenode_p.h b/src/quick/scenegraph/qsgbasicimagenode_p.h
new file mode 100644
index 0000000000..4a96e1a9f6
--- /dev/null
+++ b/src/quick/scenegraph/qsgbasicimagenode_p.h
@@ -0,0 +1,109 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGBASICIMAGENODE_P_H
+#define QSGBASICIMAGENODE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qsgadaptationlayer_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_QUICK_PRIVATE_EXPORT QSGBasicImageNode : public QSGImageNode
+{
+public:
+ QSGBasicImageNode();
+
+ void setTargetRect(const QRectF &rect) override;
+ void setInnerTargetRect(const QRectF &rect) override;
+ void setInnerSourceRect(const QRectF &rect) override;
+ void setSubSourceRect(const QRectF &rect) override;
+ void setTexture(QSGTexture *texture) override;
+ void setAntialiasing(bool antialiasing) override;
+ void setMirror(bool mirror) override;
+ void update() override;
+ void preprocess() override;
+
+ static QSGGeometry *updateGeometry(const QRectF &targetRect,
+ const QRectF &innerTargetRect,
+ const QRectF &sourceRect,
+ const QRectF &innerSourceRect,
+ const QRectF &subSourceRect,
+ QSGGeometry *geometry,
+ bool mirror = false,
+ bool antialiasing = false);
+
+protected:
+ virtual void updateMaterialAntialiasing() = 0;
+ virtual void setMaterialTexture(QSGTexture *texture) = 0;
+ virtual QSGTexture *materialTexture() const = 0;
+ virtual bool updateMaterialBlending() = 0;
+ virtual bool supportsWrap(const QSize &size) const = 0;
+
+ void updateGeometry();
+
+ QRectF m_targetRect;
+ QRectF m_innerTargetRect;
+ QRectF m_innerSourceRect;
+ QRectF m_subSourceRect;
+
+ uint m_antialiasing : 1;
+ uint m_mirror : 1;
+ uint m_dirtyGeometry : 1;
+
+ QSGGeometry m_geometry;
+
+ QSGDynamicTexture *m_dynamicTexture;
+ QSize m_dynamicTextureSize;
+ QRectF m_dynamicTextureSubRect;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/quick/scenegraph/qsgbasicrectanglenode.cpp b/src/quick/scenegraph/qsgbasicrectanglenode.cpp
new file mode 100644
index 0000000000..14d2dc9677
--- /dev/null
+++ b/src/quick/scenegraph/qsgbasicrectanglenode.cpp
@@ -0,0 +1,687 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgbasicrectanglenode_p.h"
+
+#include <QtCore/qmath.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace
+{
+ struct Color4ub
+ {
+ unsigned char r, g, b, a;
+ };
+
+ Color4ub operator *(Color4ub c, float t) { c.a *= t; c.r *= t; c.g *= t; c.b *= t; return c; }
+ Color4ub operator +(Color4ub a, Color4ub b) { a.a += b.a; a.r += b.r; a.g += b.g; a.b += b.b; return a; }
+
+ inline Color4ub colorToColor4ub(const QColor &c)
+ {
+ Color4ub color = { uchar(qRound(c.redF() * c.alphaF() * 255)),
+ uchar(qRound(c.greenF() * c.alphaF() * 255)),
+ uchar(qRound(c.blueF() * c.alphaF() * 255)),
+ uchar(qRound(c.alphaF() * 255))
+ };
+ return color;
+ }
+
+ // Same layout as QSGGeometry::ColoredPoint2D, but uses Color4ub for convenience.
+ struct Vertex
+ {
+ float x, y;
+ Color4ub color;
+ void set(float nx, float ny, Color4ub ncolor)
+ {
+ x = nx; y = ny; color = ncolor;
+ }
+ };
+
+ struct SmoothVertex : public Vertex
+ {
+ float dx, dy;
+ void set(float nx, float ny, Color4ub ncolor, float ndx, float ndy)
+ {
+ Vertex::set(nx, ny, ncolor);
+ dx = ndx; dy = ndy;
+ }
+ };
+
+ const QSGGeometry::AttributeSet &smoothAttributeSet()
+ {
+ static QSGGeometry::Attribute data[] = {
+ QSGGeometry::Attribute::createWithSemantic(0, 2, QSGGeometry::TypeFloat, QSGGeometry::Attribute::POSITION),
+ QSGGeometry::Attribute::createWithSemantic(1, 4, QSGGeometry::TypeUnsignedByte, QSGGeometry::Attribute::COLOR),
+ QSGGeometry::Attribute::createWithSemantic(2, 2, QSGGeometry::TypeFloat, QSGGeometry::Attribute::TEXCOORD)
+ };
+ static QSGGeometry::AttributeSet attrs = { 3, sizeof(SmoothVertex), data };
+ return attrs;
+ }
+}
+
+QSGBasicRectangleNode::QSGBasicRectangleNode()
+ : m_radius(0)
+ , m_pen_width(0)
+ , m_aligned(true)
+ , m_antialiasing(false)
+ , m_gradient_is_opaque(true)
+ , m_dirty_geometry(false)
+ , m_geometry(QSGGeometry::defaultAttributes_ColoredPoint2D(), 0)
+{
+ setGeometry(&m_geometry);
+
+#ifdef QSG_RUNTIME_DESCRIPTION
+ qsgnode_set_description(this, QLatin1String("rectangle"));
+#endif
+}
+
+void QSGBasicRectangleNode::setRect(const QRectF &rect)
+{
+ if (rect == m_rect)
+ return;
+ m_rect = rect;
+ m_dirty_geometry = true;
+}
+
+void QSGBasicRectangleNode::setColor(const QColor &color)
+{
+ if (color == m_color)
+ return;
+ m_color = color;
+ if (m_gradient_stops.isEmpty())
+ m_dirty_geometry = true;
+}
+
+void QSGBasicRectangleNode::setPenColor(const QColor &color)
+{
+ if (color == m_border_color)
+ return;
+ m_border_color = color;
+ if (m_pen_width > 0)
+ m_dirty_geometry = true;
+}
+
+void QSGBasicRectangleNode::setPenWidth(qreal width)
+{
+ if (width == m_pen_width)
+ return;
+ m_pen_width = width;
+ m_dirty_geometry = true;
+}
+
+
+void QSGBasicRectangleNode::setGradientStops(const QGradientStops &stops)
+{
+ if (stops.constData() == m_gradient_stops.constData())
+ return;
+
+ m_gradient_stops = stops;
+
+ m_gradient_is_opaque = true;
+ for (int i = 0; i < stops.size(); ++i)
+ m_gradient_is_opaque &= stops.at(i).second.alpha() == 0xff;
+ m_dirty_geometry = true;
+}
+
+void QSGBasicRectangleNode::setRadius(qreal radius)
+{
+ if (radius == m_radius)
+ return;
+ m_radius = radius;
+ m_dirty_geometry = true;
+}
+
+void QSGBasicRectangleNode::setAntialiasing(bool antialiasing)
+{
+ if (!supportsAntialiasing())
+ return;
+
+ if (antialiasing == m_antialiasing)
+ return;
+ m_antialiasing = antialiasing;
+ if (m_antialiasing) {
+ setGeometry(new QSGGeometry(smoothAttributeSet(), 0));
+ setFlag(OwnsGeometry, true);
+ } else {
+ setGeometry(&m_geometry);
+ setFlag(OwnsGeometry, false);
+ }
+ updateMaterialAntialiasing();
+ m_dirty_geometry = true;
+}
+
+void QSGBasicRectangleNode::setAligned(bool aligned)
+{
+ if (aligned == m_aligned)
+ return;
+ m_aligned = aligned;
+ m_dirty_geometry = true;
+}
+
+void QSGBasicRectangleNode::update()
+{
+ if (m_dirty_geometry) {
+ updateGeometry();
+ m_dirty_geometry = false;
+
+ QSGNode::DirtyState state = QSGNode::DirtyGeometry;
+ updateMaterialBlending(&state);
+ markDirty(state);
+ }
+}
+
+void QSGBasicRectangleNode::updateGeometry()
+{
+ float width = float(m_rect.width());
+ float height = float(m_rect.height());
+ float penWidth = qMin(qMin(width, height) * 0.5f, float(m_pen_width));
+
+ if (m_aligned)
+ penWidth = qRound(penWidth);
+
+ QSGGeometry *g = geometry();
+ g->setDrawingMode(QSGGeometry::DrawTriangleStrip);
+ int vertexStride = g->sizeOfVertex();
+
+ union {
+ Vertex *vertices;
+ SmoothVertex *smoothVertices;
+ };
+
+ Color4ub fillColor = colorToColor4ub(m_color);
+ Color4ub borderColor = colorToColor4ub(m_border_color);
+ Color4ub transparent = { 0, 0, 0, 0 };
+ const QGradientStops &stops = m_gradient_stops;
+
+ int nextGradientStop = 0;
+ float gradientPos = penWidth / height;
+ while (nextGradientStop < stops.size() && stops.at(nextGradientStop).first <= gradientPos)
+ ++nextGradientStop;
+ int lastGradientStop = stops.size() - 1;
+ float lastGradientPos = 1.0f - penWidth / height;
+ while (lastGradientStop >= nextGradientStop && stops.at(lastGradientStop).first >= lastGradientPos)
+ --lastGradientStop;
+ int gradientIntersections = (lastGradientStop - nextGradientStop + 1);
+
+ if (m_radius > 0) {
+ // Rounded corners.
+
+ // Radius should never exceeds half of the width or half of the height
+ float radius = qMin(qMin(width, height) * 0.5f, float(m_radius));
+ QRectF innerRect = m_rect;
+ innerRect.adjust(radius, radius, -radius, -radius);
+
+ float innerRadius = radius - penWidth * 1.0f;
+ float outerRadius = radius;
+ float delta = qMin(width, height) * 0.5f;
+
+ // Number of segments per corner, approximately one per 3 pixels.
+ int segments = qBound(3, qCeil(outerRadius * (M_PI / 6)), 18);
+
+ /*
+
+ --+--__
+ --+--__--__
+ | --__--__
+ | seg --__--+
+ --+-__ ment _+ \
+ --+-__--__ - \ \
+ --__--+ se \ \
+ + \ g \ \
+ \ \ m \ \
+ -----------+--+ e \ \ <- gradient line
+ \ \ nt\ \
+ fill +--+----+--+
+ | | | |
+ border
+ inner AA outer AA (AA = antialiasing)
+
+ */
+
+ int innerVertexCount = (segments + 1) * 4 + gradientIntersections * 2;
+ int outerVertexCount = (segments + 1) * 4;
+ int vertexCount = innerVertexCount;
+ if (m_antialiasing || penWidth)
+ vertexCount += innerVertexCount;
+ if (penWidth)
+ vertexCount += outerVertexCount;
+ if (m_antialiasing && penWidth)
+ vertexCount += outerVertexCount;
+
+ int fillIndexCount = innerVertexCount;
+ int innerAAIndexCount = innerVertexCount * 2 + 2;
+ int borderIndexCount = innerVertexCount * 2 + 2;
+ int outerAAIndexCount = outerVertexCount * 2 + 2;
+ int indexCount = 0;
+ int fillHead = 0;
+ int innerAAHead = 0;
+ int innerAATail = 0;
+ int borderHead = 0;
+ int borderTail = 0;
+ int outerAAHead = 0;
+ int outerAATail = 0;
+ bool hasFill = m_color.alpha() > 0 || !stops.isEmpty();
+ if (hasFill)
+ indexCount += fillIndexCount;
+ if (m_antialiasing) {
+ innerAATail = innerAAHead = indexCount + (innerAAIndexCount >> 1) + 1;
+ indexCount += innerAAIndexCount;
+ }
+ if (penWidth) {
+ borderTail = borderHead = indexCount + (borderIndexCount >> 1) + 1;
+ indexCount += borderIndexCount;
+ }
+ if (m_antialiasing && penWidth) {
+ outerAATail = outerAAHead = indexCount + (outerAAIndexCount >> 1) + 1;
+ indexCount += outerAAIndexCount;
+ }
+
+ g->allocate(vertexCount, indexCount);
+ vertices = reinterpret_cast<Vertex *>(g->vertexData());
+ memset(vertices, 0, vertexCount * vertexStride);
+ quint16 *indices = g->indexDataAsUShort();
+ quint16 index = 0;
+
+ float py = 0; // previous inner y-coordinate.
+ float plx = 0; // previous inner left x-coordinate.
+ float prx = 0; // previous inner right x-coordinate.
+
+ float angle = 0.5f * float(M_PI) / segments;
+ float cosStep = qFastCos(angle);
+ float sinStep = qFastSin(angle);
+
+ for (int part = 0; part < 2; ++part) {
+ float c = 1 - part;
+ float s = part;
+ for (int i = 0; i <= segments; ++i) {
+ float y, lx, rx;
+ if (innerRadius > 0) {
+ y = (part ? innerRect.bottom() : innerRect.top()) - innerRadius * c; // current inner y-coordinate.
+ lx = innerRect.left() - innerRadius * s; // current inner left x-coordinate.
+ rx = innerRect.right() + innerRadius * s; // current inner right x-coordinate.
+ gradientPos = ((part ? innerRect.height() : 0) + radius - innerRadius * c) / height;
+ } else {
+ y = (part ? innerRect.bottom() + innerRadius : innerRect.top() - innerRadius); // current inner y-coordinate.
+ lx = innerRect.left() - innerRadius; // current inner left x-coordinate.
+ rx = innerRect.right() + innerRadius; // current inner right x-coordinate.
+ gradientPos = ((part ? innerRect.height() + innerRadius : -innerRadius) + radius) / height;
+ }
+ float Y = (part ? innerRect.bottom() : innerRect.top()) - outerRadius * c; // current outer y-coordinate.
+ float lX = innerRect.left() - outerRadius * s; // current outer left x-coordinate.
+ float rX = innerRect.right() + outerRadius * s; // current outer right x-coordinate.
+
+ while (nextGradientStop <= lastGradientStop && stops.at(nextGradientStop).first <= gradientPos) {
+ // Insert vertices at gradient stops.
+ float gy = (innerRect.top() - radius) + stops.at(nextGradientStop).first * height;
+ float t = (gy - py) / (y - py);
+ float glx = plx * (1 - t) + t * lx;
+ float grx = prx * (1 - t) + t * rx;
+
+ fillColor = colorToColor4ub(stops.at(nextGradientStop).second);
+
+ if (hasFill) {
+ indices[fillHead++] = index;
+ indices[fillHead++] = index + 1;
+ }
+
+ if (penWidth) {
+ --borderHead;
+ indices[borderHead] = indices[borderHead + 2];
+ indices[--borderHead] = index + 2;
+ indices[borderTail++] = index + 3;
+ indices[borderTail] = indices[borderTail - 2];
+ ++borderTail;
+ }
+
+ if (m_antialiasing) {
+ indices[--innerAAHead] = index + 2;
+ indices[--innerAAHead] = index;
+ indices[innerAATail++] = index + 1;
+ indices[innerAATail++] = index + 3;
+
+ bool lower = stops.at(nextGradientStop).first > 0.5f;
+ float dy = lower ? qMin(0.0f, height - gy - delta) : qMax(0.0f, delta - gy);
+ smoothVertices[index++].set(grx, gy, fillColor, width - grx - delta, dy);
+ smoothVertices[index++].set(glx, gy, fillColor, delta - glx, dy);
+ if (penWidth) {
+ smoothVertices[index++].set(grx, gy, borderColor, 0.49f * penWidth * s, -0.49f * penWidth * c);
+ smoothVertices[index++].set(glx, gy, borderColor, -0.49f * penWidth * s, -0.49f * penWidth * c);
+ } else {
+ dy = lower ? delta : -delta;
+ smoothVertices[index++].set(grx, gy, transparent, delta, dy);
+ smoothVertices[index++].set(glx, gy, transparent, -delta, dy);
+ }
+ } else {
+ vertices[index++].set(grx, gy, fillColor);
+ vertices[index++].set(glx, gy, fillColor);
+ if (penWidth) {
+ vertices[index++].set(grx, gy, borderColor);
+ vertices[index++].set(glx, gy, borderColor);
+ }
+ }
+ ++nextGradientStop;
+ }
+
+ if (!stops.isEmpty()) {
+ if (nextGradientStop == 0) {
+ fillColor = colorToColor4ub(stops.at(0).second);
+ } else if (nextGradientStop == stops.size()) {
+ fillColor = colorToColor4ub(stops.last().second);
+ } else {
+ const QGradientStop &prev = stops.at(nextGradientStop - 1);
+ const QGradientStop &next = stops.at(nextGradientStop);
+ float t = (gradientPos - prev.first) / (next.first - prev.first);
+ fillColor = colorToColor4ub(prev.second) * (1 - t) + colorToColor4ub(next.second) * t;
+ }
+ }
+
+ if (hasFill) {
+ indices[fillHead++] = index;
+ indices[fillHead++] = index + 1;
+ }
+
+ if (penWidth) {
+ indices[--borderHead] = index + 4;
+ indices[--borderHead] = index + 2;
+ indices[borderTail++] = index + 3;
+ indices[borderTail++] = index + 5;
+ }
+
+ if (m_antialiasing) {
+ indices[--innerAAHead] = index + 2;
+ indices[--innerAAHead] = index;
+ indices[innerAATail++] = index + 1;
+ indices[innerAATail++] = index + 3;
+
+ float dy = part ? qMin(0.0f, height - y - delta) : qMax(0.0f, delta - y);
+ smoothVertices[index++].set(rx, y, fillColor, width - rx - delta, dy);
+ smoothVertices[index++].set(lx, y, fillColor, delta - lx, dy);
+
+ dy = part ? delta : -delta;
+ if (penWidth) {
+ smoothVertices[index++].set(rx, y, borderColor, 0.49f * penWidth * s, -0.49f * penWidth * c);
+ smoothVertices[index++].set(lx, y, borderColor, -0.49f * penWidth * s, -0.49f * penWidth * c);
+ smoothVertices[index++].set(rX, Y, borderColor, -0.49f * penWidth * s, 0.49f * penWidth * c);
+ smoothVertices[index++].set(lX, Y, borderColor, 0.49f * penWidth * s, 0.49f * penWidth * c);
+ smoothVertices[index++].set(rX, Y, transparent, delta, dy);
+ smoothVertices[index++].set(lX, Y, transparent, -delta, dy);
+
+ indices[--outerAAHead] = index - 2;
+ indices[--outerAAHead] = index - 4;
+ indices[outerAATail++] = index - 3;
+ indices[outerAATail++] = index - 1;
+ } else {
+ smoothVertices[index++].set(rx, y, transparent, delta, dy);
+ smoothVertices[index++].set(lx, y, transparent, -delta, dy);
+ }
+ } else {
+ vertices[index++].set(rx, y, fillColor);
+ vertices[index++].set(lx, y, fillColor);
+ if (penWidth) {
+ vertices[index++].set(rx, y, borderColor);
+ vertices[index++].set(lx, y, borderColor);
+ vertices[index++].set(rX, Y, borderColor);
+ vertices[index++].set(lX, Y, borderColor);
+ }
+ }
+
+ py = y;
+ plx = lx;
+ prx = rx;
+
+ // Rotate
+ qreal tmp = c;
+ c = c * cosStep - s * sinStep;
+ s = s * cosStep + tmp * sinStep;
+ }
+ }
+ Q_ASSERT(index == vertexCount);
+
+ // Close the triangle strips.
+ if (m_antialiasing) {
+ indices[--innerAAHead] = indices[innerAATail - 1];
+ indices[--innerAAHead] = indices[innerAATail - 2];
+ Q_ASSERT(innerAATail <= indexCount);
+ }
+ if (penWidth) {
+ indices[--borderHead] = indices[borderTail - 1];
+ indices[--borderHead] = indices[borderTail - 2];
+ Q_ASSERT(borderTail <= indexCount);
+ }
+ if (m_antialiasing && penWidth) {
+ indices[--outerAAHead] = indices[outerAATail - 1];
+ indices[--outerAAHead] = indices[outerAATail - 2];
+ Q_ASSERT(outerAATail == indexCount);
+ }
+ } else {
+ // Straight corners.
+ QRectF innerRect = m_rect;
+ QRectF outerRect = m_rect;
+
+ if (penWidth)
+ innerRect.adjust(1.0f * penWidth, 1.0f * penWidth, -1.0f * penWidth, -1.0f * penWidth);
+
+ float delta = qMin(width, height) * 0.5f;
+ int innerVertexCount = 4 + gradientIntersections * 2;
+ int outerVertexCount = 4;
+ int vertexCount = innerVertexCount;
+ if (m_antialiasing || penWidth)
+ vertexCount += innerVertexCount;
+ if (penWidth)
+ vertexCount += outerVertexCount;
+ if (m_antialiasing && penWidth)
+ vertexCount += outerVertexCount;
+
+ int fillIndexCount = innerVertexCount;
+ int innerAAIndexCount = innerVertexCount * 2 + 2;
+ int borderIndexCount = innerVertexCount * 2 + 2;
+ int outerAAIndexCount = outerVertexCount * 2 + 2;
+ int indexCount = 0;
+ int fillHead = 0;
+ int innerAAHead = 0;
+ int innerAATail = 0;
+ int borderHead = 0;
+ int borderTail = 0;
+ int outerAAHead = 0;
+ int outerAATail = 0;
+ bool hasFill = m_color.alpha() > 0 || !stops.isEmpty();
+ if (hasFill)
+ indexCount += fillIndexCount;
+ if (m_antialiasing) {
+ innerAATail = innerAAHead = indexCount + (innerAAIndexCount >> 1) + 1;
+ indexCount += innerAAIndexCount;
+ }
+ if (penWidth) {
+ borderTail = borderHead = indexCount + (borderIndexCount >> 1) + 1;
+ indexCount += borderIndexCount;
+ }
+ if (m_antialiasing && penWidth) {
+ outerAATail = outerAAHead = indexCount + (outerAAIndexCount >> 1) + 1;
+ indexCount += outerAAIndexCount;
+ }
+
+ g->allocate(vertexCount, indexCount);
+ vertices = reinterpret_cast<Vertex *>(g->vertexData());
+ memset(vertices, 0, vertexCount * vertexStride);
+ quint16 *indices = g->indexDataAsUShort();
+ quint16 index = 0;
+
+ float lx = innerRect.left();
+ float rx = innerRect.right();
+ float lX = outerRect.left();
+ float rX = outerRect.right();
+
+ for (int part = -1; part <= 1; part += 2) {
+ float y = (part == 1 ? innerRect.bottom() : innerRect.top());
+ float Y = (part == 1 ? outerRect.bottom() : outerRect.top());
+ gradientPos = (y - innerRect.top() + penWidth) / height;
+
+ while (nextGradientStop <= lastGradientStop && stops.at(nextGradientStop).first <= gradientPos) {
+ // Insert vertices at gradient stops.
+ float gy = (innerRect.top() - penWidth) + stops.at(nextGradientStop).first * height;
+
+ fillColor = colorToColor4ub(stops.at(nextGradientStop).second);
+
+ if (hasFill) {
+ indices[fillHead++] = index;
+ indices[fillHead++] = index + 1;
+ }
+
+ if (penWidth) {
+ --borderHead;
+ indices[borderHead] = indices[borderHead + 2];
+ indices[--borderHead] = index + 2;
+ indices[borderTail++] = index + 3;
+ indices[borderTail] = indices[borderTail - 2];
+ ++borderTail;
+ }
+
+ if (m_antialiasing) {
+ indices[--innerAAHead] = index + 2;
+ indices[--innerAAHead] = index;
+ indices[innerAATail++] = index + 1;
+ indices[innerAATail++] = index + 3;
+
+ bool lower = stops.at(nextGradientStop).first > 0.5f;
+ float dy = lower ? qMin(0.0f, height - gy - delta) : qMax(0.0f, delta - gy);
+ smoothVertices[index++].set(rx, gy, fillColor, width - rx - delta, dy);
+ smoothVertices[index++].set(lx, gy, fillColor, delta - lx, dy);
+ if (penWidth) {
+ smoothVertices[index++].set(rx, gy, borderColor, 0.49f * penWidth, (lower ? 0.49f : -0.49f) * penWidth);
+ smoothVertices[index++].set(lx, gy, borderColor, -0.49f * penWidth, (lower ? 0.49f : -0.49f) * penWidth);
+ } else {
+ smoothVertices[index++].set(rx, gy, transparent, delta, lower ? delta : -delta);
+ smoothVertices[index++].set(lx, gy, transparent, -delta, lower ? delta : -delta);
+ }
+ } else {
+ vertices[index++].set(rx, gy, fillColor);
+ vertices[index++].set(lx, gy, fillColor);
+ if (penWidth) {
+ vertices[index++].set(rx, gy, borderColor);
+ vertices[index++].set(lx, gy, borderColor);
+ }
+ }
+ ++nextGradientStop;
+ }
+
+ if (!stops.isEmpty()) {
+ if (nextGradientStop == 0) {
+ fillColor = colorToColor4ub(stops.at(0).second);
+ } else if (nextGradientStop == stops.size()) {
+ fillColor = colorToColor4ub(stops.last().second);
+ } else {
+ const QGradientStop &prev = stops.at(nextGradientStop - 1);
+ const QGradientStop &next = stops.at(nextGradientStop);
+ float t = (gradientPos - prev.first) / (next.first - prev.first);
+ fillColor = colorToColor4ub(prev.second) * (1 - t) + colorToColor4ub(next.second) * t;
+ }
+ }
+
+ if (hasFill) {
+ indices[fillHead++] = index;
+ indices[fillHead++] = index + 1;
+ }
+
+ if (penWidth) {
+ indices[--borderHead] = index + 4;
+ indices[--borderHead] = index + 2;
+ indices[borderTail++] = index + 3;
+ indices[borderTail++] = index + 5;
+ }
+
+ if (m_antialiasing) {
+ indices[--innerAAHead] = index + 2;
+ indices[--innerAAHead] = index;
+ indices[innerAATail++] = index + 1;
+ indices[innerAATail++] = index + 3;
+
+ float dy = part == 1 ? qMin(0.0f, height - y - delta) : qMax(0.0f, delta - y);
+ smoothVertices[index++].set(rx, y, fillColor, width - rx - delta, dy);
+ smoothVertices[index++].set(lx, y, fillColor, delta - lx, dy);
+
+ if (penWidth) {
+ smoothVertices[index++].set(rx, y, borderColor, 0.49f * penWidth, 0.49f * penWidth * part);
+ smoothVertices[index++].set(lx, y, borderColor, -0.49f * penWidth, 0.49f * penWidth * part);
+ smoothVertices[index++].set(rX, Y, borderColor, -0.49f * penWidth, -0.49f * penWidth * part);
+ smoothVertices[index++].set(lX, Y, borderColor, 0.49f * penWidth, -0.49f * penWidth * part);
+ smoothVertices[index++].set(rX, Y, transparent, delta, delta * part);
+ smoothVertices[index++].set(lX, Y, transparent, -delta, delta * part);
+
+ indices[--outerAAHead] = index - 2;
+ indices[--outerAAHead] = index - 4;
+ indices[outerAATail++] = index - 3;
+ indices[outerAATail++] = index - 1;
+ } else {
+ smoothVertices[index++].set(rx, y, transparent, delta, delta * part);
+ smoothVertices[index++].set(lx, y, transparent, -delta, delta * part);
+ }
+ } else {
+ vertices[index++].set(rx, y, fillColor);
+ vertices[index++].set(lx, y, fillColor);
+ if (penWidth) {
+ vertices[index++].set(rx, y, borderColor);
+ vertices[index++].set(lx, y, borderColor);
+ vertices[index++].set(rX, Y, borderColor);
+ vertices[index++].set(lX, Y, borderColor);
+ }
+ }
+ }
+ Q_ASSERT(index == vertexCount);
+
+ // Close the triangle strips.
+ if (m_antialiasing) {
+ indices[--innerAAHead] = indices[innerAATail - 1];
+ indices[--innerAAHead] = indices[innerAATail - 2];
+ Q_ASSERT(innerAATail <= indexCount);
+ }
+ if (penWidth) {
+ indices[--borderHead] = indices[borderTail - 1];
+ indices[--borderHead] = indices[borderTail - 2];
+ Q_ASSERT(borderTail <= indexCount);
+ }
+ if (m_antialiasing && penWidth) {
+ indices[--outerAAHead] = indices[outerAATail - 1];
+ indices[--outerAAHead] = indices[outerAATail - 2];
+ Q_ASSERT(outerAATail == indexCount);
+ }
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/qsgbasicrectanglenode_p.h b/src/quick/scenegraph/qsgbasicrectanglenode_p.h
new file mode 100644
index 0000000000..b1d1457590
--- /dev/null
+++ b/src/quick/scenegraph/qsgbasicrectanglenode_p.h
@@ -0,0 +1,99 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#ifndef QSGBASICRECTANGLENODE_P_H
+#define QSGBASICRECTANGLENODE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qsgadaptationlayer_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_QUICK_PRIVATE_EXPORT QSGBasicRectangleNode : public QSGRectangleNode
+{
+public:
+ QSGBasicRectangleNode();
+
+ void setRect(const QRectF &rect) override;
+ void setColor(const QColor &color) override;
+ void setPenColor(const QColor &color) override;
+ void setPenWidth(qreal width) override;
+ void setGradientStops(const QGradientStops &stops) override;
+ void setRadius(qreal radius) override;
+ void setAntialiasing(bool antialiasing) override;
+ void setAligned(bool aligned) override;
+ void update() override;
+
+protected:
+ virtual bool supportsAntialiasing() const { return true; }
+ virtual void updateMaterialAntialiasing() = 0;
+ virtual void updateMaterialBlending(QSGNode::DirtyState *state) = 0;
+
+ void updateGeometry();
+ void updateGradientTexture();
+
+ QRectF m_rect;
+ QGradientStops m_gradient_stops;
+ QColor m_color;
+ QColor m_border_color;
+ qreal m_radius;
+ qreal m_pen_width;
+
+ uint m_aligned : 1;
+ uint m_antialiasing : 1;
+ uint m_gradient_is_opaque : 1;
+ uint m_dirty_geometry : 1;
+
+ QSGGeometry m_geometry;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/quick/scenegraph/qsgcontext.cpp b/src/quick/scenegraph/qsgcontext.cpp
index 2ba16e7328..27d0f01753 100644
--- a/src/quick/scenegraph/qsgcontext.cpp
+++ b/src/quick/scenegraph/qsgcontext.cpp
@@ -38,27 +38,13 @@
****************************************************************************/
#include <QtQuick/private/qsgcontext_p.h>
-#include <QtQuick/private/qsgbatchrenderer_p.h>
-#include <QtQuick/private/qsgdistancefieldutil_p.h>
-#include <QtQuick/private/qsgdefaultdistancefieldglyphcache_p.h>
-#include <QtQuick/private/qsgdefaultrectanglenode_p.h>
-#include <QtQuick/private/qsgdefaultimagenode_p.h>
-#include <QtQuick/private/qsgdefaultpainternode_p.h>
-#include <QtQuick/private/qsgdefaultglyphnode_p.h>
-#include <QtQuick/private/qsgdistancefieldglyphnode_p.h>
-#include <QtQuick/private/qsgdistancefieldglyphnode_p_p.h>
-#include <QtQuick/private/qsgatlastexture_p.h>
-#include <QtQuick/private/qsgrenderloop_p.h>
-#include <QtQuick/private/qsgdefaultlayer_p.h>
-
#include <QtQuick/private/qsgtexture_p.h>
#include <QtQuick/private/qquickpixmapcache_p.h>
+#include <QtQuick/private/qsgadaptationlayer_p.h>
#include <QGuiApplication>
#include <QScreen>
-#include <QOpenGLContext>
#include <QQuickWindow>
-#include <QtGui/qopenglframebufferobject.h>
#include <private/qqmlglobal_p.h>
@@ -69,8 +55,6 @@
#include <private/qobject_p.h>
#include <qmutex.h>
-DEFINE_BOOL_CONFIG_OPTION(qmlDisableDistanceField, QML_DISABLE_DISTANCEFIELD)
-
/*
Comments about this class from Gunnar:
@@ -114,28 +98,6 @@ Q_LOGGING_CATEGORY(QSG_LOG_TIME_GLYPH, "qt.scenegraph.time.glyph")
// Timing inside the renderer base class
Q_LOGGING_CATEGORY(QSG_LOG_TIME_RENDERER, "qt.scenegraph.time.renderer")
-class QSGContextPrivate : public QObjectPrivate
-{
-public:
- QSGContextPrivate()
- : antialiasingMethod(QSGContext::UndecidedAntialiasing)
- , distanceFieldDisabled(qmlDisableDistanceField())
- , distanceFieldAntialiasing(QSGGlyphNode::HighQualitySubPixelAntialiasing)
- , distanceFieldAntialiasingDecided(false)
- {
- }
-
- ~QSGContextPrivate()
- {
- }
-
- QMutex mutex;
- QSGContext::AntialiasingMethod antialiasingMethod;
- bool distanceFieldDisabled;
- QSGDistanceFieldGlyphNode::AntialiasingMode distanceFieldAntialiasing;
- bool distanceFieldAntialiasingDecided;
-};
-
bool qsg_useConsistentTiming()
{
int use = -1;
@@ -274,20 +236,6 @@ public:
QSGTexture *texture;
};
-namespace QSGMultisampleAntialiasing {
- class ImageNode : public QSGDefaultImageNode {
- public:
- void setAntialiasing(bool) { }
- };
-
-
- class RectangleNode : public QSGDefaultRectangleNode {
- public:
- void setAntialiasing(bool) { }
- };
-}
-
-
/*!
\class QSGContext
@@ -300,92 +248,16 @@ namespace QSGMultisampleAntialiasing {
*/
QSGContext::QSGContext(QObject *parent) :
- QObject(*(new QSGContextPrivate), parent)
+ QObject(parent)
{
- Q_D(QSGContext);
- if (Q_UNLIKELY(!qEnvironmentVariableIsEmpty("QSG_DISTANCEFIELD_ANTIALIASING"))) {
- const QByteArray mode = qgetenv("QSG_DISTANCEFIELD_ANTIALIASING");
- d->distanceFieldAntialiasingDecided = true;
- if (mode == "subpixel")
- d->distanceFieldAntialiasing = QSGGlyphNode::HighQualitySubPixelAntialiasing;
- else if (mode == "subpixel-lowq")
- d->distanceFieldAntialiasing = QSGGlyphNode::LowQualitySubPixelAntialiasing;
- else if (mode == "gray")
- d->distanceFieldAntialiasing = QSGGlyphNode::GrayAntialiasing;
- }
-
- // Adds compatibility with Qt 5.3 and earlier's QSG_RENDER_TIMING
- if (qEnvironmentVariableIsSet("QSG_RENDER_TIMING")) {
- const_cast<QLoggingCategory &>(QSG_LOG_TIME_GLYPH()).setEnabled(QtDebugMsg, true);
- const_cast<QLoggingCategory &>(QSG_LOG_TIME_TEXTURE()).setEnabled(QtDebugMsg, true);
- const_cast<QLoggingCategory &>(QSG_LOG_TIME_RENDERER()).setEnabled(QtDebugMsg, true);
- const_cast<QLoggingCategory &>(QSG_LOG_TIME_RENDERLOOP()).setEnabled(QtDebugMsg, true);
- const_cast<QLoggingCategory &>(QSG_LOG_TIME_COMPILATION()).setEnabled(QtDebugMsg, true);
- }
}
-
QSGContext::~QSGContext()
{
}
-QSGRenderContext *QSGContext::createRenderContext()
+void QSGContext::renderContextInitialized(QSGRenderContext *)
{
- return new QSGRenderContext(this);
-}
-
-void QSGContext::renderContextInitialized(QSGRenderContext *renderContext)
-{
- Q_D(QSGContext);
-
- d->mutex.lock();
- if (d->antialiasingMethod == UndecidedAntialiasing) {
- if (Q_UNLIKELY(qEnvironmentVariableIsSet("QSG_ANTIALIASING_METHOD"))) {
- const QByteArray aaType = qgetenv("QSG_ANTIALIASING_METHOD");
- if (aaType == "msaa")
- d->antialiasingMethod = MsaaAntialiasing;
- else if (aaType == "vertex")
- d->antialiasingMethod = VertexAntialiasing;
- }
- if (d->antialiasingMethod == UndecidedAntialiasing) {
- if (renderContext->openglContext()->format().samples() > 0)
- d->antialiasingMethod = MsaaAntialiasing;
- else
- d->antialiasingMethod = VertexAntialiasing;
- }
- }
-
- // With OpenGL ES, except for Angle on Windows, use GrayAntialiasing, unless
- // some value had been requested explicitly. This could not be decided
- // before without a context. Now the context is ready.
- if (!d->distanceFieldAntialiasingDecided) {
- d->distanceFieldAntialiasingDecided = true;
-#ifndef Q_OS_WIN32
- if (renderContext->openglContext()->isOpenGLES())
- d->distanceFieldAntialiasing = QSGGlyphNode::GrayAntialiasing;
-#endif
- }
-
- static bool dumped = false;
- if (!dumped && QSG_LOG_INFO().isDebugEnabled()) {
- dumped = true;
- QSurfaceFormat format = renderContext->openglContext()->format();
- QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions();
- qCDebug(QSG_LOG_INFO) << "R/G/B/A Buffers: " << format.redBufferSize() << format.greenBufferSize() << format.blueBufferSize() << format.alphaBufferSize();
- qCDebug(QSG_LOG_INFO) << "Depth Buffer: " << format.depthBufferSize();
- qCDebug(QSG_LOG_INFO) << "Stencil Buffer: " << format.stencilBufferSize();
- qCDebug(QSG_LOG_INFO) << "Samples: " << format.samples();
- qCDebug(QSG_LOG_INFO) << "GL_VENDOR: " << (const char *) funcs->glGetString(GL_VENDOR);
- qCDebug(QSG_LOG_INFO) << "GL_RENDERER: " << (const char *) funcs->glGetString(GL_RENDERER);
- qCDebug(QSG_LOG_INFO) << "GL_VERSION: " << (const char *) funcs->glGetString(GL_VERSION);
- QSet<QByteArray> exts = renderContext->openglContext()->extensions();
- QByteArray all; foreach (const QByteArray &e, exts) all += ' ' + e;
- qCDebug(QSG_LOG_INFO) << "GL_EXTENSIONS: " << all.constData();
- qCDebug(QSG_LOG_INFO) << "Max Texture Size: " << renderContext->maxTextureSize();
- qCDebug(QSG_LOG_INFO) << "Debug context: " << format.testOption(QSurfaceFormat::DebugContext);
- }
-
- d->mutex.unlock();
}
void QSGContext::renderContextInvalidated(QSGRenderContext *)
@@ -406,88 +278,33 @@ QSGRectangleNode *QSGContext::createRectangleNode(const QRectF &rect, const QCol
}
/*!
- Factory function for scene graph backends of the Rectangle element.
- */
-QSGRectangleNode *QSGContext::createRectangleNode()
-{
- Q_D(QSGContext);
- return d->antialiasingMethod == MsaaAntialiasing
- ? new QSGMultisampleAntialiasing::RectangleNode
- : new QSGDefaultRectangleNode;
-}
-
-/*!
- Factory function for scene graph backends of the Image element.
- */
-QSGImageNode *QSGContext::createImageNode()
-{
- Q_D(QSGContext);
- return d->antialiasingMethod == MsaaAntialiasing
- ? new QSGMultisampleAntialiasing::ImageNode
- : new QSGDefaultImageNode;
-}
-
-/*!
- Factory function for scene graph backends of Painter elements
- */
-QSGPainterNode *QSGContext::createPainterNode(QQuickPaintedItem *item)
-{
- return new QSGDefaultPainterNode(item);
-}
-
-/*!
- Factory function for scene graph backends of the Text elements;
+ Creates a new shader effect helper instance. This function is called on the
+ gui thread, unlike the others. This is necessary in order to provide
+ adaptable, backend-specific shader effect functionality to the gui thread too.
*/
-QSGGlyphNode *QSGContext::createGlyphNode(QSGRenderContext *rc, bool preferNativeGlyphNode)
+QSGGuiThreadShaderEffectManager *QSGContext::createGuiThreadShaderEffectManager()
{
- Q_D(QSGContext);
-
- if (d->distanceFieldDisabled || preferNativeGlyphNode) {
- return new QSGDefaultGlyphNode;
- } else {
- QSGDistanceFieldGlyphNode *node = new QSGDistanceFieldGlyphNode(rc);
- node->setPreferredAntialiasingMode(d->distanceFieldAntialiasing);
- return node;
- }
+ return nullptr;
}
/*!
- * Factory function for scene graph backends of the QStyle stylable elements. Returns a
- * null pointer if the backend doesn't provide its own node type.
+ Creates a new shader effect node. The default of returning nullptr is
+ valid as long as the backend does not claim SupportsShaderEffectNode or
+ ignoring ShaderEffect elements is acceptable.
*/
-QSGNinePatchNode *QSGContext::createNinePatchNode()
+QSGShaderEffectNode *QSGContext::createShaderEffectNode(QSGRenderContext *, QSGGuiThreadShaderEffectManager *)
{
- return 0;
+ return nullptr;
}
/*!
- Factory function for scene graph backends of layers.
+ Creates a new animation driver.
*/
-QSGLayer *QSGContext::createLayer(QSGRenderContext *renderContext)
-{
- return new QSGDefaultLayer(renderContext);
-}
-
-QSurfaceFormat QSGContext::defaultSurfaceFormat() const
+QAnimationDriver *QSGContext::createAnimationDriver(QObject *parent)
{
- QSurfaceFormat format = QSurfaceFormat::defaultFormat();
- static bool useDepth = qEnvironmentVariableIsEmpty("QSG_NO_DEPTH_BUFFER");
- static bool useStencil = qEnvironmentVariableIsEmpty("QSG_NO_STENCIL_BUFFER");
- static bool enableDebug = qEnvironmentVariableIsSet("QSG_OPENGL_DEBUG");
- format.setDepthBufferSize(useDepth ? 24 : 0);
- format.setStencilBufferSize(useStencil ? 8 : 0);
- if (enableDebug)
- format.setOption(QSurfaceFormat::DebugContext);
- if (QQuickWindow::hasDefaultAlphaBuffer())
- format.setAlphaBufferSize(8);
- format.setSwapBehavior(QSurfaceFormat::DoubleBuffer);
- return format;
+ return new QSGAnimationDriver(parent);
}
-/*!
- Returns the minimum supported framebuffer object size.
- */
-
QSize QSGContext::minimumFBOSize() const
{
#ifdef Q_OS_MAC
@@ -497,46 +314,22 @@ QSize QSGContext::minimumFBOSize() const
return QSize(1, 1);
}
-
-
/*!
- Sets whether or not the scene graph should use the distance field technique to render text
- */
-void QSGContext::setDistanceFieldEnabled(bool enabled)
-{
- d_func()->distanceFieldDisabled = !enabled;
-}
+ Returns a pointer to the (presumably) global renderer interface.
-
-/*!
- Returns true if the scene graph uses the distance field technique to render text
+ \note This function may be called on the gui thread in order to get access
+ to QSGRendererInterface::graphicsAPI().
*/
-bool QSGContext::isDistanceFieldEnabled() const
+QSGRendererInterface *QSGContext::rendererInterface(QSGRenderContext *renderContext)
{
- return !d_func()->distanceFieldDisabled;
-}
-
-
-
-/*!
- Creates a new animation driver.
- */
-
-QAnimationDriver *QSGContext::createAnimationDriver(QObject *parent)
-{
- return new QSGAnimationDriver(parent);
+ Q_UNUSED(renderContext);
+ qWarning("QSGRendererInterface not implemented");
+ return nullptr;
}
QSGRenderContext::QSGRenderContext(QSGContext *context)
- : m_gl(0)
- , m_sg(context)
- , m_atlasManager(0)
- , m_depthStencilManager(0)
+ : m_sg(context)
, m_distanceFieldCacheManager(0)
- , m_maxTextureSize(0)
- , m_brokenIBOs(false)
- , m_serializedRender(false)
- , m_attachToGLContext(true)
{
}
@@ -545,55 +338,31 @@ QSGRenderContext::~QSGRenderContext()
invalidate();
}
-void QSGRenderContext::endSync()
+void QSGRenderContext::initialize(void *context)
{
- qDeleteAll(m_texturesToDelete);
- m_texturesToDelete.clear();
+ Q_UNUSED(context);
}
-static QBasicMutex qsg_framerender_mutex;
-
-void QSGRenderContext::renderNextFrame(QSGRenderer *renderer, GLuint fboId)
+void QSGRenderContext::invalidate()
{
- if (m_serializedRender)
- qsg_framerender_mutex.lock();
-
- renderer->renderScene(fboId);
-
- if (m_serializedRender)
- qsg_framerender_mutex.unlock();
+ m_sg->renderContextInvalidated(this);
+ emit invalidated();
+}
+void QSGRenderContext::endSync()
+{
+ qDeleteAll(m_texturesToDelete);
+ m_texturesToDelete.clear();
}
/*!
Factory function for scene graph backends of the distance-field glyph cache.
*/
-QSGDistanceFieldGlyphCache *QSGRenderContext::distanceFieldGlyphCache(const QRawFont &font)
-{
- if (!m_distanceFieldCacheManager)
- m_distanceFieldCacheManager = new QSGDistanceFieldGlyphCacheManager;
-
- QSGDistanceFieldGlyphCache *cache = m_distanceFieldCacheManager->cache(font);
- if (!cache) {
- cache = new QSGDefaultDistanceFieldGlyphCache(m_distanceFieldCacheManager, openglContext(), font);
- m_distanceFieldCacheManager->insertCache(font, cache);
- }
-
- return cache;
-}
-
-void QSGRenderContext::setAttachToGLContext(bool attach)
+QSGDistanceFieldGlyphCache *QSGRenderContext::distanceFieldGlyphCache(const QRawFont &)
{
- Q_ASSERT(!isValid());
- m_attachToGLContext = attach;
+ return nullptr;
}
-#define QSG_RENDERCONTEXT_PROPERTY "_q_sgrendercontext"
-
-QSGRenderContext *QSGRenderContext::from(QOpenGLContext *context)
-{
- return qobject_cast<QSGRenderContext *>(context->property(QSG_RENDERCONTEXT_PROPERTY).value<QObject *>());
-}
void QSGRenderContext::registerFontengineForCleanup(QFontEngine *engine)
{
@@ -602,178 +371,18 @@ void QSGRenderContext::registerFontengineForCleanup(QFontEngine *engine)
}
/*!
- Initializes the scene graph render context with the GL context \a context. This also
- emits the ready() signal so that the QML graph can start building scene graph nodes.
- */
-void QSGRenderContext::initialize(QOpenGLContext *context)
-{
- QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions();
- funcs->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &m_maxTextureSize);
-
- // Sanity check the surface format, in case it was overridden by the application
- QSurfaceFormat requested = m_sg->defaultSurfaceFormat();
- QSurfaceFormat actual = context->format();
- if (requested.depthBufferSize() > 0 && actual.depthBufferSize() <= 0)
- qWarning("QSGContext::initialize: depth buffer support missing, expect rendering errors");
- if (requested.stencilBufferSize() > 0 && actual.stencilBufferSize() <= 0)
- qWarning("QSGContext::initialize: stencil buffer support missing, expect rendering errors");
-
- if (!m_atlasManager)
- m_atlasManager = new QSGAtlasTexture::Manager();
-
- Q_ASSERT_X(!m_gl, "QSGRenderContext::initialize", "already initialized!");
- m_gl = context;
- if (m_attachToGLContext) {
- Q_ASSERT(!context->property(QSG_RENDERCONTEXT_PROPERTY).isValid());
- context->setProperty(QSG_RENDERCONTEXT_PROPERTY, QVariant::fromValue(this));
- }
- m_sg->renderContextInitialized(this);
-
-#ifdef Q_OS_LINUX
- const char *vendor = (const char *) funcs->glGetString(GL_VENDOR);
- if (strstr(vendor, "nouveau"))
- m_brokenIBOs = true;
- const char *renderer = (const char *) funcs->glGetString(GL_RENDERER);
- if (strstr(renderer, "llvmpipe"))
- m_serializedRender = true;
- if (strstr(vendor, "Hisilicon Technologies") && strstr(renderer, "Immersion.16"))
- m_brokenIBOs = true;
-#endif
-
- emit initialized();
-}
-
-void QSGRenderContext::invalidate()
-{
- if (!m_gl)
- return;
-
- qDeleteAll(m_texturesToDelete);
- m_texturesToDelete.clear();
-
- qDeleteAll(m_textures);
- m_textures.clear();
-
- /* The cleanup of the atlas textures is a bit intriguing.
- As part of the cleanup in the threaded render loop, we
- do:
- 1. call this function
- 2. call QCoreApp::sendPostedEvents() to immediately process
- any pending deferred deletes.
- 3. delete the GL context.
-
- As textures need the atlas manager while cleaning up, the
- manager needs to be cleaned up after the textures, so
- we post a deleteLater here at the very bottom so it gets
- deferred deleted last.
-
- Another alternative would be to use a QPointer in
- QSGAtlasTexture::Texture, but this seemed simpler.
- */
- m_atlasManager->invalidate();
- m_atlasManager->deleteLater();
- m_atlasManager = 0;
-
- // The following piece of code will read/write to the font engine's caches,
- // potentially from different threads. However, this is safe because this
- // code is only called from QQuickWindow's shutdown which is called
- // only when the GUI is blocked, and multiple threads will call it in
- // sequence. (see qsgdefaultglyphnode_p.cpp's init())
- for (QSet<QFontEngine *>::const_iterator it = m_fontEnginesToClean.constBegin(),
- end = m_fontEnginesToClean.constEnd(); it != end; ++it) {
- (*it)->clearGlyphCache(m_gl);
- if (!(*it)->ref.deref())
- delete *it;
- }
- m_fontEnginesToClean.clear();
-
- delete m_depthStencilManager;
- m_depthStencilManager = 0;
-
- delete m_distanceFieldCacheManager;
- m_distanceFieldCacheManager = 0;
-
- if (m_gl->property(QSG_RENDERCONTEXT_PROPERTY) == QVariant::fromValue(this))
- m_gl->setProperty(QSG_RENDERCONTEXT_PROPERTY, QVariant());
- m_gl = 0;
-
- m_sg->renderContextInvalidated(this);
- emit invalidated();
-}
-
-/*!
- Returns a shared pointer to a depth stencil buffer that can be used with \a fbo.
- */
-QSharedPointer<QSGDepthStencilBuffer> QSGRenderContext::depthStencilBufferForFbo(QOpenGLFramebufferObject *fbo)
-{
- if (!m_gl)
- return QSharedPointer<QSGDepthStencilBuffer>();
- QSGDepthStencilBufferManager *manager = depthStencilBufferManager();
- QSGDepthStencilBuffer::Format format;
- format.size = fbo->size();
- format.samples = fbo->format().samples();
- format.attachments = QSGDepthStencilBuffer::DepthAttachment | QSGDepthStencilBuffer::StencilAttachment;
- QSharedPointer<QSGDepthStencilBuffer> buffer = manager->bufferForFormat(format);
- if (buffer.isNull()) {
- buffer = QSharedPointer<QSGDepthStencilBuffer>(new QSGDefaultDepthStencilBuffer(m_gl, format));
- manager->insertBuffer(buffer);
- }
- return buffer;
-}
-
-/*!
- Returns a pointer to the context's depth/stencil buffer manager. This is useful for custom
- implementations of \l depthStencilBufferForFbo().
- */
-QSGDepthStencilBufferManager *QSGRenderContext::depthStencilBufferManager()
-{
- if (!m_gl)
- return 0;
- if (!m_depthStencilManager)
- m_depthStencilManager = new QSGDepthStencilBufferManager(m_gl);
- return m_depthStencilManager;
-}
-
-
-/*!
Factory function for texture objects.
If \a image is a valid image, the QSGTexture::setImage function
will be called with \a image as argument.
*/
-QSGTexture *QSGRenderContext::createTexture(const QImage &image, uint flags) const
-{
- bool atlas = flags & CreateTexture_Atlas;
- bool mipmap = flags & CreateTexture_Mipmap;
- bool alpha = flags & CreateTexture_Alpha;
-
- // The atlas implementation is only supported from the render thread and
- // does not support mipmaps.
- if (!mipmap && atlas && openglContext() && QThread::currentThread() == openglContext()->thread()) {
- QSGTexture *t = m_atlasManager->create(image, alpha);
- if (t)
- return t;
- }
-
- QSGPlainTexture *texture = new QSGPlainTexture();
- texture->setImage(image);
- if (texture->hasAlphaChannel() && !alpha)
- texture->setHasAlphaChannel(false);
-
- return texture;
-}
-
/*!
Factory function for the scene graph renderers.
The renderers are used for the toplevel renderer and once for every
QQuickShaderEffectSource used in the QML scene.
*/
-QSGRenderer *QSGRenderContext::createRenderer()
-{
- return new QSGBatchRenderer::Renderer(this);
-}
QSGTexture *QSGRenderContext::textureForFactory(QQuickTextureFactory *factory, QQuickWindow *window)
{
@@ -803,41 +412,6 @@ void QSGRenderContext::textureFactoryDestroyed(QObject *o)
m_mutex.unlock();
}
-/*!
- Compile \a shader, optionally using \a vertexCode and \a fragmentCode as
- replacement for the source code supplied by \a shader.
-
- If \a vertexCode or \a fragmentCode is supplied, the caller is responsible
- for setting up attribute bindings.
-
- \a material is supplied in case the implementation needs to take the
- material flags into account.
- */
-
-void QSGRenderContext::compile(QSGMaterialShader *shader, QSGMaterial *material, const char *vertexCode, const char *fragmentCode)
-{
- Q_UNUSED(material);
- if (vertexCode || fragmentCode) {
- Q_ASSERT_X((material->flags() & QSGMaterial::CustomCompileStep) == 0,
- "QSGRenderContext::compile()",
- "materials with custom compile step cannot have custom vertex/fragment code");
- QOpenGLShaderProgram *p = shader->program();
- p->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexCode ? vertexCode : shader->vertexShader());
- p->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentCode ? fragmentCode : shader->fragmentShader());
- p->link();
- if (!p->isLinked())
- qWarning() << "shader compilation failed:" << endl << p->log();
- } else {
- shader->compile();
- }
-}
-
-void QSGRenderContext::initialize(QSGMaterialShader *shader)
-{
- shader->program()->bind();
- shader->initialize();
-}
-
#include "qsgcontext.moc"
QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/qsgcontext_p.h b/src/quick/scenegraph/qsgcontext_p.h
index 38d0cdaccc..2139377ebb 100644
--- a/src/quick/scenegraph/qsgcontext_p.h
+++ b/src/quick/scenegraph/qsgcontext_p.h
@@ -53,6 +53,7 @@
#include <QtCore/QObject>
#include <QtCore/qabstractanimation.h>
+#include <QtCore/QMutex>
#include <QtGui/QImage>
#include <QtGui/QSurfaceFormat>
@@ -61,14 +62,9 @@
#include <private/qrawfont_p.h>
#include <QtQuick/qsgnode.h>
-#include <QtQuick/private/qsgdepthstencilbuffer_p.h>
QT_BEGIN_NAMESPACE
-namespace QSGAtlasTexture {
- class Manager;
-}
-
class QSGContextPrivate;
class QSGRectangleNode;
class QSGImageNode;
@@ -80,17 +76,15 @@ class QSGDistanceFieldGlyphCache;
class QQuickWindow;
class QSGTexture;
class QSGMaterial;
-class QSGMaterialShader;
class QSGRenderLoop;
class QSGLayer;
-
-class QOpenGLContext;
-class QOpenGLFramebufferObject;
-
class QQuickTextureFactory;
class QSGDistanceFieldGlyphCacheManager;
class QSGContext;
class QQuickPaintedItem;
+class QSGRendererInterface;
+class QSGShaderEffectNode;
+class QSGGuiThreadShaderEffectManager;
Q_DECLARE_LOGGING_CATEGORY(QSG_LOG_TIME_RENDERLOOP)
Q_DECLARE_LOGGING_CATEGORY(QSG_LOG_TIME_COMPILATION)
@@ -112,39 +106,24 @@ public:
};
QSGRenderContext(QSGContext *context);
- ~QSGRenderContext();
+ virtual ~QSGRenderContext();
- QOpenGLContext *openglContext() const { return m_gl; }
QSGContext *sceneGraphContext() const { return m_sg; }
- virtual bool isValid() const { return m_gl; }
+ virtual bool isValid() const { return true; }
- virtual void initialize(QOpenGLContext *context);
+ virtual void initialize(void *context);
virtual void invalidate();
-
- virtual void renderNextFrame(QSGRenderer *renderer, GLuint fboId);
+ virtual void renderNextFrame(QSGRenderer *renderer, uint fboId) = 0;
virtual void endSync();
- virtual QSharedPointer<QSGDepthStencilBuffer> depthStencilBufferForFbo(QOpenGLFramebufferObject *fbo);
- QSGDepthStencilBufferManager *depthStencilBufferManager();
-
virtual QSGDistanceFieldGlyphCache *distanceFieldGlyphCache(const QRawFont &font);
QSGTexture *textureForFactory(QQuickTextureFactory *factory, QQuickWindow *window);
- virtual QSGTexture *createTexture(const QImage &image, uint flags = CreateTexture_Alpha) const;
+ virtual QSGTexture *createTexture(const QImage &image, uint flags = CreateTexture_Alpha) const = 0;
+ virtual QSGRenderer *createRenderer() = 0;
- virtual QSGRenderer *createRenderer();
-
- virtual void compile(QSGMaterialShader *shader, QSGMaterial *material, const char *vertexCode = 0, const char *fragmentCode = 0);
- virtual void initialize(QSGMaterialShader *shader);
-
- void setAttachToGLContext(bool attach);
void registerFontengineForCleanup(QFontEngine *engine);
- static QSGRenderContext *from(QOpenGLContext *context);
-
- bool hasBrokenIndexBufferObjects() const { return m_brokenIBOs; }
- int maxTextureSize() const { return m_maxTextureSize; }
-
Q_SIGNALS:
void initialized();
void invalidated();
@@ -153,29 +132,20 @@ public Q_SLOTS:
void textureFactoryDestroyed(QObject *o);
protected:
- QOpenGLContext *m_gl;
QSGContext *m_sg;
QMutex m_mutex;
QHash<QQuickTextureFactory *, QSGTexture *> m_textures;
QSet<QSGTexture *> m_texturesToDelete;
- QSGAtlasTexture::Manager *m_atlasManager;
-
- QSGDepthStencilBufferManager *m_depthStencilManager;
QSGDistanceFieldGlyphCacheManager *m_distanceFieldCacheManager;
QSet<QFontEngine *> m_fontEnginesToClean;
- int m_maxTextureSize;
- bool m_brokenIBOs;
- bool m_serializedRender;
- bool m_attachToGLContext;
};
class Q_QUICK_PRIVATE_EXPORT QSGContext : public QObject
{
Q_OBJECT
- Q_DECLARE_PRIVATE(QSGContext)
public:
enum AntialiasingMethod {
@@ -185,26 +155,28 @@ public:
};
explicit QSGContext(QObject *parent = 0);
- ~QSGContext();
+ virtual ~QSGContext();
virtual void renderContextInitialized(QSGRenderContext *renderContext);
virtual void renderContextInvalidated(QSGRenderContext *renderContext);
- virtual QSGRenderContext *createRenderContext();
+ virtual QSGRenderContext *createRenderContext() = 0;
QSGRectangleNode *createRectangleNode(const QRectF &rect, const QColor &c);
- virtual QSGRectangleNode *createRectangleNode();
- virtual QSGImageNode *createImageNode();
- virtual QSGPainterNode *createPainterNode(QQuickPaintedItem *item);
- virtual QSGGlyphNode *createGlyphNode(QSGRenderContext *rc, bool preferNativeGlyphNode);
- virtual QSGNinePatchNode *createNinePatchNode();
- virtual QSGLayer *createLayer(QSGRenderContext *renderContext);
+ virtual QSGRectangleNode *createRectangleNode() = 0;
+ virtual QSGImageNode *createImageNode() = 0;
+ virtual QSGPainterNode *createPainterNode(QQuickPaintedItem *item) = 0;
+ virtual QSGGlyphNode *createGlyphNode(QSGRenderContext *rc, bool preferNativeGlyphNode) = 0;
+ virtual QSGNinePatchNode *createNinePatchNode() = 0;
+ virtual QSGLayer *createLayer(QSGRenderContext *renderContext) = 0;
+ virtual QSGGuiThreadShaderEffectManager *createGuiThreadShaderEffectManager();
+ virtual QSGShaderEffectNode *createShaderEffectNode(QSGRenderContext *renderContext,
+ QSGGuiThreadShaderEffectManager *mgr);
virtual QAnimationDriver *createAnimationDriver(QObject *parent);
virtual QSize minimumFBOSize() const;
- virtual QSurfaceFormat defaultSurfaceFormat() const;
+ virtual QSurfaceFormat defaultSurfaceFormat() const = 0;
- void setDistanceFieldEnabled(bool enabled);
- bool isDistanceFieldEnabled() const;
+ virtual QSGRendererInterface *rendererInterface(QSGRenderContext *renderContext);
static QSGContext *createDefaultContext();
static QQuickTextureFactory *createTextureFactoryFromImage(const QImage &image);
diff --git a/src/quick/scenegraph/qsgcontextplugin.cpp b/src/quick/scenegraph/qsgcontextplugin.cpp
index f5c2b6880b..5a5a16e005 100644
--- a/src/quick/scenegraph/qsgcontextplugin.cpp
+++ b/src/quick/scenegraph/qsgcontextplugin.cpp
@@ -43,8 +43,16 @@
#include <QtCore/private/qfactoryloader_p.h>
#include <QtCore/qlibraryinfo.h>
+// Built-in adaptations
+#include <QtQuick/private/qsgsoftwareadaptation_p.h>
+#ifndef QT_NO_OPENGL
+#include <QtQuick/private/qsgdefaultcontext_p.h>
+#endif
+
QT_BEGIN_NAMESPACE
+Q_DECLARE_LOGGING_CATEGORY(QSG_LOG_INFO)
+
QSGContextPlugin::QSGContextPlugin(QObject *parent)
: QObject(parent)
{
@@ -59,61 +67,111 @@ Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader,
(QSGContextFactoryInterface_iid, QLatin1String("/scenegraph")))
#endif
-struct QSGAdaptionPluginData
+struct QSGAdaptionBackendData
{
- QSGAdaptionPluginData()
- : tried(false)
- , factory(0)
- {
- }
-
- ~QSGAdaptionPluginData()
- {
- }
+ QSGAdaptionBackendData();
bool tried;
QSGContextFactoryInterface *factory;
- QString deviceName;
+ QString name;
+ QSGContextFactoryInterface::Flags flags;
+
+ QVector<QSGContextFactoryInterface *> builtIns;
};
-Q_GLOBAL_STATIC(QSGAdaptionPluginData, qsg_adaptation_data)
+QSGAdaptionBackendData::QSGAdaptionBackendData()
+ : tried(false)
+ , factory(nullptr)
+ , flags(0)
+{
+ // Fill in the table with the built-in adaptations.
+ builtIns.append(new QSGSoftwareAdaptation);
+}
+
+Q_GLOBAL_STATIC(QSGAdaptionBackendData, qsg_adaptation_data)
-QSGAdaptionPluginData *contextFactory()
+// This only works when the backend is loaded (contextFactory() was called),
+// otherwise the return value is 0.
+//
+// Note that the default (OpenGL) implementation always results in 0, custom flags
+// can only be returned from the other (either compiled-in or plugin-based) backends.
+QSGContextFactoryInterface::Flags qsg_backend_flags()
{
- QSGAdaptionPluginData *plugin = qsg_adaptation_data();
- if (!plugin->tried) {
+ return qsg_adaptation_data()->flags;
+}
+
+QSGAdaptionBackendData *contextFactory()
+{
+ QSGAdaptionBackendData *backendData = qsg_adaptation_data();
+
+ if (!backendData->tried) {
+ backendData->tried = true;
- plugin->tried = true;
const QStringList args = QGuiApplication::arguments();
- QString device;
+ QString requestedBackend;
+
for (int index = 0; index < args.count(); ++index) {
if (args.at(index).startsWith(QLatin1String("--device="))) {
- device = args.at(index).mid(9);
+ requestedBackend = args.at(index).mid(9);
break;
}
}
- if (device.isEmpty())
- device = QString::fromLocal8Bit(qgetenv("QMLSCENE_DEVICE"));
-#ifndef QT_NO_LIBRARY
- if (!device.isEmpty()) {
- const int index = loader()->indexOf(device);
- if (index != -1)
- plugin->factory = qobject_cast<QSGContextFactoryInterface*>(loader()->instance(index));
- plugin->deviceName = device;
+ if (requestedBackend.isEmpty() && qEnvironmentVariableIsSet("QMLSCENE_DEVICE"))
+ requestedBackend = QString::fromLocal8Bit(qgetenv("QMLSCENE_DEVICE"));
+
+ // A modern alternative. Scenegraph adaptations can represent backends
+ // for different graphics APIs as well, instead of being specific to
+ // some device or platform.
+ if (requestedBackend.isEmpty() && qEnvironmentVariableIsSet("QT_QUICK_BACKEND"))
+ requestedBackend = QString::fromLocal8Bit(qgetenv("QT_QUICK_BACKEND"));
+
+#ifdef QT_NO_OPENGL
+ // If this is a build without OpenGL, and no backend has been set
+ // default to the software renderer
+ if (requestedBackend.isEmpty())
+ requestedBackend = QString::fromLocal8Bit("software");
+#endif
+
+ if (!requestedBackend.isEmpty()) {
#ifndef QT_NO_DEBUG
- if (!plugin->factory) {
- qWarning("Could not create scene graph context for device '%s'"
- " - check that plugins are installed correctly in %s",
- qPrintable(device),
- qPrintable(QLibraryInfo::location(QLibraryInfo::PluginsPath)));
- }
+ qCDebug(QSG_LOG_INFO) << "Loading backend" << requestedBackend;
#endif
- }
+ // First look for a built-in adaptation.
+ for (QSGContextFactoryInterface *builtInBackend : qAsConst(backendData->builtIns)) {
+ if (builtInBackend->keys().contains(requestedBackend)) {
+ backendData->factory = builtInBackend;
+ backendData->name = requestedBackend;
+ backendData->flags = backendData->factory->flags(requestedBackend);
+ break;
+ }
+ }
+
+ // Then try the plugins.
+ if (!backendData->factory) {
+#ifndef QT_NO_LIBRARY
+ const int index = loader()->indexOf(requestedBackend);
+ if (index != -1)
+ backendData->factory = qobject_cast<QSGContextFactoryInterface*>(loader()->instance(index));
+ if (backendData->factory) {
+ backendData->name = requestedBackend;
+ backendData->flags = backendData->factory->flags(requestedBackend);
+ }
+#ifndef QT_NO_DEBUG
+ if (!backendData->factory) {
+ qWarning("Could not create scene graph context for backend '%s'"
+ " - check that plugins are installed correctly in %s",
+ qPrintable(requestedBackend),
+ qPrintable(QLibraryInfo::location(QLibraryInfo::PluginsPath)));
+ }
+#endif
+ }
#endif // QT_NO_LIBRARY
+ }
}
- return plugin;
+
+ return backendData;
}
@@ -126,10 +184,14 @@ QSGAdaptionPluginData *contextFactory()
*/
QSGContext *QSGContext::createDefaultContext()
{
- QSGAdaptionPluginData *plugin = contextFactory();
- if (plugin->factory)
- return plugin->factory->create(plugin->deviceName);
- return new QSGContext();
+ QSGAdaptionBackendData *backendData = contextFactory();
+ if (backendData->factory)
+ return backendData->factory->create(backendData->name);
+#ifndef QT_NO_OPENGL
+ return new QSGDefaultContext();
+#else
+ return nullptr;
+#endif
}
@@ -143,9 +205,9 @@ QSGContext *QSGContext::createDefaultContext()
QQuickTextureFactory *QSGContext::createTextureFactoryFromImage(const QImage &image)
{
- QSGAdaptionPluginData *plugin = contextFactory();
- if (plugin->factory)
- return plugin->factory->createTextureFactoryFromImage(image);
+ QSGAdaptionBackendData *backendData = contextFactory();
+ if (backendData->factory)
+ return backendData->factory->createTextureFactoryFromImage(image);
return 0;
}
@@ -157,14 +219,10 @@ QQuickTextureFactory *QSGContext::createTextureFactoryFromImage(const QImage &im
QSGRenderLoop *QSGContext::createWindowManager()
{
- QSGAdaptionPluginData *plugin = contextFactory();
- if (plugin->factory)
- return plugin->factory->createWindowManager();
+ QSGAdaptionBackendData *backendData = contextFactory();
+ if (backendData->factory)
+ return backendData->factory->createWindowManager();
return 0;
}
-
-
-
-
QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/qsgcontextplugin_p.h b/src/quick/scenegraph/qsgcontextplugin_p.h
index ca326ead07..08c3d21408 100644
--- a/src/quick/scenegraph/qsgcontextplugin_p.h
+++ b/src/quick/scenegraph/qsgcontextplugin_p.h
@@ -64,12 +64,20 @@ class QSGRenderLoop;
struct Q_QUICK_PRIVATE_EXPORT QSGContextFactoryInterface : public QFactoryInterface
{
+ enum Flag {
+ SupportsShaderEffectNode = 0x01
+ };
+ Q_DECLARE_FLAGS(Flags, Flag)
+
virtual QSGContext *create(const QString &key) const = 0;
+ virtual Flags flags(const QString &key) const = 0;
virtual QQuickTextureFactory *createTextureFactoryFromImage(const QImage &image) = 0;
virtual QSGRenderLoop *createWindowManager() = 0;
};
+Q_DECLARE_OPERATORS_FOR_FLAGS(QSGContextFactoryInterface::Flags)
+
#define QSGContextFactoryInterface_iid \
"org.qt-project.Qt.QSGContextFactoryInterface"
Q_DECLARE_INTERFACE(QSGContextFactoryInterface, QSGContextFactoryInterface_iid)
@@ -83,7 +91,6 @@ public:
virtual ~QSGContextPlugin();
virtual QStringList keys() const = 0;
- virtual QSGContext *create(const QString &key) const = 0;
virtual QQuickTextureFactory *createTextureFactoryFromImage(const QImage &) { return 0; }
virtual QSGRenderLoop *createWindowManager() { return 0; }
diff --git a/src/quick/scenegraph/qsgdefaultcontext.cpp b/src/quick/scenegraph/qsgdefaultcontext.cpp
new file mode 100644
index 0000000000..4b05f64887
--- /dev/null
+++ b/src/quick/scenegraph/qsgdefaultcontext.cpp
@@ -0,0 +1,254 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgdefaultcontext_p.h"
+
+#include <QtQuick/private/qsgdistancefieldutil_p.h>
+#include <QtQuick/private/qsgdefaultrectanglenode_p.h>
+#include <QtQuick/private/qsgdefaultimagenode_p.h>
+#include <QtQuick/private/qsgdefaultpainternode_p.h>
+#include <QtQuick/private/qsgdefaultglyphnode_p.h>
+#include <QtQuick/private/qsgdistancefieldglyphnode_p.h>
+#include <QtQuick/private/qsgdistancefieldglyphnode_p_p.h>
+#include <QtQuick/private/qsgrenderloop_p.h>
+#include <QtQuick/private/qsgdefaultlayer_p.h>
+#include <QtQuick/private/qsgdefaultrendercontext_p.h>
+
+#include <QtGui/QOpenGLContext>
+#include <QtGui/QOpenGLFramebufferObject>
+
+#include <QtQuick/QQuickWindow>
+
+#include <private/qqmlglobal_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QSGMultisampleAntialiasing {
+ class ImageNode : public QSGDefaultImageNode {
+ public:
+ void setAntialiasing(bool) { }
+ };
+
+
+ class RectangleNode : public QSGDefaultRectangleNode {
+ public:
+ void setAntialiasing(bool) { }
+ };
+}
+
+DEFINE_BOOL_CONFIG_OPTION(qmlDisableDistanceField, QML_DISABLE_DISTANCEFIELD)
+
+QSGDefaultContext::QSGDefaultContext(QObject *parent)
+ : QSGContext (parent)
+ , m_antialiasingMethod(QSGContext::UndecidedAntialiasing)
+ , m_distanceFieldDisabled(qmlDisableDistanceField())
+ , m_distanceFieldAntialiasing(QSGGlyphNode::HighQualitySubPixelAntialiasing)
+ , m_distanceFieldAntialiasingDecided(false)
+{
+ if (Q_UNLIKELY(!qEnvironmentVariableIsEmpty("QSG_DISTANCEFIELD_ANTIALIASING"))) {
+ const QByteArray mode = qgetenv("QSG_DISTANCEFIELD_ANTIALIASING");
+ m_distanceFieldAntialiasingDecided = true;
+ if (mode == "subpixel")
+ m_distanceFieldAntialiasing = QSGGlyphNode::HighQualitySubPixelAntialiasing;
+ else if (mode == "subpixel-lowq")
+ m_distanceFieldAntialiasing = QSGGlyphNode::LowQualitySubPixelAntialiasing;
+ else if (mode == "gray")
+ m_distanceFieldAntialiasing = QSGGlyphNode::GrayAntialiasing;
+ }
+
+ // Adds compatibility with Qt 5.3 and earlier's QSG_RENDER_TIMING
+ if (qEnvironmentVariableIsSet("QSG_RENDER_TIMING")) {
+ const_cast<QLoggingCategory &>(QSG_LOG_TIME_GLYPH()).setEnabled(QtDebugMsg, true);
+ const_cast<QLoggingCategory &>(QSG_LOG_TIME_TEXTURE()).setEnabled(QtDebugMsg, true);
+ const_cast<QLoggingCategory &>(QSG_LOG_TIME_RENDERER()).setEnabled(QtDebugMsg, true);
+ const_cast<QLoggingCategory &>(QSG_LOG_TIME_RENDERLOOP()).setEnabled(QtDebugMsg, true);
+ const_cast<QLoggingCategory &>(QSG_LOG_TIME_COMPILATION()).setEnabled(QtDebugMsg, true);
+ }
+}
+
+QSGDefaultContext::~QSGDefaultContext()
+{
+
+}
+
+void QSGDefaultContext::renderContextInitialized(QSGRenderContext *renderContext)
+{
+ m_mutex.lock();
+
+ auto openglRenderContext = static_cast<const QSGDefaultRenderContext *>(renderContext);
+ if (m_antialiasingMethod == UndecidedAntialiasing) {
+ if (Q_UNLIKELY(qEnvironmentVariableIsSet("QSG_ANTIALIASING_METHOD"))) {
+ const QByteArray aaType = qgetenv("QSG_ANTIALIASING_METHOD");
+ if (aaType == "msaa")
+ m_antialiasingMethod = MsaaAntialiasing;
+ else if (aaType == "vertex")
+ m_antialiasingMethod = VertexAntialiasing;
+ }
+ if (m_antialiasingMethod == UndecidedAntialiasing) {
+ if (openglRenderContext->openglContext()->format().samples() > 0)
+ m_antialiasingMethod = MsaaAntialiasing;
+ else
+ m_antialiasingMethod = VertexAntialiasing;
+ }
+ }
+
+ // With OpenGL ES, except for Angle on Windows, use GrayAntialiasing, unless
+ // some value had been requested explicitly. This could not be decided
+ // before without a context. Now the context is ready.
+ if (!m_distanceFieldAntialiasingDecided) {
+ m_distanceFieldAntialiasingDecided = true;
+#ifndef Q_OS_WIN32
+ if (openglRenderContext->openglContext()->isOpenGLES())
+ m_distanceFieldAntialiasing = QSGGlyphNode::GrayAntialiasing;
+#endif
+ }
+
+ static bool dumped = false;
+ if (!dumped && QSG_LOG_INFO().isDebugEnabled()) {
+ dumped = true;
+ QSurfaceFormat format = openglRenderContext->openglContext()->format();
+ QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions();
+ qCDebug(QSG_LOG_INFO) << "R/G/B/A Buffers: " << format.redBufferSize() << format.greenBufferSize() << format.blueBufferSize() << format.alphaBufferSize();
+ qCDebug(QSG_LOG_INFO) << "Depth Buffer: " << format.depthBufferSize();
+ qCDebug(QSG_LOG_INFO) << "Stencil Buffer: " << format.stencilBufferSize();
+ qCDebug(QSG_LOG_INFO) << "Samples: " << format.samples();
+ qCDebug(QSG_LOG_INFO) << "GL_VENDOR: " << (const char *) funcs->glGetString(GL_VENDOR);
+ qCDebug(QSG_LOG_INFO) << "GL_RENDERER: " << (const char *) funcs->glGetString(GL_RENDERER);
+ qCDebug(QSG_LOG_INFO) << "GL_VERSION: " << (const char *) funcs->glGetString(GL_VERSION);
+ QSet<QByteArray> exts = openglRenderContext->openglContext()->extensions();
+ QByteArray all; foreach (const QByteArray &e, exts) all += ' ' + e;
+ qCDebug(QSG_LOG_INFO) << "GL_EXTENSIONS: " << all.constData();
+ qCDebug(QSG_LOG_INFO) << "Max Texture Size: " << openglRenderContext->maxTextureSize();
+ qCDebug(QSG_LOG_INFO) << "Debug context: " << format.testOption(QSurfaceFormat::DebugContext);
+ }
+
+ m_mutex.unlock();
+}
+
+void QSGDefaultContext::renderContextInvalidated(QSGRenderContext *)
+{
+}
+
+QSGRenderContext *QSGDefaultContext::createRenderContext()
+{
+ return new QSGDefaultRenderContext(this);
+}
+
+QSGRectangleNode *QSGDefaultContext::createRectangleNode()
+{
+ return m_antialiasingMethod == MsaaAntialiasing
+ ? new QSGMultisampleAntialiasing::RectangleNode
+ : new QSGDefaultRectangleNode;
+}
+
+QSGImageNode *QSGDefaultContext::createImageNode()
+{
+ return m_antialiasingMethod == MsaaAntialiasing
+ ? new QSGMultisampleAntialiasing::ImageNode
+ : new QSGDefaultImageNode;
+}
+
+QSGPainterNode *QSGDefaultContext::createPainterNode(QQuickPaintedItem *item)
+{
+ return new QSGDefaultPainterNode(item);
+}
+
+QSGGlyphNode *QSGDefaultContext::createGlyphNode(QSGRenderContext *rc, bool preferNativeGlyphNode)
+{
+ if (m_distanceFieldDisabled || preferNativeGlyphNode) {
+ return new QSGDefaultGlyphNode;
+ } else {
+ QSGDistanceFieldGlyphNode *node = new QSGDistanceFieldGlyphNode(rc);
+ node->setPreferredAntialiasingMode(m_distanceFieldAntialiasing);
+ return node;
+ }
+}
+
+/*!
+ * Factory function for scene graph backends of the QStyle stylable elements. Returns a
+ * null pointer if the backend doesn't provide its own node type.
+ */
+QSGNinePatchNode *QSGDefaultContext::createNinePatchNode()
+{
+ return nullptr;
+}
+
+QSGLayer *QSGDefaultContext::createLayer(QSGRenderContext *renderContext)
+{
+ return new QSGDefaultLayer(renderContext);
+}
+
+QSurfaceFormat QSGDefaultContext::defaultSurfaceFormat() const
+{
+ QSurfaceFormat format = QSurfaceFormat::defaultFormat();
+ static bool useDepth = qEnvironmentVariableIsEmpty("QSG_NO_DEPTH_BUFFER");
+ static bool useStencil = qEnvironmentVariableIsEmpty("QSG_NO_STENCIL_BUFFER");
+ static bool enableDebug = qEnvironmentVariableIsSet("QSG_OPENGL_DEBUG");
+ format.setDepthBufferSize(useDepth ? 24 : 0);
+ format.setStencilBufferSize(useStencil ? 8 : 0);
+ if (enableDebug)
+ format.setOption(QSurfaceFormat::DebugContext);
+ if (QQuickWindow::hasDefaultAlphaBuffer())
+ format.setAlphaBufferSize(8);
+ format.setSwapBehavior(QSurfaceFormat::DoubleBuffer);
+ return format;
+}
+
+void QSGDefaultContext::setDistanceFieldEnabled(bool enabled)
+{
+ m_distanceFieldDisabled = !enabled;
+}
+
+bool QSGDefaultContext::isDistanceFieldEnabled() const
+{
+ return !m_distanceFieldDisabled;
+}
+
+QSGRendererInterface *QSGDefaultContext::rendererInterface(QSGRenderContext *renderContext)
+{
+ Q_UNUSED(renderContext);
+ return this;
+}
+
+QSGRendererInterface::GraphicsAPI QSGDefaultContext::graphicsAPI() const
+{
+ return OpenGL;
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/qsgdefaultcontext_p.h b/src/quick/scenegraph/qsgdefaultcontext_p.h
new file mode 100644
index 0000000000..923d1ee7ec
--- /dev/null
+++ b/src/quick/scenegraph/qsgdefaultcontext_p.h
@@ -0,0 +1,93 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGDEFAULTCONTEXT_H
+#define QSGDEFAULTCONTEXT_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtQuick/private/qsgcontext_p.h>
+#include <QtQuick/private/qsgdistancefieldglyphnode_p.h>
+#include "qsgrendererinterface.h"
+
+QT_BEGIN_NAMESPACE
+
+class QSGDefaultContext : public QSGContext, public QSGRendererInterface
+{
+public:
+ QSGDefaultContext(QObject *parent = 0);
+ ~QSGDefaultContext();
+
+ void renderContextInitialized(QSGRenderContext *renderContext) override;
+ void renderContextInvalidated(QSGRenderContext *) override;
+ QSGRenderContext *createRenderContext() override;
+ QSGRectangleNode *createRectangleNode() override;
+ QSGImageNode *createImageNode() override;
+ QSGPainterNode *createPainterNode(QQuickPaintedItem *item) override;
+ QSGGlyphNode *createGlyphNode(QSGRenderContext *rc, bool preferNativeGlyphNode) override;
+ QSGNinePatchNode *createNinePatchNode() override;
+ QSGLayer *createLayer(QSGRenderContext *renderContext) override;
+ QSurfaceFormat defaultSurfaceFormat() const override;
+ QSGRendererInterface *rendererInterface(QSGRenderContext *renderContext) override;
+
+ void setDistanceFieldEnabled(bool enabled);
+ bool isDistanceFieldEnabled() const;
+
+ GraphicsAPI graphicsAPI() const override;
+
+private:
+ QMutex m_mutex;
+ QSGContext::AntialiasingMethod m_antialiasingMethod;
+ bool m_distanceFieldDisabled;
+ QSGDistanceFieldGlyphNode::AntialiasingMode m_distanceFieldAntialiasing;
+ bool m_distanceFieldAntialiasingDecided;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGDEFAULTCONTEXT_H
diff --git a/src/quick/scenegraph/qsgdefaultglyphnode.cpp b/src/quick/scenegraph/qsgdefaultglyphnode.cpp
index 082a4f8c09..b856d99bc1 100644
--- a/src/quick/scenegraph/qsgdefaultglyphnode.cpp
+++ b/src/quick/scenegraph/qsgdefaultglyphnode.cpp
@@ -40,59 +40,11 @@
#include "qsgdefaultglyphnode_p.h"
#include "qsgdefaultglyphnode_p_p.h"
-#include <qopenglshaderprogram.h>
-#include <private/qfont_p.h>
-
QT_BEGIN_NAMESPACE
-QSGDefaultGlyphNode::QSGDefaultGlyphNode()
- : m_style(QQuickText::Normal)
- , m_material(0)
- , m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 0)
-{
- m_geometry.setDrawingMode(GL_TRIANGLES);
- setGeometry(&m_geometry);
-}
-
-QSGDefaultGlyphNode::~QSGDefaultGlyphNode()
-{
- delete m_material;
-}
-
-void QSGDefaultGlyphNode::setColor(const QColor &color)
-{
- m_color = color;
- if (m_material != 0) {
- m_material->setColor(color);
- markDirty(DirtyMaterial);
- }
-}
-
-void QSGDefaultGlyphNode::setGlyphs(const QPointF &position, const QGlyphRun &glyphs)
-{
- if (m_material != 0)
- delete m_material;
-
- m_position = position;
- m_glyphs = glyphs;
-
-#ifdef QSG_RUNTIME_DESCRIPTION
- qsgnode_set_description(this, QLatin1String("glyphs"));
-#endif
-}
-
-void QSGDefaultGlyphNode::setStyle(QQuickText::TextStyle style)
-{
- if (m_style == style)
- return;
- m_style = style;
-}
-
-void QSGDefaultGlyphNode::setStyleColor(const QColor &color)
+void QSGDefaultGlyphNode::setMaterialColor(const QColor &color)
{
- if (m_styleColor == color)
- return;
- m_styleColor = color;
+ static_cast<QSGTextMaskMaterial *>(m_material)->setColor(color);
}
void QSGDefaultGlyphNode::update()
@@ -120,11 +72,12 @@ void QSGDefaultGlyphNode::update()
m_material = material;
}
- m_material->setColor(m_color);
+ QSGTextMaskMaterial *textMaskMaterial = static_cast<QSGTextMaskMaterial *>(m_material);
+ textMaskMaterial->setColor(m_color);
QRectF boundingRect;
- m_material->populate(m_position, m_glyphs.glyphIndexes(), m_glyphs.positions(), geometry(),
- &boundingRect, &m_baseLine, margins);
+ textMaskMaterial->populate(m_position, m_glyphs.glyphIndexes(), m_glyphs.positions(), geometry(),
+ &boundingRect, &m_baseLine, margins);
setBoundingRect(boundingRect);
setMaterial(m_material);
diff --git a/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp b/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp
index 3da7a10456..7c2663d5a3 100644
--- a/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp
+++ b/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp
@@ -49,6 +49,7 @@
#include <QtQuick/qquickwindow.h>
#include <QtQuick/private/qsgtexture_p.h>
+#include <QtQuick/private/qsgdefaultrendercontext_p.h>
#include <private/qrawfont_p.h>
#include <QtCore/qmath.h>
@@ -427,7 +428,7 @@ void QSGTextMaskMaterial::init(QFontEngine::GlyphFormat glyphFormat)
if (!m_glyphCache || int(m_glyphCache->glyphFormat()) != glyphFormat) {
m_glyphCache = new QOpenGLTextureGlyphCache(glyphFormat, glyphCacheTransform);
fontEngine->setGlyphCache(ctx, m_glyphCache.data());
- QSGRenderContext *sg = QSGRenderContext::from(ctx);
+ auto sg = QSGDefaultRenderContext::from(ctx);
Q_ASSERT(sg);
sg->registerFontengineForCleanup(fontEngine);
}
diff --git a/src/quick/scenegraph/qsgdefaultglyphnode_p.h b/src/quick/scenegraph/qsgdefaultglyphnode_p.h
index ea4c0ff787..0eb7a4e4bd 100644
--- a/src/quick/scenegraph/qsgdefaultglyphnode_p.h
+++ b/src/quick/scenegraph/qsgdefaultglyphnode_p.h
@@ -52,38 +52,15 @@
//
#include <private/qsgadaptationlayer_p.h>
-#include <QtQuick/qsgnode.h>
+#include <private/qsgbasicglyphnode_p.h>
QT_BEGIN_NAMESPACE
-class QSGTextMaskMaterial;
-class QSGDefaultGlyphNode: public QSGGlyphNode
+class QSGDefaultGlyphNode : public QSGBasicGlyphNode
{
public:
- QSGDefaultGlyphNode();
- virtual ~QSGDefaultGlyphNode();
-
- virtual QPointF baseLine() const { return m_baseLine; }
- virtual void setGlyphs(const QPointF &position, const QGlyphRun &glyphs);
- virtual void setColor(const QColor &color);
-
- virtual void setPreferredAntialiasingMode(AntialiasingMode) { }
- virtual void setStyle(QQuickText::TextStyle);
- virtual void setStyleColor(const QColor &);
-
- virtual void update();
-
-protected:
- QGlyphRun m_glyphs;
- QPointF m_position;
- QColor m_color;
- QQuickText::TextStyle m_style;
- QColor m_styleColor;
-
- QPointF m_baseLine;
- QSGTextMaskMaterial *m_material;
-
- QSGGeometry m_geometry;
+ void setMaterialColor(const QColor &color) override;
+ void update() override;
};
QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/qsgdefaultimagenode.cpp b/src/quick/scenegraph/qsgdefaultimagenode.cpp
index 3ee72e3ce0..9fed70a7de 100644
--- a/src/quick/scenegraph/qsgdefaultimagenode.cpp
+++ b/src/quick/scenegraph/qsgdefaultimagenode.cpp
@@ -39,38 +39,12 @@
#include "qsgdefaultimagenode_p.h"
#include <private/qsgmaterialshader_p.h>
-
-#include <QtCore/qvarlengtharray.h>
-#include <QtCore/qmath.h>
-#include <QtGui/qopenglfunctions.h>
-
-#include <qsgtexturematerial.h>
#include <private/qsgtexturematerial_p.h>
-#include <qsgmaterial.h>
+#include <QtGui/qopenglfunctions.h>
+#include <QtCore/qmath.h>
QT_BEGIN_NAMESPACE
-namespace
-{
- struct SmoothVertex
- {
- float x, y, u, v;
- float dx, dy, du, dv;
- };
-
- const QSGGeometry::AttributeSet &smoothAttributeSet()
- {
- static QSGGeometry::Attribute data[] = {
- QSGGeometry::Attribute::create(0, 2, GL_FLOAT, true),
- QSGGeometry::Attribute::create(1, 2, GL_FLOAT, false),
- QSGGeometry::Attribute::create(2, 2, GL_FLOAT, false),
- QSGGeometry::Attribute::create(3, 2, GL_FLOAT, false)
- };
- static QSGGeometry::AttributeSet attrs = { 4, sizeof(SmoothVertex), data };
- return attrs;
- }
-}
-
class SmoothTextureMaterialShader : public QSGTextureMaterialShader
{
public:
@@ -144,52 +118,9 @@ void SmoothTextureMaterialShader::initialize()
}
QSGDefaultImageNode::QSGDefaultImageNode()
- : m_innerSourceRect(0, 0, 1, 1)
- , m_subSourceRect(0, 0, 1, 1)
- , m_antialiasing(false)
- , m_mirror(false)
- , m_dirtyGeometry(false)
- , m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4)
{
setMaterial(&m_materialO);
setOpaqueMaterial(&m_material);
- setGeometry(&m_geometry);
-
-#ifdef QSG_RUNTIME_DESCRIPTION
- qsgnode_set_description(this, QLatin1String("image"));
-#endif
-}
-
-void QSGDefaultImageNode::setTargetRect(const QRectF &rect)
-{
- if (rect == m_targetRect)
- return;
- m_targetRect = rect;
- m_dirtyGeometry = true;
-}
-
-void QSGDefaultImageNode::setInnerTargetRect(const QRectF &rect)
-{
- if (rect == m_innerTargetRect)
- return;
- m_innerTargetRect = rect;
- m_dirtyGeometry = true;
-}
-
-void QSGDefaultImageNode::setInnerSourceRect(const QRectF &rect)
-{
- if (rect == m_innerSourceRect)
- return;
- m_innerSourceRect = rect;
- m_dirtyGeometry = true;
-}
-
-void QSGDefaultImageNode::setSubSourceRect(const QRectF &rect)
-{
- if (rect == m_subSourceRect)
- return;
- m_subSourceRect = rect;
- m_dirtyGeometry = true;
}
void QSGDefaultImageNode::setFiltering(QSGTexture::Filtering filtering)
@@ -203,7 +134,6 @@ void QSGDefaultImageNode::setFiltering(QSGTexture::Filtering filtering)
markDirty(DirtyMaterial);
}
-
void QSGDefaultImageNode::setMipmapFiltering(QSGTexture::Filtering filtering)
{
if (m_material.mipmapFiltering() == filtering)
@@ -237,73 +167,37 @@ void QSGDefaultImageNode::setHorizontalWrapMode(QSGTexture::WrapMode wrapMode)
markDirty(DirtyMaterial);
}
-
-void QSGDefaultImageNode::setTexture(QSGTexture *texture)
-{
- Q_ASSERT(texture);
-
- m_material.setTexture(texture);
- m_materialO.setTexture(texture);
- m_smoothMaterial.setTexture(texture);
- m_material.setFlag(QSGMaterial::Blending, texture->hasAlphaChannel());
-
- markDirty(DirtyMaterial);
-
- // Because the texture can be a different part of the atlas, we need to update it...
- m_dirtyGeometry = true;
-}
-
-void QSGDefaultImageNode::setAntialiasing(bool antialiasing)
+void QSGDefaultImageNode::updateMaterialAntialiasing()
{
- if (antialiasing == m_antialiasing)
- return;
- m_antialiasing = antialiasing;
if (m_antialiasing) {
setMaterial(&m_smoothMaterial);
setOpaqueMaterial(0);
- setGeometry(new QSGGeometry(smoothAttributeSet(), 0));
- setFlag(OwnsGeometry, true);
} else {
setMaterial(&m_materialO);
setOpaqueMaterial(&m_material);
- setGeometry(&m_geometry);
- setFlag(OwnsGeometry, false);
}
- m_dirtyGeometry = true;
}
-void QSGDefaultImageNode::setMirror(bool mirror)
+void QSGDefaultImageNode::setMaterialTexture(QSGTexture *texture)
{
- if (mirror == m_mirror)
- return;
- m_mirror = mirror;
- m_dirtyGeometry = true;
+ m_material.setTexture(texture);
+ m_materialO.setTexture(texture);
+ m_smoothMaterial.setTexture(texture);
}
-
-void QSGDefaultImageNode::update()
+QSGTexture *QSGDefaultImageNode::materialTexture() const
{
- if (m_dirtyGeometry)
- updateGeometry();
+ return m_material.texture();
}
-void QSGDefaultImageNode::preprocess()
+bool QSGDefaultImageNode::updateMaterialBlending()
{
- bool doDirty = false;
- QSGDynamicTexture *t = qobject_cast<QSGDynamicTexture *>(m_material.texture());
- if (t) {
- doDirty = t->updateTexture();
- if (doDirty)
- updateGeometry();
- }
- bool alpha = m_material.flags() & QSGMaterial::Blending;
- if (m_material.texture() && alpha != m_material.texture()->hasAlphaChannel()) {
+ const bool alpha = m_material.flags() & QSGMaterial::Blending;
+ if (materialTexture() && alpha != materialTexture()->hasAlphaChannel()) {
m_material.setFlag(QSGMaterial::Blending, !alpha);
- doDirty = true;
+ return true;
}
-
- if (doDirty)
- markDirty(DirtyMaterial);
+ return false;
}
inline static bool isPowerOfTwo(int x)
@@ -312,389 +206,21 @@ inline static bool isPowerOfTwo(int x)
return x == (x & -x);
}
-namespace {
- struct X { float x, tx; };
- struct Y { float y, ty; };
-}
-
-static inline void appendQuad(quint16 **indices, quint16 topLeft, quint16 topRight,
- quint16 bottomLeft, quint16 bottomRight)
+bool QSGDefaultImageNode::supportsWrap(const QSize &size) const
{
- *(*indices)++ = topLeft;
- *(*indices)++ = bottomLeft;
- *(*indices)++ = bottomRight;
- *(*indices)++ = bottomRight;
- *(*indices)++ = topRight;
- *(*indices)++ = topLeft;
-}
+ bool wrapSupported = true;
-QSGGeometry *QSGDefaultImageNode::updateGeometry(const QRectF &targetRect,
- const QRectF &innerTargetRect,
- const QRectF &sourceRect,
- const QRectF &innerSourceRect,
- const QRectF &subSourceRect,
- QSGGeometry *geometry,
- bool mirror,
- bool antialiasing)
-{
- int floorLeft = qFloor(subSourceRect.left());
- int ceilRight = qCeil(subSourceRect.right());
- int floorTop = qFloor(subSourceRect.top());
- int ceilBottom = qCeil(subSourceRect.bottom());
- int hTiles = ceilRight - floorLeft;
- int vTiles = ceilBottom - floorTop;
-
- int hCells = hTiles;
- int vCells = vTiles;
- if (innerTargetRect.width() == 0)
- hCells = 0;
- if (innerTargetRect.left() != targetRect.left())
- ++hCells;
- if (innerTargetRect.right() != targetRect.right())
- ++hCells;
- if (innerTargetRect.height() == 0)
- vCells = 0;
- if (innerTargetRect.top() != targetRect.top())
- ++vCells;
- if (innerTargetRect.bottom() != targetRect.bottom())
- ++vCells;
- QVarLengthArray<X, 32> xData(2 * hCells);
- QVarLengthArray<Y, 32> yData(2 * vCells);
- X *xs = xData.data();
- Y *ys = yData.data();
-
- if (innerTargetRect.left() != targetRect.left()) {
- xs[0].x = targetRect.left();
- xs[0].tx = sourceRect.left();
- xs[1].x = innerTargetRect.left();
- xs[1].tx = innerSourceRect.left();
- xs += 2;
- }
- if (innerTargetRect.width() != 0) {
- xs[0].x = innerTargetRect.left();
- xs[0].tx = innerSourceRect.x() + (subSourceRect.left() - floorLeft) * innerSourceRect.width();
- ++xs;
- float b = innerTargetRect.width() / subSourceRect.width();
- float a = innerTargetRect.x() - subSourceRect.x() * b;
- for (int i = floorLeft + 1; i <= ceilRight - 1; ++i) {
- xs[0].x = xs[1].x = a + b * i;
- xs[0].tx = innerSourceRect.right();
- xs[1].tx = innerSourceRect.left();
- xs += 2;
- }
- xs[0].x = innerTargetRect.right();
- xs[0].tx = innerSourceRect.x() + (subSourceRect.right() - ceilRight + 1) * innerSourceRect.width();
- ++xs;
- }
- if (innerTargetRect.right() != targetRect.right()) {
- xs[0].x = innerTargetRect.right();
- xs[0].tx = innerSourceRect.right();
- xs[1].x = targetRect.right();
- xs[1].tx = sourceRect.right();
- xs += 2;
- }
- Q_ASSERT(xs == xData.data() + xData.size());
- if (mirror) {
- float leftPlusRight = targetRect.left() + targetRect.right();
- int count = xData.size();
- xs = xData.data();
- for (int i = 0; i < count >> 1; ++i)
- qSwap(xs[i], xs[count - 1 - i]);
- for (int i = 0; i < count; ++i)
- xs[i].x = leftPlusRight - xs[i].x;
- }
-
- if (innerTargetRect.top() != targetRect.top()) {
- ys[0].y = targetRect.top();
- ys[0].ty = sourceRect.top();
- ys[1].y = innerTargetRect.top();
- ys[1].ty = innerSourceRect.top();
- ys += 2;
- }
- if (innerTargetRect.height() != 0) {
- ys[0].y = innerTargetRect.top();
- ys[0].ty = innerSourceRect.y() + (subSourceRect.top() - floorTop) * innerSourceRect.height();
- ++ys;
- float b = innerTargetRect.height() / subSourceRect.height();
- float a = innerTargetRect.y() - subSourceRect.y() * b;
- for (int i = floorTop + 1; i <= ceilBottom - 1; ++i) {
- ys[0].y = ys[1].y = a + b * i;
- ys[0].ty = innerSourceRect.bottom();
- ys[1].ty = innerSourceRect.top();
- ys += 2;
- }
- ys[0].y = innerTargetRect.bottom();
- ys[0].ty = innerSourceRect.y() + (subSourceRect.bottom() - ceilBottom + 1) * innerSourceRect.height();
- ++ys;
- }
- if (innerTargetRect.bottom() != targetRect.bottom()) {
- ys[0].y = innerTargetRect.bottom();
- ys[0].ty = innerSourceRect.bottom();
- ys[1].y = targetRect.bottom();
- ys[1].ty = sourceRect.bottom();
- ys += 2;
- }
- Q_ASSERT(ys == yData.data() + yData.size());
-
- if (antialiasing) {
- QSGGeometry *g = geometry;
- Q_ASSERT(g);
-
- g->allocate(hCells * vCells * 4 + (hCells + vCells - 1) * 4,
- hCells * vCells * 6 + (hCells + vCells) * 12);
- g->setDrawingMode(GL_TRIANGLES);
- SmoothVertex *vertices = reinterpret_cast<SmoothVertex *>(g->vertexData());
- memset(vertices, 0, g->vertexCount() * g->sizeOfVertex());
- quint16 *indices = g->indexDataAsUShort();
-
- // The deltas are how much the fuzziness can reach into the image.
- // Only the border vertices are moved by the vertex shader, so the fuzziness
- // can't reach further into the image than the closest interior vertices.
- float leftDx = xData.at(1).x - xData.at(0).x;
- float rightDx = xData.at(xData.size() - 1).x - xData.at(xData.size() - 2).x;
- float topDy = yData.at(1).y - yData.at(0).y;
- float bottomDy = yData.at(yData.size() - 1).y - yData.at(yData.size() - 2).y;
-
- float leftDu = xData.at(1).tx - xData.at(0).tx;
- float rightDu = xData.at(xData.size() - 1).tx - xData.at(xData.size() - 2).tx;
- float topDv = yData.at(1).ty - yData.at(0).ty;
- float bottomDv = yData.at(yData.size() - 1).ty - yData.at(yData.size() - 2).ty;
-
- if (hCells == 1) {
- leftDx = rightDx *= 0.5f;
- leftDu = rightDu *= 0.5f;
- }
- if (vCells == 1) {
- topDy = bottomDy *= 0.5f;
- topDv = bottomDv *= 0.5f;
- }
-
- // This delta is how much the fuzziness can reach out from the image.
- float delta = float(qAbs(targetRect.width()) < qAbs(targetRect.height())
- ? targetRect.width() : targetRect.height()) * 0.5f;
-
- quint16 index = 0;
- ys = yData.data();
- for (int j = 0; j < vCells; ++j, ys += 2) {
- xs = xData.data();
- bool isTop = j == 0;
- bool isBottom = j == vCells - 1;
- for (int i = 0; i < hCells; ++i, xs += 2) {
- bool isLeft = i == 0;
- bool isRight = i == hCells - 1;
-
- SmoothVertex *v = vertices + index;
-
- quint16 topLeft = index;
- for (int k = (isTop || isLeft ? 2 : 1); k--; ++v, ++index) {
- v->x = xs[0].x;
- v->u = xs[0].tx;
- v->y = ys[0].y;
- v->v = ys[0].ty;
- }
-
- quint16 topRight = index;
- for (int k = (isTop || isRight ? 2 : 1); k--; ++v, ++index) {
- v->x = xs[1].x;
- v->u = xs[1].tx;
- v->y = ys[0].y;
- v->v = ys[0].ty;
- }
-
- quint16 bottomLeft = index;
- for (int k = (isBottom || isLeft ? 2 : 1); k--; ++v, ++index) {
- v->x = xs[0].x;
- v->u = xs[0].tx;
- v->y = ys[1].y;
- v->v = ys[1].ty;
- }
-
- quint16 bottomRight = index;
- for (int k = (isBottom || isRight ? 2 : 1); k--; ++v, ++index) {
- v->x = xs[1].x;
- v->u = xs[1].tx;
- v->y = ys[1].y;
- v->v = ys[1].ty;
- }
-
- appendQuad(&indices, topLeft, topRight, bottomLeft, bottomRight);
-
- if (isTop) {
- vertices[topLeft].dy = vertices[topRight].dy = topDy;
- vertices[topLeft].dv = vertices[topRight].dv = topDv;
- vertices[topLeft + 1].dy = vertices[topRight + 1].dy = -delta;
- appendQuad(&indices, topLeft + 1, topRight + 1, topLeft, topRight);
- }
-
- if (isBottom) {
- vertices[bottomLeft].dy = vertices[bottomRight].dy = -bottomDy;
- vertices[bottomLeft].dv = vertices[bottomRight].dv = -bottomDv;
- vertices[bottomLeft + 1].dy = vertices[bottomRight + 1].dy = delta;
- appendQuad(&indices, bottomLeft, bottomRight, bottomLeft + 1, bottomRight + 1);
- }
-
- if (isLeft) {
- vertices[topLeft].dx = vertices[bottomLeft].dx = leftDx;
- vertices[topLeft].du = vertices[bottomLeft].du = leftDu;
- vertices[topLeft + 1].dx = vertices[bottomLeft + 1].dx = -delta;
- appendQuad(&indices, topLeft + 1, topLeft, bottomLeft + 1, bottomLeft);
- }
-
- if (isRight) {
- vertices[topRight].dx = vertices[bottomRight].dx = -rightDx;
- vertices[topRight].du = vertices[bottomRight].du = -rightDu;
- vertices[topRight + 1].dx = vertices[bottomRight + 1].dx = delta;
- appendQuad(&indices, topRight, topRight + 1, bottomRight, bottomRight + 1);
- }
- }
- }
-
- Q_ASSERT(index == g->vertexCount());
- Q_ASSERT(indices - g->indexCount() == g->indexData());
- } else {
- if (!geometry) {
- geometry = new QSGGeometry(QSGGeometry::defaultAttributes_TexturedPoint2D(),
- hCells * vCells * 4, hCells * vCells * 6,
- GL_UNSIGNED_SHORT);
- } else {
- geometry->allocate(hCells * vCells * 4, hCells * vCells * 6);
- }
- geometry->setDrawingMode(GL_TRIANGLES);
- QSGGeometry::TexturedPoint2D *vertices = geometry->vertexDataAsTexturedPoint2D();
- ys = yData.data();
- for (int j = 0; j < vCells; ++j, ys += 2) {
- xs = xData.data();
- for (int i = 0; i < hCells; ++i, xs += 2) {
- vertices[0].x = vertices[2].x = xs[0].x;
- vertices[0].tx = vertices[2].tx = xs[0].tx;
- vertices[1].x = vertices[3].x = xs[1].x;
- vertices[1].tx = vertices[3].tx = xs[1].tx;
-
- vertices[0].y = vertices[1].y = ys[0].y;
- vertices[0].ty = vertices[1].ty = ys[0].ty;
- vertices[2].y = vertices[3].y = ys[1].y;
- vertices[2].ty = vertices[3].ty = ys[1].ty;
-
- vertices += 4;
- }
- }
-
- quint16 *indices = geometry->indexDataAsUShort();
- for (int i = 0; i < 4 * vCells * hCells; i += 4)
- appendQuad(&indices, i, i + 1, i + 2, i + 3);
- }
- return geometry;
-}
-
-void QSGDefaultImageNode::updateGeometry()
-{
- Q_ASSERT(!m_targetRect.isEmpty());
- const QSGTexture *t = m_material.texture();
- if (!t) {
- QSGGeometry *g = geometry();
- g->allocate(4);
- g->setDrawingMode(GL_TRIANGLE_STRIP);
- memset(g->vertexData(), 0, g->sizeOfVertex() * 4);
- } else {
- QRectF sourceRect = t->normalizedTextureSubRect();
-
- QRectF innerSourceRect(sourceRect.x() + m_innerSourceRect.x() * sourceRect.width(),
- sourceRect.y() + m_innerSourceRect.y() * sourceRect.height(),
- m_innerSourceRect.width() * sourceRect.width(),
- m_innerSourceRect.height() * sourceRect.height());
-
- bool hasMargins = m_targetRect != m_innerTargetRect;
-
- int floorLeft = qFloor(m_subSourceRect.left());
- int ceilRight = qCeil(m_subSourceRect.right());
- int floorTop = qFloor(m_subSourceRect.top());
- int ceilBottom = qCeil(m_subSourceRect.bottom());
- int hTiles = ceilRight - floorLeft;
- int vTiles = ceilBottom - floorTop;
-
- bool hasTiles = hTiles != 1 || vTiles != 1;
- bool fullTexture = innerSourceRect == QRectF(0, 0, 1, 1);
-
- bool wrapSupported = true;
-
- QOpenGLContext *ctx = QOpenGLContext::currentContext();
+ QOpenGLContext *ctx = QOpenGLContext::currentContext();
#ifndef QT_OPENGL_ES_2
- if (ctx->isOpenGLES())
+ if (ctx->isOpenGLES())
#endif
- {
- bool npotSupported = ctx->functions()->hasOpenGLFeature(QOpenGLFunctions::NPOTTextureRepeat);
- QSize size = t->textureSize();
- const bool isNpot = !isPowerOfTwo(size.width()) || !isPowerOfTwo(size.height());
- wrapSupported = npotSupported || !isNpot;
- }
-
- // An image can be rendered as a single quad if:
- // - There are no margins, and either:
- // - the image isn't repeated
- // - the source rectangle fills the entire texture so that texture wrapping can be used,
- // and NPOT is supported
- if (!hasMargins && (!hasTiles || (fullTexture && wrapSupported))) {
- QRectF sr;
- if (!fullTexture) {
- sr = QRectF(innerSourceRect.x() + (m_subSourceRect.left() - floorLeft) * innerSourceRect.width(),
- innerSourceRect.y() + (m_subSourceRect.top() - floorTop) * innerSourceRect.height(),
- m_subSourceRect.width() * innerSourceRect.width(),
- m_subSourceRect.height() * innerSourceRect.height());
- } else {
- sr = QRectF(m_subSourceRect.left() - floorLeft, m_subSourceRect.top() - floorTop,
- m_subSourceRect.width(), m_subSourceRect.height());
- }
- if (m_mirror) {
- qreal oldLeft = sr.left();
- sr.setLeft(sr.right());
- sr.setRight(oldLeft);
- }
-
- if (m_antialiasing) {
- QSGGeometry *g = geometry();
- Q_ASSERT(g != &m_geometry);
- g->allocate(8, 14);
- g->setDrawingMode(GL_TRIANGLE_STRIP);
- SmoothVertex *vertices = reinterpret_cast<SmoothVertex *>(g->vertexData());
- float delta = float(qAbs(m_targetRect.width()) < qAbs(m_targetRect.height())
- ? m_targetRect.width() : m_targetRect.height()) * 0.5f;
- float sx = float(sr.width() / m_targetRect.width());
- float sy = float(sr.height() / m_targetRect.height());
- for (int d = -1; d <= 1; d += 2) {
- for (int j = 0; j < 2; ++j) {
- for (int i = 0; i < 2; ++i, ++vertices) {
- vertices->x = m_targetRect.x() + i * m_targetRect.width();
- vertices->y = m_targetRect.y() + j * m_targetRect.height();
- vertices->u = sr.x() + i * sr.width();
- vertices->v = sr.y() + j * sr.height();
- vertices->dx = (i == 0 ? delta : -delta) * d;
- vertices->dy = (j == 0 ? delta : -delta) * d;
- vertices->du = (d < 0 ? 0 : vertices->dx * sx);
- vertices->dv = (d < 0 ? 0 : vertices->dy * sy);
- }
- }
- }
- Q_ASSERT(vertices - g->vertexCount() == g->vertexData());
- static const quint16 indices[] = {
- 0, 4, 1, 5, 3, 7, 2, 6, 0, 4,
- 4, 6, 5, 7
- };
- Q_ASSERT(g->sizeOfIndex() * g->indexCount() == sizeof(indices));
- memcpy(g->indexDataAsUShort(), indices, sizeof(indices));
- } else {
- m_geometry.allocate(4);
- m_geometry.setDrawingMode(GL_TRIANGLE_STRIP);
- QSGGeometry::updateTexturedRectGeometry(&m_geometry, m_targetRect, sr);
- }
- } else {
- QSGGeometry *g = m_antialiasing ? geometry() : &m_geometry;
- updateGeometry(m_targetRect, m_innerTargetRect,
- sourceRect, innerSourceRect, m_subSourceRect,
- g, m_mirror, m_antialiasing);
- }
+ {
+ bool npotSupported = ctx->functions()->hasOpenGLFeature(QOpenGLFunctions::NPOTTextureRepeat);
+ const bool isNpot = !isPowerOfTwo(size.width()) || !isPowerOfTwo(size.height());
+ wrapSupported = npotSupported || !isNpot;
}
- markDirty(DirtyGeometry);
- m_dirtyGeometry = false;
+
+ return wrapSupported;
}
QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/qsgdefaultimagenode_p.h b/src/quick/scenegraph/qsgdefaultimagenode_p.h
index 5c195869c5..688c5a5039 100644
--- a/src/quick/scenegraph/qsgdefaultimagenode_p.h
+++ b/src/quick/scenegraph/qsgdefaultimagenode_p.h
@@ -53,6 +53,7 @@
//
#include <private/qsgadaptationlayer_p.h>
+#include <private/qsgbasicimagenode_p.h>
#include <QtQuick/qsgtexturematerial.h>
QT_BEGIN_NAMESPACE
@@ -65,56 +66,30 @@ public:
void setTexture(QSGTexture *texture);
protected:
- virtual QSGMaterialType *type() const;
- virtual QSGMaterialShader *createShader() const;
+ QSGMaterialType *type() const override;
+ QSGMaterialShader *createShader() const override;
};
-class Q_QUICK_PRIVATE_EXPORT QSGDefaultImageNode : public QSGImageNode
+class Q_QUICK_PRIVATE_EXPORT QSGDefaultImageNode : public QSGBasicImageNode
{
public:
QSGDefaultImageNode();
- virtual void setTargetRect(const QRectF &rect);
- virtual void setInnerTargetRect(const QRectF &rect);
- virtual void setInnerSourceRect(const QRectF &rect);
- virtual void setSubSourceRect(const QRectF &rect);
- virtual void setTexture(QSGTexture *t);
- virtual void setAntialiasing(bool antialiasing);
- virtual void setMirror(bool mirror);
- virtual void update();
- virtual void setMipmapFiltering(QSGTexture::Filtering filtering);
- virtual void setFiltering(QSGTexture::Filtering filtering);
- virtual void setHorizontalWrapMode(QSGTexture::WrapMode wrapMode);
- virtual void setVerticalWrapMode(QSGTexture::WrapMode wrapMode);
+ void setMipmapFiltering(QSGTexture::Filtering filtering) override;
+ void setFiltering(QSGTexture::Filtering filtering) override;
+ void setHorizontalWrapMode(QSGTexture::WrapMode wrapMode) override;
+ void setVerticalWrapMode(QSGTexture::WrapMode wrapMode) override;
- virtual void preprocess();
-
- static QSGGeometry *updateGeometry(const QRectF &targetRect,
- const QRectF &innerTargetRect,
- const QRectF &sourceRect,
- const QRectF &innerSourceRect,
- const QRectF &subSourceRect,
- QSGGeometry *geometry,
- bool mirror = false,
- bool antialiasing = false);
+ void updateMaterialAntialiasing() override;
+ void setMaterialTexture(QSGTexture *texture) override;
+ QSGTexture *materialTexture() const override;
+ bool updateMaterialBlending() override;
+ bool supportsWrap(const QSize &size) const override;
private:
- void updateGeometry();
-
- QRectF m_targetRect;
- QRectF m_innerTargetRect;
- QRectF m_innerSourceRect;
- QRectF m_subSourceRect;
-
QSGOpaqueTextureMaterial m_material;
QSGTextureMaterial m_materialO;
QSGSmoothTextureMaterial m_smoothMaterial;
-
- uint m_antialiasing : 1;
- uint m_mirror : 1;
- uint m_dirtyGeometry : 1;
-
- QSGGeometry m_geometry;
};
QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/qsgdefaultlayer.cpp b/src/quick/scenegraph/qsgdefaultlayer.cpp
index 2f1c1d454c..ad5b57ff83 100644
--- a/src/quick/scenegraph/qsgdefaultlayer.cpp
+++ b/src/quick/scenegraph/qsgdefaultlayer.cpp
@@ -40,8 +40,12 @@
#include <private/qqmlglobal_p.h>
#include <private/qsgrenderer_p.h>
+#include <private/qsgdefaultrendercontext_p.h>
-#include <QOpenGLFramebufferObject>
+#include <QtGui/QOpenGLFramebufferObject>
+#include <QtGui/QOpenGLFunctions>
+
+#include <QtQuick/private/qsgdepthstencilbuffer_p.h>
#ifdef QSG_DEBUG_FBO_OVERLAY
DEFINE_BOOL_CONFIG_OPTION(qmlFboOverlay, QML_FBO_OVERLAY)
@@ -95,7 +99,6 @@ QSGDefaultLayer::QSGDefaultLayer(QSGRenderContext *context)
#ifdef QSG_DEBUG_FBO_OVERLAY
, m_debugOverlay(0)
#endif
- , m_context(context)
, m_mipmap(false)
, m_live(true)
, m_recursive(false)
@@ -106,6 +109,7 @@ QSGDefaultLayer::QSGDefaultLayer(QSGRenderContext *context)
, m_mirrorHorizontal(false)
, m_mirrorVertical(true)
{
+ m_context = static_cast<QSGDefaultRenderContext *>(context);
}
QSGDefaultLayer::~QSGDefaultLayer()
diff --git a/src/quick/scenegraph/qsgdefaultlayer_p.h b/src/quick/scenegraph/qsgdefaultlayer_p.h
index 8bb565f845..ae39994096 100644
--- a/src/quick/scenegraph/qsgdefaultlayer_p.h
+++ b/src/quick/scenegraph/qsgdefaultlayer_p.h
@@ -54,8 +54,14 @@
#include <private/qsgcontext_p.h>
#include <qsgsimplerectnode.h>
+QT_BEGIN_NAMESPACE
+
#define QSG_DEBUG_FBO_OVERLAY
+class QOpenGLFramebufferObject;
+class QSGDepthStencilBuffer;
+class QSGDefaultRenderContext;
+
class Q_QUICK_PRIVATE_EXPORT QSGDefaultLayer : public QSGLayer
{
Q_OBJECT
@@ -131,7 +137,7 @@ private:
QSGSimpleRectNode *m_debugOverlay;
#endif
- QSGRenderContext *m_context;
+ QSGDefaultRenderContext *m_context;
uint m_mipmap : 1;
uint m_live : 1;
@@ -144,4 +150,6 @@ private:
uint m_mirrorVertical : 1;
};
+QT_END_NAMESPACE
+
#endif // QSGDEFAULTLAYER_P_H
diff --git a/src/quick/scenegraph/qsgdefaultrectanglenode.cpp b/src/quick/scenegraph/qsgdefaultrectanglenode.cpp
index 5ef52e8722..117a9272e5 100644
--- a/src/quick/scenegraph/qsgdefaultrectanglenode.cpp
+++ b/src/quick/scenegraph/qsgdefaultrectanglenode.cpp
@@ -38,8 +38,6 @@
**
****************************************************************************/
-
-
#include "qsgdefaultrectanglenode_p.h"
#include <QtQuick/qsgvertexcolormaterial.h>
@@ -52,59 +50,6 @@
QT_BEGIN_NAMESPACE
-namespace
-{
- struct Color4ub
- {
- unsigned char r, g, b, a;
- };
-
- Color4ub operator *(Color4ub c, float t) { c.a *= t; c.r *= t; c.g *= t; c.b *= t; return c; }
- Color4ub operator +(Color4ub a, Color4ub b) { a.a += b.a; a.r += b.r; a.g += b.g; a.b += b.b; return a; }
-
- inline Color4ub colorToColor4ub(const QColor &c)
- {
- Color4ub color = { uchar(qRound(c.redF() * c.alphaF() * 255)),
- uchar(qRound(c.greenF() * c.alphaF() * 255)),
- uchar(qRound(c.blueF() * c.alphaF() * 255)),
- uchar(qRound(c.alphaF() * 255))
- };
- return color;
- }
-
- // Same layout as QSGGeometry::ColoredPoint2D, but uses Color4ub for convenience.
- struct Vertex
- {
- float x, y;
- Color4ub color;
- void set(float nx, float ny, Color4ub ncolor)
- {
- x = nx; y = ny; color = ncolor;
- }
- };
-
- struct SmoothVertex : public Vertex
- {
- float dx, dy;
- void set(float nx, float ny, Color4ub ncolor, float ndx, float ndy)
- {
- Vertex::set(nx, ny, ncolor);
- dx = ndx; dy = ndy;
- }
- };
-
- const QSGGeometry::AttributeSet &smoothAttributeSet()
- {
- static QSGGeometry::Attribute data[] = {
- QSGGeometry::Attribute::create(0, 2, GL_FLOAT, true),
- QSGGeometry::Attribute::create(1, 4, GL_UNSIGNED_BYTE, false),
- QSGGeometry::Attribute::create(2, 2, GL_FLOAT, false)
- };
- static QSGGeometry::AttributeSet attrs = { 3, sizeof(SmoothVertex), data };
- return attrs;
- }
-}
-
class SmoothColorMaterialShader : public QSGMaterialShader
{
public:
@@ -183,604 +128,32 @@ QSGMaterialShader *QSGSmoothColorMaterial::createShader() const
return new SmoothColorMaterialShader;
}
-
QSGDefaultRectangleNode::QSGDefaultRectangleNode()
- : m_radius(0)
- , m_pen_width(0)
- , m_aligned(true)
- , m_antialiasing(false)
- , m_gradient_is_opaque(true)
- , m_dirty_geometry(false)
- , m_geometry(QSGGeometry::defaultAttributes_ColoredPoint2D(), 0)
{
- setGeometry(&m_geometry);
setMaterial(&m_material);
-
-#ifdef QSG_RUNTIME_DESCRIPTION
- qsgnode_set_description(this, QLatin1String("rectangle"));
-#endif
-}
-
-void QSGDefaultRectangleNode::setRect(const QRectF &rect)
-{
- if (rect == m_rect)
- return;
- m_rect = rect;
- m_dirty_geometry = true;
-}
-
-void QSGDefaultRectangleNode::setColor(const QColor &color)
-{
- if (color == m_color)
- return;
- m_color = color;
- if (m_gradient_stops.isEmpty())
- m_dirty_geometry = true;
-}
-
-void QSGDefaultRectangleNode::setPenColor(const QColor &color)
-{
- if (color == m_border_color)
- return;
- m_border_color = color;
- if (m_pen_width > 0)
- m_dirty_geometry = true;
-}
-
-void QSGDefaultRectangleNode::setPenWidth(qreal width)
-{
- if (width == m_pen_width)
- return;
- m_pen_width = width;
- m_dirty_geometry = true;
-}
-
-
-void QSGDefaultRectangleNode::setGradientStops(const QGradientStops &stops)
-{
- if (stops.constData() == m_gradient_stops.constData())
- return;
-
- m_gradient_stops = stops;
-
- m_gradient_is_opaque = true;
- for (int i = 0; i < stops.size(); ++i)
- m_gradient_is_opaque &= stops.at(i).second.alpha() == 0xff;
- m_dirty_geometry = true;
}
-void QSGDefaultRectangleNode::setRadius(qreal radius)
+void QSGDefaultRectangleNode::updateMaterialAntialiasing()
{
- if (radius == m_radius)
- return;
- m_radius = radius;
- m_dirty_geometry = true;
-}
-
-void QSGDefaultRectangleNode::setAntialiasing(bool antialiasing)
-{
- if (antialiasing == m_antialiasing)
- return;
- m_antialiasing = antialiasing;
- if (m_antialiasing) {
+ if (m_antialiasing)
setMaterial(&m_smoothMaterial);
- setGeometry(new QSGGeometry(smoothAttributeSet(), 0));
- setFlag(OwnsGeometry, true);
- } else {
+ else
setMaterial(&m_material);
- setGeometry(&m_geometry);
- setFlag(OwnsGeometry, false);
- }
- m_dirty_geometry = true;
-}
-
-void QSGDefaultRectangleNode::setAligned(bool aligned)
-{
- if (aligned == m_aligned)
- return;
- m_aligned = aligned;
- m_dirty_geometry = true;
-}
-
-void QSGDefaultRectangleNode::update()
-{
- if (m_dirty_geometry) {
- updateGeometry();
- m_dirty_geometry = false;
-
- QSGNode::DirtyState state = QSGNode::DirtyGeometry;
- // smoothed material is always blended, so no change in material state
- if (material() == &m_material) {
- bool wasBlending = (m_material.flags() & QSGMaterial::Blending);
- bool isBlending = (m_gradient_stops.size() > 0 && !m_gradient_is_opaque)
- || (m_color.alpha() < 255 && m_color.alpha() != 0)
- || (m_pen_width > 0 && m_border_color.alpha() < 255);
- if (wasBlending != isBlending) {
- m_material.setFlag(QSGMaterial::Blending, isBlending);
- state |= QSGNode::DirtyMaterial;
- }
- }
-
- markDirty(state);
- }
}
-void QSGDefaultRectangleNode::updateGeometry()
+void QSGDefaultRectangleNode::updateMaterialBlending(QSGNode::DirtyState *state)
{
- float width = float(m_rect.width());
- float height = float(m_rect.height());
- float penWidth = qMin(qMin(width, height) * 0.5f, float(m_pen_width));
-
- if (m_aligned)
- penWidth = qRound(penWidth);
-
- QSGGeometry *g = geometry();
- g->setDrawingMode(GL_TRIANGLE_STRIP);
- int vertexStride = g->sizeOfVertex();
-
- union {
- Vertex *vertices;
- SmoothVertex *smoothVertices;
- };
-
- Color4ub fillColor = colorToColor4ub(m_color);
- Color4ub borderColor = colorToColor4ub(m_border_color);
- Color4ub transparent = { 0, 0, 0, 0 };
- const QGradientStops &stops = m_gradient_stops;
-
- int nextGradientStop = 0;
- float gradientPos = penWidth / height;
- while (nextGradientStop < stops.size() && stops.at(nextGradientStop).first <= gradientPos)
- ++nextGradientStop;
- int lastGradientStop = stops.size() - 1;
- float lastGradientPos = 1.0f - penWidth / height;
- while (lastGradientStop >= nextGradientStop && stops.at(lastGradientStop).first >= lastGradientPos)
- --lastGradientStop;
- int gradientIntersections = (lastGradientStop - nextGradientStop + 1);
-
- if (m_radius > 0) {
- // Rounded corners.
-
- // Radius should never exceeds half of the width or half of the height
- float radius = qMin(qMin(width, height) * 0.5f, float(m_radius));
- QRectF innerRect = m_rect;
- innerRect.adjust(radius, radius, -radius, -radius);
-
- float innerRadius = radius - penWidth * 1.0f;
- float outerRadius = radius;
- float delta = qMin(width, height) * 0.5f;
-
- // Number of segments per corner, approximately one per 3 pixels.
- int segments = qBound(3, qCeil(outerRadius * (M_PI / 6)), 18);
-
- /*
-
- --+--__
- --+--__--__
- | --__--__
- | seg --__--+
- --+-__ ment _+ \
- --+-__--__ - \ \
- --__--+ se \ \
- + \ g \ \
- \ \ m \ \
- -----------+--+ e \ \ <- gradient line
- \ \ nt\ \
- fill +--+----+--+
- | | | |
- border
- inner AA outer AA (AA = antialiasing)
-
- */
-
- int innerVertexCount = (segments + 1) * 4 + gradientIntersections * 2;
- int outerVertexCount = (segments + 1) * 4;
- int vertexCount = innerVertexCount;
- if (m_antialiasing || penWidth)
- vertexCount += innerVertexCount;
- if (penWidth)
- vertexCount += outerVertexCount;
- if (m_antialiasing && penWidth)
- vertexCount += outerVertexCount;
-
- int fillIndexCount = innerVertexCount;
- int innerAAIndexCount = innerVertexCount * 2 + 2;
- int borderIndexCount = innerVertexCount * 2 + 2;
- int outerAAIndexCount = outerVertexCount * 2 + 2;
- int indexCount = 0;
- int fillHead = 0;
- int innerAAHead = 0;
- int innerAATail = 0;
- int borderHead = 0;
- int borderTail = 0;
- int outerAAHead = 0;
- int outerAATail = 0;
- bool hasFill = m_color.alpha() > 0 || !stops.isEmpty();
- if (hasFill)
- indexCount += fillIndexCount;
- if (m_antialiasing) {
- innerAATail = innerAAHead = indexCount + (innerAAIndexCount >> 1) + 1;
- indexCount += innerAAIndexCount;
- }
- if (penWidth) {
- borderTail = borderHead = indexCount + (borderIndexCount >> 1) + 1;
- indexCount += borderIndexCount;
- }
- if (m_antialiasing && penWidth) {
- outerAATail = outerAAHead = indexCount + (outerAAIndexCount >> 1) + 1;
- indexCount += outerAAIndexCount;
- }
-
- g->allocate(vertexCount, indexCount);
- vertices = reinterpret_cast<Vertex *>(g->vertexData());
- memset(vertices, 0, vertexCount * vertexStride);
- quint16 *indices = g->indexDataAsUShort();
- quint16 index = 0;
-
- float py = 0; // previous inner y-coordinate.
- float plx = 0; // previous inner left x-coordinate.
- float prx = 0; // previous inner right x-coordinate.
-
- float angle = 0.5f * float(M_PI) / segments;
- float cosStep = qFastCos(angle);
- float sinStep = qFastSin(angle);
-
- for (int part = 0; part < 2; ++part) {
- float c = 1 - part;
- float s = part;
- for (int i = 0; i <= segments; ++i) {
- float y, lx, rx;
- if (innerRadius > 0) {
- y = (part ? innerRect.bottom() : innerRect.top()) - innerRadius * c; // current inner y-coordinate.
- lx = innerRect.left() - innerRadius * s; // current inner left x-coordinate.
- rx = innerRect.right() + innerRadius * s; // current inner right x-coordinate.
- gradientPos = ((part ? innerRect.height() : 0) + radius - innerRadius * c) / height;
- } else {
- y = (part ? innerRect.bottom() + innerRadius : innerRect.top() - innerRadius); // current inner y-coordinate.
- lx = innerRect.left() - innerRadius; // current inner left x-coordinate.
- rx = innerRect.right() + innerRadius; // current inner right x-coordinate.
- gradientPos = ((part ? innerRect.height() + innerRadius : -innerRadius) + radius) / height;
- }
- float Y = (part ? innerRect.bottom() : innerRect.top()) - outerRadius * c; // current outer y-coordinate.
- float lX = innerRect.left() - outerRadius * s; // current outer left x-coordinate.
- float rX = innerRect.right() + outerRadius * s; // current outer right x-coordinate.
-
- while (nextGradientStop <= lastGradientStop && stops.at(nextGradientStop).first <= gradientPos) {
- // Insert vertices at gradient stops.
- float gy = (innerRect.top() - radius) + stops.at(nextGradientStop).first * height;
- float t = (gy - py) / (y - py);
- float glx = plx * (1 - t) + t * lx;
- float grx = prx * (1 - t) + t * rx;
-
- fillColor = colorToColor4ub(stops.at(nextGradientStop).second);
-
- if (hasFill) {
- indices[fillHead++] = index;
- indices[fillHead++] = index + 1;
- }
-
- if (penWidth) {
- --borderHead;
- indices[borderHead] = indices[borderHead + 2];
- indices[--borderHead] = index + 2;
- indices[borderTail++] = index + 3;
- indices[borderTail] = indices[borderTail - 2];
- ++borderTail;
- }
-
- if (m_antialiasing) {
- indices[--innerAAHead] = index + 2;
- indices[--innerAAHead] = index;
- indices[innerAATail++] = index + 1;
- indices[innerAATail++] = index + 3;
-
- bool lower = stops.at(nextGradientStop).first > 0.5f;
- float dy = lower ? qMin(0.0f, height - gy - delta) : qMax(0.0f, delta - gy);
- smoothVertices[index++].set(grx, gy, fillColor, width - grx - delta, dy);
- smoothVertices[index++].set(glx, gy, fillColor, delta - glx, dy);
- if (penWidth) {
- smoothVertices[index++].set(grx, gy, borderColor, 0.49f * penWidth * s, -0.49f * penWidth * c);
- smoothVertices[index++].set(glx, gy, borderColor, -0.49f * penWidth * s, -0.49f * penWidth * c);
- } else {
- dy = lower ? delta : -delta;
- smoothVertices[index++].set(grx, gy, transparent, delta, dy);
- smoothVertices[index++].set(glx, gy, transparent, -delta, dy);
- }
- } else {
- vertices[index++].set(grx, gy, fillColor);
- vertices[index++].set(glx, gy, fillColor);
- if (penWidth) {
- vertices[index++].set(grx, gy, borderColor);
- vertices[index++].set(glx, gy, borderColor);
- }
- }
- ++nextGradientStop;
- }
-
- if (!stops.isEmpty()) {
- if (nextGradientStop == 0) {
- fillColor = colorToColor4ub(stops.at(0).second);
- } else if (nextGradientStop == stops.size()) {
- fillColor = colorToColor4ub(stops.last().second);
- } else {
- const QGradientStop &prev = stops.at(nextGradientStop - 1);
- const QGradientStop &next = stops.at(nextGradientStop);
- float t = (gradientPos - prev.first) / (next.first - prev.first);
- fillColor = colorToColor4ub(prev.second) * (1 - t) + colorToColor4ub(next.second) * t;
- }
- }
-
- if (hasFill) {
- indices[fillHead++] = index;
- indices[fillHead++] = index + 1;
- }
-
- if (penWidth) {
- indices[--borderHead] = index + 4;
- indices[--borderHead] = index + 2;
- indices[borderTail++] = index + 3;
- indices[borderTail++] = index + 5;
- }
-
- if (m_antialiasing) {
- indices[--innerAAHead] = index + 2;
- indices[--innerAAHead] = index;
- indices[innerAATail++] = index + 1;
- indices[innerAATail++] = index + 3;
-
- float dy = part ? qMin(0.0f, height - y - delta) : qMax(0.0f, delta - y);
- smoothVertices[index++].set(rx, y, fillColor, width - rx - delta, dy);
- smoothVertices[index++].set(lx, y, fillColor, delta - lx, dy);
-
- dy = part ? delta : -delta;
- if (penWidth) {
- smoothVertices[index++].set(rx, y, borderColor, 0.49f * penWidth * s, -0.49f * penWidth * c);
- smoothVertices[index++].set(lx, y, borderColor, -0.49f * penWidth * s, -0.49f * penWidth * c);
- smoothVertices[index++].set(rX, Y, borderColor, -0.49f * penWidth * s, 0.49f * penWidth * c);
- smoothVertices[index++].set(lX, Y, borderColor, 0.49f * penWidth * s, 0.49f * penWidth * c);
- smoothVertices[index++].set(rX, Y, transparent, delta, dy);
- smoothVertices[index++].set(lX, Y, transparent, -delta, dy);
-
- indices[--outerAAHead] = index - 2;
- indices[--outerAAHead] = index - 4;
- indices[outerAATail++] = index - 3;
- indices[outerAATail++] = index - 1;
- } else {
- smoothVertices[index++].set(rx, y, transparent, delta, dy);
- smoothVertices[index++].set(lx, y, transparent, -delta, dy);
- }
- } else {
- vertices[index++].set(rx, y, fillColor);
- vertices[index++].set(lx, y, fillColor);
- if (penWidth) {
- vertices[index++].set(rx, y, borderColor);
- vertices[index++].set(lx, y, borderColor);
- vertices[index++].set(rX, Y, borderColor);
- vertices[index++].set(lX, Y, borderColor);
- }
- }
-
- py = y;
- plx = lx;
- prx = rx;
-
- // Rotate
- qreal tmp = c;
- c = c * cosStep - s * sinStep;
- s = s * cosStep + tmp * sinStep;
- }
- }
- Q_ASSERT(index == vertexCount);
-
- // Close the triangle strips.
- if (m_antialiasing) {
- indices[--innerAAHead] = indices[innerAATail - 1];
- indices[--innerAAHead] = indices[innerAATail - 2];
- Q_ASSERT(innerAATail <= indexCount);
- }
- if (penWidth) {
- indices[--borderHead] = indices[borderTail - 1];
- indices[--borderHead] = indices[borderTail - 2];
- Q_ASSERT(borderTail <= indexCount);
- }
- if (m_antialiasing && penWidth) {
- indices[--outerAAHead] = indices[outerAATail - 1];
- indices[--outerAAHead] = indices[outerAATail - 2];
- Q_ASSERT(outerAATail == indexCount);
- }
- } else {
- // Straight corners.
- QRectF innerRect = m_rect;
- QRectF outerRect = m_rect;
-
- if (penWidth)
- innerRect.adjust(1.0f * penWidth, 1.0f * penWidth, -1.0f * penWidth, -1.0f * penWidth);
-
- float delta = qMin(width, height) * 0.5f;
- int innerVertexCount = 4 + gradientIntersections * 2;
- int outerVertexCount = 4;
- int vertexCount = innerVertexCount;
- if (m_antialiasing || penWidth)
- vertexCount += innerVertexCount;
- if (penWidth)
- vertexCount += outerVertexCount;
- if (m_antialiasing && penWidth)
- vertexCount += outerVertexCount;
-
- int fillIndexCount = innerVertexCount;
- int innerAAIndexCount = innerVertexCount * 2 + 2;
- int borderIndexCount = innerVertexCount * 2 + 2;
- int outerAAIndexCount = outerVertexCount * 2 + 2;
- int indexCount = 0;
- int fillHead = 0;
- int innerAAHead = 0;
- int innerAATail = 0;
- int borderHead = 0;
- int borderTail = 0;
- int outerAAHead = 0;
- int outerAATail = 0;
- bool hasFill = m_color.alpha() > 0 || !stops.isEmpty();
- if (hasFill)
- indexCount += fillIndexCount;
- if (m_antialiasing) {
- innerAATail = innerAAHead = indexCount + (innerAAIndexCount >> 1) + 1;
- indexCount += innerAAIndexCount;
- }
- if (penWidth) {
- borderTail = borderHead = indexCount + (borderIndexCount >> 1) + 1;
- indexCount += borderIndexCount;
- }
- if (m_antialiasing && penWidth) {
- outerAATail = outerAAHead = indexCount + (outerAAIndexCount >> 1) + 1;
- indexCount += outerAAIndexCount;
- }
-
- g->allocate(vertexCount, indexCount);
- vertices = reinterpret_cast<Vertex *>(g->vertexData());
- memset(vertices, 0, vertexCount * vertexStride);
- quint16 *indices = g->indexDataAsUShort();
- quint16 index = 0;
-
- float lx = innerRect.left();
- float rx = innerRect.right();
- float lX = outerRect.left();
- float rX = outerRect.right();
-
- for (int part = -1; part <= 1; part += 2) {
- float y = (part == 1 ? innerRect.bottom() : innerRect.top());
- float Y = (part == 1 ? outerRect.bottom() : outerRect.top());
- gradientPos = (y - innerRect.top() + penWidth) / height;
-
- while (nextGradientStop <= lastGradientStop && stops.at(nextGradientStop).first <= gradientPos) {
- // Insert vertices at gradient stops.
- float gy = (innerRect.top() - penWidth) + stops.at(nextGradientStop).first * height;
-
- fillColor = colorToColor4ub(stops.at(nextGradientStop).second);
-
- if (hasFill) {
- indices[fillHead++] = index;
- indices[fillHead++] = index + 1;
- }
-
- if (penWidth) {
- --borderHead;
- indices[borderHead] = indices[borderHead + 2];
- indices[--borderHead] = index + 2;
- indices[borderTail++] = index + 3;
- indices[borderTail] = indices[borderTail - 2];
- ++borderTail;
- }
-
- if (m_antialiasing) {
- indices[--innerAAHead] = index + 2;
- indices[--innerAAHead] = index;
- indices[innerAATail++] = index + 1;
- indices[innerAATail++] = index + 3;
-
- bool lower = stops.at(nextGradientStop).first > 0.5f;
- float dy = lower ? qMin(0.0f, height - gy - delta) : qMax(0.0f, delta - gy);
- smoothVertices[index++].set(rx, gy, fillColor, width - rx - delta, dy);
- smoothVertices[index++].set(lx, gy, fillColor, delta - lx, dy);
- if (penWidth) {
- smoothVertices[index++].set(rx, gy, borderColor, 0.49f * penWidth, (lower ? 0.49f : -0.49f) * penWidth);
- smoothVertices[index++].set(lx, gy, borderColor, -0.49f * penWidth, (lower ? 0.49f : -0.49f) * penWidth);
- } else {
- smoothVertices[index++].set(rx, gy, transparent, delta, lower ? delta : -delta);
- smoothVertices[index++].set(lx, gy, transparent, -delta, lower ? delta : -delta);
- }
- } else {
- vertices[index++].set(rx, gy, fillColor);
- vertices[index++].set(lx, gy, fillColor);
- if (penWidth) {
- vertices[index++].set(rx, gy, borderColor);
- vertices[index++].set(lx, gy, borderColor);
- }
- }
- ++nextGradientStop;
- }
-
- if (!stops.isEmpty()) {
- if (nextGradientStop == 0) {
- fillColor = colorToColor4ub(stops.at(0).second);
- } else if (nextGradientStop == stops.size()) {
- fillColor = colorToColor4ub(stops.last().second);
- } else {
- const QGradientStop &prev = stops.at(nextGradientStop - 1);
- const QGradientStop &next = stops.at(nextGradientStop);
- float t = (gradientPos - prev.first) / (next.first - prev.first);
- fillColor = colorToColor4ub(prev.second) * (1 - t) + colorToColor4ub(next.second) * t;
- }
- }
-
- if (hasFill) {
- indices[fillHead++] = index;
- indices[fillHead++] = index + 1;
- }
-
- if (penWidth) {
- indices[--borderHead] = index + 4;
- indices[--borderHead] = index + 2;
- indices[borderTail++] = index + 3;
- indices[borderTail++] = index + 5;
- }
-
- if (m_antialiasing) {
- indices[--innerAAHead] = index + 2;
- indices[--innerAAHead] = index;
- indices[innerAATail++] = index + 1;
- indices[innerAATail++] = index + 3;
-
- float dy = part == 1 ? qMin(0.0f, height - y - delta) : qMax(0.0f, delta - y);
- smoothVertices[index++].set(rx, y, fillColor, width - rx - delta, dy);
- smoothVertices[index++].set(lx, y, fillColor, delta - lx, dy);
-
- if (penWidth) {
- smoothVertices[index++].set(rx, y, borderColor, 0.49f * penWidth, 0.49f * penWidth * part);
- smoothVertices[index++].set(lx, y, borderColor, -0.49f * penWidth, 0.49f * penWidth * part);
- smoothVertices[index++].set(rX, Y, borderColor, -0.49f * penWidth, -0.49f * penWidth * part);
- smoothVertices[index++].set(lX, Y, borderColor, 0.49f * penWidth, -0.49f * penWidth * part);
- smoothVertices[index++].set(rX, Y, transparent, delta, delta * part);
- smoothVertices[index++].set(lX, Y, transparent, -delta, delta * part);
-
- indices[--outerAAHead] = index - 2;
- indices[--outerAAHead] = index - 4;
- indices[outerAATail++] = index - 3;
- indices[outerAATail++] = index - 1;
- } else {
- smoothVertices[index++].set(rx, y, transparent, delta, delta * part);
- smoothVertices[index++].set(lx, y, transparent, -delta, delta * part);
- }
- } else {
- vertices[index++].set(rx, y, fillColor);
- vertices[index++].set(lx, y, fillColor);
- if (penWidth) {
- vertices[index++].set(rx, y, borderColor);
- vertices[index++].set(lx, y, borderColor);
- vertices[index++].set(rX, Y, borderColor);
- vertices[index++].set(lX, Y, borderColor);
- }
- }
- }
- Q_ASSERT(index == vertexCount);
-
- // Close the triangle strips.
- if (m_antialiasing) {
- indices[--innerAAHead] = indices[innerAATail - 1];
- indices[--innerAAHead] = indices[innerAATail - 2];
- Q_ASSERT(innerAATail <= indexCount);
- }
- if (penWidth) {
- indices[--borderHead] = indices[borderTail - 1];
- indices[--borderHead] = indices[borderTail - 2];
- Q_ASSERT(borderTail <= indexCount);
- }
- if (m_antialiasing && penWidth) {
- indices[--outerAAHead] = indices[outerAATail - 1];
- indices[--outerAAHead] = indices[outerAATail - 2];
- Q_ASSERT(outerAATail == indexCount);
+ // smoothed material is always blended, so no change in material state
+ if (material() == &m_material) {
+ bool wasBlending = (m_material.flags() & QSGMaterial::Blending);
+ bool isBlending = (m_gradient_stops.size() > 0 && !m_gradient_is_opaque)
+ || (m_color.alpha() < 255 && m_color.alpha() != 0)
+ || (m_pen_width > 0 && m_border_color.alpha() < 255);
+ if (wasBlending != isBlending) {
+ m_material.setFlag(QSGMaterial::Blending, isBlending);
+ *state |= QSGNode::DirtyMaterial;
}
}
}
-
QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/qsgdefaultrectanglenode_p.h b/src/quick/scenegraph/qsgdefaultrectanglenode_p.h
index 4cfe921127..f30a3beed7 100644
--- a/src/quick/scenegraph/qsgdefaultrectanglenode_p.h
+++ b/src/quick/scenegraph/qsgdefaultrectanglenode_p.h
@@ -53,7 +53,7 @@
//
#include <private/qsgadaptationlayer_p.h>
-
+#include <private/qsgbasicrectanglenode_p.h>
#include <QtQuick/qsgvertexcolormaterial.h>
QT_BEGIN_NAMESPACE
@@ -68,45 +68,21 @@ public:
int compare(const QSGMaterial *other) const;
protected:
- virtual QSGMaterialType *type() const;
- virtual QSGMaterialShader *createShader() const;
+ QSGMaterialType *type() const override;
+ QSGMaterialShader *createShader() const override;
};
-class Q_QUICK_PRIVATE_EXPORT QSGDefaultRectangleNode : public QSGRectangleNode
+class Q_QUICK_PRIVATE_EXPORT QSGDefaultRectangleNode : public QSGBasicRectangleNode
{
public:
QSGDefaultRectangleNode();
- virtual void setRect(const QRectF &rect);
- virtual void setColor(const QColor &color);
- virtual void setPenColor(const QColor &color);
- virtual void setPenWidth(qreal width);
- virtual void setGradientStops(const QGradientStops &stops);
- virtual void setRadius(qreal radius);
- virtual void setAntialiasing(bool antialiasing);
- virtual void setAligned(bool aligned);
- virtual void update();
-
private:
- void updateGeometry();
- void updateGradientTexture();
+ void updateMaterialAntialiasing() override;
+ void updateMaterialBlending(QSGNode::DirtyState *state) override;
QSGVertexColorMaterial m_material;
QSGSmoothColorMaterial m_smoothMaterial;
-
- QRectF m_rect;
- QGradientStops m_gradient_stops;
- QColor m_color;
- QColor m_border_color;
- qreal m_radius;
- qreal m_pen_width;
-
- uint m_aligned : 1;
- uint m_antialiasing : 1;
- uint m_gradient_is_opaque : 1;
- uint m_dirty_geometry : 1;
-
- QSGGeometry m_geometry;
};
QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/qsgdefaultrendercontext.cpp b/src/quick/scenegraph/qsgdefaultrendercontext.cpp
new file mode 100644
index 0000000000..92e7f983a0
--- /dev/null
+++ b/src/quick/scenegraph/qsgdefaultrendercontext.cpp
@@ -0,0 +1,302 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgdefaultrendercontext_p.h"
+
+#include <QtGui/QOpenGLFramebufferObject>
+
+#include <QtQuick/private/qsgbatchrenderer_p.h>
+#include <QtQuick/private/qsgrenderer_p.h>
+#include <QtQuick/private/qsgatlastexture_p.h>
+#include <QtQuick/private/qsgdefaultdistancefieldglyphcache_p.h>
+#include <QtQuick/private/qsgdistancefieldutil_p.h>
+
+QT_BEGIN_NAMESPACE
+
+#define QSG_RENDERCONTEXT_PROPERTY "_q_sgrendercontext"
+
+QSGDefaultRenderContext::QSGDefaultRenderContext(QSGContext *context)
+ : QSGRenderContext(context)
+ , m_gl(nullptr)
+ , m_depthStencilManager(nullptr)
+ , m_maxTextureSize(0)
+ , m_brokenIBOs(false)
+ , m_serializedRender(false)
+ , m_attachToGLContext(true)
+ , m_atlasManager(nullptr)
+{
+
+}
+
+/*!
+ Initializes the scene graph render context with the GL context \a context. This also
+ emits the ready() signal so that the QML graph can start building scene graph nodes.
+ */
+void QSGDefaultRenderContext::initialize(void *context)
+{
+ QOpenGLContext *openglContext = static_cast<QOpenGLContext *>(context);
+
+ QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions();
+ funcs->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &m_maxTextureSize);
+
+ // Sanity check the surface format, in case it was overridden by the application
+ QSurfaceFormat requested = m_sg->defaultSurfaceFormat();
+ QSurfaceFormat actual = openglContext->format();
+ if (requested.depthBufferSize() > 0 && actual.depthBufferSize() <= 0)
+ qWarning("QSGContext::initialize: depth buffer support missing, expect rendering errors");
+ if (requested.stencilBufferSize() > 0 && actual.stencilBufferSize() <= 0)
+ qWarning("QSGContext::initialize: stencil buffer support missing, expect rendering errors");
+
+ if (!m_atlasManager)
+ m_atlasManager = new QSGAtlasTexture::Manager();
+
+ Q_ASSERT_X(!m_gl, "QSGRenderContext::initialize", "already initialized!");
+ m_gl = openglContext;
+ if (m_attachToGLContext) {
+ Q_ASSERT(!openglContext->property(QSG_RENDERCONTEXT_PROPERTY).isValid());
+ openglContext->setProperty(QSG_RENDERCONTEXT_PROPERTY, QVariant::fromValue(this));
+ }
+ m_sg->renderContextInitialized(this);
+
+#ifdef Q_OS_LINUX
+ const char *vendor = (const char *) funcs->glGetString(GL_VENDOR);
+ if (strstr(vendor, "nouveau"))
+ m_brokenIBOs = true;
+ const char *renderer = (const char *) funcs->glGetString(GL_RENDERER);
+ if (strstr(renderer, "llvmpipe"))
+ m_serializedRender = true;
+ if (strstr(vendor, "Hisilicon Technologies") && strstr(renderer, "Immersion.16"))
+ m_brokenIBOs = true;
+#endif
+
+ emit initialized();
+}
+
+
+void QSGDefaultRenderContext::invalidate()
+{
+ if (!m_gl)
+ return;
+
+ qDeleteAll(m_texturesToDelete);
+ m_texturesToDelete.clear();
+
+ qDeleteAll(m_textures);
+ m_textures.clear();
+
+ /* The cleanup of the atlas textures is a bit intriguing.
+ As part of the cleanup in the threaded render loop, we
+ do:
+ 1. call this function
+ 2. call QCoreApp::sendPostedEvents() to immediately process
+ any pending deferred deletes.
+ 3. delete the GL context.
+
+ As textures need the atlas manager while cleaning up, the
+ manager needs to be cleaned up after the textures, so
+ we post a deleteLater here at the very bottom so it gets
+ deferred deleted last.
+
+ Another alternative would be to use a QPointer in
+ QSGAtlasTexture::Texture, but this seemed simpler.
+ */
+ m_atlasManager->invalidate();
+ m_atlasManager->deleteLater();
+ m_atlasManager = nullptr;
+
+ // The following piece of code will read/write to the font engine's caches,
+ // potentially from different threads. However, this is safe because this
+ // code is only called from QQuickWindow's shutdown which is called
+ // only when the GUI is blocked, and multiple threads will call it in
+ // sequence. (see qsgdefaultglyphnode_p.cpp's init())
+ for (QSet<QFontEngine *>::const_iterator it = m_fontEnginesToClean.constBegin(),
+ end = m_fontEnginesToClean.constEnd(); it != end; ++it) {
+ (*it)->clearGlyphCache(m_gl);
+ if (!(*it)->ref.deref())
+ delete *it;
+ }
+ m_fontEnginesToClean.clear();
+
+ delete m_depthStencilManager;
+ m_depthStencilManager = 0;
+
+ delete m_distanceFieldCacheManager;
+ m_distanceFieldCacheManager = 0;
+
+ if (m_gl->property(QSG_RENDERCONTEXT_PROPERTY) == QVariant::fromValue(this))
+ m_gl->setProperty(QSG_RENDERCONTEXT_PROPERTY, QVariant());
+ m_gl = 0;
+
+ QSGRenderContext::invalidate();
+}
+
+static QBasicMutex qsg_framerender_mutex;
+
+void QSGDefaultRenderContext::renderNextFrame(QSGRenderer *renderer, uint fboId)
+{
+ if (m_serializedRender)
+ qsg_framerender_mutex.lock();
+
+ renderer->renderScene(fboId);
+
+ if (m_serializedRender)
+ qsg_framerender_mutex.unlock();
+}
+
+/*!
+ Returns a shared pointer to a depth stencil buffer that can be used with \a fbo.
+*/
+QSharedPointer<QSGDepthStencilBuffer> QSGDefaultRenderContext::depthStencilBufferForFbo(QOpenGLFramebufferObject *fbo)
+{
+ if (!m_gl)
+ return QSharedPointer<QSGDepthStencilBuffer>();
+ QSGDepthStencilBufferManager *manager = depthStencilBufferManager();
+ QSGDepthStencilBuffer::Format format;
+ format.size = fbo->size();
+ format.samples = fbo->format().samples();
+ format.attachments = QSGDepthStencilBuffer::DepthAttachment | QSGDepthStencilBuffer::StencilAttachment;
+ QSharedPointer<QSGDepthStencilBuffer> buffer = manager->bufferForFormat(format);
+ if (buffer.isNull()) {
+ buffer = QSharedPointer<QSGDepthStencilBuffer>(new QSGDefaultDepthStencilBuffer(m_gl, format));
+ manager->insertBuffer(buffer);
+ }
+ return buffer;
+}
+
+/*!
+ Returns a pointer to the context's depth/stencil buffer manager. This is useful for custom
+ implementations of \l depthStencilBufferForFbo().
+*/
+QSGDepthStencilBufferManager *QSGDefaultRenderContext::depthStencilBufferManager()
+{
+ if (!m_gl)
+ return 0;
+ if (!m_depthStencilManager)
+ m_depthStencilManager = new QSGDepthStencilBufferManager(m_gl);
+ return m_depthStencilManager;
+}
+
+QSGTexture *QSGDefaultRenderContext::createTexture(const QImage &image, uint flags) const
+{
+ bool atlas = flags & CreateTexture_Atlas;
+ bool mipmap = flags & CreateTexture_Mipmap;
+ bool alpha = flags & CreateTexture_Alpha;
+
+ // The atlas implementation is only supported from the render thread and
+ // does not support mipmaps.
+ if (!mipmap && atlas && openglContext() && QThread::currentThread() == openglContext()->thread()) {
+ QSGTexture *t = m_atlasManager->create(image, alpha);
+ if (t)
+ return t;
+ }
+
+ QSGPlainTexture *texture = new QSGPlainTexture();
+ texture->setImage(image);
+ if (texture->hasAlphaChannel() && !alpha)
+ texture->setHasAlphaChannel(false);
+
+ return texture;
+}
+
+QSGRenderer *QSGDefaultRenderContext::createRenderer()
+{
+ return new QSGBatchRenderer::Renderer(this);
+}
+
+/*!
+ Compile \a shader, optionally using \a vertexCode and \a fragmentCode as
+ replacement for the source code supplied by \a shader.
+
+ If \a vertexCode or \a fragmentCode is supplied, the caller is responsible
+ for setting up attribute bindings.
+
+ \a material is supplied in case the implementation needs to take the
+ material flags into account.
+ */
+void QSGDefaultRenderContext::compileShader(QSGMaterialShader *shader, QSGMaterial *material, const char *vertexCode, const char *fragmentCode)
+{
+ Q_UNUSED(material);
+ if (vertexCode || fragmentCode) {
+ Q_ASSERT_X((material->flags() & QSGMaterial::CustomCompileStep) == 0,
+ "QSGRenderContext::compile()",
+ "materials with custom compile step cannot have custom vertex/fragment code");
+ QOpenGLShaderProgram *p = shader->program();
+ p->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexCode ? vertexCode : shader->vertexShader());
+ p->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentCode ? fragmentCode : shader->fragmentShader());
+ p->link();
+ if (!p->isLinked())
+ qWarning() << "shader compilation failed:" << endl << p->log();
+ } else {
+ shader->compile();
+ }
+}
+
+void QSGDefaultRenderContext::initializeShader(QSGMaterialShader *shader)
+{
+ shader->program()->bind();
+ shader->initialize();
+}
+
+void QSGDefaultRenderContext::setAttachToGLContext(bool attach)
+{
+ Q_ASSERT(!isValid());
+ m_attachToGLContext = attach;
+}
+
+QSGDefaultRenderContext *QSGDefaultRenderContext::from(QOpenGLContext *context)
+{
+ return qobject_cast<QSGDefaultRenderContext *>(context->property(QSG_RENDERCONTEXT_PROPERTY).value<QObject *>());
+}
+
+QT_END_NAMESPACE
+
+
+QSGDistanceFieldGlyphCache *QSGDefaultRenderContext::distanceFieldGlyphCache(const QRawFont &font)
+{
+ if (!m_distanceFieldCacheManager)
+ m_distanceFieldCacheManager = new QSGDistanceFieldGlyphCacheManager;
+
+ QSGDistanceFieldGlyphCache *cache = m_distanceFieldCacheManager->cache(font);
+ if (!cache) {
+ cache = new QSGDefaultDistanceFieldGlyphCache(m_distanceFieldCacheManager, openglContext(), font);
+ m_distanceFieldCacheManager->insertCache(font, cache);
+ }
+
+ return cache;
+}
diff --git a/src/quick/scenegraph/qsgdefaultrendercontext_p.h b/src/quick/scenegraph/qsgdefaultrendercontext_p.h
new file mode 100644
index 0000000000..bfb15b1eb9
--- /dev/null
+++ b/src/quick/scenegraph/qsgdefaultrendercontext_p.h
@@ -0,0 +1,112 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGDEFAULTRENDERCONTEXT_H
+#define QSGDEFAULTRENDERCONTEXT_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtQuick/private/qsgcontext_p.h>
+#include <QtQuick/private/qsgdepthstencilbuffer_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QOpenGLContext;
+class QSGMaterialShader;
+class QOpenGLFramebufferObject;
+
+namespace QSGAtlasTexture {
+ class Manager;
+}
+
+class QSGDefaultRenderContext : public QSGRenderContext
+{
+ Q_OBJECT
+public:
+ QSGDefaultRenderContext(QSGContext *context);
+
+ QOpenGLContext *openglContext() const { return m_gl; }
+ bool isValid() const override { return m_gl; }
+
+ void initialize(void *context) override;
+ void invalidate() override;
+ void renderNextFrame(QSGRenderer *renderer, uint fboId) override;
+
+ QSGDistanceFieldGlyphCache *distanceFieldGlyphCache(const QRawFont &font) override;
+
+ virtual QSharedPointer<QSGDepthStencilBuffer> depthStencilBufferForFbo(QOpenGLFramebufferObject *fbo);
+ QSGDepthStencilBufferManager *depthStencilBufferManager();
+
+ QSGTexture *createTexture(const QImage &image, uint flags) const override;
+ QSGRenderer *createRenderer() override;
+
+ virtual void compileShader(QSGMaterialShader *shader, QSGMaterial *material, const char *vertexCode = 0, const char *fragmentCode = 0);
+ virtual void initializeShader(QSGMaterialShader *shader);
+
+ void setAttachToGLContext(bool attach);
+
+ static QSGDefaultRenderContext *from(QOpenGLContext *context);
+
+ bool hasBrokenIndexBufferObjects() const { return m_brokenIBOs; }
+ int maxTextureSize() const { return m_maxTextureSize; }
+
+protected:
+ QOpenGLContext *m_gl;
+ QSGDepthStencilBufferManager *m_depthStencilManager;
+ int m_maxTextureSize;
+ bool m_brokenIBOs;
+ bool m_serializedRender;
+ bool m_attachToGLContext;
+ QSGAtlasTexture::Manager *m_atlasManager;
+
+
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGDEFAULTRENDERCONTEXT_H
diff --git a/src/quick/scenegraph/qsgrenderloop.cpp b/src/quick/scenegraph/qsgrenderloop.cpp
index 1b0b1acc0e..cb8f1399a4 100644
--- a/src/quick/scenegraph/qsgrenderloop.cpp
+++ b/src/quick/scenegraph/qsgrenderloop.cpp
@@ -47,7 +47,6 @@
#include <QtCore/QLibraryInfo>
#include <QtCore/private/qabstractanimation_p.h>
-#include <QtGui/QOpenGLContext>
#include <QtGui/QOffscreenSurface>
#include <QtGui/private/qguiapplication_p.h>
#include <qpa/qplatformintegration.h>
@@ -59,7 +58,11 @@
#include <QtQuick/private/qsgcontext_p.h>
#include <private/qquickprofiler_p.h>
-#include <private/qquickshadereffectnode_p.h>
+#ifndef QT_NO_OPENGL
+# include <QtGui/QOpenGLContext>
+# include <private/qsgdefaultrendercontext_p.h>
+# include <private/qquickopenglshadereffectnode_p.h>
+#endif
#ifdef Q_OS_WIN
# include <QtCore/qt_windows.h>
@@ -69,7 +72,7 @@ QT_BEGIN_NAMESPACE
extern bool qsg_useConsistentTiming();
extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha);
-
+#ifndef QT_NO_OPENGL
/*!
expectations for this manager to work:
- one opengl context to render multiple windows
@@ -81,7 +84,7 @@ extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_
DEFINE_BOOL_CONFIG_OPTION(qmlNoThreadedRenderer, QML_BAD_GUI_RENDER_LOOP);
DEFINE_BOOL_CONFIG_OPTION(qmlForceThreadedRenderer, QML_FORCE_THREADED_RENDERER); // Might trigger graphics driver threading bugs, use at own risk
-
+#endif
QSGRenderLoop *QSGRenderLoop::s_instance = 0;
QSGRenderLoop::~QSGRenderLoop()
@@ -113,17 +116,20 @@ void QSGRenderLoop::cleanup()
*/
void QSGRenderLoop::postJob(QQuickWindow *window, QRunnable *job)
{
- Q_ASSERT(window);
Q_ASSERT(job);
-
+#ifndef QT_NO_OPENGL
+ Q_ASSERT(window);
if (window->openglContext()) {
window->openglContext()->makeCurrent(window);
job->run();
}
-
+#else
+ Q_UNUSED(window)
+ job->run();
+#endif
delete job;
}
-
+#ifndef QT_NO_OPENGL
class QSGGuiThreadRenderLoop : public QSGRenderLoop
{
Q_OBJECT
@@ -164,7 +170,7 @@ public:
QImage grabContent;
};
-
+#endif
QSGRenderLoop *QSGRenderLoop::instance()
{
if (!s_instance) {
@@ -174,7 +180,7 @@ QSGRenderLoop *QSGRenderLoop::instance()
const_cast<QLoggingCategory &>(QSG_LOG_INFO()).setEnabled(QtDebugMsg, true);
s_instance = QSGContext::createWindowManager();
-
+#ifndef QT_NO_OPENGL
if (!s_instance) {
enum RenderLoopType {
@@ -226,9 +232,10 @@ QSGRenderLoop *QSGRenderLoop::instance()
break;
}
}
-
+#endif
qAddPostRoutine(QSGRenderLoop::cleanup);
}
+
return s_instance;
}
@@ -263,7 +270,7 @@ void QSGRenderLoop::handleContextCreationFailure(QQuickWindow *window,
if (!signalEmitted)
qFatal("%s", qPrintable(untranslatedMessage));
}
-
+#ifndef QT_NO_OPENGL
QSGGuiThreadRenderLoop::QSGGuiThreadRenderLoop()
: gl(0)
{
@@ -319,7 +326,9 @@ void QSGGuiThreadRenderLoop::windowDestroyed(QQuickWindow *window)
if (Q_UNLIKELY(!current))
qCDebug(QSG_LOG_RENDERLOOP) << "cleanup without an OpenGL context";
- QQuickShaderEffectMaterial::cleanupMaterialCache();
+#ifndef QT_NO_OPENGL
+ QQuickOpenGLShaderEffectMaterial::cleanupMaterialCache();
+#endif
d->cleanupNodesOnShutdown();
if (m_windows.size() == 0) {
@@ -358,8 +367,10 @@ void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window)
cd->fireOpenGLContextCreated(gl);
current = gl->makeCurrent(window);
}
- if (current)
- cd->context->initialize(gl);
+ if (current) {
+ auto openglRenderContext = static_cast<QSGDefaultRenderContext *>(cd->context);
+ openglRenderContext->initialize(gl);
+ }
} else {
current = gl->makeCurrent(window);
}
@@ -480,6 +491,8 @@ void QSGGuiThreadRenderLoop::handleUpdateRequest(QQuickWindow *window)
renderWindow(window);
}
+#endif
+
#include "qsgrenderloop.moc"
QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/qsgrenderloop_p.h b/src/quick/scenegraph/qsgrenderloop_p.h
index 9159a2f01d..7bf40ecac4 100644
--- a/src/quick/scenegraph/qsgrenderloop_p.h
+++ b/src/quick/scenegraph/qsgrenderloop_p.h
@@ -69,6 +69,10 @@ class Q_QUICK_PRIVATE_EXPORT QSGRenderLoop : public QObject
Q_OBJECT
public:
+ enum RenderLoopFlags {
+ SupportsGrabWithoutExpose = 0x01
+ };
+
virtual ~QSGRenderLoop();
virtual void show(QQuickWindow *window) = 0;
@@ -104,6 +108,8 @@ public:
virtual bool interleaveIncubation() const { return false; }
+ virtual int flags() const { return 0; }
+
static void cleanup();
Q_SIGNALS:
diff --git a/src/quick/scenegraph/qsgthreadedrenderloop.cpp b/src/quick/scenegraph/qsgthreadedrenderloop.cpp
index 7c3405b715..1feba32ab2 100644
--- a/src/quick/scenegraph/qsgthreadedrenderloop.cpp
+++ b/src/quick/scenegraph/qsgthreadedrenderloop.cpp
@@ -63,7 +63,8 @@
#include <private/qqmldebugserviceinterfaces_p.h>
#include <private/qqmldebugconnector_p.h>
-#include <private/qquickshadereffectnode_p.h>
+#include <private/qquickopenglshadereffectnode_p.h>
+#include <private/qsgdefaultrendercontext_p.h>
/*
Overall design:
@@ -268,7 +269,6 @@ public:
QSGRenderThread(QSGThreadedRenderLoop *w, QSGRenderContext *renderContext)
: wm(w)
, gl(0)
- , sgrc(renderContext)
, animatorDriver(0)
, pendingUpdate(0)
, sleeping(false)
@@ -277,6 +277,7 @@ public:
, window(0)
, stopEventProcessing(false)
{
+ sgrc = static_cast<QSGDefaultRenderContext *>(renderContext);
#if defined(Q_OS_QNX) && !defined(Q_OS_BLACKBERRY) && defined(Q_PROCESSOR_X86)
// The SDP 6.6.0 x86 MESA driver requires a larger stack than the default.
setStackSize(1024 * 1024);
@@ -325,7 +326,7 @@ public:
QSGThreadedRenderLoop *wm;
QOpenGLContext *gl;
- QSGRenderContext *sgrc;
+ QSGDefaultRenderContext *sgrc;
QAnimationDriver *animatorDriver;
@@ -486,7 +487,7 @@ void QSGRenderThread::invalidateOpenGL(QQuickWindow *window, bool inDestructor,
QQuickWindowPrivate *dd = QQuickWindowPrivate::get(window);
- QQuickShaderEffectMaterial::cleanupMaterialCache();
+ QQuickOpenGLShaderEffectMaterial::cleanupMaterialCache();
// The canvas nodes must be cleaned up regardless if we are in the destructor..
if (wipeSG) {
diff --git a/src/quick/scenegraph/qsgwindowsrenderloop.cpp b/src/quick/scenegraph/qsgwindowsrenderloop.cpp
index 901fbbec43..5e025e9761 100644
--- a/src/quick/scenegraph/qsgwindowsrenderloop.cpp
+++ b/src/quick/scenegraph/qsgwindowsrenderloop.cpp
@@ -48,13 +48,17 @@
#include <QtQuick/private/qsgcontext_p.h>
#include <QtQuick/private/qquickwindow_p.h>
+#include <QtQuick/private/qsgdefaultrendercontext_p.h>
#include <QtQuick/QQuickWindow>
#include <private/qquickprofiler_p.h>
-#include <private/qquickshadereffectnode_p.h>
#include <private/qquickanimatorcontroller_p.h>
+#ifndef QT_NO_OPENGL
+#include <private/qquickopenglshadereffectnode_p.h>
+#endif
+
QT_BEGIN_NAMESPACE
extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha);
@@ -78,7 +82,7 @@ QSGWindowsRenderLoop::QSGWindowsRenderLoop()
, m_updateTimer(0)
, m_animationTimer(0)
{
- m_rc = m_sg->createRenderContext();
+ m_rc = static_cast<QSGDefaultRenderContext *>(m_sg->createRenderContext());
m_animationDriver = m_sg->createAnimationDriver(m_sg);
m_animationDriver->install();
@@ -241,7 +245,9 @@ void QSGWindowsRenderLoop::windowDestroyed(QQuickWindow *window)
if (Q_UNLIKELY(!current))
qCDebug(QSG_LOG_RENDERLOOP) << "cleanup without an OpenGL context";
- QQuickShaderEffectMaterial::cleanupMaterialCache();
+#ifndef QT_NO_OPENGL
+ QQuickOpenGLShaderEffectMaterial::cleanupMaterialCache();
+#endif
d->cleanupNodesOnShutdown();
if (m_windows.size() == 0) {
@@ -341,6 +347,11 @@ void QSGWindowsRenderLoop::maybeUpdate(QQuickWindow *window)
maybePostUpdateTimer();
}
+QSGRenderContext *QSGWindowsRenderLoop::createRenderContext(QSGContext *) const
+{
+ return m_rc;
+}
+
bool QSGWindowsRenderLoop::event(QEvent *event)
{
switch (event->type()) {
diff --git a/src/quick/scenegraph/qsgwindowsrenderloop_p.h b/src/quick/scenegraph/qsgwindowsrenderloop_p.h
index ad7986035f..9e5d7f04d3 100644
--- a/src/quick/scenegraph/qsgwindowsrenderloop_p.h
+++ b/src/quick/scenegraph/qsgwindowsrenderloop_p.h
@@ -61,6 +61,7 @@
QT_BEGIN_NAMESPACE
class QSGRenderContext;
+class QSGDefaultRenderContext;
class QSGWindowsRenderLoop : public QSGRenderLoop
{
@@ -83,7 +84,7 @@ public:
QAnimationDriver *animationDriver() const { return m_animationDriver; }
QSGContext *sceneGraphContext() const { return m_sg; }
- QSGRenderContext *createRenderContext(QSGContext *) const { return m_rc; }
+ QSGRenderContext *createRenderContext(QSGContext *) const;
void releaseResources(QQuickWindow *) { }
@@ -113,7 +114,7 @@ private:
QOpenGLContext *m_gl;
QSGContext *m_sg;
- QSGRenderContext *m_rc;
+ QSGDefaultRenderContext *m_rc;
QAnimationDriver *m_animationDriver;
diff --git a/src/quick/scenegraph/scenegraph.pri b/src/quick/scenegraph/scenegraph.pri
index 84cc2ba135..d8dfd01e7a 100644
--- a/src/quick/scenegraph/scenegraph.pri
+++ b/src/quick/scenegraph/scenegraph.pri
@@ -5,169 +5,202 @@
# Core API
HEADERS += \
- $$PWD/coreapi/qsgbatchrenderer_p.h \
$$PWD/coreapi/qsggeometry.h \
$$PWD/coreapi/qsgmaterial.h \
+ $$PWD/coreapi/qsgmaterialshader_p.h \
$$PWD/coreapi/qsgnode.h \
$$PWD/coreapi/qsgnode_p.h \
$$PWD/coreapi/qsgnodeupdater_p.h \
$$PWD/coreapi/qsgabstractrenderer.h \
$$PWD/coreapi/qsgabstractrenderer_p.h \
$$PWD/coreapi/qsgrenderer_p.h \
+ $$PWD/coreapi/qsgrendernode.h \
$$PWD/coreapi/qsgrendernode_p.h \
- $$PWD/coreapi/qsggeometry_p.h \
- $$PWD/coreapi/qsgmaterialshader_p.h
+ $$PWD/coreapi/qsgrendererinterface.h \
+ $$PWD/coreapi/qsggeometry_p.h
SOURCES += \
$$PWD/coreapi/qsgabstractrenderer.cpp \
- $$PWD/coreapi/qsgbatchrenderer.cpp \
$$PWD/coreapi/qsggeometry.cpp \
$$PWD/coreapi/qsgmaterial.cpp \
$$PWD/coreapi/qsgnode.cpp \
$$PWD/coreapi/qsgnodeupdater.cpp \
$$PWD/coreapi/qsgrenderer.cpp \
$$PWD/coreapi/qsgrendernode.cpp \
- $$PWD/coreapi/qsgshaderrewriter.cpp
+ $$PWD/coreapi/qsgrendererinterface.cpp
+
+contains(QT_CONFIG, opengl(es1|es2)?) {
+ HEADERS += \
+ $$PWD/coreapi/qsgbatchrenderer_p.h
+ SOURCES += \
+ $$PWD/coreapi/qsgbatchrenderer.cpp \
+ $$PWD/coreapi/qsgshaderrewriter.cpp
+}
# Util API
HEADERS += \
$$PWD/util/qsgareaallocator_p.h \
- $$PWD/util/qsgatlastexture_p.h \
- $$PWD/util/qsgdepthstencilbuffer_p.h \
$$PWD/util/qsgengine.h \
$$PWD/util/qsgengine_p.h \
- $$PWD/util/qsgflatcolormaterial.h \
- $$PWD/util/qsgsimplematerial.h \
$$PWD/util/qsgsimplerectnode.h \
$$PWD/util/qsgsimpletexturenode.h \
- $$PWD/util/qsgtexturematerial.h \
- $$PWD/util/qsgtexturematerial_p.h \
- $$PWD/util/qsgvertexcolormaterial.h \
$$PWD/util/qsgtexture.h \
$$PWD/util/qsgtexture_p.h \
$$PWD/util/qsgtextureprovider.h \
- $$PWD/util/qsgdefaultpainternode_p.h \
$$PWD/util/qsgdistancefieldutil_p.h \
- $$PWD/util/qsgshadersourcebuilder_p.h
+ $$PWD/util/qsgflatcolormaterial.h \
+ $$PWD/util/qsgsimplematerial.h \
+ $$PWD/util/qsgtexturematerial.h \
+ $$PWD/util/qsgtexturematerial_p.h \
+ $$PWD/util/qsgvertexcolormaterial.h
SOURCES += \
$$PWD/util/qsgareaallocator.cpp \
- $$PWD/util/qsgatlastexture.cpp \
- $$PWD/util/qsgdepthstencilbuffer.cpp \
$$PWD/util/qsgengine.cpp \
- $$PWD/util/qsgflatcolormaterial.cpp \
$$PWD/util/qsgsimplerectnode.cpp \
$$PWD/util/qsgsimpletexturenode.cpp \
- $$PWD/util/qsgtexturematerial.cpp \
- $$PWD/util/qsgvertexcolormaterial.cpp \
$$PWD/util/qsgtexture.cpp \
$$PWD/util/qsgtextureprovider.cpp \
- $$PWD/util/qsgdefaultpainternode.cpp \
$$PWD/util/qsgdistancefieldutil.cpp \
+ $$PWD/util/qsgflatcolormaterial.cpp \
$$PWD/util/qsgsimplematerial.cpp \
- $$PWD/util/qsgshadersourcebuilder.cpp
+ $$PWD/util/qsgtexturematerial.cpp \
+ $$PWD/util/qsgvertexcolormaterial.cpp
+contains(QT_CONFIG, opengl(es1|es2)?) {
+ HEADERS += \
+ $$PWD/util/qsgdepthstencilbuffer_p.h \
+ $$PWD/util/qsgshadersourcebuilder_p.h \
+ $$PWD/util/qsgatlastexture_p.h
+ SOURCES += \
+ $$PWD/util/qsgdepthstencilbuffer.cpp \
+ $$PWD/util/qsgatlastexture.cpp \
+ $$PWD/util/qsgshadersourcebuilder.cpp
+}
+
# QML / Adaptations API
HEADERS += \
$$PWD/qsgadaptationlayer_p.h \
$$PWD/qsgcontext_p.h \
$$PWD/qsgcontextplugin_p.h \
- $$PWD/qsgdefaultglyphnode_p.h \
- $$PWD/qsgdefaultdistancefieldglyphcache_p.h \
- $$PWD/qsgdistancefieldglyphnode_p.h \
- $$PWD/qsgdistancefieldglyphnode_p_p.h \
- $$PWD/qsgdefaultglyphnode_p_p.h \
- $$PWD/qsgdefaultimagenode_p.h \
- $$PWD/qsgdefaultrectanglenode_p.h \
- $$PWD/qsgrenderloop_p.h \
- $$PWD/qsgthreadedrenderloop_p.h \
- $$PWD/qsgwindowsrenderloop_p.h \
- $$PWD/qsgdefaultlayer_p.h
+ $$PWD/qsgbasicrectanglenode_p.h \
+ $$PWD/qsgbasicimagenode_p.h \
+ $$PWD/qsgbasicglyphnode_p.h \
+ $$PWD/qsgrenderloop_p.h
SOURCES += \
$$PWD/qsgadaptationlayer.cpp \
$$PWD/qsgcontext.cpp \
$$PWD/qsgcontextplugin.cpp \
- $$PWD/qsgdefaultglyphnode.cpp \
- $$PWD/qsgdefaultglyphnode_p.cpp \
- $$PWD/qsgdefaultdistancefieldglyphcache.cpp \
- $$PWD/qsgdistancefieldglyphnode.cpp \
- $$PWD/qsgdistancefieldglyphnode_p.cpp \
- $$PWD/qsgdefaultimagenode.cpp \
- $$PWD/qsgdefaultrectanglenode.cpp \
- $$PWD/qsgrenderloop.cpp \
- $$PWD/qsgthreadedrenderloop.cpp \
- $$PWD/qsgwindowsrenderloop.cpp \
- $$PWD/qsgdefaultlayer.cpp
+ $$PWD/qsgbasicrectanglenode.cpp \
+ $$PWD/qsgbasicimagenode.cpp \
+ $$PWD/qsgbasicglyphnode.cpp \
+ $$PWD/qsgrenderloop.cpp
+
+contains(QT_CONFIG, opengl(es1|es2)?) {
+ SOURCES += \
+ $$PWD/qsgdefaultglyphnode.cpp \
+ $$PWD/qsgdefaultglyphnode_p.cpp \
+ $$PWD/qsgdefaultdistancefieldglyphcache.cpp \
+ $$PWD/qsgdistancefieldglyphnode.cpp \
+ $$PWD/qsgdistancefieldglyphnode_p.cpp \
+ $$PWD/qsgdefaultimagenode.cpp \
+ $$PWD/qsgdefaultrectanglenode.cpp \
+ $$PWD/qsgdefaultrendercontext.cpp \
+ $$PWD/qsgdefaultcontext.cpp \
+ $$PWD/util/qsgdefaultpainternode.cpp \
+ $$PWD/qsgdefaultlayer.cpp \
+ $$PWD/qsgthreadedrenderloop.cpp \
+ $$PWD/qsgwindowsrenderloop.cpp
+ HEADERS += \
+ $$PWD/qsgdefaultglyphnode_p.h \
+ $$PWD/qsgdefaultdistancefieldglyphcache_p.h \
+ $$PWD/qsgdistancefieldglyphnode_p.h \
+ $$PWD/qsgdistancefieldglyphnode_p_p.h \
+ $$PWD/qsgdefaultglyphnode_p_p.h \
+ $$PWD/qsgdefaultimagenode_p.h \
+ $$PWD/qsgdefaultrectanglenode_p.h \
+ $$PWD/qsgdefaultrendercontext_p.h \
+ $$PWD/qsgdefaultcontext_p.h \
+ $$PWD/util/qsgdefaultpainternode_p.h \
+ $$PWD/qsgdefaultlayer_p.h \
+ $$PWD/qsgthreadedrenderloop_p.h \
+ $$PWD/qsgwindowsrenderloop_p.h
+}
+
+# Built-in, non-plugin-based adaptations
+include(adaptations/adaptations.pri)
RESOURCES += \
$$PWD/scenegraph.qrc
-OTHER_FILES += \
- $$PWD/shaders/24bittextmask.frag \
- $$PWD/shaders/8bittextmask.frag \
- $$PWD/shaders/distancefieldoutlinetext.frag \
- $$PWD/shaders/distancefieldshiftedtext.frag \
- $$PWD/shaders/distancefieldshiftedtext.vert \
- $$PWD/shaders/distancefieldtext.frag \
- $$PWD/shaders/distancefieldtext.vert \
- $$PWD/shaders/flatcolor.frag \
- $$PWD/shaders/flatcolor.vert \
- $$PWD/shaders/hiqsubpixeldistancefieldtext.frag \
- $$PWD/shaders/hiqsubpixeldistancefieldtext.vert \
- $$PWD/shaders/loqsubpixeldistancefieldtext.frag \
- $$PWD/shaders/loqsubpixeldistancefieldtext.vert \
- $$PWD/shaders/opaquetexture.frag \
- $$PWD/shaders/opaquetexture.vert \
- $$PWD/shaders/outlinedtext.frag \
- $$PWD/shaders/outlinedtext.vert \
- $$PWD/shaders/rendernode.frag \
- $$PWD/shaders/rendernode.vert \
- $$PWD/shaders/smoothcolor.frag \
- $$PWD/shaders/smoothcolor.vert \
- $$PWD/shaders/smoothtexture.frag \
- $$PWD/shaders/smoothtexture.vert \
- $$PWD/shaders/stencilclip.frag \
- $$PWD/shaders/stencilclip.vert \
- $$PWD/shaders/styledtext.frag \
- $$PWD/shaders/styledtext.vert \
- $$PWD/shaders/textmask.frag \
- $$PWD/shaders/textmask.vert \
- $$PWD/shaders/texture.frag \
- $$PWD/shaders/vertexcolor.frag \
- $$PWD/shaders/vertexcolor.vert \
- $$PWD/shaders/24bittextmask_core.frag \
- $$PWD/shaders/8bittextmask_core.frag \
- $$PWD/shaders/distancefieldoutlinetext_core.frag \
- $$PWD/shaders/distancefieldshiftedtext_core.frag \
- $$PWD/shaders/distancefieldshiftedtext_core.vert \
- $$PWD/shaders/distancefieldtext_core.frag \
- $$PWD/shaders/distancefieldtext_core.vert \
- $$PWD/shaders/flatcolor_core.frag \
- $$PWD/shaders/flatcolor_core.vert \
- $$PWD/shaders/hiqsubpixeldistancefieldtext_core.frag \
- $$PWD/shaders/hiqsubpixeldistancefieldtext_core.vert \
- $$PWD/shaders/loqsubpixeldistancefieldtext_core.frag \
- $$PWD/shaders/loqsubpixeldistancefieldtext_core.vert \
- $$PWD/shaders/opaquetexture_core.frag \
- $$PWD/shaders/opaquetexture_core.vert \
- $$PWD/shaders/outlinedtext_core.frag \
- $$PWD/shaders/outlinedtext_core.vert \
- $$PWD/shaders/rendernode_core.frag \
- $$PWD/shaders/rendernode_core.vert \
- $$PWD/shaders/smoothcolor_core.frag \
- $$PWD/shaders/smoothcolor_core.vert \
- $$PWD/shaders/smoothtexture_core.frag \
- $$PWD/shaders/smoothtexture_core.vert \
- $$PWD/shaders/stencilclip_core.frag \
- $$PWD/shaders/stencilclip_core.vert \
- $$PWD/shaders/styledtext_core.frag \
- $$PWD/shaders/styledtext_core.vert \
- $$PWD/shaders/textmask_core.frag \
- $$PWD/shaders/textmask_core.vert \
- $$PWD/shaders/texture_core.frag \
- $$PWD/shaders/vertexcolor_core.frag \
- $$PWD/shaders/vertexcolor_core.vert \
- scenegraph/shaders/visualization.frag \
- scenegraph/shaders/visualization.vert
-
+# OpenGL Shaders
+contains(QT_CONFIG, opengl(es1|es2)?) {
+ OTHER_FILES += \
+ $$PWD/shaders/24bittextmask.frag \
+ $$PWD/shaders/8bittextmask.frag \
+ $$PWD/shaders/distancefieldoutlinetext.frag \
+ $$PWD/shaders/distancefieldshiftedtext.frag \
+ $$PWD/shaders/distancefieldshiftedtext.vert \
+ $$PWD/shaders/distancefieldtext.frag \
+ $$PWD/shaders/distancefieldtext.vert \
+ $$PWD/shaders/flatcolor.frag \
+ $$PWD/shaders/flatcolor.vert \
+ $$PWD/shaders/hiqsubpixeldistancefieldtext.frag \
+ $$PWD/shaders/hiqsubpixeldistancefieldtext.vert \
+ $$PWD/shaders/loqsubpixeldistancefieldtext.frag \
+ $$PWD/shaders/loqsubpixeldistancefieldtext.vert \
+ $$PWD/shaders/opaquetexture.frag \
+ $$PWD/shaders/opaquetexture.vert \
+ $$PWD/shaders/outlinedtext.frag \
+ $$PWD/shaders/outlinedtext.vert \
+ $$PWD/shaders/rendernode.frag \
+ $$PWD/shaders/rendernode.vert \
+ $$PWD/shaders/smoothcolor.frag \
+ $$PWD/shaders/smoothcolor.vert \
+ $$PWD/shaders/smoothtexture.frag \
+ $$PWD/shaders/smoothtexture.vert \
+ $$PWD/shaders/stencilclip.frag \
+ $$PWD/shaders/stencilclip.vert \
+ $$PWD/shaders/styledtext.frag \
+ $$PWD/shaders/styledtext.vert \
+ $$PWD/shaders/textmask.frag \
+ $$PWD/shaders/textmask.vert \
+ $$PWD/shaders/texture.frag \
+ $$PWD/shaders/vertexcolor.frag \
+ $$PWD/shaders/vertexcolor.vert \
+ $$PWD/shaders/24bittextmask_core.frag \
+ $$PWD/shaders/8bittextmask_core.frag \
+ $$PWD/shaders/distancefieldoutlinetext_core.frag \
+ $$PWD/shaders/distancefieldshiftedtext_core.frag \
+ $$PWD/shaders/distancefieldshiftedtext_core.vert \
+ $$PWD/shaders/distancefieldtext_core.frag \
+ $$PWD/shaders/distancefieldtext_core.vert \
+ $$PWD/shaders/flatcolor_core.frag \
+ $$PWD/shaders/flatcolor_core.vert \
+ $$PWD/shaders/hiqsubpixeldistancefieldtext_core.frag \
+ $$PWD/shaders/hiqsubpixeldistancefieldtext_core.vert \
+ $$PWD/shaders/loqsubpixeldistancefieldtext_core.frag \
+ $$PWD/shaders/loqsubpixeldistancefieldtext_core.vert \
+ $$PWD/shaders/opaquetexture_core.frag \
+ $$PWD/shaders/opaquetexture_core.vert \
+ $$PWD/shaders/outlinedtext_core.frag \
+ $$PWD/shaders/outlinedtext_core.vert \
+ $$PWD/shaders/rendernode_core.frag \
+ $$PWD/shaders/rendernode_core.vert \
+ $$PWD/shaders/smoothcolor_core.frag \
+ $$PWD/shaders/smoothcolor_core.vert \
+ $$PWD/shaders/smoothtexture_core.frag \
+ $$PWD/shaders/smoothtexture_core.vert \
+ $$PWD/shaders/stencilclip_core.frag \
+ $$PWD/shaders/stencilclip_core.vert \
+ $$PWD/shaders/styledtext_core.frag \
+ $$PWD/shaders/styledtext_core.vert \
+ $$PWD/shaders/textmask_core.frag \
+ $$PWD/shaders/textmask_core.vert \
+ $$PWD/shaders/texture_core.frag \
+ $$PWD/shaders/vertexcolor_core.frag \
+ $$PWD/shaders/vertexcolor_core.vert \
+ $$PWD/shaders/visualization.frag \
+ $$PWD/shaders/visualization.vert
+}
diff --git a/src/quick/scenegraph/util/qsgatlastexture.cpp b/src/quick/scenegraph/util/qsgatlastexture.cpp
index a93e7bbd30..098a4a666b 100644
--- a/src/quick/scenegraph/util/qsgatlastexture.cpp
+++ b/src/quick/scenegraph/util/qsgatlastexture.cpp
@@ -44,6 +44,7 @@
#include <QtCore/QtMath>
#include <QtGui/QOpenGLContext>
+#include <QtGui/QOpenGLFunctions>
#include <QtGui/QGuiApplication>
#include <QtGui/QScreen>
#include <QtGui/QSurface>
diff --git a/src/quick/scenegraph/util/qsgatlastexture_p.h b/src/quick/scenegraph/util/qsgatlastexture_p.h
index d6c0109c98..cd24645fcf 100644
--- a/src/quick/scenegraph/util/qsgatlastexture_p.h
+++ b/src/quick/scenegraph/util/qsgatlastexture_p.h
@@ -104,17 +104,17 @@ public:
QSize size() const { return m_size; }
- GLuint internalFormat() const { return m_internalFormat; }
- GLuint externalFormat() const { return m_externalFormat; }
+ uint internalFormat() const { return m_internalFormat; }
+ uint externalFormat() const { return m_externalFormat; }
private:
QSGAreaAllocator m_allocator;
- GLuint m_texture_id;
+ unsigned int m_texture_id;
QSize m_size;
QList<Texture *> m_pending_uploads;
- GLuint m_internalFormat;
- GLuint m_externalFormat;
+ uint m_internalFormat;
+ uint m_externalFormat;
int m_atlas_transient_image_threshold;
diff --git a/src/quick/scenegraph/util/qsgdefaultpainternode.cpp b/src/quick/scenegraph/util/qsgdefaultpainternode.cpp
index 2deb993a6e..4dd60c76f5 100644
--- a/src/quick/scenegraph/util/qsgdefaultpainternode.cpp
+++ b/src/quick/scenegraph/util/qsgdefaultpainternode.cpp
@@ -41,6 +41,7 @@
#include <QtQuick/private/qquickpainteditem_p.h>
+#include <QtQuick/private/qsgdefaultrendercontext_p.h>
#include <QtQuick/private/qsgcontext_p.h>
#include <private/qopenglextensions_p.h>
#include <qopenglframebufferobject.h>
@@ -96,7 +97,7 @@ QSGDefaultPainterNode::QSGDefaultPainterNode(QQuickPaintedItem *item)
, m_dirtyRenderTarget(false)
, m_dirtyTexture(false)
{
- m_context = static_cast<QQuickPaintedItemPrivate *>(QObjectPrivate::get(item))->sceneGraphRenderContext();
+ m_context = static_cast<QSGDefaultRenderContext *>(static_cast<QQuickPaintedItemPrivate *>(QObjectPrivate::get(item))->sceneGraphRenderContext());
setMaterial(&m_materialO);
setOpaqueMaterial(&m_material);
diff --git a/src/quick/scenegraph/util/qsgdefaultpainternode_p.h b/src/quick/scenegraph/util/qsgdefaultpainternode_p.h
index 3cabe01511..069ef155b1 100644
--- a/src/quick/scenegraph/util/qsgdefaultpainternode_p.h
+++ b/src/quick/scenegraph/util/qsgdefaultpainternode_p.h
@@ -63,6 +63,7 @@ QT_BEGIN_NAMESPACE
class QOpenGLFramebufferObject;
class QOpenGLPaintDevice;
+class QSGDefaultRenderContext;
class Q_QUICK_PRIVATE_EXPORT QSGPainterTexture : public QSGPlainTexture
{
@@ -127,7 +128,7 @@ private:
void updateRenderTarget();
void updateFBOSize();
- QSGRenderContext *m_context;
+ QSGDefaultRenderContext *m_context;
QQuickPaintedItem::RenderTarget m_preferredRenderTarget;
QQuickPaintedItem::RenderTarget m_actualRenderTarget;
diff --git a/src/quick/scenegraph/util/qsgdistancefieldutil.cpp b/src/quick/scenegraph/util/qsgdistancefieldutil.cpp
index 5eb6a6f593..65a6bcd52c 100644
--- a/src/quick/scenegraph/util/qsgdistancefieldutil.cpp
+++ b/src/quick/scenegraph/util/qsgdistancefieldutil.cpp
@@ -40,7 +40,9 @@
#include "qsgdistancefieldutil_p.h"
#include <private/qsgadaptationlayer_p.h>
-#include <QtGui/private/qopenglengineshadersource_p.h>
+#ifndef QT_NO_OPENGL
+# include <QtGui/private/qopenglengineshadersource_p.h>
+#endif
#include <QtQuick/private/qsgcontext_p.h>
QT_BEGIN_NAMESPACE
diff --git a/src/quick/scenegraph/util/qsgengine.cpp b/src/quick/scenegraph/util/qsgengine.cpp
index 1ef98d222d..26a3d66077 100644
--- a/src/quick/scenegraph/util/qsgengine.cpp
+++ b/src/quick/scenegraph/util/qsgengine.cpp
@@ -44,6 +44,11 @@
#include <private/qsgrenderer_p.h>
#include <private/qsgtexture_p.h>
+#ifndef QT_NO_OPENGL
+# include <QtGui/QOpenGLContext>
+# include <private/qsgdefaultrendercontext_p.h>
+#endif
+
QT_BEGIN_NAMESPACE
@@ -83,7 +88,7 @@ QT_BEGIN_NAMESPACE
QSGEnginePrivate::QSGEnginePrivate()
: sgContext(QSGContext::createDefaultContext())
- , sgRenderContext(new QSGRenderContext(sgContext.data()))
+ , sgRenderContext(sgContext.data()->createRenderContext())
{
}
@@ -110,17 +115,23 @@ QSGEngine::~QSGEngine()
*/
void QSGEngine::initialize(QOpenGLContext *context)
{
+#ifndef QT_NO_OPENGL
Q_D(QSGEngine);
if (QOpenGLContext::currentContext() != context) {
qWarning("WARNING: The context must be current before calling QSGEngine::initialize.");
return;
}
- if (!d->sgRenderContext->isValid()) {
- d->sgRenderContext->setAttachToGLContext(false);
- d->sgRenderContext->initialize(context);
+ auto openGLRenderContext = static_cast<QSGDefaultRenderContext *>(d->sgRenderContext.data());
+
+ if (openGLRenderContext != nullptr && !openGLRenderContext->isValid()) {
+ openGLRenderContext->setAttachToGLContext(false);
+ openGLRenderContext->initialize(context);
connect(context, &QOpenGLContext::aboutToBeDestroyed, this, &QSGEngine::invalidate);
}
+#else
+ Q_UNUSED(context)
+#endif
}
/*!
@@ -198,4 +209,18 @@ QSGTexture *QSGEngine::createTextureFromId(uint id, const QSize &size, CreateTex
return 0;
}
+/*!
+ Returns the current renderer interface if there is one. Otherwise null is returned.
+
+ \sa QSGRenderNode, QSGRendererInterface
+ \since 5.8
+ */
+QSGRendererInterface *QSGEngine::rendererInterface() const
+{
+ Q_D(const QSGEngine);
+ return d->sgRenderContext->isValid()
+ ? d->sgRenderContext->sceneGraphContext()->rendererInterface(d->sgRenderContext.data())
+ : nullptr;
+}
+
QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/util/qsgengine.h b/src/quick/scenegraph/util/qsgengine.h
index 89af71fb47..9d27fb5525 100644
--- a/src/quick/scenegraph/util/qsgengine.h
+++ b/src/quick/scenegraph/util/qsgengine.h
@@ -49,6 +49,7 @@ class QOpenGLContext;
class QSGAbstractRenderer;
class QSGEnginePrivate;
class QSGTexture;
+class QSGRendererInterface;
class Q_QUICK_EXPORT QSGEngine : public QObject
{
@@ -72,6 +73,7 @@ public:
QSGAbstractRenderer *createRenderer() const;
QSGTexture *createTextureFromImage(const QImage &image, CreateTextureOptions options = CreateTextureOption()) const;
QSGTexture *createTextureFromId(uint id, const QSize &size, CreateTextureOptions options = CreateTextureOption()) const;
+ QSGRendererInterface *rendererInterface() const;
};
QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/util/qsgflatcolormaterial.cpp b/src/quick/scenegraph/util/qsgflatcolormaterial.cpp
index 836b5759a2..b9986c3d11 100644
--- a/src/quick/scenegraph/util/qsgflatcolormaterial.cpp
+++ b/src/quick/scenegraph/util/qsgflatcolormaterial.cpp
@@ -39,8 +39,9 @@
#include "qsgflatcolormaterial.h"
#include <private/qsgmaterialshader_p.h>
-
-#include <qopenglshaderprogram.h>
+#ifndef QT_NO_OPENGL
+# include <qopenglshaderprogram.h>
+#endif
QT_BEGIN_NAMESPACE
@@ -66,14 +67,16 @@ QSGMaterialType FlatColorMaterialShader::type;
FlatColorMaterialShader::FlatColorMaterialShader()
: QSGMaterialShader(*new QSGMaterialShaderPrivate)
{
+#ifndef QT_NO_OPENGL
setShaderSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/qt-project.org/scenegraph/shaders/flatcolor.vert"));
setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/scenegraph/shaders/flatcolor.frag"));
+#endif
}
void FlatColorMaterialShader::updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect)
{
+#ifndef QT_NO_OPENGL
Q_ASSERT(oldEffect == 0 || newEffect->type() == oldEffect->type());
-
QSGFlatColorMaterial *oldMaterial = static_cast<QSGFlatColorMaterial *>(oldEffect);
QSGFlatColorMaterial *newMaterial = static_cast<QSGFlatColorMaterial *>(newEffect);
@@ -90,6 +93,11 @@ void FlatColorMaterialShader::updateState(const RenderState &state, QSGMaterial
if (state.isMatrixDirty())
program()->setUniformValue(m_matrix_id, state.combinedMatrix());
+#else
+ Q_UNUSED(state)
+ Q_UNUSED(newEffect)
+ Q_UNUSED(oldEffect)
+#endif
}
char const *const *FlatColorMaterialShader::attributeNames() const
@@ -100,8 +108,10 @@ char const *const *FlatColorMaterialShader::attributeNames() const
void FlatColorMaterialShader::initialize()
{
+#ifndef QT_NO_OPENGL
m_matrix_id = program()->uniformLocation("matrix");
m_color_id = program()->uniformLocation("color");
+#endif
}
diff --git a/src/quick/scenegraph/util/qsgsimplematerial.h b/src/quick/scenegraph/util/qsgsimplematerial.h
index 9ebfc1f8d2..d07a68e850 100644
--- a/src/quick/scenegraph/util/qsgsimplematerial.h
+++ b/src/quick/scenegraph/util/qsgsimplematerial.h
@@ -50,7 +50,7 @@ class QSGSimpleMaterialShader : public QSGMaterialShader
public:
void initialize() {
QSGMaterialShader::initialize();
-
+#ifndef QT_NO_OPENGL
m_id_matrix = program()->uniformLocation(uniformMatrixName());
if (m_id_matrix < 0) {
qFatal("QSGSimpleMaterialShader does not implement 'uniform highp mat4 %s;' in its vertex shader",
@@ -67,7 +67,7 @@ public:
} else {
m_id_opacity = -1;
}
-
+#endif
resolveUniforms();
}
@@ -197,11 +197,14 @@ QSGMaterialType QSGSimpleMaterial<State>::m_type;
template <typename State>
Q_INLINE_TEMPLATE void QSGSimpleMaterialShader<State>::updateState(const RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
{
+#ifndef QT_NO_OPENGL
if (state.isMatrixDirty())
program()->setUniformValue(m_id_matrix, state.combinedMatrix());
if (state.isOpacityDirty() && m_id_opacity >= 0)
program()->setUniformValue(m_id_opacity, state.opacity());
-
+#else
+ Q_UNUSED(state)
+#endif
State *ns = static_cast<QSGSimpleMaterial<State> *>(newMaterial)->state();
State *old = 0;
if (oldMaterial)
diff --git a/src/quick/scenegraph/util/qsgtexture.cpp b/src/quick/scenegraph/util/qsgtexture.cpp
index 9b9c77dce4..19403deb73 100644
--- a/src/quick/scenegraph/util/qsgtexture.cpp
+++ b/src/quick/scenegraph/util/qsgtexture.cpp
@@ -38,7 +38,6 @@
****************************************************************************/
#include "qsgtexture_p.h"
-#include <qopenglfunctions.h>
#include <QtQuick/private/qsgcontext_p.h>
#include <qthread.h>
#include <qmath.h>
@@ -46,9 +45,12 @@
#include <private/qqmlglobal_p.h>
#include <QtGui/qguiapplication.h>
#include <QtGui/qpa/qplatformnativeinterface.h>
-#include <QtGui/qopenglcontext.h>
-#include <QtGui/qopenglfunctions.h>
-
+#ifndef QT_NO_OPENGL
+# include <qopenglfunctions.h>
+# include <QtGui/qopenglcontext.h>
+# include <QtGui/qopenglfunctions.h>
+# include <private/qsgdefaultrendercontext_p.h>
+#endif
#include <private/qsgmaterialshader_p.h>
#if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID) && !defined(__UCLIBC__)
@@ -278,6 +280,7 @@ Q_GLOBAL_STATIC(QMutex, qsg_valid_texture_mutex)
bool qsg_safeguard_texture(QSGTexture *texture)
{
+#ifndef QT_NO_OPENGL
QMutexLocker locker(qsg_valid_texture_mutex());
if (!qsg_valid_texture_set()->contains(texture)) {
qWarning() << "Invalid texture accessed:" << (void *) texture;
@@ -285,6 +288,9 @@ bool qsg_safeguard_texture(QSGTexture *texture)
QOpenGLContext::currentContext()->functions()->glBindTexture(GL_TEXTURE_2D, 0);
return false;
}
+#else
+ Q_UNUSED(texture)
+#endif
return true;
}
#endif
@@ -517,6 +523,7 @@ QSGTexture::WrapMode QSGTexture::verticalWrapMode() const
*/
void QSGTexture::updateBindOptions(bool force)
{
+#ifndef QT_NO_OPENGL
Q_D(QSGTexture);
QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions();
force |= isAtlasTexture();
@@ -551,6 +558,9 @@ void QSGTexture::updateBindOptions(bool force)
funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, d->verticalWrap == Repeat ? GL_REPEAT : GL_CLAMP_TO_EDGE);
d->wrapChanged = false;
}
+#else
+ Q_UNUSED(force)
+#endif
}
QSGPlainTexture::QSGPlainTexture()
@@ -568,8 +578,10 @@ QSGPlainTexture::QSGPlainTexture()
QSGPlainTexture::~QSGPlainTexture()
{
+#ifndef QT_NO_OPENGL
if (m_texture_id && m_owns_texture && QOpenGLContext::currentContext())
QOpenGLContext::currentContext()->functions()->glDeleteTextures(1, &m_texture_id);
+#endif
}
void qsg_swizzleBGRAToRGBA(QImage *image)
@@ -601,8 +613,10 @@ int QSGPlainTexture::textureId() const
// or ~QSGPlainTexture so just keep it minimal here.
return 0;
} else if (m_texture_id == 0){
+#ifndef QT_NO_OPENGL
// Generate a texture id for use later and return it.
QOpenGLContext::currentContext()->functions()->glGenTextures(1, &const_cast<QSGPlainTexture *>(this)->m_texture_id);
+#endif
return m_texture_id;
}
}
@@ -611,8 +625,10 @@ int QSGPlainTexture::textureId() const
void QSGPlainTexture::setTextureId(int id)
{
+#ifndef QT_NO_OPENGL
if (m_texture_id && m_owns_texture)
QOpenGLContext::currentContext()->functions()->glDeleteTextures(1, &m_texture_id);
+#endif
m_texture_id = id;
m_dirty_texture = false;
@@ -623,6 +639,7 @@ void QSGPlainTexture::setTextureId(int id)
void QSGPlainTexture::bind()
{
+#ifndef QT_NO_OPENGL
QOpenGLContext *context = QOpenGLContext::currentContext();
QOpenGLFunctions *funcs = context->functions();
if (!m_dirty_texture) {
@@ -684,7 +701,7 @@ void QSGPlainTexture::bind()
// based on QSGTexture::textureSize which is updated after this, so that
// should be ok.
int max;
- if (QSGRenderContext *rc = QSGRenderContext::from(context))
+ if (auto rc = QSGDefaultRenderContext::from(context))
max = rc->maxTextureSize();
else
funcs->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max);
@@ -789,6 +806,7 @@ void QSGPlainTexture::bind()
m_dirty_bind_options = false;
if (!m_retain_image)
m_image = QImage();
+#endif
}
diff --git a/src/quick/scenegraph/util/qsgtexture_p.h b/src/quick/scenegraph/util/qsgtexture_p.h
index 5c358aecc3..a0d7eb41e3 100644
--- a/src/quick/scenegraph/util/qsgtexture_p.h
+++ b/src/quick/scenegraph/util/qsgtexture_p.h
@@ -53,9 +53,9 @@
#include <QtQuick/qtquickglobal.h>
#include <private/qobject_p.h>
-
-#include <QtGui/qopengl.h>
-
+#ifndef QT_NO_OPENGL
+# include <QtGui/qopengl.h>
+#endif
#include "qsgtexture.h"
#include <QtQuick/private/qsgcontext_p.h>
@@ -110,7 +110,7 @@ public:
protected:
QImage m_image;
- GLuint m_texture_id;
+ uint m_texture_id;
QSize m_texture_size;
QRectF m_texture_rect;
diff --git a/src/quick/scenegraph/util/qsgtexturematerial.cpp b/src/quick/scenegraph/util/qsgtexturematerial.cpp
index 66e87a77a8..744df5e143 100644
--- a/src/quick/scenegraph/util/qsgtexturematerial.cpp
+++ b/src/quick/scenegraph/util/qsgtexturematerial.cpp
@@ -39,9 +39,10 @@
#include "qsgtexturematerial_p.h"
#include "qsgtexture_p.h"
-
-#include <QtGui/qopenglshaderprogram.h>
-#include <QtGui/qopenglfunctions.h>
+#ifndef QT_NO_OPENGL
+# include <QtGui/qopenglshaderprogram.h>
+# include <QtGui/qopenglfunctions.h>
+#endif
QT_BEGIN_NAMESPACE
@@ -56,8 +57,10 @@ QSGMaterialType QSGOpaqueTextureMaterialShader::type;
QSGOpaqueTextureMaterialShader::QSGOpaqueTextureMaterialShader()
: QSGMaterialShader()
{
+#ifndef QT_NO_OPENGL
setShaderSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/qt-project.org/scenegraph/shaders/opaquetexture.vert"));
setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/scenegraph/shaders/opaquetexture.frag"));
+#endif
}
char const *const *QSGOpaqueTextureMaterialShader::attributeNames() const
@@ -68,7 +71,9 @@ char const *const *QSGOpaqueTextureMaterialShader::attributeNames() const
void QSGOpaqueTextureMaterialShader::initialize()
{
+#ifndef QT_NO_OPENGL
m_matrix_id = program()->uniformLocation("qt_Matrix");
+#endif
}
void QSGOpaqueTextureMaterialShader::updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect)
@@ -88,6 +93,7 @@ void QSGOpaqueTextureMaterialShader::updateState(const RenderState &state, QSGMa
t->setHorizontalWrapMode(tx->horizontalWrapMode());
t->setVerticalWrapMode(tx->verticalWrapMode());
+#ifndef QT_NO_OPENGL
bool npotSupported = const_cast<QOpenGLContext *>(state.context())
->functions()->hasOpenGLFeature(QOpenGLFunctions::NPOTTextureRepeat);
if (!npotSupported) {
@@ -98,16 +104,19 @@ void QSGOpaqueTextureMaterialShader::updateState(const RenderState &state, QSGMa
t->setVerticalWrapMode(QSGTexture::ClampToEdge);
}
}
-
+#else
+ Q_UNUSED(state)
+#endif
t->setMipmapFiltering(tx->mipmapFiltering());
if (oldTx == 0 || oldTx->texture()->textureId() != t->textureId())
t->bind();
else
t->updateBindOptions();
-
+#ifndef QT_NO_OPENGL
if (state.isMatrixDirty())
program()->setUniformValue(m_matrix_id, state.combinedMatrix());
+#endif
}
@@ -362,22 +371,27 @@ QSGMaterialShader *QSGTextureMaterial::createShader() const
QSGTextureMaterialShader::QSGTextureMaterialShader()
: QSGOpaqueTextureMaterialShader()
{
+#ifndef QT_NO_OPENGL
setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/scenegraph/shaders/texture.frag"));
+#endif
}
void QSGTextureMaterialShader::updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect)
{
Q_ASSERT(oldEffect == 0 || newEffect->type() == oldEffect->type());
+#ifndef QT_NO_OPENGL
if (state.isOpacityDirty())
program()->setUniformValue(m_opacity_id, state.opacity());
-
+#endif
QSGOpaqueTextureMaterialShader::updateState(state, newEffect, oldEffect);
}
void QSGTextureMaterialShader::initialize()
{
QSGOpaqueTextureMaterialShader::initialize();
+#ifndef QT_NO_OPENGL
m_opacity_id = program()->uniformLocation("opacity");
+#endif
}
QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/util/qsgvertexcolormaterial.cpp b/src/quick/scenegraph/util/qsgvertexcolormaterial.cpp
index 8e86b3906f..18de19b04e 100644
--- a/src/quick/scenegraph/util/qsgvertexcolormaterial.cpp
+++ b/src/quick/scenegraph/util/qsgvertexcolormaterial.cpp
@@ -38,9 +38,9 @@
****************************************************************************/
#include "qsgvertexcolormaterial.h"
-
-#include <qopenglshaderprogram.h>
-
+#ifndef QT_NO_OPENGL
+# include <qopenglshaderprogram.h>
+#endif
QT_BEGIN_NAMESPACE
class QSGVertexColorMaterialShader : public QSGMaterialShader
@@ -65,17 +65,23 @@ QSGMaterialType QSGVertexColorMaterialShader::type;
QSGVertexColorMaterialShader::QSGVertexColorMaterialShader()
: QSGMaterialShader()
{
+#ifndef QT_NO_OPENGL
setShaderSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/qt-project.org/scenegraph/shaders/vertexcolor.vert"));
setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/scenegraph/shaders/vertexcolor.frag"));
+#endif
}
void QSGVertexColorMaterialShader::updateState(const RenderState &state, QSGMaterial * /*newEffect*/, QSGMaterial *)
{
+#ifndef QT_NO_OPENGL
if (state.isOpacityDirty())
program()->setUniformValue(m_opacity_id, state.opacity());
if (state.isMatrixDirty())
program()->setUniformValue(m_matrix_id, state.combinedMatrix());
+#else
+ Q_UNUSED(state)
+#endif
}
char const *const *QSGVertexColorMaterialShader::attributeNames() const
@@ -86,8 +92,10 @@ char const *const *QSGVertexColorMaterialShader::attributeNames() const
void QSGVertexColorMaterialShader::initialize()
{
+#ifndef QT_NO_OPENGL
m_matrix_id = program()->uniformLocation("matrix");
m_opacity_id = program()->uniformLocation("opacity");
+#endif
}
diff --git a/src/quick/util/qquickanimator.cpp b/src/quick/util/qquickanimator.cpp
index abae6321b0..100b7bd402 100644
--- a/src/quick/util/qquickanimator.cpp
+++ b/src/quick/util/qquickanimator.cpp
@@ -502,7 +502,7 @@ QQuickRotationAnimator::RotationDirection QQuickRotationAnimator::direction() co
Q_D(const QQuickRotationAnimator);
return d->direction;
}
-
+#ifndef QT_NO_OPENGL
/*!
\qmltype UniformAnimator
\instantiates QQuickUniformAnimator
@@ -580,5 +580,6 @@ QQuickAnimatorJob *QQuickUniformAnimator::createJob() const
job->setUniform(u.toLatin1());
return job;
}
+#endif
QT_END_NAMESPACE
diff --git a/src/quick/util/qquickanimator_p.h b/src/quick/util/qquickanimator_p.h
index c23aa0a7e9..f1e2d4e1d9 100644
--- a/src/quick/util/qquickanimator_p.h
+++ b/src/quick/util/qquickanimator_p.h
@@ -169,7 +169,7 @@ protected:
QQuickAnimatorJob *createJob() const;
QString propertyName() const { return QStringLiteral("rotation"); }
};
-
+#ifndef QT_NO_OPENGL
class QQuickUniformAnimatorPrivate;
class Q_QUICK_PRIVATE_EXPORT QQuickUniformAnimator : public QQuickAnimator
{
@@ -190,6 +190,7 @@ protected:
QQuickAnimatorJob *createJob() const;
QString propertyName() const;
};
+#endif
QT_END_NAMESPACE
@@ -199,6 +200,7 @@ QML_DECLARE_TYPE(QQuickYAnimator)
QML_DECLARE_TYPE(QQuickScaleAnimator)
QML_DECLARE_TYPE(QQuickRotationAnimator)
QML_DECLARE_TYPE(QQuickOpacityAnimator)
+#ifndef QT_NO_OPENGL
QML_DECLARE_TYPE(QQuickUniformAnimator)
-
+#endif
#endif // QQUICKANIMATOR_P_H
diff --git a/src/quick/util/qquickanimatorjob.cpp b/src/quick/util/qquickanimatorjob.cpp
index 8d5ecadab5..a0c787dae5 100644
--- a/src/quick/util/qquickanimatorjob.cpp
+++ b/src/quick/util/qquickanimatorjob.cpp
@@ -43,8 +43,10 @@
#include "qquickanimator_p_p.h"
#include <private/qquickwindow_p.h>
#include <private/qquickitem_p.h>
-#include <private/qquickshadereffectnode_p.h>
-
+#ifndef QT_NO_OPENGL
+# include <private/qquickopenglshadereffectnode_p.h>
+# include <private/qquickopenglshadereffect_p.h>
+#endif
#include <private/qanimationgroupjob_p.h>
#include <qcoreapplication.h>
@@ -173,7 +175,7 @@ void QQuickAnimatorProxyJob::setWindow(QQuickWindow *window)
} else if (!m_controller && m_job) {
m_controller = QQuickWindowPrivate::get(window)->animationController;
- if (window->openglContext())
+ if (window->isSceneGraphInitialized())
readyToAnimate();
else
connect(window, SIGNAL(sceneGraphInitialized()), this, SLOT(sceneGraphInitialized()));
@@ -390,7 +392,6 @@ void QQuickXAnimatorJob::updateCurrentTime(int time)
{
if (!m_controller)
return;
- Q_ASSERT(!m_controller->m_window->openglContext() || m_controller->m_window->openglContext()->thread() == QThread::currentThread());
m_value = m_from + (m_to - m_from) * progress(time);
m_helper->dx = m_value;
@@ -407,7 +408,6 @@ void QQuickYAnimatorJob::updateCurrentTime(int time)
{
if (!m_controller)
return;
- Q_ASSERT(!m_controller->m_window->openglContext() || m_controller->m_window->openglContext()->thread() == QThread::currentThread());
m_value = m_from + (m_to - m_from) * progress(time);
m_helper->dy = m_value;
@@ -477,7 +477,6 @@ void QQuickOpacityAnimatorJob::updateCurrentTime(int time)
{
if (!m_controller || !m_opacityNode)
return;
- Q_ASSERT(!m_controller->m_window->openglContext() || m_controller->m_window->openglContext()->thread() == QThread::currentThread());
m_value = m_from + (m_to - m_from) * progress(time);
m_opacityNode->setOpacity(m_value);
@@ -493,7 +492,6 @@ void QQuickScaleAnimatorJob::updateCurrentTime(int time)
{
if (!m_controller)
return;
- Q_ASSERT(!m_controller->m_window->openglContext() || m_controller->m_window->openglContext()->thread() == QThread::currentThread());
m_value = m_from + (m_to - m_from) * progress(time);
m_helper->scale = m_value;
@@ -513,7 +511,6 @@ void QQuickRotationAnimatorJob::updateCurrentTime(int time)
{
if (!m_controller)
return;
- Q_ASSERT(!m_controller->m_window->openglContext() || m_controller->m_window->openglContext()->thread() == QThread::currentThread());
float t = progress(time);
@@ -546,6 +543,7 @@ void QQuickRotationAnimatorJob::writeBack()
m_target->setRotation(value());
}
+#ifndef QT_NO_OPENGL
QQuickUniformAnimatorJob::QQuickUniformAnimatorJob()
: m_node(0)
, m_uniformIndex(-1)
@@ -556,7 +554,7 @@ QQuickUniformAnimatorJob::QQuickUniformAnimatorJob()
void QQuickUniformAnimatorJob::setTarget(QQuickItem *target)
{
- if (qobject_cast<QQuickShaderEffect *>(target) != 0)
+ if (qobject_cast<QQuickOpenGLShaderEffect *>(target) != 0)
m_target = target;
}
@@ -569,14 +567,14 @@ void QQuickUniformAnimatorJob::nodeWasDestroyed()
void QQuickUniformAnimatorJob::afterNodeSync()
{
- m_node = static_cast<QQuickShaderEffectNode *>(QQuickItemPrivate::get(m_target)->paintNode);
+ m_node = static_cast<QQuickOpenGLShaderEffectNode *>(QQuickItemPrivate::get(m_target)->paintNode);
if (m_node && m_uniformIndex == -1 && m_uniformType == -1) {
- QQuickShaderEffectMaterial *material =
- static_cast<QQuickShaderEffectMaterial *>(m_node->material());
+ QQuickOpenGLShaderEffectMaterial *material =
+ static_cast<QQuickOpenGLShaderEffectMaterial *>(m_node->material());
bool found = false;
- for (int t=0; !found && t<QQuickShaderEffectMaterialKey::ShaderTypeCount; ++t) {
- const QVector<QQuickShaderEffectMaterial::UniformData> &uniforms = material->uniforms[t];
+ for (int t=0; !found && t<QQuickOpenGLShaderEffectMaterialKey::ShaderTypeCount; ++t) {
+ const QVector<QQuickOpenGLShaderEffectMaterial::UniformData> &uniforms = material->uniforms[t];
for (int i=0; i<uniforms.size(); ++i) {
if (uniforms.at(i).name == m_uniform) {
m_uniformIndex = i;
@@ -594,15 +592,14 @@ void QQuickUniformAnimatorJob::updateCurrentTime(int time)
{
if (!m_controller)
return;
- Q_ASSERT(!m_controller->m_window->openglContext() || m_controller->m_window->openglContext()->thread() == QThread::currentThread());
if (!m_node || m_uniformIndex == -1 || m_uniformType == -1)
return;
m_value = m_from + (m_to - m_from) * progress(time);
- QQuickShaderEffectMaterial *material =
- static_cast<QQuickShaderEffectMaterial *>(m_node->material());
+ QQuickOpenGLShaderEffectMaterial *material =
+ static_cast<QQuickOpenGLShaderEffectMaterial *>(m_node->material());
material->uniforms[m_uniformType][m_uniformIndex].value = m_value;
// As we're not touching the nodes, we need to explicitly mark it dirty.
// Otherwise, the renderer will abort repainting if this was the only
@@ -615,5 +612,6 @@ void QQuickUniformAnimatorJob::writeBack()
if (m_target)
m_target->setProperty(m_uniform, value());
}
+#endif
QT_END_NAMESPACE
diff --git a/src/quick/util/qquickanimatorjob_p.h b/src/quick/util/qquickanimatorjob_p.h
index 2b910d3737..64e849d322 100644
--- a/src/quick/util/qquickanimatorjob_p.h
+++ b/src/quick/util/qquickanimatorjob_p.h
@@ -68,7 +68,7 @@ class QQuickAbstractAnimation;
class QQuickAnimatorController;
class QQuickAnimatorProxyJobPrivate;
-class QQuickShaderEffectNode;
+class QQuickOpenGLShaderEffectNode;
class QSGOpacityNode;
@@ -277,7 +277,7 @@ public:
private:
QSGOpacityNode *m_opacityNode;
};
-
+#ifndef QT_NO_OPENGL
class Q_QUICK_PRIVATE_EXPORT QQuickUniformAnimatorJob : public QQuickAnimatorJob
{
public:
@@ -296,11 +296,12 @@ public:
private:
QByteArray m_uniform;
- QQuickShaderEffectNode *m_node;
+ QQuickOpenGLShaderEffectNode *m_node;
int m_uniformIndex : 8;
int m_uniformType : 8;
};
+#endif
QT_END_NAMESPACE
diff --git a/src/quick/util/qquickutilmodule.cpp b/src/quick/util/qquickutilmodule.cpp
index f5d11c6230..66994e22ba 100644
--- a/src/quick/util/qquickutilmodule.cpp
+++ b/src/quick/util/qquickutilmodule.cpp
@@ -107,8 +107,9 @@ void QQuickUtilModule::defineModule()
qmlRegisterType<QQuickScaleAnimator>("QtQuick", 2, 2, "ScaleAnimator");
qmlRegisterType<QQuickRotationAnimator>("QtQuick", 2, 2, "RotationAnimator");
qmlRegisterType<QQuickOpacityAnimator>("QtQuick", 2, 2, "OpacityAnimator");
+#ifndef QT_NO_OPENGL
qmlRegisterType<QQuickUniformAnimator>("QtQuick", 2, 2, "UniformAnimator");
-
+#endif
qmlRegisterType<QQuickStateOperation>();
qmlRegisterCustomType<QQuickPropertyChanges>("QtQuick",2,0,"PropertyChanges", new QQuickPropertyChangesParser);
diff --git a/src/quickwidgets/qquickwidget.cpp b/src/quickwidgets/qquickwidget.cpp
index 2839607bae..26a540455e 100644
--- a/src/quickwidgets/qquickwidget.cpp
+++ b/src/quickwidgets/qquickwidget.cpp
@@ -45,6 +45,8 @@
#include "private/qquickitemchangelistener_p.h"
#include "private/qquickrendercontrol_p.h"
+#include "private/qsgsoftwarerenderer_p.h"
+
#include <private/qqmldebugconnector_p.h>
#include <private/qquickprofiler_p.h>
#include <private/qqmldebugserviceinterfaces_p.h>
@@ -57,6 +59,13 @@
#include <QtGui/private/qguiapplication_p.h>
#include <QtGui/qpa/qplatformintegration.h>
+#include <QtGui/QOpenGLContext>
+#include <QtGui/QOpenGLFunctions>
+#include <QtGui/private/qopenglextensions_p.h>
+#include <QtGui/QPainter>
+
+#include <QtQuick/QSGRendererInterface>
+
#ifdef Q_OS_WIN
# include <QtWidgets/QMessageBox>
# include <QtCore/QLibraryInfo>
@@ -89,10 +98,17 @@ void QQuickWidgetPrivate::init(QQmlEngine* e)
offscreenWindow->setTitle(QString::fromLatin1("Offscreen"));
// Do not call create() on offscreenWindow.
- if (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::RasterGLSurface))
- setRenderToTexture();
- else
- qWarning("QQuickWidget is not supported on this platform.");
+ // Check if the Software Adaptation is being used
+ auto sgRendererInterface = offscreenWindow->rendererInterface();
+ if (sgRendererInterface && sgRendererInterface->graphicsAPI() == QSGRendererInterface::Software)
+ useSoftwareRenderer = true;
+
+ if (!useSoftwareRenderer) {
+ if (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::RasterGLSurface))
+ setRenderToTexture();
+ else
+ qWarning("QQuickWidget is not supported on this platform.");
+ }
engine = e;
@@ -114,13 +130,15 @@ void QQuickWidgetPrivate::init(QQmlEngine* e)
void QQuickWidgetPrivate::invalidateRenderControl()
{
- if (!context) // this is not an error, could be called before creating the context, or multiple times
- return;
+ if (!useSoftwareRenderer) {
+ if (!context) // this is not an error, could be called before creating the context, or multiple times
+ return;
- bool success = context->makeCurrent(offscreenSurface);
- if (!success) {
- qWarning("QQuickWidget::invalidateRenderControl could not make context current");
- return;
+ bool success = context->makeCurrent(offscreenSurface);
+ if (!success) {
+ qWarning("QQuickWidget::invalidateRenderControl could not make context current");
+ return;
+ }
}
renderControl->invalidate();
@@ -129,7 +147,8 @@ void QQuickWidgetPrivate::invalidateRenderControl()
void QQuickWidgetPrivate::handleWindowChange()
{
invalidateRenderControl();
- destroyContext();
+ if (!useSoftwareRenderer)
+ destroyContext();
}
QQuickWidgetPrivate::QQuickWidgetPrivate()
@@ -147,6 +166,7 @@ QQuickWidgetPrivate::QQuickWidgetPrivate()
, updatePending(false)
, fakeHidden(false)
, requestedSamples(0)
+ , useSoftwareRenderer(false)
{
}
@@ -154,14 +174,19 @@ QQuickWidgetPrivate::~QQuickWidgetPrivate()
{
invalidateRenderControl();
- // context and offscreenSurface are current at this stage, if the context was created.
- Q_ASSERT(!context || (QOpenGLContext::currentContext() == context && context->surface() == offscreenSurface));
- delete renderControl; // always delete the rendercontrol first
- delete offscreenWindow;
- delete resolvedFbo;
- delete fbo;
-
- destroyContext();
+ if (useSoftwareRenderer) {
+ delete renderControl;
+ delete offscreenWindow;
+ } else {
+ // context and offscreenSurface are current at this stage, if the context was created.
+ Q_ASSERT(!context || (QOpenGLContext::currentContext() == context && context->surface() == offscreenSurface));
+ delete renderControl; // always delete the rendercontrol first
+ delete offscreenWindow;
+ delete resolvedFbo;
+ delete fbo;
+
+ destroyContext();
+ }
}
void QQuickWidgetPrivate::execute()
@@ -204,35 +229,52 @@ void QQuickWidgetPrivate::itemGeometryChanged(QQuickItem *resizeItem, const QRec
void QQuickWidgetPrivate::render(bool needsSync)
{
- // createFramebufferObject() bails out when the size is empty. In this case
- // we cannot render either.
- if (!fbo)
- return;
+ if (!useSoftwareRenderer) {
+ // createFramebufferObject() bails out when the size is empty. In this case
+ // we cannot render either.
+ if (!fbo)
+ return;
- Q_ASSERT(context);
+ Q_ASSERT(context);
- if (!context->makeCurrent(offscreenSurface)) {
- qWarning("QQuickWidget: Cannot render due to failing makeCurrent()");
- return;
- }
+ if (!context->makeCurrent(offscreenSurface)) {
+ qWarning("QQuickWidget: Cannot render due to failing makeCurrent()");
+ return;
+ }
- QOpenGLContextPrivate::get(context)->defaultFboRedirect = fbo->handle();
+ QOpenGLContextPrivate::get(context)->defaultFboRedirect = fbo->handle();
- if (needsSync) {
- renderControl->polishItems();
- renderControl->sync();
- }
+ if (needsSync) {
+ renderControl->polishItems();
+ renderControl->sync();
+ }
- renderControl->render();
+ renderControl->render();
- if (resolvedFbo) {
- QRect rect(QPoint(0, 0), fbo->size());
- QOpenGLFramebufferObject::blitFramebuffer(resolvedFbo, rect, fbo, rect);
- }
+ if (resolvedFbo) {
+ QRect rect(QPoint(0, 0), fbo->size());
+ QOpenGLFramebufferObject::blitFramebuffer(resolvedFbo, rect, fbo, rect);
+ }
+
+ static_cast<QOpenGLExtensions *>(context->functions())->flushShared();
+
+ QOpenGLContextPrivate::get(context)->defaultFboRedirect = 0;
+ } else {
+ //Software Renderer
+ if (needsSync) {
+ renderControl->polishItems();
+ renderControl->sync();
+ }
- static_cast<QOpenGLExtensions *>(context->functions())->flushShared();
+ QQuickWindowPrivate *cd = QQuickWindowPrivate::get(offscreenWindow);
+ auto softwareRenderer = static_cast<QSGSoftwareRenderer*>(cd->renderer);
+ if (softwareRenderer) {
+ softwareRenderer->setCurrentPaintDevice(&softwareImage);
+ renderControl->render();
- QOpenGLContextPrivate::get(context)->defaultFboRedirect = 0;
+ updateRegion += softwareRenderer->flushRegion();
+ }
+ }
}
void QQuickWidgetPrivate::renderSceneGraph()
@@ -243,13 +285,15 @@ void QQuickWidgetPrivate::renderSceneGraph()
if (!q->isVisible() || fakeHidden)
return;
- QOpenGLContext *context = offscreenWindow->openglContext();
- if (!context) {
- qWarning("QQuickWidget: Attempted to render scene with no context");
- return;
- }
+ if (!useSoftwareRenderer) {
+ QOpenGLContext *context = offscreenWindow->openglContext();
+ if (!context) {
+ qWarning("QQuickWidget: Attempted to render scene with no context");
+ return;
+ }
- Q_ASSERT(offscreenSurface);
+ Q_ASSERT(offscreenSurface);
+ }
render(true);
@@ -258,15 +302,22 @@ void QQuickWidgetPrivate::renderSceneGraph()
QWidgetPrivate::nearestGraphicsProxyWidget(q)->update();
else
#endif
- q->update(); // schedule composition
+ {
+ if (!useSoftwareRenderer)
+ q->update(); // schedule composition
+ else if (!updateRegion.isEmpty())
+ q->update(updateRegion);
+ }
}
QImage QQuickWidgetPrivate::grabFramebuffer()
{
- if (!context)
- return QImage();
+ if (!useSoftwareRenderer) {
+ if (!context)
+ return QImage();
- context->makeCurrent(offscreenSurface);
+ context->makeCurrent(offscreenSurface);
+ }
return renderControl->grab();
}
@@ -705,6 +756,7 @@ void QQuickWidgetPrivate::handleContextCreationFailure(const QSurfaceFormat &for
qFatal("%s", qPrintable(untranslatedMessage));
}
+// Never called by Software Rendering backend
void QQuickWidgetPrivate::createContext()
{
Q_Q(QQuickWidget);
@@ -749,6 +801,7 @@ void QQuickWidgetPrivate::createContext()
qWarning("QQuickWidget: Failed to make context current");
}
+// Never called by Software Rendering backend
void QQuickWidgetPrivate::destroyContext()
{
delete offscreenSurface;
@@ -766,6 +819,13 @@ void QQuickWidget::createFramebufferObject()
if (size().isEmpty())
return;
+ if (d->useSoftwareRenderer) {
+ const QSize imageSize = size() * devicePixelRatio();
+ d->softwareImage = QImage(imageSize, QImage::Format_ARGB32_Premultiplied);
+ d->softwareImage.setDevicePixelRatio(devicePixelRatio());
+ return;
+ }
+
QOpenGLContext *context = d->offscreenWindow->openglContext();
if (!context) {
@@ -843,6 +903,12 @@ void QQuickWidget::createFramebufferObject()
void QQuickWidget::destroyFramebufferObject()
{
Q_D(QQuickWidget);
+
+ if (d->useSoftwareRenderer) {
+ d->softwareImage = QImage();
+ return;
+ }
+
delete d->fbo;
d->fbo = 0;
delete d->resolvedFbo;
@@ -1017,26 +1083,36 @@ void QQuickWidget::resizeEvent(QResizeEvent *e)
needsSync = true;
}
- if (d->context) {
- // Bail out when receiving a resize after scenegraph invalidation. This can happen
- // during hide - resize - show sequences and also during application exit.
- if (!d->fbo && !d->offscreenWindow->openglContext())
- return;
- if (!d->fbo || d->fbo->size() != size() * devicePixelRatio()) {
- needsSync = true;
+ // Software Renderer
+ if (d->useSoftwareRenderer) {
+ needsSync = true;
+ if (d->softwareImage.size() != size() * devicePixelRatio()) {
createFramebufferObject();
}
} else {
- // This will result in a scenegraphInitialized() signal which
- // is connected to createFramebufferObject().
- needsSync = true;
- d->createContext();
- }
- QOpenGLContext *context = d->offscreenWindow->openglContext();
- if (!context) {
- qWarning("QQuickWidget::resizeEvent() no OpenGL context");
- return;
+ if (d->context) {
+ // Bail out when receiving a resize after scenegraph invalidation. This can happen
+ // during hide - resize - show sequences and also during application exit.
+ if (!d->fbo && !d->offscreenWindow->openglContext())
+ return;
+ if (!d->fbo || d->fbo->size() != size() * devicePixelRatio()) {
+ needsSync = true;
+ createFramebufferObject();
+ }
+ } else {
+ // This will result in a scenegraphInitialized() signal which
+ // is connected to createFramebufferObject().
+ needsSync = true;
+ d->createContext();
+ }
+
+ QOpenGLContext *context = d->offscreenWindow->openglContext();
+ if (!context) {
+ qWarning("QQuickWidget::resizeEvent() no OpenGL context");
+ return;
+ }
+
}
d->render(needsSync);
@@ -1100,12 +1176,16 @@ void QQuickWidget::mouseDoubleClickEvent(QMouseEvent *e)
void QQuickWidget::showEvent(QShowEvent *)
{
Q_D(QQuickWidget);
- d->createContext();
- if (d->offscreenWindow->openglContext()) {
- d->render(true);
- if (d->updatePending) {
- d->updatePending = false;
- update();
+ if (!d->useSoftwareRenderer) {
+ d->createContext();
+ if (d->offscreenWindow->openglContext()) {
+ d->render(true);
+ if (d->updatePending) {
+ d->updatePending = false;
+ update();
+ }
+ } else {
+ triggerUpdate();
}
} else {
triggerUpdate();
@@ -1225,7 +1305,7 @@ bool QQuickWidget::event(QEvent *e)
break;
case QEvent::ScreenChangeInternal:
- if (d->fbo) {
+ if (d->fbo || d->useSoftwareRenderer) {
// This will check the size taking the devicePixelRatio into account
// and recreate if needed.
createFramebufferObject();
@@ -1397,3 +1477,24 @@ QQuickWindow *QQuickWidget::quickWindow() const
}
QT_END_NAMESPACE
+
+
+void QQuickWidget::paintEvent(QPaintEvent *event)
+{
+ Q_UNUSED(event)
+ Q_D(QQuickWidget);
+ if (d->useSoftwareRenderer) {
+ QPainter painter(this);
+ if (d->updateRegion.isNull()) {
+ //Paint everything
+ painter.drawImage(rect(), d->softwareImage);
+ } else {
+ //Paint only the updated areas
+ for (auto targetRect : d->updateRegion.rects()) {
+ auto sourceRect = QRect(targetRect.topLeft() * devicePixelRatio(), targetRect.size() * devicePixelRatio());
+ painter.drawImage(targetRect, d->softwareImage, sourceRect);
+ }
+ d->updateRegion = QRegion();
+ }
+ }
+}
diff --git a/src/quickwidgets/qquickwidget.h b/src/quickwidgets/qquickwidget.h
index 0732c506bb..56e6b01ac5 100644
--- a/src/quickwidgets/qquickwidget.h
+++ b/src/quickwidgets/qquickwidget.h
@@ -87,7 +87,7 @@ public:
QList<QQmlError> errors() const;
- QSize sizeHint() const;
+ QSize sizeHint() const override;
QSize initialSize() const;
void setFormat(const QSurfaceFormat &format);
@@ -115,34 +115,35 @@ private Q_SLOTS:
void triggerUpdate();
protected:
- virtual void resizeEvent(QResizeEvent *);
- virtual void timerEvent(QTimerEvent*);
+ void resizeEvent(QResizeEvent *) override;
+ void timerEvent(QTimerEvent*) override;
- virtual void keyPressEvent(QKeyEvent *);
- virtual void keyReleaseEvent(QKeyEvent *);
- virtual void mousePressEvent(QMouseEvent *);
- virtual void mouseReleaseEvent(QMouseEvent *);
- virtual void mouseMoveEvent(QMouseEvent *);
- virtual void mouseDoubleClickEvent(QMouseEvent *);
+ void keyPressEvent(QKeyEvent *) override;
+ void keyReleaseEvent(QKeyEvent *) override;
+ void mousePressEvent(QMouseEvent *) override;
+ void mouseReleaseEvent(QMouseEvent *) override;
+ void mouseMoveEvent(QMouseEvent *) override;
+ void mouseDoubleClickEvent(QMouseEvent *) override;
- virtual void showEvent(QShowEvent *);
- virtual void hideEvent(QHideEvent *);
+ void showEvent(QShowEvent *) override;
+ void hideEvent(QHideEvent *) override;
- virtual void focusInEvent(QFocusEvent * event);
- virtual void focusOutEvent(QFocusEvent * event);
+ void focusInEvent(QFocusEvent * event) override;
+ void focusOutEvent(QFocusEvent * event) override;
#ifndef QT_NO_WHEELEVENT
- virtual void wheelEvent(QWheelEvent *);
+ void wheelEvent(QWheelEvent *) override;
#endif
#ifndef QT_NO_DRAGANDDROP
- virtual void dragEnterEvent(QDragEnterEvent *);
- virtual void dragMoveEvent(QDragMoveEvent *);
- virtual void dragLeaveEvent(QDragLeaveEvent *);
- virtual void dropEvent(QDropEvent *);
+ void dragEnterEvent(QDragEnterEvent *) override;
+ void dragMoveEvent(QDragMoveEvent *) override;
+ void dragLeaveEvent(QDragLeaveEvent *) override;
+ void dropEvent(QDropEvent *) override;
#endif
- bool event(QEvent *);
+ bool event(QEvent *) override;
+ void paintEvent(QPaintEvent *event) override;
private:
Q_DISABLE_COPY(QQuickWidget)
diff --git a/src/quickwidgets/qquickwidget_p.h b/src/quickwidgets/qquickwidget_p.h
index fd3ef8fbbf..5c35093c58 100644
--- a/src/quickwidgets/qquickwidget_p.h
+++ b/src/quickwidgets/qquickwidget_p.h
@@ -134,6 +134,10 @@ public:
bool fakeHidden;
int requestedSamples;
+
+ bool useSoftwareRenderer;
+ QImage softwareImage;
+ QRegion updateRegion;
};
QT_END_NAMESPACE
diff --git a/src/src.pro b/src/src.pro
index 385e4eb601..04fa8663bb 100644
--- a/src/src.pro
+++ b/src/src.pro
@@ -1,14 +1,12 @@
TEMPLATE = subdirs
CONFIG += ordered
SUBDIRS += \
- qml
+ qml \
+ quick \
+ qmltest
qtHaveModule(gui):contains(QT_CONFIG, opengl(es1|es2)?) {
- SUBDIRS += \
- quick \
- qmltest \
- particles
-
+ SUBDIRS += particles
qtHaveModule(widgets): SUBDIRS += quickwidgets
}
diff --git a/tests/auto/auto.pro b/tests/auto/auto.pro
index 425e88b983..b72a43d742 100644
--- a/tests/auto/auto.pro
+++ b/tests/auto/auto.pro
@@ -2,16 +2,18 @@ TEMPLATE=subdirs
SUBDIRS=\
qml \
quick \
- particles \
qmltest \
qmldevtools \
cmake \
installed_cmake \
toolsupport
-qtHaveModule(widgets): SUBDIRS += quickwidgets
+qtHaveModule(gui):contains(QT_CONFIG, opengl(es1|es2)?) {
+ SUBDIRS += particles
+ qtHaveModule(widgets): SUBDIRS += quickwidgets
+
+}
qmldevtools.CONFIG = host_build
installed_cmake.depends = cmake
-
diff --git a/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp b/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp
index f1636eb38c..c1de5ff594 100644
--- a/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp
+++ b/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp
@@ -588,6 +588,7 @@ void tst_QQmlProfilerService::scenegraphData()
checkTraceReceived();
checkJsHeap();
+
// check that at least one frame was rendered
// there should be a SGPolishAndSync + SGRendererFrame + SGRenderLoopFrame sequence
// (though we can't be sure to get the SGRenderLoopFrame in the threaded renderer)
@@ -597,7 +598,7 @@ void tst_QQmlProfilerService::scenegraphData()
// if the clocks are acting up.
qint64 contextFrameTime = -1;
qint64 renderFrameTime = -1;
-
+#ifndef QT_NO_OPENGL //Software renderer doesn't have context frames
foreach (const QQmlProfilerData &msg, m_client->asynchronousMessages) {
if (msg.messageType == QQmlProfilerDefinitions::SceneGraphFrame) {
if (msg.detailType == QQmlProfilerDefinitions::SceneGraphContextFrame) {
@@ -608,7 +609,7 @@ void tst_QQmlProfilerService::scenegraphData()
}
QVERIFY(contextFrameTime != -1);
-
+#endif
foreach (const QQmlProfilerData &msg, m_client->asynchronousMessages) {
if (msg.detailType == QQmlProfilerDefinitions::SceneGraphRendererFrame) {
QVERIFY(msg.time >= contextFrameTime);
diff --git a/tests/auto/qml/qmlplugindump/tst_qmlplugindump.cpp b/tests/auto/qml/qmlplugindump/tst_qmlplugindump.cpp
index ee417bb480..838966e2a0 100644
--- a/tests/auto/qml/qmlplugindump/tst_qmlplugindump.cpp
+++ b/tests/auto/qml/qmlplugindump/tst_qmlplugindump.cpp
@@ -108,6 +108,7 @@ void tst_qmlplugindump::singleton()
dumper.waitForFinished();
const QString &result = dumper.readAllStandardOutput();
+ qDebug() << "result: " << result;
QVERIFY(result.contains(QLatin1String("exports: [\"Singleton 1.0\"]")));
QVERIFY(result.contains(QLatin1String("exportMetaObjectRevisions: [0]")));
}
diff --git a/tests/auto/qml/qqmlenginecleanup/data/types.qml b/tests/auto/qml/qqmlenginecleanup/data/types.qml
index 3d27900744..7282c7dbfb 100644
--- a/tests/auto/qml/qqmlenginecleanup/data/types.qml
+++ b/tests/auto/qml/qqmlenginecleanup/data/types.qml
@@ -29,7 +29,7 @@
import QtQml 2.0
import QtQuick 2.0
import QtQuick.Window 2.0
-import QtQuick.Particles 2.0
+
import Test 2.0
import "."
@@ -37,7 +37,6 @@ QtObject {
//Doesn't create items, just checks that the types are accessible
property TestType tt
property TestTypeCpp ttc
- property ParticleSystem ps
property Window wi
property Item it
}
diff --git a/tests/auto/quick/examples/tst_examples.cpp b/tests/auto/quick/examples/tst_examples.cpp
index 233cb33631..872a71011d 100644
--- a/tests/auto/quick/examples/tst_examples.cpp
+++ b/tests/auto/quick/examples/tst_examples.cpp
@@ -103,6 +103,18 @@ tst_examples::tst_examples()
excludedFiles << "views/visualdatamodel/slideshow.qml";
#endif
+#ifdef QT_NO_OPENGL
+ //No support for Particles
+ excludedFiles << "examples/qml/dynamicscene/dynamicscene.qml";
+ excludedFiles << "examples/quick/animation/basics/color-animation.qml";
+ excludedFiles << "examples/quick/particles/affectors/content/age.qml";
+ excludedFiles << "examples/quick/touchinteraction/multipointtouch/bearwhack.qml";
+ excludedFiles << "examples/quick/touchinteraction/multipointtouch/multiflame.qml";
+ excludedDirs << "examples/quick/particles";
+ // No Support for ShaderEffect
+ excludedFiles << "src/quick/doc/snippets/qml/animators.qml";
+#endif
+
}
tst_examples::~tst_examples()
diff --git a/tests/auto/quick/geometry/tst_geometry.cpp b/tests/auto/quick/geometry/tst_geometry.cpp
index 8755e3a1d7..470ce3bd91 100644
--- a/tests/auto/quick/geometry/tst_geometry.cpp
+++ b/tests/auto/quick/geometry/tst_geometry.cpp
@@ -126,9 +126,9 @@ void GeometryTest::testCustomGeometry()
};
static QSGGeometry::Attribute attributes[] = {
- { 0, 2, GL_FLOAT, 0, 0},
- { 1, 4, GL_UNSIGNED_BYTE, 0, 0},
- { 2, 4, GL_FLOAT, 0, 0},
+ QSGGeometry::Attribute::create(0, 2, QSGGeometry::TypeFloat, false),
+ QSGGeometry::Attribute::create(1, 4, QSGGeometry::TypeUnsignedByte, false),
+ QSGGeometry::Attribute::create(2, 4, QSGGeometry::TypeFloat, false)
};
static QSGGeometry::AttributeSet set = { 4, 6 * sizeof(float) + 4 * sizeof(unsigned char), attributes };
diff --git a/tests/auto/quick/nodes/tst_nodestest.cpp b/tests/auto/quick/nodes/tst_nodestest.cpp
index 16c1174604..63e0aeb324 100644
--- a/tests/auto/quick/nodes/tst_nodestest.cpp
+++ b/tests/auto/quick/nodes/tst_nodestest.cpp
@@ -31,7 +31,6 @@
#include <QtGui/QOffscreenSurface>
#include <QtGui/QOpenGLContext>
-
#include <QtQuick/qsgnode.h>
#include <QtQuick/private/qsgbatchrenderer_p.h>
#include <QtQuick/private/qsgnodeupdater_p.h>
@@ -76,7 +75,7 @@ private Q_SLOTS:
private:
QOffscreenSurface *surface;
QOpenGLContext *context;
- QSGRenderContext *renderContext;
+ QSGDefaultRenderContext *renderContext;
};
void NodesTest::initTestCase()
@@ -91,7 +90,8 @@ void NodesTest::initTestCase()
QVERIFY(context->create());
QVERIFY(context->makeCurrent(surface));
- renderContext = renderLoop->createRenderContext(renderLoop->sceneGraphContext());
+ auto rc = renderLoop->createRenderContext(renderLoop->sceneGraphContext());
+ renderContext = static_cast<QSGDefaultRenderContext *>(rc);
QVERIFY(renderContext);
renderContext->initialize(context);
QVERIFY(renderContext->isValid());
@@ -110,7 +110,7 @@ void NodesTest::cleanupTestCase()
class DummyRenderer : public QSGBatchRenderer::Renderer
{
public:
- DummyRenderer(QSGRootNode *root, QSGRenderContext *renderContext)
+ DummyRenderer(QSGRootNode *root, QSGDefaultRenderContext *renderContext)
: QSGBatchRenderer::Renderer(renderContext)
, changedNode(0)
, changedState(0)
diff --git a/tests/auto/quick/nokeywords/tst_nokeywords.cpp b/tests/auto/quick/nokeywords/tst_nokeywords.cpp
index ffe76cc210..e25cd9535b 100644
--- a/tests/auto/quick/nokeywords/tst_nokeywords.cpp
+++ b/tests/auto/quick/nokeywords/tst_nokeywords.cpp
@@ -48,6 +48,7 @@
#include <QtQuick/private/qsgadaptationlayer_p.h>
#include <QtQuick/private/qsgcontext_p.h>
#include <QtQuick/private/qsgcontextplugin_p.h>
+#ifndef QT_NO_OPENGL
#include <QtQuick/private/qsgdefaultdistancefieldglyphcache_p.h>
#include <QtQuick/private/qsgdefaultglyphnode_p.h>
#include <QtQuick/private/qsgdefaultimagenode_p.h>
@@ -55,6 +56,7 @@
#include <QtQuick/private/qsgdepthstencilbuffer_p.h>
#include <QtQuick/private/qsgdistancefieldglyphnode_p.h>
#include <QtQuick/private/qsgdistancefieldutil_p.h>
+#endif
#include <QtQuick/private/qsggeometry_p.h>
#include <QtQuick/private/qsgnode_p.h>
#include <QtQuick/private/qsgnodeupdater_p.h>
diff --git a/tests/auto/quick/qquickborderimage/tst_qquickborderimage.cpp b/tests/auto/quick/qquickborderimage/tst_qquickborderimage.cpp
index e1435e739f..40cead6ac0 100644
--- a/tests/auto/quick/qquickborderimage/tst_qquickborderimage.cpp
+++ b/tests/auto/quick/qquickborderimage/tst_qquickborderimage.cpp
@@ -76,7 +76,9 @@ private slots:
void statusChanges_data();
void sourceSizeChanges();
void progressAndStatusChanges();
+#ifndef QT_NO_OPENGL
void borderImageMesh();
+#endif
private:
QQmlEngine engine;
@@ -576,7 +578,7 @@ void tst_qquickborderimage::progressAndStatusChanges()
delete obj;
}
-
+#ifndef QT_NO_OPENGL
void tst_qquickborderimage::borderImageMesh()
{
QQuickView *window = new QQuickView;
@@ -591,7 +593,7 @@ void tst_qquickborderimage::borderImageMesh()
QVERIFY(QQuickVisualTestUtil::compareImages(mesh, nonmesh));
}
-
+#endif
QTEST_MAIN(tst_qquickborderimage)
#include "tst_qquickborderimage.moc"
diff --git a/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp b/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp
index 006c55542a..2742f5c1e2 100644
--- a/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp
+++ b/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp
@@ -1137,7 +1137,9 @@ void tst_qquickflickable::flickOnRelease()
QTRY_VERIFY(!flickable->isMoving());
#ifdef Q_OS_MAC
+# ifndef QT_NO_OPENGL
QEXPECT_FAIL("", "QTBUG-26094 stopping on a full pixel doesn't work on OS X", Continue);
+# endif
#endif
// Stop on a full pixel after user interaction
QCOMPARE(flickable->contentY(), (qreal)qRound(flickable->contentY()));
diff --git a/tests/auto/quick/qquickimage/tst_qquickimage.cpp b/tests/auto/quick/qquickimage/tst_qquickimage.cpp
index 9274a1ac9e..d345163db5 100644
--- a/tests/auto/quick/qquickimage/tst_qquickimage.cpp
+++ b/tests/auto/quick/qquickimage/tst_qquickimage.cpp
@@ -306,6 +306,7 @@ void tst_qquickimage::mirror()
qreal width = 300;
qreal height = 250;
+ qreal devicePixelRatio = 1.0;
foreach (QQuickImage::FillMode fillMode, fillModes) {
#if defined(Q_OS_BLACKBERRY)
@@ -325,13 +326,15 @@ void tst_qquickimage::mirror()
QImage screenshot = window->grabWindow();
screenshots[fillMode] = screenshot;
+ devicePixelRatio = window->devicePixelRatio();
}
foreach (QQuickImage::FillMode fillMode, fillModes) {
QPixmap srcPixmap;
QVERIFY(srcPixmap.load(testFile("pattern.png")));
- QPixmap expected(width, height);
+ QPixmap expected(width * (int)devicePixelRatio, height * (int)devicePixelRatio);
+ expected.setDevicePixelRatio(devicePixelRatio);
expected.fill();
QPainter p_e(&expected);
QTransform transform;
diff --git a/tests/auto/quick/qquickitem2/tst_qquickitem.cpp b/tests/auto/quick/qquickitem2/tst_qquickitem.cpp
index 62ff09e698..de61d7aea9 100644
--- a/tests/auto/quick/qquickitem2/tst_qquickitem.cpp
+++ b/tests/auto/quick/qquickitem2/tst_qquickitem.cpp
@@ -3017,7 +3017,7 @@ void tst_QQuickItem::grab()
QVERIFY(root);
QQuickItem *item = root->findChild<QQuickItem *>("myItem");
QVERIFY(item);
-
+#ifndef QT_NO_OPENGL
{ // Default size (item is 100x100)
QSharedPointer<QQuickItemGrabResult> result = item->grabToImage();
QSignalSpy spy(result.data(), SIGNAL(ready()));
@@ -3038,7 +3038,7 @@ void tst_QQuickItem::grab()
QCOMPARE(image.pixel(0, 0), qRgb(255, 0, 0));
QCOMPARE(image.pixel(49, 49), qRgb(0, 0, 255));
}
-
+#endif
}
void tst_QQuickItem::isAncestorOf()
diff --git a/tests/auto/quick/qquickitemlayer/tst_qquickitemlayer.cpp b/tests/auto/quick/qquickitemlayer/tst_qquickitemlayer.cpp
index 92b7196919..d991f189fc 100644
--- a/tests/auto/quick/qquickitemlayer/tst_qquickitemlayer.cpp
+++ b/tests/auto/quick/qquickitemlayer/tst_qquickitemlayer.cpp
@@ -60,9 +60,10 @@ private slots:
void initTestCase() Q_DECL_OVERRIDE;
void layerEnabled();
void layerSmooth();
+#ifndef QT_NO_OPENGL
void layerMipmap();
void layerEffect();
-
+#endif
void layerVisibility_data();
void layerVisibility();
@@ -102,6 +103,7 @@ void tst_QQuickItemLayer::initTestCase()
{
QQmlDataTest::initTestCase();
QWindow window;
+#ifndef QT_NO_OPENGL
QOpenGLContext context;
window.setSurfaceType(QWindow::OpenGLSurface);
window.create();
@@ -129,6 +131,9 @@ void tst_QQuickItemLayer::initTestCase()
m_mesaVersion = QT_VERSION_CHECK(major, minor, patch);
}
}
+#else
+ window.create();
+#endif
}
// The test draws a red and a blue box next to each other and tests that the
@@ -165,8 +170,7 @@ void tst_QQuickItemLayer::layerEnabled()
QVERIFY(fb.pixel(0, 0) != fb.pixel(0, fb.height() - 1));
}
-
-
+#ifndef QT_NO_OPENGL
// The test draws a one pixel wide line and scales it down by more than a a factor 2
// If mipmpping works, the pixels should be gray, not white or black
@@ -192,8 +196,7 @@ void tst_QQuickItemLayer::layerEffect()
QCOMPARE(fb.pixel(0, 0), qRgb(0xff, 0, 0));
QCOMPARE(fb.pixel(fb.width() - 1, 0), qRgb(0, 0xff, 0));
}
-
-
+#endif
// The test draws a rectangle and verifies that there is padding on each side
// as the source rect spans outside the item. The padding is verified using
diff --git a/tests/auto/quick/qquickpainteditem/tst_qquickpainteditem.cpp b/tests/auto/quick/qquickpainteditem/tst_qquickpainteditem.cpp
index 1a04526f61..44d7b40ed9 100644
--- a/tests/auto/quick/qquickpainteditem/tst_qquickpainteditem.cpp
+++ b/tests/auto/quick/qquickpainteditem/tst_qquickpainteditem.cpp
@@ -32,8 +32,11 @@
#include <QtQuick/qquickview.h>
#include <private/qquickitem_p.h>
+#ifndef QT_NO_OPENGL
#include <private/qsgdefaultpainternode_p.h>
-
+#else
+#include <private/qsgsoftwarepainternode_p.h>
+#endif
class tst_QQuickPaintedItem: public QObject
{
Q_OBJECT
@@ -70,7 +73,7 @@ public:
++paintRequests;
clipRect = painter->clipBoundingRect();
}
-
+#ifndef QT_NO_OPENGL
QSGNode *updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data)
{
paintNode = static_cast<QSGDefaultPainterNode *>(QQuickPaintedItem::updatePaintNode(oldNode, data));
@@ -78,6 +81,15 @@ public:
}
QSGDefaultPainterNode *paintNode;
+#else
+ QSGNode *updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data)
+ {
+ paintNode = static_cast<QSGSoftwarePainterNode *>(QQuickPaintedItem::updatePaintNode(oldNode, data));
+ return paintNode;
+ }
+
+ QSGSoftwarePainterNode *paintNode;
+#endif
int paintRequests;
QRectF clipRect;
};
diff --git a/tests/auto/quick/qquickrendererinfo/data/basic.qml b/tests/auto/quick/qquickrendererinfo/data/basic.qml
new file mode 100644
index 0000000000..b7dbbb9ac9
--- /dev/null
+++ b/tests/auto/quick/qquickrendererinfo/data/basic.qml
@@ -0,0 +1,5 @@
+import QtQuick 2.8
+
+Item {
+ property int api: RendererInfo.api
+}
diff --git a/tests/auto/quick/qquickrendererinfo/qquickrendererinfo.pro b/tests/auto/quick/qquickrendererinfo/qquickrendererinfo.pro
new file mode 100644
index 0000000000..4f55b79961
--- /dev/null
+++ b/tests/auto/quick/qquickrendererinfo/qquickrendererinfo.pro
@@ -0,0 +1,14 @@
+CONFIG += testcase
+TARGET = tst_qquickrendererinfo
+SOURCES += tst_qquickrendererinfo.cpp
+
+TESTDATA = data/*
+include(../../shared/util.pri)
+
+osx:CONFIG -= app_bundle
+
+QT += quick testlib
+
+OTHER_FILES += \
+ data/basic.qml
+
diff --git a/tests/auto/quick/qquickrendererinfo/tst_qquickrendererinfo.cpp b/tests/auto/quick/qquickrendererinfo/tst_qquickrendererinfo.cpp
new file mode 100644
index 0000000000..b9e8c30ea7
--- /dev/null
+++ b/tests/auto/quick/qquickrendererinfo/tst_qquickrendererinfo.cpp
@@ -0,0 +1,68 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest/qtest.h>
+#include <QtTest/qsignalspy.h>
+
+#include <QtQuick/qquickitem.h>
+#include <QtQuick/qquickview.h>
+#include <QtQuick/qsgrendererinterface.h>
+
+#include "../../shared/util.h"
+
+class tst_QQuickRendererInfo : public QQmlDataTest
+{
+ Q_OBJECT
+
+private slots:
+ void testProperties();
+};
+
+void tst_QQuickRendererInfo::testProperties()
+{
+ QQuickView view;
+ view.setSource(QUrl::fromLocalFile("data/basic.qml"));
+
+ view.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&view));
+
+ QSignalSpy spy(&view, SIGNAL(sceneGraphInitialized()));
+ spy.wait();
+
+ QObject* obj = view.rootObject();
+ QVERIFY(obj);
+
+ QSGRendererInterface *rif = view.rendererInterface();
+ const int expectedAPI = rif ? rif->graphicsAPI() : QSGRendererInterface::Unknown;
+
+ QCOMPARE(obj->property("api").toInt(), expectedAPI);
+}
+
+QTEST_MAIN(tst_QQuickRendererInfo)
+
+#include "tst_qquickrendererinfo.moc"
diff --git a/tests/auto/quick/qquickshadereffect/tst_qquickshadereffect.cpp b/tests/auto/quick/qquickshadereffect/tst_qquickshadereffect.cpp
index d0e718fb91..171e0800e1 100644
--- a/tests/auto/quick/qquickshadereffect/tst_qquickshadereffect.cpp
+++ b/tests/auto/quick/qquickshadereffect/tst_qquickshadereffect.cpp
@@ -30,12 +30,11 @@
#include <QList>
#include <QByteArray>
-#include <private/qquickshadereffect_p.h>
-
+#include <private/qquickopenglshadereffect_p.h>
+#include <QMatrix4x4>
#include <QtQuick/QQuickView>
#include "../../shared/util.h"
-
class TestShaderEffect : public QQuickShaderEffect
{
Q_OBJECT
diff --git a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp
index 526f61ceb5..138f2759a4 100644
--- a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp
+++ b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp
@@ -44,6 +44,8 @@
#include <private/qquickwindow_p.h>
#include <private/qguiapplication_p.h>
#include <QRunnable>
+#include <QOpenGLFunctions>
+#include <QSGRendererInterface>
struct TouchEventData {
QEvent::Type type;
@@ -290,8 +292,9 @@ private slots:
QWindowSystemInterface::registerTouchDevice(touchDeviceWithVelocity);
}
void cleanup();
-
+#ifndef QT_NO_OPENGL
void openglContextCreatedSignal();
+#endif
void aboutToStopSignal();
void constantUpdates();
@@ -371,14 +374,14 @@ private:
QTouchDevice *touchDevice;
QTouchDevice *touchDeviceWithVelocity;
};
-
+#ifndef QT_NO_OPENGL
Q_DECLARE_METATYPE(QOpenGLContext *);
-
+#endif
void tst_qquickwindow::cleanup()
{
QVERIFY(QGuiApplication::topLevelWindows().isEmpty());
}
-
+#ifndef QT_NO_OPENGL
void tst_qquickwindow::openglContextCreatedSignal()
{
qRegisterMetaType<QOpenGLContext *>();
@@ -390,12 +393,15 @@ void tst_qquickwindow::openglContextCreatedSignal()
window.show();
QTest::qWaitForWindowExposed(&window);
+ if (window.rendererInterface()->graphicsAPI() != QSGRendererInterface::OpenGL)
+ QSKIP("Skipping OpenGL context test due to not running with OpenGL");
+
QVERIFY(spy.size() > 0);
QVariant ctx = spy.at(0).at(0);
QCOMPARE(qvariant_cast<QOpenGLContext *>(ctx), window.openglContext());
}
-
+#endif
void tst_qquickwindow::aboutToStopSignal()
{
QQuickWindow window;
@@ -437,8 +443,7 @@ void tst_qquickwindow::constantUpdatesOnWindow_data()
window.setGeometry(100, 100, 300, 200);
window.show();
QTest::qWaitForWindowExposed(&window);
- bool threaded = window.openglContext()->thread() != QGuiApplication::instance()->thread();
-
+ const bool threaded = QQuickWindowPrivate::get(&window)->context->thread() != QGuiApplication::instance()->thread();
if (threaded) {
QTest::newRow("blocked, beforeRender") << true << QByteArray(SIGNAL(beforeRendering()));
QTest::newRow("blocked, afterRender") << true << QByteArray(SIGNAL(afterRendering()));
@@ -1228,13 +1233,14 @@ void tst_qquickwindow::headless()
QVERIFY(QTest::qWaitForWindowExposed(window));
QVERIFY(window->isVisible());
- bool threaded = window->openglContext()->thread() != QThread::currentThread();
-
+ const bool threaded = QQuickWindowPrivate::get(window)->context->thread() != QThread::currentThread();
QSignalSpy initialized(window, SIGNAL(sceneGraphInitialized()));
QSignalSpy invalidated(window, SIGNAL(sceneGraphInvalidated()));
// Verify that the window is alive and kicking
- QVERIFY(window->openglContext() != 0);
+ QVERIFY(window->isSceneGraphInitialized());
+
+ const bool isGL = window->rendererInterface()->graphicsAPI() == QSGRendererInterface::OpenGL;
// Store the visual result
QImage originalContent = window->grabWindow();
@@ -1244,15 +1250,16 @@ void tst_qquickwindow::headless()
window->releaseResources();
if (threaded) {
- QTRY_COMPARE(invalidated.size(), 1);
- QVERIFY(!window->openglContext());
+ QTRY_VERIFY(invalidated.size() >= 1);
+ if (isGL)
+ QVERIFY(!window->isSceneGraphInitialized());
}
-
+#ifndef QT_NO_OPENGL
if (QGuiApplication::platformName() == QLatin1String("windows")
&& QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGLES) {
QSKIP("Crashes on Windows/ANGLE, QTBUG-42967");
}
-
+#endif
// Destroy the native windowing system buffers
window->destroy();
QVERIFY(!window->handle());
@@ -1263,7 +1270,8 @@ void tst_qquickwindow::headless()
if (threaded)
QTRY_COMPARE(initialized.size(), 1);
- QVERIFY(window->openglContext() != 0);
+
+ QVERIFY(window->isSceneGraphInitialized());
// Verify that the visual output is the same
QImage newContent = window->grabWindow();
@@ -1284,8 +1292,7 @@ void tst_qquickwindow::noUpdateWhenNothingChanges()
// the initial expose with a second expose or more. Let these go
// through before we let the test continue.
QTest::qWait(100);
-
- if (window.openglContext()->thread() == QGuiApplication::instance()->thread()) {
+ if (QQuickWindowPrivate::get(&window)->context->thread() == QGuiApplication::instance()->thread()) {
QSKIP("Only threaded renderloop implements this feature");
return;
}
@@ -1592,7 +1599,6 @@ void tst_qquickwindow::hideThenDelete()
QSignalSpy *openglDestroyed = 0;
QSignalSpy *sgInvalidated = 0;
- bool threaded = false;
{
QQuickWindow window;
@@ -1607,9 +1613,13 @@ void tst_qquickwindow::hideThenDelete()
window.show();
QTest::qWaitForWindowExposed(&window);
- threaded = window.openglContext()->thread() != QThread::currentThread();
+ const bool threaded = QQuickWindowPrivate::get(&window)->context->thread() != QGuiApplication::instance()->thread();
+ const bool isGL = window.rendererInterface()->graphicsAPI() == QSGRendererInterface::OpenGL;
+#ifndef QT_NO_OPENGL
+ if (isGL)
+ openglDestroyed = new QSignalSpy(window.openglContext(), SIGNAL(aboutToBeDestroyed()));
+#endif
- openglDestroyed = new QSignalSpy(window.openglContext(), SIGNAL(aboutToBeDestroyed()));
sgInvalidated = new QSignalSpy(&window, SIGNAL(sceneGraphInvalidated()));
window.hide();
@@ -1617,6 +1627,9 @@ void tst_qquickwindow::hideThenDelete()
QTRY_VERIFY(!window.isExposed());
if (threaded) {
+ if (!isGL)
+ QSKIP("Skipping persistency verification due to not running with OpenGL");
+
if (!persistentSG) {
QVERIFY(sgInvalidated->size() > 0);
if (!persistentGL)
@@ -1631,7 +1644,10 @@ void tst_qquickwindow::hideThenDelete()
}
QVERIFY(sgInvalidated->size() > 0);
- QVERIFY(openglDestroyed->size() > 0);
+#ifndef QT_NO_OPENGL
+ if (openglDestroyed)
+ QVERIFY(openglDestroyed->size() > 0);
+#endif
}
void tst_qquickwindow::showHideAnimate()
@@ -2028,6 +2044,9 @@ void tst_qquickwindow::defaultSurfaceFormat()
window.show();
QVERIFY(QTest::qWaitForWindowExposed(&window));
+ if (window.rendererInterface()->graphicsAPI() != QSGRendererInterface::OpenGL)
+ QSKIP("Skipping OpenGL context test due to not running with OpenGL");
+
const QSurfaceFormat reqFmt = window.requestedFormat();
QCOMPARE(format.swapInterval(), reqFmt.swapInterval());
QCOMPARE(format.redBufferSize(), reqFmt.redBufferSize());
@@ -2036,12 +2055,13 @@ void tst_qquickwindow::defaultSurfaceFormat()
QCOMPARE(format.profile(), reqFmt.profile());
QCOMPARE(int(format.options()), int(reqFmt.options()));
+#ifndef QT_NO_OPENGL
// Depth and stencil should be >= what has been requested. For real. But use
// the context since the window's surface format is only partially updated
// on most platforms.
QVERIFY(window.openglContext()->format().depthBufferSize() >= 16);
QVERIFY(window.openglContext()->format().stencilBufferSize() >= 8);
-
+#endif
QSurfaceFormat::setDefaultFormat(savedDefaultFormat);
}
@@ -2090,7 +2110,7 @@ public:
}
static int deleted;
};
-
+#ifndef QT_NO_OPENGL
class GlRenderJob : public QRunnable
{
public:
@@ -2112,7 +2132,7 @@ public:
QMutex *mutex;
QWaitCondition *condition;
};
-
+#endif
int RenderJob::deleted = 0;
void tst_qquickwindow::testRenderJob()
@@ -2161,25 +2181,29 @@ void tst_qquickwindow::testRenderJob()
QTRY_COMPARE(RenderJob::deleted, 1);
QCOMPARE(completedJobs.size(), 1);
- // Do a synchronized GL job.
- GLubyte readPixel[4] = {0, 0, 0, 0};
- GlRenderJob *glJob = new GlRenderJob(readPixel);
- if (window.openglContext()->thread() != QThread::currentThread()) {
- QMutex mutex;
- QWaitCondition condition;
- glJob->mutex = &mutex;
- glJob->condition = &condition;
- mutex.lock();
- window.scheduleRenderJob(glJob, QQuickWindow::NoStage);
- condition.wait(&mutex);
- mutex.unlock();
- } else {
- window.scheduleRenderJob(glJob, QQuickWindow::NoStage);
+#ifndef QT_NO_OPENGL
+ if (window.rendererInterface()->graphicsAPI() == QSGRendererInterface::OpenGL) {
+ // Do a synchronized GL job.
+ GLubyte readPixel[4] = {0, 0, 0, 0};
+ GlRenderJob *glJob = new GlRenderJob(readPixel);
+ if (window.openglContext()->thread() != QThread::currentThread()) {
+ QMutex mutex;
+ QWaitCondition condition;
+ glJob->mutex = &mutex;
+ glJob->condition = &condition;
+ mutex.lock();
+ window.scheduleRenderJob(glJob, QQuickWindow::NoStage);
+ condition.wait(&mutex);
+ mutex.unlock();
+ } else {
+ window.scheduleRenderJob(glJob, QQuickWindow::NoStage);
+ }
+ QCOMPARE(int(readPixel[0]), 255);
+ QCOMPARE(int(readPixel[1]), 0);
+ QCOMPARE(int(readPixel[2]), 0);
+ QCOMPARE(int(readPixel[3]), 255);
}
- QCOMPARE(int(readPixel[0]), 255);
- QCOMPARE(int(readPixel[1]), 0);
- QCOMPARE(int(readPixel[2]), 0);
- QCOMPARE(int(readPixel[3]), 255);
+#endif
}
// Verify that jobs are deleted when window is not rendered at all
@@ -2197,7 +2221,6 @@ void tst_qquickwindow::testRenderJob()
class EventCounter : public QQuickRectangle
{
- Q_OBJECT
public:
EventCounter(QQuickItem *parent = 0)
: QQuickRectangle(parent)
diff --git a/tests/auto/quick/quick.pro b/tests/auto/quick/quick.pro
index 2e43702e7c..fecd1aea58 100644
--- a/tests/auto/quick/quick.pro
+++ b/tests/auto/quick/quick.pro
@@ -2,10 +2,20 @@ TEMPLATE = subdirs
PUBLICTESTS += \
geometry \
- rendernode \
qquickpixmapcache
-qtHaveModule(widgets): PUBLICTESTS += nodes
+contains(QT_CONFIG, opengl(es1|es2)?) {
+ PUBLICTESTS += \
+ rendernode
+ qtHaveModule(widgets): PUBLICTESTS += nodes
+
+ QUICKTESTS += \
+ qquickanimatedsprite \
+ qquickframebufferobject \
+ qquickopenglinfo \
+ qquickspritesequence \
+ qquickshadereffect
+}
!cross_compile: PRIVATETESTS += examples
@@ -39,7 +49,6 @@ QUICKTESTS = \
qquickaccessible \
qquickanchors \
qquickanimatedimage \
- qquickanimatedsprite \
qquickdynamicpropertyanimation \
qquickborderimage \
qquickwindow \
@@ -48,7 +57,6 @@ QUICKTESTS = \
qquickflickable \
qquickflipable \
qquickfocusscope \
- qquickframebufferobject \
qquickgridview \
qquickimage \
qquickitem \
@@ -58,16 +66,14 @@ QUICKTESTS = \
qquickloader \
qquickmousearea \
qquickmultipointtoucharea \
- qquickopenglinfo \
qquickpainteditem \
qquickpathview \
qquickpincharea \
qquickpositioners \
qquickrectangle \
+ qquickrendererinfo \
qquickrepeater \
- qquickshadereffect \
qquickshortcut \
- qquickspritesequence \
qquicktext \
qquicktextdocument \
qquicktextedit \
diff --git a/tests/auto/quick/rendernode/tst_rendernode.cpp b/tests/auto/quick/rendernode/tst_rendernode.cpp
index 782ebf5aee..313e5ac196 100644
--- a/tests/auto/quick/rendernode/tst_rendernode.cpp
+++ b/tests/auto/quick/rendernode/tst_rendernode.cpp
@@ -66,12 +66,12 @@ private slots:
class ClearNode : public QSGRenderNode
{
public:
- virtual StateFlags changedStates()
+ StateFlags changedStates() const override
{
return ColorState;
}
- virtual void render(const RenderState &)
+ void render(const RenderState *) override
{
// If clip has been set, scissoring will make sure the right area is cleared.
QOpenGLContext::currentContext()->functions()->glClearColor(color.redF(), color.greenF(), color.blueF(), 1.0f);
@@ -122,13 +122,13 @@ class MessUpNode : public QSGRenderNode, protected QOpenGLFunctions
public:
MessUpNode() : initialized(false) { }
- virtual StateFlags changedStates()
+ StateFlags changedStates() const override
{
return StateFlags(DepthState) | StencilState | ScissorState | ColorState | BlendState
- | CullState | ViewportState;
+ | CullState | ViewportState | RenderTargetState;
}
- virtual void render(const RenderState &)
+ void render(const RenderState *) override
{
if (!initialized) {
initializeOpenGLFunctions();
@@ -152,6 +152,9 @@ public:
glGetIntegerv(GL_FRONT_FACE, &frontFace);
glFrontFace(frontFace == GL_CW ? GL_CCW : GL_CW);
glEnable(GL_CULL_FACE);
+ GLuint fbo;
+ glGenFramebuffers(1, &fbo);
+ glBindFramebuffer(GL_FRAMEBUFFER, fbo);
}
bool initialized;
@@ -257,8 +260,8 @@ void tst_rendernode::messUpState()
class StateRecordingRenderNode : public QSGRenderNode
{
public:
- StateFlags changedStates() { return StateFlags(-1); }
- void render(const RenderState &) {
+ StateFlags changedStates() const override { return StateFlags(-1); }
+ void render(const RenderState *) override {
matrices[name] = *matrix();
}
diff --git a/tests/auto/quick/scenegraph/tst_scenegraph.cpp b/tests/auto/quick/scenegraph/tst_scenegraph.cpp
index 28e2010906..86f971041f 100644
--- a/tests/auto/quick/scenegraph/tst_scenegraph.cpp
+++ b/tests/auto/quick/scenegraph/tst_scenegraph.cpp
@@ -28,14 +28,19 @@
#include <qtest.h>
+#ifndef QT_NO_OPENGL
#include <QOffscreenSurface>
#include <QOpenGLContext>
#include <QOpenGLFunctions>
+#endif
#include <QtQuick>
#include <QtQml>
+#ifndef QT_NO_OPENGL
#include <private/qopenglcontext_p.h>
+#endif
+
#include <private/qsgcontext_p.h>
#include <private/qsgrenderloop_p.h>
@@ -99,9 +104,9 @@ private slots:
void render_data();
void render();
-
+#ifndef QT_NO_OPENGL
void hideWithOtherContext();
-
+#endif
void createTextureFromImage_data();
void createTextureFromImage();
@@ -121,6 +126,7 @@ void tst_SceneGraph::initTestCase()
QSGRenderLoop *loop = QSGRenderLoop::instance();
qDebug() << "RenderLoop: " << loop;
+#ifndef QT_NO_OPENGL
QOpenGLContext context;
context.setFormat(loop->sceneGraphContext()->defaultSurfaceFormat());
context.create();
@@ -153,6 +159,7 @@ void tst_SceneGraph::initTestCase()
qDebug() << "Broken Mipmap: " << m_brokenMipmapSupport;
context.doneCurrent();
+#endif
}
QQuickView *createView(const QString &file, QWindow *parent = 0, int x = -1, int y = -1, int w = -1, int h = -1)
@@ -178,12 +185,17 @@ QImage showAndGrab(const QString &file, int w, int h)
// Assumes the images are opaque white...
bool containsSomethingOtherThanWhite(const QImage &image)
{
- Q_ASSERT(image.format() == QImage::Format_ARGB32_Premultiplied
- || image.format() == QImage::Format_RGB32);
- int w = image.width();
- int h = image.height();
+ QImage img;
+ if (image.format() != QImage::Format_ARGB32_Premultiplied
+ || image.format() != QImage::Format_RGB32)
+ img = image.convertToFormat(QImage::Format_ARGB32_Premultiplied);
+ else
+ img = image;
+
+ int w = img.width();
+ int h = img.height();
for (int y=0; y<h; ++y) {
- const uint *pixels = (const uint *) image.constScanLine(y);
+ const uint *pixels = (const uint *) img.constScanLine(y);
for (int x=0; x<w; ++x)
if (pixels[x] != 0xffffffff)
return true;
@@ -216,24 +228,26 @@ void tst_SceneGraph::manyWindows_data()
QTest::newRow("rects,subwindow,sharing") << QStringLiteral("manyWindows_rects.qml") << false << true;
}
+#ifndef QT_NO_OPENGL
struct ShareContextResetter {
public:
~ShareContextResetter() { qt_gl_set_global_share_context(0); }
};
+#endif
void tst_SceneGraph::manyWindows()
{
QFETCH(QString, file);
QFETCH(bool, toplevel);
QFETCH(bool, shared);
-
+#ifndef QT_NO_OPENGL
QOpenGLContext sharedGLContext;
ShareContextResetter cleanup; // To avoid dangling pointer in case of test-failure.
if (shared) {
QVERIFY(sharedGLContext.create());
qt_gl_set_global_share_context(&sharedGLContext);
}
-
+#endif
QScopedPointer<QWindow> parent;
if (!toplevel) {
parent.reset(new QWindow());
@@ -422,6 +436,13 @@ void tst_SceneGraph::render_data()
void tst_SceneGraph::render()
{
+ QQuickView dummy;
+ dummy.show();
+ QTest::qWaitForWindowExposed(&dummy);
+ if (dummy.rendererInterface()->graphicsAPI() != QSGRendererInterface::OpenGL)
+ QSKIP("Skipping complex rendering tests due to not running with OpenGL");
+ dummy.hide();
+
QFETCH(QString, file);
QFETCH(QList<Sample>, baseStage);
QFETCH(QList<Sample>, finalStage);
@@ -464,6 +485,7 @@ void tst_SceneGraph::render()
}
}
+#ifndef QT_NO_OPENGL
// Testcase for QTBUG-34898. We make another context current on another surface
// in the GUI thread and hide the QQuickWindow while the other context is
// current on the other window.
@@ -484,6 +506,9 @@ void tst_SceneGraph::hideWithOtherContext()
view.show();
QVERIFY(QTest::qWaitForWindowExposed(&view));
+ if (view.rendererInterface()->graphicsAPI() != QSGRendererInterface::OpenGL)
+ QSKIP("Skipping OpenGL context test due to not running with OpenGL");
+
renderingOnMainThread = view.openglContext()->thread() == QGuiApplication::instance()->thread();
// Make the local context current on the local window...
@@ -496,6 +521,7 @@ void tst_SceneGraph::hideWithOtherContext()
// GL calls to a new frame (see QOpenGLContext docs).
QVERIFY(!renderingOnMainThread || QOpenGLContext::currentContext() != &context);
}
+#endif
void tst_SceneGraph::createTextureFromImage_data()
{
@@ -521,6 +547,9 @@ void tst_SceneGraph::createTextureFromImage()
QFETCH(bool, expectedAlpha);
QQuickView view;
+ view.show();
+ QTest::qWaitForWindowExposed(&view);
+
QScopedPointer<QSGTexture> texture(view.createTextureFromImage(image, (QQuickWindow::CreateTextureOptions) flags));
QCOMPARE(texture->hasAlphaChannel(), expectedAlpha);
}
diff --git a/tests/benchmarks/benchmarks.pro b/tests/benchmarks/benchmarks.pro
index c7e7c6829a..bd071ecf5c 100644
--- a/tests/benchmarks/benchmarks.pro
+++ b/tests/benchmarks/benchmarks.pro
@@ -1,5 +1,5 @@
TEMPLATE = subdirs
SUBDIRS = qml script
contains(QT_CONFIG, private_tests) {
- SUBDIRS += particles
+ contains(QT_CONFIG, opengl(es1|es2)?):SUBDIRS += particles
}
diff --git a/tests/manual/nodetypes/Animators.qml b/tests/manual/nodetypes/Animators.qml
new file mode 100644
index 0000000000..7d8baf1cb8
--- /dev/null
+++ b/tests/manual/nodetypes/Animators.qml
@@ -0,0 +1,180 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.5
+
+Item {
+ id: window
+
+ Rectangle {
+ anchors.fill: parent
+ gradient: Gradient {
+ GradientStop { position: 0.0; color: "#14148c" }
+ GradientStop { position: 0.499; color: "#14aaff" }
+ GradientStop { position: 0.5; color: "#80c342" }
+ GradientStop { position: 1.0; color: "#006325" }
+ }
+ }
+
+ SequentialAnimation {
+ id: plainAnim
+ SequentialAnimation {
+ ParallelAnimation {
+ PropertyAnimation {
+ property: "y"
+ target: smiley
+ from: smiley.minHeight
+ to: smiley.maxHeight
+ easing.type: Easing.OutExpo
+ duration: 300
+ }
+ PropertyAnimation {
+ property: "scale"
+ target: shadow
+ from: 1
+ to: 0.5
+ easing.type: Easing.OutExpo
+ duration: 300
+ }
+ }
+ ParallelAnimation {
+ PropertyAnimation {
+ property: "y"
+ target: smiley
+ from: smiley.maxHeight
+ to: smiley.minHeight
+ easing.type: Easing.OutBounce
+ duration: 1000
+ }
+ PropertyAnimation {
+ property: "scale"
+ target: shadow
+ from: 0.5
+ to: 1
+ easing.type: Easing.OutBounce
+ duration: 1000
+ }
+ }
+ }
+ running: false
+ }
+
+ SequentialAnimation {
+ id: renderThreadAnim
+ SequentialAnimation {
+ ParallelAnimation {
+ YAnimator {
+ target: smiley
+ from: smiley.minHeight
+ to: smiley.maxHeight
+ easing.type: Easing.OutExpo
+ duration: 300
+ }
+ ScaleAnimator {
+ target: shadow
+ from: 1
+ to: 0.5
+ easing.type: Easing.OutExpo
+ duration: 300
+ }
+ }
+ ParallelAnimation {
+ YAnimator {
+ target: smiley
+ from: smiley.maxHeight
+ to: smiley.minHeight
+ easing.type: Easing.OutBounce
+ duration: 1000
+ }
+ ScaleAnimator {
+ target: shadow
+ from: 0.5
+ to: 1
+ easing.type: Easing.OutBounce
+ duration: 1000
+ }
+ }
+ }
+ running: false
+ }
+
+ Image {
+ id: shadow
+ anchors.horizontalCenter: parent.horizontalCenter
+ y: smiley.minHeight + smiley.height
+ source: "qrc:/shadow.png"
+ }
+
+ Image {
+ id: smiley
+ property int maxHeight: window.height / 3
+ property int minHeight: 2 * window.height / 3
+
+ anchors.horizontalCenter: parent.horizontalCenter
+ y: minHeight
+ source: "qrc:/face-smile.png"
+ }
+
+ Text {
+ text: "click left for plain animation, right for render thread Animators, middle to sleep for 2 sec on the main (gui) thread"
+ color: "white"
+ }
+
+ Text {
+ text: plainAnim.running ? "NORMAL ANIMATION" : (renderThreadAnim.running ? "RENDER THREAD ANIMATION" : "NO ANIMATION")
+ color: "red"
+ font.pointSize: 20
+ anchors.bottom: parent.bottom
+ }
+
+ MouseArea {
+ anchors.fill: parent
+ acceptedButtons: Qt.AllButtons
+ onClicked: if (mouse.button === Qt.LeftButton) {
+ renderThreadAnim.running = false;
+ plainAnim.running = true;
+ } else if (mouse.button === Qt.RightButton) {
+ plainAnim.running = false;
+ renderThreadAnim.running = true;
+ } else if (mouse.button === Qt.MiddleButton) {
+ helper.sleep(2000);
+ }
+ }
+}
diff --git a/tests/manual/nodetypes/Effects.qml b/tests/manual/nodetypes/Effects.qml
new file mode 100644
index 0000000000..4503c49df1
--- /dev/null
+++ b/tests/manual/nodetypes/Effects.qml
@@ -0,0 +1,187 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+// Use QtQuick 2.8 to get shaderType and the other new properties
+import QtQuick 2.8
+
+Item {
+ Rectangle {
+ color: "gray"
+ anchors.margins: 10
+ anchors.fill: parent
+ Image {
+ id: image1
+ source: "qrc:/qt.png"
+ }
+ ShaderEffectSource {
+ id: effectSource1
+ sourceItem: image1
+ hideSource: true
+ }
+ ShaderEffect { // wobble
+ id: eff
+ width: image1.width
+ height: image1.height
+ anchors.centerIn: parent
+
+ property variant source: effectSource1
+ property real amplitude: 0.04 * 0.2
+ property real frequency: 20
+ property real time: 0
+
+ NumberAnimation on time { loops: Animation.Infinite; from: 0; to: Math.PI * 2; duration: 600 }
+
+ property bool customVertexShader: false // the effect is fine with the default vs, but toggle this to test
+
+ property string glslVertexShader:
+ "uniform highp mat4 qt_Matrix;" +
+ "attribute highp vec4 qt_Vertex;" +
+ "attribute highp vec2 qt_MultiTexCoord0;" +
+ "varying highp vec2 qt_TexCoord0;" +
+ "void main() {" +
+ " qt_TexCoord0 = qt_MultiTexCoord0;" +
+ " gl_Position = qt_Matrix * qt_Vertex;" +
+ "}"
+
+ property string glslFragmentShader:
+ "uniform sampler2D source;" +
+ "uniform highp float amplitude;" +
+ "uniform highp float frequency;" +
+ "uniform highp float time;" +
+ "uniform lowp float qt_Opacity;" +
+ "varying highp vec2 qt_TexCoord0;" +
+ "void main() {" +
+ " highp vec2 p = sin(time + frequency * qt_TexCoord0);" +
+ " gl_FragColor = texture2D(source, qt_TexCoord0 + amplitude * vec2(p.y, -p.x)) * qt_Opacity;" +
+ "}"
+
+ property string hlslVertexShaderByteCode: "qrc:/vs_wobble.cso"
+ property string hlslPixelShaderByteCode: "qrc:/ps_wobble.cso"
+
+ vertexShader: customVertexShader ? (shaderType === ShaderEffect.HLSL ? hlslVertexShaderByteCode : (shaderType === ShaderEffect.GLSL ? glslVertexShader : "")) : ""
+
+ fragmentShader: shaderType === ShaderEffect.HLSL ? hlslPixelShaderByteCode : (shaderType === ShaderEffect.GLSL ? glslFragmentShader : "")
+ }
+
+ Image {
+ id: image2
+ source: "qrc:/face-smile.png"
+ }
+ ShaderEffectSource {
+ id: effectSource2
+ sourceItem: image2
+ hideSource: true
+ }
+ ShaderEffect { // dropshadow
+ id: eff2
+ width: image2.width
+ height: image2.height
+ scale: 2
+ x: 40
+ y: 40
+
+ property variant source: effectSource2
+
+ property string glslShaderPass1: "
+ uniform lowp float qt_Opacity;
+ uniform sampler2D source;
+ uniform highp vec2 delta;
+ varying highp vec2 qt_TexCoord0;
+ void main() {
+ gl_FragColor = (0.0538 * texture2D(source, qt_TexCoord0 - 3.182 * delta)
+ + 0.3229 * texture2D(source, qt_TexCoord0 - 1.364 * delta)
+ + 0.2466 * texture2D(source, qt_TexCoord0)
+ + 0.3229 * texture2D(source, qt_TexCoord0 + 1.364 * delta)
+ + 0.0538 * texture2D(source, qt_TexCoord0 + 3.182 * delta)) * qt_Opacity;
+ }"
+ property string glslShaderPass2: "
+ uniform lowp float qt_Opacity;
+ uniform highp vec2 offset;
+ uniform sampler2D source;
+ uniform sampler2D shadow;
+ uniform highp float darkness;
+ uniform highp vec2 delta;
+ varying highp vec2 qt_TexCoord0;
+ void main() {
+ lowp vec4 fg = texture2D(source, qt_TexCoord0);
+ lowp vec4 bg = texture2D(shadow, qt_TexCoord0 + delta);
+ gl_FragColor = (fg + vec4(0., 0., 0., darkness * bg.a) * (1. - fg.a)) * qt_Opacity;
+ }"
+
+ property variant shadow: ShaderEffectSource {
+ sourceItem: ShaderEffect {
+ width: eff2.width
+ height: eff2.height
+ property variant delta: Qt.size(0.0, 1.0 / height)
+ property variant source: ShaderEffectSource {
+ sourceItem: ShaderEffect {
+ id: innerEff
+ width: eff2.width
+ height: eff2.height
+ property variant delta: Qt.size(1.0 / width, 0.0)
+ property variant source: effectSource2
+ fragmentShader: shaderType === ShaderEffect.HLSL ? "qrc:/ps_shadow1.cso" : (shaderType === ShaderEffect.GLSL ? eff2.glslShaderPass1 : "")
+ }
+ }
+ fragmentShader: shaderType === ShaderEffect.HLSL ? "qrc:/ps_shadow1.cso" : (shaderType === ShaderEffect.GLSL ? eff2.glslShaderPass1: "")
+ }
+ }
+ property real angle: 0
+ property variant offset: Qt.point(5.0 * Math.cos(angle), 5.0 * Math.sin(angle))
+ NumberAnimation on angle { loops: Animation.Infinite; from: 0; to: Math.PI * 2; duration: 6000 }
+ property variant delta: Qt.size(offset.x / width, offset.y / height)
+ property real darkness: 0.5
+ fragmentShader: shaderType === ShaderEffect.HLSL ? "qrc:/ps_shadow2.cso" : (shaderType === ShaderEffect.GLSL ? glslShaderPass2 : "")
+ }
+
+ Column {
+ anchors.bottom: parent.bottom
+ Text {
+ color: "yellow"
+ font.pointSize: 24
+ text: "Shader effect is " + (eff.shaderType === ShaderEffect.HLSL ? "HLSL" : (eff.shaderType === ShaderEffect.GLSL ? "GLSL" : "UNKNOWN")) + " based";
+ }
+ Text {
+ // check the inner shader effect's properties as those only get updated later on, once a window gets associated
+ text: innerEff.shaderType + " " + innerEff.shaderCompilationType + " " + innerEff.shaderSourceType
+ }
+ }
+ }
+}
diff --git a/tests/manual/nodetypes/Images.qml b/tests/manual/nodetypes/Images.qml
new file mode 100644
index 0000000000..7c1ba5345b
--- /dev/null
+++ b/tests/manual/nodetypes/Images.qml
@@ -0,0 +1,97 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.3
+
+Item {
+ Rectangle {
+ width: 100
+ height: 100
+ anchors.centerIn: parent
+ color: "red"
+ NumberAnimation on rotation { from: 0; to: 360; duration: 2000; loops: Animation.Infinite; }
+ }
+
+ Image {
+ id: im
+ source: "qrc:/qt.png"
+ mipmap: true
+
+ // changing the mipmap property results in the creation of a brand new
+ // texture resource. enable the following to test.
+// Timer {
+// interval: 2000
+// onTriggered: im.mipmap = false
+// running: true
+// }
+
+ SequentialAnimation on scale {
+ loops: Animation.Infinite
+ NumberAnimation {
+ from: 1.0
+ to: 4.0
+ duration: 2000
+ }
+ NumberAnimation {
+ from: 4.0
+ to: 0.1
+ duration: 3000
+ }
+ NumberAnimation {
+ from: 0.1
+ to: 1.0
+ duration: 1000
+ }
+ }
+
+ Image {
+ anchors.centerIn: parent
+ source: "qrc:/face-smile.png"
+ }
+ }
+
+ Image {
+ source: "qrc:/face-smile.png"
+ anchors.bottom: parent.bottom
+ anchors.right: parent.right
+ antialiasing: true
+ NumberAnimation on rotation { from: 0; to: 360; duration: 2000; loops: Animation.Infinite; }
+ }
+}
diff --git a/tests/manual/nodetypes/Layers.qml b/tests/manual/nodetypes/Layers.qml
new file mode 100644
index 0000000000..52c8fa8144
--- /dev/null
+++ b/tests/manual/nodetypes/Layers.qml
@@ -0,0 +1,106 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.0
+
+Item {
+ Rectangle {
+ color: "lightGray"
+ anchors.fill: parent
+ anchors.margins: 10
+
+ Row {
+ anchors.fill: parent
+ anchors.margins: 10
+ Rectangle {
+ color: "red"
+// ColorAnimation on color {
+// from: "black"
+// to: "white"
+// duration: 2000
+// loops: Animation.Infinite
+// }
+ width: 300
+ height: 100
+ layer.enabled: true
+ Text { text: "this is in a layer, going through an offscreen render target" }
+ clip: true
+ Rectangle {
+ color: "lightGreen"
+ width: 50
+ height: 50
+ x: 275
+ y: 75
+ }
+ }
+ Rectangle {
+ color: "white"
+ width: 300
+ height: 100
+ Text { text: "this is not a layer" }
+ }
+ Rectangle {
+ color: "green"
+ width: 300
+ height: 100
+ layer.enabled: true
+ Text { text: "this is another layer" }
+ Rectangle {
+ border.width: 4
+ border.color: "black"
+ anchors.centerIn: parent
+ width: 150
+ height: 50
+ layer.enabled: true
+ Text {
+ anchors.centerIn: parent
+ text: "layer in a layer"
+ }
+ }
+ Image {
+ source: "qrc:/face-smile.png"
+ anchors.bottom: parent.bottom
+ anchors.right: parent.right
+ NumberAnimation on rotation { from: 0; to: 360; duration: 2000; loops: Animation.Infinite; }
+ }
+ }
+ }
+ }
+}
diff --git a/tests/manual/nodetypes/LotsOfRects.qml b/tests/manual/nodetypes/LotsOfRects.qml
new file mode 100644
index 0000000000..46a05a2453
--- /dev/null
+++ b/tests/manual/nodetypes/LotsOfRects.qml
@@ -0,0 +1,250 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.0
+
+Item {
+ Rectangle {
+ anchors.margins: 4
+ anchors.fill: parent
+
+ // Background
+ gradient: Gradient {
+ GradientStop { position: 0; color: "steelblue" }
+ GradientStop { position: 1; color: "black" }
+ }
+
+ // Animated gradient stops.
+ // NB! Causes a full buffer rebuild on every animated change due to the geometry change!
+ Row {
+ spacing: 10
+ Repeater {
+ model: 20
+ Rectangle {
+ width: 20
+ height: 20
+ gradient: Gradient {
+ GradientStop { position: 0.0; color: "red" }
+ GradientStop { NumberAnimation on position { from: 0.01; to: 0.99; duration: 5000; loops: Animation.Infinite } color: "yellow" }
+ GradientStop { position: 1.0; color: "green" }
+ }
+ }
+ }
+ }
+
+ // Rounded rects with border (smooth material)
+ Row {
+ spacing: 10
+ Repeater {
+ model: 5
+ Rectangle {
+ color: "blue"
+ width: 100
+ height: 50
+ y: 50
+ radius: 16
+ border.color: "red"
+ border.width: 4
+
+ SequentialAnimation on y {
+ loops: Animation.Infinite
+ NumberAnimation {
+ from: 50
+ to: 150
+ duration: 7000
+ }
+ NumberAnimation {
+ from: 150
+ to: 50
+ duration: 3000
+ }
+ }
+ }
+ }
+ }
+
+ // Clip using scissor
+ Row {
+ spacing: 10
+ Repeater {
+ model: 5
+ Rectangle {
+ color: "green"
+ width: 100
+ height: 100
+ y: 150
+ NumberAnimation on y {
+ from: 150
+ to: 200
+ duration: 2000
+ loops: Animation.Infinite
+ }
+ clip: true
+ Rectangle {
+ color: "lightGreen"
+ width: 50
+ height: 50
+ x: 75
+ y: 75
+ }
+ }
+ }
+ }
+
+ // Clip using scissor
+ Row {
+ spacing: 10
+ Repeater {
+ model: 5
+ Rectangle {
+ color: "green"
+ width: 100
+ height: 100
+ y: 300
+ NumberAnimation on y {
+ from: 300
+ to: 400
+ duration: 2000
+ loops: Animation.Infinite
+ }
+ clip: true
+ Rectangle {
+ color: "lightGreen"
+ width: 50
+ height: 50
+ x: 75
+ y: 75
+ NumberAnimation on rotation { from: 0; to: 360; duration: 2000; loops: Animation.Infinite; }
+ }
+ }
+ }
+ }
+
+ // Clip using stencil
+ Row {
+ spacing: 10
+ Repeater {
+ model: 5
+ Rectangle {
+ color: "green"
+ width: 100
+ height: 100
+ y: 450
+ NumberAnimation on y {
+ from: 450
+ to: 550
+ duration: 2000
+ loops: Animation.Infinite
+ }
+ NumberAnimation on rotation { from: 0; to: 360; duration: 2000; loops: Animation.Infinite; }
+ clip: true
+ Rectangle {
+ color: "lightGreen"
+ width: 50
+ height: 50
+ x: 75
+ y: 75
+ }
+ }
+ }
+ }
+
+ // The signature red square with another item with animated opacity blended on top
+ Rectangle {
+ width: 100
+ height: 100
+ anchors.centerIn: parent
+ color: "red"
+ NumberAnimation on rotation { from: 0; to: 360; duration: 2000; loops: Animation.Infinite; }
+
+ Rectangle {
+ color: "gray"
+ width: 50
+ height: 50
+ anchors.centerIn: parent
+
+ SequentialAnimation on opacity {
+ loops: Animation.Infinite
+ NumberAnimation {
+ from: 1.0
+ to: 0.0
+ duration: 4000
+ }
+ NumberAnimation {
+ from: 0.0
+ to: 1.0
+ duration: 4000
+ easing.type: Easing.InOutQuad
+ }
+ }
+ }
+ }
+
+ // Animated size and color.
+ // NB! Causes a full buffer rebuild on every animated change due to the geometry change!
+ Rectangle {
+ anchors.right: parent.right
+ anchors.bottom: parent.bottom
+ width: 10
+ height: 100
+ ColorAnimation on color {
+ from: "blue"
+ to: "purple"
+ duration: 5000
+ loops: Animation.Infinite
+ }
+ NumberAnimation on width {
+ from: 10
+ to: 300
+ duration: 5000
+ loops: Animation.Infinite
+ }
+ }
+
+ // Semi-transparent rect on top.
+ Rectangle {
+ anchors.centerIn: parent
+ opacity: 0.2
+ color: "black"
+ anchors.fill: parent
+ anchors.margins: 10
+ }
+ }
+}
diff --git a/tests/manual/nodetypes/Painter.qml b/tests/manual/nodetypes/Painter.qml
new file mode 100644
index 0000000000..a5973379f4
--- /dev/null
+++ b/tests/manual/nodetypes/Painter.qml
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.0
+import Stuff 1.0
+
+Item {
+ ListModel {
+ id: balloonModel
+ ListElement {
+ balloonWidth: 200
+ }
+ ListElement {
+ balloonWidth: 120
+ }
+ ListElement {
+ balloonWidth: 120
+ }
+ ListElement {
+ balloonWidth: 120
+ }
+ ListElement {
+ balloonWidth: 120
+ }
+ }
+
+ ListView {
+ anchors.fill: parent
+ anchors.margins: 10
+ id: balloonView
+ model: balloonModel
+ spacing: 5
+ delegate: TextBalloon {
+ anchors.right: index % 2 == 0 ? undefined : parent.right
+ height: 60
+ rightAligned: index % 2 == 0 ? false : true
+ width: balloonWidth
+ innerAnim: model.index === 1
+ NumberAnimation on width {
+ from: 200
+ to: 300
+ duration: 5000
+ running: model.index === 0
+ }
+ }
+ }
+}
diff --git a/tests/manual/nodetypes/Rects.qml b/tests/manual/nodetypes/Rects.qml
new file mode 100644
index 0000000000..0d3a8cd459
--- /dev/null
+++ b/tests/manual/nodetypes/Rects.qml
@@ -0,0 +1,137 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.0
+
+Item {
+ Rectangle {
+ width: 100
+ height: 100
+ anchors.centerIn: parent
+ color: "red"
+ NumberAnimation on rotation { from: 0; to: 360; duration: 2000; loops: Animation.Infinite; }
+
+ Rectangle {
+ color: "gray"
+ width: 50
+ height: 50
+ anchors.centerIn: parent
+
+ SequentialAnimation on opacity {
+ loops: Animation.Infinite
+ NumberAnimation {
+ from: 1.0
+ to: 0.0
+ duration: 4000
+ }
+ NumberAnimation {
+ from: 0.0
+ to: 1.0
+ duration: 4000
+ easing.type: Easing.InOutQuad
+ }
+ }
+ }
+ }
+
+ Rectangle {
+ color: "green"
+ width: 100
+ height: 200
+ x: 0
+ y: 0
+
+ NumberAnimation on x {
+ from: 0
+ to: 300
+ duration: 5000
+ }
+ NumberAnimation on y {
+ from: 0
+ to: 50
+ duration: 2000
+ }
+
+ clip: true
+ Rectangle {
+ color: "lightGreen"
+ width: 50
+ height: 50
+ x: 75
+ y: 175
+ }
+ }
+
+ Rectangle {
+ color: "blue"
+ width: 200
+ height: 100
+ x: 100
+ y: 300
+ radius: 16
+ border.color: "red"
+ border.width: 4
+
+ SequentialAnimation on y {
+ loops: Animation.Infinite
+ NumberAnimation {
+ from: 300
+ to: 500
+ duration: 7000
+ }
+ NumberAnimation {
+ from: 500
+ to: 300
+ duration: 3000
+ }
+ }
+ }
+
+ Rectangle {
+ anchors.right: parent.right
+ width: 100
+ height: 100
+ gradient: Gradient {
+ GradientStop { position: 0.0; color: "red" }
+ GradientStop { position: 0.33; color: "yellow" }
+ GradientStop { position: 1.0; color: "green" }
+ }
+ }
+}
diff --git a/tests/manual/nodetypes/Text.qml b/tests/manual/nodetypes/Text.qml
new file mode 100644
index 0000000000..fb0c92cb10
--- /dev/null
+++ b/tests/manual/nodetypes/Text.qml
@@ -0,0 +1,71 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.0
+
+Item {
+ Text {
+ id: text1
+ anchors.top: parent.top
+ text: "árvíztűrő tükörfúrógép\nÁRVÍZTŰRŐ TÜKÖRFÚRÓGÉP"
+ }
+ Text {
+ anchors.bottom: parent.bottom
+ text: "the quick brown fox jumps over the lazy dog\nTHE QUICK BROWN FOX JUMPS OVER THE LAZY DOG"
+ color: "red"
+ }
+ Text {
+ anchors.centerIn: parent
+ text: "rotate rotate rotate"
+ font.bold: true
+ font.pointSize: 20
+ color: "green"
+ NumberAnimation on rotation { from: 0; to: 360; duration: 2000; loops: Animation.Infinite; }
+ }
+
+ Row {
+ anchors.top: text1.bottom
+ anchors.margins: 10
+ Text { font.pointSize: 24; text: "Normal" }
+ Text { font.pointSize: 24; text: "Raised"; style: Text.Raised; styleColor: "#AAAAAA" }
+ Text { font.pointSize: 24; text: "Outline"; style: Text.Outline; styleColor: "red" }
+ Text { font.pointSize: 24; text: "Sunken"; style: Text.Sunken; styleColor: "#AAAAAA" }
+ }
+}
diff --git a/tests/manual/nodetypes/face-smile.png b/tests/manual/nodetypes/face-smile.png
new file mode 100644
index 0000000000..3d66d72578
--- /dev/null
+++ b/tests/manual/nodetypes/face-smile.png
Binary files differ
diff --git a/tests/manual/nodetypes/hlslcompile.bat b/tests/manual/nodetypes/hlslcompile.bat
new file mode 100644
index 0000000000..b24824e324
--- /dev/null
+++ b/tests/manual/nodetypes/hlslcompile.bat
@@ -0,0 +1,4 @@
+fxc /E VS_Wobble /T vs_5_0 /Fo vs_wobble.cso wobble.hlsl
+fxc /E PS_Wobble /T ps_5_0 /Fo ps_wobble.cso wobble.hlsl
+fxc /E PS_Shadow1 /T ps_5_0 /Fo ps_shadow1.cso shadow1.hlsl
+fxc /E PS_Shadow2 /T ps_5_0 /Fo ps_shadow2.cso shadow2.hlsl
diff --git a/tests/manual/nodetypes/main.qml b/tests/manual/nodetypes/main.qml
new file mode 100644
index 0000000000..2ec2d37f14
--- /dev/null
+++ b/tests/manual/nodetypes/main.qml
@@ -0,0 +1,81 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.0
+
+Item {
+ focus: true
+
+ Loader {
+ anchors.fill: parent
+ id: loader
+ }
+
+ Keys.onPressed: {
+ if (event.key === Qt.Key_S)
+ loader.source = "";
+
+ if (event.key === Qt.Key_R)
+ loader.source = "qrc:/Rects.qml";
+ if (event.key === Qt.Key_4)
+ loader.source = "qrc:/LotsOfRects.qml";
+
+ if (event.key === Qt.Key_I)
+ loader.source = "qrc:/Images.qml";
+
+ if (event.key === Qt.Key_T)
+ loader.source = "qrc:/Text.qml";
+
+ if (event.key === Qt.Key_A)
+ loader.source = "qrc:/Animators.qml";
+
+ if (event.key === Qt.Key_L)
+ loader.source = "qrc:/Layers.qml";
+
+ if (event.key === Qt.Key_E)
+ loader.source = "qrc:/Effects.qml";
+
+ if (event.key === Qt.Key_P)
+ loader.source = "qrc:/Painter.qml";
+
+ if (event.key === Qt.Key_G)
+ helper.testGrab()
+ }
+}
diff --git a/tests/manual/nodetypes/nodetypes.cpp b/tests/manual/nodetypes/nodetypes.cpp
new file mode 100644
index 0000000000..8dc282894f
--- /dev/null
+++ b/tests/manual/nodetypes/nodetypes.cpp
@@ -0,0 +1,205 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QGuiApplication>
+#include <QThread>
+#include <QQuickView>
+#include <QQmlEngine>
+#include <QQmlContext>
+#include <QQuickPaintedItem>
+#include <QPainter>
+#include <QTimer>
+
+class TextBalloon : public QQuickPaintedItem
+{
+ Q_OBJECT
+ Q_PROPERTY(bool rightAligned READ isRightAligned WRITE setRightAligned NOTIFY rightAlignedChanged)
+ Q_PROPERTY(bool innerAnim READ innerAnimEnabled WRITE setInnerAnimEnabled NOTIFY innerAnimChanged)
+
+public:
+ TextBalloon(QQuickItem *parent = nullptr) : QQuickPaintedItem(parent) {
+ connect(&m_timer, &QTimer::timeout, this, &TextBalloon::onAnim);
+ m_timer.setInterval(500);
+ }
+ void paint(QPainter *painter);
+
+ bool isRightAligned() { return m_rightAligned; }
+ void setRightAligned(bool rightAligned);
+
+ bool innerAnimEnabled() const { return m_innerAnim; }
+ void setInnerAnimEnabled(bool b);
+
+signals:
+ void rightAlignedChanged();
+ void innerAnimChanged();
+
+private slots:
+ void onAnim();
+
+private:
+ bool m_rightAligned = false;
+ bool m_innerAnim = false;
+ QTimer m_timer;
+ QRect m_animRect = QRect(10, 10, 50, 20);
+ int m_anim = 0;
+};
+
+void TextBalloon::paint(QPainter *painter)
+{
+ QBrush brush(QColor("#007430"));
+
+ painter->setBrush(brush);
+ painter->setPen(Qt::NoPen);
+ painter->setRenderHint(QPainter::Antialiasing);
+
+ painter->drawRoundedRect(0, 0, boundingRect().width(), boundingRect().height() - 10, 10, 10);
+
+ if (m_rightAligned) {
+ const QPointF points[3] = {
+ QPointF(boundingRect().width() - 10.0, boundingRect().height() - 10.0),
+ QPointF(boundingRect().width() - 20.0, boundingRect().height()),
+ QPointF(boundingRect().width() - 30.0, boundingRect().height() - 10.0),
+ };
+ painter->drawConvexPolygon(points, 3);
+ } else {
+ const QPointF points[3] = {
+ QPointF(10.0, boundingRect().height() - 10.0),
+ QPointF(20.0, boundingRect().height()),
+ QPointF(30.0, boundingRect().height() - 10.0),
+ };
+ painter->drawConvexPolygon(points, 3);
+ }
+
+ if (m_innerAnim) {
+ painter->fillRect(m_animRect, Qt::lightGray);
+ const int x = m_animRect.x() + m_anim;
+ const int y = m_animRect.y() + m_animRect.height() / 2;
+ painter->setPen(QPen(QBrush(Qt::SolidLine), 4));
+ painter->drawLine(x + 4, y, x + 10, y);
+ m_anim += 10;
+ if (m_anim > m_animRect.width())
+ m_anim = 0;
+ }
+}
+
+void TextBalloon::setRightAligned(bool rightAligned)
+{
+ if (m_rightAligned == rightAligned)
+ return;
+
+ m_rightAligned = rightAligned;
+ emit rightAlignedChanged();
+}
+
+void TextBalloon::setInnerAnimEnabled(bool b)
+{
+ if (m_innerAnim == b)
+ return;
+
+ m_innerAnim = b;
+ if (!b)
+ m_timer.stop();
+ else
+ m_timer.start();
+ emit innerAnimChanged();
+}
+
+void TextBalloon::onAnim()
+{
+ update(m_animRect);
+}
+
+class Helper : public QObject
+{
+ Q_OBJECT
+
+public:
+ Helper(QQuickWindow *w) : m_window(w) { }
+
+ Q_INVOKABLE void sleep(int ms) {
+ QThread::msleep(ms);
+ }
+
+ Q_INVOKABLE void testGrab() {
+ QImage img = m_window->grabWindow();
+ qDebug() << "Saving image to grab_result.png" << img;
+ img.save("grab_result.png");
+ }
+
+ QQuickWindow *m_window;
+};
+
+int main(int argc, char **argv)
+{
+ qputenv("QT_QUICK_BACKEND", "d3d12");
+
+ QGuiApplication app(argc, argv);
+
+ qDebug("Available tests:");
+ qDebug(" [R] - Rectangles");
+ qDebug(" [4] - A lot of rectangles (perf)");
+ qDebug(" [I] - Images");
+ qDebug(" [T] - Text");
+ qDebug(" [A] - Render thread Animator");
+ qDebug(" [L] - Layers");
+ qDebug(" [E] - Effects");
+ qDebug(" [P] - QQuickPaintedItem");
+ qDebug(" [G] - Grab current window");
+ qDebug("\nPress S to stop the currently running test\n");
+
+ QQuickView view;
+ Helper helper(&view);
+ if (app.arguments().contains(QLatin1String("--multisample"))) {
+ qDebug("Requesting sample count 4");
+ QSurfaceFormat fmt;
+ fmt.setSamples(4);
+ view.setFormat(fmt);
+ }
+ view.engine()->rootContext()->setContextProperty(QLatin1String("helper"), &helper);
+ qmlRegisterType<TextBalloon>("Stuff", 1, 0, "TextBalloon");
+ view.setResizeMode(QQuickView::SizeRootObjectToView);
+ view.resize(1024, 768);
+ view.setSource(QUrl("qrc:/main.qml"));
+ view.show();
+
+ return app.exec();
+}
+
+#include "nodetypes.moc"
diff --git a/tests/manual/nodetypes/nodetypes.pro b/tests/manual/nodetypes/nodetypes.pro
new file mode 100644
index 0000000000..afb5a46a84
--- /dev/null
+++ b/tests/manual/nodetypes/nodetypes.pro
@@ -0,0 +1,9 @@
+QT += qml quick
+
+SOURCES += nodetypes.cpp
+
+RESOURCES += nodetypes.qrc
+
+OTHER_FILES += main.qml Rects.qml LotsOfRects.qml \
+ Images.qml Text.qml Animators.qml Layers.qml Effects.qml Painter.qml \
+ wobble.hlsl shadow1.hlsl shadow2.hlsl
diff --git a/tests/manual/nodetypes/nodetypes.qrc b/tests/manual/nodetypes/nodetypes.qrc
new file mode 100644
index 0000000000..56fa2b6ee4
--- /dev/null
+++ b/tests/manual/nodetypes/nodetypes.qrc
@@ -0,0 +1,20 @@
+<RCC>
+ <qresource prefix="/">
+ <file>main.qml</file>
+ <file>Rects.qml</file>
+ <file>LotsOfRects.qml</file>
+ <file>Images.qml</file>
+ <file>Text.qml</file>
+ <file>Animators.qml</file>
+ <file>Layers.qml</file>
+ <file>Effects.qml</file>
+ <file>Painter.qml</file>
+ <file>qt.png</file>
+ <file>face-smile.png</file>
+ <file>shadow.png</file>
+ <file>vs_wobble.cso</file>
+ <file>ps_wobble.cso</file>
+ <file>ps_shadow1.cso</file>
+ <file>ps_shadow2.cso</file>
+ </qresource>
+</RCC>
diff --git a/tests/manual/nodetypes/ps_shadow1.cso b/tests/manual/nodetypes/ps_shadow1.cso
new file mode 100644
index 0000000000..b6fbe3f3c2
--- /dev/null
+++ b/tests/manual/nodetypes/ps_shadow1.cso
Binary files differ
diff --git a/tests/manual/nodetypes/ps_shadow2.cso b/tests/manual/nodetypes/ps_shadow2.cso
new file mode 100644
index 0000000000..ab8cb63f34
--- /dev/null
+++ b/tests/manual/nodetypes/ps_shadow2.cso
Binary files differ
diff --git a/tests/manual/nodetypes/ps_wobble.cso b/tests/manual/nodetypes/ps_wobble.cso
new file mode 100644
index 0000000000..4e5b6a27f4
--- /dev/null
+++ b/tests/manual/nodetypes/ps_wobble.cso
Binary files differ
diff --git a/tests/manual/nodetypes/qt.png b/tests/manual/nodetypes/qt.png
new file mode 100644
index 0000000000..f30eec0d4d
--- /dev/null
+++ b/tests/manual/nodetypes/qt.png
Binary files differ
diff --git a/tests/manual/nodetypes/shadow.png b/tests/manual/nodetypes/shadow.png
new file mode 100644
index 0000000000..8270565e87
--- /dev/null
+++ b/tests/manual/nodetypes/shadow.png
Binary files differ
diff --git a/tests/manual/nodetypes/shadow1.hlsl b/tests/manual/nodetypes/shadow1.hlsl
new file mode 100644
index 0000000000..ff3f4b6fd5
--- /dev/null
+++ b/tests/manual/nodetypes/shadow1.hlsl
@@ -0,0 +1,18 @@
+cbuffer ConstantBuffer : register(b0)
+{
+ float4x4 qt_Matrix;
+ float qt_Opacity;
+ float2 delta;
+};
+
+Texture2D source : register(t0);
+SamplerState sourceSampler : register(s0);
+
+float4 PS_Shadow1(float4 position : SV_POSITION, float2 coord : TEXCOORD0) : SV_TARGET
+{
+ return (0.0538 * source.Sample(sourceSampler, coord - 3.182 * delta)
+ + 0.3229 * source.Sample(sourceSampler, coord - 1.364 * delta)
+ + 0.2466 * source.Sample(sourceSampler, coord)
+ + 0.3229 * source.Sample(sourceSampler, coord + 1.364 * delta)
+ + 0.0538 * source.Sample(sourceSampler, coord + 3.182 * delta)) * qt_Opacity;
+}
diff --git a/tests/manual/nodetypes/shadow2.hlsl b/tests/manual/nodetypes/shadow2.hlsl
new file mode 100644
index 0000000000..eaa30cd988
--- /dev/null
+++ b/tests/manual/nodetypes/shadow2.hlsl
@@ -0,0 +1,22 @@
+cbuffer ConstantBuffer : register(b0)
+{
+ float4x4 qt_Matrix;
+ float qt_Opacity;
+ float2 offset;
+ float darkness;
+ float2 delta;
+};
+
+Texture2D source : register(t0);
+Texture2D shadow : register(t1);
+SamplerState samp : register(s0);
+// Use the same sampler for both textures. In fact the engine will create an extra static sampler
+// in any case (to match the number of textures) due to some internals, but that won't hurt, the
+// shader works either way.
+
+float4 PS_Shadow2(float4 position : SV_POSITION, float2 coord : TEXCOORD0) : SV_TARGET
+{
+ float4 fg = source.Sample(samp, coord);
+ float4 bg = shadow.Sample(samp, coord + delta);
+ return (fg + float4(0.0, 0.0, 0.0, darkness * bg.a) * (1.0 - fg.a)) * qt_Opacity;
+}
diff --git a/tests/manual/nodetypes/vs_wobble.cso b/tests/manual/nodetypes/vs_wobble.cso
new file mode 100644
index 0000000000..f3a2596457
--- /dev/null
+++ b/tests/manual/nodetypes/vs_wobble.cso
Binary files differ
diff --git a/tests/manual/nodetypes/wobble.hlsl b/tests/manual/nodetypes/wobble.hlsl
new file mode 100644
index 0000000000..203dbda7f2
--- /dev/null
+++ b/tests/manual/nodetypes/wobble.hlsl
@@ -0,0 +1,32 @@
+cbuffer ConstantBuffer : register(b0)
+{
+ float4x4 qt_Matrix;
+ float qt_Opacity;
+
+ float amplitude;
+ float frequency;
+ float time;
+};
+
+struct PSInput
+{
+ float4 position : SV_POSITION;
+ float2 coord : TEXCOORD0;
+};
+
+PSInput VS_Wobble(float4 position : POSITION, float2 coord : TEXCOORD0)
+{
+ PSInput result;
+ result.position = mul(qt_Matrix, position);
+ result.coord = coord;
+ return result;
+}
+
+Texture2D source : register(t0);
+SamplerState sourceSampler : register(s0);
+
+float4 PS_Wobble(PSInput input) : SV_TARGET
+{
+ float2 p = sin(time + frequency * input.coord);
+ return source.Sample(sourceSampler, input.coord + amplitude * float2(p.y, -p.x)) * qt_Opacity;
+}
diff --git a/tools/qmleasing/mainwindow.cpp b/tools/qmleasing/mainwindow.cpp
index e45feea76e..5a5f651396 100644
--- a/tools/qmleasing/mainwindow.cpp
+++ b/tools/qmleasing/mainwindow.cpp
@@ -29,7 +29,6 @@
#include "mainwindow.h"
#include "splineeditor.h"
#include <QtQuick/QQuickView>
-#include <QtQuick>
#include <QtQml/QQmlContext>
#include <QEasingCurve>
#include <QHBoxLayout>
diff --git a/tools/qmlscene/main.cpp b/tools/qmlscene/main.cpp
index c892f60680..1185a8e7ae 100644
--- a/tools/qmlscene/main.cpp
+++ b/tools/qmlscene/main.cpp
@@ -361,7 +361,7 @@ static void usage()
puts(" ");
exit(1);
}
-
+#ifndef QT_NO_OPENGL
// Listen on GL context creation of the QQuickWindow in order to print diagnostic output.
class DiagnosticGlContextCreationListener : public QObject {
Q_OBJECT
@@ -389,7 +389,9 @@ private slots:
context->doneCurrent();
deleteLater();
}
+
};
+#endif
static void setWindowTitle(bool verbose, const QObject *topLevel, QWindow *window)
{
@@ -403,8 +405,10 @@ static void setWindowTitle(bool verbose, const QObject *topLevel, QWindow *windo
if (verbose) {
newTitle += QLatin1String(" [Qt ") + QLatin1String(QT_VERSION_STR) + QLatin1Char(' ')
+ QGuiApplication::platformName() + QLatin1Char(' ');
+#ifndef QT_NO_OPENGL
newTitle += QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL
? QLatin1String("GL") : QLatin1String("GLES");
+#endif
newTitle += QLatin1Char(']');
}
if (oldTitle != newTitle)
@@ -592,8 +596,10 @@ int main(int argc, char ** argv)
if (window) {
setWindowTitle(options.verbose, topLevel, window.data());
+#ifndef QT_NO_OPENGL
if (options.verbose)
new DiagnosticGlContextCreationListener(window.data());
+#endif
QSurfaceFormat surfaceFormat = window->requestedFormat();
if (options.multisample)
surfaceFormat.setSamples(16);