aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore6
-rw-r--r--.qmake.conf2
-rwxr-xr-xbin/rename-qtdeclarative-symbols.sh642
-rw-r--r--config.tests/d3d12/d3d12.cpp (renamed from src/3rdparty/masm/stubs/compat/stdint.h)24
-rw-r--r--config.tests/d3d12/d3d12.pro4
-rw-r--r--configure.json6
-rw-r--r--examples/qml/qml.pro13
-rw-r--r--examples/quick/demos/samegame/samegame.pro2
-rw-r--r--examples/quick/quick.pro11
-rw-r--r--examples/quick/quickwidgets/quickwidget/customgl.qml59
-rw-r--r--examples/quick/quickwidgets/quickwidget/fbitem.cpp90
-rw-r--r--examples/quick/quickwidgets/quickwidget/fbitem.h53
-rw-r--r--examples/quick/quickwidgets/quickwidget/main.cpp36
-rw-r--r--examples/quick/quickwidgets/quickwidget/quickwidget.pro3
-rw-r--r--examples/quick/quickwidgets/quickwidget/quickwidget.qrc2
-rw-r--r--examples/quick/quickwidgets/quickwidget/rotatingsquaretab.qml66
-rw-r--r--examples/quick/scenegraph/customgeometry/beziercurve.cpp2
-rw-r--r--examples/quick/scenegraph/rendernode/customrenderitem.cpp84
-rw-r--r--examples/quick/scenegraph/rendernode/customrenderitem.h55
-rw-r--r--examples/quick/scenegraph/rendernode/d3d12renderer.cpp283
-rw-r--r--examples/quick/scenegraph/rendernode/d3d12renderer.h82
-rw-r--r--examples/quick/scenegraph/rendernode/main.cpp65
-rw-r--r--examples/quick/scenegraph/rendernode/main.qml100
-rw-r--r--examples/quick/scenegraph/rendernode/openglrenderer.cpp164
-rw-r--r--examples/quick/scenegraph/rendernode/openglrenderer.h80
-rw-r--r--examples/quick/scenegraph/rendernode/rendernode.pro38
-rw-r--r--examples/quick/scenegraph/rendernode/rendernode.qrc5
-rw-r--r--examples/quick/scenegraph/rendernode/shader.hlsl28
-rw-r--r--examples/quick/scenegraph/rendernode/softwarerenderer.cpp101
-rw-r--r--examples/quick/scenegraph/rendernode/softwarerenderer.h63
-rw-r--r--examples/quick/scenegraph/scenegraph.pro23
-rw-r--r--examples/quick/shadereffects/content/shaders/+hlsl/blur.frag18
-rw-r--r--examples/quick/shadereffects/content/shaders/+hlsl/colorize.frag17
-rw-r--r--examples/quick/shadereffects/content/shaders/+hlsl/genie.vert31
-rw-r--r--examples/quick/shadereffects/content/shaders/+hlsl/outline.frag21
-rw-r--r--examples/quick/shadereffects/content/shaders/+hlsl/shadow.frag20
-rw-r--r--examples/quick/shadereffects/content/shaders/+hlsl/wobble.frag17
-rw-r--r--examples/quick/shadereffects/content/shaders/blur.frag14
-rw-r--r--examples/quick/shadereffects/content/shaders/colorize.frag12
-rw-r--r--examples/quick/shadereffects/content/shaders/genie.vert21
-rw-r--r--examples/quick/shadereffects/content/shaders/outline.frag16
-rw-r--r--examples/quick/shadereffects/content/shaders/shadow.frag14
-rw-r--r--examples/quick/shadereffects/content/shaders/wobble.frag13
-rw-r--r--examples/quick/shadereffects/doc/src/shadereffects.qdoc15
-rw-r--r--examples/quick/shadereffects/shadereffects.qml102
-rw-r--r--examples/quick/shadereffects/shadereffects.qrc12
-rw-r--r--features/hlsl_bytecode_header.prf10
-rw-r--r--src/3rdparty/masm/LICENSE22
-rw-r--r--src/3rdparty/masm/masm-defs.pri5
-rw-r--r--src/3rdparty/masm/qt_attribution.json23
-rw-r--r--src/3rdparty/masm/wtf/StdLibExtras.h2
-rw-r--r--src/imports/builtins/builtins.qmltypes25
-rw-r--r--src/imports/folderlistmodel/fileinfothread.cpp5
-rw-r--r--src/imports/folderlistmodel/plugin.cpp2
-rw-r--r--src/imports/folderlistmodel/plugins.qmltypes275
-rw-r--r--src/imports/imports.pro4
-rw-r--r--src/imports/layouts/plugin.cpp2
-rw-r--r--src/imports/layouts/plugins.qmltypes4
-rw-r--r--src/imports/layouts/qquicklayout.cpp17
-rw-r--r--src/imports/layouts/qquicklinearlayout.cpp16
-rw-r--r--src/imports/localstorage/plugin.cpp71
-rw-r--r--src/imports/localstorage/plugins.qmltypes2
-rw-r--r--src/imports/models/plugin.cpp2
-rw-r--r--src/imports/models/plugins.qmltypes466
-rw-r--r--src/imports/particles/plugin.cpp2
-rw-r--r--src/imports/particles/plugins.qmltypes206
-rw-r--r--src/imports/qtqml/plugins.qmltypes21
-rw-r--r--src/imports/qtquick2/plugin.cpp2
-rw-r--r--src/imports/qtquick2/plugins.qmltypes194
-rw-r--r--src/imports/settings/plugin.cpp2
-rw-r--r--src/imports/settings/plugins.qmltypes2
-rw-r--r--src/imports/statemachine/plugin.cpp2
-rw-r--r--src/imports/statemachine/plugins.qmltypes2
-rw-r--r--src/imports/statemachine/signaltransition.cpp10
-rw-r--r--src/imports/statemachine/signaltransition.h5
-rw-r--r--src/imports/statemachine/statemachine.h2
-rw-r--r--src/imports/testlib/TestCase.qml72
-rw-r--r--src/imports/testlib/main.cpp6
-rw-r--r--src/imports/testlib/plugins.qmltypes5
-rw-r--r--src/imports/window/plugin.cpp2
-rw-r--r--src/imports/window/plugins.qmltypes16
-rw-r--r--src/imports/xmllistmodel/plugin.cpp2
-rw-r--r--src/imports/xmllistmodel/plugins.qmltypes267
-rw-r--r--src/particles/qquickcustomparticle.cpp42
-rw-r--r--src/particles/qquickcustomparticle_p.h22
-rw-r--r--src/particles/qquickimageparticle.cpp10
-rw-r--r--src/particles/qquickparticleemitter_p.h2
-rw-r--r--src/particles/qquickparticlepainter.cpp3
-rw-r--r--src/particles/qquickv4particledata.cpp7
-rw-r--r--src/plugins/plugins.pro2
-rw-r--r--src/plugins/qmltooling/packetprotocol/qpacketprotocol.cpp18
-rw-r--r--src/plugins/qmltooling/packetprotocol/qpacketprotocol_p.h2
-rw-r--r--src/plugins/qmltooling/qmldbg_debugger/qdebugmessageservice.cpp2
-rw-r--r--src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp29
-rw-r--r--src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.h10
-rw-r--r--src/plugins/qmltooling/qmldbg_debugger/qqmlnativedebugservice.cpp75
-rw-r--r--src/plugins/qmltooling/qmldbg_debugger/qqmlwatcher.cpp5
-rw-r--r--src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp2
-rw-r--r--src/plugins/qmltooling/qmldbg_debugger/qv4debugger.cpp4
-rw-r--r--src/plugins/qmltooling/qmldbg_debugger/qv4debugger.h5
-rw-r--r--src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.cpp2
-rw-r--r--src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.h1
-rw-r--r--src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp88
-rw-r--r--src/plugins/qmltooling/qmldbg_inspector/globalinspector.cpp9
-rw-r--r--src/plugins/qmltooling/qmldbg_inspector/globalinspector.h4
-rw-r--r--src/plugins/qmltooling/qmldbg_inspector/highlight.cpp26
-rw-r--r--src/plugins/qmltooling/qmldbg_inspector/highlight.h7
-rw-r--r--src/plugins/qmltooling/qmldbg_inspector/qqmlinspectorservice.cpp14
-rw-r--r--src/plugins/qmltooling/qmldbg_local/qlocalclientconnection.cpp7
-rw-r--r--src/plugins/qmltooling/qmldbg_native/qqmlnativedebugconnector.h4
-rw-r--r--src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.cpp95
-rw-r--r--src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.h4
-rw-r--r--src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.cpp25
-rw-r--r--src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.h5
-rw-r--r--src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.cpp79
-rw-r--r--src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.h16
-rw-r--r--src/plugins/qmltooling/qmldbg_quickprofiler/qquickprofileradapter.cpp32
-rw-r--r--src/plugins/qmltooling/qmldbg_quickprofiler/qquickprofileradapter.h4
-rw-r--r--src/plugins/qmltooling/qmldbg_server/qqmldebugserver.cpp76
-rw-r--r--src/plugins/qmltooling/qmldbg_tcp/qtcpserverconnection.cpp7
-rw-r--r--src/plugins/qmltooling/qmltooling.pro7
-rw-r--r--src/plugins/qmltooling/shared/qqmldebugserver.h2
-rw-r--r--src/plugins/scenegraph/d3d12/d3d12.json3
-rw-r--r--src/plugins/scenegraph/d3d12/d3d12.pro61
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12adaptation.cpp87
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12adaptation_p.h81
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12builtinmaterials.cpp737
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12builtinmaterials_p.h253
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12context.cpp142
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12context_p.h84
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp3251
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12engine_p.h394
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12engine_p_p.h454
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12glyphcache.cpp197
-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/qsgd3d12internalimagenode.cpp123
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12internalimagenode_p.h82
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12internalrectanglenode.cpp72
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12internalrectanglenode_p.h74
-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.cpp (renamed from src/qml/jsruntime/qv4debugging.cpp)25
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12material_p.h96
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12painternode.cpp255
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12painternode_p.h120
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12publicnodes.cpp254
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12publicnodes_p.h136
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12rendercontext.cpp169
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12rendercontext_p.h90
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12renderer.cpp785
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12renderer_p.h137
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12renderloop.cpp534
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12renderloop_p.h130
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode.cpp1047
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode_p.h177
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12spritenode.cpp314
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12spritenode_p.h90
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12texture.cpp145
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12texture_p.h87
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12threadedrenderloop.cpp1183
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12threadedrenderloop_p.h129
-rw-r--r--src/plugins/scenegraph/d3d12/shaders/flatcolor.hlsl27
-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.pri141
-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/sprite.hlsl43
-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.pro3
-rw-r--r--src/qml/animations/qabstractanimationjob.cpp21
-rw-r--r--src/qml/animations/qabstractanimationjob_p.h4
-rw-r--r--src/qml/animations/qanimationgroupjob_p.h1
-rw-r--r--src/qml/compiler/compiler.pri24
-rw-r--r--src/qml/compiler/qqmlirbuilder.cpp443
-rw-r--r--src/qml/compiler/qqmlirbuilder_p.h141
-rw-r--r--src/qml/compiler/qqmlpropertycachecreator.cpp71
-rw-r--r--src/qml/compiler/qqmlpropertycachecreator_p.h741
-rw-r--r--src/qml/compiler/qqmlpropertyvalidator.cpp703
-rw-r--r--src/qml/compiler/qqmlpropertyvalidator_p.h87
-rw-r--r--src/qml/compiler/qqmltypecompiler.cpp1952
-rw-r--r--src/qml/compiler/qqmltypecompiler_p.h274
-rw-r--r--src/qml/compiler/qv4codegen.cpp7
-rw-r--r--src/qml/compiler/qv4compilationunitmapper.cpp (renamed from src/qml/qml/qqmlaccessors.cpp)86
-rw-r--r--src/qml/compiler/qv4compilationunitmapper_p.h (renamed from src/qml/qml/qqmlmemoryprofiler_p.h)46
-rw-r--r--src/qml/compiler/qv4compilationunitmapper_unix.cpp99
-rw-r--r--src/qml/compiler/qv4compilationunitmapper_win.cpp134
-rw-r--r--src/qml/compiler/qv4compileddata.cpp409
-rw-r--r--src/qml/compiler/qv4compileddata_p.h580
-rw-r--r--src/qml/compiler/qv4compiler.cpp239
-rw-r--r--src/qml/compiler/qv4compiler_p.h11
-rw-r--r--src/qml/compiler/qv4instr_moth_p.h31
-rw-r--r--src/qml/compiler/qv4isel_moth.cpp226
-rw-r--r--src/qml/compiler/qv4isel_moth_p.h22
-rw-r--r--src/qml/compiler/qv4isel_p.cpp25
-rw-r--r--src/qml/compiler/qv4isel_p.h43
-rw-r--r--src/qml/compiler/qv4isel_util_p.h50
-rw-r--r--src/qml/compiler/qv4jsir.cpp335
-rw-r--r--src/qml/compiler/qv4jsir_p.h447
-rw-r--r--src/qml/compiler/qv4ssa.cpp1146
-rw-r--r--src/qml/compiler/qv4ssa_p.h19
-rw-r--r--src/qml/configure.json37
-rw-r--r--src/qml/debugger/debugger.pri27
-rw-r--r--src/qml/debugger/qqmlabstractprofileradapter_p.h10
-rw-r--r--src/qml/debugger/qqmldebug.cpp17
-rw-r--r--src/qml/debugger/qqmldebug.h3
-rw-r--r--src/qml/debugger/qqmldebugconnector_p.h25
-rw-r--r--src/qml/debugger/qqmldebugservice.cpp33
-rw-r--r--src/qml/debugger/qqmldebugservice_p.h20
-rw-r--r--src/qml/debugger/qqmldebugserviceinterfaces_p.h57
-rw-r--r--src/qml/debugger/qqmldebugstatesdelegate_p.h7
-rw-r--r--src/qml/debugger/qqmlmemoryprofiler.cpp (renamed from src/qml/qml/qqmlmemoryprofiler.cpp)40
-rw-r--r--src/qml/debugger/qqmlmemoryprofiler_p.h134
-rw-r--r--src/qml/debugger/qqmlprofiler.cpp18
-rw-r--r--src/qml/debugger/qqmlprofiler_p.h94
-rw-r--r--src/qml/debugger/qqmlprofilerdefinitions_p.h4
-rw-r--r--src/qml/doc/snippets/qml/qtLater.qml109
-rw-r--r--src/qml/doc/src/cppintegration/data.qdoc12
-rw-r--r--src/qml/doc/src/javascript/functionlist.qdoc24
-rw-r--r--src/qml/doc/src/qmlfunctions.qdoc36
-rw-r--r--src/qml/doc/src/qtqml.qdoc13
-rw-r--r--src/qml/jit/qv4assembler.cpp98
-rw-r--r--src/qml/jit/qv4assembler_p.h228
-rw-r--r--src/qml/jit/qv4binop.cpp72
-rw-r--r--src/qml/jit/qv4binop_p.h5
-rw-r--r--src/qml/jit/qv4isel_masm.cpp275
-rw-r--r--src/qml/jit/qv4isel_masm_p.h19
-rw-r--r--src/qml/jit/qv4regalloc.cpp161
-rw-r--r--src/qml/jit/qv4targetplatform_p.h2
-rw-r--r--src/qml/jit/qv4unop.cpp47
-rw-r--r--src/qml/jit/qv4unop_p.h4
-rw-r--r--src/qml/jsapi/qjsengine.cpp49
-rw-r--r--src/qml/jsapi/qjsengine.h8
-rw-r--r--src/qml/jsapi/qjsvalue.cpp67
-rw-r--r--src/qml/jsapi/qjsvalue.h2
-rw-r--r--src/qml/jsapi/qjsvalue_p.h1
-rw-r--r--src/qml/jsapi/qjsvalueiterator.cpp10
-rw-r--r--src/qml/jsruntime/jsruntime.pri18
-rw-r--r--src/qml/jsruntime/qv4argumentsobject.cpp31
-rw-r--r--src/qml/jsruntime/qv4argumentsobject_p.h26
-rw-r--r--src/qml/jsruntime/qv4arraybuffer.cpp55
-rw-r--r--src/qml/jsruntime/qv4arraybuffer_p.h13
-rw-r--r--src/qml/jsruntime/qv4arraydata.cpp6
-rw-r--r--src/qml/jsruntime/qv4arraydata_p.h12
-rw-r--r--src/qml/jsruntime/qv4arrayobject.cpp68
-rw-r--r--src/qml/jsruntime/qv4arrayobject_p.h6
-rw-r--r--src/qml/jsruntime/qv4booleanobject.cpp13
-rw-r--r--src/qml/jsruntime/qv4booleanobject_p.h6
-rw-r--r--src/qml/jsruntime/qv4context.cpp33
-rw-r--r--src/qml/jsruntime/qv4context_p.h69
-rw-r--r--src/qml/jsruntime/qv4context_p_p.h2
-rw-r--r--src/qml/jsruntime/qv4dataview.cpp26
-rw-r--r--src/qml/jsruntime/qv4dataview_p.h8
-rw-r--r--src/qml/jsruntime/qv4dateobject.cpp46
-rw-r--r--src/qml/jsruntime/qv4dateobject_p.h16
-rw-r--r--src/qml/jsruntime/qv4debugging_p.h15
-rw-r--r--src/qml/jsruntime/qv4engine.cpp95
-rw-r--r--src/qml/jsruntime/qv4engine_p.h51
-rw-r--r--src/qml/jsruntime/qv4errorobject.cpp146
-rw-r--r--src/qml/jsruntime/qv4errorobject_p.h60
-rw-r--r--src/qml/jsruntime/qv4function.cpp6
-rw-r--r--src/qml/jsruntime/qv4functionobject.cpp303
-rw-r--r--src/qml/jsruntime/qv4functionobject_p.h72
-rw-r--r--src/qml/jsruntime/qv4globalobject.cpp42
-rw-r--r--src/qml/jsruntime/qv4globalobject_p.h6
-rw-r--r--src/qml/jsruntime/qv4identifier.cpp23
-rw-r--r--src/qml/jsruntime/qv4identifier_p.h12
-rw-r--r--src/qml/jsruntime/qv4identifiertable.cpp10
-rw-r--r--src/qml/jsruntime/qv4include.cpp40
-rw-r--r--src/qml/jsruntime/qv4include_p.h11
-rw-r--r--src/qml/jsruntime/qv4internalclass.cpp42
-rw-r--r--src/qml/jsruntime/qv4internalclass_p.h30
-rw-r--r--src/qml/jsruntime/qv4jsonobject.cpp91
-rw-r--r--src/qml/jsruntime/qv4jsonobject_p.h2
-rw-r--r--src/qml/jsruntime/qv4lookup.cpp26
-rw-r--r--src/qml/jsruntime/qv4managed_p.h10
-rw-r--r--src/qml/jsruntime/qv4mathobject.cpp17
-rw-r--r--src/qml/jsruntime/qv4mathobject_p.h3
-rw-r--r--src/qml/jsruntime/qv4memberdata.cpp4
-rw-r--r--src/qml/jsruntime/qv4memberdata_p.h1
-rw-r--r--src/qml/jsruntime/qv4numberobject.cpp36
-rw-r--r--src/qml/jsruntime/qv4numberobject_p.h8
-rw-r--r--src/qml/jsruntime/qv4object.cpp28
-rw-r--r--src/qml/jsruntime/qv4object_p.h69
-rw-r--r--src/qml/jsruntime/qv4objectiterator.cpp24
-rw-r--r--src/qml/jsruntime/qv4objectiterator_p.h51
-rw-r--r--src/qml/jsruntime/qv4objectproto.cpp25
-rw-r--r--src/qml/jsruntime/qv4objectproto_p.h6
-rw-r--r--src/qml/jsruntime/qv4persistent.cpp26
-rw-r--r--src/qml/jsruntime/qv4persistent_p.h29
-rw-r--r--src/qml/jsruntime/qv4profiling.cpp28
-rw-r--r--src/qml/jsruntime/qv4profiling_p.h123
-rw-r--r--src/qml/jsruntime/qv4qobjectwrapper.cpp618
-rw-r--r--src/qml/jsruntime/qv4qobjectwrapper_p.h104
-rw-r--r--src/qml/jsruntime/qv4regexp.cpp30
-rw-r--r--src/qml/jsruntime/qv4regexp_p.h22
-rw-r--r--src/qml/jsruntime/qv4regexpobject.cpp73
-rw-r--r--src/qml/jsruntime/qv4regexpobject_p.h12
-rw-r--r--src/qml/jsruntime/qv4runtime.cpp575
-rw-r--r--src/qml/jsruntime/qv4runtime_p.h436
-rw-r--r--src/qml/jsruntime/qv4runtimeapi_p.h348
-rw-r--r--src/qml/jsruntime/qv4scopedvalue_p.h53
-rw-r--r--src/qml/jsruntime/qv4script.cpp66
-rw-r--r--src/qml/jsruntime/qv4script_p.h10
-rw-r--r--src/qml/jsruntime/qv4sequenceobject.cpp86
-rw-r--r--src/qml/jsruntime/qv4string.cpp133
-rw-r--r--src/qml/jsruntime/qv4string_p.h74
-rw-r--r--src/qml/jsruntime/qv4stringobject.cpp141
-rw-r--r--src/qml/jsruntime/qv4stringobject_p.h13
-rw-r--r--src/qml/jsruntime/qv4typedarray.cpp94
-rw-r--r--src/qml/jsruntime/qv4typedarray_p.h17
-rw-r--r--src/qml/jsruntime/qv4value_p.h18
-rw-r--r--src/qml/jsruntime/qv4variantobject.cpp35
-rw-r--r--src/qml/jsruntime/qv4variantobject_p.h22
-rw-r--r--src/qml/jsruntime/qv4vme_moth.cpp160
-rw-r--r--src/qml/jsruntime/qv4vme_moth_p.h3
-rw-r--r--src/qml/memory/qv4heap_p.h110
-rw-r--r--src/qml/memory/qv4mm.cpp138
-rw-r--r--src/qml/memory/qv4mm_p.h52
-rw-r--r--src/qml/parser/qqmljsengine_p.cpp2
-rw-r--r--src/qml/qml.pro12
-rw-r--r--src/qml/qml/ftw/ftw.pri5
-rw-r--r--src/qml/qml/ftw/qdeferredcleanup_p.h (renamed from src/qml/qml/ftw/qdeletewatcher_p.h)65
-rw-r--r--src/qml/qml/ftw/qhashedstring.cpp23
-rw-r--r--src/qml/qml/ftw/qhashedstring_p.h39
-rw-r--r--src/qml/qml/ftw/qpointervaluepair_p.h194
-rw-r--r--src/qml/qml/qml.pri15
-rw-r--r--src/qml/qml/qqml.h3
-rw-r--r--src/qml/qml/qqmlabstractbinding.cpp19
-rw-r--r--src/qml/qml/qqmlabstractbinding_p.h16
-rw-r--r--src/qml/qml/qqmlaccessors_p.h177
-rw-r--r--src/qml/qml/qqmlapplicationengine.cpp14
-rw-r--r--src/qml/qml/qqmlbinding.cpp476
-rw-r--r--src/qml/qml/qqmlbinding_p.h46
-rw-r--r--src/qml/qml/qqmlboundsignal.cpp21
-rw-r--r--src/qml/qml/qqmlcompileddata.cpp165
-rw-r--r--src/qml/qml/qqmlcompiler_p.h160
-rw-r--r--src/qml/qml/qqmlcomponent.cpp101
-rw-r--r--src/qml/qml/qqmlcomponent.h9
-rw-r--r--src/qml/qml/qqmlcomponent_p.h6
-rw-r--r--src/qml/qml/qqmlcontext.cpp59
-rw-r--r--src/qml/qml/qqmlcontext_p.h13
-rw-r--r--src/qml/qml/qqmlcontextwrapper.cpp24
-rw-r--r--src/qml/qml/qqmlcontextwrapper_p.h10
-rw-r--r--src/qml/qml/qqmlcustomparser.cpp15
-rw-r--r--src/qml/qml/qqmlcustomparser_p.h8
-rw-r--r--src/qml/qml/qqmldata_p.h76
-rw-r--r--src/qml/qml/qqmldelayedcallqueue.cpp215
-rw-r--r--src/qml/qml/qqmldelayedcallqueue_p.h104
-rw-r--r--src/qml/qml/qqmlengine.cpp266
-rw-r--r--src/qml/qml/qqmlengine.h5
-rw-r--r--src/qml/qml/qqmlengine_p.h17
-rw-r--r--src/qml/qml/qqmlerror.cpp4
-rw-r--r--src/qml/qml/qqmlexpression.cpp11
-rw-r--r--src/qml/qml/qqmlexpression_p.h4
-rw-r--r--src/qml/qml/qqmlfile.cpp30
-rw-r--r--src/qml/qml/qqmlfile.h2
-rw-r--r--src/qml/qml/qqmlimport.cpp71
-rw-r--r--src/qml/qml/qqmlincubator.cpp32
-rw-r--r--src/qml/qml/qqmlincubator_p.h4
-rw-r--r--src/qml/qml/qqmljavascriptexpression.cpp79
-rw-r--r--src/qml/qml/qqmljavascriptexpression_p.h27
-rw-r--r--src/qml/qml/qqmllist.cpp6
-rw-r--r--src/qml/qml/qqmllistwrapper.cpp26
-rw-r--r--src/qml/qml/qqmllistwrapper_p.h15
-rw-r--r--src/qml/qml/qqmllocale.cpp44
-rw-r--r--src/qml/qml/qqmllocale_p.h10
-rw-r--r--src/qml/qml/qqmlloggingcategory.cpp128
-rw-r--r--src/qml/qml/qqmlloggingcategory_p.h89
-rw-r--r--src/qml/qml/qqmlmetatype.cpp31
-rw-r--r--src/qml/qml/qqmlnetworkaccessmanagerfactory.cpp4
-rw-r--r--src/qml/qml/qqmlnetworkaccessmanagerfactory.h3
-rw-r--r--src/qml/qml/qqmlobjectcreator.cpp488
-rw-r--r--src/qml/qml/qqmlobjectcreator_p.h21
-rw-r--r--src/qml/qml/qqmlopenmetaobject.cpp2
-rw-r--r--src/qml/qml/qqmlplatform.cpp2
-rw-r--r--src/qml/qml/qqmlproperty.cpp403
-rw-r--r--src/qml/qml/qqmlproperty_p.h44
-rw-r--r--src/qml/qml/qqmlpropertycache.cpp771
-rw-r--r--src/qml/qml/qqmlpropertycache_p.h635
-rw-r--r--src/qml/qml/qqmlpropertyindex_p.h133
-rw-r--r--src/qml/qml/qqmlpropertyvalueinterceptor_p.h4
-rw-r--r--src/qml/qml/qqmlproxymetaobject.cpp6
-rw-r--r--src/qml/qml/qqmltypeloader.cpp666
-rw-r--r--src/qml/qml/qqmltypeloader_p.h95
-rw-r--r--src/qml/qml/qqmltypewrapper.cpp10
-rw-r--r--src/qml/qml/qqmltypewrapper_p.h6
-rw-r--r--src/qml/qml/qqmlvaluetype.cpp9
-rw-r--r--src/qml/qml/qqmlvaluetype_p.h2
-rw-r--r--src/qml/qml/qqmlvaluetypeproxybinding.cpp8
-rw-r--r--src/qml/qml/qqmlvaluetypeproxybinding_p.h6
-rw-r--r--src/qml/qml/qqmlvaluetypewrapper.cpp114
-rw-r--r--src/qml/qml/qqmlvaluetypewrapper_p.h19
-rw-r--r--src/qml/qml/qqmlvme.cpp1
-rw-r--r--src/qml/qml/qqmlvme_p.h4
-rw-r--r--src/qml/qml/qqmlvmemetaobject.cpp569
-rw-r--r--src/qml/qml/qqmlvmemetaobject_p.h118
-rw-r--r--src/qml/qml/qqmlxmlhttprequest.cpp138
-rw-r--r--src/qml/qml/qqmlxmlhttprequest_p.h4
-rw-r--r--src/qml/qml/v8/qqmlbuiltinfunctions.cpp118
-rw-r--r--src/qml/qml/v8/qqmlbuiltinfunctions_p.h21
-rw-r--r--src/qml/qml/v8/qv8engine.cpp8
-rw-r--r--src/qml/qml/v8/qv8engine_p.h11
-rw-r--r--src/qml/qml/v8/v8.pri1
-rw-r--r--src/qml/qtqmlglobal.h4
-rw-r--r--src/qml/qtqmlglobal_p.h4
-rw-r--r--src/qml/types/qqmlbind.cpp58
-rw-r--r--src/qml/types/qqmlbind_p.h5
-rw-r--r--src/qml/types/qqmlconnections.cpp11
-rw-r--r--src/qml/types/qqmlconnections_p.h2
-rw-r--r--src/qml/types/qqmldelegatemodel.cpp71
-rw-r--r--src/qml/types/qqmldelegatemodel_p_p.h9
-rw-r--r--src/qml/types/qqmllistmodel.cpp39
-rw-r--r--src/qml/types/qqmllistmodel_p.h4
-rw-r--r--src/qml/types/qqmllistmodel_p_p.h15
-rw-r--r--src/qml/types/qquickworkerscript.cpp30
-rw-r--r--src/qml/util/qqmlchangeset_p.h25
-rw-r--r--src/qmldebug/qqmldebugconnection.cpp60
-rw-r--r--src/qmldebug/qqmldebugconnection_p.h2
-rw-r--r--src/qmldebug/qqmlprofilerclient.cpp21
-rw-r--r--src/qmldebug/qqmlprofilerclient_p.h2
-rw-r--r--src/qmldebug/qqmlprofilerclient_p_p.h8
-rw-r--r--src/qmltest/qmltest.pro4
-rw-r--r--src/qmltest/quicktest.cpp13
-rw-r--r--src/qmltest/quicktestresult.cpp7
-rw-r--r--src/quick/accessible/qaccessiblequickitem.cpp4
-rw-r--r--src/quick/configure.json157
-rw-r--r--src/quick/designer/designer.pri6
-rw-r--r--src/quick/designer/qqmldesignermetaobject.cpp64
-rw-r--r--src/quick/designer/qqmldesignermetaobject_p.h3
-rw-r--r--src/quick/designer/qquickdesignercustomobjectdata.cpp11
-rw-r--r--src/quick/designer/qquickdesignercustomparserobject.cpp59
-rw-r--r--src/quick/designer/qquickdesignercustomparserobject_p.h79
-rw-r--r--src/quick/designer/qquickdesignersupport.cpp12
-rw-r--r--src/quick/designer/qquickdesignersupportitems.cpp14
-rw-r--r--src/quick/designer/qquickdesignersupportmetainfo.cpp6
-rw-r--r--src/quick/designer/qquickdesignersupportmetainfo_p.h1
-rw-r--r--src/quick/designer/qquickdesignerwindowmanager.cpp11
-rw-r--r--src/quick/designer/qquickdesignerwindowmanager_p.h7
-rw-r--r--src/quick/doc/snippets/qml/externaldrag.qml76
-rw-r--r--src/quick/doc/snippets/qml/localstorage/dbtransaction.js31
-rw-r--r--src/quick/doc/src/concepts/visualcanvas/adaptations.qdoc389
-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/doc/src/qmltypereference.qdoc13
-rw-r--r--src/quick/items/context2d/qquickcanvascontext_p.h5
-rw-r--r--src/quick/items/context2d/qquickcanvasitem.cpp77
-rw-r--r--src/quick/items/context2d/qquickcanvasitem_p.h4
-rw-r--r--src/quick/items/context2d/qquickcontext2d.cpp159
-rw-r--r--src/quick/items/context2d/qquickcontext2d_p.h9
-rw-r--r--src/quick/items/context2d/qquickcontext2dcommandbuffer.cpp6
-rw-r--r--src/quick/items/context2d/qquickcontext2dcommandbuffer_p.h26
-rw-r--r--src/quick/items/context2d/qquickcontext2dtexture.cpp17
-rw-r--r--src/quick/items/context2d/qquickcontext2dtexture_p.h22
-rw-r--r--src/quick/items/context2d/qquickcontext2dtile.cpp13
-rw-r--r--src/quick/items/context2d/qquickcontext2dtile_p.h13
-rw-r--r--src/quick/items/items.pri181
-rw-r--r--src/quick/items/items.qrc4
-rw-r--r--src/quick/items/qquickanchors.cpp32
-rw-r--r--src/quick/items/qquickanchors_p_p.h4
-rw-r--r--src/quick/items/qquickanimatedimage.cpp15
-rw-r--r--src/quick/items/qquickanimatedimage_p.h8
-rw-r--r--src/quick/items/qquickanimatedimage_p_p.h20
-rw-r--r--src/quick/items/qquickanimatedsprite.cpp684
-rw-r--r--src/quick/items/qquickanimatedsprite_p.h290
-rw-r--r--src/quick/items/qquickanimatedsprite_p_p.h100
-rw-r--r--src/quick/items/qquickborderimage.cpp103
-rw-r--r--src/quick/items/qquickborderimage_p.h2
-rw-r--r--src/quick/items/qquickborderimage_p_p.h30
-rw-r--r--src/quick/items/qquickclipnode.cpp2
-rw-r--r--src/quick/items/qquickdrag.cpp53
-rw-r--r--src/quick/items/qquickdrag_p.h6
-rw-r--r--src/quick/items/qquickdroparea.cpp2
-rw-r--r--src/quick/items/qquickevents.cpp537
-rw-r--r--src/quick/items/qquickevents_p_p.h402
-rw-r--r--src/quick/items/qquickflickable.cpp71
-rw-r--r--src/quick/items/qquickflickable_p.h2
-rw-r--r--src/quick/items/qquickflickable_p_p.h3
-rw-r--r--src/quick/items/qquickflipable_p.h4
-rw-r--r--src/quick/items/qquickframebufferobject.cpp15
-rw-r--r--src/quick/items/qquickgenericshadereffect.cpp658
-rw-r--r--src/quick/items/qquickgenericshadereffect_p.h153
-rw-r--r--src/quick/items/qquickgraphicsinfo.cpp303
-rw-r--r--src/quick/items/qquickgraphicsinfo_p.h166
-rw-r--r--src/quick/items/qquickgridview.cpp18
-rw-r--r--src/quick/items/qquickgridview_p.h5
-rw-r--r--src/quick/items/qquickimage.cpp4
-rw-r--r--src/quick/items/qquickimagebase.cpp2
-rw-r--r--src/quick/items/qquickimplicitsizeitem.cpp13
-rw-r--r--src/quick/items/qquickitem.cpp451
-rw-r--r--src/quick/items/qquickitem.h3
-rw-r--r--src/quick/items/qquickitem_p.h47
-rw-r--r--src/quick/items/qquickitemanimation.cpp10
-rw-r--r--src/quick/items/qquickitemanimation_p.h6
-rw-r--r--src/quick/items/qquickitemanimation_p_p.h5
-rw-r--r--src/quick/items/qquickitemchangelistener_p.h59
-rw-r--r--src/quick/items/qquickitemgrabresult.cpp7
-rw-r--r--src/quick/items/qquickitemsmodule.cpp109
-rw-r--r--src/quick/items/qquickitemview.cpp59
-rw-r--r--src/quick/items/qquickitemview_p.h4
-rw-r--r--src/quick/items/qquickitemview_p_p.h6
-rw-r--r--src/quick/items/qquickitemviewtransition_p.h3
-rw-r--r--src/quick/items/qquicklistview.cpp50
-rw-r--r--src/quick/items/qquicklistview_p.h10
-rw-r--r--src/quick/items/qquickloader.cpp5
-rw-r--r--src/quick/items/qquickloader_p_p.h2
-rw-r--r--src/quick/items/qquickmousearea.cpp45
-rw-r--r--src/quick/items/qquickmousearea_p_p.h3
-rw-r--r--src/quick/items/qquickmultipointtoucharea.cpp71
-rw-r--r--src/quick/items/qquickmultipointtoucharea_p.h4
-rw-r--r--src/quick/items/qquickopenglinfo.cpp4
-rw-r--r--src/quick/items/qquickopenglshadereffect.cpp973
-rw-r--r--src/quick/items/qquickopenglshadereffect_p.h198
-rw-r--r--src/quick/items/qquickopenglshadereffectnode.cpp (renamed from src/quick/items/qquickshadereffectnode.cpp)97
-rw-r--r--src/quick/items/qquickopenglshadereffectnode_p.h (renamed from src/quick/items/qquickshadereffectnode_p.h)56
-rw-r--r--src/quick/items/qquickpainteditem.cpp30
-rw-r--r--src/quick/items/qquickpathview.cpp69
-rw-r--r--src/quick/items/qquickpathview_p.h7
-rw-r--r--src/quick/items/qquickpathview_p_p.h10
-rw-r--r--src/quick/items/qquickpositioners_p.h4
-rw-r--r--src/quick/items/qquickpositioners_p_p.h8
-rw-r--r--src/quick/items/qquickrectangle.cpp7
-rw-r--r--src/quick/items/qquickrendercontrol.cpp67
-rw-r--r--src/quick/items/qquickrepeater.cpp4
-rw-r--r--src/quick/items/qquickshadereffect.cpp1315
-rw-r--r--src/quick/items/qquickshadereffect_p.h127
-rw-r--r--src/quick/items/qquickshadereffectmesh.cpp287
-rw-r--r--src/quick/items/qquickshadereffectmesh_p.h63
-rw-r--r--src/quick/items/qquickshadereffectsource.cpp11
-rw-r--r--src/quick/items/qquickshadereffectsource_p.h16
-rw-r--r--src/quick/items/qquicksprite_p.h7
-rw-r--r--src/quick/items/qquickspriteengine.cpp144
-rw-r--r--src/quick/items/qquickspriteengine_p.h42
-rw-r--r--src/quick/items/qquickspritesequence.cpp402
-rw-r--r--src/quick/items/qquickspritesequence_p.h72
-rw-r--r--src/quick/items/qquickspritesequence_p_p.h94
-rw-r--r--src/quick/items/qquickstateoperations.cpp26
-rw-r--r--src/quick/items/qquicktext.cpp46
-rw-r--r--src/quick/items/qquicktextcontrol.cpp33
-rw-r--r--src/quick/items/qquicktextcontrol_p.h3
-rw-r--r--src/quick/items/qquicktextdocument.cpp2
-rw-r--r--src/quick/items/qquicktextedit.cpp72
-rw-r--r--src/quick/items/qquicktextedit_p.h5
-rw-r--r--src/quick/items/qquicktextinput.cpp96
-rw-r--r--src/quick/items/qquicktextinput_p.h5
-rw-r--r--src/quick/items/qquicktextinput_p_p.h2
-rw-r--r--src/quick/items/qquicktextnode.cpp6
-rw-r--r--src/quick/items/qquicktextnode_p.h15
-rw-r--r--src/quick/items/qquicktextnodeengine.cpp40
-rw-r--r--src/quick/items/qquicktextnodeengine_p.h19
-rw-r--r--src/quick/items/qquickview.cpp32
-rw-r--r--src/quick/items/qquickview_p.h2
-rw-r--r--src/quick/items/qquickwindow.cpp1571
-rw-r--r--src/quick/items/qquickwindow.h21
-rw-r--r--src/quick/items/qquickwindow_p.h92
-rw-r--r--src/quick/qtquick2.cpp17
-rw-r--r--src/quick/qtquickglobal.h4
-rw-r--r--src/quick/qtquickglobal_p.h4
-rw-r--r--src/quick/quick.pro10
-rw-r--r--src/quick/scenegraph/adaptations/adaptations.pri1
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer.cpp328
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer_p.h111
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwareadaptation.cpp92
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwareadaptation_p.h77
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarecontext.cpp224
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarecontext_p.h114
-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/qsgsoftwareinternalimagenode.cpp501
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwareinternalimagenode_p.h147
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode.cpp451
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode_p.h103
-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/qsgsoftwarepainternode.cpp231
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarepainternode_p.h132
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaprenderer.cpp116
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaprenderer_p.h76
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaptexture.cpp88
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaptexture_p.h79
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes.cpp196
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes_p.h145
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp434
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode_p.h157
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater.cpp309
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater_p.h143
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer.cpp165
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer_p.h84
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarerenderlistbuilder.cpp176
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarerenderlistbuilder_p.h98
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop.cpp264
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop_p.h105
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarespritenode.cpp139
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarespritenode_p.h96
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop.cpp991
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop_p.h118
-rw-r--r--src/quick/scenegraph/adaptations/software/software.pri48
-rw-r--r--src/quick/scenegraph/coreapi/qsgabstractrenderer.h4
-rw-r--r--src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp91
-rw-r--r--src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h13
-rw-r--r--src/quick/scenegraph/coreapi/qsggeometry.cpp170
-rw-r--r--src/quick/scenegraph/coreapi/qsggeometry.h93
-rw-r--r--src/quick/scenegraph/coreapi/qsgmaterial.cpp33
-rw-r--r--src/quick/scenegraph/coreapi/qsgmaterial.h32
-rw-r--r--src/quick/scenegraph/coreapi/qsgmaterialshader_p.h2
-rw-r--r--src/quick/scenegraph/coreapi/qsgnode.cpp24
-rw-r--r--src/quick/scenegraph/coreapi/qsgnode.h6
-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.cpp197
-rw-r--r--src/quick/scenegraph/coreapi/qsgrendererinterface.h102
-rw-r--r--src/quick/scenegraph/coreapi/qsgrendernode.cpp317
-rw-r--r--src/quick/scenegraph/coreapi/qsgrendernode.h105
-rw-r--r--src/quick/scenegraph/coreapi/qsgrendernode_p.h51
-rw-r--r--src/quick/scenegraph/coreapi/qsgshaderrewriter.cpp3
-rw-r--r--src/quick/scenegraph/qsgadaptationlayer.cpp60
-rw-r--r--src/quick/scenegraph/qsgadaptationlayer_p.h197
-rw-r--r--src/quick/scenegraph/qsgbasicglyphnode.cpp95
-rw-r--r--src/quick/scenegraph/qsgbasicglyphnode_p.h92
-rw-r--r--src/quick/scenegraph/qsgbasicinternalimagenode.cpp559
-rw-r--r--src/quick/scenegraph/qsgbasicinternalimagenode_p.h109
-rw-r--r--src/quick/scenegraph/qsgbasicinternalrectanglenode.cpp (renamed from src/quick/scenegraph/qsgdefaultrectanglenode.cpp)143
-rw-r--r--src/quick/scenegraph/qsgbasicinternalrectanglenode_p.h (renamed from src/quick/scenegraph/qsgdefaultrectanglenode_p.h)49
-rw-r--r--src/quick/scenegraph/qsgcontext.cpp513
-rw-r--r--src/quick/scenegraph/qsgcontext_p.h98
-rw-r--r--src/quick/scenegraph/qsgcontextplugin.cpp159
-rw-r--r--src/quick/scenegraph/qsgcontextplugin_p.h9
-rw-r--r--src/quick/scenegraph/qsgdefaultcontext.cpp290
-rw-r--r--src/quick/scenegraph/qsgdefaultcontext_p.h101
-rw-r--r--src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp2
-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.cpp671
-rw-r--r--src/quick/scenegraph/qsgdefaultinternalimagenode.cpp226
-rw-r--r--src/quick/scenegraph/qsgdefaultinternalimagenode_p.h (renamed from src/quick/scenegraph/qsgdefaultimagenode_p.h)48
-rw-r--r--src/quick/scenegraph/qsgdefaultinternalrectanglenode.cpp159
-rw-r--r--src/quick/scenegraph/qsgdefaultinternalrectanglenode_p.h90
-rw-r--r--src/quick/scenegraph/qsgdefaultlayer.cpp8
-rw-r--r--src/quick/scenegraph/qsgdefaultlayer_p.h10
-rw-r--r--src/quick/scenegraph/qsgdefaultrendercontext.cpp307
-rw-r--r--src/quick/scenegraph/qsgdefaultrendercontext_p.h112
-rw-r--r--src/quick/scenegraph/qsgdefaultspritenode.cpp303
-rw-r--r--src/quick/scenegraph/qsgdefaultspritenode_p.h91
-rw-r--r--src/quick/scenegraph/qsgrenderloop.cpp59
-rw-r--r--src/quick/scenegraph/qsgrenderloop_p.h6
-rw-r--r--src/quick/scenegraph/qsgthreadedrenderloop.cpp15
-rw-r--r--src/quick/scenegraph/qsgwindowsrenderloop.cpp23
-rw-r--r--src/quick/scenegraph/qsgwindowsrenderloop_p.h5
-rw-r--r--src/quick/scenegraph/scenegraph.pri277
-rw-r--r--src/quick/scenegraph/scenegraph.qrc4
-rw-r--r--src/quick/scenegraph/shaders/sprite.frag (renamed from src/quick/items/shaders/sprite.frag)0
-rw-r--r--src/quick/scenegraph/shaders/sprite.vert (renamed from src/quick/items/shaders/sprite.vert)0
-rw-r--r--src/quick/scenegraph/shaders/sprite_core.frag (renamed from src/quick/items/shaders/sprite_core.frag)0
-rw-r--r--src/quick/scenegraph/shaders/sprite_core.vert (renamed from src/quick/items/shaders/sprite_core.vert)0
-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/qsgdefaultimagenode.cpp205
-rw-r--r--src/quick/scenegraph/util/qsgdefaultimagenode_p.h107
-rw-r--r--src/quick/scenegraph/util/qsgdefaultninepatchnode.cpp136
-rw-r--r--src/quick/scenegraph/util/qsgdefaultninepatchnode_p.h86
-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/qsgdefaultrectanglenode.cpp95
-rw-r--r--src/quick/scenegraph/util/qsgdefaultrectanglenode_p.h79
-rw-r--r--src/quick/scenegraph/util/qsgdistancefieldutil.cpp4
-rw-r--r--src/quick/scenegraph/util/qsgengine.cpp76
-rw-r--r--src/quick/scenegraph/util/qsgengine.h8
-rw-r--r--src/quick/scenegraph/util/qsgflatcolormaterial.cpp22
-rw-r--r--src/quick/scenegraph/util/qsgimagenode.cpp190
-rw-r--r--src/quick/scenegraph/util/qsgimagenode.h88
-rw-r--r--src/quick/scenegraph/util/qsgninepatchnode.cpp77
-rw-r--r--src/quick/scenegraph/util/qsgninepatchnode.h62
-rw-r--r--src/quick/scenegraph/util/qsgrectanglenode.cpp86
-rw-r--r--src/quick/scenegraph/util/qsgrectanglenode.h62
-rw-r--r--src/quick/scenegraph/util/qsgsimplematerial.cpp5
-rw-r--r--src/quick/scenegraph/util/qsgsimplematerial.h9
-rw-r--r--src/quick/scenegraph/util/qsgsimplerectnode.cpp6
-rw-r--r--src/quick/scenegraph/util/qsgsimpletexturenode.cpp7
-rw-r--r--src/quick/scenegraph/util/qsgtexture.cpp32
-rw-r--r--src/quick/scenegraph/util/qsgtexture_p.h8
-rw-r--r--src/quick/scenegraph/util/qsgtexturematerial.cpp34
-rw-r--r--src/quick/scenegraph/util/qsgvertexcolormaterial.cpp20
-rw-r--r--src/quick/util/qquickanimation.cpp8
-rw-r--r--src/quick/util/qquickanimator.cpp2
-rw-r--r--src/quick/util/qquickanimator_p.h5
-rw-r--r--src/quick/util/qquickanimatorcontroller.cpp22
-rw-r--r--src/quick/util/qquickanimatorjob.cpp36
-rw-r--r--src/quick/util/qquickanimatorjob_p.h7
-rw-r--r--src/quick/util/qquickapplication.cpp5
-rw-r--r--src/quick/util/qquickapplication_p.h3
-rw-r--r--src/quick/util/qquickbehavior.cpp6
-rw-r--r--src/quick/util/qquickfontloader.cpp41
-rw-r--r--src/quick/util/qquickglobal.cpp5
-rw-r--r--src/quick/util/qquickpath.cpp22
-rw-r--r--src/quick/util/qquickpath_p.h27
-rw-r--r--src/quick/util/qquickpath_p_p.h4
-rw-r--r--src/quick/util/qquickpathinterpolator_p.h4
-rw-r--r--src/quick/util/qquickpixmapcache.cpp63
-rw-r--r--src/quick/util/qquickprofiler.cpp12
-rw-r--r--src/quick/util/qquickprofiler_p.h106
-rw-r--r--src/quick/util/qquickpropertychanges.cpp51
-rw-r--r--src/quick/util/qquickpropertychanges_p.h2
-rw-r--r--src/quick/util/qquicksmoothedanimation.cpp22
-rw-r--r--src/quick/util/qquickspringanimation.cpp7
-rw-r--r--src/quick/util/qquickstate.cpp4
-rw-r--r--src/quick/util/qquickstategroup.cpp3
-rw-r--r--src/quick/util/qquicktimeline.cpp2
-rw-r--r--src/quick/util/qquicktransitionmanager.cpp16
-rw-r--r--src/quick/util/qquickutilmodule.cpp3
-rw-r--r--src/quick/util/qquickvaluetypes.cpp82
-rw-r--r--src/quick/util/qquickvaluetypes_p.h30
-rw-r--r--src/quick/util/util.pri18
-rw-r--r--src/quickwidgets/qquickwidget.cpp405
-rw-r--r--src/quickwidgets/qquickwidget.h39
-rw-r--r--src/quickwidgets/qquickwidget_p.h13
-rw-r--r--src/quickwidgets/quickwidgets.pro2
-rw-r--r--src/src.pro9
-rw-r--r--tests/auto/auto.pro11
-rw-r--r--tests/auto/particles/particles.pro3
-rw-r--r--tests/auto/particles/qquickage/tst_qquickage.cpp8
-rw-r--r--tests/auto/particles/qquickangleddirection/tst_qquickangleddirection.cpp2
-rw-r--r--tests/auto/particles/qquickcumulativedirection/tst_qquickcumulativedirection.cpp2
-rw-r--r--tests/auto/particles/qquickcustomaffector/tst_qquickcustomaffector.cpp4
-rw-r--r--tests/auto/particles/qquickcustomparticle/tst_qquickcustomparticle.cpp2
-rw-r--r--tests/auto/particles/qquickellipseextruder/tst_qquickellipseextruder.cpp4
-rw-r--r--tests/auto/particles/qquickfriction/tst_qquickfriction.cpp6
-rw-r--r--tests/auto/particles/qquickgravity/tst_qquickgravity.cpp2
-rw-r--r--tests/auto/particles/qquickgroupgoal/tst_qquickgroupgoal.cpp2
-rw-r--r--tests/auto/particles/qquickimageparticle/tst_qquickimageparticle.cpp12
-rw-r--r--tests/auto/particles/qquickitemparticle/tst_qquickitemparticle.cpp2
-rw-r--r--tests/auto/particles/qquicklineextruder/tst_qquicklineextruder.cpp4
-rw-r--r--tests/auto/particles/qquickmaskextruder/tst_qquickmaskextruder.cpp2
-rw-r--r--tests/auto/particles/qquickparticlegroup/tst_qquickparticlegroup.cpp2
-rw-r--r--tests/auto/particles/qquickparticlesystem/tst_qquickparticlesystem.cpp2
-rw-r--r--tests/auto/particles/qquickpointattractor/tst_qquickpointattractor.cpp2
-rw-r--r--tests/auto/particles/qquickpointdirection/tst_qquickpointdirection.cpp2
-rw-r--r--tests/auto/particles/qquickrectangleextruder/tst_qquickrectangleextruder.cpp4
-rw-r--r--tests/auto/particles/qquickspritegoal/tst_qquickspritegoal.cpp2
-rw-r--r--tests/auto/particles/qquicktargetdirection/tst_qquicktargetdirection.cpp2
-rw-r--r--tests/auto/particles/qquicktrailemitter/tst_qquicktrailemitter.cpp4
-rw-r--r--tests/auto/particles/qquickturbulence/tst_qquickturbulence.cpp2
-rw-r--r--tests/auto/particles/qquickwander/tst_qquickwander.cpp2
-rw-r--r--tests/auto/qml/animation/qparallelanimationgroupjob/tst_qparallelanimationgroupjob.cpp4
-rw-r--r--tests/auto/qml/debugger/debugger.pro4
-rw-r--r--tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs/tst_qqmldebugjs.cpp5
-rw-r--r--tests/auto/qml/debugger/qqmlenginedebugservice/tst_qqmlenginedebugservice.cpp7
-rw-r--r--tests/auto/qml/debugger/qqmlprofilerservice/data/controlFromJS.qml10
-rw-r--r--tests/auto/qml/debugger/qqmlprofilerservice/data/exit.qml4
-rw-r--r--tests/auto/qml/debugger/qqmlprofilerservice/data/javascript.qml8
-rw-r--r--tests/auto/qml/debugger/qqmlprofilerservice/data/signalSourceLocation.qml8
-rw-r--r--tests/auto/qml/debugger/qqmlprofilerservice/data/test.qml4
-rw-r--r--tests/auto/qml/debugger/qqmlprofilerservice/data/timer.qml10
-rw-r--r--tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp31
-rw-r--r--tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp4
-rw-r--r--tests/auto/qml/debugger/shared/debugutil_p.h2
-rw-r--r--tests/auto/qml/qjsengine/tst_qjsengine.cpp186
-rw-r--r--tests/auto/qml/qjsvalue/tst_qjsvalue.cpp14
-rw-r--r--tests/auto/qml/qml.pro12
-rw-r--r--tests/auto/qml/qmldiskcache/qmldiskcache.pro9
-rw-r--r--tests/auto/qml/qmldiskcache/test.qml4
-rw-r--r--tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp671
-rw-r--r--tests/auto/qml/qmlplugindump/tst_qmlplugindump.cpp1
-rw-r--r--tests/auto/qml/qqmlbinding/data/delayed.qml26
-rw-r--r--tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp22
-rw-r--r--tests/auto/qml/qqmlconsole/data/categorized_logging.qml65
-rw-r--r--tests/auto/qml/qqmlconsole/tst_qqmlconsole.cpp36
-rw-r--r--tests/auto/qml/qqmlecmascript/data/NullObjectInitializerBase.qml5
-rw-r--r--tests/auto/qml/qqmlecmascript/data/dependenciesWithFunctions.qml12
-rw-r--r--tests/auto/qml/qqmlecmascript/data/nullObjectInitializer.2.qml8
-rw-r--r--tests/auto/qml/qqmlecmascript/data/nullObjectInitializer.qml4
-rw-r--r--tests/auto/qml/qqmlecmascript/testtypes.h3
-rw-r--r--tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp91
-rw-r--r--tests/auto/qml/qqmlengine/data/qtqmlModule.4.qml2
-rw-r--r--tests/auto/qml/qqmlengine/tst_qqmlengine.cpp15
-rw-r--r--tests/auto/qml/qqmlenginecleanup/data/types.qml3
-rw-r--r--tests/auto/qml/qqmlextensionplugin/tst_qqmlextensionplugin.cpp2
-rw-r--r--tests/auto/qml/qqmllanguage/data/alias.12.qml15
-rw-r--r--tests/auto/qml/qqmllanguage/data/alias.13.qml16
-rw-r--r--tests/auto/qml/qqmllanguage/data/alias.14.errors.txt1
-rw-r--r--tests/auto/qml/qqmllanguage/data/alias.14.qml17
-rw-r--r--tests/auto/qml/qqmllanguage/data/arraybuffer_method_arg.qml11
-rw-r--r--tests/auto/qml/qqmllanguage/data/arraybuffer_method_overload.qml7
-rw-r--r--tests/auto/qml/qqmllanguage/data/arraybuffer_method_return.qml15
-rw-r--r--tests/auto/qml/qqmllanguage/data/arraybuffer_property_get.qml5
-rw-r--r--tests/auto/qml/qqmllanguage/data/arraybuffer_property_set.qml11
-rw-r--r--tests/auto/qml/qqmllanguage/data/arraybuffer_signal_arg.qml14
-rw-r--r--tests/auto/qml/qqmllanguage/data/badCompositeRegistration.1.errors.txt2
-rw-r--r--tests/auto/qml/qqmllanguage/data/cppnamespace.qml1
-rw-r--r--tests/auto/qml/qqmllanguage/data/invalidAlias.11.errors.txt1
-rw-r--r--tests/auto/qml/qqmllanguage/data/invalidAlias.11.qml10
-rw-r--r--tests/auto/qml/qqmllanguage/data/rootItemIsComponent.qml6
-rw-r--r--tests/auto/qml/qqmllanguage/data/singletonTest10.error.txt1
-rw-r--r--tests/auto/qml/qqmllanguage/data/singletonTest12.error.txt2
-rw-r--r--tests/auto/qml/qqmllanguage/data/singletonTest4.error.txt2
-rw-r--r--tests/auto/qml/qqmllanguage/data/uncreatableTypeAsProperty.qml6
-rw-r--r--tests/auto/qml/qqmllanguage/testtypes.cpp18
-rw-r--r--tests/auto/qml/qqmllanguage/testtypes.h73
-rw-r--r--tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp123
-rw-r--r--tests/auto/qml/qqmlmoduleplugin/plugin.2.1/childplugin/childplugin.cpp2
-rw-r--r--tests/auto/qml/qqmlmoduleplugin/plugin.2/childplugin/childplugin.cpp2
-rw-r--r--tests/auto/qml/qqmlmoduleplugin/plugin/childplugin/childplugin.cpp2
-rw-r--r--tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp65
-rw-r--r--tests/auto/qml/qqmlpropertycache/tst_qqmlpropertycache.cpp271
-rw-r--r--tests/auto/qml/qqmlqt/data/LaterComponent.qml14
-rw-r--r--tests/auto/qml/qqmlqt/data/LaterComponent2.qml13
-rw-r--r--tests/auto/qml/qqmlqt/data/LaterComponent3.qml14
-rw-r--r--tests/auto/qml/qqmlqt/data/LaterComponent4.qml12
-rw-r--r--tests/auto/qml/qqmlqt/data/exit.qml7
-rw-r--r--tests/auto/qml/qqmlqt/data/later.qml124
-rw-r--r--tests/auto/qml/qqmlqt/data/timeRoundtrip.qml8
-rw-r--r--tests/auto/qml/qqmlqt/tst_qqmlqt.cpp286
-rw-r--r--tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp17
-rw-r--r--tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp3
-rw-r--r--tests/auto/qml/qqmlvaluetypes/data/color_read.qml6
-rw-r--r--tests/auto/qml/qqmlvaluetypes/data/color_write_HSL.qml8
-rw-r--r--tests/auto/qml/qqmlvaluetypes/data/color_write_HSV.qml8
-rw-r--r--tests/auto/qml/qqmlvaluetypes/testtypes.h6
-rw-r--r--tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp54
-rw-r--r--tests/auto/qml/qqmlxmlhttprequest/data/send_patch.expect17
-rw-r--r--tests/auto/qml/qqmlxmlhttprequest/data/send_patch.qml68
-rw-r--r--tests/auto/qml/qqmlxmlhttprequest/data/send_patch.reply3
-rw-r--r--tests/auto/qml/qqmlxmlhttprequest/tst_qqmlxmlhttprequest.cpp21
-rw-r--r--tests/auto/qml/qquickfolderlistmodel/tst_qquickfolderlistmodel.cpp10
-rw-r--r--tests/auto/qml/qv4mm/qv4mm.pro8
-rw-r--r--tests/auto/qml/qv4mm/tst_qv4mm.cpp58
-rw-r--r--tests/auto/qmldevtools/compile/compile.pro2
-rw-r--r--tests/auto/qmldevtools/qmldevtools.pro6
-rw-r--r--tests/auto/qmltest/selftests/tst_tryVerify.qml72
-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.cpp6
-rw-r--r--tests/auto/quick/qquickaccessible/qquickaccessible.pro7
-rw-r--r--tests/auto/quick/qquickanimatedimage/tst_qquickanimatedimage.cpp6
-rw-r--r--tests/auto/quick/qquickapplication/tst_qquickapplication.cpp16
-rw-r--r--tests/auto/quick/qquickborderimage/data/mesh.qml22
-rw-r--r--tests/auto/quick/qquickborderimage/data/nonmesh.qml11
-rw-r--r--tests/auto/quick/qquickborderimage/qquickborderimage.pro1
-rw-r--r--tests/auto/quick/qquickborderimage/tst_qquickborderimage.cpp24
-rw-r--r--tests/auto/quick/qquickcanvasitem/data/CanvasTestCase.qml4
-rw-r--r--tests/auto/quick/qquickcanvasitem/data/tst_canvas.qml1
-rw-r--r--tests/auto/quick/qquickcanvasitem/data/tst_image.qml1
-rw-r--r--tests/auto/quick/qquickflickable/data/contentXY.qml6
-rw-r--r--tests/auto/quick/qquickflickable/data/keepGrab.qml22
-rw-r--r--tests/auto/quick/qquickflickable/tst_qquickflickable.cpp70
-rw-r--r--tests/auto/quick/qquickgraphicsinfo/data/basic.qml14
-rw-r--r--tests/auto/quick/qquickgraphicsinfo/qquickgraphicsinfo.pro14
-rw-r--r--tests/auto/quick/qquickgraphicsinfo/tst_qquickgraphicsinfo.cpp85
-rw-r--r--tests/auto/quick/qquickimage/tst_qquickimage.cpp5
-rw-r--r--tests/auto/quick/qquickitem2/data/layoutmirroring_window.qml7
-rw-r--r--tests/auto/quick/qquickitem2/tst_qquickitem.cpp287
-rw-r--r--tests/auto/quick/qquickitemlayer/tst_qquickitemlayer.cpp46
-rw-r--r--tests/auto/quick/qquicklistview/BLACKLIST3
-rw-r--r--tests/auto/quick/qquickloader/data/qmldir1
-rw-r--r--tests/auto/quick/qquickloader/tst_qquickloader.cpp6
-rw-r--r--tests/auto/quick/qquickmousearea/data/qtbug54019.qml21
-rw-r--r--tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp285
-rw-r--r--tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp4
-rw-r--r--tests/auto/quick/qquickpainteditem/tst_qquickpainteditem.cpp16
-rw-r--r--tests/auto/quick/qquickpincharea/data/pinchproperties.qml2
-rw-r--r--tests/auto/quick/qquickpincharea/tst_qquickpincharea.cpp66
-rw-r--r--tests/auto/quick/qquickshadereffect/tst_qquickshadereffect.cpp62
-rw-r--r--tests/auto/quick/qquicktext/BLACKLIST2
-rw-r--r--tests/auto/quick/qquicktext/tst_qquicktext.cpp30
-rw-r--r--tests/auto/quick/qquicktextedit/BLACKLIST4
-rw-r--r--tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp69
-rw-r--r--tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp43
-rw-r--r--tests/auto/quick/qquickview/tst_qquickview.cpp33
-rw-r--r--tests/auto/quick/qquickwindow/qquickwindow.pro2
-rw-r--r--tests/auto/quick/qquickwindow/tst_qquickwindow.cpp267
-rw-r--r--tests/auto/quick/quick.pro24
-rw-r--r--tests/auto/quick/rendernode/tst_rendernode.cpp17
-rw-r--r--tests/auto/quick/scenegraph/scenegraph.pro1
-rw-r--r--tests/auto/quick/scenegraph/tst_scenegraph.cpp89
-rw-r--r--tests/auto/quick/shared/visualtestutil.cpp35
-rw-r--r--tests/auto/quick/shared/visualtestutil.h2
-rw-r--r--tests/auto/quick/touchmouse/data/touchpointdeliveryorder.qml39
-rw-r--r--tests/auto/quick/touchmouse/touchmouse.pro2
-rw-r--r--tests/auto/quick/touchmouse/tst_touchmouse.cpp240
-rw-r--r--tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp23
-rw-r--r--tests/auto/shared/util.cpp11
-rw-r--r--tests/auto/shared/util.h5
-rw-r--r--tests/benchmarks/benchmarks.pro4
-rw-r--r--tests/benchmarks/particles/affectors/tst_affectors.cpp4
-rw-r--r--tests/benchmarks/particles/emission/tst_emission.cpp2
-rw-r--r--tests/benchmarks/qml/animation/tst_animation.cpp4
-rw-r--r--tests/benchmarks/qml/compilation/tst_compilation.cpp4
-rw-r--r--tests/benchmarks/qml/creation/tst_creation.cpp89
-rw-r--r--tests/manual/nodetypes/Animators.qml180
-rw-r--r--tests/manual/nodetypes/Effects.qml221
-rw-r--r--tests/manual/nodetypes/Images.qml97
-rw-r--r--tests/manual/nodetypes/Layers.qml106
-rw-r--r--tests/manual/nodetypes/LotsOfImages.qml73
-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.qml83
-rw-r--r--tests/manual/nodetypes/nodetypes.cpp206
-rw-r--r--tests/manual/nodetypes/nodetypes.pro9
-rw-r--r--tests/manual/nodetypes/nodetypes.qrc21
-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--tests/manual/qmlplugindump/tst_qmlplugindump.cpp11
-rw-r--r--tests/manual/scenegraph_lancelot/scenegrabber/main.cpp4
-rw-r--r--tests/manual/scenegraph_lancelot/scenegraph/tst_scenegraph.cpp6
-rw-r--r--tools/fdegen/fdegen.pro8
-rw-r--r--tools/fdegen/main.cpp362
-rw-r--r--tools/qml/main.cpp25
-rw-r--r--tools/qml/qml.pro2
-rw-r--r--tools/qmleasing/mainwindow.cpp1
-rw-r--r--tools/qmleasing/splineeditor.cpp17
-rw-r--r--tools/qmlimportscanner/main.cpp94
-rw-r--r--tools/qmljs/qmljs.cpp73
-rw-r--r--tools/qmlmin/main.cpp2
-rw-r--r--tools/qmlplugindump/main.cpp52
-rw-r--r--tools/qmlplugindump/qmlstreamwriter.cpp2
-rw-r--r--tools/qmlprofiler/commandlistener.h2
-rw-r--r--tools/qmlprofiler/main.cpp6
-rw-r--r--tools/qmlprofiler/qmlprofilerapplication.cpp38
-rw-r--r--tools/qmlprofiler/qmlprofilerapplication.h11
-rw-r--r--tools/qmlprofiler/qmlprofilerdata.cpp8
-rw-r--r--tools/qmlprofiler/qmlprofilerdata.h11
-rw-r--r--tools/qmlscene/main.cpp25
-rw-r--r--tools/qmlscene/qmlscene.pro3
-rw-r--r--tools/tools.pro5
943 files changed, 58780 insertions, 19056 deletions
diff --git a/.gitignore b/.gitignore
index 504317b59e..da9552c8e6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -287,3 +287,9 @@ src/qml/RegExpJitTables.h
src/qml/udis86_itab.c
src/qml/udis86_itab.h
+# Generated HLSL bytecode headers
+*.hlslh
+
+# Compiled QML/JS code
+*.qmlc
+*.jsc
diff --git a/.qmake.conf b/.qmake.conf
index 45d16f2971..556f554e5e 100644
--- a/.qmake.conf
+++ b/.qmake.conf
@@ -1,4 +1,4 @@
load(qt_build_config)
CONFIG += warning_clean
-MODULE_VERSION = 5.7.1
+MODULE_VERSION = 5.8.0
diff --git a/bin/rename-qtdeclarative-symbols.sh b/bin/rename-qtdeclarative-symbols.sh
deleted file mode 100755
index 3b82989fa5..0000000000
--- a/bin/rename-qtdeclarative-symbols.sh
+++ /dev/null
@@ -1,642 +0,0 @@
-#!/bin/sh
-#############################################################################
-##
-## Copyright (C) 2016 The Qt Company Ltd.
-## Contact: https://www.qt.io/licensing/
-##
-## This file is part of the QtQml 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$
-##
-#############################################################################
-
-# Replaces deprecated QDeclarative symbol names with their replacements
-#
-# Changes instances in all regular files under the specified directory;
-# use on a clean source tree!
-
-if [ "$#" -lt "1" ]
-then
- echo " Usage: $0 <directory>"
- exit 1;
-fi
-
-MODIFY_DIR="$1"
-
-QML_SYMBOLS="\
- QDeclarativeAbstractBinding
- QDeclarativeAbstractBoundSignal
- QDeclarativeAbstractExpression
- QDeclarativeAccessible
- QDeclarativeAccessors
- QDeclarativeAccessorProperties
- QDeclarativeAnimationTimer
- QDeclarativeAssociationList
- QDeclarativeAttachedPropertiesFunc
- QDeclarativeBinding
- QDeclarativeBindingPrivate
- QDeclarativeBindingProfiler
- QDeclarativeBoundSignal
- QDeclarativeBoundSignalParameters
- QDeclarativeBoundSignalProxy
- QDeclarativeBuiltinFunctions
- QDeclarativeCleanup
- QDeclarativeColorValueType
- QDeclarativeCompiledData
- QDeclarativeCompiler
- QDeclarativeCompilerTypes
- QDeclarativeCompilingProfiler
- QDeclarativeComponent
- QDeclarativeComponentAttached
- QDeclarativeComponentExtension
- QDeclarativeComponentPrivate
- QDeclarativeComponent_setQmlParent
- QDeclarativeCompositeTypeData
- QDeclarativeConnectionsParser
- QDeclarativeContext
- QDeclarativeContextData
- QDeclarativeContextPrivate
- QDeclarativeCustomParser
- QDeclarativeCustomParserNode
- QDeclarativeCustomParserNodePrivate
- QDeclarativeCustomParserProperty
- QDeclarativeCustomParserPropertyPrivate
- QDeclarativeData
- QDeclarativeDataBlob
- QDeclarativeDataExtended
- QDeclarativeDataLoader
- QDeclarativeDataLoaderNetworkReplyProxy
- QDeclarativeDataLoaderThread
- QDeclarativeDateExtension
- QDeclarativeDataTest
- QDeclarativeDebug
- QDeclarativeDebugClient
- QDeclarativeDebugClientPrivate
- QDeclarativeDebugConnection
- QDeclarativeDebugConnectionPrivate
- QDeclarativeDebugContextReference
- QDeclarativeDebugData
- QDeclarativeDebugEngineReference
- QDeclarativeDebugEnginesQuery
- QDeclarativeDebugExpressionQuery
- QDeclarativeDebugFileReference
- QDeclarativeDebugger
- QDeclarativeDebuggingEnabler
- QDeclarativeDebugHelper
- QDeclarativeDebugObjectExpressionWatch
- QDeclarativeDebugObjectQuery
- QDeclarativeDebugObjectReference
- QDeclarativeDebugPropertyReference
- QDeclarativeDebugPropertyWatch
- QDeclarativeDebugQuery
- QDeclarativeDebugRootContextQuery
- QDeclarativeDebugServer
- QDeclarativeDebugServerConnection
- QDeclarativeDebugServerPrivate
- QDeclarativeDebugServerThread
- QDeclarativeDebugService
- QDeclarativeDebugServicePrivate
- QDeclarativeDebugStatesDelegate
- QDeclarativeDebugTrace
- QDeclarativeDebugWatch
- QDeclarativeDelayedError
- QDeclarativeDirComponents
- QDeclarativeDirParser
- QDeclarativeDirScripts
- QDeclarativeDOMNodeResource
- QDeclarativeEasingValueType
- QDeclarativeElement
- QDeclarativeEngine
- QDeclarativeEngineDebug
- QDeclarativeEngineDebugClient
- QDeclarativeEngineDebugService
- QDeclarativeEngineDebugPrivate
- QDeclarativeEnginePrivate
- QDeclarativeError
- QDeclarativeErrorPrivate
- QDeclarativeExpression
- QDeclarativeExpressionPrivate
- QDeclarativeExtensionInterface
- QDeclarativeExtensionPlugin
- QDeclarativeFontValueType
- QDeclarativeGraphics_DerivedObject
- QDeclarativeGuard
- QDeclarativeGuardedContextData
- QDeclarativeGuardImpl
- QDeclarativeHandlingSignalProfiler
- QDeclarativeImportDatabase
- QDeclarativeImportedNamespace
- QDeclarativeImports
- QDeclarativeImportsPrivate
- QDeclarativeIncubationController
- QDeclarativeIncubator
- QDeclarativeIncubatorController
- QDeclarativeIncubatorPrivate
- QDeclarativeIncubators
- QDeclarativeInfo
- QDeclarativeInfoPrivate
- QDeclarativeInspector
- QDeclarativeInspectorInterface
- QDeclarativeInspectorService
- QDeclarativeInstruction
- QDeclarativeInstructionData
- QDeclarativeInstructionMeta
- QDeclarativeIntegerCache
- QDeclarativeJavaScriptExpression
- QDeclarativeJavaScriptExpressionGuard
- QDeclarativeJS
- QDeclarativeJSGrammar
- QDeclarativeListProperty
- QDeclarativeListReference
- QDeclarativeListReferencePrivate
- QDeclarativeLocale
- QDeclarativeLocalStoragePlugin
- QDeclarativeMatrix4x4ValueType
- QDeclarativeMetaType
- QDeclarativeMetaTypeData
- QDeclarativeNetworkAccessManagerFactory
- QDeclarativeNotifier
- QDeclarativeNotifierEndpoint
- QDeclarativeNullableValue
- QDeclarativeNumberExtension
- QDeclarativeObjectCreatingProfiler
- QDeclarativeObjectData
- QDeclarativeObjectProperty
- QDeclarativeObserverMode
- QDeclarativeOpenMetaObject
- QDeclarativeOpenMetaObjectPrivate
- QDeclarativeOpenMetaObjectType
- QDeclarativeOpenMetaObjectTypePrivate
- QDeclarativeParser
- QDeclarativeParserStatus
- QDeclarativePointFValueType
- QDeclarativePointValueType
- QDeclarativePool
- QDeclarativePrivate
- QDeclarativeProfilerData
- QDeclarativeProfilerService
- QDeclarativeProperties
- QDeclarativeProperty
- QDeclarativePropertyCache
- QDeclarativePropertyCacheMethodArguments
- QDeclarativePropertyData
- QDeclarativePropertyMap
- QDeclarativePropertyMapMetaObject
- QDeclarativePropertyMapPrivate
- QDeclarativePropertyPrivate
- QDeclarativePropertyRawData
- QDeclarativePropertyValueInterceptor
- QDeclarativePropertyValueSource
- QDeclarativeProxyMetaObject
- QDeclarativeQmldirData
- QDeclarativeQtQuick1Module
- QDeclarativeQtQuick2Module
- QDeclarativeQtQuick2DebugStatesDelegate
- QDeclarativeQuaternionValueType
- QDeclarativeRectFValueType
- QDeclarativeRectValueType
- QDeclarativeRefCount
- QDeclarativeRefPointer
- QDeclarativeRegisterType
- QDeclarativeRewrite
- QDeclarativeScript
- QDeclarativeScriptBlob
- QDeclarativeScriptData
- QDeclarativeScriptPrivate
- QDeclarativeScriptString
- QDeclarativeScriptStringPrivate
- QDeclarativeSizeFValueType
- QDeclarativeSizeValueType
- QDeclarativeSqlDatabaseData
- QDeclarativeStringConverters
- QDeclarativeThread
- QDeclarativeThreadPrivate
- QDeclarativeTrace
- QDeclarativeType
- QDeclarativeTypeData
- QDeclarativeTypeInfo
- QDeclarativeTypeLoader
- QDeclarativeTypeModule
- QDeclarativeTypeModulePrivate
- QDeclarativeTypeModuleVersion
- QDeclarativeTypeNameCache
- QDeclarativeTypeNotAvailable
- QDeclarativeTypePrivate
- QDeclarativeTypesExtensionInterface
- QDeclarativeV8Function
- QDeclarativeV8Handle
- QDeclarativeValueType
- QDeclarativeValueTypeProxyBinding
- QDeclarativeValueTypeFactory
- QDeclarativeVector2DValueType
- QDeclarativeVector3DValueType
- QDeclarativeVector4DValueType
- QDeclarativeVME
- QDeclarativeVMEGuard
- QDeclarativeVMEMetaData
- QDeclarativeVMEMetaObject
- QDeclarativeVMEMetaObjectEndpoint
- QDeclarativeVMEVariant
- QDeclarativeVMETypes
- QDeclarativeWatcher
- QDeclarativeWatchProxy
- QDeclarativeXMLHttpRequest
- QDeclarativeXMLHttpRequestData
- QDeclarative_isFileCaseCorrect
- QDeclarative_setParent_noEvent
- QQuickProperties
- QQuickPropertyCacheMethodArguments
- QQuickPropertyData
-"
-
-QUICK_SYMBOLS="\
- QDeclarativeAbstractAnimation
- QDeclarativeAbstractAnimationAction
- QDeclarativeAbstractAnimationPrivate
- QDeclarativeAction
- QDeclarativeActionEvent
- QDeclarativeAnchors
- QDeclarativeAnimationController
- QDeclarativeAnimationControllerPrivate
- QDeclarativeAnimationGroup
- QDeclarativeAnimationGroupPrivate
- QDeclarativeAnimationPropertyUpdater
- QDeclarativeApplication
- QDeclarativeApplicationPrivate
- QDeclarativeBehavior
- QDeclarativeBehaviorPrivate
- QDeclarativeBind
- QDeclarativeBindPrivate
- QDeclarativeBulkValueAnimator
- QDeclarativeBulkValueUpdater
- QDeclarativeCachedBezier
- QDeclarativeChangeSet
- QDeclarativeColorAnimation
- QDeclarativeConnections
- QDeclarativeConnectionsPrivate
- QDeclarativeCurve
- QDeclarativeDefaultTextureFactory
- QDeclarativeFlick
- QDeclarativeFocusPanel
- QDeclarativeFolderListModel
- QDeclarativeFolderListModelPrivate
- QDeclarativeFontLoader
- QDeclarativeFontLoaderPrivate
- QDeclarativeFontObject
- QDeclarativeGestureArea
- QDeclarativeGestureAreaParser
- QDeclarativeGestureAreaPrivate
- QDeclarativeGraphics
- QDeclarativeImageProvider
- QDeclarativeImageProviderPrivate
- QDeclarativeItem
- QDeclarativeItemAccessor
- QDeclarativeItemChangeListener
- QDeclarativeItemKeyFilter
- QDeclarativeItemPrivate
- QDeclarativeListAccessor
- QDeclarativeListCompositor
- QDeclarativeListElement
- QDeclarativeListModel
- QDeclarativeListModelParser
- QDeclarativeListModelWorkerAgent
- QDeclarativeListView
- QDeclarativeNumberAnimation
- QDeclarativePackage
- QDeclarativePackageAttached
- QDeclarativePackagePrivate
- QDeclarativeParallelAnimation
- QDeclarativeParticle
- QDeclarativeParticleMotion
- QDeclarativeParticleMotionGravity
- QDeclarativeParticleMotionLinear
- QDeclarativeParticleMotionWander
- QDeclarativeParticles
- QDeclarativeParticlesPainter
- QDeclarativeParticlesPrivate
- QDeclarativePath
- QDeclarativePathArc
- QDeclarativePathAttribute
- QDeclarativePathCatmullRomCurve
- QDeclarativePathCubic
- QDeclarativePathCurve
- QDeclarativePathData
- QDeclarativePathElement
- QDeclarativePathInterpolator
- QDeclarativePathLine
- QDeclarativePathPercent
- QDeclarativePathPrivate
- QDeclarativePathQuad
- QDeclarativePathSvg
- QDeclarativePauseAnimation
- QDeclarativePauseAnimationPrivate
- QDeclarativePixmap
- QDeclarativePixmapData
- QDeclarativePixmapKey
- QDeclarativePixmapNull
- QDeclarativePixmapReader
- QDeclarativePixmapReaderThreadObject
- QDeclarativePixmapReply
- QDeclarativePixmapStore
- QDeclarativePropertyAction
- QDeclarativePropertyActionPrivate
- QDeclarativePropertyAnimation
- QDeclarativePropertyAnimationPrivate
- QDeclarativePropertyChanges
- QDeclarativePropertyChangesParser
- QDeclarativePropertyChangesPrivate
- QDeclarativeReplaceSignalHandler
- QDeclarativeRevertAction
- QDeclarativeRotationAnimation
- QDeclarativeRotationAnimationPrivate
- QDeclarativeSequentialAnimation
- QDeclarativeScriptAction
- QDeclarativeScriptActionPrivate
- QDeclarativeSetPropertyAnimationAction
- QDeclarativeSimpleAction
- QDeclarativeSmoothedAnimation
- QDeclarativeSmoothedAnimationPrivate
- QDeclarativeSpringAnimation
- QDeclarativeSpringAnimationPrivate
- QDeclarativeState
- QDeclarativeStateActions
- QDeclarativeStateChange
- QDeclarativeStateChangeScript
- QDeclarativeStateChangeScriptPrivate
- QDeclarativeStateGroup
- QDeclarativeStateGroupPrivate
- QDeclarativeStateOperation
- QDeclarativeStateOperationPrivate
- QDeclarativeStatePrivate
- QDeclarativeStyledText
- QDeclarativeStyledTextImgTag
- QDeclarativeStyledTextPrivate
- QDeclarativeSystemPalette
- QDeclarativeSystemPalettePrivate
- QDeclarativeTextureFactory
- QDeclarativeTimeLine
- QDeclarativeTimeLineCallback
- QDeclarativeTimeLineObject
- QDeclarativeTimeLinePrivate
- QDeclarativeTimeLineValue
- QDeclarativeTimeLineValueProxy
- QDeclarativeTimeLineValues
- QDeclarativeTimer
- QDeclarativeTimerPrivate
- QDeclarativeTransition
- QDeclarativeTransitionInstance
- QDeclarativeTransitionManager
- QDeclarativeTransitionManagerPrivate
- QDeclarativeTransitionPrivate
- QDeclarativeUtilModule
- QDeclarativeVector3dAnimation
- QDeclarativeView
- QDeclarativeViewInspector
- QDeclarativeViewInspectorPrivate
- QDeclarativeViewPrivate
- QDeclarativeWebView
- QDeclarativeXmlListModel
- QDeclarativeXmlListModelPrivate
- QDeclarativeXmlListModelRole
- QDeclarativeXmlListRange
- QDeclarativeXmlQueryEngine
- QDeclarativeXmlQueryResult
- QDeclarativeXmlQueryThreadObject
- QDeclarativeXmlRoleList
- QDeclarativeSvgParser
- QDeclarativeWorkerScript
- QDeclarativeWorkerScriptEngine
- QDeclarativeWorkerScriptEnginePrivate
-"
-
-QML_INCLUDE_FILES="\
- qdeclarativeaccessible.h
- qdeclarativeaccessors_p.h
- qdeclarativebinding_p.h
- qdeclarativebinding_p_p.h
- qdeclarativeboundsignal_p.h
- qdeclarativebuiltinfunctions_p.h
- qdeclarativecleanup_p.h
- qdeclarativecompiler_p.h
- qdeclarativecomponentattached_p.h
- qdeclarativecomponent.h
- qdeclarativecomponent_p.h
- qdeclarativecontext.h
- qdeclarativecontext_p.h
- qdeclarativecustomparser_p.h
- qdeclarativecustomparser_p_p.h
- qdeclarativedata_p.h
- qdeclarativedebugclient_p.h
- qdeclarativedebug.h
- qdeclarativedebughelper_p.h
- qdeclarativedebugserverconnection_p.h
- qdeclarativedebugserver_p.h
- qdeclarativedebugservice_p.h
- qdeclarativedebugservice_p_p.h
- qdeclarativedebugstatesdelegate_p.h
- qdeclarativedebugtrace_p.h
- qdeclarativedirparser_p.h
- qdeclarativeenginedebug_p.h
- qdeclarativeenginedebugservice_p.h
- qdeclarativeengine.h
- qdeclarativeengine_p.h
- qdeclarativeerror.h
- qdeclarativeexpression.h
- qdeclarativeexpression_p.h
- qdeclarativeextensioninterface.h
- qdeclarativeextensionplugin.h
- qdeclarativeglobal_p.h
- qdeclarativeguard_p.h
- qdeclarative.h
- qdeclarativeimageprovider.h
- qdeclarativeimport_p.h
- qdeclarativeincubator.h
- qdeclarativeincubator_p.h
- qdeclarativeinfo.h
- qdeclarativeinspectorinterface_p.h
- qdeclarativeinspectorprotocol.h
- qdeclarativeinspectorservice_p.h
- qdeclarativeinstruction_p.h
- qdeclarativeintegercache_p.h
- qdeclarativejsastfwd_p.h
- qdeclarativejsast_p.h
- qdeclarativejsastvisitor_p.h
- qdeclarativejsengine_p.h
- qdeclarativejsglobal_p.h
- qdeclarativejsgrammar_p.h
- qdeclarativejskeywords_p.h
- qdeclarativejslexer_p.h
- qdeclarativejsmemorypool_p.h
- qdeclarativejsparser_p.h
- qdeclarativelist.h
- qdeclarativelist_p.h
- qdeclarativelocale_p.h
- qdeclarativemetatype_p.h
- qdeclarativenetworkaccessmanagerfactory.h
- qdeclarativenotifier_p.h
- qdeclarativenullablevalue_p_p.h
- qdeclarativeopenmetaobject_p.h
- qdeclarativeparserstatus.h
- qdeclarativepool_p.h
- qdeclarativeprivate.h
- qdeclarativeprofilerservice_p.h
- qdeclarativepropertycache_p.h
- qdeclarativeproperty.h
- qdeclarativepropertymap.h
- qdeclarativeproperty_p.h
- qdeclarativepropertyvalueinterceptor_p.h
- qdeclarativepropertyvaluesource.h
- qdeclarativeproxymetaobject_p.h
- qdeclarativerefcount_p.h
- qdeclarativerewrite_p.h
- qdeclarativescript_p.h
- qdeclarativescriptstring.h
- qdeclarativescriptstring_p.h
- qdeclarativesqldatabase_p.h
- qdeclarativestringconverters_p.h
- qdeclarativethread_p.h
- qdeclarativetrace_p.h
- qdeclarativetypeloader_p.h
- qdeclarativetypenamecache_p.h
- qdeclarativetypenotavailable_p.h
- qdeclarativevaluetype_p.h
- qdeclarativevmemetaobject_p.h
- qdeclarativevme_p.h
- qdeclarativewatcher_p.h
- qdeclarativexmlhttprequest_p.h
- qdeclarativexmllistmodel_p.h
-"
-
-QUICK_INCLUDE_FILES="\
- qdeclarativeanimation_p.h
- qdeclarativeanimation_p_p.h
- qdeclarativeanimationcontroller_p.h
- qdeclarativeapplication_p.h
- qdeclarativebehavior_p.h
- qdeclarativebind_p.h
- qdeclarativechangeset_p.h
- qdeclarativeconnections_p.h
- qdeclarativefolderlistmodel.h
- qdeclarativefontloader_p.h
- qdeclarativelistaccessor_p.h
- qdeclarativelistcompositor_p.h
- qdeclarativelistmodel_p.h
- qdeclarativelistmodel_p_p.h
- qdeclarativelistmodelworkeragent_p.h
- qdeclarativepackage_p.h
- qdeclarativepathinterpolator_p.h
- qdeclarativepath_p.h
- qdeclarativepath_p_p.h
- qdeclarativepixmapcache_p.h
- qdeclarativepropertychanges_p.h
- qdeclarativesmoothedanimation_p.h
- qdeclarativesmoothedanimation_p_p.h
- qdeclarativespringanimation_p.h
- qdeclarativestategroup_p.h
- qdeclarativestateoperations_p.h
- qdeclarativestate_p.h
- qdeclarativestate_p_p.h
- qdeclarativestyledtext_p.h
- qdeclarativesvgparser_p.h
- qdeclarativesystempalette_p.h
- qdeclarativetimeline_p_p.h
- qdeclarativetimer_p.h
- qdeclarativetransitionmanager_p_p.h
- qdeclarativetransition_p.h
- qdeclarativeutilmodule_p.h
- qdeclarativeworkerscript_p.h
-"
-
-replaceMatch()
-{
- SYMBOL="$1"
- REPLACEMENT="$2"
- echo "Replacing $SYMBOL with $REPLACEMENT:"
-
- CONTAINERS=$(find "$MODIFY_DIR" ! -path ".git" -type f -print0 | xargs -0 grep -l -I "$SYMBOL")
- for CONTAINER in $CONTAINERS
- do
- echo " $CONTAINER"
- TMP_FILE="$CONTAINER.tmp"
-
- sed 's|'"$SYMBOL"'|'"$REPLACEMENT"'|g' <"$CONTAINER" >"$TMP_FILE"
- mv "$TMP_FILE" "$CONTAINER"
- done
- echo
-}
-
-for QML_SYMBOL in $QML_SYMBOLS
-do
- QML_REPLACEMENT="QQml${QML_SYMBOL#QDeclarative}"
- replaceMatch "\bQtDeclarative/$QML_SYMBOL\b" "QtQml/$QML_REPLACEMENT"
- replaceMatch "\b$QML_SYMBOL\b" "$QML_REPLACEMENT"
-done
-
-for QUICK_SYMBOL in $QUICK_SYMBOLS
-do
- QUICK_REPLACEMENT="QQuick${QUICK_SYMBOL#QDeclarative}"
- replaceMatch "\bQtDeclarative/$QUICK_SYMBOL\b" "QtQuick/$QUICK_REPLACEMENT"
- replaceMatch "\b$QUICK_SYMBOL\b" "$QUICK_REPLACEMENT"
-done
-
-for QML_INCLUDE_FILE in $QML_INCLUDE_FILES
-do
- QML_INCLUDE_REPLACEMENT="qqml${QML_INCLUDE_FILE#qdeclarative}"
- replaceMatch "\b$QML_INCLUDE_FILE\b" "$QML_INCLUDE_REPLACEMENT"
-done
-
-for QUICK_INCLUDE_FILE in $QUICK_INCLUDE_FILES
-do
- QUICK_INCLUDE_REPLACEMENT="qquick${QUICK_INCLUDE_FILE#qdeclarative}"
- replaceMatch "\b$QUICK_INCLUDE_FILE\b" "$QUICK_INCLUDE_REPLACEMENT"
-done
-
-# Various one-off replacements
-replaceMatch "\bQtDeclarative\b" "QtQml"
-replaceMatch "\basQDeclarativeContext\b" "asQQmlContext"
-replaceMatch "\basQDeclarativeContextPrivate\b" "asQQmlContextPrivate"
-
-# Replace any references to the 'declarative' module with 'qml'
-echo "Replacing module declarative with qml:"
-CONTAINERS=$(find "$MODIFY_DIR" \( -name \*\.pro -o -name \*\.pri \) -print0 | xargs -0 grep -l -I "\bdeclarative\b")
-for CONTAINER in $CONTAINERS
-do
- echo " $CONTAINER"
- TMP_FILE="$CONTAINER.tmp"
-
- # We only want to replace standalone 'declarative' and 'declarative-private' tokens
- sed 's|\([[:space:]]\+\)declarative\([[:space:]]\+\)|\1qml\2|g' <"$CONTAINER" | sed 's|\([[:space:]]\+\)declarative$|\1qml|g' | sed 's|\([[:space:]]\+\)declarative-private\([[:space:]]\+\)|\1qml-private\2|g' | sed 's|\([[:space:]]\+\)declarative-private$|\1qml-private|g' >"$TMP_FILE"
- mv "$TMP_FILE" "$CONTAINER"
-done
-echo
-
-echo "Replacements complete"
-exit 0
diff --git a/src/3rdparty/masm/stubs/compat/stdint.h b/config.tests/d3d12/d3d12.cpp
index 394a3d824f..0bf90cc457 100644
--- a/src/3rdparty/masm/stubs/compat/stdint.h
+++ b/config.tests/d3d12/d3d12.cpp
@@ -3,7 +3,7 @@
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
-** This file is part of the QtQml module of the Qt Toolkit.
+** This file is part of the QtQuick module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
@@ -36,18 +36,16 @@
** $QT_END_LICENSE$
**
****************************************************************************/
-#ifndef QSTDINT_WRAPPER_H
-#define QSTDINT_WRAPPER_H
-/* Needed for VS 2008 and earlier that don't ship stdint.h */
+#include <d3d12.h>
+#include <dxgi1_4.h>
+#include <wrl/client.h>
-typedef signed char int8_t;
-typedef signed short int16_t;
-typedef signed int int32_t;
-typedef signed long long int64_t;
-typedef unsigned char uint8_t;
-typedef unsigned short uint16_t;
-typedef unsigned int uint32_t;
-typedef unsigned long long uint64_t;
+using namespace Microsoft::WRL;
-#endif
+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..451e7427b9
--- /dev/null
+++ b/config.tests/d3d12/d3d12.pro
@@ -0,0 +1,4 @@
+SOURCES = d3d12.cpp
+CONFIG -= qt dylib
+CONFIG += console
+LIBS += -ldxgi -ld3d12 -ld3dcompiler -ldcomp
diff --git a/configure.json b/configure.json
new file mode 100644
index 0000000000..fd1f58863c
--- /dev/null
+++ b/configure.json
@@ -0,0 +1,6 @@
+{
+ "subconfigs": [
+ "src/qml",
+ "src/quick"
+ ]
+}
diff --git a/examples/qml/qml.pro b/examples/qml/qml.pro
index eb4c98e5c4..df73fe0200 100644
--- a/examples/qml/qml.pro
+++ b/examples/qml/qml.pro
@@ -1,9 +1,14 @@
TEMPLATE = subdirs
+QT_FOR_CONFIG += qml
-qtHaveModule(quick): SUBDIRS += \
- networkaccessmanagerfactory \
- qmlextensionplugins \
- xmlhttprequest
+qtHaveModule(quick) {
+ SUBDIRS += \
+ qmlextensionplugins \
+ xmlhttprequest
+
+ qtConfig(qml-network): \
+ SUBDIRS += networkaccessmanagerfactory
+}
SUBDIRS += \
referenceexamples \
diff --git a/examples/quick/demos/samegame/samegame.pro b/examples/quick/demos/samegame/samegame.pro
index e041dd61dd..0f01654d46 100644
--- a/examples/quick/demos/samegame/samegame.pro
+++ b/examples/quick/demos/samegame/samegame.pro
@@ -7,4 +7,4 @@ RESOURCES += samegame.qrc
target.path = $$[QT_INSTALL_EXAMPLES]/quick/demos/samegame
INSTALLS += target
-!contains(sql-drivers, sqlite): QTPLUGIN += qsqlite
+!qtConfig(sql-sqlite): QTPLUGIN += qsqlite
diff --git a/examples/quick/quick.pro b/examples/quick/quick.pro
index 63730e53da..445dfb0fab 100644
--- a/examples/quick/quick.pro
+++ b/examples/quick/quick.pro
@@ -16,7 +16,6 @@ SUBDIRS = quick-accessibility \
scenegraph \
shadereffects \
text \
- textureprovider \
threading \
touchinteraction \
tutorials \
@@ -25,8 +24,14 @@ SUBDIRS = quick-accessibility \
imageresponseprovider \
window \
particles \
- demos \
- rendercontrol
+ demos
+
+#OpenGL Support Required
+qtConfig(opengl(es1|es2)?) {
+ SUBDIRS += \
+ textureprovider \
+ rendercontrol
+}
# Widget dependent examples
qtHaveModule(widgets) {
diff --git a/examples/quick/quickwidgets/quickwidget/customgl.qml b/examples/quick/quickwidgets/quickwidget/customgl.qml
new file mode 100644
index 0000000000..81e33e1ac9
--- /dev/null
+++ b/examples/quick/quickwidgets/quickwidget/customgl.qml
@@ -0,0 +1,59 @@
+/****************************************************************************
+**
+** 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.0
+import QuickWidgetExample 1.0
+
+Rectangle {
+ color: "lightGray"
+
+ FbItem {
+ anchors.fill: parent
+ anchors.margins: 10
+ }
+
+ Text {
+ anchors.left: parent.left
+ anchors.bottom: parent.bottom
+ anchors.margins: 15
+ text: "QQuickFramebufferObject with animated clear color"
+ color: "white"
+ }
+}
diff --git a/examples/quick/quickwidgets/quickwidget/fbitem.cpp b/examples/quick/quickwidgets/quickwidget/fbitem.cpp
new file mode 100644
index 0000000000..fc2a4ea7ad
--- /dev/null
+++ b/examples/quick/quickwidgets/quickwidget/fbitem.cpp
@@ -0,0 +1,90 @@
+/****************************************************************************
+**
+** 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 "fbitem.h"
+#include <QtGui/QOpenGLFramebufferObject>
+#include <QtGui/QOpenGLContext>
+#include <QtGui/QOpenGLFunctions>
+#include <QtCore/QDebug>
+
+#ifndef QT_NO_OPENGL
+class FbRenderer : public QQuickFramebufferObject::Renderer
+{
+public:
+ FbRenderer() : c(0), dir(1) { }
+
+ // The lifetime of the FBO and this class depends on how QQuickWidget
+ // manages the scenegraph and context when it comes to showing and hiding
+ // the widget. The actual behavior is proven by the debug prints.
+ ~FbRenderer() {
+ qDebug("FbRenderer destroyed");
+ }
+
+ void render() {
+ QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions();
+ f->glClearColor(c, 0, 0, 1);
+ f->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
+ c += 0.01f * dir;
+ if (c >= 1.0f || c <= 0.0f)
+ dir *= -1;
+ update();
+ }
+
+ QOpenGLFramebufferObject *createFramebufferObject(const QSize &size) {
+ qDebug() << "Creating FBO" << size;
+ QOpenGLFramebufferObjectFormat format;
+ format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
+ return new QOpenGLFramebufferObject(size, format);
+ }
+
+private:
+ float c;
+ int dir;
+};
+#endif
+
+QQuickFramebufferObject::Renderer *FbItem::createRenderer() const
+{
+#ifndef QT_NO_OPENGL
+ return new FbRenderer;
+#else
+ return nullptr;
+#endif
+}
diff --git a/examples/quick/quickwidgets/quickwidget/fbitem.h b/examples/quick/quickwidgets/quickwidget/fbitem.h
new file mode 100644
index 0000000000..59280eb3b8
--- /dev/null
+++ b/examples/quick/quickwidgets/quickwidget/fbitem.h
@@ -0,0 +1,53 @@
+/****************************************************************************
+**
+** 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 FBITEM_H
+#define FBITEM_H
+
+#include <QtQuick/QQuickFramebufferObject>
+
+class FbItem : public QQuickFramebufferObject
+{
+ Q_OBJECT
+public:
+ Renderer *createRenderer() const;
+};
+
+#endif
diff --git a/examples/quick/quickwidgets/quickwidget/main.cpp b/examples/quick/quickwidgets/quickwidget/main.cpp
index 3f3638a43f..6bb6722e02 100644
--- a/examples/quick/quickwidgets/quickwidget/main.cpp
+++ b/examples/quick/quickwidgets/quickwidget/main.cpp
@@ -42,6 +42,7 @@
#include <QQuickItem>
#include <QQmlError>
#include <QtWidgets>
+#include "fbitem.h"
class MainWindow : public QMainWindow {
Q_OBJECT
@@ -54,6 +55,7 @@ private slots:
void grabFramebuffer();
void renderToPixmap();
void grabToImage();
+ void createQuickWidgetsInTabs(QMdiArea *mdiArea);
private:
QQuickWidget *m_quickWidget;
@@ -76,7 +78,7 @@ MainWindow::MainWindow()
QLCDNumber *lcd = new QLCDNumber;
lcd->display(1337);
lcd->setMinimumSize(250,100);
- centralWidget ->addSubWindow(lcd);
+ centralWidget->addSubWindow(lcd);
QUrl source("qrc:quickwidget/rotatingsquare.qml");
@@ -88,7 +90,7 @@ MainWindow::MainWindow()
m_quickWidget->setResizeMode(QQuickWidget::SizeRootObjectToView );
m_quickWidget->setSource(source);
- centralWidget ->addSubWindow(m_quickWidget);
+ centralWidget->addSubWindow(m_quickWidget);
setCentralWidget(centralWidget);
@@ -97,6 +99,34 @@ MainWindow::MainWindow()
fileMenu->addAction(tr("Render to pixmap"), this, &MainWindow::renderToPixmap);
fileMenu->addAction(tr("Grab via grabToImage"), this, &MainWindow::grabToImage);
fileMenu->addAction(tr("Quit"), qApp, &QCoreApplication::quit);
+
+ QMenu *windowMenu = menuBar()->addMenu(tr("&Window"));
+ windowMenu->addAction(tr("Add tab widget"), this,
+ [this, centralWidget] { createQuickWidgetsInTabs(centralWidget); });
+}
+
+void MainWindow::createQuickWidgetsInTabs(QMdiArea *mdiArea)
+{
+ QTabWidget *tabWidget = new QTabWidget;
+
+ const QSize size(400, 400);
+
+ QQuickWidget *w = new QQuickWidget;
+ w->resize(size);
+ w->setResizeMode(QQuickWidget::SizeRootObjectToView);
+ w->setSource(QUrl("qrc:quickwidget/rotatingsquaretab.qml"));
+
+ tabWidget->addTab(w, tr("Plain Quick content"));
+
+ w = new QQuickWidget;
+ w->resize(size);
+ w->setResizeMode(QQuickWidget::SizeRootObjectToView);
+ w->setSource(QUrl("qrc:quickwidget/customgl.qml"));
+
+ tabWidget->addTab(w, tr("Custom OpenGL drawing"));
+
+ mdiArea->addSubWindow(tabWidget);
+ tabWidget->show();
}
void MainWindow::quickWidgetStatusChanged(QQuickWidget::Status status)
@@ -153,6 +183,8 @@ int main(int argc, char **argv)
{
QApplication app(argc, argv);
+ qmlRegisterType<FbItem>("QuickWidgetExample", 1, 0, "FbItem");
+
MainWindow mainWindow;
mainWindow.show();
diff --git a/examples/quick/quickwidgets/quickwidget/quickwidget.pro b/examples/quick/quickwidgets/quickwidget/quickwidget.pro
index 04fb5541a7..5be006f7fa 100644
--- a/examples/quick/quickwidgets/quickwidget/quickwidget.pro
+++ b/examples/quick/quickwidgets/quickwidget/quickwidget.pro
@@ -3,7 +3,8 @@ QT += core gui quick widgets quickwidgets
TARGET = quickwidget
TEMPLATE = app
-SOURCES += main.cpp
+SOURCES += main.cpp fbitem.cpp
+HEADERS += fbitem.h
RESOURCES += quickwidget.qrc
diff --git a/examples/quick/quickwidgets/quickwidget/quickwidget.qrc b/examples/quick/quickwidgets/quickwidget/quickwidget.qrc
index c073b7b80d..85a49b75ca 100644
--- a/examples/quick/quickwidgets/quickwidget/quickwidget.qrc
+++ b/examples/quick/quickwidgets/quickwidget/quickwidget.qrc
@@ -1,5 +1,7 @@
<RCC>
<qresource prefix="/quickwidget">
<file>rotatingsquare.qml</file>
+ <file>rotatingsquaretab.qml</file>
+ <file>customgl.qml</file>
</qresource>
</RCC>
diff --git a/examples/quick/quickwidgets/quickwidget/rotatingsquaretab.qml b/examples/quick/quickwidgets/quickwidget/rotatingsquaretab.qml
new file mode 100644
index 0000000000..51c17b9ffb
--- /dev/null
+++ b/examples/quick/quickwidgets/quickwidget/rotatingsquaretab.qml
@@ -0,0 +1,66 @@
+/****************************************************************************
+**
+** 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.0
+
+Rectangle {
+ gradient: Gradient {
+ GradientStop { position: 0; color: "steelblue" }
+ GradientStop { position: 1; color: "black" }
+ }
+
+ Rectangle {
+ property int d: 100
+ id: square
+ width: d
+ height: d
+ anchors.centerIn: parent
+ color: "green"
+ NumberAnimation on rotation { from: 360; to: 0; duration: 4000; loops: Animation.Infinite; }
+ }
+
+ Text {
+ anchors.centerIn: parent
+ text: "Qt Quick running in a tab widget"
+ color: "purple"
+ font.bold: true
+ font.pointSize: 14
+ }
+}
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..a92a400922
--- /dev/null
+++ b/examples/quick/scenegraph/rendernode/customrenderitem.cpp
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** 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"
+#include "softwarerenderer.h"
+
+CustomRenderItem::CustomRenderItem(QQuickItem *parent)
+ : QQuickItem(parent)
+{
+ // Our item shows something so set the flag.
+ setFlag(ItemHasContents);
+}
+
+QSGNode *CustomRenderItem::updatePaintNode(QSGNode *node, UpdatePaintNodeData *)
+{
+ QSGRenderNode *n = static_cast<QSGRenderNode *>(node);
+ if (!n) {
+ QSGRendererInterface *ri = window()->rendererInterface();
+ if (!ri)
+ return nullptr;
+ switch (ri->graphicsApi()) {
+ case QSGRendererInterface::OpenGL:
+#ifndef QT_NO_OPENGL
+ n = new OpenGLRenderNode(this);
+ break;
+#endif
+ case QSGRendererInterface::Direct3D12:
+#if QT_CONFIG(d3d12)
+ n = new D3D12RenderNode(this);
+ break;
+#endif
+ case QSGRendererInterface::Software:
+ n = new SoftwareRenderNode(this);
+ break;
+
+ default:
+ return nullptr;
+ }
+ }
+
+ return n;
+}
diff --git a/examples/quick/scenegraph/rendernode/customrenderitem.h b/examples/quick/scenegraph/rendernode/customrenderitem.h
new file mode 100644
index 0000000000..43b0b00ed5
--- /dev/null
+++ b/examples/quick/scenegraph/rendernode/customrenderitem.h
@@ -0,0 +1,55 @@
+/****************************************************************************
+**
+** 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>
+
+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..3b377f1cb1
--- /dev/null
+++ b/examples/quick/scenegraph/rendernode/d3d12renderer.cpp
@@ -0,0 +1,283 @@
+/****************************************************************************
+**
+** 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 <QQuickItem>
+#include <QQuickWindow>
+#include <QSGRendererInterface>
+
+#if QT_CONFIG(d3d12)
+
+#include "vs_shader.hlslh"
+#include "ps_shader.hlslh"
+
+D3D12RenderNode::D3D12RenderNode(QQuickItem *item)
+ : m_item(item)
+{
+}
+
+D3D12RenderNode::~D3D12RenderNode()
+{
+ releaseResources();
+}
+
+void D3D12RenderNode::releaseResources()
+{
+ if (vbPtr) {
+ vertexBuffer->Unmap(0, nullptr);
+ vbPtr = nullptr;
+ }
+ if (cbPtr) {
+ constantBuffer->Unmap(0, nullptr);
+ cbPtr = nullptr;
+ }
+ constantBuffer = nullptr;
+ vertexBuffer = nullptr;
+ rootSignature = nullptr;
+ pipelineState = nullptr;
+ m_device = nullptr;
+}
+
+void D3D12RenderNode::init()
+{
+ QSGRendererInterface *rif = m_item->window()->rendererInterface();
+ m_device = static_cast<ID3D12Device *>(rif->getResource(m_item->window(), QSGRendererInterface::Device));
+ Q_ASSERT(m_device);
+
+ D3D12_ROOT_PARAMETER rootParameter;
+ rootParameter.ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
+ rootParameter.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
+ 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
+
+ // Enable color write and blending (premultiplied alpha). The latter is
+ // needed because the example changes the item's opacity and we pass
+ // inheritedOpacity() into the pixel shader. If that wasn't the case,
+ // blending could have stayed disabled.
+ 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,
+ D3D12_COLOR_WRITE_ENABLE_ALL
+ };
+ D3D12_BLEND_DESC blendDesc = {};
+ blendDesc.RenderTarget[0] = premulBlendDesc;
+
+ 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
+ // We are rendering on the default render target so if the QuickWindow/View
+ // has requested samples > 0 then we have to follow suit.
+ const uint samples = qMax(1, m_item->window()->format().samples());
+ psoDesc.SampleDesc.Count = samples;
+ if (samples > 1) {
+ D3D12_FEATURE_DATA_MULTISAMPLE_QUALITY_LEVELS msaaInfo = {};
+ msaaInfo.Format = psoDesc.RTVFormats[0];
+ msaaInfo.SampleCount = samples;
+ if (SUCCEEDED(m_device->CheckFeatureSupport(D3D12_FEATURE_MULTISAMPLE_QUALITY_LEVELS, &msaaInfo, sizeof(msaaInfo)))) {
+ if (msaaInfo.NumQualityLevels > 0)
+ psoDesc.SampleDesc.Quality = msaaInfo.NumQualityLevels - 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 = vertexBufferSize / 3;
+ 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;
+ }
+
+ float *vp = reinterpret_cast<float *>(vbPtr);
+ vp += 2;
+ *vp++ = 1.0f; *vp++ = 0.0f; *vp++ = 0.0f;
+ vp += 2;
+ *vp++ = 0.0f; *vp++ = 1.0f; *vp++ = 0.0f;
+ vp += 2;
+ *vp++ = 0.0f; *vp++ = 0.0f; *vp++ = 1.0f;
+}
+
+void D3D12RenderNode::render(const RenderState *state)
+{
+ if (!m_device)
+ init();
+
+ QSGRendererInterface *rif = m_item->window()->rendererInterface();
+ ID3D12GraphicsCommandList *commandList = static_cast<ID3D12GraphicsCommandList *>(rif->getResource(m_item->window(), QSGRendererInterface::CommandList));
+ Q_ASSERT(commandList);
+
+ const int msize = 16 * sizeof(float);
+ memcpy(cbPtr, matrix()->constData(), msize);
+ memcpy(cbPtr + msize, state->projectionMatrix()->constData(), msize);
+ const float opacity = inheritedOpacity();
+ memcpy(cbPtr + 2 * msize, &opacity, sizeof(float));
+
+ 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 += 3;
+ *vp++ = p1.x();
+ *vp++ = p1.y();
+ vp += 3;
+ *vp++ = p2.x();
+ *vp++ = p2.y();
+
+ 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);
+}
+
+// No need to reimplement changedStates() because no relevant commands are
+// added to the command list in render().
+
+QSGRenderNode::RenderingFlags D3D12RenderNode::flags() const
+{
+ return BoundedRectRendering | DepthAwareRendering;
+}
+
+QRectF D3D12RenderNode::rect() const
+{
+ return QRect(0, 0, m_item->width(), m_item->height());
+}
+
+#endif // d3d12
diff --git a/examples/quick/scenegraph/rendernode/d3d12renderer.h b/examples/quick/scenegraph/rendernode/d3d12renderer.h
new file mode 100644
index 0000000000..1d2726819f
--- /dev/null
+++ b/examples/quick/scenegraph/rendernode/d3d12renderer.h
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** 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 <qsgrendernode.h>
+
+#if QT_CONFIG(d3d12)
+
+class QQuickItem;
+
+#include <d3d12.h>
+#include <wrl/client.h>
+
+using namespace Microsoft::WRL;
+
+class D3D12RenderNode : public QSGRenderNode
+{
+public:
+ D3D12RenderNode(QQuickItem *item);
+ ~D3D12RenderNode();
+
+ void render(const RenderState *state) override;
+ void releaseResources() override;
+ RenderingFlags flags() const override;
+ QRectF rect() const override;
+
+private:
+ void init();
+
+ QQuickItem *m_item;
+ ID3D12Device *m_device = nullptr;
+ ComPtr<ID3D12PipelineState> pipelineState;
+ ComPtr<ID3D12RootSignature> rootSignature;
+ ComPtr<ID3D12Resource> vertexBuffer;
+ ComPtr<ID3D12Resource> constantBuffer;
+ D3D12_VERTEX_BUFFER_VIEW vertexBufferView;
+ quint8 *vbPtr = nullptr;
+ quint8 *cbPtr = nullptr;
+};
+
+#endif // d3d12
+
+#endif
diff --git a/examples/quick/scenegraph/rendernode/main.cpp b/examples/quick/scenegraph/rendernode/main.cpp
new file mode 100644
index 0000000000..3e1714313e
--- /dev/null
+++ b/examples/quick/scenegraph/rendernode/main.cpp
@@ -0,0 +1,65 @@
+/****************************************************************************
+**
+** 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;
+
+ if (QCoreApplication::arguments().contains(QStringLiteral("--multisample"))) {
+ QSurfaceFormat fmt;
+ fmt.setSamples(4);
+ view.setFormat(fmt);
+ }
+
+ 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..7c8d82181f
--- /dev/null
+++ b/examples/quick/scenegraph/rendernode/main.qml
@@ -0,0 +1,100 @@
+/****************************************************************************
+**
+** 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
+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.1; duration: 1000; easing.type: Easing.InOutCubic }
+ PauseAnimation { duration: 1000 }
+ NumberAnimation { target: renderer; property: "opacity"; to: 1.0; 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: GraphicsInfo.api
+ text: "Custom rendering via the graphics API "
+ + (api === GraphicsInfo.OpenGL ? "OpenGL"
+ : api === GraphicsInfo.Direct3D12 ? "Direct3D 12"
+ : api === GraphicsInfo.Software ? "Software" : "")
+ color: "yellow"
+ }
+ }
+}
diff --git a/examples/quick/scenegraph/rendernode/openglrenderer.cpp b/examples/quick/scenegraph/rendernode/openglrenderer.cpp
new file mode 100644
index 0000000000..3de864b7b9
--- /dev/null
+++ b/examples/quick/scenegraph/rendernode/openglrenderer.cpp
@@ -0,0 +1,164 @@
+/****************************************************************************
+**
+** 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"
+#include <QQuickItem>
+
+#ifndef QT_NO_OPENGL
+
+#include <QOpenGLShaderProgram>
+#include <QOpenGLBuffer>
+#include <QOpenGLFunctions>
+
+OpenGLRenderNode::OpenGLRenderNode(QQuickItem *item)
+ : m_item(item)
+{
+}
+
+OpenGLRenderNode::~OpenGLRenderNode()
+{
+ releaseResources();
+}
+
+void OpenGLRenderNode::releaseResources()
+{
+ delete m_program;
+ m_program = nullptr;
+ delete m_vbo;
+ m_vbo = nullptr;
+}
+
+void OpenGLRenderNode::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"
+ "uniform lowp float opacity;\n"
+ "void main() {\n"
+ " gl_FragColor = col * opacity;\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");
+ m_opacityUniform = m_program->uniformLocation("opacity");
+
+ const int VERTEX_SIZE = 6 * sizeof(GLfloat);
+
+ 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();
+}
+
+void OpenGLRenderNode::render(const RenderState *state)
+{
+ if (!m_program)
+ init();
+
+ QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions();
+
+ m_program->bind();
+ m_program->setUniformValue(m_matrixUniform, *state->projectionMatrix() * *matrix());
+ m_program->setUniformValue(m_opacityUniform, float(inheritedOpacity()));
+
+ 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);
+
+ // Note that clipping (scissor or stencil) is ignored in this example.
+
+ f->glEnable(GL_BLEND);
+ f->glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+
+ f->glDrawArrays(GL_TRIANGLES, 0, 3);
+}
+
+QSGRenderNode::StateFlags OpenGLRenderNode::changedStates() const
+{
+ return BlendState;
+}
+
+QSGRenderNode::RenderingFlags OpenGLRenderNode::flags() const
+{
+ return BoundedRectRendering | DepthAwareRendering;
+}
+
+QRectF OpenGLRenderNode::rect() const
+{
+ return QRect(0, 0, m_item->width(), m_item->height());
+}
+
+#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..92cc2bc72b
--- /dev/null
+++ b/examples/quick/scenegraph/rendernode/openglrenderer.h
@@ -0,0 +1,80 @@
+/****************************************************************************
+**
+** 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 <qsgrendernode.h>
+
+#ifndef QT_NO_OPENGL
+
+QT_BEGIN_NAMESPACE
+
+class QQuickItem;
+class QOpenGLShaderProgram;
+class QOpenGLBuffer;
+
+class OpenGLRenderNode : public QSGRenderNode
+{
+public:
+ OpenGLRenderNode(QQuickItem *item);
+ ~OpenGLRenderNode();
+
+ void render(const RenderState *state) override;
+ void releaseResources() override;
+ StateFlags changedStates() const override;
+ RenderingFlags flags() const override;
+ QRectF rect() const override;
+
+private:
+ void init();
+
+ QQuickItem *m_item;
+ QOpenGLShaderProgram *m_program = nullptr;
+ int m_matrixUniform;
+ int m_opacityUniform;
+ QOpenGLBuffer *m_vbo = nullptr;
+};
+
+QT_END_NAMESPACE
+
+#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..968902a5be
--- /dev/null
+++ b/examples/quick/scenegraph/rendernode/rendernode.pro
@@ -0,0 +1,38 @@
+QT += qml quick
+
+HEADERS += customrenderitem.h \
+ openglrenderer.h \
+ softwarerenderer.h
+
+SOURCES += customrenderitem.cpp \
+ openglrenderer.cpp \
+ softwarerenderer.cpp \
+ main.cpp
+
+RESOURCES += rendernode.qrc
+
+target.path = $$[QT_INSTALL_EXAMPLES]/quick/scenegraph/rendernode
+INSTALLS += target
+
+OTHER_FILES += \
+ main.qml \
+ shader.hlsl
+
+qtConfig(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..f300fe7aa5
--- /dev/null
+++ b/examples/quick/scenegraph/rendernode/shader.hlsl
@@ -0,0 +1,28 @@
+cbuffer ConstantBuffer : register(b0)
+{
+ float4x4 modelview;
+ float4x4 projection;
+ float opacity;
+};
+
+struct PSInput
+{
+ float4 position : SV_POSITION;
+ float3 color : COLOR;
+};
+
+PSInput VS_Simple(float4 position : POSITION, float3 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 float4(input.color, 1.0) * opacity;
+}
diff --git a/examples/quick/scenegraph/rendernode/softwarerenderer.cpp b/examples/quick/scenegraph/rendernode/softwarerenderer.cpp
new file mode 100644
index 0000000000..06e406874a
--- /dev/null
+++ b/examples/quick/scenegraph/rendernode/softwarerenderer.cpp
@@ -0,0 +1,101 @@
+/****************************************************************************
+**
+** 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 "softwarerenderer.h"
+#include <QQuickItem>
+#include <QQuickWindow>
+#include <QSGRendererInterface>
+#include <QPainter>
+
+SoftwareRenderNode::SoftwareRenderNode(QQuickItem *item)
+ : m_item(item)
+{
+}
+
+SoftwareRenderNode::~SoftwareRenderNode()
+{
+ releaseResources();
+}
+
+void SoftwareRenderNode::releaseResources()
+{
+}
+
+void SoftwareRenderNode::render(const RenderState *renderState)
+{
+ QSGRendererInterface *rif = m_item->window()->rendererInterface();
+ QPainter *p = static_cast<QPainter *>(rif->getResource(m_item->window(), QSGRendererInterface::Painter));
+ Q_ASSERT(p);
+
+ p->setTransform(matrix()->toTransform());
+ p->setOpacity(inheritedOpacity());
+ const QRegion *clipRegion = renderState->clipRegion();
+ if (clipRegion && !clipRegion->isEmpty())
+ p->setClipRegion(*clipRegion, Qt::IntersectClip);
+
+ const QPointF p0(m_item->width() - 1, m_item->height() - 1);
+ const QPointF p1(0, 0);
+ const QPointF p2(0, m_item->height() - 1);
+ QPainterPath path(p0);
+ path.lineTo(p1);
+ path.lineTo(p2);
+ path.closeSubpath();
+
+ QLinearGradient gradient(QPointF(0, 0), QPointF(m_item->width(), m_item->height()));
+ gradient.setColorAt(0, Qt::green);
+ gradient.setColorAt(1, Qt::red);
+
+ p->fillPath(path, gradient);
+}
+
+QSGRenderNode::StateFlags SoftwareRenderNode::changedStates() const
+{
+ return 0;
+}
+
+QSGRenderNode::RenderingFlags SoftwareRenderNode::flags() const
+{
+ return BoundedRectRendering;
+}
+
+QRectF SoftwareRenderNode::rect() const
+{
+ return QRect(0, 0, m_item->width(), m_item->height());
+}
diff --git a/examples/quick/scenegraph/rendernode/softwarerenderer.h b/examples/quick/scenegraph/rendernode/softwarerenderer.h
new file mode 100644
index 0000000000..e91ca92d88
--- /dev/null
+++ b/examples/quick/scenegraph/rendernode/softwarerenderer.h
@@ -0,0 +1,63 @@
+/****************************************************************************
+**
+** 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 SOFTWARERENDERER_H
+#define SOFTWARERENDERER_H
+
+#include <qsgrendernode.h>
+#include <QQuickItem>
+
+class SoftwareRenderNode : public QSGRenderNode
+{
+public:
+ SoftwareRenderNode(QQuickItem *item);
+ ~SoftwareRenderNode();
+
+ void render(const RenderState *state) override;
+ void releaseResources() override;
+ StateFlags changedStates() const override;
+ RenderingFlags flags() const override;
+ QRectF rect() const override;
+
+private:
+ QQuickItem *m_item;
+};
+
+#endif
diff --git a/examples/quick/scenegraph/scenegraph.pro b/examples/quick/scenegraph/scenegraph.pro
index cf50cdb903..e13e8198b0 100644
--- a/examples/quick/scenegraph/scenegraph.pro
+++ b/examples/quick/scenegraph/scenegraph.pro
@@ -1,14 +1,21 @@
TEMPLATE = subdirs
+
+qtConfig(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/examples/quick/shadereffects/content/shaders/+hlsl/blur.frag b/examples/quick/shadereffects/content/shaders/+hlsl/blur.frag
new file mode 100644
index 0000000000..481a238d2a
--- /dev/null
+++ b/examples/quick/shadereffects/content/shaders/+hlsl/blur.frag
@@ -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 main(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/examples/quick/shadereffects/content/shaders/+hlsl/colorize.frag b/examples/quick/shadereffects/content/shaders/+hlsl/colorize.frag
new file mode 100644
index 0000000000..d6e65b6b10
--- /dev/null
+++ b/examples/quick/shadereffects/content/shaders/+hlsl/colorize.frag
@@ -0,0 +1,17 @@
+cbuffer ConstantBuffer : register(b0)
+{
+ float4x4 qt_Matrix;
+ float qt_Opacity;
+ float4 tint;
+};
+
+Texture2D source : register(t0);
+SamplerState sourceSampler : register(s0);
+
+float4 main(float4 position : SV_POSITION, float2 coord : TEXCOORD0) : SV_TARGET
+{
+ float4 c = source.Sample(sourceSampler, coord);
+ float lo = min(min(c.x, c.y), c.z);
+ float hi = max(max(c.x, c.y), c.z);
+ return float4(lerp(float3(lo, lo, lo), float3(hi, hi, hi), tint.xyz), c.w) * qt_Opacity;
+}
diff --git a/examples/quick/shadereffects/content/shaders/+hlsl/genie.vert b/examples/quick/shadereffects/content/shaders/+hlsl/genie.vert
new file mode 100644
index 0000000000..40876e7996
--- /dev/null
+++ b/examples/quick/shadereffects/content/shaders/+hlsl/genie.vert
@@ -0,0 +1,31 @@
+cbuffer ConstantBuffer : register(b0)
+{
+ float4x4 qt_Matrix;
+ float qt_Opacity;
+ float bend;
+ float minimize;
+ float side;
+ float width;
+ float height;
+};
+
+struct PSInput
+{
+ float4 position : SV_POSITION;
+ float2 coord : TEXCOORD0;
+};
+
+PSInput main(float4 position : POSITION, float2 coord : TEXCOORD0)
+{
+ PSInput result;
+ result.coord = coord;
+
+ float4 pos = position;
+ pos.y = lerp(position.y, height, minimize);
+ float t = pos.y / height;
+ t = (3.0 - 2.0 * t) * t * t;
+ pos.x = lerp(position.x, side * width, t * bend);
+ result.position = mul(qt_Matrix, pos);
+
+ return result;
+}
diff --git a/examples/quick/shadereffects/content/shaders/+hlsl/outline.frag b/examples/quick/shadereffects/content/shaders/+hlsl/outline.frag
new file mode 100644
index 0000000000..b6e7e51f35
--- /dev/null
+++ b/examples/quick/shadereffects/content/shaders/+hlsl/outline.frag
@@ -0,0 +1,21 @@
+cbuffer ConstantBuffer : register(b0)
+{
+ float4x4 qt_Matrix;
+ float qt_Opacity;
+ float2 delta;
+};
+
+Texture2D source : register(t0);
+SamplerState sourceSampler : register(s0);
+
+float4 main(float4 position : SV_POSITION, float2 coord : TEXCOORD0) : SV_TARGET
+{
+ float4 tl = source.Sample(sourceSampler, coord - delta);
+ float4 tr = source.Sample(sourceSampler, coord + float2(delta.x, -delta.y));
+ float4 bl = source.Sample(sourceSampler, coord - float2(delta.x, -delta.y));
+ float4 br = source.Sample(sourceSampler, coord + delta);
+ float4 gx = (tl + bl) - (tr + br);
+ float4 gy = (tl + tr) - (bl + br);
+ return float4(0.0, 0.0, 0.0,
+ clamp(dot(sqrt(gx * gx + gy * gy), float4(1.0, 1.0, 1.0, 1.0)), 0.0, 1.0) * qt_Opacity);
+}
diff --git a/examples/quick/shadereffects/content/shaders/+hlsl/shadow.frag b/examples/quick/shadereffects/content/shaders/+hlsl/shadow.frag
new file mode 100644
index 0000000000..a86a25e007
--- /dev/null
+++ b/examples/quick/shadereffects/content/shaders/+hlsl/shadow.frag
@@ -0,0 +1,20 @@
+cbuffer ConstantBuffer : register(b0)
+{
+ float4x4 qt_Matrix;
+ float qt_Opacity;
+ float2 offset;
+ float2 delta;
+ float darkness;
+};
+
+Texture2D source : register(t0);
+SamplerState sourceSampler : register(s0);
+Texture2D shadow : register(t1);
+SamplerState shadowSampler : register(s1);
+
+float4 main(float4 position : SV_POSITION, float2 coord : TEXCOORD0) : SV_TARGET
+{
+ float4 fg = source.Sample(sourceSampler, coord);
+ float4 bg = shadow.Sample(shadowSampler, coord + delta);
+ return (fg + float4(0.0, 0.0, 0.0, darkness * bg.a) * (1.0 - fg.a)) * qt_Opacity;
+}
diff --git a/examples/quick/shadereffects/content/shaders/+hlsl/wobble.frag b/examples/quick/shadereffects/content/shaders/+hlsl/wobble.frag
new file mode 100644
index 0000000000..c28612a2fd
--- /dev/null
+++ b/examples/quick/shadereffects/content/shaders/+hlsl/wobble.frag
@@ -0,0 +1,17 @@
+cbuffer ConstantBuffer : register(b0)
+{
+ float4x4 qt_Matrix;
+ float qt_Opacity;
+ float amplitude;
+ float frequency;
+ float time;
+};
+
+Texture2D source : register(t0);
+SamplerState sourceSampler : register(s0);
+
+float4 main(float4 position : SV_POSITION, float2 coord : TEXCOORD0) : SV_TARGET
+{
+ float2 p = sin(time + frequency * coord);
+ return source.Sample(sourceSampler, coord + amplitude * float2(p.y, -p.x)) * qt_Opacity;
+}
diff --git a/examples/quick/shadereffects/content/shaders/blur.frag b/examples/quick/shadereffects/content/shaders/blur.frag
new file mode 100644
index 0000000000..9173945eed
--- /dev/null
+++ b/examples/quick/shadereffects/content/shaders/blur.frag
@@ -0,0 +1,14 @@
+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;
+}
diff --git a/examples/quick/shadereffects/content/shaders/colorize.frag b/examples/quick/shadereffects/content/shaders/colorize.frag
new file mode 100644
index 0000000000..1219ef2460
--- /dev/null
+++ b/examples/quick/shadereffects/content/shaders/colorize.frag
@@ -0,0 +1,12 @@
+uniform sampler2D source;
+uniform lowp vec4 tint;
+uniform lowp float qt_Opacity;
+
+varying highp vec2 qt_TexCoord0;
+
+void main() {
+ lowp vec4 c = texture2D(source, qt_TexCoord0);
+ lowp float lo = min(min(c.x, c.y), c.z);
+ lowp float hi = max(max(c.x, c.y), c.z);
+ gl_FragColor = qt_Opacity * vec4(mix(vec3(lo), vec3(hi), tint.xyz), c.w);
+}
diff --git a/examples/quick/shadereffects/content/shaders/genie.vert b/examples/quick/shadereffects/content/shaders/genie.vert
new file mode 100644
index 0000000000..3ce371819f
--- /dev/null
+++ b/examples/quick/shadereffects/content/shaders/genie.vert
@@ -0,0 +1,21 @@
+attribute highp vec4 qt_Vertex;
+attribute highp vec2 qt_MultiTexCoord0;
+
+uniform highp mat4 qt_Matrix;
+uniform highp float bend;
+uniform highp float minimize;
+uniform highp float side;
+uniform highp float width;
+uniform highp float height;
+
+varying highp vec2 qt_TexCoord0;
+
+void main() {
+ qt_TexCoord0 = qt_MultiTexCoord0;
+ highp vec4 pos = qt_Vertex;
+ pos.y = mix(qt_Vertex.y, height, minimize);
+ highp float t = pos.y / height;
+ t = (3. - 2. * t) * t * t;
+ pos.x = mix(qt_Vertex.x, side * width, t * bend);
+ gl_Position = qt_Matrix * pos;
+}
diff --git a/examples/quick/shadereffects/content/shaders/outline.frag b/examples/quick/shadereffects/content/shaders/outline.frag
new file mode 100644
index 0000000000..9b46719873
--- /dev/null
+++ b/examples/quick/shadereffects/content/shaders/outline.frag
@@ -0,0 +1,16 @@
+uniform sampler2D source;
+uniform highp vec2 delta;
+uniform highp float qt_Opacity;
+
+varying highp vec2 qt_TexCoord0;
+
+void main() {
+ lowp vec4 tl = texture2D(source, qt_TexCoord0 - delta);
+ lowp vec4 tr = texture2D(source, qt_TexCoord0 + vec2(delta.x, -delta.y));
+ lowp vec4 bl = texture2D(source, qt_TexCoord0 - vec2(delta.x, -delta.y));
+ lowp vec4 br = texture2D(source, qt_TexCoord0 + delta);
+ mediump vec4 gx = (tl + bl) - (tr + br);
+ mediump vec4 gy = (tl + tr) - (bl + br);
+ gl_FragColor.xyz = vec3(0.);
+ gl_FragColor.w = clamp(dot(sqrt(gx * gx + gy * gy), vec4(1.)), 0., 1.) * qt_Opacity;
+}
diff --git a/examples/quick/shadereffects/content/shaders/shadow.frag b/examples/quick/shadereffects/content/shaders/shadow.frag
new file mode 100644
index 0000000000..8650ee4f4c
--- /dev/null
+++ b/examples/quick/shadereffects/content/shaders/shadow.frag
@@ -0,0 +1,14 @@
+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;
+}
diff --git a/examples/quick/shadereffects/content/shaders/wobble.frag b/examples/quick/shadereffects/content/shaders/wobble.frag
new file mode 100644
index 0000000000..fedbb68254
--- /dev/null
+++ b/examples/quick/shadereffects/content/shaders/wobble.frag
@@ -0,0 +1,13 @@
+uniform lowp float qt_Opacity;
+uniform highp float amplitude;
+uniform highp float frequency;
+uniform highp float time;
+uniform sampler2D source;
+
+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;
+}
diff --git a/examples/quick/shadereffects/doc/src/shadereffects.qdoc b/examples/quick/shadereffects/doc/src/shadereffects.qdoc
index dc2a2681f5..7b1d68ebb5 100644
--- a/examples/quick/shadereffects/doc/src/shadereffects.qdoc
+++ b/examples/quick/shadereffects/doc/src/shadereffects.qdoc
@@ -51,6 +51,21 @@
shader:
\snippet shadereffects/shadereffects.qml fragment
+ In order to support multiple graphics APIs, not just OpenGL, the shader
+ source is not embedded into QML. Instead, file selectors are used to select
+ the correct variant at runtime. Based on the Qt Quick backend in use, Qt
+ will automatically select either \c{shaders/wobble.frag} with the GLSL
+ source code or \c{shaders/+hlsl/wobble.frag} with the HLSL source code.
+
+ \note For simplicity shader source code is used in all variants of the
+ files. However, with the Direct3D backend of Qt Quick pre-compiled shaders
+ are also supported. For example, try the following commands in the
+ \c{content/shaders/+hlsl} directory: \c{move wobble.frag wobble.frag.src}
+ followed by \c{fxc /E main /T ps_5_0 /Fo wobble.frag wobble.frag.src}. Now
+ \c wobble.frag contains Direct3D bytecode and that is what gets shipped
+ with the application instead of the shader source. Further changes are not
+ necessary, the application will function like before.
+
You can use any custom property on the ShaderEffect in your shader. This
makes animated shader code very easy:
\snippet shadereffects/shadereffects.qml properties
diff --git a/examples/quick/shadereffects/shadereffects.qml b/examples/quick/shadereffects/shadereffects.qml
index 926394ed0f..0c24a7bbf2 100644
--- a/examples/quick/shadereffects/shadereffects.qml
+++ b/examples/quick/shadereffects/shadereffects.qml
@@ -101,7 +101,7 @@ Rectangle {
height: 140
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
- font.pixelSize: 120
+ font.pixelSize: 118
font.family: "Times"
color: "blue"
text: "Qt"
@@ -128,17 +128,7 @@ Rectangle {
property real time: 0
NumberAnimation on time { loops: Animation.Infinite; from: 0; to: Math.PI * 2; duration: 600 }
//! [fragment]
- fragmentShader:
- "uniform lowp float qt_Opacity;" +
- "uniform highp float amplitude;" +
- "uniform highp float frequency;" +
- "uniform highp float time;" +
- "uniform sampler2D source;" +
- "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;" +
- "}"
+ fragmentShader: "qrc:shadereffects/content/shaders/wobble.frag"
//! [fragment]
Slider {
id: wobbleSlider
@@ -163,32 +153,10 @@ Rectangle {
height: theItem.height
property variant delta: Qt.size(1.0 / width, 0.0)
property variant source: theSource
- fragmentShader: "
- 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;
- }"
+ fragmentShader: "qrc:shadereffects/content/shaders/blur.frag"
}
}
- fragmentShader: "
- 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;
- }"
+ fragmentShader: "qrc:shadereffects/content/shaders/blur.frag"
}
}
property real angle: 0
@@ -196,19 +164,7 @@ Rectangle {
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: shadowSlider.value
- fragmentShader: "
- 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;
- }"
+ fragmentShader: "qrc:shadereffects/content/shaders/shadow.frag"
Slider {
id: shadowSlider
anchors.left: parent.left
@@ -222,38 +178,14 @@ Rectangle {
height: 160
property variant source: theSource
property variant delta: Qt.size(0.5 / width, 0.5 / height)
- fragmentShader: "
- uniform sampler2D source;
- uniform highp vec2 delta;
- uniform highp float qt_Opacity;
- varying highp vec2 qt_TexCoord0;
- void main() {
- lowp vec4 tl = texture2D(source, qt_TexCoord0 - delta);
- lowp vec4 tr = texture2D(source, qt_TexCoord0 + vec2(delta.x, -delta.y));
- lowp vec4 bl = texture2D(source, qt_TexCoord0 - vec2(delta.x, -delta.y));
- lowp vec4 br = texture2D(source, qt_TexCoord0 + delta);
- mediump vec4 gx = (tl + bl) - (tr + br);
- mediump vec4 gy = (tl + tr) - (bl + br);
- gl_FragColor.xyz = vec3(0.);
- gl_FragColor.w = clamp(dot(sqrt(gx * gx + gy * gy), vec4(1.)), 0., 1.) * qt_Opacity;
- }"
+ fragmentShader: "qrc:shadereffects/content/shaders/outline.frag"
}
ShaderEffect {
width: 160
height: 160
property variant source: theSource
property color tint: root.sliderToColor(colorizeSlider.value)
- fragmentShader: "
- uniform sampler2D source;
- uniform lowp vec4 tint;
- uniform lowp float qt_Opacity;
- varying highp vec2 qt_TexCoord0;
- void main() {
- lowp vec4 c = texture2D(source, qt_TexCoord0);
- lowp float lo = min(min(c.x, c.y), c.z);
- lowp float hi = max(max(c.x, c.y), c.z);
- gl_FragColor = qt_Opacity * vec4(mix(vec3(lo), vec3(hi), tint.xyz), c.w);
- }"
+ fragmentShader: "qrc:shadereffects/content/shaders/colorize.frag"
Slider {
id: colorizeSlider
anchors.left: parent.left
@@ -288,25 +220,7 @@ Rectangle {
//! [properties]
//! [vertex]
mesh: Qt.size(10, 10)
- vertexShader: "
- uniform highp mat4 qt_Matrix;
- uniform highp float bend;
- uniform highp float minimize;
- uniform highp float side;
- uniform highp float width;
- uniform highp float height;
- attribute highp vec4 qt_Vertex;
- attribute highp vec2 qt_MultiTexCoord0;
- varying highp vec2 qt_TexCoord0;
- void main() {
- qt_TexCoord0 = qt_MultiTexCoord0;
- highp vec4 pos = qt_Vertex;
- pos.y = mix(qt_Vertex.y, height, minimize);
- highp float t = pos.y / height;
- t = (3. - 2. * t) * t * t;
- pos.x = mix(qt_Vertex.x, side * width, t * bend);
- gl_Position = qt_Matrix * pos;
- }"
+ vertexShader: "qrc:shadereffects/content/shaders/genie.vert"
//! [vertex]
Slider {
id: genieSlider
diff --git a/examples/quick/shadereffects/shadereffects.qrc b/examples/quick/shadereffects/shadereffects.qrc
index ff296a0155..e66b98a6df 100644
--- a/examples/quick/shadereffects/shadereffects.qrc
+++ b/examples/quick/shadereffects/shadereffects.qrc
@@ -4,5 +4,17 @@
<file>content/face-smile.png</file>
<file>content/qt-logo.png</file>
<file>content/Slider.qml</file>
+ <file>content/shaders/wobble.frag</file>
+ <file>content/shaders/+hlsl/wobble.frag</file>
+ <file>content/shaders/blur.frag</file>
+ <file>content/shaders/+hlsl/blur.frag</file>
+ <file>content/shaders/shadow.frag</file>
+ <file>content/shaders/+hlsl/shadow.frag</file>
+ <file>content/shaders/outline.frag</file>
+ <file>content/shaders/+hlsl/outline.frag</file>
+ <file>content/shaders/colorize.frag</file>
+ <file>content/shaders/+hlsl/colorize.frag</file>
+ <file>content/shaders/genie.vert</file>
+ <file>content/shaders/+hlsl/genie.vert</file>
</qresource>
</RCC>
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/src/3rdparty/masm/LICENSE b/src/3rdparty/masm/LICENSE
new file mode 100644
index 0000000000..3ab4db6d84
--- /dev/null
+++ b/src/3rdparty/masm/LICENSE
@@ -0,0 +1,22 @@
+Copyright (C) 2012 Apple Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. 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.
+
+THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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.
diff --git a/src/3rdparty/masm/masm-defs.pri b/src/3rdparty/masm/masm-defs.pri
index c4c7d3ce9a..fa0d3d3c55 100644
--- a/src/3rdparty/masm/masm-defs.pri
+++ b/src/3rdparty/masm/masm-defs.pri
@@ -24,7 +24,8 @@ INCLUDEPATH += $$PWD
disassembler {
if(isEqual(QT_ARCH, "i386")|isEqual(QT_ARCH, "x86_64")): DEFINES += WTF_USE_UDIS86=1
- if(isEqual(QT_ARCH, "arm")): DEFINES += WTF_USE_ARMV7_DISASSEMBLER=1 WTF_USE_ARM64_DISASSEMBLER=1
+ if(isEqual(QT_ARCH, "arm")): DEFINES += WTF_USE_ARMV7_DISASSEMBLER=1
+ if(isEqual(QT_ARCH, "arm64")): DEFINES += WTF_USE_ARM64_DISASSEMBLER=1
if(isEqual(QT_ARCH, "mips")): DEFINES += WTF_USE_MIPS32_DISASSEMBLER=1
} else {
DEFINES += WTF_USE_UDIS86=0
@@ -38,6 +39,4 @@ INCLUDEPATH += $$PWD/disassembler
INCLUDEPATH += $$PWD/disassembler/udis86
INCLUDEPATH += $$_OUT_PWD
-win32-msvc2008|wince*: INCLUDEPATH += $$PWD/stubs/compat
-
CONFIG(release, debug|release): DEFINES += NDEBUG
diff --git a/src/3rdparty/masm/qt_attribution.json b/src/3rdparty/masm/qt_attribution.json
new file mode 100644
index 0000000000..c53f1a4bc5
--- /dev/null
+++ b/src/3rdparty/masm/qt_attribution.json
@@ -0,0 +1,23 @@
+{
+ "Id": "masm",
+ "Name": "JavaScriptCore Macro Assembler",
+ "QDocModule": "qtqml",
+ "QtUsage": "Used in Qt QML.",
+
+ "License": "BSD 2-clause \"Simplified\" License",
+ "LicenseId": "BSD-2-Clause",
+ "LicenseFile": "LICENSE",
+ "Copyright": "Copyright (C) 2003-2015 Apple Inc. All rights reserved.
+Copyright (C) 2007 Justin Haygood (jhaygood@reaktix.com)
+Copyright (C) 2007-2009 Torch Mobile, Inc. All rights reserved. (http://www.torchmobile.com/)
+Copyright (C) 2009, 2010 University of Szeged
+Copyright (C) 2009-2011 STMicroelectronics. All rights reserved.
+Copyright (C) 2010 MIPS Technologies, Inc. All rights reserved.
+Copyright (C) 2010 Peter Varga (pvarga@inf.u-szeged.hu), University of Szeged
+Copyright (C) 2010 MIPS Technologies, Inc. All rights reserved.
+Copyright (C) 2010, 2011 Research In Motion Limited. All rights reserved.
+Copyright (C) 2011 Google Inc. All rights reserved.
+Copyright (C) 2013 Samsung Electronics. All rights reserved.
+Copyright (C) 2015 Cisco Systems, Inc. All rights reserved.
+Copyright (c) 2002-2009 Vivek Thampi"
+}
diff --git a/src/3rdparty/masm/wtf/StdLibExtras.h b/src/3rdparty/masm/wtf/StdLibExtras.h
index 605f98ec82..f0d792ed52 100644
--- a/src/3rdparty/masm/wtf/StdLibExtras.h
+++ b/src/3rdparty/masm/wtf/StdLibExtras.h
@@ -166,7 +166,7 @@ template<typename T> char (&ArrayLengthHelperFunction(T (&)[0]))[0];
// Efficient implementation that takes advantage of powers of two.
inline size_t roundUpToMultipleOf(size_t divisor, size_t x)
{
- ASSERT(divisor && !(divisor & (divisor - 1)));
+ Q_ASSERT(divisor && !(divisor & (divisor - 1)));
size_t remainderMask = divisor - 1;
return (x + remainderMask) & ~remainderMask;
}
diff --git a/src/imports/builtins/builtins.qmltypes b/src/imports/builtins/builtins.qmltypes
index cca1c20d54..88e3ac6634 100644
--- a/src/imports/builtins/builtins.qmltypes
+++ b/src/imports/builtins/builtins.qmltypes
@@ -198,9 +198,7 @@ Module {
"MacWindowToolBarButtonHint": 268435456,
"BypassGraphicsProxyWidget": 536870912,
"NoDropShadowWindowHint": 1073741824,
- "WindowFullscreenButtonHint": -2147483648,
- "WindowOkButtonHint": 524288,
- "WindowCancelButtonHint": 1048576
+ "WindowFullscreenButtonHint": -2147483648
}
}
Enum {
@@ -243,9 +241,7 @@ Module {
"MacWindowToolBarButtonHint": 268435456,
"BypassGraphicsProxyWidget": 536870912,
"NoDropShadowWindowHint": 1073741824,
- "WindowFullscreenButtonHint": -2147483648,
- "WindowOkButtonHint": 524288,
- "WindowCancelButtonHint": 1048576
+ "WindowFullscreenButtonHint": -2147483648
}
}
Enum {
@@ -422,6 +418,7 @@ Module {
"AA_DontShowIconsInMenus": 2,
"AA_NativeWindows": 3,
"AA_DontCreateNativeWidgetSiblings": 4,
+ "AA_PluginApplication": 5,
"AA_MacPluginApplication": 5,
"AA_DontUseNativeMenuBar": 6,
"AA_MacDontSwapCtrlAndMeta": 7,
@@ -438,7 +435,12 @@ Module {
"AA_SetPalette": 19,
"AA_EnableHighDpiScaling": 20,
"AA_DisableHighDpiScaling": 21,
- "AA_AttributeCount": 22
+ "AA_UseStyleSheetPropagationInWidgetStyles": 22,
+ "AA_DontUseNativeDialogs": 23,
+ "AA_SynthesizeMouseForUnhandledTabletEvents": 24,
+ "AA_CompressHighFrequencyEvents": 25,
+ "AA_DontCheckOpenGLContextThreadAffinity": 26,
+ "AA_AttributeCount": 27
}
}
Enum {
@@ -1274,8 +1276,10 @@ Module {
"ImTextBeforeCursor": 2048,
"ImTextAfterCursor": 4096,
"ImEnterKeyType": 8192,
+ "ImAnchorRectangle": 16384,
+ "ImInputItemClipRectangle": 32768,
"ImPlatformData": -2147483648,
- "ImQueryInput": 186,
+ "ImQueryInput": 16570,
"ImQueryAll": -1
}
}
@@ -1297,8 +1301,10 @@ Module {
"ImTextBeforeCursor": 2048,
"ImTextAfterCursor": 4096,
"ImEnterKeyType": 8192,
+ "ImAnchorRectangle": 16384,
+ "ImInputItemClipRectangle": 32768,
"ImPlatformData": -2147483648,
- "ImQueryInput": 186,
+ "ImQueryInput": 16570,
"ImQueryAll": -1
}
}
@@ -1579,6 +1585,7 @@ Module {
Enum {
name: "ScrollPhase"
values: {
+ "NoScrollPhase": 0,
"ScrollBegin": 1,
"ScrollUpdate": 2,
"ScrollEnd": 3
diff --git a/src/imports/folderlistmodel/fileinfothread.cpp b/src/imports/folderlistmodel/fileinfothread.cpp
index 5d911eec1e..0b62935f87 100644
--- a/src/imports/folderlistmodel/fileinfothread.cpp
+++ b/src/imports/folderlistmodel/fileinfothread.cpp
@@ -260,14 +260,13 @@ void FileInfoThread::getFileInfos(const QString &path)
sortFlags = sortFlags | QDir::DirsFirst;
QDir currentDir(path, QString(), sortFlags);
- QFileInfoList fileInfoList;
QList<FileProperty> filePropertyList;
- fileInfoList = currentDir.entryInfoList(nameFilters, filter, sortFlags);
+ const QFileInfoList fileInfoList = currentDir.entryInfoList(nameFilters, filter, sortFlags);
if (!fileInfoList.isEmpty()) {
filePropertyList.reserve(fileInfoList.count());
- foreach (const QFileInfo &info, fileInfoList) {
+ for (const QFileInfo &info : fileInfoList) {
//qDebug() << "Adding file : " << info.fileName() << "to list ";
filePropertyList << FileProperty(info);
}
diff --git a/src/imports/folderlistmodel/plugin.cpp b/src/imports/folderlistmodel/plugin.cpp
index 69d8c41698..affde1c3aa 100644
--- a/src/imports/folderlistmodel/plugin.cpp
+++ b/src/imports/folderlistmodel/plugin.cpp
@@ -59,7 +59,7 @@ class QmlFolderListModelPlugin : public QQmlExtensionPlugin
public:
QmlFolderListModelPlugin(QObject *parent = 0) : QQmlExtensionPlugin(parent) { initResources(); }
- virtual void registerTypes(const char *uri)
+ void registerTypes(const char *uri) Q_DECL_OVERRIDE
{
Q_ASSERT(QLatin1String(uri) == QLatin1String("Qt.labs.folderlistmodel"));
#ifndef QT_NO_DIRMODEL
diff --git a/src/imports/folderlistmodel/plugins.qmltypes b/src/imports/folderlistmodel/plugins.qmltypes
index 02127c63cb..e77b633932 100644
--- a/src/imports/folderlistmodel/plugins.qmltypes
+++ b/src/imports/folderlistmodel/plugins.qmltypes
@@ -4,284 +4,20 @@ import QtQuick.tooling 1.2
// It is used for QML tooling purposes only.
//
// This file was auto-generated by:
-// 'qmlplugindump -nonrelocatable Qt.labs.folderlistmodel 2.1'
+// 'qmlplugindump -nonrelocatable Qt.labs.folderlistmodel 2.2'
Module {
- dependencies: []
- Component {
- name: "QAbstractItemModel"
- prototype: "QObject"
- Enum {
- name: "LayoutChangeHint"
- values: {
- "NoLayoutChangeHint": 0,
- "VerticalSortHint": 1,
- "HorizontalSortHint": 2
- }
- }
- Signal {
- name: "dataChanged"
- Parameter { name: "topLeft"; type: "QModelIndex" }
- Parameter { name: "bottomRight"; type: "QModelIndex" }
- Parameter { name: "roles"; type: "QVector<int>" }
- }
- Signal {
- name: "dataChanged"
- Parameter { name: "topLeft"; type: "QModelIndex" }
- Parameter { name: "bottomRight"; type: "QModelIndex" }
- }
- Signal {
- name: "headerDataChanged"
- Parameter { name: "orientation"; type: "Qt::Orientation" }
- Parameter { name: "first"; type: "int" }
- Parameter { name: "last"; type: "int" }
- }
- Signal {
- name: "layoutChanged"
- Parameter { name: "parents"; type: "QList<QPersistentModelIndex>" }
- Parameter { name: "hint"; type: "QAbstractItemModel::LayoutChangeHint" }
- }
- Signal {
- name: "layoutChanged"
- Parameter { name: "parents"; type: "QList<QPersistentModelIndex>" }
- }
- Signal { name: "layoutChanged" }
- Signal {
- name: "layoutAboutToBeChanged"
- Parameter { name: "parents"; type: "QList<QPersistentModelIndex>" }
- Parameter { name: "hint"; type: "QAbstractItemModel::LayoutChangeHint" }
- }
- Signal {
- name: "layoutAboutToBeChanged"
- Parameter { name: "parents"; type: "QList<QPersistentModelIndex>" }
- }
- Signal { name: "layoutAboutToBeChanged" }
- Signal {
- name: "rowsAboutToBeInserted"
- Parameter { name: "parent"; type: "QModelIndex" }
- Parameter { name: "first"; type: "int" }
- Parameter { name: "last"; type: "int" }
- }
- Signal {
- name: "rowsInserted"
- Parameter { name: "parent"; type: "QModelIndex" }
- Parameter { name: "first"; type: "int" }
- Parameter { name: "last"; type: "int" }
- }
- Signal {
- name: "rowsAboutToBeRemoved"
- Parameter { name: "parent"; type: "QModelIndex" }
- Parameter { name: "first"; type: "int" }
- Parameter { name: "last"; type: "int" }
- }
- Signal {
- name: "rowsRemoved"
- Parameter { name: "parent"; type: "QModelIndex" }
- Parameter { name: "first"; type: "int" }
- Parameter { name: "last"; type: "int" }
- }
- Signal {
- name: "columnsAboutToBeInserted"
- Parameter { name: "parent"; type: "QModelIndex" }
- Parameter { name: "first"; type: "int" }
- Parameter { name: "last"; type: "int" }
- }
- Signal {
- name: "columnsInserted"
- Parameter { name: "parent"; type: "QModelIndex" }
- Parameter { name: "first"; type: "int" }
- Parameter { name: "last"; type: "int" }
- }
- Signal {
- name: "columnsAboutToBeRemoved"
- Parameter { name: "parent"; type: "QModelIndex" }
- Parameter { name: "first"; type: "int" }
- Parameter { name: "last"; type: "int" }
- }
- Signal {
- name: "columnsRemoved"
- Parameter { name: "parent"; type: "QModelIndex" }
- Parameter { name: "first"; type: "int" }
- Parameter { name: "last"; type: "int" }
- }
- Signal { name: "modelAboutToBeReset" }
- Signal { name: "modelReset" }
- Signal {
- name: "rowsAboutToBeMoved"
- Parameter { name: "sourceParent"; type: "QModelIndex" }
- Parameter { name: "sourceStart"; type: "int" }
- Parameter { name: "sourceEnd"; type: "int" }
- Parameter { name: "destinationParent"; type: "QModelIndex" }
- Parameter { name: "destinationRow"; type: "int" }
- }
- Signal {
- name: "rowsMoved"
- Parameter { name: "parent"; type: "QModelIndex" }
- Parameter { name: "start"; type: "int" }
- Parameter { name: "end"; type: "int" }
- Parameter { name: "destination"; type: "QModelIndex" }
- Parameter { name: "row"; type: "int" }
- }
- Signal {
- name: "columnsAboutToBeMoved"
- Parameter { name: "sourceParent"; type: "QModelIndex" }
- Parameter { name: "sourceStart"; type: "int" }
- Parameter { name: "sourceEnd"; type: "int" }
- Parameter { name: "destinationParent"; type: "QModelIndex" }
- Parameter { name: "destinationColumn"; type: "int" }
- }
- Signal {
- name: "columnsMoved"
- Parameter { name: "parent"; type: "QModelIndex" }
- Parameter { name: "start"; type: "int" }
- Parameter { name: "end"; type: "int" }
- Parameter { name: "destination"; type: "QModelIndex" }
- Parameter { name: "column"; type: "int" }
- }
- Method { name: "submit"; type: "bool" }
- Method { name: "revert" }
- Method {
- name: "hasIndex"
- type: "bool"
- Parameter { name: "row"; type: "int" }
- Parameter { name: "column"; type: "int" }
- Parameter { name: "parent"; type: "QModelIndex" }
- }
- Method {
- name: "hasIndex"
- type: "bool"
- Parameter { name: "row"; type: "int" }
- Parameter { name: "column"; type: "int" }
- }
- Method {
- name: "index"
- type: "QModelIndex"
- Parameter { name: "row"; type: "int" }
- Parameter { name: "column"; type: "int" }
- Parameter { name: "parent"; type: "QModelIndex" }
- }
- Method {
- name: "index"
- type: "QModelIndex"
- Parameter { name: "row"; type: "int" }
- Parameter { name: "column"; type: "int" }
- }
- Method {
- name: "parent"
- type: "QModelIndex"
- Parameter { name: "child"; type: "QModelIndex" }
- }
- Method {
- name: "sibling"
- type: "QModelIndex"
- Parameter { name: "row"; type: "int" }
- Parameter { name: "column"; type: "int" }
- Parameter { name: "idx"; type: "QModelIndex" }
- }
- Method {
- name: "rowCount"
- type: "int"
- Parameter { name: "parent"; type: "QModelIndex" }
- }
- Method { name: "rowCount"; type: "int" }
- Method {
- name: "columnCount"
- type: "int"
- Parameter { name: "parent"; type: "QModelIndex" }
- }
- Method { name: "columnCount"; type: "int" }
- Method {
- name: "hasChildren"
- type: "bool"
- Parameter { name: "parent"; type: "QModelIndex" }
- }
- Method { name: "hasChildren"; type: "bool" }
- Method {
- name: "data"
- type: "QVariant"
- Parameter { name: "index"; type: "QModelIndex" }
- Parameter { name: "role"; type: "int" }
- }
- Method {
- name: "data"
- type: "QVariant"
- Parameter { name: "index"; type: "QModelIndex" }
- }
- Method {
- name: "setData"
- type: "bool"
- Parameter { name: "index"; type: "QModelIndex" }
- Parameter { name: "value"; type: "QVariant" }
- Parameter { name: "role"; type: "int" }
- }
- Method {
- name: "setData"
- type: "bool"
- Parameter { name: "index"; type: "QModelIndex" }
- Parameter { name: "value"; type: "QVariant" }
- }
- Method {
- name: "headerData"
- type: "QVariant"
- Parameter { name: "section"; type: "int" }
- Parameter { name: "orientation"; type: "Qt::Orientation" }
- Parameter { name: "role"; type: "int" }
- }
- Method {
- name: "headerData"
- type: "QVariant"
- Parameter { name: "section"; type: "int" }
- Parameter { name: "orientation"; type: "Qt::Orientation" }
- }
- Method {
- name: "fetchMore"
- Parameter { name: "parent"; type: "QModelIndex" }
- }
- Method {
- name: "canFetchMore"
- type: "bool"
- Parameter { name: "parent"; type: "QModelIndex" }
- }
- Method {
- name: "flags"
- type: "Qt::ItemFlags"
- Parameter { name: "index"; type: "QModelIndex" }
- }
- Method {
- name: "match"
- type: "QModelIndexList"
- Parameter { name: "start"; type: "QModelIndex" }
- Parameter { name: "role"; type: "int" }
- Parameter { name: "value"; type: "QVariant" }
- Parameter { name: "hits"; type: "int" }
- Parameter { name: "flags"; type: "Qt::MatchFlags" }
- }
- Method {
- name: "match"
- type: "QModelIndexList"
- Parameter { name: "start"; type: "QModelIndex" }
- Parameter { name: "role"; type: "int" }
- Parameter { name: "value"; type: "QVariant" }
- Parameter { name: "hits"; type: "int" }
- }
- Method {
- name: "match"
- type: "QModelIndexList"
- Parameter { name: "start"; type: "QModelIndex" }
- Parameter { name: "role"; type: "int" }
- Parameter { name: "value"; type: "QVariant" }
- }
- }
- Component { name: "QAbstractListModel"; prototype: "QAbstractItemModel" }
+ dependencies: ["QtQuick 2.8"]
Component {
name: "QQuickFolderListModel"
prototype: "QAbstractListModel"
exports: [
"Qt.labs.folderlistmodel/FolderListModel 1.0",
"Qt.labs.folderlistmodel/FolderListModel 2.0",
- "Qt.labs.folderlistmodel/FolderListModel 2.1"
+ "Qt.labs.folderlistmodel/FolderListModel 2.1",
+ "Qt.labs.folderlistmodel/FolderListModel 2.2"
]
- exportMetaObjectRevisions: [0, 0, 1]
+ exportMetaObjectRevisions: [0, 0, 1, 2]
Enum {
name: "SortField"
values: {
@@ -304,6 +40,7 @@ Module {
Property { name: "showDotAndDotDot"; type: "bool" }
Property { name: "showHidden"; revision: 1; type: "bool" }
Property { name: "showOnlyReadable"; type: "bool" }
+ Property { name: "caseSensitive"; revision: 2; type: "bool" }
Property { name: "count"; type: "int"; isReadonly: true }
Signal { name: "rowCountChanged" }
Signal { name: "countChanged"; revision: 1 }
diff --git a/src/imports/imports.pro b/src/imports/imports.pro
index 5332fb0ef2..affc1db2c0 100644
--- a/src/imports/imports.pro
+++ b/src/imports/imports.pro
@@ -13,9 +13,11 @@ qtHaveModule(quick) {
SUBDIRS += \
layouts \
qtquick2 \
- particles \
window \
testlib
+
+ qtConfig(opengl(es1|es2)?): \
+ SUBDIRS += particles
}
qtHaveModule(xmlpatterns) : SUBDIRS += xmllistmodel
diff --git a/src/imports/layouts/plugin.cpp b/src/imports/layouts/plugin.cpp
index ca54030d13..da5f264ab5 100644
--- a/src/imports/layouts/plugin.cpp
+++ b/src/imports/layouts/plugin.cpp
@@ -61,7 +61,7 @@ public:
{
initResources();
}
- virtual void registerTypes(const char *uri)
+ void registerTypes(const char *uri) Q_DECL_OVERRIDE
{
Q_ASSERT(QLatin1String(uri) == QLatin1String("QtQuick.Layouts"));
Q_UNUSED(uri);
diff --git a/src/imports/layouts/plugins.qmltypes b/src/imports/layouts/plugins.qmltypes
index b130215b62..afb563391d 100644
--- a/src/imports/layouts/plugins.qmltypes
+++ b/src/imports/layouts/plugins.qmltypes
@@ -4,10 +4,10 @@ import QtQuick.tooling 1.2
// It is used for QML tooling purposes only.
//
// This file was auto-generated by:
-// 'qmlplugindump -nonrelocatable QtQuick.Layouts 1.2'
+// 'qmlplugindump -nonrelocatable QtQuick.Layouts 1.3'
Module {
- dependencies: []
+ dependencies: ["QtQuick 2.8"]
Component {
name: "QQuickColumnLayout"
defaultProperty: "data"
diff --git a/src/imports/layouts/qquicklayout.cpp b/src/imports/layouts/qquicklayout.cpp
index 99148265bb..07ada75a5f 100644
--- a/src/imports/layouts/qquicklayout.cpp
+++ b/src/imports/layouts/qquicklayout.cpp
@@ -758,27 +758,22 @@ bool QQuickLayout::shouldIgnoreItem(QQuickItem *child, QQuickLayoutAttached *&in
d->m_ignoredItems << child;
return ignoreItem;
}
-struct QQuickItemPublic : public QQuickItem {
- static bool isCompleted(QQuickItem *item) {
- return static_cast<QQuickItemPublic*>(item)->isComponentComplete();
- }
-};
void QQuickLayout::itemChange(ItemChange change, const ItemChangeData &value)
{
if (change == ItemChildAddedChange) {
QQuickItem *item = value.item;
- QObject::connect(item, SIGNAL(implicitWidthChanged()), this, SLOT(invalidateSenderItem()));
- QObject::connect(item, SIGNAL(implicitHeightChanged()), this, SLOT(invalidateSenderItem()));
- QObject::connect(item, SIGNAL(baselineOffsetChanged(qreal)), this, SLOT(invalidateSenderItem()));
+ qmlobject_connect(item, QQuickItem, SIGNAL(implicitWidthChanged()), this, QQuickLayout, SLOT(invalidateSenderItem()));
+ qmlobject_connect(item, QQuickItem, SIGNAL(implicitHeightChanged()), this, QQuickLayout, SLOT(invalidateSenderItem()));
+ qmlobject_connect(item, QQuickItem, SIGNAL(baselineOffsetChanged(qreal)), this, QQuickLayout, SLOT(invalidateSenderItem()));
QQuickItemPrivate::get(item)->addItemChangeListener(this, QQuickItemPrivate::SiblingOrder);
if (isReady())
updateLayoutItems();
} else if (change == ItemChildRemovedChange) {
QQuickItem *item = value.item;
- QObject::disconnect(item, SIGNAL(implicitWidthChanged()), this, SLOT(invalidateSenderItem()));
- QObject::disconnect(item, SIGNAL(implicitHeightChanged()), this, SLOT(invalidateSenderItem()));
- QObject::disconnect(item, SIGNAL(baselineOffsetChanged(qreal)), this, SLOT(invalidateSenderItem()));
+ qmlobject_disconnect(item, QQuickItem, SIGNAL(implicitWidthChanged()), this, QQuickLayout, SLOT(invalidateSenderItem()));
+ qmlobject_disconnect(item, QQuickItem, SIGNAL(implicitHeightChanged()), this, QQuickLayout, SLOT(invalidateSenderItem()));
+ qmlobject_disconnect(item, QQuickItem, SIGNAL(baselineOffsetChanged(qreal)), this, QQuickLayout, SLOT(invalidateSenderItem()));
QQuickItemPrivate::get(item)->removeItemChangeListener(this, QQuickItemPrivate::SiblingOrder);
if (isReady())
updateLayoutItems();
diff --git a/src/imports/layouts/qquicklinearlayout.cpp b/src/imports/layouts/qquicklinearlayout.cpp
index 9aa36e07a8..7fad395a29 100644
--- a/src/imports/layouts/qquicklinearlayout.cpp
+++ b/src/imports/layouts/qquicklinearlayout.cpp
@@ -309,10 +309,10 @@ QQuickGridLayoutBase::~QQuickGridLayoutBase()
*/
for (int i = 0; i < itemCount(); ++i) {
QQuickItem *item = itemAt(i);
- QObject::disconnect(item, SIGNAL(destroyed()), this, SLOT(onItemDestroyed()));
- QObject::disconnect(item, SIGNAL(visibleChanged()), this, SLOT(onItemVisibleChanged()));
- QObject::disconnect(item, SIGNAL(implicitWidthChanged()), this, SLOT(invalidateSenderItem()));
- QObject::disconnect(item, SIGNAL(implicitHeightChanged()), this, SLOT(invalidateSenderItem()));
+ qmlobject_disconnect(item, QQuickItem, SIGNAL(destroyed()), this, QQuickGridLayoutBase, SLOT(onItemDestroyed()));
+ qmlobject_disconnect(item, QQuickItem, SIGNAL(visibleChanged()), this, QQuickGridLayoutBase, SLOT(onItemVisibleChanged()));
+ qmlobject_disconnect(item, QQuickItem, SIGNAL(implicitWidthChanged()), this, QQuickGridLayoutBase, SLOT(invalidateSenderItem()));
+ qmlobject_disconnect(item, QQuickItem, SIGNAL(implicitHeightChanged()), this, QQuickGridLayoutBase, SLOT(invalidateSenderItem()));
}
delete d->styleInfo;
}
@@ -441,13 +441,13 @@ void QQuickGridLayoutBase::itemChange(ItemChange change, const ItemChangeData &v
if (change == ItemChildAddedChange) {
quickLayoutDebug() << "ItemChildAddedChange";
QQuickItem *item = value.item;
- QObject::connect(item, SIGNAL(destroyed()), this, SLOT(onItemDestroyed()));
- QObject::connect(item, SIGNAL(visibleChanged()), this, SLOT(onItemVisibleChanged()));
+ qmlobject_connect(item, QQuickItem, SIGNAL(destroyed()), this, QQuickGridLayoutBase, SLOT(onItemDestroyed()));
+ qmlobject_connect(item, QQuickItem, SIGNAL(visibleChanged()), this, QQuickGridLayoutBase, SLOT(onItemVisibleChanged()));
} else if (change == ItemChildRemovedChange) {
quickLayoutDebug() << "ItemChildRemovedChange";
QQuickItem *item = value.item;
- QObject::disconnect(item, SIGNAL(destroyed()), this, SLOT(onItemDestroyed()));
- QObject::disconnect(item, SIGNAL(visibleChanged()), this, SLOT(onItemVisibleChanged()));
+ qmlobject_disconnect(item, QQuickItem, SIGNAL(destroyed()), this, QQuickGridLayoutBase, SLOT(onItemDestroyed()));
+ qmlobject_disconnect(item, QQuickItem, SIGNAL(visibleChanged()), this, QQuickGridLayoutBase, SLOT(onItemVisibleChanged()));
}
QQuickLayout::itemChange(change, value);
diff --git a/src/imports/localstorage/plugin.cpp b/src/imports/localstorage/plugin.cpp
index 6b2dd983af..bf70fd1050 100644
--- a/src/imports/localstorage/plugin.cpp
+++ b/src/imports/localstorage/plugin.cpp
@@ -110,20 +110,31 @@ namespace QV4 {
namespace Heap {
struct QQmlSqlDatabaseWrapper : public Object {
enum Type { Database, Query, Rows };
- QQmlSqlDatabaseWrapper()
+ void init()
{
+ Object::init();
type = Database;
+ database = new QSqlDatabase;
+ version = new QString;
+ sqlQuery = new QSqlQuery;
+ }
+
+ void destroy() {
+ delete database;
+ delete version;
+ delete sqlQuery;
+ Object::destroy();
}
Type type;
- QSqlDatabase database;
+ QSqlDatabase *database;
- QString version; // type == Database
+ QString *version; // type == Database
bool inTransaction; // type == Query
bool readonly; // type == Query
- QSqlQuery sqlQuery; // type == Rows
+ QSqlQuery *sqlQuery; // type == Rows
bool forwardOnly; // type == Rows
};
}
@@ -160,7 +171,7 @@ static ReturnedValue qmlsqldatabase_version(CallContext *ctx)
if (!r || r->d()->type != Heap::QQmlSqlDatabaseWrapper::Database)
V4THROW_REFERENCE("Not a SQLDatabase object");
- return Encode(scope.engine->newString(r->d()->version));
+ return Encode(scope.engine->newString(*r->d()->version));
}
static ReturnedValue qmlsqldatabase_rows_length(CallContext *ctx)
@@ -170,11 +181,11 @@ static ReturnedValue qmlsqldatabase_rows_length(CallContext *ctx)
if (!r || r->d()->type != Heap::QQmlSqlDatabaseWrapper::Rows)
V4THROW_REFERENCE("Not a SQLDatabase::Rows object");
- int s = r->d()->sqlQuery.size();
+ int s = r->d()->sqlQuery->size();
if (s < 0) {
// Inefficient
- if (r->d()->sqlQuery.last()) {
- s = r->d()->sqlQuery.at() + 1;
+ if (r->d()->sqlQuery->last()) {
+ s = r->d()->sqlQuery->at() + 1;
} else {
s = 0;
}
@@ -188,7 +199,7 @@ static ReturnedValue qmlsqldatabase_rows_forwardOnly(CallContext *ctx)
QV4::Scoped<QQmlSqlDatabaseWrapper> r(scope, ctx->thisObject().as<QQmlSqlDatabaseWrapper>());
if (!r || r->d()->type != Heap::QQmlSqlDatabaseWrapper::Rows)
V4THROW_REFERENCE("Not a SQLDatabase::Rows object");
- return Encode(r->d()->sqlQuery.isForwardOnly());
+ return Encode(r->d()->sqlQuery->isForwardOnly());
}
static ReturnedValue qmlsqldatabase_rows_setForwardOnly(CallContext *ctx)
@@ -200,7 +211,7 @@ static ReturnedValue qmlsqldatabase_rows_setForwardOnly(CallContext *ctx)
if (ctx->argc() < 1)
return ctx->engine()->throwTypeError();
- r->d()->sqlQuery.setForwardOnly(ctx->args()[0].toBoolean());
+ r->d()->sqlQuery->setForwardOnly(ctx->args()[0].toBoolean());
return Encode::undefined();
}
@@ -230,8 +241,8 @@ static ReturnedValue qmlsqldatabase_rows_index(const QQmlSqlDatabaseWrapper *r,
{
Scope scope(v4);
- if (r->d()->sqlQuery.at() == (int)index || r->d()->sqlQuery.seek(index)) {
- QSqlRecord record = r->d()->sqlQuery.record();
+ if (r->d()->sqlQuery->at() == (int)index || r->d()->sqlQuery->seek(index)) {
+ QSqlRecord record = r->d()->sqlQuery->record();
// XXX optimize
ScopedObject row(scope, v4->newObject());
for (int ii = 0; ii < record.count(); ++ii) {
@@ -289,7 +300,7 @@ static ReturnedValue qmlsqldatabase_executeSql(CallContext *ctx)
if (!r->d()->inTransaction)
V4THROW_SQL(SQLEXCEPTION_DATABASE_ERR,QQmlEngine::tr("executeSql called outside transaction()"));
- QSqlDatabase db = r->d()->database;
+ QSqlDatabase db = *r->d()->database;
QString sql = ctx->argc() ? ctx->args()[0].toQString() : QString();
@@ -338,8 +349,8 @@ static ReturnedValue qmlsqldatabase_executeSql(CallContext *ctx)
QV4::ScopedObject p(scope, databaseData(scope.engine)->rowsProto.value());
rows->setPrototype(p.getPointer());
rows->d()->type = Heap::QQmlSqlDatabaseWrapper::Rows;
- rows->d()->database = db;
- rows->d()->sqlQuery = query;
+ *rows->d()->database = db;
+ *rows->d()->sqlQuery = query;
ScopedObject resultObject(scope, scope.engine->newObject());
result = resultObject.asReturnedValue();
@@ -401,20 +412,20 @@ static ReturnedValue qmlsqldatabase_changeVersion(CallContext *ctx)
if (!r || r->d()->type != Heap::QQmlSqlDatabaseWrapper::Database)
V4THROW_REFERENCE("Not a SQLDatabase object");
- QSqlDatabase db = r->d()->database;
+ QSqlDatabase db = *r->d()->database;
QString from_version = ctx->args()[0].toQString();
QString to_version = ctx->args()[1].toQString();
ScopedFunctionObject callback(scope, ctx->argument(2));
- if (from_version != r->d()->version)
- V4THROW_SQL(SQLEXCEPTION_VERSION_ERR, QQmlEngine::tr("Version mismatch: expected %1, found %2").arg(from_version).arg(r->d()->version));
+ if (from_version != *r->d()->version)
+ V4THROW_SQL(SQLEXCEPTION_VERSION_ERR, QQmlEngine::tr("Version mismatch: expected %1, found %2").arg(from_version).arg(*r->d()->version));
Scoped<QQmlSqlDatabaseWrapper> w(scope, QQmlSqlDatabaseWrapper::create(scope.engine));
ScopedObject p(scope, databaseData(scope.engine)->queryProto.value());
w->setPrototype(p.getPointer());
w->d()->type = Heap::QQmlSqlDatabaseWrapper::Query;
- w->d()->database = db;
- w->d()->version = r->d()->version;
+ *w->d()->database = db;
+ *w->d()->version = *r->d()->version;
bool ok = true;
if (!!callback) {
@@ -426,7 +437,7 @@ static ReturnedValue qmlsqldatabase_changeVersion(CallContext *ctx)
callData->args[0] = w;
TransactionRollback rollbackOnException(&db, &w->d()->inTransaction);
- callback->call(callData);
+ callback->call(scope, callData);
rollbackOnException.clear();
if (!db.commit()) {
db.rollback();
@@ -437,7 +448,7 @@ static ReturnedValue qmlsqldatabase_changeVersion(CallContext *ctx)
}
if (ok) {
- w->d()->version = to_version;
+ *w->d()->version = to_version;
#ifndef QT_NO_SETTINGS
QSettings ini(qmlsqldatabase_databaseFile(db.connectionName(), scope.engine) + QLatin1String(".ini"), QSettings::IniFormat);
ini.setValue(QLatin1String("Version"), to_version);
@@ -458,14 +469,14 @@ static ReturnedValue qmlsqldatabase_transaction_shared(CallContext *ctx, bool re
if (!callback)
V4THROW_SQL(SQLEXCEPTION_UNKNOWN_ERR, QQmlEngine::tr("transaction: missing callback"));
- QSqlDatabase db = r->d()->database;
+ QSqlDatabase db = *r->d()->database;
Scoped<QQmlSqlDatabaseWrapper> w(scope, QQmlSqlDatabaseWrapper::create(scope.engine));
QV4::ScopedObject p(scope, databaseData(scope.engine)->queryProto.value());
w->setPrototype(p.getPointer());
w->d()->type = Heap::QQmlSqlDatabaseWrapper::Query;
- w->d()->database = db;
- w->d()->version = r->d()->version;
+ *w->d()->database = db;
+ *w->d()->version = *r->d()->version;
w->d()->readonly = readOnly;
db.transaction();
@@ -474,7 +485,7 @@ static ReturnedValue qmlsqldatabase_transaction_shared(CallContext *ctx, bool re
callData->thisObject = scope.engine->globalObject;
callData->args[0] = w;
TransactionRollback rollbackOnException(&db, &w->d()->inTransaction);
- callback->call(callData);
+ callback->call(scope, callData);
rollbackOnException.clear();
if (!db.commit())
@@ -748,14 +759,14 @@ void QQuickLocalStorage::openDatabaseSync(QQmlV4Function *args)
QV4::Scoped<QQmlSqlDatabaseWrapper> db(scope, QQmlSqlDatabaseWrapper::create(scope.engine));
QV4::ScopedObject p(scope, databaseData(scope.engine)->databaseProto.value());
db->setPrototype(p.getPointer());
- db->d()->database = database;
- db->d()->version = version;
+ *db->d()->database = database;
+ *db->d()->version = version;
if (created && dbcreationCallback) {
ScopedCallData callData(scope, 1);
callData->thisObject = scope.engine->globalObject;
callData->args[0] = db;
- dbcreationCallback->call(callData);
+ dbcreationCallback->call(scope, callData);
}
args->setReturnValue(db.asReturnedValue());
@@ -781,7 +792,7 @@ public:
{
initResources();
}
- void registerTypes(const char *uri)
+ void registerTypes(const char *uri) Q_DECL_OVERRIDE
{
Q_ASSERT(QLatin1String(uri) == "QtQuick.LocalStorage");
qmlRegisterSingletonType<QQuickLocalStorage>(uri, 2, 0, "LocalStorage", module_api_factory);
diff --git a/src/imports/localstorage/plugins.qmltypes b/src/imports/localstorage/plugins.qmltypes
index 7d81cdf6f4..dee81a78d0 100644
--- a/src/imports/localstorage/plugins.qmltypes
+++ b/src/imports/localstorage/plugins.qmltypes
@@ -4,7 +4,7 @@ import QtQuick.tooling 1.2
// It is used for QML tooling purposes only.
//
// This file was auto-generated by:
-// 'qmlplugindump -nonrelocatable QtQuick.LocalStorage 2.0'
+// 'qmlplugindump -nonrelocatable -noforceqtquick QtQuick.LocalStorage 2.0'
Module {
dependencies: []
diff --git a/src/imports/models/plugin.cpp b/src/imports/models/plugin.cpp
index 36a98e8871..dbb62cd25d 100644
--- a/src/imports/models/plugin.cpp
+++ b/src/imports/models/plugin.cpp
@@ -78,7 +78,7 @@ class QtQmlModelsPlugin : public QQmlExtensionPlugin
Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid)
public:
QtQmlModelsPlugin(QObject *parent = 0) : QQmlExtensionPlugin(parent) { initResources(); }
- virtual void registerTypes(const char *uri)
+ void registerTypes(const char *uri) Q_DECL_OVERRIDE
{
Q_ASSERT(QLatin1String(uri) == QLatin1String("QtQml.Models"));
Q_UNUSED(uri);
diff --git a/src/imports/models/plugins.qmltypes b/src/imports/models/plugins.qmltypes
index 0bd52a13fd..aa06a2a709 100644
--- a/src/imports/models/plugins.qmltypes
+++ b/src/imports/models/plugins.qmltypes
@@ -7,272 +7,7 @@ import QtQuick.tooling 1.2
// 'qmlplugindump -nonrelocatable QtQml.Models 2.3'
Module {
- dependencies: []
- Component {
- name: "QAbstractItemModel"
- prototype: "QObject"
- Enum {
- name: "LayoutChangeHint"
- values: {
- "NoLayoutChangeHint": 0,
- "VerticalSortHint": 1,
- "HorizontalSortHint": 2
- }
- }
- Signal {
- name: "dataChanged"
- Parameter { name: "topLeft"; type: "QModelIndex" }
- Parameter { name: "bottomRight"; type: "QModelIndex" }
- Parameter { name: "roles"; type: "QVector<int>" }
- }
- Signal {
- name: "dataChanged"
- Parameter { name: "topLeft"; type: "QModelIndex" }
- Parameter { name: "bottomRight"; type: "QModelIndex" }
- }
- Signal {
- name: "headerDataChanged"
- Parameter { name: "orientation"; type: "Qt::Orientation" }
- Parameter { name: "first"; type: "int" }
- Parameter { name: "last"; type: "int" }
- }
- Signal {
- name: "layoutChanged"
- Parameter { name: "parents"; type: "QList<QPersistentModelIndex>" }
- Parameter { name: "hint"; type: "QAbstractItemModel::LayoutChangeHint" }
- }
- Signal {
- name: "layoutChanged"
- Parameter { name: "parents"; type: "QList<QPersistentModelIndex>" }
- }
- Signal { name: "layoutChanged" }
- Signal {
- name: "layoutAboutToBeChanged"
- Parameter { name: "parents"; type: "QList<QPersistentModelIndex>" }
- Parameter { name: "hint"; type: "QAbstractItemModel::LayoutChangeHint" }
- }
- Signal {
- name: "layoutAboutToBeChanged"
- Parameter { name: "parents"; type: "QList<QPersistentModelIndex>" }
- }
- Signal { name: "layoutAboutToBeChanged" }
- Signal {
- name: "rowsAboutToBeInserted"
- Parameter { name: "parent"; type: "QModelIndex" }
- Parameter { name: "first"; type: "int" }
- Parameter { name: "last"; type: "int" }
- }
- Signal {
- name: "rowsInserted"
- Parameter { name: "parent"; type: "QModelIndex" }
- Parameter { name: "first"; type: "int" }
- Parameter { name: "last"; type: "int" }
- }
- Signal {
- name: "rowsAboutToBeRemoved"
- Parameter { name: "parent"; type: "QModelIndex" }
- Parameter { name: "first"; type: "int" }
- Parameter { name: "last"; type: "int" }
- }
- Signal {
- name: "rowsRemoved"
- Parameter { name: "parent"; type: "QModelIndex" }
- Parameter { name: "first"; type: "int" }
- Parameter { name: "last"; type: "int" }
- }
- Signal {
- name: "columnsAboutToBeInserted"
- Parameter { name: "parent"; type: "QModelIndex" }
- Parameter { name: "first"; type: "int" }
- Parameter { name: "last"; type: "int" }
- }
- Signal {
- name: "columnsInserted"
- Parameter { name: "parent"; type: "QModelIndex" }
- Parameter { name: "first"; type: "int" }
- Parameter { name: "last"; type: "int" }
- }
- Signal {
- name: "columnsAboutToBeRemoved"
- Parameter { name: "parent"; type: "QModelIndex" }
- Parameter { name: "first"; type: "int" }
- Parameter { name: "last"; type: "int" }
- }
- Signal {
- name: "columnsRemoved"
- Parameter { name: "parent"; type: "QModelIndex" }
- Parameter { name: "first"; type: "int" }
- Parameter { name: "last"; type: "int" }
- }
- Signal { name: "modelAboutToBeReset" }
- Signal { name: "modelReset" }
- Signal {
- name: "rowsAboutToBeMoved"
- Parameter { name: "sourceParent"; type: "QModelIndex" }
- Parameter { name: "sourceStart"; type: "int" }
- Parameter { name: "sourceEnd"; type: "int" }
- Parameter { name: "destinationParent"; type: "QModelIndex" }
- Parameter { name: "destinationRow"; type: "int" }
- }
- Signal {
- name: "rowsMoved"
- Parameter { name: "parent"; type: "QModelIndex" }
- Parameter { name: "start"; type: "int" }
- Parameter { name: "end"; type: "int" }
- Parameter { name: "destination"; type: "QModelIndex" }
- Parameter { name: "row"; type: "int" }
- }
- Signal {
- name: "columnsAboutToBeMoved"
- Parameter { name: "sourceParent"; type: "QModelIndex" }
- Parameter { name: "sourceStart"; type: "int" }
- Parameter { name: "sourceEnd"; type: "int" }
- Parameter { name: "destinationParent"; type: "QModelIndex" }
- Parameter { name: "destinationColumn"; type: "int" }
- }
- Signal {
- name: "columnsMoved"
- Parameter { name: "parent"; type: "QModelIndex" }
- Parameter { name: "start"; type: "int" }
- Parameter { name: "end"; type: "int" }
- Parameter { name: "destination"; type: "QModelIndex" }
- Parameter { name: "column"; type: "int" }
- }
- Method { name: "submit"; type: "bool" }
- Method { name: "revert" }
- Method {
- name: "hasIndex"
- type: "bool"
- Parameter { name: "row"; type: "int" }
- Parameter { name: "column"; type: "int" }
- Parameter { name: "parent"; type: "QModelIndex" }
- }
- Method {
- name: "hasIndex"
- type: "bool"
- Parameter { name: "row"; type: "int" }
- Parameter { name: "column"; type: "int" }
- }
- Method {
- name: "index"
- type: "QModelIndex"
- Parameter { name: "row"; type: "int" }
- Parameter { name: "column"; type: "int" }
- Parameter { name: "parent"; type: "QModelIndex" }
- }
- Method {
- name: "index"
- type: "QModelIndex"
- Parameter { name: "row"; type: "int" }
- Parameter { name: "column"; type: "int" }
- }
- Method {
- name: "parent"
- type: "QModelIndex"
- Parameter { name: "child"; type: "QModelIndex" }
- }
- Method {
- name: "sibling"
- type: "QModelIndex"
- Parameter { name: "row"; type: "int" }
- Parameter { name: "column"; type: "int" }
- Parameter { name: "idx"; type: "QModelIndex" }
- }
- Method {
- name: "rowCount"
- type: "int"
- Parameter { name: "parent"; type: "QModelIndex" }
- }
- Method { name: "rowCount"; type: "int" }
- Method {
- name: "columnCount"
- type: "int"
- Parameter { name: "parent"; type: "QModelIndex" }
- }
- Method { name: "columnCount"; type: "int" }
- Method {
- name: "hasChildren"
- type: "bool"
- Parameter { name: "parent"; type: "QModelIndex" }
- }
- Method { name: "hasChildren"; type: "bool" }
- Method {
- name: "data"
- type: "QVariant"
- Parameter { name: "index"; type: "QModelIndex" }
- Parameter { name: "role"; type: "int" }
- }
- Method {
- name: "data"
- type: "QVariant"
- Parameter { name: "index"; type: "QModelIndex" }
- }
- Method {
- name: "setData"
- type: "bool"
- Parameter { name: "index"; type: "QModelIndex" }
- Parameter { name: "value"; type: "QVariant" }
- Parameter { name: "role"; type: "int" }
- }
- Method {
- name: "setData"
- type: "bool"
- Parameter { name: "index"; type: "QModelIndex" }
- Parameter { name: "value"; type: "QVariant" }
- }
- Method {
- name: "headerData"
- type: "QVariant"
- Parameter { name: "section"; type: "int" }
- Parameter { name: "orientation"; type: "Qt::Orientation" }
- Parameter { name: "role"; type: "int" }
- }
- Method {
- name: "headerData"
- type: "QVariant"
- Parameter { name: "section"; type: "int" }
- Parameter { name: "orientation"; type: "Qt::Orientation" }
- }
- Method {
- name: "fetchMore"
- Parameter { name: "parent"; type: "QModelIndex" }
- }
- Method {
- name: "canFetchMore"
- type: "bool"
- Parameter { name: "parent"; type: "QModelIndex" }
- }
- Method {
- name: "flags"
- type: "Qt::ItemFlags"
- Parameter { name: "index"; type: "QModelIndex" }
- }
- Method {
- name: "match"
- type: "QModelIndexList"
- Parameter { name: "start"; type: "QModelIndex" }
- Parameter { name: "role"; type: "int" }
- Parameter { name: "value"; type: "QVariant" }
- Parameter { name: "hits"; type: "int" }
- Parameter { name: "flags"; type: "Qt::MatchFlags" }
- }
- Method {
- name: "match"
- type: "QModelIndexList"
- Parameter { name: "start"; type: "QModelIndex" }
- Parameter { name: "role"; type: "int" }
- Parameter { name: "value"; type: "QVariant" }
- Parameter { name: "hits"; type: "int" }
- }
- Method {
- name: "match"
- type: "QModelIndexList"
- Parameter { name: "start"; type: "QModelIndex" }
- Parameter { name: "role"; type: "int" }
- Parameter { name: "value"; type: "QVariant" }
- }
- }
- Component { name: "QAbstractListModel"; prototype: "QAbstractItemModel" }
+ dependencies: ["QtQuick 2.8"]
Component {
name: "QItemSelectionModel"
prototype: "QObject"
@@ -384,203 +119,4 @@ Module {
}
Method { name: "selectedColumns"; type: "QModelIndexList" }
}
- Component {
- name: "QQmlDelegateModel"
- defaultProperty: "delegate"
- prototype: "QQmlInstanceModel"
- exports: ["QtQml.Models/DelegateModel 2.1"]
- exportMetaObjectRevisions: [0]
- attachedType: "QQmlDelegateModelAttached"
- Property { name: "model"; type: "QVariant" }
- Property { name: "delegate"; type: "QQmlComponent"; isPointer: true }
- Property { name: "filterOnGroup"; type: "string" }
- Property { name: "items"; type: "QQmlDelegateModelGroup"; isReadonly: true; isPointer: true }
- Property {
- name: "persistedItems"
- type: "QQmlDelegateModelGroup"
- isReadonly: true
- isPointer: true
- }
- Property { name: "groups"; type: "QQmlDelegateModelGroup"; isList: true; isReadonly: true }
- Property { name: "parts"; type: "QObject"; isReadonly: true; isPointer: true }
- Property { name: "rootIndex"; type: "QVariant" }
- Signal { name: "filterGroupChanged" }
- Signal { name: "defaultGroupsChanged" }
- Method {
- name: "modelIndex"
- type: "QVariant"
- Parameter { name: "idx"; type: "int" }
- }
- Method { name: "parentModelIndex"; type: "QVariant" }
- }
- Component {
- name: "QQmlDelegateModelAttached"
- prototype: "QObject"
- Property { name: "model"; type: "QQmlDelegateModel"; isReadonly: true; isPointer: true }
- Property { name: "groups"; type: "QStringList" }
- Property { name: "isUnresolved"; type: "bool"; isReadonly: true }
- Signal { name: "unresolvedChanged" }
- }
- Component {
- name: "QQmlDelegateModelGroup"
- prototype: "QObject"
- exports: ["QtQml.Models/DelegateModelGroup 2.1"]
- exportMetaObjectRevisions: [0]
- Property { name: "count"; type: "int"; isReadonly: true }
- Property { name: "name"; type: "string" }
- Property { name: "includeByDefault"; type: "bool" }
- Signal { name: "defaultIncludeChanged" }
- Signal {
- name: "changed"
- Parameter { name: "removed"; type: "QQmlV4Handle" }
- Parameter { name: "inserted"; type: "QQmlV4Handle" }
- }
- Method {
- name: "insert"
- Parameter { type: "QQmlV4Function"; isPointer: true }
- }
- Method {
- name: "create"
- Parameter { type: "QQmlV4Function"; isPointer: true }
- }
- Method {
- name: "resolve"
- Parameter { type: "QQmlV4Function"; isPointer: true }
- }
- Method {
- name: "remove"
- Parameter { type: "QQmlV4Function"; isPointer: true }
- }
- Method {
- name: "addGroups"
- Parameter { type: "QQmlV4Function"; isPointer: true }
- }
- Method {
- name: "removeGroups"
- Parameter { type: "QQmlV4Function"; isPointer: true }
- }
- Method {
- name: "setGroups"
- Parameter { type: "QQmlV4Function"; isPointer: true }
- }
- Method {
- name: "move"
- Parameter { type: "QQmlV4Function"; isPointer: true }
- }
- Method {
- name: "get"
- type: "QQmlV4Handle"
- Parameter { name: "index"; type: "int" }
- }
- }
- Component { name: "QQmlDelegateModelParts"; prototype: "QObject" }
- Component {
- name: "QQmlListElement"
- prototype: "QObject"
- exports: ["QtQml.Models/ListElement 2.1"]
- exportMetaObjectRevisions: [0]
- }
- Component {
- name: "QQmlListModel"
- prototype: "QAbstractListModel"
- exports: ["QtQml.Models/ListModel 2.1"]
- exportMetaObjectRevisions: [0]
- Property { name: "count"; type: "int"; isReadonly: true }
- Property { name: "dynamicRoles"; type: "bool" }
- Method { name: "clear" }
- Method {
- name: "remove"
- Parameter { name: "args"; type: "QQmlV4Function"; isPointer: true }
- }
- Method {
- name: "append"
- Parameter { name: "args"; type: "QQmlV4Function"; isPointer: true }
- }
- Method {
- name: "insert"
- Parameter { name: "args"; type: "QQmlV4Function"; isPointer: true }
- }
- Method {
- name: "get"
- type: "QQmlV4Handle"
- Parameter { name: "index"; type: "int" }
- }
- Method {
- name: "set"
- Parameter { name: "index"; type: "int" }
- Parameter { type: "QQmlV4Handle" }
- }
- Method {
- name: "setProperty"
- Parameter { name: "index"; type: "int" }
- Parameter { name: "property"; type: "string" }
- Parameter { name: "value"; type: "QVariant" }
- }
- Method {
- name: "move"
- Parameter { name: "from"; type: "int" }
- Parameter { name: "to"; type: "int" }
- Parameter { name: "count"; type: "int" }
- }
- Method { name: "sync" }
- }
- Component {
- name: "QQmlObjectModel"
- defaultProperty: "children"
- prototype: "QQmlInstanceModel"
- exports: [
- "QtQml.Models/ObjectModel 2.1",
- "QtQml.Models/ObjectModel 2.3"
- ]
- exportMetaObjectRevisions: [0, 3]
- attachedType: "QQmlObjectModelAttached"
- Property { name: "children"; type: "QObject"; isList: true; isReadonly: true }
- Method { name: "clear"; revision: 3 }
- Method {
- name: "get"
- revision: 3
- type: "QObject*"
- Parameter { name: "index"; type: "int" }
- }
- Method {
- name: "append"
- revision: 3
- Parameter { name: "object"; type: "QObject"; isPointer: true }
- }
- Method {
- name: "insert"
- revision: 3
- Parameter { name: "index"; type: "int" }
- Parameter { name: "object"; type: "QObject"; isPointer: true }
- }
- Method {
- name: "move"
- revision: 3
- Parameter { name: "from"; type: "int" }
- Parameter { name: "to"; type: "int" }
- Parameter { name: "n"; type: "int" }
- }
- Method {
- name: "move"
- revision: 3
- Parameter { name: "from"; type: "int" }
- Parameter { name: "to"; type: "int" }
- }
- Method {
- name: "remove"
- revision: 3
- Parameter { name: "index"; type: "int" }
- Parameter { name: "n"; type: "int" }
- }
- Method {
- name: "remove"
- revision: 3
- Parameter { name: "index"; type: "int" }
- }
- }
- Component {
- name: "QQmlObjectModelAttached"
- prototype: "QObject"
- Property { name: "index"; type: "int"; isReadonly: true }
- }
}
diff --git a/src/imports/particles/plugin.cpp b/src/imports/particles/plugin.cpp
index a228bc812a..28ce0f7796 100644
--- a/src/imports/particles/plugin.cpp
+++ b/src/imports/particles/plugin.cpp
@@ -57,7 +57,7 @@ class QtQuick2ParticlesPlugin : public QQmlExtensionPlugin
Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid)
public:
QtQuick2ParticlesPlugin(QObject *parent = 0) : QQmlExtensionPlugin(parent) { initResources(); }
- virtual void registerTypes(const char *uri)
+ void registerTypes(const char *uri) Q_DECL_OVERRIDE
{
Q_ASSERT(QLatin1String(uri) == QLatin1String("QtQuick.Particles"));
Q_UNUSED(uri);
diff --git a/src/imports/particles/plugins.qmltypes b/src/imports/particles/plugins.qmltypes
index ce78392610..5c4bd01980 100644
--- a/src/imports/particles/plugins.qmltypes
+++ b/src/imports/particles/plugins.qmltypes
@@ -7,7 +7,7 @@ import QtQuick.tooling 1.2
// 'qmlplugindump -nonrelocatable QtQuick.Particles 2.0'
Module {
- dependencies: []
+ dependencies: ["QtQuick 2.8"]
Component {
name: "QQuickAgeAffector"
defaultProperty: "data"
@@ -492,160 +492,6 @@ Module {
}
}
Component {
- name: "QQuickItem"
- defaultProperty: "data"
- prototype: "QObject"
- Enum {
- name: "TransformOrigin"
- values: {
- "TopLeft": 0,
- "Top": 1,
- "TopRight": 2,
- "Left": 3,
- "Center": 4,
- "Right": 5,
- "BottomLeft": 6,
- "Bottom": 7,
- "BottomRight": 8
- }
- }
- Property { name: "parent"; type: "QQuickItem"; isPointer: true }
- Property { name: "data"; type: "QObject"; isList: true; isReadonly: true }
- Property { name: "resources"; type: "QObject"; isList: true; isReadonly: true }
- Property { name: "children"; type: "QQuickItem"; isList: true; isReadonly: true }
- Property { name: "x"; type: "double" }
- Property { name: "y"; type: "double" }
- Property { name: "z"; type: "double" }
- Property { name: "width"; type: "double" }
- Property { name: "height"; type: "double" }
- Property { name: "opacity"; type: "double" }
- Property { name: "enabled"; type: "bool" }
- Property { name: "visible"; type: "bool" }
- Property { name: "visibleChildren"; type: "QQuickItem"; isList: true; isReadonly: true }
- Property { name: "states"; type: "QQuickState"; isList: true; isReadonly: true }
- Property { name: "transitions"; type: "QQuickTransition"; isList: true; isReadonly: true }
- Property { name: "state"; type: "string" }
- Property { name: "childrenRect"; type: "QRectF"; isReadonly: true }
- Property { name: "anchors"; type: "QQuickAnchors"; isReadonly: true; isPointer: true }
- Property { name: "left"; type: "QQuickAnchorLine"; isReadonly: true }
- Property { name: "right"; type: "QQuickAnchorLine"; isReadonly: true }
- Property { name: "horizontalCenter"; type: "QQuickAnchorLine"; isReadonly: true }
- Property { name: "top"; type: "QQuickAnchorLine"; isReadonly: true }
- Property { name: "bottom"; type: "QQuickAnchorLine"; isReadonly: true }
- Property { name: "verticalCenter"; type: "QQuickAnchorLine"; isReadonly: true }
- Property { name: "baseline"; type: "QQuickAnchorLine"; isReadonly: true }
- Property { name: "baselineOffset"; type: "double" }
- Property { name: "clip"; type: "bool" }
- Property { name: "focus"; type: "bool" }
- Property { name: "activeFocus"; type: "bool"; isReadonly: true }
- Property { name: "activeFocusOnTab"; revision: 1; type: "bool" }
- Property { name: "rotation"; type: "double" }
- Property { name: "scale"; type: "double" }
- Property { name: "transformOrigin"; type: "TransformOrigin" }
- Property { name: "transformOriginPoint"; type: "QPointF"; isReadonly: true }
- Property { name: "transform"; type: "QQuickTransform"; isList: true; isReadonly: true }
- Property { name: "smooth"; type: "bool" }
- Property { name: "antialiasing"; type: "bool" }
- Property { name: "implicitWidth"; type: "double" }
- Property { name: "implicitHeight"; type: "double" }
- Property { name: "layer"; type: "QQuickItemLayer"; isReadonly: true; isPointer: true }
- Signal {
- name: "childrenRectChanged"
- Parameter { type: "QRectF" }
- }
- Signal {
- name: "baselineOffsetChanged"
- Parameter { type: "double" }
- }
- Signal {
- name: "stateChanged"
- Parameter { type: "string" }
- }
- Signal {
- name: "focusChanged"
- Parameter { type: "bool" }
- }
- Signal {
- name: "activeFocusChanged"
- Parameter { type: "bool" }
- }
- Signal {
- name: "activeFocusOnTabChanged"
- revision: 1
- Parameter { type: "bool" }
- }
- Signal {
- name: "parentChanged"
- Parameter { type: "QQuickItem"; isPointer: true }
- }
- Signal {
- name: "transformOriginChanged"
- Parameter { type: "TransformOrigin" }
- }
- Signal {
- name: "smoothChanged"
- Parameter { type: "bool" }
- }
- Signal {
- name: "antialiasingChanged"
- Parameter { type: "bool" }
- }
- Signal {
- name: "clipChanged"
- Parameter { type: "bool" }
- }
- Signal {
- name: "windowChanged"
- revision: 1
- Parameter { name: "window"; type: "QQuickWindow"; isPointer: true }
- }
- Method { name: "update" }
- Method {
- name: "grabToImage"
- revision: 2
- type: "bool"
- Parameter { name: "callback"; type: "QJSValue" }
- Parameter { name: "targetSize"; type: "QSize" }
- }
- Method {
- name: "grabToImage"
- revision: 2
- type: "bool"
- Parameter { name: "callback"; type: "QJSValue" }
- }
- Method {
- name: "contains"
- type: "bool"
- Parameter { name: "point"; type: "QPointF" }
- }
- Method {
- name: "mapFromItem"
- Parameter { type: "QQmlV4Function"; isPointer: true }
- }
- Method {
- name: "mapToItem"
- Parameter { type: "QQmlV4Function"; isPointer: true }
- }
- Method { name: "forceActiveFocus" }
- Method {
- name: "forceActiveFocus"
- Parameter { name: "reason"; type: "Qt::FocusReason" }
- }
- Method {
- name: "nextItemInFocusChain"
- revision: 1
- type: "QQuickItem*"
- Parameter { name: "forward"; type: "bool" }
- }
- Method { name: "nextItemInFocusChain"; revision: 1; type: "QQuickItem*" }
- Method {
- name: "childAt"
- type: "QQuickItem*"
- Parameter { name: "x"; type: "double" }
- Parameter { name: "y"; type: "double" }
- }
- }
- Component {
name: "QQuickItemParticle"
defaultProperty: "data"
prototype: "QQuickParticlePainter"
@@ -1151,56 +997,6 @@ Module {
}
}
Component {
- name: "QQuickStochasticState"
- prototype: "QObject"
- Property { name: "duration"; type: "int" }
- Property { name: "durationVariation"; type: "int" }
- Property { name: "randomStart"; type: "bool" }
- Property { name: "to"; type: "QVariantMap" }
- Property { name: "name"; type: "string" }
- Signal {
- name: "durationChanged"
- Parameter { name: "arg"; type: "int" }
- }
- Signal {
- name: "nameChanged"
- Parameter { name: "arg"; type: "string" }
- }
- Signal {
- name: "toChanged"
- Parameter { name: "arg"; type: "QVariantMap" }
- }
- Signal {
- name: "durationVariationChanged"
- Parameter { name: "arg"; type: "int" }
- }
- Signal { name: "entered" }
- Signal {
- name: "randomStartChanged"
- Parameter { name: "arg"; type: "bool" }
- }
- Method {
- name: "setDuration"
- Parameter { name: "arg"; type: "int" }
- }
- Method {
- name: "setName"
- Parameter { name: "arg"; type: "string" }
- }
- Method {
- name: "setTo"
- Parameter { name: "arg"; type: "QVariantMap" }
- }
- Method {
- name: "setDurationVariation"
- Parameter { name: "arg"; type: "int" }
- }
- Method {
- name: "setRandomStart"
- Parameter { name: "arg"; type: "bool" }
- }
- }
- Component {
name: "QQuickTargetDirection"
prototype: "QQuickDirection"
exports: ["QtQuick.Particles/TargetDirection 2.0"]
diff --git a/src/imports/qtqml/plugins.qmltypes b/src/imports/qtqml/plugins.qmltypes
index 864aca1f32..82333627a0 100644
--- a/src/imports/qtqml/plugins.qmltypes
+++ b/src/imports/qtqml/plugins.qmltypes
@@ -4,7 +4,7 @@ import QtQuick.tooling 1.2
// It is used for QML tooling purposes only.
//
// This file was auto-generated by:
-// 'qmlplugindump -nonrelocatable QtQml 2.2'
+// 'qmlplugindump -nonrelocatable -noforceqtquick QtQml 2.3'
Module {
dependencies: []
@@ -27,12 +27,13 @@ Module {
Component {
name: "QQmlBind"
prototype: "QObject"
- exports: ["QtQml/Binding 2.0"]
- exportMetaObjectRevisions: [0]
+ exports: ["QtQml/Binding 2.0", "QtQml/Binding 2.8"]
+ exportMetaObjectRevisions: [0, 8]
Property { name: "target"; type: "QObject"; isPointer: true }
Property { name: "property"; type: "string" }
Property { name: "value"; type: "QVariant" }
Property { name: "when"; type: "bool" }
+ Property { name: "delayed"; revision: 8; type: "bool" }
}
Component {
name: "QQmlComponent"
@@ -92,10 +93,12 @@ Module {
Component {
name: "QQmlConnections"
prototype: "QObject"
- exports: ["QtQml/Connections 2.0"]
- exportMetaObjectRevisions: [0]
+ exports: ["QtQml/Connections 2.0", "QtQml/Connections 2.3"]
+ exportMetaObjectRevisions: [0, 1]
Property { name: "target"; type: "QObject"; isPointer: true }
+ Property { name: "enabled"; type: "bool" }
Property { name: "ignoreUnknownSignals"; type: "bool" }
+ Signal { name: "enabledChanged"; revision: 1 }
}
Component {
name: "QQmlInstanceModel"
@@ -193,6 +196,13 @@ Module {
}
}
Component {
+ name: "QQmlLoggingCategory"
+ prototype: "QObject"
+ exports: ["QtQml/LoggingCategory 2.8"]
+ exportMetaObjectRevisions: [0]
+ Property { name: "name"; type: "string" }
+ }
+ Component {
name: "QQmlTimer"
prototype: "QObject"
exports: ["QtQml/Timer 2.0"]
@@ -215,6 +225,7 @@ Module {
Property { name: "button"; type: "int"; isReadonly: true }
Property { name: "buttons"; type: "int"; isReadonly: true }
Property { name: "modifiers"; type: "int"; isReadonly: true }
+ Property { name: "source"; revision: 7; type: "int"; isReadonly: true }
Property { name: "wasHeld"; type: "bool"; isReadonly: true }
Property { name: "isClick"; type: "bool"; isReadonly: true }
Property { name: "accepted"; type: "bool" }
diff --git a/src/imports/qtquick2/plugin.cpp b/src/imports/qtquick2/plugin.cpp
index 42454f0983..d16467a5bb 100644
--- a/src/imports/qtquick2/plugin.cpp
+++ b/src/imports/qtquick2/plugin.cpp
@@ -57,7 +57,7 @@ class QtQuick2Plugin : public QQmlExtensionPlugin
Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid)
public:
QtQuick2Plugin(QObject *parent = 0) : QQmlExtensionPlugin(parent) { initResources(); }
- virtual void registerTypes(const char *uri)
+ void registerTypes(const char *uri) Q_DECL_OVERRIDE
{
Q_ASSERT(QLatin1String(uri) == QLatin1String("QtQuick"));
Q_UNUSED(uri);
diff --git a/src/imports/qtquick2/plugins.qmltypes b/src/imports/qtquick2/plugins.qmltypes
index b11906ee09..441cd743aa 100644
--- a/src/imports/qtquick2/plugins.qmltypes
+++ b/src/imports/qtquick2/plugins.qmltypes
@@ -4,7 +4,7 @@ import QtQuick.tooling 1.2
// It is used for QML tooling purposes only.
//
// This file was auto-generated by:
-// 'qmlplugindump -nonrelocatable QtQuick 2.6'
+// 'qmlplugindump -nonrelocatable -noforceqtquick QtQuick 2.8'
Module {
dependencies: []
@@ -318,7 +318,9 @@ Module {
}
}
Property { name: "cursorRectangle"; type: "QRectF"; isReadonly: true }
+ Property { name: "anchorRectangle"; type: "QRectF"; isReadonly: true }
Property { name: "keyboardRectangle"; type: "QRectF"; isReadonly: true }
+ Property { name: "inputItemClipRectangle"; type: "QRectF"; isReadonly: true }
Property { name: "visible"; type: "bool"; isReadonly: true }
Property { name: "animating"; type: "bool"; isReadonly: true }
Property { name: "locale"; type: "QLocale"; isReadonly: true }
@@ -1156,6 +1158,7 @@ Module {
Property { name: "layoutDirection"; type: "Qt::LayoutDirection"; isReadonly: true }
Property { name: "supportsMultipleWindows"; type: "bool"; isReadonly: true }
Property { name: "state"; type: "Qt::ApplicationState"; isReadonly: true }
+ Property { name: "font"; type: "QFont"; isReadonly: true }
Signal {
name: "stateChanged"
Parameter { name: "state"; type: "Qt::ApplicationState" }
@@ -1213,6 +1216,24 @@ Module {
Property { name: "sourceSize"; type: "QSize"; isReadonly: true }
}
Component {
+ name: "QQuickBorderImageMesh"
+ prototype: "QQuickShaderEffectMesh"
+ exports: ["QtQuick/BorderImageMesh 2.8"]
+ exportMetaObjectRevisions: [0]
+ Enum {
+ name: "TileMode"
+ values: {
+ "Stretch": 0,
+ "Repeat": 1,
+ "Round": 2
+ }
+ }
+ Property { name: "border"; type: "QQuickScaleGrid"; isReadonly: true; isPointer: true }
+ Property { name: "size"; type: "QSize" }
+ Property { name: "horizontalTileMode"; type: "TileMode" }
+ Property { name: "verticalTileMode"; type: "TileMode" }
+ }
+ Component {
name: "QQuickCanvasItem"
defaultProperty: "data"
prototype: "QQuickItem"
@@ -1378,6 +1399,7 @@ Module {
Property { name: "source"; type: "QObject"; isPointer: true }
Property { name: "target"; type: "QObject"; isReadonly: true; isPointer: true }
Property { name: "hotSpot"; type: "QPointF" }
+ Property { name: "imageSource"; revision: 8; type: "QUrl" }
Property { name: "keys"; type: "QStringList" }
Property { name: "mimeData"; type: "QVariantMap" }
Property { name: "supportedActions"; type: "Qt::DropActions" }
@@ -1497,7 +1519,8 @@ Module {
"AutoFlickDirection": 0,
"HorizontalFlick": 1,
"VerticalFlick": 2,
- "HorizontalAndVerticalFlick": 3
+ "HorizontalAndVerticalFlick": 3,
+ "AutoFlickIfNeeded": 12
}
}
Property { name: "contentWidth"; type: "double" }
@@ -1730,6 +1753,15 @@ Module {
"Capitalize": 4
}
}
+ Enum {
+ name: "HintingPreference"
+ values: {
+ "PreferDefaultHinting": 0,
+ "PreferNoHinting": 1,
+ "PreferVerticalHinting": 2,
+ "PreferFullHinting": 3
+ }
+ }
Property { name: "family"; type: "string" }
Property { name: "styleName"; type: "string" }
Property { name: "bold"; type: "bool" }
@@ -1743,6 +1775,7 @@ Module {
Property { name: "capitalization"; type: "Capitalization" }
Property { name: "letterSpacing"; type: "double" }
Property { name: "wordSpacing"; type: "double" }
+ Property { name: "hintingPreference"; type: "HintingPreference" }
Method { name: "toString"; type: "string" }
}
Component {
@@ -1770,6 +1803,69 @@ Module {
Property { name: "color"; type: "QColor" }
}
Component {
+ name: "QQuickGraphicsInfo"
+ prototype: "QObject"
+ exports: ["QtQuick/GraphicsInfo 2.8"]
+ isCreatable: false
+ exportMetaObjectRevisions: [0]
+ Enum {
+ name: "GraphicsApi"
+ values: {
+ "Unknown": 0,
+ "Software": 1,
+ "OpenGL": 2,
+ "Direct3D12": 3
+ }
+ }
+ Enum {
+ name: "ShaderType"
+ values: {
+ "UnknownShadingLanguage": 0,
+ "GLSL": 1,
+ "HLSL": 2
+ }
+ }
+ Enum {
+ name: "ShaderCompilationType"
+ values: {
+ "RuntimeCompilation": 1,
+ "OfflineCompilation": 2
+ }
+ }
+ Enum {
+ name: "ShaderSourceType"
+ values: {
+ "ShaderSourceString": 1,
+ "ShaderSourceFile": 2,
+ "ShaderByteCode": 4
+ }
+ }
+ Enum {
+ name: "OpenGLContextProfile"
+ values: {
+ "OpenGLNoProfile": 0,
+ "OpenGLCoreProfile": 1,
+ "OpenGLCompatibilityProfile": 2
+ }
+ }
+ Enum {
+ name: "RenderableType"
+ values: {
+ "SurfaceFormatUnspecified": 0,
+ "SurfaceFormatOpenGL": 1,
+ "SurfaceFormatOpenGLES": 2
+ }
+ }
+ Property { name: "api"; type: "GraphicsApi"; isReadonly: true }
+ Property { name: "shaderType"; type: "ShaderType"; isReadonly: true }
+ Property { name: "shaderCompilationType"; type: "ShaderCompilationType"; isReadonly: true }
+ Property { name: "shaderSourceType"; type: "ShaderSourceType"; isReadonly: true }
+ Property { name: "majorVersion"; type: "int"; isReadonly: true }
+ Property { name: "minorVersion"; type: "int"; isReadonly: true }
+ Property { name: "profile"; type: "OpenGLContextProfile"; isReadonly: true }
+ Property { name: "renderableType"; type: "RenderableType"; isReadonly: true }
+ }
+ Component {
name: "QQuickGrid"
defaultProperty: "data"
prototype: "QQuickBasePositioner"
@@ -1840,8 +1936,12 @@ Module {
name: "QQuickGridView"
defaultProperty: "data"
prototype: "QQuickItemView"
- exports: ["QtQuick/GridView 2.0", "QtQuick/GridView 2.1"]
- exportMetaObjectRevisions: [0, 1]
+ exports: [
+ "QtQuick/GridView 2.0",
+ "QtQuick/GridView 2.1",
+ "QtQuick/GridView 2.7"
+ ]
+ exportMetaObjectRevisions: [0, 1, 7]
attachedType: "QQuickGridViewAttached"
Enum {
name: "Flow"
@@ -2110,6 +2210,18 @@ Module {
Parameter { name: "point"; type: "QPointF" }
}
Method {
+ name: "mapToGlobal"
+ revision: 7
+ type: "QPointF"
+ Parameter { name: "point"; type: "QPointF" }
+ }
+ Method {
+ name: "mapFromGlobal"
+ revision: 7
+ type: "QPointF"
+ Parameter { name: "point"; type: "QPointF" }
+ }
+ Method {
name: "mapFromItem"
Parameter { type: "QQmlV4Function"; isPointer: true }
}
@@ -2250,6 +2362,7 @@ Module {
Property { name: "currentIndex"; type: "int" }
Property { name: "currentItem"; type: "QQuickItem"; isReadonly: true; isPointer: true }
Property { name: "keyNavigationWraps"; type: "bool" }
+ Property { name: "keyNavigationEnabled"; revision: 7; type: "bool" }
Property { name: "cacheBuffer"; type: "int" }
Property { name: "displayMarginBeginning"; revision: 2; type: "int" }
Property { name: "displayMarginEnd"; revision: 2; type: "int" }
@@ -2275,6 +2388,7 @@ Module {
Property { name: "preferredHighlightBegin"; type: "double" }
Property { name: "preferredHighlightEnd"; type: "double" }
Property { name: "highlightMoveDuration"; type: "int" }
+ Signal { name: "keyNavigationEnabledChanged"; revision: 7 }
Signal { name: "populateTransitionChanged" }
Signal { name: "addTransitionChanged" }
Signal { name: "addDisplacedTransitionChanged" }
@@ -2549,9 +2663,10 @@ Module {
exports: [
"QtQuick/ListView 2.0",
"QtQuick/ListView 2.1",
- "QtQuick/ListView 2.4"
+ "QtQuick/ListView 2.4",
+ "QtQuick/ListView 2.7"
]
- exportMetaObjectRevisions: [0, 1, 2]
+ exportMetaObjectRevisions: [0, 1, 2, 7]
attachedType: "QQuickListViewAttached"
Enum {
name: "Orientation"
@@ -3004,8 +3119,8 @@ Module {
name: "QQuickPathView"
defaultProperty: "data"
prototype: "QQuickItem"
- exports: ["QtQuick/PathView 2.0"]
- exportMetaObjectRevisions: [0]
+ exports: ["QtQuick/PathView 2.0", "QtQuick/PathView 2.7"]
+ exportMetaObjectRevisions: [0, 7]
attachedType: "QQuickPathViewAttached"
Enum {
name: "HighlightRangeMode"
@@ -3024,6 +3139,14 @@ Module {
}
}
Enum {
+ name: "MovementDirection"
+ values: {
+ "Shortest": 0,
+ "Negative": 1,
+ "Positive": 2
+ }
+ }
+ Enum {
name: "PositionMode"
values: {
"Beginning": 0,
@@ -3055,10 +3178,12 @@ Module {
Property { name: "delegate"; type: "QQmlComponent"; isPointer: true }
Property { name: "pathItemCount"; type: "int" }
Property { name: "snapMode"; type: "SnapMode" }
+ Property { name: "movementDirection"; revision: 7; type: "MovementDirection" }
Property { name: "cacheItemCount"; type: "int" }
Signal { name: "snapPositionChanged" }
Signal { name: "movementStarted" }
Signal { name: "movementEnded" }
+ Signal { name: "movementDirectionChanged"; revision: 7 }
Signal { name: "flickStarted" }
Signal { name: "flickEnded" }
Signal { name: "dragStarted" }
@@ -3932,23 +4057,23 @@ Module {
}
Signal {
name: "styleChanged"
- Parameter { name: "style"; type: "TextStyle" }
+ Parameter { name: "style"; type: "QQuickText::TextStyle" }
}
Signal {
name: "horizontalAlignmentChanged"
- Parameter { name: "alignment"; type: "HAlignment" }
+ Parameter { name: "alignment"; type: "QQuickText::HAlignment" }
}
Signal {
name: "verticalAlignmentChanged"
- Parameter { name: "alignment"; type: "VAlignment" }
+ Parameter { name: "alignment"; type: "QQuickText::VAlignment" }
}
Signal {
name: "textFormatChanged"
- Parameter { name: "textFormat"; type: "TextFormat" }
+ Parameter { name: "textFormat"; type: "QQuickText::TextFormat" }
}
Signal {
name: "elideModeChanged"
- Parameter { name: "mode"; type: "TextElideMode" }
+ Parameter { name: "mode"; type: "QQuickText::TextElideMode" }
}
Signal { name: "contentSizeChanged" }
Signal {
@@ -3987,9 +4112,10 @@ Module {
"QtQuick/TextEdit 2.1",
"QtQuick/TextEdit 2.2",
"QtQuick/TextEdit 2.3",
- "QtQuick/TextEdit 2.6"
+ "QtQuick/TextEdit 2.6",
+ "QtQuick/TextEdit 2.7"
]
- exportMetaObjectRevisions: [0, 1, 2, 3, 6]
+ exportMetaObjectRevisions: [0, 1, 2, 3, 6, 7]
Enum {
name: "HAlignment"
values: {
@@ -4060,6 +4186,7 @@ Module {
Property { name: "cursorPosition"; type: "int" }
Property { name: "cursorRectangle"; type: "QRectF"; isReadonly: true }
Property { name: "cursorDelegate"; type: "QQmlComponent"; isPointer: true }
+ Property { name: "overwriteMode"; type: "bool" }
Property { name: "selectionStart"; type: "int"; isReadonly: true }
Property { name: "selectionEnd"; type: "int"; isReadonly: true }
Property { name: "selectedText"; type: "string"; isReadonly: true }
@@ -4089,6 +4216,8 @@ Module {
Property { name: "leftPadding"; revision: 6; type: "double" }
Property { name: "rightPadding"; revision: 6; type: "double" }
Property { name: "bottomPadding"; revision: 6; type: "double" }
+ Property { name: "preeditText"; revision: 7; type: "string"; isReadonly: true }
+ Signal { name: "preeditTextChanged"; revision: 7 }
Signal { name: "contentSizeChanged" }
Signal {
name: "colorChanged"
@@ -4108,15 +4237,15 @@ Module {
}
Signal {
name: "horizontalAlignmentChanged"
- Parameter { name: "alignment"; type: "HAlignment" }
+ Parameter { name: "alignment"; type: "QQuickTextEdit::HAlignment" }
}
Signal {
name: "verticalAlignmentChanged"
- Parameter { name: "alignment"; type: "VAlignment" }
+ Parameter { name: "alignment"; type: "QQuickTextEdit::VAlignment" }
}
Signal {
name: "textFormatChanged"
- Parameter { name: "textFormat"; type: "TextFormat" }
+ Parameter { name: "textFormat"; type: "QQuickTextEdit::TextFormat" }
}
Signal {
name: "readOnlyChanged"
@@ -4127,6 +4256,10 @@ Module {
Parameter { name: "isCursorVisible"; type: "bool" }
}
Signal {
+ name: "overwriteModeChanged"
+ Parameter { name: "overwriteMode"; type: "bool" }
+ }
+ Signal {
name: "activeFocusOnPressChanged"
Parameter { name: "activeFocusOnPressed"; type: "bool" }
}
@@ -4149,7 +4282,7 @@ Module {
}
Signal {
name: "mouseSelectionModeChanged"
- Parameter { name: "mode"; type: "SelectionMode" }
+ Parameter { name: "mode"; type: "QQuickTextEdit::SelectionMode" }
}
Signal {
name: "linkActivated"
@@ -4200,6 +4333,7 @@ Module {
revision: 2
Parameter { name: "text"; type: "string" }
}
+ Method { name: "clear"; revision: 7 }
Method {
name: "inputMethodQuery"
revision: 4
@@ -4255,9 +4389,10 @@ Module {
"QtQuick/TextInput 2.0",
"QtQuick/TextInput 2.2",
"QtQuick/TextInput 2.4",
- "QtQuick/TextInput 2.6"
+ "QtQuick/TextInput 2.6",
+ "QtQuick/TextInput 2.7"
]
- exportMetaObjectRevisions: [0, 2, 3, 6]
+ exportMetaObjectRevisions: [0, 2, 3, 6, 7]
Enum {
name: "EchoMode"
values: {
@@ -4329,6 +4464,7 @@ Module {
Property { name: "cursorPosition"; type: "int" }
Property { name: "cursorRectangle"; type: "QRectF"; isReadonly: true }
Property { name: "cursorDelegate"; type: "QQmlComponent"; isPointer: true }
+ Property { name: "overwriteMode"; type: "bool" }
Property { name: "selectionStart"; type: "int"; isReadonly: true }
Property { name: "selectionEnd"; type: "int"; isReadonly: true }
Property { name: "selectedText"; type: "string"; isReadonly: true }
@@ -4342,6 +4478,7 @@ Module {
Property { name: "passwordCharacter"; type: "string" }
Property { name: "passwordMaskDelay"; revision: 3; type: "int" }
Property { name: "displayText"; type: "string"; isReadonly: true }
+ Property { name: "preeditText"; revision: 7; type: "string"; isReadonly: true }
Property { name: "autoScroll"; type: "bool" }
Property { name: "selectByMouse"; type: "bool" }
Property { name: "mouseSelectionMode"; type: "SelectionMode" }
@@ -4366,11 +4503,11 @@ Module {
}
Signal {
name: "horizontalAlignmentChanged"
- Parameter { name: "alignment"; type: "HAlignment" }
+ Parameter { name: "alignment"; type: "QQuickTextInput::HAlignment" }
}
Signal {
name: "verticalAlignmentChanged"
- Parameter { name: "alignment"; type: "VAlignment" }
+ Parameter { name: "alignment"; type: "QQuickTextInput::VAlignment" }
}
Signal {
name: "readOnlyChanged"
@@ -4381,6 +4518,10 @@ Module {
Parameter { name: "isCursorVisible"; type: "bool" }
}
Signal {
+ name: "overwriteModeChanged"
+ Parameter { name: "overwriteMode"; type: "bool" }
+ }
+ Signal {
name: "maximumLengthChanged"
Parameter { name: "maximumLength"; type: "int" }
}
@@ -4390,13 +4531,14 @@ Module {
}
Signal {
name: "echoModeChanged"
- Parameter { name: "echoMode"; type: "EchoMode" }
+ Parameter { name: "echoMode"; type: "QQuickTextInput::EchoMode" }
}
Signal {
name: "passwordMaskDelayChanged"
revision: 3
Parameter { name: "delay"; type: "int" }
}
+ Signal { name: "preeditTextChanged"; revision: 7 }
Signal {
name: "activeFocusOnPressChanged"
Parameter { name: "activeFocusOnPress"; type: "bool" }
@@ -4411,7 +4553,7 @@ Module {
}
Signal {
name: "mouseSelectionModeChanged"
- Parameter { name: "mode"; type: "SelectionMode" }
+ Parameter { name: "mode"; type: "QQuickTextInput::SelectionMode" }
}
Signal { name: "contentSizeChanged" }
Signal { name: "paddingChanged"; revision: 6 }
@@ -4453,6 +4595,7 @@ Module {
revision: 3
Parameter { name: "position"; type: "int" }
}
+ Method { name: "clear"; revision: 7 }
Method {
name: "positionAt"
Parameter { name: "args"; type: "QQmlV4Function"; isPointer: true }
@@ -4618,6 +4761,7 @@ Module {
Property { name: "pixelDelta"; type: "QPoint"; isReadonly: true }
Property { name: "buttons"; type: "int"; isReadonly: true }
Property { name: "modifiers"; type: "int"; isReadonly: true }
+ Property { name: "inverted"; type: "bool"; isReadonly: true }
Property { name: "accepted"; type: "bool" }
}
Component {
diff --git a/src/imports/settings/plugin.cpp b/src/imports/settings/plugin.cpp
index 04e93fa49c..c422296446 100644
--- a/src/imports/settings/plugin.cpp
+++ b/src/imports/settings/plugin.cpp
@@ -58,7 +58,7 @@ class QmlSettingsPlugin : public QQmlExtensionPlugin
public:
QmlSettingsPlugin(QObject *parent = 0) : QQmlExtensionPlugin(parent) { initResources(); }
- virtual void registerTypes(const char *uri)
+ void registerTypes(const char *uri) Q_DECL_OVERRIDE
{
Q_ASSERT(QByteArray(uri) == QByteArray("Qt.labs.settings"));
qmlRegisterType<QQmlSettings>(uri, 1, 0, "Settings");
diff --git a/src/imports/settings/plugins.qmltypes b/src/imports/settings/plugins.qmltypes
index eaa310edc9..40d8746525 100644
--- a/src/imports/settings/plugins.qmltypes
+++ b/src/imports/settings/plugins.qmltypes
@@ -4,7 +4,7 @@ import QtQuick.tooling 1.2
// It is used for QML tooling purposes only.
//
// This file was auto-generated by:
-// 'qmlplugindump -nonrelocatable Qt.labs.settings 1.0'
+// 'qmlplugindump -nonrelocatable -noforceqtquick Qt.labs.settings 1.0'
Module {
dependencies: []
diff --git a/src/imports/statemachine/plugin.cpp b/src/imports/statemachine/plugin.cpp
index 6d0549ed5d..ae32f6446a 100644
--- a/src/imports/statemachine/plugin.cpp
+++ b/src/imports/statemachine/plugin.cpp
@@ -63,7 +63,7 @@ class QtQmlStateMachinePlugin : public QQmlExtensionPlugin
public:
QtQmlStateMachinePlugin(QObject *parent = 0) : QQmlExtensionPlugin(parent) { initResources(); }
- void registerTypes(const char *uri)
+ void registerTypes(const char *uri) Q_DECL_OVERRIDE
{
qmlRegisterType<State>(uri, 1, 0, "State");
qmlRegisterType<StateMachine>(uri, 1, 0, "StateMachine");
diff --git a/src/imports/statemachine/plugins.qmltypes b/src/imports/statemachine/plugins.qmltypes
index c4b453b9e4..0fe9b63e03 100644
--- a/src/imports/statemachine/plugins.qmltypes
+++ b/src/imports/statemachine/plugins.qmltypes
@@ -4,7 +4,7 @@ import QtQuick.tooling 1.2
// It is used for QML tooling purposes only.
//
// This file was auto-generated by:
-// 'qmlplugindump -nonrelocatable QtQml.StateMachine 1.0'
+// 'qmlplugindump -nonrelocatable -noforceqtquick QtQml.StateMachine 1.0'
Module {
dependencies: []
diff --git a/src/imports/statemachine/signaltransition.cpp b/src/imports/statemachine/signaltransition.cpp
index f9de70263c..2e6381fc08 100644
--- a/src/imports/statemachine/signaltransition.cpp
+++ b/src/imports/statemachine/signaltransition.cpp
@@ -158,7 +158,7 @@ void SignalTransition::invoke()
void SignalTransition::connectTriggered()
{
- if (!m_complete || !m_cdata)
+ if (!m_complete || !m_compilationUnit)
return;
QObject *target = senderObject();
@@ -178,7 +178,7 @@ void SignalTransition::connectTriggered()
QQmlBoundSignalExpression *expression = ctxtdata ?
new QQmlBoundSignalExpression(target, signalIndex,
- ctxtdata, this, m_cdata->compilationUnit->runtimeFunctions[binding->value.compiledScriptIndex]) : 0;
+ ctxtdata, this, m_compilationUnit->runtimeFunctions[binding->value.compiledScriptIndex]) : 0;
if (expression)
expression->setNotifyOnValueChanged(false);
m_signalExpression = expression;
@@ -191,7 +191,7 @@ void SignalTransitionParser::verifyBindings(const QV4::CompiledData::Unit *qmlUn
QString propName = qmlUnit->stringAt(binding->propertyNameIndex);
- if (propName != QStringLiteral("onTriggered")) {
+ if (propName != QLatin1String("onTriggered")) {
error(props.at(ii), SignalTransition::tr("Cannot assign to non-existent property \"%1\"").arg(propName));
return;
}
@@ -203,10 +203,10 @@ void SignalTransitionParser::verifyBindings(const QV4::CompiledData::Unit *qmlUn
}
}
-void SignalTransitionParser::applyBindings(QObject *object, QQmlCompiledData *cdata, const QList<const QV4::CompiledData::Binding *> &bindings)
+void SignalTransitionParser::applyBindings(QObject *object, QV4::CompiledData::CompilationUnit *compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings)
{
SignalTransition *st = qobject_cast<SignalTransition*>(object);
- st->m_cdata = cdata;
+ st->m_compilationUnit = compilationUnit;
st->m_bindings = bindings;
}
diff --git a/src/imports/statemachine/signaltransition.h b/src/imports/statemachine/signaltransition.h
index 3471aea0c8..c6512e2b19 100644
--- a/src/imports/statemachine/signaltransition.h
+++ b/src/imports/statemachine/signaltransition.h
@@ -48,7 +48,6 @@
#include <QtQml/qqmlparserstatus.h>
#include <private/qqmlcustomparser_p.h>
#include <private/qqmlboundsignalexpressionpointer_p.h>
-#include <private/qqmlcompiler_p.h>
QT_BEGIN_NAMESPACE
@@ -90,7 +89,7 @@ private:
QJSValue m_signal;
QQmlScriptString m_guard;
bool m_complete;
- QQmlRefPointer<QQmlCompiledData> m_cdata;
+ QQmlRefPointer<QV4::CompiledData::CompilationUnit> m_compilationUnit;
QList<const QV4::CompiledData::Binding *> m_bindings;
QQmlBoundSignalExpressionPointer m_signalExpression;
};
@@ -99,7 +98,7 @@ class SignalTransitionParser : public QQmlCustomParser
{
public:
void verifyBindings(const QV4::CompiledData::Unit *qmlUnit, const QList<const QV4::CompiledData::Binding *> &props) Q_DECL_OVERRIDE;
- void applyBindings(QObject *object, QQmlCompiledData *cdata, const QList<const QV4::CompiledData::Binding *> &bindings) Q_DECL_OVERRIDE;
+ void applyBindings(QObject *object, QV4::CompiledData::CompilationUnit *compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings) Q_DECL_OVERRIDE;
};
QT_END_NAMESPACE
diff --git a/src/imports/statemachine/statemachine.h b/src/imports/statemachine/statemachine.h
index 8a05acfbde..59a810f387 100644
--- a/src/imports/statemachine/statemachine.h
+++ b/src/imports/statemachine/statemachine.h
@@ -48,8 +48,6 @@
QT_BEGIN_NAMESPACE
-class QQmlOpenMetaObject;
-
class StateMachine : public QStateMachine, public QQmlParserStatus
{
Q_OBJECT
diff --git a/src/imports/testlib/TestCase.qml b/src/imports/testlib/TestCase.qml
index ca3e3b0a30..683200a259 100644
--- a/src/imports/testlib/TestCase.qml
+++ b/src/imports/testlib/TestCase.qml
@@ -394,6 +394,73 @@ Item {
throw new Error("QtQuickTest::fail")
}
+ /*!
+ \since 5.8
+ \qmlmethod TestCase::tryVerify(function, timeout = 5000, message = "")
+
+ Fails the current test case if \a function does not evaluate to
+ \c true before the specified \a timeout (in milliseconds) has elapsed.
+ The function is evaluated multiple times until the timeout is
+ reached. An optional \a message is displayed upon failure.
+
+ This function is intended for testing applications where a condition
+ changes based on asynchronous events. Use verify() for testing
+ synchronous condition changes, and tryCompare() for testing
+ asynchronous property changes.
+
+ For example, in the code below, it's not possible to use tryCompare(),
+ because the \c currentItem property might be \c null for a short period
+ of time:
+
+ \code
+ tryCompare(listView.currentItem, "text", "Hello");
+ \endcode
+
+ Instead, we can use tryVerify() to first check that \c currentItem
+ isn't \c null, and then use a regular compare afterwards:
+
+ \code
+ tryVerify(function(){ return listView.currentItem })
+ compare(listView.currentItem.text, "Hello")
+ \endcode
+
+ \sa verify(), compare(), tryCompare(), SignalSpy::wait()
+ */
+ function tryVerify(expressionFunction, timeout, msg) {
+ if (!expressionFunction || !(expressionFunction instanceof Function)) {
+ qtest_results.fail("First argument must be a function", util.callerFile(), util.callerLine())
+ throw new Error("QtQuickTest::fail")
+ }
+
+ if (timeout && typeof(timeout) !== "number") {
+ qtest_results.fail("timeout argument must be a number", util.callerFile(), util.callerLine())
+ throw new Error("QtQuickTest::fail")
+ }
+
+ if (msg && typeof(msg) !== "string") {
+ qtest_results.fail("message argument must be a string", util.callerFile(), util.callerLine())
+ throw new Error("QtQuickTest::fail")
+ }
+
+ if (!timeout)
+ timeout = 5000
+
+ if (msg === undefined)
+ msg = "function returned false"
+
+ if (!expressionFunction())
+ wait(0)
+
+ var i = 0
+ while (i < timeout && !expressionFunction()) {
+ wait(50)
+ i += 50
+ }
+
+ if (!qtest_results.verify(expressionFunction(), msg, util.callerFile(), util.callerLine()))
+ throw new Error("QtQuickTest::fail")
+ }
+
/*! \internal */
// Determine what is o.
// Discussions and reference: http://philrathe.com/articles/equiv
@@ -711,6 +778,11 @@ Item {
\sa compare(), SignalSpy::wait()
*/
function tryCompare(obj, prop, value, timeout, msg) {
+ if (arguments.length == 1 || (typeof(prop) != "string" && typeof(prop) != "number")) {
+ qtest_results.fail("A property name as string or index is required for tryCompare",
+ util.callerFile(), util.callerLine())
+ throw new Error("QtQuickTest::fail")
+ }
if (arguments.length == 2) {
qtest_results.fail("A value is required for tryCompare",
util.callerFile(), util.callerLine())
diff --git a/src/imports/testlib/main.cpp b/src/imports/testlib/main.cpp
index 4147f0207e..4e2bd919e9 100644
--- a/src/imports/testlib/main.cpp
+++ b/src/imports/testlib/main.cpp
@@ -151,7 +151,7 @@ class QTestQmlModule : public QQmlExtensionPlugin
public:
QTestQmlModule(QObject *parent = 0) : QQmlExtensionPlugin(parent) { initResources(); }
- virtual void registerTypes(const char *uri)
+ void registerTypes(const char *uri) Q_DECL_OVERRIDE
{
Q_ASSERT(QLatin1String(uri) == QLatin1String("QtTest"));
qmlRegisterType<QuickTestResult, 0>(uri,1,0,"TestResult");
@@ -159,10 +159,6 @@ public:
qmlRegisterType<QuickTestEvent>(uri,1,0,"TestEvent");
qmlRegisterType<QuickTestUtil>(uri,1,0,"TestUtil");
}
-
- void initializeEngine(QQmlEngine *, const char *)
- {
- }
};
QT_END_NAMESPACE
diff --git a/src/imports/testlib/plugins.qmltypes b/src/imports/testlib/plugins.qmltypes
index 2beb38a940..563778e55e 100644
--- a/src/imports/testlib/plugins.qmltypes
+++ b/src/imports/testlib/plugins.qmltypes
@@ -4,15 +4,16 @@ import QtQuick.tooling 1.2
// It is used for QML tooling purposes only.
//
// This file was auto-generated by:
-// 'qmlplugindump -nonrelocatable QtTest 1.0'
+// 'qmlplugindump -nonrelocatable -noforceqtquick QtTest 1.1'
Module {
- dependencies: []
+ dependencies: ["QtQuick 2.0"]
Component {
name: "QuickTestEvent"
prototype: "QObject"
exports: ["QtTest/TestEvent 1.0"]
exportMetaObjectRevisions: [0]
+ Property { name: "defaultMouseDelay"; type: "int"; isReadonly: true }
Method {
name: "keyPress"
type: "bool"
diff --git a/src/imports/window/plugin.cpp b/src/imports/window/plugin.cpp
index 1e528fcbba..200f6f7b08 100644
--- a/src/imports/window/plugin.cpp
+++ b/src/imports/window/plugin.cpp
@@ -73,7 +73,7 @@ class QtQuick2WindowPlugin : public QQmlExtensionPlugin
Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid)
public:
QtQuick2WindowPlugin(QObject *parent = 0) : QQmlExtensionPlugin(parent) { initResources(); }
- virtual void registerTypes(const char *uri)
+ void registerTypes(const char *uri) Q_DECL_OVERRIDE
{
Q_ASSERT(QLatin1String(uri) == QLatin1String("QtQuick.Window"));
Q_UNUSED(uri);
diff --git a/src/imports/window/plugins.qmltypes b/src/imports/window/plugins.qmltypes
index 8c21271614..6a8dbfa024 100644
--- a/src/imports/window/plugins.qmltypes
+++ b/src/imports/window/plugins.qmltypes
@@ -7,7 +7,20 @@ import QtQuick.tooling 1.2
// 'qmlplugindump -nonrelocatable QtQuick.Window 2.2'
Module {
- dependencies: []
+ dependencies: ["QtQuick 2.8"]
+ Component {
+ name: "QQuickRootItem"
+ defaultProperty: "data"
+ prototype: "QQuickItem"
+ Method {
+ name: "setWidth"
+ Parameter { name: "w"; type: "int" }
+ }
+ Method {
+ name: "setHeight"
+ Parameter { name: "h"; type: "int" }
+ }
+ }
Component {
name: "QQuickScreen"
prototype: "QObject"
@@ -102,6 +115,7 @@ Module {
Property { name: "contentItem"; type: "QQuickItem"; isReadonly: true; isPointer: true }
Property { name: "width"; type: "int"; isReadonly: true }
Property { name: "height"; type: "int"; isReadonly: true }
+ Property { name: "window"; type: "QQuickWindow"; isReadonly: true; isPointer: true }
}
Component {
name: "QQuickWindowQmlImpl"
diff --git a/src/imports/xmllistmodel/plugin.cpp b/src/imports/xmllistmodel/plugin.cpp
index 341baae2a3..af7625c96a 100644
--- a/src/imports/xmllistmodel/plugin.cpp
+++ b/src/imports/xmllistmodel/plugin.cpp
@@ -58,7 +58,7 @@ class QmlXmlListModelPlugin : public QQmlExtensionPlugin
public:
QmlXmlListModelPlugin(QObject *parent = 0) : QQmlExtensionPlugin(parent) { initResources(); }
- virtual void registerTypes(const char *uri)
+ void registerTypes(const char *uri) Q_DECL_OVERRIDE
{
Q_ASSERT(QLatin1String(uri) == QLatin1String("QtQuick.XmlListModel"));
qmlRegisterType<QQuickXmlListModel>(uri,2,0,"XmlListModel");
diff --git a/src/imports/xmllistmodel/plugins.qmltypes b/src/imports/xmllistmodel/plugins.qmltypes
index d098d11409..cc675d5f83 100644
--- a/src/imports/xmllistmodel/plugins.qmltypes
+++ b/src/imports/xmllistmodel/plugins.qmltypes
@@ -7,272 +7,7 @@ import QtQuick.tooling 1.2
// 'qmlplugindump -nonrelocatable QtQuick.XmlListModel 2.0'
Module {
- dependencies: []
- Component {
- name: "QAbstractItemModel"
- prototype: "QObject"
- Enum {
- name: "LayoutChangeHint"
- values: {
- "NoLayoutChangeHint": 0,
- "VerticalSortHint": 1,
- "HorizontalSortHint": 2
- }
- }
- Signal {
- name: "dataChanged"
- Parameter { name: "topLeft"; type: "QModelIndex" }
- Parameter { name: "bottomRight"; type: "QModelIndex" }
- Parameter { name: "roles"; type: "QVector<int>" }
- }
- Signal {
- name: "dataChanged"
- Parameter { name: "topLeft"; type: "QModelIndex" }
- Parameter { name: "bottomRight"; type: "QModelIndex" }
- }
- Signal {
- name: "headerDataChanged"
- Parameter { name: "orientation"; type: "Qt::Orientation" }
- Parameter { name: "first"; type: "int" }
- Parameter { name: "last"; type: "int" }
- }
- Signal {
- name: "layoutChanged"
- Parameter { name: "parents"; type: "QList<QPersistentModelIndex>" }
- Parameter { name: "hint"; type: "QAbstractItemModel::LayoutChangeHint" }
- }
- Signal {
- name: "layoutChanged"
- Parameter { name: "parents"; type: "QList<QPersistentModelIndex>" }
- }
- Signal { name: "layoutChanged" }
- Signal {
- name: "layoutAboutToBeChanged"
- Parameter { name: "parents"; type: "QList<QPersistentModelIndex>" }
- Parameter { name: "hint"; type: "QAbstractItemModel::LayoutChangeHint" }
- }
- Signal {
- name: "layoutAboutToBeChanged"
- Parameter { name: "parents"; type: "QList<QPersistentModelIndex>" }
- }
- Signal { name: "layoutAboutToBeChanged" }
- Signal {
- name: "rowsAboutToBeInserted"
- Parameter { name: "parent"; type: "QModelIndex" }
- Parameter { name: "first"; type: "int" }
- Parameter { name: "last"; type: "int" }
- }
- Signal {
- name: "rowsInserted"
- Parameter { name: "parent"; type: "QModelIndex" }
- Parameter { name: "first"; type: "int" }
- Parameter { name: "last"; type: "int" }
- }
- Signal {
- name: "rowsAboutToBeRemoved"
- Parameter { name: "parent"; type: "QModelIndex" }
- Parameter { name: "first"; type: "int" }
- Parameter { name: "last"; type: "int" }
- }
- Signal {
- name: "rowsRemoved"
- Parameter { name: "parent"; type: "QModelIndex" }
- Parameter { name: "first"; type: "int" }
- Parameter { name: "last"; type: "int" }
- }
- Signal {
- name: "columnsAboutToBeInserted"
- Parameter { name: "parent"; type: "QModelIndex" }
- Parameter { name: "first"; type: "int" }
- Parameter { name: "last"; type: "int" }
- }
- Signal {
- name: "columnsInserted"
- Parameter { name: "parent"; type: "QModelIndex" }
- Parameter { name: "first"; type: "int" }
- Parameter { name: "last"; type: "int" }
- }
- Signal {
- name: "columnsAboutToBeRemoved"
- Parameter { name: "parent"; type: "QModelIndex" }
- Parameter { name: "first"; type: "int" }
- Parameter { name: "last"; type: "int" }
- }
- Signal {
- name: "columnsRemoved"
- Parameter { name: "parent"; type: "QModelIndex" }
- Parameter { name: "first"; type: "int" }
- Parameter { name: "last"; type: "int" }
- }
- Signal { name: "modelAboutToBeReset" }
- Signal { name: "modelReset" }
- Signal {
- name: "rowsAboutToBeMoved"
- Parameter { name: "sourceParent"; type: "QModelIndex" }
- Parameter { name: "sourceStart"; type: "int" }
- Parameter { name: "sourceEnd"; type: "int" }
- Parameter { name: "destinationParent"; type: "QModelIndex" }
- Parameter { name: "destinationRow"; type: "int" }
- }
- Signal {
- name: "rowsMoved"
- Parameter { name: "parent"; type: "QModelIndex" }
- Parameter { name: "start"; type: "int" }
- Parameter { name: "end"; type: "int" }
- Parameter { name: "destination"; type: "QModelIndex" }
- Parameter { name: "row"; type: "int" }
- }
- Signal {
- name: "columnsAboutToBeMoved"
- Parameter { name: "sourceParent"; type: "QModelIndex" }
- Parameter { name: "sourceStart"; type: "int" }
- Parameter { name: "sourceEnd"; type: "int" }
- Parameter { name: "destinationParent"; type: "QModelIndex" }
- Parameter { name: "destinationColumn"; type: "int" }
- }
- Signal {
- name: "columnsMoved"
- Parameter { name: "parent"; type: "QModelIndex" }
- Parameter { name: "start"; type: "int" }
- Parameter { name: "end"; type: "int" }
- Parameter { name: "destination"; type: "QModelIndex" }
- Parameter { name: "column"; type: "int" }
- }
- Method { name: "submit"; type: "bool" }
- Method { name: "revert" }
- Method {
- name: "hasIndex"
- type: "bool"
- Parameter { name: "row"; type: "int" }
- Parameter { name: "column"; type: "int" }
- Parameter { name: "parent"; type: "QModelIndex" }
- }
- Method {
- name: "hasIndex"
- type: "bool"
- Parameter { name: "row"; type: "int" }
- Parameter { name: "column"; type: "int" }
- }
- Method {
- name: "index"
- type: "QModelIndex"
- Parameter { name: "row"; type: "int" }
- Parameter { name: "column"; type: "int" }
- Parameter { name: "parent"; type: "QModelIndex" }
- }
- Method {
- name: "index"
- type: "QModelIndex"
- Parameter { name: "row"; type: "int" }
- Parameter { name: "column"; type: "int" }
- }
- Method {
- name: "parent"
- type: "QModelIndex"
- Parameter { name: "child"; type: "QModelIndex" }
- }
- Method {
- name: "sibling"
- type: "QModelIndex"
- Parameter { name: "row"; type: "int" }
- Parameter { name: "column"; type: "int" }
- Parameter { name: "idx"; type: "QModelIndex" }
- }
- Method {
- name: "rowCount"
- type: "int"
- Parameter { name: "parent"; type: "QModelIndex" }
- }
- Method { name: "rowCount"; type: "int" }
- Method {
- name: "columnCount"
- type: "int"
- Parameter { name: "parent"; type: "QModelIndex" }
- }
- Method { name: "columnCount"; type: "int" }
- Method {
- name: "hasChildren"
- type: "bool"
- Parameter { name: "parent"; type: "QModelIndex" }
- }
- Method { name: "hasChildren"; type: "bool" }
- Method {
- name: "data"
- type: "QVariant"
- Parameter { name: "index"; type: "QModelIndex" }
- Parameter { name: "role"; type: "int" }
- }
- Method {
- name: "data"
- type: "QVariant"
- Parameter { name: "index"; type: "QModelIndex" }
- }
- Method {
- name: "setData"
- type: "bool"
- Parameter { name: "index"; type: "QModelIndex" }
- Parameter { name: "value"; type: "QVariant" }
- Parameter { name: "role"; type: "int" }
- }
- Method {
- name: "setData"
- type: "bool"
- Parameter { name: "index"; type: "QModelIndex" }
- Parameter { name: "value"; type: "QVariant" }
- }
- Method {
- name: "headerData"
- type: "QVariant"
- Parameter { name: "section"; type: "int" }
- Parameter { name: "orientation"; type: "Qt::Orientation" }
- Parameter { name: "role"; type: "int" }
- }
- Method {
- name: "headerData"
- type: "QVariant"
- Parameter { name: "section"; type: "int" }
- Parameter { name: "orientation"; type: "Qt::Orientation" }
- }
- Method {
- name: "fetchMore"
- Parameter { name: "parent"; type: "QModelIndex" }
- }
- Method {
- name: "canFetchMore"
- type: "bool"
- Parameter { name: "parent"; type: "QModelIndex" }
- }
- Method {
- name: "flags"
- type: "Qt::ItemFlags"
- Parameter { name: "index"; type: "QModelIndex" }
- }
- Method {
- name: "match"
- type: "QModelIndexList"
- Parameter { name: "start"; type: "QModelIndex" }
- Parameter { name: "role"; type: "int" }
- Parameter { name: "value"; type: "QVariant" }
- Parameter { name: "hits"; type: "int" }
- Parameter { name: "flags"; type: "Qt::MatchFlags" }
- }
- Method {
- name: "match"
- type: "QModelIndexList"
- Parameter { name: "start"; type: "QModelIndex" }
- Parameter { name: "role"; type: "int" }
- Parameter { name: "value"; type: "QVariant" }
- Parameter { name: "hits"; type: "int" }
- }
- Method {
- name: "match"
- type: "QModelIndexList"
- Parameter { name: "start"; type: "QModelIndex" }
- Parameter { name: "role"; type: "int" }
- Parameter { name: "value"; type: "QVariant" }
- }
- }
- Component { name: "QAbstractListModel"; prototype: "QAbstractItemModel" }
+ dependencies: ["QtQuick 2.8"]
Component {
name: "QQuickXmlListModel"
defaultProperty: "roles"
diff --git a/src/particles/qquickcustomparticle.cpp b/src/particles/qquickcustomparticle.cpp
index 3cb7f6c2bf..c08ae3d9ff 100644
--- a/src/particles/qquickcustomparticle.cpp
+++ b/src/particles/qquickcustomparticle.cpp
@@ -94,6 +94,8 @@ struct PlainVertices {
QQuickCustomParticle::QQuickCustomParticle(QQuickItem* parent)
: QQuickParticlePainter(parent)
+ , m_common(this, [this](int mappedId){this->propertyChanged(mappedId);})
+ , m_myMetaObject(nullptr)
, m_dirtyUniforms(true)
, m_dirtyUniformValues(true)
, m_dirtyTextureProviders(true)
@@ -113,7 +115,10 @@ QQuickCustomParticle::~QQuickCustomParticle()
void QQuickCustomParticle::componentComplete()
{
- m_common.updateShader(this, Key::FragmentShader);
+ if (!m_myMetaObject)
+ m_myMetaObject = metaObject();
+
+ m_common.updateShader(this, m_myMetaObject, Key::FragmentShader);
updateVertexShader();
reset();
QQuickParticlePainter::componentComplete();
@@ -137,7 +142,7 @@ void QQuickCustomParticle::setFragmentShader(const QByteArray &code)
m_common.source.sourceCode[Key::FragmentShader] = code;
m_dirtyProgram = true;
if (isComponentComplete()) {
- m_common.updateShader(this, Key::FragmentShader);
+ m_common.updateShader(this, m_myMetaObject, Key::FragmentShader);
reset();
}
emit fragmentShaderChanged();
@@ -201,9 +206,8 @@ void QQuickCustomParticle::setVertexShader(const QByteArray &code)
void QQuickCustomParticle::updateVertexShader()
{
m_common.disconnectPropertySignals(this, Key::VertexShader);
- qDeleteAll(m_common.signalMappers[Key::VertexShader]);
m_common.uniformData[Key::VertexShader].clear();
- m_common.signalMappers[Key::VertexShader].clear();
+ m_common.clearSignalMappers(Key::VertexShader);
m_common.attributes.clear();
m_common.attributes.append("qt_ParticlePos");
m_common.attributes.append("qt_ParticleTex");
@@ -224,9 +228,9 @@ void QQuickCustomParticle::updateVertexShader()
const QByteArray &code = m_common.source.sourceCode[Key::VertexShader];
if (!code.isEmpty())
- m_common.lookThroughShaderCode(this, Key::VertexShader, code);
+ m_common.lookThroughShaderCode(this, m_myMetaObject, Key::VertexShader, code);
- m_common.connectPropertySignals(this, Key::VertexShader);
+ m_common.connectPropertySignals(this, m_myMetaObject, Key::VertexShader);
}
void QQuickCustomParticle::reset()
@@ -237,7 +241,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 +262,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 +273,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 +296,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 +309,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 +329,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);
@@ -391,14 +395,14 @@ void QQuickCustomParticle::sourceDestroyed(QObject *object)
void QQuickCustomParticle::propertyChanged(int mappedId)
{
bool textureProviderChanged;
- m_common.propertyChanged(this, mappedId, &textureProviderChanged);
+ m_common.propertyChanged(this, m_myMetaObject, mappedId, &textureProviderChanged);
m_dirtyTextureProviders |= textureProviderChanged;
m_dirtyUniformValues = true;
update();
}
-void QQuickCustomParticle::buildData(QQuickShaderEffectNode *rootNode)
+void QQuickCustomParticle::buildData(QQuickOpenGLShaderEffectNode *rootNode)
{
if (!rootNode)
return;
@@ -408,9 +412,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..e9d68cbe5c 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,29 +88,31 @@ 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);
private Q_SLOTS:
void sourceDestroyed(QObject *object);
- void propertyChanged(int mappedId);
private:
- typedef QQuickShaderEffectMaterialKey Key;
- typedef QQuickShaderEffectMaterial::UniformData UniformData;
+ void propertyChanged(int mappedId);
+
+ typedef QQuickOpenGLShaderEffectMaterialKey Key;
+ typedef QQuickOpenGLShaderEffectMaterial::UniformData UniformData;
- void buildData(QQuickShaderEffectNode *rootNode);
+ void buildData(QQuickOpenGLShaderEffectNode *rootNode);
void updateVertexShader();
- QQuickShaderEffectCommon m_common;
+ QQuickOpenGLShaderEffectCommon m_common;
+ const QMetaObject *m_myMetaObject;
- QHash<int, QQuickShaderEffectNode*> m_nodes;
+ QHash<int, QQuickOpenGLShaderEffectNode*> m_nodes;
qreal m_lastTime;
uint m_dirtyUniforms : 1;
diff --git a/src/particles/qquickimageparticle.cpp b/src/particles/qquickimageparticle.cpp
index 07cbee1383..c68153aca8 100644
--- a/src/particles/qquickimageparticle.cpp
+++ b/src/particles/qquickimageparticle.cpp
@@ -49,6 +49,7 @@
#include <private/qquicksprite_p.h>
#include <private/qquickspriteengine_p.h>
#include <QOpenGLFunctions>
+#include <QSGRendererInterface>
#include <QtQuick/private/qsgshadersourcebuilder_p.h>
#include <QtQuick/private/qsgtexture_p.h>
#include <private/qqmlglobal_p.h>
@@ -1469,8 +1470,17 @@ void QQuickImageParticle::finishBuildParticleNodes(QSGNode** node)
update();
}
+static inline bool isOpenGL(QSGRenderContext *rc)
+{
+ QSGRendererInterface *rif = rc->sceneGraphContext()->rendererInterface(rc);
+ return !rif || rif->graphicsApi() == QSGRendererInterface::OpenGL;
+}
+
QSGNode *QQuickImageParticle::updatePaintNode(QSGNode *node, UpdatePaintNodeData *)
{
+ if (!node && !isOpenGL(QQuickItemPrivate::get(this)->sceneGraphRenderContext()))
+ return 0;
+
if (m_pleaseReset){
if (node)
delete node;
diff --git a/src/particles/qquickparticleemitter_p.h b/src/particles/qquickparticleemitter_p.h
index dd55fdc7a6..b3f96ae3e6 100644
--- a/src/particles/qquickparticleemitter_p.h
+++ b/src/particles/qquickparticleemitter_p.h
@@ -62,7 +62,7 @@
#include <QPointF>
QT_BEGIN_NAMESPACE
-class QQuickParticleEmitter : public QQuickItem
+class Q_QUICKPARTICLES_PRIVATE_EXPORT QQuickParticleEmitter : public QQuickItem
{
Q_OBJECT
Q_PROPERTY(QQuickParticleSystem* system READ system WRITE setSystem NOTIFY systemChanged)
diff --git a/src/particles/qquickparticlepainter.cpp b/src/particles/qquickparticlepainter.cpp
index d6303eb219..0c2521cd9e 100644
--- a/src/particles/qquickparticlepainter.cpp
+++ b/src/particles/qquickparticlepainter.cpp
@@ -103,7 +103,8 @@ void QQuickParticlePainter::recalculateGroupIds() const
m_groupIdsNeedRecalculation = false;
m_groupIds.clear();
- for (const QString &str : groups()) {
+ const auto groupList = groups();
+ for (const QString &str : groupList) {
QQuickParticleGroupData::ID groupId = m_system->groupIds.value(str, QQuickParticleGroupData::InvalidID);
if (groupId == QQuickParticleGroupData::InvalidID) {
// invalid data, not finished setting up, or whatever. Fallback: do not cache.
diff --git a/src/particles/qquickv4particledata.cpp b/src/particles/qquickv4particledata.cpp
index 99451057ce..bd43d45c38 100644
--- a/src/particles/qquickv4particledata.cpp
+++ b/src/particles/qquickv4particledata.cpp
@@ -273,10 +273,11 @@ QT_BEGIN_NAMESPACE
struct QV4ParticleData : public QV4::Object
{
struct Data : QV4::Object::Data {
- Data(QQuickParticleData *datum, QQuickParticleSystem* particleSystem)
- : datum(datum)
- , particleSystem(particleSystem)
+ void init(QQuickParticleData *datum, QQuickParticleSystem* particleSystem)
{
+ Object::init();
+ this->datum = datum;
+ this->particleSystem = particleSystem;
}
QQuickParticleData* datum;//TODO: Guard needed?
QQuickParticleSystem* particleSystem;
diff --git a/src/plugins/plugins.pro b/src/plugins/plugins.pro
index 273407c19d..2467239be2 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
+qtHaveModule(quick):SUBDIRS += scenegraph
diff --git a/src/plugins/qmltooling/packetprotocol/qpacketprotocol.cpp b/src/plugins/qmltooling/packetprotocol/qpacketprotocol.cpp
index d20ddf9dc0..e541810330 100644
--- a/src/plugins/qmltooling/packetprotocol/qpacketprotocol.cpp
+++ b/src/plugins/qmltooling/packetprotocol/qpacketprotocol.cpp
@@ -124,12 +124,9 @@ QPacketProtocol::QPacketProtocol(QIODevice *dev, QObject *parent)
Q_ASSERT(4 == sizeof(qint32));
Q_ASSERT(dev);
- QObject::connect(dev, SIGNAL(readyRead()),
- this, SLOT(readyToRead()));
- QObject::connect(dev, SIGNAL(aboutToClose()),
- this, SLOT(aboutToClose()));
- QObject::connect(dev, SIGNAL(bytesWritten(qint64)),
- this, SLOT(bytesWritten(qint64)));
+ QObject::connect(dev, &QIODevice::readyRead, this, &QPacketProtocol::readyToRead);
+ QObject::connect(dev, &QIODevice::aboutToClose, this, &QPacketProtocol::aboutToClose);
+ QObject::connect(dev, &QIODevice::bytesWritten, this, &QPacketProtocol::bytesWritten);
}
/*!
@@ -247,12 +244,9 @@ void QPacketProtocol::readyToRead()
// Check sizing constraints
if (d->inProgressSize > MAX_PACKET_SIZE) {
- QObject::disconnect(d->dev, SIGNAL(readyRead()),
- this, SLOT(readyToRead()));
- QObject::disconnect(d->dev, SIGNAL(aboutToClose()),
- this, SLOT(aboutToClose()));
- QObject::disconnect(d->dev, SIGNAL(bytesWritten(qint64)),
- this, SLOT(bytesWritten(qint64)));
+ disconnect(d->dev, &QIODevice::readyRead, this, &QPacketProtocol::readyToRead);
+ disconnect(d->dev, &QIODevice::aboutToClose, this, &QPacketProtocol::aboutToClose);
+ disconnect(d->dev, &QIODevice::bytesWritten, this, &QPacketProtocol::bytesWritten);
d->dev = 0;
emit invalidPacket();
return;
diff --git a/src/plugins/qmltooling/packetprotocol/qpacketprotocol_p.h b/src/plugins/qmltooling/packetprotocol/qpacketprotocol_p.h
index 8f95a081e9..7fd722f17f 100644
--- a/src/plugins/qmltooling/packetprotocol/qpacketprotocol_p.h
+++ b/src/plugins/qmltooling/packetprotocol/qpacketprotocol_p.h
@@ -74,7 +74,7 @@ Q_SIGNALS:
void readyRead();
void invalidPacket();
-private Q_SLOTS:
+private:
void aboutToClose();
void bytesWritten(qint64 bytes);
void readyToRead();
diff --git a/src/plugins/qmltooling/qmldbg_debugger/qdebugmessageservice.cpp b/src/plugins/qmltooling/qmldbg_debugger/qdebugmessageservice.cpp
index 31873f7915..b0f59717ac 100644
--- a/src/plugins/qmltooling/qmldbg_debugger/qdebugmessageservice.cpp
+++ b/src/plugins/qmltooling/qmldbg_debugger/qdebugmessageservice.cpp
@@ -70,7 +70,7 @@ void QDebugMessageServiceImpl::sendDebugMessage(QtMsgType type,
//We just eavesdrop and forward the messages to a port
//only if a client is connected to it.
QQmlDebugPacket ws;
- ws << QByteArray("MESSAGE") << type << buf.toUtf8();
+ ws << QByteArray("MESSAGE") << int(type) << buf.toUtf8();
ws << QByteArray(ctxt.file) << ctxt.line << QByteArray(ctxt.function);
ws << QByteArray(ctxt.category) << timer.nsecsElapsed();
diff --git a/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp b/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp
index ff4e30835d..f72b8a51f9 100644
--- a/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp
+++ b/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp
@@ -63,8 +63,12 @@ QT_BEGIN_NAMESPACE
QQmlEngineDebugServiceImpl::QQmlEngineDebugServiceImpl(QObject *parent) :
QQmlEngineDebugService(2, parent), m_watch(new QQmlWatcher(this)), m_statesDelegate(0)
{
- QObject::connect(m_watch, SIGNAL(propertyChanged(int,int,QMetaProperty,QVariant)),
- this, SLOT(propertyChanged(int,int,QMetaProperty,QVariant)));
+ connect(m_watch, &QQmlWatcher::propertyChanged,
+ this, &QQmlEngineDebugServiceImpl::propertyChanged);
+
+ // Move the message into the correct thread for processing
+ connect(this, &QQmlEngineDebugServiceImpl::scheduleMessage,
+ this, &QQmlEngineDebugServiceImpl::processMessage, Qt::QueuedConnection);
}
QQmlEngineDebugServiceImpl::~QQmlEngineDebugServiceImpl()
@@ -420,7 +424,7 @@ QQmlEngineDebugServiceImpl::objectData(QObject *object)
void QQmlEngineDebugServiceImpl::messageReceived(const QByteArray &message)
{
- QMetaObject::invokeMethod(this, "processMessage", Qt::QueuedConnection, Q_ARG(QByteArray, message));
+ emit scheduleMessage(message);
}
/*!
@@ -656,7 +660,7 @@ bool QQmlEngineDebugServiceImpl::setBinding(int objectId,
filename, line, column);
QQmlPropertyPrivate::takeSignalExpression(property, qmlExpression);
} else if (property.isProperty()) {
- QQmlBinding *binding = new QQmlBinding(expression.toString(), object, QQmlContextData::get(context), filename, line, column);
+ QQmlBinding *binding = QQmlBinding::create(&QQmlPropertyPrivate::get(property)->core, expression.toString(), object, QQmlContextData::get(context), filename, line, column);
binding->setTarget(property);
QQmlPropertyPrivate::setBinding(binding);
binding->update();
@@ -749,7 +753,7 @@ bool QQmlEngineDebugServiceImpl::setMethodBody(int objectId, const QString &meth
if (!prop || !prop->isVMEFunction())
return false;
- QMetaMethod metaMethod = object->metaObject()->method(prop->coreIndex);
+ QMetaMethod metaMethod = object->metaObject()->method(prop->coreIndex());
QList<QByteArray> paramNames = metaMethod.parameterNames();
QString paramStr;
@@ -758,19 +762,22 @@ bool QQmlEngineDebugServiceImpl::setMethodBody(int objectId, const QString &meth
paramStr.append(QString::fromUtf8(paramNames.at(ii)));
}
- QString jsfunction = QLatin1String("(function ") + method + QLatin1Char('(') + paramStr +
- QLatin1String(") {");
- jsfunction += body;
- jsfunction += QLatin1String("\n})");
+ const QString jsfunction = QLatin1String("(function ") + method + QLatin1Char('(') + paramStr +
+ QLatin1String(") {") + body + QLatin1String("\n})");
QQmlVMEMetaObject *vmeMetaObject = QQmlVMEMetaObject::get(object);
Q_ASSERT(vmeMetaObject); // the fact we found the property above should guarentee this
- int lineNumber = vmeMetaObject->vmeMethodLineNumber(prop->coreIndex);
QV4::ExecutionEngine *v4 = QV8Engine::getV4(qmlEngine(object)->handle());
QV4::Scope scope(v4);
+
+ int lineNumber = 0;
+ QV4::ScopedFunctionObject oldMethod(scope, vmeMetaObject->vmeMethod(prop->coreIndex()));
+ if (oldMethod && oldMethod->d()->function) {
+ lineNumber = oldMethod->d()->function->compiledFunction->location.line;
+ }
QV4::ScopedValue v(scope, QQmlJavaScriptExpression::evalFunction(contextData, object, jsfunction, contextData->urlString(), lineNumber));
- vmeMetaObject->setVmeMethod(prop->coreIndex, v);
+ vmeMetaObject->setVmeMethod(prop->coreIndex(), v);
return true;
}
diff --git a/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.h b/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.h
index cb75a63850..2e40eb4de8 100644
--- a/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.h
+++ b/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.h
@@ -101,16 +101,18 @@ public:
void setStatesDelegate(QQmlDebugStatesDelegate *) Q_DECL_OVERRIDE;
+signals:
+ void scheduleMessage(const QByteArray &);
+
protected:
virtual void messageReceived(const QByteArray &) Q_DECL_OVERRIDE;
-private Q_SLOTS:
- void processMessage(const QByteArray &msg);
- void propertyChanged(int id, int objectId, const QMetaProperty &property, const QVariant &value);
-
private:
friend class QQmlDebuggerServiceFactory;
+ void processMessage(const QByteArray &msg);
+ void propertyChanged(int id, int objectId, const QMetaProperty &property, const QVariant &value);
+
void prepareDeferredObjects(QObject *);
void buildObjectList(QDataStream &, QQmlContext *,
const QList<QPointer<QObject> > &instances);
diff --git a/src/plugins/qmltooling/qmldbg_debugger/qqmlnativedebugservice.cpp b/src/plugins/qmltooling/qmldbg_debugger/qqmlnativedebugservice.cpp
index e3b2bc0870..5b96163b48 100644
--- a/src/plugins/qmltooling/qmldbg_debugger/qqmlnativedebugservice.cpp
+++ b/src/plugins/qmltooling/qmldbg_debugger/qqmlnativedebugservice.cpp
@@ -143,15 +143,15 @@ public:
void BreakPointHandler::handleSetBreakpoint(QJsonObject *response, const QJsonObject &arguments)
{
TRACE_PROTOCOL("SET BREAKPOINT" << arguments);
- QString type = arguments.value(QStringLiteral("type")).toString();
+ QString type = arguments.value(QLatin1String("type")).toString();
- QString fileName = arguments.value(QStringLiteral("file")).toString();
+ QString fileName = arguments.value(QLatin1String("file")).toString();
if (fileName.isEmpty()) {
setError(response, QStringLiteral("breakpoint has no file name"));
return;
}
- int line = arguments.value(QStringLiteral("line")).toInt(-1);
+ int line = arguments.value(QLatin1String("line")).toInt(-1);
if (line < 0) {
setError(response, QStringLiteral("breakpoint has an invalid line number"));
return;
@@ -161,9 +161,9 @@ void BreakPointHandler::handleSetBreakpoint(QJsonObject *response, const QJsonOb
bp.id = m_lastBreakpoint++;
bp.fileName = fileName.mid(fileName.lastIndexOf('/') + 1);
bp.lineNumber = line;
- bp.enabled = arguments.value(QStringLiteral("enabled")).toBool(true);
- bp.condition = arguments.value(QStringLiteral("condition")).toString();
- bp.ignoreCount = arguments.value(QStringLiteral("ignorecount")).toInt();
+ bp.enabled = arguments.value(QLatin1String("enabled")).toBool(true);
+ bp.condition = arguments.value(QLatin1String("condition")).toString();
+ bp.ignoreCount = arguments.value(QLatin1String("ignorecount")).toInt();
m_breakPoints.append(bp);
m_haveBreakPoints = true;
@@ -174,7 +174,7 @@ void BreakPointHandler::handleSetBreakpoint(QJsonObject *response, const QJsonOb
void BreakPointHandler::handleRemoveBreakpoint(QJsonObject *response, const QJsonObject &arguments)
{
- int id = arguments.value(QStringLiteral("id")).toInt();
+ int id = arguments.value(QLatin1String("id")).toInt();
removeBreakPoint(id);
response->insert(QStringLiteral("id"), id);
}
@@ -208,7 +208,7 @@ private:
void handleDebuggerDeleted(QObject *debugger);
- QV4::ReturnedValue evaluateExpression(QV4::Scope &scope, const QString &expression);
+ void evaluateExpression(QV4::Scope &scope, const QString &expression);
bool checkCondition(const QString &expression);
QStringList breakOnSignals;
@@ -241,12 +241,11 @@ private:
bool NativeDebugger::checkCondition(const QString &expression)
{
QV4::Scope scope(m_engine);
- QV4::ReturnedValue result = evaluateExpression(scope, expression);
- QV4::ScopedValue val(scope, result);
- return val->booleanValue();
+ evaluateExpression(scope, expression);
+ return scope.result.booleanValue();
}
-QV4::ReturnedValue NativeDebugger::evaluateExpression(QV4::Scope &scope, const QString &expression)
+void NativeDebugger::evaluateExpression(QV4::Scope &scope, const QString &expression)
{
m_runningJob = true;
@@ -261,12 +260,10 @@ QV4::ReturnedValue NativeDebugger::evaluateExpression(QV4::Scope &scope, const Q
// That is a side-effect of inheritContext.
script.inheritContext = true;
script.parse();
- QV4::ScopedValue result(scope);
if (!m_engine->hasException)
- result = script.run();
+ scope.result = script.run();
m_runningJob = false;
- return result->asReturnedValue();
}
NativeDebugger::NativeDebugger(QQmlNativeDebugServiceImpl *service, QV4::ExecutionEngine *engine)
@@ -301,19 +298,19 @@ void NativeDebugger::signalEmitted(const QString &signal)
void NativeDebugger::handleCommand(QJsonObject *response, const QString &cmd,
const QJsonObject &arguments)
{
- if (cmd == QStringLiteral("backtrace"))
+ if (cmd == QLatin1String("backtrace"))
handleBacktrace(response, arguments);
- else if (cmd == QStringLiteral("variables"))
+ else if (cmd == QLatin1String("variables"))
handleVariables(response, arguments);
- else if (cmd == QStringLiteral("expressions"))
+ else if (cmd == QLatin1String("expressions"))
handleExpressions(response, arguments);
- else if (cmd == QStringLiteral("stepin"))
+ else if (cmd == QLatin1String("stepin"))
handleContinue(response, StepIn);
- else if (cmd == QStringLiteral("stepout"))
+ else if (cmd == QLatin1String("stepout"))
handleContinue(response, StepOut);
- else if (cmd == QStringLiteral("stepover"))
+ else if (cmd == QLatin1String("stepover"))
handleContinue(response, StepOver);
- else if (cmd == QStringLiteral("continue"))
+ else if (cmd == QLatin1String("continue"))
handleContinue(response, NotStepping);
}
@@ -334,7 +331,7 @@ static void decodeContext(const QString &context, QV4::ExecutionContext **execut
void NativeDebugger::handleBacktrace(QJsonObject *response, const QJsonObject &arguments)
{
- int limit = arguments.value(QStringLiteral("limit")).toInt(0);
+ int limit = arguments.value(QLatin1String("limit")).toInt(0);
QJsonArray frameArray;
QV4::ExecutionContext *executionContext = m_engine->currentContext;
@@ -374,7 +371,7 @@ void Collector::collect(QJsonArray *out, const QString &parentIName, const QStri
dict.insert(QStringLiteral("iname"), iname);
dict.insert(QStringLiteral("name"), nonEmptyName);
- QV4::ScopedValue typeString(scope, QV4::Runtime::typeofValue(m_engine, value));
+ QV4::ScopedValue typeString(scope, QV4::Runtime::method_typeofValue(m_engine, value));
dict.insert(QStringLiteral("type"), typeString->toQStringNoThrow());
switch (value.type()) {
@@ -463,7 +460,7 @@ void NativeDebugger::handleVariables(QJsonObject *response, const QJsonObject &a
{
TRACE_PROTOCOL("Build variables");
QV4::ExecutionContext *executionContext = 0;
- decodeContext(arguments.value(QStringLiteral("context")).toString(), &executionContext);
+ decodeContext(arguments.value(QLatin1String("context")).toString(), &executionContext);
if (!executionContext) {
setError(response, QStringLiteral("No execution context passed"));
return;
@@ -478,7 +475,7 @@ void NativeDebugger::handleVariables(QJsonObject *response, const QJsonObject &a
TRACE_PROTOCOL("Engine: " << engine);
Collector collector(engine);
- QJsonArray expanded = arguments.value(QStringLiteral("expanded")).toArray();
+ QJsonArray expanded = arguments.value(QLatin1String("expanded")).toArray();
foreach (const QJsonValue &ex, expanded)
collector.m_expanded.append(ex.toString());
TRACE_PROTOCOL("Expanded: " << collector.m_expanded);
@@ -515,7 +512,7 @@ void NativeDebugger::handleExpressions(QJsonObject *response, const QJsonObject
{
TRACE_PROTOCOL("Evaluate expressions");
QV4::ExecutionContext *executionContext = 0;
- decodeContext(arguments.value(QStringLiteral("context")).toString(), &executionContext);
+ decodeContext(arguments.value(QLatin1String("context")).toString(), &executionContext);
if (!executionContext) {
setError(response, QStringLiteral("No execution context passed"));
return;
@@ -530,7 +527,7 @@ void NativeDebugger::handleExpressions(QJsonObject *response, const QJsonObject
TRACE_PROTOCOL("Engines: " << engine << m_engine);
Collector collector(engine);
- QJsonArray expanded = arguments.value(QStringLiteral("expanded")).toArray();
+ QJsonArray expanded = arguments.value(QLatin1String("expanded")).toArray();
foreach (const QJsonValue &ex, expanded)
collector.m_expanded.append(ex.toString());
TRACE_PROTOCOL("Expanded: " << collector.m_expanded);
@@ -538,15 +535,15 @@ void NativeDebugger::handleExpressions(QJsonObject *response, const QJsonObject
QJsonArray output;
QV4::Scope scope(engine);
- QJsonArray expressions = arguments.value(QStringLiteral("expressions")).toArray();
+ QJsonArray expressions = arguments.value(QLatin1String("expressions")).toArray();
foreach (const QJsonValue &expr, expressions) {
- QString expression = expr.toObject().value(QStringLiteral("expression")).toString();
- QString name = expr.toObject().value(QStringLiteral("name")).toString();
+ QString expression = expr.toObject().value(QLatin1String("expression")).toString();
+ QString name = expr.toObject().value(QLatin1String("name")).toString();
TRACE_PROTOCOL("Evaluate expression: " << expression);
m_runningJob = true;
- QV4::ReturnedValue eval = evaluateExpression(scope, expression);
- QV4::ScopedValue result(scope, eval);
+ evaluateExpression(scope, expression);
+ QV4::ScopedValue result(scope, scope.result);
m_runningJob = false;
if (result->isUndefined()) {
@@ -764,7 +761,7 @@ void QQmlNativeDebugServiceImpl::stateAboutToBeChanged(QQmlDebugService::State s
if (state == Enabled) {
foreach (NativeDebugger *debugger, m_debuggers) {
QV4::ExecutionEngine *engine = debugger->engine();
- if (!engine->debugger)
+ if (!engine->debugger())
engine->setDebugger(debugger);
}
}
@@ -776,14 +773,14 @@ void QQmlNativeDebugServiceImpl::messageReceived(const QByteArray &message)
TRACE_PROTOCOL("Native message received: " << message);
QJsonObject request = QJsonDocument::fromJson(message).object();
QJsonObject response;
- QJsonObject arguments = request.value(QStringLiteral("arguments")).toObject();
- QString cmd = request.value(QStringLiteral("command")).toString();
+ QJsonObject arguments = request.value(QLatin1String("arguments")).toObject();
+ QString cmd = request.value(QLatin1String("command")).toString();
- if (cmd == QStringLiteral("setbreakpoint")) {
+ if (cmd == QLatin1String("setbreakpoint")) {
m_breakHandler->handleSetBreakpoint(&response, arguments);
- } else if (cmd == QStringLiteral("removebreakpoint")) {
+ } else if (cmd == QLatin1String("removebreakpoint")) {
m_breakHandler->handleRemoveBreakpoint(&response, arguments);
- } else if (cmd == QStringLiteral("echo")) {
+ } else if (cmd == QLatin1String("echo")) {
response.insert(QStringLiteral("result"), arguments);
} else {
foreach (NativeDebugger *debugger, m_debuggers)
diff --git a/src/plugins/qmltooling/qmldbg_debugger/qqmlwatcher.cpp b/src/plugins/qmltooling/qmldbg_debugger/qqmlwatcher.cpp
index 392080dd51..1214212727 100644
--- a/src/plugins/qmltooling/qmldbg_debugger/qqmlwatcher.cpp
+++ b/src/plugins/qmltooling/qmldbg_debugger/qqmlwatcher.cpp
@@ -69,7 +69,7 @@ public:
QQmlWatcher *parent = 0);
public slots:
- void notifyValueChanged();
+ void notifyValueChanged(); // Needs to be a slot because of QQmlPropertyPrivate::connect()
private:
friend class QQmlWatcher;
@@ -88,7 +88,8 @@ QQmlWatchProxy::QQmlWatchProxy(int id,
QQmlWatcher *parent)
: QObject(parent), m_id(id), m_watch(parent), m_object(0), m_debugId(debugId), m_expr(exp)
{
- QObject::connect(m_expr, SIGNAL(valueChanged()), this, SLOT(notifyValueChanged()));
+ QObject::connect(m_expr, &QQmlExpression::valueChanged,
+ this, &QQmlWatchProxy::notifyValueChanged);
}
QQmlWatchProxy::QQmlWatchProxy(int id,
diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp
index 96f60b24bb..b2db23d78c 100644
--- a/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp
+++ b/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp
@@ -134,7 +134,7 @@ const QV4::Object *collectProperty(const QV4::ScopedValue &value, QV4::Execution
QJsonObject &dict)
{
QV4::Scope scope(engine);
- QV4::ScopedValue typeString(scope, QV4::Runtime::typeofValue(engine, value));
+ QV4::ScopedValue typeString(scope, QV4::Runtime::method_typeofValue(engine, value));
dict.insert(QStringLiteral("type"), typeString->toQStringNoThrow());
const QLatin1String valueKey("value");
diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debugger.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4debugger.cpp
index 53f2eab5ff..44810dd4cb 100644
--- a/src/plugins/qmltooling/qmldbg_debugger/qv4debugger.cpp
+++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debugger.cpp
@@ -79,6 +79,8 @@ QV4Debugger::QV4Debugger(QV4::ExecutionEngine *engine)
static int pauseReasonId = qRegisterMetaType<QV4Debugger::PauseReason>();
Q_UNUSED(debuggerId);
Q_UNUSED(pauseReasonId);
+ connect(this, &QV4Debugger::scheduleJob,
+ this, &QV4Debugger::runJobUnpaused, Qt::QueuedConnection);
}
QV4::ExecutionEngine *QV4Debugger::engine() const
@@ -320,7 +322,7 @@ void QV4Debugger::runInEngine_havingLock(QV4DebugJob *job)
if (state() == Paused)
m_runningCondition.wakeAll();
else
- QMetaObject::invokeMethod(this, "runJobUnpaused", Qt::QueuedConnection);
+ emit scheduleJob();
m_jobIsRunning.wait(&m_lock);
m_runningJob = 0;
}
diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debugger.h b/src/plugins/qmltooling/qmldbg_debugger/qv4debugger.h
index 3a5b6080cb..cd412e573d 100644
--- a/src/plugins/qmltooling/qmldbg_debugger/qv4debugger.h
+++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debugger.h
@@ -140,15 +140,14 @@ public:
signals:
void debuggerPaused(QV4Debugger *self, QV4Debugger::PauseReason reason);
-
-private slots:
- void runJobUnpaused();
+ void scheduleJob();
private:
// requires lock to be held
void pauseAndWait(PauseReason reason);
bool reallyHitTheBreakPoint(const QString &filename, int linenr);
void runInEngine_havingLock(QV4DebugJob *job);
+ void runJobUnpaused();
QV4::ExecutionEngine *m_engine;
QV4::PersistentValue m_currentContext;
diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.cpp
index a90d03b010..756b6b28be 100644
--- a/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.cpp
+++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.cpp
@@ -114,7 +114,7 @@ void QV4DebuggerAgent::addDebugger(QV4Debugger *debugger)
debugger->setBreakOnThrow(m_breakOnThrow);
- foreach (const BreakPoint &breakPoint, m_breakPoints.values())
+ for (const BreakPoint &breakPoint : qAsConst(m_breakPoints))
if (breakPoint.enabled)
debugger->addBreakPoint(breakPoint.fileName, breakPoint.lineNr, breakPoint.condition);
diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.h b/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.h
index 1c7eb50ac7..39ac4d4dcb 100644
--- a/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.h
+++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.h
@@ -72,7 +72,6 @@ public:
void setBreakOnThrow(bool onoff);
void clearAllPauseRequests();
-public slots:
void debuggerPaused(QV4Debugger *debugger, QV4Debugger::PauseReason reason);
void handleDebuggerDeleted(QObject *debugger);
diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp
index 5ee9e5e9e9..00c5c1ad77 100644
--- a/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp
+++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp
@@ -90,7 +90,7 @@ public:
TRACE_PROTOCOL(qDebug() << "handling command" << command() << "...");
req = request;
- seq = req.value(QStringLiteral("seq"));
+ seq = req.value(QLatin1String("seq"));
debugService = s;
handleRequest();
@@ -128,7 +128,7 @@ protected:
void createErrorResponse(const QString &msg)
{
- QJsonValue command = req.value(QStringLiteral("command"));
+ QJsonValue command = req.value(QLatin1String("command"));
response.insert(QStringLiteral("command"), command);
addRequestSequence();
addSuccess(false);
@@ -154,9 +154,9 @@ public:
virtual void handleRequest()
{
- QString msg = QStringLiteral("unimplemented command \"");
- msg += req.value(QStringLiteral("command")).toString();
- msg += QLatin1Char('"');
+ QString msg = QLatin1String("unimplemented command \"")
+ + req.value(QLatin1String("command")).toString()
+ + QLatin1Char('"');
createErrorResponse(msg);
}
};
@@ -189,23 +189,23 @@ public:
virtual void handleRequest()
{
// decypher the payload:
- QJsonObject args = req.value(QStringLiteral("arguments")).toObject();
+ QJsonObject args = req.value(QLatin1String("arguments")).toObject();
if (args.isEmpty())
return;
- QString type = args.value(QStringLiteral("type")).toString();
- if (type != QStringLiteral("scriptRegExp")) {
+ QString type = args.value(QLatin1String("type")).toString();
+ if (type != QLatin1String("scriptRegExp")) {
createErrorResponse(QStringLiteral("breakpoint type \"%1\" is not implemented").arg(type));
return;
}
- QString fileName = args.value(QStringLiteral("target")).toString();
+ QString fileName = args.value(QLatin1String("target")).toString();
if (fileName.isEmpty()) {
createErrorResponse(QStringLiteral("breakpoint has no file name"));
return;
}
- int line = args.value(QStringLiteral("line")).toInt(-1);
+ int line = args.value(QLatin1String("line")).toInt(-1);
if (line < 0) {
createErrorResponse(QStringLiteral("breakpoint has an invalid line number"));
return;
@@ -240,11 +240,11 @@ public:
virtual void handleRequest()
{
// decypher the payload:
- QJsonObject args = req.value(QStringLiteral("arguments")).toObject();
+ QJsonObject args = req.value(QLatin1String("arguments")).toObject();
if (args.isEmpty())
return;
- int id = args.value(QStringLiteral("breakpoint")).toInt(-1);
+ int id = args.value(QLatin1String("breakpoint")).toInt(-1);
if (id < 0) {
createErrorResponse(QStringLiteral("breakpoint has an invalid number"));
return;
@@ -274,9 +274,9 @@ public:
{
// decypher the payload:
- QJsonObject arguments = req.value(QStringLiteral("arguments")).toObject();
- int fromFrame = arguments.value(QStringLiteral("fromFrame")).toInt(0);
- int toFrame = arguments.value(QStringLiteral("toFrame")).toInt(fromFrame + 10);
+ QJsonObject arguments = req.value(QLatin1String("arguments")).toObject();
+ int fromFrame = arguments.value(QLatin1String("fromFrame")).toInt(0);
+ int toFrame = arguments.value(QLatin1String("toFrame")).toInt(fromFrame + 10);
// no idea what the bottom property is for, so we'll ignore it.
QV4Debugger *debugger = debugService->debuggerAgent.pausedDebugger();
@@ -306,8 +306,8 @@ public:
virtual void handleRequest()
{
// decypher the payload:
- QJsonObject arguments = req.value(QStringLiteral("arguments")).toObject();
- const int frameNr = arguments.value(QStringLiteral("number")).toInt(
+ QJsonObject arguments = req.value(QLatin1String("arguments")).toObject();
+ const int frameNr = arguments.value(QLatin1String("number")).toInt(
debugService->selectedFrame());
QV4Debugger *debugger = debugService->debuggerAgent.pausedDebugger();
@@ -348,10 +348,10 @@ public:
virtual void handleRequest()
{
// decypher the payload:
- QJsonObject arguments = req.value(QStringLiteral("arguments")).toObject();
- const int frameNr = arguments.value(QStringLiteral("frameNumber")).toInt(
+ QJsonObject arguments = req.value(QLatin1String("arguments")).toObject();
+ const int frameNr = arguments.value(QLatin1String("frameNumber")).toInt(
debugService->selectedFrame());
- const int scopeNr = arguments.value(QStringLiteral("number")).toInt(0);
+ const int scopeNr = arguments.value(QLatin1String("number")).toInt(0);
QV4Debugger *debugger = debugService->debuggerAgent.pausedDebugger();
if (!debugger) {
@@ -393,8 +393,8 @@ public:
virtual void handleRequest()
{
// decypher the payload:
- QJsonObject arguments = req.value(QStringLiteral("arguments")).toObject();
- QJsonArray handles = arguments.value(QStringLiteral("handles")).toArray();
+ QJsonObject arguments = req.value(QLatin1String("arguments")).toObject();
+ QJsonArray handles = arguments.value(QLatin1String("handles")).toArray();
QV4Debugger *debugger = debugService->debuggerAgent.pausedDebugger();
if (!debugger) {
@@ -433,7 +433,7 @@ public:
virtual void handleRequest()
{
// decypher the payload:
- QJsonObject arguments = req.value(QStringLiteral("arguments")).toObject();
+ QJsonObject arguments = req.value(QLatin1String("arguments")).toObject();
QV4Debugger *debugger = debugService->debuggerAgent.pausedDebugger();
if (!debugger) {
@@ -445,17 +445,17 @@ public:
if (arguments.empty()) {
debugger->resume(QV4Debugger::FullThrottle);
} else {
- QJsonObject arguments = req.value(QStringLiteral("arguments")).toObject();
- QString stepAction = arguments.value(QStringLiteral("stepaction")).toString();
- const int stepcount = arguments.value(QStringLiteral("stepcount")).toInt(1);
+ QJsonObject arguments = req.value(QLatin1String("arguments")).toObject();
+ QString stepAction = arguments.value(QLatin1String("stepaction")).toString();
+ const int stepcount = arguments.value(QLatin1String("stepcount")).toInt(1);
if (stepcount != 1)
qWarning() << "Step count other than 1 is not supported.";
- if (stepAction == QStringLiteral("in")) {
+ if (stepAction == QLatin1String("in")) {
debugger->resume(QV4Debugger::StepIn);
- } else if (stepAction == QStringLiteral("out")) {
+ } else if (stepAction == QLatin1String("out")) {
debugger->resume(QV4Debugger::StepOut);
- } else if (stepAction == QStringLiteral("next")) {
+ } else if (stepAction == QLatin1String("next")) {
debugger->resume(QV4Debugger::StepOver);
} else {
createErrorResponse(QStringLiteral("continue command has invalid stepaction"));
@@ -499,13 +499,13 @@ public:
bool wasEnabled = debugService->debuggerAgent.breakOnThrow();
//decypher the payload:
- QJsonObject arguments = req.value(QStringLiteral("arguments")).toObject();
- QString type = arguments.value(QStringLiteral("type")).toString();
- bool enabled = arguments.value(QStringLiteral("number")).toBool(!wasEnabled);
+ QJsonObject arguments = req.value(QLatin1String("arguments")).toObject();
+ QString type = arguments.value(QLatin1String("type")).toString();
+ bool enabled = arguments.value(QLatin1String("number")).toBool(!wasEnabled);
- if (type == QStringLiteral("all")) {
+ if (type == QLatin1String("all")) {
// that's fine
- } else if (type == QStringLiteral("uncaught")) {
+ } else if (type == QLatin1String("uncaught")) {
createErrorResponse(QStringLiteral("breaking only on uncaught exceptions is not supported yet"));
return;
} else {
@@ -537,8 +537,8 @@ public:
virtual void handleRequest()
{
//decypher the payload:
- QJsonObject arguments = req.value(QStringLiteral("arguments")).toObject();
- int types = arguments.value(QStringLiteral("types")).toInt(-1);
+ QJsonObject arguments = req.value(QLatin1String("arguments")).toObject();
+ int types = arguments.value(QLatin1String("types")).toInt(-1);
if (types < 0 || types > 7) {
createErrorResponse(QStringLiteral("invalid types value in scripts command"));
return;
@@ -608,8 +608,8 @@ public:
virtual void handleRequest()
{
- QJsonObject arguments = req.value(QStringLiteral("arguments")).toObject();
- QString expression = arguments.value(QStringLiteral("expression")).toString();
+ QJsonObject arguments = req.value(QLatin1String("arguments")).toObject();
+ QString expression = arguments.value(QLatin1String("expression")).toString();
int frame = -1;
QV4Debugger *debugger = debugService->debuggerAgent.pausedDebugger();
@@ -624,7 +624,7 @@ public:
}
debugger = debuggers.first();
} else {
- frame = arguments.value(QStringLiteral("frame")).toInt(0);
+ frame = arguments.value(QLatin1String("frame")).toInt(0);
}
ExpressionEvalJob job(debugger->engine(), frame, expression, debugger->collector());
@@ -706,7 +706,7 @@ void QV4DebugServiceImpl::engineAboutToBeRemoved(QJSEngine *engine)
if (engine){
const QV4::ExecutionEngine *ee = QV8Engine::getV4(engine->handle());
if (ee) {
- QV4Debugger *debugger = qobject_cast<QV4Debugger *>(ee->debugger);
+ QV4Debugger *debugger = qobject_cast<QV4Debugger *>(ee->debugger());
if (debugger)
debuggerAgent.removeDebugger(debugger);
}
@@ -720,7 +720,7 @@ void QV4DebugServiceImpl::stateAboutToBeChanged(State state)
if (state == Enabled) {
foreach (QV4Debugger *debugger, debuggerAgent.debuggers()) {
QV4::ExecutionEngine *ee = debugger->engine();
- if (!ee->debugger)
+ if (!ee->debugger())
ee->setDebugger(debugger);
}
}
@@ -802,9 +802,9 @@ void QV4DebugServiceImpl::handleV8Request(const QByteArray &payload)
QJsonDocument request = QJsonDocument::fromJson(payload);
QJsonObject o = request.object();
- QJsonValue type = o.value(QStringLiteral("type"));
- if (type.toString() == QStringLiteral("request")) {
- QJsonValue command = o.value(QStringLiteral("command"));
+ QJsonValue type = o.value(QLatin1String("type"));
+ if (type.toString() == QLatin1String("request")) {
+ QJsonValue command = o.value(QLatin1String("command"));
V8CommandHandler *h = v8CommandHandler(command.toString());
if (h)
h->handle(o, this);
diff --git a/src/plugins/qmltooling/qmldbg_inspector/globalinspector.cpp b/src/plugins/qmltooling/qmldbg_inspector/globalinspector.cpp
index 2150b68f32..107d54c626 100644
--- a/src/plugins/qmltooling/qmldbg_inspector/globalinspector.cpp
+++ b/src/plugins/qmltooling/qmldbg_inspector/globalinspector.cpp
@@ -148,10 +148,6 @@ public:
m_component.setData(qml, filename);
}
-signals:
- void result(int requestId, bool success);
-
-public slots:
void tryCreateObject(QQmlComponent::Status status)
{
switch (status) {
@@ -171,7 +167,7 @@ public slots:
else
emit result(m_requestId, false);
}
- delete this;
+ deleteLater(); // The component might send more signals
return;
}
default:
@@ -179,6 +175,9 @@ public slots:
}
}
+signals:
+ void result(int requestId, bool success);
+
private:
QQmlComponent m_component;
int m_requestId;
diff --git a/src/plugins/qmltooling/qmldbg_inspector/globalinspector.h b/src/plugins/qmltooling/qmldbg_inspector/globalinspector.h
index 7bbe6d6aa2..338eee14c3 100644
--- a/src/plugins/qmltooling/qmldbg_inspector/globalinspector.h
+++ b/src/plugins/qmltooling/qmldbg_inspector/globalinspector.h
@@ -72,10 +72,8 @@ public:
signals:
void messageToClient(const QString &name, const QByteArray &data);
-private slots:
- void sendResult(int requestId, bool success);
-
private:
+ void sendResult(int requestId, bool success);
void sendCurrentObjects(const QList<QObject *> &objects);
void removeFromSelectedItems(QObject *object);
QString titleForItem(QQuickItem *item) const;
diff --git a/src/plugins/qmltooling/qmldbg_inspector/highlight.cpp b/src/plugins/qmltooling/qmldbg_inspector/highlight.cpp
index 26eb0f8ed8..88a6ea6b6d 100644
--- a/src/plugins/qmltooling/qmldbg_inspector/highlight.cpp
+++ b/src/plugins/qmltooling/qmldbg_inspector/highlight.cpp
@@ -72,24 +72,22 @@ void Highlight::setItem(QQuickItem *item)
m_item->disconnect(this);
if (item) {
- connect(item, SIGNAL(xChanged()), SLOT(adjust()));
- connect(item, SIGNAL(yChanged()), SLOT(adjust()));
- connect(item, SIGNAL(widthChanged()), SLOT(adjust()));
- connect(item, SIGNAL(heightChanged()), SLOT(adjust()));
- connect(item, SIGNAL(rotationChanged()), SLOT(adjust()));
- connect(item, SIGNAL(transformOriginChanged(TransformOrigin)),
- SLOT(adjust()));
+ connect(item, &QQuickItem::xChanged, this, &Highlight::adjust);
+ connect(item, &QQuickItem::yChanged, this, &Highlight::adjust);
+ connect(item, &QQuickItem::widthChanged, this, &Highlight::adjust);
+ connect(item, &QQuickItem::heightChanged, this, &Highlight::adjust);
+ connect(item, &QQuickItem::rotationChanged, this, &Highlight::adjust);
+ connect(item, &QQuickItem::transformOriginChanged, this, &Highlight::adjust);
}
QQuickWindow *view = item->window();
QQuickItem * contentItem = view->contentItem();
if (contentItem) {
- connect(contentItem, SIGNAL(xChanged()), SLOT(adjust()));
- connect(contentItem, SIGNAL(yChanged()), SLOT(adjust()));
- connect(contentItem, SIGNAL(widthChanged()), SLOT(adjust()));
- connect(contentItem, SIGNAL(heightChanged()), SLOT(adjust()));
- connect(contentItem, SIGNAL(rotationChanged()), SLOT(adjust()));
- connect(contentItem, SIGNAL(transformOriginChanged(TransformOrigin)),
- SLOT(adjust()));
+ connect(contentItem, &QQuickItem::xChanged, this, &Highlight::adjust);
+ connect(contentItem, &QQuickItem::yChanged, this, &Highlight::adjust);
+ connect(contentItem, &QQuickItem::widthChanged, this, &Highlight::adjust);
+ connect(contentItem, &QQuickItem::heightChanged, this, &Highlight::adjust);
+ connect(contentItem, &QQuickItem::rotationChanged, this, &Highlight::adjust);
+ connect(contentItem, &QQuickItem::transformOriginChanged, this, &Highlight::adjust);
}
m_item = item;
setContentsSize(view->size());
diff --git a/src/plugins/qmltooling/qmldbg_inspector/highlight.h b/src/plugins/qmltooling/qmldbg_inspector/highlight.h
index 4a85cb4d50..2bf4fc1ad5 100644
--- a/src/plugins/qmltooling/qmldbg_inspector/highlight.h
+++ b/src/plugins/qmltooling/qmldbg_inspector/highlight.h
@@ -65,8 +65,6 @@ protected:
private:
void initRenderDetails();
-
-private slots:
void adjust();
private:
@@ -86,13 +84,12 @@ public:
void paint(QPainter *painter);
void showName(const QPointF &displayPoint);
-private slots:
- void disableNameDisplay();
-
private:
QPointF m_displayPoint;
QString m_name;
bool m_nameDisplayActive;
+
+ void disableNameDisplay();
};
/**
diff --git a/src/plugins/qmltooling/qmldbg_inspector/qqmlinspectorservice.cpp b/src/plugins/qmltooling/qmldbg_inspector/qqmlinspectorservice.cpp
index 48a3f656b0..ab1aeebf64 100644
--- a/src/plugins/qmltooling/qmldbg_inspector/qqmlinspectorservice.cpp
+++ b/src/plugins/qmltooling/qmldbg_inspector/qqmlinspectorservice.cpp
@@ -55,23 +55,27 @@ public:
void setParentWindow(QQuickWindow *window, QWindow *parent) Q_DECL_OVERRIDE;
void removeWindow(QQuickWindow *window) Q_DECL_OVERRIDE;
+signals:
+ void scheduleMessage(const QByteArray &message);
+
protected:
virtual void messageReceived(const QByteArray &) Q_DECL_OVERRIDE;
-private slots:
- void messageFromClient(const QByteArray &message);
-
private:
friend class QQmlInspectorServiceFactory;
QmlJSDebugger::GlobalInspector *checkInspector();
QmlJSDebugger::GlobalInspector *m_globalInspector;
QHash<QQuickWindow *, QWindow *> m_waitingWindows;
+
+ void messageFromClient(const QByteArray &message);
};
QQmlInspectorServiceImpl::QQmlInspectorServiceImpl(QObject *parent):
QQmlInspectorService(1, parent), m_globalInspector(0)
{
+ connect(this, &QQmlInspectorServiceImpl::scheduleMessage,
+ this, &QQmlInspectorServiceImpl::messageFromClient, Qt::QueuedConnection);
}
QmlJSDebugger::GlobalInspector *QQmlInspectorServiceImpl::checkInspector()
@@ -122,8 +126,8 @@ void QQmlInspectorServiceImpl::setParentWindow(QQuickWindow *window, QWindow *pa
void QQmlInspectorServiceImpl::messageReceived(const QByteArray &message)
{
- QMetaObject::invokeMethod(this, "messageFromClient", Qt::QueuedConnection,
- Q_ARG(QByteArray, message));
+ // Move the message to the right thread via queued signal
+ emit scheduleMessage(message);
}
void QQmlInspectorServiceImpl::messageFromClient(const QByteArray &message)
diff --git a/src/plugins/qmltooling/qmldbg_local/qlocalclientconnection.cpp b/src/plugins/qmltooling/qmldbg_local/qlocalclientconnection.cpp
index 01c24f2395..64b26bdd0d 100644
--- a/src/plugins/qmltooling/qmldbg_local/qlocalclientconnection.cpp
+++ b/src/plugins/qmltooling/qmldbg_local/qlocalclientconnection.cpp
@@ -65,10 +65,8 @@ public:
void waitForConnection();
void flush();
-private slots:
- void connectionEstablished();
-
private:
+ void connectionEstablished();
bool connectToServer();
bool m_block;
@@ -135,7 +133,8 @@ bool QLocalClientConnection::connectToServer()
{
m_socket = new QLocalSocket;
m_socket->setParent(this);
- QObject::connect(m_socket, SIGNAL(connected()), this, SLOT(connectionEstablished()));
+ QObject::connect(m_socket, &QLocalSocket::connected,
+ this, &QLocalClientConnection::connectionEstablished);
m_socket->connectToServer(m_filename);
qDebug("QML Debugger: Connecting to socket %s...", m_filename.toLatin1().constData());
return true;
diff --git a/src/plugins/qmltooling/qmldbg_native/qqmlnativedebugconnector.h b/src/plugins/qmltooling/qmldbg_native/qqmlnativedebugconnector.h
index 03b5b5eb1e..1184925e53 100644
--- a/src/plugins/qmltooling/qmldbg_native/qqmlnativedebugconnector.h
+++ b/src/plugins/qmltooling/qmldbg_native/qqmlnativedebugconnector.h
@@ -63,11 +63,9 @@ public:
bool open(const QVariantHash &configuration) Q_DECL_OVERRIDE;
static void setDataStreamVersion(int version);
-private slots:
+private:
void sendMessage(const QString &name, const QByteArray &message);
void sendMessages(const QString &name, const QList<QByteArray> &messages);
-
-private:
void announceObjectAvailability(const QString &objectType, QObject *object, bool available);
QVector<QQmlDebugService *> m_services;
diff --git a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.cpp b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.cpp
index a193ddea0b..90e817e2fc 100644
--- a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.cpp
+++ b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.cpp
@@ -48,69 +48,86 @@ QQmlProfilerAdapter::QQmlProfilerAdapter(QQmlProfilerService *service, QQmlEngin
next(0)
{
setService(service);
- engine->enableProfiler();
- connect(this, SIGNAL(profilingEnabled(quint64)), engine->profiler, SLOT(startProfiling(quint64)));
- connect(this, SIGNAL(profilingEnabledWhileWaiting(quint64)),
- engine->profiler, SLOT(startProfiling(quint64)), Qt::DirectConnection);
- connect(this, SIGNAL(profilingDisabled()), engine->profiler, SLOT(stopProfiling()));
- connect(this, SIGNAL(profilingDisabledWhileWaiting()),
- engine->profiler, SLOT(stopProfiling()), Qt::DirectConnection);
- connect(this, SIGNAL(dataRequested()), engine->profiler, SLOT(reportData()));
- connect(this, SIGNAL(referenceTimeKnown(QElapsedTimer)),
- engine->profiler, SLOT(setTimer(QElapsedTimer)));
- connect(engine->profiler,
- SIGNAL(dataReady(QVector<QQmlProfilerData>,QQmlProfiler::LocationHash)),
- this,
- SLOT(receiveData(QVector<QQmlProfilerData>,QQmlProfiler::LocationHash)));
+ engine->profiler = new QQmlProfiler;
+ connect(this, &QQmlProfilerAdapter::profilingEnabled,
+ engine->profiler, &QQmlProfiler::startProfiling);
+ connect(this, &QQmlAbstractProfilerAdapter::profilingEnabledWhileWaiting,
+ engine->profiler, &QQmlProfiler::startProfiling, Qt::DirectConnection);
+ connect(this, &QQmlAbstractProfilerAdapter::profilingDisabled,
+ engine->profiler, &QQmlProfiler::stopProfiling);
+ connect(this, &QQmlAbstractProfilerAdapter::profilingDisabledWhileWaiting,
+ engine->profiler, &QQmlProfiler::stopProfiling, Qt::DirectConnection);
+ connect(this, &QQmlAbstractProfilerAdapter::dataRequested,
+ engine->profiler, &QQmlProfiler::reportData);
+ connect(this, &QQmlAbstractProfilerAdapter::referenceTimeKnown,
+ engine->profiler, &QQmlProfiler::setTimer);
+ connect(engine->profiler, &QQmlProfiler::dataReady,
+ this, &QQmlProfilerAdapter::receiveData);
}
// convert to QByteArrays that can be sent to the debug client
// use of QDataStream can skew results
// (see tst_qqmldebugtrace::trace() benchmark)
static void qQmlProfilerDataToByteArrays(const QQmlProfilerData &d,
- const QQmlProfiler::LocationHash &locations,
- QList<QByteArray> &messages)
+ QQmlProfiler::LocationHash &locations,
+ QList<QByteArray> &messages,
+ bool trackLocations)
{
QQmlDebugPacket ds;
Q_ASSERT_X((d.messageType & (1 << 31)) == 0, Q_FUNC_INFO,
"You can use at most 31 message types.");
for (quint32 decodedMessageType = 0; (d.messageType >> decodedMessageType) != 0;
++decodedMessageType) {
- if ((d.messageType & (1 << decodedMessageType)) == 0)
- continue;
-
- //### using QDataStream is relatively expensive
- ds << d.time << decodedMessageType << static_cast<quint32>(d.detailType);
-
- QQmlProfiler::Location l = locations.value(d.locationId);
+ if (decodedMessageType == QQmlProfilerDefinitions::RangeData
+ || (d.messageType & (1 << decodedMessageType)) == 0) {
+ continue; // RangeData is sent together with RangeLocation
+ }
- switch (decodedMessageType) {
- case QQmlProfilerDefinitions::RangeStart:
- case QQmlProfilerDefinitions::RangeEnd:
- break;
- case QQmlProfilerDefinitions::RangeData:
- ds << (l.location.sourceFile.isEmpty() ? l.url.toString() : l.location.sourceFile);
- break;
- case QQmlProfilerDefinitions::RangeLocation:
- ds << (l.url.isEmpty() ? l.location.sourceFile : l.url.toString())
- << static_cast<qint32>(l.location.line) << static_cast<qint32>(l.location.column);
- break;
- default:
- Q_ASSERT_X(false, Q_FUNC_INFO, "Invalid message type.");
- break;
+ if (decodedMessageType == QQmlProfilerDefinitions::RangeEnd
+ || decodedMessageType == QQmlProfilerDefinitions::RangeStart) {
+ ds << d.time << decodedMessageType << static_cast<quint32>(d.detailType);
+ if (trackLocations && d.locationId != 0)
+ ds << static_cast<qint64>(d.locationId);
+ } else {
+ auto i = locations.find(d.locationId);
+ if (i != locations.end()) {
+ ds << d.time << decodedMessageType << static_cast<quint32>(d.detailType);
+ ds << (i->url.isEmpty() ? i->location.sourceFile : i->url.toString())
+ << static_cast<qint32>(i->location.line)
+ << static_cast<qint32>(i->location.column);
+ if (d.messageType & (1 << QQmlProfilerDefinitions::RangeData)) {
+ // Send both, location and data ...
+ if (trackLocations)
+ ds << static_cast<qint64>(d.locationId);
+ messages.append(ds.squeezedData());
+ ds.clear();
+ ds << d.time << int(QQmlProfilerDefinitions::RangeData)
+ << static_cast<quint32>(d.detailType)
+ << (i->location.sourceFile.isEmpty() ? i->url.toString() :
+ i->location.sourceFile);
+ }
+ if (trackLocations) {
+ ds << static_cast<qint64>(d.locationId);
+ locations.erase(i); // ... so that we can erase here without missing anything.
+ }
+ } else {
+ // Skip RangeData and RangeLocation: We've already sent them
+ continue;
+ }
}
messages.append(ds.squeezedData());
ds.clear();
}
}
-qint64 QQmlProfilerAdapter::sendMessages(qint64 until, QList<QByteArray> &messages)
+qint64 QQmlProfilerAdapter::sendMessages(qint64 until, QList<QByteArray> &messages,
+ bool trackLocations)
{
while (next != data.length()) {
const QQmlProfilerData &nextData = data.at(next);
if (nextData.time > until || messages.length() > s_numMessagesPerBatch)
return nextData.time;
- qQmlProfilerDataToByteArrays(nextData, locations, messages);
+ qQmlProfilerDataToByteArrays(nextData, locations, messages, trackLocations);
++next;
}
diff --git a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.h b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.h
index 7e13b6c479..1fee5c389f 100644
--- a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.h
+++ b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.h
@@ -60,9 +60,9 @@ class QQmlProfilerAdapter : public QQmlAbstractProfilerAdapter {
Q_OBJECT
public:
QQmlProfilerAdapter(QQmlProfilerService *service, QQmlEnginePrivate *engine);
- qint64 sendMessages(qint64 until, QList<QByteArray> &messages) Q_DECL_OVERRIDE;
+ qint64 sendMessages(qint64 until, QList<QByteArray> &messages,
+ bool trackLocations) Q_DECL_OVERRIDE;
-public slots:
void receiveData(const QVector<QQmlProfilerData> &new_data,
const QQmlProfiler::LocationHash &locations);
diff --git a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.cpp b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.cpp
index e17722bb3d..dba2fd3cc3 100644
--- a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.cpp
+++ b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.cpp
@@ -57,7 +57,7 @@ Q_QML_DEBUG_PLUGIN_LOADER(QQmlAbstractProfilerAdapter)
QQmlProfilerServiceImpl::QQmlProfilerServiceImpl(QObject *parent) :
QQmlConfigurableDebugService<QQmlProfilerService>(1, parent),
- m_waitingForStop(false)
+ m_waitingForStop(false), m_useMessageTypes(false)
{
m_timer.start();
QQmlAbstractProfilerAdapter *quickAdapter =
@@ -309,7 +309,7 @@ void QQmlProfilerServiceImpl::stopProfiling(QJSEngine *engine)
m_waitingForStop = true;
foreach (QQmlAbstractProfilerAdapter *profiler, reporting)
- profiler->reportData();
+ profiler->reportData(m_useMessageTypes);
foreach (QQmlAbstractProfilerAdapter *profiler, stopping)
profiler->stopProfiling();
@@ -343,7 +343,8 @@ void QQmlProfilerServiceImpl::sendMessages()
m_startTimes.erase(m_startTimes.begin());
qint64 next = first->sendMessages(m_startTimes.isEmpty() ?
std::numeric_limits<qint64>::max() :
- m_startTimes.begin().key(), messages);
+ m_startTimes.begin().key(), messages,
+ m_useMessageTypes);
if (next != -1)
m_startTimes.insert(next, first);
@@ -408,16 +409,20 @@ void QQmlProfilerServiceImpl::messageReceived(const QByteArray &message)
if (!stream.atEnd()) {
stream >> flushInterval;
m_flushTimer.setInterval(flushInterval);
+ auto timerStart = static_cast<void(QTimer::*)()>(&QTimer::start);
if (flushInterval > 0) {
- connect(&m_flushTimer, SIGNAL(timeout()), this, SLOT(flush()));
- connect(this, SIGNAL(startFlushTimer()), &m_flushTimer, SLOT(start()));
- connect(this, SIGNAL(stopFlushTimer()), &m_flushTimer, SLOT(stop()));
+ connect(&m_flushTimer, &QTimer::timeout, this, &QQmlProfilerServiceImpl::flush);
+ connect(this, &QQmlProfilerServiceImpl::startFlushTimer, &m_flushTimer, timerStart);
+ connect(this, &QQmlProfilerServiceImpl::stopFlushTimer, &m_flushTimer, &QTimer::stop);
} else {
- disconnect(&m_flushTimer, SIGNAL(timeout()), this, SLOT(flush()));
- disconnect(this, SIGNAL(startFlushTimer()), &m_flushTimer, SLOT(start()));
- disconnect(this, SIGNAL(stopFlushTimer()), &m_flushTimer, SLOT(stop()));
+ disconnect(&m_flushTimer, &QTimer::timeout, this, &QQmlProfilerServiceImpl::flush);
+ disconnect(this, &QQmlProfilerServiceImpl::startFlushTimer, &m_flushTimer, timerStart);
+ disconnect(this, &QQmlProfilerServiceImpl::stopFlushTimer,
+ &m_flushTimer, &QTimer::stop);
}
}
+ if (!stream.atEnd())
+ stream >> m_useMessageTypes;
// If engineId == -1 objectForId() and then the cast will return 0.
if (enabled)
@@ -448,7 +453,7 @@ void QQmlProfilerServiceImpl::flush()
}
foreach (QQmlAbstractProfilerAdapter *profiler, reporting)
- profiler->reportData();
+ profiler->reportData(m_useMessageTypes);
}
QT_END_NAMESPACE
diff --git a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.h b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.h
index 6490e77f44..bbfc32b681 100644
--- a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.h
+++ b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.h
@@ -99,9 +99,6 @@ signals:
void startFlushTimer();
void stopFlushTimer();
-private slots:
- void flush();
-
protected:
virtual void stateAboutToBeChanged(State state) Q_DECL_OVERRIDE;
virtual void messageReceived(const QByteArray &) Q_DECL_OVERRIDE;
@@ -112,10 +109,12 @@ private:
void sendMessages();
void addEngineProfiler(QQmlAbstractProfilerAdapter *profiler, QJSEngine *engine);
void removeProfilerFromStartTimes(const QQmlAbstractProfilerAdapter *profiler);
+ void flush();
QElapsedTimer m_timer;
QTimer m_flushTimer;
bool m_waitingForStop;
+ bool m_useMessageTypes;
QList<QQmlAbstractProfilerAdapter *> m_globalProfilers;
QMultiHash<QJSEngine *, QQmlAbstractProfilerAdapter *> m_engineProfilers;
diff --git a/src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.cpp b/src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.cpp
index 68a71a5524..eae5e0a795 100644
--- a/src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.cpp
+++ b/src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.cpp
@@ -46,27 +46,26 @@ QV4ProfilerAdapter::QV4ProfilerAdapter(QQmlProfilerService *service, QV4::Execut
m_functionCallPos(0), m_memoryPos(0)
{
setService(service);
- engine->enableProfiler();
- connect(this, SIGNAL(profilingEnabled(quint64)),
- this, SLOT(forwardEnabled(quint64)));
- connect(this, SIGNAL(profilingEnabledWhileWaiting(quint64)),
- this, SLOT(forwardEnabledWhileWaiting(quint64)), Qt::DirectConnection);
- connect(this, SIGNAL(v4ProfilingEnabled(quint64)),
- engine->profiler, SLOT(startProfiling(quint64)));
- connect(this, SIGNAL(v4ProfilingEnabledWhileWaiting(quint64)),
- engine->profiler, SLOT(startProfiling(quint64)), Qt::DirectConnection);
- connect(this, SIGNAL(profilingDisabled()), engine->profiler, SLOT(stopProfiling()));
- connect(this, SIGNAL(profilingDisabledWhileWaiting()), engine->profiler, SLOT(stopProfiling()),
+ engine->setProfiler(new QV4::Profiling::Profiler(engine));
+ connect(this, &QQmlAbstractProfilerAdapter::profilingEnabled,
+ this, &QV4ProfilerAdapter::forwardEnabled);
+ connect(this, &QQmlAbstractProfilerAdapter::profilingEnabledWhileWaiting,
+ this, &QV4ProfilerAdapter::forwardEnabledWhileWaiting, Qt::DirectConnection);
+ connect(this, &QV4ProfilerAdapter::v4ProfilingEnabled,
+ engine->profiler(), &QV4::Profiling::Profiler::startProfiling);
+ connect(this, &QV4ProfilerAdapter::v4ProfilingEnabledWhileWaiting,
+ engine->profiler(), &QV4::Profiling::Profiler::startProfiling, Qt::DirectConnection);
+ connect(this, &QQmlAbstractProfilerAdapter::profilingDisabled,
+ engine->profiler(), &QV4::Profiling::Profiler::stopProfiling);
+ connect(this, &QQmlAbstractProfilerAdapter::profilingDisabledWhileWaiting,
+ engine->profiler(), &QV4::Profiling::Profiler::stopProfiling,
Qt::DirectConnection);
- connect(this, SIGNAL(dataRequested()), engine->profiler, SLOT(reportData()));
- connect(this, SIGNAL(referenceTimeKnown(QElapsedTimer)),
- engine->profiler, SLOT(setTimer(QElapsedTimer)));
- connect(engine->profiler, SIGNAL(dataReady(QV4::Profiling::FunctionLocationHash,
- QVector<QV4::Profiling::FunctionCallProperties>,
- QVector<QV4::Profiling::MemoryAllocationProperties>)),
- this, SLOT(receiveData(QV4::Profiling::FunctionLocationHash,
- QVector<QV4::Profiling::FunctionCallProperties>,
- QVector<QV4::Profiling::MemoryAllocationProperties>)));
+ connect(this, &QQmlAbstractProfilerAdapter::dataRequested,
+ engine->profiler(), &QV4::Profiling::Profiler::reportData);
+ connect(this, &QQmlAbstractProfilerAdapter::referenceTimeKnown,
+ engine->profiler(), &QV4::Profiling::Profiler::setTimer);
+ connect(engine->profiler(), &QV4::Profiling::Profiler::dataReady,
+ this, &QV4ProfilerAdapter::receiveData);
}
qint64 QV4ProfilerAdapter::appendMemoryEvents(qint64 until, QList<QByteArray> &messages,
@@ -77,7 +76,7 @@ qint64 QV4ProfilerAdapter::appendMemoryEvents(qint64 until, QList<QByteArray> &m
while (memoryData.length() > m_memoryPos && memoryData[m_memoryPos].timestamp <= until) {
const QV4::Profiling::MemoryAllocationProperties &props = memoryData[m_memoryPos];
- d << props.timestamp << MemoryAllocation << props.type << props.size;
+ d << props.timestamp << int(MemoryAllocation) << int(props.type) << props.size;
++m_memoryPos;
messages.append(d.squeezedData());
d.clear();
@@ -105,13 +104,13 @@ qint64 QV4ProfilerAdapter::finalizeMessages(qint64 until, QList<QByteArray> &mes
return callNext == -1 ? memoryNext : qMin(callNext, memoryNext);
}
-qint64 QV4ProfilerAdapter::sendMessages(qint64 until, QList<QByteArray> &messages)
+qint64 QV4ProfilerAdapter::sendMessages(qint64 until, QList<QByteArray> &messages,
+ bool trackLocations)
{
QQmlDebugPacket d;
// Make it const, so that we cannot accidentally detach it.
const QVector<QV4::Profiling::FunctionCallProperties> &functionCallData = m_functionCallData;
- const QV4::Profiling::FunctionLocationHash &functionLocations = m_functionLocations;
while (true) {
while (!m_stack.isEmpty() &&
@@ -121,7 +120,7 @@ qint64 QV4ProfilerAdapter::sendMessages(qint64 until, QList<QByteArray> &message
return finalizeMessages(until, messages, m_stack.top(), d);
appendMemoryEvents(m_stack.top(), messages, d);
- d << m_stack.pop() << RangeEnd << Javascript;
+ d << m_stack.pop() << int(RangeEnd) << int(Javascript);
messages.append(d.squeezedData());
d.clear();
}
@@ -133,17 +132,27 @@ qint64 QV4ProfilerAdapter::sendMessages(qint64 until, QList<QByteArray> &message
return finalizeMessages(until, messages, props.start, d);
appendMemoryEvents(props.start, messages, d);
- auto location = functionLocations.constFind(props.id);
- Q_ASSERT(location != functionLocations.constEnd());
-
- d << props.start << RangeStart << Javascript;
- messages.push_back(d.squeezedData());
- d.clear();
- d << props.start << RangeLocation << Javascript << location->file << location->line
- << location->column;
- messages.push_back(d.squeezedData());
- d.clear();
- d << props.start << RangeData << Javascript << location->name;
+ auto location = m_functionLocations.find(props.id);
+
+ d << props.start << int(RangeStart) << int(Javascript);
+ if (trackLocations)
+ d << static_cast<qint64>(props.id);
+ if (location != m_functionLocations.end()) {
+ messages.push_back(d.squeezedData());
+ d.clear();
+ d << props.start << int(RangeLocation) << int(Javascript) << location->file << location->line
+ << location->column;
+ if (trackLocations)
+ d << static_cast<qint64>(props.id);
+ messages.push_back(d.squeezedData());
+ d.clear();
+ d << props.start << int(RangeData) << int(Javascript) << location->name;
+
+ if (trackLocations) {
+ d << static_cast<qint64>(props.id);
+ m_functionLocations.erase(location);
+ }
+ }
messages.push_back(d.squeezedData());
d.clear();
m_stack.push(props.end);
diff --git a/src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.h b/src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.h
index 968825c346..5d5b83f7ca 100644
--- a/src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.h
+++ b/src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.h
@@ -67,20 +67,16 @@ class QV4ProfilerAdapter : public QQmlAbstractProfilerAdapter {
public:
QV4ProfilerAdapter(QQmlProfilerService *service, QV4::ExecutionEngine *engine);
- virtual qint64 sendMessages(qint64 until, QList<QByteArray> &messages);
+ virtual qint64 sendMessages(qint64 until, QList<QByteArray> &messages,
+ bool trackLocations) override;
-signals:
- void v4ProfilingEnabled(quint64 v4Features);
- void v4ProfilingEnabledWhileWaiting(quint64 v4Features);
-
-public slots:
void receiveData(const QV4::Profiling::FunctionLocationHash &,
const QVector<QV4::Profiling::FunctionCallProperties> &,
const QVector<QV4::Profiling::MemoryAllocationProperties> &);
-private slots:
- void forwardEnabled(quint64 features);
- void forwardEnabledWhileWaiting(quint64 features);
+signals:
+ void v4ProfilingEnabled(quint64 v4Features);
+ void v4ProfilingEnabledWhileWaiting(quint64 v4Features);
private:
QV4::Profiling::FunctionLocationHash m_functionLocations;
@@ -92,6 +88,8 @@ private:
qint64 appendMemoryEvents(qint64 until, QList<QByteArray> &messages, QQmlDebugPacket &d);
qint64 finalizeMessages(qint64 until, QList<QByteArray> &messages, qint64 callNext,
QQmlDebugPacket &d);
+ void forwardEnabled(quint64 features);
+ void forwardEnabledWhileWaiting(quint64 features);
static quint64 translateFeatures(quint64 qmlFeatures);
};
diff --git a/src/plugins/qmltooling/qmldbg_quickprofiler/qquickprofileradapter.cpp b/src/plugins/qmltooling/qmldbg_quickprofiler/qquickprofileradapter.cpp
index 9a2afd367d..0c9fc36463 100644
--- a/src/plugins/qmltooling/qmldbg_quickprofiler/qquickprofileradapter.cpp
+++ b/src/plugins/qmltooling/qmldbg_quickprofiler/qquickprofileradapter.cpp
@@ -51,20 +51,20 @@ QQuickProfilerAdapter::QQuickProfilerAdapter(QObject *parent) :
QQuickProfiler::initialize(this);
// We can always do DirectConnection here as all methods are protected by mutexes
- connect(this, SIGNAL(profilingEnabled(quint64)),
- QQuickProfiler::s_instance, SLOT(startProfilingImpl(quint64)), Qt::DirectConnection);
- connect(this, SIGNAL(profilingEnabledWhileWaiting(quint64)),
- QQuickProfiler::s_instance, SLOT(startProfilingImpl(quint64)), Qt::DirectConnection);
- connect(this, SIGNAL(referenceTimeKnown(QElapsedTimer)),
- QQuickProfiler::s_instance, SLOT(setTimer(QElapsedTimer)), Qt::DirectConnection);
- connect(this, SIGNAL(profilingDisabled()),
- QQuickProfiler::s_instance, SLOT(stopProfilingImpl()), Qt::DirectConnection);
- connect(this, SIGNAL(profilingDisabledWhileWaiting()),
- QQuickProfiler::s_instance, SLOT(stopProfilingImpl()), Qt::DirectConnection);
- connect(this, SIGNAL(dataRequested()),
- QQuickProfiler::s_instance, SLOT(reportDataImpl()), Qt::DirectConnection);
- connect(QQuickProfiler::s_instance, SIGNAL(dataReady(QVector<QQuickProfilerData>)),
- this, SLOT(receiveData(QVector<QQuickProfilerData>)), Qt::DirectConnection);
+ connect(this, &QQmlAbstractProfilerAdapter::profilingEnabled,
+ QQuickProfiler::s_instance, &QQuickProfiler::startProfilingImpl, Qt::DirectConnection);
+ connect(this, &QQmlAbstractProfilerAdapter::profilingEnabledWhileWaiting,
+ QQuickProfiler::s_instance, &QQuickProfiler::startProfilingImpl, Qt::DirectConnection);
+ connect(this, &QQmlAbstractProfilerAdapter::referenceTimeKnown,
+ QQuickProfiler::s_instance, &QQuickProfiler::setTimer, Qt::DirectConnection);
+ connect(this, &QQmlAbstractProfilerAdapter::profilingDisabled,
+ QQuickProfiler::s_instance, &QQuickProfiler::stopProfilingImpl, Qt::DirectConnection);
+ connect(this, &QQmlAbstractProfilerAdapter::profilingDisabledWhileWaiting,
+ QQuickProfiler::s_instance, &QQuickProfiler::stopProfilingImpl, Qt::DirectConnection);
+ connect(this, &QQmlAbstractProfilerAdapter::dataRequested,
+ QQuickProfiler::s_instance, &QQuickProfiler::reportDataImpl, Qt::DirectConnection);
+ connect(QQuickProfiler::s_instance, &QQuickProfiler::dataReady,
+ this, &QQuickProfilerAdapter::receiveData, Qt::DirectConnection);
}
QQuickProfilerAdapter::~QQuickProfilerAdapter()
@@ -150,8 +150,10 @@ static void qQuickProfilerDataToByteArrays(const QQuickProfilerData &data,
}
}
-qint64 QQuickProfilerAdapter::sendMessages(qint64 until, QList<QByteArray> &messages)
+qint64 QQuickProfilerAdapter::sendMessages(qint64 until, QList<QByteArray> &messages,
+ bool trackLocations)
{
+ Q_UNUSED(trackLocations);
while (next < m_data.size()) {
if (m_data[next].time <= until && messages.length() <= s_numMessagesPerBatch)
qQuickProfilerDataToByteArrays(m_data[next++], messages);
diff --git a/src/plugins/qmltooling/qmldbg_quickprofiler/qquickprofileradapter.h b/src/plugins/qmltooling/qmldbg_quickprofiler/qquickprofileradapter.h
index 0983561d2c..1ad020afd6 100644
--- a/src/plugins/qmltooling/qmldbg_quickprofiler/qquickprofileradapter.h
+++ b/src/plugins/qmltooling/qmldbg_quickprofiler/qquickprofileradapter.h
@@ -61,9 +61,7 @@ class QQuickProfilerAdapter : public QQmlAbstractProfilerAdapter {
public:
QQuickProfilerAdapter(QObject *parent = 0);
~QQuickProfilerAdapter();
- qint64 sendMessages(qint64 until, QList<QByteArray> &messages);
-
-public slots:
+ qint64 sendMessages(qint64 until, QList<QByteArray> &messages, bool trackLocations) override;
void receiveData(const QVector<QQuickProfilerData> &new_data);
private:
diff --git a/src/plugins/qmltooling/qmldbg_server/qqmldebugserver.cpp b/src/plugins/qmltooling/qmldbg_server/qqmldebugserver.cpp
index cbde86e389..96b3455790 100644
--- a/src/plugins/qmltooling/qmldbg_server/qqmldebugserver.cpp
+++ b/src/plugins/qmltooling/qmldbg_server/qqmldebugserver.cpp
@@ -149,15 +149,6 @@ public:
static void cleanup();
-private slots:
- void wakeEngine(QJSEngine *engine);
- void sendMessage(const QString &name, const QByteArray &message);
- void sendMessages(const QString &name, const QList<QByteArray> &messages);
- void changeServiceState(const QString &serviceName, QQmlDebugService::State state);
- void removeThread();
- void receiveMessage();
- void invalidPacket();
-
private:
friend class QQmlDebugServerThread;
friend class QQmlDebugServerFactory;
@@ -179,6 +170,13 @@ private:
bool canSendMessage(const QString &name);
void doSendMessage(const QString &name, const QByteArray &message);
+ void wakeEngine(QJSEngine *engine);
+ void sendMessage(const QString &name, const QByteArray &message);
+ void sendMessages(const QString &name, const QList<QByteArray> &messages);
+ void changeServiceState(const QString &serviceName, QQmlDebugService::State state);
+ void removeThread();
+ void receiveMessage();
+ void invalidPacket();
QQmlDebugServerConnection *m_connection;
QHash<QString, QQmlDebugService *> m_plugins;
@@ -203,18 +201,22 @@ void QQmlDebugServerImpl::cleanup()
if (!server)
return;
- for (QHash<QString, QQmlDebugService *>::ConstIterator i = server->m_plugins.constBegin();
- i != server->m_plugins.constEnd(); ++i) {
- server->m_changeServiceStateCalls.ref();
- QMetaObject::invokeMethod(server, "changeServiceState", Qt::QueuedConnection,
- Q_ARG(QString, i.key()),
- Q_ARG(QQmlDebugService::State,
- QQmlDebugService::NotConnected));
+ {
+ QObject signalSource;
+ for (QHash<QString, QQmlDebugService *>::ConstIterator i = server->m_plugins.constBegin();
+ i != server->m_plugins.constEnd(); ++i) {
+ server->m_changeServiceStateCalls.ref();
+ QString key = i.key();
+ // Process this in the server's thread.
+ connect(&signalSource, &QObject::destroyed, server, [key, server](){
+ server->changeServiceState(key, QQmlDebugService::NotConnected);
+ }, Qt::QueuedConnection);
+ }
}
// Wait for changeServiceState calls to finish
// (while running an event loop because some services
- // might again use slots to execute stuff in the GUI thread)
+ // might again defer execution of stuff in the GUI thread)
QEventLoop loop;
while (!server->m_changeServiceStateCalls.testAndSetOrdered(0, 0))
loop.processEvents();
@@ -293,7 +295,7 @@ QQmlDebugServerImpl::QQmlDebugServerImpl() :
// Remove the thread immmediately when it finishes, so that we don't have to wait for the
// event loop to signal that.
- QObject::connect(&m_thread, SIGNAL(finished()), this, SLOT(removeThread()),
+ QObject::connect(&m_thread, &QThread::finished, this, &QQmlDebugServerImpl::removeThread,
Qt::DirectConnection);
m_thread.setObjectName(QStringLiteral("QQmlDebugServerThread"));
parseArguments();
@@ -631,15 +633,15 @@ bool QQmlDebugServerImpl::addService(const QString &name, QQmlDebugService *serv
if (!service || m_plugins.contains(name))
return false;
- connect(service, SIGNAL(messageToClient(QString,QByteArray)),
- this, SLOT(sendMessage(QString,QByteArray)));
- connect(service, SIGNAL(messagesToClient(QString,QList<QByteArray>)),
- this, SLOT(sendMessages(QString,QList<QByteArray>)));
+ connect(service, &QQmlDebugService::messageToClient,
+ this, &QQmlDebugServerImpl::sendMessage);
+ connect(service, &QQmlDebugService::messagesToClient,
+ this, &QQmlDebugServerImpl::sendMessages);
- connect(service, SIGNAL(attachedToEngine(QJSEngine*)),
- this, SLOT(wakeEngine(QJSEngine*)), Qt::QueuedConnection);
- connect(service, SIGNAL(detachedFromEngine(QJSEngine*)),
- this, SLOT(wakeEngine(QJSEngine*)), Qt::QueuedConnection);
+ connect(service, &QQmlDebugService::attachedToEngine,
+ this, &QQmlDebugServerImpl::wakeEngine, Qt::QueuedConnection);
+ connect(service, &QQmlDebugService::detachedFromEngine,
+ this, &QQmlDebugServerImpl::wakeEngine, Qt::QueuedConnection);
service->setState(QQmlDebugService::Unavailable);
m_plugins.insert(name, service);
@@ -659,15 +661,15 @@ bool QQmlDebugServerImpl::removeService(const QString &name)
m_plugins.remove(name);
service->setState(QQmlDebugService::NotConnected);
- disconnect(service, SIGNAL(detachedFromEngine(QJSEngine*)),
- this, SLOT(wakeEngine(QJSEngine*)));
- disconnect(service, SIGNAL(attachedToEngine(QJSEngine*)),
- this, SLOT(wakeEngine(QJSEngine*)));
+ disconnect(service, &QQmlDebugService::detachedFromEngine,
+ this, &QQmlDebugServerImpl::wakeEngine);
+ disconnect(service, &QQmlDebugService::attachedToEngine,
+ this, &QQmlDebugServerImpl::wakeEngine);
- disconnect(service, SIGNAL(messagesToClient(QString,QList<QByteArray>)),
- this, SLOT(sendMessages(QString,QList<QByteArray>)));
- disconnect(service, SIGNAL(messageToClient(QString,QByteArray)),
- this, SLOT(sendMessage(QString,QByteArray)));
+ disconnect(service, &QQmlDebugService::messagesToClient,
+ this, &QQmlDebugServerImpl::sendMessages);
+ disconnect(service, &QQmlDebugService::messageToClient,
+ this, &QQmlDebugServerImpl::sendMessage);
return true;
}
@@ -738,8 +740,10 @@ void QQmlDebugServerImpl::EngineCondition::wake()
void QQmlDebugServerImpl::setDevice(QIODevice *socket)
{
m_protocol = new QPacketProtocol(socket, this);
- QObject::connect(m_protocol, SIGNAL(readyRead()), this, SLOT(receiveMessage()));
- QObject::connect(m_protocol, SIGNAL(invalidPacket()), this, SLOT(invalidPacket()));
+ QObject::connect(m_protocol, &QPacketProtocol::readyRead,
+ this, &QQmlDebugServerImpl::receiveMessage);
+ QObject::connect(m_protocol, &QPacketProtocol::invalidPacket,
+ this, &QQmlDebugServerImpl::invalidPacket);
if (blockingMode())
m_protocol->waitForReadyRead(-1);
diff --git a/src/plugins/qmltooling/qmldbg_tcp/qtcpserverconnection.cpp b/src/plugins/qmltooling/qmldbg_tcp/qtcpserverconnection.cpp
index 3d64312b16..b305c3f535 100644
--- a/src/plugins/qmltooling/qmldbg_tcp/qtcpserverconnection.cpp
+++ b/src/plugins/qmltooling/qmldbg_tcp/qtcpserverconnection.cpp
@@ -65,10 +65,8 @@ public:
void waitForConnection();
void flush();
-private slots:
- void newConnection();
-
private:
+ void newConnection();
bool listen();
int m_portFrom;
@@ -152,7 +150,8 @@ void QTcpServerConnection::flush()
bool QTcpServerConnection::listen()
{
m_tcpServer = new QTcpServer(this);
- QObject::connect(m_tcpServer, SIGNAL(newConnection()), this, SLOT(newConnection()));
+ QObject::connect(m_tcpServer, &QTcpServer::newConnection,
+ this, &QTcpServerConnection::newConnection);
QHostAddress hostaddress;
if (!m_hostaddress.isEmpty()) {
if (!hostaddress.setAddress(m_hostaddress)) {
diff --git a/src/plugins/qmltooling/qmltooling.pro b/src/plugins/qmltooling/qmltooling.pro
index 3728126dd9..907fbe9273 100644
--- a/src/plugins/qmltooling/qmltooling.pro
+++ b/src/plugins/qmltooling/qmltooling.pro
@@ -1,4 +1,5 @@
TEMPLATE = subdirs
+QT_FOR_CONFIG += qml
# Utilities
SUBDIRS += \
@@ -7,9 +8,13 @@ SUBDIRS += \
# Connectors
SUBDIRS += \
qmldbg_native \
- qmldbg_server \
+ qmldbg_server
+
+qtConfig(qml-network) {
+ SUBDIRS += \
qmldbg_local \
qmldbg_tcp
+}
# Services
SUBDIRS += \
diff --git a/src/plugins/qmltooling/shared/qqmldebugserver.h b/src/plugins/qmltooling/shared/qqmldebugserver.h
index 424c7c4120..109f1e246c 100644
--- a/src/plugins/qmltooling/shared/qqmldebugserver.h
+++ b/src/plugins/qmltooling/shared/qqmldebugserver.h
@@ -58,7 +58,7 @@
QT_BEGIN_NAMESPACE
-class QQmlDebugServer : protected QQmlDebugConnector
+class QQmlDebugServer : public QQmlDebugConnector
{
Q_OBJECT
public:
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..9cca5458ee
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/d3d12.pro
@@ -0,0 +1,61 @@
+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/qsgd3d12threadedrenderloop.cpp \
+ $$PWD/qsgd3d12renderer.cpp \
+ $$PWD/qsgd3d12context.cpp \
+ $$PWD/qsgd3d12rendercontext.cpp \
+ $$PWD/qsgd3d12internalrectanglenode.cpp \
+ $$PWD/qsgd3d12material.cpp \
+ $$PWD/qsgd3d12builtinmaterials.cpp \
+ $$PWD/qsgd3d12texture.cpp \
+ $$PWD/qsgd3d12internalimagenode.cpp \
+ $$PWD/qsgd3d12glyphnode.cpp \
+ $$PWD/qsgd3d12glyphcache.cpp \
+ $$PWD/qsgd3d12layer.cpp \
+ $$PWD/qsgd3d12shadereffectnode.cpp \
+ $$PWD/qsgd3d12painternode.cpp \
+ $$PWD/qsgd3d12publicnodes.cpp \
+ $$PWD/qsgd3d12spritenode.cpp
+
+NO_PCH_SOURCES += \
+ $$PWD/qsgd3d12engine.cpp
+
+HEADERS += \
+ $$PWD/qsgd3d12adaptation_p.h \
+ $$PWD/qsgd3d12renderloop_p.h \
+ $$PWD/qsgd3d12threadedrenderloop_p.h \
+ $$PWD/qsgd3d12renderer_p.h \
+ $$PWD/qsgd3d12context_p.h \
+ $$PWD/qsgd3d12rendercontext_p.h \
+ $$PWD/qsgd3d12engine_p.h \
+ $$PWD/qsgd3d12engine_p_p.h \
+ $$PWD/qsgd3d12internalrectanglenode_p.h \
+ $$PWD/qsgd3d12material_p.h \
+ $$PWD/qsgd3d12builtinmaterials_p.h \
+ $$PWD/qsgd3d12texture_p.h \
+ $$PWD/qsgd3d12internalimagenode_p.h \
+ $$PWD/qsgd3d12glyphnode_p.h \
+ $$PWD/qsgd3d12glyphcache_p.h \
+ $$PWD/qsgd3d12layer_p.h \
+ $$PWD/qsgd3d12shadereffectnode_p.h \
+ $$PWD/qsgd3d12painternode_p.h \
+ $$PWD/qsgd3d12publicnodes_p.h \
+ $$PWD/qsgd3d12spritenode_p.h
+
+LIBS += -ldxgi -ld3d12 -ld3dcompiler -ldcomp
+
+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..b93da0ae01
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12adaptation.cpp
@@ -0,0 +1,87 @@
+/****************************************************************************
+**
+** 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 "qsgd3d12threadedrenderloop_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()
+{
+ static bool threaded = false;
+ static bool envChecked = false;
+ if (!envChecked) {
+ envChecked = true;
+ threaded = qgetenv("QSG_RENDER_LOOP") == QByteArrayLiteral("threaded");
+ }
+
+ if (threaded)
+ return new QSGD3D12ThreadedRenderLoop;
+
+ 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..3351486bc6
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12builtinmaterials.cpp
@@ -0,0 +1,737 @@
+/****************************************************************************
+**
+** 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_flatcolor.hlslh"
+#include "ps_flatcolor.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.
+
+static inline QVector4D qsg_premultiply(const QVector4D &c, float globalOpacity)
+{
+ const float o = c.w() * globalOpacity;
+ return QVector4D(c.x() * o, c.y() * o, c.z() * o, o);
+}
+
+static inline QVector4D qsg_premultiply(const QColor &c, float globalOpacity)
+{
+ const float o = c.alphaF() * globalOpacity;
+ return QVector4D(c.redF() * o, c.greenF() * o, c.blueF() * 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;
+}
+
+QSGMaterialType QSGD3D12VertexColorMaterial::mtype;
+
+QSGMaterialType *QSGD3D12VertexColorMaterial::type() const
+{
+ return &QSGD3D12VertexColorMaterial::mtype;
+}
+
+int QSGD3D12VertexColorMaterial::compare(const QSGMaterial *other) const
+{
+ Q_UNUSED(other);
+ 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 QSGD3D12MaterialRenderState &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;
+}
+
+QSGD3D12FlatColorMaterial::QSGD3D12FlatColorMaterial()
+ : m_color(QColor(255, 255, 255))
+{
+}
+
+QSGMaterialType QSGD3D12FlatColorMaterial::mtype;
+
+QSGMaterialType *QSGD3D12FlatColorMaterial::type() const
+{
+ return &QSGD3D12FlatColorMaterial::mtype;
+}
+
+int QSGD3D12FlatColorMaterial::compare(const QSGMaterial *other) const
+{
+ Q_ASSERT(other && type() == other->type());
+ const QSGD3D12FlatColorMaterial *o = static_cast<const QSGD3D12FlatColorMaterial *>(other);
+ return m_color.rgba() - o->color().rgba();
+}
+
+static const int FLAT_COLOR_CB_SIZE_0 = 16 * sizeof(float); // float4x4
+static const int FLAT_COLOR_CB_SIZE_1 = 4 * sizeof(float); // float4
+static const int FLAT_COLOR_CB_SIZE = FLAT_COLOR_CB_SIZE_0 + FLAT_COLOR_CB_SIZE_1;
+
+int QSGD3D12FlatColorMaterial::constantBufferSize() const
+{
+ return QSGD3D12Engine::alignedConstantBufferSize(FLAT_COLOR_CB_SIZE);
+}
+
+void QSGD3D12FlatColorMaterial::preparePipeline(QSGD3D12PipelineState *pipelineState)
+{
+ pipelineState->shaders.vs = g_VS_FlatColor;
+ pipelineState->shaders.vsSize = sizeof(g_VS_FlatColor);
+ pipelineState->shaders.ps = g_PS_FlatColor;
+ pipelineState->shaders.psSize = sizeof(g_PS_FlatColor);
+}
+
+QSGD3D12Material::UpdateResults QSGD3D12FlatColorMaterial::updatePipeline(const QSGD3D12MaterialRenderState &state,
+ QSGD3D12PipelineState *,
+ ExtraState *,
+ quint8 *constantBuffer)
+{
+ QSGD3D12Material::UpdateResults r = 0;
+ quint8 *p = constantBuffer;
+
+ if (state.isMatrixDirty()) {
+ memcpy(p, state.combinedMatrix().constData(), FLAT_COLOR_CB_SIZE_0);
+ r |= UpdatedConstantBuffer;
+ }
+ p += FLAT_COLOR_CB_SIZE_0;
+
+ const QVector4D color = qsg_premultiply(m_color, state.opacity());
+ const float f[] = { color.x(), color.y(), color.z(), color.w() };
+ if (state.isOpacityDirty() || memcmp(p, f, FLAT_COLOR_CB_SIZE_1)) {
+ memcpy(p, f, FLAT_COLOR_CB_SIZE_1);
+ r |= UpdatedConstantBuffer;
+ }
+
+ return r;
+}
+
+void QSGD3D12FlatColorMaterial::setColor(const QColor &color)
+{
+ m_color = color;
+ setFlag(Blending, m_color.alpha() != 0xFF);
+}
+
+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_UNUSED(other);
+ 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 QSGD3D12MaterialRenderState &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.textureViewCount = 1;
+}
+
+QSGD3D12Material::UpdateResults QSGD3D12TextureMaterial::updatePipeline(const QSGD3D12MaterialRenderState &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;
+}
+
+void QSGD3D12TextureMaterial::setTexture(QSGTexture *texture)
+{
+ m_texture = texture;
+ setFlag(Blending, m_texture ? m_texture->hasAlphaChannel() : false);
+}
+
+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.textureViewCount = 1;
+}
+
+QSGD3D12Material::UpdateResults QSGD3D12SmoothTextureMaterial::updatePipeline(const QSGD3D12MaterialRenderState &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;
+}
+
+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.textureViewCount = 1;
+}
+
+QSGD3D12Material::UpdateResults QSGD3D12TextMaterial::updatePipeline(const QSGD3D12MaterialRenderState &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;
+
+ const QSize sz = glyphCache()->currentSize();
+ const float textureScale[] = { 1.0f / sz.width(), 1.0f / sz.height() };
+ if (state.isCachedMaterialDataDirty() || memcmp(p, textureScale, TEXT_CB_SIZE_1)) {
+ memcpy(p, textureScale, TEXT_CB_SIZE_1);
+ r |= UpdatedConstantBuffer;
+ }
+ p += TEXT_CB_SIZE_1;
+
+ const float dpr = m_rc->engine()->windowDevicePixelRatio();
+ if (state.isCachedMaterialDataDirty() || memcmp(p, &dpr, TEXT_CB_SIZE_2)) {
+ memcpy(p, &dpr, TEXT_CB_SIZE_2);
+ r |= UpdatedConstantBuffer;
+ }
+ p += TEXT_CB_SIZE_2;
+
+ if (glyphCache()->glyphFormat() == QFontEngine::Format_A32) {
+ const QVector4D color = qsg_premultiply(m_color, state.opacity());
+ const float alpha = color.w();
+ if (state.isOpacityDirty() || memcmp(p, &alpha, TEXT_CB_SIZE_3)) {
+ memcpy(p, &alpha, TEXT_CB_SIZE_3);
+ r |= UpdatedConstantBuffer;
+ }
+ } else if (glyphCache()->glyphFormat() == QFontEngine::Format_ARGB) {
+ const float opacity = m_color.w() * state.opacity();
+ if (state.isOpacityDirty() || memcmp(p, &opacity, TEXT_CB_SIZE_3)) {
+ memcpy(p, &opacity, TEXT_CB_SIZE_3);
+ r |= UpdatedConstantBuffer;
+ }
+ } else {
+ const QVector4D color = qsg_premultiply(m_color, state.opacity());
+ const float f[] = { color.x(), color.y(), color.z(), color.w() };
+ if (state.isOpacityDirty() || memcmp(p, f, TEXT_CB_SIZE_4)) {
+ 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) {
+ const float f[] = { m_styleShift.x(), m_styleShift.y() };
+ if (state.isCachedMaterialDataDirty() || memcmp(p, f, TEXT_CB_SIZE_5)) {
+ 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) {
+ const QVector4D color = qsg_premultiply(m_styleColor, state.opacity());
+ const float f[] = { color.x(), color.y(), color.z(), color.w() };
+ if (state.isOpacityDirty() || memcmp(p, f, TEXT_CB_SIZE_6)) {
+ 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::UnsignedShortType);
+ 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..8e488f8cd1
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12builtinmaterials_p.h
@@ -0,0 +1,253 @@
+/****************************************************************************
+**
+** 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 QSGD3D12MaterialRenderState &state,
+ QSGD3D12PipelineState *pipelineState,
+ ExtraState *extraState,
+ quint8 *constantBuffer) override;
+
+private:
+ static QSGMaterialType mtype;
+};
+
+class QSGD3D12FlatColorMaterial : public QSGD3D12Material
+{
+public:
+ QSGD3D12FlatColorMaterial();
+ QSGMaterialType *type() const override;
+ int compare(const QSGMaterial *other) const override;
+
+ int constantBufferSize() const override;
+ void preparePipeline(QSGD3D12PipelineState *pipelineState) override;
+ UpdateResults updatePipeline(const QSGD3D12MaterialRenderState &state,
+ QSGD3D12PipelineState *pipelineState,
+ ExtraState *extraState,
+ quint8 *constantBuffer) override;
+
+ void setColor(const QColor &color);
+ QColor color() const { return m_color; }
+
+private:
+ static QSGMaterialType mtype;
+ QColor m_color;
+};
+
+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 QSGD3D12MaterialRenderState &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 QSGD3D12MaterialRenderState &state,
+ QSGD3D12PipelineState *pipelineState,
+ ExtraState *extraState,
+ quint8 *constantBuffer) override;
+
+ void setTexture(QSGTexture *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 QSGD3D12MaterialRenderState &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 QSGD3D12MaterialRenderState &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;
+};
+
+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..9b88af995d
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12context.cpp
@@ -0,0 +1,142 @@
+/****************************************************************************
+**
+** 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 "qsgd3d12internalrectanglenode_p.h"
+#include "qsgd3d12internalimagenode_p.h"
+#include "qsgd3d12glyphnode_p.h"
+#include "qsgd3d12layer_p.h"
+#include "qsgd3d12shadereffectnode_p.h"
+#include "qsgd3d12painternode_p.h"
+#include "qsgd3d12publicnodes_p.h"
+#include "qsgd3d12spritenode_p.h"
+#include <QtQuick/qquickwindow.h>
+
+QT_BEGIN_NAMESPACE
+
+QSGRenderContext *QSGD3D12Context::createRenderContext()
+{
+ return new QSGD3D12RenderContext(this);
+}
+
+QSGInternalRectangleNode *QSGD3D12Context::createInternalRectangleNode()
+{
+ return new QSGD3D12InternalRectangleNode;
+}
+
+QSGInternalImageNode *QSGD3D12Context::createInternalImageNode()
+{
+ return new QSGD3D12InternalImageNode;
+}
+
+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);
+}
+
+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
+{
+ QSurfaceFormat format = QSurfaceFormat::defaultFormat();
+
+ if (QQuickWindow::hasDefaultAlphaBuffer())
+ format.setAlphaBufferSize(8);
+
+ return format;
+}
+
+QSGRendererInterface *QSGD3D12Context::rendererInterface(QSGRenderContext *renderContext)
+{
+ return static_cast<QSGD3D12RenderContext *>(renderContext);
+}
+
+QSGRectangleNode *QSGD3D12Context::createRectangleNode()
+{
+ return new QSGD3D12RectangleNode;
+}
+
+QSGImageNode *QSGD3D12Context::createImageNode()
+{
+ return new QSGD3D12ImageNode;
+}
+
+QSGNinePatchNode *QSGD3D12Context::createNinePatchNode()
+{
+ return new QSGD3D12NinePatchNode;
+}
+
+QSGSpriteNode *QSGD3D12Context::createSpriteNode()
+{
+ return new QSGD3D12SpriteNode;
+}
+
+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..70cc606b52
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12context_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 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;
+ QSGInternalRectangleNode *createInternalRectangleNode() override;
+ QSGInternalImageNode *createInternalImageNode() override;
+ QSGPainterNode *createPainterNode(QQuickPaintedItem *item) override;
+ QSGGlyphNode *createGlyphNode(QSGRenderContext *renderContext, bool preferNativeGlyphNode) 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;
+ QSGRectangleNode *createRectangleNode() override;
+ QSGImageNode *createImageNode() override;
+ QSGNinePatchNode *createNinePatchNode() override;
+ QSGSpriteNode *createSpriteNode() 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..a318ce23f7
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp
@@ -0,0 +1,3251 @@
+/****************************************************************************
+**
+** 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 <QLoggingCategory>
+#include <qmath.h>
+#include <qalgorithms.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
+
+#ifdef Q_OS_WINRT
+#include <QtCore/private/qeventdispatcher_winrt_p.h>
+#include <functional>
+#include <windows.ui.xaml.h>
+#include <windows.ui.xaml.media.dxinterop.h>
+#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)
+DECLARE_DEBUG_VAR(descheap)
+DECLARE_DEBUG_VAR(buffer)
+DECLARE_DEBUG_VAR(texture)
+
+// Except for system info on startup.
+Q_LOGGING_CATEGORY(QSG_LOG_INFO_GENERAL, "qt.scenegraph.general")
+
+
+// Any changes to the defaults below must be reflected in adaptations.qdoc as
+// well and proven by qmlbench or similar.
+
+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 = 4096;
+
+static const int MAX_CACHED_ROOTSIG = 16;
+static const int MAX_CACHED_PSO = 64;
+
+static const int GPU_CBVSRVUAV_DESCRIPTORS = 512;
+
+static const DXGI_FORMAT RT_COLOR_FORMAT = DXGI_FORMAT_R8G8B8A8_UNORM;
+
+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]) {
+ uint freePos = qCountTrailingZeroBits(heap.freeMap[bucket]);
+ heap.freeMap[bucket] &= ~(1UL << freePos);
+ if (Q_UNLIKELY(debug_descheap()))
+ qDebug("descriptor handle heap %p type %x reserve in bucket %d index %d", &heap, 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_descheap()))
+ 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_descheap()))
+ qDebug("free descriptor handle heap %p type %x bucket %d index %d", &heap, 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);
+ qCDebug(QSG_LOG_INFO_GENERAL, "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))) {
+ adapter->GetDesc1(&desc);
+ const QString name = QString::fromUtf16((char16_t *) desc.Description);
+ HRESULT hr = D3D12CreateDevice(adapter.Get(), fl, _uuidof(ID3D12Device), nullptr);
+ if (SUCCEEDED(hr)) {
+ qCDebug(QSG_LOG_INFO_GENERAL, "Using requested adapter '%s'", qPrintable(name));
+ *outAdapter = adapter.Detach();
+ return;
+ } else {
+ qWarning("Failed to create device for requested adapter '%s': 0x%x", qPrintable(name), hr);
+ }
+ }
+ }
+
+ 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);
+ qCDebug(QSG_LOG_INFO_GENERAL, "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) {
+ qCDebug(QSG_LOG_INFO_GENERAL, "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))) {
+ qCDebug(QSG_LOG_INFO_GENERAL, "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))) {
+ qCDebug(QSG_LOG_INFO_GENERAL, "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, bool alpha)
+{
+ if (d->isInitialized()) {
+ qWarning("QSGD3D12Engine: Cannot attach active engine to window");
+ return false;
+ }
+
+ d->initialize(window, size, dpr, surfaceFormatSamples, alpha);
+ 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, TextureUploadFlags flags)
+{
+ d->queueTextureUpload(id, QVector<QImage>() << image, QVector<QPoint>() << dstPos, flags);
+}
+
+void QSGD3D12Engine::queueTextureUpload(uint id, const QVector<QImage> &images, const QVector<QPoint> &dstPos,
+ TextureUploadFlags flags)
+{
+ d->queueTextureUpload(id, images, dstPos, flags);
+}
+
+void QSGD3D12Engine::releaseTexture(uint id)
+{
+ d->releaseTexture(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();
+}
+
+void *QSGD3D12Engine::getResource(QQuickWindow *, QSGRendererInterface::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::UnsignedByteType:
+ format = formatMap_ub[tupleSize];
+ if (size)
+ *size = tupleSize;
+ break;
+ case QSGGeometry::FloatType:
+ format = formatMap_f[tupleSize];
+ if (size)
+ *size = sizeof(float) * tupleSize;
+ break;
+
+ case QSGGeometry::UnsignedShortType:
+ format = FmtUnsignedShort;
+ if (size)
+ *size = sizeof(ushort) * tupleSize;
+ break;
+ case QSGGeometry::UnsignedIntType:
+ format = FmtUnsignedInt;
+ if (size)
+ *size = sizeof(uint) * tupleSize;
+ break;
+
+ case QSGGeometry::ByteType:
+ case QSGGeometry::IntType:
+ case QSGGeometry::ShortType:
+ 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;
+
+#ifndef Q_OS_WINRT
+ dcompTarget = nullptr;
+ dcompVisual = nullptr;
+ dcompDevice = nullptr;
+#endif
+
+ 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, bool alpha)
+{
+ if (initialized)
+ return;
+
+ window = w;
+ windowSize = size;
+ windowDpr = dpr;
+ windowSamples = qMax(1, surfaceFormatSamples); // may be -1 or 0, whereas windowSamples is uint and >= 1
+ windowAlpha = alpha;
+
+ 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);
+
+ if (qEnvironmentVariableIsSet("QSG_INFO"))
+ const_cast<QLoggingCategory &>(QSG_LOG_INFO_GENERAL()).setEnabled(QtDebugMsg, true);
+
+ qCDebug(QSG_LOG_INFO_GENERAL, "d3d12 engine init. swap chain buffer count %d, max frames prepared without blocking %d",
+ swapChainBufferCount, frameInFlightCount);
+ if (waitableSwapChainMaxLatency)
+ qCDebug(QSG_LOG_INFO_GENERAL, "Swap chain frame latency waitable object enabled. Frame latency is %d", waitableSwapChainMaxLatency);
+
+ const bool debugLayer = qEnvironmentVariableIntValue("QT_D3D_DEBUG") != 0;
+ if (debugLayer) {
+ qCDebug(QSG_LOG_INFO_GENERAL, "Enabling debug layer");
+#if !defined(Q_OS_WINRT) || !defined(NDEBUG)
+ ComPtr<ID3D12Debug> debugController;
+ if (SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(&debugController))))
+ debugController->EnableDebugLayer();
+#else
+ qCDebug(QSG_LOG_INFO_GENERAL, "Using DebugInterface will not allow certification to pass");
+#endif
+ }
+
+ QSGD3D12DeviceManager *dev = deviceManager();
+ device = dev->ref();
+ dev->registerDeviceLossObserver(this);
+
+ if (debugLayer) {
+ ComPtr<ID3D12InfoQueue> infoQueue;
+ if (SUCCEEDED(device->QueryInterface(IID_PPV_ARGS(&infoQueue)))) {
+ infoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_CORRUPTION, true);
+ infoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_ERROR, true);
+ const bool breakOnWarning = qEnvironmentVariableIntValue("QT_D3D_DEBUG_BREAK_ON_WARNING") != 0;
+ infoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_WARNING, breakOnWarning);
+ D3D12_INFO_QUEUE_FILTER filter = {};
+ D3D12_MESSAGE_ID suppressedMessages[] = {
+ // When using a render target other than the default one we
+ // have no way to know the custom clear color, if there is one.
+ D3D12_MESSAGE_ID_CLEARRENDERTARGETVIEW_MISMATCHINGCLEARVALUE
+ };
+ filter.DenyList.NumIDs = _countof(suppressedMessages);
+ filter.DenyList.pIDList = suppressedMessages;
+ // setting the filter would enable Info messages which we don't need
+ D3D12_MESSAGE_SEVERITY infoSev = D3D12_MESSAGE_SEVERITY_INFO;
+ filter.DenyList.NumSeverities = 1;
+ filter.DenyList.pSeverityList = &infoSev;
+ infoQueue->PushStorageFilter(&filter);
+ }
+ }
+
+ 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;
+ }
+
+#ifndef Q_OS_WINRT
+ HWND hwnd = reinterpret_cast<HWND>(w);
+
+ if (windowAlpha) {
+ // Go through DirectComposition for semi-transparent windows since the
+ // traditional approaches won't fly with flip model swapchains.
+ HRESULT hr = DCompositionCreateDevice(nullptr, IID_PPV_ARGS(&dcompDevice));
+ if (SUCCEEDED(hr)) {
+ hr = dcompDevice->CreateTargetForHwnd(hwnd, true, &dcompTarget);
+ if (SUCCEEDED(hr)) {
+ hr = dcompDevice->CreateVisual(&dcompVisual);
+ if (FAILED(hr)) {
+ qWarning("Failed to create DirectComposition visual: 0x%x", hr);
+ windowAlpha = false;
+ }
+ } else {
+ qWarning("Failed to create DirectComposition target: 0x%x", hr);
+ windowAlpha = false;
+ }
+ } else {
+ qWarning("Failed to create DirectComposition device: 0x%x", hr);
+ windowAlpha = false;
+ }
+ }
+
+ if (windowAlpha) {
+ DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {};
+ swapChainDesc.Width = windowSize.width() * windowDpr;
+ swapChainDesc.Height = windowSize.height() * windowDpr;
+ swapChainDesc.Format = RT_COLOR_FORMAT;
+ swapChainDesc.SampleDesc.Count = 1;
+ swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
+ swapChainDesc.BufferCount = swapChainBufferCount;
+ swapChainDesc.Scaling = DXGI_SCALING_STRETCH;
+ swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
+ swapChainDesc.AlphaMode = DXGI_ALPHA_MODE_PREMULTIPLIED;
+ if (waitableSwapChainMaxLatency)
+ swapChainDesc.Flags = DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT;
+
+ ComPtr<IDXGISwapChain1> baseSwapChain;
+ HRESULT hr = dev->dxgi()->CreateSwapChainForComposition(commandQueue.Get(), &swapChainDesc, nullptr, &baseSwapChain);
+ if (SUCCEEDED(hr)) {
+ if (SUCCEEDED(baseSwapChain.As(&swapChain))) {
+ hr = dcompVisual->SetContent(swapChain.Get());
+ if (SUCCEEDED(hr)) {
+ hr = dcompTarget->SetRoot(dcompVisual.Get());
+ if (FAILED(hr)) {
+ qWarning("SetRoot failed for DirectComposition target: 0x%x", hr);
+ windowAlpha = false;
+ }
+ } else {
+ qWarning("SetContent failed for DirectComposition visual: 0x%x", hr);
+ windowAlpha = false;
+ }
+ } else {
+ qWarning("Failed to cast swap chain");
+ windowAlpha = false;
+ }
+ } else {
+ qWarning("Failed to create swap chain for composition: 0x%x", hr);
+ windowAlpha = false;
+ }
+ }
+
+ if (!windowAlpha) {
+ DXGI_SWAP_CHAIN_DESC swapChainDesc = {};
+ swapChainDesc.BufferCount = swapChainBufferCount;
+ swapChainDesc.BufferDesc.Width = windowSize.width() * windowDpr;
+ swapChainDesc.BufferDesc.Height = windowSize.height() * windowDpr;
+ swapChainDesc.BufferDesc.Format = RT_COLOR_FORMAT;
+ 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);
+#else
+ DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {};
+ swapChainDesc.Width = windowSize.width() * windowDpr;
+ swapChainDesc.Height = windowSize.height() * windowDpr;
+ swapChainDesc.Format = RT_COLOR_FORMAT;
+ swapChainDesc.SampleDesc.Count = 1;
+ swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
+ swapChainDesc.BufferCount = swapChainBufferCount;
+ swapChainDesc.Scaling = DXGI_SCALING_STRETCH;
+ swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
+ swapChainDesc.AlphaMode = DXGI_ALPHA_MODE_PREMULTIPLIED;
+ if (waitableSwapChainMaxLatency)
+ swapChainDesc.Flags = DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT;
+
+ ComPtr<IDXGISwapChain1> baseSwapChain;
+ HRESULT hr = dev->dxgi()->CreateSwapChainForComposition(commandQueue.Get(), &swapChainDesc, nullptr, &baseSwapChain);
+ if (FAILED(hr)) {
+ qWarning("Failed to create swap chain for composition: 0x%x", hr);
+ return;
+ }
+ if (FAILED(baseSwapChain.As(&swapChain))) {
+ qWarning("Failed to cast swap chain");
+ return;
+ }
+
+ // The winrt platform plugin returns an ISwapChainPanel* from winId().
+ ComPtr<ABI::Windows::UI::Xaml::Controls::ISwapChainPanel> swapChainPanel
+ = reinterpret_cast<ABI::Windows::UI::Xaml::Controls::ISwapChainPanel *>(window);
+ ComPtr<ISwapChainPanelNative> swapChainPanelNative;
+ if (FAILED(swapChainPanel.As(&swapChainPanelNative))) {
+ qWarning("Failed to cast swap chain panel to native");
+ return;
+ }
+ hr = QEventDispatcherWinRT::runOnXamlThread([this, &swapChainPanelNative]() {
+ return swapChainPanelNative->SetSwapChain(swapChain.Get());
+ });
+ if (FAILED(hr)) {
+ qWarning("Failed to set swap chain on panel: 0x%x", hr);
+ return;
+ }
+#endif
+
+ 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 = RT_COLOR_FORMAT;
+ 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 = RT_COLOR_FORMAT;
+ 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);
+ // Not optimal if the user called setClearColor, but there's so
+ // much we can do. The debug layer warning is suppressed so we're good to go.
+ const QColor cc(Qt::white);
+ 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, RT_COLOR_FORMAT,
+ 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, RT_COLOR_FORMAT);
+
+ 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_buffer()))
+ 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_buffer()))
+ 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, windowAlpha);
+}
+
+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_buffer()))
+ 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_buffer()))
+ 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_texture()))
+ 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_texture()))
+ qDebug("Cleaned staging data for texture %u", id);
+ }
+ }
+ pfd.pendingTextureUploads.clear();
+ if (!pfd.pendingTextureMipMap.isEmpty()) {
+ if (Q_UNLIKELY(debug_texture()))
+ 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_texture()))
+ 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.activeTextureCount = 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_buffer()))
+ 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_texture()))
+ 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_texture()))
+ 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 one SRV per texture (must be a table since root descriptor SRVs cannot be textures) - 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;
+
+ D3D12_DESCRIPTOR_RANGE tvDescRange;
+ if (pipelineState.shaders.rootSig.textureViewCount > 0) {
+ rootParams[rootParamCount].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
+ rootParams[rootParamCount].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
+ rootParams[rootParamCount].DescriptorTable.NumDescriptorRanges = 1;
+ tvDescRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
+ tvDescRange.NumDescriptors = pipelineState.shaders.rootSig.textureViewCount;
+ tvDescRange.BaseShaderRegister = 0; // t0, t1, ...
+ tvDescRange.RegisterSpace = 0;
+ tvDescRange.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
+ rootParams[rootParamCount].DescriptorTable.pDescriptorRanges = &tvDescRange;
+ ++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.textureViewCount;
+ D3D12_STATIC_SAMPLER_DESC staticSamplers[8];
+ int sdIdx = 0;
+ Q_ASSERT(pipelineState.shaders.rootSig.textureViewCount <= _countof(staticSamplers));
+ for (int i = 0; i < pipelineState.shaders.rootSig.textureViewCount; ++i) {
+ const QSGD3D12TextureView &tv(pipelineState.shaders.rootSig.textureViews[i]);
+ 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))) {
+ QByteArray msg(static_cast<const char *>(error->GetBufferPointer()), error->GetBufferSize());
+ qWarning("Failed to serialize root signature: %s", qPrintable(msg));
+ 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[QSGD3D12_MAX_INPUT_ELEMENTS];
+ int ieIdx = 0;
+ for (int i = 0; i < pipelineState.inputElementCount; ++i) {
+ const QSGD3D12InputElement &ie(pipelineState.inputElements[i]);
+ 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] = RT_COLOR_FORMAT;
+ 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.textureViewCount > 0)
+ 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.activeTextureCount == tframeData.pipelineState.shaders.rootSig.textureViewCount);
+ if (tframeData.activeTextureCount > 0) {
+ if (!skip) {
+ ensureGPUDescriptorHeap(tframeData.activeTextureCount);
+ 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 (int i = 0; i < tframeData.activeTextureCount; ++i) {
+ const TransientFrameData::ActiveTexture &t(tframeData.activeTextures[i]);
+ 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.activeTextureCount;
+ }
+ tframeData.activeTextureCount = 0;
+ }
+
+ // 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_descheap()))
+ 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;
+ }
+
+#ifndef Q_OS_WINRT
+ if (dcompDevice)
+ dcompDevice->Commit();
+#endif
+
+ ++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_buffer()))
+ 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_buffer()))
+ 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, bool force32bit,
+ 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:
+ if (!force32bit) {
+ f = DXGI_FORMAT_R8_UNORM;
+ bpp = 1;
+ } else {
+ convFormat = QImage::Format_RGBA8888;
+ }
+ 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(),
+ createFlags.testFlag(QSGD3D12Engine::TextureAlways32Bit),
+ 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_texture()))
+ 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_texture()))
+ 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_texture()))
+ 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,
+ QSGD3D12Engine::TextureUploadFlags flags)
+{
+ 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_texture()))
+ 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(),
+ flags.testFlag(QSGD3D12Engine::TextureUploadAlways32Bit),
+ 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_texture()))
+ 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(),
+ flags.testFlag(QSGD3D12Engine::TextureUploadAlways32Bit),
+ &convFormat, &bytesPerPixel);
+ if (Q_UNLIKELY(debug_texture() && 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_texture()))
+ 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));
+}
+
+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());
+
+ // Within one frame the order of calling this function determines the
+ // texture register (0, 1, ...) so fill up activeTextures accordingly.
+ tframeData.activeTextures[tframeData.activeTextureCount++]
+ = 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 = RT_COLOR_FORMAT;
+ 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[tframeData.activeTextureCount++] =
+ 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..b30994fe0d
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12engine_p.h
@@ -0,0 +1,394 @@
+/****************************************************************************
+**
+** 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;
+}
+
+const int QSGD3D12_MAX_TEXTURE_VIEWS = 8;
+
+struct QSGD3D12RootSignature
+{
+ int textureViewCount = 0;
+ QSGD3D12TextureView textureViews[QSGD3D12_MAX_TEXTURE_VIEWS];
+
+ bool operator==(const QSGD3D12RootSignature &other) const {
+ if (textureViewCount != other.textureViewCount)
+ return false;
+ for (int i = 0; i < textureViewCount; ++i)
+ if (!(textureViews[i] == other.textureViews[i]))
+ return false;
+ return true;
+ }
+};
+
+inline uint qHash(const QSGD3D12RootSignature &key, uint seed = 0)
+{
+ return key.textureViewCount + (key.textureViewCount > 0 ? qHash(key.textureViews[0], seed) : 0);
+}
+
+// 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);
+}
+
+const int QSGD3D12_MAX_INPUT_ELEMENTS = 8;
+
+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;
+
+ int inputElementCount = 0;
+ QSGD3D12InputElement inputElements[QSGD3D12_MAX_INPUT_ELEMENTS];
+
+ 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 {
+ bool eq = shaders == other.shaders
+ && inputElementCount == other.inputElementCount
+ && 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;
+ if (eq) {
+ for (int i = 0; i < inputElementCount; ++i) {
+ if (!(inputElements[i] == other.inputElements[i])) {
+ eq = false;
+ break;
+ }
+ }
+ }
+ return eq;
+ }
+};
+
+inline uint qHash(const QSGD3D12PipelineState &key, uint seed = 0)
+{
+ return qHash(key.shaders, seed) + key.inputElementCount
+ + key.cullMode + key.frontCCW
+ + key.colorWrite + key.blend
+ + key.depthEnable + key.depthWrite
+ + key.stencilEnable
+ + key.topologyType;
+}
+
+class QSGD3D12Engine
+{
+public:
+ QSGD3D12Engine();
+ ~QSGD3D12Engine();
+
+ bool attachToWindow(WId window, const QSize &size, float dpr, int surfaceFormatSamples, bool alpha);
+ 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 = 0x01,
+ TextureWithMipMaps = 0x02,
+ TextureAlways32Bit = 0x04
+ };
+ Q_DECLARE_FLAGS(TextureCreateFlags, TextureCreateFlag)
+
+ enum TextureUploadFlag {
+ TextureUploadAlways32Bit = 0x01
+ };
+ Q_DECLARE_FLAGS(TextureUploadFlags, TextureUploadFlag)
+
+ 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(), TextureUploadFlags flags = 0);
+ void queueTextureUpload(uint id, const QVector<QImage> &images, const QVector<QPoint> &dstPos, TextureUploadFlags flags = 0);
+ void releaseTexture(uint id);
+ 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();
+
+ void *getResource(QQuickWindow *window, QSGRendererInterface::Resource resource) const;
+
+private:
+ QSGD3D12EnginePrivate *d;
+ Q_DISABLE_COPY(QSGD3D12Engine)
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QSGD3D12Engine::ClearFlags)
+Q_DECLARE_OPERATORS_FOR_FLAGS(QSGD3D12Engine::TextureCreateFlags)
+Q_DECLARE_OPERATORS_FOR_FLAGS(QSGD3D12Engine::TextureUploadFlags)
+
+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..1048ed63e7
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12engine_p_p.h
@@ -0,0 +1,454 @@
+/****************************************************************************
+**
+** 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 <dcomp.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 alpha);
+ 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,
+ QSGD3D12Engine::TextureUploadFlags flags);
+ void releaseTexture(uint id);
+ 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;
+ bool windowAlpha;
+ 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() { }
+ };
+ int activeTextureCount;
+ ActiveTexture activeTextures[QSGD3D12_MAX_TEXTURE_VIEWS];
+ 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;
+
+#ifndef Q_OS_WINRT
+ ComPtr<IDCompositionDevice> dcompDevice;
+ ComPtr<IDCompositionTarget> dcompTarget;
+ ComPtr<IDCompositionVisual> dcompVisual;
+#endif
+};
+
+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..915917c3d5
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12glyphcache.cpp
@@ -0,0 +1,197 @@
+/****************************************************************************
+**
+** 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
+
+// Convert A8 glyphs to 32-bit in the engine. This is here to work around
+// QTBUG-55330 for AMD cards.
+// If removing, textmask.hlsl must be adjusted! (.a -> .r)
+#define ALWAYS_32BIT
+
+// 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
+#ifdef ALWAYS_32BIT
+ | QSGD3D12Engine::TextureAlways32Bit
+#endif
+ );
+}
+
+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
+#ifdef ALWAYS_32BIT
+ , QSGD3D12Engine::TextureUploadAlways32Bit
+#endif
+ );
+
+ // 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/qsgd3d12internalimagenode.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12internalimagenode.cpp
new file mode 100644
index 0000000000..aa163cacbf
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12internalimagenode.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 "qsgd3d12internalimagenode_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QSGD3D12InternalImageNode::QSGD3D12InternalImageNode()
+{
+ setMaterial(&m_material);
+}
+
+void QSGD3D12InternalImageNode::setFiltering(QSGTexture::Filtering filtering)
+{
+ if (m_material.filtering() == filtering)
+ return;
+
+ m_material.setFiltering(filtering);
+ m_smoothMaterial.setFiltering(filtering);
+ markDirty(DirtyMaterial);
+}
+
+void QSGD3D12InternalImageNode::setMipmapFiltering(QSGTexture::Filtering filtering)
+{
+ if (m_material.mipmapFiltering() == filtering)
+ return;
+
+ m_material.setMipmapFiltering(filtering);
+ m_smoothMaterial.setMipmapFiltering(filtering);
+ markDirty(DirtyMaterial);
+}
+
+void QSGD3D12InternalImageNode::setVerticalWrapMode(QSGTexture::WrapMode wrapMode)
+{
+ if (m_material.verticalWrapMode() == wrapMode)
+ return;
+
+ m_material.setVerticalWrapMode(wrapMode);
+ m_smoothMaterial.setVerticalWrapMode(wrapMode);
+ markDirty(DirtyMaterial);
+}
+
+void QSGD3D12InternalImageNode::setHorizontalWrapMode(QSGTexture::WrapMode wrapMode)
+{
+ if (m_material.horizontalWrapMode() == wrapMode)
+ return;
+
+ m_material.setHorizontalWrapMode(wrapMode);
+ m_smoothMaterial.setHorizontalWrapMode(wrapMode);
+ markDirty(DirtyMaterial);
+}
+
+void QSGD3D12InternalImageNode::updateMaterialAntialiasing()
+{
+ if (m_antialiasing)
+ setMaterial(&m_smoothMaterial);
+ else
+ setMaterial(&m_material);
+}
+
+void QSGD3D12InternalImageNode::setMaterialTexture(QSGTexture *texture)
+{
+ m_material.setTexture(texture);
+ m_smoothMaterial.setTexture(texture);
+}
+
+QSGTexture *QSGD3D12InternalImageNode::materialTexture() const
+{
+ return m_material.texture();
+}
+
+bool QSGD3D12InternalImageNode::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 QSGD3D12InternalImageNode::supportsWrap(const QSize &) const
+{
+ return true;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12internalimagenode_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12internalimagenode_p.h
new file mode 100644
index 0000000000..26284740ee
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12internalimagenode_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 QSGD3D12INTERNALIMAGENODE_P_H
+#define QSGD3D12INTERNALIMAGENODE_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/qsgbasicinternalimagenode_p.h>
+#include "qsgd3d12builtinmaterials_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QSGD3D12InternalImageNode : public QSGBasicInternalImageNode
+{
+public:
+ QSGD3D12InternalImageNode();
+
+ 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 // QSGD3D12INTERNALIMAGENODE_P_H
diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12internalrectanglenode.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12internalrectanglenode.cpp
new file mode 100644
index 0000000000..2d9c5b55d1
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12internalrectanglenode.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 "qsgd3d12internalrectanglenode_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QSGD3D12InternalRectangleNode::QSGD3D12InternalRectangleNode()
+{
+ setMaterial(&m_material);
+}
+
+void QSGD3D12InternalRectangleNode::updateMaterialAntialiasing()
+{
+ if (m_antialiasing)
+ setMaterial(&m_smoothMaterial);
+ else
+ setMaterial(&m_material);
+}
+
+void QSGD3D12InternalRectangleNode::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/qsgd3d12internalrectanglenode_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12internalrectanglenode_p.h
new file mode 100644
index 0000000000..2fc3c69285
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12internalrectanglenode_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 QSGD3D12INTERNALRECTANGLENODE_P_H
+#define QSGD3D12INTERNALRECTANGLENODE_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/qsgbasicinternalrectanglenode_p.h>
+#include "qsgd3d12builtinmaterials_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QSGD3D12InternalRectangleNode : public QSGBasicInternalRectangleNode
+{
+public:
+ QSGD3D12InternalRectangleNode();
+
+private:
+ void updateMaterialAntialiasing() override;
+ void updateMaterialBlending(QSGNode::DirtyState *state) override;
+
+ QSGD3D12VertexColorMaterial m_material;
+ QSGD3D12SmoothColorMaterial m_smoothMaterial;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGD3D12INTERNALRECTANGLENODE_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/qml/jsruntime/qv4debugging.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12material.cpp
index 9fcba64038..1b638106ee 100644
--- a/src/qml/jsruntime/qv4debugging.cpp
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12material.cpp
@@ -3,7 +3,7 @@
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
-** This file is part of the QtQml module of the Qt Toolkit.
+** This file is part of the QtQuick module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
@@ -37,24 +37,13 @@
**
****************************************************************************/
-#include "qv4debugging_p.h"
-#include "qv4object_p.h"
-#include "qv4functionobject_p.h"
-#include "qv4function_p.h"
-#include "qv4instr_moth_p.h"
-#include "qv4runtime_p.h"
-#include "qv4script_p.h"
-#include "qv4identifier_p.h"
-#include "qv4string_p.h"
-#include "qv4objectiterator_p.h"
-
-#include <iostream>
-#include <algorithm>
-
-#include <QtCore/QJsonArray>
-#include <QtCore/QJsonDocument>
-#include <QtCore/QJsonValue>
+#include "qsgd3d12material_p.h"
QT_BEGIN_NAMESPACE
+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..65d53600c3
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12material_p.h
@@ -0,0 +1,96 @@
+/****************************************************************************
+**
+** 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.
+
+typedef QSGMaterialShader::RenderState QSGD3D12MaterialRenderState;
+
+class QSGD3D12Material : public QSGMaterial
+{
+public:
+ struct ExtraState {
+ QVector4D blendFactor;
+ };
+
+ enum UpdateResult {
+ UpdatedShaders = 0x0001,
+ UpdatedConstantBuffer = 0x0002,
+ UpdatedBlendFactor = 0x0004
+ };
+ Q_DECLARE_FLAGS(UpdateResults, UpdateResult)
+
+ virtual int constantBufferSize() const = 0;
+ virtual void preparePipeline(QSGD3D12PipelineState *pipelineState) = 0;
+ virtual UpdateResults updatePipeline(const QSGD3D12MaterialRenderState &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::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..b22c42f2e5
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12painternode.cpp
@@ -0,0 +1,255 @@
+/****************************************************************************
+**
+** 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)
+{
+ setGeometry(&m_geometry);
+ m_material.setTexture(m_texture);
+ setMaterial(&m_material);
+}
+
+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..7f4842b3a6
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12painternode_p.h
@@ -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$
+**
+****************************************************************************/
+
+#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;
+ bool hasAlphaChannel() const override { return true; }
+
+ 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/qsgd3d12publicnodes.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12publicnodes.cpp
new file mode 100644
index 0000000000..783caa280f
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12publicnodes.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 "qsgd3d12publicnodes_p.h"
+
+// for rebuildGeometry
+#include <private/qsgdefaultninepatchnode_p.h>
+#include <private/qsgdefaultimagenode_p.h>
+
+QT_BEGIN_NAMESPACE
+
+QSGD3D12RectangleNode::QSGD3D12RectangleNode()
+ : m_geometry(QSGGeometry::defaultAttributes_Point2D(), 4)
+{
+ QSGGeometry::updateRectGeometry(&m_geometry, QRectF());
+ setMaterial(&m_material);
+ setGeometry(&m_geometry);
+#ifdef QSG_RUNTIME_DESCRIPTION
+ qsgnode_set_description(this, QLatin1String("rectangle"));
+#endif
+}
+
+void QSGD3D12RectangleNode::setRect(const QRectF &rect)
+{
+ QSGGeometry::updateRectGeometry(&m_geometry, rect);
+ markDirty(QSGNode::DirtyGeometry);
+}
+
+QRectF QSGD3D12RectangleNode::rect() const
+{
+ const QSGGeometry::Point2D *pts = m_geometry.vertexDataAsPoint2D();
+ return QRectF(pts[0].x,
+ pts[0].y,
+ pts[3].x - pts[0].x,
+ pts[3].y - pts[0].y);
+}
+
+void QSGD3D12RectangleNode::setColor(const QColor &color)
+{
+ if (color != m_material.color()) {
+ m_material.setColor(color);
+ markDirty(QSGNode::DirtyMaterial);
+ }
+}
+
+QColor QSGD3D12RectangleNode::color() const
+{
+ return m_material.color();
+}
+
+QSGD3D12ImageNode::QSGD3D12ImageNode()
+ : m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4),
+ m_texCoordMode(QSGD3D12ImageNode::NoTransform),
+ m_isAtlasTexture(false),
+ m_ownsTexture(false)
+{
+ setGeometry(&m_geometry);
+ setMaterial(&m_material);
+ m_material.setMipmapFiltering(QSGTexture::None);
+#ifdef QSG_RUNTIME_DESCRIPTION
+ qsgnode_set_description(this, QLatin1String("image"));
+#endif
+}
+
+QSGD3D12ImageNode::~QSGD3D12ImageNode()
+{
+ if (m_ownsTexture)
+ delete m_material.texture();
+}
+
+void QSGD3D12ImageNode::setFiltering(QSGTexture::Filtering filtering)
+{
+ if (m_material.filtering() == filtering)
+ return;
+
+ m_material.setFiltering(filtering);
+ markDirty(DirtyMaterial);
+}
+
+QSGTexture::Filtering QSGD3D12ImageNode::filtering() const
+{
+ return m_material.filtering();
+}
+
+void QSGD3D12ImageNode::setMipmapFiltering(QSGTexture::Filtering filtering)
+{
+ if (m_material.mipmapFiltering() == filtering)
+ return;
+
+ m_material.setMipmapFiltering(filtering);
+ markDirty(DirtyMaterial);
+}
+
+QSGTexture::Filtering QSGD3D12ImageNode::mipmapFiltering() const
+{
+ return m_material.mipmapFiltering();
+}
+
+void QSGD3D12ImageNode::setRect(const QRectF &r)
+{
+ if (m_rect == r)
+ return;
+
+ m_rect = r;
+ QSGDefaultImageNode::rebuildGeometry(&m_geometry, texture(), m_rect, m_sourceRect, m_texCoordMode);
+ markDirty(DirtyGeometry);
+}
+
+QRectF QSGD3D12ImageNode::rect() const
+{
+ return m_rect;
+}
+
+void QSGD3D12ImageNode::setSourceRect(const QRectF &r)
+{
+ if (m_sourceRect == r)
+ return;
+
+ m_sourceRect = r;
+ QSGDefaultImageNode::rebuildGeometry(&m_geometry, texture(), m_rect, m_sourceRect, m_texCoordMode);
+ markDirty(DirtyGeometry);
+}
+
+QRectF QSGD3D12ImageNode::sourceRect() const
+{
+ return m_sourceRect;
+}
+
+void QSGD3D12ImageNode::setTexture(QSGTexture *texture)
+{
+ Q_ASSERT(texture);
+
+ if (m_ownsTexture)
+ delete m_material.texture();
+
+ m_material.setTexture(texture);
+ QSGDefaultImageNode::rebuildGeometry(&m_geometry, texture, m_rect, m_sourceRect, m_texCoordMode);
+
+ DirtyState dirty = DirtyMaterial;
+ const bool wasAtlas = m_isAtlasTexture;
+ m_isAtlasTexture = texture->isAtlasTexture();
+ if (wasAtlas || m_isAtlasTexture)
+ dirty |= DirtyGeometry;
+
+ markDirty(dirty);
+}
+
+QSGTexture *QSGD3D12ImageNode::texture() const
+{
+ return m_material.texture();
+}
+
+void QSGD3D12ImageNode::setTextureCoordinatesTransform(TextureCoordinatesTransformMode mode)
+{
+ if (m_texCoordMode == mode)
+ return;
+
+ m_texCoordMode = mode;
+ QSGDefaultImageNode::rebuildGeometry(&m_geometry, texture(), m_rect, m_sourceRect, m_texCoordMode);
+ markDirty(DirtyMaterial);
+}
+
+QSGD3D12ImageNode::TextureCoordinatesTransformMode QSGD3D12ImageNode::textureCoordinatesTransform() const
+{
+ return m_texCoordMode;
+}
+
+void QSGD3D12ImageNode::setOwnsTexture(bool owns)
+{
+ m_ownsTexture = owns;
+}
+
+bool QSGD3D12ImageNode::ownsTexture() const
+{
+ return m_ownsTexture;
+}
+
+QSGD3D12NinePatchNode::QSGD3D12NinePatchNode()
+ : m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4)
+{
+ m_geometry.setDrawingMode(QSGGeometry::DrawTriangleStrip);
+ setGeometry(&m_geometry);
+ setMaterial(&m_material);
+}
+
+QSGD3D12NinePatchNode::~QSGD3D12NinePatchNode()
+{
+ delete m_material.texture();
+}
+
+void QSGD3D12NinePatchNode::setTexture(QSGTexture *texture)
+{
+ delete m_material.texture();
+ m_material.setTexture(texture);
+}
+
+void QSGD3D12NinePatchNode::setBounds(const QRectF &bounds)
+{
+ m_bounds = bounds;
+}
+
+void QSGD3D12NinePatchNode::setDevicePixelRatio(qreal ratio)
+{
+ m_devicePixelRatio = ratio;
+}
+
+void QSGD3D12NinePatchNode::setPadding(qreal left, qreal top, qreal right, qreal bottom)
+{
+ m_padding = QVector4D(left, top, right, bottom);
+}
+
+void QSGD3D12NinePatchNode::update()
+{
+ QSGDefaultNinePatchNode::rebuildGeometry(m_material.texture(), &m_geometry, m_padding, m_bounds, m_devicePixelRatio);
+ markDirty(QSGNode::DirtyGeometry | QSGNode::DirtyMaterial);
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12publicnodes_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12publicnodes_p.h
new file mode 100644
index 0000000000..6150083aaf
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12publicnodes_p.h
@@ -0,0 +1,136 @@
+/****************************************************************************
+**
+** 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 QSGD3D12PUBLICNODES_P_H
+#define QSGD3D12PUBLICNODES_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/qsgrectanglenode.h>
+#include <QtQuick/qsgimagenode.h>
+#include <QtQuick/qsgninepatchnode.h>
+#include "qsgd3d12builtinmaterials_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QSGD3D12RectangleNode : public QSGRectangleNode
+{
+public:
+ QSGD3D12RectangleNode();
+
+ void setRect(const QRectF &rect) override;
+ QRectF rect() const override;
+
+ void setColor(const QColor &color) override;
+ QColor color() const override;
+
+private:
+ QSGGeometry m_geometry;
+ QSGD3D12FlatColorMaterial m_material;
+};
+
+class QSGD3D12ImageNode : public QSGImageNode
+{
+public:
+ QSGD3D12ImageNode();
+ ~QSGD3D12ImageNode();
+
+ void setRect(const QRectF &rect) override;
+ QRectF rect() const override;
+
+ void setSourceRect(const QRectF &r) override;
+ QRectF sourceRect() const override;
+
+ void setTexture(QSGTexture *texture) override;
+ QSGTexture *texture() const override;
+
+ void setFiltering(QSGTexture::Filtering filtering) override;
+ QSGTexture::Filtering filtering() const override;
+
+ void setMipmapFiltering(QSGTexture::Filtering filtering) override;
+ QSGTexture::Filtering mipmapFiltering() const override;
+
+ void setTextureCoordinatesTransform(TextureCoordinatesTransformMode mode) override;
+ TextureCoordinatesTransformMode textureCoordinatesTransform() const override;
+
+ void setOwnsTexture(bool owns) override;
+ bool ownsTexture() const override;
+
+private:
+ QSGGeometry m_geometry;
+ QSGD3D12TextureMaterial m_material;
+ QRectF m_rect;
+ QRectF m_sourceRect;
+ TextureCoordinatesTransformMode m_texCoordMode;
+ uint m_isAtlasTexture : 1;
+ uint m_ownsTexture : 1;
+};
+
+class QSGD3D12NinePatchNode : public QSGNinePatchNode
+{
+public:
+ QSGD3D12NinePatchNode();
+ ~QSGD3D12NinePatchNode();
+
+ 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;
+
+private:
+ QSGGeometry m_geometry;
+ QSGD3D12TextureMaterial m_material;
+ QRectF m_bounds;
+ qreal m_devicePixelRatio;
+ QVector4D m_padding;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12rendercontext.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12rendercontext.cpp
new file mode 100644
index 0000000000..4ee4656e63
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12rendercontext.cpp
@@ -0,0 +1,169 @@
+/****************************************************************************
+**
+** 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::initialize(void *)
+{
+ if (m_initialized)
+ return;
+
+ m_initialized = true;
+ emit initialized();
+}
+
+void QSGD3D12RenderContext::invalidate()
+{
+ if (!m_initialized)
+ return;
+
+ m_initialized = false;
+
+ 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);
+}
+
+int QSGD3D12RenderContext::maxTextureSize() const
+{
+ return 16384; // D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION
+}
+
+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)
+ initialize(nullptr);
+}
+
+QSGRendererInterface::GraphicsApi QSGD3D12RenderContext::graphicsApi() const
+{
+ return Direct3D12;
+}
+
+void *QSGD3D12RenderContext::getResource(QQuickWindow *window, Resource resource) const
+{
+ if (!m_engine) {
+ qWarning("getResource: No D3D12 engine available yet (window not exposed?)");
+ return nullptr;
+ }
+ // window can be ignored since the rendercontext and engine are both per window
+ return m_engine->getResource(window, resource);
+}
+
+QSGRendererInterface::ShaderType QSGD3D12RenderContext::shaderType() const
+{
+ return HLSL;
+}
+
+QSGRendererInterface::ShaderCompilationTypes QSGD3D12RenderContext::shaderCompilationType() const
+{
+ return RuntimeCompilation | OfflineCompilation;
+}
+
+QSGRendererInterface::ShaderSourceTypes QSGD3D12RenderContext::shaderSourceType() const
+{
+ return ShaderSourceString | ShaderSourceFile | ShaderByteCode;
+}
+
+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..35aca100f4
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12rendercontext_p.h
@@ -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$
+**
+****************************************************************************/
+
+#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>
+#include <qsgrendererinterface.h>
+
+QT_BEGIN_NAMESPACE
+
+class QSGD3D12Engine;
+
+class QSGD3D12RenderContext : public QSGRenderContext, public QSGRendererInterface
+{
+public:
+ QSGD3D12RenderContext(QSGContext *ctx);
+ bool isValid() const override;
+ void initialize(void *context) override;
+ void invalidate() override;
+ void renderNextFrame(QSGRenderer *renderer, uint fbo) override;
+ QSGTexture *createTexture(const QImage &image, uint flags) const override;
+ QSGRenderer *createRenderer() override;
+ int maxTextureSize() const override;
+
+ void setEngine(QSGD3D12Engine *engine);
+ QSGD3D12Engine *engine() { return m_engine; }
+
+ // QSGRendererInterface
+ GraphicsApi graphicsApi() const override;
+ void *getResource(QQuickWindow *window, Resource resource) const override;
+ ShaderType shaderType() const override;
+ ShaderCompilationTypes shaderCompilationType() const override;
+ ShaderSourceTypes shaderSourceType() const override;
+
+private:
+ QSGD3D12Engine *m_engine = nullptr;
+ bool m_initialized = 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..c0f111ee83
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12renderer.cpp
@@ -0,0 +1,785 @@
+/****************************************************************************
+**
+** 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);
+}
+
+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] |= QSGD3D12MaterialRenderState::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->setRendererMatrix(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] |= QSGD3D12MaterialRenderState::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->setRendererClipList(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 QRegion *clipRegion() const override { return nullptr; }
+
+ 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);
+ }
+
+ QSGD3D12MaterialRenderState::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 = QSGD3D12MaterialRenderState::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 |= QSGD3D12MaterialRenderState::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(state(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->inputElementCount = 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 };
+ const int semantic = attrs[i].attributeType;
+ Q_ASSERT(semantic >= 1 && semantic < _countof(semanticNames));
+ const int tupleSize = attrs[i].tupleSize;
+ ie.semanticName = semanticNames[semantic];
+ ie.semanticIndex = semanticIndices[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::FloatType);
+
+ 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..df30a49f0d
--- /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 *, QSGD3D12MaterialRenderState::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..c53a1fa6c1
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12renderloop.cpp
@@ -0,0 +1,534 @@
+/****************************************************************************
+**
+** 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/qquickwindow_p.h>
+#include <private/qquickprofiler_p.h>
+#include <private/qquickanimatorcontroller_p.h>
+#include <QElapsedTimer>
+#include <QGuiApplication>
+#include <QScreen>
+
+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)
+
+
+// This render loop operates on the gui (main) thread.
+// Conceptually it matches the OpenGL 'windows' render loop.
+
+static inline int qsgrl_animation_interval()
+{
+ const qreal refreshRate = QGuiApplication::primaryScreen() ? QGuiApplication::primaryScreen()->refreshRate() : 0;
+ return refreshRate < 1 ? 16 : int(1000 / refreshRate);
+}
+
+QSGD3D12RenderLoop::QSGD3D12RenderLoop()
+{
+ if (Q_UNLIKELY(debug_loop()))
+ qDebug("new d3d12 render loop");
+
+ sg = new QSGD3D12Context;
+
+ m_anims = sg->createAnimationDriver(this);
+ connect(m_anims, &QAnimationDriver::started, this, &QSGD3D12RenderLoop::onAnimationStarted);
+ connect(m_anims, &QAnimationDriver::stopped, this, &QSGD3D12RenderLoop::onAnimationStopped);
+ m_anims->install();
+
+ m_vsyncDelta = qsgrl_animation_interval();
+}
+
+QSGD3D12RenderLoop::~QSGD3D12RenderLoop()
+{
+ 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;
+}
+
+void QSGD3D12RenderLoop::resize(QQuickWindow *window)
+{
+ if (!m_windows.contains(window) || window->size().isEmpty())
+ return;
+
+ if (Q_UNLIKELY(debug_loop()))
+ qDebug() << "resize" << window;
+
+ const WindowData &data(m_windows[window]);
+
+ if (!data.exposed)
+ return;
+
+ if (data.engine)
+ data.engine->setWindowSize(window->size(), window->effectiveDevicePixelRatio());
+}
+
+void QSGD3D12RenderLoop::windowDestroyed(QQuickWindow *window)
+{
+ if (Q_UNLIKELY(debug_loop()))
+ qDebug() << "window destroyed" << window;
+
+ if (!m_windows.contains(window))
+ return;
+
+ QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window);
+ wd->fireAboutToStop();
+
+ WindowData &data(m_windows[window]);
+ QSGD3D12Engine *engine = data.engine;
+ QSGD3D12RenderContext *rc = data.rc;
+ m_windows.remove(window);
+
+ // QSGNode destruction may release graphics resources in use so wait first.
+ engine->waitGPU();
+
+ // Bye bye nodes...
+ wd->cleanupNodesOnShutdown();
+
+ QSGD3D12ShaderEffectNode::cleanupMaterialTypeCache();
+
+ rc->invalidate();
+
+ if (m_windows.isEmpty())
+ QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
+
+ delete rc;
+ delete engine;
+
+ delete wd->animationController;
+}
+
+void QSGD3D12RenderLoop::exposeWindow(QQuickWindow *window)
+{
+ WindowData data;
+ data.exposed = true;
+ data.engine = new QSGD3D12Engine;
+ data.rc = static_cast<QSGD3D12RenderContext *>(QQuickWindowPrivate::get(window)->context);
+ data.rc->setEngine(data.engine);
+ m_windows[window] = data;
+
+ const int samples = window->format().samples();
+ const bool alpha = window->format().alphaBufferSize() > 0;
+ const qreal dpr = window->effectiveDevicePixelRatio();
+
+ if (Q_UNLIKELY(debug_loop()))
+ qDebug() << "initializing D3D12 engine" << window << window->size() << dpr << samples << alpha;
+
+ data.engine->attachToWindow(window->winId(), window->size(), dpr, samples, alpha);
+}
+
+void QSGD3D12RenderLoop::obscureWindow(QQuickWindow *window)
+{
+ m_windows[window].exposed = false;
+ QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window);
+ wd->fireAboutToStop();
+}
+
+void QSGD3D12RenderLoop::exposureChanged(QQuickWindow *window)
+{
+ if (Q_UNLIKELY(debug_loop()))
+ qDebug() << "exposure changed" << window << window->isExposed();
+
+ if (window->isExposed()) {
+ if (!m_windows.contains(window))
+ exposeWindow(window);
+
+ // Stop non-visual animation timer as we now have a window rendering.
+ if (m_animationTimer && somethingVisible()) {
+ killTimer(m_animationTimer);
+ m_animationTimer = 0;
+ }
+ // If we have a pending timer and we get an expose, we need to stop it.
+ // Otherwise we get two frames and two animation ticks in the same time interval.
+ if (m_updateTimer) {
+ killTimer(m_updateTimer);
+ m_updateTimer = 0;
+ }
+
+ WindowData &data(m_windows[window]);
+ data.exposed = true;
+ data.updatePending = true;
+
+ render();
+
+ } else if (m_windows.contains(window)) {
+ obscureWindow(window);
+
+ // Potentially start the non-visual animation timer if nobody is rendering.
+ if (m_anims->isRunning() && !somethingVisible() && !m_animationTimer)
+ m_animationTimer = startTimer(m_vsyncDelta);
+ }
+}
+
+QImage QSGD3D12RenderLoop::grab(QQuickWindow *window)
+{
+ const bool tempExpose = !m_windows.contains(window);
+ if (tempExpose)
+ exposeWindow(window);
+
+ m_windows[window].grabOnly = true;
+
+ renderWindow(window);
+
+ QImage grabbed = m_grabContent;
+ m_grabContent = QImage();
+
+ if (tempExpose)
+ obscureWindow(window);
+
+ return grabbed;
+}
+
+bool QSGD3D12RenderLoop::somethingVisible() const
+{
+ for (auto it = m_windows.constBegin(), itEnd = m_windows.constEnd(); it != itEnd; ++it) {
+ if (it.key()->isVisible() && it.key()->isExposed())
+ return true;
+ }
+ return false;
+}
+
+void QSGD3D12RenderLoop::maybePostUpdateTimer()
+{
+ if (!m_updateTimer) {
+ if (Q_UNLIKELY(debug_loop()))
+ qDebug("starting update timer");
+ m_updateTimer = startTimer(m_vsyncDelta / 3);
+ }
+}
+
+void QSGD3D12RenderLoop::update(QQuickWindow *window)
+{
+ maybeUpdate(window);
+}
+
+void QSGD3D12RenderLoop::maybeUpdate(QQuickWindow *window)
+{
+ if (!m_windows.contains(window) || !somethingVisible())
+ return;
+
+ m_windows[window].updatePending = true;
+ maybePostUpdateTimer();
+}
+
+QAnimationDriver *QSGD3D12RenderLoop::animationDriver() const
+{
+ return m_anims;
+}
+
+QSGContext *QSGD3D12RenderLoop::sceneGraphContext() const
+{
+ return sg;
+}
+
+QSGRenderContext *QSGD3D12RenderLoop::createRenderContext(QSGContext *) const
+{
+ // The rendercontext and engine are per-window, like with the threaded
+ // loop, but unlike the non-threaded OpenGL variants.
+ return sg->createRenderContext();
+}
+
+void QSGD3D12RenderLoop::releaseResources(QQuickWindow *window)
+{
+ if (Q_UNLIKELY(debug_loop()))
+ qDebug() << "releaseResources" << window;
+}
+
+void QSGD3D12RenderLoop::postJob(QQuickWindow *window, QRunnable *job)
+{
+ Q_UNUSED(window);
+ Q_ASSERT(job);
+ Q_ASSERT(window);
+ job->run();
+ delete job;
+}
+
+QSurface::SurfaceType QSGD3D12RenderLoop::windowSurfaceType() const
+{
+ return QSurface::OpenGLSurface;
+}
+
+bool QSGD3D12RenderLoop::interleaveIncubation() const
+{
+ return m_anims->isRunning() && somethingVisible();
+}
+
+void QSGD3D12RenderLoop::onAnimationStarted()
+{
+ if (!somethingVisible()) {
+ if (!m_animationTimer) {
+ if (Q_UNLIKELY(debug_loop()))
+ qDebug("starting non-visual animation timer");
+ m_animationTimer = startTimer(m_vsyncDelta);
+ }
+ } else {
+ maybePostUpdateTimer();
+ }
+}
+
+void QSGD3D12RenderLoop::onAnimationStopped()
+{
+ if (m_animationTimer) {
+ if (Q_UNLIKELY(debug_loop()))
+ qDebug("stopping non-visual animation timer");
+ killTimer(m_animationTimer);
+ m_animationTimer = 0;
+ }
+}
+
+bool QSGD3D12RenderLoop::event(QEvent *event)
+{
+ switch (event->type()) {
+ case QEvent::Timer:
+ {
+ QTimerEvent *te = static_cast<QTimerEvent *>(event);
+ if (te->timerId() == m_animationTimer) {
+ if (Q_UNLIKELY(debug_loop()))
+ qDebug("animation tick while no windows exposed");
+ m_anims->advance();
+ } else if (te->timerId() == m_updateTimer) {
+ if (Q_UNLIKELY(debug_loop()))
+ qDebug("update timeout - rendering");
+ killTimer(m_updateTimer);
+ m_updateTimer = 0;
+ render();
+ }
+ return true;
+ }
+ default:
+ break;
+ }
+
+ return QObject::event(event);
+}
+
+void QSGD3D12RenderLoop::render()
+{
+ bool rendered = false;
+ for (auto it = m_windows.begin(), itEnd = m_windows.end(); it != itEnd; ++it) {
+ if (it->updatePending) {
+ it->updatePending = false;
+ renderWindow(it.key());
+ rendered = true;
+ }
+ }
+
+ if (!rendered) {
+ if (Q_UNLIKELY(debug_loop()))
+ qDebug("render - no changes, sleep");
+ QThread::msleep(m_vsyncDelta);
+ }
+
+ if (m_anims->isRunning()) {
+ if (Q_UNLIKELY(debug_loop()))
+ qDebug("render - advancing animations");
+
+ m_anims->advance();
+
+ // It is not given that animations triggered another maybeUpdate()
+ // and thus another render pass, so to keep things running,
+ // make sure there is another frame pending.
+ maybePostUpdateTimer();
+
+ emit timeToIncubate();
+ }
+}
+
+void QSGD3D12RenderLoop::renderWindow(QQuickWindow *window)
+{
+ if (Q_UNLIKELY(debug_loop()))
+ qDebug() << "renderWindow" << window;
+
+ QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window);
+ if (!m_windows.contains(window) || !window->geometry().isValid())
+ return;
+
+ WindowData &data(m_windows[window]);
+ if (!data.exposed) { // not the same as window->isExposed(), when grabbing invisible windows for instance
+ if (Q_UNLIKELY(debug_loop()))
+ qDebug("renderWindow - not exposed, abort");
+ return;
+ }
+
+ if (!data.grabOnly)
+ wd->flushFrameSynchronousEvents();
+
+ QElapsedTimer renderTimer;
+ qint64 renderTime = 0, syncTime = 0, polishTime = 0;
+ const bool profileFrames = debug_time();
+ if (profileFrames)
+ renderTimer.start();
+ Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphPolishFrame);
+
+ wd->polishItems();
+
+ if (profileFrames)
+ polishTime = renderTimer.nsecsElapsed();
+ Q_QUICK_SG_PROFILE_SWITCH(QQuickProfiler::SceneGraphPolishFrame,
+ QQuickProfiler::SceneGraphRenderLoopFrame);
+
+ emit window->afterAnimating();
+
+ // The native window may change in some (quite artificial) cases, e.g. due
+ // to a hide - destroy - show on the QWindow.
+ bool needsWindow = !data.engine->window();
+ if (data.engine->window() && data.engine->window() != window->winId()) {
+ if (Q_UNLIKELY(debug_loop()))
+ qDebug("sync - native window handle changes for active engine");
+ data.engine->waitGPU();
+ wd->cleanupNodesOnShutdown();
+ QSGD3D12ShaderEffectNode::cleanupMaterialTypeCache();
+ data.rc->invalidate();
+ data.engine->releaseResources();
+ needsWindow = true;
+ }
+ if (needsWindow) {
+ // Must only ever get here when there is no window or releaseResources() has been called.
+ const int samples = window->format().samples();
+ const bool alpha = window->format().alphaBufferSize() > 0;
+ const qreal dpr = window->effectiveDevicePixelRatio();
+ if (Q_UNLIKELY(debug_loop()))
+ qDebug() << "sync - reinitializing D3D12 engine" << window << window->size() << dpr << samples << alpha;
+ data.engine->attachToWindow(window->winId(), window->size(), dpr, samples, alpha);
+ }
+
+ // Recover from device loss.
+ if (!data.engine->hasResources()) {
+ if (Q_UNLIKELY(debug_loop()))
+ qDebug("sync - device was lost, resetting scenegraph");
+ wd->cleanupNodesOnShutdown();
+ QSGD3D12ShaderEffectNode::cleanupMaterialTypeCache();
+ data.rc->invalidate();
+ }
+
+ data.rc->initialize(nullptr);
+
+ wd->syncSceneGraph();
+
+ if (profileFrames)
+ syncTime = renderTimer.nsecsElapsed();
+ Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame);
+
+ wd->renderSceneGraph(window->size());
+
+ if (profileFrames)
+ renderTime = renderTimer.nsecsElapsed();
+ Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame);
+
+ if (!data.grabOnly) {
+ // 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 (window->isVisible()) {
+ data.engine->present();
+ if (blockOnEachFrame)
+ data.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 {
+ if (blockOnEachFrame)
+ data.engine->waitGPU();
+ }
+ } else {
+ m_grabContent = data.engine->executeAndWaitReadbackRenderTarget();
+ data.grabOnly = false;
+ }
+
+ qint64 swapTime = 0;
+ if (profileFrames)
+ swapTime = renderTimer.nsecsElapsed();
+ Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphRenderLoopFrame);
+
+ if (Q_UNLIKELY(debug_time())) {
+ static QTime lastFrameTime = QTime::currentTime();
+ qDebug("Frame rendered with 'd3d12' 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();
+ }
+
+ // Simulate device loss if requested.
+ 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();
+ data.engine->simulateDeviceLoss();
+ }
+ }
+}
+
+int QSGD3D12RenderLoop::flags() const
+{
+ return SupportsGrabWithoutExpose;
+}
+
+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..c0333ffad0
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12renderloop_p.h
@@ -0,0 +1,130 @@
+/****************************************************************************
+**
+** 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 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;
+
+ 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 *event) override;
+
+public Q_SLOTS:
+ void onAnimationStarted();
+ void onAnimationStopped();
+
+private:
+ void exposeWindow(QQuickWindow *window);
+ void obscureWindow(QQuickWindow *window);
+ void renderWindow(QQuickWindow *window);
+ void render();
+ void maybePostUpdateTimer();
+ bool somethingVisible() const;
+
+ QSGD3D12Context *sg;
+ QAnimationDriver *m_anims;
+ int m_vsyncDelta;
+ int m_updateTimer = 0;
+ int m_animationTimer = 0;
+
+ struct WindowData {
+ QSGD3D12RenderContext *rc = nullptr;
+ QSGD3D12Engine *engine = nullptr;
+ bool updatePending = false;
+ bool grabOnly = false;
+ bool exposed = false;
+ };
+
+ QHash<QQuickWindow *, WindowData> m_windows;
+
+ QImage m_grabContent;
+};
+
+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..62771eb8f9
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode.cpp
@@ -0,0 +1,1047 @@
+/****************************************************************************
+**
+** 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/qthreadpool.h>
+#include <QtCore/qfile.h>
+#include <QtCore/qfileselector.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(shader)
+
+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) {
+ Q_ASSERT(shader.varData.at(i).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.textureViewCount = 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 QSGD3D12MaterialRenderState &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_shader()))
+ 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_shader()))
+ 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_shader()))
+ 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_shader()))
+ 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();
+ }
+ }
+}
+
+bool QSGD3D12GuiThreadShaderEffectManager::hasSeparateSamplerAndTextureObjects() const
+{
+ return true;
+}
+
+QString QSGD3D12GuiThreadShaderEffectManager::log() const
+{
+ return m_log;
+}
+
+QSGGuiThreadShaderEffectManager::Status QSGD3D12GuiThreadShaderEffectManager::status() const
+{
+ return m_status;
+}
+
+struct RefGuard {
+ RefGuard(IUnknown *p) : p(p) { }
+ ~RefGuard() { p->Release(); }
+ IUnknown *p;
+};
+
+class QSGD3D12ShaderCompileTask : public QRunnable
+{
+public:
+ QSGD3D12ShaderCompileTask(QSGD3D12GuiThreadShaderEffectManager *mgr,
+ QSGD3D12GuiThreadShaderEffectManager::ShaderInfo::Type typeHint,
+ const QByteArray &src,
+ QSGD3D12GuiThreadShaderEffectManager::ShaderInfo *result)
+ : mgr(mgr), typeHint(typeHint), src(src), result(result) { }
+
+ void run() override;
+
+private:
+ QSGD3D12GuiThreadShaderEffectManager *mgr;
+ QSGD3D12GuiThreadShaderEffectManager::ShaderInfo::Type typeHint;
+ QByteArray src;
+ QSGD3D12GuiThreadShaderEffectManager::ShaderInfo *result;
+};
+
+void QSGD3D12ShaderCompileTask::run()
+{
+ const char *target = typeHint == QSGD3D12GuiThreadShaderEffectManager::ShaderInfo::TypeVertex ? "vs_5_0" : "ps_5_0";
+ ID3DBlob *bytecode = nullptr;
+ ID3DBlob *errors = nullptr;
+ HRESULT hr = D3DCompile(src.constData(), src.size(), nullptr, nullptr, nullptr,
+ "main", target, 0, 0, &bytecode, &errors);
+ if (FAILED(hr) || !bytecode) {
+ qWarning("HLSL shader compilation failed: 0x%x", hr);
+ if (errors) {
+ mgr->m_log += QString::fromUtf8(static_cast<const char *>(errors->GetBufferPointer()), errors->GetBufferSize());
+ errors->Release();
+ }
+ mgr->m_status = QSGGuiThreadShaderEffectManager::Error;
+ emit mgr->shaderCodePrepared(false, typeHint, src, result);
+ emit mgr->logAndStatusChanged();
+ return;
+ }
+
+ result->blob.resize(bytecode->GetBufferSize());
+ memcpy(result->blob.data(), bytecode->GetBufferPointer(), result->blob.size());
+ bytecode->Release();
+
+ const bool ok = mgr->reflect(result);
+ mgr->m_status = ok ? QSGGuiThreadShaderEffectManager::Compiled : QSGGuiThreadShaderEffectManager::Error;
+ emit mgr->shaderCodePrepared(ok, typeHint, src, result);
+ emit mgr->logAndStatusChanged();
+}
+
+static const int BYTECODE_MAGIC = 0x43425844; // 'DXBC'
+
+void QSGD3D12GuiThreadShaderEffectManager::prepareShaderCode(ShaderInfo::Type typeHint, const QByteArray &src, ShaderInfo *result)
+{
+ // The D3D12 backend's ShaderEffect implementation supports both HLSL
+ // source strings and bytecode or source in files as input. Bytecode is
+ // strongly recommended, but in order to make ShaderEffect users' (and
+ // anything that stiches shader strings together dynamically, e.g.
+ // qtgraphicaleffects) life easier, and since we link to d3dcompiler
+ // anyways, compiling from source is also supported.
+
+ QByteArray shaderSourceCode = src;
+ QUrl srcUrl(QString::fromUtf8(src));
+ if (!srcUrl.scheme().compare(QLatin1String("qrc"), Qt::CaseInsensitive) || srcUrl.isLocalFile()) {
+ if (!m_fileSelector) {
+ m_fileSelector = new QFileSelector(this);
+ m_fileSelector->setExtraSelectors(QStringList() << QStringLiteral("hlsl"));
+ }
+ const QString fn = m_fileSelector->select(QQmlFile::urlToLocalFileOrQrc(srcUrl));
+ QFile f(fn);
+ if (!f.open(QIODevice::ReadOnly)) {
+ qWarning("ShaderEffect: Failed to read %s", qPrintable(fn));
+ emit shaderCodePrepared(false, typeHint, src, result);
+ return;
+ }
+ QByteArray blob = f.readAll();
+ f.close();
+ if (blob.size() > 4) {
+ const quint32 *p = reinterpret_cast<const quint32 *>(blob.constData());
+ if (*p == BYTECODE_MAGIC) {
+ // already compiled D3D bytecode, skip straight to reflection
+ result->blob = blob;
+ const bool ok = reflect(result);
+ m_status = ok ? Compiled : Error;
+ emit shaderCodePrepared(ok, typeHint, src, result);
+ emit logAndStatusChanged();
+ return;
+ }
+ // assume the file contained HLSL source code
+ shaderSourceCode = blob;
+ }
+ }
+
+ QThreadPool::globalInstance()->start(new QSGD3D12ShaderCompileTask(this, typeHint, shaderSourceCode, result));
+}
+
+bool QSGD3D12GuiThreadShaderEffectManager::reflect(ShaderInfo *result)
+{
+ 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_shader()))
+ 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_shader())) {
+ 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..ee17e59130
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode_p.h
@@ -0,0 +1,177 @@
+/****************************************************************************
+**
+** 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 QFileSelector;
+
+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 QSGD3D12MaterialRenderState &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:
+ bool hasSeparateSamplerAndTextureObjects() const override;
+
+ QString log() const override;
+ Status status() const override;
+
+ void prepareShaderCode(ShaderInfo::Type typeHint, const QByteArray &src, ShaderInfo *result) override;
+
+private:
+ bool reflect(ShaderInfo *result);
+ QString m_log;
+ Status m_status = Uncompiled;
+ QFileSelector *m_fileSelector = nullptr;
+
+ friend class QSGD3D12ShaderCompileTask;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGD3D12SHADEREFFECTNODE_P_H
diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12spritenode.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12spritenode.cpp
new file mode 100644
index 0000000000..807fbcdcec
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12spritenode.cpp
@@ -0,0 +1,314 @@
+/****************************************************************************
+**
+** 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 "qsgd3d12spritenode_p.h"
+#include "qsgd3d12material_p.h"
+
+#include "vs_sprite.hlslh"
+#include "ps_sprite.hlslh"
+
+QT_BEGIN_NAMESPACE
+
+struct SpriteVertex
+{
+ float x;
+ float y;
+ float tx;
+ float ty;
+};
+
+struct SpriteVertices
+{
+ SpriteVertex v1;
+ SpriteVertex v2;
+ SpriteVertex v3;
+ SpriteVertex v4;
+};
+
+class QSGD3D12SpriteMaterial : public QSGD3D12Material
+{
+public:
+ QSGD3D12SpriteMaterial();
+ ~QSGD3D12SpriteMaterial();
+
+ QSGMaterialType *type() const override { static QSGMaterialType type; return &type; }
+
+ int compare(const QSGMaterial *other) const override
+ {
+ return this - static_cast<const QSGD3D12SpriteMaterial *>(other);
+ }
+
+ int constantBufferSize() const override;
+ void preparePipeline(QSGD3D12PipelineState *pipelineState) override;
+ UpdateResults updatePipeline(const QSGD3D12MaterialRenderState &state,
+ QSGD3D12PipelineState *pipelineState,
+ ExtraState *extraState,
+ quint8 *constantBuffer) override;
+
+ QSGTexture *texture;
+
+ float animT;
+ float animX1;
+ float animY1;
+ float animX2;
+ float animY2;
+ float animW;
+ float animH;
+};
+
+QSGD3D12SpriteMaterial::QSGD3D12SpriteMaterial()
+ : texture(nullptr),
+ animT(0.0f),
+ animX1(0.0f),
+ animY1(0.0f),
+ animX2(0.0f),
+ animY2(0.0f),
+ animW(1.0f),
+ animH(1.0f)
+{
+ setFlag(Blending, true);
+}
+
+QSGD3D12SpriteMaterial::~QSGD3D12SpriteMaterial()
+{
+ delete texture;
+}
+
+static const int SPRITE_CB_SIZE_0 = 16 * sizeof(float); // float4x4
+static const int SPRITE_CB_SIZE_1 = 4 * sizeof(float); // float4
+static const int SPRITE_CB_SIZE_2 = 3 * sizeof(float); // float3
+static const int SPRITE_CB_SIZE_3 = sizeof(float); // float
+static const int SPRITE_CB_SIZE = SPRITE_CB_SIZE_0 + SPRITE_CB_SIZE_1 + SPRITE_CB_SIZE_2 + SPRITE_CB_SIZE_3;
+
+int QSGD3D12SpriteMaterial::constantBufferSize() const
+{
+ return QSGD3D12Engine::alignedConstantBufferSize(SPRITE_CB_SIZE);
+}
+
+void QSGD3D12SpriteMaterial::preparePipeline(QSGD3D12PipelineState *pipelineState)
+{
+ pipelineState->shaders.vs = g_VS_Sprite;
+ pipelineState->shaders.vsSize = sizeof(g_VS_Sprite);
+ pipelineState->shaders.ps = g_PS_Sprite;
+ pipelineState->shaders.psSize = sizeof(g_PS_Sprite);
+
+ pipelineState->shaders.rootSig.textureViewCount = 1;
+}
+
+QSGD3D12Material::UpdateResults QSGD3D12SpriteMaterial::updatePipeline(const QSGD3D12MaterialRenderState &state,
+ QSGD3D12PipelineState *,
+ ExtraState *,
+ quint8 *constantBuffer)
+{
+ QSGD3D12Material::UpdateResults r = UpdatedConstantBuffer;
+ quint8 *p = constantBuffer;
+
+ if (state.isMatrixDirty())
+ memcpy(p, state.combinedMatrix().constData(), SPRITE_CB_SIZE_0);
+ p += SPRITE_CB_SIZE_0;
+
+ {
+ const float v[] = { animX1, animY1, animX2, animY2 };
+ memcpy(p, v, SPRITE_CB_SIZE_1);
+ }
+ p += SPRITE_CB_SIZE_1;
+
+ {
+ const float v[] = { animW, animH, animT };
+ memcpy(p, v, SPRITE_CB_SIZE_2);
+ }
+ p += SPRITE_CB_SIZE_2;
+
+ if (state.isOpacityDirty()) {
+ const float opacity = state.opacity();
+ memcpy(p, &opacity, SPRITE_CB_SIZE_3);
+ }
+
+ texture->bind();
+
+ return r;
+}
+
+static QSGGeometry::Attribute Sprite_Attributes[] = {
+ QSGGeometry::Attribute::createWithAttributeType(0, 2, QSGGeometry::FloatType, QSGGeometry::PositionAttribute),
+ QSGGeometry::Attribute::createWithAttributeType(1, 2, QSGGeometry::FloatType, QSGGeometry::TexCoordAttribute),
+};
+
+static QSGGeometry::AttributeSet Sprite_AttributeSet = { 2, 4 * sizeof(float), Sprite_Attributes };
+
+QSGD3D12SpriteNode::QSGD3D12SpriteNode()
+ : m_material(new QSGD3D12SpriteMaterial)
+ , m_geometryDirty(true)
+ , m_sheetSize(QSize(64, 64))
+{
+ m_geometry = new QSGGeometry(Sprite_AttributeSet, 4, 6);
+ m_geometry->setDrawingMode(QSGGeometry::DrawTriangles);
+
+ quint16 *indices = m_geometry->indexDataAsUShort();
+ indices[0] = 0;
+ indices[1] = 1;
+ indices[2] = 2;
+ indices[3] = 1;
+ indices[4] = 3;
+ indices[5] = 2;
+
+ setGeometry(m_geometry);
+ setMaterial(m_material);
+ setFlag(OwnsGeometry, true);
+ setFlag(OwnsMaterial, true);
+}
+
+void QSGD3D12SpriteNode::setTexture(QSGTexture *texture)
+{
+ m_material->texture = texture;
+ m_geometryDirty = true;
+ markDirty(DirtyMaterial);
+}
+
+void QSGD3D12SpriteNode::setTime(float time)
+{
+ m_material->animT = time;
+ markDirty(DirtyMaterial);
+}
+
+void QSGD3D12SpriteNode::setSourceA(const QPoint &source)
+{
+ if (m_sourceA != source) {
+ m_sourceA = source;
+ m_material->animX1 = static_cast<float>(source.x()) / m_sheetSize.width();
+ m_material->animY1 = static_cast<float>(source.y()) / m_sheetSize.height();
+ markDirty(DirtyMaterial);
+ }
+}
+
+void QSGD3D12SpriteNode::setSourceB(const QPoint &source)
+{
+ if (m_sourceB != source) {
+ m_sourceB = source;
+ m_material->animX2 = static_cast<float>(source.x()) / m_sheetSize.width();
+ m_material->animY2 = static_cast<float>(source.y()) / m_sheetSize.height();
+ markDirty(DirtyMaterial);
+ }
+}
+
+void QSGD3D12SpriteNode::setSpriteSize(const QSize &size)
+{
+ if (m_spriteSize != size) {
+ m_spriteSize = size;
+ m_material->animW = static_cast<float>(size.width()) / m_sheetSize.width();
+ m_material->animH = static_cast<float>(size.height()) / m_sheetSize.height();
+ markDirty(DirtyMaterial);
+ }
+}
+
+void QSGD3D12SpriteNode::setSheetSize(const QSize &size)
+{
+ if (m_sheetSize != size) {
+ m_sheetSize = size;
+
+ // Update all dependent properties
+ m_material->animX1 = static_cast<float>(m_sourceA.x()) / m_sheetSize.width();
+ m_material->animY1 = static_cast<float>(m_sourceA.y()) / m_sheetSize.height();
+ m_material->animX2 = static_cast<float>(m_sourceB.x()) / m_sheetSize.width();
+ m_material->animY2 = static_cast<float>(m_sourceB.y()) / m_sheetSize.height();
+ m_material->animW = static_cast<float>(m_spriteSize.width()) / m_sheetSize.width();
+ m_material->animH = static_cast<float>(m_spriteSize.height()) / m_sheetSize.height();
+
+ markDirty(DirtyMaterial);
+ }
+}
+
+void QSGD3D12SpriteNode::setSize(const QSizeF &size)
+{
+ if (m_size != size) {
+ m_size = size;
+ m_geometryDirty = true;
+ }
+}
+
+void QSGD3D12SpriteNode::setFiltering(QSGTexture::Filtering filtering)
+{
+ m_material->texture->setFiltering(filtering);
+ markDirty(DirtyMaterial);
+}
+
+void QSGD3D12SpriteNode::update()
+{
+ if (m_geometryDirty) {
+ m_geometryDirty = false;
+ updateGeometry();
+ }
+}
+
+void QSGD3D12SpriteNode::updateGeometry()
+{
+ if (!m_material->texture)
+ return;
+
+ SpriteVertices *p = static_cast<SpriteVertices *>(m_geometry->vertexData());
+ const QRectF texRect = m_material->texture->normalizedTextureSubRect();
+
+ p->v1.tx = texRect.topLeft().x();
+ p->v1.ty = texRect.topLeft().y();
+
+ p->v2.tx = texRect.topRight().x();
+ p->v2.ty = texRect.topRight().y();
+
+ p->v3.tx = texRect.bottomLeft().x();
+ p->v3.ty = texRect.bottomLeft().y();
+
+ p->v4.tx = texRect.bottomRight().x();
+ p->v4.ty = texRect.bottomRight().y();
+
+ p->v1.x = 0;
+ p->v1.y = 0;
+
+ p->v2.x = m_size.width();
+ p->v2.y = 0;
+
+ p->v3.x = 0;
+ p->v3.y = m_size.height();
+
+ p->v4.x = m_size.width();
+ p->v4.y = m_size.height();
+
+ markDirty(DirtyGeometry);
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12spritenode_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12spritenode_p.h
new file mode 100644
index 0000000000..265bec7c78
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12spritenode_p.h
@@ -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$
+**
+****************************************************************************/
+
+#ifndef QSGD3D12SPRITENODE_H
+#define QSGD3D12SPRITENODE_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 QSGD3D12SpriteMaterial;
+
+class QSGD3D12SpriteNode : public QSGSpriteNode
+{
+public:
+ QSGD3D12SpriteNode();
+
+ void setTexture(QSGTexture *texture) override;
+ void setTime(float time) override;
+ void setSourceA(const QPoint &source) override;
+ void setSourceB(const QPoint &source) override;
+ void setSpriteSize(const QSize &size) override;
+ void setSheetSize(const QSize &size) override;
+ void setSize(const QSizeF &size) override;
+ void setFiltering(QSGTexture::Filtering filtering) override;
+ void update() override;
+
+private:
+ void updateGeometry();
+
+ QSGD3D12SpriteMaterial *m_material;
+ QSGGeometry *m_geometry;
+ bool m_geometryDirty;
+ QPoint m_sourceA;
+ QPoint m_sourceB;
+ QSize m_spriteSize;
+ QSize m_sheetSize;
+ QSizeF m_size;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGD3D12SPRITENODE_H
diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12texture.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12texture.cpp
new file mode 100644
index 0000000000..a5f3eb7a31
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12texture.cpp
@@ -0,0 +1,145 @@
+/****************************************************************************
+**
+** 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
+
+#define RETAIN_IMAGE
+
+void QSGD3D12Texture::create(const QImage &image, uint flags)
+{
+ // ### atlas?
+
+ const bool alphaRequest = flags & QSGRenderContext::CreateTexture_Alpha;
+ m_alphaWanted = alphaRequest && image.hasAlphaChannel();
+
+ // The engine maps 8-bit formats to R8. This is fine for glyphs and such
+ // but may not be what apps expect for ordinary image data. The OpenGL
+ // implementation maps these to ARGB32_Pre so let's follow suit.
+ if (image.depth() != 8)
+ m_image = image;
+ else
+ m_image = image.convertToFormat(m_alphaWanted ? QImage::Format_ARGB32_Premultiplied : QImage::Format_RGB32);
+
+ 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) {
+#ifdef RETAIN_IMAGE
+ m_engine->releaseTexture(m_id);
+ m_id = m_engine->genTexture();
+ Q_ASSERT(m_id);
+ m_createPending = true;
+#else
+ // ### this can be made working some day (something similar to
+ // queueTextureResize) but skip for now
+ qWarning("D3D12: mipmap property cannot be changed once the texture is created");
+#endif
+ }
+
+ 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);
+
+#ifndef RETAIN_IMAGE
+ m_image = QImage();
+#endif
+ }
+
+ // 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);
+}
+
+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..3d0e226ddb
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12texture_p.h
@@ -0,0 +1,87 @@
+/****************************************************************************
+**
+** 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;
+
+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/qsgd3d12threadedrenderloop.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12threadedrenderloop.cpp
new file mode 100644
index 0000000000..a803f67380
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12threadedrenderloop.cpp
@@ -0,0 +1,1183 @@
+/****************************************************************************
+**
+** 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 "qsgd3d12threadedrenderloop_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/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)
+
+
+// NOTE: The threaded renderloop is not currently safe to use in practice as it
+// is prone to deadlocks, in particular when multiple windows are active. This
+// is because DXGI's limitation of relying on the gui message pump in certain
+// cases. See
+// https://msdn.microsoft.com/en-us/library/windows/desktop/ee417025(v=vs.85).aspx#multithreading_and_dxgi
+//
+// This means that if swap chain functions like create, release, and
+// potentially even Present, are called outside the gui thread, then the
+// application must ensure the gui thread does not ever block and wait for the
+// render thread - since on the render thread a DXGI call may be in turn
+// waiting for the gui thread to deliver a window message...
+//
+// Ensuring this is impossible with the current design where the gui thread
+// must block at certain points, waiting for the render thread. Qt moves out
+// rendering from the main thread, in order to make application's life easier,
+// whereas the typical DXGI-compatible model would require moving work, but not
+// windowing and presenting, out to additional threads.
+
+
+/*
+ The D3D render loop mostly mirrors the threaded OpenGL render loop.
+
+ There are two classes here. QSGD3D12ThreadedRenderLoop 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(QSGD3D12ThreadedRenderLoop *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;
+ QSGD3D12ThreadedRenderLoop *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;
+ }
+ if (needsWindow) {
+ // Must only ever get here when there is no window or releaseResources() has been called.
+ const int samples = wme->window->format().samples();
+ const bool alpha = wme->window->format().alphaBufferSize() > 0;
+ if (Q_UNLIKELY(debug_loop()))
+ qDebug() << "RT - WM_RequestSync - initializing D3D12 engine" << wme->window
+ << wme->size << wme->dpr << samples << alpha;
+ engine->attachToWindow(wme->window->winId(), wme->size, wme->dpr, samples, alpha);
+ }
+ 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->initialize(nullptr);
+ 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->initialize(nullptr);
+ 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;
+}
+
+QSGD3D12ThreadedRenderLoop::QSGD3D12ThreadedRenderLoop()
+{
+ if (Q_UNLIKELY(debug_loop()))
+ qDebug("d3d12 THREADED render loop ctor");
+
+ sg = new QSGD3D12Context;
+
+ anim = sg->createAnimationDriver(this);
+ connect(anim, &QAnimationDriver::started, this, &QSGD3D12ThreadedRenderLoop::onAnimationStarted);
+ connect(anim, &QAnimationDriver::stopped, this, &QSGD3D12ThreadedRenderLoop::onAnimationStopped);
+ anim->install();
+}
+
+QSGD3D12ThreadedRenderLoop::~QSGD3D12ThreadedRenderLoop()
+{
+ if (Q_UNLIKELY(debug_loop()))
+ qDebug("d3d12 THREADED render loop dtor");
+
+ delete sg;
+}
+
+void QSGD3D12ThreadedRenderLoop::show(QQuickWindow *window)
+{
+ if (Q_UNLIKELY(debug_loop()))
+ qDebug() << "show" << window;
+}
+
+void QSGD3D12ThreadedRenderLoop::hide(QQuickWindow *window)
+{
+ if (Q_UNLIKELY(debug_loop()))
+ qDebug() << "hide" << window;
+
+ if (window->isExposed())
+ handleObscurity(windowFor(windows, window));
+
+ releaseResources(window);
+}
+
+void QSGD3D12ThreadedRenderLoop::resize(QQuickWindow *window)
+{
+ if (!window->isExposed() || window->size().isEmpty())
+ return;
+
+ if (Q_UNLIKELY(debug_loop()))
+ qDebug() << "resize" << window << window->size();
+}
+
+void QSGD3D12ThreadedRenderLoop::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 QSGD3D12ThreadedRenderLoop::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 QSGD3D12ThreadedRenderLoop::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 QSGD3D12ThreadedRenderLoop::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 QSGD3D12ThreadedRenderLoop::maybeUpdate(QQuickWindow *window)
+{
+ WindowData *w = windowFor(windows, window);
+ if (w)
+ scheduleUpdate(w);
+}
+
+// called in response to window->requestUpdate()
+void QSGD3D12ThreadedRenderLoop::handleUpdateRequest(QQuickWindow *window)
+{
+ if (Q_UNLIKELY(debug_loop()))
+ qDebug() << "handleUpdateRequest" << window;
+
+ WindowData *w = windowFor(windows, window);
+ if (w)
+ polishAndSync(w, false);
+}
+
+QAnimationDriver *QSGD3D12ThreadedRenderLoop::animationDriver() const
+{
+ return anim;
+}
+
+QSGContext *QSGD3D12ThreadedRenderLoop::sceneGraphContext() const
+{
+ return sg;
+}
+
+QSGRenderContext *QSGD3D12ThreadedRenderLoop::createRenderContext(QSGContext *) const
+{
+ return sg->createRenderContext();
+}
+
+void QSGD3D12ThreadedRenderLoop::releaseResources(QQuickWindow *window)
+{
+ if (Q_UNLIKELY(debug_loop()))
+ qDebug() << "releaseResources" << window;
+
+ WindowData *w = windowFor(windows, window);
+ if (w)
+ handleResourceRelease(w, false);
+}
+
+void QSGD3D12ThreadedRenderLoop::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 QSGD3D12ThreadedRenderLoop::windowSurfaceType() const
+{
+ return QSurface::OpenGLSurface;
+}
+
+bool QSGD3D12ThreadedRenderLoop::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 QSGD3D12ThreadedRenderLoop::flags() const
+{
+ return SupportsGrabWithoutExpose;
+}
+
+bool QSGD3D12ThreadedRenderLoop::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 QSGD3D12ThreadedRenderLoop::onAnimationStarted()
+{
+ startOrStopAnimationTimer();
+
+ for (const WindowData &w : qAsConst(windows))
+ w.window->requestUpdate();
+}
+
+void QSGD3D12ThreadedRenderLoop::onAnimationStopped()
+{
+ startOrStopAnimationTimer();
+}
+
+void QSGD3D12ThreadedRenderLoop::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 QSGD3D12ThreadedRenderLoop::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() << "QSGD3D12ThreadedRenderLoop: 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 QSGD3D12ThreadedRenderLoop::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 QSGD3D12ThreadedRenderLoop::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 QSGD3D12ThreadedRenderLoop::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 QSGD3D12ThreadedRenderLoop::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)->flushFrameSynchronousEvents();
+ // 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 "qsgd3d12threadedrenderloop.moc"
+
+QT_END_NAMESPACE
diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12threadedrenderloop_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12threadedrenderloop_p.h
new file mode 100644
index 0000000000..46f62948f1
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12threadedrenderloop_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 QSGD3D12THREADEDRENDERLOOP_P_H
+#define QSGD3D12THREADEDRENDERLOOP_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 QSGD3D12ThreadedRenderLoop : public QSGRenderLoop
+{
+ Q_OBJECT
+
+public:
+ QSGD3D12ThreadedRenderLoop();
+ ~QSGD3D12ThreadedRenderLoop();
+
+ 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 // QSGD3D12THREADEDRENDERLOOP_P_H
diff --git a/src/plugins/scenegraph/d3d12/shaders/flatcolor.hlsl b/src/plugins/scenegraph/d3d12/shaders/flatcolor.hlsl
new file mode 100644
index 0000000000..034b51435a
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/shaders/flatcolor.hlsl
@@ -0,0 +1,27 @@
+struct VSInput
+{
+ float4 position : POSITION;
+};
+
+cbuffer ConstantBuffer : register(b0)
+{
+ float4x4 mvp;
+ float4 color;
+};
+
+struct PSInput
+{
+ float4 position : SV_POSITION;
+};
+
+PSInput VS_FlatColor(VSInput input)
+{
+ PSInput result;
+ result.position = mul(mvp, input.position);
+ return result;
+}
+
+float4 PS_FlatColor(PSInput input) : SV_TARGET
+{
+ return color;
+}
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..963f4c5d8c
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/shaders/shaders.pri
@@ -0,0 +1,141 @@
+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
+
+flatcolor_VSPS = $$PWD/flatcolor.hlsl
+flatcolor_vshader.input = flatcolor_VSPS
+flatcolor_vshader.header = vs_flatcolor.hlslh
+flatcolor_vshader.entry = VS_FlatColor
+flatcolor_vshader.type = vs_5_0
+flatcolor_pshader.input = flatcolor_VSPS
+flatcolor_pshader.header = ps_flatcolor.hlslh
+flatcolor_pshader.entry = PS_FlatColor
+flatcolor_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
+
+sprite_VSPS = $$PWD/sprite.hlsl
+sprite_vshader.input = sprite_VSPS
+sprite_vshader.header = vs_sprite.hlslh
+sprite_vshader.entry = VS_Sprite
+sprite_vshader.type = vs_5_0
+sprite_pshader.input = sprite_VSPS
+sprite_pshader.header = ps_sprite.hlslh
+sprite_pshader.entry = PS_Sprite
+sprite_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 \
+ flatcolor_vshader flatcolor_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 \
+ sprite_vshader sprite_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/sprite.hlsl b/src/plugins/scenegraph/d3d12/shaders/sprite.hlsl
new file mode 100644
index 0000000000..d4e3b066ee
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/shaders/sprite.hlsl
@@ -0,0 +1,43 @@
+struct VSInput
+{
+ float4 position : POSITION;
+ float2 coord : TEXCOORD0;
+};
+
+cbuffer ConstantBuffer : register(b0)
+{
+ float4x4 mvp;
+ float4 animPos;
+ float3 animData;
+ float opacity;
+};
+
+struct PSInput
+{
+ float4 position : SV_POSITION;
+ float4 fTexS : TEXCOORD0;
+ float progress : TEXCOORD1;
+};
+
+Texture2D tex : register(t0);
+SamplerState samp : register(s0);
+
+PSInput VS_Sprite(VSInput input)
+{
+ PSInput result;
+
+ result.position = mul(mvp, input.position);
+ result.progress = animData.z;
+
+ // Calculate frame location in texture
+ result.fTexS.xy = animPos.xy + input.coord.xy * animData.xy;
+ // Next frame is also passed, for interpolation
+ result.fTexS.zw = animPos.zw + input.coord.xy * animData.xy;
+
+ return result;
+}
+
+float4 PS_Sprite(PSInput input) : SV_TARGET
+{
+ return lerp(tex.Sample(samp, input.fTexS.xy), tex.Sample(samp, input.fTexS.zw), input.progress) * opacity;
+}
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..bb9381e7c0
--- /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).a;
+}
+
+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).a;
+ float style = clamp(tex.Sample(samp, input.shiftedCoord).a - 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).a;
+ float outline = clamp(clamp(tex.Sample(samp, input.coordUp).a
+ + tex.Sample(samp, input.coordDown).a
+ + tex.Sample(samp, input.coordLeft).a
+ + tex.Sample(samp, input.coordRight).a, 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..a90e8d4814
--- /dev/null
+++ b/src/plugins/scenegraph/scenegraph.pro
@@ -0,0 +1,3 @@
+TEMPLATE = subdirs
+QT_FOR_CONFIG += quick
+qtConfig(d3d12): SUBDIRS += d3d12
diff --git a/src/qml/animations/qabstractanimationjob.cpp b/src/qml/animations/qabstractanimationjob.cpp
index 4bd02c1934..f7f6939e4b 100644
--- a/src/qml/animations/qabstractanimationjob.cpp
+++ b/src/qml/animations/qabstractanimationjob.cpp
@@ -588,8 +588,7 @@ void QAbstractAnimationJob::updateDirection(QAbstractAnimationJob::Direction dir
void QAbstractAnimationJob::finished()
{
//TODO: update this code so it is valid to delete the animation in animationFinished
- for (int i = 0; i < changeListeners.count(); ++i) {
- const QAbstractAnimationJob::ChangeListener &change = changeListeners.at(i);
+ for (const auto &change : changeListeners) {
if (change.types & QAbstractAnimationJob::Completion) {
RETURN_IF_DELETED(change.listener->animationFinished(this));
}
@@ -603,8 +602,7 @@ void QAbstractAnimationJob::finished()
void QAbstractAnimationJob::stateChanged(QAbstractAnimationJob::State newState, QAbstractAnimationJob::State oldState)
{
- for (int i = 0; i < changeListeners.count(); ++i) {
- const QAbstractAnimationJob::ChangeListener &change = changeListeners.at(i);
+ for (const auto &change : changeListeners) {
if (change.types & QAbstractAnimationJob::StateChange) {
RETURN_IF_DELETED(change.listener->animationStateChanged(this, newState, oldState));
}
@@ -613,8 +611,7 @@ void QAbstractAnimationJob::stateChanged(QAbstractAnimationJob::State newState,
void QAbstractAnimationJob::currentLoopChanged()
{
- for (int i = 0; i < changeListeners.count(); ++i) {
- const QAbstractAnimationJob::ChangeListener &change = changeListeners.at(i);
+ for (const auto &change : changeListeners) {
if (change.types & QAbstractAnimationJob::CurrentLoop) {
RETURN_IF_DELETED(change.listener->animationCurrentLoopChanged(this));
}
@@ -625,8 +622,7 @@ void QAbstractAnimationJob::currentTimeChanged(int currentTime)
{
Q_ASSERT(m_hasCurrentTimeChangeListeners);
- for (int i = 0; i < changeListeners.count(); ++i) {
- const QAbstractAnimationJob::ChangeListener &change = changeListeners.at(i);
+ for (const auto &change : changeListeners) {
if (change.types & QAbstractAnimationJob::CurrentTime) {
RETURN_IF_DELETED(change.listener->animationCurrentTimeChanged(this, currentTime));
}
@@ -638,17 +634,18 @@ void QAbstractAnimationJob::addAnimationChangeListener(QAnimationJobChangeListen
if (changes & QAbstractAnimationJob::CurrentTime)
m_hasCurrentTimeChangeListeners = true;
- changeListeners.append(ChangeListener(listener, changes));
+ changeListeners.push_back(ChangeListener(listener, changes));
}
void QAbstractAnimationJob::removeAnimationChangeListener(QAnimationJobChangeListener *listener, QAbstractAnimationJob::ChangeTypes changes)
{
m_hasCurrentTimeChangeListeners = false;
- changeListeners.removeOne(ChangeListener(listener, changes));
+ const auto it = std::find(changeListeners.begin(), changeListeners.end(), ChangeListener(listener, changes));
+ if (it != changeListeners.end())
+ changeListeners.erase(it);
- for (int i = 0; i < changeListeners.count(); ++i) {
- const QAbstractAnimationJob::ChangeListener &change = changeListeners.at(i);
+ for (const auto &change: changeListeners) {
if (change.types & QAbstractAnimationJob::CurrentTime) {
m_hasCurrentTimeChangeListeners = true;
break;
diff --git a/src/qml/animations/qabstractanimationjob_p.h b/src/qml/animations/qabstractanimationjob_p.h
index b04f523585..efc86a5823 100644
--- a/src/qml/animations/qabstractanimationjob_p.h
+++ b/src/qml/animations/qabstractanimationjob_p.h
@@ -54,7 +54,7 @@
#include <private/qtqmlglobal_p.h>
#include <QtCore/QObject>
#include <QtCore/private/qabstractanimation_p.h>
-#include "private/qpodvector_p.h"
+#include <vector>
QT_BEGIN_NAMESPACE
@@ -164,7 +164,7 @@ protected:
QAbstractAnimationJob::ChangeTypes types;
bool operator==(const ChangeListener &other) const { return listener == other.listener && types == other.types; }
};
- QPODVector<ChangeListener,1> changeListeners;
+ std::vector<ChangeListener> changeListeners;
QAbstractAnimationJob *m_nextSibling;
QAbstractAnimationJob *m_previousSibling;
diff --git a/src/qml/animations/qanimationgroupjob_p.h b/src/qml/animations/qanimationgroupjob_p.h
index 4b94e79d40..9bcd63127a 100644
--- a/src/qml/animations/qanimationgroupjob_p.h
+++ b/src/qml/animations/qanimationgroupjob_p.h
@@ -52,6 +52,7 @@
//
#include "private/qabstractanimationjob_p.h"
+#include <QtCore/qdebug.h>
QT_BEGIN_NAMESPACE
diff --git a/src/qml/compiler/compiler.pri b/src/qml/compiler/compiler.pri
index 585fef7603..1de5dfa6fa 100644
--- a/src/qml/compiler/compiler.pri
+++ b/src/qml/compiler/compiler.pri
@@ -25,13 +25,29 @@ SOURCES += \
HEADERS += \
$$PWD/qqmltypecompiler_p.h \
- $$PWD/qv4isel_moth_p.h \
- $$PWD/qv4instr_moth_p.h
+ $$PWD/qqmlpropertycachecreator_p.h \
+ $$PWD/qqmlpropertyvalidator_p.h \
+ $$PWD/qv4compilationunitmapper_p.h
SOURCES += \
$$PWD/qqmltypecompiler.cpp \
- $$PWD/qv4instr_moth.cpp \
- $$PWD/qv4isel_moth.cpp
+ $$PWD/qqmlpropertycachecreator.cpp \
+ $$PWD/qqmlpropertyvalidator.cpp \
+ $$PWD/qv4compilationunitmapper.cpp
+unix: SOURCES += $$PWD/qv4compilationunitmapper_unix.cpp
+else: SOURCES += $$PWD/qv4compilationunitmapper_win.cpp
+
+qtConfig(qml-interpreter) {
+ HEADERS += \
+ $$PWD/qv4instr_moth_p.h \
+ $$PWD/qv4isel_moth_p.h
+ SOURCES += \
+ $$PWD/qv4instr_moth.cpp \
+ $$PWD/qv4isel_moth.cpp
+}
+
+
+qtConfig(private_tests): LIBS_PRIVATE += $$QMAKE_LIBS_DYNLOAD
}
diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp
index eaf0e72296..eb83962630 100644
--- a/src/qml/compiler/qqmlirbuilder.cpp
+++ b/src/qml/compiler/qqmlirbuilder.cpp
@@ -44,12 +44,12 @@
#include <private/qqmljsparser_p.h>
#include <private/qqmljslexer_p.h>
#include <QCoreApplication>
+#include <QCryptographicHash>
#ifndef V4_BOOTSTRAP
#include <private/qqmlglobal_p.h>
#include <private/qqmltypeloader_p.h>
#include <private/qqmlengine_p.h>
-#include <private/qqmlcompiler_p.h>
#endif
#ifdef CONST
@@ -72,21 +72,24 @@ using namespace QmlIR;
return false; \
}
-void Object::init(QQmlJS::MemoryPool *pool, int typeNameIndex, int id, const QQmlJS::AST::SourceLocation &loc)
+void Object::init(QQmlJS::MemoryPool *pool, int typeNameIndex, int idIndex, const QQmlJS::AST::SourceLocation &loc)
{
inheritedTypeNameIndex = typeNameIndex;
location.line = loc.startLine;
location.column = loc.startColumn;
- idIndex = id;
- indexOfDefaultProperty = -1;
+ idNameIndex = idIndex;
+ id = -1;
+ indexOfDefaultPropertyOrAlias = -1;
+ defaultPropertyIsAlias = false;
+ flags = QV4::CompiledData::Object::NoFlag;
properties = pool->New<PoolList<Property> >();
+ aliases = pool->New<PoolList<Alias> >();
qmlSignals = pool->New<PoolList<Signal> >();
bindings = pool->New<PoolList<Binding> >();
functions = pool->New<PoolList<Function> >();
functionsAndExpressions = pool->New<PoolList<CompiledFunctionOrExpression> >();
- runtimeFunctionIndices = 0;
declarationsOverride = 0;
}
@@ -145,15 +148,42 @@ QString Object::appendProperty(Property *prop, const QString &propertyName, bool
const int index = target->properties->append(prop);
if (isDefaultProperty) {
- if (target->indexOfDefaultProperty != -1) {
+ if (target->indexOfDefaultPropertyOrAlias != -1) {
*errorLocation = defaultToken;
return tr("Duplicate default property");
}
- target->indexOfDefaultProperty = index;
+ target->indexOfDefaultPropertyOrAlias = index;
}
return QString(); // no error
}
+QString Object::appendAlias(Alias *alias, const QString &aliasName, bool isDefaultProperty, const QQmlJS::AST::SourceLocation &defaultToken, QQmlJS::AST::SourceLocation *errorLocation)
+{
+ Object *target = declarationsOverride;
+ if (!target)
+ target = this;
+
+ for (Alias *p = target->aliases->first; p; p = p->next)
+ if (p->nameIndex == alias->nameIndex)
+ return tr("Duplicate alias name");
+
+ if (aliasName.constData()->isUpper())
+ return tr("Alias names cannot begin with an upper case letter");
+
+ const int index = target->aliases->append(alias);
+
+ if (isDefaultProperty) {
+ if (target->indexOfDefaultPropertyOrAlias != -1) {
+ *errorLocation = defaultToken;
+ return tr("Duplicate default property");
+ }
+ target->indexOfDefaultPropertyOrAlias = index;
+ target->defaultPropertyIsAlias = true;
+ }
+
+ return QString(); // no error
+}
+
void Object::appendFunction(QmlIR::Function *f)
{
Object *target = declarationsOverride;
@@ -164,7 +194,7 @@ void Object::appendFunction(QmlIR::Function *f)
QString Object::appendBinding(Binding *b, bool isListBinding)
{
- const bool bindingToDefaultProperty = (b->propertyNameIndex == 0);
+ const bool bindingToDefaultProperty = (b->propertyNameIndex == quint32(0));
if (!isListBinding && !bindingToDefaultProperty
&& b->type != QV4::CompiledData::Binding::Type_GroupProperty
&& b->type != QV4::CompiledData::Binding::Type_AttachedProperty
@@ -190,7 +220,7 @@ Binding *Object::findBinding(quint32 nameIndex) const
void Object::insertSorted(Binding *b)
{
- Binding *insertionPoint = bindings->findSortedInsertionPoint<QV4::CompiledData::Location, QV4::CompiledData::Binding, &QV4::CompiledData::Binding::valueLocation>(b);
+ Binding *insertionPoint = bindings->findSortedInsertionPoint<quint32, Binding, &Binding::offset>(b);
bindings->insertAfter(insertionPoint, b);
}
@@ -222,32 +252,6 @@ static void replaceWithSpace(QString &str, int idx, int n)
*data++ = space;
}
-void Document::collectTypeReferences()
-{
- foreach (Object *obj, objects) {
- if (obj->inheritedTypeNameIndex != emptyStringIndex) {
- QV4::CompiledData::TypeReference &r = typeReferences.add(obj->inheritedTypeNameIndex, obj->location);
- r.needsCreation = true;
- r.errorWhenNotFound = true;
- }
-
- for (const Property *prop = obj->firstProperty(); prop; prop = prop->next) {
- if (prop->type >= QV4::CompiledData::Property::Custom) {
- // ### FIXME: We could report the more accurate location here by using prop->location, but the old
- // compiler can't and the tests expect it to be the object location right now.
- QV4::CompiledData::TypeReference &r = typeReferences.add(prop->customTypeNameIndex, obj->location);
- r.needsCreation = true;
- r.errorWhenNotFound = true;
- }
- }
-
- for (const Binding *binding = obj->firstBinding(); binding; binding = binding->next) {
- if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty)
- typeReferences.add(binding->propertyNameIndex, binding->location);
- }
- }
-}
-
void Document::removeScriptPragmas(QString &script)
{
const QLatin1String pragma("pragma");
@@ -298,7 +302,6 @@ Document::Document(bool debugMode)
, program(0)
, indexOfRootObject(0)
, jsGenerator(&jsModule)
- , unitFlags(0)
{
}
@@ -637,7 +640,10 @@ bool IRBuilder::visit(QQmlJS::AST::UiImport *node)
}
if (node->versionToken.isValid()) {
- extractVersion(textRefAt(node->versionToken), &import->majorVersion, &import->minorVersion);
+ int major, minor;
+ extractVersion(textRefAt(node->versionToken), &major, &minor);
+ import->majorVersion = major;
+ import->minorVersion = minor;
} else if (import->type == QV4::CompiledData::Import::ImportLibrary) {
recordError(node->importIdToken, QCoreApplication::translate("QQmlParser","Library import requires a version"));
return false;
@@ -807,130 +813,87 @@ bool IRBuilder::visit(QQmlJS::AST::UiPublicMember *node)
}
} else {
const QStringRef &memberType = node->memberType;
- const QStringRef &name = node->name;
-
- bool typeFound = false;
- QV4::CompiledData::Property::Type type;
-
if (memberType == QLatin1String("alias")) {
- type = QV4::CompiledData::Property::Alias;
- typeFound = true;
- }
+ return appendAlias(node);
+ } else {
+ const QStringRef &name = node->name;
- for (int ii = 0; !typeFound && ii < propTypeNameToTypesCount; ++ii) {
- const TypeNameToType *t = propTypeNameToTypes + ii;
- if (memberType == QLatin1String(t->name, static_cast<int>(t->nameLength))) {
- type = t->type;
- typeFound = true;
+ bool typeFound = false;
+ QV4::CompiledData::Property::Type type = QV4::CompiledData::Property::Var;
+
+ for (int ii = 0; !typeFound && ii < propTypeNameToTypesCount; ++ii) {
+ const TypeNameToType *t = propTypeNameToTypes + ii;
+ if (memberType == QLatin1String(t->name, static_cast<int>(t->nameLength))) {
+ type = t->type;
+ typeFound = true;
+ }
}
- }
- if (!typeFound && memberType.at(0).isUpper()) {
- const QStringRef &typeModifier = node->typeModifier;
+ if (!typeFound && memberType.at(0).isUpper()) {
+ const QStringRef &typeModifier = node->typeModifier;
- if (typeModifier.isEmpty()) {
- type = QV4::CompiledData::Property::Custom;
- } else if (typeModifier == QLatin1String("list")) {
- type = QV4::CompiledData::Property::CustomList;
- } else {
- recordError(node->typeModifierToken, QCoreApplication::translate("QQmlParser","Invalid property type modifier"));
+ if (typeModifier.isEmpty()) {
+ type = QV4::CompiledData::Property::Custom;
+ } else if (typeModifier == QLatin1String("list")) {
+ type = QV4::CompiledData::Property::CustomList;
+ } else {
+ recordError(node->typeModifierToken, QCoreApplication::translate("QQmlParser","Invalid property type modifier"));
+ return false;
+ }
+ typeFound = true;
+ } else if (!node->typeModifier.isNull()) {
+ recordError(node->typeModifierToken, QCoreApplication::translate("QQmlParser","Unexpected property type modifier"));
return false;
}
- typeFound = true;
- } else if (!node->typeModifier.isNull()) {
- recordError(node->typeModifierToken, QCoreApplication::translate("QQmlParser","Unexpected property type modifier"));
- return false;
- }
-
- if (!typeFound) {
- recordError(node->typeToken, QCoreApplication::translate("QQmlParser","Expected property type"));
- return false;
- }
-
- Property *property = New<Property>();
- property->flags = 0;
- if (node->isReadonlyMember)
- property->flags |= QV4::CompiledData::Property::IsReadOnly;
- property->type = type;
- if (type >= QV4::CompiledData::Property::Custom)
- property->customTypeNameIndex = registerString(memberType.toString());
- else
- property->customTypeNameIndex = emptyStringIndex;
-
- const QString propName = name.toString();
- property->nameIndex = registerString(propName);
- QQmlJS::AST::SourceLocation loc = node->firstSourceLocation();
- property->location.line = loc.startLine;
- property->location.column = loc.startColumn;
-
- property->aliasPropertyValueIndex = emptyStringIndex;
-
- if (type == QV4::CompiledData::Property::Alias) {
- if (!node->statement && !node->binding)
- COMPILE_EXCEPTION(loc, tr("No property alias location"));
+ if (!typeFound) {
+ recordError(node->typeToken, QCoreApplication::translate("QQmlParser","Expected property type"));
+ return false;
+ }
- QQmlJS::AST::SourceLocation rhsLoc;
- if (node->binding)
- rhsLoc = node->binding->firstSourceLocation();
- else if (node->statement)
- rhsLoc = node->statement->firstSourceLocation();
+ Property *property = New<Property>();
+ property->flags = 0;
+ if (node->isReadonlyMember)
+ property->flags |= QV4::CompiledData::Property::IsReadOnly;
+ property->type = type;
+ if (type >= QV4::CompiledData::Property::Custom)
+ property->customTypeNameIndex = registerString(memberType.toString());
else
- rhsLoc = node->semicolonToken;
- property->aliasLocation.line = rhsLoc.startLine;
- property->aliasLocation.column = rhsLoc.startColumn;
-
- QStringList alias;
-
- if (QQmlJS::AST::ExpressionStatement *stmt = QQmlJS::AST::cast<QQmlJS::AST::ExpressionStatement*>(node->statement)) {
- alias = astNodeToStringList(stmt->expression);
- if (alias.isEmpty()) {
- if (isStatementNodeScript(node->statement)) {
- COMPILE_EXCEPTION(rhsLoc, tr("Invalid alias reference. An alias reference must be specified as <id>, <id>.<property> or <id>.<value property>.<property>"));
- } else {
- COMPILE_EXCEPTION(rhsLoc, tr("Invalid alias location"));
- }
- }
- } else {
- COMPILE_EXCEPTION(rhsLoc, tr("Invalid alias reference. An alias reference must be specified as <id>, <id>.<property> or <id>.<value property>.<property>"));
- }
+ property->customTypeNameIndex = emptyStringIndex;
- if (alias.count() < 1 || alias.count() > 3)
- COMPILE_EXCEPTION(rhsLoc, tr("Invalid alias reference. An alias reference must be specified as <id>, <id>.<property> or <id>.<value property>.<property>"));
+ const QString propName = name.toString();
+ property->nameIndex = registerString(propName);
- property->aliasIdValueIndex = registerString(alias.first());
+ QQmlJS::AST::SourceLocation loc = node->firstSourceLocation();
+ property->location.line = loc.startLine;
+ property->location.column = loc.startColumn;
- QString propertyValue = alias.value(1);
- if (alias.count() == 3) {
- propertyValue += QLatin1Char('.');
- propertyValue += alias.at(2);
- }
- property->aliasPropertyValueIndex = registerString(propertyValue);
- }
- QQmlJS::AST::SourceLocation errorLocation;
- QString error;
+ QQmlJS::AST::SourceLocation errorLocation;
+ QString error;
- if (illegalNames.contains(propName))
- error = tr("Illegal property name");
- else
- error = _object->appendProperty(property, propName, node->isDefaultMember, node->defaultToken, &errorLocation);
+ if (illegalNames.contains(propName))
+ error = tr("Illegal property name");
+ else
+ error = _object->appendProperty(property, propName, node->isDefaultMember, node->defaultToken, &errorLocation);
- if (!error.isEmpty()) {
- if (errorLocation.startLine == 0)
- errorLocation = node->identifierToken;
+ if (!error.isEmpty()) {
+ if (errorLocation.startLine == 0)
+ errorLocation = node->identifierToken;
- recordError(errorLocation, error);
- return false;
- }
+ recordError(errorLocation, error);
+ return false;
+ }
- qSwap(_propertyDeclaration, property);
- if (node->binding) {
- // process QML-like initializers (e.g. property Object o: Object {})
- QQmlJS::AST::Node::accept(node->binding, this);
- } else if (node->statement && type != QV4::CompiledData::Property::Alias) {
- appendBinding(node->identifierToken, node->identifierToken, _propertyDeclaration->nameIndex, node->statement);
+ qSwap(_propertyDeclaration, property);
+ if (node->binding) {
+ // process QML-like initializers (e.g. property Object o: Object {})
+ QQmlJS::AST::Node::accept(node->binding, this);
+ } else if (node->statement) {
+ if (!isRedundantNullInitializerForPropertyDeclaration(_propertyDeclaration, node->statement))
+ appendBinding(node->identifierToken, node->identifierToken, _propertyDeclaration->nameIndex, node->statement);
+ }
+ qSwap(_propertyDeclaration, property);
}
- qSwap(_propertyDeclaration, property);
}
return false;
@@ -952,6 +915,16 @@ bool IRBuilder::visit(QQmlJS::AST::UiSourceElement *node)
f->location.column = loc.startColumn;
f->index = index;
f->nameIndex = registerString(funDecl->name.toString());
+
+ int formalsCount = 0;
+ for (QQmlJS::AST::FormalParameterList *it = funDecl->formals; it; it = it->next)
+ ++formalsCount;
+ f->formals.allocate(pool, formalsCount);
+
+ int i = 0;
+ for (QQmlJS::AST::FormalParameterList *it = funDecl->formals; it; it = it->next, ++i)
+ f->formals[i] = registerString(it->name.toString());
+
_object->appendFunction(f);
} else {
recordError(node->firstSourceLocation(), QCoreApplication::translate("QQmlParser","JavaScript declaration outside Script element"));
@@ -1027,13 +1000,13 @@ void IRBuilder::setBindingValue(QV4::CompiledData::Binding *binding, QQmlJS::AST
binding->value.b = false;
} else if (QQmlJS::AST::NumericLiteral *lit = QQmlJS::AST::cast<QQmlJS::AST::NumericLiteral *>(expr)) {
binding->type = QV4::CompiledData::Binding::Type_Number;
- binding->value.d = lit->value;
+ binding->setNumberValueInternal(lit->value);
} else {
if (QQmlJS::AST::UnaryMinusExpression *unaryMinus = QQmlJS::AST::cast<QQmlJS::AST::UnaryMinusExpression *>(expr)) {
if (QQmlJS::AST::NumericLiteral *lit = QQmlJS::AST::cast<QQmlJS::AST::NumericLiteral *>(unaryMinus->expression)) {
binding->type = QV4::CompiledData::Binding::Type_Number;
- binding->value.d = -lit->value;
+ binding->setNumberValueInternal(-lit->value);
}
}
}
@@ -1085,6 +1058,7 @@ void IRBuilder::appendBinding(const QQmlJS::AST::SourceLocation &qualifiedNameLo
{
Binding *binding = New<Binding>();
binding->propertyNameIndex = propertyNameIndex;
+ binding->offset = nameLocation.offset;
binding->location.line = nameLocation.startLine;
binding->location.column = nameLocation.startColumn;
binding->flags = 0;
@@ -1104,6 +1078,7 @@ void IRBuilder::appendBinding(const QQmlJS::AST::SourceLocation &qualifiedNameLo
Binding *binding = New<Binding>();
binding->propertyNameIndex = propertyNameIndex;
+ binding->offset = nameLocation.offset;
binding->location.line = nameLocation.startLine;
binding->location.column = nameLocation.startColumn;
@@ -1133,6 +1108,79 @@ void IRBuilder::appendBinding(const QQmlJS::AST::SourceLocation &qualifiedNameLo
}
}
+bool IRBuilder::appendAlias(QQmlJS::AST::UiPublicMember *node)
+{
+ Alias *alias = New<Alias>();
+ alias->flags = 0;
+ if (node->isReadonlyMember)
+ alias->flags |= QV4::CompiledData::Alias::IsReadOnly;
+
+ const QString propName = node->name.toString();
+ alias->nameIndex = registerString(propName);
+
+ QQmlJS::AST::SourceLocation loc = node->firstSourceLocation();
+ alias->location.line = loc.startLine;
+ alias->location.column = loc.startColumn;
+
+ alias->propertyNameIndex = emptyStringIndex;
+
+ if (!node->statement && !node->binding)
+ COMPILE_EXCEPTION(loc, tr("No property alias location"));
+
+ QQmlJS::AST::SourceLocation rhsLoc;
+ if (node->binding)
+ rhsLoc = node->binding->firstSourceLocation();
+ else if (node->statement)
+ rhsLoc = node->statement->firstSourceLocation();
+ else
+ rhsLoc = node->semicolonToken;
+ alias->referenceLocation.line = rhsLoc.startLine;
+ alias->referenceLocation.column = rhsLoc.startColumn;
+
+ QStringList aliasReference;
+
+ if (QQmlJS::AST::ExpressionStatement *stmt = QQmlJS::AST::cast<QQmlJS::AST::ExpressionStatement*>(node->statement)) {
+ aliasReference = astNodeToStringList(stmt->expression);
+ if (aliasReference.isEmpty()) {
+ if (isStatementNodeScript(node->statement)) {
+ COMPILE_EXCEPTION(rhsLoc, tr("Invalid alias reference. An alias reference must be specified as <id>, <id>.<property> or <id>.<value property>.<property>"));
+ } else {
+ COMPILE_EXCEPTION(rhsLoc, tr("Invalid alias location"));
+ }
+ }
+ } else {
+ COMPILE_EXCEPTION(rhsLoc, tr("Invalid alias reference. An alias reference must be specified as <id>, <id>.<property> or <id>.<value property>.<property>"));
+ }
+
+ if (aliasReference.count() < 1 || aliasReference.count() > 3)
+ COMPILE_EXCEPTION(rhsLoc, tr("Invalid alias reference. An alias reference must be specified as <id>, <id>.<property> or <id>.<value property>.<property>"));
+
+ alias->idIndex = registerString(aliasReference.first());
+
+ QString propertyValue = aliasReference.value(1);
+ if (aliasReference.count() == 3)
+ propertyValue += QLatin1Char('.') + aliasReference.at(2);
+ alias->propertyNameIndex = registerString(propertyValue);
+
+ QQmlJS::AST::SourceLocation errorLocation;
+ QString error;
+
+ if (illegalNames.contains(propName))
+ error = tr("Illegal property name");
+ else
+ error = _object->appendAlias(alias, propName, node->isDefaultMember, node->defaultToken, &errorLocation);
+
+ if (!error.isEmpty()) {
+ if (errorLocation.startLine == 0)
+ errorLocation = node->identifierToken;
+
+ recordError(errorLocation, error);
+ return false;
+ }
+
+ return false;
+}
+
Object *IRBuilder::bindingsTarget() const
{
if (_propertyDeclaration && _object->declarationsOverride)
@@ -1178,10 +1226,10 @@ bool IRBuilder::setId(const QQmlJS::AST::SourceLocation &idLocation, QQmlJS::AST
if (illegalNames.contains(idQString))
COMPILE_EXCEPTION(loc, tr( "ID illegally masks global JavaScript property"));
- if (_object->idIndex != emptyStringIndex)
+ if (_object->idNameIndex != emptyStringIndex)
COMPILE_EXCEPTION(idLocation, tr("Property value set multiple times"));
- _object->idIndex = registerString(idQString);
+ _object->idNameIndex = registerString(idQString);
_object->locationOfIdProperty.line = idLocation.startLine;
_object->locationOfIdProperty.column = idLocation.startColumn;
@@ -1202,8 +1250,7 @@ bool IRBuilder::resolveQualifiedId(QQmlJS::AST::UiQualifiedId **nameToResolve, O
if (import->qualifierIndex != emptyStringIndex
&& stringAt(import->qualifierIndex) == currentName) {
qualifiedIdElement = qualifiedIdElement->next;
- currentName += QLatin1Char('.');
- currentName += qualifiedIdElement->name;
+ currentName += QLatin1Char('.') + qualifiedIdElement->name;
if (!qualifiedIdElement->name.unicode()->isUpper())
COMPILE_EXCEPTION(qualifiedIdElement->firstSourceLocation(), tr("Expected type name"));
@@ -1229,6 +1276,7 @@ bool IRBuilder::resolveQualifiedId(QQmlJS::AST::UiQualifiedId **nameToResolve, O
if (!binding) {
binding = New<Binding>();
binding->propertyNameIndex = propertyNameIndex;
+ binding->offset = qualifiedIdElement->identifierToken.offset;
binding->location.line = qualifiedIdElement->identifierToken.startLine;
binding->location.column = qualifiedIdElement->identifierToken.startColumn;
binding->valueLocation.line = qualifiedIdElement->next->identifierToken.startLine;
@@ -1300,7 +1348,18 @@ bool IRBuilder::isStatementNodeScript(QQmlJS::AST::Statement *statement)
return true;
}
-QV4::CompiledData::Unit *QmlUnitGenerator::generate(Document &output)
+bool IRBuilder::isRedundantNullInitializerForPropertyDeclaration(Property *property, QQmlJS::AST::Statement *statement)
+{
+ if (property->type != QV4::CompiledData::Property::Custom)
+ return false;
+ QQmlJS::AST::ExpressionStatement *exprStmt = QQmlJS::AST::cast<QQmlJS::AST::ExpressionStatement *>(statement);
+ if (!exprStmt)
+ return false;
+ QQmlJS::AST::ExpressionNode * const expr = exprStmt->expression;
+ return QQmlJS::AST::cast<QQmlJS::AST::NullExpression *>(expr);
+}
+
+QV4::CompiledData::Unit *QmlUnitGenerator::generate(Document &output, QQmlEngine *engine, const QV4::CompiledData::ResolvedTypeReferenceMap &dependentTypes)
{
QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit = output.javaScriptCompilationUnit;
QV4::CompiledData::Unit *jsUnit = compilationUnit->createUnitData(&output);
@@ -1309,12 +1368,12 @@ QV4::CompiledData::Unit *QmlUnitGenerator::generate(Document &output)
const int importSize = sizeof(QV4::CompiledData::Import) * output.imports.count();
const int objectOffsetTableSize = output.objects.count() * sizeof(quint32);
- QHash<Object*, quint32> objectOffsets;
+ QHash<const Object*, quint32> objectOffsets;
int objectsSize = 0;
foreach (Object *o, output.objects) {
objectOffsets.insert(o, unitSize + importSize + objectOffsetTableSize + objectsSize);
- objectsSize += QV4::CompiledData::Object::calculateSizeExcludingSignals(o->functionCount(), o->propertyCount(), o->signalCount(), o->bindingCount());
+ objectsSize += QV4::CompiledData::Object::calculateSizeExcludingSignals(o->functionCount(), o->propertyCount(), o->aliasCount(), o->signalCount(), o->bindingCount(), o->namedObjectsInComponent.count);
int signalTableSize = 0;
for (const Signal *s = o->firstSignal(); s; s = s->next)
@@ -1333,7 +1392,6 @@ QV4::CompiledData::Unit *QmlUnitGenerator::generate(Document &output)
QV4::CompiledData::Unit *qmlUnit = reinterpret_cast<QV4::CompiledData::Unit *>(data);
qmlUnit->unitSize = totalSize;
- qmlUnit->flags |= output.unitFlags;
qmlUnit->flags |= QV4::CompiledData::Unit::IsQml;
qmlUnit->offsetToImports = unitSize;
qmlUnit->nImports = output.imports.count();
@@ -1343,6 +1401,20 @@ QV4::CompiledData::Unit *QmlUnitGenerator::generate(Document &output)
qmlUnit->offsetToStringTable = totalSize - output.jsGenerator.stringTable.sizeOfTableAndData();
qmlUnit->stringTableSize = output.jsGenerator.stringTable.stringCount();
+#ifndef V4_BOOTSTRAP
+ if (!dependentTypes.isEmpty()) {
+ QCryptographicHash hash(QCryptographicHash::Md5);
+ if (dependentTypes.addToHash(&hash, engine)) {
+ QByteArray checksum = hash.result();
+ Q_ASSERT(checksum.size() == sizeof(qmlUnit->dependencyMD5Checksum));
+ memcpy(qmlUnit->dependencyMD5Checksum, checksum.constData(), sizeof(qmlUnit->dependencyMD5Checksum));
+ }
+ }
+#else
+ Q_UNUSED(dependentTypes);
+ Q_UNUSED(engine);
+#endif
+
// write imports
char *importPtr = data + qmlUnit->offsetToImports;
foreach (const QV4::CompiledData::Import *imp, output.imports) {
@@ -1354,13 +1426,17 @@ QV4::CompiledData::Unit *QmlUnitGenerator::generate(Document &output)
// write objects
quint32 *objectTable = reinterpret_cast<quint32*>(data + qmlUnit->offsetToObjects);
char *objectPtr = data + qmlUnit->offsetToObjects + objectOffsetTableSize;
- foreach (Object *o, output.objects) {
+ for (int i = 0; i < output.objects.count(); ++i) {
+ const Object *o = output.objects.at(i);
*objectTable++ = objectOffsets.value(o);
QV4::CompiledData::Object *objectToWrite = reinterpret_cast<QV4::CompiledData::Object*>(objectPtr);
objectToWrite->inheritedTypeNameIndex = o->inheritedTypeNameIndex;
- objectToWrite->indexOfDefaultProperty = o->indexOfDefaultProperty;
- objectToWrite->idIndex = o->idIndex;
+ objectToWrite->indexOfDefaultPropertyOrAlias = o->indexOfDefaultPropertyOrAlias;
+ objectToWrite->defaultPropertyIsAlias = o->defaultPropertyIsAlias;
+ objectToWrite->flags = o->flags;
+ objectToWrite->idNameIndex = o->idNameIndex;
+ objectToWrite->id = o->id;
objectToWrite->location = o->location;
objectToWrite->locationOfIdProperty = o->locationOfIdProperty;
@@ -1374,6 +1450,10 @@ QV4::CompiledData::Unit *QmlUnitGenerator::generate(Document &output)
objectToWrite->offsetToProperties = nextOffset;
nextOffset += objectToWrite->nProperties * sizeof(QV4::CompiledData::Property);
+ objectToWrite->nAliases = o->aliasCount();
+ objectToWrite->offsetToAliases = nextOffset;
+ nextOffset += objectToWrite->nAliases * sizeof(QV4::CompiledData::Alias);
+
objectToWrite->nSignals = o->signalCount();
objectToWrite->offsetToSignals = nextOffset;
nextOffset += objectToWrite->nSignals * sizeof(quint32);
@@ -1382,9 +1462,13 @@ QV4::CompiledData::Unit *QmlUnitGenerator::generate(Document &output)
objectToWrite->offsetToBindings = nextOffset;
nextOffset += objectToWrite->nBindings * sizeof(QV4::CompiledData::Binding);
+ objectToWrite->nNamedObjectsInComponent = o->namedObjectsInComponent.count;
+ objectToWrite->offsetToNamedObjectsInComponent = nextOffset;
+ nextOffset += objectToWrite->nNamedObjectsInComponent * sizeof(quint32);
+
quint32 *functionsTable = reinterpret_cast<quint32*>(objectPtr + objectToWrite->offsetToFunctions);
for (const Function *f = o->firstFunction(); f; f = f->next)
- *functionsTable++ = o->runtimeFunctionIndices->at(f->index);
+ *functionsTable++ = o->runtimeFunctionIndices.at(f->index);
char *propertiesPtr = objectPtr + objectToWrite->offsetToProperties;
for (const Property *p = o->firstProperty(); p; p = p->next) {
@@ -1393,6 +1477,13 @@ QV4::CompiledData::Unit *QmlUnitGenerator::generate(Document &output)
propertiesPtr += sizeof(QV4::CompiledData::Property);
}
+ char *aliasesPtr = objectPtr + objectToWrite->offsetToAliases;
+ for (const Alias *a = o->firstAlias(); a; a = a->next) {
+ QV4::CompiledData::Alias *aliasToWrite = reinterpret_cast<QV4::CompiledData::Alias*>(aliasesPtr);
+ *aliasToWrite = *a;
+ aliasesPtr += sizeof(QV4::CompiledData::Alias);
+ }
+
char *bindingPtr = objectPtr + objectToWrite->offsetToBindings;
bindingPtr = writeBindings(bindingPtr, o, &QV4::CompiledData::Binding::isValueBindingNoAlias);
bindingPtr = writeBindings(bindingPtr, o, &QV4::CompiledData::Binding::isSignalHandler);
@@ -1421,7 +1512,12 @@ QV4::CompiledData::Unit *QmlUnitGenerator::generate(Document &output)
signalPtr += size;
}
- objectPtr += QV4::CompiledData::Object::calculateSizeExcludingSignals(o->functionCount(), o->propertyCount(), o->signalCount(), o->bindingCount());
+ quint32 *namedObjectInComponentPtr = reinterpret_cast<quint32*>(objectPtr + objectToWrite->offsetToNamedObjectsInComponent);
+ for (int i = 0; i < o->namedObjectsInComponent.count; ++i) {
+ *namedObjectInComponentPtr++ = o->namedObjectsInComponent.at(i);
+ }
+
+ objectPtr += QV4::CompiledData::Object::calculateSizeExcludingSignals(o->functionCount(), o->propertyCount(), o->aliasCount(), o->signalCount(), o->bindingCount(), o->namedObjectsInComponent.count);
objectPtr += signalTableSize;
}
@@ -1435,10 +1531,12 @@ QV4::CompiledData::Unit *QmlUnitGenerator::generate(Document &output)
output.jsGenerator.stringTable.serialize(qmlUnit);
+ qmlUnit->generateChecksum();
+
return qmlUnit;
}
-char *QmlUnitGenerator::writeBindings(char *bindingPtr, Object *o, BindingFilter filter) const
+char *QmlUnitGenerator::writeBindings(char *bindingPtr, const Object *o, BindingFilter filter) const
{
for (const Binding *b = o->firstBinding(); b; b = b->next) {
if (!(b->*(filter))())
@@ -1446,7 +1544,7 @@ char *QmlUnitGenerator::writeBindings(char *bindingPtr, Object *o, BindingFilter
QV4::CompiledData::Binding *bindingToWrite = reinterpret_cast<QV4::CompiledData::Binding*>(bindingPtr);
*bindingToWrite = *b;
if (b->type == QV4::CompiledData::Binding::Type_Script)
- bindingToWrite->value.compiledScriptIndex = o->runtimeFunctionIndices->at(b->value.compiledScriptIndex);
+ bindingToWrite->value.compiledScriptIndex = o->runtimeFunctionIndices.at(b->value.compiledScriptIndex);
bindingPtr += sizeof(QV4::CompiledData::Binding);
}
return bindingPtr;
@@ -1614,7 +1712,7 @@ static QV4::IR::DiscoveredType resolveQmlType(QQmlEnginePrivate *qmlEngine,
if (tdata->isComplete()) {
auto newResolver = resolver->owner->New<QV4::IR::MemberExpressionResolver>();
newResolver->owner = resolver->owner;
- initMetaObjectResolver(newResolver, qmlEngine->propertyCacheForType(tdata->compiledData()->metaTypeId));
+ initMetaObjectResolver(newResolver, qmlEngine->propertyCacheForType(tdata->compilationUnit()->metaTypeId));
newResolver->flags |= AllPropertiesAreFinal;
return newResolver->resolveMember(qmlEngine, newResolver, member);
}
@@ -1627,7 +1725,11 @@ static QV4::IR::DiscoveredType resolveQmlType(QQmlEnginePrivate *qmlEngine,
member->kind = QV4::IR::Member::MemberOfSingletonObject;
return newResolver->resolveMember(qmlEngine, newResolver, member);
}
- } else if (const QMetaObject *attachedMeta = type->attachedPropertiesType(qmlEngine)) {
+ }
+#if 0
+ else if (const QMetaObject *attachedMeta = type->attachedPropertiesType(qmlEngine)) {
+ // Right now the attached property IDs are not stable and cannot be embedded in the
+ // code that is cached on disk.
QQmlPropertyCache *cache = qmlEngine->cache(attachedMeta);
auto newResolver = resolver->owner->New<QV4::IR::MemberExpressionResolver>();
newResolver->owner = resolver->owner;
@@ -1635,6 +1737,7 @@ static QV4::IR::DiscoveredType resolveQmlType(QQmlEnginePrivate *qmlEngine,
member->setAttachedPropertiesId(type->attachedPropertiesId(qmlEngine));
return newResolver->resolveMember(qmlEngine, newResolver, member);
}
+#endif
return result;
}
@@ -1742,20 +1845,20 @@ static QV4::IR::DiscoveredType resolveMetaObjectProperty(
if (property->isEnum())
return QV4::IR::VarType;
- switch (property->propType) {
+ switch (property->propType()) {
case QMetaType::Bool: result = QV4::IR::BoolType; break;
case QMetaType::Int: result = QV4::IR::SInt32Type; break;
case QMetaType::Double: result = QV4::IR::DoubleType; break;
case QMetaType::QString: result = QV4::IR::StringType; break;
default:
if (property->isQObject()) {
- if (QQmlPropertyCache *cache = qmlEngine->propertyCacheForType(property->propType)) {
+ if (QQmlPropertyCache *cache = qmlEngine->propertyCacheForType(property->propType())) {
auto newResolver = resolver->owner->New<QV4::IR::MemberExpressionResolver>();
newResolver->owner = resolver->owner;
initMetaObjectResolver(newResolver, cache);
return QV4::IR::DiscoveredType(newResolver);
}
- } else if (const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(property->propType)) {
+ } else if (const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(property->propType())) {
if (QQmlPropertyCache *cache = qmlEngine->cache(valueTypeMetaObject)) {
auto newResolver = resolver->owner->New<QV4::IR::MemberExpressionResolver>();
newResolver->owner = resolver->owner;
@@ -1822,7 +1925,9 @@ QV4::IR::Expr *JSCodeGen::fallbackNameLookup(const QString &name, int line, int
// Look for IDs first.
foreach (const IdMapping &mapping, _idObjects)
if (name == mapping.name) {
- _function->idObjectDependencies.insert(mapping.idIndex);
+ if (_function->isQmlBinding)
+ _function->idObjectDependencies.insert(mapping.idIndex);
+
QV4::IR::Expr *s = _block->MEMBER(_block->TEMP(_qmlContextTemp), _function->newString(name), 0, QV4::IR::Member::MemberOfIdObjectsArray, mapping.idIndex);
QV4::IR::Temp *result = _block->TEMP(_block->newTemp());
_block->MOVE(result, s);
@@ -1907,7 +2012,7 @@ QV4::IR::Expr *JSCodeGen::fallbackNameLookup(const QString &name, int line, int
#ifndef V4_BOOTSTRAP
-QQmlPropertyData *PropertyResolver::property(const QString &name, bool *notInRevision, RevisionCheck check)
+QQmlPropertyData *PropertyResolver::property(const QString &name, bool *notInRevision, RevisionCheck check) const
{
if (notInRevision) *notInRevision = false;
@@ -1926,7 +2031,7 @@ QQmlPropertyData *PropertyResolver::property(const QString &name, bool *notInRev
}
-QQmlPropertyData *PropertyResolver::signal(const QString &name, bool *notInRevision)
+QQmlPropertyData *PropertyResolver::signal(const QString &name, bool *notInRevision) const
{
if (notInRevision) *notInRevision = false;
@@ -1948,7 +2053,7 @@ QQmlPropertyData *PropertyResolver::signal(const QString &name, bool *notInRevis
d = property(propName, notInRevision);
if (d)
- return cache->signal(d->notifyIndex);
+ return cache->signal(d->notifyIndex());
}
return 0;
diff --git a/src/qml/compiler/qqmlirbuilder_p.h b/src/qml/compiler/qqmlirbuilder_p.h
index 057ed1be9f..cc16dc2104 100644
--- a/src/qml/compiler/qqmlirbuilder_p.h
+++ b/src/qml/compiler/qqmlirbuilder_p.h
@@ -113,7 +113,7 @@ struct PoolList
T *insertPos = 0;
for (T *it = first; it; it = it->next) {
- if (!(it->*sortMember < item->*sortMember))
+ if (!(it->*sortMember <= item->*sortMember))
break;
insertPos = it;
}
@@ -161,6 +161,43 @@ struct PoolList
}
return result;
}
+
+ struct Iterator {
+ T *ptr;
+
+ explicit Iterator(T *p) : ptr(p) {}
+
+ T *operator->() {
+ return ptr;
+ }
+
+ const T *operator->() const {
+ return ptr;
+ }
+
+ T &operator*() {
+ return *ptr;
+ }
+
+ const T &operator*() const {
+ return *ptr;
+ }
+
+ void operator++() {
+ ptr = ptr->next;
+ }
+
+ bool operator==(const Iterator &rhs) const {
+ return ptr == rhs.ptr;
+ }
+
+ bool operator!=(const Iterator &rhs) const {
+ return ptr != rhs.ptr;
+ }
+ };
+
+ Iterator begin() { return Iterator(first); }
+ Iterator end() { return Iterator(nullptr); }
};
template <typename T>
@@ -170,7 +207,18 @@ class FixedPoolArray
public:
int count;
- void init(QQmlJS::MemoryPool *pool, const QVector<T> &vector)
+ FixedPoolArray()
+ : data(0)
+ , count(0)
+ {}
+
+ void allocate(QQmlJS::MemoryPool *pool, int size)
+ {
+ count = size;
+ data = reinterpret_cast<T*>(pool->allocate(count * sizeof(T)));
+ }
+
+ void allocate(QQmlJS::MemoryPool *pool, const QVector<T> &vector)
{
count = vector.count();
data = reinterpret_cast<T*>(pool->allocate(count * sizeof(T)));
@@ -183,6 +231,16 @@ public:
}
}
+ template <typename Container>
+ void allocate(QQmlJS::MemoryPool *pool, const Container &container)
+ {
+ count = container.count();
+ data = reinterpret_cast<T*>(pool->allocate(count * sizeof(T)));
+ typename Container::ConstIterator it = container.constBegin();
+ for (int i = 0; i < count; ++i)
+ new (data + i) T(*it++);
+ }
+
const T &at(int index) const {
Q_ASSERT(index >= 0 && index < count);
return data[index];
@@ -200,6 +258,9 @@ public:
return i;
return -1;
}
+
+ const T *begin() const { return data; }
+ const T *end() const { return data + count; }
};
struct Object;
@@ -217,6 +278,10 @@ struct Signal
QStringList parameterStringList(const QV4::Compiler::StringTableGenerator *stringPool) const;
+ int parameterCount() const { return parameters->count; }
+ PoolList<SignalParameter>::Iterator parametersBegin() const { return parameters->begin(); }
+ PoolList<SignalParameter>::Iterator parametersEnd() const { return parameters->end(); }
+
Signal *next;
};
@@ -227,16 +292,32 @@ struct Property : public QV4::CompiledData::Property
struct Binding : public QV4::CompiledData::Binding
{
+ // The offset in the source file where the binding appeared. This is used for sorting to ensure
+ // that assignments to list properties are done in the correct order. We use the offset here instead
+ // of Binding::location as the latter has limited precision.
+ quint32 offset;
// Binding's compiledScriptIndex is index in object's functionsAndExpressions
Binding *next;
};
+struct Alias : public QV4::CompiledData::Alias
+{
+ Alias *next;
+};
+
struct Function
{
QQmlJS::AST::FunctionDeclaration *functionDeclaration;
QV4::CompiledData::Location location;
int nameIndex;
quint32 index; // index in parsedQML::functions
+ FixedPoolArray<int> formals;
+
+ // --- QQmlPropertyCacheCreator interface
+ const int *formalsBegin() const { return formals.begin(); }
+ const int *formalsEnd() const { return formals.end(); }
+ // ---
+
Function *next;
};
@@ -265,14 +346,19 @@ struct Q_QML_PRIVATE_EXPORT Object
Q_DECLARE_TR_FUNCTIONS(Object)
public:
quint32 inheritedTypeNameIndex;
- quint32 idIndex;
- int indexOfDefaultProperty;
+ quint32 idNameIndex;
+ int id;
+ int indexOfDefaultPropertyOrAlias;
+ bool defaultPropertyIsAlias;
+ quint32 flags;
QV4::CompiledData::Location location;
QV4::CompiledData::Location locationOfIdProperty;
const Property *firstProperty() const { return properties->first; }
int propertyCount() const { return properties->count; }
+ Alias *firstAlias() const { return aliases->first; }
+ int aliasCount() const { return aliases->count; }
const Signal *firstSignal() const { return qmlSignals->first; }
int signalCount() const { return qmlSignals->count; }
Binding *firstBinding() const { return bindings->first; }
@@ -280,16 +366,28 @@ public:
const Function *firstFunction() const { return functions->first; }
int functionCount() const { return functions->count; }
+ PoolList<Binding>::Iterator bindingsBegin() const { return bindings->begin(); }
+ PoolList<Binding>::Iterator bindingsEnd() const { return bindings->end(); }
+ PoolList<Property>::Iterator propertiesBegin() const { return properties->begin(); }
+ PoolList<Property>::Iterator propertiesEnd() const { return properties->end(); }
+ PoolList<Alias>::Iterator aliasesBegin() const { return aliases->begin(); }
+ PoolList<Alias>::Iterator aliasesEnd() const { return aliases->end(); }
+ PoolList<Signal>::Iterator signalsBegin() const { return qmlSignals->begin(); }
+ PoolList<Signal>::Iterator signalsEnd() const { return qmlSignals->end(); }
+ PoolList<Function>::Iterator functionsBegin() const { return functions->begin(); }
+ PoolList<Function>::Iterator functionsEnd() const { return functions->end(); }
+
// If set, then declarations for this object (and init bindings for these) should go into the
// specified object. Used for declarations inside group properties.
Object *declarationsOverride;
- void init(QQmlJS::MemoryPool *pool, int typeNameIndex, int id, const QQmlJS::AST::SourceLocation &location = QQmlJS::AST::SourceLocation());
+ void init(QQmlJS::MemoryPool *pool, int typeNameIndex, int idIndex, const QQmlJS::AST::SourceLocation &location = QQmlJS::AST::SourceLocation());
QString sanityCheckFunctionNames(const QSet<QString> &illegalNames, QQmlJS::AST::SourceLocation *errorLocation);
QString appendSignal(Signal *signal);
QString appendProperty(Property *prop, const QString &propertyName, bool isDefaultProperty, const QQmlJS::AST::SourceLocation &defaultToken, QQmlJS::AST::SourceLocation *errorLocation);
+ QString appendAlias(Alias *prop, const QString &aliasName, bool isDefaultProperty, const QQmlJS::AST::SourceLocation &defaultToken, QQmlJS::AST::SourceLocation *errorLocation);
void appendFunction(QmlIR::Function *f);
QString appendBinding(Binding *b, bool isListBinding);
@@ -299,12 +397,17 @@ public:
QString bindingAsString(Document *doc, int scriptIndex) const;
PoolList<CompiledFunctionOrExpression> *functionsAndExpressions;
- FixedPoolArray<int> *runtimeFunctionIndices;
+ FixedPoolArray<int> runtimeFunctionIndices;
+
+ FixedPoolArray<quint32> namedObjectsInComponent;
+ int namedObjectsInComponentCount() const { return namedObjectsInComponent.count; }
+ const quint32 *namedObjectsInComponentTable() const { return namedObjectsInComponent.begin(); }
private:
friend struct IRLoader;
PoolList<Property> *properties;
+ PoolList<Alias> *aliases;
PoolList<Signal> *qmlSignals;
PoolList<Binding> *bindings;
PoolList<Function> *functions;
@@ -330,15 +433,10 @@ struct Q_QML_PRIVATE_EXPORT Document
QList<Pragma*> pragmas;
QQmlJS::AST::UiProgram *program;
int indexOfRootObject;
- QList<Object*> objects;
+ QVector<Object*> objects;
QV4::Compiler::JSUnitGenerator jsGenerator;
- quint32 unitFlags;
QQmlRefPointer<QV4::CompiledData::CompilationUnit> javaScriptCompilationUnit;
- QHash<int, QStringList> extraSignalParameters;
-
- QV4::CompiledData::TypeReferenceMap typeReferences;
- void collectTypeReferences();
int registerString(const QString &str) { return jsGenerator.registerString(str); }
QString stringAt(int index) const { return jsGenerator.stringForIndex(index); }
@@ -410,6 +508,8 @@ public:
void appendBinding(const QQmlJS::AST::SourceLocation &qualifiedNameLocation, const QQmlJS::AST::SourceLocation &nameLocation, quint32 propertyNameIndex, QQmlJS::AST::Statement *value);
void appendBinding(const QQmlJS::AST::SourceLocation &qualifiedNameLocation, const QQmlJS::AST::SourceLocation &nameLocation, quint32 propertyNameIndex, int objectIndex, bool isListItem = false, bool isOnAssignment = false);
+ bool appendAlias(QQmlJS::AST::UiPublicMember *node);
+
Object *bindingsTarget() const;
bool setId(const QQmlJS::AST::SourceLocation &idLocation, QQmlJS::AST::Statement *value);
@@ -426,6 +526,7 @@ public:
QString stringAt(int index) const { return jsGenerator->stringForIndex(index); }
static bool isStatementNodeScript(QQmlJS::AST::Statement *statement);
+ static bool isRedundantNullInitializerForPropertyDeclaration(Property *property, QQmlJS::AST::Statement *statement);
QList<QQmlJS::DiagnosticMessage> errors;
@@ -433,7 +534,7 @@ public:
QList<const QV4::CompiledData::Import *> _imports;
QList<Pragma*> _pragmas;
- QList<Object*> _objects;
+ QVector<Object*> _objects;
QV4::CompiledData::TypeReferenceMap _typeReferences;
@@ -447,21 +548,21 @@ public:
struct Q_QML_PRIVATE_EXPORT QmlUnitGenerator
{
- QV4::CompiledData::Unit *generate(Document &output);
+ QV4::CompiledData::Unit *generate(Document &output, QQmlEngine *engine, const QV4::CompiledData::ResolvedTypeReferenceMap &dependentTypes);
private:
typedef bool (Binding::*BindingFilter)() const;
- char *writeBindings(char *bindingPtr, Object *o, BindingFilter filter) const;
+ char *writeBindings(char *bindingPtr, const Object *o, BindingFilter filter) const;
};
#ifndef V4_BOOTSTRAP
struct Q_QML_EXPORT PropertyResolver
{
- PropertyResolver(QQmlPropertyCache *cache)
+ PropertyResolver(const QQmlPropertyCache *cache)
: cache(cache)
{}
- QQmlPropertyData *property(int index)
+ QQmlPropertyData *property(int index) const
{
return cache->property(index);
}
@@ -471,12 +572,12 @@ struct Q_QML_EXPORT PropertyResolver
IgnoreRevision
};
- QQmlPropertyData *property(const QString &name, bool *notInRevision = 0, RevisionCheck check = CheckRevision);
+ QQmlPropertyData *property(const QString &name, bool *notInRevision = 0, RevisionCheck check = CheckRevision) const;
// This code must match the semantics of QQmlPropertyPrivate::findSignalByName
- QQmlPropertyData *signal(const QString &name, bool *notInRevision);
+ QQmlPropertyData *signal(const QString &name, bool *notInRevision) const;
- QQmlPropertyCache *cache;
+ const QQmlPropertyCache *cache;
};
#endif
diff --git a/src/qml/compiler/qqmlpropertycachecreator.cpp b/src/qml/compiler/qqmlpropertycachecreator.cpp
new file mode 100644
index 0000000000..f8d63ec634
--- /dev/null
+++ b/src/qml/compiler/qqmlpropertycachecreator.cpp
@@ -0,0 +1,71 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the tools applications 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 "qqmlpropertycachecreator_p.h"
+
+#include <private/qqmlengine_p.h>
+
+QT_BEGIN_NAMESPACE
+
+QAtomicInt QQmlPropertyCacheCreatorBase::classIndexCounter(0);
+
+QQmlBindingInstantiationContext::QQmlBindingInstantiationContext()
+ : referencingObjectIndex(-1)
+ , instantiatingBinding(nullptr)
+ , instantiatingProperty(nullptr)
+{
+
+}
+
+QQmlBindingInstantiationContext::QQmlBindingInstantiationContext(int referencingObjectIndex, const QV4::CompiledData::Binding *instantiatingBinding, const QString &instantiatingPropertyName, const QQmlPropertyCache *referencingObjectPropertyCache)
+ : referencingObjectIndex(referencingObjectIndex)
+ , instantiatingBinding(instantiatingBinding)
+ , instantiatingProperty(nullptr)
+{
+ if (instantiatingBinding && instantiatingBinding->type == QV4::CompiledData::Binding::Type_GroupProperty) {
+ Q_ASSERT(referencingObjectIndex >= 0);
+ Q_ASSERT(referencingObjectPropertyCache);
+ Q_ASSERT(instantiatingBinding->propertyNameIndex != 0);
+
+ bool notInRevision = false;
+ instantiatingProperty = QmlIR::PropertyResolver(referencingObjectPropertyCache).property(instantiatingPropertyName, &notInRevision);
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/qml/compiler/qqmlpropertycachecreator_p.h b/src/qml/compiler/qqmlpropertycachecreator_p.h
new file mode 100644
index 0000000000..10bcd1dbc1
--- /dev/null
+++ b/src/qml/compiler/qqmlpropertycachecreator_p.h
@@ -0,0 +1,741 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the tools applications 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 QQMLPROPERTYCACHECREATOR_P_H
+#define QQMLPROPERTYCACHECREATOR_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 "qqmltypecompiler_p.h"
+#include <private/qqmlvaluetype_p.h>
+#include <private/qqmlengine_p.h>
+
+QT_BEGIN_NAMESPACE
+
+struct QQmlBindingInstantiationContext {
+ QQmlBindingInstantiationContext();
+ QQmlBindingInstantiationContext(int referencingObjectIndex, const QV4::CompiledData::Binding *instantiatingBinding, const QString &instantiatingPropertyName, const QQmlPropertyCache *referencingObjectPropertyCache);
+ int referencingObjectIndex;
+ const QV4::CompiledData::Binding *instantiatingBinding;
+ QQmlPropertyData *instantiatingProperty;
+};
+
+struct QQmlPropertyCacheCreatorBase
+{
+ Q_DECLARE_TR_FUNCTIONS(QQmlPropertyCacheCreatorBase)
+public:
+ static QAtomicInt classIndexCounter;
+};
+
+template <typename ObjectContainer>
+class QQmlPropertyCacheCreator : public QQmlPropertyCacheCreatorBase
+{
+public:
+ typedef typename ObjectContainer::CompiledObject CompiledObject;
+
+ QQmlPropertyCacheCreator(QQmlPropertyCacheVector *propertyCaches, QQmlEnginePrivate *enginePrivate, const ObjectContainer *objectContainer, const QQmlImports *imports);
+
+ QQmlCompileError buildMetaObjects();
+
+protected:
+ QQmlCompileError buildMetaObjectRecursively(int objectIndex, const QQmlBindingInstantiationContext &context);
+ QQmlPropertyCache *propertyCacheForObject(const CompiledObject *obj, const QQmlBindingInstantiationContext &context, QQmlCompileError *error) const;
+ QQmlCompileError createMetaObject(int objectIndex, const CompiledObject *obj, QQmlPropertyCache *baseTypeCache);
+
+ QString stringAt(int index) const { return objectContainer->stringAt(index); }
+
+ QQmlEnginePrivate * const enginePrivate;
+ const ObjectContainer * const objectContainer;
+ const QQmlImports * const imports;
+ QQmlPropertyCacheVector *propertyCaches;
+};
+
+template <typename ObjectContainer>
+inline QQmlPropertyCacheCreator<ObjectContainer>::QQmlPropertyCacheCreator(QQmlPropertyCacheVector *propertyCaches, QQmlEnginePrivate *enginePrivate, const ObjectContainer *objectContainer, const QQmlImports *imports)
+ : enginePrivate(enginePrivate)
+ , objectContainer(objectContainer)
+ , imports(imports)
+ , propertyCaches(propertyCaches)
+{
+ propertyCaches->resize(objectContainer->objectCount());
+}
+
+template <typename ObjectContainer>
+inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObjects()
+{
+ QQmlBindingInstantiationContext context;
+ return buildMetaObjectRecursively(objectContainer->rootObjectIndex(), context);
+}
+
+template <typename ObjectContainer>
+inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObjectRecursively(int objectIndex, const QQmlBindingInstantiationContext &context)
+{
+ const CompiledObject *obj = objectContainer->objectAt(objectIndex);
+
+ bool needVMEMetaObject = obj->propertyCount() != 0 || obj->aliasCount() != 0 || obj->signalCount() != 0 || obj->functionCount() != 0;
+ if (!needVMEMetaObject) {
+ for (auto binding = obj->bindingsBegin(), end = obj->bindingsEnd(); binding != end; ++binding) {
+ if (binding->type == QV4::CompiledData::Binding::Type_Object && (binding->flags & QV4::CompiledData::Binding::IsOnAssignment)) {
+ // If the on assignment is inside a group property, we need to distinguish between QObject based
+ // group properties and value type group properties. For the former the base type is derived from
+ // the property that references us, for the latter we only need a meta-object on the referencing object
+ // because interceptors can't go to the shared value type instances.
+ if (context.instantiatingProperty && QQmlValueTypeFactory::isValueType(context.instantiatingProperty->propType())) {
+ if (!propertyCaches->needsVMEMetaObject(context.referencingObjectIndex)) {
+ const CompiledObject *obj = objectContainer->objectAt(context.referencingObjectIndex);
+ auto *typeRef = objectContainer->resolvedTypes.value(obj->inheritedTypeNameIndex);
+ Q_ASSERT(typeRef);
+ QQmlPropertyCache *baseTypeCache = typeRef->createPropertyCache(QQmlEnginePrivate::get(enginePrivate));
+ QQmlCompileError error = createMetaObject(context.referencingObjectIndex, obj, baseTypeCache);
+ if (error.isSet())
+ return error;
+ }
+ } else {
+ // On assignments are implemented using value interceptors, which require a VME meta object.
+ needVMEMetaObject = true;
+ }
+ break;
+ }
+ }
+ }
+
+ QQmlPropertyCache *baseTypeCache;
+ {
+ QQmlCompileError error;
+ baseTypeCache = propertyCacheForObject(obj, context, &error);
+ if (error.isSet())
+ return error;
+ }
+
+ if (baseTypeCache) {
+ if (needVMEMetaObject) {
+ QQmlCompileError error = createMetaObject(objectIndex, obj, baseTypeCache);
+ if (error.isSet())
+ return error;
+ } else {
+ propertyCaches->set(objectIndex, baseTypeCache);
+ }
+ }
+
+ if (QQmlPropertyCache *thisCache = propertyCaches->at(objectIndex)) {
+ for (auto binding = obj->bindingsBegin(), end = obj->bindingsEnd(); binding != end; ++binding)
+ if (binding->type >= QV4::CompiledData::Binding::Type_Object) {
+ QQmlBindingInstantiationContext context(objectIndex, &(*binding), stringAt(binding->propertyNameIndex), thisCache);
+ QQmlCompileError error = buildMetaObjectRecursively(binding->value.objectIndex, context);
+ if (error.isSet())
+ return error;
+ }
+ }
+
+ QQmlCompileError noError;
+ return noError;
+}
+
+template <typename ObjectContainer>
+inline QQmlPropertyCache *QQmlPropertyCacheCreator<ObjectContainer>::propertyCacheForObject(const CompiledObject *obj, const QQmlBindingInstantiationContext &context, QQmlCompileError *error) const
+{
+ if (context.instantiatingProperty) {
+ if (context.instantiatingProperty->isQObject()) {
+ return enginePrivate->rawPropertyCacheForType(context.instantiatingProperty->propType());
+ } else if (const QMetaObject *vtmo = QQmlValueTypeFactory::metaObjectForMetaType(context.instantiatingProperty->propType())) {
+ return enginePrivate->cache(vtmo);
+ }
+ } else if (obj->inheritedTypeNameIndex != 0) {
+ auto *typeRef = objectContainer->resolvedTypes.value(obj->inheritedTypeNameIndex);
+ Q_ASSERT(typeRef);
+
+ if (typeRef->isFullyDynamicType) {
+ if (obj->propertyCount() > 0 || obj->aliasCount() > 0) {
+ *error = QQmlCompileError(obj->location, QQmlPropertyCacheCreatorBase::tr("Fully dynamic types cannot declare new properties."));
+ return nullptr;
+ }
+ if (obj->signalCount() > 0) {
+ *error = QQmlCompileError(obj->location, QQmlPropertyCacheCreatorBase::tr("Fully dynamic types cannot declare new signals."));
+ return nullptr;
+ }
+ if (obj->functionCount() > 0) {
+ *error = QQmlCompileError(obj->location, QQmlPropertyCacheCreatorBase::tr("Fully Dynamic types cannot declare new functions."));
+ return nullptr;
+ }
+ }
+
+ return typeRef->createPropertyCache(QQmlEnginePrivate::get(enginePrivate));
+ } else if (context.instantiatingBinding && context.instantiatingBinding->isAttachedProperty()) {
+ auto *typeRef = objectContainer->resolvedTypes.value(context.instantiatingBinding->propertyNameIndex);
+ Q_ASSERT(typeRef);
+ QQmlType *qmltype = typeRef->type;
+ if (!qmltype) {
+ QString propertyName = stringAt(context.instantiatingBinding->propertyNameIndex);
+ if (imports->resolveType(propertyName, &qmltype, 0, 0, 0)) {
+ if (qmltype->isComposite()) {
+ QQmlTypeData *tdata = enginePrivate->typeLoader.getType(qmltype->sourceUrl());
+ Q_ASSERT(tdata);
+ Q_ASSERT(tdata->isComplete());
+
+ auto compilationUnit = tdata->compilationUnit();
+ qmltype = QQmlMetaType::qmlType(compilationUnit->metaTypeId);
+
+ tdata->release();
+ }
+ }
+ }
+
+ const QMetaObject *attachedMo = qmltype ? qmltype->attachedPropertiesType(enginePrivate) : 0;
+ if (!attachedMo) {
+ *error = QQmlCompileError(context.instantiatingBinding->location, QQmlPropertyCacheCreatorBase::tr("Non-existent attached object"));
+ return nullptr;
+ }
+ return enginePrivate->cache(attachedMo);
+ }
+ return nullptr;
+}
+
+template <typename ObjectContainer>
+inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObject(int objectIndex, const CompiledObject *obj, QQmlPropertyCache *baseTypeCache)
+{
+ QQmlRefPointer<QQmlPropertyCache> cache;
+ cache.adopt(baseTypeCache->copyAndReserve(obj->propertyCount() + obj->aliasCount(),
+ obj->functionCount() + obj->propertyCount() + obj->aliasCount() + obj->signalCount(),
+ obj->signalCount() + obj->propertyCount() + obj->aliasCount()));
+
+ propertyCaches->set(objectIndex, cache);
+ propertyCaches->setNeedsVMEMetaObject(objectIndex);
+
+ struct TypeData {
+ QV4::CompiledData::Property::Type dtype;
+ int metaType;
+ } builtinTypes[] = {
+ { QV4::CompiledData::Property::Var, QMetaType::QVariant },
+ { QV4::CompiledData::Property::Variant, QMetaType::QVariant },
+ { QV4::CompiledData::Property::Int, QMetaType::Int },
+ { QV4::CompiledData::Property::Bool, QMetaType::Bool },
+ { QV4::CompiledData::Property::Real, QMetaType::Double },
+ { QV4::CompiledData::Property::String, QMetaType::QString },
+ { QV4::CompiledData::Property::Url, QMetaType::QUrl },
+ { QV4::CompiledData::Property::Color, QMetaType::QColor },
+ { QV4::CompiledData::Property::Font, QMetaType::QFont },
+ { QV4::CompiledData::Property::Time, QMetaType::QTime },
+ { QV4::CompiledData::Property::Date, QMetaType::QDate },
+ { QV4::CompiledData::Property::DateTime, QMetaType::QDateTime },
+ { QV4::CompiledData::Property::Rect, QMetaType::QRectF },
+ { QV4::CompiledData::Property::Point, QMetaType::QPointF },
+ { QV4::CompiledData::Property::Size, QMetaType::QSizeF },
+ { QV4::CompiledData::Property::Vector2D, QMetaType::QVector2D },
+ { QV4::CompiledData::Property::Vector3D, QMetaType::QVector3D },
+ { QV4::CompiledData::Property::Vector4D, QMetaType::QVector4D },
+ { QV4::CompiledData::Property::Matrix4x4, QMetaType::QMatrix4x4 },
+ { QV4::CompiledData::Property::Quaternion, QMetaType::QQuaternion }
+};
+ static const uint builtinTypeCount = sizeof(builtinTypes) / sizeof(TypeData);
+
+ QByteArray newClassName;
+
+ if (objectIndex == objectContainer->rootObjectIndex()) {
+ const QString path = objectContainer->url().path();
+ int lastSlash = path.lastIndexOf(QLatin1Char('/'));
+ if (lastSlash > -1) {
+ const QStringRef nameBase = path.midRef(lastSlash + 1, path.length() - lastSlash - 5);
+ if (!nameBase.isEmpty() && nameBase.at(0).isUpper())
+ newClassName = nameBase.toUtf8() + "_QMLTYPE_" +
+ QByteArray::number(classIndexCounter.fetchAndAddRelaxed(1));
+ }
+ }
+ if (newClassName.isEmpty()) {
+ newClassName = QQmlMetaObject(baseTypeCache).className();
+ newClassName.append("_QML_");
+ newClassName.append(QByteArray::number(classIndexCounter.fetchAndAddRelaxed(1)));
+ }
+
+ cache->_dynamicClassName = newClassName;
+
+ int varPropCount = 0;
+
+ QmlIR::PropertyResolver resolver(baseTypeCache);
+
+ for (auto p = obj->propertiesBegin(), end = obj->propertiesEnd(); p != end; ++p) {
+ if (p->type == QV4::CompiledData::Property::Var)
+ varPropCount++;
+
+ bool notInRevision = false;
+ QQmlPropertyData *d = resolver.property(stringAt(p->nameIndex), &notInRevision);
+ if (d && d->isFinal())
+ return QQmlCompileError(p->location, QQmlPropertyCacheCreatorBase::tr("Cannot override FINAL property"));
+ }
+
+ for (auto a = obj->aliasesBegin(), end = obj->aliasesEnd(); a != end; ++a) {
+ bool notInRevision = false;
+ QQmlPropertyData *d = resolver.property(stringAt(a->nameIndex), &notInRevision);
+ if (d && d->isFinal())
+ return QQmlCompileError(a->location, QQmlPropertyCacheCreatorBase::tr("Cannot override FINAL property"));
+ }
+
+ int effectivePropertyIndex = cache->propertyIndexCacheStart;
+ int effectiveMethodIndex = cache->methodIndexCacheStart;
+
+ // For property change signal override detection.
+ // We prepopulate a set of signal names which already exist in the object,
+ // and throw an error if there is a signal/method defined as an override.
+ QSet<QString> seenSignals;
+ seenSignals << QStringLiteral("destroyed") << QStringLiteral("parentChanged") << QStringLiteral("objectNameChanged");
+ QQmlPropertyCache *parentCache = cache;
+ while ((parentCache = parentCache->parent())) {
+ if (int pSigCount = parentCache->signalCount()) {
+ int pSigOffset = parentCache->signalOffset();
+ for (int i = pSigOffset; i < pSigCount; ++i) {
+ QQmlPropertyData *currPSig = parentCache->signal(i);
+ // XXX TODO: find a better way to get signal name from the property data :-/
+ for (QQmlPropertyCache::StringCache::ConstIterator iter = parentCache->stringCache.begin();
+ iter != parentCache->stringCache.end(); ++iter) {
+ if (currPSig == (*iter).second) {
+ seenSignals.insert(iter.key());
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ // Set up notify signals for properties - first normal, then alias
+ for (auto p = obj->propertiesBegin(), end = obj->propertiesEnd(); p != end; ++p) {
+ auto flags = QQmlPropertyData::defaultSignalFlags();
+
+ QString changedSigName = stringAt(p->nameIndex) + QLatin1String("Changed");
+ seenSignals.insert(changedSigName);
+
+ cache->appendSignal(changedSigName, flags, effectiveMethodIndex++);
+ }
+
+ for (auto a = obj->aliasesBegin(), end = obj->aliasesEnd(); a != end; ++a) {
+ auto flags = QQmlPropertyData::defaultSignalFlags();
+
+ QString changedSigName = stringAt(a->nameIndex) + QLatin1String("Changed");
+ seenSignals.insert(changedSigName);
+
+ cache->appendSignal(changedSigName, flags, effectiveMethodIndex++);
+ }
+
+ // Dynamic signals
+ for (auto s = obj->signalsBegin(), end = obj->signalsEnd(); s != end; ++s) {
+ const int paramCount = s->parameterCount();
+
+ QList<QByteArray> names;
+ names.reserve(paramCount);
+ QVarLengthArray<int, 10> paramTypes(paramCount?(paramCount + 1):0);
+
+ if (paramCount) {
+ paramTypes[0] = paramCount;
+
+ int i = 0;
+ for (auto param = s->parametersBegin(), end = s->parametersEnd(); param != end; ++param, ++i) {
+ names.append(stringAt(param->nameIndex).toUtf8());
+ if (param->type < builtinTypeCount) {
+ // built-in type
+ paramTypes[i + 1] = builtinTypes[param->type].metaType;
+ } else {
+ // lazily resolved type
+ Q_ASSERT(param->type == QV4::CompiledData::Property::Custom);
+ const QString customTypeName = stringAt(param->customTypeNameIndex);
+ QQmlType *qmltype = 0;
+ if (!imports->resolveType(customTypeName, &qmltype, 0, 0, 0))
+ return QQmlCompileError(s->location, QQmlPropertyCacheCreatorBase::tr("Invalid signal parameter type: %1").arg(customTypeName));
+
+ if (qmltype->isComposite()) {
+ QQmlTypeData *tdata = enginePrivate->typeLoader.getType(qmltype->sourceUrl());
+ Q_ASSERT(tdata);
+ Q_ASSERT(tdata->isComplete());
+
+ auto compilationUnit = tdata->compilationUnit();
+
+ paramTypes[i + 1] = compilationUnit->metaTypeId;
+
+ tdata->release();
+ } else {
+ paramTypes[i + 1] = qmltype->typeId();
+ }
+ }
+ }
+ }
+
+ auto flags = QQmlPropertyData::defaultSignalFlags();
+ if (paramCount)
+ flags.hasArguments = true;
+
+ QString signalName = stringAt(s->nameIndex);
+ if (seenSignals.contains(signalName))
+ return QQmlCompileError(s->location, QQmlPropertyCacheCreatorBase::tr("Duplicate signal name: invalid override of property change signal or superclass signal"));
+ seenSignals.insert(signalName);
+
+ cache->appendSignal(signalName, flags, effectiveMethodIndex++,
+ paramCount?paramTypes.constData():0, names);
+ }
+
+
+ // Dynamic slots
+ for (auto function = objectContainer->objectFunctionsBegin(obj), end = objectContainer->objectFunctionsEnd(obj); function != end; ++function) {
+ auto flags = QQmlPropertyData::defaultSlotFlags();
+
+ const QString slotName = stringAt(function->nameIndex);
+ if (seenSignals.contains(slotName))
+ return QQmlCompileError(function->location, QQmlPropertyCacheCreatorBase::tr("Duplicate method name: invalid override of property change signal or superclass signal"));
+ // Note: we don't append slotName to the seenSignals list, since we don't
+ // protect against overriding change signals or methods with properties.
+
+ QList<QByteArray> parameterNames;
+ for (auto formal = function->formalsBegin(), end = function->formalsEnd(); formal != end; ++formal) {
+ flags.hasArguments = true;
+ parameterNames << stringAt(*formal).toUtf8();
+ }
+
+ cache->appendMethod(slotName, flags, effectiveMethodIndex++, parameterNames);
+ }
+
+
+ // Dynamic properties
+ int effectiveSignalIndex = cache->signalHandlerIndexCacheStart;
+ int propertyIdx = 0;
+ for (auto p = obj->propertiesBegin(), end = obj->propertiesEnd(); p != end; ++p, ++propertyIdx) {
+ int propertyType = 0;
+ QQmlPropertyData::Flags propertyFlags;
+
+ if (p->type == QV4::CompiledData::Property::Var) {
+ propertyType = QMetaType::QVariant;
+ propertyFlags.type = QQmlPropertyData::Flags::VarPropertyType;
+ } else if (p->type < builtinTypeCount) {
+ propertyType = builtinTypes[p->type].metaType;
+
+ if (p->type == QV4::CompiledData::Property::Variant)
+ propertyFlags.type = QQmlPropertyData::Flags::QVariantType;
+ } else {
+ Q_ASSERT(p->type == QV4::CompiledData::Property::CustomList ||
+ p->type == QV4::CompiledData::Property::Custom);
+
+ QQmlType *qmltype = 0;
+ if (!imports->resolveType(stringAt(p->customTypeNameIndex), &qmltype, 0, 0, 0)) {
+ return QQmlCompileError(p->location, QQmlPropertyCacheCreatorBase::tr("Invalid property type"));
+ }
+
+ Q_ASSERT(qmltype);
+ if (qmltype->isComposite()) {
+ QQmlTypeData *tdata = enginePrivate->typeLoader.getType(qmltype->sourceUrl());
+ Q_ASSERT(tdata);
+ Q_ASSERT(tdata->isComplete());
+
+ auto compilationUnit = tdata->compilationUnit();
+
+ if (p->type == QV4::CompiledData::Property::Custom) {
+ propertyType = compilationUnit->metaTypeId;
+ } else {
+ propertyType = compilationUnit->listMetaTypeId;
+ }
+
+ tdata->release();
+ } else {
+ if (p->type == QV4::CompiledData::Property::Custom) {
+ propertyType = qmltype->typeId();
+ } else {
+ propertyType = qmltype->qListTypeId();
+ }
+ }
+
+ if (p->type == QV4::CompiledData::Property::Custom)
+ propertyFlags.type = QQmlPropertyData::Flags::QObjectDerivedType;
+ else
+ propertyFlags.type = QQmlPropertyData::Flags::QListType;
+ }
+
+ if (!(p->flags & QV4::CompiledData::Property::IsReadOnly) && p->type != QV4::CompiledData::Property::CustomList)
+ propertyFlags.isWritable = true;
+
+
+ QString propertyName = stringAt(p->nameIndex);
+ if (!obj->defaultPropertyIsAlias && propertyIdx == obj->indexOfDefaultPropertyOrAlias)
+ cache->_defaultPropertyName = propertyName;
+ cache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++,
+ propertyType, effectiveSignalIndex);
+
+ effectiveSignalIndex++;
+ }
+
+ QQmlCompileError noError;
+ return noError;
+}
+
+template <typename ObjectContainer>
+class QQmlPropertyCacheAliasCreator
+{
+public:
+ typedef typename ObjectContainer::CompiledObject CompiledObject;
+
+ QQmlPropertyCacheAliasCreator(QQmlPropertyCacheVector *propertyCaches, const ObjectContainer *objectContainer);
+
+ void appendAliasPropertiesToMetaObjects();
+
+ void appendAliasesToPropertyCache(const CompiledObject &component, int objectIndex);
+
+private:
+ void appendAliasPropertiesInMetaObjectsWithinComponent(const CompiledObject &component, int firstObjectIndex);
+ void propertyDataForAlias(const CompiledObject &component, const QV4::CompiledData::Alias &alias, int *type, QQmlPropertyRawData::Flags *propertyFlags);
+
+ void collectObjectsWithAliasesRecursively(int objectIndex, QVector<int> *objectsWithAliases) const;
+
+ int objectForId(const CompiledObject &component, int id) const;
+
+ QQmlPropertyCacheVector *propertyCaches;
+ const ObjectContainer *objectContainer;
+};
+
+template <typename ObjectContainer>
+inline QQmlPropertyCacheAliasCreator<ObjectContainer>::QQmlPropertyCacheAliasCreator(QQmlPropertyCacheVector *propertyCaches, const ObjectContainer *objectContainer)
+ : propertyCaches(propertyCaches)
+ , objectContainer(objectContainer)
+{
+
+}
+
+template <typename ObjectContainer>
+inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasPropertiesToMetaObjects()
+{
+ for (int i = 0; i < objectContainer->objectCount(); ++i) {
+ const CompiledObject &component = *objectContainer->objectAt(i);
+ if (!(component.flags & QV4::CompiledData::Object::IsComponent))
+ continue;
+
+ const auto rootBinding = component.bindingsBegin();
+ appendAliasPropertiesInMetaObjectsWithinComponent(component, rootBinding->value.objectIndex);
+ }
+
+ const int rootObjectIndex = objectContainer->rootObjectIndex();
+ appendAliasPropertiesInMetaObjectsWithinComponent(*objectContainer->objectAt(rootObjectIndex), rootObjectIndex);
+}
+
+template <typename ObjectContainer>
+inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasPropertiesInMetaObjectsWithinComponent(const CompiledObject &component, int firstObjectIndex)
+{
+ QVector<int> objectsWithAliases;
+ collectObjectsWithAliasesRecursively(firstObjectIndex, &objectsWithAliases);
+ if (objectsWithAliases.isEmpty())
+ return;
+
+ const auto allAliasTargetsExist = [this, &component](const CompiledObject &object) {
+ for (auto alias = object.aliasesBegin(), end = object.aliasesEnd(); alias != end; ++alias) {
+ Q_ASSERT(alias->flags & QV4::CompiledData::Alias::Resolved);
+
+ const int targetObjectIndex = objectForId(component, alias->targetObjectId);
+ Q_ASSERT(targetObjectIndex >= 0);
+
+ if (alias->aliasToLocalAlias)
+ continue;
+
+ if (alias->encodedMetaPropertyIndex == -1)
+ continue;
+
+ const QQmlPropertyCache *targetCache = propertyCaches->at(targetObjectIndex);
+ Q_ASSERT(targetCache);
+
+ int coreIndex = QQmlPropertyIndex::fromEncoded(alias->encodedMetaPropertyIndex).coreIndex();
+ QQmlPropertyData *targetProperty = targetCache->property(coreIndex);
+ if (!targetProperty)
+ return false;
+ }
+ return true;
+ };
+
+ do {
+ QVector<int> pendingObjects;
+
+ for (int objectIndex: qAsConst(objectsWithAliases)) {
+ const CompiledObject &object = *objectContainer->objectAt(objectIndex);
+
+ if (allAliasTargetsExist(object)) {
+ appendAliasesToPropertyCache(component, objectIndex);
+ } else {
+ pendingObjects.append(objectIndex);
+ }
+
+ }
+ qSwap(objectsWithAliases, pendingObjects);
+ } while (!objectsWithAliases.isEmpty());
+}
+
+template <typename ObjectContainer>
+inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::collectObjectsWithAliasesRecursively(int objectIndex, QVector<int> *objectsWithAliases) const
+{
+ const CompiledObject &object = *objectContainer->objectAt(objectIndex);
+ if (object.aliasCount() > 0)
+ objectsWithAliases->append(objectIndex);
+
+ // Stop at Component boundary
+ if (object.flags & QV4::CompiledData::Object::IsComponent && objectIndex != objectContainer->rootObjectIndex())
+ return;
+
+ for (auto binding = object.bindingsBegin(), end = object.bindingsEnd(); binding != end; ++binding) {
+ if (binding->type != QV4::CompiledData::Binding::Type_Object
+ && binding->type != QV4::CompiledData::Binding::Type_AttachedProperty
+ && binding->type != QV4::CompiledData::Binding::Type_GroupProperty)
+ continue;
+
+ collectObjectsWithAliasesRecursively(binding->value.objectIndex, objectsWithAliases);
+ }
+}
+
+template <typename ObjectContainer>
+inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::propertyDataForAlias(
+ const CompiledObject &component, const QV4::CompiledData::Alias &alias, int *type,
+ QQmlPropertyData::Flags *propertyFlags)
+{
+ const int targetObjectIndex = objectForId(component, alias.targetObjectId);
+ Q_ASSERT(targetObjectIndex >= 0);
+
+ const CompiledObject &targetObject = *objectContainer->objectAt(targetObjectIndex);
+
+ *type = 0;
+ bool writable = false;
+ bool resettable = false;
+
+ propertyFlags->isAlias = true;
+
+ if (alias.aliasToLocalAlias) {
+ auto targetAlias = targetObject.aliasesBegin();
+ for (uint i = 0; i < alias.localAliasIndex; ++i)
+ ++targetAlias;
+ propertyDataForAlias(component, *targetAlias, type, propertyFlags);
+ return;
+ } else if (alias.encodedMetaPropertyIndex == -1) {
+ Q_ASSERT(alias.flags & QV4::CompiledData::Alias::AliasPointsToPointerObject);
+ auto *typeRef = objectContainer->resolvedTypes.value(targetObject.inheritedTypeNameIndex);
+ Q_ASSERT(typeRef);
+
+ if (typeRef->type)
+ *type = typeRef->type->typeId();
+ else
+ *type = typeRef->compilationUnit->metaTypeId;
+
+ propertyFlags->type = QQmlPropertyData::Flags::QObjectDerivedType;
+ } else {
+ int coreIndex = QQmlPropertyIndex::fromEncoded(alias.encodedMetaPropertyIndex).coreIndex();
+ int valueTypeIndex = QQmlPropertyIndex::fromEncoded(alias.encodedMetaPropertyIndex).valueTypeIndex();
+
+ QQmlPropertyCache *targetCache = propertyCaches->at(targetObjectIndex);
+ Q_ASSERT(targetCache);
+ QQmlPropertyData *targetProperty = targetCache->property(coreIndex);
+ Q_ASSERT(targetProperty);
+
+ *type = targetProperty->propType();
+
+ writable = targetProperty->isWritable();
+ resettable = targetProperty->isResettable();
+
+ if (valueTypeIndex != -1) {
+ const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(*type);
+ if (valueTypeMetaObject->property(valueTypeIndex).isEnumType())
+ *type = QVariant::Int;
+ else
+ *type = valueTypeMetaObject->property(valueTypeIndex).userType();
+ } else {
+ if (targetProperty->isEnum()) {
+ *type = QVariant::Int;
+ } else {
+ // Copy type flags
+ propertyFlags->copyPropertyTypeFlags(targetProperty->flags());
+
+ if (targetProperty->isVarProperty())
+ propertyFlags->type = QQmlPropertyData::Flags::QVariantType;
+ }
+ }
+ }
+
+ propertyFlags->isWritable = !(alias.flags & QV4::CompiledData::Property::IsReadOnly) && writable;
+ propertyFlags->isResettable = resettable;
+}
+
+template <typename ObjectContainer>
+inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasesToPropertyCache(
+ const CompiledObject &component, int objectIndex)
+{
+ const CompiledObject &object = *objectContainer->objectAt(objectIndex);
+ if (!object.aliasCount())
+ return;
+
+ QQmlPropertyCache *propertyCache = propertyCaches->at(objectIndex);
+ Q_ASSERT(propertyCache);
+
+ int effectiveSignalIndex = propertyCache->signalHandlerIndexCacheStart + propertyCache->propertyIndexCache.count();
+ int effectivePropertyIndex = propertyCache->propertyIndexCacheStart + propertyCache->propertyIndexCache.count();
+
+ int aliasIndex = 0;
+ for (auto alias = object.aliasesBegin(), end = object.aliasesEnd(); alias != end; ++alias, ++aliasIndex) {
+ Q_ASSERT(alias->flags & QV4::CompiledData::Alias::Resolved);
+
+ int type = 0;
+ QQmlPropertyData::Flags propertyFlags;
+ propertyDataForAlias(component, *alias, &type, &propertyFlags);
+
+ const QString propertyName = objectContainer->stringAt(alias->nameIndex);
+
+ if (object.defaultPropertyIsAlias && aliasIndex == object.indexOfDefaultPropertyOrAlias)
+ propertyCache->_defaultPropertyName = propertyName;
+
+ propertyCache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++,
+ type, effectiveSignalIndex++);
+ }
+}
+
+template <typename ObjectContainer>
+inline int QQmlPropertyCacheAliasCreator<ObjectContainer>::objectForId(const CompiledObject &component, int id) const
+{
+ for (quint32 i = 0, count = component.namedObjectsInComponentCount(); i < count; ++i) {
+ const int candidateIndex = component.namedObjectsInComponentTable()[i];
+ const CompiledObject &candidate = *objectContainer->objectAt(candidateIndex);
+ if (candidate.id == id)
+ return candidateIndex;
+ }
+ return -1;
+}
+
+QT_END_NAMESPACE
+
+#endif // QQMLPROPERTYCACHECREATOR_P_H
diff --git a/src/qml/compiler/qqmlpropertyvalidator.cpp b/src/qml/compiler/qqmlpropertyvalidator.cpp
new file mode 100644
index 0000000000..45379d5155
--- /dev/null
+++ b/src/qml/compiler/qqmlpropertyvalidator.cpp
@@ -0,0 +1,703 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the tools applications 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 "qqmlpropertyvalidator_p.h"
+
+#include <private/qqmlcustomparser_p.h>
+#include <private/qqmlstringconverters_p.h>
+#include <QtCore/qdatetime.h>
+
+QT_BEGIN_NAMESPACE
+
+QQmlPropertyValidator::QQmlPropertyValidator(QQmlEnginePrivate *enginePrivate, const QQmlImports &imports, QV4::CompiledData::CompilationUnit *compilationUnit)
+ : enginePrivate(enginePrivate)
+ , imports(imports)
+ , qmlUnit(compilationUnit->data)
+ , resolvedTypes(compilationUnit->resolvedTypes)
+ , propertyCaches(compilationUnit->propertyCaches)
+ , bindingPropertyDataPerObject(&compilationUnit->bindingPropertyDataPerObject)
+{
+ bindingPropertyDataPerObject->resize(qmlUnit->nObjects);
+}
+
+QVector<QQmlCompileError> QQmlPropertyValidator::validate()
+{
+ return validateObject(qmlUnit->indexOfRootObject, /*instantiatingBinding*/0);
+}
+
+typedef QVarLengthArray<const QV4::CompiledData::Binding *, 8> GroupPropertyVector;
+
+struct BindingFinder
+{
+ bool operator()(quint32 name, const QV4::CompiledData::Binding *binding) const
+ {
+ return name < binding->propertyNameIndex;
+ }
+ bool operator()(const QV4::CompiledData::Binding *binding, quint32 name) const
+ {
+ return binding->propertyNameIndex < name;
+ }
+ bool operator()(const QV4::CompiledData::Binding *lhs, const QV4::CompiledData::Binding *rhs) const
+ {
+ return lhs->propertyNameIndex < rhs->propertyNameIndex;
+ }
+};
+
+QVector<QQmlCompileError> QQmlPropertyValidator::validateObject(int objectIndex, const QV4::CompiledData::Binding *instantiatingBinding, bool populatingValueTypeGroupProperty) const
+{
+ const QV4::CompiledData::Object *obj = qmlUnit->objectAt(objectIndex);
+
+ if (obj->flags & QV4::CompiledData::Object::IsComponent) {
+ Q_ASSERT(obj->nBindings == 1);
+ const QV4::CompiledData::Binding *componentBinding = obj->bindingTable();
+ Q_ASSERT(componentBinding->type == QV4::CompiledData::Binding::Type_Object);
+ return validateObject(componentBinding->value.objectIndex, componentBinding);
+ }
+
+ QQmlPropertyCache *propertyCache = propertyCaches.at(objectIndex);
+ if (!propertyCache)
+ return QVector<QQmlCompileError>();
+
+ QStringList deferredPropertyNames;
+ {
+ const QMetaObject *mo = propertyCache->firstCppMetaObject();
+ const int namesIndex = mo->indexOfClassInfo("DeferredPropertyNames");
+ if (namesIndex != -1) {
+ QMetaClassInfo classInfo = mo->classInfo(namesIndex);
+ deferredPropertyNames = QString::fromUtf8(classInfo.value()).split(QLatin1Char(','));
+ }
+ }
+
+ QQmlCustomParser *customParser = 0;
+ if (auto typeRef = resolvedTypes.value(obj->inheritedTypeNameIndex)) {
+ if (typeRef->type)
+ customParser = typeRef->type->customParser();
+ }
+
+ QList<const QV4::CompiledData::Binding*> customBindings;
+
+ // Collect group properties first for sanity checking
+ // vector values are sorted by property name string index.
+ GroupPropertyVector groupProperties;
+ const QV4::CompiledData::Binding *binding = obj->bindingTable();
+ for (quint32 i = 0; i < obj->nBindings; ++i, ++binding) {
+ if (!binding->isGroupProperty())
+ continue;
+
+ if (binding->flags & QV4::CompiledData::Binding::IsOnAssignment)
+ continue;
+
+ if (populatingValueTypeGroupProperty) {
+ return recordError(binding->location, tr("Property assignment expected"));
+ }
+
+ GroupPropertyVector::const_iterator pos = std::lower_bound(groupProperties.constBegin(), groupProperties.constEnd(), binding->propertyNameIndex, BindingFinder());
+ groupProperties.insert(pos, binding);
+ }
+
+ QmlIR::PropertyResolver propertyResolver(propertyCache);
+
+ QString defaultPropertyName;
+ QQmlPropertyData *defaultProperty = 0;
+ if (obj->indexOfDefaultPropertyOrAlias != -1) {
+ QQmlPropertyCache *cache = propertyCache->parent();
+ defaultPropertyName = cache->defaultPropertyName();
+ defaultProperty = cache->defaultProperty();
+ } else {
+ defaultPropertyName = propertyCache->defaultPropertyName();
+ defaultProperty = propertyCache->defaultProperty();
+ }
+
+ QV4::CompiledData::BindingPropertyData collectedBindingPropertyData(obj->nBindings);
+
+ binding = obj->bindingTable();
+ for (quint32 i = 0; i < obj->nBindings; ++i, ++binding) {
+ QString name = stringAt(binding->propertyNameIndex);
+
+ if (customParser) {
+ if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) {
+ if (customParser->flags() & QQmlCustomParser::AcceptsAttachedProperties) {
+ customBindings << binding;
+ continue;
+ }
+ } else if (QmlIR::IRBuilder::isSignalPropertyName(name)
+ && !(customParser->flags() & QQmlCustomParser::AcceptsSignalHandlers)) {
+ customBindings << binding;
+ continue;
+ }
+ }
+
+ bool bindingToDefaultProperty = false;
+ bool isGroupProperty = instantiatingBinding && instantiatingBinding->type == QV4::CompiledData::Binding::Type_GroupProperty;
+
+ bool notInRevision = false;
+ QQmlPropertyData *pd = 0;
+ if (!name.isEmpty()) {
+ if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression
+ || binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject)
+ pd = propertyResolver.signal(name, &notInRevision);
+ else
+ pd = propertyResolver.property(name, &notInRevision, isGroupProperty ? QmlIR::PropertyResolver::IgnoreRevision : QmlIR::PropertyResolver::CheckRevision);
+
+ if (notInRevision) {
+ QString typeName = stringAt(obj->inheritedTypeNameIndex);
+ auto *objectType = resolvedTypes.value(obj->inheritedTypeNameIndex);
+ if (objectType && objectType->type) {
+ return recordError(binding->location, tr("\"%1.%2\" is not available in %3 %4.%5.").arg(typeName).arg(name).arg(objectType->type->module()).arg(objectType->majorVersion).arg(objectType->minorVersion));
+ } else {
+ return recordError(binding->location, tr("\"%1.%2\" is not available due to component versioning.").arg(typeName).arg(name));
+ }
+ }
+ } else {
+ if (isGroupProperty)
+ return recordError(binding->location, tr("Cannot assign a value directly to a grouped property"));
+
+ pd = defaultProperty;
+ name = defaultPropertyName;
+ bindingToDefaultProperty = true;
+ }
+
+ if (pd)
+ collectedBindingPropertyData[i] = pd;
+
+ if (name.constData()->isUpper() && !binding->isAttachedProperty()) {
+ QQmlType *type = 0;
+ QQmlImportNamespace *typeNamespace = 0;
+ imports.resolveType(stringAt(binding->propertyNameIndex), &type, 0, 0, &typeNamespace);
+ if (typeNamespace)
+ return recordError(binding->location, tr("Invalid use of namespace"));
+ return recordError(binding->location, tr("Invalid attached object assignment"));
+ }
+
+ if (binding->type >= QV4::CompiledData::Binding::Type_Object && (pd || binding->isAttachedProperty())) {
+ const QVector<QQmlCompileError> subObjectValidatorErrors = validateObject(binding->value.objectIndex, binding, pd && QQmlValueTypeFactory::metaObjectForMetaType(pd->propType()));
+ if (!subObjectValidatorErrors.isEmpty())
+ return subObjectValidatorErrors;
+ }
+
+ // Signal handlers were resolved and checked earlier in the signal handler conversion pass.
+ if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression
+ || binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject)
+ continue;
+
+ if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) {
+ if (instantiatingBinding && (instantiatingBinding->isAttachedProperty() || instantiatingBinding->isGroupProperty())) {
+ return recordError(binding->location, tr("Attached properties cannot be used here"));
+ }
+ continue;
+ }
+
+ if (pd) {
+ GroupPropertyVector::const_iterator assignedGroupProperty = std::lower_bound(groupProperties.constBegin(), groupProperties.constEnd(), binding->propertyNameIndex, BindingFinder());
+ const bool assigningToGroupProperty = assignedGroupProperty != groupProperties.constEnd() && !(binding->propertyNameIndex < (*assignedGroupProperty)->propertyNameIndex);
+
+ if (!pd->isWritable()
+ && !pd->isQList()
+ && !binding->isGroupProperty()
+ && !(binding->flags & QV4::CompiledData::Binding::InitializerForReadOnlyDeclaration)
+ ) {
+
+ if (assigningToGroupProperty && binding->type < QV4::CompiledData::Binding::Type_Object)
+ return recordError(binding->valueLocation, tr("Cannot assign a value directly to a grouped property"));
+ return recordError(binding->valueLocation, tr("Invalid property assignment: \"%1\" is a read-only property").arg(name));
+ }
+
+ if (!pd->isQList() && (binding->flags & QV4::CompiledData::Binding::IsListItem)) {
+ QString error;
+ if (pd->propType() == qMetaTypeId<QQmlScriptString>())
+ error = tr( "Cannot assign multiple values to a script property");
+ else
+ error = tr( "Cannot assign multiple values to a singular property");
+ return recordError(binding->valueLocation, error);
+ }
+
+ if (!bindingToDefaultProperty
+ && !binding->isGroupProperty()
+ && !(binding->flags & QV4::CompiledData::Binding::IsOnAssignment)
+ && assigningToGroupProperty) {
+ QV4::CompiledData::Location loc = binding->valueLocation;
+ if (loc < (*assignedGroupProperty)->valueLocation)
+ loc = (*assignedGroupProperty)->valueLocation;
+
+ if (pd && QQmlValueTypeFactory::isValueType(pd->propType()))
+ return recordError(loc, tr("Property has already been assigned a value"));
+ return recordError(loc, tr("Cannot assign a value directly to a grouped property"));
+ }
+
+ if (binding->type < QV4::CompiledData::Binding::Type_Script) {
+ QQmlCompileError bindingError = validateLiteralBinding(propertyCache, pd, binding);
+ if (bindingError.isSet())
+ return recordError(bindingError);
+ } else if (binding->type == QV4::CompiledData::Binding::Type_Object) {
+ QQmlCompileError bindingError = validateObjectBinding(pd, name, binding);
+ if (bindingError.isSet())
+ return recordError(bindingError);
+ } else if (binding->isGroupProperty()) {
+ if (QQmlValueTypeFactory::isValueType(pd->propType())) {
+ if (QQmlValueTypeFactory::metaObjectForMetaType(pd->propType())) {
+ if (!pd->isWritable()) {
+ return recordError(binding->location, tr("Invalid property assignment: \"%1\" is a read-only property").arg(name));
+ }
+ } else {
+ return recordError(binding->location, tr("Invalid grouped property access"));
+ }
+ } else {
+ if (!enginePrivate->propertyCacheForType(pd->propType())) {
+ return recordError(binding->location, tr("Invalid grouped property access"));
+ }
+ }
+ }
+ } else {
+ if (customParser) {
+ customBindings << binding;
+ continue;
+ }
+ if (bindingToDefaultProperty) {
+ return recordError(binding->location, tr("Cannot assign to non-existent default property"));
+ } else {
+ return recordError(binding->location, tr("Cannot assign to non-existent property \"%1\"").arg(name));
+ }
+ }
+ }
+
+ if (obj->idNameIndex) {
+ bool notInRevision = false;
+ collectedBindingPropertyData << propertyResolver.property(QStringLiteral("id"), &notInRevision);
+ }
+
+ if (customParser && !customBindings.isEmpty()) {
+ customParser->clearErrors();
+ customParser->validator = this;
+ customParser->engine = enginePrivate;
+ customParser->imports = &imports;
+ customParser->verifyBindings(qmlUnit, customBindings);
+ customParser->validator = 0;
+ customParser->engine = 0;
+ customParser->imports = (QQmlImports*)0;
+ QVector<QQmlCompileError> parserErrors = customParser->errors();
+ if (!parserErrors.isEmpty())
+ return parserErrors;
+ }
+
+ (*bindingPropertyDataPerObject)[objectIndex] = collectedBindingPropertyData;
+
+ QVector<QQmlCompileError> noError;
+ return noError;
+}
+
+QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache *propertyCache, QQmlPropertyData *property, const QV4::CompiledData::Binding *binding) const
+{
+ if (property->isQList()) {
+ return QQmlCompileError(binding->valueLocation, tr("Cannot assign primitives to lists"));
+ }
+
+ QQmlCompileError noError;
+
+ if (property->isEnum()) {
+ if (binding->flags & QV4::CompiledData::Binding::IsResolvedEnum)
+ return noError;
+
+ QString value = binding->valueAsString(qmlUnit);
+ QMetaProperty p = propertyCache->firstCppMetaObject()->property(property->coreIndex());
+ bool ok;
+ if (p.isFlagType()) {
+ p.enumerator().keysToValue(value.toUtf8().constData(), &ok);
+ } else
+ p.enumerator().keyToValue(value.toUtf8().constData(), &ok);
+
+ if (!ok) {
+ return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: unknown enumeration"));
+ }
+ return noError;
+ }
+
+ switch (property->propType()) {
+ case QMetaType::QVariant:
+ break;
+ case QVariant::String: {
+ if (!binding->evaluatesToString()) {
+ return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: string expected"));
+ }
+ }
+ break;
+ case QVariant::StringList: {
+ if (!binding->evaluatesToString()) {
+ return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: string or string list expected"));
+ }
+ }
+ break;
+ case QVariant::ByteArray: {
+ if (binding->type != QV4::CompiledData::Binding::Type_String) {
+ return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: byte array expected"));
+ }
+ }
+ break;
+ case QVariant::Url: {
+ if (binding->type != QV4::CompiledData::Binding::Type_String) {
+ return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: url expected"));
+ }
+ }
+ break;
+ case QVariant::UInt: {
+ if (binding->type == QV4::CompiledData::Binding::Type_Number) {
+ double d = binding->valueAsNumber();
+ if (double(uint(d)) == d)
+ return noError;
+ }
+ return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: unsigned int expected"));
+ }
+ break;
+ case QVariant::Int: {
+ if (binding->type == QV4::CompiledData::Binding::Type_Number) {
+ double d = binding->valueAsNumber();
+ if (double(int(d)) == d)
+ return noError;
+ }
+ return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: int expected"));
+ }
+ break;
+ case QMetaType::Float: {
+ if (binding->type != QV4::CompiledData::Binding::Type_Number) {
+ return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: number expected"));
+ }
+ }
+ break;
+ case QVariant::Double: {
+ if (binding->type != QV4::CompiledData::Binding::Type_Number) {
+ return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: number expected"));
+ }
+ }
+ break;
+ case QVariant::Color: {
+ bool ok = false;
+ QQmlStringConverters::rgbaFromString(binding->valueAsString(qmlUnit), &ok);
+ if (!ok) {
+ return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: color expected"));
+ }
+ }
+ break;
+#ifndef QT_NO_DATESTRING
+ case QVariant::Date: {
+ bool ok = false;
+ QQmlStringConverters::dateFromString(binding->valueAsString(qmlUnit), &ok);
+ if (!ok) {
+ return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: date expected"));
+ }
+ }
+ break;
+ case QVariant::Time: {
+ bool ok = false;
+ QQmlStringConverters::timeFromString(binding->valueAsString(qmlUnit), &ok);
+ if (!ok) {
+ return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: time expected"));
+ }
+ }
+ break;
+ case QVariant::DateTime: {
+ bool ok = false;
+ QQmlStringConverters::dateTimeFromString(binding->valueAsString(qmlUnit), &ok);
+ if (!ok) {
+ return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: datetime expected"));
+ }
+ }
+ break;
+#endif // QT_NO_DATESTRING
+ case QVariant::Point: {
+ bool ok = false;
+ QQmlStringConverters::pointFFromString(binding->valueAsString(qmlUnit), &ok);
+ if (!ok) {
+ return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: point expected"));
+ }
+ }
+ break;
+ case QVariant::PointF: {
+ bool ok = false;
+ QQmlStringConverters::pointFFromString(binding->valueAsString(qmlUnit), &ok);
+ if (!ok) {
+ return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: point expected"));
+ }
+ }
+ break;
+ case QVariant::Size: {
+ bool ok = false;
+ QQmlStringConverters::sizeFFromString(binding->valueAsString(qmlUnit), &ok);
+ if (!ok) {
+ return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: size expected"));
+ }
+ }
+ break;
+ case QVariant::SizeF: {
+ bool ok = false;
+ QQmlStringConverters::sizeFFromString(binding->valueAsString(qmlUnit), &ok);
+ if (!ok) {
+ return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: size expected"));
+ }
+ }
+ break;
+ case QVariant::Rect: {
+ bool ok = false;
+ QQmlStringConverters::rectFFromString(binding->valueAsString(qmlUnit), &ok);
+ if (!ok) {
+ return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: rect expected"));
+ }
+ }
+ break;
+ case QVariant::RectF: {
+ bool ok = false;
+ QQmlStringConverters::rectFFromString(binding->valueAsString(qmlUnit), &ok);
+ if (!ok) {
+ return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: point expected"));
+ }
+ }
+ break;
+ case QVariant::Bool: {
+ if (binding->type != QV4::CompiledData::Binding::Type_Boolean) {
+ return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: boolean expected"));
+ }
+ }
+ break;
+ case QVariant::Vector2D: {
+ struct {
+ float xp;
+ float yp;
+ } vec;
+ if (!QQmlStringConverters::createFromString(QMetaType::QVector2D, binding->valueAsString(qmlUnit), &vec, sizeof(vec))) {
+ return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: 2D vector expected"));
+ }
+ }
+ break;
+ case QVariant::Vector3D: {
+ struct {
+ float xp;
+ float yp;
+ float zy;
+ } vec;
+ if (!QQmlStringConverters::createFromString(QMetaType::QVector3D, binding->valueAsString(qmlUnit), &vec, sizeof(vec))) {
+ return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: 3D vector expected"));
+ }
+ }
+ break;
+ case QVariant::Vector4D: {
+ struct {
+ float xp;
+ float yp;
+ float zy;
+ float wp;
+ } vec;
+ if (!QQmlStringConverters::createFromString(QMetaType::QVector4D, binding->valueAsString(qmlUnit), &vec, sizeof(vec))) {
+ return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: 4D vector expected"));
+ }
+ }
+ break;
+ case QVariant::Quaternion: {
+ struct {
+ float wp;
+ float xp;
+ float yp;
+ float zp;
+ } vec;
+ if (!QQmlStringConverters::createFromString(QMetaType::QQuaternion, binding->valueAsString(qmlUnit), &vec, sizeof(vec))) {
+ return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: quaternion expected"));
+ }
+ }
+ break;
+ case QVariant::RegExp:
+ return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: regular expression expected; use /pattern/ syntax"));
+ default: {
+ // generate single literal value assignment to a list property if required
+ if (property->propType() == qMetaTypeId<QList<qreal> >()) {
+ if (binding->type != QV4::CompiledData::Binding::Type_Number) {
+ return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: number or array of numbers expected"));
+ }
+ break;
+ } else if (property->propType() == qMetaTypeId<QList<int> >()) {
+ bool ok = (binding->type == QV4::CompiledData::Binding::Type_Number);
+ if (ok) {
+ double n = binding->valueAsNumber();
+ if (double(int(n)) != n)
+ ok = false;
+ }
+ if (!ok)
+ return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: int or array of ints expected"));
+ break;
+ } else if (property->propType() == qMetaTypeId<QList<bool> >()) {
+ if (binding->type != QV4::CompiledData::Binding::Type_Boolean) {
+ return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: bool or array of bools expected"));
+ }
+ break;
+ } else if (property->propType() == qMetaTypeId<QList<QUrl> >()) {
+ if (binding->type != QV4::CompiledData::Binding::Type_String) {
+ return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: url or array of urls expected"));
+ }
+ break;
+ } else if (property->propType() == qMetaTypeId<QList<QString> >()) {
+ if (!binding->evaluatesToString()) {
+ return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: string or array of strings expected"));
+ }
+ break;
+ } else if (property->propType() == qMetaTypeId<QJSValue>()) {
+ break;
+ } else if (property->propType() == qMetaTypeId<QQmlScriptString>()) {
+ break;
+ }
+
+ // otherwise, try a custom type assignment
+ QQmlMetaType::StringConverter converter = QQmlMetaType::customStringConverter(property->propType());
+ if (!converter) {
+ return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: unsupported type \"%1\"").arg(QString::fromLatin1(QMetaType::typeName(property->propType()))));
+ }
+ }
+ break;
+ }
+ return noError;
+}
+
+/*!
+ Returns true if from can be assigned to a (QObject) property of type
+ to.
+*/
+bool QQmlPropertyValidator::canCoerce(int to, QQmlPropertyCache *fromMo) const
+{
+ QQmlPropertyCache *toMo = enginePrivate->rawPropertyCacheForType(to);
+
+ while (fromMo) {
+ if (fromMo == toMo)
+ return true;
+ fromMo = fromMo->parent();
+ }
+ return false;
+}
+
+QVector<QQmlCompileError> QQmlPropertyValidator::recordError(const QV4::CompiledData::Location &location, const QString &description) const
+{
+ QVector<QQmlCompileError> errors;
+ errors.append(QQmlCompileError(location, description));
+ return errors;
+}
+
+QVector<QQmlCompileError> QQmlPropertyValidator::recordError(const QQmlCompileError &error) const
+{
+ QVector<QQmlCompileError> errors;
+ errors.append(error);
+ return errors;
+}
+
+QQmlCompileError QQmlPropertyValidator::validateObjectBinding(QQmlPropertyData *property, const QString &propertyName, const QV4::CompiledData::Binding *binding) const
+{
+ QQmlCompileError noError;
+
+ if (binding->flags & QV4::CompiledData::Binding::IsOnAssignment) {
+ Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Object);
+
+ bool isValueSource = false;
+ bool isPropertyInterceptor = false;
+
+ QQmlType *qmlType = 0;
+ const QV4::CompiledData::Object *targetObject = qmlUnit->objectAt(binding->value.objectIndex);
+ if (auto *typeRef = resolvedTypes.value(targetObject->inheritedTypeNameIndex)) {
+ QQmlPropertyCache *cache = typeRef->createPropertyCache(QQmlEnginePrivate::get(enginePrivate));
+ const QMetaObject *mo = cache->firstCppMetaObject();
+ while (mo && !qmlType) {
+ qmlType = QQmlMetaType::qmlType(mo);
+ mo = mo->superClass();
+ }
+ Q_ASSERT(qmlType);
+ }
+
+ if (qmlType) {
+ isValueSource = qmlType->propertyValueSourceCast() != -1;
+ isPropertyInterceptor = qmlType->propertyValueInterceptorCast() != -1;
+ }
+
+ if (!isValueSource && !isPropertyInterceptor) {
+ return QQmlCompileError(binding->valueLocation, tr("\"%1\" cannot operate on \"%2\"").arg(stringAt(targetObject->inheritedTypeNameIndex)).arg(propertyName));
+ }
+
+ return noError;
+ }
+
+ if (QQmlMetaType::isInterface(property->propType())) {
+ // Can only check at instantiation time if the created sub-object successfully casts to the
+ // target interface.
+ return noError;
+ } else if (property->propType() == QMetaType::QVariant) {
+ // We can convert everything to QVariant :)
+ return noError;
+ } else if (property->isQList()) {
+ const int listType = enginePrivate->listType(property->propType());
+ if (!QQmlMetaType::isInterface(listType)) {
+ QQmlPropertyCache *source = propertyCaches.at(binding->value.objectIndex);
+ if (!canCoerce(listType, source)) {
+ return QQmlCompileError(binding->valueLocation, tr("Cannot assign object to list property \"%1\"").arg(propertyName));
+ }
+ }
+ return noError;
+ } else if (qmlUnit->objectAt(binding->value.objectIndex)->flags & QV4::CompiledData::Object::IsComponent) {
+ return noError;
+ } else if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject && property->isFunction()) {
+ return noError;
+ } else if (QQmlValueTypeFactory::isValueType(property->propType())) {
+ return QQmlCompileError(binding->location, tr("Unexpected object assignment"));
+ } else if (property->propType() == qMetaTypeId<QQmlScriptString>()) {
+ return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: script expected"));
+ } else {
+ // We want to raw metaObject here as the raw metaobject is the
+ // actual property type before we applied any extensions that might
+ // effect the properties on the type, but don't effect assignability
+ QQmlPropertyCache *propertyMetaObject = enginePrivate->rawPropertyCacheForType(property->propType());
+
+ // Will be true if the assgned type inherits propertyMetaObject
+ bool isAssignable = false;
+ // Determine isAssignable value
+ if (propertyMetaObject) {
+ QQmlPropertyCache *c = propertyCaches.at(binding->value.objectIndex);
+ while (c && !isAssignable) {
+ isAssignable |= c == propertyMetaObject;
+ c = c->parent();
+ }
+ }
+
+ if (!isAssignable) {
+ return QQmlCompileError(binding->valueLocation, tr("Cannot assign object to property"));
+ }
+ }
+ return noError;
+}
+
+QT_END_NAMESPACE
diff --git a/src/qml/compiler/qqmlpropertyvalidator_p.h b/src/qml/compiler/qqmlpropertyvalidator_p.h
new file mode 100644
index 0000000000..d0bd314461
--- /dev/null
+++ b/src/qml/compiler/qqmlpropertyvalidator_p.h
@@ -0,0 +1,87 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the tools applications 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 QQMLPROPERTYVALIDATOR_P_H
+#define QQMLPROPERTYVALIDATOR_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/qqmltypecompiler_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQmlPropertyValidator
+{
+ Q_DECLARE_TR_FUNCTIONS(QQmlPropertyValidator)
+public:
+ QQmlPropertyValidator(QQmlEnginePrivate *enginePrivate, const QQmlImports &imports, QV4::CompiledData::CompilationUnit *compilationUnit);
+
+ QVector<QQmlCompileError> validate();
+
+private:
+ QVector<QQmlCompileError> validateObject(int objectIndex, const QV4::CompiledData::Binding *instantiatingBinding, bool populatingValueTypeGroupProperty = false) const;
+ QQmlCompileError validateLiteralBinding(QQmlPropertyCache *propertyCache, QQmlPropertyData *property, const QV4::CompiledData::Binding *binding) const;
+ QQmlCompileError validateObjectBinding(QQmlPropertyData *property, const QString &propertyName, const QV4::CompiledData::Binding *binding) const;
+
+ bool canCoerce(int to, QQmlPropertyCache *fromMo) const;
+
+ QVector<QQmlCompileError> recordError(const QV4::CompiledData::Location &location, const QString &description) const Q_REQUIRED_RESULT;
+ QVector<QQmlCompileError> recordError(const QQmlCompileError &error) const Q_REQUIRED_RESULT;
+ QString stringAt(int index) const { return qmlUnit->stringAt(index); }
+
+ QQmlEnginePrivate *enginePrivate;
+ const QQmlImports &imports;
+ const QV4::CompiledData::Unit *qmlUnit;
+ const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypes;
+ const QQmlPropertyCacheVector &propertyCaches;
+
+ QVector<QV4::CompiledData::BindingPropertyData> * const bindingPropertyDataPerObject;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLPROPERTYVALIDATOR_P_H
diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp
index caa4a55d3d..2308e66609 100644
--- a/src/qml/compiler/qqmltypecompiler.cpp
+++ b/src/qml/compiler/qqmltypecompiler.cpp
@@ -44,9 +44,10 @@
#include <private/qqmlcustomparser_p.h>
#include <private/qqmlvmemetaobject_p.h>
#include <private/qqmlcomponent_p.h>
-#include <private/qqmlstringconverters_p.h>
#include <private/qv4ssa_p.h>
+#include "qqmlpropertycachecreator_p.h"
+
#define COMPILE_EXCEPTION(token, desc) \
{ \
recordError((token)->location, desc); \
@@ -55,95 +56,35 @@
QT_BEGIN_NAMESPACE
-QQmlTypeCompiler::QQmlTypeCompiler(QQmlEnginePrivate *engine, QQmlCompiledData *compiledData, QQmlTypeData *typeData, QmlIR::Document *parsedQML)
- : engine(engine)
- , compiledData(compiledData)
+QQmlTypeCompiler::QQmlTypeCompiler(QQmlEnginePrivate *engine, QQmlTypeData *typeData,
+ QmlIR::Document *parsedQML, const QQmlRefPointer<QQmlTypeNameCache> &importCache,
+ const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypeCache)
+ : resolvedTypes(resolvedTypeCache)
+ , engine(engine)
, typeData(typeData)
+ , importCache(importCache)
, document(parsedQML)
{
}
-bool QQmlTypeCompiler::compile()
+QV4::CompiledData::CompilationUnit *QQmlTypeCompiler::compile()
{
- compiledData->importCache = new QQmlTypeNameCache;
-
- foreach (const QString &ns, typeData->namespaces())
- compiledData->importCache->add(ns);
-
- // Add any Composite Singletons that were used to the import cache
- foreach (const QQmlTypeData::TypeReference &singleton, typeData->compositeSingletons())
- compiledData->importCache->add(singleton.type->qmlTypeName(), singleton.type->sourceUrl(), singleton.prefix);
-
- typeData->imports().populateCache(compiledData->importCache);
-
- const QHash<int, QQmlTypeData::TypeReference> &resolvedTypes = typeData->resolvedTypeRefs();
- for (QHash<int, QQmlTypeData::TypeReference>::ConstIterator resolvedType = resolvedTypes.constBegin(), end = resolvedTypes.constEnd();
- resolvedType != end; ++resolvedType) {
- QScopedPointer<QQmlCompiledData::TypeReference> ref(new QQmlCompiledData::TypeReference);
- QQmlType *qmlType = resolvedType->type;
- if (resolvedType->typeData) {
- if (resolvedType->needsCreation && qmlType->isCompositeSingleton()) {
- QQmlError error;
- QString reason = tr("Composite Singleton Type %1 is not creatable.").arg(qmlType->qmlTypeName());
- error.setDescription(reason);
- error.setColumn(resolvedType->location.column);
- error.setLine(resolvedType->location.line);
- recordError(error);
- return false;
- }
- ref->component = resolvedType->typeData->compiledData();
- ref->component->addref();
- } else if (qmlType) {
- ref->type = qmlType;
- Q_ASSERT(ref->type);
-
- if (resolvedType->needsCreation && !ref->type->isCreatable()) {
- QQmlError error;
- QString reason = ref->type->noCreationReason();
- if (reason.isEmpty())
- reason = tr("Element is not creatable.");
- error.setDescription(reason);
- error.setColumn(resolvedType->location.column);
- error.setLine(resolvedType->location.line);
- recordError(error);
- return false;
- }
-
- if (ref->type->containsRevisionedAttributes()) {
- ref->typePropertyCache = engine->cache(ref->type,
- resolvedType->minorVersion);
- if (!ref->typePropertyCache) {
- QQmlError cacheError;
- cacheError.setColumn(resolvedType->location.column);
- cacheError.setLine(resolvedType->location.line);
- recordError(cacheError);
- return false;
- }
- ref->typePropertyCache->addref();
- }
- }
- ref->majorVersion = resolvedType->majorVersion;
- ref->minorVersion = resolvedType->minorVersion;
- ref->doDynamicTypeCheck();
- compiledData->resolvedTypes.insert(resolvedType.key(), ref.take());
- }
-
// Build property caches and VME meta object data
- for (QHash<int, QQmlCompiledData::TypeReference*>::ConstIterator it = compiledData->resolvedTypes.constBegin(), end = compiledData->resolvedTypes.constEnd();
+ for (auto it = resolvedTypes.constBegin(), end = resolvedTypes.constEnd();
it != end; ++it) {
QQmlCustomParser *customParser = (*it)->type ? (*it)->type->customParser() : 0;
if (customParser)
customParsers.insert(it.key(), customParser);
}
- compiledData->metaObjects.reserve(document->objects.count());
- compiledData->propertyCaches.reserve(document->objects.count());
-
{
- QQmlPropertyCacheCreator propertyCacheBuilder(this);
- if (!propertyCacheBuilder.buildMetaObjects())
- return false;
+ QQmlPropertyCacheCreator<QQmlTypeCompiler> propertyCacheBuilder(&m_propertyCaches, engine, this, imports());
+ QQmlCompileError error = propertyCacheBuilder.buildMetaObjects();
+ if (error.isSet()) {
+ recordError(error);
+ return nullptr;
+ }
}
{
@@ -154,13 +95,13 @@ bool QQmlTypeCompiler::compile()
{
SignalHandlerConverter converter(this);
if (!converter.convertSignalHandlerExpressionsToFunctionDeclarations())
- return false;
+ return nullptr;
}
{
QQmlEnumTypeResolver enumResolver(this);
if (!enumResolver.resolveEnumBindings())
- return false;
+ return nullptr;
}
{
@@ -173,34 +114,19 @@ bool QQmlTypeCompiler::compile()
annotator.annotateBindingsToAliases();
}
- // Collect imported scripts
- const QList<QQmlTypeData::ScriptReference> &scripts = typeData->resolvedScripts();
- compiledData->scripts.reserve(scripts.count());
- for (int scriptIndex = 0; scriptIndex < scripts.count(); ++scriptIndex) {
- const QQmlTypeData::ScriptReference &script = scripts.at(scriptIndex);
-
- QStringRef qualifier(&script.qualifier);
- QString enclosingNamespace;
-
- const int lastDotIndex = qualifier.lastIndexOf(QLatin1Char('.'));
- if (lastDotIndex != -1) {
- enclosingNamespace = qualifier.left(lastDotIndex).toString();
- qualifier = qualifier.mid(lastDotIndex+1);
- }
-
- compiledData->importCache->add(qualifier.toString(), scriptIndex, enclosingNamespace);
- QQmlScriptData *scriptData = script.script->scriptData();
- scriptData->addref();
- compiledData->scripts << scriptData;
- }
-
// Resolve component boundaries and aliases
{
// Scan for components, determine their scopes and resolve aliases within the scope.
QQmlComponentAndAliasResolver resolver(this);
if (!resolver.resolve())
- return false;
+ return nullptr;
+ }
+
+ {
+ QQmlDeferredAndCustomParserBindingScanner deferredAndCustomParserBindingScanner(this);
+ if (!deferredAndCustomParserBindingScanner.scanObject())
+ return nullptr;
}
// Compile JS binding expressions and signal handlers
@@ -212,10 +138,10 @@ bool QQmlTypeCompiler::compile()
sss.scan();
}
- QmlIR::JSCodeGen v4CodeGenerator(typeData->finalUrlString(), document->code, &document->jsModule, &document->jsParserEngine, document->program, compiledData->importCache, &document->jsGenerator.stringTable);
+ QmlIR::JSCodeGen v4CodeGenerator(typeData->finalUrlString(), document->code, &document->jsModule, &document->jsParserEngine, document->program, importCache, &document->jsGenerator.stringTable);
QQmlJSCodeGenerator jsCodeGen(this, &v4CodeGenerator);
if (!jsCodeGen.generateCodeForComponents())
- return false;
+ return nullptr;
QQmlJavaScriptBindingExpressionSimplificationPass pass(this);
pass.reduceTranslationBindings();
@@ -230,70 +156,45 @@ bool QQmlTypeCompiler::compile()
// Generate QML compiled type data structures
QmlIR::QmlUnitGenerator qmlGenerator;
- QV4::CompiledData::Unit *qmlUnit = qmlGenerator.generate(*document);
+ QV4::CompiledData::Unit *qmlUnit = qmlGenerator.generate(*document, QQmlEnginePrivate::get(engine), resolvedTypes);
Q_ASSERT(document->javaScriptCompilationUnit);
// The js unit owns the data and will free the qml unit.
document->javaScriptCompilationUnit->data = qmlUnit;
- compiledData->compilationUnit = document->javaScriptCompilationUnit;
-
- // Add to type registry of composites
- if (compiledData->isCompositeType())
- engine->registerInternalCompositeType(compiledData);
- else {
- const QV4::CompiledData::Object *obj = qmlUnit->objectAt(qmlUnit->indexOfRootObject);
- QQmlCompiledData::TypeReference *typeRef = compiledData->resolvedTypes.value(obj->inheritedTypeNameIndex);
- Q_ASSERT(typeRef);
- if (typeRef->component) {
- compiledData->metaTypeId = typeRef->component->metaTypeId;
- compiledData->listMetaTypeId = typeRef->component->listMetaTypeId;
- } else {
- compiledData->metaTypeId = typeRef->type->typeId();
- compiledData->listMetaTypeId = typeRef->type->qListTypeId();
- }
- }
-
- // Sanity check property bindings
- QQmlPropertyValidator validator(this);
- if (!validator.validate())
- return false;
+ QV4::CompiledData::CompilationUnit *compilationUnit = document->javaScriptCompilationUnit;
+ compilationUnit = document->javaScriptCompilationUnit;
+ compilationUnit->importCache = importCache;
+ compilationUnit->resolvedTypes = resolvedTypes;
+ compilationUnit->propertyCaches = std::move(m_propertyCaches);
+ Q_ASSERT(compilationUnit->propertyCaches.count() == static_cast<int>(compilationUnit->data->nObjects));
- // Collect some data for instantiation later.
- int bindingCount = 0;
- int parserStatusCount = 0;
- int objectCount = 0;
- for (quint32 i = 0; i < qmlUnit->nObjects; ++i) {
- const QV4::CompiledData::Object *obj = qmlUnit->objectAt(i);
- bindingCount += obj->nBindings;
- if (QQmlCompiledData::TypeReference *typeRef = compiledData->resolvedTypes.value(obj->inheritedTypeNameIndex)) {
- if (QQmlType *qmlType = typeRef->type) {
- if (qmlType->parserStatusCast() != -1)
- ++parserStatusCount;
- }
- ++objectCount;
- if (typeRef->component) {
- bindingCount += typeRef->component->totalBindingsCount;
- parserStatusCount += typeRef->component->totalParserStatusCount;
- objectCount += typeRef->component->totalObjectCount;
- }
- }
- }
- compiledData->totalBindingsCount = bindingCount;
- compiledData->totalParserStatusCount = parserStatusCount;
- compiledData->totalObjectCount = objectCount;
+ if (errors.isEmpty())
+ return compilationUnit;
+ else
+ return nullptr;
+}
- Q_ASSERT(compiledData->propertyCaches.count() == static_cast<int>(compiledData->compilationUnit->data->nObjects));
+void QQmlTypeCompiler::recordError(QQmlError error)
+{
+ error.setUrl(url());
+ errors << error;
+}
- return errors.isEmpty();
+void QQmlTypeCompiler::recordError(const QV4::CompiledData::Location &location, const QString &description)
+{
+ QQmlError error;
+ error.setLine(location.line);
+ error.setColumn(location.column);
+ error.setDescription(description);
+ error.setUrl(url());
+ errors << error;
}
-void QQmlTypeCompiler::recordError(const QQmlError &error)
+void QQmlTypeCompiler::recordError(const QQmlCompileError &error)
{
- QQmlError e = error;
- e.setUrl(url());
- errors << e;
+ recordError(error.location, error.description);
}
QString QQmlTypeCompiler::stringAt(int idx) const
@@ -313,7 +214,7 @@ QV4::IR::Module *QQmlTypeCompiler::jsIRModule() const
const QV4::CompiledData::Unit *QQmlTypeCompiler::qmlUnit() const
{
- return compiledData->compilationUnit->data;
+ return document->javaScriptCompilationUnit->data;
}
const QQmlImports *QQmlTypeCompiler::imports() const
@@ -321,12 +222,7 @@ const QQmlImports *QQmlTypeCompiler::imports() const
return &typeData->imports();
}
-QHash<int, QQmlCompiledData::TypeReference*> *QQmlTypeCompiler::resolvedTypes()
-{
- return &compiledData->resolvedTypes;
-}
-
-QList<QmlIR::Object *> *QQmlTypeCompiler::qmlObjects()
+QVector<QmlIR::Object *> *QQmlTypeCompiler::qmlObjects() const
{
return &document->objects;
}
@@ -336,45 +232,20 @@ int QQmlTypeCompiler::rootObjectIndex() const
return document->indexOfRootObject;
}
-void QQmlTypeCompiler::setPropertyCaches(const QVector<QQmlPropertyCache *> &caches)
+void QQmlTypeCompiler::setPropertyCaches(QQmlPropertyCacheVector &&caches)
{
- compiledData->propertyCaches = caches;
- Q_ASSERT(caches.count() >= document->indexOfRootObject);
- if (compiledData->rootPropertyCache)
- compiledData->rootPropertyCache->release();
- compiledData->rootPropertyCache = caches.at(document->indexOfRootObject);
- compiledData->rootPropertyCache->addref();
+ m_propertyCaches = std::move(caches);
+ Q_ASSERT(m_propertyCaches.count() >= document->indexOfRootObject);
}
-const QVector<QQmlPropertyCache *> &QQmlTypeCompiler::propertyCaches() const
+const QQmlPropertyCacheVector *QQmlTypeCompiler::propertyCaches() const
{
- return compiledData->propertyCaches;
+ return &m_propertyCaches;
}
-void QQmlTypeCompiler::setVMEMetaObjects(const QVector<QByteArray> &metaObjects)
+QQmlPropertyCacheVector &&QQmlTypeCompiler::takePropertyCaches()
{
- Q_ASSERT(compiledData->metaObjects.isEmpty());
- compiledData->metaObjects = metaObjects;
-}
-
-QVector<QByteArray> *QQmlTypeCompiler::vmeMetaObjects() const
-{
- return &compiledData->metaObjects;
-}
-
-QHash<int, int> *QQmlTypeCompiler::objectIndexToIdForRoot()
-{
- return &compiledData->objectIndexToIdForRoot;
-}
-
-QHash<int, QHash<int, int> > *QQmlTypeCompiler::objectIndexToIdPerComponent()
-{
- return &compiledData->objectIndexToIdPerComponent;
-}
-
-QHash<int, QBitArray> *QQmlTypeCompiler::customParserBindings()
-{
- return &compiledData->customParserBindings;
+ return std::move(m_propertyCaches);
}
QQmlJS::MemoryPool *QQmlTypeCompiler::memoryPool()
@@ -392,14 +263,9 @@ const QV4::Compiler::StringTableGenerator *QQmlTypeCompiler::stringPool() const
return &document->jsGenerator.stringTable;
}
-void QQmlTypeCompiler::setDeferredBindingsPerObject(const QHash<int, QBitArray> &deferredBindingsPerObject)
-{
- compiledData->deferredBindingsPerObject = deferredBindingsPerObject;
-}
-
void QQmlTypeCompiler::setBindingPropertyDataPerObject(const QVector<QV4::CompiledData::BindingPropertyData> &propertyData)
{
- compiledData->compilationUnit->bindingPropertyDataPerObject = propertyData;
+ document->javaScriptCompilationUnit->bindingPropertyDataPerObject = propertyData;
}
QString QQmlTypeCompiler::bindingAsString(const QmlIR::Object *object, int scriptIndex) const
@@ -407,499 +273,34 @@ QString QQmlTypeCompiler::bindingAsString(const QmlIR::Object *object, int scrip
return object->bindingAsString(document, scriptIndex);
}
-QQmlCompilePass::QQmlCompilePass(QQmlTypeCompiler *typeCompiler)
- : compiler(typeCompiler)
-{
-}
-
-void QQmlCompilePass::recordError(const QV4::CompiledData::Location &location, const QString &description) const
-{
- QQmlError error;
- error.setLine(location.line);
- error.setColumn(location.column);
- error.setDescription(description);
- compiler->recordError(error);
-}
-
-static QAtomicInt classIndexCounter(0);
-
-QQmlPropertyCacheCreator::QQmlPropertyCacheCreator(QQmlTypeCompiler *typeCompiler)
- : QQmlCompilePass(typeCompiler)
- , enginePrivate(typeCompiler->enginePrivate())
- , qmlObjects(*typeCompiler->qmlObjects())
- , imports(typeCompiler->imports())
- , resolvedTypes(typeCompiler->resolvedTypes())
-{
-}
-
-QQmlPropertyCacheCreator::~QQmlPropertyCacheCreator()
-{
- for (int i = 0; i < propertyCaches.count(); ++i)
- if (QQmlPropertyCache *cache = propertyCaches.at(i))
- cache->release();
- propertyCaches.clear();
-}
-
-bool QQmlPropertyCacheCreator::buildMetaObjects()
-{
- propertyCaches.resize(qmlObjects.count());
- vmeMetaObjects.resize(qmlObjects.count());
-
- if (!buildMetaObjectRecursively(compiler->rootObjectIndex(), /*referencing object*/-1, /*instantiating binding*/0))
- return false;
-
- compiler->setVMEMetaObjects(vmeMetaObjects);
- compiler->setPropertyCaches(propertyCaches);
- propertyCaches.clear();
-
- return true;
-}
-
-bool QQmlPropertyCacheCreator::buildMetaObjectRecursively(int objectIndex, int referencingObjectIndex, const QV4::CompiledData::Binding *instantiatingBinding)
+void QQmlTypeCompiler::addImport(const QString &module, const QString &qualifier, int majorVersion, int minorVersion)
{
- const QmlIR::Object *obj = qmlObjects.at(objectIndex);
-
- QQmlPropertyCache *baseTypeCache = 0;
- QQmlPropertyData *instantiatingProperty = 0;
- if (instantiatingBinding && instantiatingBinding->type == QV4::CompiledData::Binding::Type_GroupProperty) {
- Q_ASSERT(referencingObjectIndex >= 0);
- QQmlPropertyCache *parentCache = propertyCaches.at(referencingObjectIndex);
- Q_ASSERT(parentCache);
- Q_ASSERT(instantiatingBinding->propertyNameIndex != 0);
-
- bool notInRevision = false;
- instantiatingProperty = QmlIR::PropertyResolver(parentCache).property(stringAt(instantiatingBinding->propertyNameIndex), &notInRevision);
- if (instantiatingProperty) {
- if (instantiatingProperty->isQObject()) {
- baseTypeCache = enginePrivate->rawPropertyCacheForType(instantiatingProperty->propType);
- Q_ASSERT(baseTypeCache);
- } else if (const QMetaObject *vtmo = QQmlValueTypeFactory::metaObjectForMetaType(instantiatingProperty->propType)) {
- baseTypeCache = enginePrivate->cache(vtmo);
- Q_ASSERT(baseTypeCache);
- }
- }
- }
-
- bool needVMEMetaObject = obj->propertyCount() != 0 || obj->signalCount() != 0 || obj->functionCount() != 0;
- if (!needVMEMetaObject) {
- for (const QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) {
- if (binding->type == QV4::CompiledData::Binding::Type_Object && (binding->flags & QV4::CompiledData::Binding::IsOnAssignment)) {
-
- // On assignments are implemented using value interceptors, which require a VME meta object.
- needVMEMetaObject = true;
-
- // If the on assignment is inside a group property, we need to distinguish between QObject based
- // group properties and value type group properties. For the former the base type is derived from
- // the property that references us, for the latter we only need a meta-object on the referencing object
- // because interceptors can't go to the shared value type instances.
- if (instantiatingProperty && QQmlValueTypeFactory::isValueType(instantiatingProperty->propType)) {
- needVMEMetaObject = false;
- if (!ensureMetaObject(referencingObjectIndex))
- return false;
- }
- break;
- }
- }
- }
-
- if (obj->inheritedTypeNameIndex != 0) {
- QQmlCompiledData::TypeReference *typeRef = resolvedTypes->value(obj->inheritedTypeNameIndex);
- Q_ASSERT(typeRef);
-
- if (typeRef->isFullyDynamicType) {
- if (obj->propertyCount() > 0) {
- recordError(obj->location, tr("Fully dynamic types cannot declare new properties."));
- return false;
- }
- if (obj->signalCount() > 0) {
- recordError(obj->location, tr("Fully dynamic types cannot declare new signals."));
- return false;
- }
- if (obj->functionCount() > 0) {
- recordError(obj->location, tr("Fully Dynamic types cannot declare new functions."));
- return false;
- }
- }
-
- baseTypeCache = typeRef->createPropertyCache(QQmlEnginePrivate::get(enginePrivate));
- Q_ASSERT(baseTypeCache);
- } else if (instantiatingBinding && instantiatingBinding->isAttachedProperty()) {
- QQmlCompiledData::TypeReference *typeRef = resolvedTypes->value(instantiatingBinding->propertyNameIndex);
- Q_ASSERT(typeRef);
- QQmlType *qmltype = typeRef->type;
- if (!qmltype) {
- QString propertyName = stringAt(instantiatingBinding->propertyNameIndex);
- if (imports->resolveType(propertyName, &qmltype, 0, 0, 0)) {
- if (qmltype->isComposite()) {
- QQmlTypeData *tdata = enginePrivate->typeLoader.getType(qmltype->sourceUrl());
- Q_ASSERT(tdata);
- Q_ASSERT(tdata->isComplete());
-
- QQmlCompiledData *data = tdata->compiledData();
- qmltype = QQmlMetaType::qmlType(data->metaTypeId);
-
- tdata->release();
- }
- }
- }
+ const quint32 moduleIdx = registerString(module);
+ const quint32 qualifierIdx = registerString(qualifier);
- const QMetaObject *attachedMo = qmltype ? qmltype->attachedPropertiesType(enginePrivate) : 0;
- if (!attachedMo) {
- recordError(instantiatingBinding->location, tr("Non-existent attached object"));
- return false;
- }
- baseTypeCache = enginePrivate->cache(attachedMo);
- Q_ASSERT(baseTypeCache);
- }
-
- if (baseTypeCache) {
- if (needVMEMetaObject) {
- if (!createMetaObject(objectIndex, obj, baseTypeCache))
- return false;
- } else {
- propertyCaches[objectIndex] = baseTypeCache;
- baseTypeCache->addref();
- }
- }
-
- if (propertyCaches.at(objectIndex)) {
- for (const QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next)
- if (binding->type >= QV4::CompiledData::Binding::Type_Object) {
- if (!buildMetaObjectRecursively(binding->value.objectIndex, objectIndex, binding))
- return false;
- }
+ for (int i = 0, count = document->imports.count(); i < count; ++i) {
+ const QV4::CompiledData::Import *existingImport = document->imports.at(i);
+ if (existingImport->type == QV4::CompiledData::Import::ImportLibrary
+ && existingImport->uriIndex == moduleIdx
+ && existingImport->qualifierIndex == qualifierIdx)
+ return;
}
-
- return true;
+ auto pool = memoryPool();
+ QV4::CompiledData::Import *import = pool->New<QV4::CompiledData::Import>();
+ import->type = QV4::CompiledData::Import::ImportLibrary;
+ import->majorVersion = majorVersion;
+ import->minorVersion = minorVersion;
+ import->uriIndex = moduleIdx;
+ import->qualifierIndex = qualifierIdx;
+ document->imports.append(import);
}
-bool QQmlPropertyCacheCreator::ensureMetaObject(int objectIndex)
+QQmlCompilePass::QQmlCompilePass(QQmlTypeCompiler *typeCompiler)
+ : compiler(typeCompiler)
{
- if (!vmeMetaObjects.at(objectIndex).isEmpty())
- return true;
- const QmlIR::Object *obj = qmlObjects.at(objectIndex);
- QQmlCompiledData::TypeReference *typeRef = resolvedTypes->value(obj->inheritedTypeNameIndex);
- Q_ASSERT(typeRef);
- QQmlPropertyCache *baseTypeCache = typeRef->createPropertyCache(QQmlEnginePrivate::get(enginePrivate));
- return createMetaObject(objectIndex, obj, baseTypeCache);
-}
-
-bool QQmlPropertyCacheCreator::createMetaObject(int objectIndex, const QmlIR::Object *obj, QQmlPropertyCache *baseTypeCache)
-{
- QQmlPropertyCache *cache = baseTypeCache->copyAndReserve(obj->propertyCount(),
- obj->functionCount() + obj->propertyCount() + obj->signalCount(),
- obj->signalCount() + obj->propertyCount());
- propertyCaches[objectIndex] = cache;
-
- struct TypeData {
- QV4::CompiledData::Property::Type dtype;
- int metaType;
- } builtinTypes[] = {
- { QV4::CompiledData::Property::Var, QMetaType::QVariant },
- { QV4::CompiledData::Property::Variant, QMetaType::QVariant },
- { QV4::CompiledData::Property::Int, QMetaType::Int },
- { QV4::CompiledData::Property::Bool, QMetaType::Bool },
- { QV4::CompiledData::Property::Real, QMetaType::Double },
- { QV4::CompiledData::Property::String, QMetaType::QString },
- { QV4::CompiledData::Property::Url, QMetaType::QUrl },
- { QV4::CompiledData::Property::Color, QMetaType::QColor },
- { QV4::CompiledData::Property::Font, QMetaType::QFont },
- { QV4::CompiledData::Property::Time, QMetaType::QTime },
- { QV4::CompiledData::Property::Date, QMetaType::QDate },
- { QV4::CompiledData::Property::DateTime, QMetaType::QDateTime },
- { QV4::CompiledData::Property::Rect, QMetaType::QRectF },
- { QV4::CompiledData::Property::Point, QMetaType::QPointF },
- { QV4::CompiledData::Property::Size, QMetaType::QSizeF },
- { QV4::CompiledData::Property::Vector2D, QMetaType::QVector2D },
- { QV4::CompiledData::Property::Vector3D, QMetaType::QVector3D },
- { QV4::CompiledData::Property::Vector4D, QMetaType::QVector4D },
- { QV4::CompiledData::Property::Matrix4x4, QMetaType::QMatrix4x4 },
- { QV4::CompiledData::Property::Quaternion, QMetaType::QQuaternion }
- };
- static const uint builtinTypeCount = sizeof(builtinTypes) / sizeof(TypeData);
-
- QByteArray newClassName;
-
- if (objectIndex == compiler->rootObjectIndex()) {
- QString path = compiler->url().path();
- int lastSlash = path.lastIndexOf(QLatin1Char('/'));
- if (lastSlash > -1) {
- const QStringRef nameBase = path.midRef(lastSlash + 1, path.length() - lastSlash - 5);
- if (!nameBase.isEmpty() && nameBase.at(0).isUpper())
- newClassName = nameBase.toUtf8() + "_QMLTYPE_" +
- QByteArray::number(classIndexCounter.fetchAndAddRelaxed(1));
- }
- }
- if (newClassName.isEmpty()) {
- newClassName = QQmlMetaObject(baseTypeCache).className();
- newClassName.append("_QML_");
- newClassName.append(QByteArray::number(classIndexCounter.fetchAndAddRelaxed(1)));
- }
-
- cache->_dynamicClassName = newClassName;
-
- int aliasCount = 0;
- int varPropCount = 0;
-
- QmlIR::PropertyResolver resolver(baseTypeCache);
-
- for (const QmlIR::Property *p = obj->firstProperty(); p; p = p->next) {
- if (p->type == QV4::CompiledData::Property::Alias)
- aliasCount++;
- else if (p->type == QV4::CompiledData::Property::Var)
- varPropCount++;
-
- // No point doing this for both the alias and non alias cases
- bool notInRevision = false;
- QQmlPropertyData *d = resolver.property(stringAt(p->nameIndex), &notInRevision);
- if (d && d->isFinal())
- COMPILE_EXCEPTION(p, tr("Cannot override FINAL property"));
- }
-
- typedef QQmlVMEMetaData VMD;
-
- QByteArray &dynamicData = vmeMetaObjects[objectIndex] = QByteArray(sizeof(QQmlVMEMetaData)
- + obj->propertyCount() * sizeof(VMD::PropertyData)
- + obj->functionCount() * sizeof(VMD::MethodData)
- + aliasCount * sizeof(VMD::AliasData), 0);
-
- int effectivePropertyIndex = cache->propertyIndexCacheStart;
- int effectiveMethodIndex = cache->methodIndexCacheStart;
-
- // For property change signal override detection.
- // We prepopulate a set of signal names which already exist in the object,
- // and throw an error if there is a signal/method defined as an override.
- QSet<QString> seenSignals;
- seenSignals << QStringLiteral("destroyed") << QStringLiteral("parentChanged") << QStringLiteral("objectNameChanged");
- QQmlPropertyCache *parentCache = cache;
- while ((parentCache = parentCache->parent())) {
- if (int pSigCount = parentCache->signalCount()) {
- int pSigOffset = parentCache->signalOffset();
- for (int i = pSigOffset; i < pSigCount; ++i) {
- QQmlPropertyData *currPSig = parentCache->signal(i);
- // XXX TODO: find a better way to get signal name from the property data :-/
- for (QQmlPropertyCache::StringCache::ConstIterator iter = parentCache->stringCache.begin();
- iter != parentCache->stringCache.end(); ++iter) {
- if (currPSig == (*iter).second) {
- seenSignals.insert(iter.key());
- break;
- }
- }
- }
- }
- }
-
- // First set up notify signals for properties - first normal, then var, then alias
- enum { NSS_Normal = 0, NSS_Alias = 1 };
- for (int ii = NSS_Normal; ii <= NSS_Alias; ++ii) { // 0 == normal, 1 == var, 2 == alias
-
- if (ii == NSS_Alias && aliasCount == 0) continue;
-
- for (const QmlIR::Property *p = obj->firstProperty(); p; p = p->next) {
- if ((ii == NSS_Normal && p->type == QV4::CompiledData::Property::Alias) ||
- (ii == NSS_Alias && p->type != QV4::CompiledData::Property::Alias))
- continue;
-
- quint32 flags = QQmlPropertyData::IsSignal | QQmlPropertyData::IsFunction |
- QQmlPropertyData::IsVMESignal;
-
- QString changedSigName = stringAt(p->nameIndex) + QLatin1String("Changed");
- seenSignals.insert(changedSigName);
-
- cache->appendSignal(changedSigName, flags, effectiveMethodIndex++);
- }
- }
-
- // Dynamic signals
- for (const QmlIR::Signal *s = obj->firstSignal(); s; s = s->next) {
- const int paramCount = s->parameters->count;
-
- QList<QByteArray> names;
- names.reserve(paramCount);
- QVarLengthArray<int, 10> paramTypes(paramCount?(paramCount + 1):0);
-
- if (paramCount) {
- paramTypes[0] = paramCount;
-
- QmlIR::SignalParameter *param = s->parameters->first;
- for (int i = 0; i < paramCount; ++i, param = param->next) {
- names.append(stringAt(param->nameIndex).toUtf8());
- if (param->type < builtinTypeCount) {
- // built-in type
- paramTypes[i + 1] = builtinTypes[param->type].metaType;
- } else {
- // lazily resolved type
- Q_ASSERT(param->type == QV4::CompiledData::Property::Custom);
- const QString customTypeName = stringAt(param->customTypeNameIndex);
- QQmlType *qmltype = 0;
- if (!imports->resolveType(customTypeName, &qmltype, 0, 0, 0))
- COMPILE_EXCEPTION(s, tr("Invalid signal parameter type: %1").arg(customTypeName));
-
- if (qmltype->isComposite()) {
- QQmlTypeData *tdata = enginePrivate->typeLoader.getType(qmltype->sourceUrl());
- Q_ASSERT(tdata);
- Q_ASSERT(tdata->isComplete());
-
- QQmlCompiledData *data = tdata->compiledData();
-
- paramTypes[i + 1] = data->metaTypeId;
-
- tdata->release();
- } else {
- paramTypes[i + 1] = qmltype->typeId();
- }
- }
- }
- }
-
- ((QQmlVMEMetaData *)dynamicData.data())->signalCount++;
-
- quint32 flags = QQmlPropertyData::IsSignal | QQmlPropertyData::IsFunction |
- QQmlPropertyData::IsVMESignal;
- if (paramCount)
- flags |= QQmlPropertyData::HasArguments;
-
- QString signalName = stringAt(s->nameIndex);
- if (seenSignals.contains(signalName))
- COMPILE_EXCEPTION(s, tr("Duplicate signal name: invalid override of property change signal or superclass signal"));
- seenSignals.insert(signalName);
-
- cache->appendSignal(signalName, flags, effectiveMethodIndex++,
- paramCount?paramTypes.constData():0, names);
- }
-
-
- // Dynamic slots
- for (const QmlIR::Function *s = obj->firstFunction(); s; s = s->next) {
- QQmlJS::AST::FunctionDeclaration *astFunction = s->functionDeclaration;
-
- quint32 flags = QQmlPropertyData::IsFunction | QQmlPropertyData::IsVMEFunction;
-
- if (astFunction->formals)
- flags |= QQmlPropertyData::HasArguments;
-
- QString slotName = astFunction->name.toString();
- if (seenSignals.contains(slotName))
- COMPILE_EXCEPTION(s, tr("Duplicate method name: invalid override of property change signal or superclass signal"));
- // Note: we don't append slotName to the seenSignals list, since we don't
- // protect against overriding change signals or methods with properties.
-
- QList<QByteArray> parameterNames;
- QQmlJS::AST::FormalParameterList *param = astFunction->formals;
- while (param) {
- parameterNames << param->name.toUtf8();
- param = param->next;
- }
-
- cache->appendMethod(slotName, flags, effectiveMethodIndex++, parameterNames);
- }
-
-
- // Dynamic properties (except var and aliases)
- int effectiveSignalIndex = cache->signalHandlerIndexCacheStart;
- int propertyIdx = 0;
- for (const QmlIR::Property *p = obj->firstProperty(); p; p = p->next, ++propertyIdx) {
-
- if (p->type == QV4::CompiledData::Property::Alias)
- continue;
-
- int propertyType = 0;
- int vmePropertyType = 0;
- quint32 propertyFlags = 0;
-
- if (p->type == QV4::CompiledData::Property::Var) {
- propertyType = QMetaType::QVariant;
- vmePropertyType = QQmlVMEMetaData::VarPropertyType;
- propertyFlags = QQmlPropertyData::IsVarProperty;
- } else if (p->type < builtinTypeCount) {
- propertyType = builtinTypes[p->type].metaType;
- vmePropertyType = propertyType;
-
- if (p->type == QV4::CompiledData::Property::Variant)
- propertyFlags |= QQmlPropertyData::IsQVariant;
- } else {
- Q_ASSERT(p->type == QV4::CompiledData::Property::CustomList ||
- p->type == QV4::CompiledData::Property::Custom);
-
- QQmlType *qmltype = 0;
- if (!imports->resolveType(stringAt(p->customTypeNameIndex), &qmltype, 0, 0, 0)) {
- COMPILE_EXCEPTION(p, tr("Invalid property type"));
- }
-
- Q_ASSERT(qmltype);
- if (qmltype->isComposite()) {
- QQmlTypeData *tdata = enginePrivate->typeLoader.getType(qmltype->sourceUrl());
- Q_ASSERT(tdata);
- Q_ASSERT(tdata->isComplete());
-
- QQmlCompiledData *data = tdata->compiledData();
-
- if (p->type == QV4::CompiledData::Property::Custom) {
- propertyType = data->metaTypeId;
- vmePropertyType = QMetaType::QObjectStar;
- } else {
- propertyType = data->listMetaTypeId;
- vmePropertyType = qMetaTypeId<QQmlListProperty<QObject> >();
- }
-
- tdata->release();
- } else {
- if (p->type == QV4::CompiledData::Property::Custom) {
- propertyType = qmltype->typeId();
- vmePropertyType = QMetaType::QObjectStar;
- } else {
- propertyType = qmltype->qListTypeId();
- vmePropertyType = qMetaTypeId<QQmlListProperty<QObject> >();
- }
- }
-
- if (p->type == QV4::CompiledData::Property::Custom)
- propertyFlags |= QQmlPropertyData::IsQObjectDerived;
- else
- propertyFlags |= QQmlPropertyData::IsQList;
- }
-
- if (!(p->flags & QV4::CompiledData::Property::IsReadOnly) && p->type != QV4::CompiledData::Property::CustomList)
- propertyFlags |= QQmlPropertyData::IsWritable;
-
-
- QString propertyName = stringAt(p->nameIndex);
- if (propertyIdx == obj->indexOfDefaultProperty) cache->_defaultPropertyName = propertyName;
- cache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++,
- propertyType, effectiveSignalIndex);
-
- effectiveSignalIndex++;
-
- VMD *vmd = (QQmlVMEMetaData *)dynamicData.data();
- (vmd->propertyData() + vmd->propertyCount)->propertyType = vmePropertyType;
- vmd->propertyCount++;
- }
-
- // Alias property count. Actual data is setup in buildDynamicMetaAliases
- ((QQmlVMEMetaData *)dynamicData.data())->aliasCount = aliasCount;
-
- // Dynamic slot data - comes after the property data
- for (const QmlIR::Function *s = obj->firstFunction(); s; s = s->next) {
- QQmlJS::AST::FunctionDeclaration *astFunction = s->functionDeclaration;
- int formalsCount = 0;
- QQmlJS::AST::FormalParameterList *param = astFunction->formals;
- while (param) {
- formalsCount++;
- param = param->next;
- }
-
- VMD::MethodData methodData = { /* runtimeFunctionIndex*/ 0, // ###
- formalsCount,
- /* s->location.start.line */0 }; // ###
+}
- VMD *vmd = (QQmlVMEMetaData *)dynamicData.data();
- VMD::MethodData &md = *(vmd->methodData() + vmd->methodCount);
- vmd->methodCount++;
- md = methodData;
- }
- return true;
-}
SignalHandlerConverter::SignalHandlerConverter(QQmlTypeCompiler *typeCompiler)
: QQmlCompilePass(typeCompiler)
@@ -907,7 +308,7 @@ SignalHandlerConverter::SignalHandlerConverter(QQmlTypeCompiler *typeCompiler)
, qmlObjects(*typeCompiler->qmlObjects())
, imports(typeCompiler->imports())
, customParsers(typeCompiler->customParserCache())
- , resolvedTypes(*typeCompiler->resolvedTypes())
+ , resolvedTypes(typeCompiler->resolvedTypes)
, illegalNames(QV8Engine::get(QQmlEnginePrivate::get(typeCompiler->enginePrivate()))->illegalNames())
, propertyCaches(typeCompiler->propertyCaches())
{
@@ -917,7 +318,7 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio
{
for (int objectIndex = 0; objectIndex < qmlObjects.count(); ++objectIndex) {
const QmlIR::Object * const obj = qmlObjects.at(objectIndex);
- QQmlPropertyCache *cache = propertyCaches.at(objectIndex);
+ QQmlPropertyCache *cache = propertyCaches->at(objectIndex);
if (!cache)
continue;
if (QQmlCustomParser *customParser = customParsers.value(obj->inheritedTypeNameIndex)) {
@@ -941,7 +342,7 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio
// Attached property?
if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) {
const QmlIR::Object *attachedObj = qmlObjects.at(binding->value.objectIndex);
- QQmlCompiledData::TypeReference *typeRef = resolvedTypes.value(binding->propertyNameIndex);
+ auto *typeRef = resolvedTypes.value(binding->propertyNameIndex);
QQmlType *type = typeRef ? typeRef->type : 0;
if (!type) {
if (imports->resolveType(propertyName, &type, 0, 0, 0)) {
@@ -950,8 +351,8 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio
Q_ASSERT(tdata);
Q_ASSERT(tdata->isComplete());
- QQmlCompiledData *data = tdata->compiledData();
- type = QQmlMetaType::qmlType(data->metaTypeId);
+ auto compilationUnit = tdata->compilationUnit();
+ type = QQmlMetaType::qmlType(compilationUnit->metaTypeId);
tdata->release();
}
@@ -989,7 +390,7 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio
bool notInRevision = false;
QQmlPropertyData *signal = resolver.signal(propertyName, &notInRevision);
if (signal) {
- int sigIndex = propertyCache->methodIndexToSignalIndex(signal->coreIndex);
+ int sigIndex = propertyCache->methodIndexToSignalIndex(signal->coreIndex());
sigIndex = propertyCache->originalClone(sigIndex);
bool unnamedParameter = false;
@@ -1014,7 +415,7 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio
const QString &originalPropertyName = stringAt(binding->propertyNameIndex);
- QQmlCompiledData::TypeReference *typeRef = resolvedTypes.value(obj->inheritedTypeNameIndex);
+ auto *typeRef = resolvedTypes.value(obj->inheritedTypeNameIndex);
const QQmlType *type = typeRef ? typeRef->type : 0;
if (type) {
COMPILE_EXCEPTION(binding, tr("\"%1.%2\" is not available in %3 %4.%5.").arg(typeName).arg(originalPropertyName).arg(type->module()).arg(type->majorVersion()).arg(type->minorVersion()));
@@ -1118,14 +519,14 @@ QQmlEnumTypeResolver::QQmlEnumTypeResolver(QQmlTypeCompiler *typeCompiler)
, qmlObjects(*typeCompiler->qmlObjects())
, propertyCaches(typeCompiler->propertyCaches())
, imports(typeCompiler->imports())
- , resolvedTypes(typeCompiler->resolvedTypes())
+ , resolvedTypes(&typeCompiler->resolvedTypes)
{
}
bool QQmlEnumTypeResolver::resolveEnumBindings()
{
for (int i = 0; i < qmlObjects.count(); ++i) {
- QQmlPropertyCache *propertyCache = propertyCaches.at(i);
+ QQmlPropertyCache *propertyCache = propertyCaches->at(i);
if (!propertyCache)
continue;
const QmlIR::Object *obj = qmlObjects.at(i);
@@ -1146,7 +547,7 @@ bool QQmlEnumTypeResolver::resolveEnumBindings()
if (!pd)
continue;
- if (!pd->isEnum() && pd->propType != QMetaType::Int)
+ if (!pd->isEnum() && pd->propType() != QMetaType::Int)
continue;
if (!tryQualifiedEnumAssignment(obj, propertyCache, pd, binding))
@@ -1169,14 +570,14 @@ bool QQmlEnumTypeResolver::assignEnumToBinding(QmlIR::Binding *binding, const QS
COMPILE_EXCEPTION(binding, tr("Invalid property assignment: Enum value \"%1\" cannot start with a lowercase letter").arg(enumName.toString()));
}
binding->type = QV4::CompiledData::Binding::Type_Number;
- binding->value.d = (double)enumValue;
+ binding->setNumberValueInternal((double)enumValue);
binding->flags |= QV4::CompiledData::Binding::IsResolvedEnum;
return true;
}
bool QQmlEnumTypeResolver::tryQualifiedEnumAssignment(const QmlIR::Object *obj, const QQmlPropertyCache *propertyCache, const QQmlPropertyData *prop, QmlIR::Binding *binding)
{
- bool isIntProp = (prop->propType == QMetaType::Int) && !prop->isEnum();
+ bool isIntProp = (prop->propType() == QMetaType::Int) && !prop->isEnum();
if (!prop->isEnum() && !isIntProp)
return true;
@@ -1218,9 +619,9 @@ bool QQmlEnumTypeResolver::tryQualifiedEnumAssignment(const QmlIR::Object *obj,
int value = 0;
bool ok = false;
- QQmlCompiledData::TypeReference *tr = resolvedTypes->value(obj->inheritedTypeNameIndex);
+ auto *tr = resolvedTypes->value(obj->inheritedTypeNameIndex);
if (type && tr && tr->type == type) {
- QMetaProperty mprop = propertyCache->firstCppMetaObject()->property(prop->coreIndex);
+ QMetaProperty mprop = propertyCache->firstCppMetaObject()->property(prop->coreIndex());
// When these two match, we can short cut the search
if (mprop.isFlagType()) {
@@ -1311,20 +712,20 @@ QQmlAliasAnnotator::QQmlAliasAnnotator(QQmlTypeCompiler *typeCompiler)
void QQmlAliasAnnotator::annotateBindingsToAliases()
{
for (int i = 0; i < qmlObjects.count(); ++i) {
- QQmlPropertyCache *propertyCache = propertyCaches.at(i);
+ QQmlPropertyCache *propertyCache = propertyCaches->at(i);
if (!propertyCache)
continue;
const QmlIR::Object *obj = qmlObjects.at(i);
QmlIR::PropertyResolver resolver(propertyCache);
- QQmlPropertyData *defaultProperty = obj->indexOfDefaultProperty != -1 ? propertyCache->parent()->defaultProperty() : propertyCache->defaultProperty();
+ QQmlPropertyData *defaultProperty = obj->indexOfDefaultPropertyOrAlias != -1 ? propertyCache->parent()->defaultProperty() : propertyCache->defaultProperty();
for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) {
if (!binding->isValueBinding())
continue;
bool notInRevision = false;
- QQmlPropertyData *pd = binding->propertyNameIndex != 0 ? resolver.property(stringAt(binding->propertyNameIndex), &notInRevision) : defaultProperty;
+ QQmlPropertyData *pd = binding->propertyNameIndex != quint32(0) ? resolver.property(stringAt(binding->propertyNameIndex), &notInRevision) : defaultProperty;
if (pd && pd->isAlias())
binding->flags |= QV4::CompiledData::Binding::IsBindingToAlias;
}
@@ -1343,21 +744,21 @@ void QQmlScriptStringScanner::scan()
{
const int scriptStringMetaType = qMetaTypeId<QQmlScriptString>();
for (int i = 0; i < qmlObjects.count(); ++i) {
- QQmlPropertyCache *propertyCache = propertyCaches.at(i);
+ QQmlPropertyCache *propertyCache = propertyCaches->at(i);
if (!propertyCache)
continue;
const QmlIR::Object *obj = qmlObjects.at(i);
QmlIR::PropertyResolver resolver(propertyCache);
- QQmlPropertyData *defaultProperty = obj->indexOfDefaultProperty != -1 ? propertyCache->parent()->defaultProperty() : propertyCache->defaultProperty();
+ QQmlPropertyData *defaultProperty = obj->indexOfDefaultPropertyOrAlias != -1 ? propertyCache->parent()->defaultProperty() : propertyCache->defaultProperty();
for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) {
if (binding->type != QV4::CompiledData::Binding::Type_Script)
continue;
bool notInRevision = false;
- QQmlPropertyData *pd = binding->propertyNameIndex != 0 ? resolver.property(stringAt(binding->propertyNameIndex), &notInRevision) : defaultProperty;
- if (!pd || pd->propType != scriptStringMetaType)
+ QQmlPropertyData *pd = binding->propertyNameIndex != quint32(0) ? resolver.property(stringAt(binding->propertyNameIndex), &notInRevision) : defaultProperty;
+ if (!pd || pd->propType() != scriptStringMetaType)
continue;
QmlIR::CompiledFunctionOrExpression *foe = obj->functionsAndExpressions->slowAt(binding->value.compiledScriptIndex);
@@ -1376,13 +777,8 @@ QQmlComponentAndAliasResolver::QQmlComponentAndAliasResolver(QQmlTypeCompiler *t
, pool(typeCompiler->memoryPool())
, qmlObjects(typeCompiler->qmlObjects())
, indexOfRootObject(typeCompiler->rootObjectIndex())
- , _componentIndex(-1)
- , _objectIndexToIdInScope(0)
- , resolvedTypes(typeCompiler->resolvedTypes())
- , propertyCaches(typeCompiler->propertyCaches())
- , vmeMetaObjectData(typeCompiler->vmeMetaObjects())
- , objectIndexToIdForRoot(typeCompiler->objectIndexToIdForRoot())
- , objectIndexToIdPerComponent(typeCompiler->objectIndexToIdPerComponent())
+ , resolvedTypes(&typeCompiler->resolvedTypes)
+ , propertyCaches(std::move(typeCompiler->takePropertyCaches()))
{
}
@@ -1390,7 +786,7 @@ void QQmlComponentAndAliasResolver::findAndRegisterImplicitComponents(const QmlI
{
QmlIR::PropertyResolver propertyResolver(propertyCache);
- QQmlPropertyData *defaultProperty = obj->indexOfDefaultProperty != -1 ? propertyCache->parent()->defaultProperty() : propertyCache->defaultProperty();
+ QQmlPropertyData *defaultProperty = obj->indexOfDefaultPropertyOrAlias != -1 ? propertyCache->parent()->defaultProperty() : propertyCache->defaultProperty();
for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) {
if (binding->type != QV4::CompiledData::Binding::Type_Object)
@@ -1399,18 +795,18 @@ void QQmlComponentAndAliasResolver::findAndRegisterImplicitComponents(const QmlI
continue;
const QmlIR::Object *targetObject = qmlObjects->at(binding->value.objectIndex);
- QQmlCompiledData::TypeReference *tr = resolvedTypes->value(targetObject->inheritedTypeNameIndex);
+ auto *tr = resolvedTypes->value(targetObject->inheritedTypeNameIndex);
Q_ASSERT(tr);
if (QQmlType *targetType = tr->type) {
if (targetType->metaObject() == &QQmlComponent::staticMetaObject)
continue;
- } else if (tr->component) {
- if (tr->component->rootPropertyCache->firstCppMetaObject() == &QQmlComponent::staticMetaObject)
+ } else if (tr->compilationUnit) {
+ if (tr->compilationUnit->rootPropertyCache()->firstCppMetaObject() == &QQmlComponent::staticMetaObject)
continue;
}
QQmlPropertyData *pd = 0;
- if (binding->propertyNameIndex != 0) {
+ if (binding->propertyNameIndex != quint32(0)) {
bool notInRevision = false;
pd = propertyResolver.property(stringAt(binding->propertyNameIndex), &notInRevision);
} else {
@@ -1419,7 +815,7 @@ void QQmlComponentAndAliasResolver::findAndRegisterImplicitComponents(const QmlI
if (!pd || !pd->isQObject())
continue;
- QQmlPropertyCache *pc = enginePrivate->rawPropertyCacheForType(pd->propType);
+ QQmlPropertyCache *pc = enginePrivate->rawPropertyCacheForType(pd->propType());
const QMetaObject *mo = pc ? pc->firstCppMetaObject() : 0;
while (mo) {
if (mo == &QQmlComponent::staticMetaObject)
@@ -1430,15 +826,20 @@ void QQmlComponentAndAliasResolver::findAndRegisterImplicitComponents(const QmlI
if (!mo)
continue;
+ // emulate "import Qml 2.0 as QmlInternals" and then wrap the component in "QmlInternals.Component {}"
QQmlType *componentType = QQmlMetaType::qmlType(&QQmlComponent::staticMetaObject);
Q_ASSERT(componentType);
+ const QString qualifier = QStringLiteral("QmlInternals");
+
+ compiler->addImport(componentType->module(), qualifier, componentType->majorVersion(), componentType->minorVersion());
QmlIR::Object *syntheticComponent = pool->New<QmlIR::Object>();
- syntheticComponent->init(pool, compiler->registerString(QString::fromUtf8(componentType->typeName())), compiler->registerString(QString()));
+ syntheticComponent->init(pool, compiler->registerString(qualifier + QLatin1Char('.') + componentType->elementName()), compiler->registerString(QString()));
syntheticComponent->location = binding->valueLocation;
+ syntheticComponent->flags |= QV4::CompiledData::Object::IsComponent;
if (!resolvedTypes->contains(syntheticComponent->inheritedTypeNameIndex)) {
- QQmlCompiledData::TypeReference *typeRef = new QQmlCompiledData::TypeReference;
+ auto typeRef = new QV4::CompiledData::ResolvedTypeReference;
typeRef->type = componentType;
typeRef->majorVersion = componentType->majorVersion();
typeRef->minorVersion = componentType->minorVersion();
@@ -1449,7 +850,6 @@ void QQmlComponentAndAliasResolver::findAndRegisterImplicitComponents(const QmlI
const int componentIndex = qmlObjects->count() - 1;
// Keep property caches symmetric
QQmlPropertyCache *componentCache = enginePrivate->cache(&QQmlComponent::staticMetaObject);
- componentCache->addref();
propertyCaches.append(componentCache);
QmlIR::Binding *syntheticBinding = pool->New<QmlIR::Binding>();
@@ -1462,7 +862,6 @@ void QQmlComponentAndAliasResolver::findAndRegisterImplicitComponents(const QmlI
binding->value.objectIndex = componentIndex;
componentRoots.append(componentIndex);
- componentBoundaries.append(syntheticBinding->value.objectIndex);
}
}
@@ -1474,7 +873,7 @@ bool QQmlComponentAndAliasResolver::resolve()
// on the left hand side is of QQmlComponent type.
const int objCountWithoutSynthesizedComponents = qmlObjects->count();
for (int i = 0; i < objCountWithoutSynthesizedComponents; ++i) {
- const QmlIR::Object *obj = qmlObjects->at(i);
+ QmlIR::Object *obj = qmlObjects->at(i);
QQmlPropertyCache *cache = propertyCaches.at(i);
if (obj->inheritedTypeNameIndex == 0 && !cache)
continue;
@@ -1482,7 +881,7 @@ bool QQmlComponentAndAliasResolver::resolve()
bool isExplicitComponent = false;
if (obj->inheritedTypeNameIndex) {
- QQmlCompiledData::TypeReference *tref = resolvedTypes->value(obj->inheritedTypeNameIndex);
+ auto *tref = resolvedTypes->value(obj->inheritedTypeNameIndex);
Q_ASSERT(tref);
if (tref->type && tref->type->metaObject() == &QQmlComponent::staticMetaObject)
isExplicitComponent = true;
@@ -1493,11 +892,11 @@ bool QQmlComponentAndAliasResolver::resolve()
continue;
}
- componentRoots.append(i);
+ obj->flags |= QV4::CompiledData::Object::IsComponent;
if (obj->functionCount() > 0)
COMPILE_EXCEPTION(obj, tr("Component objects cannot declare new functions."));
- if (obj->propertyCount() > 0)
+ if (obj->propertyCount() > 0 || obj->aliasCount() > 0)
COMPILE_EXCEPTION(obj, tr("Component objects cannot declare new properties."));
if (obj->signalCount() > 0)
COMPILE_EXCEPTION(obj, tr("Component objects cannot declare new signals."));
@@ -1515,65 +914,69 @@ bool QQmlComponentAndAliasResolver::resolve()
if (rootBinding->next || rootBinding->type != QV4::CompiledData::Binding::Type_Object)
COMPILE_EXCEPTION(obj, tr("Invalid component body specification"));
- componentBoundaries.append(rootBinding->value.objectIndex);
- }
+ // We are going to collect ids/aliases and resolve them for the root object as a separate
+ // last pass.
+ if (i != indexOfRootObject)
+ componentRoots.append(i);
- std::sort(componentBoundaries.begin(), componentBoundaries.end());
+ }
for (int i = 0; i < componentRoots.count(); ++i) {
- const QmlIR::Object *component = qmlObjects->at(componentRoots.at(i));
+ QmlIR::Object *component = qmlObjects->at(componentRoots.at(i));
const QmlIR::Binding *rootBinding = component->firstBinding();
- _componentIndex = i;
_idToObjectIndex.clear();
- _objectIndexToIdInScope = &(*objectIndexToIdPerComponent)[componentRoots.at(i)];
-
_objectsWithAliases.clear();
if (!collectIdsAndAliases(rootBinding->value.objectIndex))
return false;
- if (!resolveAliases())
+ component->namedObjectsInComponent.allocate(pool, _idToObjectIndex);
+
+ if (!resolveAliases(componentRoots.at(i)))
return false;
}
// Collect ids and aliases for root
- _componentIndex = -1;
_idToObjectIndex.clear();
- _objectIndexToIdInScope = objectIndexToIdForRoot;
_objectsWithAliases.clear();
collectIdsAndAliases(indexOfRootObject);
- resolveAliases();
+ QmlIR::Object *rootComponent = qmlObjects->at(indexOfRootObject);
+ rootComponent->namedObjectsInComponent.allocate(pool, _idToObjectIndex);
+
+ if (!resolveAliases(indexOfRootObject))
+ return false;
// Implicit component insertion may have added objects and thus we also need
// to extend the symmetric propertyCaches.
- compiler->setPropertyCaches(propertyCaches);
+ compiler->setPropertyCaches(std::move(propertyCaches));
+ compiler->setComponentRoots(componentRoots);
return true;
}
bool QQmlComponentAndAliasResolver::collectIdsAndAliases(int objectIndex)
{
- const QmlIR::Object *obj = qmlObjects->at(objectIndex);
+ QmlIR::Object *obj = qmlObjects->at(objectIndex);
- if (obj->idIndex != 0) {
- if (_idToObjectIndex.contains(obj->idIndex)) {
+ if (obj->idNameIndex != 0) {
+ if (_idToObjectIndex.contains(obj->idNameIndex)) {
recordError(obj->locationOfIdProperty, tr("id is not unique"));
return false;
}
- _idToObjectIndex.insert(obj->idIndex, objectIndex);
- _objectIndexToIdInScope->insert(objectIndex, _objectIndexToIdInScope->count());
+ obj->id = _idToObjectIndex.count();
+ _idToObjectIndex.insert(obj->idNameIndex, objectIndex);
}
- for (const QmlIR::Property *property = obj->firstProperty(); property; property = property->next) {
- if (property->type == QV4::CompiledData::Property::Alias) {
- _objectsWithAliases.append(objectIndex);
- break;
- }
- }
+ if (obj->aliasCount() > 0)
+ _objectsWithAliases.append(objectIndex);
+
+ // Stop at Component boundary
+ if (obj->flags & QV4::CompiledData::Object::IsComponent && objectIndex != compiler->rootObjectIndex())
+ return true;
for (const QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) {
if (binding->type != QV4::CompiledData::Binding::Type_Object
@@ -1581,10 +984,6 @@ bool QQmlComponentAndAliasResolver::collectIdsAndAliases(int objectIndex)
&& binding->type != QV4::CompiledData::Binding::Type_GroupProperty)
continue;
- // Stop at Component boundary
- if (std::binary_search(componentBoundaries.constBegin(), componentBoundaries.constEnd(), binding->value.objectIndex))
- continue;
-
if (!collectIdsAndAliases(binding->value.objectIndex))
return false;
}
@@ -1592,257 +991,205 @@ bool QQmlComponentAndAliasResolver::collectIdsAndAliases(int objectIndex)
return true;
}
-bool QQmlComponentAndAliasResolver::resolveAliases()
+bool QQmlComponentAndAliasResolver::resolveAliases(int componentIndex)
{
- foreach (int objectIndex, _objectsWithAliases) {
- const QmlIR::Object *obj = qmlObjects->at(objectIndex);
+ if (_objectsWithAliases.isEmpty())
+ return true;
- QQmlPropertyCache *propertyCache = propertyCaches.at(objectIndex);
- Q_ASSERT(propertyCache);
+ QQmlPropertyCacheAliasCreator<QQmlTypeCompiler> aliasCacheCreator(&propertyCaches, compiler);
- int effectiveSignalIndex = propertyCache->signalHandlerIndexCacheStart + propertyCache->propertyIndexCache.count();
- int effectivePropertyIndex = propertyCache->propertyIndexCacheStart + propertyCache->propertyIndexCache.count();
- int effectiveAliasIndex = 0;
+ bool atLeastOneAliasResolved;
+ do {
+ atLeastOneAliasResolved = false;
+ QVector<int> pendingObjects;
- const QmlIR::Property *p = obj->firstProperty();
- for (int propertyIndex = 0; propertyIndex < obj->propertyCount(); ++propertyIndex, p = p->next) {
- if (p->type != QV4::CompiledData::Property::Alias)
- continue;
+ for (int objectIndex: qAsConst(_objectsWithAliases)) {
- const int idIndex = p->aliasIdValueIndex;
- const int targetObjectIndex = _idToObjectIndex.value(idIndex, -1);
- if (targetObjectIndex == -1) {
- recordError(p->aliasLocation, tr("Invalid alias reference. Unable to find id \"%1\"").arg(stringAt(idIndex)));
+ QQmlCompileError error;
+ const auto result = resolveAliasesInObject(objectIndex, &error);
+
+ if (error.isSet()) {
+ recordError(error);
return false;
}
- const int targetId = _objectIndexToIdInScope->value(targetObjectIndex, -1);
- Q_ASSERT(targetId != -1);
-
- const QString aliasPropertyValue = stringAt(p->aliasPropertyValueIndex);
-
- QStringRef property;
- QStringRef subProperty;
-
- const int propertySeparator = aliasPropertyValue.indexOf(QLatin1Char('.'));
- if (propertySeparator != -1) {
- property = aliasPropertyValue.leftRef(propertySeparator);
- subProperty = aliasPropertyValue.midRef(propertySeparator + 1);
- } else
- property = QStringRef(&aliasPropertyValue, 0, aliasPropertyValue.length());
-
- int propIdx = -1;
- int propType = 0;
- int notifySignal = -1;
- int flags = 0;
- int type = 0;
- bool writable = false;
- bool resettable = false;
-
- quint32 propertyFlags = QQmlPropertyData::IsAlias;
-
- if (property.isEmpty()) {
- const QmlIR::Object *targetObject = qmlObjects->at(targetObjectIndex);
- QQmlCompiledData::TypeReference *typeRef = resolvedTypes->value(targetObject->inheritedTypeNameIndex);
- Q_ASSERT(typeRef);
-
- if (typeRef->type)
- type = typeRef->type->typeId();
- else
- type = typeRef->component->metaTypeId;
-
- flags |= QML_ALIAS_FLAG_PTR;
- propertyFlags |= QQmlPropertyData::IsQObjectDerived;
+
+ if (result == AllAliasesResolved) {
+ aliasCacheCreator.appendAliasesToPropertyCache(*qmlObjects->at(componentIndex), objectIndex);
+ atLeastOneAliasResolved = true;
+ } else if (result == SomeAliasesResolved) {
+ atLeastOneAliasResolved = true;
+ pendingObjects.append(objectIndex);
} else {
- QQmlPropertyCache *targetCache = propertyCaches.at(targetObjectIndex);
- Q_ASSERT(targetCache);
- QmlIR::PropertyResolver resolver(targetCache);
+ pendingObjects.append(objectIndex);
+ }
+ }
+ qSwap(_objectsWithAliases, pendingObjects);
+ } while (!_objectsWithAliases.isEmpty() && atLeastOneAliasResolved);
- QQmlPropertyData *targetProperty = resolver.property(property.toString());
- if (!targetProperty || targetProperty->coreIndex > 0x0000FFFF) {
- recordError(p->aliasLocation, tr("Invalid alias target location: %1").arg(property.toString()));
- return false;
- }
+ if (!atLeastOneAliasResolved && !_objectsWithAliases.isEmpty()) {
+ const QmlIR::Object *obj = qmlObjects->at(_objectsWithAliases.first());
+ for (auto alias = obj->aliasesBegin(), end = obj->aliasesEnd(); alias != end; ++alias) {
+ if (!(alias->flags & QV4::CompiledData::Alias::Resolved)) {
+ recordError(alias->location, tr("Circular alias reference detected"));
+ return false;
+ }
+ }
+ }
- propIdx = targetProperty->coreIndex;
- type = targetProperty->propType;
+ return true;
+}
- writable = targetProperty->isWritable();
- resettable = targetProperty->isResettable();
- notifySignal = targetProperty->notifyIndex;
+QQmlComponentAndAliasResolver::AliasResolutionResult QQmlComponentAndAliasResolver::resolveAliasesInObject(int objectIndex, QQmlCompileError *error)
+{
+ const QmlIR::Object * const obj = qmlObjects->at(objectIndex);
+ if (!obj->aliasCount())
+ return AllAliasesResolved;
- if (!subProperty.isEmpty()) {
- const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(type);
- if (!valueTypeMetaObject) {
- recordError(p->aliasLocation, tr("Invalid alias target location: %1").arg(subProperty.toString()));
- return false;
- }
+ int numResolvedAliases = 0;
+ bool seenUnresolvedAlias = false;
- propType = type;
+ for (QmlIR::Alias *alias = obj->firstAlias(); alias; alias = alias->next) {
+ if (alias->flags & QV4::CompiledData::Alias::Resolved)
+ continue;
- int valueTypeIndex =
- valueTypeMetaObject->indexOfProperty(subProperty.toString().toUtf8().constData());
- if (valueTypeIndex == -1) {
- recordError(p->aliasLocation, tr("Invalid alias target location: %1").arg(subProperty.toString()));
- return false;
- }
- Q_ASSERT(valueTypeIndex <= 0x0000FFFF);
+ seenUnresolvedAlias = true;
- propIdx = QQmlPropertyData::encodeValueTypePropertyIndex(propIdx, valueTypeIndex);
- if (valueTypeMetaObject->property(valueTypeIndex).isEnumType())
- type = QVariant::Int;
- else
- type = valueTypeMetaObject->property(valueTypeIndex).userType();
+ const int idIndex = alias->idIndex;
+ const int targetObjectIndex = _idToObjectIndex.value(idIndex, -1);
+ if (targetObjectIndex == -1) {
+ *error = QQmlCompileError(alias->referenceLocation, tr("Invalid alias reference. Unable to find id \"%1\"").arg(stringAt(idIndex)));
+ break;
+ }
- } else {
- if (targetProperty->isEnum()) {
- type = QVariant::Int;
- } else {
- // Copy type flags
- propertyFlags |= targetProperty->getFlags() & QQmlPropertyData::PropTypeFlagMask;
+ const QmlIR::Object *targetObject = qmlObjects->at(targetObjectIndex);
+ Q_ASSERT(targetObject->id >= 0);
+ alias->targetObjectId = targetObject->id;
+ alias->aliasToLocalAlias = false;
- if (targetProperty->isVarProperty())
- propertyFlags |= QQmlPropertyData::IsQVariant;
+ const QString aliasPropertyValue = stringAt(alias->propertyNameIndex);
- if (targetProperty->isQObject())
- flags |= QML_ALIAS_FLAG_PTR;
+ QStringRef property;
+ QStringRef subProperty;
+
+ const int propertySeparator = aliasPropertyValue.indexOf(QLatin1Char('.'));
+ if (propertySeparator != -1) {
+ property = aliasPropertyValue.leftRef(propertySeparator);
+ subProperty = aliasPropertyValue.midRef(propertySeparator + 1);
+ } else
+ property = QStringRef(&aliasPropertyValue, 0, aliasPropertyValue.length());
+
+ QQmlPropertyIndex propIdx;
+
+ if (property.isEmpty()) {
+ alias->flags |= QV4::CompiledData::Alias::AliasPointsToPointerObject;
+ } else {
+ QQmlPropertyCache *targetCache = propertyCaches.at(targetObjectIndex);
+ Q_ASSERT(targetCache);
+ QmlIR::PropertyResolver resolver(targetCache);
+
+ QQmlPropertyData *targetProperty = resolver.property(property.toString());
+
+ // If it's an alias that we haven't resolved yet, try again later.
+ if (!targetProperty) {
+ bool aliasPointsToOtherAlias = false;
+ int localAliasIndex = 0;
+ for (auto targetAlias = targetObject->aliasesBegin(), end = targetObject->aliasesEnd(); targetAlias != end; ++targetAlias, ++localAliasIndex) {
+ if (stringAt(targetAlias->nameIndex) == property) {
+ aliasPointsToOtherAlias = true;
+ break;
}
}
- }
-
- QQmlVMEMetaData::AliasData aliasData = { targetId, propIdx, propType, flags, notifySignal };
+ if (aliasPointsToOtherAlias) {
+ if (targetObjectIndex == objectIndex) {
+ alias->localAliasIndex = localAliasIndex;
+ alias->aliasToLocalAlias = true;
+ alias->flags |= QV4::CompiledData::Alias::Resolved;
+ ++numResolvedAliases;
+ continue;
+ }
- typedef QQmlVMEMetaData VMD;
- QByteArray &dynamicData = (*vmeMetaObjectData)[objectIndex];
- Q_ASSERT(!dynamicData.isEmpty());
- VMD *vmd = (QQmlVMEMetaData *)dynamicData.data();
- *(vmd->aliasData() + effectiveAliasIndex++) = aliasData;
+ // Try again later and resolve the target alias first.
+ _objectsWithAliases.append(objectIndex);
+ // restore
+ alias->idIndex = idIndex;
+ break;
+ }
+ }
- Q_ASSERT(dynamicData.isDetached());
+ if (!targetProperty || targetProperty->coreIndex() > 0x0000FFFF) {
+ *error = QQmlCompileError(alias->referenceLocation, tr("Invalid alias target location: %1").arg(property.toString()));
+ break;
+ }
- if (!(p->flags & QV4::CompiledData::Property::IsReadOnly) && writable)
- propertyFlags |= QQmlPropertyData::IsWritable;
- else
- propertyFlags &= ~QQmlPropertyData::IsWritable;
+ propIdx = QQmlPropertyIndex(targetProperty->coreIndex());
- if (resettable)
- propertyFlags |= QQmlPropertyData::IsResettable;
- else
- propertyFlags &= ~QQmlPropertyData::IsResettable;
+ if (!subProperty.isEmpty()) {
+ const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(targetProperty->propType());
+ if (!valueTypeMetaObject) {
+ *error = QQmlCompileError(alias->referenceLocation, tr("Invalid alias target location: %1").arg(subProperty.toString()));
+ break;
+ }
- QString propertyName = stringAt(p->nameIndex);
- if (propertyIndex == obj->indexOfDefaultProperty) propertyCache->_defaultPropertyName = propertyName;
- propertyCache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++,
- type, effectiveSignalIndex++);
+ int valueTypeIndex =
+ valueTypeMetaObject->indexOfProperty(subProperty.toString().toUtf8().constData());
+ if (valueTypeIndex == -1) {
+ *error = QQmlCompileError(alias->referenceLocation, tr("Invalid alias target location: %1").arg(subProperty.toString()));
+ break;
+ }
+ Q_ASSERT(valueTypeIndex <= 0x0000FFFF);
+ propIdx = QQmlPropertyIndex(propIdx.coreIndex(), valueTypeIndex);
+ } else {
+ if (targetProperty->isQObject())
+ alias->flags |= QV4::CompiledData::Alias::AliasPointsToPointerObject;
+ }
}
+
+ alias->encodedMetaPropertyIndex = propIdx.toEncoded();
+ alias->flags |= QV4::CompiledData::Alias::Resolved;
+ numResolvedAliases++;
}
- return true;
+
+ if (numResolvedAliases == 0)
+ return seenUnresolvedAlias ? NoAliasResolved : AllAliasesResolved;
+
+ return SomeAliasesResolved;
}
-QQmlPropertyValidator::QQmlPropertyValidator(QQmlTypeCompiler *typeCompiler)
+QQmlDeferredAndCustomParserBindingScanner::QQmlDeferredAndCustomParserBindingScanner(QQmlTypeCompiler *typeCompiler)
: QQmlCompilePass(typeCompiler)
- , enginePrivate(typeCompiler->enginePrivate())
- , qmlUnit(typeCompiler->qmlUnit())
- , resolvedTypes(*typeCompiler->resolvedTypes())
- , customParsers(typeCompiler->customParserCache())
+ , qmlObjects(typeCompiler->qmlObjects())
, propertyCaches(typeCompiler->propertyCaches())
- , objectIndexToIdPerComponent(*typeCompiler->objectIndexToIdPerComponent())
- , customParserBindingsPerObject(typeCompiler->customParserBindings())
+ , customParsers(typeCompiler->customParserCache())
, _seenObjectWithId(false)
{
}
-bool QQmlPropertyValidator::validate()
-{
- _bindingPropertyDataPerObject.resize(qmlUnit->nObjects);
- if (!validateObject(qmlUnit->indexOfRootObject, /*instantiatingBinding*/0))
- return false;
- compiler->setDeferredBindingsPerObject(_deferredBindingsPerObject);
- compiler->setBindingPropertyDataPerObject(_bindingPropertyDataPerObject);
- return true;
-}
-
-const QQmlImports &QQmlPropertyValidator::imports() const
+bool QQmlDeferredAndCustomParserBindingScanner::scanObject()
{
- return *compiler->imports();
+ return scanObject(compiler->rootObjectIndex());
}
-typedef QVarLengthArray<const QV4::CompiledData::Binding *, 8> GroupPropertyVector;
-
-struct BindingFinder
-{
- bool operator()(quint32 name, const QV4::CompiledData::Binding *binding) const
- {
- return name < binding->propertyNameIndex;
- }
- bool operator()(const QV4::CompiledData::Binding *binding, quint32 name) const
- {
- return binding->propertyNameIndex < name;
- }
- bool operator()(const QV4::CompiledData::Binding *lhs, const QV4::CompiledData::Binding *rhs) const
- {
- return lhs->propertyNameIndex < rhs->propertyNameIndex;
- }
-};
-
-bool QQmlPropertyValidator::validateObject(int objectIndex, const QV4::CompiledData::Binding *instantiatingBinding, bool populatingValueTypeGroupProperty) const
+bool QQmlDeferredAndCustomParserBindingScanner::scanObject(int objectIndex)
{
- const QV4::CompiledData::Object *obj = qmlUnit->objectAt(objectIndex);
- if (obj->idIndex != 0)
+ QmlIR::Object *obj = qmlObjects->at(objectIndex);
+ if (obj->idNameIndex != 0)
_seenObjectWithId = true;
- if (isComponent(objectIndex)) {
- Q_ASSERT(obj->nBindings == 1);
- const QV4::CompiledData::Binding *componentBinding = obj->bindingTable();
+ if (obj->flags & QV4::CompiledData::Object::IsComponent) {
+ Q_ASSERT(obj->bindingCount() == 1);
+ const QV4::CompiledData::Binding *componentBinding = obj->firstBinding();
Q_ASSERT(componentBinding->type == QV4::CompiledData::Binding::Type_Object);
- return validateObject(componentBinding->value.objectIndex, componentBinding);
+ return scanObject(componentBinding->value.objectIndex);
}
- QQmlPropertyCache *propertyCache = propertyCaches.at(objectIndex);
+ QQmlPropertyCache *propertyCache = propertyCaches->at(objectIndex);
if (!propertyCache)
return true;
- QStringList deferredPropertyNames;
- {
- const QMetaObject *mo = propertyCache->firstCppMetaObject();
- const int namesIndex = mo->indexOfClassInfo("DeferredPropertyNames");
- if (namesIndex != -1) {
- QMetaClassInfo classInfo = mo->classInfo(namesIndex);
- deferredPropertyNames = QString::fromUtf8(classInfo.value()).split(QLatin1Char(','));
- }
- }
-
- QQmlCustomParser *customParser = customParsers.value(obj->inheritedTypeNameIndex);
- QList<const QV4::CompiledData::Binding*> customBindings;
-
- // Collect group properties first for sanity checking
- // vector values are sorted by property name string index.
- GroupPropertyVector groupProperties;
- const QV4::CompiledData::Binding *binding = obj->bindingTable();
- for (quint32 i = 0; i < obj->nBindings; ++i, ++binding) {
- if (!binding->isGroupProperty())
- continue;
-
- if (binding->flags & QV4::CompiledData::Binding::IsOnAssignment)
- continue;
-
- if (populatingValueTypeGroupProperty) {
- recordError(binding->location, tr("Property assignment expected"));
- return false;
- }
-
- GroupPropertyVector::const_iterator pos = std::lower_bound(groupProperties.constBegin(), groupProperties.constEnd(), binding->propertyNameIndex, BindingFinder());
- groupProperties.insert(pos, binding);
- }
-
- QBitArray customParserBindings(obj->nBindings);
- QBitArray deferredBindings;
-
- QmlIR::PropertyResolver propertyResolver(propertyCache);
-
QString defaultPropertyName;
QQmlPropertyData *defaultProperty = 0;
- if (obj->indexOfDefaultProperty != -1) {
+ if (obj->indexOfDefaultPropertyOrAlias != -1) {
QQmlPropertyCache *cache = propertyCache->parent();
defaultPropertyName = cache->defaultPropertyName();
defaultProperty = cache->defaultProperty();
@@ -1851,76 +1198,55 @@ bool QQmlPropertyValidator::validateObject(int objectIndex, const QV4::CompiledD
defaultProperty = propertyCache->defaultProperty();
}
- QV4::CompiledData::BindingPropertyData collectedBindingPropertyData(obj->nBindings);
+ QQmlCustomParser *customParser = customParsers.value(obj->inheritedTypeNameIndex);
+
+ QmlIR::PropertyResolver propertyResolver(propertyCache);
+
+ QStringList deferredPropertyNames;
+ {
+ const QMetaObject *mo = propertyCache->firstCppMetaObject();
+ const int namesIndex = mo->indexOfClassInfo("DeferredPropertyNames");
+ if (namesIndex != -1) {
+ QMetaClassInfo classInfo = mo->classInfo(namesIndex);
+ deferredPropertyNames = QString::fromUtf8(classInfo.value()).split(QLatin1Char(','));
+ }
+ }
- binding = obj->bindingTable();
- for (quint32 i = 0; i < obj->nBindings; ++i, ++binding) {
+ for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) {
+ QQmlPropertyData *pd = 0;
QString name = stringAt(binding->propertyNameIndex);
if (customParser) {
if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) {
if (customParser->flags() & QQmlCustomParser::AcceptsAttachedProperties) {
- customBindings << binding;
- customParserBindings.setBit(i);
+ binding->flags |= QV4::CompiledData::Binding::IsCustomParserBinding;
+ obj->flags |= QV4::CompiledData::Object::HasCustomParserBindings;
continue;
}
} else if (QmlIR::IRBuilder::isSignalPropertyName(name)
&& !(customParser->flags() & QQmlCustomParser::AcceptsSignalHandlers)) {
- customBindings << binding;
- customParserBindings.setBit(i);
+ obj->flags |= QV4::CompiledData::Object::HasCustomParserBindings;
+ binding->flags |= QV4::CompiledData::Binding::IsCustomParserBinding;
continue;
}
}
- bool bindingToDefaultProperty = false;
- bool isGroupProperty = instantiatingBinding && instantiatingBinding->type == QV4::CompiledData::Binding::Type_GroupProperty;
-
- bool notInRevision = false;
- QQmlPropertyData *pd = 0;
- if (!name.isEmpty()) {
- if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression
- || binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject)
- pd = propertyResolver.signal(name, &notInRevision);
- else
- pd = propertyResolver.property(name, &notInRevision, isGroupProperty ? QmlIR::PropertyResolver::IgnoreRevision : QmlIR::PropertyResolver::CheckRevision);
-
- if (notInRevision) {
- QString typeName = stringAt(obj->inheritedTypeNameIndex);
- QQmlCompiledData::TypeReference *objectType = resolvedTypes.value(obj->inheritedTypeNameIndex);
- if (objectType && objectType->type) {
- COMPILE_EXCEPTION(binding, tr("\"%1.%2\" is not available in %3 %4.%5.").arg(typeName).arg(name).arg(objectType->type->module()).arg(objectType->majorVersion).arg(objectType->minorVersion));
- } else {
- COMPILE_EXCEPTION(binding, tr("\"%1.%2\" is not available due to component versioning.").arg(typeName).arg(name));
- }
- }
+ if (name.isEmpty()) {
+ pd = defaultProperty;
+ name = defaultPropertyName;
} else {
- if (isGroupProperty)
- COMPILE_EXCEPTION(binding, tr("Cannot assign a value directly to a grouped property"));
-
- pd = defaultProperty;
- name = defaultPropertyName;
- bindingToDefaultProperty = true;
- }
-
- if (pd)
- collectedBindingPropertyData[i] = pd;
+ if (name.constData()->isUpper())
+ continue;
- if (name.constData()->isUpper() && !binding->isAttachedProperty()) {
- QQmlType *type = 0;
- QQmlImportNamespace *typeNamespace = 0;
- compiler->imports()->resolveType(stringAt(binding->propertyNameIndex), &type, 0, 0, &typeNamespace);
- if (typeNamespace)
- recordError(binding->location, tr("Invalid use of namespace"));
- else
- recordError(binding->location, tr("Invalid attached object assignment"));
- return false;
+ bool notInRevision = false;
+ pd = propertyResolver.property(name, &notInRevision, QmlIR::PropertyResolver::CheckRevision);
}
bool seenSubObjectWithId = false;
if (binding->type >= QV4::CompiledData::Binding::Type_Object && (pd || binding->isAttachedProperty())) {
qSwap(_seenObjectWithId, seenSubObjectWithId);
- const bool subObjectValid = validateObject(binding->value.objectIndex, binding, pd && QQmlValueTypeFactory::metaObjectForMetaType(pd->propType));
+ const bool subObjectValid = scanObject(binding->value.objectIndex);
qSwap(_seenObjectWithId, seenSubObjectWithId);
if (!subObjectValid)
return false;
@@ -1930,538 +1256,28 @@ bool QQmlPropertyValidator::validateObject(int objectIndex, const QV4::CompiledD
if (!seenSubObjectWithId
&& !deferredPropertyNames.isEmpty() && deferredPropertyNames.contains(name)) {
- if (deferredBindings.isEmpty())
- deferredBindings.resize(obj->nBindings);
-
- deferredBindings.setBit(i);
+ binding->flags |= QV4::CompiledData::Binding::IsDeferredBinding;
+ obj->flags |= QV4::CompiledData::Object::HasDeferredBindings;
}
- // Signal handlers were resolved and checked earlier in the signal handler conversion pass.
if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression
|| binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject)
continue;
- if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) {
- if (instantiatingBinding && (instantiatingBinding->isAttachedProperty() || instantiatingBinding->isGroupProperty())) {
- recordError(binding->location, tr("Attached properties cannot be used here"));
- return false;
- }
- continue;
- }
-
- if (pd) {
- GroupPropertyVector::const_iterator assignedGroupProperty = std::lower_bound(groupProperties.constBegin(), groupProperties.constEnd(), binding->propertyNameIndex, BindingFinder());
- const bool assigningToGroupProperty = assignedGroupProperty != groupProperties.constEnd() && !(binding->propertyNameIndex < (*assignedGroupProperty)->propertyNameIndex);
-
- if (!pd->isWritable()
- && !pd->isQList()
- && !binding->isGroupProperty()
- && !(binding->flags & QV4::CompiledData::Binding::InitializerForReadOnlyDeclaration)
- ) {
-
- if (assigningToGroupProperty && binding->type < QV4::CompiledData::Binding::Type_Object)
- recordError(binding->valueLocation, tr("Cannot assign a value directly to a grouped property"));
- else
- recordError(binding->valueLocation, tr("Invalid property assignment: \"%1\" is a read-only property").arg(name));
- return false;
- }
-
- if (!pd->isQList() && (binding->flags & QV4::CompiledData::Binding::IsListItem)) {
- QString error;
- if (pd->propType == qMetaTypeId<QQmlScriptString>())
- error = tr( "Cannot assign multiple values to a script property");
- else
- error = tr( "Cannot assign multiple values to a singular property");
- recordError(binding->valueLocation, error);
- return false;
- }
-
- if (!bindingToDefaultProperty
- && !binding->isGroupProperty()
- && !(binding->flags & QV4::CompiledData::Binding::IsOnAssignment)
- && assigningToGroupProperty) {
- QV4::CompiledData::Location loc = binding->valueLocation;
- if (loc < (*assignedGroupProperty)->valueLocation)
- loc = (*assignedGroupProperty)->valueLocation;
-
- if (pd && QQmlValueTypeFactory::isValueType(pd->propType))
- recordError(loc, tr("Property has already been assigned a value"));
- else
- recordError(loc, tr("Cannot assign a value directly to a grouped property"));
- return false;
- }
-
- if (binding->type < QV4::CompiledData::Binding::Type_Script) {
- if (!validateLiteralBinding(propertyCache, pd, binding))
- return false;
- } else if (binding->type == QV4::CompiledData::Binding::Type_Object) {
- if (!validateObjectBinding(pd, name, binding))
- return false;
- } else if (binding->isGroupProperty()) {
- if (QQmlValueTypeFactory::isValueType(pd->propType)) {
- if (QQmlValueTypeFactory::metaObjectForMetaType(pd->propType)) {
- if (!pd->isWritable()) {
- recordError(binding->location, tr("Invalid property assignment: \"%1\" is a read-only property").arg(name));
- return false;
- }
- } else {
- recordError(binding->location, tr("Invalid grouped property access"));
- return false;
- }
- } else {
- if (!enginePrivate->propertyCacheForType(pd->propType)) {
- recordError(binding->location, tr("Invalid grouped property access"));
- return false;
- }
- }
- }
- } else {
+ if (!pd) {
if (customParser) {
- customBindings << binding;
- customParserBindings.setBit(i);
- continue;
- }
- if (bindingToDefaultProperty) {
- COMPILE_EXCEPTION(binding, tr("Cannot assign to non-existent default property"));
- } else {
- COMPILE_EXCEPTION(binding, tr("Cannot assign to non-existent property \"%1\"").arg(name));
- }
- }
- }
-
- if (obj->idIndex) {
- bool notInRevision = false;
- collectedBindingPropertyData << propertyResolver.property(QStringLiteral("id"), &notInRevision);
- }
-
- if (customParser && !customBindings.isEmpty()) {
- customParser->clearErrors();
- customParser->validator = this;
- customParser->engine = enginePrivate;
- customParser->imports = compiler->imports();
- customParser->verifyBindings(qmlUnit, customBindings);
- customParser->validator = 0;
- customParser->engine = 0;
- customParser->imports = (QQmlImports*)0;
- customParserBindingsPerObject->insert(objectIndex, customParserBindings);
- const QList<QQmlError> parserErrors = customParser->errors();
- if (!parserErrors.isEmpty()) {
- foreach (const QQmlError &error, parserErrors)
- compiler->recordError(error);
- return false;
- }
- }
-
- if (!deferredBindings.isEmpty())
- _deferredBindingsPerObject.insert(objectIndex, deferredBindings);
-
- _bindingPropertyDataPerObject[objectIndex] = collectedBindingPropertyData;
-
- return true;
-}
-
-bool QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache *propertyCache, QQmlPropertyData *property, const QV4::CompiledData::Binding *binding) const
-{
- if (property->isQList()) {
- recordError(binding->valueLocation, tr("Cannot assign primitives to lists"));
- return false;
- }
-
- if (property->isEnum()) {
- if (binding->flags & QV4::CompiledData::Binding::IsResolvedEnum)
- return true;
-
- QString value = binding->valueAsString(qmlUnit);
- QMetaProperty p = propertyCache->firstCppMetaObject()->property(property->coreIndex);
- bool ok;
- if (p.isFlagType()) {
- p.enumerator().keysToValue(value.toUtf8().constData(), &ok);
- } else
- p.enumerator().keyToValue(value.toUtf8().constData(), &ok);
-
- if (!ok) {
- recordError(binding->valueLocation, tr("Invalid property assignment: unknown enumeration"));
- return false;
- }
- return true;
- }
-
- switch (property->propType) {
- case QMetaType::QVariant:
- break;
- case QVariant::String: {
- if (!binding->evaluatesToString()) {
- recordError(binding->valueLocation, tr("Invalid property assignment: string expected"));
- return false;
- }
- }
- break;
- case QVariant::StringList: {
- if (!binding->evaluatesToString()) {
- recordError(binding->valueLocation, tr("Invalid property assignment: string or string list expected"));
- return false;
- }
- }
- break;
- case QVariant::ByteArray: {
- if (binding->type != QV4::CompiledData::Binding::Type_String) {
- recordError(binding->valueLocation, tr("Invalid property assignment: byte array expected"));
- return false;
- }
- }
- break;
- case QVariant::Url: {
- if (binding->type != QV4::CompiledData::Binding::Type_String) {
- recordError(binding->valueLocation, tr("Invalid property assignment: url expected"));
- return false;
- }
- }
- break;
- case QVariant::UInt: {
- if (binding->type == QV4::CompiledData::Binding::Type_Number) {
- double d = binding->valueAsNumber();
- if (double(uint(d)) == d)
- return true;
- }
- recordError(binding->valueLocation, tr("Invalid property assignment: unsigned int expected"));
- return false;
- }
- break;
- case QVariant::Int: {
- if (binding->type == QV4::CompiledData::Binding::Type_Number) {
- double d = binding->valueAsNumber();
- if (double(int(d)) == d)
- return true;
- }
- recordError(binding->valueLocation, tr("Invalid property assignment: int expected"));
- return false;
- }
- break;
- case QMetaType::Float: {
- if (binding->type != QV4::CompiledData::Binding::Type_Number) {
- recordError(binding->valueLocation, tr("Invalid property assignment: number expected"));
- return false;
- }
- }
- break;
- case QVariant::Double: {
- if (binding->type != QV4::CompiledData::Binding::Type_Number) {
- recordError(binding->valueLocation, tr("Invalid property assignment: number expected"));
- return false;
- }
- }
- break;
- case QVariant::Color: {
- bool ok = false;
- QQmlStringConverters::rgbaFromString(binding->valueAsString(qmlUnit), &ok);
- if (!ok) {
- recordError(binding->valueLocation, tr("Invalid property assignment: color expected"));
- return false;
- }
- }
- break;
-#ifndef QT_NO_DATESTRING
- case QVariant::Date: {
- bool ok = false;
- QQmlStringConverters::dateFromString(binding->valueAsString(qmlUnit), &ok);
- if (!ok) {
- recordError(binding->valueLocation, tr("Invalid property assignment: date expected"));
- return false;
- }
- }
- break;
- case QVariant::Time: {
- bool ok = false;
- QQmlStringConverters::timeFromString(binding->valueAsString(qmlUnit), &ok);
- if (!ok) {
- recordError(binding->valueLocation, tr("Invalid property assignment: time expected"));
- return false;
- }
- }
- break;
- case QVariant::DateTime: {
- bool ok = false;
- QQmlStringConverters::dateTimeFromString(binding->valueAsString(qmlUnit), &ok);
- if (!ok) {
- recordError(binding->valueLocation, tr("Invalid property assignment: datetime expected"));
- return false;
- }
- }
- break;
-#endif // QT_NO_DATESTRING
- case QVariant::Point: {
- bool ok = false;
- QQmlStringConverters::pointFFromString(binding->valueAsString(qmlUnit), &ok);
- if (!ok) {
- recordError(binding->valueLocation, tr("Invalid property assignment: point expected"));
- return false;
- }
- }
- break;
- case QVariant::PointF: {
- bool ok = false;
- QQmlStringConverters::pointFFromString(binding->valueAsString(qmlUnit), &ok);
- if (!ok) {
- recordError(binding->valueLocation, tr("Invalid property assignment: point expected"));
- return false;
- }
- }
- break;
- case QVariant::Size: {
- bool ok = false;
- QQmlStringConverters::sizeFFromString(binding->valueAsString(qmlUnit), &ok);
- if (!ok) {
- recordError(binding->valueLocation, tr("Invalid property assignment: size expected"));
- return false;
- }
- }
- break;
- case QVariant::SizeF: {
- bool ok = false;
- QQmlStringConverters::sizeFFromString(binding->valueAsString(qmlUnit), &ok);
- if (!ok) {
- recordError(binding->valueLocation, tr("Invalid property assignment: size expected"));
- return false;
- }
- }
- break;
- case QVariant::Rect: {
- bool ok = false;
- QQmlStringConverters::rectFFromString(binding->valueAsString(qmlUnit), &ok);
- if (!ok) {
- recordError(binding->valueLocation, tr("Invalid property assignment: rect expected"));
- return false;
- }
- }
- break;
- case QVariant::RectF: {
- bool ok = false;
- QQmlStringConverters::rectFFromString(binding->valueAsString(qmlUnit), &ok);
- if (!ok) {
- recordError(binding->valueLocation, tr("Invalid property assignment: point expected"));
- return false;
- }
- }
- break;
- case QVariant::Bool: {
- if (binding->type != QV4::CompiledData::Binding::Type_Boolean) {
- recordError(binding->valueLocation, tr("Invalid property assignment: boolean expected"));
- return false;
- }
- }
- break;
- case QVariant::Vector2D: {
- struct {
- float xp;
- float yp;
- } vec;
- if (!QQmlStringConverters::createFromString(QMetaType::QVector2D, binding->valueAsString(qmlUnit), &vec, sizeof(vec))) {
- recordError(binding->valueLocation, tr("Invalid property assignment: 2D vector expected"));
- return false;
- }
- }
- break;
- case QVariant::Vector3D: {
- struct {
- float xp;
- float yp;
- float zy;
- } vec;
- if (!QQmlStringConverters::createFromString(QMetaType::QVector3D, binding->valueAsString(qmlUnit), &vec, sizeof(vec))) {
- recordError(binding->valueLocation, tr("Invalid property assignment: 3D vector expected"));
- return false;
- }
- }
- break;
- case QVariant::Vector4D: {
- struct {
- float xp;
- float yp;
- float zy;
- float wp;
- } vec;
- if (!QQmlStringConverters::createFromString(QMetaType::QVector4D, binding->valueAsString(qmlUnit), &vec, sizeof(vec))) {
- recordError(binding->valueLocation, tr("Invalid property assignment: 4D vector expected"));
- return false;
- }
- }
- break;
- case QVariant::Quaternion: {
- struct {
- float wp;
- float xp;
- float yp;
- float zp;
- } vec;
- if (!QQmlStringConverters::createFromString(QMetaType::QQuaternion, binding->valueAsString(qmlUnit), &vec, sizeof(vec))) {
- recordError(binding->valueLocation, tr("Invalid property assignment: quaternion expected"));
- return false;
- }
- }
- break;
- case QVariant::RegExp:
- recordError(binding->valueLocation, tr("Invalid property assignment: regular expression expected; use /pattern/ syntax"));
- return false;
- default: {
- // generate single literal value assignment to a list property if required
- if (property->propType == qMetaTypeId<QList<qreal> >()) {
- if (binding->type != QV4::CompiledData::Binding::Type_Number) {
- recordError(binding->valueLocation, tr("Invalid property assignment: number or array of numbers expected"));
- return false;
- }
- break;
- } else if (property->propType == qMetaTypeId<QList<int> >()) {
- bool ok = (binding->type == QV4::CompiledData::Binding::Type_Number);
- if (ok) {
- double n = binding->valueAsNumber();
- if (double(int(n)) != n)
- ok = false;
- }
- if (!ok)
- recordError(binding->valueLocation, tr("Invalid property assignment: int or array of ints expected"));
- break;
- } else if (property->propType == qMetaTypeId<QList<bool> >()) {
- if (binding->type != QV4::CompiledData::Binding::Type_Boolean) {
- recordError(binding->valueLocation, tr("Invalid property assignment: bool or array of bools expected"));
- return false;
- }
- break;
- } else if (property->propType == qMetaTypeId<QList<QUrl> >()) {
- if (binding->type != QV4::CompiledData::Binding::Type_String) {
- recordError(binding->valueLocation, tr("Invalid property assignment: url or array of urls expected"));
- return false;
+ obj->flags |= QV4::CompiledData::Object::HasCustomParserBindings;
+ binding->flags |= QV4::CompiledData::Binding::IsCustomParserBinding;
}
- break;
- } else if (property->propType == qMetaTypeId<QList<QString> >()) {
- if (!binding->evaluatesToString()) {
- recordError(binding->valueLocation, tr("Invalid property assignment: string or array of strings expected"));
- return false;
- }
- break;
- } else if (property->propType == qMetaTypeId<QJSValue>()) {
- break;
- } else if (property->propType == qMetaTypeId<QQmlScriptString>()) {
- break;
- }
-
- // otherwise, try a custom type assignment
- QQmlMetaType::StringConverter converter = QQmlMetaType::customStringConverter(property->propType);
- if (!converter) {
- recordError(binding->valueLocation, tr("Invalid property assignment: unsupported type \"%1\"").arg(QString::fromLatin1(QMetaType::typeName(property->propType))));
- return false;
}
}
- break;
- }
- return true;
-}
-
-/*!
- Returns true if from can be assigned to a (QObject) property of type
- to.
-*/
-bool QQmlPropertyValidator::canCoerce(int to, QQmlPropertyCache *fromMo) const
-{
- QQmlPropertyCache *toMo = enginePrivate->rawPropertyCacheForType(to);
-
- while (fromMo) {
- if (fromMo == toMo)
- return true;
- fromMo = fromMo->parent();
- }
- return false;
-}
-bool QQmlPropertyValidator::validateObjectBinding(QQmlPropertyData *property, const QString &propertyName, const QV4::CompiledData::Binding *binding) const
-{
- if (binding->flags & QV4::CompiledData::Binding::IsOnAssignment) {
- Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Object);
-
- bool isValueSource = false;
- bool isPropertyInterceptor = false;
-
- QQmlType *qmlType = 0;
- const QV4::CompiledData::Object *targetObject = qmlUnit->objectAt(binding->value.objectIndex);
- QQmlCompiledData::TypeReference *typeRef = resolvedTypes.value(targetObject->inheritedTypeNameIndex);
- if (typeRef) {
- QQmlPropertyCache *cache = typeRef->createPropertyCache(QQmlEnginePrivate::get(enginePrivate));
- const QMetaObject *mo = cache->firstCppMetaObject();
- while (mo && !qmlType) {
- qmlType = QQmlMetaType::qmlType(mo);
- mo = mo->superClass();
- }
- Q_ASSERT(qmlType);
- }
-
- if (qmlType) {
- isValueSource = qmlType->propertyValueSourceCast() != -1;
- isPropertyInterceptor = qmlType->propertyValueInterceptorCast() != -1;
- }
-
- if (!isValueSource && !isPropertyInterceptor) {
- recordError(binding->valueLocation, tr("\"%1\" cannot operate on \"%2\"").arg(stringAt(targetObject->inheritedTypeNameIndex)).arg(propertyName));
- return false;
- }
-
- return true;
- }
-
- if (QQmlMetaType::isInterface(property->propType)) {
- // Can only check at instantiation time if the created sub-object successfully casts to the
- // target interface.
- return true;
- } else if (property->propType == QMetaType::QVariant) {
- // We can convert everything to QVariant :)
- return true;
- } else if (property->isQList()) {
- const int listType = enginePrivate->listType(property->propType);
- if (!QQmlMetaType::isInterface(listType)) {
- QQmlPropertyCache *source = propertyCaches.at(binding->value.objectIndex);
- if (!canCoerce(listType, source)) {
- recordError(binding->valueLocation, tr("Cannot assign object to list property \"%1\"").arg(propertyName));
- return false;
- }
- }
- return true;
- } else if (isComponent(binding->value.objectIndex)) {
- return true;
- } else if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject && property->isFunction()) {
- return true;
- } else if (QQmlValueTypeFactory::isValueType(property->propType)) {
- recordError(binding->location, tr("Unexpected object assignment"));
- return false;
- } else if (property->propType == qMetaTypeId<QQmlScriptString>()) {
- recordError(binding->valueLocation, tr("Invalid property assignment: script expected"));
- return false;
- } else {
- // We want to raw metaObject here as the raw metaobject is the
- // actual property type before we applied any extensions that might
- // effect the properties on the type, but don't effect assignability
- QQmlPropertyCache *propertyMetaObject = enginePrivate->rawPropertyCacheForType(property->propType);
-
- // Will be true if the assgned type inherits propertyMetaObject
- bool isAssignable = false;
- // Determine isAssignable value
- if (propertyMetaObject) {
- QQmlPropertyCache *c = propertyCaches.at(binding->value.objectIndex);
- while (c && !isAssignable) {
- isAssignable |= c == propertyMetaObject;
- c = c->parent();
- }
- }
-
- if (!isAssignable) {
- recordError(binding->valueLocation, tr("Cannot assign object to property"));
- return false;
- }
- }
return true;
}
QQmlJSCodeGenerator::QQmlJSCodeGenerator(QQmlTypeCompiler *typeCompiler, QmlIR::JSCodeGen *v4CodeGen)
: QQmlCompilePass(typeCompiler)
- , objectIndexToIdPerComponent(*typeCompiler->objectIndexToIdPerComponent())
- , resolvedTypes(*typeCompiler->resolvedTypes())
+ , resolvedTypes(typeCompiler->resolvedTypes)
, customParsers(typeCompiler->customParserCache())
, qmlObjects(*typeCompiler->qmlObjects())
, propertyCaches(typeCompiler->propertyCaches())
@@ -2471,48 +1287,42 @@ QQmlJSCodeGenerator::QQmlJSCodeGenerator(QQmlTypeCompiler *typeCompiler, QmlIR::
bool QQmlJSCodeGenerator::generateCodeForComponents()
{
- const QHash<int, QHash<int, int> > &objectIndexToIdPerComponent = *compiler->objectIndexToIdPerComponent();
- for (QHash<int, QHash<int, int> >::ConstIterator component = objectIndexToIdPerComponent.constBegin(), end = objectIndexToIdPerComponent.constEnd();
- component != end; ++component) {
- if (!compileComponent(component.key(), component.value()))
+ const QVector<quint32> &componentRoots = compiler->componentRoots();
+ for (int i = 0; i < componentRoots.count(); ++i) {
+ if (!compileComponent(componentRoots.at(i)))
return false;
}
- return compileComponent(compiler->rootObjectIndex(), *compiler->objectIndexToIdForRoot());
+ return compileComponent(compiler->rootObjectIndex());
}
-bool QQmlJSCodeGenerator::compileComponent(int contextObject, const QHash<int, int> &objectIndexToId)
+bool QQmlJSCodeGenerator::compileComponent(int contextObject)
{
- if (isComponent(contextObject)) {
- const QmlIR::Object *component = qmlObjects.at(contextObject);
- Q_ASSERT(component->bindingCount() == 1);
- const QV4::CompiledData::Binding *componentBinding = component->firstBinding();
+ const QmlIR::Object *obj = qmlObjects.at(contextObject);
+ if (obj->flags & QV4::CompiledData::Object::IsComponent) {
+ Q_ASSERT(obj->bindingCount() == 1);
+ const QV4::CompiledData::Binding *componentBinding = obj->firstBinding();
Q_ASSERT(componentBinding->type == QV4::CompiledData::Binding::Type_Object);
contextObject = componentBinding->value.objectIndex;
}
QmlIR::JSCodeGen::ObjectIdMapping idMapping;
- if (!objectIndexToId.isEmpty()) {
- idMapping.reserve(objectIndexToId.count());
+ idMapping.reserve(obj->namedObjectsInComponent.count);
+ for (int i = 0; i < obj->namedObjectsInComponent.count; ++i) {
+ const int objectIndex = obj->namedObjectsInComponent.at(i);
+ QmlIR::JSCodeGen::IdMapping m;
+ const QmlIR::Object *obj = qmlObjects.at(objectIndex);
+ m.name = stringAt(obj->idNameIndex);
+ m.idIndex = obj->id;
+ m.type = propertyCaches->at(objectIndex);
- for (QHash<int, int>::ConstIterator idIt = objectIndexToId.constBegin(), end = objectIndexToId.constEnd();
- idIt != end; ++idIt) {
+ auto *tref = resolvedTypes.value(obj->inheritedTypeNameIndex);
+ if (tref && tref->isFullyDynamicType)
+ m.type = 0;
- const int objectIndex = idIt.key();
- QmlIR::JSCodeGen::IdMapping m;
- const QmlIR::Object *obj = qmlObjects.at(objectIndex);
- m.name = stringAt(obj->idIndex);
- m.idIndex = idIt.value();
- m.type = propertyCaches.at(objectIndex);
-
- QQmlCompiledData::TypeReference *tref = resolvedTypes.value(obj->inheritedTypeNameIndex);
- if (tref && tref->isFullyDynamicType)
- m.type = 0;
-
- idMapping << m;
- }
+ idMapping << m;
}
- v4CodeGen->beginContextScope(idMapping, propertyCaches.at(contextObject));
+ v4CodeGen->beginContextScope(idMapping, propertyCaches->at(contextObject));
if (!compileJavaScriptCodeInObjectsRecursively(contextObject, contextObject))
return false;
@@ -2522,12 +1332,12 @@ bool QQmlJSCodeGenerator::compileComponent(int contextObject, const QHash<int, i
bool QQmlJSCodeGenerator::compileJavaScriptCodeInObjectsRecursively(int objectIndex, int scopeObjectIndex)
{
- if (isComponent(objectIndex))
+ QmlIR::Object *object = qmlObjects.at(objectIndex);
+ if (object->flags & QV4::CompiledData::Object::IsComponent)
return true;
- QmlIR::Object *object = qmlObjects.at(objectIndex);
if (object->functionsAndExpressions->count > 0) {
- QQmlPropertyCache *scopeObject = propertyCaches.at(scopeObjectIndex);
+ QQmlPropertyCache *scopeObject = propertyCaches->at(scopeObjectIndex);
v4CodeGen->beginObjectScope(scopeObject);
QList<QmlIR::CompiledFunctionOrExpression> functionsToCompile;
@@ -2546,8 +1356,7 @@ bool QQmlJSCodeGenerator::compileJavaScriptCodeInObjectsRecursively(int objectIn
}
QQmlJS::MemoryPool *pool = compiler->memoryPool();
- object->runtimeFunctionIndices = pool->New<QmlIR::FixedPoolArray<int> >();
- object->runtimeFunctionIndices->init(pool, runtimeFunctionIndices);
+ object->runtimeFunctionIndices.allocate(pool, runtimeFunctionIndices);
}
for (const QmlIR::Binding *binding = object->firstBinding(); binding; binding = binding->next) {
@@ -2580,20 +1389,20 @@ void QQmlDefaultPropertyMerger::mergeDefaultProperties()
void QQmlDefaultPropertyMerger::mergeDefaultProperties(int objectIndex)
{
- QQmlPropertyCache *propertyCache = propertyCaches.at(objectIndex);
+ QQmlPropertyCache *propertyCache = propertyCaches->at(objectIndex);
if (!propertyCache)
return;
QmlIR::Object *object = qmlObjects.at(objectIndex);
- QString defaultProperty = object->indexOfDefaultProperty != -1 ? propertyCache->parent()->defaultPropertyName() : propertyCache->defaultPropertyName();
+ QString defaultProperty = object->indexOfDefaultPropertyOrAlias != -1 ? propertyCache->parent()->defaultPropertyName() : propertyCache->defaultPropertyName();
QmlIR::Binding *bindingsToReinsert = 0;
QmlIR::Binding *tail = 0;
QmlIR::Binding *previousBinding = 0;
QmlIR::Binding *binding = object->firstBinding();
while (binding) {
- if (binding->propertyNameIndex == 0 || stringAt(binding->propertyNameIndex) != defaultProperty) {
+ if (binding->propertyNameIndex == quint32(0) || stringAt(binding->propertyNameIndex) != defaultProperty) {
previousBinding = binding;
binding = binding->next;
continue;
@@ -2646,7 +1455,7 @@ void QQmlJavaScriptBindingExpressionSimplificationPass::reduceTranslationBinding
if (binding->type != QV4::CompiledData::Binding::Type_Script)
continue;
- const int irFunctionIndex = obj->runtimeFunctionIndices->at(binding->value.compiledScriptIndex);
+ const int irFunctionIndex = obj->runtimeFunctionIndices.at(binding->value.compiledScriptIndex);
QV4::IR::Function *irFunction = jsModule->functions.at(irFunctionIndex);
if (simplifyBinding(irFunction, binding)) {
irFunctionsToRemove.append(irFunctionIndex);
@@ -2757,7 +1566,7 @@ bool QQmlJavaScriptBindingExpressionSimplificationPass::simplifyBinding(QV4::IR:
for (QV4::IR::BasicBlock *bb : function->basicBlocks()) {
for (QV4::IR::Stmt *s : bb->statements()) {
- s->accept(this);
+ visit(s);
if (!_canSimplify)
return false;
}
@@ -2927,22 +1736,41 @@ void QQmlIRFunctionCleanser::clean()
foreach (QV4::IR::Function *function, module->functions) {
for (QV4::IR::BasicBlock *block : function->basicBlocks()) {
for (QV4::IR::Stmt *s : block->statements()) {
- s->accept(this);
+ visit(s);
}
}
}
foreach (QmlIR::Object *obj, *compiler->qmlObjects()) {
- if (!obj->runtimeFunctionIndices)
- continue;
- for (int i = 0; i < obj->runtimeFunctionIndices->count; ++i)
- (*obj->runtimeFunctionIndices)[i] = newFunctionIndices[obj->runtimeFunctionIndices->at(i)];
+ for (int i = 0; i < obj->runtimeFunctionIndices.count; ++i)
+ obj->runtimeFunctionIndices[i] = newFunctionIndices[obj->runtimeFunctionIndices.at(i)];
+ }
+}
+
+void QQmlIRFunctionCleanser::visit(QV4::IR::Stmt *s)
+{
+
+ switch (s->stmtKind) {
+ case QV4::IR::Stmt::PhiStmt:
+ // nothing to do
+ break;
+ default:
+ STMT_VISIT_ALL_KINDS(s);
+ break;
}
}
-void QQmlIRFunctionCleanser::visitClosure(QV4::IR::Closure *closure)
+void QQmlIRFunctionCleanser::visit(QV4::IR::Expr *e)
{
- closure->value = newFunctionIndices.at(closure->value);
+ switch (e->exprKind) {
+ case QV4::IR::Expr::ClosureExpr: {
+ auto closure = e->asClosure();
+ closure->value = newFunctionIndices.at(closure->value);
+ } break;
+ default:
+ EXPR_VISIT_ALL_KINDS(e);
+ break;
+ }
}
QT_END_NAMESPACE
diff --git a/src/qml/compiler/qqmltypecompiler_p.h b/src/qml/compiler/qqmltypecompiler_p.h
index 273ba01a88..de6abb4ced 100644
--- a/src/qml/compiler/qqmltypecompiler_p.h
+++ b/src/qml/compiler/qqmltypecompiler_p.h
@@ -53,13 +53,12 @@
#include <qglobal.h>
#include <qqmlerror.h>
#include <qhash.h>
-#include <private/qqmlcompiler_p.h>
+#include <private/qqmltypeloader_p.h>
#include <private/qqmlirbuilder_p.h>
QT_BEGIN_NAMESPACE
class QQmlEnginePrivate;
-class QQmlCompiledData;
class QQmlError;
class QQmlTypeData;
class QQmlImports;
@@ -75,18 +74,40 @@ struct Location;
}
}
+struct QQmlCompileError
+{
+ QQmlCompileError() {}
+ QQmlCompileError(const QV4::CompiledData::Location &location, const QString &description)
+ : location(location), description(description) {}
+ QV4::CompiledData::Location location;
+ QString description;
+
+ bool isSet() const { return !description.isEmpty(); }
+};
+
struct QQmlTypeCompiler
{
Q_DECLARE_TR_FUNCTIONS(QQmlTypeCompiler)
public:
- QQmlTypeCompiler(QQmlEnginePrivate *engine, QQmlCompiledData *compiledData, QQmlTypeData *typeData, QmlIR::Document *document);
+ QQmlTypeCompiler(QQmlEnginePrivate *engine, QQmlTypeData *typeData, QmlIR::Document *document, const QQmlRefPointer<QQmlTypeNameCache> &importCache, const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypeCache);
+
+ // --- interface used by QQmlPropertyCacheCreator
+ typedef QmlIR::Object CompiledObject;
+ const QmlIR::Object *objectAt(int index) const { return document->objects.at(index); }
+ int objectCount() const { return document->objects.count(); }
+ QString stringAt(int idx) const;
+ QmlIR::PoolList<QmlIR::Function>::Iterator objectFunctionsBegin(const QmlIR::Object *object) const { return object->functionsBegin(); }
+ QmlIR::PoolList<QmlIR::Function>::Iterator objectFunctionsEnd(const QmlIR::Object *object) const { return object->functionsEnd(); }
+ QV4::CompiledData::ResolvedTypeReferenceMap resolvedTypes;
+ // ---
- bool compile();
+ QV4::CompiledData::CompilationUnit *compile();
QList<QQmlError> compilationErrors() const { return errors; }
- void recordError(const QQmlError &error);
+ void recordError(QQmlError error);
+ void recordError(const QV4::CompiledData::Location &location, const QString &description);
+ void recordError(const QQmlCompileError &error);
- QString stringAt(int idx) const;
int registerString(const QString &str);
QV4::IR::Module *jsIRModule() const;
@@ -96,70 +117,52 @@ public:
QUrl url() const { return typeData->finalUrl(); }
QQmlEnginePrivate *enginePrivate() const { return engine; }
const QQmlImports *imports() const;
- QHash<int, QQmlCompiledData::TypeReference *> *resolvedTypes();
- QList<QmlIR::Object*> *qmlObjects();
+ QVector<QmlIR::Object *> *qmlObjects() const;
int rootObjectIndex() const;
- void setPropertyCaches(const QVector<QQmlPropertyCache *> &caches);
- const QVector<QQmlPropertyCache *> &propertyCaches() const;
- void setVMEMetaObjects(const QVector<QByteArray> &metaObjects);
- QVector<QByteArray> *vmeMetaObjects() const;
- QHash<int, int> *objectIndexToIdForRoot();
- QHash<int, QHash<int, int> > *objectIndexToIdPerComponent();
- QHash<int, QBitArray> *customParserBindings();
+ void setPropertyCaches(QQmlPropertyCacheVector &&caches);
+ const QQmlPropertyCacheVector *propertyCaches() const;
+ QQmlPropertyCacheVector &&takePropertyCaches();
+ void setComponentRoots(const QVector<quint32> &roots) { m_componentRoots = roots; }
+ const QVector<quint32> &componentRoots() const { return m_componentRoots; }
QQmlJS::MemoryPool *memoryPool();
QStringRef newStringRef(const QString &string);
const QV4::Compiler::StringTableGenerator *stringPool() const;
- void setDeferredBindingsPerObject(const QHash<int, QBitArray> &deferredBindingsPerObject);
void setBindingPropertyDataPerObject(const QVector<QV4::CompiledData::BindingPropertyData> &propertyData);
const QHash<int, QQmlCustomParser*> &customParserCache() const { return customParsers; }
QString bindingAsString(const QmlIR::Object *object, int scriptIndex) const;
+ void addImport(const QString &module, const QString &qualifier, int majorVersion, int minorVersion);
+
private:
QList<QQmlError> errors;
QQmlEnginePrivate *engine;
- QQmlCompiledData *compiledData;
QQmlTypeData *typeData;
+ QQmlRefPointer<QQmlTypeNameCache> importCache;
QmlIR::Document *document;
// index is string index of type name (use obj->inheritedTypeNameIndex)
QHash<int, QQmlCustomParser*> customParsers;
+
+ // index in first hash is component index, vector inside contains object indices of objects with id property
+ QVector<quint32> m_componentRoots;
+ QQmlPropertyCacheVector m_propertyCaches;
};
struct QQmlCompilePass
{
- virtual ~QQmlCompilePass() {}
-
QQmlCompilePass(QQmlTypeCompiler *typeCompiler);
QString stringAt(int idx) const { return compiler->stringAt(idx); }
protected:
- void recordError(const QV4::CompiledData::Location &location, const QString &description) const;
+ void recordError(const QV4::CompiledData::Location &location, const QString &description) const
+ { compiler->recordError(location, description); }
+ void recordError(const QQmlCompileError &error)
+ { compiler->recordError(error); }
QQmlTypeCompiler *compiler;
};
-class QQmlPropertyCacheCreator : public QQmlCompilePass
-{
- Q_DECLARE_TR_FUNCTIONS(QQmlPropertyCacheCreator)
-public:
- QQmlPropertyCacheCreator(QQmlTypeCompiler *typeCompiler);
- ~QQmlPropertyCacheCreator();
-
- bool buildMetaObjects();
-protected:
- bool buildMetaObjectRecursively(int objectIndex, int referencingObjectIndex, const QV4::CompiledData::Binding *instantiatingBinding);
- bool ensureMetaObject(int objectIndex);
- bool createMetaObject(int objectIndex, const QmlIR::Object *obj, QQmlPropertyCache *baseTypeCache);
-
- QQmlEnginePrivate *enginePrivate;
- const QList<QmlIR::Object*> &qmlObjects;
- const QQmlImports *imports;
- QHash<int, QQmlCompiledData::TypeReference*> *resolvedTypes;
- QVector<QByteArray> vmeMetaObjects;
- QVector<QQmlPropertyCache*> propertyCaches;
-};
-
// "Converts" signal expressions to full-fleged function declarations with
// parameters taken from the signal declarations
// It also updates the QV4::CompiledData::Binding objects to set the property name
@@ -176,12 +179,12 @@ private:
bool convertSignalHandlerExpressionsToFunctionDeclarations(const QmlIR::Object *obj, const QString &typeName, QQmlPropertyCache *propertyCache);
QQmlEnginePrivate *enginePrivate;
- const QList<QmlIR::Object*> &qmlObjects;
+ const QVector<QmlIR::Object*> &qmlObjects;
const QQmlImports *imports;
const QHash<int, QQmlCustomParser*> &customParsers;
- const QHash<int, QQmlCompiledData::TypeReference*> &resolvedTypes;
+ const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypes;
const QSet<QString> &illegalNames;
- const QVector<QQmlPropertyCache*> &propertyCaches;
+ const QQmlPropertyCacheVector * const propertyCaches;
};
// ### This will go away when the codegen resolves all enums to constant expressions
@@ -207,10 +210,10 @@ private:
int evaluateEnum(const QString &scope, const QByteArray &enumValue, bool *ok) const;
- const QList<QmlIR::Object*> &qmlObjects;
- const QVector<QQmlPropertyCache *> propertyCaches;
+ const QVector<QmlIR::Object*> &qmlObjects;
+ const QQmlPropertyCacheVector * const propertyCaches;
const QQmlImports *imports;
- QHash<int, QQmlCompiledData::TypeReference *> *resolvedTypes;
+ QV4::CompiledData::ResolvedTypeReferenceMap *resolvedTypes;
};
class QQmlCustomParserScriptIndexer: public QQmlCompilePass
@@ -223,7 +226,7 @@ public:
private:
void scanObjectRecursively(int objectIndex, bool annotateScriptBindings = false);
- const QList<QmlIR::Object*> &qmlObjects;
+ const QVector<QmlIR::Object*> &qmlObjects;
const QHash<int, QQmlCustomParser*> &customParsers;
};
@@ -235,8 +238,8 @@ public:
void annotateBindingsToAliases();
private:
- const QList<QmlIR::Object*> &qmlObjects;
- const QVector<QQmlPropertyCache *> propertyCaches;
+ const QVector<QmlIR::Object*> &qmlObjects;
+ const QQmlPropertyCacheVector * const propertyCaches;
};
class QQmlScriptStringScanner : public QQmlCompilePass
@@ -247,8 +250,8 @@ public:
void scan();
private:
- const QList<QmlIR::Object*> &qmlObjects;
- const QVector<QQmlPropertyCache *> propertyCaches;
+ const QVector<QmlIR::Object*> &qmlObjects;
+ const QQmlPropertyCacheVector * const propertyCaches;
};
class QQmlComponentAndAliasResolver : public QQmlCompilePass
@@ -262,64 +265,49 @@ public:
protected:
void findAndRegisterImplicitComponents(const QmlIR::Object *obj, QQmlPropertyCache *propertyCache);
bool collectIdsAndAliases(int objectIndex);
- bool resolveAliases();
+ bool resolveAliases(int componentIndex);
+ void propertyDataForAlias(QmlIR::Alias *alias, int *type, quint32 *propertyFlags);
+
+ enum AliasResolutionResult {
+ NoAliasResolved,
+ SomeAliasesResolved,
+ AllAliasesResolved
+ };
+
+ AliasResolutionResult resolveAliasesInObject(int objectIndex, QQmlCompileError *error);
QQmlEnginePrivate *enginePrivate;
QQmlJS::MemoryPool *pool;
- QList<QmlIR::Object*> *qmlObjects;
+ QVector<QmlIR::Object*> *qmlObjects;
const int indexOfRootObject;
// indices of the objects that are actually Component {}
- QVector<int> componentRoots;
- // indices of objects that are the beginning of a new component
- // scope. This is sorted and used for binary search.
- QVector<quint32> componentBoundaries;
-
- int _componentIndex;
- QHash<int, int> _idToObjectIndex;
- QHash<int, int> *_objectIndexToIdInScope;
- QList<int> _objectsWithAliases;
-
- QHash<int, QQmlCompiledData::TypeReference*> *resolvedTypes;
- QVector<QQmlPropertyCache *> propertyCaches;
- QVector<QByteArray> *vmeMetaObjectData;
- QHash<int, int> *objectIndexToIdForRoot;
- QHash<int, QHash<int, int> > *objectIndexToIdPerComponent;
+ QVector<quint32> componentRoots;
+
+ // Deliberate choice of map over hash here to ensure stable generated output.
+ QMap<int, int> _idToObjectIndex;
+ QVector<int> _objectsWithAliases;
+
+ QV4::CompiledData::ResolvedTypeReferenceMap *resolvedTypes;
+ QQmlPropertyCacheVector propertyCaches;
};
-class QQmlPropertyValidator : public QQmlCompilePass
+class QQmlDeferredAndCustomParserBindingScanner : public QQmlCompilePass
{
- Q_DECLARE_TR_FUNCTIONS(QQmlPropertyValidator)
public:
- QQmlPropertyValidator(QQmlTypeCompiler *typeCompiler);
-
- bool validate();
+ QQmlDeferredAndCustomParserBindingScanner(QQmlTypeCompiler *typeCompiler);
- const QQmlImports &imports() const;
- QQmlEnginePrivate *engine() const { return enginePrivate; }
+ bool scanObject();
private:
- bool validateObject(int objectIndex, const QV4::CompiledData::Binding *instantiatingBinding, bool populatingValueTypeGroupProperty = false) const;
- bool validateLiteralBinding(QQmlPropertyCache *propertyCache, QQmlPropertyData *property, const QV4::CompiledData::Binding *binding) const;
- bool validateObjectBinding(QQmlPropertyData *property, const QString &propertyName, const QV4::CompiledData::Binding *binding) const;
-
- bool isComponent(int objectIndex) const { return objectIndexToIdPerComponent.contains(objectIndex); }
-
- bool canCoerce(int to, QQmlPropertyCache *fromMo) const;
+ bool scanObject(int objectIndex);
- QQmlEnginePrivate *enginePrivate;
- const QV4::CompiledData::Unit *qmlUnit;
- const QHash<int, QQmlCompiledData::TypeReference*> &resolvedTypes;
+ QVector<QmlIR::Object*> *qmlObjects;
+ const QQmlPropertyCacheVector * const propertyCaches;
const QHash<int, QQmlCustomParser*> &customParsers;
- const QVector<QQmlPropertyCache *> &propertyCaches;
- const QHash<int, QHash<int, int> > objectIndexToIdPerComponent;
- QHash<int, QBitArray> *customParserBindingsPerObject;
-
- // collected state variables, essentially write-only
- mutable QHash<int, QBitArray> _deferredBindingsPerObject;
- mutable bool _seenObjectWithId;
- mutable QVector<QV4::CompiledData::BindingPropertyData> _bindingPropertyDataPerObject;
+
+ bool _seenObjectWithId;
};
// ### merge with QtQml::JSCodeGen and operate directly on object->functionsAndExpressions once old compiler is gone.
@@ -331,16 +319,13 @@ public:
bool generateCodeForComponents();
private:
- bool compileComponent(int componentRoot, const QHash<int, int> &objectIndexToId);
+ bool compileComponent(int componentRoot);
bool compileJavaScriptCodeInObjectsRecursively(int objectIndex, int scopeObjectIndex);
- bool isComponent(int objectIndex) const { return objectIndexToIdPerComponent.contains(objectIndex); }
-
- const QHash<int, QHash<int, int> > &objectIndexToIdPerComponent;
- const QHash<int, QQmlCompiledData::TypeReference*> &resolvedTypes;
+ const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypes;
const QHash<int, QQmlCustomParser*> &customParsers;
- const QList<QmlIR::Object*> &qmlObjects;
- const QVector<QQmlPropertyCache *> &propertyCaches;
+ const QVector<QmlIR::Object*> &qmlObjects;
+ const QQmlPropertyCacheVector * const propertyCaches;
QmlIR::JSCodeGen * const v4CodeGen;
};
@@ -354,11 +339,11 @@ public:
private:
void mergeDefaultProperties(int objectIndex);
- const QList<QmlIR::Object*> &qmlObjects;
- const QVector<QQmlPropertyCache*> &propertyCaches;
+ const QVector<QmlIR::Object*> &qmlObjects;
+ const QQmlPropertyCacheVector * const propertyCaches;
};
-class QQmlJavaScriptBindingExpressionSimplificationPass : public QQmlCompilePass, public QV4::IR::StmtVisitor
+class QQmlJavaScriptBindingExpressionSimplificationPass : public QQmlCompilePass
{
public:
QQmlJavaScriptBindingExpressionSimplificationPass(QQmlTypeCompiler *typeCompiler);
@@ -368,12 +353,30 @@ public:
private:
void reduceTranslationBindings(int objectIndex);
- virtual void visitMove(QV4::IR::Move *move);
- virtual void visitJump(QV4::IR::Jump *) {}
- virtual void visitCJump(QV4::IR::CJump *) { discard(); }
- virtual void visitExp(QV4::IR::Exp *) { discard(); }
- virtual void visitPhi(QV4::IR::Phi *) {}
- virtual void visitRet(QV4::IR::Ret *ret);
+ void visit(QV4::IR::Stmt *s)
+ {
+ switch (s->stmtKind) {
+ case QV4::IR::Stmt::MoveStmt:
+ visitMove(s->asMove());
+ break;
+ case QV4::IR::Stmt::RetStmt:
+ visitRet(s->asRet());
+ break;
+ case QV4::IR::Stmt::CJumpStmt:
+ discard();
+ break;
+ case QV4::IR::Stmt::ExpStmt:
+ discard();
+ break;
+ case QV4::IR::Stmt::JumpStmt:
+ break;
+ case QV4::IR::Stmt::PhiStmt:
+ break;
+ }
+ }
+
+ void visitMove(QV4::IR::Move *move);
+ void visitRet(QV4::IR::Ret *ret);
void visitFunctionCall(const QString *name, QV4::IR::ExprList *args, QV4::IR::Temp *target);
@@ -382,7 +385,7 @@ private:
bool simplifyBinding(QV4::IR::Function *function, QmlIR::Binding *binding);
bool detectTranslationCallAndConvertBinding(QmlIR::Binding *binding);
- const QList<QmlIR::Object*> &qmlObjects;
+ const QVector<QmlIR::Object*> &qmlObjects;
QV4::IR::Module *jsModule;
bool _canSimplify;
@@ -397,8 +400,7 @@ private:
QVector<int> irFunctionsToRemove;
};
-class QQmlIRFunctionCleanser : public QQmlCompilePass, public QV4::IR::StmtVisitor,
- public QV4::IR::ExprVisitor
+class QQmlIRFunctionCleanser : public QQmlCompilePass
{
public:
QQmlIRFunctionCleanser(QQmlTypeCompiler *typeCompiler, const QVector<int> &functionsToRemove);
@@ -406,51 +408,13 @@ public:
void clean();
private:
- virtual void visitClosure(QV4::IR::Closure *closure);
-
- virtual void visitTemp(QV4::IR::Temp *) {}
- virtual void visitArgLocal(QV4::IR::ArgLocal *) {}
-
virtual void visitMove(QV4::IR::Move *s) {
- s->source->accept(this);
- s->target->accept(this);
- }
-
- virtual void visitConvert(QV4::IR::Convert *e) { e->expr->accept(this); }
- virtual void visitPhi(QV4::IR::Phi *) { }
-
- virtual void visitExp(QV4::IR::Exp *s) { s->expr->accept(this); }
-
- virtual void visitJump(QV4::IR::Jump *) {}
- virtual void visitCJump(QV4::IR::CJump *s) { s->cond->accept(this); }
- virtual void visitRet(QV4::IR::Ret *s) { s->expr->accept(this); }
-
- virtual void visitConst(QV4::IR::Const *) {}
- virtual void visitString(QV4::IR::String *) {}
- virtual void visitRegExp(QV4::IR::RegExp *) {}
- virtual void visitName(QV4::IR::Name *) {}
- virtual void visitUnop(QV4::IR::Unop *e) { e->expr->accept(this); }
- virtual void visitBinop(QV4::IR::Binop *e) { e->left->accept(this); e->right->accept(this); }
- virtual void visitCall(QV4::IR::Call *e) {
- e->base->accept(this);
- for (QV4::IR::ExprList *it = e->args; it; it = it->next)
- it->expr->accept(this);
+ visit(s->source);
+ visit(s->target);
}
- virtual void visitNew(QV4::IR::New *e) {
- e->base->accept(this);
- for (QV4::IR::ExprList *it = e->args; it; it = it->next)
- it->expr->accept(this);
- }
-
- virtual void visitSubscript(QV4::IR::Subscript *e) {
- e->base->accept(this);
- e->index->accept(this);
- }
-
- virtual void visitMember(QV4::IR::Member *e) {
- e->base->accept(this);
- }
+ void visit(QV4::IR::Stmt *s);
+ void visit(QV4::IR::Expr *e);
private:
QV4::IR::Module *module;
diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp
index 461ff89550..e0def1021b 100644
--- a/src/qml/compiler/qv4codegen.cpp
+++ b/src/qml/compiler/qv4codegen.cpp
@@ -2026,6 +2026,7 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast,
function->maxNumberOfArguments = qMax(_env->maxNumberOfArguments, (int)QV4::Global::ReservedArgumentCount);
function->isStrict = _env->isStrict;
function->isNamedExpression = _env->isNamedFunctionExpression;
+ function->isQmlBinding = _env->compilationMode == QmlBinding;
AST::SourceLocation loc = ast->firstSourceLocation();
function->line = loc.startLine;
@@ -2046,7 +2047,7 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast,
}
} else {
if (!_env->isStrict) {
- foreach (const QString &inheritedLocal, inheritedLocals) {
+ for (const QString &inheritedLocal : qAsConst(inheritedLocals)) {
function->LOCAL(inheritedLocal);
unsigned tempIndex = entryBlock->newTemp();
Environment::Member member = { Environment::UndefinedMember,
@@ -2088,7 +2089,7 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast,
_function->RECEIVE(it->name.toString());
}
- foreach (const Environment::Member &member, _env->members) {
+ for (const Environment::Member &member : qAsConst(_env->members)) {
if (member.function) {
const int function = defineFunction(member.function->name.toString(), member.function, member.function->formals,
member.function->body ? member.function->body->elements : 0);
@@ -2939,7 +2940,7 @@ QList<QQmlError> Codegen::qmlErrors() const
qmlErrors.reserve(_errors.size());
QUrl url(_fileNameIsUrl ? QUrl(_module->fileName) : QUrl::fromLocalFile(_module->fileName));
- foreach (const QQmlJS::DiagnosticMessage &msg, _errors) {
+ for (const QQmlJS::DiagnosticMessage &msg: qAsConst(_errors)) {
QQmlError e;
e.setUrl(url);
e.setLine(msg.loc.startLine);
diff --git a/src/qml/qml/qqmlaccessors.cpp b/src/qml/compiler/qv4compilationunitmapper.cpp
index 7b0fafdc90..2e1213464c 100644
--- a/src/qml/qml/qqmlaccessors.cpp
+++ b/src/qml/compiler/qv4compilationunitmapper.cpp
@@ -37,69 +37,63 @@
**
****************************************************************************/
-#include "qqmlaccessors_p.h"
+#include "qv4compilationunitmapper_p.h"
-#include "qqmldata_p.h"
-#include "qqmlnotifier_p.h"
+#include "qv4compileddata_p.h"
+#include <QFileInfo>
+#include <QDateTime>
+#include <QCoreApplication>
QT_BEGIN_NAMESPACE
-struct AccessorProperties {
- AccessorProperties();
+using namespace QV4;
- QReadWriteLock lock;
- QHash<const QMetaObject *, QQmlAccessorProperties::Properties> properties;
-};
-
-Q_GLOBAL_STATIC(AccessorProperties, accessorProperties)
-
-static void buildNameMask(QQmlAccessorProperties::Properties &properties)
+CompilationUnitMapper::CompilationUnitMapper()
+ : dataPtr(nullptr)
{
- quint32 mask = 0;
-
- for (int ii = 0; ii < properties.count; ++ii) {
- Q_ASSERT(strlen(properties.properties[ii].name) == properties.properties[ii].nameLength);
- Q_ASSERT(properties.properties[ii].nameLength > 0);
-
- mask |= (1 << qMin(31U, properties.properties[ii].nameLength - 1));
- }
- properties.nameMask = mask;
}
-AccessorProperties::AccessorProperties()
+CompilationUnitMapper::~CompilationUnitMapper()
{
+ close();
}
-QQmlAccessorProperties::Properties::Properties(Property *properties, int count)
-: count(count), properties(properties)
+bool CompilationUnitMapper::verifyHeader(const CompiledData::Unit *header, const QString &sourcePath, QString *errorString)
{
- buildNameMask(*this);
-}
-
-QQmlAccessorProperties::Properties
-QQmlAccessorProperties::properties(const QMetaObject *mo)
-{
- AccessorProperties *This = accessorProperties();
-
- QReadLocker lock(&This->lock);
- return This->properties.value(mo);
-}
-
-void QQmlAccessorProperties::registerProperties(const QMetaObject *mo, int count,
- Property *props)
-{
- Q_ASSERT(count > 0);
-
- Properties properties(props, count);
+ if (strncmp(header->magic, CompiledData::magic_str, sizeof(header->magic))) {
+ *errorString = QStringLiteral("Magic bytes in the header do not match");
+ return false;
+ }
- AccessorProperties *This = accessorProperties();
+ if (header->version != quint32(QV4_DATA_STRUCTURE_VERSION)) {
+ *errorString = QString::fromUtf8("V4 data structure version mismatch. Found %1 expected %2").arg(header->version, 0, 16).arg(QV4_DATA_STRUCTURE_VERSION, 0, 16);
+ return false;
+ }
- QWriteLocker lock(&This->lock);
+ if (header->qtVersion != quint32(QT_VERSION)) {
+ *errorString = QString::fromUtf8("Qt version mismatch. Found %1 expected %2").arg(header->qtVersion, 0, 16).arg(QT_VERSION, 0, 16);
+ return false;
+ }
- Q_ASSERT(!This->properties.contains(mo) || This->properties.value(mo) == properties);
+ {
+ QFileInfo sourceCode(sourcePath);
+ QDateTime sourceTimeStamp;
+ if (sourceCode.exists())
+ sourceTimeStamp = sourceCode.lastModified();
+
+ // Files from the resource system do not have any time stamps, so fall back to the application
+ // executable.
+ if (!sourceTimeStamp.isValid())
+ sourceTimeStamp = QFileInfo(QCoreApplication::applicationFilePath()).lastModified();
+
+ if (sourceTimeStamp.isValid() && sourceTimeStamp.toMSecsSinceEpoch() != header->sourceTimeStamp) {
+ *errorString = QStringLiteral("QML source file has a different time stamp than cached file.");
+ return false;
+ }
+ }
- This->properties.insert(mo, properties);
+ return true;
}
QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmlmemoryprofiler_p.h b/src/qml/compiler/qv4compilationunitmapper_p.h
index 4b0ba823ba..5b6939f1cf 100644
--- a/src/qml/qml/qqmlmemoryprofiler_p.h
+++ b/src/qml/compiler/qv4compilationunitmapper_p.h
@@ -37,8 +37,8 @@
**
****************************************************************************/
-#ifndef QQMLMEMORYPROFILER_H
-#define QQMLMEMORYPROFILER_H
+#ifndef QV4COMPILATIONUNITMAPPER_H
+#define QV4COMPILATIONUNITMAPPER_H
//
// W A R N I N G
@@ -51,37 +51,37 @@
// We mean it.
//
-#include <private/qtqmlglobal_p.h>
+#include <private/qv4global_p.h>
+#include <QFile>
QT_BEGIN_NAMESPACE
-class QUrl;
+namespace QV4 {
-class Q_QML_PRIVATE_EXPORT QQmlMemoryScope
+namespace CompiledData {
+struct Unit;
+}
+
+class CompilationUnitMapper
{
public:
- explicit QQmlMemoryScope(const QUrl &url);
- explicit QQmlMemoryScope(const char *string);
- ~QQmlMemoryScope();
+ CompilationUnitMapper();
+ ~CompilationUnitMapper();
-private:
- bool pushed;
-};
+ CompiledData::Unit *open(const QString &cacheFilePath, const QString &sourcePath, QString *errorString);
+ void close();
-class Q_QML_PRIVATE_EXPORT QQmlMemoryProfiler
-{
-public:
- static void enable();
- static void disable();
- static bool isEnabled();
+private:
+ static bool verifyHeader(const QV4::CompiledData::Unit *header, const QString &sourcePath, QString *errorString);
- static void clear();
- static void stats(int *allocCount, int *bytesAllocated);
- static void save(const char *filename);
+#if defined(Q_OS_UNIX)
+ size_t length;
+#endif
+ void *dataPtr;
};
-#define QML_MEMORY_SCOPE_URL(url) QQmlMemoryScope _qml_memory_scope(url)
-#define QML_MEMORY_SCOPE_STRING(s) QQmlMemoryScope _qml_memory_scope(s)
+}
QT_END_NAMESPACE
-#endif // QQMLMEMORYPROFILER_H
+
+#endif // QV4COMPILATIONUNITMAPPER_H
diff --git a/src/qml/compiler/qv4compilationunitmapper_unix.cpp b/src/qml/compiler/qv4compilationunitmapper_unix.cpp
new file mode 100644
index 0000000000..1aa3e05f5f
--- /dev/null
+++ b/src/qml/compiler/qv4compilationunitmapper_unix.cpp
@@ -0,0 +1,99 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml 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 "qv4compilationunitmapper_p.h"
+
+#include <sys/mman.h>
+#include <functional>
+#include <private/qcore_unix_p.h>
+#include <private/qdeferredcleanup_p.h>
+
+#include "qv4compileddata_p.h"
+
+QT_BEGIN_NAMESPACE
+
+using namespace QV4;
+
+CompiledData::Unit *CompilationUnitMapper::open(const QString &cacheFileName, const QString &sourcePath, QString *errorString)
+{
+ close();
+
+ int fd = qt_safe_open(QFile::encodeName(cacheFileName).constData(), O_RDONLY);
+ if (fd == -1) {
+ *errorString = qt_error_string(errno);
+ return nullptr;
+ }
+
+ QDeferredCleanup cleanup([fd]{
+ qt_safe_close(fd) ;
+ });
+
+ CompiledData::Unit header;
+ qint64 bytesRead = qt_safe_read(fd, reinterpret_cast<char *>(&header), sizeof(header));
+
+ if (bytesRead != sizeof(header)) {
+ *errorString = QStringLiteral("File too small for the header fields");
+ return nullptr;
+ }
+
+ if (!verifyHeader(&header, sourcePath, errorString))
+ return nullptr;
+
+ // Data structure and qt version matched, so now we can access the rest of the file safely.
+
+ length = static_cast<size_t>(lseek(fd, 0, SEEK_END));
+
+ void *ptr = mmap(nullptr, length, PROT_READ, MAP_SHARED, fd, /*offset*/0);
+ if (ptr == MAP_FAILED) {
+ *errorString = qt_error_string(errno);
+ return nullptr;
+ }
+ dataPtr = ptr;
+
+ return reinterpret_cast<CompiledData::Unit*>(dataPtr);
+}
+
+void CompilationUnitMapper::close()
+{
+ if (dataPtr != nullptr)
+ munmap(dataPtr, length);
+ dataPtr = nullptr;
+}
+
+QT_END_NAMESPACE
diff --git a/src/qml/compiler/qv4compilationunitmapper_win.cpp b/src/qml/compiler/qv4compilationunitmapper_win.cpp
new file mode 100644
index 0000000000..457b702ac3
--- /dev/null
+++ b/src/qml/compiler/qv4compilationunitmapper_win.cpp
@@ -0,0 +1,134 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml 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 "qv4compilationunitmapper_p.h"
+
+#include "qv4compileddata_p.h"
+#include <private/qdeferredcleanup_p.h>
+#include <QFileInfo>
+#include <QDateTime>
+#include <qt_windows.h>
+
+QT_BEGIN_NAMESPACE
+
+using namespace QV4;
+
+CompiledData::Unit *CompilationUnitMapper::open(const QString &cacheFileName, const QString &sourcePath, QString *errorString)
+{
+ close();
+
+ // ### TODO: fix up file encoding/normalization/unc handling once QFileSystemEntry
+ // is exported from QtCore.
+ HANDLE handle =
+#if defined(Q_OS_WINRT)
+ CreateFile2(reinterpret_cast<const wchar_t*>(cacheFileName.constData()),
+ GENERIC_READ | GENERIC_EXECUTE, FILE_SHARE_READ,
+ OPEN_EXISTING, nullptr);
+#else
+ CreateFile(reinterpret_cast<const wchar_t*>(cacheFileName.constData()),
+ GENERIC_READ | GENERIC_EXECUTE, FILE_SHARE_READ,
+ nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
+ nullptr);
+#endif
+ if (handle == INVALID_HANDLE_VALUE) {
+ *errorString = qt_error_string(GetLastError());
+ return nullptr;
+ }
+
+ QDeferredCleanup fileHandleCleanup([handle]{
+ CloseHandle(handle);
+ });
+
+#if !defined(Q_OS_WINRT) || _MSC_VER >= 1900
+ CompiledData::Unit header;
+ DWORD bytesRead;
+ if (!ReadFile(handle, reinterpret_cast<char *>(&header), sizeof(header), &bytesRead, nullptr)) {
+ *errorString = qt_error_string(GetLastError());
+ return nullptr;
+ }
+
+ if (bytesRead != sizeof(header)) {
+ *errorString = QStringLiteral("File too small for the header fields");
+ return nullptr;
+ }
+
+ if (!verifyHeader(&header, sourcePath, errorString))
+ return nullptr;
+
+ const uint mappingFlags = header.flags & QV4::CompiledData::Unit::ContainsMachineCode
+ ? PAGE_EXECUTE_READ : PAGE_READONLY;
+ const uint viewFlags = header.flags & QV4::CompiledData::Unit::ContainsMachineCode
+ ? (FILE_MAP_READ | FILE_MAP_EXECUTE) : FILE_MAP_READ;
+
+ // Data structure and qt version matched, so now we can access the rest of the file safely.
+
+ HANDLE fileMappingHandle = CreateFileMapping(handle, 0, mappingFlags, 0, 0, 0);
+ if (!fileMappingHandle) {
+ *errorString = qt_error_string(GetLastError());
+ return nullptr;
+ }
+
+ QDeferredCleanup mappingCleanup([fileMappingHandle]{
+ CloseHandle(fileMappingHandle);
+ });
+
+ dataPtr = MapViewOfFile(fileMappingHandle, viewFlags, 0, 0, 0);
+ if (!dataPtr) {
+ *errorString = qt_error_string(GetLastError());
+ return nullptr;
+ }
+
+ return reinterpret_cast<CompiledData::Unit*>(dataPtr);
+#else
+ Q_UNUSED(sourcePath);
+ *errorString = QStringLiteral("Compilation unit mapping not supported on WinRT 8.1");
+ return nullptr;
+#endif
+}
+
+void CompilationUnitMapper::close()
+{
+#if !defined(Q_OS_WINRT) || _MSC_VER >= 1900
+ if (dataPtr != nullptr)
+ UnmapViewOfFile(dataPtr);
+#endif
+ dataPtr = nullptr;
+}
+
+QT_END_NAMESPACE
diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp
index a63f35152a..e815c41a86 100644
--- a/src/qml/compiler/qv4compileddata.cpp
+++ b/src/qml/compiler/qv4compileddata.cpp
@@ -47,12 +47,30 @@
#include <private/qv4lookup_p.h>
#include <private/qv4regexpobject_p.h>
#include <private/qqmlpropertycache_p.h>
+#include <private/qqmltypeloader_p.h>
+#include <private/qqmlengine_p.h>
+#include "qv4compilationunitmapper_p.h"
+#include <QQmlPropertyMap>
+#include <QDateTime>
+#include <QSaveFile>
+#include <QFile>
+#include <QFileInfo>
+#include <QScopedValueRollback>
+#include <QStandardPaths>
+#include <QDir>
#endif
#include <private/qqmlirbuilder_p.h>
#include <QCoreApplication>
+#include <QCryptographicHash>
#include <algorithm>
+#if defined(QT_BUILD_INTERNAL)
+#if defined(Q_OS_UNIX) && !defined(QT_NO_DYNAMIC_CAST)
+#include <dlfcn.h>
+#endif
+#endif
+
QT_BEGIN_NAMESPACE
namespace QV4 {
@@ -67,11 +85,20 @@ CompilationUnit::CompilationUnit()
, runtimeLookups(0)
, runtimeRegularExpressions(0)
, runtimeClasses(0)
+ , totalBindingsCount(0)
+ , totalParserStatusCount(0)
+ , totalObjectCount(0)
+ , metaTypeId(-1)
+ , listMetaTypeId(-1)
+ , isRegisteredWithEngine(false)
{}
CompilationUnit::~CompilationUnit()
{
unlink();
+ if (data && !(data->flags & QV4::CompiledData::Unit::StaticData))
+ free(const_cast<Unit *>(data));
+ data = 0;
}
QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine)
@@ -109,7 +136,7 @@ QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine)
for (uint i = 0; i < data->lookupTableSize; ++i) {
QV4::Lookup *l = runtimeLookups + i;
- Lookup::Type type = Lookup::Type(compiledLookups[i].type_and_flags);
+ Lookup::Type type = Lookup::Type(uint(compiledLookups[i].type_and_flags));
if (type == CompiledData::Lookup::Type_Getter)
l->getter = QV4::Lookup::getterGeneric;
else if (type == CompiledData::Lookup::Type_Setter)
@@ -144,14 +171,18 @@ QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine)
}
}
- linkBackendToEngine(engine);
-
-#if 0
- runtimeFunctionsSortedByAddress.resize(runtimeFunctions.size());
- memcpy(runtimeFunctionsSortedByAddress.data(), runtimeFunctions.data(), runtimeFunctions.size() * sizeof(QV4::Function*));
- std::sort(runtimeFunctionsSortedByAddress.begin(), runtimeFunctionsSortedByAddress.end(), functionSortHelper);
+#if Q_BYTE_ORDER == Q_BIG_ENDIAN
+ Value *bigEndianConstants = new Value[data->constantTableSize];
+ const LEUInt64 *littleEndianConstants = data->constants();
+ for (uint i = 0; i < data->constantTableSize; ++i)
+ bigEndianConstants[i] = Value::fromReturnedValue(littleEndianConstants[i]);
+ constants = bigEndianConstants;
+#else
+ constants = reinterpret_cast<const Value*>(data->constants());
#endif
+ linkBackendToEngine(engine);
+
if (data->indexOfRootFunction != -1)
return runtimeFunctions[data->indexOfRootFunction];
else
@@ -162,10 +193,26 @@ void CompilationUnit::unlink()
{
if (engine)
engine->compilationUnits.erase(engine->compilationUnits.find(this));
+
+ if (isRegisteredWithEngine) {
+ Q_ASSERT(data && quint32(propertyCaches.count()) > data->indexOfRootObject && propertyCaches.at(data->indexOfRootObject));
+ QQmlEnginePrivate *qmlEngine = QQmlEnginePrivate::get(propertyCaches.at(data->indexOfRootObject)->engine);
+ qmlEngine->unregisterInternalCompositeType(this);
+ isRegisteredWithEngine = false;
+ }
+
+ propertyCaches.clear();
+
+ for (int ii = 0; ii < dependentScripts.count(); ++ii)
+ dependentScripts.at(ii)->release();
+ dependentScripts.clear();
+
+ importCache = nullptr;
+
+ qDeleteAll(resolvedTypes);
+ resolvedTypes.clear();
+
engine = 0;
- if (data && !(data->flags & QV4::CompiledData::Unit::StaticData))
- free(data);
- data = 0;
free(runtimeStrings);
runtimeStrings = 0;
delete [] runtimeLookups;
@@ -176,6 +223,9 @@ void CompilationUnit::unlink()
runtimeClasses = 0;
qDeleteAll(runtimeFunctions);
runtimeFunctions.clear();
+#if Q_BYTE_ORDER == Q_BIG_ENDIAN
+ delete [] constants;
+#endif
}
void CompilationUnit::markObjects(QV4::ExecutionEngine *e)
@@ -189,6 +239,219 @@ void CompilationUnit::markObjects(QV4::ExecutionEngine *e)
}
}
+void CompilationUnit::destroy()
+{
+ QQmlEngine *qmlEngine = 0;
+ if (engine && engine->v8Engine)
+ qmlEngine = engine->v8Engine->engine();
+ if (qmlEngine)
+ QQmlEnginePrivate::deleteInEngineThread(qmlEngine, this);
+ else
+ delete this;
+}
+
+IdentifierHash<int> CompilationUnit::namedObjectsPerComponent(int componentObjectIndex)
+{
+ auto it = namedObjectsPerComponentCache.find(componentObjectIndex);
+ if (it == namedObjectsPerComponentCache.end()) {
+ IdentifierHash<int> namedObjectCache(engine);
+ const CompiledData::Object *component = data->objectAt(componentObjectIndex);
+ const LEUInt32 *namedObjectIndexPtr = component->namedObjectsInComponentTable();
+ for (quint32 i = 0; i < component->nNamedObjectsInComponent; ++i, ++namedObjectIndexPtr) {
+ const CompiledData::Object *namedObject = data->objectAt(*namedObjectIndexPtr);
+ namedObjectCache.add(runtimeStrings[namedObject->idNameIndex], namedObject->id);
+ }
+ it = namedObjectsPerComponentCache.insert(componentObjectIndex, namedObjectCache);
+ }
+ return *it;
+}
+
+void CompilationUnit::finalize(QQmlEnginePrivate *engine)
+{
+ // Add to type registry of composites
+ if (propertyCaches.needsVMEMetaObject(data->indexOfRootObject))
+ engine->registerInternalCompositeType(this);
+ else {
+ const QV4::CompiledData::Object *obj = objectAt(data->indexOfRootObject);
+ auto *typeRef = resolvedTypes.value(obj->inheritedTypeNameIndex);
+ Q_ASSERT(typeRef);
+ if (typeRef->compilationUnit) {
+ metaTypeId = typeRef->compilationUnit->metaTypeId;
+ listMetaTypeId = typeRef->compilationUnit->listMetaTypeId;
+ } else {
+ metaTypeId = typeRef->type->typeId();
+ listMetaTypeId = typeRef->type->qListTypeId();
+ }
+ }
+
+ // Collect some data for instantiation later.
+ int bindingCount = 0;
+ int parserStatusCount = 0;
+ int objectCount = 0;
+ for (quint32 i = 0; i < data->nObjects; ++i) {
+ const QV4::CompiledData::Object *obj = data->objectAt(i);
+ bindingCount += obj->nBindings;
+ if (auto *typeRef = resolvedTypes.value(obj->inheritedTypeNameIndex)) {
+ if (QQmlType *qmlType = typeRef->type) {
+ if (qmlType->parserStatusCast() != -1)
+ ++parserStatusCount;
+ }
+ ++objectCount;
+ if (typeRef->compilationUnit) {
+ bindingCount += typeRef->compilationUnit->totalBindingsCount;
+ parserStatusCount += typeRef->compilationUnit->totalParserStatusCount;
+ objectCount += typeRef->compilationUnit->totalObjectCount;
+ }
+ }
+ }
+
+ totalBindingsCount = bindingCount;
+ totalParserStatusCount = parserStatusCount;
+ totalObjectCount = objectCount;
+}
+
+bool CompilationUnit::verifyChecksum(QQmlEngine *engine,
+ const ResolvedTypeReferenceMap &dependentTypes) const
+{
+ if (dependentTypes.isEmpty()) {
+ for (size_t i = 0; i < sizeof(data->dependencyMD5Checksum); ++i) {
+ if (data->dependencyMD5Checksum[i] != 0)
+ return false;
+ }
+ return true;
+ }
+ QCryptographicHash hash(QCryptographicHash::Md5);
+ if (!dependentTypes.addToHash(&hash, engine))
+ return false;
+ QByteArray checksum = hash.result();
+ Q_ASSERT(checksum.size() == sizeof(data->dependencyMD5Checksum));
+ return memcmp(data->dependencyMD5Checksum, checksum.constData(),
+ sizeof(data->dependencyMD5Checksum)) == 0;
+}
+
+static QString cacheFilePath(const QUrl &url)
+{
+ const QString localSourcePath = QQmlFile::urlToLocalFileOrQrc(url);
+ const QString localCachePath = localSourcePath + QLatin1Char('c');
+ if (QFileInfo(QFileInfo(localSourcePath).dir().absolutePath()).isWritable())
+ return localCachePath;
+ QCryptographicHash fileNameHash(QCryptographicHash::Sha1);
+ fileNameHash.addData(localSourcePath.toUtf8());
+ QString directory = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + QLatin1String("/qmlcache/");
+ QDir::root().mkpath(directory);
+ return directory + QString::fromUtf8(fileNameHash.result().toHex()) + QLatin1Char('.') + QFileInfo(localCachePath).completeSuffix();
+}
+
+bool CompilationUnit::saveToDisk(const QUrl &unitUrl, QString *errorString)
+{
+ errorString->clear();
+
+ if (data->sourceTimeStamp == 0) {
+ *errorString = QStringLiteral("Missing time stamp for source file");
+ return false;
+ }
+
+ if (!QQmlFile::isLocalFile(unitUrl)) {
+ *errorString = QStringLiteral("File has to be a local file.");
+ return false;
+ }
+
+ // Foo.qml -> Foo.qmlc
+ QSaveFile cacheFile(cacheFilePath(unitUrl));
+ if (!cacheFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
+ *errorString = cacheFile.errorString();
+ return false;
+ }
+
+ QByteArray modifiedUnit;
+ modifiedUnit.resize(data->unitSize);
+ memcpy(modifiedUnit.data(), data, data->unitSize);
+ const char *dataPtr = modifiedUnit.data();
+ Unit *unitPtr;
+ memcpy(&unitPtr, &dataPtr, sizeof(unitPtr));
+ unitPtr->flags |= Unit::StaticData;
+
+ prepareCodeOffsetsForDiskStorage(unitPtr);
+
+ qint64 headerWritten = cacheFile.write(modifiedUnit);
+ if (headerWritten != modifiedUnit.size()) {
+ *errorString = cacheFile.errorString();
+ return false;
+ }
+
+ if (!saveCodeToDisk(&cacheFile, unitPtr, errorString))
+ return false;
+
+ if (!cacheFile.commit()) {
+ *errorString = cacheFile.errorString();
+ return false;
+ }
+
+ return true;
+}
+
+bool CompilationUnit::loadFromDisk(const QUrl &url, EvalISelFactory *iselFactory, QString *errorString)
+{
+ if (!QQmlFile::isLocalFile(url)) {
+ *errorString = QStringLiteral("File has to be a local file.");
+ return false;
+ }
+
+ const QString sourcePath = url.toLocalFile();
+ QScopedPointer<CompilationUnitMapper> cacheFile(new CompilationUnitMapper());
+
+ CompiledData::Unit *mappedUnit = cacheFile->open(cacheFilePath(url), sourcePath, errorString);
+ if (!mappedUnit)
+ return false;
+
+ const Unit * const oldDataPtr = (data && !(data->flags & QV4::CompiledData::Unit::StaticData)) ? data : nullptr;
+ QScopedValueRollback<const Unit *> dataPtrChange(data, mappedUnit);
+
+ {
+ const QString foundArchitecture = stringAt(data->architectureIndex);
+ const QString expectedArchitecture = QSysInfo::buildAbi();
+ if (foundArchitecture != expectedArchitecture) {
+ *errorString = QString::fromUtf8("Architecture mismatch. Found %1 expected %2").arg(foundArchitecture).arg(expectedArchitecture);
+ return false;
+ }
+ }
+
+ {
+ const QString foundCodeGenerator = stringAt(data->codeGeneratorIndex);
+ const QString expectedCodeGenerator = iselFactory->codeGeneratorName;
+ if (foundCodeGenerator != expectedCodeGenerator) {
+ *errorString = QString::fromUtf8("Code generator mismatch. Found code generated by %1 but expected %2").arg(foundCodeGenerator).arg(expectedCodeGenerator);
+ return false;
+ }
+ }
+
+ if (!memoryMapCode(errorString))
+ return false;
+
+ dataPtrChange.commit();
+ free(const_cast<Unit*>(oldDataPtr));
+ backingFile.reset(cacheFile.take());
+ return true;
+}
+
+void CompilationUnit::prepareCodeOffsetsForDiskStorage(Unit *unit)
+{
+ Q_UNUSED(unit);
+}
+
+bool CompilationUnit::saveCodeToDisk(QIODevice *device, const Unit *unit, QString *errorString)
+{
+ Q_UNUSED(device);
+ Q_UNUSED(unit);
+ *errorString = QStringLiteral("Saving code to disk is not supported in this configuration");
+ return false;
+}
+
+bool CompilationUnit::memoryMapCode(QString *errorString)
+{
+ *errorString = QStringLiteral("Missing code mapping backend");
+ return false;
+}
#endif // V4_BOOTSTRAP
Unit *CompilationUnit::createUnitData(QmlIR::Document *irDocument)
@@ -205,7 +468,7 @@ QString Binding::valueAsString(const Unit *unit) const
case Type_Boolean:
return value.b ? QStringLiteral("true") : QStringLiteral("false");
case Type_Number:
- return QString::number(value.d);
+ return QString::number(valueAsNumber());
case Type_Invalid:
return QString();
#ifdef QT_NO_TRANSLATION
@@ -287,6 +550,130 @@ QString Binding::valueAsScriptString(const Unit *unit) const
return valueAsString(unit);
}
+#ifndef V4_BOOTSTRAP
+/*!
+Returns the property cache, if one alread exists. The cache is not referenced.
+*/
+QQmlPropertyCache *ResolvedTypeReference::propertyCache() const
+{
+ if (type)
+ return typePropertyCache;
+ else
+ return compilationUnit->rootPropertyCache();
+}
+
+/*!
+Returns the property cache, creating one if it doesn't already exist. The cache is not referenced.
+*/
+QQmlPropertyCache *ResolvedTypeReference::createPropertyCache(QQmlEngine *engine)
+{
+ if (typePropertyCache) {
+ return typePropertyCache;
+ } else if (type) {
+ typePropertyCache = QQmlEnginePrivate::get(engine)->cache(type->metaObject());
+ return typePropertyCache;
+ } else {
+ return compilationUnit->rootPropertyCache();
+ }
+}
+
+bool ResolvedTypeReference::addToHash(QCryptographicHash *hash, QQmlEngine *engine)
+{
+ if (type) {
+ bool ok = false;
+ hash->addData(createPropertyCache(engine)->checksum(&ok));
+ return ok;
+ }
+ hash->addData(compilationUnit->data->md5Checksum, sizeof(compilationUnit->data->md5Checksum));
+ return true;
+}
+
+template <typename T>
+bool qtTypeInherits(const QMetaObject *mo) {
+ while (mo) {
+ if (mo == &T::staticMetaObject)
+ return true;
+ mo = mo->superClass();
+ }
+ return false;
+}
+
+void ResolvedTypeReference::doDynamicTypeCheck()
+{
+ const QMetaObject *mo = 0;
+ if (typePropertyCache)
+ mo = typePropertyCache->firstCppMetaObject();
+ else if (type)
+ mo = type->metaObject();
+ else if (compilationUnit)
+ mo = compilationUnit->rootPropertyCache()->firstCppMetaObject();
+ isFullyDynamicType = qtTypeInherits<QQmlPropertyMap>(mo);
+}
+
+#if defined(QT_BUILD_INTERNAL)
+
+static QByteArray ownLibraryChecksum()
+{
+ static QByteArray libraryChecksum;
+ static bool checksumInitialized = false;
+ if (checksumInitialized)
+ return libraryChecksum;
+ checksumInitialized = true;
+#if defined(Q_OS_UNIX) && !defined(QT_NO_DYNAMIC_CAST)
+ Dl_info libInfo;
+ if (dladdr(reinterpret_cast<const void *>(&ownLibraryChecksum), &libInfo) != 0) {
+ QFile library(QFile::decodeName(libInfo.dli_fname));
+ if (library.open(QIODevice::ReadOnly)) {
+ QCryptographicHash hash(QCryptographicHash::Sha1);
+ hash.addData(&library);
+ libraryChecksum = hash.result();
+ }
+ }
+#else
+ // Not implemented.
+#endif
+ return libraryChecksum;
+}
+
+#endif
+
+bool ResolvedTypeReferenceMap::addToHash(QCryptographicHash *hash, QQmlEngine *engine) const
+{
+ for (auto it = constBegin(), end = constEnd(); it != end; ++it) {
+ if (!it.value()->addToHash(hash, engine))
+ return false;
+ }
+
+ // This is a bit of a hack to make development easier. When hacking on the code generator
+ // the cache files may end up being re-used. To avoid that we also add the checksum of
+ // the QtQml library.
+#if defined(QT_BUILD_INTERNAL)
+ hash->addData(ownLibraryChecksum());
+#endif
+
+ return true;
+}
+
+#endif
+
+void Unit::generateChecksum()
+{
+#ifndef V4_BOOTSTRAP
+ QCryptographicHash hash(QCryptographicHash::Md5);
+
+ const int checksummableDataOffset = qOffsetOf(QV4::CompiledData::Unit, md5Checksum) + sizeof(md5Checksum);
+
+ const char *dataPtr = reinterpret_cast<const char *>(this) + checksummableDataOffset;
+ hash.addData(dataPtr, unitSize - checksummableDataOffset);
+
+ QByteArray checksum = hash.result();
+ Q_ASSERT(checksum.size() == sizeof(md5Checksum));
+ memcpy(md5Checksum, checksum.constData(), sizeof(md5Checksum));
+#else
+ memset(md5Checksum, 0, sizeof(md5Checksum));
+#endif
+}
+
}
}
diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h
index 8c617875e0..90cbe04505 100644
--- a/src/qml/compiler/qv4compileddata_p.h
+++ b/src/qml/compiler/qv4compileddata_p.h
@@ -60,11 +60,26 @@
#include <private/qv4executableallocator_p.h>
#include <private/qqmlrefcount_p.h>
#include <private/qqmlnullablevalue_p.h>
+#include <private/qv4identifier_p.h>
+#include <private/qflagpointer_p.h>
+#include <private/qjson_p.h>
+#ifndef V4_BOOTSTRAP
+#include <private/qqmltypenamecache_p.h>
+#include <private/qqmlpropertycache_p.h>
+#endif
QT_BEGIN_NAMESPACE
+// Bump this whenever the compiler data structures change in an incompatible way.
+#define QV4_DATA_STRUCTURE_VERSION 0x07
+
+class QIODevice;
class QQmlPropertyCache;
class QQmlPropertyData;
+class QQmlTypeNameCache;
+class QQmlScriptData;
+class QQmlType;
+class QQmlEngine;
namespace QmlIR {
struct Document;
@@ -76,25 +91,49 @@ struct Function;
}
struct Function;
+class EvalISelFactory;
+class CompilationUnitMapper;
namespace CompiledData {
+typedef QJsonPrivate::q_littleendian<qint16> LEInt16;
+typedef QJsonPrivate::q_littleendian<quint16> LEUInt16;
+typedef QJsonPrivate::q_littleendian<quint32> LEUInt32;
+typedef QJsonPrivate::q_littleendian<qint32> LEInt32;
+typedef QJsonPrivate::q_littleendian<quint64> LEUInt64;
+typedef QJsonPrivate::q_littleendian<qint64> LEInt64;
+
struct String;
struct Function;
struct Lookup;
struct RegExp;
struct Unit;
+template <typename ItemType, typename Container, const ItemType *(Container::*IndexedGetter)(int index) const>
+struct TableIterator
+{
+ TableIterator(const Container *container, int index) : container(container), index(index) {}
+ const Container *container;
+ int index;
+
+ const ItemType *operator->() { return (container->*IndexedGetter)(index); }
+ void operator++() { ++index; }
+ bool operator==(const TableIterator &rhs) const { return index == rhs.index; }
+ bool operator!=(const TableIterator &rhs) const { return index != rhs.index; }
+};
+
#if defined(Q_CC_MSVC) || defined(Q_CC_GNU)
#pragma pack(push, 1)
#endif
struct Location
{
- qint32 line;
- qint32 column;
+ union {
+ QJsonPrivate::qle_bitfield<0, 20> line;
+ QJsonPrivate::qle_bitfield<20, 12> column;
+ };
- Location(): line(-1), column(-1) {}
+ Location() { line = 0; column = 0; }
inline bool operator<(const Location &other) const {
return line < other.line ||
@@ -104,20 +143,22 @@ struct Location
struct RegExp
{
- enum Flags {
+ enum Flags : unsigned int {
RegExp_Global = 0x01,
RegExp_IgnoreCase = 0x02,
RegExp_Multiline = 0x04
};
- quint32 flags;
- quint32 stringIndex;
+ union {
+ QJsonPrivate::qle_bitfield<0, 4> flags;
+ QJsonPrivate::qle_bitfield<4, 28> stringIndex;
+ };
- static int calculateSize() { return sizeof(RegExp); }
+ RegExp() { flags = 0; stringIndex = 0; }
};
struct Lookup
{
- enum Type {
+ enum Type : unsigned int {
Type_Getter = 0x0,
Type_Setter = 0x1,
Type_GlobalGetter = 2,
@@ -125,21 +166,27 @@ struct Lookup
Type_IndexedSetter = 4
};
- quint32 type_and_flags;
- quint32 nameIndex;
+ union {
+ QJsonPrivate::qle_bitfield<0, 4> type_and_flags;
+ QJsonPrivate::qle_bitfield<4, 28> nameIndex;
+ };
- static int calculateSize() { return sizeof(Lookup); }
+ Lookup() { type_and_flags = 0; nameIndex = 0; }
};
struct JSClassMember
{
- uint nameOffset : 31;
- uint isAccessor : 1;
+ union {
+ QJsonPrivate::qle_bitfield<0, 31> nameOffset;
+ QJsonPrivate::qle_bitfield<31, 1> isAccessor;
+ };
+
+ JSClassMember() { nameOffset = 0; isAccessor = 0; }
};
struct JSClass
{
- uint nMembers;
+ LEUInt32 nMembers;
// JSClassMember[nMembers]
static int calculateSize(int nMembers) { return (sizeof(JSClass) + nMembers * sizeof(JSClassMember) + 7) & ~7; }
@@ -147,8 +194,7 @@ struct JSClass
struct String
{
- quint32 flags; // isArrayIndex
- qint32 size;
+ LEInt32 size;
// uint16 strdata[]
static int calculateSize(const QString &str) {
@@ -156,9 +202,11 @@ struct String
}
};
+// Function is aligned on an 8-byte boundary to make sure there are no bus errors or penalties
+// for unaligned access. The ordering of the fields is also from largest to smallest.
struct Function
{
- enum Flags {
+ enum Flags : unsigned int {
HasDirectEval = 0x1,
UsesArgumentsObject = 0x2,
IsStrict = 0x4,
@@ -166,24 +214,26 @@ struct Function
HasCatchOrWith = 0x10
};
- quint32 index; // in CompilationUnit's function table
- quint32 nameIndex;
- qint64 flags;
- quint32 nFormals;
- quint32 formalsOffset;
- quint32 nLocals;
- quint32 localsOffset;
- quint32 nInnerFunctions;
- quint32 innerFunctionsOffset;
+ // Absolute offset into file where the code for this function is located. Only used when the function
+ // is serialized.
+ LEUInt64 codeOffset;
+ LEUInt64 codeSize;
+
+ LEUInt32 nameIndex;
+ LEUInt32 nFormals;
+ LEUInt32 formalsOffset;
+ LEUInt32 nLocals;
+ LEUInt32 localsOffset;
+ LEUInt32 nInnerFunctions;
Location location;
// Qml Extensions Begin
- quint32 nDependingIdObjects;
- quint32 dependingIdObjectsOffset; // Array of resolved ID objects
- quint32 nDependingContextProperties;
- quint32 dependingContextPropertiesOffset; // Array of int pairs (property index and notify index)
- quint32 nDependingScopeProperties;
- quint32 dependingScopePropertiesOffset; // Array of int pairs (property index and notify index)
+ LEUInt32 nDependingIdObjects;
+ LEUInt32 dependingIdObjectsOffset; // Array of resolved ID objects
+ LEUInt32 nDependingContextProperties;
+ LEUInt32 dependingContextPropertiesOffset; // Array of int pairs (property index and notify index)
+ LEUInt32 nDependingScopeProperties;
+ LEUInt32 dependingScopePropertiesOffset; // Array of int pairs (property index and notify index)
// Qml Extensions End
// quint32 formalsIndex[nFormals]
@@ -191,11 +241,19 @@ struct Function
// quint32 offsetForInnerFunctions[nInnerFunctions]
// Function[nInnerFunctions]
- const quint32 *formalsTable() const { return reinterpret_cast<const quint32 *>(reinterpret_cast<const char *>(this) + formalsOffset); }
- const quint32 *localsTable() const { return reinterpret_cast<const quint32 *>(reinterpret_cast<const char *>(this) + localsOffset); }
- const quint32 *qmlIdObjectDependencyTable() const { return reinterpret_cast<const quint32 *>(reinterpret_cast<const char *>(this) + dependingIdObjectsOffset); }
- const quint32 *qmlContextPropertiesDependencyTable() const { return reinterpret_cast<const quint32 *>(reinterpret_cast<const char *>(this) + dependingContextPropertiesOffset); }
- const quint32 *qmlScopePropertiesDependencyTable() const { return reinterpret_cast<const quint32 *>(reinterpret_cast<const char *>(this) + dependingScopePropertiesOffset); }
+ // Keep all unaligned data at the end
+ quint8 flags;
+
+ const LEUInt32 *formalsTable() const { return reinterpret_cast<const LEUInt32 *>(reinterpret_cast<const char *>(this) + formalsOffset); }
+ const LEUInt32 *localsTable() const { return reinterpret_cast<const LEUInt32 *>(reinterpret_cast<const char *>(this) + localsOffset); }
+ const LEUInt32 *qmlIdObjectDependencyTable() const { return reinterpret_cast<const LEUInt32 *>(reinterpret_cast<const char *>(this) + dependingIdObjectsOffset); }
+ const LEUInt32 *qmlContextPropertiesDependencyTable() const { return reinterpret_cast<const LEUInt32 *>(reinterpret_cast<const char *>(this) + dependingContextPropertiesOffset); }
+ const LEUInt32 *qmlScopePropertiesDependencyTable() const { return reinterpret_cast<const LEUInt32 *>(reinterpret_cast<const char *>(this) + dependingScopePropertiesOffset); }
+
+ // --- QQmlPropertyCacheCreator interface
+ const LEUInt32 *formalsBegin() const { return formalsTable(); }
+ const LEUInt32 *formalsEnd() const { return formalsTable() + nFormals; }
+ // ---
inline bool hasQmlDependencies() const { return nDependingIdObjects > 0 || nDependingContextProperties > 0 || nDependingScopeProperties > 0; }
@@ -207,15 +265,15 @@ struct Function
// Qml data structures
struct Q_QML_EXPORT TranslationData {
- quint32 commentIndex;
- int number;
+ LEUInt32 commentIndex;
+ LEInt32 number;
};
struct Q_QML_PRIVATE_EXPORT Binding
{
- quint32 propertyNameIndex;
+ LEUInt32 propertyNameIndex;
- enum ValueType {
+ enum ValueType : unsigned int {
Type_Invalid,
Type_Boolean,
Type_Number,
@@ -228,26 +286,30 @@ struct Q_QML_PRIVATE_EXPORT Binding
Type_GroupProperty
};
- enum Flags {
+ enum Flags : unsigned int {
IsSignalHandlerExpression = 0x1,
IsSignalHandlerObject = 0x2,
IsOnAssignment = 0x4,
InitializerForReadOnlyDeclaration = 0x8,
IsResolvedEnum = 0x10,
IsListItem = 0x20,
- IsBindingToAlias = 0x40
+ IsBindingToAlias = 0x40,
+ IsDeferredBinding = 0x80,
+ IsCustomParserBinding = 0x100,
};
- quint32 flags : 16;
- quint32 type : 16;
+ union {
+ QJsonPrivate::qle_bitfield<0, 16> flags;
+ QJsonPrivate::qle_bitfield<16, 16> type;
+ };
union {
bool b;
- double d;
- quint32 compiledScriptIndex; // used when Type_Script
- quint32 objectIndex;
+ quint64 doubleValue; // do not access directly, needs endian protected access
+ LEUInt32 compiledScriptIndex; // used when Type_Script
+ LEUInt32 objectIndex;
TranslationData translationData; // used when Type_Translation
} value;
- quint32 stringIndex; // Set for Type_String, Type_Translation and Type_Script (the latter because of script strings)
+ LEUInt32 stringIndex; // Set for Type_String, Type_Translation and Type_Script (the latter because of script strings)
Location location;
Location valueLocation;
@@ -307,11 +369,20 @@ struct Q_QML_PRIVATE_EXPORT Binding
QString valueAsScriptString(const Unit *unit) const;
double valueAsNumber() const
{
- if (type == Type_Number)
- return value.d;
- return 0.0;
-
+ if (type != Type_Number)
+ return 0.0;
+ quint64 intval = qFromLittleEndian<quint64>(value.doubleValue);
+ double d;
+ memcpy(&d, &intval, sizeof(double));
+ return d;
}
+ void setNumberValueInternal(double d)
+ {
+ quint64 intval;
+ memcpy(&intval, &d, sizeof(double));
+ value.doubleValue = qToLittleEndian<quint64>(intval);
+ }
+
bool valueAsBoolean() const
{
if (type == Type_Boolean)
@@ -323,17 +394,16 @@ struct Q_QML_PRIVATE_EXPORT Binding
struct Parameter
{
- quint32 nameIndex;
- quint32 type;
- quint32 customTypeNameIndex;
- quint32 reserved;
+ LEUInt32 nameIndex;
+ LEUInt32 type;
+ LEUInt32 customTypeNameIndex;
Location location;
};
struct Signal
{
- quint32 nameIndex;
- quint32 nParameters;
+ LEUInt32 nameIndex;
+ LEUInt32 nParameters;
Location location;
// Parameter parameters[1];
@@ -346,47 +416,95 @@ struct Signal
+ nParameters * sizeof(Parameter)
+ 7) & ~0x7;
}
+
+ // --- QQmlPropertyCacheCceatorInterface
+ const Parameter *parametersBegin() const { return parameterAt(0); }
+ const Parameter *parametersEnd() const { return parameterAt(nParameters); }
+ int parameterCount() const { return nParameters; }
+ // ---
};
struct Property
{
- enum Type { Var = 0, Variant, Int, Bool, Real, String, Url, Color,
+ enum Type : unsigned int { Var = 0, Variant, Int, Bool, Real, String, Url, Color,
Font, Time, Date, DateTime, Rect, Point, Size,
Vector2D, Vector3D, Vector4D, Matrix4x4, Quaternion,
- Alias, Custom, CustomList };
+ Custom, CustomList };
- enum Flags {
+ enum Flags : unsigned int {
IsReadOnly = 0x1
};
- quint32 nameIndex;
- quint32 type;
+ LEUInt32 nameIndex;
union {
- quint32 customTypeNameIndex; // If type >= Custom
- quint32 aliasIdValueIndex; // If type == Alias
+ QJsonPrivate::qle_bitfield<0, 31> type;
+ QJsonPrivate::qle_bitfield<31, 1> flags; // readonly
};
- quint32 aliasPropertyValueIndex;
- quint32 flags; // readonly
+ LEUInt32 customTypeNameIndex; // If type >= Custom
Location location;
- Location aliasLocation; // If type == Alias
+};
+
+struct Alias {
+ enum Flags : unsigned int {
+ IsReadOnly = 0x1,
+ Resolved = 0x2,
+ AliasPointsToPointerObject = 0x4
+ };
+ union {
+ QJsonPrivate::qle_bitfield<0, 29> nameIndex;
+ QJsonPrivate::qle_bitfield<29, 3> flags;
+ };
+ union {
+ LEUInt32 idIndex; // string index
+ QJsonPrivate::qle_bitfield<0, 31> targetObjectId; // object id index (in QQmlContextData::idValues)
+ QJsonPrivate::qle_bitfield<31, 1> aliasToLocalAlias;
+ };
+ union {
+ LEUInt32 propertyNameIndex; // string index
+ LEInt32 encodedMetaPropertyIndex;
+ LEUInt32 localAliasIndex; // index in list of aliases local to the object (if targetObjectId == objectId)
+ };
+ Location location;
+ Location referenceLocation;
+
+ bool isObjectAlias() const {
+ Q_ASSERT(flags & Resolved);
+ return encodedMetaPropertyIndex == -1;
+ }
};
struct Object
{
+ enum Flags : unsigned int {
+ NoFlag = 0x0,
+ IsComponent = 0x1, // object was identified to be an explicit or implicit component boundary
+ HasDeferredBindings = 0x2, // any of the bindings are deferred
+ HasCustomParserBindings = 0x4
+ };
+
// Depending on the use, this may be the type name to instantiate before instantiating this
// object. For grouped properties the type name will be empty and for attached properties
// it will be the name of the attached type.
- quint32 inheritedTypeNameIndex;
- quint32 idIndex;
- qint32 indexOfDefaultProperty; // -1 means no default property declared in this object
- quint32 nFunctions;
- quint32 offsetToFunctions;
- quint32 nProperties;
- quint32 offsetToProperties;
- quint32 nSignals;
- quint32 offsetToSignals; // which in turn will be a table with offsets to variable-sized Signal objects
- quint32 nBindings;
- quint32 offsetToBindings;
+ LEUInt32 inheritedTypeNameIndex;
+ LEUInt32 idNameIndex;
+ union {
+ QJsonPrivate::qle_bitfield<0, 15> flags;
+ QJsonPrivate::qle_bitfield<15, 1> defaultPropertyIsAlias;
+ QJsonPrivate::qle_signedbitfield<16, 16> id;
+ };
+ LEInt32 indexOfDefaultPropertyOrAlias; // -1 means no default property declared in this object
+ LEUInt32 nFunctions;
+ LEUInt32 offsetToFunctions;
+ LEUInt32 nProperties;
+ LEUInt32 offsetToProperties;
+ LEUInt32 nAliases;
+ LEUInt32 offsetToAliases;
+ LEUInt32 nSignals;
+ LEUInt32 offsetToSignals; // which in turn will be a table with offsets to variable-sized Signal objects
+ LEUInt32 nBindings;
+ LEUInt32 offsetToBindings;
+ LEUInt32 nNamedObjectsInComponent;
+ LEUInt32 offsetToNamedObjectsInComponent;
Location location;
Location locationOfIdProperty;
// Function[]
@@ -394,20 +512,22 @@ struct Object
// Signal[]
// Binding[]
- static int calculateSizeExcludingSignals(int nFunctions, int nProperties, int nSignals, int nBindings)
+ static int calculateSizeExcludingSignals(int nFunctions, int nProperties, int nAliases, int nSignals, int nBindings, int nNamedObjectsInComponent)
{
return ( sizeof(Object)
+ nFunctions * sizeof(quint32)
+ nProperties * sizeof(Property)
+ + nAliases * sizeof(Alias)
+ nSignals * sizeof(quint32)
+ nBindings * sizeof(Binding)
+ + nNamedObjectsInComponent * sizeof(int)
+ 0x7
) & ~0x7;
}
- const quint32 *functionOffsetTable() const
+ const LEUInt32 *functionOffsetTable() const
{
- return reinterpret_cast<const quint32*>(reinterpret_cast<const char *>(this) + offsetToFunctions);
+ return reinterpret_cast<const LEUInt32*>(reinterpret_cast<const char *>(this) + offsetToFunctions);
}
const Property *propertyTable() const
@@ -415,6 +535,11 @@ struct Object
return reinterpret_cast<const Property*>(reinterpret_cast<const char *>(this) + offsetToProperties);
}
+ const Alias *aliasTable() const
+ {
+ return reinterpret_cast<const Alias*>(reinterpret_cast<const char *>(this) + offsetToAliases);
+ }
+
const Binding *bindingTable() const
{
return reinterpret_cast<const Binding*>(reinterpret_cast<const char *>(this) + offsetToBindings);
@@ -422,78 +547,116 @@ struct Object
const Signal *signalAt(int idx) const
{
- const uint *offsetTable = reinterpret_cast<const uint*>((reinterpret_cast<const char *>(this)) + offsetToSignals);
- const uint offset = offsetTable[idx];
+ const LEUInt32 *offsetTable = reinterpret_cast<const LEUInt32*>((reinterpret_cast<const char *>(this)) + offsetToSignals);
+ const LEUInt32 offset = offsetTable[idx];
return reinterpret_cast<const Signal*>(reinterpret_cast<const char*>(this) + offset);
}
+
+ const LEUInt32 *namedObjectsInComponentTable() const
+ {
+ return reinterpret_cast<const LEUInt32*>(reinterpret_cast<const char *>(this) + offsetToNamedObjectsInComponent);
+ }
+
+ // --- QQmlPropertyCacheCreator interface
+ int propertyCount() const { return nProperties; }
+ int aliasCount() const { return nAliases; }
+ int signalCount() const { return nSignals; }
+ int functionCount() const { return nFunctions; }
+
+ const Binding *bindingsBegin() const { return bindingTable(); }
+ const Binding *bindingsEnd() const { return bindingTable() + nBindings; }
+
+ const Property *propertiesBegin() const { return propertyTable(); }
+ const Property *propertiesEnd() const { return propertyTable() + nProperties; }
+
+ const Alias *aliasesBegin() const { return aliasTable(); }
+ const Alias *aliasesEnd() const { return aliasTable() + nAliases; }
+
+ typedef TableIterator<Signal, Object, &Object::signalAt> SignalIterator;
+ SignalIterator signalsBegin() const { return SignalIterator(this, 0); }
+ SignalIterator signalsEnd() const { return SignalIterator(this, nSignals); }
+
+ int namedObjectsInComponentCount() const { return nNamedObjectsInComponent; }
+ // ---
};
struct Import
{
- enum ImportType {
+ enum ImportType : unsigned int {
ImportLibrary = 0x1,
ImportFile = 0x2,
ImportScript = 0x3
};
- quint32 type;
+ LEUInt32 type;
- quint32 uriIndex;
- quint32 qualifierIndex;
+ LEUInt32 uriIndex;
+ LEUInt32 qualifierIndex;
- qint32 majorVersion;
- qint32 minorVersion;
+ LEInt32 majorVersion;
+ LEInt32 minorVersion;
Location location;
- Import(): type(0), uriIndex(0), qualifierIndex(0), majorVersion(0), minorVersion(0) {}
+ Import() { type = 0; uriIndex = 0; qualifierIndex = 0; majorVersion = 0; minorVersion = 0; }
};
static const char magic_str[] = "qv4cdata";
struct Unit
{
+ // DO NOT CHANGE THESE FIELDS EVER
char magic[8];
- qint16 architecture;
- qint16 version;
- quint32 unitSize; // Size of the Unit and any depending data.
+ LEUInt32 version;
+ LEUInt32 qtVersion;
+ LEInt64 sourceTimeStamp;
+ LEUInt32 unitSize; // Size of the Unit and any depending data.
+ // END DO NOT CHANGE THESE FIELDS EVER
- enum {
+ char md5Checksum[16]; // checksum of all bytes following this field.
+ void generateChecksum();
+
+ LEUInt32 architectureIndex; // string index to QSysInfo::buildAbi()
+ LEUInt32 codeGeneratorIndex;
+ char dependencyMD5Checksum[16];
+
+ enum : unsigned int {
IsJavascript = 0x1,
IsQml = 0x2,
StaticData = 0x4, // Unit data persistent in memory?
IsSingleton = 0x8,
- IsSharedLibrary = 0x10 // .pragma shared?
+ IsSharedLibrary = 0x10, // .pragma shared?
+ ContainsMachineCode = 0x20 // used to determine if we need to mmap with execute permissions
};
- quint32 flags;
- uint stringTableSize;
- uint offsetToStringTable;
- uint functionTableSize;
- uint offsetToFunctionTable;
- uint lookupTableSize;
- uint offsetToLookupTable;
- uint regexpTableSize;
- uint offsetToRegexpTable;
- uint constantTableSize;
- uint offsetToConstantTable;
- uint jsClassTableSize;
- uint offsetToJSClassTable;
- qint32 indexOfRootFunction;
- quint32 sourceFileIndex;
+ LEUInt32 flags;
+ LEUInt32 stringTableSize;
+ LEUInt32 offsetToStringTable;
+ LEUInt32 functionTableSize;
+ LEUInt32 offsetToFunctionTable;
+ LEUInt32 lookupTableSize;
+ LEUInt32 offsetToLookupTable;
+ LEUInt32 regexpTableSize;
+ LEUInt32 offsetToRegexpTable;
+ LEUInt32 constantTableSize;
+ LEUInt32 offsetToConstantTable;
+ LEUInt32 jsClassTableSize;
+ LEUInt32 offsetToJSClassTable;
+ LEInt32 indexOfRootFunction;
+ LEUInt32 sourceFileIndex;
/* QML specific fields */
- quint32 nImports;
- quint32 offsetToImports;
- quint32 nObjects;
- quint32 offsetToObjects;
- quint32 indexOfRootObject;
+ LEUInt32 nImports;
+ LEUInt32 offsetToImports;
+ LEUInt32 nObjects;
+ LEUInt32 offsetToObjects;
+ LEUInt32 indexOfRootObject;
const Import *importAt(int idx) const {
return reinterpret_cast<const Import*>((reinterpret_cast<const char *>(this)) + offsetToImports + idx * sizeof(Import));
}
const Object *objectAt(int idx) const {
- const uint *offsetTable = reinterpret_cast<const uint*>((reinterpret_cast<const char *>(this)) + offsetToObjects);
- const uint offset = offsetTable[idx];
+ const LEUInt32 *offsetTable = reinterpret_cast<const LEUInt32*>((reinterpret_cast<const char *>(this)) + offsetToObjects);
+ const LEUInt32 offset = offsetTable[idx];
return reinterpret_cast<const Object*>(reinterpret_cast<const char*>(this) + offset);
}
@@ -503,22 +666,33 @@ struct Unit
/* end QML specific fields*/
QString stringAt(int idx) const {
- const uint *offsetTable = reinterpret_cast<const uint*>((reinterpret_cast<const char *>(this)) + offsetToStringTable);
- const uint offset = offsetTable[idx];
+ const LEUInt32 *offsetTable = reinterpret_cast<const LEUInt32*>((reinterpret_cast<const char *>(this)) + offsetToStringTable);
+ const LEUInt32 offset = offsetTable[idx];
const String *str = reinterpret_cast<const String*>(reinterpret_cast<const char *>(this) + offset);
if (str->size == 0)
return QString();
+#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
const QChar *characters = reinterpret_cast<const QChar *>(str + 1);
- if (flags & StaticData)
- return QString::fromRawData(characters, str->size);
+ // Too risky to do this while we unmap disk backed compilation but keep pointers to string
+ // data in the identifier tables.
+ // if (flags & StaticData)
+ // return QString::fromRawData(characters, str->size);
return QString(characters, str->size);
+#else
+ const LEUInt16 *characters = reinterpret_cast<const LEUInt16 *>(str + 1);
+ QString qstr(str->size, Qt::Uninitialized);
+ QChar *ch = qstr.data();
+ for (int i = 0; i < str->size; ++i)
+ ch[i] = QChar(characters[i]);
+ return qstr;
+#endif
}
- const uint *functionOffsetTable() const { return reinterpret_cast<const uint*>((reinterpret_cast<const char *>(this)) + offsetToFunctionTable); }
+ const LEUInt32 *functionOffsetTable() const { return reinterpret_cast<const LEUInt32*>((reinterpret_cast<const char *>(this)) + offsetToFunctionTable); }
const Function *functionAt(int idx) const {
- const uint *offsetTable = functionOffsetTable();
- const uint offset = offsetTable[idx];
+ const LEUInt32 *offsetTable = functionOffsetTable();
+ const LEUInt32 offset = offsetTable[idx];
return reinterpret_cast<const Function*>(reinterpret_cast<const char *>(this) + offset);
}
@@ -526,27 +700,18 @@ struct Unit
const RegExp *regexpAt(int index) const {
return reinterpret_cast<const RegExp*>(reinterpret_cast<const char *>(this) + offsetToRegexpTable + index * sizeof(RegExp));
}
- const QV4::Value *constants() const {
- return reinterpret_cast<const QV4::Value*>(reinterpret_cast<const char *>(this) + offsetToConstantTable);
+ const LEUInt64 *constants() const {
+ return reinterpret_cast<const LEUInt64*>(reinterpret_cast<const char *>(this) + offsetToConstantTable);
}
const JSClassMember *jsClassAt(int idx, int *nMembers) const {
- const uint *offsetTable = reinterpret_cast<const uint *>(reinterpret_cast<const char *>(this) + offsetToJSClassTable);
- const uint offset = offsetTable[idx];
+ const LEUInt32 *offsetTable = reinterpret_cast<const LEUInt32 *>(reinterpret_cast<const char *>(this) + offsetToJSClassTable);
+ const LEUInt32 offset = offsetTable[idx];
const char *ptr = reinterpret_cast<const char *>(this) + offset;
const JSClass *klass = reinterpret_cast<const JSClass *>(ptr);
*nMembers = klass->nMembers;
return reinterpret_cast<const JSClassMember*>(ptr + sizeof(JSClass));
}
-
- static int calculateSize(uint nFunctions, uint nRegExps, uint nConstants,
- uint nLookups, uint nClasses) {
- return (sizeof(Unit)
- + (nFunctions + nClasses) * sizeof(uint)
- + nRegExps * RegExp::calculateSize()
- + nConstants * sizeof(QV4::ReturnedValue)
- + nLookups * Lookup::calculateSize()
- + 7) & ~7; }
};
#if defined(Q_CC_MSVC) || defined(Q_CC_GNU)
@@ -565,7 +730,7 @@ struct TypeReference
bool errorWhenNotFound: 1;
};
-// map from name index to location of first use
+// Map from name index to location of first use.
struct TypeReferenceMap : QHash<int, TypeReference>
{
TypeReference &add(int nameIndex, const Location &loc) {
@@ -574,7 +739,75 @@ struct TypeReferenceMap : QHash<int, TypeReference>
return *it;
return *insert(nameIndex, loc);
}
+
+ template <typename CompiledObject>
+ void collectFromObject(const CompiledObject *obj)
+ {
+ if (obj->inheritedTypeNameIndex != 0) {
+ TypeReference &r = this->add(obj->inheritedTypeNameIndex, obj->location);
+ r.needsCreation = true;
+ r.errorWhenNotFound = true;
+ }
+
+ for (auto prop = obj->propertiesBegin(), propEnd = obj->propertiesEnd(); prop != propEnd; ++prop) {
+ if (prop->type >= QV4::CompiledData::Property::Custom) {
+ // ### FIXME: We could report the more accurate location here by using prop->location, but the old
+ // compiler can't and the tests expect it to be the object location right now.
+ TypeReference &r = this->add(prop->customTypeNameIndex, obj->location);
+ r.errorWhenNotFound = true;
+ }
+ }
+
+ for (auto binding = obj->bindingsBegin(), bindingEnd = obj->bindingsEnd(); binding != bindingEnd; ++binding) {
+ if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty)
+ this->add(binding->propertyNameIndex, binding->location);
+ }
+ }
+
+ template <typename Iterator>
+ void collectFromObjects(Iterator it, Iterator end)
+ {
+ for (; it != end; ++it)
+ collectFromObject(*it);
+ }
+};
+
+#ifndef V4_BOOTSTRAP
+struct ResolvedTypeReference
+{
+ ResolvedTypeReference()
+ : type(0)
+ , majorVersion(0)
+ , minorVersion(0)
+ , isFullyDynamicType(false)
+ {}
+
+ QQmlType *type;
+ QQmlRefPointer<QQmlPropertyCache> typePropertyCache;
+ QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit;
+
+ int majorVersion;
+ int minorVersion;
+ // Types such as QQmlPropertyMap can add properties dynamically at run-time and
+ // therefore cannot have a property cache installed when instantiated.
+ bool isFullyDynamicType;
+
+ QQmlPropertyCache *propertyCache() const;
+ QQmlPropertyCache *createPropertyCache(QQmlEngine *);
+ bool addToHash(QCryptographicHash *hash, QQmlEngine *engine);
+
+ void doDynamicTypeCheck();
};
+// map from name index
+// While this could be a hash, a map is chosen here to provide a stable
+// order, which is used to calculating a check-sum on dependent meta-objects.
+struct ResolvedTypeReferenceMap: public QMap<int, ResolvedTypeReference*>
+{
+ bool addToHash(QCryptographicHash *hash, QQmlEngine *engine) const;
+};
+#else
+struct ResolvedTypeReferenceMap {};
+#endif
// index is per-object binding index
typedef QVector<QQmlPropertyData*> BindingPropertyData;
@@ -597,7 +830,7 @@ struct Q_QML_PRIVATE_EXPORT CompilationUnit : public QQmlRefCount
virtual ~CompilationUnit();
#endif
- Unit *data;
+ const Unit *data;
// Called only when building QML, when we build the header for JS first and append QML data
virtual QV4::CompiledData::Unit *createUnitData(QmlIR::Document *irDocument);
@@ -614,20 +847,81 @@ struct Q_QML_PRIVATE_EXPORT CompilationUnit : public QQmlRefCount
QVector<QV4::Function *> runtimeFunctions;
mutable QQmlNullableValue<QUrl> m_url;
+ // QML specific fields
+ QQmlPropertyCacheVector propertyCaches;
+ QQmlPropertyCache *rootPropertyCache() const { return propertyCaches.at(data->indexOfRootObject); }
+
+ QQmlRefPointer<QQmlTypeNameCache> importCache;
+
// index is object index. This allows fast access to the
// property data when initializing bindings, avoiding expensive
// lookups by string (property name).
QVector<BindingPropertyData> bindingPropertyDataPerObject;
+ // mapping from component object index (CompiledData::Unit object index that points to component) to identifier hash of named objects
+ // this is initialized on-demand by QQmlContextData
+ QHash<int, IdentifierHash<int>> namedObjectsPerComponentCache;
+ IdentifierHash<int> namedObjectsPerComponent(int componentObjectIndex);
+
+ // pointers either to data->constants() or little-endian memory copy.
+ const Value* constants;
+
+ void finalize(QQmlEnginePrivate *engine);
+
+ int totalBindingsCount; // Number of bindings used in this type
+ int totalParserStatusCount; // Number of instantiated types that are QQmlParserStatus subclasses
+ int totalObjectCount; // Number of objects explicitly instantiated
+
+ QVector<QQmlScriptData *> dependentScripts;
+ ResolvedTypeReferenceMap resolvedTypes;
+
+ bool verifyChecksum(QQmlEngine *engine,
+ const ResolvedTypeReferenceMap &dependentTypes) const;
+
+ int metaTypeId;
+ int listMetaTypeId;
+ bool isRegisteredWithEngine;
+
+ QScopedPointer<CompilationUnitMapper> backingFile;
+
+ // --- interface for QQmlPropertyCacheCreator
+ typedef Object CompiledObject;
+ int objectCount() const { return data->nObjects; }
+ int rootObjectIndex() const { return data->indexOfRootObject; }
+ const Object *objectAt(int index) const { return data->objectAt(index); }
+ QString stringAt(int index) const { return data->stringAt(index); }
+
+ struct FunctionIterator
+ {
+ FunctionIterator(const Unit *unit, const Object *object, int index) : unit(unit), object(object), index(index) {}
+ const Unit *unit;
+ const Object *object;
+ int index;
+
+ const Function *operator->() const { return unit->functionAt(object->functionOffsetTable()[index]); }
+ void operator++() { ++index; }
+ bool operator==(const FunctionIterator &rhs) const { return index == rhs.index; }
+ bool operator!=(const FunctionIterator &rhs) const { return index != rhs.index; }
+ };
+ FunctionIterator objectFunctionsBegin(const Object *object) const { return FunctionIterator(data, object, 0); }
+ FunctionIterator objectFunctionsEnd(const Object *object) const { return FunctionIterator(data, object, object->nFunctions); }
+ // ---
+
QV4::Function *linkToEngine(QV4::ExecutionEngine *engine);
void unlink();
- virtual QV4::ExecutableAllocator::ChunkOfPages *chunkForFunction(int /*functionIndex*/) { return 0; }
-
void markObjects(QV4::ExecutionEngine *e);
+ void destroy() Q_DECL_OVERRIDE;
+
+ bool saveToDisk(const QUrl &unitUrl, QString *errorString);
+ bool loadFromDisk(const QUrl &url, EvalISelFactory *iselFactory, QString *errorString);
+
protected:
virtual void linkBackendToEngine(QV4::ExecutionEngine *engine) = 0;
+ virtual void prepareCodeOffsetsForDiskStorage(CompiledData::Unit *unit);
+ virtual bool saveCodeToDisk(QIODevice *device, const CompiledData::Unit *unit, QString *errorString);
+ virtual bool memoryMapCode(QString *errorString);
#endif // V4_BOOTSTRAP
};
diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp
index 3943642146..e1ea3a9b88 100644
--- a/src/qml/compiler/qv4compiler.cpp
+++ b/src/qml/compiler/qv4compiler.cpp
@@ -42,6 +42,9 @@
#include <qv4isel_p.h>
#include <private/qv4string_p.h>
#include <private/qv4value_p.h>
+#include <private/qv4alloca_p.h>
+#include <wtf/MathExtras.h>
+#include <QCryptographicHash>
QV4::Compiler::StringTableGenerator::StringTableGenerator()
{
@@ -75,16 +78,21 @@ void QV4::Compiler::StringTableGenerator::clear()
void QV4::Compiler::StringTableGenerator::serialize(CompiledData::Unit *unit)
{
char *dataStart = reinterpret_cast<char *>(unit);
- uint *stringTable = reinterpret_cast<uint *>(dataStart + unit->offsetToStringTable);
+ CompiledData::LEUInt32 *stringTable = reinterpret_cast<CompiledData::LEUInt32 *>(dataStart + unit->offsetToStringTable);
char *stringData = dataStart + unit->offsetToStringTable + unit->stringTableSize * sizeof(uint);
for (int i = 0; i < strings.size(); ++i) {
stringTable[i] = stringData - dataStart;
const QString &qstr = strings.at(i);
- QV4::CompiledData::String *s = (QV4::CompiledData::String*)(stringData);
- s->flags = 0; // ###
+ QV4::CompiledData::String *s = reinterpret_cast<QV4::CompiledData::String *>(stringData);
s->size = qstr.length();
+#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
memcpy(s + 1, qstr.constData(), qstr.length()*sizeof(ushort));
+#else
+ ushort *uc = reinterpret_cast<ushort *>(s + 1);
+ for (int i = 0; i < qstr.length(); ++i)
+ uc[i] = qToLittleEndian<ushort>(qstr.at(i).unicode());
+#endif
stringData += QV4::CompiledData::String::calculateSize(qstr);
}
@@ -92,7 +100,6 @@ void QV4::Compiler::StringTableGenerator::serialize(CompiledData::Unit *unit)
QV4::Compiler::JSUnitGenerator::JSUnitGenerator(QV4::IR::Module *module)
: irModule(module)
- , jsClassDataSize(0)
{
// Make sure the empty string always gets index 0
registerString(QString());
@@ -174,30 +181,32 @@ int QV4::Compiler::JSUnitGenerator::registerJSClass(int count, IR::ExprList *arg
{
// ### re-use existing class definitions.
- QList<CompiledData::JSClassMember> members;
- members.reserve(count);
+ const int size = CompiledData::JSClass::calculateSize(count);
+ jsClassOffsets.append(jsClassData.size());
+ const int oldSize = jsClassData.size();
+ jsClassData.resize(jsClassData.size() + size);
+ memset(jsClassData.data() + oldSize, 0, size);
- IR::ExprList *it = args;
- for (int i = 0; i < count; ++i, it = it->next) {
- CompiledData::JSClassMember member;
+ CompiledData::JSClass *jsClass = reinterpret_cast<CompiledData::JSClass*>(jsClassData.data() + oldSize);
+ jsClass->nMembers = count;
+ CompiledData::JSClassMember *member = reinterpret_cast<CompiledData::JSClassMember*>(jsClass + 1);
+ IR::ExprList *it = args;
+ for (int i = 0; i < count; ++i, it = it->next, ++member) {
QV4::IR::Name *name = it->expr->asName();
it = it->next;
const bool isData = it->expr->asConst()->value;
it = it->next;
- member.nameOffset = registerString(*name->id);
- member.isAccessor = !isData;
- members << member;
+ member->nameOffset = registerString(*name->id);
+ member->isAccessor = !isData;
if (!isData)
it = it->next;
}
- jsClasses << members;
- jsClassDataSize += CompiledData::JSClass::calculateSize(members.count());
- return jsClasses.size() - 1;
+ return jsClassOffsets.size() - 1;
}
QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(GeneratorOption option)
@@ -211,110 +220,70 @@ QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(GeneratorO
registerString(*f->locals.at(i));
}
- int unitSize = QV4::CompiledData::Unit::calculateSize(irModule->functions.size(), regexps.size(),
- constants.size(), lookups.size(), jsClasses.count());
-
- uint functionDataSize = 0;
- for (int i = 0; i < irModule->functions.size(); ++i) {
- QV4::IR::Function *f = irModule->functions.at(i);
- functionOffsets.insert(f, functionDataSize + unitSize);
-
- const int qmlIdDepsCount = f->idObjectDependencies.count();
- const int qmlPropertyDepsCount = f->scopeObjectPropertyDependencies.count() + f->contextObjectPropertyDependencies.count();
- functionDataSize += QV4::CompiledData::Function::calculateSize(f->formals.size(), f->locals.size(), f->nestedFunctions.size(), qmlIdDepsCount, qmlPropertyDepsCount);
+ CompiledData::LEUInt32 *functionOffsets = reinterpret_cast<CompiledData::LEUInt32*>(alloca(irModule->functions.size() * sizeof(CompiledData::LEUInt32)));
+ uint jsClassDataOffset = 0;
+
+ char *dataPtr;
+ CompiledData::Unit *unit;
+ {
+ QV4::CompiledData::Unit tempHeader = generateHeader(option, functionOffsets, &jsClassDataOffset);
+ dataPtr = reinterpret_cast<char *>(malloc(tempHeader.unitSize));
+ memset(dataPtr, 0, tempHeader.unitSize);
+ memcpy(&unit, &dataPtr, sizeof(CompiledData::Unit*));
+ memcpy(unit, &tempHeader, sizeof(tempHeader));
}
- const int totalSize = unitSize + functionDataSize + jsClassDataSize + (option == GenerateWithStringTable ? stringTable.sizeOfTableAndData() : 0);
- char *data = (char *)malloc(totalSize);
- memset(data, 0, totalSize);
- QV4::CompiledData::Unit *unit = (QV4::CompiledData::Unit*)data;
-
- memcpy(unit->magic, QV4::CompiledData::magic_str, sizeof(unit->magic));
- unit->architecture = 0; // ###
- unit->flags = QV4::CompiledData::Unit::IsJavascript;
- unit->version = 1;
- unit->unitSize = totalSize;
- unit->functionTableSize = irModule->functions.size();
- unit->offsetToFunctionTable = sizeof(*unit);
- unit->lookupTableSize = lookups.count();
- unit->offsetToLookupTable = unit->offsetToFunctionTable + unit->functionTableSize * sizeof(uint);
- unit->regexpTableSize = regexps.size();
- unit->offsetToRegexpTable = unit->offsetToLookupTable + unit->lookupTableSize * CompiledData::Lookup::calculateSize();
- unit->constantTableSize = constants.size();
- unit->offsetToConstantTable = unit->offsetToRegexpTable + unit->regexpTableSize * CompiledData::RegExp::calculateSize();
- unit->jsClassTableSize = jsClasses.count();
- unit->offsetToJSClassTable = unit->offsetToConstantTable + unit->constantTableSize * sizeof(ReturnedValue);
- if (option == GenerateWithStringTable) {
- unit->stringTableSize = stringTable.stringCount();
- unit->offsetToStringTable = unitSize + functionDataSize + jsClassDataSize;
- } else {
- unit->stringTableSize = 0;
- unit->offsetToStringTable = 0;
- }
- unit->indexOfRootFunction = -1;
- unit->sourceFileIndex = getStringId(irModule->fileName);
- unit->nImports = 0;
- unit->offsetToImports = 0;
- unit->nObjects = 0;
- unit->offsetToObjects = 0;
- unit->indexOfRootObject = 0;
-
- uint *functionTable = (uint *)(data + unit->offsetToFunctionTable);
- for (int i = 0; i < irModule->functions.size(); ++i)
- functionTable[i] = functionOffsets.value(irModule->functions.at(i));
-
- char *f = data + unitSize;
+ memcpy(dataPtr + unit->offsetToFunctionTable, functionOffsets, unit->functionTableSize * sizeof(CompiledData::LEUInt32));
+
for (int i = 0; i < irModule->functions.size(); ++i) {
QV4::IR::Function *function = irModule->functions.at(i);
if (function == irModule->rootFunction)
unit->indexOfRootFunction = i;
- const int bytes = writeFunction(f, i, function);
- f += bytes;
+ writeFunction(dataPtr + functionOffsets[i], function);
}
- CompiledData::Lookup *lookupsToWrite = (CompiledData::Lookup*)(data + unit->offsetToLookupTable);
+ CompiledData::Lookup *lookupsToWrite = reinterpret_cast<CompiledData::Lookup*>(dataPtr + unit->offsetToLookupTable);
foreach (const CompiledData::Lookup &l, lookups)
*lookupsToWrite++ = l;
- CompiledData::RegExp *regexpTable = (CompiledData::RegExp *)(data + unit->offsetToRegexpTable);
+ CompiledData::RegExp *regexpTable = reinterpret_cast<CompiledData::RegExp *>(dataPtr + unit->offsetToRegexpTable);
memcpy(regexpTable, regexps.constData(), regexps.size() * sizeof(*regexpTable));
- ReturnedValue *constantTable = (ReturnedValue *)(data + unit->offsetToConstantTable);
+#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
+ ReturnedValue *constantTable = reinterpret_cast<ReturnedValue *>(dataPtr + unit->offsetToConstantTable);
memcpy(constantTable, constants.constData(), constants.size() * sizeof(ReturnedValue));
-
- // write js classes and js class lookup table
- uint *jsClassTable = (uint*)(data + unit->offsetToJSClassTable);
- char *jsClass = data + unitSize + functionDataSize;
- for (int i = 0; i < jsClasses.count(); ++i) {
- jsClassTable[i] = jsClass - data;
-
- const QList<CompiledData::JSClassMember> members = jsClasses.at(i);
-
- CompiledData::JSClass *c = reinterpret_cast<CompiledData::JSClass*>(jsClass);
- c->nMembers = members.count();
-
- CompiledData::JSClassMember *memberToWrite = reinterpret_cast<CompiledData::JSClassMember*>(jsClass + sizeof(CompiledData::JSClass));
- foreach (const CompiledData::JSClassMember &member, members)
- *memberToWrite++ = member;
-
- jsClass += CompiledData::JSClass::calculateSize(members.count());
+#else
+ CompiledData::LEUInt64 *constantTable = reinterpret_cast<CompiledData::LEUInt64 *>(dataPtr + unit->offsetToConstantTable);
+ for (int i = 0; i < constants.count(); ++i)
+ constantTable[i] = constants.at(i);
+#endif
+
+ {
+ memcpy(dataPtr + jsClassDataOffset, jsClassData.constData(), jsClassData.size());
+
+ // write js classes and js class lookup table
+ CompiledData::LEUInt32 *jsClassOffsetTable = reinterpret_cast<CompiledData::LEUInt32 *>(dataPtr + unit->offsetToJSClassTable);
+ for (int i = 0; i < jsClassOffsets.count(); ++i)
+ jsClassOffsetTable[i] = jsClassDataOffset + jsClassOffsets.at(i);
}
// write strings and string table
if (option == GenerateWithStringTable)
stringTable.serialize(unit);
+ unit->generateChecksum();
+
return unit;
}
-int QV4::Compiler::JSUnitGenerator::writeFunction(char *f, int index, QV4::IR::Function *irFunction)
+void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::IR::Function *irFunction) const
{
QV4::CompiledData::Function *function = (QV4::CompiledData::Function *)f;
quint32 currentOffset = sizeof(QV4::CompiledData::Function);
+ currentOffset = (currentOffset + 7) & ~quint32(0x7);
- function->index = index;
function->nameIndex = getStringId(*irFunction->name);
function->flags = 0;
if (irFunction->hasDirectEval)
@@ -336,8 +305,6 @@ int QV4::Compiler::JSUnitGenerator::writeFunction(char *f, int index, QV4::IR::F
currentOffset += function->nLocals * sizeof(quint32);
function->nInnerFunctions = irFunction->nestedFunctions.size();
- function->innerFunctionsOffset = currentOffset;
- currentOffset += function->nInnerFunctions * sizeof(quint32);
function->nDependingIdObjects = 0;
function->nDependingContextProperties = 0;
@@ -364,6 +331,9 @@ int QV4::Compiler::JSUnitGenerator::writeFunction(char *f, int index, QV4::IR::F
function->location.line = irFunction->line;
function->location.column = irFunction->column;
+ function->codeOffset = 0;
+ function->codeSize = 0;
+
// write formals
quint32 *formals = (quint32 *)(f + function->formalsOffset);
for (int i = 0; i < irFunction->formals.size(); ++i)
@@ -374,15 +344,12 @@ int QV4::Compiler::JSUnitGenerator::writeFunction(char *f, int index, QV4::IR::F
for (int i = 0; i < irFunction->locals.size(); ++i)
locals[i] = getStringId(*irFunction->locals.at(i));
- // write inner functions
- quint32 *innerFunctions = (quint32 *)(f + function->innerFunctionsOffset);
- for (int i = 0; i < irFunction->nestedFunctions.size(); ++i)
- innerFunctions[i] = functionOffsets.value(irFunction->nestedFunctions.at(i));
-
// write QML dependencies
quint32 *writtenDeps = (quint32 *)(f + function->dependingIdObjectsOffset);
- foreach (int id, irFunction->idObjectDependencies)
- *writtenDeps++ = id;
+ for (int id : irFunction->idObjectDependencies) {
+ Q_ASSERT(id >= 0);
+ *writtenDeps++ = static_cast<quint32>(id);
+ }
writtenDeps = (quint32 *)(f + function->dependingContextPropertiesOffset);
for (auto property : irFunction->contextObjectPropertyDependencies) {
@@ -395,7 +362,77 @@ int QV4::Compiler::JSUnitGenerator::writeFunction(char *f, int index, QV4::IR::F
*writtenDeps++ = property.key(); // property index
*writtenDeps++ = property.value(); // notify index
}
+}
+
+QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Compiler::JSUnitGenerator::GeneratorOption option, QJsonPrivate::q_littleendian<quint32> *functionOffsets, uint *jsClassDataOffset)
+{
+ CompiledData::Unit unit;
+ memset(&unit, 0, sizeof(unit));
+ memcpy(unit.magic, CompiledData::magic_str, sizeof(unit.magic));
+ unit.flags = QV4::CompiledData::Unit::IsJavascript;
+ unit.flags |= irModule->unitFlags;
+ unit.version = QV4_DATA_STRUCTURE_VERSION;
+ unit.qtVersion = QT_VERSION;
+ memset(unit.md5Checksum, 0, sizeof(unit.md5Checksum));
+ unit.architectureIndex = registerString(QSysInfo::buildAbi());
+ unit.codeGeneratorIndex = registerString(codeGeneratorName);
+ memset(unit.dependencyMD5Checksum, 0, sizeof(unit.dependencyMD5Checksum));
+
+ quint32 nextOffset = sizeof(CompiledData::Unit);
+
+ unit.functionTableSize = irModule->functions.size();
+ unit.offsetToFunctionTable = nextOffset;
+ nextOffset += unit.functionTableSize * sizeof(uint);
+
+ unit.lookupTableSize = lookups.count();
+ unit.offsetToLookupTable = nextOffset;
+ nextOffset += unit.lookupTableSize * sizeof(CompiledData::Lookup);
+
+ unit.regexpTableSize = regexps.size();
+ unit.offsetToRegexpTable = nextOffset;
+ nextOffset += unit.regexpTableSize * sizeof(CompiledData::RegExp);
+
+ unit.constantTableSize = constants.size();
+
+ // Ensure we load constants from well-aligned addresses into for example SSE registers.
+ nextOffset = static_cast<quint32>(WTF::roundUpToMultipleOf(16, nextOffset));
+ unit.offsetToConstantTable = nextOffset;
+ nextOffset += unit.constantTableSize * sizeof(ReturnedValue);
+
+ unit.jsClassTableSize = jsClassOffsets.count();
+ unit.offsetToJSClassTable = nextOffset;
+ nextOffset += unit.jsClassTableSize * sizeof(uint);
+
+ *jsClassDataOffset = nextOffset;
+ nextOffset += jsClassData.size();
+
+ for (int i = 0; i < irModule->functions.size(); ++i) {
+ QV4::IR::Function *f = irModule->functions.at(i);
+ functionOffsets[i] = nextOffset;
+
+ const int qmlIdDepsCount = f->idObjectDependencies.count();
+ const int qmlPropertyDepsCount = f->scopeObjectPropertyDependencies.count() + f->contextObjectPropertyDependencies.count();
+ nextOffset += QV4::CompiledData::Function::calculateSize(f->formals.size(), f->locals.size(), f->nestedFunctions.size(), qmlIdDepsCount, qmlPropertyDepsCount);
+ }
- return CompiledData::Function::calculateSize(function->nFormals, function->nLocals, function->nInnerFunctions,
- function->nDependingIdObjects, function->nDependingContextProperties + function->nDependingScopeProperties);
+ if (option == GenerateWithStringTable) {
+ unit.stringTableSize = stringTable.stringCount();
+ unit.offsetToStringTable = nextOffset;
+ nextOffset += stringTable.sizeOfTableAndData();
+ } else {
+ unit.stringTableSize = 0;
+ unit.offsetToStringTable = 0;
+ }
+ unit.indexOfRootFunction = -1;
+ unit.sourceFileIndex = getStringId(irModule->fileName);
+ unit.sourceTimeStamp = irModule->sourceTimeStamp;
+ unit.nImports = 0;
+ unit.offsetToImports = 0;
+ unit.nObjects = 0;
+ unit.offsetToObjects = 0;
+ unit.indexOfRootObject = 0;
+
+ unit.unitSize = nextOffset;
+
+ return unit;
}
diff --git a/src/qml/compiler/qv4compiler_p.h b/src/qml/compiler/qv4compiler_p.h
index 0321a83b4f..49b8664513 100644
--- a/src/qml/compiler/qv4compiler_p.h
+++ b/src/qml/compiler/qv4compiler_p.h
@@ -52,6 +52,7 @@
#include <QtCore/qstring.h>
#include "qv4jsir_p.h"
+#include <private/qjson_p.h>
QT_BEGIN_NAMESPACE
@@ -114,18 +115,20 @@ struct Q_QML_PRIVATE_EXPORT JSUnitGenerator {
QV4::CompiledData::Unit *generateUnit(GeneratorOption option = GenerateWithStringTable);
// Returns bytes written
- int writeFunction(char *f, int index, IR::Function *irFunction);
+ void writeFunction(char *f, IR::Function *irFunction) const;
StringTableGenerator stringTable;
+ QString codeGeneratorName;
private:
+ CompiledData::Unit generateHeader(GeneratorOption option, QJsonPrivate::q_littleendian<quint32> *functionOffsets, uint *jsClassDataOffset);
+
IR::Module *irModule;
- QHash<IR::Function *, uint> functionOffsets;
QList<CompiledData::Lookup> lookups;
QVector<CompiledData::RegExp> regexps;
QVector<ReturnedValue> constants;
- QList<QList<CompiledData::JSClassMember> > jsClasses;
- uint jsClassDataSize;
+ QByteArray jsClassData;
+ QVector<int> jsClassOffsets;
};
}
diff --git a/src/qml/compiler/qv4instr_moth_p.h b/src/qml/compiler/qv4instr_moth_p.h
index 90010ccf52..ca4e0b73d4 100644
--- a/src/qml/compiler/qv4instr_moth_p.h
+++ b/src/qml/compiler/qv4instr_moth_p.h
@@ -50,18 +50,26 @@
//
// We mean it.
//
-
-#include <QtCore/qglobal.h>
+#include <private/qv4global_p.h>
#include <private/qv4value_p.h>
#include <private/qv4function_p.h>
#include <private/qv4runtime_p.h>
+QT_REQUIRE_CONFIG(qml_interpreter);
+
QT_BEGIN_NAMESPACE
+#ifdef QT_NO_QML_DEBUGGER
+#define MOTH_DEBUG_INSTR(F)
+#else
+#define MOTH_DEBUG_INSTR(F) \
+ F(Line, line) \
+ F(Debug, debug)
+#endif
+
#define FOR_EACH_MOTH_INSTR(F) \
F(Ret, ret) \
- F(Line, line) \
- F(Debug, debug) \
+ MOTH_DEBUG_INSTR(F) \
F(LoadRuntimeString, loadRuntimeString) \
F(LoadRegExp, loadRegExp) \
F(LoadClosure, loadClosure) \
@@ -162,7 +170,7 @@ QT_BEGIN_NAMESPACE
#define MOTH_INSTR_ALIGN_MASK (Q_ALIGNOF(QV4::Moth::Instr) - 1)
#ifdef MOTH_THREADED_INTERPRETER
-# define MOTH_INSTR_HEADER void *code;
+# define MOTH_INSTR_HEADER union { quint32 instructionType; void *code; };
#else
# define MOTH_INSTR_HEADER quint32 instructionType;
#endif
@@ -174,6 +182,8 @@ QT_BEGIN_NAMESPACE
namespace QV4 {
namespace Moth {
+ // When making changes to the instructions, make sure to bump QV4_DATA_STRUCTURE_VERSION in qv4compileddata_p.h
+
struct Param {
// Params are looked up as follows:
// Constant: 0
@@ -243,6 +253,7 @@ union Instr
{
enum Type {
FOR_EACH_MOTH_INSTR(MOTH_INSTR_ENUM)
+ LastInstruction
};
struct instr_common {
@@ -252,6 +263,8 @@ union Instr
MOTH_INSTR_HEADER
Param result;
};
+
+#ifndef QT_NO_QML_DEBUGGING
struct instr_line {
MOTH_INSTR_HEADER
qint32 lineNumber;
@@ -260,6 +273,8 @@ union Instr
MOTH_INSTR_HEADER
qint32 lineNumber;
};
+#endif // QT_NO_QML_DEBUGGING
+
struct instr_loadRuntimeString {
MOTH_INSTR_HEADER
int stringId;
@@ -322,12 +337,14 @@ union Instr
int propertyIndex;
Param base;
Param result;
+ bool captureRequired;
};
struct instr_loadContextObjectProperty {
MOTH_INSTR_HEADER
int propertyIndex;
Param base;
Param result;
+ bool captureRequired;
};
struct instr_loadIdObject {
MOTH_INSTR_HEADER
@@ -672,7 +689,7 @@ union Instr
};
struct instr_binop {
MOTH_INSTR_HEADER
- QV4::Runtime::BinaryOperation alu;
+ uint alu; // offset inside the runtime methods
Param lhs;
Param rhs;
Param result;
@@ -757,7 +774,7 @@ union Instr
};
struct instr_binopContext {
MOTH_INSTR_HEADER
- QV4::Runtime::BinaryOperationContext alu;
+ uint alu; // offset inside the runtime methods
Param lhs;
Param rhs;
Param result;
diff --git a/src/qml/compiler/qv4isel_moth.cpp b/src/qml/compiler/qv4isel_moth.cpp
index e84b9c6ec9..ca6319ef3c 100644
--- a/src/qml/compiler/qv4isel_moth.cpp
+++ b/src/qml/compiler/qv4isel_moth.cpp
@@ -46,6 +46,7 @@
#include <private/qv4regexpobject_p.h>
#include <private/qv4compileddata_p.h>
#include <private/qqmlengine_p.h>
+#include <wtf/MathExtras.h>
#undef USE_TYPE_INFO
@@ -54,7 +55,7 @@ using namespace QV4::Moth;
namespace {
-inline QV4::Runtime::BinaryOperation aluOpFunction(IR::AluOp op)
+inline uint aluOpFunction(IR::AluOp op)
{
switch (op) {
case IR::OpInvalid:
@@ -70,43 +71,43 @@ inline QV4::Runtime::BinaryOperation aluOpFunction(IR::AluOp op)
case IR::OpCompl:
return 0;
case IR::OpBitAnd:
- return QV4::Runtime::bitAnd;
+ return offsetof(QV4::Runtime, bitAnd);
case IR::OpBitOr:
- return QV4::Runtime::bitOr;
+ return offsetof(QV4::Runtime, bitOr);
case IR::OpBitXor:
- return QV4::Runtime::bitXor;
+ return offsetof(QV4::Runtime, bitXor);
case IR::OpAdd:
return 0;
case IR::OpSub:
- return QV4::Runtime::sub;
+ return offsetof(QV4::Runtime, sub);
case IR::OpMul:
- return QV4::Runtime::mul;
+ return offsetof(QV4::Runtime, mul);
case IR::OpDiv:
- return QV4::Runtime::div;
+ return offsetof(QV4::Runtime, div);
case IR::OpMod:
- return QV4::Runtime::mod;
+ return offsetof(QV4::Runtime, mod);
case IR::OpLShift:
- return QV4::Runtime::shl;
+ return offsetof(QV4::Runtime, shl);
case IR::OpRShift:
- return QV4::Runtime::shr;
+ return offsetof(QV4::Runtime, shr);
case IR::OpURShift:
- return QV4::Runtime::ushr;
+ return offsetof(QV4::Runtime, ushr);
case IR::OpGt:
- return QV4::Runtime::greaterThan;
+ return offsetof(QV4::Runtime, greaterThan);
case IR::OpLt:
- return QV4::Runtime::lessThan;
+ return offsetof(QV4::Runtime, lessThan);
case IR::OpGe:
- return QV4::Runtime::greaterEqual;
+ return offsetof(QV4::Runtime, greaterEqual);
case IR::OpLe:
- return QV4::Runtime::lessEqual;
+ return offsetof(QV4::Runtime, lessEqual);
case IR::OpEqual:
- return QV4::Runtime::equal;
+ return offsetof(QV4::Runtime, equal);
case IR::OpNotEqual:
- return QV4::Runtime::notEqual;
+ return offsetof(QV4::Runtime, notEqual);
case IR::OpStrictEqual:
- return QV4::Runtime::strictEqual;
+ return offsetof(QV4::Runtime, strictEqual);
case IR::OpStrictNotEqual:
- return QV4::Runtime::strictNotEqual;
+ return offsetof(QV4::Runtime, strictNotEqual);
case IR::OpInstanceof:
return 0;
case IR::OpIn:
@@ -151,8 +152,8 @@ inline bool isBoolType(IR::Expr *e)
} // anonymous namespace
-InstructionSelection::InstructionSelection(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator)
- : EvalInstructionSelection(execAllocator, module, jsGenerator)
+InstructionSelection::InstructionSelection(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator, EvalISelFactory *iselFactory)
+ : EvalInstructionSelection(execAllocator, module, jsGenerator, iselFactory)
, qmlEngine(qmlEngine)
, _block(0)
, _codeStart(0)
@@ -250,6 +251,7 @@ void InstructionSelection::run(int functionIndex)
if (s->location.startLine != currentLine) {
blockNeedsDebugInstruction = false;
currentLine = s->location.startLine;
+#ifndef QT_NO_QML_DEBUGGER
if (irModule->debugMode) {
Instruction::Debug debug;
debug.lineNumber = currentLine;
@@ -259,10 +261,11 @@ void InstructionSelection::run(int functionIndex)
line.lineNumber = currentLine;
addInstruction(line);
}
+#endif
}
}
- s->accept(this);
+ visit(s);
}
}
@@ -576,18 +579,20 @@ void InstructionSelection::setQObjectProperty(IR::Expr *source, IR::Expr *target
addInstruction(store);
}
-void InstructionSelection::getQmlContextProperty(IR::Expr *source, IR::Member::MemberKind kind, int index, IR::Expr *target)
+void InstructionSelection::getQmlContextProperty(IR::Expr *source, IR::Member::MemberKind kind, int index, bool captureRequired, IR::Expr *target)
{
if (kind == IR::Member::MemberOfQmlScopeObject) {
Instruction::LoadScopeObjectProperty load;
load.base = getParam(source);
load.propertyIndex = index;
+ load.captureRequired = captureRequired;
load.result = getResultParam(target);
addInstruction(load);
} else if (kind == IR::Member::MemberOfQmlContextObject) {
Instruction::LoadContextObjectProperty load;
load.base = getParam(source);
load.propertyIndex = index;
+ load.captureRequired = captureRequired;
load.result = getResultParam(target);
addInstruction(load);
} else if (kind == IR::Member::MemberOfIdObjectsArray) {
@@ -879,11 +884,11 @@ Param InstructionSelection::binopHelper(IR::AluOp oper, IR::Expr *leftSource, IR
if (oper == IR::OpInstanceof || oper == IR::OpIn || oper == IR::OpAdd) {
Instruction::BinopContext binop;
if (oper == IR::OpInstanceof)
- binop.alu = QV4::Runtime::instanceof;
+ binop.alu = offsetof(QV4::Runtime, instanceof);
else if (oper == IR::OpIn)
- binop.alu = QV4::Runtime::in;
+ binop.alu = offsetof(QV4::Runtime, in);
else
- binop.alu = QV4::Runtime::add;
+ binop.alu = offsetof(QV4::Runtime, add);
binop.lhs = getParam(leftSource);
binop.rhs = getParam(rightSource);
binop.result = getResultParam(target);
@@ -930,6 +935,17 @@ void InstructionSelection::prepareCallArgs(IR::ExprList *e, quint32 &argc, quint
}
}
+void InstructionSelection::addDebugInstruction()
+{
+#ifndef QT_NO_QML_DEBUGGER
+ if (blockNeedsDebugInstruction) {
+ Instruction::Debug debug;
+ debug.lineNumber = -int(currentLine);
+ addInstruction(debug);
+ }
+#endif
+}
+
void InstructionSelection::visitJump(IR::Jump *s)
{
if (s->target == _nextBlock)
@@ -937,11 +953,7 @@ void InstructionSelection::visitJump(IR::Jump *s)
if (_removableJumps.contains(s))
return;
- if (blockNeedsDebugInstruction) {
- Instruction::Debug debug;
- debug.lineNumber = -int(currentLine);
- addInstruction(debug);
- }
+ addDebugInstruction();
Instruction::Jump jump;
jump.offset = 0;
@@ -952,11 +964,7 @@ void InstructionSelection::visitJump(IR::Jump *s)
void InstructionSelection::visitCJump(IR::CJump *s)
{
- if (blockNeedsDebugInstruction) {
- Instruction::Debug debug;
- debug.lineNumber = -int(currentLine);
- addInstruction(debug);
- }
+ addDebugInstruction();
Param condition;
if (IR::Temp *t = s->cond->asTemp()) {
@@ -991,12 +999,8 @@ void InstructionSelection::visitCJump(IR::CJump *s)
void InstructionSelection::visitRet(IR::Ret *s)
{
- if (blockNeedsDebugInstruction) {
- // this is required so stepOut will always be guaranteed to stop in every stack frame
- Instruction::Debug debug;
- debug.lineNumber = -int(currentLine);
- addInstruction(debug);
- }
+ // this is required so stepOut will always be guaranteed to stop in every stack frame
+ addDebugInstruction();
Instruction::Ret ret;
ret.result = getParam(s->expr);
@@ -1332,12 +1336,7 @@ void QV4::Moth::InstructionSelection::callBuiltinConvertThisToObject()
ptrdiff_t InstructionSelection::addInstructionHelper(Instr::Type type, Instr &instr)
{
-
-#ifdef MOTH_THREADED_INTERPRETER
- instr.common.code = VME::instructionJumpTable()[static_cast<int>(type)];
-#else
instr.common.instructionType = type;
-#endif
int instructionSize = Instr::size(type);
if (_codeEnd - _codeNext < instructionSize) {
@@ -1423,6 +1422,29 @@ CompilationUnit::~CompilationUnit()
void CompilationUnit::linkBackendToEngine(QV4::ExecutionEngine *engine)
{
+#ifdef MOTH_THREADED_INTERPRETER
+ // link byte code against addresses of instructions
+ for (int i = 0; i < codeRefs.count(); ++i) {
+ QByteArray &codeRef = codeRefs[i];
+ char *code = codeRef.data();
+ int index = 0;
+ while (index < codeRef.size()) {
+ Instr *genericInstr = reinterpret_cast<Instr *>(code + index);
+
+ switch (genericInstr->common.instructionType) {
+#define LINK_INSTRUCTION(InstructionType, Member) \
+ case Instr::InstructionType: \
+ genericInstr->common.code = VME::instructionJumpTable()[static_cast<int>(genericInstr->common.instructionType)]; \
+ index += InstrMeta<(int)Instr::InstructionType>::Size; \
+ break;
+
+ FOR_EACH_MOTH_INSTR(LINK_INSTRUCTION)
+
+ }
+ }
+ }
+#endif
+
runtimeFunctions.resize(data->functionTableSize);
runtimeFunctions.fill(0);
for (int i = 0 ;i < runtimeFunctions.size(); ++i) {
@@ -1433,3 +1455,113 @@ void CompilationUnit::linkBackendToEngine(QV4::ExecutionEngine *engine)
runtimeFunctions[i] = runtimeFunction;
}
}
+
+void CompilationUnit::prepareCodeOffsetsForDiskStorage(CompiledData::Unit *unit)
+{
+ const int codeAlignment = 16;
+ quint64 offset = WTF::roundUpToMultipleOf(codeAlignment, unit->unitSize);
+ Q_ASSERT(int(unit->functionTableSize) == codeRefs.size());
+ for (int i = 0; i < codeRefs.size(); ++i) {
+ CompiledData::Function *compiledFunction = const_cast<CompiledData::Function *>(unit->functionAt(i));
+ compiledFunction->codeOffset = offset;
+ compiledFunction->codeSize = codeRefs.at(i).size();
+ offset = WTF::roundUpToMultipleOf(codeAlignment, offset + compiledFunction->codeSize);
+ }
+}
+
+bool CompilationUnit::saveCodeToDisk(QIODevice *device, const CompiledData::Unit *unit, QString *errorString)
+{
+ Q_ASSERT(device->pos() == unit->unitSize);
+ Q_ASSERT(device->atEnd());
+ Q_ASSERT(int(unit->functionTableSize) == codeRefs.size());
+
+ QByteArray padding;
+
+#ifdef MOTH_THREADED_INTERPRETER
+ // Map from instruction label back to instruction type. Only needed when persisting
+ // already linked compilation units;
+ QHash<void*, int> reverseInstructionMapping;
+ if (engine) {
+ void **instructions = VME::instructionJumpTable();
+ for (int i = 0; i < Instr::LastInstruction; ++i)
+ reverseInstructionMapping.insert(instructions[i], i);
+ }
+#endif
+
+ for (int i = 0; i < codeRefs.size(); ++i) {
+ const CompiledData::Function *compiledFunction = unit->functionAt(i);
+
+ if (device->pos() > qint64(compiledFunction->codeOffset)) {
+ *errorString = QStringLiteral("Invalid state of cache file to write.");
+ return false;
+ }
+
+ const quint64 paddingSize = compiledFunction->codeOffset - device->pos();
+ padding.fill(0, paddingSize);
+ qint64 written = device->write(padding);
+ if (written != padding.size()) {
+ *errorString = device->errorString();
+ return false;
+ }
+
+ QByteArray code = codeRefs.at(i);
+
+#ifdef MOTH_THREADED_INTERPRETER
+ if (!reverseInstructionMapping.isEmpty()) {
+ char *codePtr = code.data(); // detaches
+ int index = 0;
+ while (index < code.size()) {
+ Instr *genericInstr = reinterpret_cast<Instr *>(codePtr + index);
+
+ genericInstr->common.instructionType = reverseInstructionMapping.value(genericInstr->common.code);
+
+ switch (genericInstr->common.instructionType) {
+ #define REVERSE_INSTRUCTION(InstructionType, Member) \
+ case Instr::InstructionType: \
+ index += InstrMeta<(int)Instr::InstructionType>::Size; \
+ break;
+
+ FOR_EACH_MOTH_INSTR(REVERSE_INSTRUCTION)
+ }
+ }
+ }
+#endif
+
+ written = device->write(code.constData(), compiledFunction->codeSize);
+ if (written != qint64(compiledFunction->codeSize)) {
+ *errorString = device->errorString();
+ return false;
+ }
+ }
+ return true;
+}
+
+bool CompilationUnit::memoryMapCode(QString *errorString)
+{
+ Q_UNUSED(errorString);
+ codeRefs.resize(data->functionTableSize);
+
+ const char *basePtr = reinterpret_cast<const char *>(data);
+
+ for (uint i = 0; i < data->functionTableSize; ++i) {
+ const CompiledData::Function *compiledFunction = data->functionAt(i);
+ const char *codePtr = const_cast<const char *>(reinterpret_cast<const char *>(basePtr + compiledFunction->codeOffset));
+#ifdef MOTH_THREADED_INTERPRETER
+ // for the threaded interpreter we need to make a copy of the data because it needs to be
+ // modified for the instruction handler addresses.
+ QByteArray code(codePtr, compiledFunction->codeSize);
+#else
+ QByteArray code = QByteArray::fromRawData(codePtr, compiledFunction->codeSize);
+#endif
+ codeRefs[i] = code;
+ }
+
+ return true;
+}
+
+QQmlRefPointer<CompiledData::CompilationUnit> ISelFactory::createUnitForLoading()
+{
+ QQmlRefPointer<CompiledData::CompilationUnit> result;
+ result.adopt(new Moth::CompilationUnit);
+ return result;
+}
diff --git a/src/qml/compiler/qv4isel_moth_p.h b/src/qml/compiler/qv4isel_moth_p.h
index 29d117af38..74323a2912 100644
--- a/src/qml/compiler/qv4isel_moth_p.h
+++ b/src/qml/compiler/qv4isel_moth_p.h
@@ -58,6 +58,8 @@
#include <private/qv4value_p.h>
#include "qv4instr_moth_p.h"
+QT_REQUIRE_CONFIG(qml_interpreter);
+
QT_BEGIN_NAMESPACE
namespace QV4 {
@@ -66,7 +68,10 @@ namespace Moth {
struct CompilationUnit : public QV4::CompiledData::CompilationUnit
{
virtual ~CompilationUnit();
- virtual void linkBackendToEngine(QV4::ExecutionEngine *engine);
+ void linkBackendToEngine(QV4::ExecutionEngine *engine) Q_DECL_OVERRIDE;
+ void prepareCodeOffsetsForDiskStorage(CompiledData::Unit *unit) Q_DECL_OVERRIDE;
+ bool saveCodeToDisk(QIODevice *device, const CompiledData::Unit *unit, QString *errorString) Q_DECL_OVERRIDE;
+ bool memoryMapCode(QString *errorString) Q_DECL_OVERRIDE;
QVector<QByteArray> codeRefs;
@@ -77,7 +82,7 @@ class Q_QML_EXPORT InstructionSelection:
public EvalInstructionSelection
{
public:
- InstructionSelection(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator);
+ InstructionSelection(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator, EvalISelFactory *iselFactory);
~InstructionSelection();
virtual void run(int functionIndex);
@@ -134,7 +139,7 @@ protected:
virtual void setProperty(IR::Expr *source, IR::Expr *targetBase, const QString &targetName);
virtual void setQmlContextProperty(IR::Expr *source, IR::Expr *targetBase, IR::Member::MemberKind kind, int propertyIndex);
virtual void setQObjectProperty(IR::Expr *source, IR::Expr *targetBase, int propertyIndex);
- virtual void getQmlContextProperty(IR::Expr *source, IR::Member::MemberKind kind, int index, IR::Expr *target);
+ virtual void getQmlContextProperty(IR::Expr *source, IR::Member::MemberKind kind, int index, bool captureRequired, IR::Expr *target);
virtual void getQObjectProperty(IR::Expr *base, int propertyIndex, bool captureRequired, bool isSingleton, int attachedPropertiesId, IR::Expr *target);
virtual void getElement(IR::Expr *base, IR::Expr *index, IR::Expr *target);
virtual void setElement(IR::Expr *source, IR::Expr *targetBase, IR::Expr *targetIndex);
@@ -174,6 +179,8 @@ private:
template <int Instr>
inline ptrdiff_t addInstruction(const InstrData<Instr> &data);
+ inline void addDebugInstruction();
+
ptrdiff_t addInstructionHelper(Instr::Type type, Instr &instr);
void patchJumpAddresses();
QByteArray squeezeCode() const;
@@ -202,11 +209,14 @@ private:
class Q_QML_EXPORT ISelFactory: public EvalISelFactory
{
public:
+ ISelFactory() : EvalISelFactory(QStringLiteral("moth")) {}
virtual ~ISelFactory() {}
- virtual EvalInstructionSelection *create(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator)
- { return new InstructionSelection(qmlEngine, execAllocator, module, jsGenerator); }
- virtual bool jitCompileRegexps() const
+ EvalInstructionSelection *create(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator) Q_DECL_OVERRIDE Q_DECL_FINAL
+ { return new InstructionSelection(qmlEngine, execAllocator, module, jsGenerator, this); }
+ bool jitCompileRegexps() const Q_DECL_OVERRIDE Q_DECL_FINAL
{ return false; }
+ QQmlRefPointer<QV4::CompiledData::CompilationUnit> createUnitForLoading() Q_DECL_OVERRIDE;
+
};
template<int InstrT>
diff --git a/src/qml/compiler/qv4isel_p.cpp b/src/qml/compiler/qv4isel_p.cpp
index 0ae08160ab..efcfb9bd77 100644
--- a/src/qml/compiler/qv4isel_p.cpp
+++ b/src/qml/compiler/qv4isel_p.cpp
@@ -52,7 +52,7 @@
using namespace QV4;
using namespace QV4::IR;
-EvalInstructionSelection::EvalInstructionSelection(QV4::ExecutableAllocator *execAllocator, Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator)
+EvalInstructionSelection::EvalInstructionSelection(QV4::ExecutableAllocator *execAllocator, Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator, EvalISelFactory *iselFactory)
: useFastLookups(true)
, useTypeInference(true)
, executableAllocator(execAllocator)
@@ -67,6 +67,7 @@ EvalInstructionSelection::EvalInstructionSelection(QV4::ExecutableAllocator *exe
Q_ASSERT(execAllocator);
#endif
Q_ASSERT(module);
+ jsGenerator->codeGeneratorName = iselFactory->codeGeneratorName;
}
EvalInstructionSelection::~EvalInstructionSelection()
@@ -146,24 +147,24 @@ void IRDecoder::visitMove(IR::Move *s)
const int attachedPropertiesId = m->attachedPropertiesId;
const bool isSingletonProperty = m->kind == IR::Member::MemberOfSingletonObject;
- if (_function && attachedPropertiesId == 0 && !m->property->isConstant()) {
+ if (_function && attachedPropertiesId == 0 && !m->property->isConstant() && _function->isQmlBinding) {
if (m->kind == IR::Member::MemberOfQmlContextObject) {
- _function->contextObjectPropertyDependencies.insert(m->property->coreIndex, m->property->notifyIndex);
+ _function->contextObjectPropertyDependencies.insert(m->property->coreIndex(), m->property->notifyIndex());
captureRequired = false;
} else if (m->kind == IR::Member::MemberOfQmlScopeObject) {
- _function->scopeObjectPropertyDependencies.insert(m->property->coreIndex, m->property->notifyIndex);
+ _function->scopeObjectPropertyDependencies.insert(m->property->coreIndex(), m->property->notifyIndex());
captureRequired = false;
}
}
if (m->kind == IR::Member::MemberOfQmlScopeObject || m->kind == IR::Member::MemberOfQmlContextObject) {
- getQmlContextProperty(m->base, (IR::Member::MemberKind)m->kind, m->property->coreIndex, s->target);
+ getQmlContextProperty(m->base, (IR::Member::MemberKind)m->kind, m->property->coreIndex(), captureRequired, s->target);
return;
}
- getQObjectProperty(m->base, m->property->coreIndex, captureRequired, isSingletonProperty, attachedPropertiesId, s->target);
+ getQObjectProperty(m->base, m->property->coreIndex(), captureRequired, isSingletonProperty, attachedPropertiesId, s->target);
#endif // V4_BOOTSTRAP
return;
} else if (m->kind == IR::Member::MemberOfIdObjectsArray) {
- getQmlContextProperty(m->base, (IR::Member::MemberKind)m->kind, m->idIndex, s->target);
+ getQmlContextProperty(m->base, (IR::Member::MemberKind)m->kind, m->idIndex, /*captureRequired*/false, s->target);
return;
} else if (m->base->asTemp() || m->base->asConst() || m->base->asArgLocal()) {
getProperty(m->base, *m->name, s->target);
@@ -186,7 +187,7 @@ void IRDecoder::visitMove(IR::Move *s)
#ifndef V4_BOOTSTRAP
Q_ASSERT(member->kind != IR::Member::MemberOfIdObjectsArray);
if (member->kind == IR::Member::MemberOfQmlScopeObject || member->kind == IR::Member::MemberOfQmlContextObject) {
- callQmlContextProperty(member->base, (IR::Member::MemberKind)member->kind, member->property->coreIndex, c->args, s->target);
+ callQmlContextProperty(member->base, (IR::Member::MemberKind)member->kind, member->property->coreIndex(), c->args, s->target);
return;
}
#endif
@@ -215,10 +216,10 @@ void IRDecoder::visitMove(IR::Move *s)
Q_UNIMPLEMENTED();
#else
if (m->kind == IR::Member::MemberOfQmlScopeObject || m->kind == IR::Member::MemberOfQmlContextObject) {
- setQmlContextProperty(s->source, m->base, (IR::Member::MemberKind)m->kind, m->property->coreIndex);
+ setQmlContextProperty(s->source, m->base, (IR::Member::MemberKind)m->kind, m->property->coreIndex());
return;
}
- setQObjectProperty(s->source, m->base, m->property->coreIndex);
+ setQObjectProperty(s->source, m->base, m->property->coreIndex());
#endif
return;
} else {
@@ -262,7 +263,7 @@ void IRDecoder::visitExp(IR::Exp *s)
#ifndef V4_BOOTSTRAP
Q_ASSERT(member->kind != IR::Member::MemberOfIdObjectsArray);
if (member->kind == IR::Member::MemberOfQmlScopeObject || member->kind == IR::Member::MemberOfQmlContextObject) {
- callQmlContextProperty(member->base, (IR::Member::MemberKind)member->kind, member->property->coreIndex, c->args, 0);
+ callQmlContextProperty(member->base, (IR::Member::MemberKind)member->kind, member->property->coreIndex(), c->args, 0);
return;
}
#endif
@@ -294,7 +295,7 @@ void IRDecoder::callBuiltin(IR::Call *call, Expr *result)
if (member->kind == IR::Member::MemberOfQmlScopeObject || member->kind == IR::Member::MemberOfQmlContextObject) {
callBuiltinTypeofQmlContextProperty(member->base,
IR::Member::MemberKind(member->kind),
- member->property->coreIndex, result);
+ member->property->coreIndex(), result);
return;
}
#endif
diff --git a/src/qml/compiler/qv4isel_p.h b/src/qml/compiler/qv4isel_p.h
index 88d2071c52..037c02e5ea 100644
--- a/src/qml/compiler/qv4isel_p.h
+++ b/src/qml/compiler/qv4isel_p.h
@@ -65,13 +65,14 @@ class QQmlEnginePrivate;
namespace QV4 {
+class EvalISelFactory;
class ExecutableAllocator;
struct Function;
class Q_QML_PRIVATE_EXPORT EvalInstructionSelection
{
public:
- EvalInstructionSelection(QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator);
+ EvalInstructionSelection(QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator, EvalISelFactory *iselFactory);
virtual ~EvalInstructionSelection() = 0;
QQmlRefPointer<QV4::CompiledData::CompilationUnit> compile(bool generateUnitData = true);
@@ -104,23 +105,44 @@ protected:
class Q_QML_PRIVATE_EXPORT EvalISelFactory
{
public:
+ EvalISelFactory(const QString &codeGeneratorName) : codeGeneratorName(codeGeneratorName) {}
virtual ~EvalISelFactory() = 0;
virtual EvalInstructionSelection *create(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator) = 0;
virtual bool jitCompileRegexps() const = 0;
+ virtual QQmlRefPointer<QV4::CompiledData::CompilationUnit> createUnitForLoading() = 0;
+
+ const QString codeGeneratorName;
};
namespace IR {
-class Q_QML_PRIVATE_EXPORT IRDecoder: protected IR::StmtVisitor
+class Q_QML_PRIVATE_EXPORT IRDecoder
{
public:
IRDecoder() : _function(0) {}
virtual ~IRDecoder() = 0;
- virtual void visitPhi(IR::Phi *) {}
-
-public: // visitor methods for StmtVisitor:
- virtual void visitMove(IR::Move *s);
- virtual void visitExp(IR::Exp *s);
+ void visit(Stmt *s)
+ {
+ if (auto e = s->asExp()) {
+ visitExp(e);
+ } else if (auto m = s->asMove()) {
+ visitMove(m);
+ } else if (auto j = s->asJump()) {
+ visitJump(j);
+ } else if (auto c = s->asCJump()) {
+ visitCJump(c);
+ } else if (auto r = s->asRet()) {
+ visitRet(r);
+ } else if (auto p = s->asPhi()) {
+ visitPhi(p);
+ } else {
+ Q_UNREACHABLE();
+ }
+ }
+
+private: // visitor methods for StmtVisitor:
+ void visitMove(IR::Move *s);
+ void visitExp(IR::Exp *s);
public: // to implement by subclasses:
virtual void callBuiltinInvalid(IR::Name *func, IR::ExprList *args, IR::Expr *result) = 0;
@@ -166,7 +188,7 @@ public: // to implement by subclasses:
virtual void initClosure(IR::Closure *closure, IR::Expr *target) = 0;
virtual void getProperty(IR::Expr *base, const QString &name, IR::Expr *target) = 0;
virtual void getQObjectProperty(IR::Expr *base, int propertyIndex, bool captureRequired, bool isSingletonProperty, int attachedPropertiesId, IR::Expr *target) = 0;
- virtual void getQmlContextProperty(IR::Expr *source, IR::Member::MemberKind kind, int index, IR::Expr *target) = 0;
+ virtual void getQmlContextProperty(IR::Expr *source, IR::Member::MemberKind kind, int index, bool captureRequired, IR::Expr *target) = 0;
virtual void setProperty(IR::Expr *source, IR::Expr *targetBase, const QString &targetName) = 0;
virtual void setQmlContextProperty(IR::Expr *source, IR::Expr *targetBase, IR::Member::MemberKind kind, int propertyIndex) = 0;
virtual void setQObjectProperty(IR::Expr *source, IR::Expr *targetBase, int propertyIndex) = 0;
@@ -178,6 +200,11 @@ public: // to implement by subclasses:
virtual void binop(IR::AluOp oper, IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *target) = 0;
protected:
+ virtual void visitJump(IR::Jump *) = 0;
+ virtual void visitCJump(IR::CJump *) = 0;
+ virtual void visitRet(IR::Ret *) = 0;
+ virtual void visitPhi(IR::Phi *) {}
+
virtual void callBuiltin(IR::Call *c, IR::Expr *result);
IR::Function *_function; // subclass needs to set
diff --git a/src/qml/compiler/qv4isel_util_p.h b/src/qml/compiler/qv4isel_util_p.h
index 674fc01623..1755193d32 100644
--- a/src/qml/compiler/qv4isel_util_p.h
+++ b/src/qml/compiler/qv4isel_util_p.h
@@ -104,7 +104,7 @@ inline Primitive convertToValue(IR::Const *c)
return Primitive::undefinedValue();
}
-class ConvertTemps: protected IR::StmtVisitor, protected IR::ExprVisitor
+class ConvertTemps
{
void renumber(IR::Temp *t)
{
@@ -132,7 +132,7 @@ protected:
virtual void process(IR::Stmt *s)
{
- s->accept(this);
+ visit(s);
}
public:
@@ -157,34 +157,28 @@ public:
}
protected:
- virtual void visitConst(IR::Const *) {}
- virtual void visitString(IR::String *) {}
- virtual void visitRegExp(IR::RegExp *) {}
- virtual void visitName(IR::Name *) {}
- virtual void visitTemp(IR::Temp *e) { renumber(e); }
- virtual void visitArgLocal(IR::ArgLocal *) {}
- virtual void visitClosure(IR::Closure *) {}
- virtual void visitConvert(IR::Convert *e) { e->expr->accept(this); }
- virtual void visitUnop(IR::Unop *e) { e->expr->accept(this); }
- virtual void visitBinop(IR::Binop *e) { e->left->accept(this); e->right->accept(this); }
- virtual void visitCall(IR::Call *e) {
- e->base->accept(this);
- for (IR::ExprList *it = e->args; it; it = it->next)
- it->expr->accept(this);
+ void visit(IR::Stmt *s) {
+ switch (s->stmtKind) {
+ case IR::Stmt::PhiStmt:
+ visitPhi(s->asPhi());
+ break;
+ default:
+ STMT_VISIT_ALL_KINDS(s);
+ break;
+ }
}
- virtual void visitNew(IR::New *e) {
- e->base->accept(this);
- for (IR::ExprList *it = e->args; it; it = it->next)
- it->expr->accept(this);
+
+ virtual void visitPhi(IR::Phi *)
+ { Q_UNREACHABLE(); }
+
+private:
+ void visit(IR::Expr *e) {
+ if (auto temp = e->asTemp()) {
+ renumber(temp);
+ } else {
+ EXPR_VISIT_ALL_KINDS(e);
+ }
}
- virtual void visitSubscript(IR::Subscript *e) { e->base->accept(this); e->index->accept(this); }
- virtual void visitMember(IR::Member *e) { e->base->accept(this); }
- virtual void visitExp(IR::Exp *s) { s->expr->accept(this); }
- virtual void visitMove(IR::Move *s) { s->target->accept(this); s->source->accept(this); }
- virtual void visitJump(IR::Jump *) {}
- virtual void visitCJump(IR::CJump *s) { s->cond->accept(this); }
- virtual void visitRet(IR::Ret *s) { s->expr->accept(this); }
- virtual void visitPhi(IR::Phi *) { Q_UNREACHABLE(); }
};
} // namespace QV4
diff --git a/src/qml/compiler/qv4jsir.cpp b/src/qml/compiler/qv4jsir.cpp
index b28db59190..5687834b00 100644
--- a/src/qml/compiler/qv4jsir.cpp
+++ b/src/qml/compiler/qv4jsir.cpp
@@ -157,12 +157,13 @@ AluOp binaryOperator(int op)
}
}
-struct RemoveSharedExpressions: IR::StmtVisitor, IR::ExprVisitor
+class RemoveSharedExpressions
{
CloneExpr clone;
std::vector<Expr *> subexpressions; // contains all the non-cloned subexpressions in the given function. sorted using std::lower_bound.
Expr *uniqueExpr;
+public:
RemoveSharedExpressions(): uniqueExpr(0) {}
void operator()(IR::Function *function)
@@ -176,11 +177,12 @@ struct RemoveSharedExpressions: IR::StmtVisitor, IR::ExprVisitor
clone.setBasicBlock(block);
for (Stmt *s : block->statements()) {
- s->accept(this);
+ visit(s);
}
}
}
+private:
template <typename Expr_>
Expr_ *cleanup(Expr_ *expr)
{
@@ -189,7 +191,7 @@ struct RemoveSharedExpressions: IR::StmtVisitor, IR::ExprVisitor
subexpressions.insert(it, expr);
IR::Expr *e = expr;
qSwap(uniqueExpr, e);
- expr->accept(this);
+ visit(expr);
qSwap(uniqueExpr, e);
return static_cast<Expr_ *>(e);
}
@@ -199,83 +201,45 @@ struct RemoveSharedExpressions: IR::StmtVisitor, IR::ExprVisitor
return clone(expr);
}
- // statements
- virtual void visitExp(Exp *s)
+ void visit(Stmt *s)
{
- s->expr = cleanup(s->expr);
- }
-
- virtual void visitMove(Move *s)
- {
- s->target = cleanup(s->target);
- s->source = cleanup(s->source);
- }
-
- virtual void visitJump(Jump *)
- {
- // nothing to do for Jump statements
- }
-
- virtual void visitCJump(CJump *s)
- {
- s->cond = cleanup(s->cond);
- }
-
- virtual void visitRet(Ret *s)
- {
- s->expr = cleanup(s->expr);
- }
-
- virtual void visitPhi(IR::Phi *) { Q_UNIMPLEMENTED(); }
-
- // expressions
- virtual void visitConst(Const *) {}
- virtual void visitString(String *) {}
- virtual void visitRegExp(RegExp *) {}
- virtual void visitName(Name *) {}
- virtual void visitTemp(Temp *) {}
- virtual void visitArgLocal(ArgLocal *) {}
- virtual void visitClosure(Closure *) {}
-
- virtual void visitConvert(Convert *e)
- {
- e->expr = cleanup(e->expr);
- }
-
- virtual void visitUnop(Unop *e)
- {
- e->expr = cleanup(e->expr);
- }
-
- virtual void visitBinop(Binop *e)
- {
- e->left = cleanup(e->left);
- e->right = cleanup(e->right);
- }
-
- virtual void visitCall(Call *e)
- {
- e->base = cleanup(e->base);
- for (IR::ExprList *it = e->args; it; it = it->next)
- it->expr = cleanup(it->expr);
- }
-
- virtual void visitNew(New *e)
- {
- e->base = cleanup(e->base);
- for (IR::ExprList *it = e->args; it; it = it->next)
- it->expr = cleanup(it->expr);
- }
-
- virtual void visitSubscript(Subscript *e)
- {
- e->base = cleanup(e->base);
- e->index = cleanup(e->index);
+ if (auto e = s->asExp()) {
+ e->expr = cleanup(e->expr);
+ } else if (auto m = s->asMove()) {
+ m->target = cleanup(m->target);
+ m->source = cleanup(m->source);
+ } else if (auto c = s->asCJump()) {
+ c->cond = cleanup(c->cond);
+ } else if (auto r = s->asRet()) {
+ r->expr = cleanup(r->expr);
+ }
}
- virtual void visitMember(Member *e)
+ void visit(Expr *e)
{
- e->base = cleanup(e->base);
+ if (auto c = e->asConvert()) {
+ c->expr = cleanup(c->expr);
+ } else if (auto u = e->asUnop()) {
+ u->expr = cleanup(u->expr);
+ } else if (auto b = e->asBinop()) {
+ b->left = cleanup(b->left);
+ b->right = cleanup(b->right);
+ } else if (auto c = e->asCall()) {
+ c->base = cleanup(c->base);
+ for (IR::ExprList *it = c->args; it; it = it->next) {
+ it->expr = cleanup(it->expr);
+ }
+ } else if (auto n = e->asNew()) {
+ n->base = cleanup(n->base);
+ for (IR::ExprList *it = n->args; it; it = it->next) {
+ it->expr = cleanup(it->expr);
+ }
+ } else if (auto s = e->asSubscript()) {
+ s->base = cleanup(s->base);
+ s->index = cleanup(s->index);
+ } else if (auto m = e->asMember()) {
+ m->base = cleanup(m->base);
+ }
}
};
@@ -404,9 +368,10 @@ Function::Function(Module *module, Function *outer, const QString &name)
, isNamedExpression(false)
, hasTry(false)
, hasWith(false)
+ , isQmlBinding(false)
, unused(0)
- , line(-1)
- , column(-1)
+ , line(0)
+ , column(0)
, _allBasicBlocks(0)
, _statementCount(0)
{
@@ -548,75 +513,39 @@ ExprList *CloneExpr::clone(ExprList *list)
return clonedList;
}
-void CloneExpr::visitConst(Const *e)
-{
- cloned = cloneConst(e, block->function);
-}
-
-void CloneExpr::visitString(String *e)
-{
- cloned = block->STRING(e->value);
-}
-
-void CloneExpr::visitRegExp(RegExp *e)
-{
- cloned = block->REGEXP(e->value, e->flags);
-}
-
-void CloneExpr::visitName(Name *e)
-{
- cloned = cloneName(e, block->function);
-}
-
-void CloneExpr::visitTemp(Temp *e)
-{
- cloned = cloneTemp(e, block->function);
-}
-
-void CloneExpr::visitArgLocal(ArgLocal *e)
-{
- cloned = cloneArgLocal(e, block->function);
-}
-
-void CloneExpr::visitClosure(Closure *e)
-{
- cloned = block->CLOSURE(e->value);
-}
-
-void CloneExpr::visitConvert(Convert *e)
-{
- cloned = block->CONVERT(clone(e->expr), e->type);
-}
-
-void CloneExpr::visitUnop(Unop *e)
-{
- cloned = block->UNOP(e->op, clone(e->expr));
-}
-
-void CloneExpr::visitBinop(Binop *e)
-{
- cloned = block->BINOP(e->op, clone(e->left), clone(e->right));
-}
-
-void CloneExpr::visitCall(Call *e)
-{
- cloned = block->CALL(clone(e->base), clone(e->args));
-}
-
-void CloneExpr::visitNew(New *e)
-{
- cloned = block->NEW(clone(e->base), clone(e->args));
-}
-
-void CloneExpr::visitSubscript(Subscript *e)
-{
- cloned = block->SUBSCRIPT(clone(e->base), clone(e->index));
-}
-
-void CloneExpr::visitMember(Member *e)
-{
- Expr *clonedBase = clone(e->base);
- cloned = block->MEMBER(clonedBase, e->name, e->property, e->kind, e->idIndex);
+void CloneExpr::visit(Expr *e)
+{
+ if (auto c = e->asConst()) {
+ cloned = cloneConst(c, block->function);
+ } else if (auto s = e->asString()) {
+ cloned = block->STRING(s->value);
+ } else if (auto r = e->asRegExp()) {
+ cloned = block->REGEXP(r->value, r->flags);
+ } else if (auto n = e->asName()) {
+ cloned = cloneName(n, block->function);
+ } else if (auto t = e->asTemp()) {
+ cloned = cloneTemp(t, block->function);
+ } else if (auto a = e->asArgLocal()) {
+ cloned = cloneArgLocal(a, block->function);
+ } else if (auto c = e->asClosure()) {
+ cloned = block->CLOSURE(c->value);
+ } else if (auto c = e->asConvert()) {
+ cloned = block->CONVERT(clone(c->expr), c->type);
+ } else if (auto u = e->asUnop()) {
+ cloned = block->UNOP(u->op, clone(u->expr));
+ } else if (auto b = e->asBinop()) {
+ cloned = block->BINOP(b->op, clone(b->left), clone(b->right));
+ } else if (auto c = e->asCall()) {
+ cloned = block->CALL(clone(c->base), clone(c->args));
+ } else if (auto n = e->asNew()) {
+ cloned = block->NEW(clone(n->base), clone(n->args));
+ } else if (auto s = e->asSubscript()) {
+ cloned = block->SUBSCRIPT(clone(s->base), clone(s->index));
+ } else if (auto m = e->asMember()) {
+ cloned = block->MEMBER(clone(m->base), m->name, m->property, m->kind, m->idIndex);
+ } else {
+ Q_UNREACHABLE();
+ }
}
IRPrinter::IRPrinter(QTextStream *out)
@@ -632,17 +561,17 @@ IRPrinter::~IRPrinter()
void IRPrinter::print(Stmt *s)
{
- s->accept(this);
+ visit(s);
}
void IRPrinter::print(const Expr &e)
{
- const_cast<Expr *>(&e)->accept(this);
+ visit(const_cast<Expr *>(&e));
}
void IRPrinter::print(Expr *e)
{
- e->accept(this);
+ visit(e);
}
void IRPrinter::print(Function *f)
@@ -696,7 +625,7 @@ void IRPrinter::print(BasicBlock *bb)
QTextStream *prevOut = &os;
std::swap(out, prevOut);
addStmtNr(s);
- s->accept(this);
+ visit(s);
if (s->location.startLine) {
out->flush();
for (int i = 58 - str.length(); i > 0; --i)
@@ -713,10 +642,29 @@ void IRPrinter::print(BasicBlock *bb)
std::swap(currentBB, bb);
}
+void IRPrinter::visit(Stmt *s)
+{
+ if (auto e = s->asExp()) {
+ visitExp(e);
+ } else if (auto m = s->asMove()) {
+ visitMove(m);
+ } else if (auto j = s->asJump()) {
+ visitJump(j);
+ } else if (auto c = s->asCJump()) {
+ visitCJump(c);
+ } else if (auto r = s->asRet()) {
+ visitRet(r);
+ } else if (auto p = s->asPhi()) {
+ visitPhi(p);
+ } else {
+ Q_UNREACHABLE();
+ }
+}
+
void IRPrinter::visitExp(Exp *s)
{
*out << "void ";
- s->expr->accept(this);
+ visit(s->expr);
}
void IRPrinter::visitMove(Move *s)
@@ -725,13 +673,13 @@ void IRPrinter::visitMove(Move *s)
if (!s->swap && targetTemp->type != UnknownType)
*out << typeName(targetTemp->type) << ' ';
- s->target->accept(this);
+ visit(s->target);
*out << ' ';
if (s->swap)
*out << "<=> ";
else
*out << "= ";
- s->source->accept(this);
+ visit(s->source);
}
void IRPrinter::visitJump(Jump *s)
@@ -742,7 +690,7 @@ void IRPrinter::visitJump(Jump *s)
void IRPrinter::visitCJump(CJump *s)
{
*out << "if ";
- s->cond->accept(this);
+ visit(s->cond);
*out << " goto L" << s->iftrue->index()
<< " else goto L" << s->iffalse->index();
}
@@ -752,7 +700,7 @@ void IRPrinter::visitRet(Ret *s)
*out << "return";
if (s->expr) {
*out << ' ';
- s->expr->accept(this);
+ visit(s->expr);
}
}
@@ -761,7 +709,7 @@ void IRPrinter::visitPhi(Phi *s)
if (s->targetTemp->type != UnknownType)
*out << typeName(s->targetTemp->type) << ' ';
- s->targetTemp->accept(this);
+ visit(s->targetTemp);
*out << " = phi ";
for (int i = 0, ei = s->incoming.size(); i < ei; ++i) {
if (i > 0)
@@ -769,7 +717,42 @@ void IRPrinter::visitPhi(Phi *s)
if (currentBB)
*out << 'L' << currentBB->in.at(i)->index() << ": ";
if (s->incoming[i])
- s->incoming[i]->accept(this);
+ visit(s->incoming[i]);
+ }
+}
+
+void IRPrinter::visit(Expr *e)
+{
+ if (auto c = e->asConst()) {
+ visitConst(c);
+ } else if (auto s = e->asString()) {
+ visitString(s);
+ } else if (auto r = e->asRegExp()) {
+ visitRegExp(r);
+ } else if (auto n = e->asName()) {
+ visitName(n);
+ } else if (auto t = e->asTemp()) {
+ visitTemp(t);
+ } else if (auto a = e->asArgLocal()) {
+ visitArgLocal(a);
+ } else if (auto c = e->asClosure()) {
+ visitClosure(c);
+ } else if (auto c = e->asConvert()) {
+ visitConvert(c);
+ } else if (auto u = e->asUnop()) {
+ visitUnop(u);
+ } else if (auto b = e->asBinop()) {
+ visitBinop(b);
+ } else if (auto c = e->asCall()) {
+ visitCall(c);
+ } else if (auto n = e->asNew()) {
+ visitNew(n);
+ } else if (auto s = e->asSubscript()) {
+ visitSubscript(s);
+ } else if (auto m = e->asMember()) {
+ visitMember(m);
+ } else {
+ Q_UNREACHABLE();
}
}
@@ -867,32 +850,32 @@ void IRPrinter::visitClosure(Closure *e)
void IRPrinter::visitConvert(Convert *e)
{
*out << "convert " << typeName(e->expr->type) << " to " << typeName(e->type) << ' ';
- e->expr->accept(this);
+ visit(e->expr);
}
void IRPrinter::visitUnop(Unop *e)
{
*out << opname(e->op) << ' ';
- e->expr->accept(this);
+ visit(e->expr);
}
void IRPrinter::visitBinop(Binop *e)
{
*out << opname(e->op) << ' ';
- e->left->accept(this);
+ visit(e->left);
*out << ", ";
- e->right->accept(this);
+ visit(e->right);
}
void IRPrinter::visitCall(Call *e)
{
*out << "call ";
- e->base->accept(this);
+ visit(e->base);
*out << '(';
for (ExprList *it = e->args; it; it = it->next) {
if (it != e->args)
*out << ", ";
- it->expr->accept(this);
+ visit(it->expr);
}
*out << ')';
}
@@ -900,21 +883,21 @@ void IRPrinter::visitCall(Call *e)
void IRPrinter::visitNew(New *e)
{
*out << "new ";
- e->base->accept(this);
+ visit(e->base);
*out << '(';
for (ExprList *it = e->args; it; it = it->next) {
if (it != e->args)
*out << ", ";
- it->expr->accept(this);
+ visit(it->expr);
}
*out << ')';
}
void IRPrinter::visitSubscript(Subscript *e)
{
- e->base->accept(this);
+ visit(e->base);
*out << '[';
- e->index->accept(this);
+ visit(e->index);
*out << ']';
}
@@ -924,12 +907,12 @@ void IRPrinter::visitMember(Member *e)
&& e->attachedPropertiesId != 0 && !e->base->asTemp())
*out << "[[attached property from " << e->attachedPropertiesId << "]]";
else
- e->base->accept(this);
+ visit(e->base);
*out << '.' << *e->name;
#ifndef V4_BOOTSTRAP
if (e->property)
- *out << " (meta-property " << e->property->coreIndex
- << " <" << QMetaType::typeName(e->property->propType)
+ *out << " (meta-property " << e->property->coreIndex()
+ << " <" << QMetaType::typeName(e->property->propType())
<< ">)";
else if (e->kind == Member::MemberOfIdObjectsArray)
*out << "(id object " << e->idIndex << ")";
@@ -942,15 +925,15 @@ QString IRPrinter::escape(const QString &s)
for (int i = 0; i < s.length(); ++i) {
const QChar ch = s.at(i);
if (ch == QLatin1Char('\n'))
- r += QStringLiteral("\\n");
+ r += QLatin1String("\\n");
else if (ch == QLatin1Char('\r'))
- r += QStringLiteral("\\r");
+ r += QLatin1String("\\r");
else if (ch == QLatin1Char('\\'))
- r += QStringLiteral("\\\\");
+ r += QLatin1String("\\\\");
else if (ch == QLatin1Char('"'))
- r += QStringLiteral("\\\"");
+ r += QLatin1String("\\\"");
else if (ch == QLatin1Char('\''))
- r += QStringLiteral("\\'");
+ r += QLatin1String("\\'");
else
r += ch;
}
diff --git a/src/qml/compiler/qv4jsir_p.h b/src/qml/compiler/qv4jsir_p.h
index 94fa65cf71..73aa6c4975 100644
--- a/src/qml/compiler/qv4jsir_p.h
+++ b/src/qml/compiler/qv4jsir_p.h
@@ -192,7 +192,7 @@ enum AluOp {
AluOp binaryOperator(int op);
const char *opname(IR::AluOp op);
-enum Type {
+enum Type : quint16 {
UnknownType = 0,
MissingType = 1 << 0,
@@ -217,34 +217,6 @@ inline bool strictlyEqualTypes(Type t1, Type t2)
QString typeName(Type t);
-struct ExprVisitor {
- virtual ~ExprVisitor() {}
- virtual void visitConst(Const *) = 0;
- virtual void visitString(String *) = 0;
- virtual void visitRegExp(RegExp *) = 0;
- virtual void visitName(Name *) = 0;
- virtual void visitTemp(Temp *) = 0;
- virtual void visitArgLocal(ArgLocal *) = 0;
- virtual void visitClosure(Closure *) = 0;
- virtual void visitConvert(Convert *) = 0;
- virtual void visitUnop(Unop *) = 0;
- virtual void visitBinop(Binop *) = 0;
- virtual void visitCall(Call *) = 0;
- virtual void visitNew(New *) = 0;
- virtual void visitSubscript(Subscript *) = 0;
- virtual void visitMember(Member *) = 0;
-};
-
-struct StmtVisitor {
- virtual ~StmtVisitor() {}
- virtual void visitExp(Exp *) = 0;
- virtual void visitMove(Move *) = 0;
- virtual void visitJump(Jump *) = 0;
- virtual void visitCJump(CJump *) = 0;
- virtual void visitRet(Ret *) = 0;
- virtual void visitPhi(Phi *) = 0;
-};
-
struct MemberExpressionResolver;
struct DiscoveredType {
@@ -288,28 +260,129 @@ struct MemberExpressionResolver
};
struct Q_AUTOTEST_EXPORT Expr {
+ enum ExprKind : quint8 {
+ NameExpr,
+ TempExpr,
+ ArgLocalExpr,
+ SubscriptExpr,
+ MemberExpr,
+
+ LastLValue = MemberExpr,
+
+ ConstExpr,
+ StringExpr,
+ RegExpExpr,
+ ClosureExpr,
+ ConvertExpr,
+ UnopExpr,
+ BinopExpr,
+ CallExpr,
+ NewExpr
+ };
+
Type type;
+ const ExprKind exprKind;
+
+ Expr &operator=(const Expr &other) {
+ Q_ASSERT(exprKind == other.exprKind);
+ type = other.type;
+ return *this;
+ }
+
+ template <typename To>
+ inline bool isa() const {
+ return To::classof(this);
+ }
- Expr(): type(UnknownType) {}
- virtual ~Expr() {}
- virtual void accept(ExprVisitor *) = 0;
- virtual bool isLValue() { return false; }
- virtual Const *asConst() { return 0; }
- virtual String *asString() { return 0; }
- virtual RegExp *asRegExp() { return 0; }
- virtual Name *asName() { return 0; }
- virtual Temp *asTemp() { return 0; }
- virtual ArgLocal *asArgLocal() { return 0; }
- virtual Closure *asClosure() { return 0; }
- virtual Convert *asConvert() { return 0; }
- virtual Unop *asUnop() { return 0; }
- virtual Binop *asBinop() { return 0; }
- virtual Call *asCall() { return 0; }
- virtual New *asNew() { return 0; }
- virtual Subscript *asSubscript() { return 0; }
- virtual Member *asMember() { return 0; }
+ template <typename To>
+ inline To *as() {
+ if (isa<To>()) {
+ return static_cast<To *>(this);
+ } else {
+ return nullptr;
+ }
+ }
+
+ template <typename To>
+ inline const To *as() const {
+ if (isa<To>()) {
+ return static_cast<const To *>(this);
+ } else {
+ return nullptr;
+ }
+ }
+
+ Expr(ExprKind exprKind): type(UnknownType), exprKind(exprKind) {}
+ bool isLValue() const;
+
+ Const *asConst() { return as<Const>(); }
+ String *asString() { return as<String>(); }
+ RegExp *asRegExp() { return as<RegExp>(); }
+ Name *asName() { return as<Name>(); }
+ Temp *asTemp() { return as<Temp>(); }
+ ArgLocal *asArgLocal() { return as<ArgLocal>(); }
+ Closure *asClosure() { return as<Closure>(); }
+ Convert *asConvert() { return as<Convert>(); }
+ Unop *asUnop() { return as<Unop>(); }
+ Binop *asBinop() { return as<Binop>(); }
+ Call *asCall() { return as<Call>(); }
+ New *asNew() { return as<New>(); }
+ Subscript *asSubscript() { return as<Subscript>(); }
+ Member *asMember() { return as<Member>(); }
};
+#define EXPR_VISIT_ALL_KINDS(e) \
+ switch (e->exprKind) { \
+ case QV4::IR::Expr::ConstExpr: \
+ break; \
+ case QV4::IR::Expr::StringExpr: \
+ break; \
+ case QV4::IR::Expr::RegExpExpr: \
+ break; \
+ case QV4::IR::Expr::NameExpr: \
+ break; \
+ case QV4::IR::Expr::TempExpr: \
+ break; \
+ case QV4::IR::Expr::ArgLocalExpr: \
+ break; \
+ case QV4::IR::Expr::ClosureExpr: \
+ break; \
+ case QV4::IR::Expr::ConvertExpr: { \
+ auto casted = e->asConvert(); \
+ visit(casted->expr); \
+ } break; \
+ case QV4::IR::Expr::UnopExpr: { \
+ auto casted = e->asUnop(); \
+ visit(casted->expr); \
+ } break; \
+ case QV4::IR::Expr::BinopExpr: { \
+ auto casted = e->asBinop(); \
+ visit(casted->left); \
+ visit(casted->right); \
+ } break; \
+ case QV4::IR::Expr::CallExpr: { \
+ auto casted = e->asCall(); \
+ visit(casted->base); \
+ for (QV4::IR::ExprList *it = casted->args; it; it = it->next) \
+ visit(it->expr); \
+ } break; \
+ case QV4::IR::Expr::NewExpr: { \
+ auto casted = e->asNew(); \
+ visit(casted->base); \
+ for (QV4::IR::ExprList *it = casted->args; it; it = it->next) \
+ visit(it->expr); \
+ } break; \
+ case QV4::IR::Expr::SubscriptExpr: { \
+ auto casted = e->asSubscript(); \
+ visit(casted->base); \
+ visit(casted->index); \
+ } break; \
+ case QV4::IR::Expr::MemberExpr: { \
+ auto casted = e->asMember(); \
+ visit(casted->base); \
+ } break; \
+ }
+
struct ExprList {
Expr *expr;
ExprList *next;
@@ -326,26 +399,28 @@ struct ExprList {
struct Const: Expr {
double value;
+ Const(): Expr(ConstExpr) {}
+
void init(Type type, double value)
{
this->type = type;
this->value = value;
}
- virtual void accept(ExprVisitor *v) { v->visitConst(this); }
- virtual Const *asConst() { return this; }
+ static bool classof(const Expr *c) { return c->exprKind == ConstExpr; }
};
struct String: Expr {
const QString *value;
+ String(): Expr(StringExpr) {}
+
void init(const QString *value)
{
this->value = value;
}
- virtual void accept(ExprVisitor *v) { v->visitString(this); }
- virtual String *asString() { return this; }
+ static bool classof(const Expr *c) { return c->exprKind == StringExpr; }
};
struct RegExp: Expr {
@@ -359,14 +434,15 @@ struct RegExp: Expr {
const QString *value;
int flags;
+ RegExp(): Expr(RegExpExpr) {}
+
void init(const QString *value, int flags)
{
this->value = value;
this->flags = flags;
}
- virtual void accept(ExprVisitor *v) { v->visitRegExp(this); }
- virtual RegExp *asRegExp() { return this; }
+ static bool classof(const Expr *c) { return c->exprKind == RegExpExpr; }
};
struct Name: Expr {
@@ -399,13 +475,13 @@ struct Name: Expr {
quint32 line;
quint32 column;
+ Name(): Expr(NameExpr) {}
+
void initGlobal(const QString *id, quint32 line, quint32 column);
void init(const QString *id, quint32 line, quint32 column);
void init(Builtin builtin, quint32 line, quint32 column);
- virtual void accept(ExprVisitor *v) { v->visitName(this); }
- virtual bool isLValue() { return true; }
- virtual Name *asName() { return this; }
+ static bool classof(const Expr *c) { return c->exprKind == NameExpr; }
};
struct Q_AUTOTEST_EXPORT Temp: Expr {
@@ -424,7 +500,8 @@ struct Q_AUTOTEST_EXPORT Temp: Expr {
MemberExpressionResolver *memberResolver;
Temp()
- : index((1 << 28) - 1)
+ : Expr(TempExpr)
+ , index((1 << 28) - 1)
, isReadOnly(0)
, kind(Invalid)
, memberResolver(0)
@@ -438,9 +515,8 @@ struct Q_AUTOTEST_EXPORT Temp: Expr {
}
bool isInvalid() const { return kind == Invalid; }
- virtual void accept(ExprVisitor *v) { v->visitTemp(this); }
- virtual bool isLValue() { return !isReadOnly; }
- virtual Temp *asTemp() { return this; }
+
+ static bool classof(const Expr *c) { return c->exprKind == TempExpr; }
};
inline bool operator==(const Temp &t1, const Temp &t2) Q_DECL_NOTHROW
@@ -479,44 +555,48 @@ struct Q_AUTOTEST_EXPORT ArgLocal: Expr {
this->isArgumentsOrEval = false;
}
- virtual void accept(ExprVisitor *v) { v->visitArgLocal(this); }
- virtual bool isLValue() { return true; }
- virtual ArgLocal *asArgLocal() { return this; }
+ ArgLocal(): Expr(ArgLocalExpr) {}
bool operator==(const ArgLocal &other) const
{ return index == other.index && scope == other.scope && kind == other.kind; }
+
+ static bool classof(const Expr *c) { return c->exprKind == ArgLocalExpr; }
};
struct Closure: Expr {
int value; // index in _module->functions
const QString *functionName;
+ Closure(): Expr(ClosureExpr) {}
+
void init(int functionInModule, const QString *functionName)
{
this->value = functionInModule;
this->functionName = functionName;
}
- virtual void accept(ExprVisitor *v) { v->visitClosure(this); }
- virtual Closure *asClosure() { return this; }
+ static bool classof(const Expr *c) { return c->exprKind == ClosureExpr; }
};
struct Convert: Expr {
Expr *expr;
+ Convert(): Expr(ConvertExpr) {}
+
void init(Expr *expr, Type type)
{
this->expr = expr;
this->type = type;
}
- virtual void accept(ExprVisitor *v) { v->visitConvert(this); }
- virtual Convert *asConvert() { return this; }
+ static bool classof(const Expr *c) { return c->exprKind == ConvertExpr; }
};
struct Unop: Expr {
- AluOp op;
Expr *expr;
+ AluOp op;
+
+ Unop(): Expr(UnopExpr) {}
void init(AluOp op, Expr *expr)
{
@@ -524,14 +604,15 @@ struct Unop: Expr {
this->expr = expr;
}
- virtual void accept(ExprVisitor *v) { v->visitUnop(this); }
- virtual Unop *asUnop() { return this; }
+ static bool classof(const Expr *c) { return c->exprKind == UnopExpr; }
};
struct Binop: Expr {
- AluOp op;
Expr *left; // Temp or Const
Expr *right; // Temp or Const
+ AluOp op;
+
+ Binop(): Expr(BinopExpr) {}
void init(AluOp op, Expr *left, Expr *right)
{
@@ -540,14 +621,15 @@ struct Binop: Expr {
this->right = right;
}
- virtual void accept(ExprVisitor *v) { v->visitBinop(this); }
- virtual Binop *asBinop() { return this; }
+ static bool classof(const Expr *c) { return c->exprKind == BinopExpr; }
};
struct Call: Expr {
Expr *base; // Name, Member, Temp
ExprList *args; // List of Temps
+ Call(): Expr(CallExpr) {}
+
void init(Expr *base, ExprList *args)
{
this->base = base;
@@ -560,14 +642,15 @@ struct Call: Expr {
return 0;
}
- virtual void accept(ExprVisitor *v) { v->visitCall(this); }
- virtual Call *asCall() { return this; }
+ static bool classof(const Expr *c) { return c->exprKind == CallExpr; }
};
struct New: Expr {
Expr *base; // Name, Member, Temp
ExprList *args; // List of Temps
+ New(): Expr(NewExpr) {}
+
void init(Expr *base, ExprList *args)
{
this->base = base;
@@ -580,23 +663,22 @@ struct New: Expr {
return 0;
}
- virtual void accept(ExprVisitor *v) { v->visitNew(this); }
- virtual New *asNew() { return this; }
+ static bool classof(const Expr *c) { return c->exprKind == NewExpr; }
};
struct Subscript: Expr {
Expr *base;
Expr *index;
+ Subscript(): Expr(SubscriptExpr) {}
+
void init(Expr *base, Expr *index)
{
this->base = base;
this->index = index;
}
- virtual void accept(ExprVisitor *v) { v->visitSubscript(this); }
- virtual bool isLValue() { return true; }
- virtual Subscript *asSubscript() { return this; }
+ static bool classof(const Expr *c) { return c->exprKind == SubscriptExpr; }
};
struct Member: Expr {
@@ -628,6 +710,8 @@ struct Member: Expr {
uchar kind: 3; // MemberKind
+ Member(): Expr(MemberExpr) {}
+
void setEnumValue(int value) {
kind = MemberOfEnum;
enumValue = value;
@@ -649,35 +733,52 @@ struct Member: Expr {
this->kind = kind;
}
- virtual void accept(ExprVisitor *v) { v->visitMember(this); }
- virtual bool isLValue() { return true; }
- virtual Member *asMember() { return this; }
+ static bool classof(const Expr *c) { return c->exprKind == MemberExpr; }
};
+inline bool Expr::isLValue() const {
+ if (auto t = as<Temp>())
+ return !t->isReadOnly;
+ return exprKind <= LastLValue;
+}
+
struct Stmt {
+ enum StmtKind: quint8 {
+ MoveStmt,
+ ExpStmt,
+ JumpStmt,
+ CJumpStmt,
+ RetStmt,
+ PhiStmt
+ };
+
+ template <typename To>
+ inline bool isa() const {
+ return To::classof(this);
+ }
+
+ template <typename To>
+ inline To *as() {
+ if (isa<To>())
+ return static_cast<To *>(this);
+ else
+ return nullptr;
+ }
+
enum { InvalidId = -1 };
QQmlJS::AST::SourceLocation location;
- explicit Stmt(int id): _id(id) {}
+ explicit Stmt(int id, StmtKind stmtKind): _id(id), stmtKind(stmtKind) {}
- virtual ~Stmt()
- {
-#ifdef Q_CC_MSVC
- // MSVC complains about potential memory leaks if a destructor never returns.
-#else
- Q_UNREACHABLE();
-#endif
- }
- virtual Stmt *asTerminator() { return 0; }
+ Stmt *asTerminator();
- virtual void accept(StmtVisitor *) = 0;
- virtual Exp *asExp() { return 0; }
- virtual Move *asMove() { return 0; }
- virtual Jump *asJump() { return 0; }
- virtual CJump *asCJump() { return 0; }
- virtual Ret *asRet() { return 0; }
- virtual Phi *asPhi() { return 0; }
+ Exp *asExp() { return as<Exp>(); }
+ Move *asMove() { return as<Move>(); }
+ Jump *asJump() { return as<Jump>(); }
+ CJump *asCJump() { return as<CJump>(); }
+ Ret *asRet() { return as<Ret>(); }
+ Phi *asPhi() { return as<Phi>(); }
int id() const { return _id; }
@@ -687,21 +788,52 @@ private: // For memory management in BasicBlock
private:
friend struct Function;
int _id;
+
+public:
+ const StmtKind stmtKind;
};
+#define STMT_VISIT_ALL_KINDS(s) \
+ switch (s->stmtKind) { \
+ case QV4::IR::Stmt::MoveStmt: { \
+ auto casted = s->asMove(); \
+ visit(casted->target); \
+ visit(casted->source); \
+ } break; \
+ case QV4::IR::Stmt::ExpStmt: { \
+ auto casted = s->asExp(); \
+ visit(casted->expr); \
+ } break; \
+ case QV4::IR::Stmt::JumpStmt: \
+ break; \
+ case QV4::IR::Stmt::CJumpStmt: { \
+ auto casted = s->asCJump(); \
+ visit(casted->cond); \
+ } break; \
+ case QV4::IR::Stmt::RetStmt: { \
+ auto casted = s->asRet(); \
+ visit(casted->expr); \
+ } break; \
+ case QV4::IR::Stmt::PhiStmt: { \
+ auto casted = s->asPhi(); \
+ visit(casted->targetTemp); \
+ for (auto *e : casted->incoming) { \
+ visit(e); \
+ } \
+ } break; \
+ }
+
struct Exp: Stmt {
Expr *expr;
- Exp(int id): Stmt(id) {}
+ Exp(int id): Stmt(id, ExpStmt) {}
void init(Expr *expr)
{
this->expr = expr;
}
- virtual void accept(StmtVisitor *v) { v->visitExp(this); }
- virtual Exp *asExp() { return this; }
-
+ static bool classof(const Stmt *c) { return c->stmtKind == ExpStmt; }
};
struct Move: Stmt {
@@ -709,7 +841,7 @@ struct Move: Stmt {
Expr *source;
bool swap;
- Move(int id): Stmt(id) {}
+ Move(int id): Stmt(id, MoveStmt) {}
void init(Expr *target, Expr *source)
{
@@ -718,25 +850,20 @@ struct Move: Stmt {
this->swap = false;
}
- virtual void accept(StmtVisitor *v) { v->visitMove(this); }
- virtual Move *asMove() { return this; }
-
+ static bool classof(const Stmt *c) { return c->stmtKind == MoveStmt; }
};
struct Jump: Stmt {
BasicBlock *target;
- Jump(int id): Stmt(id) {}
+ Jump(int id): Stmt(id, JumpStmt) {}
void init(BasicBlock *target)
{
this->target = target;
}
- virtual Stmt *asTerminator() { return this; }
-
- virtual void accept(StmtVisitor *v) { v->visitJump(this); }
- virtual Jump *asJump() { return this; }
+ static bool classof(const Stmt *c) { return c->stmtKind == JumpStmt; }
};
struct CJump: Stmt {
@@ -745,7 +872,7 @@ struct CJump: Stmt {
BasicBlock *iffalse;
BasicBlock *parent;
- CJump(int id): Stmt(id) {}
+ CJump(int id): Stmt(id, CJumpStmt) {}
void init(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse, BasicBlock *parent)
{
@@ -755,26 +882,20 @@ struct CJump: Stmt {
this->parent = parent;
}
- virtual Stmt *asTerminator() { return this; }
-
- virtual void accept(StmtVisitor *v) { v->visitCJump(this); }
- virtual CJump *asCJump() { return this; }
+ static bool classof(const Stmt *c) { return c->stmtKind == CJumpStmt; }
};
struct Ret: Stmt {
Expr *expr;
- Ret(int id): Stmt(id) {}
+ Ret(int id): Stmt(id, RetStmt) {}
void init(Expr *expr)
{
this->expr = expr;
}
- virtual Stmt *asTerminator() { return this; }
-
- virtual void accept(StmtVisitor *v) { v->visitRet(this); }
- virtual Ret *asRet() { return this; }
+ static bool classof(const Stmt *c) { return c->stmtKind == RetStmt; }
};
// Phi nodes can only occur at the start of a basic block. If there are any, they need to be
@@ -785,30 +906,54 @@ struct Phi: Stmt {
Temp *targetTemp;
VarLengthArray<Expr *, 4> incoming;
- Phi(int id): Stmt(id) {}
+ Phi(int id): Stmt(id, PhiStmt) {}
- virtual void accept(StmtVisitor *v) { v->visitPhi(this); }
- virtual Phi *asPhi() { return this; }
+ static bool classof(const Stmt *c) { return c->stmtKind == PhiStmt; }
void destroyData()
{ incoming.~VarLengthArray(); }
};
+inline Stmt *Stmt::asTerminator()
+{
+ if (auto s = asJump()) {
+ return s;
+ } else if (auto s = asCJump()) {
+ return s;
+ } else if (auto s = asRet()) {
+ return s;
+ } else {
+ return nullptr;
+ }
+}
+
struct Q_QML_PRIVATE_EXPORT Module {
QQmlJS::MemoryPool pool;
QVector<Function *> functions;
Function *rootFunction;
QString fileName;
+ qint64 sourceTimeStamp;
bool isQmlModule; // implies rootFunction is always 0
+ uint unitFlags; // flags merged into CompiledData::Unit::flags
+#ifdef QT_NO_QML_DEBUGGER
+ static const bool debugMode = false;
+#else
bool debugMode;
+#endif
Function *newFunction(const QString &name, Function *outer);
Module(bool debugMode)
: rootFunction(0)
+ , sourceTimeStamp(0)
, isQmlModule(false)
+ , unitFlags(0)
+#ifndef QT_NO_QML_DEBUGGER
, debugMode(debugMode)
{}
+#else
+ { Q_UNUSED(debugMode); }
+#endif
~Module();
void setFileName(const QString &name);
@@ -1060,6 +1205,20 @@ private:
unsigned _isRemoved : 1;
};
+template <typename T>
+class SmallSet: public QVarLengthArray<T, 8>
+{
+public:
+ void insert(int value)
+ {
+ for (auto it : *this) {
+ if (it == value)
+ return;
+ }
+ this->append(value);
+ }
+};
+
// Map from meta property index (existence implies dependency) to notify signal index
struct KeyValuePair
{
@@ -1125,14 +1284,15 @@ struct Function {
uint isNamedExpression : 1;
uint hasTry: 1;
uint hasWith: 1;
- uint unused : 25;
+ uint isQmlBinding: 1;
+ uint unused : 24;
- // Location of declaration in source code (-1 if not specified)
- int line;
- int column;
+ // Location of declaration in source code (0 if not specified)
+ uint line;
+ uint column;
// Qml extension:
- QSet<int> idObjectDependencies;
+ SmallSet<int> idObjectDependencies;
PropertyDependencyMap contextObjectPropertyDependencies;
PropertyDependencyMap scopeObjectPropertyDependencies;
@@ -1192,7 +1352,7 @@ private:
int _statementCount;
};
-class CloneExpr: protected IR::ExprVisitor
+class CloneExpr
{
public:
explicit CloneExpr(IR::BasicBlock *block = 0);
@@ -1210,7 +1370,7 @@ public:
{
Expr *c = expr;
qSwap(cloned, c);
- expr->accept(this);
+ visit(expr);
qSwap(cloned, c);
return static_cast<ExprSubclass *>(c);
}
@@ -1253,23 +1413,10 @@ public:
return newArgLocal;
}
-protected:
+private:
IR::ExprList *clone(IR::ExprList *list);
- virtual void visitConst(Const *);
- virtual void visitString(String *);
- virtual void visitRegExp(RegExp *);
- virtual void visitName(Name *);
- virtual void visitTemp(Temp *);
- virtual void visitArgLocal(ArgLocal *);
- virtual void visitClosure(Closure *);
- virtual void visitConvert(Convert *);
- virtual void visitUnop(Unop *);
- virtual void visitBinop(Binop *);
- virtual void visitCall(Call *);
- virtual void visitNew(New *);
- virtual void visitSubscript(Subscript *);
- virtual void visitMember(Member *);
+ void visit(Expr *e);
protected:
IR::BasicBlock *block;
@@ -1278,7 +1425,7 @@ private:
IR::Expr *cloned;
};
-class Q_AUTOTEST_EXPORT IRPrinter: public StmtVisitor, public ExprVisitor
+class Q_AUTOTEST_EXPORT IRPrinter
{
public:
IRPrinter(QTextStream *out);
@@ -1291,6 +1438,7 @@ public:
virtual void print(Function *f);
virtual void print(BasicBlock *bb);
+ void visit(Stmt *s);
virtual void visitExp(Exp *s);
virtual void visitMove(Move *s);
virtual void visitJump(Jump *s);
@@ -1298,6 +1446,7 @@ public:
virtual void visitRet(Ret *s);
virtual void visitPhi(Phi *s);
+ void visit(Expr *e);
virtual void visitConst(Const *e);
virtual void visitString(String *e);
virtual void visitRegExp(RegExp *e);
diff --git a/src/qml/compiler/qv4ssa.cpp b/src/qml/compiler/qv4ssa.cpp
index f021e1f760..4111cc77db 100644
--- a/src/qml/compiler/qv4ssa.cpp
+++ b/src/qml/compiler/qv4ssa.cpp
@@ -642,7 +642,7 @@ public:
qout << from;
else
qout << "(none)";
- qout << " -> " << to->index() << endl;
+ qout << " dominates " << to->index() << endl;
}
qDebug("%s", buf.data().constData());
}
@@ -740,6 +740,21 @@ public:
return order;
}
+ void mergeIntoPredecessor(BasicBlock *successor)
+ {
+ int succIdx = successor->index();
+ if (succIdx == InvalidBasicBlockIndex) {
+ return;
+ }
+
+ int succDom = idom[unsigned(succIdx)];
+ for (BasicBlockIndex &idx : idom) {
+ if (idx == succIdx) {
+ idx = succDom;
+ }
+ }
+ }
+
private:
bool dominates(BasicBlockIndex dominator, BasicBlockIndex dominated) const {
// dominator can be Invalid when the dominated block has no dominator (i.e. the start node)
@@ -898,7 +913,7 @@ private:
}
};
-class VariableCollector: public StmtVisitor, ExprVisitor {
+class VariableCollector {
std::vector<Temp> _allTemps;
std::vector<BasicBlockSet> _defsites;
std::vector<std::vector<int> > A_orig;
@@ -946,7 +961,7 @@ public:
currentBB = bb;
killed.assign(function->tempCount, false);
for (Stmt *s : bb->statements())
- s->accept(this);
+ visit(s);
}
}
@@ -971,62 +986,45 @@ public:
return nonLocals.at(var.index);
}
-protected:
- virtual void visitPhi(Phi *) {}
- virtual void visitConvert(Convert *e) { e->expr->accept(this); }
-
- virtual void visitConst(Const *) {}
- virtual void visitString(IR::String *) {}
- virtual void visitRegExp(IR::RegExp *) {}
- virtual void visitName(Name *) {}
- virtual void visitArgLocal(ArgLocal *) {}
- virtual void visitClosure(Closure *) {}
- virtual void visitUnop(Unop *e) { e->expr->accept(this); }
- virtual void visitBinop(Binop *e) { e->left->accept(this); e->right->accept(this); }
- virtual void visitSubscript(Subscript *e) { e->base->accept(this); e->index->accept(this); }
- virtual void visitMember(Member *e) { e->base->accept(this); }
- virtual void visitExp(Exp *s) { s->expr->accept(this); }
- virtual void visitJump(Jump *) {}
- virtual void visitCJump(CJump *s) { s->cond->accept(this); }
- virtual void visitRet(Ret *s) { s->expr->accept(this); }
-
- virtual void visitCall(Call *e) {
- e->base->accept(this);
- for (ExprList *it = e->args; it; it = it->next)
- it->expr->accept(this);
- }
-
- virtual void visitNew(New *e) {
- e->base->accept(this);
- for (ExprList *it = e->args; it; it = it->next)
- it->expr->accept(this);
- }
-
- virtual void visitMove(Move *s) {
- s->source->accept(this);
+private:
+ void visit(Stmt *s)
+ {
+ if (s->asPhi()) {
+ // nothing to do
+ } else if (auto move = s->asMove()) {
+ visit(move->source);
- if (Temp *t = s->target->asTemp()) {
- addTemp(t);
+ if (Temp *t = move->target->asTemp()) {
+ addTemp(t);
- if (isCollectable(t)) {
- _defsites[t->index].insert(currentBB);
- addDefInCurrentBlock(t);
+ if (isCollectable(t)) {
+ _defsites[t->index].insert(currentBB);
+ addDefInCurrentBlock(t);
- // For semi-pruned SSA:
- killed.setBit(t->index);
+ // For semi-pruned SSA:
+ killed.setBit(t->index);
+ }
+ } else {
+ visit(move->target);
}
} else {
- s->target->accept(this);
+ STMT_VISIT_ALL_KINDS(s)
}
}
- virtual void visitTemp(Temp *t)
+ void visit(Expr *e)
{
- addTemp(t);
+ if (auto t = e->asTemp()) {
+ addTemp(t);
- if (isCollectable(t))
- if (!killed.at(t->index))
- nonLocals.setBit(t->index);
+ if (isCollectable(t)) {
+ if (!killed.at(t->index)) {
+ nonLocals.setBit(t->index);
+ }
+ }
+ } else {
+ EXPR_VISIT_ALL_KINDS(e);
+ }
}
};
@@ -1192,6 +1190,15 @@ public:
return _defUses[variable.index].blockOfStatement;
}
+ void replaceBasicBlock(BasicBlock *from, BasicBlock *to)
+ {
+ for (auto &du : _defUses) {
+ if (du.blockOfStatement == from) {
+ du.blockOfStatement = to;
+ }
+ }
+ }
+
void removeUse(Stmt *usingStmt, const Temp &var)
{
Q_ASSERT(static_cast<unsigned>(var.index) < _defUses.size());
@@ -1345,7 +1352,7 @@ void insertPhiNode(const Temp &a, BasicBlock *y, IR::Function *f) {
//
// Undo(t, c) =
// mapping[t] = c
-class VariableRenamer: public StmtVisitor, public ExprVisitor
+class VariableRenamer
{
Q_DISABLE_COPY(VariableRenamer)
@@ -1466,7 +1473,7 @@ private:
for (Stmt *s : bb->statements()) {
currentStmt = s;
- s->accept(this);
+ visit(s);
}
for (BasicBlock *Y : bb->out) {
@@ -1532,23 +1539,35 @@ private:
return newIndex;
}
-protected:
- virtual void visitTemp(Temp *e) { // only called for uses, not defs
-// qDebug()<<"I: replacing use of"<<e->index<<"with"<<stack[e->index].top();
- e->index = currentNumber(*e);
- e->kind = Temp::VirtualRegister;
- defUses.addUse(*e, currentStmt);
- }
+private:
+ void visit(Stmt *s)
+ {
+ if (auto move = s->asMove()) {
+ // uses:
+ visit(move->source);
- virtual void visitMove(Move *s) {
- // uses:
- s->source->accept(this);
+ // defs:
+ if (Temp *t = move->target->asTemp()) {
+ renameTemp(t);
+ } else {
+ visit(move->target);
+ }
+ } else if (auto phi = s->asPhi()) {
+ renameTemp(phi->targetTemp);
+ } else {
+ STMT_VISIT_ALL_KINDS(s);
+ }
+ }
- // defs:
- if (Temp *t = s->target->asTemp())
- renameTemp(t);
- else
- s->target->accept(this);
+ void visit(Expr *e)
+ {
+ if (auto temp = e->asTemp()) {
+ temp->index = currentNumber(*temp);
+ temp->kind = Temp::VirtualRegister;
+ defUses.addUse(*temp, currentStmt);
+ } else {
+ EXPR_VISIT_ALL_KINDS(e);
+ }
}
void renameTemp(Temp *t) { // only called for defs, not uses
@@ -1558,44 +1577,6 @@ protected:
t->index = newIdx;
defUses.addDef(t, currentStmt, currentBB);
}
-
- virtual void visitConvert(Convert *e) { e->expr->accept(this); }
- virtual void visitPhi(Phi *s) { renameTemp(s->targetTemp); }
-
- virtual void visitExp(Exp *s) { s->expr->accept(this); }
-
- virtual void visitJump(Jump *) {}
- virtual void visitCJump(CJump *s) { s->cond->accept(this); }
- virtual void visitRet(Ret *s) { s->expr->accept(this); }
-
- virtual void visitConst(Const *) {}
- virtual void visitString(IR::String *) {}
- virtual void visitRegExp(IR::RegExp *) {}
- virtual void visitName(Name *) {}
- virtual void visitArgLocal(ArgLocal *) {}
- virtual void visitClosure(Closure *) {}
- virtual void visitUnop(Unop *e) { e->expr->accept(this); }
- virtual void visitBinop(Binop *e) { e->left->accept(this); e->right->accept(this); }
- virtual void visitCall(Call *e) {
- e->base->accept(this);
- for (ExprList *it = e->args; it; it = it->next)
- it->expr->accept(this);
- }
-
- virtual void visitNew(New *e) {
- e->base->accept(this);
- for (ExprList *it = e->args; it; it = it->next)
- it->expr->accept(this);
- }
-
- virtual void visitSubscript(Subscript *e) {
- e->base->accept(this);
- e->index->accept(this);
- }
-
- virtual void visitMember(Member *e) {
- e->base->accept(this);
- }
};
// This function converts the IR to semi-pruned SSA form. For details about SSA and the algorightm,
@@ -1922,7 +1903,7 @@ private:
}
};
-class SideEffectsChecker: public ExprVisitor
+class SideEffectsChecker
{
bool _sideEffect;
@@ -1931,11 +1912,14 @@ public:
: _sideEffect(false)
{}
+ ~SideEffectsChecker()
+ {}
+
bool hasSideEffects(Expr *expr)
{
bool sideEffect = false;
qSwap(_sideEffect, sideEffect);
- expr->accept(this);
+ visit(expr);
qSwap(_sideEffect, sideEffect);
return sideEffect;
}
@@ -1948,12 +1932,35 @@ protected:
bool seenSideEffects() const { return _sideEffect; }
-protected:
- void visitConst(Const *) Q_DECL_OVERRIDE {}
- void visitString(IR::String *) Q_DECL_OVERRIDE {}
- void visitRegExp(IR::RegExp *) Q_DECL_OVERRIDE {}
+ void visit(Expr *e)
+ {
+ if (auto n = e->asName()) {
+ visitName(n);
+ } else if (auto t = e->asTemp()) {
+ visitTemp(t);
+ } else if (auto c = e->asClosure()) {
+ visitClosure(c);
+ } else if (auto c = e->asConvert()) {
+ visitConvert(c);
+ } else if (auto u = e->asUnop()) {
+ visitUnop(u);
+ } else if (auto b = e->asBinop()) {
+ visitBinop(b);
+ } else if (auto c = e->asCall()) {
+ visitCall(c);
+ } else if (auto n = e->asNew()) {
+ visitNew(n);
+ } else if (auto s = e->asSubscript()) {
+ visitSubscript(s);
+ } else if (auto m = e->asMember()) {
+ visitMember(m);
+ }
+ }
- void visitName(Name *e) Q_DECL_OVERRIDE {
+ virtual void visitTemp(Temp *) {}
+
+private:
+ void visitName(Name *e) {
if (e->freeOfSideEffects)
return;
// TODO: maybe we can distinguish between built-ins of which we know that they do not have
@@ -1962,15 +1969,12 @@ protected:
markAsSideEffect();
}
- void visitTemp(Temp *) Q_DECL_OVERRIDE {}
- void visitArgLocal(ArgLocal *) Q_DECL_OVERRIDE {}
-
- void visitClosure(Closure *) Q_DECL_OVERRIDE {
+ void visitClosure(Closure *) {
markAsSideEffect();
}
- void visitConvert(Convert *e) Q_DECL_OVERRIDE {
- e->expr->accept(this);
+ void visitConvert(Convert *e) {
+ visit(e->expr);
switch (e->expr->type) {
case QObjectType:
@@ -1983,8 +1987,8 @@ protected:
}
}
- void visitUnop(Unop *e) Q_DECL_OVERRIDE {
- e->expr->accept(this);
+ void visitUnop(Unop *e) {
+ visit(e->expr);
switch (e->op) {
case OpUPlus:
@@ -2001,7 +2005,7 @@ protected:
}
}
- void visitBinop(Binop *e) Q_DECL_OVERRIDE {
+ void visitBinop(Binop *e) {
// TODO: prune parts that don't have a side-effect. For example, in:
// function f(x) { +x+1; return 0; }
// we can prune the binop and leave the unop/conversion.
@@ -2013,30 +2017,30 @@ protected:
markAsSideEffect();
}
- void visitSubscript(Subscript *e) Q_DECL_OVERRIDE {
- e->base->accept(this);
- e->index->accept(this);
+ void visitSubscript(Subscript *e) {
+ visit(e->base);
+ visit(e->index);
markAsSideEffect();
}
- void visitMember(Member *e) Q_DECL_OVERRIDE {
- e->base->accept(this);
+ void visitMember(Member *e) {
+ visit(e->base);
if (e->freeOfSideEffects)
return;
markAsSideEffect();
}
- void visitCall(Call *e) Q_DECL_OVERRIDE {
- e->base->accept(this);
+ void visitCall(Call *e) {
+ visit(e->base);
for (ExprList *args = e->args; args; args = args->next)
- args->expr->accept(this);
+ visit(args->expr);
markAsSideEffect(); // TODO: there are built-in functions that have no side effect.
}
- void visitNew(New *e) Q_DECL_OVERRIDE {
- e->base->accept(this);
+ void visitNew(New *e) {
+ visit(e->base);
for (ExprList *args = e->args; args; args = args->next)
- args->expr->accept(this);
+ visit(args->expr);
markAsSideEffect(); // TODO: there are built-in types that have no side effect.
}
};
@@ -2045,21 +2049,19 @@ class EliminateDeadCode: public SideEffectsChecker
{
DefUses &_defUses;
StatementWorklist &_worklist;
- QVector<Temp *> _collectedTemps;
+ QVarLengthArray<Temp *, 8> _collectedTemps;
public:
EliminateDeadCode(DefUses &defUses, StatementWorklist &worklist)
: _defUses(defUses)
, _worklist(worklist)
- {
- _collectedTemps.reserve(8);
- }
+ {}
void run(Expr *&expr, Stmt *stmt) {
_collectedTemps.clear();
if (!hasSideEffects(expr)) {
expr = 0;
- foreach (Temp *t, _collectedTemps) {
+ for (Temp *t : _collectedTemps) {
_defUses.removeUse(stmt, *t);
_worklist += _defUses.defStmt(*t);
}
@@ -2067,13 +2069,13 @@ public:
}
protected:
- void visitTemp(Temp *e) Q_DECL_OVERRIDE
+ void visitTemp(Temp *e) Q_DECL_OVERRIDE Q_DECL_FINAL
{
_collectedTemps.append(e);
}
};
-class PropagateTempTypes: public StmtVisitor, ExprVisitor
+class PropagateTempTypes
{
const DefUses &defUses;
UntypedTemp theTemp;
@@ -2089,64 +2091,31 @@ public:
newType = type;
theTemp = temp;
if (Stmt *defStmt = defUses.defStmt(temp.temp))
- defStmt->accept(this);
+ visit(defStmt);
foreach (Stmt *use, defUses.uses(temp.temp))
- use->accept(this);
- }
-
-protected:
- virtual void visitConst(Const *) {}
- virtual void visitString(IR::String *) {}
- virtual void visitRegExp(IR::RegExp *) {}
- virtual void visitName(Name *) {}
- virtual void visitTemp(Temp *e) {
- if (theTemp == UntypedTemp(*e)) {
- e->type = static_cast<Type>(newType.type);
- e->memberResolver = newType.memberResolver;
- }
- }
- virtual void visitArgLocal(ArgLocal *) {}
- virtual void visitClosure(Closure *) {}
- virtual void visitConvert(Convert *e) { e->expr->accept(this); }
- virtual void visitUnop(Unop *e) { e->expr->accept(this); }
- virtual void visitBinop(Binop *e) { e->left->accept(this); e->right->accept(this); }
-
- virtual void visitCall(Call *e) {
- e->base->accept(this);
- for (ExprList *it = e->args; it; it = it->next)
- it->expr->accept(this);
- }
- virtual void visitNew(New *e) {
- e->base->accept(this);
- for (ExprList *it = e->args; it; it = it->next)
- it->expr->accept(this);
- }
- virtual void visitSubscript(Subscript *e) {
- e->base->accept(this);
- e->index->accept(this);
- }
-
- virtual void visitMember(Member *e) {
- e->base->accept(this);
+ visit(use);
}
- virtual void visitExp(Exp *s) {s->expr->accept(this);}
- virtual void visitMove(Move *s) {
- s->source->accept(this);
- s->target->accept(this);
+private:
+ void visit(Stmt *s)
+ {
+ STMT_VISIT_ALL_KINDS(s);
}
- virtual void visitJump(Jump *) {}
- virtual void visitCJump(CJump *s) { s->cond->accept(this); }
- virtual void visitRet(Ret *s) { s->expr->accept(this); }
- virtual void visitPhi(Phi *s) {
- s->targetTemp->accept(this);
- foreach (Expr *e, s->incoming)
- e->accept(this);
+ void visit(Expr *e)
+ {
+ if (auto temp = e->asTemp()) {
+ if (theTemp == UntypedTemp(*temp)) {
+ temp->type = static_cast<Type>(newType.type);
+ temp->memberResolver = newType.memberResolver;
+ }
+ } else {
+ EXPR_VISIT_ALL_KINDS(e);
+ }
}
};
-class TypeInference: public StmtVisitor, public ExprVisitor
+class TypeInference
{
enum { DebugTypeInference = 0 };
@@ -2248,7 +2217,7 @@ private:
TypingResult ty;
std::swap(_ty, ty);
std::swap(_currentStmt, s);
- _currentStmt->accept(this);
+ visit(_currentStmt);
std::swap(_currentStmt, s);
std::swap(_ty, ty);
return ty.fullyTyped;
@@ -2257,7 +2226,7 @@ private:
TypingResult run(Expr *e) {
TypingResult ty;
std::swap(_ty, ty);
- e->accept(this);
+ visit(e);
std::swap(_ty, ty);
if (ty.type != UnknownType)
@@ -2299,39 +2268,74 @@ private:
}
}
-protected:
- virtual void visitConst(Const *e) {
- if (e->type & NumberType) {
- if (canConvertToSignedInteger(e->value))
+private:
+ void visit(Expr *e)
+ {
+ if (auto c = e->asConst()) {
+ visitConst(c);
+ } else if (auto s = e->asString()) {
+ visitString(s);
+ } else if (auto r = e->asRegExp()) {
+ visitRegExp(r);
+ } else if (auto n = e->asName()) {
+ visitName(n);
+ } else if (auto t = e->asTemp()) {
+ visitTemp(t);
+ } else if (auto a = e->asArgLocal()) {
+ visitArgLocal(a);
+ } else if (auto c = e->asClosure()) {
+ visitClosure(c);
+ } else if (auto c = e->asConvert()) {
+ visitConvert(c);
+ } else if (auto u = e->asUnop()) {
+ visitUnop(u);
+ } else if (auto b = e->asBinop()) {
+ visitBinop(b);
+ } else if (auto c = e->asCall()) {
+ visitCall(c);
+ } else if (auto n = e->asNew()) {
+ visitNew(n);
+ } else if (auto s = e->asSubscript()) {
+ visitSubscript(s);
+ } else if (auto m = e->asMember()) {
+ visitMember(m);
+ } else {
+ Q_UNREACHABLE();
+ }
+ }
+
+ void visitConst(Const *c) {
+ if (c->type & NumberType) {
+ if (canConvertToSignedInteger(c->value))
_ty = TypingResult(SInt32Type);
- else if (canConvertToUnsignedInteger(e->value))
+ else if (canConvertToUnsignedInteger(c->value))
_ty = TypingResult(UInt32Type);
else
- _ty = TypingResult(e->type);
+ _ty = TypingResult(c->type);
} else
- _ty = TypingResult(e->type);
+ _ty = TypingResult(c->type);
}
- virtual void visitString(IR::String *) { _ty = TypingResult(StringType); }
- virtual void visitRegExp(IR::RegExp *) { _ty = TypingResult(VarType); }
- virtual void visitName(Name *) { _ty = TypingResult(VarType); }
- virtual void visitTemp(Temp *e) {
+ void visitString(IR::String *) { _ty = TypingResult(StringType); }
+ void visitRegExp(IR::RegExp *) { _ty = TypingResult(VarType); }
+ void visitName(Name *) { _ty = TypingResult(VarType); }
+ void visitTemp(Temp *e) {
if (e->memberResolver && e->memberResolver->isValid())
_ty = TypingResult(e->memberResolver);
else
_ty = TypingResult(_tempTypes[e->index]);
setType(e, _ty.type);
}
- virtual void visitArgLocal(ArgLocal *e) {
+ void visitArgLocal(ArgLocal *e) {
_ty = TypingResult(VarType);
setType(e, _ty.type);
}
- virtual void visitClosure(Closure *) { _ty = TypingResult(VarType); }
- virtual void visitConvert(Convert *e) {
+ void visitClosure(Closure *) { _ty = TypingResult(VarType); }
+ void visitConvert(Convert *e) {
_ty = TypingResult(e->type);
}
- virtual void visitUnop(Unop *e) {
+ void visitUnop(Unop *e) {
_ty = run(e->expr);
switch (e->op) {
case OpUPlus: _ty.type = DoubleType; return;
@@ -2348,7 +2352,7 @@ protected:
}
}
- virtual void visitBinop(Binop *e) {
+ void visitBinop(Binop *e) {
TypingResult leftTy = run(e->left);
TypingResult rightTy = run(e->right);
_ty.fullyTyped = leftTy.fullyTyped && rightTy.fullyTyped;
@@ -2406,24 +2410,24 @@ protected:
}
}
- virtual void visitCall(Call *e) {
+ void visitCall(Call *e) {
_ty = run(e->base);
for (ExprList *it = e->args; it; it = it->next)
_ty.fullyTyped &= run(it->expr).fullyTyped;
_ty.type = VarType;
}
- virtual void visitNew(New *e) {
+ void visitNew(New *e) {
_ty = run(e->base);
for (ExprList *it = e->args; it; it = it->next)
_ty.fullyTyped &= run(it->expr).fullyTyped;
_ty.type = VarType;
}
- virtual void visitSubscript(Subscript *e) {
+ void visitSubscript(Subscript *e) {
_ty.fullyTyped = run(e->base).fullyTyped && run(e->index).fullyTyped;
_ty.type = VarType;
}
- virtual void visitMember(Member *e) {
+ void visitMember(Member *e) {
_ty = run(e->base);
if (_ty.fullyTyped && _ty.type.memberResolver && _ty.type.memberResolver->isValid()) {
@@ -2433,8 +2437,27 @@ protected:
_ty.type = VarType;
}
- virtual void visitExp(Exp *s) { _ty = run(s->expr); }
- virtual void visitMove(Move *s) {
+ void visit(Stmt *s)
+ {
+ if (auto e = s->asExp()) {
+ visitExp(e);
+ } else if (auto m = s->asMove()) {
+ visitMove(m);
+ } else if (auto j = s->asJump()) {
+ visitJump(j);
+ } else if (auto c = s->asCJump()) {
+ visitCJump(c);
+ } else if (auto r = s->asRet()) {
+ visitRet(r);
+ } else if (auto p = s->asPhi()) {
+ visitPhi(p);
+ } else {
+ Q_UNREACHABLE();
+ }
+ }
+
+ void visitExp(Exp *s) { _ty = run(s->expr); }
+ void visitMove(Move *s) {
if (Temp *t = s->target->asTemp()) {
if (Name *n = s->source->asName()) {
if (n->builtin == Name::builtin_qml_context) {
@@ -2455,10 +2478,10 @@ protected:
_ty.fullyTyped &= sourceTy.fullyTyped;
}
- virtual void visitJump(Jump *) { _ty = TypingResult(MissingType); }
- virtual void visitCJump(CJump *s) { _ty = run(s->cond); }
- virtual void visitRet(Ret *s) { _ty = run(s->expr); }
- virtual void visitPhi(Phi *s) {
+ void visitJump(Jump *) { _ty = TypingResult(MissingType); }
+ void visitCJump(CJump *s) { _ty = run(s->cond); }
+ void visitRet(Ret *s) { _ty = run(s->expr); }
+ void visitPhi(Phi *s) {
_ty = run(s->incoming[0]);
for (int i = 1, ei = s->incoming.size(); i != ei; ++i) {
TypingResult ty = run(s->incoming[i]);
@@ -2677,14 +2700,15 @@ void convertConst(Const *c, Type targetType)
c->type = targetType;
}
-class TypePropagation: public StmtVisitor, public ExprVisitor {
+class TypePropagation
+{
DefUses &_defUses;
Type _ty;
IR::Function *_f;
bool run(Expr *&e, Type requestedType = UnknownType, bool insertConversion = true) {
qSwap(_ty, requestedType);
- e->accept(this);
+ visit(e);
qSwap(_ty, requestedType);
if (requestedType != UnknownType) {
@@ -2731,7 +2755,7 @@ public:
for (Stmt *s : bb->statements()) {
_currStmt = s;
- s->accept(this);
+ visit(s);
}
foreach (const Conversion &conversion, _conversions) {
@@ -2817,8 +2841,29 @@ public:
}
}
-protected:
- virtual void visitConst(Const *c) {
+private:
+ void visit(Expr *e)
+ {
+ if (auto c = e->asConst()) {
+ visitConst(c);
+ } else if (auto c = e->asConvert()) {
+ run(c->expr, c->type);
+ } else if (auto u = e->asUnop()) {
+ run(u->expr, u->type);
+ } else if (auto b = e->asBinop()) {
+ visitBinop(b);
+ } else if (auto c = e->asCall()) {
+ visitCall(c);
+ } else if (auto n = e->asNew()) {
+ visitNew(n);
+ } else if (auto s = e->asSubscript()) {
+ visitSubscript(s);
+ } else if (auto m = e->asMember()) {
+ visitMember(m);
+ }
+ }
+
+ void visitConst(Const *c) {
if (_ty & NumberType && c->type & NumberType) {
if (_ty == SInt32Type)
c->value = QV4::Primitive::toInt32(c->value);
@@ -2828,15 +2873,7 @@ protected:
}
}
- virtual void visitString(IR::String *) {}
- virtual void visitRegExp(IR::RegExp *) {}
- virtual void visitName(Name *) {}
- virtual void visitTemp(Temp *) {}
- virtual void visitArgLocal(ArgLocal *) {}
- virtual void visitClosure(Closure *) {}
- virtual void visitConvert(Convert *e) { run(e->expr, e->type); }
- virtual void visitUnop(Unop *e) { run(e->expr, e->type); }
- virtual void visitBinop(Binop *e) {
+ void visitBinop(Binop *e) {
// FIXME: This routine needs more tuning!
switch (e->op) {
case OpAdd:
@@ -2887,20 +2924,36 @@ protected:
Q_UNREACHABLE();
}
}
- virtual void visitCall(Call *e) {
+ void visitCall(Call *e) {
run(e->base);
for (ExprList *it = e->args; it; it = it->next)
run(it->expr);
}
- virtual void visitNew(New *e) {
+ void visitNew(New *e) {
run(e->base);
for (ExprList *it = e->args; it; it = it->next)
run(it->expr);
}
- virtual void visitSubscript(Subscript *e) { run(e->base); run(e->index); }
- virtual void visitMember(Member *e) { run(e->base); }
- virtual void visitExp(Exp *s) { run(s->expr); }
- virtual void visitMove(Move *s) {
+ void visitSubscript(Subscript *e) { run(e->base); run(e->index); }
+ void visitMember(Member *e) { run(e->base); }
+
+ void visit(Stmt *s)
+ {
+ if (auto e = s->asExp()) {
+ visitExp(e);
+ } else if (auto m = s->asMove()) {
+ visitMove(m);
+ } else if (auto c = s->asCJump()) {
+ visitCJump(c);
+ } else if (auto r = s->asRet()) {
+ visitRet(r);
+ } else if (auto p = s->asPhi()) {
+ visitPhi(p);
+ }
+ }
+
+ void visitExp(Exp *s) { run(s->expr); }
+ void visitMove(Move *s) {
if (s->source->asConvert())
return; // this statement got inserted for a phi-node type conversion
@@ -2925,12 +2978,11 @@ protected:
run(s->source, s->target->type, !inhibitConversion);
}
- virtual void visitJump(Jump *) {}
- virtual void visitCJump(CJump *s) {
+ void visitCJump(CJump *s) {
run(s->cond, BoolType);
}
- virtual void visitRet(Ret *s) { run(s->expr); }
- virtual void visitPhi(Phi *s) {
+ void visitRet(Ret *s) { run(s->expr); }
+ void visitPhi(Phi *s) {
Type ty = s->targetTemp->type;
for (int i = 0, ei = s->incoming.size(); i != ei; ++i)
run(s->incoming[i], ty);
@@ -3299,6 +3351,15 @@ class BlockScheduler
// this is a loop, where there in -> candidate edge is the jump back to the top of the loop.
continue;
+ if (in == candidate)
+ // this is a very tight loop, e.g.:
+ // L1: ...
+ // goto L1
+ // This can happen when, for example, the basic-block merging gets rid of the empty
+ // body block. In this case, we can safely schedule this block (if all other
+ // incoming edges are either loop-back edges, or have been scheduled already).
+ continue;
+
return false; // an incoming edge that is not yet emitted, and is not a back-edge
}
@@ -3504,7 +3565,7 @@ static Expr *clone(Expr *e, IR::Function *function) {
}
}
-class ExprReplacer: public StmtVisitor, public ExprVisitor
+class ExprReplacer
{
DefUses &_defUses;
IR::Function* _function;
@@ -3535,7 +3596,7 @@ public:
// qout << " " << uses.size() << " uses:"<<endl;
foreach (Stmt *use, uses) {
// qout<<" ";use->dump(qout);qout<<"\n";
- use->accept(this);
+ visit(use);
// qout<<" -> ";use->dump(qout);qout<<"\n";
W += use;
if (newUses)
@@ -3546,45 +3607,101 @@ public:
qSwap(_toReplace, toReplace);
}
-protected:
- virtual void visitConst(Const *) {}
- virtual void visitString(IR::String *) {}
- virtual void visitRegExp(IR::RegExp *) {}
- virtual void visitName(Name *) {}
- virtual void visitTemp(Temp *) {}
- virtual void visitArgLocal(ArgLocal *) {}
- virtual void visitClosure(Closure *) {}
- virtual void visitConvert(Convert *e) { check(e->expr); }
- virtual void visitUnop(Unop *e) { check(e->expr); }
- virtual void visitBinop(Binop *e) { check(e->left); check(e->right); }
- virtual void visitCall(Call *e) {
+private:
+ void visit(Expr *e)
+ {
+ if (auto c = e->asConst()) {
+ visitConst(c);
+ } else if (auto s = e->asString()) {
+ visitString(s);
+ } else if (auto r = e->asRegExp()) {
+ visitRegExp(r);
+ } else if (auto n = e->asName()) {
+ visitName(n);
+ } else if (auto t = e->asTemp()) {
+ visitTemp(t);
+ } else if (auto a = e->asArgLocal()) {
+ visitArgLocal(a);
+ } else if (auto c = e->asClosure()) {
+ visitClosure(c);
+ } else if (auto c = e->asConvert()) {
+ visitConvert(c);
+ } else if (auto u = e->asUnop()) {
+ visitUnop(u);
+ } else if (auto b = e->asBinop()) {
+ visitBinop(b);
+ } else if (auto c = e->asCall()) {
+ visitCall(c);
+ } else if (auto n = e->asNew()) {
+ visitNew(n);
+ } else if (auto s = e->asSubscript()) {
+ visitSubscript(s);
+ } else if (auto m = e->asMember()) {
+ visitMember(m);
+ } else {
+ Q_UNREACHABLE();
+ }
+ }
+
+ void visitConst(Const *) {}
+ void visitString(IR::String *) {}
+ void visitRegExp(IR::RegExp *) {}
+ void visitName(Name *) {}
+ void visitTemp(Temp *) {}
+ void visitArgLocal(ArgLocal *) {}
+ void visitClosure(Closure *) {}
+ void visitConvert(Convert *e) { check(e->expr); }
+ void visitUnop(Unop *e) { check(e->expr); }
+ void visitBinop(Binop *e) { check(e->left); check(e->right); }
+ void visitCall(Call *e) {
check(e->base);
for (ExprList *it = e->args; it; it = it->next)
check(it->expr);
}
- virtual void visitNew(New *e) {
+ void visitNew(New *e) {
check(e->base);
for (ExprList *it = e->args; it; it = it->next)
check(it->expr);
}
- virtual void visitSubscript(Subscript *e) { check(e->base); check(e->index); }
- virtual void visitMember(Member *e) { check(e->base); }
- virtual void visitExp(Exp *s) { check(s->expr); }
- virtual void visitMove(Move *s) { check(s->target); check(s->source); }
- virtual void visitJump(Jump *) {}
- virtual void visitCJump(CJump *s) { check(s->cond); }
- virtual void visitRet(Ret *s) { check(s->expr); }
- virtual void visitPhi(Phi *s) {
+ void visitSubscript(Subscript *e) { check(e->base); check(e->index); }
+ void visitMember(Member *e) { check(e->base); }
+
+ void visit(Stmt *s)
+ {
+ if (auto e = s->asExp()) {
+ visitExp(e);
+ } else if (auto m = s->asMove()) {
+ visitMove(m);
+ } else if (auto j = s->asJump()) {
+ visitJump(j);
+ } else if (auto c = s->asCJump()) {
+ visitCJump(c);
+ } else if (auto r = s->asRet()) {
+ visitRet(r);
+ } else if (auto p = s->asPhi()) {
+ visitPhi(p);
+ } else {
+ Q_UNREACHABLE();
+ }
+ }
+
+ void visitExp(Exp *s) { check(s->expr); }
+ void visitMove(Move *s) { check(s->target); check(s->source); }
+ void visitJump(Jump *) {}
+ void visitCJump(CJump *s) { check(s->cond); }
+ void visitRet(Ret *s) { check(s->expr); }
+ void visitPhi(Phi *s) {
for (int i = 0, ei = s->incoming.size(); i != ei; ++i)
check(s->incoming[i]);
}
private:
void check(Expr *&e) {
- if (equals(e, _toReplace))
+ if (equals(e, _toReplace)) {
e = clone(_replacement, _function);
- else
- e->accept(this);
+ } else {
+ visit(e);
+ }
}
// This only calculates equality for everything needed by constant propagation
@@ -3625,6 +3742,8 @@ namespace {
void unlink(BasicBlock *from, BasicBlock *to, IR::Function *func, DefUses &defUses,
StatementWorklist &W, DominatorTree &dt)
{
+ enum { DebugUnlinking = 0 };
+
struct Util {
static void removeIncomingEdge(BasicBlock *from, BasicBlock *to, DefUses &defUses, StatementWorklist &W)
{
@@ -3663,11 +3782,17 @@ void unlink(BasicBlock *from, BasicBlock *to, IR::Function *func, DefUses &defUs
}
};
+ Q_ASSERT(!from->isRemoved());
+ Q_ASSERT(!to->isRemoved());
+
// don't purge blocks that are entry points for catch statements. They might not be directly
// connected, but are required anyway
if (to->isExceptionHandler())
return;
+ if (DebugUnlinking)
+ qDebug("Unlinking L%d -> L%d...", from->index(), to->index());
+
// First, unlink the edge
from->out.removeOne(to);
Util::removeIncomingEdge(from, to, defUses, W);
@@ -3677,8 +3802,12 @@ void unlink(BasicBlock *from, BasicBlock *to, IR::Function *func, DefUses &defUs
// Check if the target is still reachable...
if (Util::isReachable(to, dt)) { // yes, recalculate the immediate dominator, and we're done.
+ if (DebugUnlinking)
+ qDebug(".. L%d is still reachable, recalulate idom.", to->index());
dt.collectSiblings(to, siblings);
} else {
+ if (DebugUnlinking)
+ qDebug(".. L%d is unreachable, purging it:", to->index());
// The target is unreachable, so purge it:
QVector<BasicBlock *> toPurge;
toPurge.reserve(8);
@@ -3686,6 +3815,8 @@ void unlink(BasicBlock *from, BasicBlock *to, IR::Function *func, DefUses &defUs
while (!toPurge.isEmpty()) {
BasicBlock *bb = toPurge.first();
toPurge.removeFirst();
+ if (DebugUnlinking)
+ qDebug("... purging L%d", bb->index());
if (bb->isRemoved())
continue;
@@ -3729,6 +3860,8 @@ void unlink(BasicBlock *from, BasicBlock *to, IR::Function *func, DefUses &defUs
}
dt.recalculateIDoms(siblings);
+ if (DebugUnlinking)
+ qDebug("Unlinking done.");
}
bool tryOptimizingComparison(Expr *&expr)
@@ -3748,42 +3881,42 @@ bool tryOptimizingComparison(Expr *&expr)
switch (b->op) {
case OpGt:
- leftConst->value = Runtime::compareGreaterThan(l, r);
+ leftConst->value = Runtime::method_compareGreaterThan(l, r);
leftConst->type = BoolType;
expr = leftConst;
return true;
case OpLt:
- leftConst->value = Runtime::compareLessThan(l, r);
+ leftConst->value = Runtime::method_compareLessThan(l, r);
leftConst->type = BoolType;
expr = leftConst;
return true;
case OpGe:
- leftConst->value = Runtime::compareGreaterEqual(l, r);
+ leftConst->value = Runtime::method_compareGreaterEqual(l, r);
leftConst->type = BoolType;
expr = leftConst;
return true;
case OpLe:
- leftConst->value = Runtime::compareLessEqual(l, r);
+ leftConst->value = Runtime::method_compareLessEqual(l, r);
leftConst->type = BoolType;
expr = leftConst;
return true;
case OpStrictEqual:
- leftConst->value = Runtime::compareStrictEqual(l, r);
+ leftConst->value = Runtime::method_compareStrictEqual(l, r);
leftConst->type = BoolType;
expr = leftConst;
return true;
case OpEqual:
- leftConst->value = Runtime::compareEqual(l, r);
+ leftConst->value = Runtime::method_compareEqual(l, r);
leftConst->type = BoolType;
expr = leftConst;
return true;
case OpStrictNotEqual:
- leftConst->value = Runtime::compareStrictNotEqual(l, r);
+ leftConst->value = Runtime::method_compareStrictNotEqual(l, r);
leftConst->type = BoolType;
expr = leftConst;
return true;
case OpNotEqual:
- leftConst->value = Runtime::compareNotEqual(l, r);
+ leftConst->value = Runtime::method_compareNotEqual(l, r);
leftConst->type = BoolType;
expr = leftConst;
return true;
@@ -4153,7 +4286,8 @@ void optimizeSSA(StatementWorklist &W, DefUses &defUses, DominatorTree &df)
}
//### TODO: use DefUses from the optimizer, because it already has all this information
-class InputOutputCollector: protected StmtVisitor, protected ExprVisitor {
+class InputOutputCollector
+{
void setOutput(Temp *out)
{
Q_ASSERT(!output);
@@ -4170,48 +4304,33 @@ public:
void collect(Stmt *s) {
inputs.resize(0);
output = 0;
- s->accept(this);
+ visit(s);
}
-protected:
- virtual void visitConst(Const *) {}
- virtual void visitString(IR::String *) {}
- virtual void visitRegExp(IR::RegExp *) {}
- virtual void visitName(Name *) {}
- virtual void visitTemp(Temp *e) {
- inputs.push_back(e);
- }
- virtual void visitArgLocal(ArgLocal *) {}
- virtual void visitClosure(Closure *) {}
- virtual void visitConvert(Convert *e) { e->expr->accept(this); }
- virtual void visitUnop(Unop *e) { e->expr->accept(this); }
- virtual void visitBinop(Binop *e) { e->left->accept(this); e->right->accept(this); }
- virtual void visitCall(Call *e) {
- e->base->accept(this);
- for (ExprList *it = e->args; it; it = it->next)
- it->expr->accept(this);
- }
- virtual void visitNew(New *e) {
- e->base->accept(this);
- for (ExprList *it = e->args; it; it = it->next)
- it->expr->accept(this);
- }
- virtual void visitSubscript(Subscript *e) { e->base->accept(this); e->index->accept(this); }
- virtual void visitMember(Member *e) { e->base->accept(this); }
- virtual void visitExp(Exp *s) { s->expr->accept(this); }
- virtual void visitMove(Move *s) {
- s->source->accept(this);
- if (Temp *t = s->target->asTemp()) {
- setOutput(t);
+private:
+ void visit(Expr *e)
+ {
+ if (auto t = e->asTemp()) {
+ inputs.push_back(t);
} else {
- s->target->accept(this);
+ EXPR_VISIT_ALL_KINDS(e);
}
}
- virtual void visitJump(Jump *) {}
- virtual void visitCJump(CJump *s) { s->cond->accept(this); }
- virtual void visitRet(Ret *s) { s->expr->accept(this); }
- virtual void visitPhi(Phi *) {
- // Handled separately
+
+ void visit(Stmt *s)
+ {
+ if (auto m = s->asMove()) {
+ visit(m->source);
+ if (Temp *t = m->target->asTemp()) {
+ setOutput(t);
+ } else {
+ visit(m->target);
+ }
+ } else if (s->asPhi()) {
+ // Handled separately
+ } else {
+ STMT_VISIT_ALL_KINDS(s);
+ }
}
};
@@ -4224,7 +4343,59 @@ protected:
* See LifeTimeIntervals::renumber for details on the numbering.
*/
class LifeRanges {
- typedef QSet<Temp> LiveRegs;
+ class LiveRegs
+ {
+ typedef std::vector<int> Storage;
+ Storage regs;
+
+ public:
+ void insert(int r)
+ {
+ if (find(r) == end())
+ regs.push_back(r);
+ }
+
+ void unite(const LiveRegs &other)
+ {
+ if (other.empty())
+ return;
+ if (empty()) {
+ regs = other.regs;
+ return;
+ }
+ for (int r : other.regs)
+ insert(r);
+ }
+
+ typedef Storage::iterator iterator;
+ iterator find(int r)
+ { return std::find(regs.begin(), regs.end(), r); }
+
+ iterator begin()
+ { return regs.begin(); }
+
+ iterator end()
+ { return regs.end(); }
+
+ void erase(iterator it)
+ { regs.erase(it); }
+
+ void remove(int r)
+ {
+ iterator it = find(r);
+ if (it != end())
+ erase(it);
+ }
+
+ bool empty() const
+ { return regs.empty(); }
+
+ int size() const
+ { return int(regs.size()); }
+
+ int at(int idx) const
+ { return regs.at(idx); }
+ };
std::vector<LiveRegs> _liveIn;
std::vector<LifeTimeInterval *> _intervals;
@@ -4232,14 +4403,21 @@ class LifeRanges {
LifeTimeInterval &interval(const Temp *temp)
{
- LifeTimeInterval *&lti = _intervals[temp->index];
- if (Q_UNLIKELY(!lti)) {
- lti = new LifeTimeInterval;
- lti->setTemp(*temp);
- }
+ LifeTimeInterval *lti = _intervals[temp->index];
+ Q_ASSERT(lti);
return *lti;
}
+ void ensureInterval(const IR::Temp &temp)
+ {
+ Q_ASSERT(!temp.isInvalid());
+ LifeTimeInterval *&lti = _intervals[temp.index];
+ if (lti)
+ return;
+ lti = new LifeTimeInterval;
+ lti->setTemp(temp);
+ }
+
int defPosition(IR::Stmt *s) const
{
return usePosition(s) + 1;
@@ -4293,13 +4471,13 @@ public:
IRPrinter printer(&qout);
for (size_t i = 0, ei = _liveIn.size(); i != ei; ++i) {
qout << "L" << i <<" live-in: ";
- QList<Temp> live = QList<Temp>::fromSet(_liveIn.at(i));
- if (live.isEmpty())
+ auto live = _liveIn.at(i);
+ if (live.empty())
qout << "(none)";
std::sort(live.begin(), live.end());
for (int i = 0; i < live.size(); ++i) {
if (i > 0) qout << ", ";
- printer.print(&live[i]);
+ qout << '%' << live.at(i);
}
qout << endl;
}
@@ -4318,8 +4496,10 @@ private:
for (Stmt *s : successor->statements()) {
if (Phi *phi = s->asPhi()) {
- if (Temp *t = phi->incoming.at(bbIndex)->asTemp())
- live.insert(*t);
+ if (Temp *t = phi->incoming.at(bbIndex)->asTemp()) {
+ ensureInterval(*t);
+ live.insert(t->index);
+ }
} else {
break;
}
@@ -4328,14 +4508,15 @@ private:
const QVector<Stmt *> &statements = bb->statements();
- foreach (const Temp &opd, live)
- interval(&opd).addRange(start(bb), end(bb));
+ for (int reg : live)
+ _intervals[reg]->addRange(start(bb), end(bb));
InputOutputCollector collector;
for (int i = statements.size() - 1; i >= 0; --i) {
Stmt *s = statements.at(i);
if (Phi *phi = s->asPhi()) {
- LiveRegs::iterator it = live.find(*phi->targetTemp);
+ ensureInterval(*phi->targetTemp);
+ LiveRegs::iterator it = live.find(phi->targetTemp->index);
if (it == live.end()) {
// a phi node target that is only defined, but never used
interval(phi->targetTemp).setFrom(start(bb));
@@ -4348,25 +4529,27 @@ private:
collector.collect(s);
//### TODO: use DefUses from the optimizer, because it already has all this information
if (Temp *opd = collector.output) {
+ ensureInterval(*opd);
LifeTimeInterval &lti = interval(opd);
lti.setFrom(defPosition(s));
- live.remove(lti.temp());
+ live.remove(lti.temp().index);
_sortedIntervals->add(&lti);
}
//### TODO: use DefUses from the optimizer, because it already has all this information
for (size_t i = 0, ei = collector.inputs.size(); i != ei; ++i) {
Temp *opd = collector.inputs[i];
+ ensureInterval(*opd);
interval(opd).addRange(start(bb), usePosition(s));
- live.insert(*opd);
+ live.insert(opd->index);
}
}
if (loopEnd) { // Meaning: bb is a loop header, because loopEnd is set to non-null.
- foreach (const Temp &opd, live)
- interval(&opd).addRange(start(bb), usePosition(loopEnd->terminator()));
+ for (int reg : live)
+ _intervals[reg]->addRange(start(bb), usePosition(loopEnd->terminator()));
}
- _liveIn[bb->index()] = live;
+ _liveIn[bb->index()] = std::move(live);
}
};
@@ -4381,7 +4564,7 @@ void removeUnreachleBlocks(IR::Function *function)
function->renumberBasicBlocks();
}
-class ConvertArgLocals: protected StmtVisitor, protected ExprVisitor
+class ConvertArgLocals
{
public:
ConvertArgLocals(IR::Function *function)
@@ -4419,10 +4602,13 @@ public:
}
}
- for (BasicBlock *bb : function->basicBlocks())
- if (!bb->isRemoved())
- for (Stmt *s : bb->statements())
- s->accept(this);
+ for (BasicBlock *bb : function->basicBlocks()) {
+ if (!bb->isRemoved()) {
+ for (Stmt *s : bb->statements()) {
+ visit(s);
+ }
+ }
+ }
if (convertArgs && function->formals.size() > 0)
function->basicBlock(0)->prependStatements(extraMoves);
@@ -4430,39 +4616,45 @@ public:
function->locals.clear();
}
-protected:
- virtual void visitConst(Const *) {}
- virtual void visitString(IR::String *) {}
- virtual void visitRegExp(IR::RegExp *) {}
- virtual void visitName(Name *) {}
- virtual void visitTemp(Temp *) {}
- virtual void visitArgLocal(ArgLocal *) {}
- virtual void visitClosure(Closure *) {}
- virtual void visitConvert(Convert *e) { check(e->expr); }
- virtual void visitUnop(Unop *e) { check(e->expr); }
- virtual void visitBinop(Binop *e) { check(e->left); check(e->right); }
- virtual void visitCall(Call *e) {
- check(e->base);
- for (ExprList *it = e->args; it; it = it->next)
- check(it->expr);
- }
- virtual void visitNew(New *e) {
- check(e->base);
- for (ExprList *it = e->args; it; it = it->next)
- check(it->expr);
+private:
+ void visit(Stmt *s)
+ {
+ if (auto e = s->asExp()) {
+ check(e->expr);
+ } else if (auto m = s->asMove()) {
+ check(m->target); check(m->source);
+ } else if (auto c = s->asCJump()) {
+ check(c->cond);
+ } else if (auto r = s->asRet()) {
+ check(r->expr);
+ }
}
- virtual void visitSubscript(Subscript *e) { check(e->base); check(e->index); }
- virtual void visitMember(Member *e) { check(e->base); }
- virtual void visitExp(Exp *s) { check(s->expr); }
- virtual void visitMove(Move *s) { check(s->target); check(s->source); }
- virtual void visitJump(Jump *) {}
- virtual void visitCJump(CJump *s) { check(s->cond); }
- virtual void visitRet(Ret *s) { check(s->expr); }
- virtual void visitPhi(Phi *) {
- Q_UNREACHABLE();
+
+ void visit(Expr *e)
+ {
+ if (auto c = e->asConvert()) {
+ check(c->expr);
+ } else if (auto u = e->asUnop()) {
+ check(u->expr);
+ } else if (auto b = e->asBinop()) {
+ check(b->left); check(b->right);
+ } else if (auto c = e->asCall()) {
+ check(c->base);
+ for (ExprList *it = c->args; it; it = it->next) {
+ check(it->expr);
+ }
+ } else if (auto n = e->asNew()) {
+ check(n->base);
+ for (ExprList *it = n->args; it; it = it->next) {
+ check(it->expr);
+ }
+ } else if (auto s = e->asSubscript()) {
+ check(s->base); check(s->index);
+ } else if (auto m = e->asMember()) {
+ check(m->base);
+ }
}
-private:
void check(Expr *&e) {
if (ArgLocal *al = e->asArgLocal()) {
if (al->kind == ArgLocal::Local) {
@@ -4475,7 +4667,7 @@ private:
e = t;
}
} else {
- e->accept(this);
+ visit(e);
}
}
@@ -4498,7 +4690,7 @@ private:
std::vector<int> tempForLocal;
};
-class CloneBasicBlock: protected IR::StmtVisitor, protected CloneExpr
+class CloneBasicBlock: protected CloneExpr
{
public:
BasicBlock *operator()(IR::BasicBlock *originalBlock)
@@ -4506,38 +4698,37 @@ public:
block = new BasicBlock(originalBlock->function, 0);
for (Stmt *s : originalBlock->statements()) {
- s->accept(this);
+ visit(s);
clonedStmt->location = s->location;
}
return block;
}
-protected:
- virtual void visitExp(Exp *stmt)
- { clonedStmt = block->EXP(clone(stmt->expr)); }
-
- virtual void visitMove(Move *stmt)
- { clonedStmt = block->MOVE(clone(stmt->target), clone(stmt->source)); }
-
- virtual void visitJump(Jump *stmt)
- { clonedStmt = block->JUMP(stmt->target); }
-
- virtual void visitCJump(CJump *stmt)
- { clonedStmt = block->CJUMP(clone(stmt->cond), stmt->iftrue, stmt->iffalse); }
-
- virtual void visitRet(Ret *stmt)
- { clonedStmt = block->RET(clone(stmt->expr)); }
-
- virtual void visitPhi(Phi *stmt)
+private:
+ void visit(Stmt *s)
{
- Phi *phi = block->function->NewStmt<Phi>();
- clonedStmt = phi;
-
- phi->targetTemp = clone(stmt->targetTemp);
- foreach (Expr *in, stmt->incoming)
- phi->incoming.append(clone(in));
- block->appendStatement(phi);
+ if (auto e = s->asExp()) {
+ clonedStmt = block->EXP(clone(e->expr));
+ } else if (auto m = s->asMove()) {
+ clonedStmt = block->MOVE(clone(m->target), clone(m->source));
+ } else if (auto j = s->asJump()) {
+ clonedStmt = block->JUMP(j->target);
+ } else if (auto c = s->asCJump()) {
+ clonedStmt = block->CJUMP(clone(c->cond), c->iftrue, c->iffalse);
+ } else if (auto r = s->asRet()) {
+ clonedStmt = block->RET(clone(r->expr));
+ } else if (auto p = s->asPhi()) {
+ Phi *phi = block->function->NewStmt<Phi>();
+ clonedStmt = phi;
+
+ phi->targetTemp = clone(p->targetTemp);
+ foreach (Expr *in, p->incoming)
+ phi->incoming.append(clone(in));
+ block->appendStatement(phi);
+ } else {
+ Q_UNREACHABLE();
+ }
}
private:
@@ -4706,13 +4897,20 @@ static void verifyCFG(IR::Function *function)
Q_ASSERT(function->basicBlock(bb->index()) == bb);
// Check the terminators:
- if (Jump *jump = bb->terminator()->asJump()) {
+ Stmt *terminator = bb->terminator();
+ if (terminator == nullptr) {
+ Stmt *last = bb->statements().last();
+ Call *call = last->asExp()->expr->asCall();
+ Name *baseName = call->base->asName();
+ Q_ASSERT(baseName->builtin == Name::builtin_rethrow);
+ Q_UNUSED(baseName);
+ } else if (Jump *jump = terminator->asJump()) {
Q_UNUSED(jump);
Q_ASSERT(jump->target);
Q_ASSERT(!jump->target->isRemoved());
Q_ASSERT(bb->out.size() == 1);
Q_ASSERT(bb->out.first() == jump->target);
- } else if (CJump *cjump = bb->terminator()->asCJump()) {
+ } else if (CJump *cjump = terminator->asCJump()) {
Q_UNUSED(cjump);
Q_ASSERT(bb->out.size() == 2);
Q_ASSERT(cjump->iftrue);
@@ -4721,7 +4919,7 @@ static void verifyCFG(IR::Function *function)
Q_ASSERT(cjump->iffalse);
Q_ASSERT(!cjump->iffalse->isRemoved());
Q_ASSERT(cjump->iffalse == bb->out[1]);
- } else if (bb->terminator()->asRet()) {
+ } else if (terminator->asRet()) {
Q_ASSERT(bb->out.size() == 0);
} else {
Q_UNREACHABLE();
@@ -4769,7 +4967,7 @@ static void verifyNoPointerSharing(IR::Function *function)
if (!DoVerification)
return;
- class : public StmtVisitor, public ExprVisitor {
+ class {
public:
void operator()(IR::Function *f)
{
@@ -4777,44 +4975,23 @@ static void verifyNoPointerSharing(IR::Function *function)
if (bb->isRemoved())
continue;
- for (Stmt *s : bb->statements())
- s->accept(this);
+ for (Stmt *s : bb->statements()) {
+ visit(s);
+ }
}
}
- protected:
- virtual void visitExp(Exp *s) { check(s); s->expr->accept(this); }
- virtual void visitMove(Move *s) { check(s); s->target->accept(this); s->source->accept(this); }
- virtual void visitJump(Jump *s) { check(s); }
- virtual void visitCJump(CJump *s) { check(s); s->cond->accept(this); }
- virtual void visitRet(Ret *s) { check(s); s->expr->accept(this); }
- virtual void visitPhi(Phi *s)
+ private:
+ void visit(Stmt *s)
{
check(s);
- s->targetTemp->accept(this);
- foreach (Expr *e, s->incoming)
- e->accept(this);
- }
-
- virtual void visitConst(Const *e) { check(e); }
- virtual void visitString(IR::String *e) { check(e); }
- virtual void visitRegExp(IR::RegExp *e) { check(e); }
- virtual void visitName(Name *e) { check(e); }
- virtual void visitTemp(Temp *e) { check(e); }
- virtual void visitArgLocal(ArgLocal *e) { check(e); }
- virtual void visitClosure(Closure *e) { check(e); }
- virtual void visitConvert(Convert *e) { check(e); e->expr->accept(this); }
- virtual void visitUnop(Unop *e) { check(e); e->expr->accept(this); }
- virtual void visitBinop(Binop *e) { check(e); e->left->accept(this); e->right->accept(this); }
- virtual void visitCall(Call *e) { check(e); e->base->accept(this); check(e->args); }
- virtual void visitNew(New *e) { check(e); e->base->accept(this); check(e->args); }
- virtual void visitSubscript(Subscript *e) { check(e); e->base->accept(this); e->index->accept(this); }
- virtual void visitMember(Member *e) { check(e); e->base->accept(this); }
-
- void check(ExprList *l)
+ STMT_VISIT_ALL_KINDS(s);
+ }
+
+ void visit(Expr *e)
{
- for (ExprList *it = l; it; it = it->next)
- check(it->expr);
+ check(e);
+ EXPR_VISIT_ALL_KINDS(e);
}
private:
@@ -4836,7 +5013,7 @@ static void verifyNoPointerSharing(IR::Function *function)
V(function);
}
-class RemoveLineNumbers: public SideEffectsChecker, public StmtVisitor
+class RemoveLineNumbers: private SideEffectsChecker
{
public:
static void run(IR::Function *function)
@@ -4854,28 +5031,99 @@ public:
}
private:
+ ~RemoveLineNumbers() {}
+
static bool hasSideEffects(Stmt *stmt)
{
RemoveLineNumbers checker;
- stmt->accept(&checker);
+ if (auto e = stmt->asExp()) {
+ checker.visit(e->expr);
+ } else if (auto m = stmt->asMove()) {
+ checker.visit(m->source);
+ if (!checker.seenSideEffects()) {
+ checker.visit(m->target);
+ }
+ } else if (auto c = stmt->asCJump()) {
+ checker.visit(c->cond);
+ } else if (auto r = stmt->asRet()) {
+ checker.visit(r->expr);
+ }
return checker.seenSideEffects();
}
- void visitExp(Exp *s) Q_DECL_OVERRIDE { s->expr->accept(this); }
- void visitMove(Move *s) Q_DECL_OVERRIDE { s->source->accept(this); s->target->accept(this); }
- void visitJump(Jump *) Q_DECL_OVERRIDE {}
- void visitCJump(CJump *s) Q_DECL_OVERRIDE { s->cond->accept(this); }
- void visitRet(Ret *s) Q_DECL_OVERRIDE { s->expr->accept(this); }
- void visitPhi(Phi *) Q_DECL_OVERRIDE {}
+ void visitTemp(Temp *) Q_DECL_OVERRIDE Q_DECL_FINAL {}
};
+void mergeBasicBlocks(IR::Function *function, DefUses *du, DominatorTree *dt)
+{
+ enum { DebugBlockMerging = 0 };
+
+ if (function->hasTry)
+ return;
+
+ showMeTheCode(function, "Before basic block merging");
+
+ // Now merge a basic block with its successor when there is one outgoing edge, and the
+ // successor has one incoming edge.
+ for (int i = 0, ei = function->basicBlockCount(); i != ei; ++i) {
+ BasicBlock *bb = function->basicBlock(i);
+
+ bb->nextLocation = QQmlJS::AST::SourceLocation(); // make sure appendStatement doesn't mess with the line info
+
+ if (bb->isRemoved()) continue; // the block has been removed, so ignore it
+ if (bb->out.size() != 1) continue; // more than one outgoing edge
+ BasicBlock *successor = bb->out.first();
+ if (successor->in.size() != 1) continue; // more than one incoming edge
+
+ // Loop header? No efficient way to update the other blocks that refer to this as containing group,
+ // so don't do merging yet.
+ if (successor->isGroupStart()) continue;
+
+ // Ok, we can merge the two basic blocks.
+ if (DebugBlockMerging) {
+ qDebug("Merging L%d into L%d", successor->index(), bb->index());
+ }
+ Q_ASSERT(bb->terminator()->asJump());
+ bb->removeStatement(bb->statementCount() - 1); // remove the terminator, and replace it with:
+ for (Stmt *s : successor->statements()) {
+ bb->appendStatement(s); // add all statements from the successor to the current basic block
+ if (auto cjump = s->asCJump())
+ cjump->parent = bb;
+ }
+ bb->out = successor->out; // set the outgoing edges to the successor's so they're now in sync with our new terminator
+ for (auto newSuccessor : bb->out) {
+ for (auto &backlink : newSuccessor->in) {
+ if (backlink == successor) {
+ backlink = bb; // for all successors of our successor: set the incoming edges to come from bb, because we'll now jump there.
+ }
+ }
+ }
+ if (du) {
+ // all statements in successor have moved to bb, so make sure that the containing blocks
+ // stored in DefUses get updated (meaning: point to bb)
+ du->replaceBasicBlock(successor, bb);
+ }
+ if (dt) {
+ // update the immediate dominators to: any block that was dominated by the successor
+ // will now need to point to bb's immediate dominator. The reason is that bb itself
+ // won't be anyones immediate dominator, because it had just one outgoing edge.
+ dt->mergeIntoPredecessor(successor);
+ }
+ function->removeBasicBlock(successor);
+ --i; // re-run on the current basic-block, so any chain gets collapsed.
+ }
+
+ showMeTheCode(function, "After basic block merging");
+ verifyCFG(function);
+}
+
} // anonymous namespace
void LifeTimeInterval::setFrom(int from) {
Q_ASSERT(from > 0);
if (_ranges.isEmpty()) { // this is the case where there is no use, only a define
- _ranges.push_front(Range(from, from));
+ _ranges.prepend(Range(from, from));
if (_end == InvalidPosition)
_end = from;
} else {
@@ -4889,7 +5137,7 @@ void LifeTimeInterval::addRange(int from, int to) {
Q_ASSERT(to >= from);
if (_ranges.isEmpty()) {
- _ranges.push_front(Range(from, to));
+ _ranges.prepend(Range(from, to));
_end = to;
return;
}
@@ -4904,12 +5152,12 @@ void LifeTimeInterval::addRange(int from, int to) {
break;
p1->start = qMin(p->start, p1->start);
p1->end = qMax(p->end, p1->end);
- _ranges.pop_front();
+ _ranges.remove(0);
p = &_ranges.first();
}
} else {
if (to < p->start) {
- _ranges.push_front(Range(from, to));
+ _ranges.prepend(Range(from, to));
} else {
Q_ASSERT(from > _ranges.last().end);
_ranges.push_back(Range(from, to));
@@ -4950,7 +5198,7 @@ LifeTimeInterval LifeTimeInterval::split(int atPosition, int newStart)
}
if (newInterval._ranges.first().end == atPosition)
- newInterval._ranges.removeFirst();
+ newInterval._ranges.remove(0);
if (newStart == InvalidPosition) {
// the temp stays inactive for the rest of its lifetime
@@ -4970,7 +5218,7 @@ LifeTimeInterval LifeTimeInterval::split(int atPosition, int newStart)
break;
} else {
// the temp stays inactive for this interval, so remove it.
- newInterval._ranges.removeFirst();
+ newInterval._ranges.remove(0);
}
}
Q_ASSERT(!newInterval._ranges.isEmpty());
@@ -5001,15 +5249,6 @@ void LifeTimeInterval::dump(QTextStream &out) const {
out << " (register " << _reg << ")";
}
-bool LifeTimeInterval::lessThan(const LifeTimeInterval *r1, const LifeTimeInterval *r2) {
- if (r1->_ranges.first().start == r2->_ranges.first().start) {
- if (r1->isSplitFromInterval() == r2->isSplitFromInterval())
- return r1->_ranges.last().end < r2->_ranges.last().end;
- else
- return r1->isSplitFromInterval();
- } else
- return r1->_ranges.first().start < r2->_ranges.first().start;
-}
bool LifeTimeInterval::lessThanForTemp(const LifeTimeInterval *r1, const LifeTimeInterval *r2)
{
@@ -5103,6 +5342,8 @@ void Optimizer::run(QQmlEnginePrivate *qmlEngine, bool doTypeInference, bool pee
if (!function->hasTry && !function->hasWith && !function->module->debugMode && doSSA && statementCount <= 300) {
// qout << "SSA for " << (function->name ? qPrintable(*function->name) : "<anonymous>") << endl;
+ mergeBasicBlocks(function, nullptr, nullptr);
+
ConvertArgLocals(function).toTemps();
showMeTheCode(function, "After converting arguments to locals");
@@ -5178,6 +5419,7 @@ void Optimizer::run(QQmlEnginePrivate *qmlEngine, bool doTypeInference, bool pee
}
verifyNoPointerSharing(function);
+ mergeBasicBlocks(function, &defUses, &df);
// Basic-block cycles that are unreachable (i.e. for loops in a then-part where the
// condition is calculated to be always false) are not yet removed. This will choke the
diff --git a/src/qml/compiler/qv4ssa_p.h b/src/qml/compiler/qv4ssa_p.h
index 5d4b12e275..3a787f0347 100644
--- a/src/qml/compiler/qv4ssa_p.h
+++ b/src/qml/compiler/qv4ssa_p.h
@@ -75,7 +75,7 @@ public:
bool covers(int position) const { return start <= position && position <= end; }
};
- typedef QVector<Range> Ranges;
+ typedef QVarLengthArray<Range, 4> Ranges;
private:
Temp _temp;
@@ -89,7 +89,7 @@ public:
enum { InvalidPosition = -1 };
enum { InvalidRegister = -1 };
- explicit LifeTimeInterval(int rangeCapacity = 2)
+ explicit LifeTimeInterval(int rangeCapacity = 4)
: _end(InvalidPosition)
, _reg(InvalidRegister)
, _isFixedInterval(0)
@@ -146,6 +146,17 @@ public:
}
};
+inline bool LifeTimeInterval::lessThan(const LifeTimeInterval *r1, const LifeTimeInterval *r2)
+{
+ if (r1->_ranges.first().start == r2->_ranges.first().start) {
+ if (r1->isSplitFromInterval() == r2->isSplitFromInterval())
+ return r1->_ranges.last().end < r2->_ranges.last().end;
+ else
+ return r1->isSplitFromInterval();
+ } else
+ return r1->_ranges.first().start < r2->_ranges.first().start;
+}
+
class LifeTimeIntervals
{
Q_DISABLE_COPY(LifeTimeIntervals)
@@ -379,7 +390,7 @@ protected:
_unhandled.removeLast();
}
- s->accept(this);
+ visit(s);
}
if (IR::Jump *jump = s->asJump()) {
@@ -402,7 +413,7 @@ protected:
moves.order();
QList<IR::Move *> newMoves = moves.insertMoves(_currentBasicBlock, _function, true);
foreach (IR::Move *move, newMoves)
- move->accept(this);
+ visit(move);
}
}
diff --git a/src/qml/configure.json b/src/qml/configure.json
new file mode 100644
index 0000000000..d22ba3b8f0
--- /dev/null
+++ b/src/qml/configure.json
@@ -0,0 +1,37 @@
+{
+ "module": "qml",
+ "depends": [
+ "core-private",
+ "network-private"
+ ],
+
+ "commandline": {
+ "options": {
+ "qml-interpreter": "boolean",
+ "qml-network": "boolean"
+ }
+ },
+
+ "features": {
+ "qml-interpreter": {
+ "label": "QML interpreter",
+ "purpose": "Support for the QML interpreter",
+ "output": [ "privateFeature" ]
+ },
+ "qml-network": {
+ "label": "QML network support",
+ "purpose": "Provides network transparency for QML",
+ "output": [ "publicFeature" ]
+ }
+ },
+
+ "summary": [
+ {
+ "section": "Qt QML",
+ "entries": [
+ "qml-interpreter",
+ "qml-network"
+ ]
+ }
+ ]
+}
diff --git a/src/qml/debugger/debugger.pri b/src/qml/debugger/debugger.pri
index 30a44eedd1..da1ab867d4 100644
--- a/src/qml/debugger/debugger.pri
+++ b/src/qml/debugger/debugger.pri
@@ -1,21 +1,28 @@
-contains(QT_CONFIG, no-qml-debug):DEFINES += QT_NO_QML_DEBUGGER
+contains(QT_CONFIG, no-qml-debug) {
+ DEFINES += QT_NO_QML_DEBUGGER
+ MODULE_DEFINES += QT_NO_QML_DEBUGGER
+} else {
+ HEADERS += \
+ $$PWD/qqmldebugpluginmanager_p.h \
+ $$PWD/qqmldebugservicefactory_p.h
-SOURCES += \
- $$PWD/qqmldebug.cpp \
- $$PWD/qqmldebugconnector.cpp \
- $$PWD/qqmldebugservice.cpp \
- $$PWD/qqmldebugserviceinterfaces.cpp \
- $$PWD/qqmlabstractprofileradapter.cpp \
- $$PWD/qqmlprofiler.cpp
+ SOURCES += \
+ $$PWD/qqmldebug.cpp \
+ $$PWD/qqmldebugconnector.cpp \
+ $$PWD/qqmldebugservice.cpp \
+ $$PWD/qqmlabstractprofileradapter.cpp \
+ $$PWD/qqmlmemoryprofiler.cpp \
+ $$PWD/qqmlprofiler.cpp \
+ $$PWD/qqmldebugserviceinterfaces.cpp
+}
HEADERS += \
$$PWD/qqmldebugconnector_p.h \
- $$PWD/qqmldebugpluginmanager_p.h \
$$PWD/qqmldebugservice_p.h \
- $$PWD/qqmldebugservicefactory_p.h \
$$PWD/qqmldebugserviceinterfaces_p.h \
$$PWD/qqmldebugstatesdelegate_p.h \
$$PWD/qqmldebug.h \
+ $$PWD/qqmlmemoryprofiler_p.h \
$$PWD/qqmlprofilerdefinitions_p.h \
$$PWD/qqmlabstractprofileradapter_p.h \
$$PWD/qqmlprofiler_p.h
diff --git a/src/qml/debugger/qqmlabstractprofileradapter_p.h b/src/qml/debugger/qqmlabstractprofileradapter_p.h
index 1104608055..6a05a80f37 100644
--- a/src/qml/debugger/qqmlabstractprofileradapter_p.h
+++ b/src/qml/debugger/qqmlabstractprofileradapter_p.h
@@ -59,6 +59,8 @@
QT_BEGIN_NAMESPACE
+#ifndef QT_NO_QML_DEBUGGER
+
class QQmlProfilerService;
class Q_QML_PRIVATE_EXPORT QQmlAbstractProfilerAdapter : public QObject, public QQmlProfilerDefinitions {
Q_OBJECT
@@ -71,13 +73,13 @@ public:
virtual ~QQmlAbstractProfilerAdapter() {}
void setService(QQmlProfilerService *new_service) { service = new_service; }
- virtual qint64 sendMessages(qint64 until, QList<QByteArray> &messages) = 0;
+ virtual qint64 sendMessages(qint64 until, QList<QByteArray> &messages, bool trackLocations) = 0;
void startProfiling(quint64 features);
void stopProfiling();
- void reportData() { emit dataRequested(); }
+ void reportData(bool trackLocations) { emit dataRequested(trackLocations); }
void stopWaiting() { waiting = false; }
void startWaiting() { waiting = true; }
@@ -94,7 +96,7 @@ signals:
void profilingDisabled();
void profilingDisabledWhileWaiting();
- void dataRequested();
+ void dataRequested(bool trackLocations);
void referenceTimeKnown(const QElapsedTimer &timer);
protected:
@@ -114,6 +116,8 @@ public:
#define QQmlAbstractProfilerAdapterFactory_iid "org.qt-project.Qt.QQmlAbstractProfilerAdapterFactory"
+#endif // QT_NO_QML_DEBUGGER
+
QT_END_NAMESPACE
#endif // QQMLABSTRACTPROFILERADAPTER_P_H
diff --git a/src/qml/debugger/qqmldebug.cpp b/src/qml/debugger/qqmldebug.cpp
index 557cce08d5..b2c4b139ee 100644
--- a/src/qml/debugger/qqmldebug.cpp
+++ b/src/qml/debugger/qqmldebug.cpp
@@ -47,15 +47,11 @@ QT_BEGIN_NAMESPACE
QQmlDebuggingEnabler::QQmlDebuggingEnabler(bool printWarning)
{
-#ifndef QQML_NO_DEBUG_PROTOCOL
if (!QQmlEnginePrivate::qml_debugging_enabled
&& printWarning) {
qDebug("QML debugging is enabled. Only use this in a safe environment.");
}
QQmlEnginePrivate::qml_debugging_enabled = true;
-#else
- Q_UNUSED(printWarning);
-#endif
}
/*!
@@ -105,11 +101,7 @@ QStringList QQmlDebuggingEnabler::profilerServices()
*/
void QQmlDebuggingEnabler::setServices(const QStringList &services)
{
-#ifndef QQML_NO_DEBUG_PROTOCOL
QQmlDebugConnector::setServices(services);
-#else
- Q_UNUSED(services);
-#endif
}
/*!
@@ -172,16 +164,9 @@ bool QQmlDebuggingEnabler::connectToLocalDebugger(const QString &socketFileName,
bool QQmlDebuggingEnabler::startDebugConnector(const QString &pluginName,
const QVariantHash &configuration)
{
-#ifndef QQML_NO_DEBUG_PROTOCOL
QQmlDebugConnector::setPluginKey(pluginName);
QQmlDebugConnector *connector = QQmlDebugConnector::instance();
- if (connector)
- return connector->open(configuration);
-#else
- Q_UNUSED(pluginName);
- Q_UNUSED(configuration);
-#endif
- return false;
+ return connector ? connector->open(configuration) : false;
}
enum { HookCount = 3 };
diff --git a/src/qml/debugger/qqmldebug.h b/src/qml/debugger/qqmldebug.h
index 660b9e4d46..fb41039867 100644
--- a/src/qml/debugger/qqmldebug.h
+++ b/src/qml/debugger/qqmldebug.h
@@ -46,6 +46,7 @@
QT_BEGIN_NAMESPACE
+#ifndef QT_NO_QML_DEBUGGER
struct Q_QML_EXPORT QQmlDebuggingEnabler
{
@@ -77,6 +78,8 @@ static QQmlDebuggingEnabler qQmlEnableDebuggingHelper(false);
static QQmlDebuggingEnabler qQmlEnableDebuggingHelper(true);
#endif
+#endif
+
QT_END_NAMESPACE
#endif // QQMLDEBUG_H
diff --git a/src/qml/debugger/qqmldebugconnector_p.h b/src/qml/debugger/qqmldebugconnector_p.h
index 05755250bd..0d3e2e2e47 100644
--- a/src/qml/debugger/qqmldebugconnector_p.h
+++ b/src/qml/debugger/qqmldebugconnector_p.h
@@ -59,6 +59,29 @@
QT_BEGIN_NAMESPACE
+#ifdef QT_NO_QML_DEBUGGER
+
+class Q_QML_PRIVATE_EXPORT QQmlDebugConnector
+{
+public:
+ static QQmlDebugConnector *instance() { return nullptr; }
+
+ template<class Service>
+ static Service *service() { return nullptr; }
+
+ bool hasEngine(QJSEngine *) const { return false; }
+ void addEngine(QJSEngine *) {}
+ void removeEngine(QJSEngine *) {}
+
+ bool open(const QVariantHash &configuration = QVariantHash())
+ {
+ Q_UNUSED(configuration);
+ return false;
+ }
+};
+
+#else
+
class QQmlDebugService;
class Q_QML_PRIVATE_EXPORT QQmlDebugConnector : public QObject
{
@@ -106,6 +129,8 @@ public:
#define QQmlDebugConnectorFactory_iid "org.qt-project.Qt.QQmlDebugConnectorFactory"
+#endif
+
QT_END_NAMESPACE
#endif // QQMLDEBUGCONNECTOR_H
diff --git a/src/qml/debugger/qqmldebugservice.cpp b/src/qml/debugger/qqmldebugservice.cpp
index b780735f48..b576c3bb85 100644
--- a/src/qml/debugger/qqmldebugservice.cpp
+++ b/src/qml/debugger/qqmldebugservice.cpp
@@ -132,7 +132,6 @@ public:
int nextId;
-private slots:
void remove(QObject *obj);
};
}
@@ -163,7 +162,7 @@ int QQmlDebugService::idForObject(QObject *object)
int id = hash->nextId++;
hash->ids.insert(id, object);
iter = hash->objects.insert(object, id);
- connect(object, SIGNAL(destroyed(QObject*)), hash, SLOT(remove(QObject*)));
+ connect(object, &QObject::destroyed, hash, &ObjectReferenceHash::remove);
}
return iter.value();
}
@@ -176,36 +175,6 @@ const QHash<int, QObject *> &QQmlDebugService::objectsForIds()
return objectReferenceHash()->ids;
}
-void QQmlDebugService::stateAboutToBeChanged(State)
-{
-}
-
-void QQmlDebugService::stateChanged(State)
-{
-}
-
-void QQmlDebugService::messageReceived(const QByteArray &)
-{
-}
-
-void QQmlDebugService::engineAboutToBeAdded(QJSEngine *engine)
-{
- emit attachedToEngine(engine);
-}
-
-void QQmlDebugService::engineAboutToBeRemoved(QJSEngine *engine)
-{
- emit detachedFromEngine(engine);
-}
-
-void QQmlDebugService::engineAdded(QJSEngine *)
-{
-}
-
-void QQmlDebugService::engineRemoved(QJSEngine *)
-{
-}
-
QT_END_NAMESPACE
#include "qqmldebugservice.moc"
diff --git a/src/qml/debugger/qqmldebugservice_p.h b/src/qml/debugger/qqmldebugservice_p.h
index 9ddc692ecc..42a57a39f2 100644
--- a/src/qml/debugger/qqmldebugservice_p.h
+++ b/src/qml/debugger/qqmldebugservice_p.h
@@ -58,6 +58,8 @@
QT_BEGIN_NAMESPACE
+#ifndef QT_NO_QML_DEBUGGER
+
class QJSEngine;
class QQmlDebugServicePrivate;
@@ -65,7 +67,6 @@ class Q_QML_PRIVATE_EXPORT QQmlDebugService : public QObject
{
Q_OBJECT
Q_DECLARE_PRIVATE(QQmlDebugService)
- Q_DISABLE_COPY(QQmlDebugService)
public:
~QQmlDebugService();
@@ -77,14 +78,15 @@ public:
State state() const;
void setState(State newState);
- virtual void stateAboutToBeChanged(State);
- virtual void stateChanged(State);
- virtual void messageReceived(const QByteArray &);
+ virtual void stateAboutToBeChanged(State) {}
+ virtual void stateChanged(State) {}
+ virtual void messageReceived(const QByteArray &) {}
+
+ virtual void engineAboutToBeAdded(QJSEngine *engine) { emit attachedToEngine(engine); }
+ virtual void engineAboutToBeRemoved(QJSEngine *engine) { emit detachedFromEngine(engine); }
- virtual void engineAboutToBeAdded(QJSEngine *);
- virtual void engineAboutToBeRemoved(QJSEngine *);
- virtual void engineAdded(QJSEngine *);
- virtual void engineRemoved(QJSEngine *);
+ virtual void engineAdded(QJSEngine *) {}
+ virtual void engineRemoved(QJSEngine *) {}
static const QHash<int, QObject *> &objectsForIds();
static int idForObject(QObject *);
@@ -101,6 +103,8 @@ signals:
void messagesToClient(const QString &name, const QList<QByteArray> &messages);
};
+#endif
+
QT_END_NAMESPACE
#endif // QQMLDEBUGSERVICE_H
diff --git a/src/qml/debugger/qqmldebugserviceinterfaces_p.h b/src/qml/debugger/qqmldebugserviceinterfaces_p.h
index 8f66779872..ca6293c3ec 100644
--- a/src/qml/debugger/qqmldebugserviceinterfaces_p.h
+++ b/src/qml/debugger/qqmldebugserviceinterfaces_p.h
@@ -62,7 +62,46 @@
QT_BEGIN_NAMESPACE
-class Q_QML_PRIVATE_EXPORT QV4DebugService : protected QQmlDebugService
+class QWindow;
+class QQuickWindow;
+
+#ifdef QT_NO_QML_DEBUGGER
+
+struct QV4DebugService
+{
+ void signalEmitted(const QString &) {}
+};
+
+struct QQmlProfilerService
+{
+ void startProfiling(QJSEngine *engine, quint64 features = std::numeric_limits<quint64>::max())
+ {
+ Q_UNUSED(engine);
+ Q_UNUSED(features);
+ }
+
+ void stopProfiling(QJSEngine *) {}
+};
+
+struct QQmlEngineDebugService
+{
+ void objectCreated(QJSEngine *, QObject *) {}
+ virtual void setStatesDelegate(QQmlDebugStatesDelegate *) {}
+};
+
+struct QQmlInspectorService {
+ void addWindow(QQuickWindow *) {}
+ void setParentWindow(QQuickWindow *, QWindow *) {}
+ void removeWindow(QQuickWindow *) {}
+};
+
+struct QDebugMessageService {};
+struct QQmlEngineControlService {};
+struct QQmlNativeDebugService {};
+
+#else
+
+class Q_QML_PRIVATE_EXPORT QV4DebugService : public QQmlDebugService
{
Q_OBJECT
public:
@@ -77,7 +116,7 @@ protected:
QQmlDebugService(s_key, version, parent) {}
};
-class Q_QML_PRIVATE_EXPORT QQmlProfilerService : protected QQmlDebugService
+class Q_QML_PRIVATE_EXPORT QQmlProfilerService : public QQmlDebugService
{
Q_OBJECT
public:
@@ -99,7 +138,7 @@ protected:
QQmlDebugService(s_key, version, parent) {}
};
-class Q_QML_PRIVATE_EXPORT QQmlEngineDebugService : protected QQmlDebugService
+class Q_QML_PRIVATE_EXPORT QQmlEngineDebugService : public QQmlDebugService
{
Q_OBJECT
public:
@@ -117,9 +156,7 @@ protected:
QQmlBoundSignal *nextSignal(QQmlBoundSignal *prev) { return prev->m_nextSignal; }
};
-class QWindow;
-class QQuickWindow;
-class Q_QML_PRIVATE_EXPORT QQmlInspectorService : protected QQmlDebugService
+class Q_QML_PRIVATE_EXPORT QQmlInspectorService : public QQmlDebugService
{
Q_OBJECT
public:
@@ -136,7 +173,7 @@ protected:
QQmlDebugService(s_key, version, parent) {}
};
-class Q_QML_PRIVATE_EXPORT QDebugMessageService : protected QQmlDebugService
+class Q_QML_PRIVATE_EXPORT QDebugMessageService : public QQmlDebugService
{
Q_OBJECT
public:
@@ -151,7 +188,7 @@ protected:
QQmlDebugService(s_key, version, parent) {}
};
-class Q_QML_PRIVATE_EXPORT QQmlEngineControlService : protected QQmlDebugService
+class Q_QML_PRIVATE_EXPORT QQmlEngineControlService : public QQmlDebugService
{
Q_OBJECT
public:
@@ -165,7 +202,7 @@ protected:
};
-class Q_QML_PRIVATE_EXPORT QQmlNativeDebugService : protected QQmlDebugService
+class Q_QML_PRIVATE_EXPORT QQmlNativeDebugService : public QQmlDebugService
{
Q_OBJECT
@@ -178,6 +215,8 @@ protected:
static const QString s_key;
};
+#endif
+
QT_END_NAMESPACE
#endif // QQMLDEBUGSERVICEINTERFACES_P_H
diff --git a/src/qml/debugger/qqmldebugstatesdelegate_p.h b/src/qml/debugger/qqmldebugstatesdelegate_p.h
index 42c4e94b50..95f727fb2d 100644
--- a/src/qml/debugger/qqmldebugstatesdelegate_p.h
+++ b/src/qml/debugger/qqmldebugstatesdelegate_p.h
@@ -57,6 +57,11 @@
QT_BEGIN_NAMESPACE
+#ifdef QT_NO_QML_DEBUGGER
+
+class QQmlDebugStatesDelegate {};
+
+#else
class QQmlContext;
class QQmlProperty;
@@ -90,6 +95,8 @@ private:
Q_DISABLE_COPY(QQmlDebugStatesDelegate)
};
+#endif
+
QT_END_NAMESPACE
#endif // QQMLDEBUGSTATESDELEGATE_P_H
diff --git a/src/qml/qml/qqmlmemoryprofiler.cpp b/src/qml/debugger/qqmlmemoryprofiler.cpp
index d9e121bb1b..53d4e7ab21 100644
--- a/src/qml/qml/qqmlmemoryprofiler.cpp
+++ b/src/qml/debugger/qqmlmemoryprofiler.cpp
@@ -38,18 +38,11 @@
****************************************************************************/
#include "qqmlmemoryprofiler_p.h"
-#include <QUrl>
QT_BEGIN_NAMESPACE
-enum LibraryState
-{
- Unloaded,
- Failed,
- Loaded
-};
-static LibraryState state = Unloaded;
+QQmlMemoryScope::LibraryState QQmlMemoryScope::state = QQmlMemoryScope::Unloaded;
typedef void (qmlmemprofile_stats)(int *allocCount, int *bytesAllocated);
typedef void (qmlmemprofile_clear)();
@@ -73,7 +66,7 @@ static qmlmemprofile_is_enabled *memprofile_is_enabled;
extern QFunctionPointer qt_linux_find_symbol_sys(const char *symbol);
#endif
-static bool openLibrary()
+bool QQmlMemoryScope::doOpenLibrary()
{
#if defined(Q_OS_LINUX) && !defined(QT_NO_LIBRARY)
if (state == Unloaded) {
@@ -97,31 +90,22 @@ static bool openLibrary()
return state == Loaded;
}
-QQmlMemoryScope::QQmlMemoryScope(const QUrl &url) : pushed(false)
-{
- if (openLibrary() && memprofile_is_enabled()) {
- memprofile_push_location(url.path().toUtf8().constData(), 0);
- pushed = true;
- }
-}
-
-QQmlMemoryScope::QQmlMemoryScope(const char *string) : pushed(false)
+void QQmlMemoryScope::init(const char *string)
{
- if (openLibrary() && memprofile_is_enabled()) {
+ if (memprofile_is_enabled()) {
memprofile_push_location(string, 0);
pushed = true;
}
}
-QQmlMemoryScope::~QQmlMemoryScope()
+void QQmlMemoryScope::done()
{
- if (pushed)
- memprofile_pop_location();
+ memprofile_pop_location();
}
bool QQmlMemoryProfiler::isEnabled()
{
- if (openLibrary())
+ if (QQmlMemoryScope::openLibrary())
return memprofile_is_enabled();
return false;
@@ -129,31 +113,31 @@ bool QQmlMemoryProfiler::isEnabled()
void QQmlMemoryProfiler::enable()
{
- if (openLibrary())
+ if (QQmlMemoryScope::openLibrary())
memprofile_enable();
}
void QQmlMemoryProfiler::disable()
{
- if (openLibrary())
+ if (QQmlMemoryScope::openLibrary())
memprofile_disable();
}
void QQmlMemoryProfiler::clear()
{
- if (openLibrary())
+ if (QQmlMemoryScope::openLibrary())
memprofile_clear();
}
void QQmlMemoryProfiler::stats(int *allocCount, int *bytesAllocated)
{
- if (openLibrary())
+ if (QQmlMemoryScope::openLibrary())
memprofile_stats(allocCount, bytesAllocated);
}
void QQmlMemoryProfiler::save(const char *filename)
{
- if (openLibrary())
+ if (QQmlMemoryScope::openLibrary())
memprofile_save(filename);
}
diff --git a/src/qml/debugger/qqmlmemoryprofiler_p.h b/src/qml/debugger/qqmlmemoryprofiler_p.h
new file mode 100644
index 0000000000..fb71c999c3
--- /dev/null
+++ b/src/qml/debugger/qqmlmemoryprofiler_p.h
@@ -0,0 +1,134 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml 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 QQMLMEMORYPROFILER_H
+#define QQMLMEMORYPROFILER_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/qtqmlglobal_p.h>
+#include <QUrl>
+
+QT_BEGIN_NAMESPACE
+
+#ifdef QT_NO_QML_DEBUGGER
+
+#define QML_MEMORY_SCOPE_URL(url)
+#define QML_MEMORY_SCOPE_STRING(s)
+
+#else
+
+class Q_QML_PRIVATE_EXPORT QQmlMemoryScope
+{
+public:
+ explicit QQmlMemoryScope(const QUrl &url)
+ : pushed(false)
+ {
+ if (Q_UNLIKELY(openLibrary()))
+ init(url.path().toUtf8().constData());
+ }
+
+ explicit QQmlMemoryScope(const char *string)
+ : pushed(false)
+ {
+ if (Q_UNLIKELY(openLibrary()))
+ init(string);
+ }
+
+ ~QQmlMemoryScope()
+ {
+ if (Q_UNLIKELY(pushed))
+ done();
+ }
+
+ enum LibraryState
+ {
+ Unloaded,
+ Failed,
+ Loaded
+ };
+
+ static bool openLibrary()
+ {
+ if (Q_LIKELY(state == Loaded))
+ return true;
+ if (state == Failed)
+ return false;
+
+ return doOpenLibrary();
+ }
+
+private:
+ Q_NEVER_INLINE void init(const char *string);
+ Q_NEVER_INLINE void done();
+ Q_NEVER_INLINE static bool doOpenLibrary();
+
+ static LibraryState state;
+
+ bool pushed;
+};
+
+class Q_QML_PRIVATE_EXPORT QQmlMemoryProfiler
+{
+public:
+ static void enable();
+ static void disable();
+ static bool isEnabled();
+
+ static void clear();
+ static void stats(int *allocCount, int *bytesAllocated);
+ static void save(const char *filename);
+};
+
+#define QML_MEMORY_SCOPE_URL(url) QQmlMemoryScope _qml_memory_scope(url)
+#define QML_MEMORY_SCOPE_STRING(s) QQmlMemoryScope _qml_memory_scope(s)
+
+#endif
+
+QT_END_NAMESPACE
+#endif // QQMLMEMORYPROFILER_H
diff --git a/src/qml/debugger/qqmlprofiler.cpp b/src/qml/debugger/qqmlprofiler.cpp
index 629d5cb7b8..ffba731b13 100644
--- a/src/qml/debugger/qqmlprofiler.cpp
+++ b/src/qml/debugger/qqmlprofiler.cpp
@@ -59,19 +59,21 @@ void QQmlProfiler::startProfiling(quint64 features)
void QQmlProfiler::stopProfiling()
{
featuresEnabled = false;
- reportData();
+ reportData(true);
+ m_locations.clear();
}
-void QQmlProfiler::reportData()
+void QQmlProfiler::reportData(bool trackLocations)
{
LocationHash resolved;
resolved.reserve(m_locations.size());
- for (auto it = m_locations.constBegin(), end = m_locations.constEnd(); it != end; ++it)
- resolved.insert(it.key(), it.value());
-
- // This unrefs all the objects. We have to make sure we do this in the GUI thread. Also, it's
- // a good idea to release the memory before creating the packets to be sent.
- m_locations.clear();
+ for (auto it = m_locations.begin(), end = m_locations.end(); it != end; ++it) {
+ if (!trackLocations || !it->sent) {
+ resolved.insert(it.key(), it.value());
+ if (trackLocations)
+ it->sent = true;
+ }
+ }
QVector<QQmlProfilerData> data;
data.swap(m_data);
diff --git a/src/qml/debugger/qqmlprofiler_p.h b/src/qml/debugger/qqmlprofiler_p.h
index 1380599fb7..6643695d11 100644
--- a/src/qml/debugger/qqmlprofiler_p.h
+++ b/src/qml/debugger/qqmlprofiler_p.h
@@ -55,7 +55,6 @@
#include <private/qqmlboundsignal_p.h>
#include <private/qfinitestack_p.h>
#include <private/qqmlbinding_p.h>
-#include <private/qqmlcompiler_p.h>
#include "qqmlprofilerdefinitions_p.h"
#include "qqmlabstractprofileradapter_p.h"
@@ -64,6 +63,54 @@
QT_BEGIN_NAMESPACE
+#ifdef QT_NO_QML_DEBUGGER
+
+#define Q_QML_PROFILE_IF_ENABLED(feature, profiler, Code)
+#define Q_QML_PROFILE(feature, profiler, Method)
+#define Q_QML_OC_PROFILE(member, Code)
+
+struct QQmlProfiler {};
+
+struct QQmlBindingProfiler
+{
+ QQmlBindingProfiler(quintptr, QQmlBinding *, QV4::FunctionObject *) {}
+};
+
+struct QQmlHandlingSignalProfiler
+{
+ QQmlHandlingSignalProfiler(quintptr, QQmlBoundSignalExpression *) {}
+};
+
+struct QQmlCompilingProfiler
+{
+ QQmlCompilingProfiler(quintptr, QQmlDataBlob *) {}
+};
+
+struct QQmlVmeProfiler {
+ QQmlVmeProfiler() {}
+
+ void init(quintptr, int) {}
+
+ const QV4::CompiledData::Object *pop() { return nullptr; }
+ void push(const QV4::CompiledData::Object *) {}
+
+ static const quintptr profiler = 0;
+};
+
+struct QQmlObjectCreationProfiler
+{
+ QQmlObjectCreationProfiler(quintptr, const QV4::CompiledData::Object *) {}
+ void update(QV4::CompiledData::CompilationUnit *, const QV4::CompiledData::Object *,
+ const QString &, const QUrl &) {}
+};
+
+struct QQmlObjectCompletionProfiler
+{
+ QQmlObjectCompletionProfiler(QQmlVmeProfiler *) {}
+};
+
+#else
+
#define Q_QML_PROFILE_IF_ENABLED(feature, profiler, Code)\
if (profiler && (profiler->featuresEnabled & (1 << feature))) {\
Code;\
@@ -73,6 +120,9 @@ QT_BEGIN_NAMESPACE
#define Q_QML_PROFILE(feature, profiler, Method)\
Q_QML_PROFILE_IF_ENABLED(feature, profiler, profiler->Method)
+#define Q_QML_OC_PROFILE(member, Code)\
+ Q_QML_PROFILE_IF_ENABLED(QQmlProfilerDefinitions::ProfileCreating, member.profiler, Code)
+
// This struct is somewhat dangerous to use:
// The messageType is a bit field. You can pack multiple messages into
// one object, e.g. RangeStart and RangeLocation. Each one will be read
@@ -95,7 +145,7 @@ struct Q_AUTOTEST_EXPORT QQmlProfilerData : public QQmlProfilerDefinitions
Q_DECLARE_TYPEINFO(QQmlProfilerData, Q_MOVABLE_TYPE);
-class QQmlProfiler : public QObject, public QQmlProfilerDefinitions {
+class Q_QML_PRIVATE_EXPORT QQmlProfiler : public QObject, public QQmlProfilerDefinitions {
Q_OBJECT
public:
@@ -146,26 +196,27 @@ public:
// Unfortunately we have to resolve the locations right away because the QML context might not
// be available anymore when we send the data.
struct RefLocation : public Location {
- RefLocation() : Location(), locationType(MaximumRangeType), ref(nullptr)
+ RefLocation() : Location(), locationType(MaximumRangeType), ref(nullptr), sent(false)
{}
RefLocation(QQmlBinding *binding, QV4::FunctionObject *function) :
Location(function->sourceLocation()), locationType(Binding),
- ref(new BindingRefCount(binding), QQmlRefPointer<QQmlRefCount>::Adopt)
+ ref(new BindingRefCount(binding), QQmlRefPointer<QQmlRefCount>::Adopt), sent(false)
{}
- RefLocation(QQmlCompiledData *ref, const QUrl &url, const QV4::CompiledData::Object *obj,
+ RefLocation(QV4::CompiledData::CompilationUnit *ref, const QUrl &url, const QV4::CompiledData::Object *obj,
const QString &type) :
Location(QQmlSourceLocation(type, obj->location.line, obj->location.column), url),
- locationType(Creating), ref(ref)
+ locationType(Creating), ref(ref), sent(false)
{}
RefLocation(QQmlBoundSignalExpression *ref) :
- Location(ref->sourceLocation()), locationType(HandlingSignal), ref(ref)
+ Location(ref->sourceLocation()), locationType(HandlingSignal), ref(ref), sent(false)
{}
RefLocation(QQmlDataBlob *ref) :
- Location(QQmlSourceLocation(), ref->url()), locationType(Compiling), ref(ref)
+ Location(QQmlSourceLocation(), ref->url()), locationType(Compiling), ref(ref),
+ sent(false)
{}
bool isValid() const
@@ -175,6 +226,7 @@ public:
RangeType locationType;
QQmlRefPointer<QQmlRefCount> ref;
+ bool sent;
};
typedef QHash<quintptr, Location> LocationHash;
@@ -217,11 +269,6 @@ public:
location = RefLocation(expression);
}
- void startCreating()
- {
- m_data.append(QQmlProfilerData(m_timer.nsecsElapsed(), 1 << RangeStart, Creating));
- }
-
void startCreating(const QV4::CompiledData::Object *obj)
{
m_data.append(QQmlProfilerData(m_timer.nsecsElapsed(),
@@ -229,14 +276,10 @@ public:
Creating, id(obj)));
}
- void updateCreating(const QV4::CompiledData::Object *obj, QQmlCompiledData *ref,
+ void updateCreating(const QV4::CompiledData::Object *obj, QV4::CompiledData::CompilationUnit *ref,
const QUrl &url, const QString &type)
{
quintptr locationId(id(obj));
- m_data.append(QQmlProfilerData(m_timer.nsecsElapsed(),
- (1 << RangeLocation | 1 << RangeData),
- Creating, locationId));
-
RefLocation &location = m_locations[locationId];
if (!location.isValid())
location = RefLocation(ref, url, obj, type);
@@ -258,10 +301,9 @@ public:
return reinterpret_cast<quintptr>(pointer);
}
-public slots:
void startProfiling(quint64 features);
void stopProfiling();
- void reportData();
+ void reportData(bool trackLocations);
void setTimer(const QElapsedTimer &timer) { m_timer = timer; }
signals:
@@ -357,15 +399,13 @@ private:
QFiniteStack<const QV4::CompiledData::Object *> ranges;
};
-#define Q_QML_OC_PROFILE(member, Code)\
- Q_QML_PROFILE_IF_ENABLED(QQmlProfilerDefinitions::ProfileCreating, member.profiler, Code)
-
class QQmlObjectCreationProfiler {
public:
- QQmlObjectCreationProfiler(QQmlProfiler *profiler) : profiler(profiler)
+ QQmlObjectCreationProfiler(QQmlProfiler *profiler, const QV4::CompiledData::Object *obj)
+ : profiler(profiler)
{
- Q_QML_PROFILE(QQmlProfilerDefinitions::ProfileCreating, profiler, startCreating());
+ Q_QML_PROFILE(QQmlProfilerDefinitions::ProfileCreating, profiler, startCreating(obj));
}
~QQmlObjectCreationProfiler()
@@ -373,7 +413,7 @@ public:
Q_QML_PROFILE(QQmlProfilerDefinitions::ProfileCreating, profiler, endRange<QQmlProfilerDefinitions::Creating>());
}
- void update(QQmlCompiledData *ref, const QV4::CompiledData::Object *obj,
+ void update(QV4::CompiledData::CompilationUnit *ref, const QV4::CompiledData::Object *obj,
const QString &typeName, const QUrl &url)
{
profiler->updateCreating(obj, ref, url, typeName);
@@ -406,4 +446,6 @@ QT_END_NAMESPACE
Q_DECLARE_METATYPE(QVector<QQmlProfilerData>)
Q_DECLARE_METATYPE(QQmlProfiler::LocationHash)
+#endif // QT_NO_QML_DEBUGGER
+
#endif // QQMLPROFILER_P_H
diff --git a/src/qml/debugger/qqmlprofilerdefinitions_p.h b/src/qml/debugger/qqmlprofilerdefinitions_p.h
index 2b2eda22e1..c6ae4593a9 100644
--- a/src/qml/debugger/qqmlprofilerdefinitions_p.h
+++ b/src/qml/debugger/qqmlprofilerdefinitions_p.h
@@ -56,6 +56,8 @@
QT_BEGIN_NAMESPACE
+#ifndef QT_NO_QML_DEBUGGER
+
struct QQmlProfilerDefinitions {
enum Message {
Event,
@@ -161,6 +163,8 @@ struct QQmlProfilerDefinitions {
};
};
+#endif // QT_NO_QML_DEBUGGER
+
QT_END_NAMESPACE
#endif
diff --git a/src/qml/doc/snippets/qml/qtLater.qml b/src/qml/doc/snippets/qml/qtLater.qml
new file mode 100644
index 0000000000..e2bc02edb4
--- /dev/null
+++ b/src/qml/doc/snippets/qml/qtLater.qml
@@ -0,0 +1,109 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation 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$
+**
+****************************************************************************/
+//![0]
+import QtQuick 2.0
+
+Rectangle {
+ width: 480
+ height: 320
+
+ property int callsToUpdateMinimumWidth: 0
+ property bool optimize: true
+
+ property int currentTextModel: 0
+ property var columnTexts: [
+ ["Click on either", "rectangle above", "and note how the counter", "below updates", "significantly faster using the", "regular (non-optimized)", "implementation"],
+ ["The width", "of this column", "is", "no wider than the", "widest item"],
+ ["Note how using Qt.callLater()", "the minimum width is", "calculated a bare-minimum", "number", "of times"]
+ ]
+
+ Text {
+ x: 20; y: 280
+ text: "Times minimum width has been calculated: " + callsToUpdateMinimumWidth
+ }
+
+ Row {
+ y: 25; spacing: 30; anchors.horizontalCenter: parent.horizontalCenter
+ Rectangle {
+ width: 200; height: 50; color: "lightgreen"
+ Text { text: "Optimized behavior\nusing Qt.callLater()"; anchors.centerIn: parent }
+ MouseArea { anchors.fill: parent; onClicked: { optimize = true; currentTextModel++ } }
+ }
+ Rectangle {
+ width: 200; height: 50; color: "lightblue"
+ Text { text: "Regular behavior"; anchors.centerIn: parent}
+ MouseArea { anchors.fill: parent; onClicked: { optimize = false; currentTextModel++ } }
+ }
+ }
+
+ Column {
+ id: column
+ anchors.centerIn: parent
+
+ onChildrenChanged: optimize ? Qt.callLater(updateMinimumWidth) : updateMinimumWidth()
+
+ property int widestChild
+ function updateMinimumWidth() {
+ callsToUpdateMinimumWidth++
+ var w = 0;
+ for (var i in children) {
+ var child = children[i];
+ if (child.implicitWidth > w) {
+ w = child.implicitWidth;
+ }
+ }
+
+ widestChild = w;
+ }
+
+ Repeater {
+ id: repeater
+ model: columnTexts[currentTextModel%3]
+ delegate: Text {
+ color: "white"
+ text: modelData
+ width: column.widestChild
+ horizontalAlignment: Text.Center
+ Rectangle { anchors.fill: parent; z: -1; color: index%2 ? "gray" : "darkgray" }
+ }
+ }
+ }
+}
+//![0]
diff --git a/src/qml/doc/src/cppintegration/data.qdoc b/src/qml/doc/src/cppintegration/data.qdoc
index 42236524a9..ac6600f38c 100644
--- a/src/qml/doc/src/cppintegration/data.qdoc
+++ b/src/qml/doc/src/cppintegration/data.qdoc
@@ -249,6 +249,18 @@ parameter, the value can be created as a JavaScript \c Date object in QML, and
is automatically converted to a QDateTime value when it is passed to C++.
+\section2 QTime to JavaScript Date
+
+The QML engine provides automatic type conversion from QTime values to
+JavaScript \c Date objects. The date component of the resulting Date
+object should not be relied upon, as it is operating system dependent.
+Specifically, the year (and month and day) are set to zero. Conversion
+from a JavaScript \c Date object to QTime is done by converting to a
+QDateTime, and then relying on QVariant to convert it to a QTime. The end
+effect is that the date part of the \c Date object is ignored, but the
+local timezone will be used ignoring any DST complications it may have.
+
+
\section2 Sequence Type to JavaScript Array
Certain C++ sequence types are supported transparently in QML as JavaScript
diff --git a/src/qml/doc/src/javascript/functionlist.qdoc b/src/qml/doc/src/javascript/functionlist.qdoc
index 7f0e844b65..b9a25a2440 100644
--- a/src/qml/doc/src/javascript/functionlist.qdoc
+++ b/src/qml/doc/src/javascript/functionlist.qdoc
@@ -174,6 +174,8 @@
\li charAt(pos)
\li charCodeAt(pos)
\li concat([string1 [, string2 [, ...]]])
+ \li endsWith(searchString [, endPosition ]) // ECMAScript 6: Added in Qt 5.8
+ \li includes(searchString [, position ]) // ECMAScript 6: Added in 5.8
\li indexOf(searchString ,position)
\li lastIndexOf(searchString, position)
\li localeCompare(that)
@@ -182,6 +184,7 @@
\li search(regexp)
\li slice(start, end)
\li split(separator, limit)
+ \li startsWith(searchString [, position ]) // ECMAScript 6: Added in Qt 5.8
\li substring(start, end)
\li toLowerCase()
\li toLocaleLowerCase()
@@ -228,6 +231,26 @@
\li \l {Number::toLocaleString}{toLocaleString(locale, format, precision)}
\endlist
+ \section2 The Number Object
+
+ \section3 Value Properties
+
+ \list
+ \li NaN
+ \li NEGATIVE_INFINITY
+ \li POSITIVE_INFINITY
+ \li MAX_VALUE
+ \li MIN_VALUE
+ \li EPSILON // ECMAScript 6: Added in Qt 5.8
+ \endlist
+
+ \section3 Function Properties
+
+ \list
+ \li isFinite(x) // ECMAScript 6: Added in Qt 5.8
+ \li isNaN(x) // ECMAScript 6: Added in Qt 5.8
+ \endlist
+
\section1 The Math Object
\section2 Value Properties
@@ -261,6 +284,7 @@
\li pow(x, y)
\li random()
\li round(x)
+ \li sign(x) // ECMAScript 6: Added in Qt 5.8
\li sin(x)
\li sqrt(x)
\li tan(x)
diff --git a/src/qml/doc/src/qmlfunctions.qdoc b/src/qml/doc/src/qmlfunctions.qdoc
index 26fc40ff37..8b24d19891 100644
--- a/src/qml/doc/src/qmlfunctions.qdoc
+++ b/src/qml/doc/src/qmlfunctions.qdoc
@@ -180,6 +180,42 @@
*/
/*!
+ \fn static inline int qmlRegisterUncreatableMetaObject(const QMetaObject &staticMetaObject, const char *uri, int versionMajor, int versionMinor, const char *qmlName, const QString& reason)
+ \relates QQmlEngine
+ \since 5.8
+
+ This function registers the \a staticMetaObject and its extension
+ in the QML system with the name \a qmlName in the library imported
+ from \a uri having version number composed from \a versionMajor and
+ \a versionMinor.
+
+ This function is useful to register Q_NAMESPACE namespaces.
+
+ Returns the QML type id.
+
+ Example:
+
+ \code
+ namespace MyNamespace {
+ Q_NAMESPACE
+ enum MyEnum {
+ Key1,
+ Key2,
+ };
+ Q_ENUMS(MyEnum)
+ }
+
+ //...
+ qmlRegisterUncreatableMetaObject(MyNamespace::staticMetaObject, "io.qt", 1, 0, "MyNamespace", "Access to enums & flags only");
+ \endcode
+
+ Now on QML side you can use the registered enums:
+ \code
+ Component.onCompleted: console.log(MyNamespace.Key2)
+ \endcode
+*/
+
+/*!
\fn int qmlRegisterCustomExtendedType(const char *uri, int versionMajor, int versionMinor, const char *qmlName, QQmlCustomParser *parser)
\relates QQmlEngine
\internal
diff --git a/src/qml/doc/src/qtqml.qdoc b/src/qml/doc/src/qtqml.qdoc
index 33bb0c0750..747466281e 100644
--- a/src/qml/doc/src/qtqml.qdoc
+++ b/src/qml/doc/src/qtqml.qdoc
@@ -131,6 +131,19 @@ the QML code to interact with C++ code.
\li \l{The Declarative State Machine Framework}
\endlist
+\section1 Licenses and Attributions
+
+Qt QML is available under commercial licenses from \l{The Qt Company}.
+In addition, it is available under the
+\l{GNU Lesser General Public License, version 3}, or
+the \l{GNU General Public License, version 2}.
+See \l{Qt Licensing} for further details.
+
+Furthermore Qt QML potentially contains third party
+modules under following permissive licenses:
+
+\generatelist{groupsbymodule attributions-qtqml}
+
\section1 Guides and Other Information
Further information for writing QML applications:
diff --git a/src/qml/jit/qv4assembler.cpp b/src/qml/jit/qv4assembler.cpp
index 4d5d090088..e1acc33f82 100644
--- a/src/qml/jit/qv4assembler.cpp
+++ b/src/qml/jit/qv4assembler.cpp
@@ -79,30 +79,87 @@ void CompilationUnit::linkBackendToEngine(ExecutionEngine *engine)
}
}
-QV4::ExecutableAllocator::ChunkOfPages *CompilationUnit::chunkForFunction(int functionIndex)
+void CompilationUnit::prepareCodeOffsetsForDiskStorage(CompiledData::Unit *unit)
{
- if (functionIndex < 0 || functionIndex >= codeRefs.count())
- return 0;
- JSC::ExecutableMemoryHandle *handle = codeRefs[functionIndex].executableMemory();
- if (!handle)
- return 0;
- return handle->chunk();
+ const int codeAlignment = 16;
+ quint64 offset = WTF::roundUpToMultipleOf(codeAlignment, unit->unitSize);
+ Q_ASSERT(int(unit->functionTableSize) == codeRefs.size());
+ for (int i = 0; i < codeRefs.size(); ++i) {
+ CompiledData::Function *compiledFunction = const_cast<CompiledData::Function *>(unit->functionAt(i));
+ compiledFunction->codeOffset = offset;
+ compiledFunction->codeSize = codeRefs.at(i).size();
+ offset = WTF::roundUpToMultipleOf(codeAlignment, offset + compiledFunction->codeSize);
+ }
+}
+
+bool CompilationUnit::saveCodeToDisk(QIODevice *device, const CompiledData::Unit *unit, QString *errorString)
+{
+ Q_ASSERT(device->pos() == unit->unitSize);
+ Q_ASSERT(device->atEnd());
+ Q_ASSERT(int(unit->functionTableSize) == codeRefs.size());
+
+ QByteArray padding;
+
+ for (int i = 0; i < codeRefs.size(); ++i) {
+ const CompiledData::Function *compiledFunction = unit->functionAt(i);
+
+ if (device->pos() > qint64(compiledFunction->codeOffset)) {
+ *errorString = QStringLiteral("Invalid state of cache file to write.");
+ return false;
+ }
+
+ const quint64 paddingSize = compiledFunction->codeOffset - device->pos();
+ padding.fill(0, paddingSize);
+ qint64 written = device->write(padding);
+ if (written != padding.size()) {
+ *errorString = device->errorString();
+ return false;
+ }
+
+ const void *undecoratedCodePtr = codeRefs.at(i).code().dataLocation();
+ written = device->write(reinterpret_cast<const char *>(undecoratedCodePtr), compiledFunction->codeSize);
+ if (written != qint64(compiledFunction->codeSize)) {
+ *errorString = device->errorString();
+ return false;
+ }
+ }
+ return true;
+}
+
+bool CompilationUnit::memoryMapCode(QString *errorString)
+{
+ Q_UNUSED(errorString);
+ codeRefs.resize(data->functionTableSize);
+
+ const char *basePtr = reinterpret_cast<const char *>(data);
+
+ for (uint i = 0; i < data->functionTableSize; ++i) {
+ const CompiledData::Function *compiledFunction = data->functionAt(i);
+ void *codePtr = const_cast<void *>(reinterpret_cast<const void *>(basePtr + compiledFunction->codeOffset));
+ JSC::MacroAssemblerCodeRef codeRef = JSC::MacroAssemblerCodeRef::createSelfManagedCodeRef(JSC::MacroAssemblerCodePtr(codePtr));
+ JSC::ExecutableAllocator::makeExecutable(codePtr, compiledFunction->codeSize);
+ codeRefs[i] = codeRef;
+ }
+
+ return true;
}
const Assembler::VoidType Assembler::Void;
Assembler::Assembler(InstructionSelection *isel, IR::Function* function, QV4::ExecutableAllocator *executableAllocator)
- : _constTable(this)
- , _function(function)
+ : _function(function)
, _nextBlock(0)
, _executableAllocator(executableAllocator)
, _isel(isel)
{
+ _addrs.resize(_function->basicBlockCount());
+ _patches.resize(_function->basicBlockCount());
+ _labelPatches.resize(_function->basicBlockCount());
}
void Assembler::registerBlock(IR::BasicBlock* block, IR::BasicBlock *nextBlock)
{
- _addrs[block] = label();
+ _addrs[block->index()] = label();
catchBlock = block->catchBlock;
_nextBlock = nextBlock;
}
@@ -112,12 +169,12 @@ void Assembler::jumpToBlock(IR::BasicBlock* current, IR::BasicBlock *target)
Q_UNUSED(current);
if (target != _nextBlock)
- _patches[target].append(jump());
+ _patches[target->index()].push_back(jump());
}
void Assembler::addPatch(IR::BasicBlock* targetBlock, Jump targetJump)
{
- _patches[targetBlock].append(targetJump);
+ _patches[targetBlock->index()].push_back(targetJump);
}
void Assembler::addPatch(DataLabelPtr patch, Label target)
@@ -125,12 +182,12 @@ void Assembler::addPatch(DataLabelPtr patch, Label target)
DataLabelPatch p;
p.dataLabel = patch;
p.target = target;
- _dataLabelPatches.append(p);
+ _dataLabelPatches.push_back(p);
}
void Assembler::addPatch(DataLabelPtr patch, IR::BasicBlock *target)
{
- _labelPatches[target].append(patch);
+ _labelPatches[target->index()].push_back(patch);
}
void Assembler::generateCJumpOnNonZero(RegisterID reg, IR::BasicBlock *currentBlock,
@@ -248,6 +305,19 @@ Assembler::Pointer Assembler::loadStringAddress(RegisterID reg, const QString &s
return Pointer(reg, id * sizeof(QV4::String*));
}
+Assembler::Address Assembler::loadConstant(IR::Const *c, RegisterID baseReg)
+{
+ return loadConstant(convertToValue(c), baseReg);
+}
+
+Assembler::Address Assembler::loadConstant(const Primitive &v, RegisterID baseReg)
+{
+ loadPtr(Address(Assembler::EngineRegister, qOffsetOf(QV4::ExecutionEngine, current)), baseReg);
+ loadPtr(Address(baseReg, qOffsetOf(QV4::Heap::ExecutionContext, constantTable)), baseReg);
+ const int index = _isel->jsUnitGenerator()->registerConstant(v.asReturnedValue());
+ return Address(baseReg, index * sizeof(QV4::Value));
+}
+
void Assembler::loadStringRef(RegisterID reg, const QString &string)
{
const int id = _isel->registerString(string);
diff --git a/src/qml/jit/qv4assembler_p.h b/src/qml/jit/qv4assembler_p.h
index 234254513d..94478cd9cd 100644
--- a/src/qml/jit/qv4assembler_p.h
+++ b/src/qml/jit/qv4assembler_p.h
@@ -58,11 +58,11 @@
#include "private/qv4lookup_p.h"
#include "qv4targetplatform_p.h"
-#include <QtCore/QHash>
-#include <QtCore/QStack>
#include <config.h>
#include <wtf/Vector.h>
+#include <climits>
+
#if ENABLE(ASSEMBLER)
#include <assembler/MacroAssembler.h>
@@ -73,33 +73,20 @@ QT_BEGIN_NAMESPACE
namespace QV4 {
namespace JIT {
-#define OP(op) \
- { isel_stringIfy(op), op, 0, 0, 0 }
-#define OPCONTEXT(op) \
- { isel_stringIfy(op), 0, op, 0, 0 }
-
class InstructionSelection;
struct CompilationUnit : public QV4::CompiledData::CompilationUnit
{
virtual ~CompilationUnit();
- virtual void linkBackendToEngine(QV4::ExecutionEngine *engine);
-
- virtual QV4::ExecutableAllocator::ChunkOfPages *chunkForFunction(int functionIndex);
+ void linkBackendToEngine(QV4::ExecutionEngine *engine) Q_DECL_OVERRIDE;
+ void prepareCodeOffsetsForDiskStorage(CompiledData::Unit *unit) Q_DECL_OVERRIDE;
+ bool saveCodeToDisk(QIODevice *device, const CompiledData::Unit *unit, QString *errorString) Q_DECL_OVERRIDE;
+ bool memoryMapCode(QString *errorString) Q_DECL_OVERRIDE;
// Coderef + execution engine
QVector<JSC::MacroAssemblerCodeRef> codeRefs;
- QList<QVector<QV4::Primitive> > constantValues;
-};
-
-struct RelativeCall {
- JSC::MacroAssembler::Address addr;
-
- explicit RelativeCall(const JSC::MacroAssembler::Address &addr)
- : addr(addr)
- {}
};
struct LookupCall {
@@ -112,34 +99,11 @@ struct LookupCall {
{}
};
-template <typename T>
-struct ExceptionCheck {
- enum { NeedsCheck = 1 };
-};
-// push_catch and pop context methods shouldn't check for exceptions
-template <>
-struct ExceptionCheck<void (*)(QV4::ExecutionEngine *)> {
- enum { NeedsCheck = 0 };
-};
-template <typename A>
-struct ExceptionCheck<void (*)(A, QV4::NoThrowEngine)> {
- enum { NeedsCheck = 0 };
-};
-template <>
-struct ExceptionCheck<QV4::ReturnedValue (*)(QV4::NoThrowEngine *)> {
- enum { NeedsCheck = 0 };
-};
-template <typename A>
-struct ExceptionCheck<QV4::ReturnedValue (*)(QV4::NoThrowEngine *, A)> {
- enum { NeedsCheck = 0 };
-};
-template <typename A, typename B>
-struct ExceptionCheck<QV4::ReturnedValue (*)(QV4::NoThrowEngine *, A, B)> {
- enum { NeedsCheck = 0 };
-};
-template <typename A, typename B, typename C>
-struct ExceptionCheck<void (*)(QV4::NoThrowEngine *, A, B, C)> {
- enum { NeedsCheck = 0 };
+struct RuntimeCall {
+ JSC::MacroAssembler::Address addr;
+
+ inline RuntimeCall(uint offset = uint(INT_MIN));
+ bool isValid() const { return addr.offset >= 0; }
};
class Assembler : public JSC::MacroAssembler, public TargetPlatform
@@ -300,33 +264,17 @@ public:
int savedRegCount;
};
- class ConstantTable
- {
- public:
- ConstantTable(Assembler *as): _as(as) {}
-
- int add(const QV4::Primitive &v);
- Address loadValueAddress(IR::Const *c, RegisterID baseReg);
- Address loadValueAddress(const QV4::Primitive &v, RegisterID baseReg);
- void finalize(JSC::LinkBuffer &linkBuffer, InstructionSelection *isel);
-
- private:
- Assembler *_as;
- QVector<QV4::Primitive> _values;
- QVector<DataLabelPtr> _toPatch;
- };
-
struct VoidType { VoidType() {} };
static const VoidType Void;
typedef JSC::FunctionPtr FunctionPtr;
- struct CallToLink {
- Call call;
- FunctionPtr externalFunction;
+#ifndef QT_NO_DEBUG
+ struct CallInfo {
Label label;
const char* functionName;
};
+#endif
struct PointerToValue {
PointerToValue(IR::Expr *value)
: value(value)
@@ -344,32 +292,23 @@ public:
IR::Expr *value;
};
- struct ReentryBlock {
- ReentryBlock(IR::BasicBlock *b) : block(b) {}
- IR::BasicBlock *block;
- };
-
- void callAbsolute(const char* functionName, FunctionPtr function) {
- CallToLink ctl;
- ctl.call = call();
- ctl.externalFunction = function;
- ctl.functionName = functionName;
- ctl.label = label();
- _callsToLink.append(ctl);
- }
-
- void callAbsolute(const char* /*functionName*/, Address addr) {
- call(addr);
- }
-
- void callAbsolute(const char* /*functionName*/, const RelativeCall &relativeCall)
+ void callAbsolute(const char* /*functionName*/, const LookupCall &lookupCall)
{
- call(relativeCall.addr);
+ call(lookupCall.addr);
}
- void callAbsolute(const char* /*functionName*/, const LookupCall &lookupCall)
+ void callAbsolute(const char *functionName, const RuntimeCall &runtimeCall)
{
- call(lookupCall.addr);
+ call(runtimeCall.addr);
+#ifndef QT_NO_DEBUG
+ // the code below is to get proper function names in the disassembly
+ CallInfo info;
+ info.functionName = functionName;
+ info.label = label();
+ _callInfos.append(info);
+#else
+ Q_UNUSED(functionName)
+#endif
}
void registerBlock(IR::BasicBlock*, IR::BasicBlock *nextBlock);
@@ -399,6 +338,8 @@ public:
Pointer loadTempAddress(IR::Temp *t);
Pointer loadArgLocalAddress(RegisterID baseReg, IR::ArgLocal *al);
Pointer loadStringAddress(RegisterID reg, const QString &string);
+ Address loadConstant(IR::Const *c, RegisterID baseReg);
+ Address loadConstant(const Primitive &v, RegisterID baseReg);
void loadStringRef(RegisterID reg, const QString &string);
Pointer stackSlotPointer(IR::Temp *t) const
{
@@ -446,13 +387,6 @@ public:
move(source, dest);
}
- void loadArgumentInRegister(TrustedImmPtr ptr, RegisterID dest, int argumentNumber)
- {
- Q_UNUSED(argumentNumber);
-
- move(TrustedImmPtr(ptr), dest);
- }
-
void loadArgumentInRegister(const Pointer& ptr, RegisterID dest, int argumentNumber)
{
Q_UNUSED(argumentNumber);
@@ -462,7 +396,7 @@ public:
void loadArgumentInRegister(PointerToValue temp, RegisterID dest, int argumentNumber)
{
if (!temp.value) {
- loadArgumentInRegister(TrustedImmPtr(0), dest, argumentNumber);
+ move(TrustedImmPtr(0), dest);
} else {
Pointer addr = toAddress(dest, temp.value, argumentNumber);
loadArgumentInRegister(addr, dest, argumentNumber);
@@ -481,15 +415,6 @@ public:
loadArgumentInRegister(addr, dest, argumentNumber);
}
- void loadArgumentInRegister(ReentryBlock block, RegisterID dest, int argumentNumber)
- {
- Q_UNUSED(argumentNumber);
-
- Q_ASSERT(block.block);
- DataLabelPtr patch = moveWithPatch(TrustedImmPtr(0), dest);
- addPatch(patch, block.block);
- }
-
#ifdef VALUE_FITS_IN_REGISTER
void loadArgumentInRegister(IR::Temp* temp, RegisterID dest, int argumentNumber)
{
@@ -549,11 +474,6 @@ public:
}
#endif
- void loadArgumentInRegister(QV4::String* string, RegisterID dest, int argumentNumber)
- {
- loadArgumentInRegister(TrustedImmPtr(string), dest, argumentNumber);
- }
-
void loadArgumentInRegister(TrustedImm32 imm32, RegisterID dest, int argumentNumber)
{
Q_UNUSED(argumentNumber);
@@ -710,34 +630,6 @@ public:
loadArgumentOnStack<StackSlot>(ptr, argumentNumber);
}
- template <int StackSlot>
- void loadArgumentOnStack(ReentryBlock block, int argumentNumber)
- {
- Q_UNUSED(argumentNumber);
-
- Q_ASSERT(block.block);
- DataLabelPtr patch = moveWithPatch(TrustedImmPtr(0), ScratchRegister);
- poke(ScratchRegister, StackSlot);
- addPatch(patch, block.block);
- }
-
- template <int StackSlot>
- void loadArgumentOnStack(TrustedImmPtr ptr, int argumentNumber)
- {
- Q_UNUSED(argumentNumber);
-
- move(TrustedImmPtr(ptr), ScratchRegister);
- poke(ScratchRegister, StackSlot);
- }
-
- template <int StackSlot>
- void loadArgumentOnStack(QV4::String* name, int argumentNumber)
- {
- Q_UNUSED(argumentNumber);
-
- poke(TrustedImmPtr(name), StackSlot);
- }
-
void loadDouble(IR::Expr *source, FPRegisterID dest)
{
IR::Temp *sourceTemp = source->asTemp();
@@ -890,7 +782,7 @@ public:
};
template <typename ArgRet, typename Callable, typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5, typename Arg6>
- void generateFunctionCallImp(ArgRet r, const char* functionName, Callable function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5, Arg6 arg6)
+ void generateFunctionCallImp(bool needsExceptionCheck, ArgRet r, const char* functionName, Callable function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5, Arg6 arg6)
{
int stackSpaceNeeded = SizeOnStack<0, Arg1>::Size
+ SizeOnStack<1, Arg2>::Size
@@ -933,7 +825,7 @@ public:
if (stackSpaceNeeded)
addPtr(TrustedImm32(stackSpaceNeeded), StackPointerRegister);
- if (ExceptionCheck<Callable>::NeedsCheck) {
+ if (needsExceptionCheck) {
checkException();
}
@@ -942,33 +834,33 @@ public:
}
template <typename ArgRet, typename Callable, typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5>
- void generateFunctionCallImp(ArgRet r, const char* functionName, Callable function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5)
+ void generateFunctionCallImp(bool needsExceptionCheck, ArgRet r, const char* functionName, Callable function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5)
{
- generateFunctionCallImp(r, functionName, function, arg1, arg2, arg3, arg4, arg5, VoidType());
+ generateFunctionCallImp(needsExceptionCheck, r, functionName, function, arg1, arg2, arg3, arg4, arg5, VoidType());
}
template <typename ArgRet, typename Callable, typename Arg1, typename Arg2, typename Arg3, typename Arg4>
- void generateFunctionCallImp(ArgRet r, const char* functionName, Callable function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4)
+ void generateFunctionCallImp(bool needsExceptionCheck, ArgRet r, const char* functionName, Callable function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4)
{
- generateFunctionCallImp(r, functionName, function, arg1, arg2, arg3, arg4, VoidType(), VoidType());
+ generateFunctionCallImp(needsExceptionCheck, r, functionName, function, arg1, arg2, arg3, arg4, VoidType(), VoidType());
}
template <typename ArgRet, typename Callable, typename Arg1, typename Arg2, typename Arg3>
- void generateFunctionCallImp(ArgRet r, const char* functionName, Callable function, Arg1 arg1, Arg2 arg2, Arg3 arg3)
+ void generateFunctionCallImp(bool needsExceptionCheck, ArgRet r, const char* functionName, Callable function, Arg1 arg1, Arg2 arg2, Arg3 arg3)
{
- generateFunctionCallImp(r, functionName, function, arg1, arg2, arg3, VoidType(), VoidType(), VoidType());
+ generateFunctionCallImp(needsExceptionCheck, r, functionName, function, arg1, arg2, arg3, VoidType(), VoidType(), VoidType());
}
template <typename ArgRet, typename Callable, typename Arg1, typename Arg2>
- void generateFunctionCallImp(ArgRet r, const char* functionName, Callable function, Arg1 arg1, Arg2 arg2)
+ void generateFunctionCallImp(bool needsExceptionCheck, ArgRet r, const char* functionName, Callable function, Arg1 arg1, Arg2 arg2)
{
- generateFunctionCallImp(r, functionName, function, arg1, arg2, VoidType(), VoidType(), VoidType(), VoidType());
+ generateFunctionCallImp(needsExceptionCheck, r, functionName, function, arg1, arg2, VoidType(), VoidType(), VoidType(), VoidType());
}
template <typename ArgRet, typename Callable, typename Arg1>
- void generateFunctionCallImp(ArgRet r, const char* functionName, Callable function, Arg1 arg1)
+ void generateFunctionCallImp(bool needsExceptionCheck, ArgRet r, const char* functionName, Callable function, Arg1 arg1)
{
- generateFunctionCallImp(r, functionName, function, arg1, VoidType(), VoidType(), VoidType(), VoidType(), VoidType());
+ generateFunctionCallImp(needsExceptionCheck, r, functionName, function, arg1, VoidType(), VoidType(), VoidType(), VoidType(), VoidType());
}
Pointer toAddress(RegisterID tmpReg, IR::Expr *e, int offset)
@@ -1095,7 +987,7 @@ public:
move(TrustedImm64(i), ReturnValueRegister);
move64ToDouble(ReturnValueRegister, target);
#else
- JSC::MacroAssembler::loadDouble(constantTable().loadValueAddress(c, ScratchRegister), target);
+ JSC::MacroAssembler::loadDouble(loadConstant(c, ScratchRegister), target);
#endif
return target;
}
@@ -1159,9 +1051,8 @@ public:
// it's not in signed int range, so load it as a double, and truncate it down
loadDouble(addr, FPGpr0);
- static const double magic = double(INT_MAX) + 1;
- move(TrustedImmPtr(&magic), scratchReg);
- subDouble(Address(scratchReg, 0), FPGpr0);
+ Address inversionAddress = loadConstant(QV4::Primitive::fromDouble(double(INT_MAX) + 1), scratchReg);
+ subDouble(inversionAddress, FPGpr0);
Jump canNeverHappen = branchTruncateDoubleToUint32(FPGpr0, scratchReg);
canNeverHappen.link(this);
or32(TrustedImm32(1 << 31), scratchReg);
@@ -1178,26 +1069,26 @@ public:
void setStackLayout(int maxArgCountForBuiltins, int regularRegistersToSave, int fpRegistersToSave);
const StackLayout &stackLayout() const { return *_stackLayout.data(); }
- ConstantTable &constantTable() { return _constTable; }
Label exceptionReturnLabel;
IR::BasicBlock * catchBlock;
QVector<Jump> exceptionPropagationJumps;
private:
QScopedPointer<const StackLayout> _stackLayout;
- ConstantTable _constTable;
IR::Function *_function;
- QHash<IR::BasicBlock *, Label> _addrs;
- QHash<IR::BasicBlock *, QVector<Jump> > _patches;
- QList<CallToLink> _callsToLink;
+ std::vector<Label> _addrs;
+ std::vector<std::vector<Jump>> _patches;
+#ifndef QT_NO_DEBUG
+ QVector<CallInfo> _callInfos;
+#endif
struct DataLabelPatch {
DataLabelPtr dataLabel;
Label target;
};
- QList<DataLabelPatch> _dataLabelPatches;
+ std::vector<DataLabelPatch> _dataLabelPatches;
- QHash<IR::BasicBlock *, QVector<DataLabelPtr> > _labelPatches;
+ std::vector<std::vector<DataLabelPtr>> _labelPatches;
IR::BasicBlock *_nextBlock;
QV4::ExecutableAllocator *_executableAllocator;
@@ -1250,24 +1141,21 @@ void Assembler::copyValue(Result result, IR::Expr* source)
}
}
+inline RuntimeCall::RuntimeCall(uint offset)
+ : addr(Assembler::EngineRegister, offset + qOffsetOf(QV4::ExecutionEngine, runtime))
+{
+}
+
template <typename T> inline bool prepareCall(T &, Assembler *)
{ return true; }
-template <> inline bool prepareCall(RelativeCall &relativeCall, Assembler *as)
-{
- as->loadPtr(Assembler::Address(Assembler::EngineRegister, qOffsetOf(QV4::ExecutionEngine, current)), Assembler::ScratchRegister);
- as->loadPtr(Assembler::Address(Assembler::ScratchRegister, qOffsetOf(QV4::Heap::ExecutionContext, lookups)),
- relativeCall.addr.base);
- return true;
-}
-
template <> inline bool prepareCall(LookupCall &lookupCall, Assembler *as)
{
// IMPORTANT! See generateLookupCall in qv4isel_masm_p.h for details!
- // same as prepareCall(RelativeCall ....) : load the table from the context
+ // load the table from the context
as->loadPtr(Assembler::Address(Assembler::EngineRegister, qOffsetOf(QV4::ExecutionEngine, current)), Assembler::ScratchRegister);
as->loadPtr(Assembler::Address(Assembler::ScratchRegister, qOffsetOf(QV4::Heap::ExecutionContext, lookups)),
lookupCall.addr.base);
diff --git a/src/qml/jit/qv4binop.cpp b/src/qml/jit/qv4binop.cpp
index 50b6cec975..9c535bb0bb 100644
--- a/src/qml/jit/qv4binop.cpp
+++ b/src/qml/jit/qv4binop.cpp
@@ -45,17 +45,17 @@ using namespace QV4;
using namespace JIT;
#define OP(op) \
- { isel_stringIfy(op), op, 0, 0, 0 }
+ { "Runtime::" isel_stringIfy(op), offsetof(QV4::Runtime, op), INT_MIN, 0, 0, QV4::Runtime::Method_##op##_NeedsExceptionCheck }
#define OPCONTEXT(op) \
- { isel_stringIfy(op), 0, op, 0, 0 }
+ { "Runtime::" isel_stringIfy(op), INT_MIN, offsetof(QV4::Runtime, op), 0, 0, QV4::Runtime::Method_##op##_NeedsExceptionCheck }
#define INLINE_OP(op, memOp, immOp) \
- { isel_stringIfy(op), op, 0, memOp, immOp }
+ { "Runtime::" isel_stringIfy(op), offsetof(QV4::Runtime, op), INT_MIN, memOp, immOp, QV4::Runtime::Method_##op##_NeedsExceptionCheck }
#define INLINE_OPCONTEXT(op, memOp, immOp) \
- { isel_stringIfy(op), 0, op, memOp, immOp }
+ { "Runtime::" isel_stringIfy(op), INT_MIN, offsetof(QV4::Runtime, op), memOp, immOp, QV4::Runtime::Method_##op##_NeedsExceptionCheck }
#define NULL_OP \
- { 0, 0, 0, 0, 0 }
+ { 0, 0, 0, 0, 0, false }
const Binop::OpInfo Binop::operations[IR::LastAluOp + 1] = {
NULL_OP, // OpInvalid
@@ -67,32 +67,32 @@ const Binop::OpInfo Binop::operations[IR::LastAluOp + 1] = {
NULL_OP, // OpIncrement
NULL_OP, // OpDecrement
- INLINE_OP(Runtime::bitAnd, &Binop::inline_and32, &Binop::inline_and32), // OpBitAnd
- INLINE_OP(Runtime::bitOr, &Binop::inline_or32, &Binop::inline_or32), // OpBitOr
- INLINE_OP(Runtime::bitXor, &Binop::inline_xor32, &Binop::inline_xor32), // OpBitXor
+ INLINE_OP(bitAnd, &Binop::inline_and32, &Binop::inline_and32), // OpBitAnd
+ INLINE_OP(bitOr, &Binop::inline_or32, &Binop::inline_or32), // OpBitOr
+ INLINE_OP(bitXor, &Binop::inline_xor32, &Binop::inline_xor32), // OpBitXor
- INLINE_OPCONTEXT(Runtime::add, &Binop::inline_add32, &Binop::inline_add32), // OpAdd
- INLINE_OP(Runtime::sub, &Binop::inline_sub32, &Binop::inline_sub32), // OpSub
- INLINE_OP(Runtime::mul, &Binop::inline_mul32, &Binop::inline_mul32), // OpMul
+ INLINE_OPCONTEXT(add, &Binop::inline_add32, &Binop::inline_add32), // OpAdd
+ INLINE_OP(sub, &Binop::inline_sub32, &Binop::inline_sub32), // OpSub
+ INLINE_OP(mul, &Binop::inline_mul32, &Binop::inline_mul32), // OpMul
- OP(Runtime::div), // OpDiv
- OP(Runtime::mod), // OpMod
+ OP(div), // OpDiv
+ OP(mod), // OpMod
- INLINE_OP(Runtime::shl, &Binop::inline_shl32, &Binop::inline_shl32), // OpLShift
- INLINE_OP(Runtime::shr, &Binop::inline_shr32, &Binop::inline_shr32), // OpRShift
- INLINE_OP(Runtime::ushr, &Binop::inline_ushr32, &Binop::inline_ushr32), // OpURShift
+ INLINE_OP(shl, &Binop::inline_shl32, &Binop::inline_shl32), // OpLShift
+ INLINE_OP(shr, &Binop::inline_shr32, &Binop::inline_shr32), // OpRShift
+ INLINE_OP(ushr, &Binop::inline_ushr32, &Binop::inline_ushr32), // OpURShift
- OP(Runtime::greaterThan), // OpGt
- OP(Runtime::lessThan), // OpLt
- OP(Runtime::greaterEqual), // OpGe
- OP(Runtime::lessEqual), // OpLe
- OP(Runtime::equal), // OpEqual
- OP(Runtime::notEqual), // OpNotEqual
- OP(Runtime::strictEqual), // OpStrictEqual
- OP(Runtime::strictNotEqual), // OpStrictNotEqual
+ OP(greaterThan), // OpGt
+ OP(lessThan), // OpLt
+ OP(greaterEqual), // OpGe
+ OP(lessEqual), // OpLe
+ OP(equal), // OpEqual
+ OP(notEqual), // OpNotEqual
+ OP(strictEqual), // OpStrictEqual
+ OP(strictNotEqual), // OpStrictNotEqual
- OPCONTEXT(Runtime::instanceof), // OpInstanceof
- OPCONTEXT(Runtime::in), // OpIn
+ OPCONTEXT(instanceof), // OpInstanceof
+ OPCONTEXT(in), // OpIn
NULL_OP, // OpAnd
NULL_OP // OpOr
@@ -121,16 +121,18 @@ void Binop::generate(IR::Expr *lhs, IR::Expr *rhs, IR::Expr *target)
if (op == IR::OpAdd &&
(lhs->type == IR::StringType || rhs->type == IR::StringType)) {
- const Binop::OpInfo stringAdd = OPCONTEXT(Runtime::addString);
+ const Binop::OpInfo stringAdd = OPCONTEXT(addString);
info = stringAdd;
}
- if (info.fallbackImplementation) {
- as->generateFunctionCallImp(target, info.name, info.fallbackImplementation,
+ RuntimeCall fallBack(info.fallbackImplementation);
+ RuntimeCall context(info.contextImplementation);
+ if (fallBack.isValid()) {
+ as->generateFunctionCallImp(info.needsExceptionCheck, target, info.name, fallBack,
Assembler::PointerToValue(lhs),
Assembler::PointerToValue(rhs));
- } else if (info.contextImplementation) {
- as->generateFunctionCallImp(target, info.name, info.contextImplementation,
+ } else if (context.isValid()) {
+ as->generateFunctionCallImp(info.needsExceptionCheck, target, info.name, context,
Assembler::EngineRegister,
Assembler::PointerToValue(lhs),
Assembler::PointerToValue(rhs));
@@ -160,7 +162,7 @@ void Binop::doubleBinop(IR::Expr *lhs, IR::Expr *rhs, IR::Expr *target)
#if CPU(X86)
if (IR::Const *c = rhs->asConst()) { // Y = X + constant -> Y = X; Y += [constant-address]
as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg);
- Assembler::Address addr = as->constantTable().loadValueAddress(c, Assembler::ScratchRegister);
+ Assembler::Address addr = as->loadConstant(c, Assembler::ScratchRegister);
as->addDouble(addr, targetReg);
break;
}
@@ -182,7 +184,7 @@ void Binop::doubleBinop(IR::Expr *lhs, IR::Expr *rhs, IR::Expr *target)
#if CPU(X86)
if (IR::Const *c = rhs->asConst()) { // Y = X * constant -> Y = X; Y *= [constant-address]
as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg);
- Assembler::Address addr = as->constantTable().loadValueAddress(c, Assembler::ScratchRegister);
+ Assembler::Address addr = as->loadConstant(c, Assembler::ScratchRegister);
as->mulDouble(addr, targetReg);
break;
}
@@ -201,7 +203,7 @@ void Binop::doubleBinop(IR::Expr *lhs, IR::Expr *rhs, IR::Expr *target)
#if CPU(X86)
if (IR::Const *c = rhs->asConst()) { // Y = X - constant -> Y = X; Y -= [constant-address]
as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg);
- Assembler::Address addr = as->constantTable().loadValueAddress(c, Assembler::ScratchRegister);
+ Assembler::Address addr = as->loadConstant(c, Assembler::ScratchRegister);
as->subDouble(addr, targetReg);
break;
}
@@ -229,7 +231,7 @@ void Binop::doubleBinop(IR::Expr *lhs, IR::Expr *rhs, IR::Expr *target)
#if CPU(X86)
if (IR::Const *c = rhs->asConst()) { // Y = X / constant -> Y = X; Y /= [constant-address]
as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg);
- Assembler::Address addr = as->constantTable().loadValueAddress(c, Assembler::ScratchRegister);
+ Assembler::Address addr = as->loadConstant(c, Assembler::ScratchRegister);
as->divDouble(addr, targetReg);
break;
}
diff --git a/src/qml/jit/qv4binop_p.h b/src/qml/jit/qv4binop_p.h
index 791e335970..37601f54ba 100644
--- a/src/qml/jit/qv4binop_p.h
+++ b/src/qml/jit/qv4binop_p.h
@@ -77,10 +77,11 @@ struct Binop {
struct OpInfo {
const char *name;
- QV4::Runtime::BinaryOperation fallbackImplementation;
- QV4::Runtime::BinaryOperationContext contextImplementation;
+ int fallbackImplementation; // offsetOf(Runtime,...)
+ int contextImplementation; // offsetOf(Runtime,...)
MemRegOp inlineMemRegOp;
ImmRegOp inlineImmRegOp;
+ bool needsExceptionCheck;
};
static const OpInfo operations[IR::LastAluOp + 1];
diff --git a/src/qml/jit/qv4isel_masm.cpp b/src/qml/jit/qv4isel_masm.cpp
index 859ecfbe64..71069d64a5 100644
--- a/src/qml/jit/qv4isel_masm.cpp
+++ b/src/qml/jit/qv4isel_masm.cpp
@@ -145,13 +145,10 @@ JSC::MacroAssemblerCodeRef Assembler::link(int *codeSize)
Label endOfCode = label();
{
- QHashIterator<IR::BasicBlock *, QVector<Jump> > it(_patches);
- while (it.hasNext()) {
- it.next();
- IR::BasicBlock *block = it.key();
- Label target = _addrs.value(block);
+ for (size_t i = 0, ei = _patches.size(); i != ei; ++i) {
+ Label target = _addrs.at(i);
Q_ASSERT(target.isSet());
- foreach (Jump jump, it.value())
+ for (Jump jump : qAsConst(_patches.at(i)))
jump.linkTo(target, this);
}
}
@@ -159,31 +156,21 @@ JSC::MacroAssemblerCodeRef Assembler::link(int *codeSize)
JSC::JSGlobalData dummy(_executableAllocator);
JSC::LinkBuffer linkBuffer(dummy, this, 0);
- QHash<void*, const char*> functions;
- foreach (CallToLink ctl, _callsToLink) {
- linkBuffer.link(ctl.call, ctl.externalFunction);
- functions[linkBuffer.locationOf(ctl.label).dataLocation()] = ctl.functionName;
- }
-
- foreach (const DataLabelPatch &p, _dataLabelPatches)
+ for (const DataLabelPatch &p : qAsConst(_dataLabelPatches))
linkBuffer.patch(p.dataLabel, linkBuffer.locationOf(p.target));
// link exception handlers
- foreach(Jump jump, exceptionPropagationJumps)
+ for (Jump jump : qAsConst(exceptionPropagationJumps))
linkBuffer.link(jump, linkBuffer.locationOf(exceptionReturnLabel));
{
- QHashIterator<IR::BasicBlock *, QVector<DataLabelPtr> > it(_labelPatches);
- while (it.hasNext()) {
- it.next();
- IR::BasicBlock *block = it.key();
- Label target = _addrs.value(block);
+ for (size_t i = 0, ei = _labelPatches.size(); i != ei; ++i) {
+ Label target = _addrs.at(i);
Q_ASSERT(target.isSet());
- foreach (DataLabelPtr label, it.value())
+ for (DataLabelPtr label : _labelPatches.at(i))
linkBuffer.patch(label, linkBuffer.locationOf(target));
}
}
- _constTable.finalize(linkBuffer, _isel);
*codeSize = linkBuffer.offsetOf(endOfCode);
@@ -193,6 +180,12 @@ JSC::MacroAssemblerCodeRef Assembler::link(int *codeSize)
static const bool showCode = qEnvironmentVariableIsSet("QV4_SHOW_ASM");
if (showCode) {
+ QHash<void*, const char*> functions;
+#ifndef QT_NO_DEBUG
+ for (CallInfo call : qAsConst(_callInfos))
+ functions[linkBuffer.locationOf(call.label).dataLocation()] = call.functionName;
+#endif
+
QBuffer buf;
buf.open(QIODevice::WriteOnly);
WTF::setDataFile(new QIODevicePrintStream(&buf));
@@ -258,14 +251,15 @@ JSC::MacroAssemblerCodeRef Assembler::link(int *codeSize)
return codeRef;
}
-InstructionSelection::InstructionSelection(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, Compiler::JSUnitGenerator *jsGenerator)
- : EvalInstructionSelection(execAllocator, module, jsGenerator)
+InstructionSelection::InstructionSelection(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, Compiler::JSUnitGenerator *jsGenerator, EvalISelFactory *iselFactory)
+ : EvalInstructionSelection(execAllocator, module, jsGenerator, iselFactory)
, _block(0)
, _as(0)
, compilationUnit(new CompilationUnit)
, qmlEngine(qmlEngine)
{
compilationUnit->codeRefs.resize(module->functions.size());
+ module->unitFlags |= QV4::CompiledData::Unit::ContainsMachineCode;
}
InstructionSelection::~InstructionSelection()
@@ -344,7 +338,7 @@ void InstructionSelection::run(int functionIndex)
continue;
_as->registerBlock(_block, nextBlock);
- foreach (IR::Stmt *s, _block->statements()) {
+ for (IR::Stmt *s : _block->statements()) {
if (s->location.isValid()) {
if (int(s->location.startLine) != lastLine) {
_as->loadPtr(Address(Assembler::EngineRegister, qOffsetOf(QV4::ExecutionEngine, current)), Assembler::ScratchRegister);
@@ -353,7 +347,7 @@ void InstructionSelection::run(int functionIndex)
lastLine = s->location.startLine;
}
}
- s->accept(this);
+ visit(s);
}
}
@@ -370,16 +364,6 @@ void InstructionSelection::run(int functionIndex)
qSwap(_removableJumps, removableJumps);
}
-const void *InstructionSelection::addConstantTable(QVector<Primitive> *values)
-{
- compilationUnit->constantValues.append(*values);
- values->clear();
-
- QVector<QV4::Primitive> &finalValues = compilationUnit->constantValues.last();
- finalValues.squeeze();
- return finalValues.constData();
-}
-
QQmlRefPointer<QV4::CompiledData::CompilationUnit> InstructionSelection::backendCompileStep()
{
QQmlRefPointer<QV4::CompiledData::CompilationUnit> result;
@@ -393,12 +377,12 @@ void InstructionSelection::callBuiltinInvalid(IR::Name *func, IR::ExprList *args
if (useFastLookups && func->global) {
uint index = registerGlobalGetterLookup(*func->id);
- generateFunctionCall(result, Runtime::callGlobalLookup,
+ generateRuntimeCall(result, callGlobalLookup,
Assembler::EngineRegister,
Assembler::TrustedImm32(index),
baseAddressForCallData());
} else {
- generateFunctionCall(result, Runtime::callActivationProperty,
+ generateRuntimeCall(result, callActivationProperty,
Assembler::EngineRegister,
Assembler::StringToIndex(*func->id),
baseAddressForCallData());
@@ -410,11 +394,11 @@ void InstructionSelection::callBuiltinTypeofQmlContextProperty(IR::Expr *base,
int propertyIndex, IR::Expr *result)
{
if (kind == IR::Member::MemberOfQmlScopeObject) {
- generateFunctionCall(result, Runtime::typeofScopeObjectProperty, Assembler::EngineRegister,
+ generateRuntimeCall(result, typeofScopeObjectProperty, Assembler::EngineRegister,
Assembler::PointerToValue(base),
Assembler::TrustedImm32(propertyIndex));
} else if (kind == IR::Member::MemberOfQmlContextObject) {
- generateFunctionCall(result, Runtime::typeofContextObjectProperty,
+ generateRuntimeCall(result, typeofContextObjectProperty,
Assembler::EngineRegister, Assembler::PointerToValue(base),
Assembler::TrustedImm32(propertyIndex));
} else {
@@ -425,46 +409,46 @@ void InstructionSelection::callBuiltinTypeofQmlContextProperty(IR::Expr *base,
void InstructionSelection::callBuiltinTypeofMember(IR::Expr *base, const QString &name,
IR::Expr *result)
{
- generateFunctionCall(result, Runtime::typeofMember, Assembler::EngineRegister,
+ generateRuntimeCall(result, typeofMember, Assembler::EngineRegister,
Assembler::PointerToValue(base), Assembler::StringToIndex(name));
}
void InstructionSelection::callBuiltinTypeofSubscript(IR::Expr *base, IR::Expr *index,
IR::Expr *result)
{
- generateFunctionCall(result, Runtime::typeofElement,
+ generateRuntimeCall(result, typeofElement,
Assembler::EngineRegister,
Assembler::PointerToValue(base), Assembler::PointerToValue(index));
}
void InstructionSelection::callBuiltinTypeofName(const QString &name, IR::Expr *result)
{
- generateFunctionCall(result, Runtime::typeofName, Assembler::EngineRegister,
+ generateRuntimeCall(result, typeofName, Assembler::EngineRegister,
Assembler::StringToIndex(name));
}
void InstructionSelection::callBuiltinTypeofValue(IR::Expr *value, IR::Expr *result)
{
- generateFunctionCall(result, Runtime::typeofValue, Assembler::EngineRegister,
+ generateRuntimeCall(result, typeofValue, Assembler::EngineRegister,
Assembler::PointerToValue(value));
}
void InstructionSelection::callBuiltinDeleteMember(IR::Expr *base, const QString &name, IR::Expr *result)
{
- generateFunctionCall(result, Runtime::deleteMember, Assembler::EngineRegister,
+ generateRuntimeCall(result, deleteMember, Assembler::EngineRegister,
Assembler::Reference(base), Assembler::StringToIndex(name));
}
void InstructionSelection::callBuiltinDeleteSubscript(IR::Expr *base, IR::Expr *index,
IR::Expr *result)
{
- generateFunctionCall(result, Runtime::deleteElement, Assembler::EngineRegister,
+ generateRuntimeCall(result, deleteElement, Assembler::EngineRegister,
Assembler::Reference(base), Assembler::PointerToValue(index));
}
void InstructionSelection::callBuiltinDeleteName(const QString &name, IR::Expr *result)
{
- generateFunctionCall(result, Runtime::deleteName, Assembler::EngineRegister,
+ generateRuntimeCall(result, deleteName, Assembler::EngineRegister,
Assembler::StringToIndex(name));
}
@@ -475,7 +459,7 @@ void InstructionSelection::callBuiltinDeleteValue(IR::Expr *result)
void InstructionSelection::callBuiltinThrow(IR::Expr *arg)
{
- generateFunctionCall(Assembler::ReturnValueRegister, Runtime::throwException, Assembler::EngineRegister,
+ generateRuntimeCall(Assembler::ReturnValueRegister, throwException, Assembler::EngineRegister,
Assembler::PointerToValue(arg));
}
@@ -486,13 +470,13 @@ void InstructionSelection::callBuiltinReThrow()
void InstructionSelection::callBuiltinUnwindException(IR::Expr *result)
{
- generateFunctionCall(result, Runtime::unwindException, Assembler::EngineRegister);
+ generateRuntimeCall(result, unwindException, Assembler::EngineRegister);
}
void InstructionSelection::callBuiltinPushCatchScope(const QString &exceptionName)
{
- generateFunctionCall(Assembler::Void, Runtime::pushCatchScope, Assembler::EngineRegister, Assembler::StringToIndex(exceptionName));
+ generateRuntimeCall(Assembler::Void, pushCatchScope, Assembler::EngineRegister, Assembler::StringToIndex(exceptionName));
}
void InstructionSelection::callBuiltinForeachIteratorObject(IR::Expr *arg, IR::Expr *result)
@@ -500,7 +484,7 @@ void InstructionSelection::callBuiltinForeachIteratorObject(IR::Expr *arg, IR::E
Q_ASSERT(arg);
Q_ASSERT(result);
- generateFunctionCall(result, Runtime::foreachIterator, Assembler::EngineRegister, Assembler::PointerToValue(arg));
+ generateRuntimeCall(result, foreachIterator, Assembler::EngineRegister, Assembler::PointerToValue(arg));
}
void InstructionSelection::callBuiltinForeachNextPropertyname(IR::Expr *arg, IR::Expr *result)
@@ -508,24 +492,24 @@ void InstructionSelection::callBuiltinForeachNextPropertyname(IR::Expr *arg, IR:
Q_ASSERT(arg);
Q_ASSERT(result);
- generateFunctionCall(result, Runtime::foreachNextPropertyName, Assembler::Reference(arg));
+ generateRuntimeCall(result, foreachNextPropertyName, Assembler::Reference(arg));
}
void InstructionSelection::callBuiltinPushWithScope(IR::Expr *arg)
{
Q_ASSERT(arg);
- generateFunctionCall(Assembler::Void, Runtime::pushWithScope, Assembler::Reference(arg), Assembler::EngineRegister);
+ generateRuntimeCall(Assembler::Void, pushWithScope, Assembler::Reference(arg), Assembler::EngineRegister);
}
void InstructionSelection::callBuiltinPopScope()
{
- generateFunctionCall(Assembler::Void, Runtime::popScope, Assembler::EngineRegister);
+ generateRuntimeCall(Assembler::Void, popScope, Assembler::EngineRegister);
}
void InstructionSelection::callBuiltinDeclareVar(bool deletable, const QString &name)
{
- generateFunctionCall(Assembler::Void, Runtime::declareVar, Assembler::EngineRegister,
+ generateRuntimeCall(Assembler::Void, declareVar, Assembler::EngineRegister,
Assembler::TrustedImm32(deletable), Assembler::StringToIndex(name));
}
@@ -534,7 +518,7 @@ void InstructionSelection::callBuiltinDefineArray(IR::Expr *result, IR::ExprList
Q_ASSERT(result);
int length = prepareVariableArguments(args);
- generateFunctionCall(result, Runtime::arrayLiteral, Assembler::EngineRegister,
+ generateRuntimeCall(result, arrayLiteral, Assembler::EngineRegister,
baseAddressForCallArguments(), Assembler::TrustedImm32(length));
}
@@ -614,19 +598,19 @@ void InstructionSelection::callBuiltinDefineObjectLiteral(IR::Expr *result, int
it = it->next;
}
- generateFunctionCall(result, Runtime::objectLiteral, Assembler::EngineRegister,
+ generateRuntimeCall(result, objectLiteral, Assembler::EngineRegister,
baseAddressForCallArguments(), Assembler::TrustedImm32(classId),
Assembler::TrustedImm32(arrayValueCount), Assembler::TrustedImm32(arrayGetterSetterCount | (needSparseArray << 30)));
}
void InstructionSelection::callBuiltinSetupArgumentObject(IR::Expr *result)
{
- generateFunctionCall(result, Runtime::setupArgumentsObject, Assembler::EngineRegister);
+ generateRuntimeCall(result, setupArgumentsObject, Assembler::EngineRegister);
}
void InstructionSelection::callBuiltinConvertThisToObject()
{
- generateFunctionCall(Assembler::Void, Runtime::convertThisToObject, Assembler::EngineRegister);
+ generateRuntimeCall(Assembler::Void, convertThisToObject, Assembler::EngineRegister);
}
void InstructionSelection::callValue(IR::Expr *value, IR::ExprList *args, IR::Expr *result)
@@ -635,11 +619,11 @@ void InstructionSelection::callValue(IR::Expr *value, IR::ExprList *args, IR::Ex
prepareCallData(args, 0);
if (value->asConst())
- generateFunctionCall(result, Runtime::callValue, Assembler::EngineRegister,
+ generateRuntimeCall(result, callValue, Assembler::EngineRegister,
Assembler::PointerToValue(value),
baseAddressForCallData());
else
- generateFunctionCall(result, Runtime::callValue, Assembler::EngineRegister,
+ generateRuntimeCall(result, callValue, Assembler::EngineRegister,
Assembler::Reference(value),
baseAddressForCallData());
}
@@ -659,17 +643,17 @@ void InstructionSelection::loadThisObject(IR::Expr *temp)
void InstructionSelection::loadQmlContext(IR::Expr *temp)
{
- generateFunctionCall(temp, Runtime::getQmlContext, Assembler::EngineRegister);
+ generateRuntimeCall(temp, getQmlContext, Assembler::EngineRegister);
}
void InstructionSelection::loadQmlImportedScripts(IR::Expr *temp)
{
- generateFunctionCall(temp, Runtime::getQmlImportedScripts, Assembler::EngineRegister);
+ generateRuntimeCall(temp, getQmlImportedScripts, Assembler::EngineRegister);
}
void InstructionSelection::loadQmlSingleton(const QString &name, IR::Expr *temp)
{
- generateFunctionCall(temp, Runtime::getQmlSingleton, Assembler::EngineRegister, Assembler::StringToIndex(name));
+ generateRuntimeCall(temp, getQmlSingleton, Assembler::EngineRegister, Assembler::StringToIndex(name));
}
void InstructionSelection::loadConst(IR::Const *sourceConst, IR::Expr *target)
@@ -716,7 +700,7 @@ void InstructionSelection::loadString(const QString &str, IR::Expr *target)
void InstructionSelection::loadRegexp(IR::RegExp *sourceRegexp, IR::Expr *target)
{
int id = registerRegExp(sourceRegexp);
- generateFunctionCall(target, Runtime::regexpLiteral, Assembler::EngineRegister, Assembler::TrustedImm32(id));
+ generateRuntimeCall(target, regexpLiteral, Assembler::EngineRegister, Assembler::TrustedImm32(id));
}
void InstructionSelection::getActivationProperty(const IR::Name *name, IR::Expr *target)
@@ -726,20 +710,20 @@ void InstructionSelection::getActivationProperty(const IR::Name *name, IR::Expr
generateLookupCall(target, index, qOffsetOf(QV4::Lookup, globalGetter), Assembler::EngineRegister, Assembler::Void);
return;
}
- generateFunctionCall(target, Runtime::getActivationProperty, Assembler::EngineRegister, Assembler::StringToIndex(*name->id));
+ generateRuntimeCall(target, getActivationProperty, Assembler::EngineRegister, Assembler::StringToIndex(*name->id));
}
void InstructionSelection::setActivationProperty(IR::Expr *source, const QString &targetName)
{
// ### should use a lookup call here
- generateFunctionCall(Assembler::Void, Runtime::setActivationProperty,
+ generateRuntimeCall(Assembler::Void, setActivationProperty,
Assembler::EngineRegister, Assembler::StringToIndex(targetName), Assembler::PointerToValue(source));
}
void InstructionSelection::initClosure(IR::Closure *closure, IR::Expr *target)
{
int id = closure->value;
- generateFunctionCall(target, Runtime::closure, Assembler::EngineRegister, Assembler::TrustedImm32(id));
+ generateRuntimeCall(target, closure, Assembler::EngineRegister, Assembler::TrustedImm32(id));
}
void InstructionSelection::getProperty(IR::Expr *base, const QString &name, IR::Expr *target)
@@ -748,19 +732,19 @@ void InstructionSelection::getProperty(IR::Expr *base, const QString &name, IR::
uint index = registerGetterLookup(name);
generateLookupCall(target, index, qOffsetOf(QV4::Lookup, getter), Assembler::EngineRegister, Assembler::PointerToValue(base), Assembler::Void);
} else {
- generateFunctionCall(target, Runtime::getProperty, Assembler::EngineRegister,
+ generateRuntimeCall(target, getProperty, Assembler::EngineRegister,
Assembler::PointerToValue(base), Assembler::StringToIndex(name));
}
}
-void InstructionSelection::getQmlContextProperty(IR::Expr *base, IR::Member::MemberKind kind, int index, IR::Expr *target)
+void InstructionSelection::getQmlContextProperty(IR::Expr *base, IR::Member::MemberKind kind, int index, bool captureRequired, IR::Expr *target)
{
if (kind == IR::Member::MemberOfQmlScopeObject)
- generateFunctionCall(target, Runtime::getQmlScopeObjectProperty, Assembler::EngineRegister, Assembler::PointerToValue(base), Assembler::TrustedImm32(index));
+ generateRuntimeCall(target, getQmlScopeObjectProperty, Assembler::EngineRegister, Assembler::PointerToValue(base), Assembler::TrustedImm32(index), Assembler::TrustedImm32(captureRequired));
else if (kind == IR::Member::MemberOfQmlContextObject)
- generateFunctionCall(target, Runtime::getQmlContextObjectProperty, Assembler::EngineRegister, Assembler::PointerToValue(base), Assembler::TrustedImm32(index));
+ generateRuntimeCall(target, getQmlContextObjectProperty, Assembler::EngineRegister, Assembler::PointerToValue(base), Assembler::TrustedImm32(index), Assembler::TrustedImm32(captureRequired));
else if (kind == IR::Member::MemberOfIdObjectsArray)
- generateFunctionCall(target, Runtime::getQmlIdObject, Assembler::EngineRegister, Assembler::PointerToValue(base), Assembler::TrustedImm32(index));
+ generateRuntimeCall(target, getQmlIdObject, Assembler::EngineRegister, Assembler::PointerToValue(base), Assembler::TrustedImm32(index));
else
Q_ASSERT(false);
}
@@ -768,12 +752,12 @@ void InstructionSelection::getQmlContextProperty(IR::Expr *base, IR::Member::Mem
void InstructionSelection::getQObjectProperty(IR::Expr *base, int propertyIndex, bool captureRequired, bool isSingleton, int attachedPropertiesId, IR::Expr *target)
{
if (attachedPropertiesId != 0)
- generateFunctionCall(target, Runtime::getQmlAttachedProperty, Assembler::EngineRegister, Assembler::TrustedImm32(attachedPropertiesId), Assembler::TrustedImm32(propertyIndex));
+ generateRuntimeCall(target, getQmlAttachedProperty, Assembler::EngineRegister, Assembler::TrustedImm32(attachedPropertiesId), Assembler::TrustedImm32(propertyIndex));
else if (isSingleton)
- generateFunctionCall(target, Runtime::getQmlSingletonQObjectProperty, Assembler::EngineRegister, Assembler::PointerToValue(base), Assembler::TrustedImm32(propertyIndex),
+ generateRuntimeCall(target, getQmlSingletonQObjectProperty, Assembler::EngineRegister, Assembler::PointerToValue(base), Assembler::TrustedImm32(propertyIndex),
Assembler::TrustedImm32(captureRequired));
else
- generateFunctionCall(target, Runtime::getQmlQObjectProperty, Assembler::EngineRegister, Assembler::PointerToValue(base), Assembler::TrustedImm32(propertyIndex),
+ generateRuntimeCall(target, getQmlQObjectProperty, Assembler::EngineRegister, Assembler::PointerToValue(base), Assembler::TrustedImm32(propertyIndex),
Assembler::TrustedImm32(captureRequired));
}
@@ -787,7 +771,7 @@ void InstructionSelection::setProperty(IR::Expr *source, IR::Expr *targetBase,
Assembler::PointerToValue(targetBase),
Assembler::PointerToValue(source));
} else {
- generateFunctionCall(Assembler::Void, Runtime::setProperty, Assembler::EngineRegister,
+ generateRuntimeCall(Assembler::Void, setProperty, Assembler::EngineRegister,
Assembler::PointerToValue(targetBase), Assembler::StringToIndex(targetName),
Assembler::PointerToValue(source));
}
@@ -796,10 +780,10 @@ void InstructionSelection::setProperty(IR::Expr *source, IR::Expr *targetBase,
void InstructionSelection::setQmlContextProperty(IR::Expr *source, IR::Expr *targetBase, IR::Member::MemberKind kind, int propertyIndex)
{
if (kind == IR::Member::MemberOfQmlScopeObject)
- generateFunctionCall(Assembler::Void, Runtime::setQmlScopeObjectProperty, Assembler::EngineRegister, Assembler::PointerToValue(targetBase),
+ generateRuntimeCall(Assembler::Void, setQmlScopeObjectProperty, Assembler::EngineRegister, Assembler::PointerToValue(targetBase),
Assembler::TrustedImm32(propertyIndex), Assembler::PointerToValue(source));
else if (kind == IR::Member::MemberOfQmlContextObject)
- generateFunctionCall(Assembler::Void, Runtime::setQmlContextObjectProperty, Assembler::EngineRegister, Assembler::PointerToValue(targetBase),
+ generateRuntimeCall(Assembler::Void, setQmlContextObjectProperty, Assembler::EngineRegister, Assembler::PointerToValue(targetBase),
Assembler::TrustedImm32(propertyIndex), Assembler::PointerToValue(source));
else
Q_ASSERT(false);
@@ -807,7 +791,7 @@ void InstructionSelection::setQmlContextProperty(IR::Expr *source, IR::Expr *tar
void InstructionSelection::setQObjectProperty(IR::Expr *source, IR::Expr *targetBase, int propertyIndex)
{
- generateFunctionCall(Assembler::Void, Runtime::setQmlQObjectProperty, Assembler::EngineRegister, Assembler::PointerToValue(targetBase),
+ generateRuntimeCall(Assembler::Void, setQmlQObjectProperty, Assembler::EngineRegister, Assembler::PointerToValue(targetBase),
Assembler::TrustedImm32(propertyIndex), Assembler::PointerToValue(source));
}
@@ -821,7 +805,7 @@ void InstructionSelection::getElement(IR::Expr *base, IR::Expr *index, IR::Expr
return;
}
- generateFunctionCall(target, Runtime::getElement, Assembler::EngineRegister,
+ generateRuntimeCall(target, getElement, Assembler::EngineRegister,
Assembler::PointerToValue(base), Assembler::PointerToValue(index));
}
@@ -834,7 +818,7 @@ void InstructionSelection::setElement(IR::Expr *source, IR::Expr *targetBase, IR
Assembler::PointerToValue(source));
return;
}
- generateFunctionCall(Assembler::Void, Runtime::setElement, Assembler::EngineRegister,
+ generateRuntimeCall(Assembler::Void, setElement, Assembler::EngineRegister,
Assembler::PointerToValue(targetBase), Assembler::PointerToValue(targetIndex),
Assembler::PointerToValue(source));
}
@@ -981,9 +965,15 @@ void InstructionSelection::swapValues(IR::Expr *source, IR::Expr *target)
}
#define setOp(op, opName, operation) \
- do { op = operation; opName = isel_stringIfy(operation); } while (0)
+ do { \
+ op = RuntimeCall(qOffsetOf(QV4::Runtime, operation)); opName = "Runtime::" isel_stringIfy(operation); \
+ needsExceptionCheck = QV4::Runtime::Method_##operation##_NeedsExceptionCheck; \
+ } while (0)
#define setOpContext(op, opName, operation) \
- do { opContext = operation; opName = isel_stringIfy(operation); } while (0)
+ do { \
+ opContext = RuntimeCall(qOffsetOf(QV4::Runtime, operation)); opName = "Runtime::" isel_stringIfy(operation); \
+ needsExceptionCheck = QV4::Runtime::Method_##operation##_NeedsExceptionCheck; \
+ } while (0)
void InstructionSelection::unop(IR::AluOp oper, IR::Expr *source, IR::Expr *target)
{
@@ -1003,12 +993,12 @@ void InstructionSelection::callQmlContextProperty(IR::Expr *base, IR::Member::Me
prepareCallData(args, base);
if (kind == IR::Member::MemberOfQmlScopeObject)
- generateFunctionCall(result, Runtime::callQmlScopeObjectProperty,
+ generateRuntimeCall(result, callQmlScopeObjectProperty,
Assembler::EngineRegister,
Assembler::TrustedImm32(propertyIndex),
baseAddressForCallData());
else if (kind == IR::Member::MemberOfQmlContextObject)
- generateFunctionCall(result, Runtime::callQmlContextObjectProperty,
+ generateRuntimeCall(result, callQmlContextObjectProperty,
Assembler::EngineRegister,
Assembler::TrustedImm32(propertyIndex),
baseAddressForCallData());
@@ -1025,12 +1015,12 @@ void InstructionSelection::callProperty(IR::Expr *base, const QString &name, IR:
if (useFastLookups) {
uint index = registerGetterLookup(name);
- generateFunctionCall(result, Runtime::callPropertyLookup,
+ generateRuntimeCall(result, callPropertyLookup,
Assembler::EngineRegister,
Assembler::TrustedImm32(index),
baseAddressForCallData());
} else {
- generateFunctionCall(result, Runtime::callProperty, Assembler::EngineRegister,
+ generateRuntimeCall(result, callProperty, Assembler::EngineRegister,
Assembler::StringToIndex(name),
baseAddressForCallData());
}
@@ -1042,7 +1032,7 @@ void InstructionSelection::callSubscript(IR::Expr *base, IR::Expr *index, IR::Ex
Q_ASSERT(base != 0);
prepareCallData(args, base);
- generateFunctionCall(result, Runtime::callElement, Assembler::EngineRegister,
+ generateRuntimeCall(result, callElement, Assembler::EngineRegister,
Assembler::PointerToValue(index),
baseAddressForCallData());
}
@@ -1118,7 +1108,7 @@ void InstructionSelection::convertTypeToDouble(IR::Expr *source, IR::Expr *targe
Assembler::TrustedImm32(Value::NotDouble_Mask));
#endif
- generateFunctionCall(target, Runtime::toDouble, Assembler::PointerToValue(source));
+ generateRuntimeCall(target, toDouble, Assembler::PointerToValue(source));
Assembler::Jump noDoubleDone = _as->jump();
// it is a double:
@@ -1183,7 +1173,7 @@ void InstructionSelection::convertTypeToBool(IR::Expr *source, IR::Expr *target)
case IR::StringType:
case IR::VarType:
default:
- generateFunctionCall(Assembler::ReturnValueRegister, Runtime::toBoolean,
+ generateRuntimeCall(Assembler::ReturnValueRegister, toBoolean,
Assembler::PointerToValue(source));
_as->storeBool(Assembler::ReturnValueRegister, target);
break;
@@ -1220,7 +1210,7 @@ void InstructionSelection::convertTypeToSInt32(IR::Expr *source, IR::Expr *targe
// not an int:
fallback.link(_as);
- generateFunctionCall(Assembler::ReturnValueRegister, Runtime::toInt,
+ generateRuntimeCall(Assembler::ReturnValueRegister, toInt,
_as->loadAddress(Assembler::ScratchRegister, source));
isIntConvertible.link(_as);
@@ -1258,7 +1248,7 @@ void InstructionSelection::convertTypeToSInt32(IR::Expr *source, IR::Expr *targe
// not an int:
fallback.link(_as);
- generateFunctionCall(Assembler::ReturnValueRegister, Runtime::toInt,
+ generateRuntimeCall(Assembler::ReturnValueRegister, toInt,
_as->loadAddress(Assembler::ScratchRegister, source));
_as->storeInt32(Assembler::ReturnValueRegister, target);
@@ -1271,7 +1261,7 @@ void InstructionSelection::convertTypeToSInt32(IR::Expr *source, IR::Expr *targe
_as->branchTruncateDoubleToInt32(_as->toDoubleRegister(source),
Assembler::ReturnValueRegister,
Assembler::BranchIfTruncateSuccessful);
- generateFunctionCall(Assembler::ReturnValueRegister, Runtime::doubleToInt,
+ generateRuntimeCall(Assembler::ReturnValueRegister, doubleToInt,
Assembler::PointerToValue(source));
success.link(_as);
_as->storeInt32(Assembler::ReturnValueRegister, target);
@@ -1289,7 +1279,7 @@ void InstructionSelection::convertTypeToSInt32(IR::Expr *source, IR::Expr *targe
break;
case IR::StringType:
default:
- generateFunctionCall(Assembler::ReturnValueRegister, Runtime::toInt,
+ generateRuntimeCall(Assembler::ReturnValueRegister, toInt,
_as->loadAddress(Assembler::ScratchRegister, source));
_as->storeInt32(Assembler::ReturnValueRegister, target);
break;
@@ -1314,7 +1304,7 @@ void InstructionSelection::convertTypeToUInt32(IR::Expr *source, IR::Expr *targe
// not an int:
isNoInt.link(_as);
- generateFunctionCall(Assembler::ReturnValueRegister, Runtime::toUInt,
+ generateRuntimeCall(Assembler::ReturnValueRegister, toUInt,
_as->loadAddress(Assembler::ScratchRegister, source));
_as->storeInt32(Assembler::ReturnValueRegister, target);
@@ -1325,7 +1315,7 @@ void InstructionSelection::convertTypeToUInt32(IR::Expr *source, IR::Expr *targe
Assembler::Jump success =
_as->branchTruncateDoubleToUint32(reg, Assembler::ReturnValueRegister,
Assembler::BranchIfTruncateSuccessful);
- generateFunctionCall(Assembler::ReturnValueRegister, Runtime::doubleToUInt,
+ generateRuntimeCall(Assembler::ReturnValueRegister, doubleToUInt,
Assembler::PointerToValue(source));
success.link(_as);
_as->storeUInt32(Assembler::ReturnValueRegister, target);
@@ -1336,7 +1326,7 @@ void InstructionSelection::convertTypeToUInt32(IR::Expr *source, IR::Expr *targe
_as->storeUInt32(Assembler::ReturnValueRegister, target);
break;
case IR::StringType:
- generateFunctionCall(Assembler::ReturnValueRegister, Runtime::toUInt,
+ generateRuntimeCall(Assembler::ReturnValueRegister, toUInt,
Assembler::PointerToValue(source));
_as->storeUInt32(Assembler::ReturnValueRegister, target);
break;
@@ -1356,13 +1346,13 @@ void InstructionSelection::constructActivationProperty(IR::Name *func, IR::ExprL
if (useFastLookups && func->global) {
uint index = registerGlobalGetterLookup(*func->id);
- generateFunctionCall(result, Runtime::constructGlobalLookup,
+ generateRuntimeCall(result, constructGlobalLookup,
Assembler::EngineRegister,
Assembler::TrustedImm32(index), baseAddressForCallData());
return;
}
- generateFunctionCall(result, Runtime::constructActivationProperty,
+ generateRuntimeCall(result, constructActivationProperty,
Assembler::EngineRegister,
Assembler::StringToIndex(*func->id),
baseAddressForCallData());
@@ -1374,14 +1364,14 @@ void InstructionSelection::constructProperty(IR::Expr *base, const QString &name
prepareCallData(args, base);
if (useFastLookups) {
uint index = registerGetterLookup(name);
- generateFunctionCall(result, Runtime::constructPropertyLookup,
+ generateRuntimeCall(result, constructPropertyLookup,
Assembler::EngineRegister,
Assembler::TrustedImm32(index),
baseAddressForCallData());
return;
}
- generateFunctionCall(result, Runtime::constructProperty, Assembler::EngineRegister,
+ generateRuntimeCall(result, constructProperty, Assembler::EngineRegister,
Assembler::StringToIndex(name),
baseAddressForCallData());
}
@@ -1391,7 +1381,7 @@ void InstructionSelection::constructValue(IR::Expr *value, IR::ExprList *args, I
Q_ASSERT(value != 0);
prepareCallData(args, 0);
- generateFunctionCall(result, Runtime::constructValue,
+ generateRuntimeCall(result, constructValue,
Assembler::EngineRegister,
Assembler::Reference(value),
baseAddressForCallData());
@@ -1427,7 +1417,7 @@ void InstructionSelection::visitCJump(IR::CJump *s)
booleanConversion.link(_as);
reg = Assembler::ReturnValueRegister;
- generateFunctionCall(reg, Runtime::toBoolean, Assembler::Reference(s->cond));
+ generateRuntimeCall(reg, toBoolean, Assembler::Reference(s->cond));
testBoolean.link(_as);
}
@@ -1437,7 +1427,7 @@ void InstructionSelection::visitCJump(IR::CJump *s)
} else if (IR::Const *c = s->cond->asConst()) {
// TODO: SSA optimization for constant condition evaluation should remove this.
// See also visitCJump() in RegAllocInfo.
- generateFunctionCall(Assembler::ReturnValueRegister, Runtime::toBoolean,
+ generateRuntimeCall(Assembler::ReturnValueRegister, toBoolean,
Assembler::PointerToValue(c));
_as->generateCJumpOnNonZero(Assembler::ReturnValueRegister, _block, s->iftrue, s->iffalse);
return;
@@ -1459,21 +1449,22 @@ void InstructionSelection::visitCJump(IR::CJump *s)
return;
}
- Runtime::CompareOperation op = 0;
- Runtime::CompareOperationContext opContext = 0;
+ RuntimeCall op;
+ RuntimeCall opContext;
const char *opName = 0;
+ bool needsExceptionCheck;
switch (b->op) {
default: Q_UNREACHABLE(); Q_ASSERT(!"todo"); break;
- case IR::OpGt: setOp(op, opName, Runtime::compareGreaterThan); break;
- case IR::OpLt: setOp(op, opName, Runtime::compareLessThan); break;
- case IR::OpGe: setOp(op, opName, Runtime::compareGreaterEqual); break;
- case IR::OpLe: setOp(op, opName, Runtime::compareLessEqual); break;
- case IR::OpEqual: setOp(op, opName, Runtime::compareEqual); break;
- case IR::OpNotEqual: setOp(op, opName, Runtime::compareNotEqual); break;
- case IR::OpStrictEqual: setOp(op, opName, Runtime::compareStrictEqual); break;
- case IR::OpStrictNotEqual: setOp(op, opName, Runtime::compareStrictNotEqual); break;
- case IR::OpInstanceof: setOpContext(op, opName, Runtime::compareInstanceof); break;
- case IR::OpIn: setOpContext(op, opName, Runtime::compareIn); break;
+ case IR::OpGt: setOp(op, opName, compareGreaterThan); break;
+ case IR::OpLt: setOp(op, opName, compareLessThan); break;
+ case IR::OpGe: setOp(op, opName, compareGreaterEqual); break;
+ case IR::OpLe: setOp(op, opName, compareLessEqual); break;
+ case IR::OpEqual: setOp(op, opName, compareEqual); break;
+ case IR::OpNotEqual: setOp(op, opName, compareNotEqual); break;
+ case IR::OpStrictEqual: setOp(op, opName, compareStrictEqual); break;
+ case IR::OpStrictNotEqual: setOp(op, opName, compareStrictNotEqual); break;
+ case IR::OpInstanceof: setOpContext(op, opName, compareInstanceof); break;
+ case IR::OpIn: setOpContext(op, opName, compareIn); break;
} // switch
// TODO: in SSA optimization, do constant expression evaluation.
@@ -1481,13 +1472,15 @@ void InstructionSelection::visitCJump(IR::CJump *s)
// if (true === true) .....
// Of course, after folding the CJUMP to a JUMP, dead-code (dead-basic-block)
// elimination (which isn't there either) would remove the whole else block.
- if (opContext)
- _as->generateFunctionCallImp(Assembler::ReturnValueRegister, opName, opContext,
+ if (opContext.isValid())
+ _as->generateFunctionCallImp(needsExceptionCheck,
+ Assembler::ReturnValueRegister, opName, opContext,
Assembler::EngineRegister,
Assembler::PointerToValue(b->left),
Assembler::PointerToValue(b->right));
else
- _as->generateFunctionCallImp(Assembler::ReturnValueRegister, opName, op,
+ _as->generateFunctionCallImp(needsExceptionCheck,
+ Assembler::ReturnValueRegister, opName, op,
Assembler::PointerToValue(b->left),
Assembler::PointerToValue(b->right));
@@ -1695,7 +1688,7 @@ void InstructionSelection::calculateRegistersToSave(const RegisterInformation &u
regularRegistersToSave.clear();
fpRegistersToSave.clear();
- foreach (const RegisterInfo &ri, Assembler::getRegisterInfo()) {
+ for (const RegisterInfo &ri : Assembler::getRegisterInfo()) {
#if defined(RESTORE_EBX_ON_CALL)
if (ri.isRegularRegister() && ri.reg<JSC::X86Registers::RegisterID>() == JSC::X86Registers::ebx) {
regularRegistersToSave.append(ri);
@@ -1724,38 +1717,6 @@ bool operator==(const Primitive &v1, const Primitive &v2)
} // QV4 namespace
QT_END_NAMESPACE
-int Assembler::ConstantTable::add(const Primitive &v)
-{
- int idx = _values.indexOf(v);
- if (idx == -1) {
- idx = _values.size();
- _values.append(v);
- }
- return idx;
-}
-
-Assembler::Address Assembler::ConstantTable::loadValueAddress(IR::Const *c, RegisterID baseReg)
-{
- return loadValueAddress(convertToValue(c), baseReg);
-}
-
-Assembler::Address Assembler::ConstantTable::loadValueAddress(const Primitive &v, RegisterID baseReg)
-{
- _toPatch.append(_as->moveWithPatch(TrustedImmPtr(0), baseReg));
- Address addr(baseReg);
- addr.offset = add(v) * sizeof(QV4::Primitive);
- Q_ASSERT(addr.offset >= 0);
- return addr;
-}
-
-void Assembler::ConstantTable::finalize(JSC::LinkBuffer &linkBuffer, InstructionSelection *isel)
-{
- const void *tablePtr = isel->addConstantTable(&_values);
-
- foreach (DataLabelPtr label, _toPatch)
- linkBuffer.patch(label, const_cast<void *>(tablePtr));
-}
-
bool InstructionSelection::visitCJumpDouble(IR::AluOp op, IR::Expr *left, IR::Expr *right,
IR::BasicBlock *iftrue, IR::BasicBlock *iffalse)
{
@@ -1805,7 +1766,7 @@ void InstructionSelection::visitCJumpStrict(IR::Binop *binop, IR::BasicBlock *tr
IR::Expr *left = binop->left;
IR::Expr *right = binop->right;
- _as->generateFunctionCallImp(Assembler::ReturnValueRegister, "Runtime::compareStrictEqual", Runtime::compareStrictEqual,
+ generateRuntimeCall(Assembler::ReturnValueRegister, compareStrictEqual,
Assembler::PointerToValue(left), Assembler::PointerToValue(right));
_as->generateCJumpOnCompare(binop->op == IR::OpStrictEqual ? Assembler::NotEqual : Assembler::Equal,
Assembler::ReturnValueRegister, Assembler::TrustedImm32(0),
@@ -2006,12 +1967,18 @@ void InstructionSelection::visitCJumpEqual(IR::Binop *binop, IR::BasicBlock *tru
IR::Expr *left = binop->left;
IR::Expr *right = binop->right;
- _as->generateFunctionCallImp(Assembler::ReturnValueRegister, "Runtime::compareEqual", Runtime::compareEqual,
+ generateRuntimeCall(Assembler::ReturnValueRegister, compareEqual,
Assembler::PointerToValue(left), Assembler::PointerToValue(right));
_as->generateCJumpOnCompare(binop->op == IR::OpEqual ? Assembler::NotEqual : Assembler::Equal,
Assembler::ReturnValueRegister, Assembler::TrustedImm32(0),
_block, trueBlock, falseBlock);
}
+QQmlRefPointer<CompiledData::CompilationUnit> ISelFactory::createUnitForLoading()
+{
+ QQmlRefPointer<CompiledData::CompilationUnit> result;
+ result.adopt(new JIT::CompilationUnit);
+ return result;
+}
#endif // ENABLE(ASSEMBLER)
diff --git a/src/qml/jit/qv4isel_masm_p.h b/src/qml/jit/qv4isel_masm_p.h
index 1e6ac1f51c..b6ddd3c1c9 100644
--- a/src/qml/jit/qv4isel_masm_p.h
+++ b/src/qml/jit/qv4isel_masm_p.h
@@ -76,12 +76,11 @@ class Q_QML_EXPORT InstructionSelection:
public EvalInstructionSelection
{
public:
- InstructionSelection(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator);
+ InstructionSelection(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator, EvalISelFactory *iselFactory);
~InstructionSelection();
virtual void run(int functionIndex);
- const void *addConstantTable(QVector<QV4::Primitive> *values);
protected:
virtual QQmlRefPointer<QV4::CompiledData::CompilationUnit> backendCompileStep();
@@ -124,7 +123,7 @@ protected:
virtual void setActivationProperty(IR::Expr *source, const QString &targetName);
virtual void initClosure(IR::Closure *closure, IR::Expr *target);
virtual void getProperty(IR::Expr *base, const QString &name, IR::Expr *target);
- virtual void getQmlContextProperty(IR::Expr *source, IR::Member::MemberKind kind, int index, IR::Expr *target);
+ virtual void getQmlContextProperty(IR::Expr *source, IR::Member::MemberKind kind, int index, bool captureRequired, IR::Expr *target);
virtual void getQObjectProperty(IR::Expr *base, int propertyIndex, bool captureRequired, bool isSingleton, int attachedPropertiesId, IR::Expr *target);
virtual void setProperty(IR::Expr *source, IR::Expr *targetBase, const QString &targetName);
virtual void setQmlContextProperty(IR::Expr *source, IR::Expr *targetBase, IR::Member::MemberKind kind, int propertyIndex);
@@ -244,8 +243,8 @@ private:
#define isel_stringIfyx(s) #s
#define isel_stringIfy(s) isel_stringIfyx(s)
- #define generateFunctionCall(t, function, ...) \
- _as->generateFunctionCallImp(t, isel_stringIfy(function), function, __VA_ARGS__)
+ #define generateRuntimeCall(t, function, ...) \
+ _as->generateFunctionCallImp(Runtime::Method_##function##_NeedsExceptionCheck, t, "Runtime::" isel_stringIfy(function), RuntimeCall(qOffsetOf(QV4::Runtime, function)), __VA_ARGS__)
int prepareVariableArguments(IR::ExprList* args);
int prepareCallData(IR::ExprList* args, IR::Expr *thisObject);
@@ -261,7 +260,7 @@ private:
// address.
Assembler::Pointer lookupAddr(Assembler::ReturnValueRegister, index * sizeof(QV4::Lookup));
- _as->generateFunctionCallImp(retval, "lookup getter/setter",
+ _as->generateFunctionCallImp(true, retval, "lookup getter/setter",
LookupCall(lookupAddr, getterSetterOffset), lookupAddr,
arg1, arg2, arg3);
}
@@ -285,11 +284,13 @@ private:
class Q_QML_EXPORT ISelFactory: public EvalISelFactory
{
public:
+ ISelFactory() : EvalISelFactory(QStringLiteral("jit")) {}
virtual ~ISelFactory() {}
- virtual EvalInstructionSelection *create(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator)
- { return new InstructionSelection(qmlEngine, execAllocator, module, jsGenerator); }
- virtual bool jitCompileRegexps() const
+ EvalInstructionSelection *create(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator) Q_DECL_OVERRIDE Q_DECL_FINAL
+ { return new InstructionSelection(qmlEngine, execAllocator, module, jsGenerator, this); }
+ bool jitCompileRegexps() const Q_DECL_OVERRIDE Q_DECL_FINAL
{ return true; }
+ QQmlRefPointer<CompiledData::CompilationUnit> createUnitForLoading() Q_DECL_OVERRIDE Q_DECL_FINAL;
};
} // end of namespace JIT
diff --git a/src/qml/jit/qv4regalloc.cpp b/src/qml/jit/qv4regalloc.cpp
index c21f52ecd3..406b9096ea 100644
--- a/src/qml/jit/qv4regalloc.cpp
+++ b/src/qml/jit/qv4regalloc.cpp
@@ -87,7 +87,7 @@ public:
{}
protected:
- void addStmtNr(Stmt *s)
+ void addStmtNr(Stmt *s) Q_DECL_OVERRIDE Q_DECL_FINAL
{
addJustifiedNr(intervals->positionForStatement(s));
}
@@ -115,7 +115,7 @@ public:
}
protected:
- void visitTemp(Temp *e)
+ void visitTemp(Temp *e) Q_DECL_OVERRIDE Q_DECL_FINAL
{
switch (e->kind) {
case Temp::PhysicalRegister: {
@@ -184,7 +184,7 @@ public:
_currentBB = bb;
for (Stmt *s : bb->statements()) {
_currentStmt = s;
- s->accept(this);
+ visit(s);
}
}
}
@@ -528,7 +528,7 @@ protected: // IRDecoder
addCall();
}
- virtual void getQmlContextProperty(IR::Expr *base, IR::Member::MemberKind /*kind*/, int /*index*/, IR::Expr *target)
+ virtual void getQmlContextProperty(IR::Expr *base, IR::Member::MemberKind /*kind*/, int /*index*/, bool /*captureRequired*/, IR::Expr *target)
{
addDef(target);
addUses(base->asTemp(), Use::CouldHaveRegister);
@@ -809,14 +809,15 @@ using namespace QT_PREPEND_NAMESPACE(QV4::IR);
using namespace QT_PREPEND_NAMESPACE(QV4);
namespace {
-class ResolutionPhase: protected StmtVisitor, protected ExprVisitor {
+class ResolutionPhase
+{
Q_DISABLE_COPY(ResolutionPhase)
LifeTimeIntervals::Ptr _intervals;
- QVector<LifeTimeInterval *> _unprocessed;
+ QVector<LifeTimeInterval *> _unprocessedReverseOrder;
IR::Function *_function;
const std::vector<int> &_assignedSpillSlots;
- QHash<IR::Temp, const LifeTimeInterval *> _intervalForTemp;
+ std::vector<const LifeTimeInterval *> _liveIntervals;
const QVector<const RegisterInfo *> &_intRegs;
const QVector<const RegisterInfo *> &_fpRegs;
@@ -824,26 +825,26 @@ class ResolutionPhase: protected StmtVisitor, protected ExprVisitor {
std::vector<Move *> _loads;
std::vector<Move *> _stores;
- QHash<BasicBlock *, QList<const LifeTimeInterval *> > _liveAtStart;
- QHash<BasicBlock *, QList<const LifeTimeInterval *> > _liveAtEnd;
+ std::vector<std::vector<const LifeTimeInterval *> > _liveAtStart;
+ std::vector<std::vector<const LifeTimeInterval *> > _liveAtEnd;
public:
- ResolutionPhase(const QVector<LifeTimeInterval *> &unprocessed,
+ ResolutionPhase(QVector<LifeTimeInterval *> &&unprocessedReversedOrder,
const LifeTimeIntervals::Ptr &intervals,
IR::Function *function,
const std::vector<int> &assignedSpillSlots,
const QVector<const RegisterInfo *> &intRegs,
const QVector<const RegisterInfo *> &fpRegs)
: _intervals(intervals)
+ , _unprocessedReverseOrder(unprocessedReversedOrder)
, _function(function)
, _assignedSpillSlots(assignedSpillSlots)
, _intRegs(intRegs)
, _fpRegs(fpRegs)
, _currentStmt(0)
{
- _unprocessed = unprocessed;
- _liveAtStart.reserve(function->basicBlockCount());
- _liveAtEnd.reserve(function->basicBlockCount());
+ _liveAtStart.resize(function->basicBlockCount());
+ _liveAtEnd.resize(function->basicBlockCount());
}
void run() {
@@ -882,7 +883,7 @@ private:
cleanOldIntervals(_intervals->startPosition(bb));
addNewIntervals(_intervals->startPosition(bb));
- _liveAtStart[bb] = _intervalForTemp.values();
+ _liveAtStart[bb->index()] = _liveIntervals;
for (int i = 0, ei = statements.size(); i != ei; ++i) {
_currentStmt = statements.at(i);
@@ -892,7 +893,7 @@ private:
addNewIntervals(usePosition(_currentStmt));
else
addNewIntervals(defPosition(_currentStmt));
- _currentStmt->accept(this);
+ visit(_currentStmt);
for (Move *load : _loads)
newStatements.append(load);
if (_currentStmt->asPhi())
@@ -904,24 +905,24 @@ private:
}
cleanOldIntervals(_intervals->endPosition(bb));
- _liveAtEnd[bb] = _intervalForTemp.values();
+ _liveAtEnd[bb->index()] = _liveIntervals;
if (DebugRegAlloc) {
QBuffer buf;
buf.open(QIODevice::WriteOnly);
QTextStream os(&buf);
os << "Intervals live at the start of L" << bb->index() << ":" << endl;
- if (_liveAtStart[bb].isEmpty())
+ if (_liveAtStart[bb->index()].empty())
os << "\t(none)" << endl;
- for (const LifeTimeInterval *i : _liveAtStart.value(bb)) {
+ for (const LifeTimeInterval *i : _liveAtStart.at(bb->index())) {
os << "\t";
i->dump(os);
os << endl;
}
os << "Intervals live at the end of L" << bb->index() << ":" << endl;
- if (_liveAtEnd[bb].isEmpty())
+ if (_liveAtEnd[bb->index()].empty())
os << "\t(none)" << endl;
- for (const LifeTimeInterval *i : _liveAtEnd.value(bb)) {
+ for (const LifeTimeInterval *i : _liveAtEnd.at(bb->index())) {
os << "\t";
i->dump(os);
os << endl;
@@ -934,9 +935,19 @@ private:
}
+ const LifeTimeInterval *findLiveInterval(Temp *t) const
+ {
+ for (const LifeTimeInterval *lti : _liveIntervals) {
+ if (lti->temp() == *t)
+ return lti;
+ }
+
+ return nullptr;
+ }
+
void maybeGenerateSpill(Temp *t)
{
- const LifeTimeInterval *i = _intervalForTemp[*t];
+ const LifeTimeInterval *i = findLiveInterval(t);
if (i->reg() == LifeTimeInterval::InvalidRegister)
return;
@@ -952,26 +963,27 @@ private:
if (position == Stmt::InvalidId)
return;
- while (!_unprocessed.isEmpty()) {
- const LifeTimeInterval *i = _unprocessed.constFirst();
+ while (!_unprocessedReverseOrder.isEmpty()) {
+ const LifeTimeInterval *i = _unprocessedReverseOrder.constLast();
if (i->start() > position)
break;
Q_ASSERT(!i->isFixedInterval());
- _intervalForTemp[i->temp()] = i;
+ _liveIntervals.push_back(i);
// qDebug() << "-- Activating interval for temp" << i->temp().index;
- _unprocessed.removeFirst();
+ _unprocessedReverseOrder.removeLast();
}
}
void cleanOldIntervals(int position)
{
- QMutableHashIterator<Temp, const LifeTimeInterval *> it(_intervalForTemp);
- while (it.hasNext()) {
- const LifeTimeInterval *i = it.next().value();
- if (i->end() < position || i->isFixedInterval())
- it.remove();
+ for (size_t it = 0; it != _liveIntervals.size(); ) {
+ const LifeTimeInterval *lti = _liveIntervals.at(it);
+ if (lti->end() < position || lti->isFixedInterval())
+ _liveIntervals.erase(_liveIntervals.begin() + it);
+ else
+ ++it;
}
}
@@ -1018,7 +1030,7 @@ private:
int successorStart = _intervals->startPosition(successor);
Q_ASSERT(successorStart > 0);
- for (const LifeTimeInterval *it : _liveAtStart.value(successor)) {
+ for (const LifeTimeInterval *it : _liveAtStart.at(successor->index())) {
bool isPhiTarget = false;
Expr *moveFrom = 0;
@@ -1032,7 +1044,7 @@ private:
Temp *t = opd->asTemp();
Q_ASSERT(t);
- for (const LifeTimeInterval *it2 : _liveAtEnd.value(predecessor)) {
+ for (const LifeTimeInterval *it2 : _liveAtEnd.at(predecessor->index())) {
if (it2->temp() == *t
&& it2->reg() != LifeTimeInterval::InvalidRegister
&& it2->covers(predecessorEnd)) {
@@ -1047,7 +1059,7 @@ private:
}
}
} else {
- for (const LifeTimeInterval *predIt : _liveAtEnd.value(predecessor)) {
+ for (const LifeTimeInterval *predIt : _liveAtEnd.at(predecessor->index())) {
if (predIt->temp() == it->temp()) {
if (predIt->reg() != LifeTimeInterval::InvalidRegister
&& predIt->covers(predecessorEnd)) {
@@ -1179,13 +1191,25 @@ private:
return load;
}
-protected:
- virtual void visitTemp(Temp *t)
+private:
+ void visit(Expr *e)
+ {
+ switch (e->exprKind) {
+ case Expr::TempExpr:
+ visitTemp(e->asTemp());
+ break;
+ default:
+ EXPR_VISIT_ALL_KINDS(e);
+ break;
+ }
+ }
+
+ void visitTemp(Temp *t)
{
if (t->kind != Temp::VirtualRegister)
return;
- const LifeTimeInterval *i = _intervalForTemp[*t];
+ const LifeTimeInterval *i = findLiveInterval(t);
Q_ASSERT(i->isValid());
if (_currentStmt != 0 && i->start() == usePosition(_currentStmt)) {
@@ -1210,47 +1234,25 @@ protected:
}
}
- virtual void visitArgLocal(ArgLocal *) {}
- virtual void visitConst(Const *) {}
- virtual void visitString(IR::String *) {}
- virtual void visitRegExp(IR::RegExp *) {}
- virtual void visitName(Name *) {}
- virtual void visitClosure(Closure *) {}
- virtual void visitConvert(Convert *e) { e->expr->accept(this); }
- virtual void visitUnop(Unop *e) { e->expr->accept(this); }
- virtual void visitBinop(Binop *e) { e->left->accept(this); e->right->accept(this); }
- virtual void visitSubscript(Subscript *e) { e->base->accept(this); e->index->accept(this); }
- virtual void visitMember(Member *e) { e->base->accept(this); }
-
- virtual void visitCall(Call *e) {
- e->base->accept(this);
- for (ExprList *it = e->args; it; it = it->next)
- it->expr->accept(this);
- }
-
- virtual void visitNew(New *e) {
- e->base->accept(this);
- for (ExprList *it = e->args; it; it = it->next)
- it->expr->accept(this);
- }
-
- virtual void visitExp(Exp *s) { s->expr->accept(this); }
-
- virtual void visitMove(Move *s)
+ void visit(Stmt *s)
{
- if (Temp *t = s->target->asTemp())
- maybeGenerateSpill(t);
-
- s->source->accept(this);
- s->target->accept(this);
- }
+ switch (s->stmtKind) {
+ case Stmt::MoveStmt: {
+ auto m = s->asMove();
+ if (Temp *t = m->target->asTemp())
+ maybeGenerateSpill(t);
- virtual void visitJump(Jump *) {}
- virtual void visitCJump(CJump *s) { s->cond->accept(this); }
- virtual void visitRet(Ret *s) { s->expr->accept(this); }
- virtual void visitPhi(Phi *s)
- {
- maybeGenerateSpill(s->targetTemp);
+ visit(m->source);
+ visit(m->target);
+ } break;
+ case Stmt::PhiStmt: {
+ auto p = s->asPhi();
+ maybeGenerateSpill(p->targetTemp);
+ } break;
+ default:
+ STMT_VISIT_ALL_KINDS(s);
+ break;
+ }
}
};
} // anonymous namespace
@@ -1323,8 +1325,13 @@ void RegisterAllocator::run(IR::Function *function, const Optimizer &opt)
if (DebugRegAlloc)
dump(function);
- std::sort(_handled.begin(), _handled.end(), LifeTimeInterval::lessThan);
- ResolutionPhase(_handled, _lifeTimeIntervals, function, _assignedSpillSlots, _normalRegisters, _fpRegisters).run();
+ // sort the ranges in reverse order, so the ResolutionPhase can take from the end (and thereby
+ // prevent the copy overhead that taking from the beginning would give).
+ std::sort(_handled.begin(), _handled.end(),
+ [](const LifeTimeInterval *r1, const LifeTimeInterval *r2) -> bool {
+ return LifeTimeInterval::lessThan(r2, r1);
+ });
+ ResolutionPhase(std::move(_handled), _lifeTimeIntervals, function, _assignedSpillSlots, _normalRegisters, _fpRegisters).run();
function->tempCount = *std::max_element(_assignedSpillSlots.begin(), _assignedSpillSlots.end()) + 1;
diff --git a/src/qml/jit/qv4targetplatform_p.h b/src/qml/jit/qv4targetplatform_p.h
index 6f0a7374c3..7e265258d5 100644
--- a/src/qml/jit/qv4targetplatform_p.h
+++ b/src/qml/jit/qv4targetplatform_p.h
@@ -563,7 +563,7 @@ public:
#endif // Linux on MIPS (32 bit)
public: // utility functions
- static RegisterInformation getRegisterInfo()
+ static const RegisterInformation getRegisterInfo()
{
static const RegisterInformation info = getPlatformRegisterInfo();
diff --git a/src/qml/jit/qv4unop.cpp b/src/qml/jit/qv4unop.cpp
index cb9131d731..799103849b 100644
--- a/src/qml/jit/qv4unop.cpp
+++ b/src/qml/jit/qv4unop.cpp
@@ -47,11 +47,15 @@ using namespace JIT;
#define stringIfyx(s) #s
#define stringIfy(s) stringIfyx(s)
#define setOp(operation) \
- do { call = operation; name = stringIfy(operation); } while (0)
+ do { \
+ call = RuntimeCall(qOffsetOf(QV4::Runtime, operation)); name = "Runtime::" stringIfy(operation); \
+ needsExceptionCheck = Runtime::Method_##operation##_NeedsExceptionCheck; \
+ } while (0)
void Unop::generate(IR::Expr *source, IR::Expr *target)
{
- Runtime::UnaryOperation call = 0;
+ bool needsExceptionCheck;
+ RuntimeCall call;
const char *name = 0;
switch (op) {
case IR::OpNot:
@@ -60,19 +64,18 @@ void Unop::generate(IR::Expr *source, IR::Expr *target)
case IR::OpUMinus:
generateUMinus(source, target);
return;
- case IR::OpUPlus: setOp(Runtime::uPlus); break;
+ case IR::OpUPlus: setOp(uPlus); break;
case IR::OpCompl:
generateCompl(source, target);
return;
- case IR::OpIncrement: setOp(Runtime::increment); break;
- case IR::OpDecrement: setOp(Runtime::decrement); break;
+ case IR::OpIncrement: setOp(increment); break;
+ case IR::OpDecrement: setOp(decrement); break;
default:
Q_UNREACHABLE();
} // switch
- if (call) {
- as->generateFunctionCallImp(target, name, call, Assembler::PointerToValue(source));
- }
+ Q_ASSERT(call.isValid());
+ _as->generateFunctionCallImp(needsExceptionCheck, target, name, call, Assembler::PointerToValue(source));
}
void Unop::generateUMinus(IR::Expr *source, IR::Expr *target)
@@ -82,15 +85,15 @@ void Unop::generateUMinus(IR::Expr *source, IR::Expr *target)
Assembler::RegisterID tReg = Assembler::ScratchRegister;
if (targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister)
tReg = (Assembler::RegisterID) targetTemp->index;
- Assembler::RegisterID sReg = as->toInt32Register(source, tReg);
- as->move(sReg, tReg);
- as->neg32(tReg);
+ Assembler::RegisterID sReg = _as->toInt32Register(source, tReg);
+ _as->move(sReg, tReg);
+ _as->neg32(tReg);
if (!targetTemp || targetTemp->kind != IR::Temp::PhysicalRegister)
- as->storeInt32(tReg, target);
+ _as->storeInt32(tReg, target);
return;
}
- as->generateFunctionCallImp(target, "Runtime::uMinus", Runtime::uMinus, Assembler::PointerToValue(source));
+ generateRuntimeCall(target, uMinus, Assembler::PointerToValue(source));
}
void Unop::generateNot(IR::Expr *source, IR::Expr *target)
@@ -100,26 +103,26 @@ void Unop::generateNot(IR::Expr *source, IR::Expr *target)
Assembler::RegisterID tReg = Assembler::ScratchRegister;
if (targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister)
tReg = (Assembler::RegisterID) targetTemp->index;
- as->xor32(Assembler::TrustedImm32(0x1), as->toInt32Register(source, tReg), tReg);
+ _as->xor32(Assembler::TrustedImm32(0x1), _as->toInt32Register(source, tReg), tReg);
if (!targetTemp || targetTemp->kind != IR::Temp::PhysicalRegister)
- as->storeBool(tReg, target);
+ _as->storeBool(tReg, target);
return;
} else if (source->type == IR::SInt32Type) {
Assembler::RegisterID tReg = Assembler::ScratchRegister;
if (targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister)
tReg = (Assembler::RegisterID) targetTemp->index;
- as->compare32(Assembler::Equal,
- as->toInt32Register(source, Assembler::ScratchRegister), Assembler::TrustedImm32(0),
+ _as->compare32(Assembler::Equal,
+ _as->toInt32Register(source, Assembler::ScratchRegister), Assembler::TrustedImm32(0),
tReg);
if (!targetTemp || targetTemp->kind != IR::Temp::PhysicalRegister)
- as->storeBool(tReg, target);
+ _as->storeBool(tReg, target);
return;
} else if (source->type == IR::DoubleType) {
// ###
}
// ## generic implementation testing for int/bool
- as->generateFunctionCallImp(target, "Runtime::uNot", Runtime::uNot, Assembler::PointerToValue(source));
+ generateRuntimeCall(target, uNot, Assembler::PointerToValue(source));
}
void Unop::generateCompl(IR::Expr *source, IR::Expr *target)
@@ -129,12 +132,12 @@ void Unop::generateCompl(IR::Expr *source, IR::Expr *target)
Assembler::RegisterID tReg = Assembler::ScratchRegister;
if (targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister)
tReg = (Assembler::RegisterID) targetTemp->index;
- as->xor32(Assembler::TrustedImm32(0xffffffff), as->toInt32Register(source, tReg), tReg);
+ _as->xor32(Assembler::TrustedImm32(0xffffffff), _as->toInt32Register(source, tReg), tReg);
if (!targetTemp || targetTemp->kind != IR::Temp::PhysicalRegister)
- as->storeInt32(tReg, target);
+ _as->storeInt32(tReg, target);
return;
}
- as->generateFunctionCallImp(target, "Runtime::complement", Runtime::complement, Assembler::PointerToValue(source));
+ generateRuntimeCall(target, complement, Assembler::PointerToValue(source));
}
#endif
diff --git a/src/qml/jit/qv4unop_p.h b/src/qml/jit/qv4unop_p.h
index f0b5b9c223..1141a84913 100644
--- a/src/qml/jit/qv4unop_p.h
+++ b/src/qml/jit/qv4unop_p.h
@@ -64,7 +64,7 @@ class Assembler;
struct Unop {
Unop(Assembler *assembler, IR::AluOp operation)
- : as(assembler)
+ : _as(assembler)
, op(operation)
{}
@@ -74,7 +74,7 @@ struct Unop {
void generateNot(IR::Expr *source, IR::Expr *target);
void generateCompl(IR::Expr *source, IR::Expr *target);
- Assembler *as;
+ Assembler *_as;
IR::AluOp op;
};
diff --git a/src/qml/jsapi/qjsengine.cpp b/src/qml/jsapi/qjsengine.cpp
index 9c952f0d42..4404a5d79f 100644
--- a/src/qml/jsapi/qjsengine.cpp
+++ b/src/qml/jsapi/qjsengine.cpp
@@ -166,6 +166,17 @@ Q_DECLARE_METATYPE(QList<int>)
properties of the proxy object. No binding code is needed because it
is done dynamically using the Qt meta object system.
+ Use newQMetaObject() to wrap a QMetaObject; this gives you a
+ "script representation" of a QObject-based class. newQMetaObject()
+ returns a proxy script object; enum values of the class are available
+ as properties of the proxy object.
+
+ Constructors exposed to the meta-object system ( using Q_INVOKABLE ) can be
+ called from the script to create a new QObject instance with
+ JavaScriptOwnership.
+
+
+
\snippet code/src_script_qjsengine.cpp 5
\section1 Extensions
@@ -261,12 +272,8 @@ static void checkForApplicationInstance()
\l{ECMA-262}, Section 15.1.
*/
QJSEngine::QJSEngine()
- : QObject(*new QJSEnginePrivate, 0)
- , d(new QV8Engine(this))
+ : QJSEngine(nullptr)
{
- checkForApplicationInstance();
-
- QJSEnginePrivate::addToDebugServer(this);
}
/*!
@@ -515,6 +522,38 @@ QJSValue QJSEngine::newQObject(QObject *object)
}
/*!
+ \since 5.8
+
+ Creates a JavaScript object that wraps the given QMetaObject
+ The metaObject must outlive the script engine. It is recommended to only
+ use this method with static metaobjects.
+
+
+ When called as a constructor, a new instance of the class will be created.
+ Only constructors exposed by Q_INVOKABLE will be visible from the script engine.
+
+ \sa newQObject()
+*/
+
+QJSValue QJSEngine::newQMetaObject(const QMetaObject* metaObject) {
+ Q_D(QJSEngine);
+ QV4::ExecutionEngine *v4 = QV8Engine::getV4(d);
+ QV4::Scope scope(v4);
+ QV4::ScopedValue v(scope, QV4::QMetaObjectWrapper::create(v4, metaObject));
+ return QJSValue(v4, v->asReturnedValue());
+}
+
+/*! \fn QJSValue QJSEngine::newQMetaObject<T>()
+
+ \since 5.8
+ Creates a JavaScript object that wraps the static QMetaObject associated
+ with class \c{T}.
+
+ \sa newQObject()
+*/
+
+
+/*!
Returns this engine's Global Object.
By default, the Global Object contains the built-in objects that are
diff --git a/src/qml/jsapi/qjsengine.h b/src/qml/jsapi/qjsengine.h
index 6ecd0c7ec0..41c4b81270 100644
--- a/src/qml/jsapi/qjsengine.h
+++ b/src/qml/jsapi/qjsengine.h
@@ -74,6 +74,14 @@ public:
QJSValue newQObject(QObject *object);
+ QJSValue newQMetaObject(const QMetaObject* metaObject);
+
+ template <typename T>
+ QJSValue newQMetaObject()
+ {
+ return newQMetaObject(&T::staticMetaObject);
+ }
+
template <typename T>
inline QJSValue toScriptValue(const T &value)
{
diff --git a/src/qml/jsapi/qjsvalue.cpp b/src/qml/jsapi/qjsvalue.cpp
index ec7848aba2..a4a96a96a7 100644
--- a/src/qml/jsapi/qjsvalue.cpp
+++ b/src/qml/jsapi/qjsvalue.cpp
@@ -178,7 +178,7 @@ QJSValue::QJSValue(SpecialValue value)
: d(0)
{
if (value == NullValue)
- QJSValuePrivate::setVariant(this, QVariant(QMetaType::VoidStar, (void *)0));
+ QJSValuePrivate::setVariant(this, QVariant::fromValue(nullptr));
}
/*!
@@ -293,7 +293,10 @@ bool QJSValue::isNull() const
if (val)
return val->isNull();
QVariant *variant = QJSValuePrivate::getVariant(this);
- return variant && variant->userType() == QMetaType::VoidStar;
+ if (!variant)
+ return false;
+ const int type = variant->userType();
+ return type == QMetaType::Nullptr || type == QMetaType::VoidStar;
}
/*!
@@ -582,7 +585,7 @@ quint32 QJSValue::toUInt() const
\table
\header \li Input Type \li Result
\row \li Undefined \li An invalid QVariant.
- \row \li Null \li A QVariant containing a null pointer (QMetaType::VoidStar).
+ \row \li Null \li A QVariant containing a null pointer (QMetaType::Nullptr).
\row \li Boolean \li A QVariant containing the value of the boolean.
\row \li Number \li A QVariant containing the value of the number.
\row \li String \li A QVariant containing the value of the string.
@@ -619,7 +622,7 @@ QVariant QJSValue::toVariant() const
return QVariant(val->asDouble());
}
if (val->isNull())
- return QVariant(QMetaType::VoidStar, 0);
+ return QVariant(QMetaType::Nullptr, 0);
Q_ASSERT(val->isUndefined());
return QVariant();
}
@@ -663,11 +666,11 @@ QJSValue QJSValue::call(const QJSValueList &args)
callData->args[i] = QJSValuePrivate::convertedToValue(engine, args.at(i));
}
- ScopedValue result(scope, f->call(callData));
+ f->call(scope, callData);
if (engine->hasException)
- result = engine->catchException();
+ scope.result = engine->catchException();
- return QJSValue(engine, result->asReturnedValue());
+ return QJSValue(engine, scope.result.asReturnedValue());
}
/*!
@@ -719,11 +722,11 @@ QJSValue QJSValue::callWithInstance(const QJSValue &instance, const QJSValueList
callData->args[i] = QJSValuePrivate::convertedToValue(engine, args.at(i));
}
- ScopedValue result(scope, f->call(callData));
+ f->call(scope, callData);
if (engine->hasException)
- result = engine->catchException();
+ scope.result = engine->catchException();
- return QJSValue(engine, result->asReturnedValue());
+ return QJSValue(engine, scope.result.asReturnedValue());
}
/*!
@@ -767,11 +770,11 @@ QJSValue QJSValue::callAsConstructor(const QJSValueList &args)
callData->args[i] = QJSValuePrivate::convertedToValue(engine, args.at(i));
}
- ScopedValue result(scope, f->construct(callData));
+ f->construct(scope, callData);
if (engine->hasException)
- result = engine->catchException();
+ scope.result = engine->catchException();
- return QJSValue(engine, result->asReturnedValue());
+ return QJSValue(engine, scope.result.asReturnedValue());
}
#ifdef QT_DEPRECATED
@@ -938,7 +941,7 @@ bool QJSValue::equals(const QJSValue& other) const
if (!ov)
return other.equals(*this);
- return Runtime::compareEqual(*v, *ov);
+ return Runtime::method_compareEqual(*v, *ov);
}
/*!
@@ -1234,6 +1237,28 @@ QObject *QJSValue::toQObject() const
}
/*!
+ \since 5.8
+
+ * If this QJSValue is a QMetaObject, returns the QMetaObject pointer
+ * that the QJSValue represents; otherwise, returns 0.
+ *
+ * \sa isQMetaObject()
+ */
+const QMetaObject *QJSValue::toQMetaObject() const
+{
+ QV4::ExecutionEngine *engine = QJSValuePrivate::engine(this);
+ if (!engine)
+ return 0;
+ QV4::Scope scope(engine);
+ QV4::Scoped<QV4::QMetaObjectWrapper> wrapper(scope, QJSValuePrivate::getValue(this));
+ if (!wrapper)
+ return 0;
+
+ return wrapper->metaObject();
+}
+
+
+/*!
Returns a QDateTime representation of this value, in local time.
If this QJSValue is not a date, or the value of the date is NaN
(Not-a-Number), an invalid QDateTime is returned.
@@ -1286,4 +1311,18 @@ bool QJSValue::isQObject() const
return val && val->as<QV4::QObjectWrapper>() != 0;
}
+/*!
+ \since 5.8
+
+ Returns true if this QJSValue is a QMetaObject; otherwise returns
+ false.
+
+ \sa toQMetaObject(), QJSEngine::newQMetaObject()
+*/
+bool QJSValue::isQMetaObject() const
+{
+ QV4::Value *val = QJSValuePrivate::getValue(this);
+ return val && val->as<QV4::QMetaObjectWrapper>() != 0;
+}
+
QT_END_NAMESPACE
diff --git a/src/qml/jsapi/qjsvalue.h b/src/qml/jsapi/qjsvalue.h
index e207e1b099..ab20a2607d 100644
--- a/src/qml/jsapi/qjsvalue.h
+++ b/src/qml/jsapi/qjsvalue.h
@@ -98,6 +98,7 @@ public:
bool isUndefined() const;
bool isVariant() const;
bool isQObject() const;
+ bool isQMetaObject() const;
bool isObject() const;
bool isDate() const;
bool isRegExp() const;
@@ -111,6 +112,7 @@ public:
bool toBool() const;
QVariant toVariant() const;
QObject *toQObject() const;
+ const QMetaObject *toQMetaObject() const;
QDateTime toDateTime() const;
bool equals(const QJSValue &other) const;
diff --git a/src/qml/jsapi/qjsvalue_p.h b/src/qml/jsapi/qjsvalue_p.h
index 25afd9275c..c4761ad6ea 100644
--- a/src/qml/jsapi/qjsvalue_p.h
+++ b/src/qml/jsapi/qjsvalue_p.h
@@ -132,6 +132,7 @@ public:
case QMetaType::Void:
*v = QV4::Encode::undefined();
break;
+ case QMetaType::Nullptr:
case QMetaType::VoidStar:
*v = QV4::Encode::null();
break;
diff --git a/src/qml/jsapi/qjsvalueiterator.cpp b/src/qml/jsapi/qjsvalueiterator.cpp
index 86c7554924..ce472ce7e5 100644
--- a/src/qml/jsapi/qjsvalueiterator.cpp
+++ b/src/qml/jsapi/qjsvalueiterator.cpp
@@ -103,11 +103,11 @@ QJSValueIterator::QJSValueIterator(const QJSValue& object)
return;
QV4::Scope scope(v4);
QV4::Scoped<QV4::ForEachIteratorObject> it(scope, d_ptr->iterator.value());
- it->d()->it.flags = QV4::ObjectIterator::NoFlags;
+ it->d()->it().flags = QV4::ObjectIterator::NoFlags;
QV4::ScopedString nm(scope);
QV4::Property nextProperty;
QV4::PropertyAttributes nextAttributes;
- it->d()->it.next(nm.getRef(), &d_ptr->nextIndex, &nextProperty, &nextAttributes);
+ it->d()->it().next(nm.getRef(), &d_ptr->nextIndex, &nextProperty, &nextAttributes);
d_ptr->nextName.set(v4, nm.asReturnedValue());
}
@@ -157,7 +157,7 @@ bool QJSValueIterator::next()
QV4::ScopedString nm(scope);
QV4::Property nextProperty;
QV4::PropertyAttributes nextAttributes;
- it->d()->it.next(nm.getRef(), &d_ptr->nextIndex, &nextProperty, &nextAttributes);
+ it->d()->it().next(nm.getRef(), &d_ptr->nextIndex, &nextProperty, &nextAttributes);
d_ptr->nextName.set(v4, nm.asReturnedValue());
return d_ptr->currentName.as<QV4::String>() || d_ptr->currentIndex != UINT_MAX;
}
@@ -231,11 +231,11 @@ QJSValueIterator& QJSValueIterator::operator=(QJSValue& object)
QV4::ScopedObject o(scope, QJSValuePrivate::getValue(&object));
d_ptr->iterator.set(v4, v4->newForEachIteratorObject(o));
QV4::Scoped<QV4::ForEachIteratorObject> it(scope, d_ptr->iterator.value());
- it->d()->it.flags = QV4::ObjectIterator::NoFlags;
+ it->d()->it().flags = QV4::ObjectIterator::NoFlags;
QV4::ScopedString nm(scope);
QV4::Property nextProperty;
QV4::PropertyAttributes nextAttributes;
- it->d()->it.next(nm.getRef(), &d_ptr->nextIndex, &nextProperty, &nextAttributes);
+ it->d()->it().next(nm.getRef(), &d_ptr->nextIndex, &nextProperty, &nextAttributes);
d_ptr->nextName.set(v4, nm.asReturnedValue());
return *this;
}
diff --git a/src/qml/jsruntime/jsruntime.pri b/src/qml/jsruntime/jsruntime.pri
index 038b23e8d3..dcc04cbd54 100644
--- a/src/qml/jsruntime/jsruntime.pri
+++ b/src/qml/jsruntime/jsruntime.pri
@@ -6,7 +6,6 @@ SOURCES += \
$$PWD/qv4engine.cpp \
$$PWD/qv4context.cpp \
$$PWD/qv4persistent.cpp \
- $$PWD/qv4debugging.cpp \
$$PWD/qv4lookup.cpp \
$$PWD/qv4identifier.cpp \
$$PWD/qv4identifiertable.cpp \
@@ -39,12 +38,12 @@ SOURCES += \
$$PWD/qv4sequenceobject.cpp \
$$PWD/qv4include.cpp \
$$PWD/qv4qobjectwrapper.cpp \
- $$PWD/qv4vme_moth.cpp \
- $$PWD/qv4profiling.cpp \
$$PWD/qv4arraybuffer.cpp \
$$PWD/qv4typedarray.cpp \
$$PWD/qv4dataview.cpp
+!contains(QT_CONFIG, no-qml-debug): SOURCES += $$PWD/qv4profiling.cpp
+
HEADERS += \
$$PWD/qv4global_p.h \
$$PWD/qv4engine_p.h \
@@ -88,17 +87,24 @@ HEADERS += \
$$PWD/qv4sequenceobject_p.h \
$$PWD/qv4include_p.h \
$$PWD/qv4qobjectwrapper_p.h \
- $$PWD/qv4vme_moth_p.h \
$$PWD/qv4profiling_p.h \
$$PWD/qv4arraybuffer_p.h \
$$PWD/qv4typedarray_p.h \
$$PWD/qv4dataview_p.h
+qtConfig(qml-interpreter) {
+ HEADERS += \
+ $$PWD/qv4vme_moth_p.h
+ SOURCES += \
+ $$PWD/qv4vme_moth.cpp
+}
+
}
HEADERS += \
$$PWD/qv4runtime_p.h \
+ $$PWD/qv4runtimeapi_p.h \
$$PWD/qv4value_p.h \
$$PWD/qv4string_p.h \
$$PWD/qv4value_p.h
@@ -111,3 +117,7 @@ SOURCES += \
valgrind {
DEFINES += V4_USE_VALGRIND
}
+
+heaptrack {
+ DEFINES += V4_USE_HEAPTRACK
+}
diff --git a/src/qml/jsruntime/qv4argumentsobject.cpp b/src/qml/jsruntime/qv4argumentsobject.cpp
index 94f418cae1..0dfdf25158 100644
--- a/src/qml/jsruntime/qv4argumentsobject.cpp
+++ b/src/qml/jsruntime/qv4argumentsobject.cpp
@@ -45,10 +45,11 @@ using namespace QV4;
DEFINE_OBJECT_VTABLE(ArgumentsObject);
-Heap::ArgumentsObject::ArgumentsObject(QV4::CallContext *context)
- : context(context->d())
- , fullyCreated(false)
+void Heap::ArgumentsObject::init(QV4::CallContext *context)
{
+ Object::init();
+ fullyCreated = false;
+ this->context = context->d();
Q_ASSERT(vtable() == QV4::ArgumentsObject::staticVTable());
ExecutionEngine *v4 = context->d()->engine;
@@ -134,7 +135,7 @@ bool ArgumentsObject::defineOwnProperty(ExecutionEngine *engine, uint index, con
ScopedCallData callData(scope, 1);
callData->thisObject = this->asReturnedValue();
callData->args[0] = desc->value;
- setter->call(callData);
+ setter->call(scope, callData);
if (attrs.isWritable()) {
setArrayAttributes(index, mapAttrs);
@@ -203,33 +204,35 @@ PropertyAttributes ArgumentsObject::queryIndexed(const Managed *m, uint index)
DEFINE_OBJECT_VTABLE(ArgumentsGetterFunction);
-ReturnedValue ArgumentsGetterFunction::call(const Managed *getter, CallData *callData)
+void ArgumentsGetterFunction::call(const Managed *getter, Scope &scope, CallData *callData)
{
ExecutionEngine *v4 = static_cast<const ArgumentsGetterFunction *>(getter)->engine();
- Scope scope(v4);
Scoped<ArgumentsGetterFunction> g(scope, static_cast<const ArgumentsGetterFunction *>(getter));
Scoped<ArgumentsObject> o(scope, callData->thisObject.as<ArgumentsObject>());
- if (!o)
- return v4->throwTypeError();
+ if (!o) {
+ scope.result = v4->throwTypeError();
+ return;
+ }
Q_ASSERT(g->index() < static_cast<unsigned>(o->context()->callData->argc));
- return o->context()->callData->args[g->index()].asReturnedValue();
+ scope.result = o->context()->callData->args[g->index()];
}
DEFINE_OBJECT_VTABLE(ArgumentsSetterFunction);
-ReturnedValue ArgumentsSetterFunction::call(const Managed *setter, CallData *callData)
+void ArgumentsSetterFunction::call(const Managed *setter, Scope &scope, CallData *callData)
{
ExecutionEngine *v4 = static_cast<const ArgumentsSetterFunction *>(setter)->engine();
- Scope scope(v4);
Scoped<ArgumentsSetterFunction> s(scope, static_cast<const ArgumentsSetterFunction *>(setter));
Scoped<ArgumentsObject> o(scope, callData->thisObject.as<ArgumentsObject>());
- if (!o)
- return v4->throwTypeError();
+ if (!o) {
+ scope.result = v4->throwTypeError();
+ return;
+ }
Q_ASSERT(s->index() < static_cast<unsigned>(o->context()->callData->argc));
o->context()->callData->args[s->index()] = callData->argc ? callData->args[0].asReturnedValue() : Encode::undefined();
- return Encode::undefined();
+ scope.result = Encode::undefined();
}
void ArgumentsObject::markObjects(Heap::Base *that, ExecutionEngine *e)
diff --git a/src/qml/jsruntime/qv4argumentsobject_p.h b/src/qml/jsruntime/qv4argumentsobject_p.h
index eeedcaf995..37a8d0a94a 100644
--- a/src/qml/jsruntime/qv4argumentsobject_p.h
+++ b/src/qml/jsruntime/qv4argumentsobject_p.h
@@ -60,12 +60,12 @@ namespace QV4 {
namespace Heap {
struct ArgumentsGetterFunction : FunctionObject {
- inline ArgumentsGetterFunction(QV4::ExecutionContext *scope, uint index);
+ inline void init(QV4::ExecutionContext *scope, uint index);
uint index;
};
struct ArgumentsSetterFunction : FunctionObject {
- inline ArgumentsSetterFunction(QV4::ExecutionContext *scope, uint index);
+ inline void init(QV4::ExecutionContext *scope, uint index);
uint index;
};
@@ -75,7 +75,7 @@ struct ArgumentsObject : Object {
CalleePropertyIndex = 1,
CallerPropertyIndex = 3
};
- ArgumentsObject(QV4::CallContext *context);
+ void init(QV4::CallContext *context);
Pointer<CallContext> context;
bool fullyCreated;
Pointer<MemberData> mappedArguments;
@@ -88,14 +88,14 @@ struct ArgumentsGetterFunction: FunctionObject
V4_OBJECT2(ArgumentsGetterFunction, FunctionObject)
uint index() const { return d()->index; }
- static ReturnedValue call(const Managed *that, CallData *d);
+ static void call(const Managed *that, Scope &scope, CallData *d);
};
-inline
-Heap::ArgumentsGetterFunction::ArgumentsGetterFunction(QV4::ExecutionContext *scope, uint index)
- : Heap::FunctionObject(scope)
- , index(index)
+inline void
+Heap::ArgumentsGetterFunction::init(QV4::ExecutionContext *scope, uint index)
{
+ Heap::FunctionObject::init(scope);
+ this->index = index;
}
struct ArgumentsSetterFunction: FunctionObject
@@ -103,14 +103,14 @@ struct ArgumentsSetterFunction: FunctionObject
V4_OBJECT2(ArgumentsSetterFunction, FunctionObject)
uint index() const { return d()->index; }
- static ReturnedValue call(const Managed *that, CallData *callData);
+ static void call(const Managed *that, Scope &scope, CallData *callData);
};
-inline
-Heap::ArgumentsSetterFunction::ArgumentsSetterFunction(QV4::ExecutionContext *scope, uint index)
- : Heap::FunctionObject(scope)
- , index(index)
+inline void
+Heap::ArgumentsSetterFunction::init(QV4::ExecutionContext *scope, uint index)
{
+ Heap::FunctionObject::init(scope);
+ this->index = index;
}
diff --git a/src/qml/jsruntime/qv4arraybuffer.cpp b/src/qml/jsruntime/qv4arraybuffer.cpp
index d170bde0e8..23075aa78c 100644
--- a/src/qml/jsruntime/qv4arraybuffer.cpp
+++ b/src/qml/jsruntime/qv4arraybuffer.cpp
@@ -46,34 +46,39 @@ using namespace QV4;
DEFINE_OBJECT_VTABLE(ArrayBufferCtor);
DEFINE_OBJECT_VTABLE(ArrayBuffer);
-Heap::ArrayBufferCtor::ArrayBufferCtor(QV4::ExecutionContext *scope)
- : Heap::FunctionObject(scope, QStringLiteral("ArrayBuffer"))
+void Heap::ArrayBufferCtor::init(QV4::ExecutionContext *scope)
{
+ Heap::FunctionObject::init(scope, QStringLiteral("ArrayBuffer"));
}
-ReturnedValue ArrayBufferCtor::construct(const Managed *m, CallData *callData)
+void ArrayBufferCtor::construct(const Managed *m, Scope &scope, CallData *callData)
{
ExecutionEngine *v4 = static_cast<const Object *>(m)->engine();
- Scope scope(v4);
ScopedValue l(scope, callData->argument(0));
double dl = l->toInteger();
- if (v4->hasException)
- return Encode::undefined();
+ if (v4->hasException) {
+ scope.result = Encode::undefined();
+ return;
+ }
uint len = (uint)qBound(0., dl, (double)UINT_MAX);
- if (len != dl)
- return v4->throwRangeError(QLatin1String("ArrayBuffer constructor: invalid length"));
+ if (len != dl) {
+ scope.result = v4->throwRangeError(QLatin1String("ArrayBuffer constructor: invalid length"));
+ return;
+ }
Scoped<ArrayBuffer> a(scope, v4->newArrayBuffer(len));
- if (scope.engine->hasException)
- return Encode::undefined();
- return a.asReturnedValue();
+ if (scope.engine->hasException) {
+ scope.result = Encode::undefined();
+ } else {
+ scope.result = a->asReturnedValue();
+ }
}
-ReturnedValue ArrayBufferCtor::call(const Managed *that, CallData *callData)
+void ArrayBufferCtor::call(const Managed *that, Scope &scope, CallData *callData)
{
- return construct(that, callData);
+ construct(that, scope, callData);
}
ReturnedValue ArrayBufferCtor::method_isView(CallContext *ctx)
@@ -89,8 +94,9 @@ ReturnedValue ArrayBufferCtor::method_isView(CallContext *ctx)
}
-Heap::ArrayBuffer::ArrayBuffer(size_t length)
+void Heap::ArrayBuffer::init(size_t length)
{
+ Object::init();
data = QTypedArrayData<char>::allocate(length + 1);
if (!data) {
data = 0;
@@ -101,16 +107,18 @@ Heap::ArrayBuffer::ArrayBuffer(size_t length)
memset(data->data(), 0, length + 1);
}
-Heap::ArrayBuffer::ArrayBuffer(const QByteArray& array)
- : data(const_cast<QByteArray&>(array).data_ptr())
+void Heap::ArrayBuffer::init(const QByteArray& array)
{
+ Object::init();
+ data = const_cast<QByteArray&>(array).data_ptr();
data->ref.ref();
}
-Heap::ArrayBuffer::~ArrayBuffer()
+void Heap::ArrayBuffer::destroy()
{
if (!data->ref.deref())
QTypedArrayData<char>::deallocate(data);
+ Object::destroy();
}
QByteArray ArrayBuffer::asByteArray() const
@@ -149,6 +157,7 @@ void ArrayBufferPrototype::init(ExecutionEngine *engine, Object *ctor)
defineDefaultProperty(engine->id_constructor(), (o = ctor));
defineAccessorProperty(QStringLiteral("byteLength"), method_get_byteLength, 0);
defineDefaultProperty(QStringLiteral("slice"), method_slice, 2);
+ defineDefaultProperty(QStringLiteral("toString"), method_toString, 0);
}
ReturnedValue ArrayBufferPrototype::method_get_byteLength(CallContext *ctx)
@@ -184,7 +193,8 @@ ReturnedValue ArrayBufferPrototype::method_slice(CallContext *ctx)
ScopedCallData callData(scope, 1);
double newLen = qMax(final - first, 0.);
callData->args[0] = QV4::Encode(newLen);
- QV4::Scoped<ArrayBuffer> newBuffer(scope, constructor->construct(callData));
+ constructor->construct(scope, callData);
+ QV4::Scoped<ArrayBuffer> newBuffer(scope, scope.result.asReturnedValue());
if (!newBuffer || newBuffer->d()->data->size < (int)newLen)
return scope.engine->throwTypeError();
@@ -192,3 +202,12 @@ ReturnedValue ArrayBufferPrototype::method_slice(CallContext *ctx)
return newBuffer.asReturnedValue();
}
+
+ReturnedValue ArrayBufferPrototype::method_toString(CallContext *ctx)
+{
+ Scope scope(ctx);
+ Scoped<ArrayBuffer> a(scope, ctx->thisObject());
+ if (!a)
+ return Encode::undefined();
+ return Encode(ctx->engine()->newString(QString::fromUtf8(a->asByteArray())));
+}
diff --git a/src/qml/jsruntime/qv4arraybuffer_p.h b/src/qml/jsruntime/qv4arraybuffer_p.h
index 0413d2f28d..bc56d1ea31 100644
--- a/src/qml/jsruntime/qv4arraybuffer_p.h
+++ b/src/qml/jsruntime/qv4arraybuffer_p.h
@@ -60,13 +60,13 @@ namespace QV4 {
namespace Heap {
struct ArrayBufferCtor : FunctionObject {
- ArrayBufferCtor(QV4::ExecutionContext *scope);
+ void init(QV4::ExecutionContext *scope);
};
struct Q_QML_PRIVATE_EXPORT ArrayBuffer : Object {
- ArrayBuffer(size_t length);
- ArrayBuffer(const QByteArray& array);
- ~ArrayBuffer();
+ void init(size_t length);
+ void init(const QByteArray& array);
+ void destroy();
QTypedArrayData<char> *data;
uint byteLength() const { return data->size; }
@@ -78,8 +78,8 @@ struct ArrayBufferCtor: FunctionObject
{
V4_OBJECT2(ArrayBufferCtor, FunctionObject)
- static ReturnedValue construct(const Managed *m, CallData *callData);
- static ReturnedValue call(const Managed *that, CallData *callData);
+ static void construct(const Managed *m, Scope &scope, CallData *callData);
+ static void call(const Managed *that, Scope &scope, CallData *callData);
static ReturnedValue method_isView(CallContext *ctx);
@@ -106,6 +106,7 @@ struct ArrayBufferPrototype: Object
static ReturnedValue method_get_byteLength(CallContext *ctx);
static ReturnedValue method_slice(CallContext *ctx);
+ static ReturnedValue method_toString(CallContext *ctx);
};
diff --git a/src/qml/jsruntime/qv4arraydata.cpp b/src/qml/jsruntime/qv4arraydata.cpp
index aa64ae1411..bfeb3d4699 100644
--- a/src/qml/jsruntime/qv4arraydata.cpp
+++ b/src/qml/jsruntime/qv4arraydata.cpp
@@ -147,13 +147,13 @@ void ArrayData::realloc(Object *o, Type newType, uint requested, bool enforceAtt
Scoped<ArrayData> newData(scope);
if (newType < Heap::ArrayData::Sparse) {
Heap::SimpleArrayData *n = scope.engine->memoryManager->allocManaged<SimpleArrayData>(size);
- new (n) Heap::SimpleArrayData;
+ n->init();
n->offset = 0;
n->len = d ? d->d()->len : 0;
newData = n;
} else {
Heap::SparseArrayData *n = scope.engine->memoryManager->allocManaged<SparseArrayData>(size);
- new (n) Heap::SparseArrayData;
+ n->init();
newData = n;
}
newData->setAlloc(alloc);
@@ -691,7 +691,7 @@ bool ArrayElementLessThan::operator()(Value v1, Value v2) const
callData->thisObject = Primitive::undefinedValue();
callData->args[0] = v1;
callData->args[1] = v2;
- result = Runtime::callValue(scope.engine, m_comparefn, callData);
+ result = scope.engine->runtime.callValue(scope.engine, m_comparefn, callData);
return result->toNumber() < 0;
}
diff --git a/src/qml/jsruntime/qv4arraydata_p.h b/src/qml/jsruntime/qv4arraydata_p.h
index 8ad4704227..24b948f01e 100644
--- a/src/qml/jsruntime/qv4arraydata_p.h
+++ b/src/qml/jsruntime/qv4arraydata_p.h
@@ -133,6 +133,7 @@ struct ArrayData : public Base {
}
};
+V4_ASSERT_IS_TRIVIAL(ArrayData)
struct SimpleArrayData : public ArrayData {
uint mappedIndex(uint index) const { return (index + offset) % alloc; }
@@ -152,9 +153,13 @@ struct SimpleArrayData : public ArrayData {
return attrs ? attrs[i] : Attr_Data;
}
};
+V4_ASSERT_IS_TRIVIAL(SimpleArrayData)
struct SparseArrayData : public ArrayData {
- inline ~SparseArrayData();
+ void destroy() {
+ delete sparse;
+ ArrayData::destroy();
+ }
uint mappedIndex(uint index) const {
SparseArrayNode *n = sparse->findNode(index);
@@ -285,11 +290,6 @@ struct Q_QML_EXPORT SparseArrayData : public ArrayData
namespace Heap {
-inline SparseArrayData::~SparseArrayData()
-{
- delete sparse;
-}
-
void ArrayData::getProperty(uint index, Property *p, PropertyAttributes *attrs)
{
Property *pd = getProperty(index);
diff --git a/src/qml/jsruntime/qv4arrayobject.cpp b/src/qml/jsruntime/qv4arrayobject.cpp
index 555eb964c1..659ede7552 100644
--- a/src/qml/jsruntime/qv4arrayobject.cpp
+++ b/src/qml/jsruntime/qv4arrayobject.cpp
@@ -49,23 +49,24 @@ using namespace QV4;
DEFINE_OBJECT_VTABLE(ArrayCtor);
-Heap::ArrayCtor::ArrayCtor(QV4::ExecutionContext *scope)
- : Heap::FunctionObject(scope, QStringLiteral("Array"))
+void Heap::ArrayCtor::init(QV4::ExecutionContext *scope)
{
+ Heap::FunctionObject::init(scope, QStringLiteral("Array"));
}
-ReturnedValue ArrayCtor::construct(const Managed *m, CallData *callData)
+void ArrayCtor::construct(const Managed *m, Scope &scope, CallData *callData)
{
ExecutionEngine *v4 = static_cast<const ArrayCtor *>(m)->engine();
- Scope scope(v4);
ScopedArrayObject a(scope, v4->newArrayObject());
uint len;
if (callData->argc == 1 && callData->args[0].isNumber()) {
bool ok;
len = callData->args[0].asArrayLength(&ok);
- if (!ok)
- return v4->throwRangeError(callData->args[0]);
+ if (!ok) {
+ scope.result = v4->throwRangeError(callData->args[0]);
+ return;
+ }
if (len < 0x1000)
a->arrayReserve(len);
@@ -76,12 +77,12 @@ ReturnedValue ArrayCtor::construct(const Managed *m, CallData *callData)
}
a->setArrayLengthUnchecked(len);
- return a.asReturnedValue();
+ scope.result = a.asReturnedValue();
}
-ReturnedValue ArrayCtor::call(const Managed *that, CallData *callData)
+void ArrayCtor::call(const Managed *that, Scope &scope, CallData *callData)
{
- return construct(that, callData);
+ construct(that, scope, callData);
}
void ArrayPrototype::init(ExecutionEngine *engine, Object *ctor)
@@ -132,7 +133,8 @@ ReturnedValue ArrayPrototype::method_toString(CallContext *ctx)
if (!!f) {
ScopedCallData d(scope, 0);
d->thisObject = ctx->thisObject();
- return f->call(d);
+ f->call(scope, d);
+ return scope.result.asReturnedValue();
}
return ObjectPrototype::method_toString(ctx);
}
@@ -707,7 +709,6 @@ ReturnedValue ArrayPrototype::method_every(CallContext *ctx)
ScopedCallData callData(scope, 3);
callData->args[2] = instance;
callData->thisObject = ctx->argument(1);
- ScopedValue r(scope);
ScopedValue v(scope);
bool ok = true;
@@ -719,8 +720,8 @@ ReturnedValue ArrayPrototype::method_every(CallContext *ctx)
callData->args[0] = v;
callData->args[1] = Primitive::fromDouble(k);
- r = callback->call(callData);
- ok = r->toBoolean();
+ callback->call(scope, callData);
+ ok = scope.result.toBoolean();
}
return Encode(ok);
}
@@ -743,7 +744,6 @@ ReturnedValue ArrayPrototype::method_some(CallContext *ctx)
callData->args[2] = instance;
ScopedValue v(scope);
- ScopedValue r(scope);
for (uint k = 0; k < len; ++k) {
bool exists;
v = instance->getIndexed(k, &exists);
@@ -752,8 +752,8 @@ ReturnedValue ArrayPrototype::method_some(CallContext *ctx)
callData->args[0] = v;
callData->args[1] = Primitive::fromDouble(k);
- r = callback->call(callData);
- if (r->toBoolean())
+ callback->call(scope, callData);
+ if (scope.result.toBoolean())
return Encode(true);
}
return Encode(false);
@@ -785,7 +785,7 @@ ReturnedValue ArrayPrototype::method_forEach(CallContext *ctx)
callData->args[0] = v;
callData->args[1] = Primitive::fromDouble(k);
- callback->call(callData);
+ callback->call(scope, callData);
}
return Encode::undefined();
}
@@ -807,7 +807,6 @@ ReturnedValue ArrayPrototype::method_map(CallContext *ctx)
a->arrayReserve(len);
a->setArrayLengthUnchecked(len);
- ScopedValue mapped(scope);
ScopedCallData callData(scope, 3);
callData->thisObject = ctx->argument(1);
callData->args[2] = instance;
@@ -821,8 +820,8 @@ ReturnedValue ArrayPrototype::method_map(CallContext *ctx)
callData->args[0] = v;
callData->args[1] = Primitive::fromDouble(k);
- mapped = callback->call(callData);
- a->arraySet(k, mapped);
+ callback->call(scope, callData);
+ a->arraySet(k, scope.result);
}
return a.asReturnedValue();
}
@@ -843,7 +842,6 @@ ReturnedValue ArrayPrototype::method_filter(CallContext *ctx)
ScopedArrayObject a(scope, ctx->d()->engine->newArrayObject());
a->arrayReserve(len);
- ScopedValue selected(scope);
ScopedCallData callData(scope, 3);
callData->thisObject = ctx->argument(1);
callData->args[2] = instance;
@@ -859,8 +857,8 @@ ReturnedValue ArrayPrototype::method_filter(CallContext *ctx)
callData->args[0] = v;
callData->args[1] = Primitive::fromDouble(k);
- selected = callback->call(callData);
- if (selected->toBoolean()) {
+ callback->call(scope, callData);
+ if (scope.result.toBoolean()) {
a->arraySet(to, v);
++to;
}
@@ -882,17 +880,16 @@ ReturnedValue ArrayPrototype::method_reduce(CallContext *ctx)
return ctx->engine()->throwTypeError();
uint k = 0;
- ScopedValue acc(scope);
ScopedValue v(scope);
if (ctx->argc() > 1) {
- acc = ctx->argument(1);
+ scope.result = ctx->argument(1);
} else {
bool kPresent = false;
while (k < len && !kPresent) {
v = instance->getIndexed(k, &kPresent);
if (kPresent)
- acc = v;
+ scope.result = v;
++k;
}
if (!kPresent)
@@ -901,21 +898,21 @@ ReturnedValue ArrayPrototype::method_reduce(CallContext *ctx)
ScopedCallData callData(scope, 4);
callData->thisObject = Primitive::undefinedValue();
- callData->args[0] = acc;
+ callData->args[0] = scope.result;
callData->args[3] = instance;
while (k < len) {
bool kPresent;
v = instance->getIndexed(k, &kPresent);
if (kPresent) {
- callData->args[0] = acc;
+ callData->args[0] = scope.result;
callData->args[1] = v;
callData->args[2] = Primitive::fromDouble(k);
- acc = callback->call(callData);
+ callback->call(scope, callData);
}
++k;
}
- return acc->asReturnedValue();
+ return scope.result.asReturnedValue();
}
ReturnedValue ArrayPrototype::method_reduceRight(CallContext *ctx)
@@ -938,16 +935,15 @@ ReturnedValue ArrayPrototype::method_reduceRight(CallContext *ctx)
}
uint k = len;
- ScopedValue acc(scope);
ScopedValue v(scope);
if (ctx->argc() > 1) {
- acc = ctx->argument(1);
+ scope.result = ctx->argument(1);
} else {
bool kPresent = false;
while (k > 0 && !kPresent) {
v = instance->getIndexed(k - 1, &kPresent);
if (kPresent)
- acc = v;
+ scope.result = v;
--k;
}
if (!kPresent)
@@ -962,13 +958,13 @@ ReturnedValue ArrayPrototype::method_reduceRight(CallContext *ctx)
bool kPresent;
v = instance->getIndexed(k - 1, &kPresent);
if (kPresent) {
- callData->args[0] = acc;
+ callData->args[0] = scope.result;
callData->args[1] = v;
callData->args[2] = Primitive::fromDouble(k - 1);
- acc = callback->call(callData);
+ callback->call(scope, callData);
}
--k;
}
- return acc->asReturnedValue();
+ return scope.result.asReturnedValue();
}
diff --git a/src/qml/jsruntime/qv4arrayobject_p.h b/src/qml/jsruntime/qv4arrayobject_p.h
index bae5f9e0da..9a05bb8681 100644
--- a/src/qml/jsruntime/qv4arrayobject_p.h
+++ b/src/qml/jsruntime/qv4arrayobject_p.h
@@ -61,7 +61,7 @@ namespace QV4 {
namespace Heap {
struct ArrayCtor : FunctionObject {
- ArrayCtor(QV4::ExecutionContext *scope);
+ void init(QV4::ExecutionContext *scope);
};
}
@@ -70,8 +70,8 @@ struct ArrayCtor: FunctionObject
{
V4_OBJECT2(ArrayCtor, FunctionObject)
- static ReturnedValue construct(const Managed *m, CallData *callData);
- static ReturnedValue call(const Managed *that, CallData *callData);
+ static void construct(const Managed *m, Scope &scope, CallData *callData);
+ static void call(const Managed *that, Scope &scope, CallData *callData);
};
struct ArrayPrototype: ArrayObject
diff --git a/src/qml/jsruntime/qv4booleanobject.cpp b/src/qml/jsruntime/qv4booleanobject.cpp
index d9da7d7754..8047993266 100644
--- a/src/qml/jsruntime/qv4booleanobject.cpp
+++ b/src/qml/jsruntime/qv4booleanobject.cpp
@@ -45,22 +45,21 @@ using namespace QV4;
DEFINE_OBJECT_VTABLE(BooleanCtor);
DEFINE_OBJECT_VTABLE(BooleanObject);
-Heap::BooleanCtor::BooleanCtor(QV4::ExecutionContext *scope)
- : Heap::FunctionObject(scope, QStringLiteral("Boolean"))
+void Heap::BooleanCtor::init(QV4::ExecutionContext *scope)
{
+ Heap::FunctionObject::init(scope, QStringLiteral("Boolean"));
}
-ReturnedValue BooleanCtor::construct(const Managed *m, CallData *callData)
+void BooleanCtor::construct(const Managed *, Scope &scope, CallData *callData)
{
- Scope scope(static_cast<const BooleanCtor *>(m)->engine());
bool n = callData->argc ? callData->args[0].toBoolean() : false;
- return Encode(scope.engine->newBooleanObject(n));
+ scope.result = Encode(scope.engine->newBooleanObject(n));
}
-ReturnedValue BooleanCtor::call(const Managed *, CallData *callData)
+void BooleanCtor::call(const Managed *, Scope &scope, CallData *callData)
{
bool value = callData->argc ? callData->args[0].toBoolean() : 0;
- return Encode(value);
+ scope.result = Encode(value);
}
void BooleanPrototype::init(ExecutionEngine *engine, Object *ctor)
diff --git a/src/qml/jsruntime/qv4booleanobject_p.h b/src/qml/jsruntime/qv4booleanobject_p.h
index eedafa6126..4c2f3c09e7 100644
--- a/src/qml/jsruntime/qv4booleanobject_p.h
+++ b/src/qml/jsruntime/qv4booleanobject_p.h
@@ -61,7 +61,7 @@ namespace QV4 {
namespace Heap {
struct BooleanCtor : FunctionObject {
- BooleanCtor(QV4::ExecutionContext *scope);
+ void init(QV4::ExecutionContext *scope);
};
}
@@ -70,8 +70,8 @@ struct BooleanCtor: FunctionObject
{
V4_OBJECT2(BooleanCtor, FunctionObject)
- static ReturnedValue construct(const Managed *, CallData *callData);
- static ReturnedValue call(const Managed *that, CallData *callData);
+ static void construct(const Managed *, Scope &scope, CallData *callData);
+ static void call(const Managed *that, Scope &scope, CallData *callData);
};
struct BooleanPrototype: BooleanObject
diff --git a/src/qml/jsruntime/qv4context.cpp b/src/qml/jsruntime/qv4context.cpp
index 97b3e26a26..390a5e7d7a 100644
--- a/src/qml/jsruntime/qv4context.cpp
+++ b/src/qml/jsruntime/qv4context.cpp
@@ -61,8 +61,9 @@ Heap::CallContext *ExecutionContext::newCallContext(const FunctionObject *functi
{
Q_ASSERT(function->function());
- Heap::CallContext *c = d()->engine->memoryManager->allocManaged<CallContext>(requiredMemoryForExecutionContect(function, callData->argc));
- new (c) Heap::CallContext(d()->engine, Heap::ExecutionContext::Type_CallContext);
+ Heap::CallContext *c = d()->engine->memoryManager->allocManaged<CallContext>(
+ requiredMemoryForExecutionContect(function, callData->argc));
+ c->init(d()->engine, Heap::ExecutionContext::Type_CallContext);
c->function = function->d();
@@ -73,6 +74,7 @@ Heap::CallContext *ExecutionContext::newCallContext(const FunctionObject *functi
c->compilationUnit = function->function()->compilationUnit;
c->lookups = c->compilationUnit->runtimeLookups;
+ c->constantTable = c->compilationUnit->constants;
c->locals = (Value *)((quintptr(c + 1) + 7) & ~7);
const CompiledData::Function *compiledFunction = function->function()->compiledFunction;
@@ -159,44 +161,35 @@ void ExecutionContext::createMutableBinding(String *name, bool deletable)
activation->__defineOwnProperty__(scope.engine, name, desc, attrs);
}
-
-Heap::GlobalContext::GlobalContext(ExecutionEngine *eng)
- : Heap::ExecutionContext(eng, Heap::ExecutionContext::Type_GlobalContext)
+void Heap::GlobalContext::init(ExecutionEngine *eng)
{
+ Heap::ExecutionContext::init(eng, Heap::ExecutionContext::Type_GlobalContext);
global = eng->globalObject->d();
}
-Heap::WithContext::WithContext(ExecutionContext *outerContext, Object *with)
- : Heap::ExecutionContext(outerContext->engine, Heap::ExecutionContext::Type_WithContext)
-{
- outer = outerContext;
- callData = outer->callData;
- lookups = outer->lookups;
- compilationUnit = outer->compilationUnit;
-
- withObject = with;
-}
-
-Heap::CatchContext::CatchContext(ExecutionContext *outerContext, String *exceptionVarName, const Value &exceptionValue)
- : Heap::ExecutionContext(outerContext->engine, Heap::ExecutionContext::Type_CatchContext)
+void Heap::CatchContext::init(ExecutionContext *outerContext, String *exceptionVarName,
+ const Value &exceptionValue)
{
+ Heap::ExecutionContext::init(outerContext->engine, Heap::ExecutionContext::Type_CatchContext);
outer = outerContext;
strictMode = outer->strictMode;
callData = outer->callData;
lookups = outer->lookups;
+ constantTable = outer->constantTable;
compilationUnit = outer->compilationUnit;
this->exceptionVarName = exceptionVarName;
this->exceptionValue = exceptionValue;
}
-Heap::QmlContext::QmlContext(QV4::ExecutionContext *outerContext, QV4::QmlContextWrapper *qml)
- : Heap::ExecutionContext(outerContext->engine(), Heap::ExecutionContext::Type_QmlContext)
+void Heap::QmlContext::init(QV4::ExecutionContext *outerContext, QV4::QmlContextWrapper *qml)
{
+ Heap::ExecutionContext::init(outerContext->engine(), Heap::ExecutionContext::Type_QmlContext);
outer = outerContext->d();
strictMode = false;
callData = outer->callData;
lookups = outer->lookups;
+ constantTable = outer->constantTable;
compilationUnit = outer->compilationUnit;
this->qml = qml->d();
diff --git a/src/qml/jsruntime/qv4context_p.h b/src/qml/jsruntime/qv4context_p.h
index 2e6773a927..0b42288ccc 100644
--- a/src/qml/jsruntime/qv4context_p.h
+++ b/src/qml/jsruntime/qv4context_p.h
@@ -101,36 +101,41 @@ struct ExecutionContext : Base {
Type_CallContext = 0x6
};
- inline ExecutionContext(ExecutionEngine *engine, ContextType t);
+ void init(ExecutionEngine *engine, ContextType t)
+ {
+ Base::init();
+
+ callData = nullptr;
+ this->engine = engine;
+ outer = nullptr;
+ lookups = nullptr;
+ constantTable = nullptr;
+ compilationUnit = nullptr;
+ type = t;
+ strictMode = false;
+ lineNumber = -1;
+ }
CallData *callData;
ExecutionEngine *engine;
Pointer<ExecutionContext> outer;
Lookup *lookups;
+ const QV4::Value *constantTable;
CompiledData::CompilationUnit *compilationUnit;
ContextType type : 8;
bool strictMode : 8;
int lineNumber;
};
-
-inline
-ExecutionContext::ExecutionContext(ExecutionEngine *engine, ContextType t)
- : engine(engine)
- , outer(0)
- , lookups(0)
- , compilationUnit(0)
- , type(t)
- , strictMode(false)
- , lineNumber(-1)
-{}
-
+V4_ASSERT_IS_TRIVIAL(ExecutionContext)
struct CallContext : ExecutionContext {
- CallContext(ExecutionEngine *engine, ContextType t = Type_SimpleCallContext)
- : ExecutionContext(engine, t)
+ static CallContext createOnStack(ExecutionEngine *v4);
+
+ void init(ExecutionEngine *engine, ContextType t = Type_SimpleCallContext)
{
+ ExecutionContext::init(engine, t);
function = 0;
locals = 0;
activation = 0;
@@ -140,27 +145,43 @@ struct CallContext : ExecutionContext {
Value *locals;
Pointer<Object> activation;
};
+V4_ASSERT_IS_TRIVIAL(CallContext)
struct GlobalContext : ExecutionContext {
- GlobalContext(ExecutionEngine *engine);
+ void init(ExecutionEngine *engine);
Pointer<Object> global;
};
+V4_ASSERT_IS_TRIVIAL(GlobalContext)
struct CatchContext : ExecutionContext {
- CatchContext(ExecutionContext *outerContext, String *exceptionVarName, const Value &exceptionValue);
+ void init(ExecutionContext *outerContext, String *exceptionVarName, const Value &exceptionValue);
Pointer<String> exceptionVarName;
Value exceptionValue;
};
+V4_ASSERT_IS_TRIVIAL(CatchContext)
struct WithContext : ExecutionContext {
- WithContext(ExecutionContext *outerContext, Object *with);
+ void init(ExecutionContext *outerContext, Object *with)
+ {
+ Heap::ExecutionContext::init(outerContext->engine, Heap::ExecutionContext::Type_WithContext);
+ outer = outerContext;
+ callData = outer->callData;
+ lookups = outer->lookups;
+ constantTable = outer->constantTable;
+ compilationUnit = outer->compilationUnit;
+
+ withObject = with;
+ }
+
Pointer<Object> withObject;
};
+V4_ASSERT_IS_TRIVIAL(WithContext)
struct QmlContextWrapper;
struct QmlContext : ExecutionContext {
- QmlContext(QV4::ExecutionContext *outerContext, QV4::QmlContextWrapper *qml);
+ void init(QV4::ExecutionContext *outerContext, QV4::QmlContextWrapper *qml);
+
Pointer<QmlContextWrapper> qml;
};
@@ -277,6 +298,16 @@ inline const WithContext *ExecutionContext::asWithContext() const
return d()->type == Heap::ExecutionContext::Type_WithContext ? static_cast<const WithContext *>(this) : 0;
}
+inline Heap::CallContext Heap::CallContext::createOnStack(ExecutionEngine *v4)
+{
+ Heap::CallContext ctxt;
+ memset(&ctxt, 0, sizeof(Heap::CallContext));
+ ctxt.mm_data = 0;
+ ctxt.setVtable(QV4::CallContext::staticVTable());
+ ctxt.init(v4);
+ return ctxt;
+}
+
/* Function *f, int argc */
#define requiredMemoryForExecutionContect(f, argc) \
((sizeof(CallContext::Data) + 7) & ~7) + sizeof(Value) * (f->varCount() + qMax((uint)argc, f->formalParameterCount())) + sizeof(CallData)
diff --git a/src/qml/jsruntime/qv4context_p_p.h b/src/qml/jsruntime/qv4context_p_p.h
index 0da9f678ed..ca8dc0b518 100644
--- a/src/qml/jsruntime/qv4context_p_p.h
+++ b/src/qml/jsruntime/qv4context_p_p.h
@@ -69,7 +69,7 @@ QObject *QmlContext::qmlScope() const
QQmlContextData *QmlContext::qmlContext() const
{
- return d()->qml->context;
+ return *d()->qml->context;
}
void QmlContext::takeContextOwnership() {
diff --git a/src/qml/jsruntime/qv4dataview.cpp b/src/qml/jsruntime/qv4dataview.cpp
index f296ffd71e..db8376272d 100644
--- a/src/qml/jsruntime/qv4dataview.cpp
+++ b/src/qml/jsruntime/qv4dataview.cpp
@@ -49,37 +49,39 @@ using namespace QV4;
DEFINE_OBJECT_VTABLE(DataViewCtor);
DEFINE_OBJECT_VTABLE(DataView);
-Heap::DataViewCtor::DataViewCtor(QV4::ExecutionContext *scope)
- : Heap::FunctionObject(scope, QStringLiteral("DataView"))
+void Heap::DataViewCtor::init(QV4::ExecutionContext *scope)
{
+ Heap::FunctionObject::init(scope, QStringLiteral("DataView"));
}
-ReturnedValue DataViewCtor::construct(const Managed *m, CallData *callData)
+void DataViewCtor::construct(const Managed *, Scope &scope, CallData *callData)
{
- Scope scope(static_cast<const Object *>(m)->engine());
Scoped<ArrayBuffer> buffer(scope, callData->argument(0));
- if (!buffer)
- return scope.engine->throwTypeError();
+ if (!buffer) {
+ scope.result = scope.engine->throwTypeError();
+ return;
+ }
double bo = callData->argc > 1 ? callData->args[1].toNumber() : 0;
uint byteOffset = (uint)bo;
uint bufferLength = buffer->d()->data->size;
double bl = callData->argc < 3 || callData->args[2].isUndefined() ? (bufferLength - bo) : callData->args[2].toNumber();
uint byteLength = (uint)bl;
- if (bo != byteOffset || bl != byteLength || byteOffset + byteLength > bufferLength)
- return scope.engine->throwRangeError(QStringLiteral("DataView: constructor arguments out of range"));
+ if (bo != byteOffset || bl != byteLength || byteOffset + byteLength > bufferLength) {
+ scope.result = scope.engine->throwRangeError(QStringLiteral("DataView: constructor arguments out of range"));
+ return;
+ }
Scoped<DataView> a(scope, scope.engine->memoryManager->allocObject<DataView>());
a->d()->buffer = buffer->d();
a->d()->byteLength = byteLength;
a->d()->byteOffset = byteOffset;
- return a.asReturnedValue();
-
+ scope.result = a.asReturnedValue();
}
-ReturnedValue DataViewCtor::call(const Managed *that, CallData *callData)
+void DataViewCtor::call(const Managed *that, Scope &scope, CallData *callData)
{
- return construct(that, callData);
+ construct(that, scope, callData);
}
diff --git a/src/qml/jsruntime/qv4dataview_p.h b/src/qml/jsruntime/qv4dataview_p.h
index 2e8e94cecd..246124394a 100644
--- a/src/qml/jsruntime/qv4dataview_p.h
+++ b/src/qml/jsruntime/qv4dataview_p.h
@@ -60,11 +60,11 @@ namespace QV4 {
namespace Heap {
struct DataViewCtor : FunctionObject {
- DataViewCtor(QV4::ExecutionContext *scope);
+ void init(QV4::ExecutionContext *scope);
};
struct DataView : Object {
- DataView() {}
+ void init() { Object::init(); }
Pointer<ArrayBuffer> buffer;
uint byteLength;
uint byteOffset;
@@ -76,8 +76,8 @@ struct DataViewCtor: FunctionObject
{
V4_OBJECT2(DataViewCtor, FunctionObject)
- static ReturnedValue construct(const Managed *m, CallData *callData);
- static ReturnedValue call(const Managed *that, CallData *callData);
+ static void construct(const Managed *m, Scope &scope, CallData *callData);
+ static void call(const Managed *that, Scope &scope, CallData *callData);
};
struct DataView : Object
diff --git a/src/qml/jsruntime/qv4dateobject.cpp b/src/qml/jsruntime/qv4dateobject.cpp
index 5397ad43c5..4f3138a452 100644
--- a/src/qml/jsruntime/qv4dateobject.cpp
+++ b/src/qml/jsruntime/qv4dateobject.cpp
@@ -466,7 +466,7 @@ static inline double ParseString(const QString &s)
if (format < Minute || format >= TimezoneHour)
error = true;
format = TimezoneHour;
- } else if (*ch == 'Z' || *ch == 0) {
+ } else if (*ch == 'Z' || ch->unicode() == 0) {
format = Done;
}
current = 0;
@@ -565,7 +565,7 @@ static inline QString ToString(double t)
{
if (std::isnan(t))
return QStringLiteral("Invalid Date");
- QString str = ToDateTime(t, Qt::LocalTime).toString() + QStringLiteral(" GMT");
+ QString str = ToDateTime(t, Qt::LocalTime).toString() + QLatin1String(" GMT");
double tzoffset = LocalTZA + DaylightSavingTA(t);
if (tzoffset) {
int hours = static_cast<int>(::fabs(tzoffset) / 1000 / 60 / 60);
@@ -634,11 +634,35 @@ static double getLocalTZA()
DEFINE_OBJECT_VTABLE(DateObject);
-Heap::DateObject::DateObject(const QDateTime &date)
+void Heap::DateObject::init(const QDateTime &date)
{
+ Object::init();
this->date = date.isValid() ? date.toMSecsSinceEpoch() : qt_qnan();
}
+void Heap::DateObject::init(const QTime &time)
+{
+ Object::init();
+ if (!time.isValid()) {
+ date = qt_qnan();
+ return;
+ }
+
+ /* All programmers know that stuff starts at 0. Whatever that may mean in this context (and
+ * local timezone), it's before the epoch, so there is defenitely no DST problem. Specifically:
+ * you can't start with a date before the epoch, add some[*] hours, and end up with a date
+ * after. That's a problem for timezones where new year happens during DST, like
+ * Australia/Hobart, because we have to ignore DST before the epoch (but honor it after the
+ * epoch).
+ *
+ * [*] Well, when "some" is in the range 0-24. If you add something like 1M then this might
+ * still happen.
+ */
+ static const double d = MakeDay(0, 0, 0);
+ double t = MakeTime(time.hour(), time.minute(), time.second(), time.msec());
+ date = TimeClip(UTC(MakeDate(d, t)));
+}
+
QDateTime DateObject::toQDateTime() const
{
return ToDateTime(date(), Qt::LocalTime);
@@ -646,14 +670,13 @@ QDateTime DateObject::toQDateTime() const
DEFINE_OBJECT_VTABLE(DateCtor);
-Heap::DateCtor::DateCtor(QV4::ExecutionContext *scope)
- : Heap::FunctionObject(scope, QStringLiteral("Date"))
+void Heap::DateCtor::init(QV4::ExecutionContext *scope)
{
+ Heap::FunctionObject::init(scope, QStringLiteral("Date"));
}
-ReturnedValue DateCtor::construct(const Managed *m, CallData *callData)
+void DateCtor::construct(const Managed *, Scope &scope, CallData *callData)
{
- Scope scope(static_cast<const DateCtor *>(m)->engine());
double t = 0;
if (callData->argc == 0)
@@ -687,13 +710,13 @@ ReturnedValue DateCtor::construct(const Managed *m, CallData *callData)
t = TimeClip(UTC(t));
}
- return Encode(scope.engine->newDateObject(Primitive::fromDouble(t)));
+ scope.result = Encode(scope.engine->newDateObject(Primitive::fromDouble(t)));
}
-ReturnedValue DateCtor::call(const Managed *m, CallData *)
+void DateCtor::call(const Managed *m, Scope &scope, CallData *)
{
double t = currentTime();
- return static_cast<const DateCtor *>(m)->engine()->newString(ToString(t))->asReturnedValue();
+ scope.result = static_cast<const DateCtor *>(m)->engine()->newString(ToString(t));
}
void DatePrototype::init(ExecutionEngine *engine, Object *ctor)
@@ -1311,7 +1334,8 @@ ReturnedValue DatePrototype::method_toJSON(CallContext *ctx)
ScopedCallData callData(scope);
callData->thisObject = ctx->thisObject();
- return toIso->call(callData);
+ toIso->call(scope, callData);
+ return scope.result.asReturnedValue();
}
void DatePrototype::timezoneUpdated()
diff --git a/src/qml/jsruntime/qv4dateobject_p.h b/src/qml/jsruntime/qv4dateobject_p.h
index e3615d76a7..2d0648396e 100644
--- a/src/qml/jsruntime/qv4dateobject_p.h
+++ b/src/qml/jsruntime/qv4dateobject_p.h
@@ -63,22 +63,26 @@ namespace QV4 {
namespace Heap {
struct DateObject : Object {
- DateObject()
+ void init()
{
+ Object::init();
date = qt_qnan();
}
- DateObject(const Value &date)
+ void init(const Value &date)
{
+ Object::init();
this->date = date.toNumber();
}
- DateObject(const QDateTime &date);
+ void init(const QDateTime &date);
+ void init(const QTime &time);
+
double date;
};
struct DateCtor : FunctionObject {
- DateCtor(QV4::ExecutionContext *scope);
+ void init(QV4::ExecutionContext *scope);
};
}
@@ -104,8 +108,8 @@ struct DateCtor: FunctionObject
{
V4_OBJECT2(DateCtor, FunctionObject)
- static ReturnedValue construct(const Managed *, CallData *callData);
- static ReturnedValue call(const Managed *that, CallData *);
+ static void construct(const Managed *, Scope &scope, CallData *callData);
+ static void call(const Managed *that, Scope &scope, CallData *);
};
struct DatePrototype: DateObject
diff --git a/src/qml/jsruntime/qv4debugging_p.h b/src/qml/jsruntime/qv4debugging_p.h
index 9dca7e9979..3b589a41f1 100644
--- a/src/qml/jsruntime/qv4debugging_p.h
+++ b/src/qml/jsruntime/qv4debugging_p.h
@@ -59,6 +59,19 @@ QT_BEGIN_NAMESPACE
namespace QV4 {
namespace Debugging {
+#ifdef QT_NO_QML_DEBUGGER
+
+struct Debugger
+{
+ bool pauseAtNextOpportunity() const { return false; }
+ void maybeBreakAtInstruction() {}
+ void enteringFunction() {}
+ void leavingFunction(const ReturnedValue &) {}
+ void aboutToThrow() {}
+};
+
+#else
+
class Q_QML_EXPORT Debugger : public QObject
{
Q_OBJECT
@@ -72,6 +85,8 @@ public:
virtual void aboutToThrow() = 0;
};
+#endif // QT_NO_QML_DEBUGGING
+
} // namespace Debugging
} // namespace QV4
diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp
index 26f473a7aa..a9284f2e69 100644
--- a/src/qml/jsruntime/qv4engine.cpp
+++ b/src/qml/jsruntime/qv4engine.cpp
@@ -86,7 +86,9 @@
#include "qv4isel_masm_p.h"
#endif // V4_ENABLE_JIT
+#if QT_CONFIG(qml_interpreter)
#include "qv4isel_moth_p.h"
+#endif
#if USE(PTHREADS)
# include <pthread.h>
@@ -136,8 +138,6 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory)
, currentContext(0)
, bumperPointerAllocator(new WTF::BumpPointerAllocator)
, jsStack(new WTF::PageAllocation)
- , debugger(0)
- , profiler(0)
, globalCode(0)
, v8Engine(0)
, argumentsAccessors(0)
@@ -145,6 +145,10 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory)
, m_engineId(engineSerial.fetchAndAddOrdered(1))
, regExpCache(0)
, m_multiplyWrappedQObjects(0)
+#ifndef QT_NO_QML_DEBUGGER
+ , m_debugger(0)
+ , m_profiler(0)
+#endif
{
if (maxCallDepth == -1) {
bool ok = false;
@@ -158,6 +162,7 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory)
MemoryManager::GCBlocker gcBlocker(memoryManager);
if (!factory) {
+#if QT_CONFIG(qml_interpreter)
bool jitDisabled = true;
#ifdef V4_ENABLE_JIT
@@ -178,6 +183,9 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory)
"very slow. Visit https://wiki.qt.io/V4 to learn about possible "
"solutions for your platform.");
}
+#else
+ factory = new JIT::ISelFactory;
+#endif
}
iselFactory.reset(factory);
@@ -442,10 +450,12 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory)
ExecutionEngine::~ExecutionEngine()
{
- delete debugger;
- debugger = 0;
- delete profiler;
- profiler = 0;
+#ifndef QT_NO_QML_DEBUGGER
+ delete m_debugger;
+ m_debugger = 0;
+ delete m_profiler;
+ m_profiler = 0;
+#endif
delete m_multiplyWrappedQObjects;
m_multiplyWrappedQObjects = 0;
delete identifierTable;
@@ -467,23 +477,26 @@ ExecutionEngine::~ExecutionEngine()
delete [] argumentsAccessors;
}
-void ExecutionEngine::setDebugger(Debugging::Debugger *debugger_)
+#ifndef QT_NO_QML_DEBUGGER
+void ExecutionEngine::setDebugger(Debugging::Debugger *debugger)
{
- Q_ASSERT(!debugger);
- debugger = debugger_;
+ Q_ASSERT(!m_debugger);
+ m_debugger = debugger;
}
-void ExecutionEngine::enableProfiler()
+void ExecutionEngine::setProfiler(Profiling::Profiler *profiler)
{
- Q_ASSERT(!profiler);
- profiler = new QV4::Profiling::Profiler(this);
+ Q_ASSERT(!m_profiler);
+ m_profiler = profiler;
}
+#endif // QT_NO_QML_DEBUGGER
void ExecutionEngine::initRootContext()
{
Scope scope(this);
- Scoped<GlobalContext> r(scope, memoryManager->allocManaged<GlobalContext>(sizeof(GlobalContext::Data) + sizeof(CallData)));
- new (r->d()) GlobalContext::Data(this);
+ Scoped<GlobalContext> r(scope, memoryManager->allocManaged<GlobalContext>(
+ sizeof(GlobalContext::Data) + sizeof(CallData)));
+ r->d_unchecked()->init(this);
r->d()->callData = reinterpret_cast<CallData *>(r->d() + 1);
r->d()->callData->tag = QV4::Value::Integer_Type_Internal;
r->d()->callData->argc = 0;
@@ -566,7 +579,7 @@ Heap::ArrayObject *ExecutionEngine::newArrayObject(const Value *values, int leng
if (length) {
size_t size = sizeof(Heap::ArrayData) + (length-1)*sizeof(Value);
Heap::SimpleArrayData *d = scope.engine->memoryManager->allocManaged<SimpleArrayData>(size);
- new (d) Heap::SimpleArrayData;
+ d->init();
d->alloc = length;
d->type = Heap::ArrayData::Simple;
d->offset = 0;
@@ -615,6 +628,13 @@ Heap::DateObject *ExecutionEngine::newDateObject(const QDateTime &dt)
return object->d();
}
+Heap::DateObject *ExecutionEngine::newDateObjectFromTime(const QTime &t)
+{
+ Scope scope(this);
+ Scoped<DateObject> object(scope, memoryManager->allocObject<DateObject>(t));
+ return object->d();
+}
+
Heap::RegExpObject *ExecutionEngine::newRegExpObject(const QString &pattern, int flags)
{
bool global = (flags & IR::RegExp::RegExp_Global);
@@ -730,7 +750,7 @@ QQmlContextData *ExecutionEngine::callingQmlContext() const
if (!ctx)
return 0;
- return ctx->qml->context.contextData();
+ return ctx->qml->context->contextData();
}
QVector<StackFrame> ExecutionEngine::stackTrace(int frameLimit) const
@@ -906,12 +926,12 @@ ReturnedValue ExecutionEngine::throwError(const Value &value)
QV4::Scope scope(this);
QV4::Scoped<ErrorObject> error(scope, value);
if (!!error)
- exceptionStackTrace = error->d()->stackTrace;
+ exceptionStackTrace = *error->d()->stackTrace;
else
exceptionStackTrace = stackTrace();
- if (debugger)
- debugger->aboutToThrow();
+ if (QV4::Debugging::Debugger *debug = debugger())
+ debug->aboutToThrow();
return Encode::undefined();
}
@@ -969,7 +989,7 @@ ReturnedValue ExecutionEngine::throwReferenceError(const Value &value)
{
Scope scope(this);
ScopedString s(scope, value.toString(this));
- QString msg = s->toQString() + QStringLiteral(" is not defined");
+ QString msg = s->toQString() + QLatin1String(" is not defined");
ScopedObject error(scope, newReferenceErrorObject(msg));
return throwError(error);
}
@@ -993,7 +1013,7 @@ ReturnedValue ExecutionEngine::throwRangeError(const Value &value)
{
Scope scope(this);
ScopedString s(scope, value.toString(this));
- QString msg = s->toQString() + QStringLiteral(" out of range");
+ QString msg = s->toQString() + QLatin1String(" out of range");
ScopedObject error(scope, newRangeErrorObject(msg));
return throwError(error);
}
@@ -1065,7 +1085,7 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, int
QV4::Scope scope(e);
if (const QV4::VariantObject *v = value.as<QV4::VariantObject>())
- return v->d()->data;
+ return v->d()->data();
if (typeHint == QVariant::Bool)
return QVariant(value.toBoolean());
@@ -1124,7 +1144,7 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, int
if (value.isUndefined())
return QVariant();
if (value.isNull())
- return QVariant(QMetaType::VoidStar, (void *)0);
+ return QVariant::fromValue(nullptr);
if (value.isBoolean())
return value.booleanValue();
if (value.isInteger())
@@ -1139,10 +1159,10 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, int
return str;
}
if (const QV4::QQmlLocaleData *ld = value.as<QV4::QQmlLocaleData>())
- return ld->d()->locale;
+ return *ld->d()->locale;
if (const QV4::DateObject *d = value.as<DateObject>())
return d->toQDateTime();
- if (const QV4::ArrayBuffer *d = value.as<ArrayBuffer>())
+ if (const ArrayBuffer *d = value.as<ArrayBuffer>())
return d->asByteArray();
// NOTE: since we convert QTime to JS Date, round trip will change the variant type (to QDateTime)!
@@ -1254,6 +1274,7 @@ QV4::ReturnedValue QV4::ExecutionEngine::fromVariant(const QVariant &variant)
case QMetaType::UnknownType:
case QMetaType::Void:
return QV4::Encode::undefined();
+ case QMetaType::Nullptr:
case QMetaType::VoidStar:
return QV4::Encode::null();
case QMetaType::Bool:
@@ -1270,6 +1291,8 @@ QV4::ReturnedValue QV4::ExecutionEngine::fromVariant(const QVariant &variant)
return QV4::Encode(*reinterpret_cast<const double*>(ptr));
case QMetaType::QString:
return newString(*reinterpret_cast<const QString*>(ptr))->asReturnedValue();
+ case QMetaType::QByteArray:
+ return newArrayBuffer(*reinterpret_cast<const QByteArray*>(ptr))->asReturnedValue();
case QMetaType::Float:
return QV4::Encode(*reinterpret_cast<const float*>(ptr));
case QMetaType::Short:
@@ -1287,7 +1310,7 @@ QV4::ReturnedValue QV4::ExecutionEngine::fromVariant(const QVariant &variant)
case QMetaType::QDate:
return QV4::Encode(newDateObject(QDateTime(*reinterpret_cast<const QDate *>(ptr))));
case QMetaType::QTime:
- return QV4::Encode(newDateObject(QDateTime(QDate(1970,1,1), *reinterpret_cast<const QTime *>(ptr))));
+ return QV4::Encode(newDateObjectFromTime(*reinterpret_cast<const QTime *>(ptr)));
case QMetaType::QRegExp:
return QV4::Encode(newRegExpObject(*reinterpret_cast<const QRegExp *>(ptr)));
case QMetaType::QObjectStar:
@@ -1423,6 +1446,7 @@ QV4::ReturnedValue ExecutionEngine::metaTypeToJS(int type, const void *data)
case QMetaType::UnknownType:
case QMetaType::Void:
return QV4::Encode::undefined();
+ case QMetaType::Nullptr:
case QMetaType::VoidStar:
return QV4::Encode::null();
case QMetaType::Bool:
@@ -1446,6 +1470,8 @@ QV4::ReturnedValue ExecutionEngine::metaTypeToJS(int type, const void *data)
return QV4::Encode(*reinterpret_cast<const double*>(data));
case QMetaType::QString:
return newString(*reinterpret_cast<const QString*>(data))->asReturnedValue();
+ case QMetaType::QByteArray:
+ return newArrayBuffer(*reinterpret_cast<const QByteArray*>(data))->asReturnedValue();
case QMetaType::Float:
return QV4::Encode(*reinterpret_cast<const float*>(data));
case QMetaType::Short:
@@ -1507,6 +1533,11 @@ void ExecutionEngine::assertObjectBelongsToEngine(const Heap::Base &baseObject)
Q_UNUSED(baseObject);
}
+void ExecutionEngine::failStackLimitCheck(Scope &scope)
+{
+ scope.result = throwRangeError(QStringLiteral("Maximum call stack size exceeded."));
+}
+
// Converts a JS value to a meta-type.
// data must point to a place that can store a value of the given type.
// Returns true if conversion succeeded, false otherwise.
@@ -1538,6 +1569,12 @@ bool ExecutionEngine::metaTypeFromJS(const Value *value, int type, void *data)
else
*reinterpret_cast<QString*>(data) = value->toQString();
return true;
+ case QMetaType::QByteArray:
+ if (const ArrayBuffer *ab = value->as<ArrayBuffer>())
+ *reinterpret_cast<QByteArray*>(data) = ab->asByteArray();
+ else
+ *reinterpret_cast<QByteArray*>(data) = QByteArray();
+ return true;
case QMetaType::Float:
*reinterpret_cast<float*>(data) = value->toNumber();
return true;
@@ -1662,7 +1699,7 @@ bool ExecutionEngine::metaTypeFromJS(const Value *value, int type, void *data)
return true;
if (value->as<QV4::VariantObject>() && name.endsWith('*')) {
int valueType = QMetaType::type(name.left(name.size()-1));
- QVariant &var = value->as<QV4::VariantObject>()->d()->data;
+ QVariant &var = value->as<QV4::VariantObject>()->d()->data();
if (valueType == var.userType()) {
// We have T t, T* is requested, so return &t.
*reinterpret_cast<void* *>(data) = var.data();
@@ -1674,7 +1711,7 @@ bool ExecutionEngine::metaTypeFromJS(const Value *value, int type, void *data)
while (proto) {
bool canCast = false;
if (QV4::VariantObject *vo = proto->as<QV4::VariantObject>()) {
- const QVariant &v = vo->d()->data;
+ const QVariant &v = vo->d()->data();
canCast = (type == v.userType()) || (valueType && (valueType == v.userType()));
}
else if (proto->as<QV4::QObjectWrapper>()) {
@@ -1729,7 +1766,7 @@ static QObject *qtObjectFromJS(QV4::ExecutionEngine *engine, const Value &value)
QV4::Scoped<QV4::VariantObject> v(scope, value);
if (v) {
- QVariant variant = v->d()->data;
+ QVariant variant = v->d()->data();
int type = variant.userType();
if (type == QMetaType::QObjectStar)
return *reinterpret_cast<QObject* const *>(variant.constData());
diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h
index aeb2533d35..843a6f4d94 100644
--- a/src/qml/jsruntime/qv4engine_p.h
+++ b/src/qml/jsruntime/qv4engine_p.h
@@ -54,7 +54,7 @@
#include "private/qv4isel_p.h"
#include "qv4managed_p.h"
#include "qv4context_p.h"
-#include "qv4internalclass_p.h"
+#include "qv4runtimeapi_p.h"
#include <private/qintrusivelist_p.h>
#ifndef V4_BOOTSTRAP
@@ -85,6 +85,9 @@ namespace CompiledData {
struct CompilationUnit;
}
+struct InternalClass;
+struct InternalClassPool;
+
struct Q_QML_EXPORT ExecutionEngine
{
private:
@@ -109,6 +112,8 @@ public:
Value *jsStackLimit;
+ Runtime runtime;
+
WTF::BumpPointerAllocator *bumperPointerAllocator; // Used by Yarr Regex engine.
enum { JSStackLimit = 4*1024*1024 };
@@ -132,9 +137,6 @@ public:
IdentifierTable *identifierTable;
- QV4::Debugging::Debugger *debugger;
- QV4::Profiling::Profiler *profiler;
-
Object *globalObject;
Function *globalCode;
@@ -377,8 +379,19 @@ public:
ExecutionEngine(EvalISelFactory *iselFactory = 0);
~ExecutionEngine();
+#ifdef QT_NO_QML_DEBUGGER
+ QV4::Debugging::Debugger *debugger() const { return nullptr; }
+ QV4::Profiling::Profiler *profiler() const { return nullptr; }
+
+ void setDebugger(Debugging::Debugger *) {}
+ void setProfiler(Profiling::Profiler *) {}
+#else
+ QV4::Debugging::Debugger *debugger() const { return m_debugger; }
+ QV4::Profiling::Profiler *profiler() const { return m_profiler; }
+
void setDebugger(Debugging::Debugger *debugger);
- void enableProfiler();
+ void setProfiler(Profiling::Profiler *profiler);
+#endif // QT_NO_QML_DEBUGGER
ExecutionContext *pushGlobalContext();
void pushContext(Heap::ExecutionContext *context);
@@ -406,6 +419,7 @@ public:
Heap::DateObject *newDateObject(const Value &value);
Heap::DateObject *newDateObject(const QDateTime &dt);
+ Heap::DateObject *newDateObjectFromTime(const QTime &t);
Heap::RegExpObject *newRegExpObject(const QString &pattern, int flags);
Heap::RegExpObject *newRegExpObject(RegExp *re, bool global);
@@ -475,9 +489,28 @@ public:
void assertObjectBelongsToEngine(const Heap::Base &baseObject);
- bool checkStackLimits(ReturnedValue &exception);
+ bool checkStackLimits(Scope &scope);
+
+private:
+ void failStackLimitCheck(Scope &scope);
+
+#ifndef QT_NO_QML_DEBUGGER
+ QV4::Debugging::Debugger *m_debugger;
+ QV4::Profiling::Profiler *m_profiler;
+#endif
};
+// This is a trick to tell the code generators that functions taking a NoThrowContext won't
+// throw exceptions and therefore don't need a check after the call.
+#ifndef V4_BOOTSTRAP
+struct NoThrowEngine : public ExecutionEngine
+{
+};
+#else
+struct NoThrowEngine;
+#endif
+
+
inline void ExecutionEngine::pushContext(Heap::ExecutionContext *context)
{
Q_ASSERT(currentContext && context);
@@ -557,7 +590,7 @@ inline void Value::mark(ExecutionEngine *e)
o->mark(e);
}
-#define CHECK_STACK_LIMITS(v4) { ReturnedValue e; if ((v4)->checkStackLimits(e)) return e; } \
+#define CHECK_STACK_LIMITS(v4, scope) if ((v4)->checkStackLimits(scope)) return; \
ExecutionEngineCallDepthRecorder _executionEngineCallDepthRecorder(v4);
struct ExecutionEngineCallDepthRecorder
@@ -568,10 +601,10 @@ struct ExecutionEngineCallDepthRecorder
~ExecutionEngineCallDepthRecorder() { --ee->callDepth; }
};
-inline bool ExecutionEngine::checkStackLimits(ReturnedValue &exception)
+inline bool ExecutionEngine::checkStackLimits(Scope &scope)
{
if (Q_UNLIKELY((jsStackTop > jsStackLimit) || (callDepth >= maxCallDepth))) {
- exception = throwRangeError(QStringLiteral("Maximum call stack size exceeded."));
+ failStackLimitCheck(scope);
return true;
}
diff --git a/src/qml/jsruntime/qv4errorobject.cpp b/src/qml/jsruntime/qv4errorobject.cpp
index 9f1e6b613b..597ded6ae1 100644
--- a/src/qml/jsruntime/qv4errorobject.cpp
+++ b/src/qml/jsruntime/qv4errorobject.cpp
@@ -67,8 +67,11 @@
using namespace QV4;
-Heap::ErrorObject::ErrorObject()
+void Heap::ErrorObject::init()
{
+ Object::init();
+ stackTrace = nullptr;
+
Scope scope(internalClass->engine);
Scoped<QV4::ErrorObject> e(scope, this);
@@ -81,8 +84,9 @@ Heap::ErrorObject::ErrorObject()
*propertyData(QV4::ErrorObject::Index_LineNumber) = Encode::undefined();
}
-Heap::ErrorObject::ErrorObject(const Value &message, ErrorType t)
+void Heap::ErrorObject::init(const Value &message, ErrorType t)
{
+ Object::init();
errorType = t;
Scope scope(internalClass->engine);
@@ -91,18 +95,19 @@ Heap::ErrorObject::ErrorObject(const Value &message, ErrorType t)
*propertyData(QV4::ErrorObject::Index_Stack) = scope.engine->getStackFunction();
*propertyData(QV4::ErrorObject::Index_Stack + QV4::Object::SetterOffset) = Encode::undefined();
- e->d()->stackTrace = scope.engine->stackTrace();
- if (!e->d()->stackTrace.isEmpty()) {
- *propertyData(QV4::ErrorObject::Index_FileName) = scope.engine->newString(e->d()->stackTrace.at(0).source);
- *propertyData(QV4::ErrorObject::Index_LineNumber) = Primitive::fromInt32(e->d()->stackTrace.at(0).line);
+ e->d()->stackTrace = new StackTrace(scope.engine->stackTrace());
+ if (!e->d()->stackTrace->isEmpty()) {
+ *propertyData(QV4::ErrorObject::Index_FileName) = scope.engine->newString(e->d()->stackTrace->at(0).source);
+ *propertyData(QV4::ErrorObject::Index_LineNumber) = Primitive::fromInt32(e->d()->stackTrace->at(0).line);
}
if (!message.isUndefined())
*propertyData(QV4::ErrorObject::Index_Message) = message;
}
-Heap::ErrorObject::ErrorObject(const Value &message, const QString &fileName, int line, int column, ErrorObject::ErrorType t)
+void Heap::ErrorObject::init(const Value &message, const QString &fileName, int line, int column, ErrorObject::ErrorType t)
{
+ Object::init();
errorType = t;
Scope scope(internalClass->engine);
@@ -111,16 +116,16 @@ Heap::ErrorObject::ErrorObject(const Value &message, const QString &fileName, in
*propertyData(QV4::ErrorObject::Index_Stack) = scope.engine->getStackFunction();
*propertyData(QV4::ErrorObject::Index_Stack + QV4::Object::SetterOffset) = Encode::undefined();
- e->d()->stackTrace = scope.engine->stackTrace();
+ e->d()->stackTrace = new StackTrace(scope.engine->stackTrace());
StackFrame frame;
frame.source = fileName;
frame.line = line;
frame.column = column;
- e->d()->stackTrace.prepend(frame);
+ e->d()->stackTrace->prepend(frame);
- if (!e->d()->stackTrace.isEmpty()) {
- *propertyData(QV4::ErrorObject::Index_FileName) = scope.engine->newString(e->d()->stackTrace.at(0).source);
- *propertyData(QV4::ErrorObject::Index_LineNumber) = Primitive::fromInt32(e->d()->stackTrace.at(0).line);
+ if (!e->d()->stackTrace->isEmpty()) {
+ *propertyData(QV4::ErrorObject::Index_FileName) = scope.engine->newString(e->d()->stackTrace->at(0).source);
+ *propertyData(QV4::ErrorObject::Index_LineNumber) = Primitive::fromInt32(e->d()->stackTrace->at(0).line);
}
if (!message.isUndefined())
@@ -156,17 +161,13 @@ ReturnedValue ErrorObject::method_get_stack(CallContext *ctx)
return ctx->engine()->throwTypeError();
if (!This->d()->stack) {
QString trace;
- for (int i = 0; i < This->d()->stackTrace.count(); ++i) {
+ for (int i = 0; i < This->d()->stackTrace->count(); ++i) {
if (i > 0)
trace += QLatin1Char('\n');
- const StackFrame &frame = This->d()->stackTrace[i];
- trace += frame.function;
- trace += QLatin1Char('@');
- trace += frame.source;
- if (frame.line >= 0) {
- trace += QLatin1Char(':');
- trace += QString::number(frame.line);
- }
+ const StackFrame &frame = This->d()->stackTrace->at(i);
+ trace += frame.function + QLatin1Char('@') + frame.source;
+ if (frame.line >= 0)
+ trace += QLatin1Char(':') + QString::number(frame.line);
}
This->d()->stack = ctx->d()->engine->newString(trace);
}
@@ -185,44 +186,44 @@ DEFINE_OBJECT_VTABLE(ErrorObject);
DEFINE_OBJECT_VTABLE(SyntaxErrorObject);
-Heap::SyntaxErrorObject::SyntaxErrorObject(const Value &msg)
- : Heap::ErrorObject(msg, SyntaxError)
+void Heap::SyntaxErrorObject::init(const Value &msg)
{
+ Heap::ErrorObject::init(msg, SyntaxError);
}
-Heap::SyntaxErrorObject::SyntaxErrorObject(const Value &msg, const QString &fileName, int lineNumber, int columnNumber)
- : Heap::ErrorObject(msg, fileName, lineNumber, columnNumber, SyntaxError)
+void Heap::SyntaxErrorObject::init(const Value &msg, const QString &fileName, int lineNumber, int columnNumber)
{
+ Heap::ErrorObject::init(msg, fileName, lineNumber, columnNumber, SyntaxError);
}
-Heap::EvalErrorObject::EvalErrorObject(const Value &message)
- : Heap::ErrorObject(message, EvalError)
+void Heap::EvalErrorObject::init(const Value &message)
{
+ Heap::ErrorObject::init(message, EvalError);
}
-Heap::RangeErrorObject::RangeErrorObject(const Value &message)
- : Heap::ErrorObject(message, RangeError)
+void Heap::RangeErrorObject::init(const Value &message)
{
+ Heap::ErrorObject::init(message, RangeError);
}
-Heap::ReferenceErrorObject::ReferenceErrorObject(const Value &message)
- : Heap::ErrorObject(message, ReferenceError)
+void Heap::ReferenceErrorObject::init(const Value &message)
{
+ Heap::ErrorObject::init(message, ReferenceError);
}
-Heap::ReferenceErrorObject::ReferenceErrorObject(const Value &msg, const QString &fileName, int lineNumber, int columnNumber)
- : Heap::ErrorObject(msg, fileName, lineNumber, columnNumber, ReferenceError)
+void Heap::ReferenceErrorObject::init(const Value &msg, const QString &fileName, int lineNumber, int columnNumber)
{
+ Heap::ErrorObject::init(msg, fileName, lineNumber, columnNumber, ReferenceError);
}
-Heap::TypeErrorObject::TypeErrorObject(const Value &message)
- : Heap::ErrorObject(message, TypeError)
+void Heap::TypeErrorObject::init(const Value &message)
{
+ Heap::ErrorObject::init(message, TypeError);
}
-Heap::URIErrorObject::URIErrorObject(const Value &message)
- : Heap::ErrorObject(message, URIError)
+void Heap::URIErrorObject::init(const Value &message)
{
+ Heap::ErrorObject::init(message, URIError);
}
DEFINE_OBJECT_VTABLE(ErrorCtor);
@@ -233,98 +234,91 @@ DEFINE_OBJECT_VTABLE(SyntaxErrorCtor);
DEFINE_OBJECT_VTABLE(TypeErrorCtor);
DEFINE_OBJECT_VTABLE(URIErrorCtor);
-Heap::ErrorCtor::ErrorCtor(QV4::ExecutionContext *scope)
- : Heap::FunctionObject(scope, QStringLiteral("Error"))
+void Heap::ErrorCtor::init(QV4::ExecutionContext *scope)
{
+ Heap::FunctionObject::init(scope, QStringLiteral("Error"));
}
-Heap::ErrorCtor::ErrorCtor(QV4::ExecutionContext *scope, const QString &name)
- : Heap::FunctionObject(scope, name)
+void Heap::ErrorCtor::init(QV4::ExecutionContext *scope, const QString &name)
{
+ Heap::FunctionObject::init(scope, name);
}
-ReturnedValue ErrorCtor::construct(const Managed *m, CallData *callData)
+void ErrorCtor::construct(const Managed *, Scope &scope, CallData *callData)
{
- Scope scope(static_cast<const ErrorCtor *>(m)->engine());
ScopedValue v(scope, callData->argument(0));
- return ErrorObject::create<ErrorObject>(scope.engine, v)->asReturnedValue();
+ scope.result = ErrorObject::create<ErrorObject>(scope.engine, v);
}
-ReturnedValue ErrorCtor::call(const Managed *that, CallData *callData)
+void ErrorCtor::call(const Managed *that, Scope &scope, CallData *callData)
{
- return static_cast<const Object *>(that)->construct(callData);
+ static_cast<const Object *>(that)->construct(scope, callData);
}
-Heap::EvalErrorCtor::EvalErrorCtor(QV4::ExecutionContext *scope)
- : Heap::ErrorCtor(scope, QStringLiteral("EvalError"))
+void Heap::EvalErrorCtor::init(QV4::ExecutionContext *scope)
{
+ Heap::ErrorCtor::init(scope, QStringLiteral("EvalError"));
}
-ReturnedValue EvalErrorCtor::construct(const Managed *m, CallData *callData)
+void EvalErrorCtor::construct(const Managed *, Scope &scope, CallData *callData)
{
- Scope scope(static_cast<const EvalErrorCtor *>(m)->engine());
ScopedValue v(scope, callData->argument(0));
- return ErrorObject::create<EvalErrorObject>(scope.engine, v)->asReturnedValue();
+ scope.result = ErrorObject::create<EvalErrorObject>(scope.engine, v);
}
-Heap::RangeErrorCtor::RangeErrorCtor(QV4::ExecutionContext *scope)
- : Heap::ErrorCtor(scope, QStringLiteral("RangeError"))
+void Heap::RangeErrorCtor::init(QV4::ExecutionContext *scope)
{
+ Heap::ErrorCtor::init(scope, QStringLiteral("RangeError"));
}
-ReturnedValue RangeErrorCtor::construct(const Managed *m, CallData *callData)
+void RangeErrorCtor::construct(const Managed *, Scope &scope, CallData *callData)
{
- Scope scope(static_cast<const RangeErrorCtor *>(m)->engine());
ScopedValue v(scope, callData->argument(0));
- return ErrorObject::create<RangeErrorObject>(scope.engine, v)->asReturnedValue();
+ scope.result = ErrorObject::create<RangeErrorObject>(scope.engine, v);
}
-Heap::ReferenceErrorCtor::ReferenceErrorCtor(QV4::ExecutionContext *scope)
- : Heap::ErrorCtor(scope, QStringLiteral("ReferenceError"))
+void Heap::ReferenceErrorCtor::init(QV4::ExecutionContext *scope)
{
+ Heap::ErrorCtor::init(scope, QStringLiteral("ReferenceError"));
}
-ReturnedValue ReferenceErrorCtor::construct(const Managed *m, CallData *callData)
+void ReferenceErrorCtor::construct(const Managed *, Scope &scope, CallData *callData)
{
- Scope scope(static_cast<const ReferenceErrorCtor *>(m)->engine());
ScopedValue v(scope, callData->argument(0));
- return ErrorObject::create<ReferenceErrorObject>(scope.engine, v)->asReturnedValue();
+ scope.result = ErrorObject::create<ReferenceErrorObject>(scope.engine, v);
}
-Heap::SyntaxErrorCtor::SyntaxErrorCtor(QV4::ExecutionContext *scope)
- : Heap::ErrorCtor(scope, QStringLiteral("SyntaxError"))
+void Heap::SyntaxErrorCtor::init(QV4::ExecutionContext *scope)
{
+ Heap::ErrorCtor::init(scope, QStringLiteral("SyntaxError"));
}
-ReturnedValue SyntaxErrorCtor::construct(const Managed *m, CallData *callData)
+void SyntaxErrorCtor::construct(const Managed *, Scope &scope, CallData *callData)
{
- Scope scope(static_cast<const SyntaxErrorCtor *>(m)->engine());
ScopedValue v(scope, callData->argument(0));
- return ErrorObject::create<SyntaxErrorObject>(scope.engine, v)->asReturnedValue();
+ scope.result = ErrorObject::create<SyntaxErrorObject>(scope.engine, v);
}
-Heap::TypeErrorCtor::TypeErrorCtor(QV4::ExecutionContext *scope)
- : Heap::ErrorCtor(scope, QStringLiteral("TypeError"))
+void Heap::TypeErrorCtor::init(QV4::ExecutionContext *scope)
{
+ Heap::ErrorCtor::init(scope, QStringLiteral("TypeError"));
}
-ReturnedValue TypeErrorCtor::construct(const Managed *m, CallData *callData)
+void TypeErrorCtor::construct(const Managed *, Scope &scope, CallData *callData)
{
- Scope scope(static_cast<const TypeErrorCtor *>(m)->engine());
ScopedValue v(scope, callData->argument(0));
- return ErrorObject::create<TypeErrorObject>(scope.engine, v)->asReturnedValue();
+ scope.result = ErrorObject::create<TypeErrorObject>(scope.engine, v);
}
-Heap::URIErrorCtor::URIErrorCtor(QV4::ExecutionContext *scope)
- : Heap::ErrorCtor(scope, QStringLiteral("URIError"))
+void Heap::URIErrorCtor::init(QV4::ExecutionContext *scope)
{
+ Heap::ErrorCtor::init(scope, QStringLiteral("URIError"));
}
-ReturnedValue URIErrorCtor::construct(const Managed *m, CallData *callData)
+void URIErrorCtor::construct(const Managed *, Scope &scope, CallData *callData)
{
- Scope scope(static_cast<const URIErrorCtor *>(m)->engine());
ScopedValue v(scope, callData->argument(0));
- return ErrorObject::create<URIErrorObject>(scope.engine, v)->asReturnedValue();
+ scope.result = ErrorObject::create<URIErrorObject>(scope.engine, v);
}
void ErrorPrototype::init(ExecutionEngine *engine, Object *ctor, Object *obj, Heap::ErrorObject::ErrorType t)
diff --git a/src/qml/jsruntime/qv4errorobject_p.h b/src/qml/jsruntime/qv4errorobject_p.h
index 1ca2fedd7b..42a6e0b4b1 100644
--- a/src/qml/jsruntime/qv4errorobject_p.h
+++ b/src/qml/jsruntime/qv4errorobject_p.h
@@ -73,68 +73,72 @@ struct ErrorObject : Object {
URIError
};
- ErrorObject();
- ErrorObject(const Value &message, ErrorType t = Error);
- ErrorObject(const Value &message, const QString &fileName, int line, int column, ErrorType t = Error);
+ void init();
+ void init(const Value &message, ErrorType t = Error);
+ void init(const Value &message, const QString &fileName, int line, int column, ErrorType t = Error);
+ void destroy() {
+ delete stackTrace;
+ Object::destroy();
+ }
ErrorType errorType;
- StackTrace stackTrace;
+ StackTrace *stackTrace;
Pointer<String> stack;
};
struct EvalErrorObject : ErrorObject {
- EvalErrorObject(const Value &message);
+ void init(const Value &message);
};
struct RangeErrorObject : ErrorObject {
- RangeErrorObject(const Value &message);
+ void init(const Value &message);
};
struct ReferenceErrorObject : ErrorObject {
- ReferenceErrorObject(const Value &message);
- ReferenceErrorObject(const Value &msg, const QString &fileName, int lineNumber, int columnNumber);
+ void init(const Value &message);
+ void init(const Value &msg, const QString &fileName, int lineNumber, int columnNumber);
};
struct SyntaxErrorObject : ErrorObject {
- SyntaxErrorObject(const Value &message);
- SyntaxErrorObject(const Value &msg, const QString &fileName, int lineNumber, int columnNumber);
+ void init(const Value &message);
+ void init(const Value &msg, const QString &fileName, int lineNumber, int columnNumber);
};
struct TypeErrorObject : ErrorObject {
- TypeErrorObject(const Value &message);
+ void init(const Value &message);
};
struct URIErrorObject : ErrorObject {
- URIErrorObject(const Value &message);
+ void init(const Value &message);
};
struct ErrorCtor : Heap::FunctionObject {
- ErrorCtor(QV4::ExecutionContext *scope);
- ErrorCtor(QV4::ExecutionContext *scope, const QString &name);
+ void init(QV4::ExecutionContext *scope);
+ void init(QV4::ExecutionContext *scope, const QString &name);
};
struct EvalErrorCtor : ErrorCtor {
- EvalErrorCtor(QV4::ExecutionContext *scope);
+ void init(QV4::ExecutionContext *scope);
};
struct RangeErrorCtor : ErrorCtor {
- RangeErrorCtor(QV4::ExecutionContext *scope);
+ void init(QV4::ExecutionContext *scope);
};
struct ReferenceErrorCtor : ErrorCtor {
- ReferenceErrorCtor(QV4::ExecutionContext *scope);
+ void init(QV4::ExecutionContext *scope);
};
struct SyntaxErrorCtor : ErrorCtor {
- SyntaxErrorCtor(QV4::ExecutionContext *scope);
+ void init(QV4::ExecutionContext *scope);
};
struct TypeErrorCtor : ErrorCtor {
- TypeErrorCtor(QV4::ExecutionContext *scope);
+ void init(QV4::ExecutionContext *scope);
};
struct URIErrorCtor : ErrorCtor {
- URIErrorCtor(QV4::ExecutionContext *scope);
+ void init(QV4::ExecutionContext *scope);
};
}
@@ -221,50 +225,50 @@ struct ErrorCtor: FunctionObject
{
V4_OBJECT2(ErrorCtor, FunctionObject)
- static ReturnedValue construct(const Managed *, CallData *callData);
- static ReturnedValue call(const Managed *that, CallData *callData);
+ static void construct(const Managed *, Scope &scope, CallData *callData);
+ static void call(const Managed *that, Scope &scope, CallData *callData);
};
struct EvalErrorCtor: ErrorCtor
{
V4_OBJECT2(EvalErrorCtor, ErrorCtor)
- static ReturnedValue construct(const Managed *m, CallData *callData);
+ static void construct(const Managed *m, Scope &scope, CallData *callData);
};
struct RangeErrorCtor: ErrorCtor
{
V4_OBJECT2(RangeErrorCtor, ErrorCtor)
- static ReturnedValue construct(const Managed *m, CallData *callData);
+ static void construct(const Managed *, Scope &scope, CallData *callData);
};
struct ReferenceErrorCtor: ErrorCtor
{
V4_OBJECT2(ReferenceErrorCtor, ErrorCtor)
- static ReturnedValue construct(const Managed *m, CallData *callData);
+ static void construct(const Managed *m, Scope &scope, CallData *callData);
};
struct SyntaxErrorCtor: ErrorCtor
{
V4_OBJECT2(SyntaxErrorCtor, ErrorCtor)
- static ReturnedValue construct(const Managed *m, CallData *callData);
+ static void construct(const Managed *m, Scope &scope, CallData *callData);
};
struct TypeErrorCtor: ErrorCtor
{
V4_OBJECT2(TypeErrorCtor, ErrorCtor)
- static ReturnedValue construct(const Managed *m, CallData *callData);
+ static void construct(const Managed *m, Scope &scope, CallData *callData);
};
struct URIErrorCtor: ErrorCtor
{
V4_OBJECT2(URIErrorCtor, ErrorCtor)
- static ReturnedValue construct(const Managed *m, CallData *callData);
+ static void construct(const Managed *m, Scope &scope, CallData *callData);
};
diff --git a/src/qml/jsruntime/qv4function.cpp b/src/qml/jsruntime/qv4function.cpp
index f314e20863..caabee322a 100644
--- a/src/qml/jsruntime/qv4function.cpp
+++ b/src/qml/jsruntime/qv4function.cpp
@@ -59,7 +59,7 @@ Function::Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit,
Q_UNUSED(engine);
internalClass = engine->emptyClass;
- const quint32 *formalsIndices = compiledFunction->formalsTable();
+ const CompiledData::LEUInt32 *formalsIndices = compiledFunction->formalsTable();
// iterate backwards, so we get the right ordering for duplicate names
Scope scope(engine);
ScopedString arg(scope);
@@ -78,7 +78,7 @@ Function::Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit,
}
nFormals = compiledFunction->nFormals;
- const quint32 *localsIndices = compiledFunction->localsTable();
+ const CompiledData::LEUInt32 *localsIndices = compiledFunction->localsTable();
for (quint32 i = 0; i < compiledFunction->nLocals; ++i)
internalClass = internalClass->addMember(compilationUnit->runtimeStrings[localsIndices[i]]->identifier, Attr_NotConfigurable);
@@ -110,7 +110,7 @@ void Function::updateInternalClass(ExecutionEngine *engine, const QList<QByteArr
}
nFormals = parameters.size();
- const quint32 *localsIndices = compiledFunction->localsTable();
+ const CompiledData::LEUInt32 *localsIndices = compiledFunction->localsTable();
for (quint32 i = 0; i < compiledFunction->nLocals; ++i)
internalClass = internalClass->addMember(compilationUnit->runtimeStrings[localsIndices[i]]->identifier, Attr_NotConfigurable);
diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp
index 5d2c57a2ba..2cc58b74a6 100644
--- a/src/qml/jsruntime/qv4functionobject.cpp
+++ b/src/qml/jsruntime/qv4functionobject.cpp
@@ -68,78 +68,86 @@ using namespace QV4;
DEFINE_OBJECT_VTABLE(FunctionObject);
-Heap::FunctionObject::FunctionObject(QV4::ExecutionContext *scope, QV4::String *name, bool createProto)
- : scope(scope->d())
- , function(Q_NULLPTR)
+void Heap::FunctionObject::init(QV4::ExecutionContext *scope, QV4::String *name, bool createProto)
{
+ Object::init();
+ function = nullptr;
+ this->scope = scope->d();
Scope s(scope->engine());
ScopedFunctionObject f(s, this);
f->init(name, createProto);
}
-Heap::FunctionObject::FunctionObject(QV4::ExecutionContext *scope, Function *function, bool createProto)
- : scope(scope->d())
- , function(Q_NULLPTR)
+void Heap::FunctionObject::init(QV4::ExecutionContext *scope, Function *function, bool createProto)
{
+ Object::init();
+ function = nullptr;
+ this->scope = scope->d();
Scope s(scope->engine());
ScopedString name(s, function->name());
ScopedFunctionObject f(s, this);
f->init(name, createProto);
}
-Heap::FunctionObject::FunctionObject(QV4::ExecutionContext *scope, const QString &name, bool createProto)
- : scope(scope->d())
- , function(Q_NULLPTR)
+void Heap::FunctionObject::init(QV4::ExecutionContext *scope, const QString &name, bool createProto)
{
+ Object::init();
+ function = nullptr;
+ this->scope = scope->d();
Scope s(scope->engine());
ScopedFunctionObject f(s, this);
ScopedString n(s, s.engine->newString(name));
f->init(n, createProto);
}
-Heap::FunctionObject::FunctionObject(ExecutionContext *scope, const QString &name, bool createProto)
- : scope(scope)
- , function(Q_NULLPTR)
+void Heap::FunctionObject::init(ExecutionContext *scope, const QString &name, bool createProto)
{
+ Object::init();
+ function = nullptr;
+ this->scope = scope;
Scope s(scope->engine);
ScopedFunctionObject f(s, this);
ScopedString n(s, s.engine->newString(name));
f->init(n, createProto);
}
-Heap::FunctionObject::FunctionObject(QV4::ExecutionContext *scope, const ReturnedValue name)
- : scope(scope->d())
- , function(Q_NULLPTR)
+void Heap::FunctionObject::init(QV4::ExecutionContext *scope, const ReturnedValue name)
{
+ Object::init();
+ function = nullptr;
+ this->scope = scope->d();
Scope s(scope);
ScopedFunctionObject f(s, this);
ScopedString n(s, name);
f->init(n, false);
}
-Heap::FunctionObject::FunctionObject(ExecutionContext *scope, const ReturnedValue name)
- : scope(scope)
- , function(Q_NULLPTR)
+void Heap::FunctionObject::init(ExecutionContext *scope, const ReturnedValue name)
{
+ Object::init();
+ function = nullptr;
+ this->scope = scope;
Scope s(scope->engine);
ScopedFunctionObject f(s, this);
ScopedString n(s, name);
f->init(n, false);
}
-Heap::FunctionObject::FunctionObject()
- : scope(internalClass->engine->rootContext()->d())
- , function(Q_NULLPTR)
+void Heap::FunctionObject::init()
{
+ Object::init();
+ function = nullptr;
+ this->scope = internalClass->engine->rootContext()->d();
Q_ASSERT(internalClass && internalClass->find(internalClass->engine->id_prototype()) == Index_Prototype);
*propertyData(Index_Prototype) = Encode::undefined();
}
-Heap::FunctionObject::~FunctionObject()
+void Heap::FunctionObject::destroy()
{
if (function)
function->compilationUnit->release();
+ Object::destroy();
}
void FunctionObject::init(String *n, bool createProto)
@@ -166,22 +174,14 @@ ReturnedValue FunctionObject::name() const
return get(scope()->engine->id_name());
}
-
-ReturnedValue FunctionObject::newInstance()
+void FunctionObject::construct(const Managed *that, Scope &scope, CallData *)
{
- Scope scope(internalClass()->engine);
- ScopedCallData callData(scope);
- return construct(callData);
+ scope.result = static_cast<const FunctionObject *>(that)->engine()->throwTypeError();
}
-ReturnedValue FunctionObject::construct(const Managed *that, CallData *)
+void FunctionObject::call(const Managed *, Scope &scope, CallData *)
{
- return static_cast<const FunctionObject *>(that)->engine()->throwTypeError();
-}
-
-ReturnedValue FunctionObject::call(const Managed *, CallData *)
-{
- return Encode::undefined();
+ scope.result = Encode::undefined();
}
void FunctionObject::markObjects(Heap::Base *that, ExecutionEngine *e)
@@ -235,7 +235,7 @@ QQmlSourceLocation FunctionObject::sourceLocation() const
{
if (isBinding()) {
Q_ASSERT(as<const QV4::QQmlBindingFunction>());
- return static_cast<QV4::Heap::QQmlBindingFunction *>(d())->bindingLocation;
+ return *static_cast<QV4::Heap::QQmlBindingFunction *>(d())->bindingLocation;
}
QV4::Function *function = d()->function;
Q_ASSERT(function);
@@ -245,15 +245,14 @@ QQmlSourceLocation FunctionObject::sourceLocation() const
DEFINE_OBJECT_VTABLE(FunctionCtor);
-Heap::FunctionCtor::FunctionCtor(QV4::ExecutionContext *scope)
- : Heap::FunctionObject(scope, QStringLiteral("Function"))
+void Heap::FunctionCtor::init(QV4::ExecutionContext *scope)
{
+ Heap::FunctionObject::init(scope, QStringLiteral("Function"));
}
// 15.3.2
-ReturnedValue FunctionCtor::construct(const Managed *that, CallData *callData)
+void FunctionCtor::construct(const Managed *that, Scope &scope, CallData *callData)
{
- Scope scope(static_cast<const Object *>(that)->engine());
Scoped<FunctionCtor> f(scope, static_cast<const FunctionCtor *>(that));
QString arguments;
@@ -266,8 +265,10 @@ ReturnedValue FunctionCtor::construct(const Managed *that, CallData *callData)
}
body = callData->args[callData->argc - 1].toQString();
}
- if (scope.engine->hasException)
- return Encode::undefined();
+ if (scope.engine->hasException) {
+ scope.result = Encode::undefined();
+ return;
+ }
QString function = QLatin1String("function(") + arguments + QLatin1String("){") + body + QLatin1Char('}');
@@ -278,15 +279,19 @@ ReturnedValue FunctionCtor::construct(const Managed *that, CallData *callData)
const bool parsed = parser.parseExpression();
- if (!parsed)
- return scope.engine->throwSyntaxError(QLatin1String("Parse error"));
+ if (!parsed) {
+ scope.result = scope.engine->throwSyntaxError(QLatin1String("Parse error"));
+ return;
+ }
using namespace QQmlJS::AST;
FunctionExpression *fe = QQmlJS::AST::cast<FunctionExpression *>(parser.rootNode());
- if (!fe)
- return scope.engine->throwSyntaxError(QLatin1String("Parse error"));
+ if (!fe) {
+ scope.result = scope.engine->throwSyntaxError(QLatin1String("Parse error"));
+ return;
+ }
- IR::Module module(scope.engine->debugger != 0);
+ IR::Module module(scope.engine->debugger() != 0);
QQmlJS::RuntimeCodegen cg(scope.engine, f->strictMode());
cg.generateFromFunctionExpression(QString(), function, fe, &module);
@@ -297,19 +302,20 @@ ReturnedValue FunctionCtor::construct(const Managed *that, CallData *callData)
Function *vmf = compilationUnit->linkToEngine(scope.engine);
ExecutionContext *global = scope.engine->rootContext();
- return FunctionObject::createScriptFunction(global, vmf)->asReturnedValue();
+ scope.result = FunctionObject::createScriptFunction(global, vmf);
}
// 15.3.1: This is equivalent to new Function(...)
-ReturnedValue FunctionCtor::call(const Managed *that, CallData *callData)
+void FunctionCtor::call(const Managed *that, Scope &scope, CallData *callData)
{
- return construct(that, callData);
+ construct(that, scope, callData);
}
DEFINE_OBJECT_VTABLE(FunctionPrototype);
-Heap::FunctionPrototype::FunctionPrototype()
+void Heap::FunctionPrototype::init()
{
+ Heap::FunctionObject::init();
}
void FunctionPrototype::init(ExecutionEngine *engine, Object *ctor)
@@ -376,7 +382,8 @@ ReturnedValue FunctionPrototype::method_apply(CallContext *ctx)
}
callData->thisObject = ctx->argument(0);
- return o->call(callData);
+ o->call(scope, callData);
+ return scope.result.asReturnedValue();
}
ReturnedValue FunctionPrototype::method_call(CallContext *ctx)
@@ -393,7 +400,9 @@ ReturnedValue FunctionPrototype::method_call(CallContext *ctx)
callData->args[i - 1] = ctx->args()[i];
}
callData->thisObject = ctx->argument(0);
- return o->call(callData);
+
+ o->call(scope, callData);
+ return scope.result.asReturnedValue();
}
ReturnedValue FunctionPrototype::method_bind(CallContext *ctx)
@@ -417,24 +426,25 @@ ReturnedValue FunctionPrototype::method_bind(CallContext *ctx)
DEFINE_OBJECT_VTABLE(ScriptFunction);
-Heap::ScriptFunction::ScriptFunction(QV4::ExecutionContext *scope, Function *function)
- : Heap::SimpleScriptFunction(scope, function, true)
+void Heap::ScriptFunction::init(QV4::ExecutionContext *scope, Function *function)
{
+ Heap::SimpleScriptFunction::init(scope, function, true);
}
-ReturnedValue ScriptFunction::construct(const Managed *that, CallData *callData)
+void ScriptFunction::construct(const Managed *that, Scope &scope, CallData *callData)
{
- ExecutionEngine *v4 = static_cast<const Object *>(that)->engine();
- if (v4->hasException)
- return Encode::undefined();
- CHECK_STACK_LIMITS(v4);
+ ExecutionEngine *v4 = scope.engine;
+ if (v4->hasException) {
+ scope.result = Encode::undefined();
+ return;
+ }
+ CHECK_STACK_LIMITS(v4, scope);
- Scope scope(v4);
ExecutionContextSaver ctxSaver(scope);
Scoped<ScriptFunction> f(scope, static_cast<const ScriptFunction *>(that));
- InternalClass *ic = scope.engine->emptyClass;
+ InternalClass *ic = v4->emptyClass;
ScopedObject proto(scope, f->protoForConstructor());
ScopedObject obj(scope, v4->newObject(ic, proto));
@@ -442,45 +452,44 @@ ReturnedValue ScriptFunction::construct(const Managed *that, CallData *callData)
Scoped<CallContext> ctx(scope, v4->currentContext->newCallContext(f, callData));
v4->pushContext(ctx);
- ScopedValue result(scope, Q_V4_PROFILE(v4, f->function()));
+ scope.result = Q_V4_PROFILE(v4, f->function());
if (f->function()->compiledFunction->hasQmlDependencies())
- QQmlPropertyCapture::registerQmlDependencies(v4, f->function()->compiledFunction);
-
- if (v4->hasException)
- return Encode::undefined();
+ QQmlPropertyCapture::registerQmlDependencies(f->function()->compiledFunction, scope);
- if (result->isObject())
- return result->asReturnedValue();
- return obj.asReturnedValue();
+ if (v4->hasException) {
+ scope.result = Encode::undefined();
+ } else if (!scope.result.isObject()) {
+ scope.result = obj.asReturnedValue();
+ }
}
-ReturnedValue ScriptFunction::call(const Managed *that, CallData *callData)
+void ScriptFunction::call(const Managed *that, Scope &scope, CallData *callData)
{
- ExecutionEngine *v4 = static_cast<const Object *>(that)->engine();
- if (v4->hasException)
- return Encode::undefined();
- CHECK_STACK_LIMITS(v4);
+ ExecutionEngine *v4 = scope.engine;
+ if (v4->hasException) {
+ scope.result = Encode::undefined();
+ return;
+ }
+ CHECK_STACK_LIMITS(v4, scope);
- Scope scope(v4);
ExecutionContextSaver ctxSaver(scope);
Scoped<ScriptFunction> f(scope, static_cast<const ScriptFunction *>(that));
Scoped<CallContext> ctx(scope, v4->currentContext->newCallContext(f, callData));
v4->pushContext(ctx);
- ScopedValue result(scope, Q_V4_PROFILE(v4, f->function()));
+ scope.result = Q_V4_PROFILE(v4, f->function());
if (f->function()->compiledFunction->hasQmlDependencies())
- QQmlPropertyCapture::registerQmlDependencies(scope.engine, f->function()->compiledFunction);
-
- return result->asReturnedValue();
+ QQmlPropertyCapture::registerQmlDependencies(f->function()->compiledFunction, scope);
}
DEFINE_OBJECT_VTABLE(SimpleScriptFunction);
-Heap::SimpleScriptFunction::SimpleScriptFunction(QV4::ExecutionContext *scope, Function *function, bool createProto)
+void Heap::SimpleScriptFunction::init(QV4::ExecutionContext *scope, Function *function, bool createProto)
{
+ FunctionObject::init();
this->scope = scope->d();
this->function = function;
@@ -511,14 +520,15 @@ Heap::SimpleScriptFunction::SimpleScriptFunction(QV4::ExecutionContext *scope, F
}
}
-ReturnedValue SimpleScriptFunction::construct(const Managed *that, CallData *callData)
+void SimpleScriptFunction::construct(const Managed *that, Scope &scope, CallData *callData)
{
- ExecutionEngine *v4 = static_cast<const Object *>(that)->engine();
- if (v4->hasException)
- return Encode::undefined();
- CHECK_STACK_LIMITS(v4);
+ ExecutionEngine *v4 = scope.engine;
+ if (v4->hasException) {
+ scope.result = Encode::undefined();
+ return;
+ }
+ CHECK_STACK_LIMITS(v4, scope);
- Scope scope(v4);
ExecutionContextSaver ctxSaver(scope);
Scoped<SimpleScriptFunction> f(scope, static_cast<const SimpleScriptFunction *>(that));
@@ -527,14 +537,13 @@ ReturnedValue SimpleScriptFunction::construct(const Managed *that, CallData *cal
ScopedObject proto(scope, f->protoForConstructor());
callData->thisObject = v4->newObject(ic, proto);
- CallContext::Data ctx(v4);
- ctx.mm_data = 0;
- ctx.setVtable(CallContext::staticVTable());
+ CallContext::Data ctx = CallContext::Data::createOnStack(v4);
ctx.strictMode = f->strictMode();
ctx.callData = callData;
ctx.function = f->d();
ctx.compilationUnit = f->function()->compilationUnit;
ctx.lookups = ctx.compilationUnit->runtimeLookups;
+ ctx.constantTable = ctx.compilationUnit->constants;
ctx.outer = f->scope();
ctx.locals = scope.alloc(f->varCount());
for (int i = callData->argc; i < (int)f->formalParameterCount(); ++i)
@@ -542,36 +551,38 @@ ReturnedValue SimpleScriptFunction::construct(const Managed *that, CallData *cal
v4->pushContext(&ctx);
Q_ASSERT(v4->current == &ctx);
- ScopedObject result(scope, Q_V4_PROFILE(v4, f->function()));
+ scope.result = Q_V4_PROFILE(v4, f->function());
if (f->function()->compiledFunction->hasQmlDependencies())
- QQmlPropertyCapture::registerQmlDependencies(v4, f->function()->compiledFunction);
+ QQmlPropertyCapture::registerQmlDependencies(f->function()->compiledFunction, scope);
- if (!result)
- return callData->thisObject.asReturnedValue();
- return result.asReturnedValue();
+ if (v4->hasException) {
+ scope.result = Encode::undefined();
+ } else if (!scope.result.isObject()) {
+ scope.result = callData->thisObject;
+ }
}
-ReturnedValue SimpleScriptFunction::call(const Managed *that, CallData *callData)
+void SimpleScriptFunction::call(const Managed *that, Scope &scope, CallData *callData)
{
- ExecutionEngine *v4 = static_cast<const SimpleScriptFunction *>(that)->internalClass()->engine;
- if (v4->hasException)
- return Encode::undefined();
- CHECK_STACK_LIMITS(v4);
+ ExecutionEngine *v4 = scope.engine;
+ if (v4->hasException) {
+ scope.result = Encode::undefined();
+ return;
+ }
+ CHECK_STACK_LIMITS(v4, scope);
- Scope scope(v4);
ExecutionContextSaver ctxSaver(scope);
Scoped<SimpleScriptFunction> f(scope, static_cast<const SimpleScriptFunction *>(that));
- CallContext::Data ctx(v4);
- ctx.mm_data = 0;
- ctx.setVtable(CallContext::staticVTable());
+ CallContext::Data ctx = CallContext::Data::createOnStack(v4);
ctx.strictMode = f->strictMode();
ctx.callData = callData;
ctx.function = f->d();
ctx.compilationUnit = f->function()->compilationUnit;
ctx.lookups = ctx.compilationUnit->runtimeLookups;
+ ctx.constantTable = ctx.compilationUnit->constants;
ctx.outer = f->scope();
ctx.locals = scope.alloc(f->varCount());
for (int i = callData->argc; i < (int)f->formalParameterCount(); ++i)
@@ -579,12 +590,10 @@ ReturnedValue SimpleScriptFunction::call(const Managed *that, CallData *callData
v4->pushContext(&ctx);
Q_ASSERT(v4->current == &ctx);
- ScopedValue result(scope, Q_V4_PROFILE(v4, f->function()));
+ scope.result = Q_V4_PROFILE(v4, f->function());
if (f->function()->compiledFunction->hasQmlDependencies())
- QQmlPropertyCapture::registerQmlDependencies(v4, f->function()->compiledFunction);
-
- return result->asReturnedValue();
+ QQmlPropertyCapture::registerQmlDependencies(f->function()->compiledFunction, scope);
}
Heap::Object *SimpleScriptFunction::protoForConstructor()
@@ -600,71 +609,69 @@ Heap::Object *SimpleScriptFunction::protoForConstructor()
DEFINE_OBJECT_VTABLE(BuiltinFunction);
-Heap::BuiltinFunction::BuiltinFunction(QV4::ExecutionContext *scope, QV4::String *name, ReturnedValue (*code)(QV4::CallContext *))
- : Heap::FunctionObject(scope, name)
- , code(code)
+void Heap::BuiltinFunction::init(QV4::ExecutionContext *scope, QV4::String *name, ReturnedValue (*code)(QV4::CallContext *))
{
+ Heap::FunctionObject::init(scope, name);
+ this->code = code;
}
-ReturnedValue BuiltinFunction::construct(const Managed *f, CallData *)
+void BuiltinFunction::construct(const Managed *f, Scope &scope, CallData *)
{
- return static_cast<const BuiltinFunction *>(f)->internalClass()->engine->throwTypeError();
+ scope.result = static_cast<const BuiltinFunction *>(f)->internalClass()->engine->throwTypeError();
}
-ReturnedValue BuiltinFunction::call(const Managed *that, CallData *callData)
+void BuiltinFunction::call(const Managed *that, Scope &scope, CallData *callData)
{
const BuiltinFunction *f = static_cast<const BuiltinFunction *>(that);
- ExecutionEngine *v4 = f->internalClass()->engine;
- if (v4->hasException)
- return Encode::undefined();
- CHECK_STACK_LIMITS(v4);
+ ExecutionEngine *v4 = scope.engine;
+ if (v4->hasException) {
+ scope.result = Encode::undefined();
+ return;
+ }
+ CHECK_STACK_LIMITS(v4, scope);
- Scope scope(v4);
ExecutionContextSaver ctxSaver(scope);
- CallContext::Data ctx(v4);
- ctx.mm_data = 0;
- ctx.setVtable(CallContext::staticVTable());
+ CallContext::Data ctx = CallContext::Data::createOnStack(v4);
ctx.strictMode = f->scope()->strictMode; // ### needed? scope or parent context?
ctx.callData = callData;
v4->pushContext(&ctx);
Q_ASSERT(v4->current == &ctx);
- return f->d()->code(static_cast<QV4::CallContext *>(v4->currentContext));
+ scope.result = f->d()->code(static_cast<QV4::CallContext *>(v4->currentContext));
}
-ReturnedValue IndexedBuiltinFunction::call(const Managed *that, CallData *callData)
+void IndexedBuiltinFunction::call(const Managed *that, Scope &scope, CallData *callData)
{
const IndexedBuiltinFunction *f = static_cast<const IndexedBuiltinFunction *>(that);
- ExecutionEngine *v4 = f->internalClass()->engine;
- if (v4->hasException)
- return Encode::undefined();
- CHECK_STACK_LIMITS(v4);
+ ExecutionEngine *v4 = scope.engine;
+ if (v4->hasException) {
+ scope.result = Encode::undefined();
+ return;
+ }
+ CHECK_STACK_LIMITS(v4, scope);
- Scope scope(v4);
ExecutionContextSaver ctxSaver(scope);
- CallContext::Data ctx(v4);
- ctx.mm_data = 0;
- ctx.setVtable(CallContext::staticVTable());
+ CallContext::Data ctx = CallContext::Data::createOnStack(v4);
ctx.strictMode = f->scope()->strictMode; // ### needed? scope or parent context?
ctx.callData = callData;
v4->pushContext(&ctx);
Q_ASSERT(v4->current == &ctx);
- return f->d()->code(static_cast<QV4::CallContext *>(v4->currentContext), f->d()->index);
+ scope.result = f->d()->code(static_cast<QV4::CallContext *>(v4->currentContext), f->d()->index);
}
DEFINE_OBJECT_VTABLE(IndexedBuiltinFunction);
DEFINE_OBJECT_VTABLE(BoundFunction);
-Heap::BoundFunction::BoundFunction(QV4::ExecutionContext *scope, QV4::FunctionObject *target,
- const Value &boundThis, QV4::MemberData *boundArgs)
- : Heap::FunctionObject(scope, QStringLiteral("__bound function__"))
- , target(target->d())
- , boundArgs(boundArgs ? boundArgs->d() : 0)
+void Heap::BoundFunction::init(QV4::ExecutionContext *scope, QV4::FunctionObject *target,
+ const Value &boundThis, QV4::MemberData *boundArgs)
{
+ Heap::FunctionObject::init(scope, QStringLiteral("__bound function__"));
+ this->target = target->d();
+ this->boundArgs = boundArgs ? boundArgs->d() : 0;
this->boundThis = boundThis;
Scope s(scope);
@@ -685,12 +692,13 @@ Heap::BoundFunction::BoundFunction(QV4::ExecutionContext *scope, QV4::FunctionOb
f->insertMember(s.engine->id_caller(), pd, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable);
}
-ReturnedValue BoundFunction::call(const Managed *that, CallData *dd)
+void BoundFunction::call(const Managed *that, Scope &scope, CallData *dd)
{
const BoundFunction *f = static_cast<const BoundFunction *>(that);
- Scope scope(f->engine());
- if (scope.hasException())
- return Encode::undefined();
+ if (scope.hasException()) {
+ scope.result = Encode::undefined();
+ return;
+ }
Scoped<MemberData> boundArgs(scope, f->boundArgs());
ScopedCallData callData(scope, (boundArgs ? boundArgs->size() : 0) + dd->argc);
@@ -702,15 +710,16 @@ ReturnedValue BoundFunction::call(const Managed *that, CallData *dd)
}
memcpy(argp, dd->args, dd->argc*sizeof(Value));
ScopedFunctionObject t(scope, f->target());
- return t->call(callData);
+ t->call(scope, callData);
}
-ReturnedValue BoundFunction::construct(const Managed *that, CallData *dd)
+void BoundFunction::construct(const Managed *that, Scope &scope, CallData *dd)
{
const BoundFunction *f = static_cast<const BoundFunction *>(that);
- Scope scope(f->engine());
- if (scope.hasException())
- return Encode::undefined();
+ if (scope.hasException()) {
+ scope.result = Encode::undefined();
+ return;
+ }
Scoped<MemberData> boundArgs(scope, f->boundArgs());
ScopedCallData callData(scope, (boundArgs ? boundArgs->size() : 0) + dd->argc);
@@ -721,7 +730,7 @@ ReturnedValue BoundFunction::construct(const Managed *that, CallData *dd)
}
memcpy(argp, dd->args, dd->argc*sizeof(Value));
ScopedFunctionObject t(scope, f->target());
- return t->construct(callData);
+ t->construct(scope, callData);
}
void BoundFunction::markObjects(Heap::Base *that, ExecutionEngine *e)
diff --git a/src/qml/jsruntime/qv4functionobject_p.h b/src/qml/jsruntime/qv4functionobject_p.h
index 4a4545eca4..e58b83e2c3 100644
--- a/src/qml/jsruntime/qv4functionobject_p.h
+++ b/src/qml/jsruntime/qv4functionobject_p.h
@@ -69,14 +69,14 @@ struct Q_QML_PRIVATE_EXPORT FunctionObject : Object {
Index_ProtoConstructor = 0
};
- FunctionObject(QV4::ExecutionContext *scope, QV4::String *name, bool createProto = false);
- FunctionObject(QV4::ExecutionContext *scope, QV4::Function *function, bool createProto = false);
- FunctionObject(QV4::ExecutionContext *scope, const QString &name = QString(), bool createProto = false);
- FunctionObject(ExecutionContext *scope, const QString &name = QString(), bool createProto = false);
- FunctionObject(QV4::ExecutionContext *scope, const ReturnedValue name);
- FunctionObject(ExecutionContext *scope, const ReturnedValue name);
- FunctionObject();
- ~FunctionObject();
+ void init(QV4::ExecutionContext *scope, QV4::String *name, bool createProto = false);
+ void init(QV4::ExecutionContext *scope, QV4::Function *function, bool createProto = false);
+ void init(QV4::ExecutionContext *scope, const QString &name = QString(), bool createProto = false);
+ void init(ExecutionContext *scope, const QString &name = QString(), bool createProto = false);
+ void init(QV4::ExecutionContext *scope, const ReturnedValue name);
+ void init(ExecutionContext *scope, const ReturnedValue name);
+ void init();
+ void destroy();
unsigned int formalParameterCount() { return function ? function->nFormals : 0; }
unsigned int varCount() { return function ? function->compiledFunction->nLocals : 0; }
@@ -87,20 +87,20 @@ struct Q_QML_PRIVATE_EXPORT FunctionObject : Object {
};
struct FunctionCtor : FunctionObject {
- FunctionCtor(QV4::ExecutionContext *scope);
+ void init(QV4::ExecutionContext *scope);
};
struct FunctionPrototype : FunctionObject {
- FunctionPrototype();
+ void init();
};
struct Q_QML_EXPORT BuiltinFunction : FunctionObject {
- BuiltinFunction(QV4::ExecutionContext *scope, QV4::String *name, ReturnedValue (*code)(QV4::CallContext *));
+ void init(QV4::ExecutionContext *scope, QV4::String *name, ReturnedValue (*code)(QV4::CallContext *));
ReturnedValue (*code)(QV4::CallContext *);
};
struct IndexedBuiltinFunction : FunctionObject {
- inline IndexedBuiltinFunction(QV4::ExecutionContext *scope, uint index, ReturnedValue (*code)(QV4::CallContext *ctx, uint index));
+ inline void init(QV4::ExecutionContext *scope, uint index, ReturnedValue (*code)(QV4::CallContext *ctx, uint index));
ReturnedValue (*code)(QV4::CallContext *, uint index);
uint index;
};
@@ -110,15 +110,15 @@ struct SimpleScriptFunction : FunctionObject {
Index_Name = FunctionObject::Index_Prototype + 1,
Index_Length
};
- SimpleScriptFunction(QV4::ExecutionContext *scope, Function *function, bool createProto);
+ void init(QV4::ExecutionContext *scope, Function *function, bool createProto);
};
struct ScriptFunction : SimpleScriptFunction {
- ScriptFunction(QV4::ExecutionContext *scope, Function *function);
+ void init(QV4::ExecutionContext *scope, Function *function);
};
struct BoundFunction : FunctionObject {
- BoundFunction(QV4::ExecutionContext *scope, QV4::FunctionObject *target, const Value &boundThis, QV4::MemberData *boundArgs);
+ void init(QV4::ExecutionContext *scope, QV4::FunctionObject *target, const Value &boundThis, QV4::MemberData *boundArgs);
Pointer<FunctionObject> target;
Value boundThis;
Pointer<MemberData> boundArgs;
@@ -145,12 +145,10 @@ struct Q_QML_EXPORT FunctionObject: Object {
void init(String *name, bool createProto);
- ReturnedValue newInstance();
-
using Object::construct;
using Object::call;
- static ReturnedValue construct(const Managed *that, CallData *);
- static ReturnedValue call(const Managed *that, CallData *d);
+ static void construct(const Managed *that, Scope &scope, CallData *);
+ static void call(const Managed *that, Scope &scope, CallData *d);
static Heap::FunctionObject *createScriptFunction(ExecutionContext *scope, Function *function, bool createProto = true);
static Heap::FunctionObject *createQmlFunction(QQmlContextData *qmlContext, QObject *scopeObject, QV4::Function *runtimeFunction,
@@ -178,8 +176,8 @@ struct FunctionCtor: FunctionObject
{
V4_OBJECT2(FunctionCtor, FunctionObject)
- static ReturnedValue construct(const Managed *that, CallData *callData);
- static ReturnedValue call(const Managed *that, CallData *callData);
+ static void construct(const Managed *that, Scope &scope, CallData *callData);
+ static void call(const Managed *that, Scope &scope, CallData *callData);
};
struct FunctionPrototype: FunctionObject
@@ -202,28 +200,28 @@ struct Q_QML_EXPORT BuiltinFunction: FunctionObject {
return scope->engine()->memoryManager->allocObject<BuiltinFunction>(scope, name, code);
}
- static ReturnedValue construct(const Managed *, CallData *);
- static ReturnedValue call(const Managed *that, CallData *callData);
+ static void construct(const Managed *, Scope &scope, CallData *);
+ static void call(const Managed *that, Scope &scope, CallData *callData);
};
struct IndexedBuiltinFunction: FunctionObject
{
V4_OBJECT2(IndexedBuiltinFunction, FunctionObject)
- static ReturnedValue construct(const Managed *m, CallData *)
+ static void construct(const Managed *m, Scope &scope, CallData *)
{
- return static_cast<const IndexedBuiltinFunction *>(m)->engine()->throwTypeError();
+ scope.result = static_cast<const IndexedBuiltinFunction *>(m)->engine()->throwTypeError();
}
- static ReturnedValue call(const Managed *that, CallData *callData);
+ static void call(const Managed *that, Scope &scope, CallData *callData);
};
-Heap::IndexedBuiltinFunction::IndexedBuiltinFunction(QV4::ExecutionContext *scope, uint index,
- ReturnedValue (*code)(QV4::CallContext *ctx, uint index))
- : Heap::FunctionObject(scope),
- code(code)
- , index(index)
+void Heap::IndexedBuiltinFunction::init(QV4::ExecutionContext *scope, uint index,
+ ReturnedValue (*code)(QV4::CallContext *ctx, uint index))
{
+ Heap::FunctionObject::init(scope);
+ this->index = index;
+ this->code = code;
}
@@ -231,8 +229,8 @@ struct SimpleScriptFunction: FunctionObject {
V4_OBJECT2(SimpleScriptFunction, FunctionObject)
V4_INTERNALCLASS(simpleScriptFunctionClass)
- static ReturnedValue construct(const Managed *, CallData *callData);
- static ReturnedValue call(const Managed *that, CallData *callData);
+ static void construct(const Managed *, Scope &scope, CallData *callData);
+ static void call(const Managed *that, Scope &scope, CallData *callData);
Heap::Object *protoForConstructor();
};
@@ -240,8 +238,8 @@ struct SimpleScriptFunction: FunctionObject {
struct ScriptFunction: SimpleScriptFunction {
V4_OBJECT2(ScriptFunction, FunctionObject)
- static ReturnedValue construct(const Managed *, CallData *callData);
- static ReturnedValue call(const Managed *that, CallData *callData);
+ static void construct(const Managed *, Scope &scope, CallData *callData);
+ static void call(const Managed *that, Scope &scope, CallData *callData);
};
@@ -257,8 +255,8 @@ struct BoundFunction: FunctionObject {
Value boundThis() const { return d()->boundThis; }
Heap::MemberData *boundArgs() const { return d()->boundArgs; }
- static ReturnedValue construct(const Managed *, CallData *d);
- static ReturnedValue call(const Managed *that, CallData *dd);
+ static void construct(const Managed *, Scope &scope, CallData *d);
+ static void call(const Managed *that, Scope &scope, CallData *dd);
static void markObjects(Heap::Base *that, ExecutionEngine *e);
};
diff --git a/src/qml/jsruntime/qv4globalobject.cpp b/src/qml/jsruntime/qv4globalobject.cpp
index 2c767e3302..feb0d90d26 100644
--- a/src/qml/jsruntime/qv4globalobject.cpp
+++ b/src/qml/jsruntime/qv4globalobject.cpp
@@ -330,21 +330,22 @@ static QString decode(const QString &input, DecodeMode decodeMode, bool *ok)
DEFINE_OBJECT_VTABLE(EvalFunction);
-Heap::EvalFunction::EvalFunction(QV4::ExecutionContext *scope)
- : Heap::FunctionObject(scope, scope->d()->engine->id_eval())
+void Heap::EvalFunction::init(QV4::ExecutionContext *scope)
{
+ Heap::FunctionObject::init(scope, scope->d()->engine->id_eval());
Scope s(scope);
ScopedFunctionObject f(s, this);
f->defineReadonlyProperty(s.engine->id_length(), Primitive::fromInt32(1));
}
-ReturnedValue EvalFunction::evalCall(CallData *callData, bool directCall) const
+void EvalFunction::evalCall(Scope &scope, CallData *callData, bool directCall) const
{
- if (callData->argc < 1)
- return Encode::undefined();
+ if (callData->argc < 1) {
+ scope.result = Encode::undefined();
+ return;
+ }
ExecutionEngine *v4 = engine();
- Scope scope(v4);
ExecutionContextSaver ctxSaver(scope);
ExecutionContext *currentContext = v4->currentContext;
@@ -356,8 +357,10 @@ ReturnedValue EvalFunction::evalCall(CallData *callData, bool directCall) const
ctx = v4->pushGlobalContext();
}
- if (!callData->args[0].isString())
- return callData->args[0].asReturnedValue();
+ if (!callData->args[0].isString()) {
+ scope.result = callData->args[0].asReturnedValue();
+ return;
+ }
const QString code = callData->args[0].stringValue()->toQString();
bool inheritContext = !ctx->d()->strictMode;
@@ -366,18 +369,23 @@ ReturnedValue EvalFunction::evalCall(CallData *callData, bool directCall) const
script.strictMode = (directCall && currentContext->d()->strictMode);
script.inheritContext = inheritContext;
script.parse();
- if (v4->hasException)
- return Encode::undefined();
+ if (v4->hasException) {
+ scope.result = Encode::undefined();
+ return;
+ }
Function *function = script.function();
- if (!function)
- return Encode::undefined();
+ if (!function) {
+ scope.result = Encode::undefined();
+ return;
+ }
if (function->isStrict() || (ctx->d()->strictMode)) {
ScopedFunctionObject e(scope, FunctionObject::createScriptFunction(ctx, function));
ScopedCallData callData(scope, 0);
callData->thisObject = ctx->thisObject();
- return e->call(callData);
+ e->call(scope, callData);
+ return;
}
ContextStateSaver stateSaver(scope, ctx);
@@ -386,14 +394,14 @@ ReturnedValue EvalFunction::evalCall(CallData *callData, bool directCall) const
ctx->d()->strictMode = false;
ctx->d()->compilationUnit = function->compilationUnit;
- return Q_V4_PROFILE(ctx->engine(), function);
+ scope.result = Q_V4_PROFILE(ctx->engine(), function);
}
-ReturnedValue EvalFunction::call(const Managed *that, CallData *callData)
+void EvalFunction::call(const Managed *that, Scope &scope, CallData *callData)
{
// indirect call
- return static_cast<const EvalFunction *>(that)->evalCall(callData, false);
+ static_cast<const EvalFunction *>(that)->evalCall(scope, callData, false);
}
@@ -512,7 +520,7 @@ ReturnedValue GlobalFunctions::method_parseFloat(CallContext *ctx)
if (trimmed.startsWith(QLatin1String("Infinity"))
|| trimmed.startsWith(QLatin1String("+Infinity")))
return Encode(Q_INFINITY);
- if (trimmed.startsWith(QStringLiteral("-Infinity")))
+ if (trimmed.startsWith(QLatin1String("-Infinity")))
return Encode(-Q_INFINITY);
QByteArray ba = trimmed.toLatin1();
bool ok;
diff --git a/src/qml/jsruntime/qv4globalobject_p.h b/src/qml/jsruntime/qv4globalobject_p.h
index ea7a3b06ce..e8b3a92d34 100644
--- a/src/qml/jsruntime/qv4globalobject_p.h
+++ b/src/qml/jsruntime/qv4globalobject_p.h
@@ -60,7 +60,7 @@ namespace QV4 {
namespace Heap {
struct EvalFunction : FunctionObject {
- EvalFunction(QV4::ExecutionContext *scope);
+ void init(QV4::ExecutionContext *scope);
};
}
@@ -69,9 +69,9 @@ struct Q_QML_EXPORT EvalFunction : FunctionObject
{
V4_OBJECT2(EvalFunction, FunctionObject)
- ReturnedValue evalCall(CallData *callData, bool directCall) const;
+ void evalCall(Scope &scope, CallData *callData, bool directCall) const;
- static ReturnedValue call(const Managed *that, CallData *callData);
+ static void call(const Managed *that, Scope &scope, CallData *callData);
};
struct GlobalFunctions
diff --git a/src/qml/jsruntime/qv4identifier.cpp b/src/qml/jsruntime/qv4identifier.cpp
index c8d66b1254..6260fd0cc8 100644
--- a/src/qml/jsruntime/qv4identifier.cpp
+++ b/src/qml/jsruntime/qv4identifier.cpp
@@ -64,12 +64,33 @@ IdentifierHashData::IdentifierHashData(int numBits)
memset(entries, 0, alloc*sizeof(IdentifierHashEntry));
}
+IdentifierHashData::IdentifierHashData(IdentifierHashData *other)
+ : size(other->size)
+ , numBits(other->numBits)
+ , identifierTable(other->identifierTable)
+{
+ refCount.store(1);
+ alloc = other->alloc;
+ entries = (IdentifierHashEntry *)malloc(alloc*sizeof(IdentifierHashEntry));
+ memcpy(entries, other->entries, alloc*sizeof(IdentifierHashEntry));
+}
+
IdentifierHashBase::IdentifierHashBase(ExecutionEngine *engine)
{
d = new IdentifierHashData(3);
d->identifierTable = engine->identifierTable;
}
+void IdentifierHashBase::detach()
+{
+ if (!d || d->refCount == 1)
+ return;
+ IdentifierHashData *newData = new IdentifierHashData(d);
+ if (d && !d->refCount.deref())
+ delete d;
+ d = newData;
+}
+
IdentifierHashEntry *IdentifierHashBase::addEntry(const Identifier *identifier)
{
@@ -131,7 +152,7 @@ const IdentifierHashEntry *IdentifierHashBase::lookup(const QString &str) const
return 0;
Q_ASSERT(d->entries);
- uint hash = String::createHashValue(str.constData(), str.length());
+ uint hash = String::createHashValue(str.constData(), str.length(), Q_NULLPTR);
uint idx = hash % d->alloc;
while (1) {
if (!d->entries[idx].identifier)
diff --git a/src/qml/jsruntime/qv4identifier_p.h b/src/qml/jsruntime/qv4identifier_p.h
index a3abfd8e13..2695bbc875 100644
--- a/src/qml/jsruntime/qv4identifier_p.h
+++ b/src/qml/jsruntime/qv4identifier_p.h
@@ -85,6 +85,7 @@ struct IdentifierHashEntry {
struct IdentifierHashData
{
IdentifierHashData(int numBits);
+ explicit IdentifierHashData(IdentifierHashData *other);
~IdentifierHashData() {
free(entries);
}
@@ -115,6 +116,8 @@ struct IdentifierHashBase
bool contains(const QString &str) const;
bool contains(String *str) const;
+ void detach();
+
protected:
IdentifierHashEntry *addEntry(const Identifier *i);
const IdentifierHashEntry *lookup(const Identifier *identifier) const;
@@ -141,6 +144,7 @@ struct IdentifierHash : public IdentifierHashBase
}
void add(const QString &str, const T &value);
+ void add(Heap::String *str, const T &value);
inline T value(const QString &str) const;
inline T value(String *str) const;
@@ -198,6 +202,13 @@ void IdentifierHash<T>::add(const QString &str, const T &value)
}
template<typename T>
+void IdentifierHash<T>::add(Heap::String *str, const T &value)
+{
+ IdentifierHashEntry *e = addEntry(toIdentifier(str));
+ e->value = value;
+}
+
+template<typename T>
inline T IdentifierHash<T>::value(const QString &str) const
{
return IdentifierHashEntry::get(lookup(str), (T*)0);
@@ -223,7 +234,6 @@ QString IdentifierHash<T>::findId(T value) const
return QString();
}
-
}
QT_END_NAMESPACE
diff --git a/src/qml/jsruntime/qv4identifiertable.cpp b/src/qml/jsruntime/qv4identifiertable.cpp
index 5adb17b4ea..3def6defbf 100644
--- a/src/qml/jsruntime/qv4identifiertable.cpp
+++ b/src/qml/jsruntime/qv4identifiertable.cpp
@@ -118,7 +118,8 @@ void IdentifierTable::addEntry(Heap::String *str)
Heap::String *IdentifierTable::insertString(const QString &s)
{
- uint hash = String::createHashValue(s.constData(), s.length());
+ uint subtype;
+ uint hash = String::createHashValue(s.constData(), s.length(), &subtype);
uint idx = hash % alloc;
while (Heap::String *e = entries[idx]) {
if (e->stringHash == hash && e->toQString() == s)
@@ -128,6 +129,8 @@ Heap::String *IdentifierTable::insertString(const QString &s)
}
Heap::String *str = engine->newString(s);
+ str->stringHash = hash;
+ str->subtype = subtype;
addEntry(str);
return str;
}
@@ -178,7 +181,8 @@ Identifier *IdentifierTable::identifier(const QString &s)
Identifier *IdentifierTable::identifier(const char *s, int len)
{
- uint hash = String::createHashValue(s, len);
+ uint subtype;
+ uint hash = String::createHashValue(s, len, &subtype);
if (hash == UINT_MAX)
return identifier(QString::fromUtf8(s, len));
@@ -192,6 +196,8 @@ Identifier *IdentifierTable::identifier(const char *s, int len)
}
Heap::String *str = engine->newString(QString::fromLatin1(s, len));
+ str->stringHash = hash;
+ str->subtype = subtype;
addEntry(str);
return str->identifier;
}
diff --git a/src/qml/jsruntime/qv4include.cpp b/src/qml/jsruntime/qv4include.cpp
index 29f83da522..be8057e9f5 100644
--- a/src/qml/jsruntime/qv4include.cpp
+++ b/src/qml/jsruntime/qv4include.cpp
@@ -41,8 +41,10 @@
#include "qv4scopedvalue_p.h"
#include <QtQml/qjsengine.h>
+#if QT_CONFIG(qml_network)
#include <QtNetwork/qnetworkrequest.h>
#include <QtNetwork/qnetworkreply.h>
+#endif
#include <QtCore/qfile.h>
#include <QtQml/qqmlfile.h>
@@ -57,7 +59,10 @@ QT_BEGIN_NAMESPACE
QV4Include::QV4Include(const QUrl &url, QV4::ExecutionEngine *engine,
QV4::QmlContext *qmlContext, const QV4::Value &callback)
- : v4(engine), m_network(0), m_reply(0), m_url(url), m_redirectCount(0)
+ : v4(engine), m_url(url)
+#if QT_CONFIG(qml_network)
+ , m_redirectCount(0), m_network(0) , m_reply(0)
+#endif
{
if (qmlContext)
m_qmlContext.set(engine, *qmlContext);
@@ -66,6 +71,7 @@ QV4Include::QV4Include(const QUrl &url, QV4::ExecutionEngine *engine,
m_resultObject.set(v4, resultValue(v4));
+#if QT_CONFIG(qml_network)
m_network = engine->v8Engine->networkAccessManager();
QNetworkRequest request;
@@ -73,11 +79,17 @@ QV4Include::QV4Include(const QUrl &url, QV4::ExecutionEngine *engine,
m_reply = m_network->get(request);
QObject::connect(m_reply, SIGNAL(finished()), this, SLOT(finished()));
+#else
+ finished();
+#endif
}
QV4Include::~QV4Include()
{
- delete m_reply; m_reply = 0;
+#if QT_CONFIG(qml_network)
+ delete m_reply;
+ m_reply = 0;
+#endif
}
QV4::ReturnedValue QV4Include::resultValue(QV4::ExecutionEngine *v4, Status status)
@@ -110,7 +122,7 @@ void QV4Include::callback(const QV4::Value &callback, const QV4::Value &status)
QV4::ScopedCallData callData(scope, 1);
callData->thisObject = v4->globalObject->asReturnedValue();
callData->args[0] = status;
- f->call(callData);
+ f->call(scope, callData);
if (scope.hasException())
scope.engine->catchException();
}
@@ -123,6 +135,7 @@ QV4::ReturnedValue QV4Include::result()
#define INCLUDE_MAXIMUM_REDIRECT_RECURSION 15
void QV4Include::finished()
{
+#if QT_CONFIG(qml_network)
m_redirectCount++;
if (m_redirectCount < INCLUDE_MAXIMUM_REDIRECT_RECURSION) {
@@ -166,6 +179,12 @@ void QV4Include::finished()
} else {
resultObj->put(status, QV4::ScopedValue(scope, QV4::Primitive::fromInt32(NetworkError)));
}
+#else
+ QV4::Scope scope(v4);
+ QV4::ScopedObject resultObj(scope, m_resultObject.value());
+ QV4::ScopedString status(scope, v4->newString(QStringLiteral("status")));
+ resultObj->put(status, QV4::ScopedValue(scope, QV4::Primitive::fromInt32(NetworkError)));
+#endif // qml_network
QV4::ScopedValue cb(scope, m_callbackFunction.value());
callback(cb, resultObj);
@@ -188,14 +207,15 @@ QV4::ReturnedValue QV4Include::method_include(QV4::CallContext *ctx)
if (!context || !context->isJSContext)
V4THROW_ERROR("Qt.include(): Can only be called from JavaScript files");
- QUrl url(scope.engine->resolvedUrl(ctx->args()[0].toQStringNoThrow()));
- if (scope.engine->qmlEngine() && scope.engine->qmlEngine()->urlInterceptor())
- url = scope.engine->qmlEngine()->urlInterceptor()->intercept(url, QQmlAbstractUrlInterceptor::JavaScriptFile);
-
QV4::ScopedValue callbackFunction(scope, QV4::Primitive::undefinedValue());
if (ctx->argc() >= 2 && ctx->args()[1].as<QV4::FunctionObject>())
callbackFunction = ctx->args()[1];
+#if QT_CONFIG(qml_network)
+ QUrl url(scope.engine->resolvedUrl(ctx->args()[0].toQStringNoThrow()));
+ if (scope.engine->qmlEngine() && scope.engine->qmlEngine()->urlInterceptor())
+ url = scope.engine->qmlEngine()->urlInterceptor()->intercept(url, QQmlAbstractUrlInterceptor::JavaScriptFile);
+
QString localFile = QQmlFile::urlToLocalFileOrQrc(url);
QV4::ScopedValue result(scope);
@@ -243,6 +263,12 @@ QV4::ReturnedValue QV4Include::method_include(QV4::CallContext *ctx)
}
return result->asReturnedValue();
+#else
+ QV4::ScopedValue result(scope);
+ result = resultValue(scope.engine, NetworkError);
+ callback(callbackFunction, result);
+ return result->asReturnedValue();
+#endif
}
QT_END_NAMESPACE
diff --git a/src/qml/jsruntime/qv4include_p.h b/src/qml/jsruntime/qv4include_p.h
index 257dc05e65..4c601a5e7b 100644
--- a/src/qml/jsruntime/qv4include_p.h
+++ b/src/qml/jsruntime/qv4include_p.h
@@ -62,7 +62,9 @@
QT_BEGIN_NAMESPACE
class QQmlEngine;
+#if QT_CONFIG(qml_network)
class QNetworkAccessManager;
+#endif
class QNetworkReply;
class QV4Include : public QObject
{
@@ -90,15 +92,16 @@ private:
static void callback(const QV4::Value &callback, const QV4::Value &status);
QV4::ExecutionEngine *v4;
- QNetworkAccessManager *m_network;
- QPointer<QNetworkReply> m_reply;
-
QUrl m_url;
+
+#if QT_CONFIG(qml_network)
int m_redirectCount;
+ QNetworkAccessManager *m_network;
+ QPointer<QNetworkReply> m_reply;
+#endif
QV4::PersistentValue m_callbackFunction;
QV4::PersistentValue m_resultObject;
-
QV4::PersistentValue m_qmlContext;
};
diff --git a/src/qml/jsruntime/qv4internalclass.cpp b/src/qml/jsruntime/qv4internalclass.cpp
index 2f69defd5b..d17da9af0c 100644
--- a/src/qml/jsruntime/qv4internalclass.cpp
+++ b/src/qml/jsruntime/qv4internalclass.cpp
@@ -101,20 +101,6 @@ void PropertyHash::addEntry(const PropertyHash::Entry &entry, int classSize)
++d->size;
}
-uint PropertyHash::lookup(const Identifier *identifier) const
-{
- Q_ASSERT(d->entries);
-
- uint idx = identifier->hashValue % d->alloc;
- while (1) {
- if (d->entries[idx].identifier == identifier)
- return d->entries[idx].index;
- if (!d->entries[idx].identifier)
- return UINT_MAX;
- ++idx;
- idx %= d->alloc;
- }
-}
InternalClass::InternalClass(ExecutionEngine *engine)
: engine(engine)
@@ -364,18 +350,6 @@ void InternalClass::removeMember(Object *object, Identifier *id)
Q_ASSERT(t.lookup);
}
-uint InternalClass::find(const String *string)
-{
- engine->identifierTable->identifier(string);
- const Identifier *id = string->d()->identifier;
-
- uint index = propertyTable.lookup(id);
- if (index < size)
- return index;
-
- return UINT_MAX;
-}
-
uint InternalClass::find(const Identifier *id)
{
uint index = propertyTable.lookup(id);
@@ -433,11 +407,13 @@ InternalClass *InternalClass::propertiesFrozen() const
void InternalClass::destroy()
{
- QList<InternalClass *> destroyStack;
- destroyStack.append(this);
+ std::vector<InternalClass *> destroyStack;
+ destroyStack.reserve(64);
+ destroyStack.push_back(this);
- while (!destroyStack.isEmpty()) {
- InternalClass *next = destroyStack.takeLast();
+ while (!destroyStack.empty()) {
+ InternalClass *next = destroyStack.back();
+ destroyStack.pop_back();
if (!next->engine)
continue;
next->engine = 0;
@@ -445,13 +421,13 @@ void InternalClass::destroy()
next->nameMap.~SharedInternalClassData<Identifier *>();
next->propertyData.~SharedInternalClassData<PropertyAttributes>();
if (next->m_sealed)
- destroyStack.append(next->m_sealed);
+ destroyStack.push_back(next->m_sealed);
if (next->m_frozen)
- destroyStack.append(next->m_frozen);
+ destroyStack.push_back(next->m_frozen);
for (size_t i = 0; i < next->transitions.size(); ++i) {
Q_ASSERT(next->transitions.at(i).lookup);
- destroyStack.append(next->transitions.at(i).lookup);
+ destroyStack.push_back(next->transitions.at(i).lookup);
}
next->transitions.~vector<Transition>();
diff --git a/src/qml/jsruntime/qv4internalclass_p.h b/src/qml/jsruntime/qv4internalclass_p.h
index c10af4ce01..dcda949c97 100644
--- a/src/qml/jsruntime/qv4internalclass_p.h
+++ b/src/qml/jsruntime/qv4internalclass_p.h
@@ -54,6 +54,8 @@
#include <QHash>
#include <private/qqmljsmemorypool_p.h>
+#include <private/qv4engine_p.h>
+#include <private/qv4identifiertable_p.h>
QT_BEGIN_NAMESPACE
@@ -117,6 +119,20 @@ inline PropertyHash::~PropertyHash()
delete d;
}
+inline uint PropertyHash::lookup(const Identifier *identifier) const
+{
+ Q_ASSERT(d->entries);
+
+ uint idx = identifier->hashValue % d->alloc;
+ while (1) {
+ if (d->entries[idx].identifier == identifier)
+ return d->entries[idx].index;
+ if (!d->entries[idx].identifier)
+ return UINT_MAX;
+ ++idx;
+ idx %= d->alloc;
+ }
+}
template <typename T>
struct SharedInternalClassData {
@@ -245,7 +261,7 @@ struct InternalClass : public QQmlJS::Managed {
InternalClass *changeMember(Identifier *identifier, PropertyAttributes data, uint *index = 0);
static void changeMember(Object *object, String *string, PropertyAttributes data, uint *index = 0);
static void removeMember(Object *object, Identifier *id);
- uint find(const String *s);
+ uint find(const String *string);
uint find(const Identifier *id);
InternalClass *sealed();
@@ -261,6 +277,18 @@ private:
InternalClass(const InternalClass &other);
};
+inline uint InternalClass::find(const String *string)
+{
+ engine->identifierTable->identifier(string);
+ const Identifier *id = string->d()->identifier;
+
+ uint index = propertyTable.lookup(id);
+ if (index < size)
+ return index;
+
+ return UINT_MAX;
+}
+
struct InternalClassPool : public QQmlJS::MemoryPool
{
void markObjects(ExecutionEngine *engine);
diff --git a/src/qml/jsruntime/qv4jsonobject.cpp b/src/qml/jsruntime/qv4jsonobject.cpp
index 14bbb189b6..94a6e4daa1 100644
--- a/src/qml/jsruntime/qv4jsonobject.cpp
+++ b/src/qml/jsruntime/qv4jsonobject.cpp
@@ -591,8 +591,7 @@ bool JsonParser::parseString(QString *string)
return false;
}
if (QChar::requiresSurrogates(ch)) {
- *string += QChar(QChar::highSurrogate(ch));
- *string += QChar(QChar::lowSurrogate(ch));
+ *string += QChar(QChar::highSurrogate(ch)) + QChar(QChar::lowSurrogate(ch));
} else {
*string += QChar(ch);
}
@@ -645,36 +644,39 @@ struct Stringify
static QString quote(const QString &str)
{
- QString product = QStringLiteral("\"");
- for (int i = 0; i < str.length(); ++i) {
+ QString product;
+ const int length = str.length();
+ product.reserve(length + 2);
+ product += QLatin1Char('"');
+ for (int i = 0; i < length; ++i) {
QChar c = str.at(i);
switch (c.unicode()) {
case '"':
- product += QStringLiteral("\\\"");
+ product += QLatin1String("\\\"");
break;
case '\\':
- product += QStringLiteral("\\\\");
+ product += QLatin1String("\\\\");
break;
case '\b':
- product += QStringLiteral("\\b");
+ product += QLatin1String("\\b");
break;
case '\f':
- product += QStringLiteral("\\f");
+ product += QLatin1String("\\f");
break;
case '\n':
- product += QStringLiteral("\\n");
+ product += QLatin1String("\\n");
break;
case '\r':
- product += QStringLiteral("\\r");
+ product += QLatin1String("\\r");
break;
case '\t':
- product += QStringLiteral("\\t");
+ product += QLatin1String("\\t");
break;
default:
if (c.unicode() <= 0x1f) {
- product += QStringLiteral("\\u00");
- product += c.unicode() > 0xf ? QLatin1Char('1') : QLatin1Char('0');
- product += QLatin1Char("0123456789abcdef"[c.unicode() & 0xf]);
+ product += QLatin1String("\\u00");
+ product += (c.unicode() > 0xf ? QLatin1Char('1') : QLatin1Char('0')) +
+ QLatin1Char("0123456789abcdef"[c.unicode() & 0xf]);
} else {
product += c;
}
@@ -687,57 +689,57 @@ static QString quote(const QString &str)
QString Stringify::Str(const QString &key, const Value &v)
{
Scope scope(v4);
+ scope.result = v;
- ScopedValue value(scope, v);
- ScopedObject o(scope, value);
+ ScopedObject o(scope, scope.result);
if (o) {
ScopedString s(scope, v4->newString(QStringLiteral("toJSON")));
ScopedFunctionObject toJSON(scope, o->get(s));
if (!!toJSON) {
ScopedCallData callData(scope, 1);
- callData->thisObject = value;
+ callData->thisObject = scope.result;
callData->args[0] = v4->newString(key);
- value = toJSON->call(callData);
+ toJSON->call(scope, callData);
}
}
if (replacerFunction) {
ScopedObject holder(scope, v4->newObject());
- holder->put(scope.engine, QString(), value);
+ holder->put(scope.engine, QString(), scope.result);
ScopedCallData callData(scope, 2);
callData->args[0] = v4->newString(key);
- callData->args[1] = value;
+ callData->args[1] = scope.result;
callData->thisObject = holder;
- value = replacerFunction->call(callData);
+ replacerFunction->call(scope, callData);
}
- o = value->asReturnedValue();
+ o = scope.result.asReturnedValue();
if (o) {
if (NumberObject *n = o->as<NumberObject>())
- value = Encode(n->value());
+ scope.result = Encode(n->value());
else if (StringObject *so = o->as<StringObject>())
- value = so->d()->string;
+ scope.result = so->d()->string;
else if (BooleanObject *b = o->as<BooleanObject>())
- value = Encode(b->value());
+ scope.result = Encode(b->value());
}
- if (value->isNull())
+ if (scope.result.isNull())
return QStringLiteral("null");
- if (value->isBoolean())
- return value->booleanValue() ? QStringLiteral("true") : QStringLiteral("false");
- if (value->isString())
- return quote(value->stringValue()->toQString());
-
- if (value->isNumber()) {
- double d = value->toNumber();
- return std::isfinite(d) ? value->toQString() : QStringLiteral("null");
+ if (scope.result.isBoolean())
+ return scope.result.booleanValue() ? QStringLiteral("true") : QStringLiteral("false");
+ if (scope.result.isString())
+ return quote(scope.result.stringValue()->toQString());
+
+ if (scope.result.isNumber()) {
+ double d = scope.result.toNumber();
+ return std::isfinite(d) ? scope.result.toQString() : QStringLiteral("null");
}
- if (const QV4::VariantObject *v = value->as<QV4::VariantObject>()) {
- return v->d()->data.toString();
+ if (const QV4::VariantObject *v = scope.result.as<QV4::VariantObject>()) {
+ return v->d()->data().toString();
}
- o = value->asReturnedValue();
+ o = scope.result.asReturnedValue();
if (o) {
if (!o->as<FunctionObject>()) {
if (o->as<ArrayObject>()) {
@@ -812,10 +814,10 @@ QString Stringify::JO(Object *o)
if (partial.isEmpty()) {
result = QStringLiteral("{}");
} else if (gap.isEmpty()) {
- result = QStringLiteral("{") + partial.join(QLatin1Char(',')) + QLatin1Char('}');
+ result = QLatin1Char('{') + partial.join(QLatin1Char(',')) + QLatin1Char('}');
} else {
- QString separator = QStringLiteral(",\n") + indent;
- result = QStringLiteral("{\n") + indent + partial.join(separator) + QLatin1Char('\n')
+ QString separator = QLatin1String(",\n") + indent;
+ result = QLatin1String("{\n") + indent + partial.join(separator) + QLatin1Char('\n')
+ stepback + QLatin1Char('}');
}
@@ -858,10 +860,10 @@ QString Stringify::JA(ArrayObject *a)
if (partial.isEmpty()) {
result = QStringLiteral("[]");
} else if (gap.isEmpty()) {
- result = QStringLiteral("[") + partial.join(QLatin1Char(',')) + QStringLiteral("]");
+ result = QLatin1Char('[') + partial.join(QLatin1Char(',')) + QLatin1Char(']');
} else {
- QString separator = QStringLiteral(",\n") + indent;
- result = QStringLiteral("[\n") + indent + partial.join(separator) + QStringLiteral("\n") + stepback + QStringLiteral("]");
+ QString separator = QLatin1String(",\n") + indent;
+ result = QLatin1String("[\n") + indent + partial.join(separator) + QLatin1Char('\n') + stepback + QLatin1Char(']');
}
indent = stepback;
@@ -870,8 +872,9 @@ QString Stringify::JA(ArrayObject *a)
}
-Heap::JsonObject::JsonObject()
+void Heap::JsonObject::init()
{
+ Object::init();
Scope scope(internalClass->engine);
ScopedObject o(scope, this);
diff --git a/src/qml/jsruntime/qv4jsonobject_p.h b/src/qml/jsruntime/qv4jsonobject_p.h
index c3a3b191c0..43248a214d 100644
--- a/src/qml/jsruntime/qv4jsonobject_p.h
+++ b/src/qml/jsruntime/qv4jsonobject_p.h
@@ -64,7 +64,7 @@ namespace QV4 {
namespace Heap {
struct JsonObject : Object {
- JsonObject();
+ void init();
};
}
diff --git a/src/qml/jsruntime/qv4lookup.cpp b/src/qml/jsruntime/qv4lookup.cpp
index 46e47307ef..84755a6402 100644
--- a/src/qml/jsruntime/qv4lookup.cpp
+++ b/src/qml/jsruntime/qv4lookup.cpp
@@ -306,7 +306,7 @@ ReturnedValue Lookup::getterTwoClasses(Lookup *l, ExecutionEngine *engine, const
ReturnedValue v = o->getLookup(l);
Lookup l2 = *l;
- if (l2.getter == Lookup::getter0 || l2.getter == Lookup::getter1) {
+ if (l->index != UINT_MAX && (l2.getter == Lookup::getter0 || l2.getter == Lookup::getter1)) {
// if we have a getter0, make sure it comes first
if (l2.getter == Lookup::getter0)
qSwap(l1, l2);
@@ -451,7 +451,8 @@ ReturnedValue Lookup::getterAccessor0(Lookup *l, ExecutionEngine *engine, const
ScopedCallData callData(scope, 0);
callData->thisObject = object;
- return getter->call(callData);
+ getter->call(scope, callData);
+ return scope.result.asReturnedValue();
}
}
l->getter = getterFallback;
@@ -473,7 +474,8 @@ ReturnedValue Lookup::getterAccessor1(Lookup *l, ExecutionEngine *engine, const
ScopedCallData callData(scope, 0);
callData->thisObject = object;
- return getter->call(callData);
+ getter->call(scope, callData);
+ return scope.result.asReturnedValue();
}
}
l->getter = getterFallback;
@@ -498,7 +500,8 @@ ReturnedValue Lookup::getterAccessor2(Lookup *l, ExecutionEngine *engine, const
ScopedCallData callData(scope, 0);
callData->thisObject = object;
- return getter->call(callData);
+ getter->call(scope, callData);
+ return scope.result.asReturnedValue();
}
}
}
@@ -542,7 +545,8 @@ ReturnedValue Lookup::primitiveGetterAccessor0(Lookup *l, ExecutionEngine *engin
ScopedCallData callData(scope, 0);
callData->thisObject = object;
- return getter->call(callData);
+ getter->call(scope, callData);
+ return scope.result.asReturnedValue();
}
}
l->getter = getterGeneric;
@@ -562,7 +566,8 @@ ReturnedValue Lookup::primitiveGetterAccessor1(Lookup *l, ExecutionEngine *engin
ScopedCallData callData(scope, 0);
callData->thisObject = object;
- return getter->call(callData);
+ getter->call(scope, callData);
+ return scope.result.asReturnedValue();
}
}
l->getter = getterGeneric;
@@ -665,7 +670,8 @@ ReturnedValue Lookup::globalGetterAccessor0(Lookup *l, ExecutionEngine *engine)
ScopedCallData callData(scope, 0);
callData->thisObject = Primitive::undefinedValue();
- return getter->call(callData);
+ getter->call(scope, callData);
+ return scope.result.asReturnedValue();
}
l->globalGetter = globalGetterGeneric;
return globalGetterGeneric(l, engine);
@@ -683,7 +689,8 @@ ReturnedValue Lookup::globalGetterAccessor1(Lookup *l, ExecutionEngine *engine)
ScopedCallData callData(scope, 0);
callData->thisObject = Primitive::undefinedValue();
- return getter->call(callData);
+ getter->call(scope, callData);
+ return scope.result.asReturnedValue();
}
l->globalGetter = globalGetterGeneric;
return globalGetterGeneric(l, engine);
@@ -704,7 +711,8 @@ ReturnedValue Lookup::globalGetterAccessor2(Lookup *l, ExecutionEngine *engine)
ScopedCallData callData(scope, 0);
callData->thisObject = Primitive::undefinedValue();
- return getter->call(callData);
+ getter->call(scope, callData);
+ return scope.result.asReturnedValue();
}
}
}
diff --git a/src/qml/jsruntime/qv4managed_p.h b/src/qml/jsruntime/qv4managed_p.h
index d73b990470..1fff5a45da 100644
--- a/src/qml/jsruntime/qv4managed_p.h
+++ b/src/qml/jsruntime/qv4managed_p.h
@@ -74,7 +74,7 @@ inline void qYouForgotTheQ_MANAGED_Macro(T1, T2) {}
#define V4_MANAGED_SIZE_TEST
#endif
-#define V4_NEEDS_DESTROY static void destroy(QV4::Heap::Base *b) { static_cast<Data *>(b)->~Data(); }
+#define V4_NEEDS_DESTROY static void destroy(QV4::Heap::Base *b) { static_cast<Data *>(b)->destroy(); }
#define V4_MANAGED_ITSELF(DataClass, superClass) \
@@ -85,7 +85,13 @@ inline void qYouForgotTheQ_MANAGED_Macro(T1, T2) {}
static const QV4::VTable static_vtbl; \
static inline const QV4::VTable *staticVTable() { return &static_vtbl; } \
V4_MANAGED_SIZE_TEST \
- QV4::Heap::DataClass *d() const { return static_cast<QV4::Heap::DataClass *>(m()); }
+ QV4::Heap::DataClass *d_unchecked() const { return static_cast<QV4::Heap::DataClass *>(m()); } \
+ QV4::Heap::DataClass *d() const { \
+ QV4::Heap::DataClass *dptr = d_unchecked(); \
+ dptr->_checkIsInitialized(); \
+ return dptr; \
+ } \
+ V4_ASSERT_IS_TRIVIAL(QV4::Heap::DataClass)
#define V4_MANAGED(DataClass, superClass) \
private: \
diff --git a/src/qml/jsruntime/qv4mathobject.cpp b/src/qml/jsruntime/qv4mathobject.cpp
index cb17583b98..e03b2762cc 100644
--- a/src/qml/jsruntime/qv4mathobject.cpp
+++ b/src/qml/jsruntime/qv4mathobject.cpp
@@ -51,8 +51,9 @@ using namespace QV4;
DEFINE_OBJECT_VTABLE(MathObject);
-Heap::MathObject::MathObject()
+void Heap::MathObject::init()
{
+ Object::init();
Scope scope(internalClass->engine);
ScopedObject m(scope, this);
@@ -80,6 +81,7 @@ Heap::MathObject::MathObject()
m->defineDefaultProperty(QStringLiteral("pow"), QV4::MathObject::method_pow, 2);
m->defineDefaultProperty(QStringLiteral("random"), QV4::MathObject::method_random, 0);
m->defineDefaultProperty(QStringLiteral("round"), QV4::MathObject::method_round, 1);
+ m->defineDefaultProperty(QStringLiteral("sign"), QV4::MathObject::method_sign, 1);
m->defineDefaultProperty(QStringLiteral("sin"), QV4::MathObject::method_sin, 1);
m->defineDefaultProperty(QStringLiteral("sqrt"), QV4::MathObject::method_sqrt, 1);
m->defineDefaultProperty(QStringLiteral("tan"), QV4::MathObject::method_tan, 1);
@@ -292,6 +294,19 @@ ReturnedValue MathObject::method_round(CallContext *context)
return Encode(v);
}
+ReturnedValue MathObject::method_sign(CallContext *context)
+{
+ double v = context->argc() ? context->args()[0].toNumber() : qt_qnan();
+
+ if (std::isnan(v))
+ return Encode(qt_qnan());
+
+ if (qIsNull(v))
+ return v;
+
+ return Encode(std::signbit(v) ? -1 : 1);
+}
+
ReturnedValue MathObject::method_sin(CallContext *context)
{
double v = context->argc() ? context->args()[0].toNumber() : qt_qnan();
diff --git a/src/qml/jsruntime/qv4mathobject_p.h b/src/qml/jsruntime/qv4mathobject_p.h
index 2b842a312f..f6b1a4395f 100644
--- a/src/qml/jsruntime/qv4mathobject_p.h
+++ b/src/qml/jsruntime/qv4mathobject_p.h
@@ -59,7 +59,7 @@ namespace QV4 {
namespace Heap {
struct MathObject : Object {
- MathObject();
+ void init();
};
}
@@ -84,6 +84,7 @@ struct MathObject: Object
static ReturnedValue method_pow(CallContext *context);
static ReturnedValue method_random(CallContext *context);
static ReturnedValue method_round(CallContext *context);
+ static ReturnedValue method_sign(CallContext *context);
static ReturnedValue method_sin(CallContext *context);
static ReturnedValue method_sqrt(CallContext *context);
static ReturnedValue method_tan(CallContext *context);
diff --git a/src/qml/jsruntime/qv4memberdata.cpp b/src/qml/jsruntime/qv4memberdata.cpp
index 62e4f0a14d..5646a44891 100644
--- a/src/qml/jsruntime/qv4memberdata.cpp
+++ b/src/qml/jsruntime/qv4memberdata.cpp
@@ -58,9 +58,9 @@ static Heap::MemberData *reallocateHelper(ExecutionEngine *e, Heap::MemberData *
Scope scope(e);
Scoped<MemberData> newMemberData(scope, e->memoryManager->allocManaged<MemberData>(alloc));
if (old)
- memcpy(newMemberData->d(), old, sizeof(Heap::MemberData) + old->size * sizeof(Value));
+ memcpy(newMemberData->d_unchecked(), old, sizeof(Heap::MemberData) + old->size * sizeof(Value));
else
- new (newMemberData->d()) Heap::MemberData;
+ newMemberData->d_unchecked()->init();
newMemberData->d()->size = n;
return newMemberData->d();
}
diff --git a/src/qml/jsruntime/qv4memberdata_p.h b/src/qml/jsruntime/qv4memberdata_p.h
index 2742e0b212..969eee3619 100644
--- a/src/qml/jsruntime/qv4memberdata_p.h
+++ b/src/qml/jsruntime/qv4memberdata_p.h
@@ -66,6 +66,7 @@ struct MemberData : Base {
};
Value data[1];
};
+V4_ASSERT_IS_TRIVIAL(MemberData)
}
diff --git a/src/qml/jsruntime/qv4numberobject.cpp b/src/qml/jsruntime/qv4numberobject.cpp
index 0e653c18cb..1733df34ae 100644
--- a/src/qml/jsruntime/qv4numberobject.cpp
+++ b/src/qml/jsruntime/qv4numberobject.cpp
@@ -45,6 +45,7 @@
#include <QtCore/qmath.h>
#include <QtCore/QDebug>
#include <cassert>
+#include <limits>
using namespace QV4;
@@ -70,22 +71,21 @@ const NumberLocale *NumberLocale::instance()
return numberLocaleHolder();
}
-Heap::NumberCtor::NumberCtor(QV4::ExecutionContext *scope)
- : Heap::FunctionObject(scope, QStringLiteral("Number"))
+void Heap::NumberCtor::init(QV4::ExecutionContext *scope)
{
+ Heap::FunctionObject::init(scope, QStringLiteral("Number"));
}
-ReturnedValue NumberCtor::construct(const Managed *m, CallData *callData)
+void NumberCtor::construct(const Managed *, Scope &scope, CallData *callData)
{
- Scope scope(m->cast<NumberCtor>()->engine());
double dbl = callData->argc ? callData->args[0].toNumber() : 0.;
- return Encode(scope.engine->newNumberObject(dbl));
+ scope.result = Encode(scope.engine->newNumberObject(dbl));
}
-ReturnedValue NumberCtor::call(const Managed *, CallData *callData)
+void NumberCtor::call(const Managed *, Scope &scope, CallData *callData)
{
double dbl = callData->argc ? callData->args[0].toNumber() : 0.;
- return Encode(dbl);
+ scope.result = Encode(dbl);
}
void NumberPrototype::init(ExecutionEngine *engine, Object *ctor)
@@ -99,12 +99,16 @@ void NumberPrototype::init(ExecutionEngine *engine, Object *ctor)
ctor->defineReadonlyProperty(QStringLiteral("NEGATIVE_INFINITY"), Primitive::fromDouble(-qInf()));
ctor->defineReadonlyProperty(QStringLiteral("POSITIVE_INFINITY"), Primitive::fromDouble(qInf()));
ctor->defineReadonlyProperty(QStringLiteral("MAX_VALUE"), Primitive::fromDouble(1.7976931348623158e+308));
+ ctor->defineReadonlyProperty(QStringLiteral("EPSILON"), Primitive::fromDouble(std::numeric_limits<double>::epsilon()));
QT_WARNING_PUSH
QT_WARNING_DISABLE_INTEL(239)
ctor->defineReadonlyProperty(QStringLiteral("MIN_VALUE"), Primitive::fromDouble(5e-324));
QT_WARNING_POP
+ ctor->defineDefaultProperty(QStringLiteral("isFinite"), method_isFinite, 1);
+ ctor->defineDefaultProperty(QStringLiteral("isNaN"), method_isNaN, 1);
+
defineDefaultProperty(QStringLiteral("constructor"), (o = ctor));
defineDefaultProperty(engine->id_toString(), method_toString);
defineDefaultProperty(QStringLiteral("toLocaleString"), method_toLocaleString);
@@ -134,6 +138,24 @@ inline double thisNumber(ExecutionContext *ctx)
return n->value();
}
+ReturnedValue NumberPrototype::method_isFinite(CallContext *ctx)
+{
+ if (!ctx->argc())
+ return Encode(false);
+
+ double v = ctx->args()[0].toNumber();
+ return Encode(!std::isnan(v) && !qt_is_inf(v));
+}
+
+ReturnedValue NumberPrototype::method_isNaN(CallContext *ctx)
+{
+ if (!ctx->argc())
+ return Encode(false);
+
+ double v = ctx->args()[0].toNumber();
+ return Encode(std::isnan(v));
+}
+
ReturnedValue NumberPrototype::method_toString(CallContext *ctx)
{
Scope scope(ctx);
diff --git a/src/qml/jsruntime/qv4numberobject_p.h b/src/qml/jsruntime/qv4numberobject_p.h
index ca6f686304..6022b3a029 100644
--- a/src/qml/jsruntime/qv4numberobject_p.h
+++ b/src/qml/jsruntime/qv4numberobject_p.h
@@ -61,7 +61,7 @@ namespace QV4 {
namespace Heap {
struct NumberCtor : FunctionObject {
- NumberCtor(QV4::ExecutionContext *scope);
+ void init(QV4::ExecutionContext *scope);
};
}
@@ -79,14 +79,16 @@ struct NumberCtor: FunctionObject
{
V4_OBJECT2(NumberCtor, FunctionObject)
- static ReturnedValue construct(const Managed *that, CallData *callData);
- static ReturnedValue call(const Managed *, CallData *callData);
+ static void construct(const Managed *that, Scope &scope, CallData *callData);
+ static void call(const Managed *, Scope &scope, CallData *callData);
};
struct NumberPrototype: NumberObject
{
void init(ExecutionEngine *engine, Object *ctor);
+ static ReturnedValue method_isFinite(CallContext *ctx);
+ static ReturnedValue method_isNaN(CallContext *ctx);
static ReturnedValue method_toString(CallContext *ctx);
static ReturnedValue method_toLocaleString(CallContext *ctx);
static ReturnedValue method_valueOf(CallContext *ctx);
diff --git a/src/qml/jsruntime/qv4object.cpp b/src/qml/jsruntime/qv4object.cpp
index 0727f35edd..00e6d230da 100644
--- a/src/qml/jsruntime/qv4object.cpp
+++ b/src/qml/jsruntime/qv4object.cpp
@@ -109,7 +109,8 @@ ReturnedValue Object::getValue(const Value &thisObject, const Value &v, Property
Scope scope(f->engine());
ScopedCallData callData(scope);
callData->thisObject = thisObject;
- return f->call(callData);
+ f->call(scope, callData);
+ return scope.result.asReturnedValue();
}
void Object::putValue(uint memberIndex, const Value &value)
@@ -128,7 +129,7 @@ void Object::putValue(uint memberIndex, const Value &value)
ScopedCallData callData(scope, 1);
callData->args[0] = value;
callData->thisObject = this;
- setter->call(callData);
+ setter->call(scope, callData);
return;
}
goto reject;
@@ -389,14 +390,14 @@ bool Object::hasOwnProperty(uint index) const
return false;
}
-ReturnedValue Object::construct(const Managed *m, CallData *)
+void Object::construct(const Managed *m, Scope &scope, CallData *)
{
- return static_cast<const Object *>(m)->engine()->throwTypeError();
+ scope.result = static_cast<const Object *>(m)->engine()->throwTypeError();
}
-ReturnedValue Object::call(const Managed *m, CallData *)
+void Object::call(const Managed *m, Scope &scope, CallData *)
{
- return static_cast<const Object *>(m)->engine()->throwTypeError();
+ scope.result = static_cast<const Object *>(m)->engine()->throwTypeError();
}
ReturnedValue Object::get(const Managed *m, String *name, bool *hasProperty)
@@ -744,7 +745,7 @@ void Object::internalPut(String *name, const Value &value)
ScopedCallData callData(scope, 1);
callData->args[0] = value;
callData->thisObject = this;
- setter->call(callData);
+ setter->call(scope, callData);
return;
}
@@ -753,9 +754,8 @@ void Object::internalPut(String *name, const Value &value)
reject:
if (engine()->current->strictMode) {
- QString message = QStringLiteral("Cannot assign to read-only property \"");
- message += name->toQString();
- message += QLatin1Char('\"');
+ QString message = QLatin1String("Cannot assign to read-only property \"") +
+ name->toQString() + QLatin1Char('\"');
engine()->throwTypeError(message);
}
}
@@ -815,7 +815,7 @@ void Object::internalPutIndexed(uint index, const Value &value)
ScopedCallData callData(scope, 1);
callData->args[0] = value;
callData->thisObject = this;
- setter->call(callData);
+ setter->call(scope, callData);
return;
}
@@ -1160,10 +1160,10 @@ void Object::initSparseArray()
DEFINE_OBJECT_VTABLE(ArrayObject);
-Heap::ArrayObject::ArrayObject(const QStringList &list)
- : Heap::Object()
+void Heap::ArrayObject::init(const QStringList &list)
{
- init();
+ Object::init();
+ commonInit();
Scope scope(internalClass->engine);
ScopedObject a(scope, this);
diff --git a/src/qml/jsruntime/qv4object_p.h b/src/qml/jsruntime/qv4object_p.h
index 82f75a49c6..d5195adaf0 100644
--- a/src/qml/jsruntime/qv4object_p.h
+++ b/src/qml/jsruntime/qv4object_p.h
@@ -56,6 +56,7 @@
#include "qv4engine_p.h"
#include "qv4scopedvalue_p.h"
#include "qv4value_p.h"
+#include "qv4internalclass_p.h"
#include <QtCore/qtypetraits.h>
@@ -67,7 +68,8 @@ namespace QV4 {
namespace Heap {
struct Object : Base {
- inline Object() {}
+ void init() { Base::init(); }
+ void destroy() { Base::destroy(); }
const Value *propertyData(uint index) const { if (index < inlineMemberSize) return reinterpret_cast<const Value *>(this) + inlineMemberOffset + index; return memberData->data + index - inlineMemberSize; }
Value *propertyData(uint index) { if (index < inlineMemberSize) return reinterpret_cast<Value *>(this) + inlineMemberOffset + index; return memberData->data + index - inlineMemberSize; }
@@ -89,7 +91,13 @@ struct Object : Base {
static const QV4::ObjectVTable static_vtbl; \
static inline const QV4::VTable *staticVTable() { return &static_vtbl.vTable; } \
V4_MANAGED_SIZE_TEST \
- Data *d() const { return static_cast<Data *>(m()); }
+ Data *d_unchecked() const { return static_cast<Data *>(m()); } \
+ Data *d() const { \
+ Data *dptr = d_unchecked(); \
+ dptr->_checkIsInitialized(); \
+ return dptr; \
+ } \
+ V4_ASSERT_IS_TRIVIAL(Data);
#define V4_OBJECT2(DataClass, superClass) \
private: \
@@ -102,7 +110,13 @@ struct Object : Base {
static const QV4::ObjectVTable static_vtbl; \
static inline const QV4::VTable *staticVTable() { return &static_vtbl.vTable; } \
V4_MANAGED_SIZE_TEST \
- QV4::Heap::DataClass *d() const { return static_cast<QV4::Heap::DataClass *>(m()); }
+ QV4::Heap::DataClass *d_unchecked() const { return static_cast<QV4::Heap::DataClass *>(m()); } \
+ QV4::Heap::DataClass *d() const { \
+ QV4::Heap::DataClass *dptr = d_unchecked(); \
+ dptr->_checkIsInitialized(); \
+ return dptr; \
+ } \
+ V4_ASSERT_IS_TRIVIAL(QV4::Heap::DataClass);
#define V4_INTERNALCLASS(c) \
static QV4::InternalClass *defaultInternalClass(QV4::ExecutionEngine *e) \
@@ -114,8 +128,8 @@ struct Object : Base {
struct ObjectVTable
{
VTable vTable;
- ReturnedValue (*call)(const Managed *, CallData *data);
- ReturnedValue (*construct)(const Managed *, CallData *data);
+ void (*call)(const Managed *, Scope &scope, CallData *data);
+ void (*construct)(const Managed *, Scope &scope, CallData *data);
ReturnedValue (*get)(const Managed *, String *name, bool *hasProperty);
ReturnedValue (*getIndexed)(const Managed *, uint index, bool *hasProperty);
void (*put)(Managed *, String *name, const Value &value);
@@ -334,14 +348,14 @@ public:
{ vtable()->advanceIterator(this, it, name, index, p, attributes); }
uint getLength() const { return vtable()->getLength(this); }
- inline ReturnedValue construct(CallData *d) const
- { return vtable()->construct(this, d); }
- inline ReturnedValue call(CallData *d) const
- { return vtable()->call(this, d); }
+ inline void construct(Scope &scope, CallData *d) const
+ { return vtable()->construct(this, scope, d); }
+ inline void call(Scope &scope, CallData *d) const
+ { vtable()->call(this, scope, d); }
protected:
static void markObjects(Heap::Base *that, ExecutionEngine *e);
- static ReturnedValue construct(const Managed *m, CallData *);
- static ReturnedValue call(const Managed *m, CallData *);
+ static void construct(const Managed *m, Scope &scope, CallData *);
+ static void call(const Managed *m, Scope &scope, CallData *);
static ReturnedValue get(const Managed *m, String *name, bool *hasProperty);
static ReturnedValue getIndexed(const Managed *m, uint index, bool *hasProperty);
static void put(Managed *m, String *name, const Value &value);
@@ -372,18 +386,22 @@ private:
namespace Heap {
struct BooleanObject : Object {
- BooleanObject() {}
- BooleanObject(bool b)
- : b(b)
- {}
+ void init() { Object::init(); }
+ void init(bool b) {
+ Object::init();
+ this->b = b;
+ }
+
bool b;
};
struct NumberObject : Object {
- NumberObject() {}
- NumberObject(double val)
- : value(val)
- {}
+ void init() { Object::init(); }
+ void init(double val) {
+ Object::init();
+ value = val;
+ }
+
double value;
};
@@ -392,10 +410,15 @@ struct ArrayObject : Object {
LengthPropertyIndex = 0
};
- ArrayObject()
- { init(); }
- ArrayObject(const QStringList &list);
- void init()
+ void init() {
+ Object::init();
+ commonInit();
+ }
+
+ void init(const QStringList &list);
+
+private:
+ void commonInit()
{ *propertyData(LengthPropertyIndex) = Primitive::fromInt32(0); }
};
diff --git a/src/qml/jsruntime/qv4objectiterator.cpp b/src/qml/jsruntime/qv4objectiterator.cpp
index 4354e09248..7943a13ac0 100644
--- a/src/qml/jsruntime/qv4objectiterator.cpp
+++ b/src/qml/jsruntime/qv4objectiterator.cpp
@@ -45,30 +45,6 @@
using namespace QV4;
-ObjectIterator::ObjectIterator(ExecutionEngine *e, Value *scratch1, Value *scratch2, Object *o, uint flags)
- : engine(e)
- , object(scratch1)
- , current(scratch2)
- , arrayNode(0)
- , arrayIndex(0)
- , memberIndex(0)
- , flags(flags)
-{
- init(o);
-}
-
-ObjectIterator::ObjectIterator(Scope &scope, const Object *o, uint flags)
- : engine(scope.engine)
- , object(scope.alloc(1))
- , current(scope.alloc(1))
- , arrayNode(0)
- , arrayIndex(0)
- , memberIndex(0)
- , flags(flags)
-{
- init(o);
-}
-
void ObjectIterator::init(const Object *o)
{
object->setM(o ? o->m() : 0);
diff --git a/src/qml/jsruntime/qv4objectiterator_p.h b/src/qml/jsruntime/qv4objectiterator_p.h
index 6bef703a4d..98e94a95ea 100644
--- a/src/qml/jsruntime/qv4objectiterator_p.h
+++ b/src/qml/jsruntime/qv4objectiterator_p.h
@@ -57,7 +57,7 @@ QT_BEGIN_NAMESPACE
namespace QV4 {
-struct Q_QML_EXPORT ObjectIterator
+struct Q_QML_EXPORT ObjectIteratorData
{
enum Flags {
NoFlags = 0,
@@ -72,21 +72,52 @@ struct Q_QML_EXPORT ObjectIterator
uint arrayIndex;
uint memberIndex;
uint flags;
+};
+V4_ASSERT_IS_TRIVIAL(ObjectIteratorData)
+
+struct Q_QML_EXPORT ObjectIterator: ObjectIteratorData
+{
+ ObjectIterator(ExecutionEngine *e, Value *scratch1, Value *scratch2, Object *o, uint flags)
+ {
+ engine = e;
+ object = scratch1;
+ current = scratch2;
+ arrayNode = nullptr;
+ arrayIndex = 0;
+ memberIndex = 0;
+ this->flags = flags;
+ init(o);
+ }
+
+ ObjectIterator(Scope &scope, const Object *o, uint flags)
+ {
+ engine = scope.engine;
+ object = scope.alloc(1);
+ current = scope.alloc(1);
+ arrayNode = nullptr;
+ arrayIndex = 0;
+ memberIndex = 0;
+ this->flags = flags;
+ init(o);
+ }
- ObjectIterator(ExecutionEngine *e, Value *scratch1, Value *scratch2, Object *o, uint flags);
- ObjectIterator(Scope &scope, const Object *o, uint flags);
- void init(const Object *o);
void next(Value *name, uint *index, Property *pd, PropertyAttributes *attributes = 0);
ReturnedValue nextPropertyName(Value *value);
ReturnedValue nextPropertyNameAsString(Value *value);
ReturnedValue nextPropertyNameAsString();
+
+private:
+ void init(const Object *o);
};
namespace Heap {
struct ForEachIteratorObject : Object {
- ForEachIteratorObject(QV4::Object *o);
- ObjectIterator it;
+ void init(QV4::Object *o);
+ ObjectIterator &it() { return *reinterpret_cast<ObjectIterator*>(&itData); }
Value workArea[2];
+
+private:
+ ObjectIteratorData itData;
};
}
@@ -95,16 +126,18 @@ struct ForEachIteratorObject: Object {
V4_OBJECT2(ForEachIteratorObject, Object)
Q_MANAGED_TYPE(ForeachIteratorObject)
- ReturnedValue nextPropertyName() { return d()->it.nextPropertyNameAsString(); }
+ ReturnedValue nextPropertyName() { return d()->it().nextPropertyNameAsString(); }
protected:
static void markObjects(Heap::Base *that, ExecutionEngine *e);
};
inline
-Heap::ForEachIteratorObject::ForEachIteratorObject(QV4::Object *o)
- : it(internalClass->engine, workArea, workArea + 1, o, ObjectIterator::EnumerableOnly|ObjectIterator::WithProtoChain)
+void Heap::ForEachIteratorObject::init(QV4::Object *o)
{
+ Object::init();
+ it() = ObjectIterator(internalClass->engine, workArea, workArea + 1, o,
+ ObjectIterator::EnumerableOnly | ObjectIterator::WithProtoChain);
}
diff --git a/src/qml/jsruntime/qv4objectproto.cpp b/src/qml/jsruntime/qv4objectproto.cpp
index 015294e48a..6020c48250 100644
--- a/src/qml/jsruntime/qv4objectproto.cpp
+++ b/src/qml/jsruntime/qv4objectproto.cpp
@@ -54,33 +54,35 @@ using namespace QV4;
DEFINE_OBJECT_VTABLE(ObjectCtor);
-Heap::ObjectCtor::ObjectCtor(QV4::ExecutionContext *scope)
- : Heap::FunctionObject(scope, QStringLiteral("Object"))
+void Heap::ObjectCtor::init(QV4::ExecutionContext *scope)
{
+ Heap::FunctionObject::init(scope, QStringLiteral("Object"));
}
-ReturnedValue ObjectCtor::construct(const Managed *that, CallData *callData)
+void ObjectCtor::construct(const Managed *that, Scope &scope, CallData *callData)
{
const ObjectCtor *ctor = static_cast<const ObjectCtor *>(that);
ExecutionEngine *v4 = ctor->engine();
- Scope scope(v4);
if (!callData->argc || callData->args[0].isUndefined() || callData->args[0].isNull()) {
ScopedObject obj(scope, v4->newObject());
ScopedObject proto(scope, ctor->get(v4->id_prototype()));
if (!!proto)
obj->setPrototype(proto);
- return obj.asReturnedValue();
+ scope.result = obj.asReturnedValue();
+ } else {
+ scope.result = RuntimeHelpers::toObject(scope.engine, callData->args[0]);
}
- return RuntimeHelpers::toObject(scope.engine, callData->args[0]);
}
-ReturnedValue ObjectCtor::call(const Managed *m, CallData *callData)
+void ObjectCtor::call(const Managed *m, Scope &scope, CallData *callData)
{
const ObjectCtor *ctor = static_cast<const ObjectCtor *>(m);
ExecutionEngine *v4 = ctor->engine();
- if (!callData->argc || callData->args[0].isUndefined() || callData->args[0].isNull())
- return v4->newObject()->asReturnedValue();
- return RuntimeHelpers::toObject(v4, callData->args[0]);
+ if (!callData->argc || callData->args[0].isUndefined() || callData->args[0].isNull()) {
+ scope.result = v4->newObject()->asReturnedValue();
+ } else {
+ scope.result = RuntimeHelpers::toObject(v4, callData->args[0]);
+ }
}
void ObjectPrototype::init(ExecutionEngine *v4, Object *ctor)
@@ -413,7 +415,8 @@ ReturnedValue ObjectPrototype::method_toLocaleString(CallContext *ctx)
return ctx->engine()->throwTypeError();
ScopedCallData callData(scope);
callData->thisObject = o;
- return f->call(callData);
+ f->call(scope, callData);
+ return scope.result.asReturnedValue();
}
ReturnedValue ObjectPrototype::method_valueOf(CallContext *ctx)
diff --git a/src/qml/jsruntime/qv4objectproto_p.h b/src/qml/jsruntime/qv4objectproto_p.h
index ac1964103e..e3d85782d5 100644
--- a/src/qml/jsruntime/qv4objectproto_p.h
+++ b/src/qml/jsruntime/qv4objectproto_p.h
@@ -61,7 +61,7 @@ namespace QV4 {
namespace Heap {
struct ObjectCtor : FunctionObject {
- ObjectCtor(QV4::ExecutionContext *scope);
+ void init(QV4::ExecutionContext *scope);
};
}
@@ -70,8 +70,8 @@ struct ObjectCtor: FunctionObject
{
V4_OBJECT2(ObjectCtor, FunctionObject)
- static ReturnedValue construct(const Managed *that, CallData *callData);
- static ReturnedValue call(const Managed *that, CallData *callData);
+ static void construct(const Managed *that, Scope &scope, CallData *callData);
+ static void call(const Managed *that, Scope &scope, CallData *callData);
};
struct ObjectPrototype: Object
diff --git a/src/qml/jsruntime/qv4persistent.cpp b/src/qml/jsruntime/qv4persistent.cpp
index a892194df3..987c322e47 100644
--- a/src/qml/jsruntime/qv4persistent.cpp
+++ b/src/qml/jsruntime/qv4persistent.cpp
@@ -358,14 +358,14 @@ WeakValue::WeakValue(const WeakValue &other)
: val(0)
{
if (other.val) {
- val = other.engine()->memoryManager->m_weakValues->allocate();
+ allocVal(other.engine());
*val = *other.val;
}
}
WeakValue::WeakValue(ExecutionEngine *engine, const Value &value)
{
- val = engine->memoryManager->m_weakValues->allocate();
+ allocVal(engine);
*val = value;
}
@@ -374,7 +374,7 @@ WeakValue &WeakValue::operator=(const WeakValue &other)
if (!val) {
if (!other.val)
return *this;
- val = other.engine()->memoryManager->m_weakValues->allocate();
+ allocVal(other.engine());
}
Q_ASSERT(engine() == other.engine());
@@ -388,25 +388,9 @@ WeakValue::~WeakValue()
free();
}
-void WeakValue::set(ExecutionEngine *engine, const Value &value)
-{
- if (!val)
- val = engine->memoryManager->m_weakValues->allocate();
- *val = value;
-}
-
-void WeakValue::set(ExecutionEngine *engine, ReturnedValue value)
-{
- if (!val)
- val = engine->memoryManager->m_weakValues->allocate();
- *val = value;
-}
-
-void WeakValue::set(ExecutionEngine *engine, Heap::Base *obj)
+void WeakValue::allocVal(ExecutionEngine *engine)
{
- if (!val)
- val = engine->memoryManager->m_weakValues->allocate();
- *val = obj;
+ val = engine->memoryManager->m_weakValues->allocate();
}
void WeakValue::markOnce(ExecutionEngine *e)
diff --git a/src/qml/jsruntime/qv4persistent_p.h b/src/qml/jsruntime/qv4persistent_p.h
index 5b1926468a..c1cd1f34df 100644
--- a/src/qml/jsruntime/qv4persistent_p.h
+++ b/src/qml/jsruntime/qv4persistent_p.h
@@ -118,7 +118,7 @@ public:
Managed *asManaged() const {
if (!val)
return 0;
- return val->as<Managed>();
+ return val->managed();
}
template<typename T>
T *as() const {
@@ -154,9 +154,26 @@ public:
WeakValue &operator=(const WeakValue &other);
~WeakValue();
- void set(ExecutionEngine *engine, const Value &value);
- void set(ExecutionEngine *engine, ReturnedValue value);
- void set(ExecutionEngine *engine, Heap::Base *obj);
+ void set(ExecutionEngine *engine, const Value &value)
+ {
+ if (!val)
+ allocVal(engine);
+ *val = value;
+ }
+
+ void set(ExecutionEngine *engine, ReturnedValue value)
+ {
+ if (!val)
+ allocVal(engine);
+ *val = value;
+ }
+
+ void set(ExecutionEngine *engine, Heap::Base *obj)
+ {
+ if (!val)
+ allocVal(engine);
+ *val = obj;
+ }
ReturnedValue value() const {
return (val ? val->asReturnedValue() : Encode::undefined());
@@ -167,7 +184,7 @@ public:
Managed *asManaged() const {
if (!val)
return 0;
- return val->as<Managed>();
+ return val->managed();
}
template <typename T>
T *as() const {
@@ -192,6 +209,8 @@ private:
Value *val;
private:
+ Q_NEVER_INLINE void allocVal(ExecutionEngine *engine);
+
void free();
};
diff --git a/src/qml/jsruntime/qv4profiling.cpp b/src/qml/jsruntime/qv4profiling.cpp
index a59190b846..349ec48e06 100644
--- a/src/qml/jsruntime/qv4profiling.cpp
+++ b/src/qml/jsruntime/qv4profiling.cpp
@@ -48,13 +48,10 @@ namespace Profiling {
FunctionLocation FunctionCall::resolveLocation() const
{
- FunctionLocation location = {
- m_function->name()->toQString(),
- m_function->compilationUnit->fileName(),
- m_function->compiledFunction->location.line,
- m_function->compiledFunction->location.column
- };
- return location;
+ return FunctionLocation(m_function->name()->toQString(),
+ m_function->compilationUnit->fileName(),
+ m_function->compiledFunction->location.line,
+ m_function->compiledFunction->location.column);
}
FunctionCallProperties FunctionCall::properties() const
@@ -81,7 +78,8 @@ Profiler::Profiler(QV4::ExecutionEngine *engine) : featuresEnabled(0), m_engine(
void Profiler::stopProfiling()
{
featuresEnabled = 0;
- reportData();
+ reportData(true);
+ m_sentLocations.clear();
}
bool operator<(const FunctionCall &call1, const FunctionCall &call2)
@@ -91,16 +89,24 @@ bool operator<(const FunctionCall &call1, const FunctionCall &call2)
(call1.m_end == call2.m_end && call1.m_function < call2.m_function)));
}
-void Profiler::reportData()
+void Profiler::reportData(bool trackLocations)
{
std::sort(m_data.begin(), m_data.end());
QVector<FunctionCallProperties> properties;
- QHash<qint64, FunctionLocation> locations;
+ FunctionLocationHash locations;
properties.reserve(m_data.size());
foreach (const FunctionCall &call, m_data) {
properties.append(call.properties());
- locations[properties.constLast().id] = call.resolveLocation();
+ Function *function = call.function();
+ SentMarker &marker = m_sentLocations[reinterpret_cast<quintptr>(function)];
+ if (!trackLocations || !marker.isValid()) {
+ FunctionLocation &location = locations[properties.constLast().id];
+ if (!location.isValid())
+ location = call.resolveLocation();
+ if (trackLocations)
+ marker.setFunction(function);
+ }
}
emit dataReady(locations, properties, m_memory_data);
diff --git a/src/qml/jsruntime/qv4profiling_p.h b/src/qml/jsruntime/qv4profiling_p.h
index 0b4193204f..e06cb64a61 100644
--- a/src/qml/jsruntime/qv4profiling_p.h
+++ b/src/qml/jsruntime/qv4profiling_p.h
@@ -57,6 +57,40 @@
#include <QElapsedTimer>
+#ifdef QT_NO_QML_DEBUGGER
+
+#define Q_V4_PROFILE_ALLOC(engine, size, type) (!engine)
+#define Q_V4_PROFILE_DEALLOC(engine, size, type) (!engine)
+#define Q_V4_PROFILE(engine, function) (function->code(engine, function->codeData))
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+namespace Profiling {
+struct Profiler {};
+}
+}
+
+QT_END_NAMESPACE
+
+#else
+
+#define Q_V4_PROFILE_ALLOC(engine, size, type)\
+ (engine->profiler() &&\
+ (engine->profiler()->featuresEnabled & (1 << Profiling::FeatureMemoryAllocation)) ?\
+ engine->profiler()->trackAlloc(size, type) : false)
+
+#define Q_V4_PROFILE_DEALLOC(engine, size, type) \
+ (engine->profiler() &&\
+ (engine->profiler()->featuresEnabled & (1 << Profiling::FeatureMemoryAllocation)) ?\
+ engine->profiler()->trackDealloc(size, type) : false)
+
+#define Q_V4_PROFILE(engine, function)\
+ (engine->profiler() &&\
+ (engine->profiler()->featuresEnabled & (1 << Profiling::FeatureFunctionCall)) ?\
+ Profiling::FunctionCallProfiler::profileCall(engine->profiler(), engine, function) :\
+ function->code(engine, function->codeData))
+
QT_BEGIN_NAMESPACE
namespace QV4 {
@@ -81,13 +115,23 @@ struct FunctionCallProperties {
};
struct FunctionLocation {
+ FunctionLocation(const QString &name = QString(), const QString &file = QString(),
+ int line = -1, int column = -1) :
+ name(name), file(file), line(line), column(column)
+ {}
+
+ bool isValid()
+ {
+ return !name.isEmpty();
+ }
+
QString name;
QString file;
int line;
int column;
};
-typedef QHash<qint64, QV4::Profiling::FunctionLocation> FunctionLocationHash;
+typedef QHash<quintptr, QV4::Profiling::FunctionLocation> FunctionLocationHash;
struct MemoryAllocationProperties {
qint64 timestamp;
@@ -124,6 +168,11 @@ public:
return *this;
}
+ Function *function() const
+ {
+ return m_function;
+ }
+
FunctionLocation resolveLocation() const;
FunctionCallProperties properties() const;
@@ -135,48 +184,70 @@ private:
qint64 m_end;
};
-#define Q_V4_PROFILE_ALLOC(engine, size, type)\
- (engine->profiler &&\
- (engine->profiler->featuresEnabled & (1 << Profiling::FeatureMemoryAllocation)) ?\
- engine->profiler->trackAlloc(size, type) : size)
-
-#define Q_V4_PROFILE_DEALLOC(engine, pointer, size, type) \
- (engine->profiler &&\
- (engine->profiler->featuresEnabled & (1 << Profiling::FeatureMemoryAllocation)) ?\
- engine->profiler->trackDealloc(pointer, size, type) : pointer)
-
-#define Q_V4_PROFILE(engine, function)\
- (engine->profiler &&\
- (engine->profiler->featuresEnabled & (1 << Profiling::FeatureFunctionCall)) ?\
- Profiling::FunctionCallProfiler::profileCall(engine->profiler, engine, function) :\
- function->code(engine, function->codeData))
-
class Q_QML_EXPORT Profiler : public QObject {
Q_OBJECT
- Q_DISABLE_COPY(Profiler)
public:
+ struct SentMarker {
+ SentMarker() : m_function(nullptr) {}
+
+ SentMarker(const SentMarker &other) : m_function(other.m_function)
+ {
+ if (m_function)
+ m_function->compilationUnit->addref();
+ }
+
+ ~SentMarker()
+ {
+ if (m_function)
+ m_function->compilationUnit->release();
+ }
+
+ SentMarker &operator=(const SentMarker &other)
+ {
+ if (&other != this) {
+ if (m_function)
+ m_function->compilationUnit->release();
+ m_function = other.m_function;
+ m_function->compilationUnit->addref();
+ }
+ return *this;
+ }
+
+ void setFunction(Function *function)
+ {
+ Q_ASSERT(m_function == nullptr);
+ m_function = function;
+ m_function->compilationUnit->addref();
+ }
+
+ bool isValid() const
+ { return m_function != nullptr; }
+
+ private:
+ Function *m_function;
+ };
+
Profiler(QV4::ExecutionEngine *engine);
- size_t trackAlloc(size_t size, MemoryType type)
+ bool trackAlloc(size_t size, MemoryType type)
{
MemoryAllocationProperties allocation = {m_timer.nsecsElapsed(), (qint64)size, type};
m_memory_data.append(allocation);
- return size;
+ return true;
}
- void *trackDealloc(void *pointer, size_t size, MemoryType type)
+ bool trackDealloc(size_t size, MemoryType type)
{
MemoryAllocationProperties allocation = {m_timer.nsecsElapsed(), -(qint64)size, type};
m_memory_data.append(allocation);
- return pointer;
+ return true;
}
quint64 featuresEnabled;
-public slots:
void stopProfiling();
void startProfiling(quint64 features);
- void reportData();
+ void reportData(bool trackLocations);
void setTimer(const QElapsedTimer &timer) { m_timer = timer; }
signals:
@@ -189,6 +260,7 @@ private:
QElapsedTimer m_timer;
QVector<FunctionCall> m_data;
QVector<MemoryAllocationProperties> m_memory_data;
+ QHash<quintptr, SentMarker> m_sentLocations;
friend class FunctionCallProfiler;
};
@@ -227,10 +299,13 @@ Q_DECLARE_TYPEINFO(QV4::Profiling::MemoryAllocationProperties, Q_MOVABLE_TYPE);
Q_DECLARE_TYPEINFO(QV4::Profiling::FunctionCallProperties, Q_MOVABLE_TYPE);
Q_DECLARE_TYPEINFO(QV4::Profiling::FunctionCall, Q_MOVABLE_TYPE);
Q_DECLARE_TYPEINFO(QV4::Profiling::FunctionLocation, Q_MOVABLE_TYPE);
+Q_DECLARE_TYPEINFO(QV4::Profiling::Profiler::SentMarker, Q_MOVABLE_TYPE);
QT_END_NAMESPACE
Q_DECLARE_METATYPE(QV4::Profiling::FunctionLocationHash)
Q_DECLARE_METATYPE(QVector<QV4::Profiling::FunctionCallProperties>)
Q_DECLARE_METATYPE(QVector<QV4::Profiling::MemoryAllocationProperties>)
+#endif // QT_NO_QML_DEBUGGER
+
#endif // QV4PROFILING_H
diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp
index 2c9fc8f9dd..d91965a350 100644
--- a/src/qml/jsruntime/qv4qobjectwrapper.cpp
+++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp
@@ -44,7 +44,6 @@
#include <private/qqmlvmemetaobject_p.h>
#include <private/qqmlbinding_p.h>
#include <private/qjsvalue_p.h>
-#include <private/qqmlaccessors_p.h>
#include <private/qqmlexpression_p.h>
#include <private/qqmlglobal_p.h>
#include <private/qqmltypewrapper_p.h>
@@ -54,6 +53,7 @@
#include <private/qqmlbuiltinfunctions_p.h>
#include <private/qv8engine_p.h>
+#include <private/qv4arraybuffer_p.h>
#include <private/qv4functionobject_p.h>
#include <private/qv4runtime_p.h>
#include <private/qv4variantobject_p.h>
@@ -74,6 +74,7 @@
#include <QtCore/qvarlengtharray.h>
#include <QtCore/qtimer.h>
#include <QtCore/qatomic.h>
+#include <QtCore/qmetaobject.h>
QT_BEGIN_NAMESPACE
@@ -83,7 +84,7 @@ QT_WARNING_DISABLE_GCC("-Wstrict-aliasing")
using namespace QV4;
-static QPair<QObject *, int> extractQtMethod(QV4::FunctionObject *function)
+QPair<QObject *, int> QObjectMethod::extractQtMethod(const QV4::FunctionObject *function)
{
QV4::ExecutionEngine *v4 = function->engine();
if (v4) {
@@ -103,7 +104,7 @@ static QPair<QObject *, int> extractQtSignal(const Value &value)
QV4::Scope scope(v4);
QV4::ScopedFunctionObject function(scope, value);
if (function)
- return extractQtMethod(function);
+ return QObjectMethod::extractQtMethod(function);
QV4::Scoped<QV4::QmlSignalHandler> handler(scope, value);
if (handler)
@@ -113,132 +114,87 @@ static QPair<QObject *, int> extractQtSignal(const Value &value)
return qMakePair((QObject *)0, -1);
}
-
-struct ReadAccessor {
- static inline void Indirect(QObject *object, const QQmlPropertyData &property,
- void *output, QQmlNotifier **n)
- {
- Q_ASSERT(n == 0);
- Q_UNUSED(n);
-
- void *args[] = { output, 0 };
- QMetaObject::metacall(object, QMetaObject::ReadProperty, property.coreIndex, args);
- }
-
- static inline void Direct(QObject *object, const QQmlPropertyData &property,
- void *output, QQmlNotifier **n)
- {
- Q_ASSERT(n == 0);
- Q_UNUSED(n);
-
- void *args[] = { output, 0 };
- object->qt_metacall(QMetaObject::ReadProperty, property.coreIndex, args);
- }
-
- static inline void Accessor(QObject *object, const QQmlPropertyData &property,
- void *output, QQmlNotifier **n)
- {
- Q_ASSERT(property.accessors);
-
- property.accessors->read(object, output);
- if (n) property.accessors->notifier(object, n);
- }
-};
-
-// Load value properties
-template<void (*ReadFunction)(QObject *, const QQmlPropertyData &,
- void *, QQmlNotifier **)>
-static QV4::ReturnedValue LoadProperty(QV4::ExecutionEngine *v4, QObject *object,
- const QQmlPropertyData &property,
- QQmlNotifier **notifier)
+static QV4::ReturnedValue loadProperty(QV4::ExecutionEngine *v4, QObject *object,
+ const QQmlPropertyData &property)
{
Q_ASSERT(!property.isFunction());
QV4::Scope scope(v4);
if (property.isQObject()) {
QObject *rv = 0;
- ReadFunction(object, property, &rv, notifier);
+ property.readProperty(object, &rv);
return QV4::QObjectWrapper::wrap(v4, rv);
} else if (property.isQList()) {
- return QmlListWrapper::create(v4, object, property.coreIndex, property.propType);
- } else if (property.propType == QMetaType::QReal) {
+ return QmlListWrapper::create(v4, object, property.coreIndex(), property.propType());
+ } else if (property.propType() == QMetaType::QReal) {
qreal v = 0;
- ReadFunction(object, property, &v, notifier);
+ property.readProperty(object, &v);
return QV4::Encode(v);
- } else if (property.propType == QMetaType::Int || property.isEnum()) {
+ } else if (property.propType() == QMetaType::Int || property.isEnum()) {
int v = 0;
- ReadFunction(object, property, &v, notifier);
+ property.readProperty(object, &v);
return QV4::Encode(v);
- } else if (property.propType == QMetaType::Bool) {
+ } else if (property.propType() == QMetaType::Bool) {
bool v = false;
- ReadFunction(object, property, &v, notifier);
+ property.readProperty(object, &v);
return QV4::Encode(v);
- } else if (property.propType == QMetaType::QString) {
+ } else if (property.propType() == QMetaType::QString) {
QString v;
- ReadFunction(object, property, &v, notifier);
+ property.readProperty(object, &v);
return v4->newString(v)->asReturnedValue();
- } else if (property.propType == QMetaType::UInt) {
+ } else if (property.propType() == QMetaType::UInt) {
uint v = 0;
- ReadFunction(object, property, &v, notifier);
+ property.readProperty(object, &v);
return QV4::Encode(v);
- } else if (property.propType == QMetaType::Float) {
+ } else if (property.propType() == QMetaType::Float) {
float v = 0;
- ReadFunction(object, property, &v, notifier);
+ property.readProperty(object, &v);
return QV4::Encode(v);
- } else if (property.propType == QMetaType::Double) {
+ } else if (property.propType() == QMetaType::Double) {
double v = 0;
- ReadFunction(object, property, &v, notifier);
+ property.readProperty(object, &v);
return QV4::Encode(v);
} else if (property.isV4Handle()) {
QQmlV4Handle handle;
- ReadFunction(object, property, &handle, notifier);
+ property.readProperty(object, &handle);
return handle;
- } else if (property.propType == qMetaTypeId<QJSValue>()) {
+ } else if (property.propType() == qMetaTypeId<QJSValue>()) {
QJSValue v;
- ReadFunction(object, property, &v, notifier);
+ property.readProperty(object, &v);
return QJSValuePrivate::convertedToValue(v4, v);
} else if (property.isQVariant()) {
QVariant v;
- ReadFunction(object, property, &v, notifier);
+ property.readProperty(object, &v);
if (QQmlValueTypeFactory::isValueType(v.userType())) {
if (const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(v.userType()))
- return QV4::QQmlValueTypeWrapper::create(v4, object, property.coreIndex, valueTypeMetaObject, v.userType()); // VariantReference value-type.
+ return QV4::QQmlValueTypeWrapper::create(v4, object, property.coreIndex(), valueTypeMetaObject, v.userType()); // VariantReference value-type.
}
return scope.engine->fromVariant(v);
- } else if (QQmlValueTypeFactory::isValueType(property.propType)) {
- Q_ASSERT(notifier == 0);
-
- if (const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(property.propType))
- return QV4::QQmlValueTypeWrapper::create(v4, object, property.coreIndex, valueTypeMetaObject, property.propType);
+ } else if (QQmlValueTypeFactory::isValueType(property.propType())) {
+ if (const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(property.propType()))
+ return QV4::QQmlValueTypeWrapper::create(v4, object, property.coreIndex(), valueTypeMetaObject, property.propType());
} else {
- Q_ASSERT(notifier == 0);
-
// see if it's a sequence type
bool succeeded = false;
- QV4::ScopedValue retn(scope, QV4::SequencePrototype::newSequence(v4, property.propType, object, property.coreIndex, &succeeded));
+ QV4::ScopedValue retn(scope, QV4::SequencePrototype::newSequence(v4, property.propType(), object, property.coreIndex(), &succeeded));
if (succeeded)
return retn->asReturnedValue();
}
- if (property.propType == QMetaType::UnknownType) {
- QMetaProperty p = object->metaObject()->property(property.coreIndex);
+ if (property.propType() == QMetaType::UnknownType) {
+ QMetaProperty p = object->metaObject()->property(property.coreIndex());
qWarning("QMetaProperty::read: Unable to handle unregistered datatype '%s' for property "
"'%s::%s'", p.typeName(), object->metaObject()->className(), p.name());
return QV4::Encode::undefined();
} else {
- QVariant v(property.propType, (void *)0);
- ReadFunction(object, property, v.data(), notifier);
+ QVariant v(property.propType(), (void *)0);
+ property.readProperty(object, v.data());
return scope.engine->fromVariant(v);
}
}
-Heap::QObjectWrapper::QObjectWrapper(QObject *object)
- : object(object)
-{
-}
-
void QObjectWrapper::initializeBindings(ExecutionEngine *engine)
{
engine->functionPrototype()->defineDefaultProperty(QStringLiteral("connect"), method_connect);
@@ -249,21 +205,21 @@ QQmlPropertyData *QObjectWrapper::findProperty(ExecutionEngine *engine, QQmlCont
{
Q_UNUSED(revisionMode);
- QQmlData *ddata = QQmlData::get(d()->object, false);
+ QQmlData *ddata = QQmlData::get(d()->object(), false);
if (!ddata)
return 0;
QQmlPropertyData *result = 0;
if (ddata && ddata->propertyCache)
- result = ddata->propertyCache->property(name, d()->object, qmlContext);
+ result = ddata->propertyCache->property(name, d()->object(), qmlContext);
else
- result = QQmlPropertyCache::property(engine->jsEngine(), d()->object, name, qmlContext, *local);
+ result = QQmlPropertyCache::property(engine->jsEngine(), d()->object(), name, qmlContext, *local);
return result;
}
ReturnedValue QObjectWrapper::getQmlProperty(QQmlContextData *qmlContext, String *name, QObjectWrapper::RevisionMode revisionMode,
bool *hasProperty, bool includeImports) const
{
- if (QQmlData::wasDeleted(d()->object)) {
+ if (QQmlData::wasDeleted(d()->object())) {
if (hasProperty)
*hasProperty = false;
return QV4::Encode::undefined();
@@ -276,7 +232,7 @@ ReturnedValue QObjectWrapper::getQmlProperty(QQmlContextData *qmlContext, String
if (hasProperty)
*hasProperty = true;
ExecutionContext *global = v4->rootContext();
- return QV4::QObjectMethod::create(global, d()->object, index);
+ return QV4::QObjectMethod::create(global, d()->object(), index);
}
QQmlPropertyData local;
@@ -295,10 +251,10 @@ ReturnedValue QObjectWrapper::getQmlProperty(QQmlContextData *qmlContext, String
if (r.scriptIndex != -1) {
return QV4::Encode::undefined();
} else if (r.type) {
- return QmlTypeWrapper::create(v4, d()->object,
+ return QmlTypeWrapper::create(v4, d()->object(),
r.type, Heap::QmlTypeWrapper::ExcludeEnums);
} else if (r.importNamespace) {
- return QmlTypeWrapper::create(v4, d()->object,
+ return QmlTypeWrapper::create(v4, d()->object(),
qmlContext->imports, r.importNamespace, Heap::QmlTypeWrapper::ExcludeEnums);
}
Q_ASSERT(!"Unreachable");
@@ -308,7 +264,7 @@ ReturnedValue QObjectWrapper::getQmlProperty(QQmlContextData *qmlContext, String
return QV4::Object::get(this, name, hasProperty);
}
- QQmlData *ddata = QQmlData::get(d()->object, false);
+ QQmlData *ddata = QQmlData::get(d()->object(), false);
if (revisionMode == QV4::QObjectWrapper::CheckRevision && result->hasRevision()) {
if (ddata && ddata->propertyCache && !ddata->propertyCache->isAllowedInRevision(result)) {
@@ -321,69 +277,44 @@ ReturnedValue QObjectWrapper::getQmlProperty(QQmlContextData *qmlContext, String
if (hasProperty)
*hasProperty = true;
- return getProperty(v4, d()->object, result);
+ return getProperty(v4, d()->object(), result);
}
ReturnedValue QObjectWrapper::getProperty(ExecutionEngine *engine, QObject *object, QQmlPropertyData *property, bool captureRequired)
{
- QQmlData::flushPendingBinding(object, property->coreIndex);
+ QQmlData::flushPendingBinding(object, QQmlPropertyIndex(property->coreIndex()));
if (property->isFunction() && !property->isVarProperty()) {
if (property->isVMEFunction()) {
QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(object);
Q_ASSERT(vmemo);
- return vmemo->vmeMethod(property->coreIndex);
+ return vmemo->vmeMethod(property->coreIndex());
} else if (property->isV4Function()) {
Scope scope(engine);
ScopedContext global(scope, engine->qmlContext());
if (!global)
global = engine->rootContext();
- return QV4::QObjectMethod::create(global, object, property->coreIndex);
+ return QV4::QObjectMethod::create(global, object, property->coreIndex());
} else if (property->isSignalHandler()) {
QmlSignalHandler::initProto(engine);
- return engine->memoryManager->allocObject<QV4::QmlSignalHandler>(object, property->coreIndex)->asReturnedValue();
+ return engine->memoryManager->allocObject<QV4::QmlSignalHandler>(object, property->coreIndex())->asReturnedValue();
} else {
ExecutionContext *global = engine->rootContext();
- return QV4::QObjectMethod::create(global, object, property->coreIndex);
+ return QV4::QObjectMethod::create(global, object, property->coreIndex());
}
}
QQmlEnginePrivate *ep = engine->qmlEngine() ? QQmlEnginePrivate::get(engine->qmlEngine()) : 0;
- if (property->hasAccessors()) {
- QQmlNotifier *n = 0;
- QQmlNotifier **nptr = 0;
-
- if (ep && ep->propertyCapture && property->accessors->notifier)
- nptr = &n;
-
- Scope scope(engine);
- QV4::ScopedValue rv(scope, LoadProperty<ReadAccessor::Accessor>(engine, object, *property, nptr));
-
- if (captureRequired) {
- if (property->accessors->notifier) {
- if (n && ep->propertyCapture)
- ep->propertyCapture->captureProperty(n);
- } else {
- if (ep->propertyCapture)
- ep->propertyCapture->captureProperty(object, property->coreIndex, property->notifyIndex);
- }
- }
-
- return rv->asReturnedValue();
- }
-
if (captureRequired && ep && ep->propertyCapture && !property->isConstant())
- ep->propertyCapture->captureProperty(object, property->coreIndex, property->notifyIndex);
+ ep->propertyCapture->captureProperty(object, property->coreIndex(), property->notifyIndex());
if (property->isVarProperty()) {
QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(object);
Q_ASSERT(vmemo);
- return vmemo->vmeProperty(property->coreIndex);
- } else if (property->isDirect()) {
- return LoadProperty<ReadAccessor::Direct>(engine, object, *property, 0);
+ return vmemo->vmeProperty(property->coreIndex());
} else {
- return LoadProperty<ReadAccessor::Indirect>(engine, object, *property, 0);
+ return loadProperty(engine, object, *property);
}
}
@@ -446,13 +377,13 @@ void QObjectWrapper::setProperty(ExecutionEngine *engine, QObject *object, QQmlP
QV4::ScopedFunctionObject f(scope, value);
if (f) {
if (!f->isBinding()) {
- if (!property->isVarProperty() && property->propType != qMetaTypeId<QJSValue>()) {
+ if (!property->isVarProperty() && property->propType() != qMetaTypeId<QJSValue>()) {
// assigning a JS function to a non var or QJSValue property or is not allowed.
QString error = QLatin1String("Cannot assign JavaScript function to ");
- if (!QMetaType::typeName(property->propType))
+ if (!QMetaType::typeName(property->propType()))
error += QLatin1String("[unknown property type]");
else
- error += QLatin1String(QMetaType::typeName(property->propType));
+ error += QLatin1String(QMetaType::typeName(property->propType()));
scope.engine->throwError(error);
return;
}
@@ -463,21 +394,21 @@ void QObjectWrapper::setProperty(ExecutionEngine *engine, QObject *object, QQmlP
QV4::Scoped<QQmlBindingFunction> bindingFunction(scope, (const Value &)f);
bindingFunction->initBindingLocation();
- newBinding = new QQmlBinding(value, object, callingQmlContext);
- newBinding->setTarget(object, *property);
+ newBinding = QQmlBinding::create(property, value, object, callingQmlContext);
+ newBinding->setTarget(object, *property, nullptr);
}
}
if (newBinding)
QQmlPropertyPrivate::setBinding(newBinding);
else
- QQmlPropertyPrivate::removeBinding(object, property->encodedIndex());
+ QQmlPropertyPrivate::removeBinding(object, QQmlPropertyIndex(property->coreIndex()));
if (!newBinding && property->isVarProperty()) {
// allow assignment of "special" values (null, undefined, function) to var properties
QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(object);
Q_ASSERT(vmemo);
- vmemo->setVMEProperty(property->coreIndex, value);
+ vmemo->setVMEProperty(property->coreIndex(), value);
return;
}
@@ -486,44 +417,44 @@ void QObjectWrapper::setProperty(ExecutionEngine *engine, QObject *object, QQmlP
int status = -1; \
int flags = 0; \
void *argv[] = { &o, 0, &status, &flags }; \
- QMetaObject::metacall(object, QMetaObject::WriteProperty, property->coreIndex, argv);
+ QMetaObject::metacall(object, QMetaObject::WriteProperty, property->coreIndex(), argv);
if (value.isNull() && property->isQObject()) {
PROPERTY_STORE(QObject*, 0);
} else if (value.isUndefined() && property->isResettable()) {
void *a[] = { 0 };
- QMetaObject::metacall(object, QMetaObject::ResetProperty, property->coreIndex, a);
- } else if (value.isUndefined() && property->propType == qMetaTypeId<QVariant>()) {
+ QMetaObject::metacall(object, QMetaObject::ResetProperty, property->coreIndex(), a);
+ } else if (value.isUndefined() && property->propType() == qMetaTypeId<QVariant>()) {
PROPERTY_STORE(QVariant, QVariant());
- } else if (value.isUndefined() && property->propType == QMetaType::QJsonValue) {
+ } else if (value.isUndefined() && property->propType() == QMetaType::QJsonValue) {
PROPERTY_STORE(QJsonValue, QJsonValue(QJsonValue::Undefined));
- } else if (!newBinding && property->propType == qMetaTypeId<QJSValue>()) {
+ } else if (!newBinding && property->propType() == qMetaTypeId<QJSValue>()) {
PROPERTY_STORE(QJSValue, QJSValue(scope.engine, value.asReturnedValue()));
- } else if (value.isUndefined() && property->propType != qMetaTypeId<QQmlScriptString>()) {
+ } else if (value.isUndefined() && property->propType() != qMetaTypeId<QQmlScriptString>()) {
QString error = QLatin1String("Cannot assign [undefined] to ");
- if (!QMetaType::typeName(property->propType))
+ if (!QMetaType::typeName(property->propType()))
error += QLatin1String("[unknown property type]");
else
- error += QLatin1String(QMetaType::typeName(property->propType));
+ error += QLatin1String(QMetaType::typeName(property->propType()));
scope.engine->throwError(error);
return;
} else if (value.as<FunctionObject>()) {
// this is handled by the binding creation above
- } else if (property->propType == QMetaType::Int && value.isNumber()) {
+ } else if (property->propType() == QMetaType::Int && value.isNumber()) {
PROPERTY_STORE(int, value.asDouble());
- } else if (property->propType == QMetaType::QReal && value.isNumber()) {
+ } else if (property->propType() == QMetaType::QReal && value.isNumber()) {
PROPERTY_STORE(qreal, qreal(value.asDouble()));
- } else if (property->propType == QMetaType::Float && value.isNumber()) {
+ } else if (property->propType() == QMetaType::Float && value.isNumber()) {
PROPERTY_STORE(float, float(value.asDouble()));
- } else if (property->propType == QMetaType::Double && value.isNumber()) {
+ } else if (property->propType() == QMetaType::Double && value.isNumber()) {
PROPERTY_STORE(double, double(value.asDouble()));
- } else if (property->propType == QMetaType::QString && value.isString()) {
+ } else if (property->propType() == QMetaType::QString && value.isString()) {
PROPERTY_STORE(QString, value.toQStringNoThrow());
} else if (property->isVarProperty()) {
QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(object);
Q_ASSERT(vmemo);
- vmemo->setVMEProperty(property->coreIndex, value);
- } else if (property->propType == qMetaTypeId<QQmlScriptString>() && (value.isUndefined() || value.isPrimitive())) {
+ vmemo->setVMEProperty(property->coreIndex(), value);
+ } else if (property->propType() == qMetaTypeId<QQmlScriptString>() && (value.isUndefined() || value.isPrimitive())) {
QQmlScriptString ss(value.toQStringNoThrow(), 0 /* context */, object);
if (value.isNumber()) {
ss.d->numberValue = value.toNumber();
@@ -538,7 +469,7 @@ void QObjectWrapper::setProperty(ExecutionEngine *engine, QObject *object, QQmlP
if (property->isQList())
v = scope.engine->toVariant(value, qMetaTypeId<QList<QObject *> >());
else
- v = scope.engine->toVariant(value, property->propType);
+ v = scope.engine->toVariant(value, property->propType());
QQmlContextData *callingQmlContext = scope.engine->callingQmlContext();
if (!QQmlPropertyPrivate::write(object, *property, v, callingQmlContext)) {
@@ -546,7 +477,7 @@ void QObjectWrapper::setProperty(ExecutionEngine *engine, QObject *object, QQmlP
if (v.userType() == QVariant::Invalid) valueType = "null";
else valueType = QMetaType::typeName(v.userType());
- const char *targetTypeName = QMetaType::typeName(property->propType);
+ const char *targetTypeName = QMetaType::typeName(property->propType());
if (!targetTypeName)
targetTypeName = "an unregistered type";
@@ -641,11 +572,14 @@ ReturnedValue QObjectWrapper::getProperty(ExecutionEngine *engine, QObject *obje
void QObjectWrapper::setProperty(ExecutionEngine *engine, int propertyIndex, const Value &value)
{
- setProperty(engine, d()->object, propertyIndex, value);
+ setProperty(engine, d()->object(), propertyIndex, value);
}
void QObjectWrapper::setProperty(ExecutionEngine *engine, QObject *object, int propertyIndex, const Value &value)
{
+ Q_ASSERT(propertyIndex < 0xffff);
+ Q_ASSERT(propertyIndex >= 0);
+
if (QQmlData::wasDeleted(object))
return;
QQmlData *ddata = QQmlData::get(object, /*create*/false);
@@ -697,12 +631,12 @@ void QObjectWrapper::put(Managed *m, String *name, const Value &value)
QObjectWrapper *that = static_cast<QObjectWrapper*>(m);
ExecutionEngine *v4 = that->engine();
- if (v4->hasException || QQmlData::wasDeleted(that->d()->object))
+ if (v4->hasException || QQmlData::wasDeleted(that->d()->object()))
return;
QQmlContextData *qmlContext = v4->callingQmlContext();
- if (!setQmlProperty(v4, qmlContext, that->d()->object, name, QV4::QObjectWrapper::IgnoreRevision, value)) {
- QQmlData *ddata = QQmlData::get(that->d()->object);
+ if (!setQmlProperty(v4, qmlContext, that->d()->object(), name, QV4::QObjectWrapper::IgnoreRevision, value)) {
+ QQmlData *ddata = QQmlData::get(that->d()->object());
// Types created by QML are not extensible at run-time, but for other QObjects we can store them
// as regular JavaScript properties, like on JavaScript objects.
if (ddata && ddata->context) {
@@ -740,8 +674,8 @@ void QObjectWrapper::advanceIterator(Managed *m, ObjectIterator *it, Value *name
QObjectWrapper *that = static_cast<QObjectWrapper*>(m);
- if (that->d()->object) {
- const QMetaObject *mo = that->d()->object->metaObject();
+ if (that->d()->object()) {
+ const QMetaObject *mo = that->d()->object()->metaObject();
// These indices don't apply to gadgets, so don't block them.
const bool preventDestruction = mo->superClass() || mo == &QObject::staticMetaObject;
const int propertyCount = mo->propertyCount();
@@ -801,8 +735,8 @@ struct QObjectSlotDispatcher : public QtPrivate::QSlotObjectBase
if (!v4)
break;
- QVarLengthArray<int, 9> dummy;
- int *argsTypes = QQmlMetaObject(r).methodParameterTypes(This->signalIndex, dummy, 0);
+ QQmlMetaObject::ArgTypeStorage storage;
+ int *argsTypes = QQmlMetaObject(r).methodParameterTypes(This->signalIndex, &storage, 0);
int argCount = argsTypes ? argsTypes[0]:0;
@@ -820,7 +754,7 @@ struct QObjectSlotDispatcher : public QtPrivate::QSlotObjectBase
}
}
- f->call(callData);
+ f->call(scope, callData);
if (scope.hasException()) {
QQmlError error = v4->catchExceptionAsQmlError();
if (error.description().isEmpty()) {
@@ -865,7 +799,7 @@ struct QObjectSlotDispatcher : public QtPrivate::QSlotObjectBase
(connection->thisObject.isUndefined() || RuntimeHelpers::strictEqual(*connection->thisObject.valueRef(), thisObject))) {
QV4::ScopedFunctionObject f(scope, connection->function.value());
- QPair<QObject *, int> connectedFunctionData = extractQtMethod(f);
+ QPair<QObject *, int> connectedFunctionData = QObjectMethod::extractQtMethod(f);
if (connectedFunctionData.first == receiverToDisconnect &&
connectedFunctionData.second == slotIndexToDisconnect) {
*ret = true;
@@ -980,7 +914,7 @@ ReturnedValue QObjectWrapper::method_disconnect(CallContext *ctx)
if (!functionThisValue->isUndefined() && !functionThisValue->isObject())
V4THROW_ERROR("Function.prototype.disconnect: target this is not an object");
- QPair<QObject *, int> functionData = extractQtMethod(functionValue);
+ QPair<QObject *, int> functionData = QObjectMethod::extractQtMethod(functionValue);
void *a[] = {
ctx->d()->engine,
@@ -1011,7 +945,7 @@ void QObjectWrapper::markObjects(Heap::Base *that, QV4::ExecutionEngine *e)
{
QObjectWrapper::Data *This = static_cast<QObjectWrapper::Data *>(that);
- if (QObject *o = This->object.data()) {
+ if (QObject *o = This->object()) {
QQmlVMEMetaObject *vme = QQmlVMEMetaObject::get(o);
if (vme)
vme->mark(e);
@@ -1033,18 +967,18 @@ void QObjectWrapper::destroyObject(bool lastCall)
if (!h->internalClass)
return; // destroyObject already got called
- if (h->object) {
- QQmlData *ddata = QQmlData::get(h->object, false);
+ if (h->object()) {
+ QQmlData *ddata = QQmlData::get(h->object(), false);
if (ddata) {
- if (!h->object->parent() && !ddata->indestructible) {
+ if (!h->object()->parent() && !ddata->indestructible) {
if (ddata && ddata->ownContext && ddata->context)
ddata->context->emitDestruction();
// This object is notionally destroyed now
ddata->isQueuedForDeletion = true;
if (lastCall)
- delete h->object;
+ delete h->object();
else
- h->object->deleteLater();
+ h->object()->deleteLater();
} else {
// If the object is C++-owned, we still have to release the weak reference we have
// to it.
@@ -1108,6 +1042,7 @@ private:
// Pointers to allocData
union {
QString *qstringPtr;
+ QByteArray *qbyteArrayPtr;
QVariant *qvariantPtr;
QList<QObject *> *qlistPtr;
QJSValue *qjsValuePtr;
@@ -1122,7 +1057,8 @@ private:
}
static QV4::ReturnedValue CallMethod(const QQmlObjectOrGadget &object, int index, int returnType, int argCount,
- int *argTypes, QV4::ExecutionEngine *engine, QV4::CallData *callArgs)
+ int *argTypes, QV4::ExecutionEngine *engine, QV4::CallData *callArgs,
+ QMetaObject::Call callType = QMetaObject::InvokeMetaMethod)
{
if (argCount > 0) {
// Convert all arguments.
@@ -1134,7 +1070,7 @@ static QV4::ReturnedValue CallMethod(const QQmlObjectOrGadget &object, int index
for (int ii = 0; ii < args.count(); ++ii)
argData[ii] = args[ii].dataPtr();
- object.metacall(QMetaObject::InvokeMetaMethod, index, argData.data());
+ object.metacall(callType, index, argData.data());
return args[0].toValue(engine);
@@ -1145,14 +1081,14 @@ static QV4::ReturnedValue CallMethod(const QQmlObjectOrGadget &object, int index
void *args[] = { arg.dataPtr() };
- object.metacall(QMetaObject::InvokeMetaMethod, index, args);
+ object.metacall(callType, index, args);
return arg.toValue(engine);
} else {
void *args[] = { 0 };
- object.metacall(QMetaObject::InvokeMetaMethod, index, args);
+ object.metacall(callType, index, args);
return Encode::undefined();
}
@@ -1229,6 +1165,13 @@ static int MatchScore(const QV4::Value &actual, int conversionType)
default:
return 10;
}
+ } else if (actual.as<ArrayBuffer>()) {
+ switch (conversionType) {
+ case QMetaType::QByteArray:
+ return 0;
+ default:
+ return 10;
+ }
} else if (actual.as<ArrayObject>()) {
switch (conversionType) {
case QMetaType::QJsonArray:
@@ -1246,6 +1189,7 @@ static int MatchScore(const QV4::Value &actual, int conversionType)
}
} else if (actual.isNull()) {
switch (conversionType) {
+ case QMetaType::Nullptr:
case QMetaType::VoidStar:
case QMetaType::QObjectStar:
case QMetaType::QJsonValue:
@@ -1318,34 +1262,34 @@ static const QQmlPropertyData * RelatedMethod(const QQmlObjectOrGadget &object,
if (!current->isOverload())
return 0;
- Q_ASSERT(!current->overrideIndexIsProperty);
+ Q_ASSERT(!current->overrideIndexIsProperty());
if (propertyCache) {
- return propertyCache->method(current->overrideIndex);
+ return propertyCache->method(current->overrideIndex());
} else {
const QMetaObject *mo = object.metaObject();
int methodOffset = mo->methodCount() - QMetaObject_methods(mo);
- while (methodOffset > current->overrideIndex) {
+ while (methodOffset > current->overrideIndex()) {
mo = mo->superClass();
methodOffset -= QMetaObject_methods(mo);
}
// If we've been called before with the same override index, then
// we can't go any further...
- if (&dummy == current && dummy.coreIndex == current->overrideIndex)
+ if (&dummy == current && dummy.coreIndex() == current->overrideIndex())
return 0;
- QMetaMethod method = mo->method(current->overrideIndex);
+ QMetaMethod method = mo->method(current->overrideIndex());
dummy.load(method);
// Look for overloaded methods
QByteArray methodName = method.name();
- for (int ii = current->overrideIndex - 1; ii >= methodOffset; --ii) {
+ for (int ii = current->overrideIndex() - 1; ii >= methodOffset; --ii) {
if (methodName == mo->method(ii).name()) {
- dummy.setFlags(dummy.getFlags() | QQmlPropertyData::IsOverload);
- dummy.overrideIndexIsProperty = 0;
- dummy.overrideIndex = ii;
+ dummy.setOverload(true);
+ dummy.setOverrideIndexIsProperty(0);
+ dummy.setOverrideIndex(ii);
return &dummy;
}
}
@@ -1355,7 +1299,8 @@ static const QQmlPropertyData * RelatedMethod(const QQmlObjectOrGadget &object,
}
static QV4::ReturnedValue CallPrecise(const QQmlObjectOrGadget &object, const QQmlPropertyData &data,
- QV4::ExecutionEngine *engine, QV4::CallData *callArgs)
+ QV4::ExecutionEngine *engine, QV4::CallData *callArgs,
+ QMetaObject::Call callType = QMetaObject::InvokeMetaMethod)
{
QByteArray unknownTypeError;
@@ -1370,9 +1315,13 @@ static QV4::ReturnedValue CallPrecise(const QQmlObjectOrGadget &object, const QQ
if (data.hasArguments()) {
int *args = 0;
- QVarLengthArray<int, 9> dummy;
+ QQmlMetaObject::ArgTypeStorage storage;
- args = object.methodParameterTypes(data.coreIndex, dummy, &unknownTypeError);
+ if (data.isConstructor())
+ args = static_cast<const QQmlStaticMetaObject&>(object).constructorParameterTypes(
+ data.coreIndex(), &storage, &unknownTypeError);
+ else
+ args = object.methodParameterTypes(data.coreIndex(), &storage, &unknownTypeError);
if (!args) {
QString typeName = QString::fromLatin1(unknownTypeError);
@@ -1385,11 +1334,11 @@ static QV4::ReturnedValue CallPrecise(const QQmlObjectOrGadget &object, const QQ
return engine->throwError(error);
}
- return CallMethod(object, data.coreIndex, returnType, args[0], args + 1, engine, callArgs);
+ return CallMethod(object, data.coreIndex(), returnType, args[0], args + 1, engine, callArgs, callType);
} else {
- return CallMethod(object, data.coreIndex, returnType, 0, 0, engine, callArgs);
+ return CallMethod(object, data.coreIndex(), returnType, 0, 0, engine, callArgs, callType);
}
}
@@ -1408,7 +1357,8 @@ Resolve the overloaded method to call. The algorithm works conceptually like th
score is constructed by adding the matchScore() result for each of the parameters.
*/
static QV4::ReturnedValue CallOverloaded(const QQmlObjectOrGadget &object, const QQmlPropertyData &data,
- QV4::ExecutionEngine *engine, QV4::CallData *callArgs, const QQmlPropertyCache *propertyCache)
+ QV4::ExecutionEngine *engine, QV4::CallData *callArgs, const QQmlPropertyCache *propertyCache,
+ QMetaObject::Call callType = QMetaObject::InvokeMetaMethod)
{
int argumentCount = callArgs->argc;
@@ -1423,11 +1373,11 @@ static QV4::ReturnedValue CallOverloaded(const QQmlObjectOrGadget &object, const
QV4::ScopedValue v(scope);
do {
- QVarLengthArray<int, 9> dummy;
+ QQmlMetaObject::ArgTypeStorage storage;
int methodArgumentCount = 0;
int *methodArgTypes = 0;
if (attempt->hasArguments()) {
- int *args = object.methodParameterTypes(attempt->coreIndex, dummy, 0);
+ int *args = object.methodParameterTypes(attempt->coreIndex(), &storage, 0);
if (!args) // Must be an unknown argument
continue;
@@ -1458,13 +1408,13 @@ static QV4::ReturnedValue CallOverloaded(const QQmlObjectOrGadget &object, const
} while ((attempt = RelatedMethod(object, attempt, dummy, propertyCache)) != 0);
if (best.isValid()) {
- return CallPrecise(object, best, engine, callArgs);
+ return CallPrecise(object, best, engine, callArgs, callType);
} else {
QString error = QLatin1String("Unable to determine callable overload. Candidates are:");
const QQmlPropertyData *candidate = &data;
while (candidate) {
error += QLatin1String("\n ") +
- QString::fromUtf8(object.metaObject()->method(candidate->coreIndex)
+ QString::fromUtf8(object.metaObject()->method(candidate->coreIndex())
.methodSignature());
candidate = RelatedMethod(object, candidate, dummy, propertyCache);
}
@@ -1487,6 +1437,8 @@ void CallArgument::cleanup()
{
if (type == QMetaType::QString) {
qstringPtr->~QString();
+ } else if (type == QMetaType::QByteArray) {
+ qbyteArrayPtr->~QByteArray();
} else if (type == -1 || type == QMetaType::QVariant) {
qvariantPtr->~QVariant();
} else if (type == qMetaTypeId<QJSValue>()) {
@@ -1686,6 +1638,8 @@ QV4::ReturnedValue CallArgument::toValue(QV4::ExecutionEngine *engine)
return QV4::Encode(floatValue);
} else if (type == QMetaType::QString) {
return QV4::Encode(engine->newString(*qstringPtr));
+ } else if (type == QMetaType::QByteArray) {
+ return QV4::Encode(engine->newArrayBuffer(*qbyteArrayPtr));
} else if (type == QMetaType::QObjectStar) {
QObject *object = qobjectPtr;
if (object)
@@ -1728,10 +1682,10 @@ ReturnedValue QObjectMethod::create(ExecutionContext *scope, QObject *object, in
{
Scope valueScope(scope);
Scoped<QObjectMethod> method(valueScope, scope->d()->engine->memoryManager->allocObject<QObjectMethod>(scope));
- method->d()->object = object;
+ method->d()->setObject(object);
if (QQmlData *ddata = QQmlData::get(object))
- method->d()->propertyCache = ddata->propertyCache;
+ method->d()->setPropertyCache(ddata->propertyCache);
method->d()->index = index;
return method.asReturnedValue();
@@ -1740,23 +1694,23 @@ ReturnedValue QObjectMethod::create(ExecutionContext *scope, QObject *object, in
ReturnedValue QObjectMethod::create(ExecutionContext *scope, const QQmlValueTypeWrapper *valueType, int index)
{
Scope valueScope(scope);
- Scoped<QObjectMethod> method(valueScope, scope->d()->engine->memoryManager->allocObject<QObjectMethod>(scope));
- method->d()->propertyCache = valueType->d()->propertyCache;
+ Scoped<QObjectMethod> method(valueScope, valueScope.engine->memoryManager->allocObject<QObjectMethod>(scope));
+ method->d()->setPropertyCache(valueType->d()->propertyCache());
method->d()->index = index;
method->d()->valueTypeWrapper = valueType->d();
return method.asReturnedValue();
}
-Heap::QObjectMethod::QObjectMethod(QV4::ExecutionContext *scope)
- : Heap::FunctionObject(scope)
+void Heap::QObjectMethod::init(QV4::ExecutionContext *scope)
{
+ Heap::FunctionObject::init(scope);
}
const QMetaObject *Heap::QObjectMethod::metaObject()
{
- if (propertyCache)
- return propertyCache->createMetaObject();
- return object->metaObject();
+ if (propertyCache())
+ return propertyCache()->createMetaObject();
+ return object()->metaObject();
}
QV4::ReturnedValue QObjectMethod::method_toString(QV4::ExecutionContext *ctx) const
@@ -1764,17 +1718,13 @@ QV4::ReturnedValue QObjectMethod::method_toString(QV4::ExecutionContext *ctx) co
QString result;
if (const QMetaObject *metaObject = d()->metaObject()) {
- result += QString::fromUtf8(metaObject->className());
- result += QLatin1String("(0x");
- result += QString::number((quintptr)d()->object.data(),16);
+ result += QString::fromUtf8(metaObject->className()) +
+ QLatin1String("(0x") + QString::number((quintptr)d()->object(),16);
- if (d()->object) {
- QString objectName = d()->object->objectName();
- if (!objectName.isEmpty()) {
- result += QLatin1String(", \"");
- result += objectName;
- result += QLatin1Char('\"');
- }
+ if (d()->object()) {
+ QString objectName = d()->object()->objectName();
+ if (!objectName.isEmpty())
+ result += QLatin1String(", \"") + objectName + QLatin1Char('\"');
}
result += QLatin1Char(')');
@@ -1787,9 +1737,9 @@ QV4::ReturnedValue QObjectMethod::method_toString(QV4::ExecutionContext *ctx) co
QV4::ReturnedValue QObjectMethod::method_destroy(QV4::ExecutionContext *ctx, const Value *args, int argc) const
{
- if (!d()->object)
+ if (!d()->object())
return Encode::undefined();
- if (QQmlData::keepAliveDuringGarbageCollection(d()->object))
+ if (QQmlData::keepAliveDuringGarbageCollection(d()->object()))
return ctx->engine()->throwError(QStringLiteral("Invalid attempt to destroy() an indestructible object"));
int delay = 0;
@@ -1797,80 +1747,90 @@ QV4::ReturnedValue QObjectMethod::method_destroy(QV4::ExecutionContext *ctx, con
delay = args[0].toUInt32();
if (delay > 0)
- QTimer::singleShot(delay, d()->object, SLOT(deleteLater()));
+ QTimer::singleShot(delay, d()->object(), SLOT(deleteLater()));
else
- d()->object->deleteLater();
+ d()->object()->deleteLater();
return Encode::undefined();
}
-ReturnedValue QObjectMethod::call(const Managed *m, CallData *callData)
+void QObjectMethod::call(const Managed *m, Scope &scope, CallData *callData)
{
const QObjectMethod *This = static_cast<const QObjectMethod*>(m);
- return This->callInternal(callData);
+ This->callInternal(callData, scope);
}
-ReturnedValue QObjectMethod::callInternal(CallData *callData) const
+void QObjectMethod::callInternal(CallData *callData, Scope &scope) const
{
ExecutionEngine *v4 = engine();
ExecutionContext *context = v4->currentContext;
- if (d()->index == DestroyMethod)
- return method_destroy(context, callData->args, callData->argc);
- else if (d()->index == ToStringMethod)
- return method_toString(context);
+ if (d()->index == DestroyMethod) {
+ scope.result = method_destroy(context, callData->args, callData->argc);
+ return;
+ }
+
+ else if (d()->index == ToStringMethod) {
+ scope.result = method_toString(context);
+ return;
+ }
- QQmlObjectOrGadget object(d()->object.data());
- if (!d()->object) {
- if (!d()->valueTypeWrapper)
- return Encode::undefined();
+ QQmlObjectOrGadget object(d()->object());
+ if (!d()->object()) {
+ if (!d()->valueTypeWrapper) {
+ scope.result = Encode::undefined();
+ return;
+ }
- object = QQmlObjectOrGadget(d()->propertyCache.data(), d()->valueTypeWrapper->gadgetPtr);
+ object = QQmlObjectOrGadget(d()->propertyCache(), d()->valueTypeWrapper->gadgetPtr);
}
QQmlPropertyData method;
- if (d()->propertyCache) {
- QQmlPropertyData *data = d()->propertyCache->method(d()->index);
- if (!data)
- return QV4::Encode::undefined();
+ if (d()->propertyCache()) {
+ QQmlPropertyData *data = d()->propertyCache()->method(d()->index);
+ if (!data) {
+ scope.result = QV4::Encode::undefined();
+ return;
+ }
method = *data;
} else {
- const QMetaObject *mo = d()->object->metaObject();
+ const QMetaObject *mo = d()->object()->metaObject();
const QMetaMethod moMethod = mo->method(d()->index);
method.load(moMethod);
- if (method.coreIndex == -1)
- return QV4::Encode::undefined();
+ if (method.coreIndex() == -1) {
+ scope.result = QV4::Encode::undefined();
+ return;
+ }
// Look for overloaded methods
QByteArray methodName = moMethod.name();
const int methodOffset = mo->methodOffset();
for (int ii = d()->index - 1; ii >= methodOffset; --ii) {
if (methodName == mo->method(ii).name()) {
- method.setFlags(method.getFlags() | QQmlPropertyData::IsOverload);
- method.overrideIndexIsProperty = 0;
- method.overrideIndex = ii;
+ method.setOverload(true);
+ method.setOverrideIndexIsProperty(0);
+ method.setOverrideIndex(ii);
break;
}
}
}
if (method.isV4Function()) {
- Scope scope(v4);
- QV4::ScopedValue rv(scope, QV4::Primitive::undefinedValue());
- QQmlV4Function func(callData, rv, v4);
+ scope.result = QV4::Encode::undefined();
+ QQmlV4Function func(callData, &scope.result, v4);
QQmlV4Function *funcptr = &func;
void *args[] = { 0, &funcptr };
- object.metacall(QMetaObject::InvokeMetaMethod, method.coreIndex, args);
+ object.metacall(QMetaObject::InvokeMetaMethod, method.coreIndex(), args);
- return rv->asReturnedValue();
+ return;
}
if (!method.isOverload()) {
- return CallPrecise(object, method, v4, callData);
+ scope.result = CallPrecise(object, method, v4, callData);
} else {
- return CallOverloaded(object, method, v4, callData, d()->propertyCache);
+ scope.result = CallOverloaded(object, method, v4, callData, d()->propertyCache());
}
}
@@ -1885,17 +1845,193 @@ void QObjectMethod::markObjects(Heap::Base *that, ExecutionEngine *e)
DEFINE_OBJECT_VTABLE(QObjectMethod);
-Heap::QmlSignalHandler::QmlSignalHandler(QObject *object, int signalIndex)
- : object(object)
- , signalIndex(signalIndex)
+
+void Heap::QMetaObjectWrapper::init(const QMetaObject *metaObject)
+{
+ FunctionObject::init();
+ this->metaObject = metaObject;
+ constructors = nullptr;
+ constructorCount = 0;
+}
+
+void Heap::QMetaObjectWrapper::destroy()
+{
+ delete[] constructors;
+}
+
+void Heap::QMetaObjectWrapper::ensureConstructorsCache() {
+
+ const int count = metaObject->constructorCount();
+ if (constructorCount != count) {
+ delete[] constructors;
+ constructorCount = count;
+ if (count == 0) {
+ constructors = nullptr;
+ return;
+ }
+ constructors = new QQmlPropertyData[count];
+
+ for (int i = 0; i < count; ++i) {
+ QMetaMethod method = metaObject->constructor(i);
+ QQmlPropertyData &d = constructors[i];
+ d.load(method);
+ d.setCoreIndex(i);
+ }
+ }
+}
+
+
+ReturnedValue QMetaObjectWrapper::create(ExecutionEngine *engine, const QMetaObject* metaObject) {
+
+ QV4::Scope scope(engine);
+ Scoped<QMetaObjectWrapper> mo(scope, engine->memoryManager->allocObject<QV4::QMetaObjectWrapper>(metaObject)->asReturnedValue());
+ mo->init(engine);
+ return mo->asReturnedValue();
+}
+
+void QMetaObjectWrapper::init(ExecutionEngine *) {
+ const QMetaObject & mo = *d()->metaObject;
+
+ for (int i = 0; i < mo.enumeratorCount(); i++) {
+ QMetaEnum Enum = mo.enumerator(i);
+ for (int k = 0; k < Enum.keyCount(); k++) {
+ const char* key = Enum.key(k);
+ const int value = Enum.value(k);
+ defineReadonlyProperty(QLatin1String(key), Primitive::fromInt32(value));
+ }
+ }
+}
+
+void QMetaObjectWrapper::construct(const Managed *m, Scope &scope, CallData *callData)
+{
+ const QMetaObjectWrapper *This = static_cast<const QMetaObjectWrapper*>(m);
+ scope.result = This->constructInternal(callData);
+}
+
+ReturnedValue QMetaObjectWrapper::constructInternal(CallData * callData) const {
+
+ d()->ensureConstructorsCache();
+
+ ExecutionEngine *v4 = engine();
+ const QMetaObject* mo = d()->metaObject;
+ if (d()->constructorCount == 0) {
+ return v4->throwTypeError(QStringLiteral("%1 has no invokable constructor")
+ .arg(QLatin1String(mo->className())));
+ }
+
+ Scope scope(v4);
+ Scoped<QObjectWrapper> object(scope);
+
+ if (d()->constructorCount == 1) {
+ object = callConstructor(d()->constructors[0], v4, callData);
+ }
+ else {
+ object = callOverloadedConstructor(v4, callData);
+ }
+ Scoped<QMetaObjectWrapper> metaObject(scope, this);
+ object->defineDefaultProperty(v4->id_constructor(), metaObject);
+ object->setPrototype(const_cast<QMetaObjectWrapper*>(this));
+ return object.asReturnedValue();
+
+}
+
+ReturnedValue QMetaObjectWrapper::callConstructor(const QQmlPropertyData &data, QV4::ExecutionEngine *engine, QV4::CallData *callArgs) const {
+
+ const QMetaObject* mo = d()->metaObject;
+ const QQmlStaticMetaObject object(mo);
+ return CallPrecise(object, data, engine, callArgs, QMetaObject::CreateInstance);
+}
+
+
+ReturnedValue QMetaObjectWrapper::callOverloadedConstructor(QV4::ExecutionEngine *engine, QV4::CallData *callArgs) const {
+ const int numberOfConstructors = d()->constructorCount;
+ const int argumentCount = callArgs->argc;
+ const QQmlStaticMetaObject object(d()->metaObject);
+
+ QQmlPropertyData best;
+ int bestParameterScore = INT_MAX;
+ int bestMatchScore = INT_MAX;
+
+ QV4::Scope scope(engine);
+ QV4::ScopedValue v(scope);
+
+ for (int i = 0; i < numberOfConstructors; i++) {
+ const QQmlPropertyData & attempt = d()->constructors[i];
+ int methodArgumentCount = 0;
+ int *methodArgTypes = 0;
+ if (attempt.hasArguments()) {
+ QQmlMetaObject::ArgTypeStorage storage;
+ int *args = object.constructorParameterTypes(attempt.coreIndex(), &storage, 0);
+ if (!args) // Must be an unknown argument
+ continue;
+
+ methodArgumentCount = args[0];
+ methodArgTypes = args + 1;
+ }
+
+ if (methodArgumentCount > argumentCount)
+ continue; // We don't have sufficient arguments to call this method
+
+ int methodParameterScore = argumentCount - methodArgumentCount;
+ if (methodParameterScore > bestParameterScore)
+ continue; // We already have a better option
+
+ int methodMatchScore = 0;
+ for (int ii = 0; ii < methodArgumentCount; ++ii)
+ methodMatchScore += MatchScore((v = callArgs->args[ii]), methodArgTypes[ii]);
+
+ if (bestParameterScore > methodParameterScore || bestMatchScore > methodMatchScore) {
+ best = attempt;
+ bestParameterScore = methodParameterScore;
+ bestMatchScore = methodMatchScore;
+ }
+
+ if (bestParameterScore == 0 && bestMatchScore == 0)
+ break; // We can't get better than that
+ };
+
+ if (best.isValid()) {
+ return CallPrecise(object, best, engine, callArgs, QMetaObject::CreateInstance);
+ } else {
+ QString error = QLatin1String("Unable to determine callable overload. Candidates are:");
+ for (int i = 0; i < numberOfConstructors; i++) {
+ const QQmlPropertyData & candidate = d()->constructors[i];
+ error += QLatin1String("\n ") +
+ QString::fromUtf8(d()->metaObject->constructor(candidate.coreIndex())
+ .methodSignature());
+ }
+
+ return engine->throwError(error);
+ }
+}
+
+bool QMetaObjectWrapper::isEqualTo(Managed *a, Managed *b)
+{
+ Q_ASSERT(a->as<QMetaObjectWrapper>());
+ QMetaObjectWrapper *aMetaObject = a->as<QMetaObjectWrapper>();
+ QMetaObjectWrapper *bMetaObject = b->as<QMetaObjectWrapper>();
+ if (!bMetaObject)
+ return true;
+ return aMetaObject->metaObject() == bMetaObject->metaObject();
+}
+
+DEFINE_OBJECT_VTABLE(QMetaObjectWrapper);
+
+
+
+
+void Heap::QmlSignalHandler::init(QObject *object, int signalIndex)
{
+ Object::init();
+ this->signalIndex = signalIndex;
+ setObject(object);
}
DEFINE_OBJECT_VTABLE(QmlSignalHandler);
void QmlSignalHandler::initProto(ExecutionEngine *engine)
{
- if (engine->signalHandlerPrototype()->d())
+ if (engine->signalHandlerPrototype()->d_unchecked())
return;
Scope scope(engine);
diff --git a/src/qml/jsruntime/qv4qobjectwrapper_p.h b/src/qml/jsruntime/qv4qobjectwrapper_p.h
index 9c5862b80e..504f6a69b8 100644
--- a/src/qml/jsruntime/qv4qobjectwrapper_p.h
+++ b/src/qml/jsruntime/qv4qobjectwrapper_p.h
@@ -78,25 +78,78 @@ namespace Heap {
struct QQmlValueTypeWrapper;
struct Q_QML_EXPORT QObjectWrapper : Object {
- QObjectWrapper(QObject *object);
- QPointer<QObject> object;
+ void init(QObject *object)
+ {
+ Object::init();
+ qObj.init(object);
+ }
+
+ void destroy() {
+ qObj.destroy();
+ Object::destroy();
+ }
+
+ QObject *object() const { return qObj.data(); }
+
+private:
+ QQmlQPointer<QObject> qObj;
};
struct QObjectMethod : FunctionObject {
- QObjectMethod(QV4::ExecutionContext *scope);
- QPointer<QObject> object;
- QQmlRefPointer<QQmlPropertyCache> propertyCache;
- int index;
+ void init(QV4::ExecutionContext *scope);
+ void destroy()
+ {
+ setPropertyCache(nullptr);
+ qObj.destroy();
+ FunctionObject::destroy();
+ }
+
+ QQmlPropertyCache *propertyCache() const { return _propertyCache; }
+ void setPropertyCache(QQmlPropertyCache *c) {
+ if (c)
+ c->addref();
+ if (_propertyCache)
+ _propertyCache->release();
+ _propertyCache = c;
+ }
Pointer<QQmlValueTypeWrapper> valueTypeWrapper;
const QMetaObject *metaObject();
+ QObject *object() const { return qObj.data(); }
+ void setObject(QObject *o) { qObj = o; }
+
+private:
+ QQmlQPointer<QObject> qObj;
+ QQmlPropertyCache *_propertyCache;
+
+public:
+ int index;
+};
+
+struct QMetaObjectWrapper : FunctionObject {
+ const QMetaObject* metaObject;
+ QQmlPropertyData *constructors;
+ int constructorCount;
+
+ void init(const QMetaObject* metaObject);
+ void destroy();
+ void ensureConstructorsCache();
};
struct QmlSignalHandler : Object {
- QmlSignalHandler(QObject *object, int signalIndex);
- QPointer<QObject> object;
+ void init(QObject *object, int signalIndex);
+ void destroy() {
+ qObj.destroy();
+ Object::destroy();
+ }
int signalIndex;
+
+ QObject *object() const { return qObj.data(); }
+ void setObject(QObject *o) { qObj = o; }
+
+private:
+ QQmlQPointer<QObject> qObj;
};
}
@@ -104,12 +157,13 @@ struct QmlSignalHandler : Object {
struct Q_QML_EXPORT QObjectWrapper : public Object
{
V4_OBJECT2(QObjectWrapper, Object)
+ V4_NEEDS_DESTROY
enum RevisionMode { IgnoreRevision, CheckRevision };
static void initializeBindings(ExecutionEngine *engine);
- QObject *object() const { return d()->object.data(); }
+ QObject *object() const { return d()->object(); }
ReturnedValue getQmlProperty(QQmlContextData *qmlContext, String *name, RevisionMode revisionMode, bool *hasProperty = 0, bool includeImports = false) const;
static ReturnedValue getQmlProperty(ExecutionEngine *engine, QQmlContextData *qmlContext, QObject *object, String *name, RevisionMode revisionMode, bool *hasProperty = 0);
@@ -147,7 +201,7 @@ protected:
static ReturnedValue method_disconnect(CallContext *ctx);
private:
- static ReturnedValue wrap_slowPath(ExecutionEngine *engine, QObject *object);
+ Q_NEVER_INLINE static ReturnedValue wrap_slowPath(ExecutionEngine *engine, QObject *object);
};
inline ReturnedValue QObjectWrapper::wrap(ExecutionEngine *engine, QObject *object)
@@ -179,16 +233,38 @@ struct Q_QML_EXPORT QObjectMethod : public QV4::FunctionObject
static ReturnedValue create(QV4::ExecutionContext *scope, const QQmlValueTypeWrapper *valueType, int index);
int methodIndex() const { return d()->index; }
- QObject *object() const { return d()->object.data(); }
+ QObject *object() const { return d()->object(); }
QV4::ReturnedValue method_toString(QV4::ExecutionContext *ctx) const;
QV4::ReturnedValue method_destroy(QV4::ExecutionContext *ctx, const Value *args, int argc) const;
- static ReturnedValue call(const Managed *, CallData *callData);
+ static void call(const Managed *, Scope &scope, CallData *callData);
- ReturnedValue callInternal(CallData *callData) const;
+ void callInternal(CallData *callData, Scope &scope) const;
static void markObjects(Heap::Base *that, QV4::ExecutionEngine *e);
+
+ static QPair<QObject *, int> extractQtMethod(const QV4::FunctionObject *function);
+};
+
+
+struct Q_QML_EXPORT QMetaObjectWrapper : public QV4::FunctionObject
+{
+ V4_OBJECT2(QMetaObjectWrapper, QV4::FunctionObject)
+ V4_NEEDS_DESTROY
+
+ static ReturnedValue create(ExecutionEngine *engine, const QMetaObject* metaObject);
+ static void construct(const Managed *, Scope &scope, CallData *callData);
+ static bool isEqualTo(Managed *a, Managed *b);
+
+ const QMetaObject *metaObject() const { return d()->metaObject; }
+
+private:
+ void init(ExecutionEngine *engine);
+ ReturnedValue constructInternal(CallData *callData) const;
+ ReturnedValue callConstructor(const QQmlPropertyData &data, QV4::ExecutionEngine *engine, QV4::CallData *callArgs) const;
+ ReturnedValue callOverloadedConstructor(QV4::ExecutionEngine *engine, QV4::CallData *callArgs) const;
+
};
struct Q_QML_EXPORT QmlSignalHandler : public QV4::Object
@@ -198,7 +274,7 @@ struct Q_QML_EXPORT QmlSignalHandler : public QV4::Object
V4_NEEDS_DESTROY
int signalIndex() const { return d()->signalIndex; }
- QObject *object() const { return d()->object.data(); }
+ QObject *object() const { return d()->object(); }
static void initProto(ExecutionEngine *v4);
};
diff --git a/src/qml/jsruntime/qv4regexp.cpp b/src/qml/jsruntime/qv4regexp.cpp
index af5355c964..9e94c58432 100644
--- a/src/qml/jsruntime/qv4regexp.cpp
+++ b/src/qml/jsruntime/qv4regexp.cpp
@@ -62,11 +62,11 @@ uint RegExp::match(const QString &string, int start, uint *matchOffsets)
WTF::String s(string);
#if ENABLE(YARR_JIT)
- if (!jitCode().isFallBack() && jitCode().has16BitCode())
- return uint(jitCode().execute(s.characters16(), start, s.length(), (int*)matchOffsets).start);
+ if (!jitCode()->isFallBack() && jitCode()->has16BitCode())
+ return uint(jitCode()->execute(s.characters16(), start, s.length(), (int*)matchOffsets).start);
#endif
- return JSC::Yarr::interpret(byteCode().get(), s.characters16(), string.length(), start, matchOffsets);
+ return JSC::Yarr::interpret(byteCode(), s.characters16(), string.length(), start, matchOffsets);
}
Heap::RegExp *RegExp::create(ExecutionEngine* engine, const QString& pattern, bool ignoreCase, bool multiline)
@@ -90,31 +90,41 @@ Heap::RegExp *RegExp::create(ExecutionEngine* engine, const QString& pattern, bo
return result->d();
}
-Heap::RegExp::RegExp(ExecutionEngine* engine, const QString &pattern, bool ignoreCase, bool multiline)
- : pattern(pattern)
- , ignoreCase(ignoreCase)
- , multiLine(multiline)
+void Heap::RegExp::init(ExecutionEngine* engine, const QString &pattern, bool ignoreCase, bool multiline)
{
+ Base::init();
+ this->pattern = new QString(pattern);
+ this->ignoreCase = ignoreCase;
+ this->multiLine = multiline;
+
const char* error = 0;
JSC::Yarr::YarrPattern yarrPattern(WTF::String(pattern), ignoreCase, multiline, &error);
if (error)
return;
subPatternCount = yarrPattern.m_numSubpatterns;
- byteCode = JSC::Yarr::byteCompile(yarrPattern, engine->bumperPointerAllocator);
+ OwnPtr<JSC::Yarr::BytecodePattern> p = JSC::Yarr::byteCompile(yarrPattern, engine->bumperPointerAllocator);
+ byteCode = p.take();
#if ENABLE(YARR_JIT)
+ jitCode = new JSC::Yarr::YarrCodeBlock;
if (!yarrPattern.m_containsBackreferences && engine->iselFactory->jitCompileRegexps()) {
JSC::JSGlobalData dummy(engine->regExpAllocator);
- JSC::Yarr::jitCompile(yarrPattern, JSC::Yarr::Char16, &dummy, jitCode);
+ JSC::Yarr::jitCompile(yarrPattern, JSC::Yarr::Char16, &dummy, *jitCode);
}
#endif
}
-Heap::RegExp::~RegExp()
+void Heap::RegExp::destroy()
{
if (cache) {
RegExpCacheKey key(this);
cache->remove(key);
}
+#if ENABLE(YARR_JIT)
+ delete jitCode;
+#endif
+ delete byteCode;
+ delete pattern;
+ Base::destroy();
}
void RegExp::markObjects(Heap::Base *that, ExecutionEngine *e)
diff --git a/src/qml/jsruntime/qv4regexp_p.h b/src/qml/jsruntime/qv4regexp_p.h
index b99d717847..d3e63375a5 100644
--- a/src/qml/jsruntime/qv4regexp_p.h
+++ b/src/qml/jsruntime/qv4regexp_p.h
@@ -76,12 +76,13 @@ struct RegExpCacheKey;
namespace Heap {
struct RegExp : Base {
- RegExp(ExecutionEngine* engine, const QString& pattern, bool ignoreCase, bool multiline);
- ~RegExp();
- QString pattern;
- OwnPtr<JSC::Yarr::BytecodePattern> byteCode;
+ void init(ExecutionEngine* engine, const QString& pattern, bool ignoreCase, bool multiline);
+ void destroy();
+
+ QString *pattern;
+ JSC::Yarr::BytecodePattern *byteCode;
#if ENABLE(YARR_JIT)
- JSC::Yarr::YarrCodeBlock jitCode;
+ JSC::Yarr::YarrCodeBlock *jitCode;
#endif
RegExpCache *cache;
int subPatternCount;
@@ -90,6 +91,7 @@ struct RegExp : Base {
int captureCount() const { return subPatternCount + 1; }
};
+V4_ASSERT_IS_TRIVIAL(RegExp)
}
@@ -99,10 +101,10 @@ struct RegExp : public Managed
Q_MANAGED_TYPE(RegExp)
V4_NEEDS_DESTROY
- QString pattern() const { return d()->pattern; }
- OwnPtr<JSC::Yarr::BytecodePattern> &byteCode() { return d()->byteCode; }
+ QString pattern() const { return *d()->pattern; }
+ JSC::Yarr::BytecodePattern *byteCode() { return d()->byteCode; }
#if ENABLE(YARR_JIT)
- JSC::Yarr::YarrCodeBlock jitCode() const { return d()->jitCode; }
+ JSC::Yarr::YarrCodeBlock *jitCode() const { return d()->jitCode; }
#endif
RegExpCache *cache() const { return d()->cache; }
int subPatternCount() const { return d()->subPatternCount; }
@@ -111,7 +113,7 @@ struct RegExp : public Managed
static Heap::RegExp *create(ExecutionEngine* engine, const QString& pattern, bool ignoreCase = false, bool multiline = false);
- bool isValid() const { return d()->byteCode.get(); }
+ bool isValid() const { return d()->byteCode; }
uint match(const QString& string, int start, uint *matchOffsets);
@@ -142,7 +144,7 @@ struct RegExpCacheKey
};
inline RegExpCacheKey::RegExpCacheKey(const RegExp::Data *re)
- : pattern(re->pattern)
+ : pattern(*re->pattern)
, ignoreCase(re->ignoreCase)
, multiLine(re->multiLine)
{}
diff --git a/src/qml/jsruntime/qv4regexpobject.cpp b/src/qml/jsruntime/qv4regexpobject.cpp
index 71e2bd1a06..4022d98c3f 100644
--- a/src/qml/jsruntime/qv4regexpobject.cpp
+++ b/src/qml/jsruntime/qv4regexpobject.cpp
@@ -69,8 +69,9 @@ using namespace QV4;
DEFINE_OBJECT_VTABLE(RegExpObject);
-Heap::RegExpObject::RegExpObject()
+void Heap::RegExpObject::init()
{
+ Object::init();
Scope scope(internalClass->engine);
Scoped<QV4::RegExpObject> o(scope, this);
o->d()->value = QV4::RegExp::create(scope.engine, QString(), false, false);
@@ -78,10 +79,11 @@ Heap::RegExpObject::RegExpObject()
o->initProperties();
}
-Heap::RegExpObject::RegExpObject(QV4::RegExp *value, bool global)
- : value(value->d())
- , global(global)
+void Heap::RegExpObject::init(QV4::RegExp *value, bool global)
{
+ Object::init();
+ this->global = global;
+ this->value = value->d();
Scope scope(internalClass->engine);
Scoped<QV4::RegExpObject> o(scope, this);
o->initProperties();
@@ -90,8 +92,9 @@ Heap::RegExpObject::RegExpObject(QV4::RegExp *value, bool global)
// Converts a QRegExp to a JS RegExp.
// The conversion is not 100% exact since ECMA regexp and QRegExp
// have different semantics/flags, but we try to do our best.
-Heap::RegExpObject::RegExpObject(const QRegExp &re)
+void Heap::RegExpObject::init(const QRegExp &re)
{
+ Object::init();
global = false;
// Convert the pattern to a ECMAScript pattern.
@@ -145,7 +148,7 @@ void RegExpObject::initProperties()
Q_ASSERT(value());
- QString p = value()->pattern;
+ QString p = *value()->pattern;
if (p.isEmpty()) {
p = QStringLiteral("(?:)");
} else {
@@ -180,13 +183,12 @@ Value *RegExpObject::lastIndexProperty()
QRegExp RegExpObject::toQRegExp() const
{
Qt::CaseSensitivity caseSensitivity = value()->ignoreCase ? Qt::CaseInsensitive : Qt::CaseSensitive;
- return QRegExp(value()->pattern, caseSensitivity, QRegExp::RegExp2);
+ return QRegExp(*value()->pattern, caseSensitivity, QRegExp::RegExp2);
}
QString RegExpObject::toString() const
{
- QString result = QLatin1Char('/') + source();
- result += QLatin1Char('/');
+ QString result = QLatin1Char('/') + source() + QLatin1Char('/');
if (global())
result += QLatin1Char('g');
if (value()->ignoreCase)
@@ -218,9 +220,9 @@ uint RegExpObject::flags() const
DEFINE_OBJECT_VTABLE(RegExpCtor);
-Heap::RegExpCtor::RegExpCtor(QV4::ExecutionContext *scope)
- : Heap::FunctionObject(scope, QStringLiteral("RegExp"))
+void Heap::RegExpCtor::init(QV4::ExecutionContext *scope)
{
+ Heap::FunctionObject::init(scope, QStringLiteral("RegExp"));
clearLastMatch();
}
@@ -232,34 +234,39 @@ void Heap::RegExpCtor::clearLastMatch()
lastMatchEnd = 0;
}
-ReturnedValue RegExpCtor::construct(const Managed *m, CallData *callData)
+void RegExpCtor::construct(const Managed *, Scope &scope, CallData *callData)
{
- Scope scope(static_cast<const Object *>(m)->engine());
-
ScopedValue r(scope, callData->argument(0));
ScopedValue f(scope, callData->argument(1));
Scoped<RegExpObject> re(scope, r);
if (re) {
- if (!f->isUndefined())
- return scope.engine->throwTypeError();
+ if (!f->isUndefined()) {
+ scope.result = scope.engine->throwTypeError();
+ return;
+ }
Scoped<RegExp> regexp(scope, re->value());
- return Encode(scope.engine->newRegExpObject(regexp, re->global()));
+ scope.result = Encode(scope.engine->newRegExpObject(regexp, re->global()));
+ return;
}
QString pattern;
if (!r->isUndefined())
pattern = r->toQString();
- if (scope.hasException())
- return Encode::undefined();
+ if (scope.hasException()) {
+ scope.result = Encode::undefined();
+ return;
+ }
bool global = false;
bool ignoreCase = false;
bool multiLine = false;
if (!f->isUndefined()) {
f = RuntimeHelpers::toString(scope.engine, f);
- if (scope.hasException())
- return Encode::undefined();
+ if (scope.hasException()) {
+ scope.result = Encode::undefined();
+ return;
+ }
QString str = f->stringValue()->toQString();
for (int i = 0; i < str.length(); ++i) {
if (str.at(i) == QLatin1Char('g') && !global) {
@@ -269,26 +276,31 @@ ReturnedValue RegExpCtor::construct(const Managed *m, CallData *callData)
} else if (str.at(i) == QLatin1Char('m') && !multiLine) {
multiLine = true;
} else {
- return scope.engine->throwSyntaxError(QStringLiteral("Invalid flags supplied to RegExp constructor"));
+ scope.result = scope.engine->throwSyntaxError(QStringLiteral("Invalid flags supplied to RegExp constructor"));
+ return;
}
}
}
Scoped<RegExp> regexp(scope, RegExp::create(scope.engine, pattern, ignoreCase, multiLine));
- if (!regexp->isValid())
- return scope.engine->throwSyntaxError(QStringLiteral("Invalid regular expression"));
+ if (!regexp->isValid()) {
+ scope.result = scope.engine->throwSyntaxError(QStringLiteral("Invalid regular expression"));
+ return;
+ }
- return Encode(scope.engine->newRegExpObject(regexp, global));
+ scope.result = Encode(scope.engine->newRegExpObject(regexp, global));
}
-ReturnedValue RegExpCtor::call(const Managed *that, CallData *callData)
+void RegExpCtor::call(const Managed *that, Scope &scope, CallData *callData)
{
if (callData->argc > 0 && callData->args[0].as<RegExpObject>()) {
- if (callData->argc == 1 || callData->args[1].isUndefined())
- return callData->args[0].asReturnedValue();
+ if (callData->argc == 1 || callData->args[1].isUndefined()) {
+ scope.result = callData->args[0];
+ return;
+ }
}
- return construct(that, callData);
+ construct(that, scope, callData);
}
void RegExpCtor::markObjects(Heap::Base *that, ExecutionEngine *e)
@@ -420,7 +432,8 @@ ReturnedValue RegExpPrototype::method_compile(CallContext *ctx)
ScopedCallData callData(scope, ctx->argc());
memcpy(callData->args, ctx->args(), ctx->argc()*sizeof(Value));
- Scoped<RegExpObject> re(scope, ctx->d()->engine->regExpCtor()->as<FunctionObject>()->construct(callData));
+ ctx->d()->engine->regExpCtor()->as<FunctionObject>()->construct(scope, callData);
+ Scoped<RegExpObject> re(scope, scope.result.asReturnedValue());
r->d()->value = re->value();
r->d()->global = re->global();
diff --git a/src/qml/jsruntime/qv4regexpobject_p.h b/src/qml/jsruntime/qv4regexpobject_p.h
index 4bd91bbedd..2c82cfdfd1 100644
--- a/src/qml/jsruntime/qv4regexpobject_p.h
+++ b/src/qml/jsruntime/qv4regexpobject_p.h
@@ -74,16 +74,16 @@ namespace QV4 {
namespace Heap {
struct RegExpObject : Object {
- RegExpObject();
- RegExpObject(QV4::RegExp *value, bool global);
- RegExpObject(const QRegExp &re);
+ void init();
+ void init(QV4::RegExp *value, bool global);
+ void init(const QRegExp &re);
Pointer<RegExp> value;
bool global;
};
struct RegExpCtor : FunctionObject {
- RegExpCtor(QV4::ExecutionContext *scope);
+ void init(QV4::ExecutionContext *scope);
Value lastMatch;
Pointer<String> lastInput;
int lastMatchStart;
@@ -140,8 +140,8 @@ struct RegExpCtor: FunctionObject
int lastMatchStart() { return d()->lastMatchStart; }
int lastMatchEnd() { return d()->lastMatchEnd; }
- static ReturnedValue construct(const Managed *m, CallData *callData);
- static ReturnedValue call(const Managed *that, CallData *callData);
+ static void construct(const Managed *m, Scope &scope, CallData *callData);
+ static void call(const Managed *that, Scope &scope, CallData *callData);
static void markObjects(Heap::Base *that, ExecutionEngine *e);
};
diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp
index 60136a9bd9..0a71f0000a 100644
--- a/src/qml/jsruntime/qv4runtime.cpp
+++ b/src/qml/jsruntime/qv4runtime.cpp
@@ -252,14 +252,14 @@ void RuntimeHelpers::numberToString(QString *result, double num, int radix)
result->append(QLatin1Char('+'));
result->append(QString::number(decpt - 1));
} else if (decpt <= 0) {
- result->prepend(QString::fromLatin1("0.%1").arg(QString().fill(zero, -decpt)));
+ result->prepend(QLatin1String("0.") + QString(-decpt, zero));
} else if (decpt < result->length()) {
result->insert(decpt, dot);
} else {
- result->append(QString().fill(zero, decpt - result->length()));
+ result->append(QString(decpt - result->length(), zero));
}
- if (sign)
+ if (sign && num)
result->prepend(QLatin1Char('-'));
return;
@@ -298,14 +298,14 @@ void RuntimeHelpers::numberToString(QString *result, double num, int radix)
result->prepend(QLatin1Char('-'));
}
-ReturnedValue Runtime::closure(ExecutionEngine *engine, int functionId)
+ReturnedValue Runtime::method_closure(ExecutionEngine *engine, int functionId)
{
QV4::Function *clos = engine->current->compilationUnit->runtimeFunctions[functionId];
Q_ASSERT(clos);
return FunctionObject::createScriptFunction(engine->currentContext, clos)->asReturnedValue();
}
-ReturnedValue Runtime::deleteElement(ExecutionEngine *engine, const Value &base, const Value &index)
+ReturnedValue Runtime::method_deleteElement(ExecutionEngine *engine, const Value &base, const Value &index)
{
Scope scope(engine);
ScopedObject o(scope, base);
@@ -317,17 +317,17 @@ ReturnedValue Runtime::deleteElement(ExecutionEngine *engine, const Value &base,
}
ScopedString name(scope, index.toString(engine));
- return Runtime::deleteMemberString(engine, base, name);
+ return method_deleteMemberString(engine, base, name);
}
-ReturnedValue Runtime::deleteMember(ExecutionEngine *engine, const Value &base, int nameIndex)
+ReturnedValue Runtime::method_deleteMember(ExecutionEngine *engine, const Value &base, int nameIndex)
{
Scope scope(engine);
ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[nameIndex]);
- return deleteMemberString(engine, base, name);
+ return method_deleteMemberString(engine, base, name);
}
-ReturnedValue Runtime::deleteMemberString(ExecutionEngine *engine, const Value &base, String *name)
+ReturnedValue Runtime::method_deleteMemberString(ExecutionEngine *engine, const Value &base, String *name)
{
Scope scope(engine);
ScopedObject obj(scope, base.toObject(engine));
@@ -336,14 +336,14 @@ ReturnedValue Runtime::deleteMemberString(ExecutionEngine *engine, const Value &
return Encode(obj->deleteProperty(name));
}
-ReturnedValue Runtime::deleteName(ExecutionEngine *engine, int nameIndex)
+ReturnedValue Runtime::method_deleteName(ExecutionEngine *engine, int nameIndex)
{
Scope scope(engine);
ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[nameIndex]);
return Encode(engine->currentContext->deleteProperty(name));
}
-QV4::ReturnedValue Runtime::instanceof(ExecutionEngine *engine, const Value &left, const Value &right)
+QV4::ReturnedValue Runtime::method_instanceof(ExecutionEngine *engine, const Value &left, const Value &right)
{
Scope scope(engine);
ScopedFunctionObject f(scope, right.as<FunctionObject>());
@@ -373,7 +373,7 @@ QV4::ReturnedValue Runtime::instanceof(ExecutionEngine *engine, const Value &lef
return Encode(false);
}
-QV4::ReturnedValue Runtime::in(ExecutionEngine *engine, const Value &left, const Value &right)
+QV4::ReturnedValue Runtime::method_in(ExecutionEngine *engine, const Value &left, const Value &right)
{
if (!right.isObject())
return engine->throwTypeError();
@@ -387,7 +387,7 @@ QV4::ReturnedValue Runtime::in(ExecutionEngine *engine, const Value &left, const
double RuntimeHelpers::stringToNumber(const QString &string)
{
- QString s = string.trimmed();
+ const QStringRef s = QStringRef(&string).trimmed();
if (s.startsWith(QLatin1String("0x")) || s.startsWith(QLatin1String("0X")))
return s.toLong(0, 16);
bool ok;
@@ -438,9 +438,9 @@ ReturnedValue RuntimeHelpers::objectDefaultValue(const Object *object, int typeH
ScopedValue conv(scope, object->get(meth1));
if (FunctionObject *o = conv->as<FunctionObject>()) {
- ScopedValue r(scope, o->call(callData));
- if (r->isPrimitive())
- return r->asReturnedValue();
+ o->call(scope, callData);
+ if (scope.result.isPrimitive())
+ return scope.result.asReturnedValue();
}
if (engine->hasException)
@@ -448,9 +448,9 @@ ReturnedValue RuntimeHelpers::objectDefaultValue(const Object *object, int typeH
conv = object->get(meth2);
if (FunctionObject *o = conv->as<FunctionObject>()) {
- ScopedValue r(scope, o->call(callData));
- if (r->isPrimitive())
- return r->asReturnedValue();
+ o->call(scope, callData);
+ if (scope.result.isPrimitive())
+ return scope.result.asReturnedValue();
}
return engine->throwTypeError();
@@ -562,7 +562,7 @@ QV4::ReturnedValue RuntimeHelpers::addHelper(ExecutionEngine *engine, const Valu
return Encode(x + y);
}
-QV4::ReturnedValue Runtime::addString(ExecutionEngine *engine, const Value &left, const Value &right)
+QV4::ReturnedValue Runtime::method_addString(ExecutionEngine *engine, const Value &left, const Value &right)
{
Q_ASSERT(left.isString() || right.isString());
@@ -593,7 +593,7 @@ QV4::ReturnedValue Runtime::addString(ExecutionEngine *engine, const Value &left
return (mm->alloc<String>(mm, pleft->stringValue()->d(), pright->stringValue()->d()))->asReturnedValue();
}
-void Runtime::setProperty(ExecutionEngine *engine, const Value &object, int nameIndex, const Value &value)
+void Runtime::method_setProperty(ExecutionEngine *engine, const Value &object, int nameIndex, const Value &value)
{
Scope scope(engine);
ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[nameIndex]);
@@ -603,7 +603,7 @@ void Runtime::setProperty(ExecutionEngine *engine, const Value &object, int name
o->put(name, value);
}
-ReturnedValue Runtime::getElement(ExecutionEngine *engine, const Value &object, const Value &index)
+ReturnedValue Runtime::method_getElement(ExecutionEngine *engine, const Value &object, const Value &index)
{
Scope scope(engine);
uint idx = index.asArrayIndex();
@@ -646,7 +646,7 @@ ReturnedValue Runtime::getElement(ExecutionEngine *engine, const Value &object,
return o->get(name);
}
-void Runtime::setElement(ExecutionEngine *engine, const Value &object, const Value &index, const Value &value)
+void Runtime::method_setElement(ExecutionEngine *engine, const Value &object, const Value &index, const Value &value)
{
Scope scope(engine);
ScopedObject o(scope, object.toObject(engine));
@@ -670,7 +670,7 @@ void Runtime::setElement(ExecutionEngine *engine, const Value &object, const Val
o->put(name, value);
}
-ReturnedValue Runtime::foreachIterator(ExecutionEngine *engine, const Value &in)
+ReturnedValue Runtime::method_foreachIterator(ExecutionEngine *engine, const Value &in)
{
Scope scope(engine);
ScopedObject o(scope, (Object *)0);
@@ -679,7 +679,7 @@ ReturnedValue Runtime::foreachIterator(ExecutionEngine *engine, const Value &in)
return engine->newForEachIteratorObject(o)->asReturnedValue();
}
-ReturnedValue Runtime::foreachNextPropertyName(const Value &foreach_iterator)
+ReturnedValue Runtime::method_foreachNextPropertyName(const Value &foreach_iterator)
{
Q_ASSERT(foreach_iterator.isObject());
@@ -690,14 +690,14 @@ ReturnedValue Runtime::foreachNextPropertyName(const Value &foreach_iterator)
}
-void Runtime::setActivationProperty(ExecutionEngine *engine, int nameIndex, const Value &value)
+void Runtime::method_setActivationProperty(ExecutionEngine *engine, int nameIndex, const Value &value)
{
Scope scope(engine);
ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[nameIndex]);
engine->currentContext->setProperty(name, value);
}
-ReturnedValue Runtime::getProperty(ExecutionEngine *engine, const Value &object, int nameIndex)
+ReturnedValue Runtime::method_getProperty(ExecutionEngine *engine, const Value &object, int nameIndex)
{
Scope scope(engine);
ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[nameIndex]);
@@ -717,7 +717,7 @@ ReturnedValue Runtime::getProperty(ExecutionEngine *engine, const Value &object,
return o->get(name);
}
-ReturnedValue Runtime::getActivationProperty(ExecutionEngine *engine, int nameIndex)
+ReturnedValue Runtime::method_getActivationProperty(ExecutionEngine *engine, int nameIndex)
{
Scope scope(engine);
ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[nameIndex]);
@@ -743,9 +743,9 @@ uint RuntimeHelpers::equalHelper(const Value &x, const Value &y)
double dx = RuntimeHelpers::toNumber(x);
return dx == y.asDouble();
} else if (x.isBoolean()) {
- return Runtime::compareEqual(Primitive::fromDouble((double) x.booleanValue()), y);
+ return Runtime::method_compareEqual(Primitive::fromDouble((double) x.booleanValue()), y);
} else if (y.isBoolean()) {
- return Runtime::compareEqual(x, Primitive::fromDouble((double) y.booleanValue()));
+ return Runtime::method_compareEqual(x, Primitive::fromDouble((double) y.booleanValue()));
} else {
#ifdef V4_BOOTSTRAP
Q_UNIMPLEMENTED();
@@ -753,11 +753,11 @@ uint RuntimeHelpers::equalHelper(const Value &x, const Value &y)
if ((x.isNumber() || x.isString()) && y.isObject()) {
Scope scope(y.objectValue()->engine());
ScopedValue py(scope, RuntimeHelpers::toPrimitive(y, PREFERREDTYPE_HINT));
- return Runtime::compareEqual(x, py);
+ return Runtime::method_compareEqual(x, py);
} else if (x.isObject() && (y.isNumber() || y.isString())) {
Scope scope(x.objectValue()->engine());
ScopedValue px(scope, RuntimeHelpers::toPrimitive(x, PREFERREDTYPE_HINT));
- return Runtime::compareEqual(px, y);
+ return Runtime::method_compareEqual(px, y);
}
#endif
}
@@ -780,7 +780,7 @@ Bool RuntimeHelpers::strictEqual(const Value &x, const Value &y)
return false;
}
-QV4::Bool Runtime::compareGreaterThan(const Value &l, const Value &r)
+QV4::Bool Runtime::method_compareGreaterThan(const Value &l, const Value &r)
{
TRACE2(l, r);
if (l.isInteger() && r.isInteger())
@@ -804,7 +804,7 @@ QV4::Bool Runtime::compareGreaterThan(const Value &l, const Value &r)
QV4::Scope scope(e);
QV4::ScopedValue pl(scope, RuntimeHelpers::toPrimitive(l, QV4::NUMBER_HINT));
QV4::ScopedValue pr(scope, RuntimeHelpers::toPrimitive(r, QV4::NUMBER_HINT));
- return Runtime::compareGreaterThan(pl, pr);
+ return Runtime::method_compareGreaterThan(pl, pr);
#endif
}
@@ -813,7 +813,7 @@ QV4::Bool Runtime::compareGreaterThan(const Value &l, const Value &r)
return dl > dr;
}
-QV4::Bool Runtime::compareLessThan(const Value &l, const Value &r)
+QV4::Bool Runtime::method_compareLessThan(const Value &l, const Value &r)
{
TRACE2(l, r);
if (l.isInteger() && r.isInteger())
@@ -837,7 +837,7 @@ QV4::Bool Runtime::compareLessThan(const Value &l, const Value &r)
QV4::Scope scope(e);
QV4::ScopedValue pl(scope, RuntimeHelpers::toPrimitive(l, QV4::NUMBER_HINT));
QV4::ScopedValue pr(scope, RuntimeHelpers::toPrimitive(r, QV4::NUMBER_HINT));
- return Runtime::compareLessThan(pl, pr);
+ return Runtime::method_compareLessThan(pl, pr);
#endif
}
@@ -846,7 +846,7 @@ QV4::Bool Runtime::compareLessThan(const Value &l, const Value &r)
return dl < dr;
}
-QV4::Bool Runtime::compareGreaterEqual(const Value &l, const Value &r)
+QV4::Bool Runtime::method_compareGreaterEqual(const Value &l, const Value &r)
{
TRACE2(l, r);
if (l.isInteger() && r.isInteger())
@@ -870,7 +870,7 @@ QV4::Bool Runtime::compareGreaterEqual(const Value &l, const Value &r)
QV4::Scope scope(e);
QV4::ScopedValue pl(scope, RuntimeHelpers::toPrimitive(l, QV4::NUMBER_HINT));
QV4::ScopedValue pr(scope, RuntimeHelpers::toPrimitive(r, QV4::NUMBER_HINT));
- return Runtime::compareGreaterEqual(pl, pr);
+ return Runtime::method_compareGreaterEqual(pl, pr);
#endif
}
@@ -879,7 +879,7 @@ QV4::Bool Runtime::compareGreaterEqual(const Value &l, const Value &r)
return dl >= dr;
}
-QV4::Bool Runtime::compareLessEqual(const Value &l, const Value &r)
+QV4::Bool Runtime::method_compareLessEqual(const Value &l, const Value &r)
{
TRACE2(l, r);
if (l.isInteger() && r.isInteger())
@@ -903,7 +903,7 @@ QV4::Bool Runtime::compareLessEqual(const Value &l, const Value &r)
QV4::Scope scope(e);
QV4::ScopedValue pl(scope, RuntimeHelpers::toPrimitive(l, QV4::NUMBER_HINT));
QV4::ScopedValue pr(scope, RuntimeHelpers::toPrimitive(r, QV4::NUMBER_HINT));
- return Runtime::compareLessEqual(pl, pr);
+ return Runtime::method_compareLessEqual(pl, pr);
#endif
}
@@ -913,26 +913,26 @@ QV4::Bool Runtime::compareLessEqual(const Value &l, const Value &r)
}
#ifndef V4_BOOTSTRAP
-Bool Runtime::compareInstanceof(ExecutionEngine *engine, const Value &left, const Value &right)
+Bool Runtime::method_compareInstanceof(ExecutionEngine *engine, const Value &left, const Value &right)
{
TRACE2(left, right);
Scope scope(engine);
- ScopedValue v(scope, Runtime::instanceof(engine, left, right));
+ ScopedValue v(scope, method_instanceof(engine, left, right));
return v->booleanValue();
}
-uint Runtime::compareIn(ExecutionEngine *engine, const Value &left, const Value &right)
+uint Runtime::method_compareIn(ExecutionEngine *engine, const Value &left, const Value &right)
{
TRACE2(left, right);
Scope scope(engine);
- ScopedValue v(scope, Runtime::in(engine, left, right));
+ ScopedValue v(scope, method_in(engine, left, right));
return v->booleanValue();
}
-ReturnedValue Runtime::callGlobalLookup(ExecutionEngine *engine, uint index, CallData *callData)
+ReturnedValue Runtime::method_callGlobalLookup(ExecutionEngine *engine, uint index, CallData *callData)
{
Scope scope(engine);
Q_ASSERT(callData->thisObject.isUndefined());
@@ -943,14 +943,17 @@ ReturnedValue Runtime::callGlobalLookup(ExecutionEngine *engine, uint index, Cal
return engine->throwTypeError();
ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[l->nameIndex]);
- if (o->d() == scope.engine->evalFunction()->d() && name->equals(scope.engine->id_eval()))
- return static_cast<EvalFunction *>(o.getPointer())->evalCall(callData, true);
+ if (o->d() == scope.engine->evalFunction()->d() && name->equals(scope.engine->id_eval())) {
+ static_cast<EvalFunction *>(o.getPointer())->evalCall(scope, callData, true);
+ } else {
+ o->call(scope, callData);
+ }
- return o->call(callData);
+ return scope.result.asReturnedValue();
}
-ReturnedValue Runtime::callActivationProperty(ExecutionEngine *engine, int nameIndex, CallData *callData)
+ReturnedValue Runtime::method_callActivationProperty(ExecutionEngine *engine, int nameIndex, CallData *callData)
{
Q_ASSERT(callData->thisObject.isUndefined());
Scope scope(engine);
@@ -974,36 +977,41 @@ ReturnedValue Runtime::callActivationProperty(ExecutionEngine *engine, int nameI
}
if (o->d() == scope.engine->evalFunction()->d() && name->equals(scope.engine->id_eval())) {
- return static_cast<EvalFunction *>(o)->evalCall(callData, true);
+ static_cast<EvalFunction *>(o)->evalCall(scope, callData, true);
+ } else {
+ o->call(scope, callData);
}
- return o->call(callData);
+ return scope.result.asReturnedValue();
}
-ReturnedValue Runtime::callQmlScopeObjectProperty(ExecutionEngine *engine, int propertyIndex, CallData *callData)
+ReturnedValue Runtime::method_callQmlScopeObjectProperty(ExecutionEngine *engine, int propertyIndex, CallData *callData)
{
Scope scope(engine);
- ScopedFunctionObject o(scope, getQmlScopeObjectProperty(engine, callData->thisObject, propertyIndex));
+ ScopedFunctionObject o(scope, method_getQmlScopeObjectProperty(engine, callData->thisObject, propertyIndex, /*captureRequired*/true));
if (!o) {
QString error = QStringLiteral("Property '%1' of scope object is not a function").arg(propertyIndex);
return engine->throwTypeError(error);
}
- return o->call(callData);
+
+ o->call(scope, callData);
+ return scope.result.asReturnedValue();
}
-ReturnedValue Runtime::callQmlContextObjectProperty(ExecutionEngine *engine, int propertyIndex, CallData *callData)
+ReturnedValue Runtime::method_callQmlContextObjectProperty(ExecutionEngine *engine, int propertyIndex, CallData *callData)
{
Scope scope(engine);
- ScopedFunctionObject o(scope, getQmlContextObjectProperty(engine, callData->thisObject, propertyIndex));
+ ScopedFunctionObject o(scope, method_getQmlContextObjectProperty(engine, callData->thisObject, propertyIndex, /*captureRequired*/true));
if (!o) {
QString error = QStringLiteral("Property '%1' of context object is not a function").arg(propertyIndex);
return engine->throwTypeError(error);
}
- return o->call(callData);
+ o->call(scope, callData);
+ return scope.result.asReturnedValue();
}
-ReturnedValue Runtime::callProperty(ExecutionEngine *engine, int nameIndex, CallData *callData)
+ReturnedValue Runtime::method_callProperty(ExecutionEngine *engine, int nameIndex, CallData *callData)
{
Scope scope(engine);
ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[nameIndex]);
@@ -1022,26 +1030,31 @@ ReturnedValue Runtime::callProperty(ExecutionEngine *engine, int nameIndex, Call
}
ScopedFunctionObject o(scope, baseObject->get(name));
- if (!o) {
+ if (o) {
+ o->call(scope, callData);
+ return scope.result.asReturnedValue();
+ } else {
QString error = QStringLiteral("Property '%1' of object %2 is not a function").arg(name->toQString(), callData->thisObject.toQStringNoThrow());
return engine->throwTypeError(error);
}
- return o->call(callData);
}
-ReturnedValue Runtime::callPropertyLookup(ExecutionEngine *engine, uint index, CallData *callData)
+ReturnedValue Runtime::method_callPropertyLookup(ExecutionEngine *engine, uint index, CallData *callData)
{
Lookup *l = engine->current->lookups + index;
Value v;
v = l->getter(l, engine, callData->thisObject);
- if (!v.isObject())
+ if (v.isObject()) {
+ Scope scope(engine);
+ v.objectValue()->call(scope, callData);
+ return scope.result.asReturnedValue();
+ } else {
return engine->throwTypeError();
-
- return v.objectValue()->call(callData);
+ }
}
-ReturnedValue Runtime::callElement(ExecutionEngine *engine, const Value &index, CallData *callData)
+ReturnedValue Runtime::method_callElement(ExecutionEngine *engine, const Value &index, CallData *callData)
{
Scope scope(engine);
ScopedObject baseObject(scope, callData->thisObject.toObject(engine));
@@ -1055,33 +1068,38 @@ ReturnedValue Runtime::callElement(ExecutionEngine *engine, const Value &index,
if (!o)
return engine->throwTypeError();
- return o->call(callData);
+ o->call(scope, callData);
+ return scope.result.asReturnedValue();
}
-ReturnedValue Runtime::callValue(ExecutionEngine *engine, const Value &func, CallData *callData)
+ReturnedValue Runtime::method_callValue(ExecutionEngine *engine, const Value &func, CallData *callData)
{
if (!func.isObject())
return engine->throwTypeError(QStringLiteral("%1 is not a function").arg(func.toQStringNoThrow()));
- return func.objectValue()->call(callData);
+ Scope scope(engine);
+ func.objectValue()->call(scope, callData);
+ return scope.result.asReturnedValue();
}
-ReturnedValue Runtime::constructGlobalLookup(ExecutionEngine *engine, uint index, CallData *callData)
+ReturnedValue Runtime::method_constructGlobalLookup(ExecutionEngine *engine, uint index, CallData *callData)
{
Scope scope(engine);
Q_ASSERT(callData->thisObject.isUndefined());
Lookup *l = engine->current->lookups + index;
ScopedObject f(scope, l->globalGetter(l, engine));
- if (!f)
+ if (f) {
+ f->construct(scope, callData);
+ return scope.result.asReturnedValue();
+ } else {
return engine->throwTypeError();
-
- return f->construct(callData);
+ }
}
-ReturnedValue Runtime::constructActivationProperty(ExecutionEngine *engine, int nameIndex, CallData *callData)
+ReturnedValue Runtime::method_constructActivationProperty(ExecutionEngine *engine, int nameIndex, CallData *callData)
{
Scope scope(engine);
ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[nameIndex]);
@@ -1093,19 +1111,22 @@ ReturnedValue Runtime::constructActivationProperty(ExecutionEngine *engine, int
if (!f)
return engine->throwTypeError();
- return f->construct(callData);
+ f->construct(scope, callData);
+ return scope.result.asReturnedValue();
}
-ReturnedValue Runtime::constructValue(ExecutionEngine *engine, const Value &func, CallData *callData)
+ReturnedValue Runtime::method_constructValue(ExecutionEngine *engine, const Value &func, CallData *callData)
{
const Object *f = func.as<Object>();
if (!f)
return engine->throwTypeError();
- return f->construct(callData);
+ Scope scope(engine);
+ f->construct(scope, callData);
+ return scope.result.asReturnedValue();
}
-ReturnedValue Runtime::constructProperty(ExecutionEngine *engine, int nameIndex, CallData *callData)
+ReturnedValue Runtime::method_constructProperty(ExecutionEngine *engine, int nameIndex, CallData *callData)
{
Scope scope(engine);
ScopedObject thisObject(scope, callData->thisObject.toObject(engine));
@@ -1114,31 +1135,38 @@ ReturnedValue Runtime::constructProperty(ExecutionEngine *engine, int nameIndex,
return Encode::undefined();
ScopedObject f(scope, thisObject->get(name));
- if (!f)
+ if (f) {
+ Scope scope(engine);
+ f->construct(scope, callData);
+ return scope.result.asReturnedValue();
+ } else {
return engine->throwTypeError();
-
- return f->construct(callData);
+ }
}
-ReturnedValue Runtime::constructPropertyLookup(ExecutionEngine *engine, uint index, CallData *callData)
+ReturnedValue Runtime::method_constructPropertyLookup(ExecutionEngine *engine, uint index, CallData *callData)
{
Lookup *l = engine->current->lookups + index;
Value v;
v = l->getter(l, engine, callData->thisObject);
- if (!v.isObject())
+ if (v.isObject()) {
+ Scope scope(engine);
+ ScopedValue result(scope);
+ v.objectValue()->construct(scope, callData);
+ return scope.result.asReturnedValue();
+ } else {
return engine->throwTypeError();
-
- return v.objectValue()->construct(callData);
+ }
}
-void Runtime::throwException(ExecutionEngine *engine, const Value &value)
+void Runtime::method_throwException(ExecutionEngine *engine, const Value &value)
{
if (!value.isEmpty())
engine->throwError(value);
}
-ReturnedValue Runtime::typeofValue(ExecutionEngine *engine, const Value &value)
+ReturnedValue Runtime::method_typeofValue(ExecutionEngine *engine, const Value &value)
{
Scope scope(engine);
ScopedString res(scope);
@@ -1167,39 +1195,37 @@ ReturnedValue Runtime::typeofValue(ExecutionEngine *engine, const Value &value)
return res.asReturnedValue();
}
-QV4::ReturnedValue Runtime::typeofName(ExecutionEngine *engine, int nameIndex)
+QV4::ReturnedValue Runtime::method_typeofName(ExecutionEngine *engine, int nameIndex)
{
Scope scope(engine);
ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[nameIndex]);
ScopedValue prop(scope, engine->currentContext->getProperty(name));
// typeof doesn't throw. clear any possible exception
scope.engine->hasException = false;
- return Runtime::typeofValue(engine, prop);
+ return method_typeofValue(engine, prop);
}
#ifndef V4_BOOTSTRAP
-ReturnedValue Runtime::typeofScopeObjectProperty(ExecutionEngine *engine, const Value &context,
- int propertyIndex)
+ReturnedValue Runtime::method_typeofScopeObjectProperty(ExecutionEngine *engine, const Value &context, int propertyIndex)
{
Scope scope(engine);
- ScopedValue prop(scope, getQmlScopeObjectProperty(engine, context, propertyIndex));
+ ScopedValue prop(scope, method_getQmlScopeObjectProperty(engine, context, propertyIndex, /*captureRequired*/true));
if (scope.engine->hasException)
return Encode::undefined();
- return Runtime::typeofValue(engine, prop);
+ return method_typeofValue(engine, prop);
}
-ReturnedValue Runtime::typeofContextObjectProperty(ExecutionEngine *engine, const Value &context,
- int propertyIndex)
+ReturnedValue Runtime::method_typeofContextObjectProperty(ExecutionEngine *engine, const Value &context, int propertyIndex)
{
Scope scope(engine);
- ScopedValue prop(scope, getQmlContextObjectProperty(engine, context, propertyIndex));
+ ScopedValue prop(scope, method_getQmlContextObjectProperty(engine, context, propertyIndex, /*captureRequired*/true));
if (scope.engine->hasException)
return Encode::undefined();
- return Runtime::typeofValue(engine, prop);
+ return method_typeofValue(engine, prop);
}
#endif // V4_BOOTSTRAP
-QV4::ReturnedValue Runtime::typeofMember(ExecutionEngine *engine, const Value &base, int nameIndex)
+QV4::ReturnedValue Runtime::method_typeofMember(ExecutionEngine *engine, const Value &base, int nameIndex)
{
Scope scope(engine);
ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[nameIndex]);
@@ -1207,10 +1233,10 @@ QV4::ReturnedValue Runtime::typeofMember(ExecutionEngine *engine, const Value &b
if (scope.engine->hasException)
return Encode::undefined();
ScopedValue prop(scope, obj->get(name));
- return Runtime::typeofValue(engine, prop);
+ return method_typeofValue(engine, prop);
}
-QV4::ReturnedValue Runtime::typeofElement(ExecutionEngine *engine, const Value &base, const Value &index)
+QV4::ReturnedValue Runtime::method_typeofElement(ExecutionEngine *engine, const Value &base, const Value &index)
{
Scope scope(engine);
ScopedString name(scope, index.toString(engine));
@@ -1218,10 +1244,10 @@ QV4::ReturnedValue Runtime::typeofElement(ExecutionEngine *engine, const Value &
if (scope.engine->hasException)
return Encode::undefined();
ScopedValue prop(scope, obj->get(name));
- return Runtime::typeofValue(engine, prop);
+ return method_typeofValue(engine, prop);
}
-ReturnedValue Runtime::unwindException(ExecutionEngine *engine)
+ReturnedValue Runtime::method_unwindException(ExecutionEngine *engine)
{
if (!engine->hasException)
return Primitive::emptyValue().asReturnedValue();
@@ -1233,39 +1259,39 @@ ReturnedValue Runtime::unwindException(ExecutionEngine *engine)
*
* Instead the push/pop pair acts as a non local scope.
*/
-void Runtime::pushWithScope(const Value &o, ExecutionEngine *engine)
+void Runtime::method_pushWithScope(const Value &o, ExecutionEngine *engine)
{
engine->pushContext(engine->currentContext->newWithContext(o.toObject(engine)));
Q_ASSERT(engine->jsStackTop == engine->currentContext + 2);
}
-void Runtime::pushCatchScope(NoThrowEngine *engine, int exceptionVarNameIndex)
+void Runtime::method_pushCatchScope(NoThrowEngine *engine, int exceptionVarNameIndex)
{
ExecutionContext *c = engine->currentContext;
engine->pushContext(c->newCatchContext(c->d()->compilationUnit->runtimeStrings[exceptionVarNameIndex], engine->catchException(0)));
Q_ASSERT(engine->jsStackTop == engine->currentContext + 2);
}
-void Runtime::popScope(ExecutionEngine *engine)
+void Runtime::method_popScope(ExecutionEngine *engine)
{
Q_ASSERT(engine->jsStackTop == engine->currentContext + 2);
engine->popContext();
engine->jsStackTop -= 2;
}
-void Runtime::declareVar(ExecutionEngine *engine, bool deletable, int nameIndex)
+void Runtime::method_declareVar(ExecutionEngine *engine, bool deletable, int nameIndex)
{
Scope scope(engine);
ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[nameIndex]);
engine->currentContext->createMutableBinding(name, deletable);
}
-ReturnedValue Runtime::arrayLiteral(ExecutionEngine *engine, Value *values, uint length)
+ReturnedValue Runtime::method_arrayLiteral(ExecutionEngine *engine, Value *values, uint length)
{
return engine->newArrayObject(values, length)->asReturnedValue();
}
-ReturnedValue Runtime::objectLiteral(ExecutionEngine *engine, const QV4::Value *args, int classId, int arrayValueCount, int arrayGetterSetterCountAndFlags)
+ReturnedValue Runtime::method_objectLiteral(ExecutionEngine *engine, const QV4::Value *args, int classId, int arrayValueCount, int arrayGetterSetterCountAndFlags)
{
Scope scope(engine);
QV4::InternalClass *klass = engine->current->compilationUnit->runtimeClasses[classId];
@@ -1307,7 +1333,7 @@ ReturnedValue Runtime::objectLiteral(ExecutionEngine *engine, const QV4::Value *
return o.asReturnedValue();
}
-QV4::ReturnedValue Runtime::setupArgumentsObject(ExecutionEngine *engine)
+QV4::ReturnedValue Runtime::method_setupArgumentsObject(ExecutionEngine *engine)
{
Q_ASSERT(engine->current->type == Heap::ExecutionContext::Type_CallContext);
QV4::CallContext *c = static_cast<QV4::CallContext *>(engine->currentContext);
@@ -1317,7 +1343,7 @@ QV4::ReturnedValue Runtime::setupArgumentsObject(ExecutionEngine *engine)
#endif // V4_BOOTSTRAP
-QV4::ReturnedValue Runtime::increment(const Value &value)
+QV4::ReturnedValue Runtime::method_increment(const Value &value)
{
TRACE1(value);
@@ -1329,7 +1355,7 @@ QV4::ReturnedValue Runtime::increment(const Value &value)
}
}
-QV4::ReturnedValue Runtime::decrement(const Value &value)
+QV4::ReturnedValue Runtime::method_decrement(const Value &value)
{
TRACE1(value);
@@ -1364,31 +1390,31 @@ QV4::ReturnedValue RuntimeHelpers::toObject(ExecutionEngine *engine, const Value
#endif // V4_BOOTSTRAP
-ReturnedValue Runtime::toDouble(const Value &value)
+ReturnedValue Runtime::method_toDouble(const Value &value)
{
TRACE1(value);
return Encode(value.toNumber());
}
-int Runtime::toInt(const Value &value)
+int Runtime::method_toInt(const Value &value)
{
TRACE1(value);
return value.toInt32();
}
-int Runtime::doubleToInt(const double &d)
+int Runtime::method_doubleToInt(const double &d)
{
TRACE0();
return Primitive::toInt32(d);
}
-unsigned Runtime::toUInt(const Value &value)
+unsigned Runtime::method_toUInt(const Value &value)
{
TRACE1(value);
return value.toUInt32();
}
-unsigned Runtime::doubleToUInt(const double &d)
+unsigned Runtime::method_doubleToUInt(const double &d)
{
TRACE0();
return Primitive::toUInt32(d);
@@ -1396,17 +1422,17 @@ unsigned Runtime::doubleToUInt(const double &d)
#ifndef V4_BOOTSTRAP
-ReturnedValue Runtime::getQmlContext(NoThrowEngine *engine)
+ReturnedValue Runtime::method_getQmlContext(NoThrowEngine *engine)
{
return engine->qmlContext()->asReturnedValue();
}
-ReturnedValue Runtime::regexpLiteral(ExecutionEngine *engine, int id)
+ReturnedValue Runtime::method_regexpLiteral(ExecutionEngine *engine, int id)
{
return engine->current->compilationUnit->runtimeRegularExpressions[id].asReturnedValue();
}
-ReturnedValue Runtime::getQmlQObjectProperty(ExecutionEngine *engine, const Value &object, int propertyIndex, bool captureRequired)
+ReturnedValue Runtime::method_getQmlQObjectProperty(ExecutionEngine *engine, const Value &object, int propertyIndex, bool captureRequired)
{
Scope scope(engine);
QV4::Scoped<QObjectWrapper> wrapper(scope, object);
@@ -1417,7 +1443,7 @@ ReturnedValue Runtime::getQmlQObjectProperty(ExecutionEngine *engine, const Valu
return QV4::QObjectWrapper::getProperty(scope.engine, wrapper->object(), propertyIndex, captureRequired);
}
-QV4::ReturnedValue Runtime::getQmlAttachedProperty(ExecutionEngine *engine, int attachedPropertiesId, int propertyIndex)
+QV4::ReturnedValue Runtime::method_getQmlAttachedProperty(ExecutionEngine *engine, int attachedPropertiesId, int propertyIndex)
{
QObject *scopeObject = engine->qmlScopeObject();
QObject *attachedObject = qmlAttachedPropertiesObjectById(attachedPropertiesId, scopeObject);
@@ -1427,19 +1453,19 @@ QV4::ReturnedValue Runtime::getQmlAttachedProperty(ExecutionEngine *engine, int
return QV4::QObjectWrapper::getProperty(engine, attachedObject, propertyIndex, /*captureRequired*/true);
}
-ReturnedValue Runtime::getQmlScopeObjectProperty(ExecutionEngine *engine, const Value &context, int propertyIndex)
+ReturnedValue Runtime::method_getQmlScopeObjectProperty(ExecutionEngine *engine, const Value &context, int propertyIndex, bool captureRequired)
{
const QmlContext &c = static_cast<const QmlContext &>(context);
- return QV4::QObjectWrapper::getProperty(engine, c.d()->qml->scopeObject, propertyIndex, false);
+ return QV4::QObjectWrapper::getProperty(engine, c.d()->qml->scopeObject, propertyIndex, captureRequired);
}
-ReturnedValue Runtime::getQmlContextObjectProperty(ExecutionEngine *engine, const Value &context, int propertyIndex)
+ReturnedValue Runtime::method_getQmlContextObjectProperty(ExecutionEngine *engine, const Value &context, int propertyIndex, bool captureRequired)
{
const QmlContext &c = static_cast<const QmlContext &>(context);
- return QV4::QObjectWrapper::getProperty(engine, c.d()->qml->context->contextObject, propertyIndex, false);
+ return QV4::QObjectWrapper::getProperty(engine, (*c.d()->qml->context)->contextObject, propertyIndex, captureRequired);
}
-ReturnedValue Runtime::getQmlSingletonQObjectProperty(ExecutionEngine *engine, const Value &object, int propertyIndex, bool captureRequired)
+ReturnedValue Runtime::method_getQmlSingletonQObjectProperty(ExecutionEngine *engine, const Value &object, int propertyIndex, bool captureRequired)
{
Scope scope(engine);
QV4::Scoped<QmlTypeWrapper> wrapper(scope, object);
@@ -1450,11 +1476,11 @@ ReturnedValue Runtime::getQmlSingletonQObjectProperty(ExecutionEngine *engine, c
return QV4::QObjectWrapper::getProperty(scope.engine, wrapper->singletonObject(), propertyIndex, captureRequired);
}
-ReturnedValue Runtime::getQmlIdObject(ExecutionEngine *engine, const Value &c, uint index)
+ReturnedValue Runtime::method_getQmlIdObject(ExecutionEngine *engine, const Value &c, uint index)
{
Scope scope(engine);
const QmlContext &qmlContext = static_cast<const QmlContext &>(c);
- QQmlContextData *context = qmlContext.d()->qml->context;
+ QQmlContextData *context = *qmlContext.d()->qml->context;
if (!context || index >= (uint)context->idValueCount)
return Encode::undefined();
@@ -1465,19 +1491,19 @@ ReturnedValue Runtime::getQmlIdObject(ExecutionEngine *engine, const Value &c, u
return QObjectWrapper::wrap(engine, context->idValues[index].data());
}
-void Runtime::setQmlScopeObjectProperty(ExecutionEngine *engine, const Value &context, int propertyIndex, const Value &value)
+void Runtime::method_setQmlScopeObjectProperty(ExecutionEngine *engine, const Value &context, int propertyIndex, const Value &value)
{
const QmlContext &c = static_cast<const QmlContext &>(context);
return QV4::QObjectWrapper::setProperty(engine, c.d()->qml->scopeObject, propertyIndex, value);
}
-void Runtime::setQmlContextObjectProperty(ExecutionEngine *engine, const Value &context, int propertyIndex, const Value &value)
+void Runtime::method_setQmlContextObjectProperty(ExecutionEngine *engine, const Value &context, int propertyIndex, const Value &value)
{
const QmlContext &c = static_cast<const QmlContext &>(context);
- return QV4::QObjectWrapper::setProperty(engine, c.d()->qml->context->contextObject, propertyIndex, value);
+ return QV4::QObjectWrapper::setProperty(engine, (*c.d()->qml->context)->contextObject, propertyIndex, value);
}
-void Runtime::setQmlQObjectProperty(ExecutionEngine *engine, const Value &object, int propertyIndex, const Value &value)
+void Runtime::method_setQmlQObjectProperty(ExecutionEngine *engine, const Value &object, int propertyIndex, const Value &value)
{
Scope scope(engine);
QV4::Scoped<QObjectWrapper> wrapper(scope, object);
@@ -1488,7 +1514,7 @@ void Runtime::setQmlQObjectProperty(ExecutionEngine *engine, const Value &object
wrapper->setProperty(engine, propertyIndex, value);
}
-ReturnedValue Runtime::getQmlImportedScripts(NoThrowEngine *engine)
+ReturnedValue Runtime::method_getQmlImportedScripts(NoThrowEngine *engine)
{
QQmlContextData *context = engine->callingQmlContext();
if (!context)
@@ -1496,14 +1522,14 @@ ReturnedValue Runtime::getQmlImportedScripts(NoThrowEngine *engine)
return context->importedScripts.value();
}
-QV4::ReturnedValue Runtime::getQmlSingleton(QV4::NoThrowEngine *engine, int nameIndex)
+QV4::ReturnedValue Runtime::method_getQmlSingleton(QV4::NoThrowEngine *engine, int nameIndex)
{
Scope scope(engine);
ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[nameIndex]);
return engine->qmlSingletonWrapper(name);
}
-void Runtime::convertThisToObject(ExecutionEngine *engine)
+void Runtime::method_convertThisToObject(ExecutionEngine *engine)
{
Value *t = &engine->current->callData->thisObject;
if (t->isObject())
@@ -1515,8 +1541,291 @@ void Runtime::convertThisToObject(ExecutionEngine *engine)
}
}
+ReturnedValue Runtime::method_uPlus(const Value &value)
+{
+ TRACE1(value);
+
+ if (value.isNumber())
+ return value.asReturnedValue();
+ if (value.integerCompatible())
+ return Encode(value.int_32());
+
+ double n = value.toNumberImpl();
+ return Encode(n);
+}
+
+ReturnedValue Runtime::method_uMinus(const Value &value)
+{
+ TRACE1(value);
+
+ // +0 != -0, so we need to convert to double when negating 0
+ if (value.isInteger() && value.integerValue())
+ return Encode(-value.integerValue());
+ else {
+ double n = RuntimeHelpers::toNumber(value);
+ return Encode(-n);
+ }
+}
+
+ReturnedValue Runtime::method_complement(const Value &value)
+{
+ TRACE1(value);
+
+ int n = value.toInt32();
+ return Encode((int)~n);
+}
+
+ReturnedValue Runtime::method_uNot(const Value &value)
+{
+ TRACE1(value);
+
+ bool b = value.toBoolean();
+ return Encode(!b);
+}
+
+// binary operators
+ReturnedValue Runtime::method_bitOr(const Value &left, const Value &right)
+{
+ TRACE2(left, right);
+
+ int lval = left.toInt32();
+ int rval = right.toInt32();
+ return Encode(lval | rval);
+}
+
+ReturnedValue Runtime::method_bitXor(const Value &left, const Value &right)
+{
+ TRACE2(left, right);
+
+ int lval = left.toInt32();
+ int rval = right.toInt32();
+ return Encode(lval ^ rval);
+}
+
+ReturnedValue Runtime::method_bitAnd(const Value &left, const Value &right)
+{
+ TRACE2(left, right);
+
+ int lval = left.toInt32();
+ int rval = right.toInt32();
+ return Encode(lval & rval);
+}
+
+#ifndef V4_BOOTSTRAP
+ReturnedValue Runtime::method_add(ExecutionEngine *engine, const Value &left, const Value &right)
+{
+ TRACE2(left, right);
+
+ if (Q_LIKELY(left.isInteger() && right.isInteger()))
+ return add_int32(left.integerValue(), right.integerValue());
+ if (left.isNumber() && right.isNumber())
+ return Primitive::fromDouble(left.asDouble() + right.asDouble()).asReturnedValue();
+
+ return RuntimeHelpers::addHelper(engine, left, right);
+}
+#endif // V4_BOOTSTRAP
+
+ReturnedValue Runtime::method_sub(const Value &left, const Value &right)
+{
+ TRACE2(left, right);
+
+ if (Q_LIKELY(left.isInteger() && right.isInteger()))
+ return sub_int32(left.integerValue(), right.integerValue());
+
+ double lval = left.isNumber() ? left.asDouble() : left.toNumberImpl();
+ double rval = right.isNumber() ? right.asDouble() : right.toNumberImpl();
+
+ return Primitive::fromDouble(lval - rval).asReturnedValue();
+}
+
+ReturnedValue Runtime::method_mul(const Value &left, const Value &right)
+{
+ TRACE2(left, right);
+
+ if (Q_LIKELY(left.isInteger() && right.isInteger()))
+ return mul_int32(left.integerValue(), right.integerValue());
+
+ double lval = left.isNumber() ? left.asDouble() : left.toNumberImpl();
+ double rval = right.isNumber() ? right.asDouble() : right.toNumberImpl();
+
+ return Primitive::fromDouble(lval * rval).asReturnedValue();
+}
+
+ReturnedValue Runtime::method_div(const Value &left, const Value &right)
+{
+ TRACE2(left, right);
+
+ if (Value::integerCompatible(left, right)) {
+ int lval = left.integerValue();
+ int rval = right.integerValue();
+ if (rval != 0 && (lval % rval == 0))
+ return Encode(int(lval / rval));
+ else
+ return Encode(double(lval) / rval);
+ }
+
+ double lval = left.toNumber();
+ double rval = right.toNumber();
+ return Primitive::fromDouble(lval / rval).asReturnedValue();
+}
+
+ReturnedValue Runtime::method_mod(const Value &left, const Value &right)
+{
+ TRACE2(left, right);
+
+ if (Value::integerCompatible(left, right) && right.integerValue() != 0) {
+ int intRes = left.integerValue() % right.integerValue();
+ if (intRes != 0 || left.integerValue() >= 0)
+ return Encode(intRes);
+ }
+
+ double lval = RuntimeHelpers::toNumber(left);
+ double rval = RuntimeHelpers::toNumber(right);
+#ifdef fmod
+# undef fmod
+#endif
+ return Primitive::fromDouble(std::fmod(lval, rval)).asReturnedValue();
+}
+
+ReturnedValue Runtime::method_shl(const Value &left, const Value &right)
+{
+ TRACE2(left, right);
+
+ int lval = left.toInt32();
+ int rval = right.toInt32() & 0x1f;
+ return Encode((int)(lval << rval));
+}
+
+ReturnedValue Runtime::method_shr(const Value &left, const Value &right)
+{
+ TRACE2(left, right);
+
+ int lval = left.toInt32();
+ unsigned rval = right.toUInt32() & 0x1f;
+ return Encode((int)(lval >> rval));
+}
+
+ReturnedValue Runtime::method_ushr(const Value &left, const Value &right)
+{
+ TRACE2(left, right);
+
+ unsigned lval = left.toUInt32();
+ unsigned rval = right.toUInt32() & 0x1f;
+ uint res = lval >> rval;
+
+ return Encode(res);
+}
+
#endif // V4_BOOTSTRAP
+ReturnedValue Runtime::method_greaterThan(const Value &left, const Value &right)
+{
+ TRACE2(left, right);
+
+ bool r = method_compareGreaterThan(left, right);
+ return Encode(r);
+}
+
+ReturnedValue Runtime::method_lessThan(const Value &left, const Value &right)
+{
+ TRACE2(left, right);
+
+ bool r = method_compareLessThan(left, right);
+ return Encode(r);
+}
+
+ReturnedValue Runtime::method_greaterEqual(const Value &left, const Value &right)
+{
+ TRACE2(left, right);
+
+ bool r = method_compareGreaterEqual(left, right);
+ return Encode(r);
+}
+
+ReturnedValue Runtime::method_lessEqual(const Value &left, const Value &right)
+{
+ TRACE2(left, right);
+
+ bool r = method_compareLessEqual(left, right);
+ return Encode(r);
+}
+
+Bool Runtime::method_compareEqual(const Value &left, const Value &right)
+{
+ TRACE2(left, right);
+
+ if (left.rawValue() == right.rawValue())
+ // NaN != NaN
+ return !left.isNaN();
+
+ if (left.type() == right.type()) {
+ if (!left.isManaged())
+ return false;
+ if (left.isString() == right.isString())
+ return left.cast<Managed>()->isEqualTo(right.cast<Managed>());
+ }
+
+ return RuntimeHelpers::equalHelper(left, right);
+}
+
+ReturnedValue Runtime::method_equal(const Value &left, const Value &right)
+{
+ TRACE2(left, right);
+
+ bool r = method_compareEqual(left, right);
+ return Encode(r);
+}
+
+ReturnedValue Runtime::method_notEqual(const Value &left, const Value &right)
+{
+ TRACE2(left, right);
+
+ bool r = !method_compareEqual(left, right);
+ return Encode(r);
+}
+
+ReturnedValue Runtime::method_strictEqual(const Value &left, const Value &right)
+{
+ TRACE2(left, right);
+
+ bool r = RuntimeHelpers::strictEqual(left, right);
+ return Encode(r);
+}
+
+ReturnedValue Runtime::method_strictNotEqual(const Value &left, const Value &right)
+{
+ TRACE2(left, right);
+
+ bool r = ! RuntimeHelpers::strictEqual(left, right);
+ return Encode(r);
+}
+
+Bool Runtime::method_compareNotEqual(const Value &left, const Value &right)
+{
+ TRACE2(left, right);
+
+ return !Runtime::method_compareEqual(left, right);
+}
+
+Bool Runtime::method_compareStrictEqual(const Value &left, const Value &right)
+{
+ TRACE2(left, right);
+
+ return RuntimeHelpers::strictEqual(left, right);
+}
+
+Bool Runtime::method_compareStrictNotEqual(const Value &left, const Value &right)
+{
+ TRACE2(left, right);
+
+ return ! RuntimeHelpers::strictEqual(left, right);
+}
+
+Bool Runtime::method_toBoolean(const Value &value)
+{
+ return value.toBoolean();
+}
+
} // namespace QV4
QT_END_NAMESPACE
diff --git a/src/qml/jsruntime/qv4runtime_p.h b/src/qml/jsruntime/qv4runtime_p.h
index b63777e164..a32b3f1663 100644
--- a/src/qml/jsruntime/qv4runtime_p.h
+++ b/src/qml/jsruntime/qv4runtime_p.h
@@ -55,14 +55,11 @@
#include "qv4context_p.h"
#include "qv4engine_p.h"
#include "qv4math_p.h"
-
+#include "qv4runtimeapi_p.h"
#include <QtCore/qnumeric.h>
-
QT_BEGIN_NAMESPACE
-class QQmlAccessors;
-
#undef QV4_COUNT_RUNTIME_FUNCTIONS
namespace QV4 {
@@ -100,156 +97,6 @@ enum TypeHint {
STRING_HINT
};
-// This is a trick to tell the code generators that functions taking a NoThrowContext won't
-// throw exceptions and therefore don't need a check after the call.
-
-#ifndef V4_BOOTSTRAP
-struct NoThrowEngine : public ExecutionEngine
-{
-};
-#else
-struct NoThrowEngine;
-#endif
-
-struct Q_QML_PRIVATE_EXPORT Runtime {
- // call
- static ReturnedValue callGlobalLookup(ExecutionEngine *engine, uint index, CallData *callData);
- static ReturnedValue callActivationProperty(ExecutionEngine *engine, int nameIndex, CallData *callData);
- static ReturnedValue callQmlScopeObjectProperty(ExecutionEngine *engine, int propertyIndex, CallData *callData);
- static ReturnedValue callQmlContextObjectProperty(ExecutionEngine *engine, int propertyIndex, CallData *callData);
- static ReturnedValue callProperty(ExecutionEngine *engine, int nameIndex, CallData *callData);
- static ReturnedValue callPropertyLookup(ExecutionEngine *engine, uint index, CallData *callData);
- static ReturnedValue callElement(ExecutionEngine *engine, const Value &index, CallData *callData);
- static ReturnedValue callValue(ExecutionEngine *engine, const Value &func, CallData *callData);
-
- // construct
- static ReturnedValue constructGlobalLookup(ExecutionEngine *engine, uint index, CallData *callData);
- static ReturnedValue constructActivationProperty(ExecutionEngine *engine, int nameIndex, CallData *callData);
- static ReturnedValue constructProperty(ExecutionEngine *engine, int nameIndex, CallData *callData);
- static ReturnedValue constructPropertyLookup(ExecutionEngine *engine, uint index, CallData *callData);
- static ReturnedValue constructValue(ExecutionEngine *engine, const Value &func, CallData *callData);
-
- // set & get
- static void setActivationProperty(ExecutionEngine *engine, int nameIndex, const Value &value);
- static void setProperty(ExecutionEngine *engine, const Value &object, int nameIndex, const Value &value);
- static void setElement(ExecutionEngine *engine, const Value &object, const Value &index, const Value &value);
- static ReturnedValue getProperty(ExecutionEngine *engine, const Value &object, int nameIndex);
- static ReturnedValue getActivationProperty(ExecutionEngine *engine, int nameIndex);
- static ReturnedValue getElement(ExecutionEngine *engine, const Value &object, const Value &index);
-
- // typeof
- static ReturnedValue typeofValue(ExecutionEngine *engine, const Value &val);
- static ReturnedValue typeofName(ExecutionEngine *engine, int nameIndex);
- static ReturnedValue typeofScopeObjectProperty(ExecutionEngine *engine, const Value &context, int propertyIndex);
- static ReturnedValue typeofContextObjectProperty(ExecutionEngine *engine, const Value &context, int propertyIndex);
- static ReturnedValue typeofMember(ExecutionEngine *engine, const Value &base, int nameIndex);
- static ReturnedValue typeofElement(ExecutionEngine *engine, const Value &base, const Value &index);
-
- // delete
- static ReturnedValue deleteElement(ExecutionEngine *engine, const Value &base, const Value &index);
- static ReturnedValue deleteMember(ExecutionEngine *engine, const Value &base, int nameIndex);
- static ReturnedValue deleteMemberString(ExecutionEngine *engine, const Value &base, String *name);
- static ReturnedValue deleteName(ExecutionEngine *engine, int nameIndex);
-
- // exceptions & scopes
- static void throwException(ExecutionEngine *engine, const Value &value);
- static ReturnedValue unwindException(ExecutionEngine *engine);
- static void pushWithScope(const Value &o, ExecutionEngine *engine);
- static void pushCatchScope(NoThrowEngine *engine, int exceptionVarNameIndex);
- static void popScope(ExecutionEngine *engine);
-
- // closures
- static ReturnedValue closure(ExecutionEngine *engine, int functionId);
-
- // function header
- static void declareVar(ExecutionEngine *engine, bool deletable, int nameIndex);
- static ReturnedValue setupArgumentsObject(ExecutionEngine *engine);
- static void convertThisToObject(ExecutionEngine *engine);
-
- // literals
- static ReturnedValue arrayLiteral(ExecutionEngine *engine, Value *values, uint length);
- static ReturnedValue objectLiteral(ExecutionEngine *engine, const Value *args, int classId, int arrayValueCount, int arrayGetterSetterCountAndFlags);
- static ReturnedValue regexpLiteral(ExecutionEngine *engine, int id);
-
- // foreach
- static ReturnedValue foreachIterator(ExecutionEngine *engine, const Value &in);
- static ReturnedValue foreachNextPropertyName(const Value &foreach_iterator);
-
- // unary operators
- typedef ReturnedValue (*UnaryOperation)(const Value &value);
- static ReturnedValue uPlus(const Value &value);
- static ReturnedValue uMinus(const Value &value);
- static ReturnedValue uNot(const Value &value);
- static ReturnedValue complement(const Value &value);
- static ReturnedValue increment(const Value &value);
- static ReturnedValue decrement(const Value &value);
-
- // binary operators
- typedef ReturnedValue (*BinaryOperation)(const Value &left, const Value &right);
- typedef ReturnedValue (*BinaryOperationContext)(ExecutionEngine *engine, const Value &left, const Value &right);
-
- static ReturnedValue instanceof(ExecutionEngine *engine, const Value &left, const Value &right);
- static ReturnedValue in(ExecutionEngine *engine, const Value &left, const Value &right);
- static ReturnedValue add(ExecutionEngine *engine, const Value &left, const Value &right);
- static ReturnedValue addString(ExecutionEngine *engine, const Value &left, const Value &right);
- static ReturnedValue bitOr(const Value &left, const Value &right);
- static ReturnedValue bitXor(const Value &left, const Value &right);
- static ReturnedValue bitAnd(const Value &left, const Value &right);
- static ReturnedValue sub(const Value &left, const Value &right);
- static ReturnedValue mul(const Value &left, const Value &right);
- static ReturnedValue div(const Value &left, const Value &right);
- static ReturnedValue mod(const Value &left, const Value &right);
- static ReturnedValue shl(const Value &left, const Value &right);
- static ReturnedValue shr(const Value &left, const Value &right);
- static ReturnedValue ushr(const Value &left, const Value &right);
- static ReturnedValue greaterThan(const Value &left, const Value &right);
- static ReturnedValue lessThan(const Value &left, const Value &right);
- static ReturnedValue greaterEqual(const Value &left, const Value &right);
- static ReturnedValue lessEqual(const Value &left, const Value &right);
- static ReturnedValue equal(const Value &left, const Value &right);
- static ReturnedValue notEqual(const Value &left, const Value &right);
- static ReturnedValue strictEqual(const Value &left, const Value &right);
- static ReturnedValue strictNotEqual(const Value &left, const Value &right);
-
- // comparisons
- typedef Bool (*CompareOperation)(const Value &left, const Value &right);
- static Bool compareGreaterThan(const Value &l, const Value &r);
- static Bool compareLessThan(const Value &l, const Value &r);
- static Bool compareGreaterEqual(const Value &l, const Value &r);
- static Bool compareLessEqual(const Value &l, const Value &r);
- static Bool compareEqual(const Value &left, const Value &right);
- static Bool compareNotEqual(const Value &left, const Value &right);
- static Bool compareStrictEqual(const Value &left, const Value &right);
- static Bool compareStrictNotEqual(const Value &left, const Value &right);
-
- typedef Bool (*CompareOperationContext)(ExecutionEngine *engine, const Value &left, const Value &right);
- static Bool compareInstanceof(ExecutionEngine *engine, const Value &left, const Value &right);
- static Bool compareIn(ExecutionEngine *engine, const Value &left, const Value &right);
-
- // conversions
- static Bool toBoolean(const Value &value);
- static ReturnedValue toDouble(const Value &value);
- static int toInt(const Value &value);
- static int doubleToInt(const double &d);
- static unsigned toUInt(const Value &value);
- static unsigned doubleToUInt(const double &d);
-
- // qml
- static ReturnedValue getQmlContext(NoThrowEngine *engine);
- static ReturnedValue getQmlImportedScripts(NoThrowEngine *engine);
- static ReturnedValue getQmlSingleton(NoThrowEngine *engine, int nameIndex);
- static ReturnedValue getQmlAttachedProperty(ExecutionEngine *engine, int attachedPropertiesId, int propertyIndex);
- static ReturnedValue getQmlScopeObjectProperty(ExecutionEngine *engine, const Value &context, int propertyIndex);
- static ReturnedValue getQmlContextObjectProperty(ExecutionEngine *engine, const Value &context, int propertyIndex);
- static ReturnedValue getQmlQObjectProperty(ExecutionEngine *engine, const Value &object, int propertyIndex, bool captureRequired);
- static ReturnedValue getQmlSingletonQObjectProperty(ExecutionEngine *engine, const Value &object, int propertyIndex, bool captureRequired);
- static ReturnedValue getQmlIdObject(ExecutionEngine *engine, const Value &context, uint index);
-
- static void setQmlScopeObjectProperty(ExecutionEngine *engine, const Value &context, int propertyIndex, const Value &value);
- static void setQmlContextObjectProperty(ExecutionEngine *engine, const Value &context, int propertyIndex, const Value &value);
- static void setQmlQObjectProperty(ExecutionEngine *engine, const Value &object, int propertyIndex, const Value &value);
-};
-
struct Q_QML_PRIVATE_EXPORT RuntimeHelpers {
static ReturnedValue objectDefaultValue(const Object *object, int typeHint);
static ReturnedValue toPrimitive(const Value &value, int typeHint);
@@ -287,287 +134,6 @@ inline double RuntimeHelpers::toNumber(const Value &value)
{
return value.toNumber();
}
-
-inline ReturnedValue Runtime::uPlus(const Value &value)
-{
- TRACE1(value);
-
- if (value.isNumber())
- return value.asReturnedValue();
- if (value.integerCompatible())
- return Encode(value.int_32());
-
- double n = value.toNumberImpl();
- return Encode(n);
-}
-
-inline ReturnedValue Runtime::uMinus(const Value &value)
-{
- TRACE1(value);
-
- // +0 != -0, so we need to convert to double when negating 0
- if (value.isInteger() && value.integerValue())
- return Encode(-value.integerValue());
- else {
- double n = RuntimeHelpers::toNumber(value);
- return Encode(-n);
- }
-}
-
-inline ReturnedValue Runtime::complement(const Value &value)
-{
- TRACE1(value);
-
- int n = value.toInt32();
- return Encode((int)~n);
-}
-
-inline ReturnedValue Runtime::uNot(const Value &value)
-{
- TRACE1(value);
-
- bool b = value.toBoolean();
- return Encode(!b);
-}
-
-// binary operators
-inline ReturnedValue Runtime::bitOr(const Value &left, const Value &right)
-{
- TRACE2(left, right);
-
- int lval = left.toInt32();
- int rval = right.toInt32();
- return Encode(lval | rval);
-}
-
-inline ReturnedValue Runtime::bitXor(const Value &left, const Value &right)
-{
- TRACE2(left, right);
-
- int lval = left.toInt32();
- int rval = right.toInt32();
- return Encode(lval ^ rval);
-}
-
-inline ReturnedValue Runtime::bitAnd(const Value &left, const Value &right)
-{
- TRACE2(left, right);
-
- int lval = left.toInt32();
- int rval = right.toInt32();
- return Encode(lval & rval);
-}
-
-#ifndef V4_BOOTSTRAP
-inline ReturnedValue Runtime::add(ExecutionEngine *engine, const Value &left, const Value &right)
-{
- TRACE2(left, right);
-
- if (Q_LIKELY(left.isInteger() && right.isInteger()))
- return add_int32(left.integerValue(), right.integerValue());
- if (left.isNumber() && right.isNumber())
- return Primitive::fromDouble(left.asDouble() + right.asDouble()).asReturnedValue();
-
- return RuntimeHelpers::addHelper(engine, left, right);
-}
-#endif // V4_BOOTSTRAP
-
-inline ReturnedValue Runtime::sub(const Value &left, const Value &right)
-{
- TRACE2(left, right);
-
- if (Q_LIKELY(left.isInteger() && right.isInteger()))
- return sub_int32(left.integerValue(), right.integerValue());
-
- double lval = left.isNumber() ? left.asDouble() : left.toNumberImpl();
- double rval = right.isNumber() ? right.asDouble() : right.toNumberImpl();
-
- return Primitive::fromDouble(lval - rval).asReturnedValue();
-}
-
-inline ReturnedValue Runtime::mul(const Value &left, const Value &right)
-{
- TRACE2(left, right);
-
- if (Q_LIKELY(left.isInteger() && right.isInteger()))
- return mul_int32(left.integerValue(), right.integerValue());
-
- double lval = left.isNumber() ? left.asDouble() : left.toNumberImpl();
- double rval = right.isNumber() ? right.asDouble() : right.toNumberImpl();
-
- return Primitive::fromDouble(lval * rval).asReturnedValue();
-}
-
-inline ReturnedValue Runtime::div(const Value &left, const Value &right)
-{
- TRACE2(left, right);
-
- if (Value::integerCompatible(left, right)) {
- int lval = left.integerValue();
- int rval = right.integerValue();
- if (rval != 0 && (lval % rval == 0))
- return Encode(int(lval / rval));
- else
- return Encode(double(lval) / rval);
- }
-
- double lval = left.toNumber();
- double rval = right.toNumber();
- return Primitive::fromDouble(lval / rval).asReturnedValue();
-}
-
-inline ReturnedValue Runtime::mod(const Value &left, const Value &right)
-{
- TRACE2(left, right);
-
- if (Value::integerCompatible(left, right) && right.integerValue() != 0) {
- int intRes = left.integerValue() % right.integerValue();
- if (intRes != 0 || left.integerValue() >= 0)
- return Encode(intRes);
- }
-
- double lval = RuntimeHelpers::toNumber(left);
- double rval = RuntimeHelpers::toNumber(right);
- return Primitive::fromDouble(std::fmod(lval, rval)).asReturnedValue();
-}
-
-inline ReturnedValue Runtime::shl(const Value &left, const Value &right)
-{
- TRACE2(left, right);
-
- int lval = left.toInt32();
- int rval = right.toInt32() & 0x1f;
- return Encode((int)(lval << rval));
-}
-
-inline ReturnedValue Runtime::shr(const Value &left, const Value &right)
-{
- TRACE2(left, right);
-
- int lval = left.toInt32();
- unsigned rval = right.toUInt32() & 0x1f;
- return Encode((int)(lval >> rval));
-}
-
-inline ReturnedValue Runtime::ushr(const Value &left, const Value &right)
-{
- TRACE2(left, right);
-
- unsigned lval = left.toUInt32();
- unsigned rval = right.toUInt32() & 0x1f;
- uint res = lval >> rval;
-
- return Encode(res);
-}
-
-inline ReturnedValue Runtime::greaterThan(const Value &left, const Value &right)
-{
- TRACE2(left, right);
-
- bool r = Runtime::compareGreaterThan(left, right);
- return Encode(r);
-}
-
-inline ReturnedValue Runtime::lessThan(const Value &left, const Value &right)
-{
- TRACE2(left, right);
-
- bool r = Runtime::compareLessThan(left, right);
- return Encode(r);
-}
-
-inline ReturnedValue Runtime::greaterEqual(const Value &left, const Value &right)
-{
- TRACE2(left, right);
-
- bool r = Runtime::compareGreaterEqual(left, right);
- return Encode(r);
-}
-
-inline ReturnedValue Runtime::lessEqual(const Value &left, const Value &right)
-{
- TRACE2(left, right);
-
- bool r = Runtime::compareLessEqual(left, right);
- return Encode(r);
-}
-
-inline Bool Runtime::compareEqual(const Value &left, const Value &right)
-{
- TRACE2(left, right);
-
- if (left.rawValue() == right.rawValue())
- // NaN != NaN
- return !left.isNaN();
-
- if (left.type() == right.type()) {
- if (!left.isManaged())
- return false;
- if (left.isString() == right.isString())
- return left.cast<Managed>()->isEqualTo(right.cast<Managed>());
- }
-
- return RuntimeHelpers::equalHelper(left, right);
-}
-
-inline ReturnedValue Runtime::equal(const Value &left, const Value &right)
-{
- TRACE2(left, right);
-
- bool r = Runtime::compareEqual(left, right);
- return Encode(r);
-}
-
-inline ReturnedValue Runtime::notEqual(const Value &left, const Value &right)
-{
- TRACE2(left, right);
-
- bool r = !Runtime::compareEqual(left, right);
- return Encode(r);
-}
-
-inline ReturnedValue Runtime::strictEqual(const Value &left, const Value &right)
-{
- TRACE2(left, right);
-
- bool r = RuntimeHelpers::strictEqual(left, right);
- return Encode(r);
-}
-
-inline ReturnedValue Runtime::strictNotEqual(const Value &left, const Value &right)
-{
- TRACE2(left, right);
-
- bool r = ! RuntimeHelpers::strictEqual(left, right);
- return Encode(r);
-}
-
-inline Bool Runtime::compareNotEqual(const Value &left, const Value &right)
-{
- TRACE2(left, right);
-
- return !Runtime::compareEqual(left, right);
-}
-
-inline Bool Runtime::compareStrictEqual(const Value &left, const Value &right)
-{
- TRACE2(left, right);
-
- return RuntimeHelpers::strictEqual(left, right);
-}
-
-inline Bool Runtime::compareStrictNotEqual(const Value &left, const Value &right)
-{
- TRACE2(left, right);
-
- return ! RuntimeHelpers::strictEqual(left, right);
-}
-
-inline Bool Runtime::toBoolean(const Value &value)
-{
- return value.toBoolean();
-}
-
} // namespace QV4
QT_END_NAMESPACE
diff --git a/src/qml/jsruntime/qv4runtimeapi_p.h b/src/qml/jsruntime/qv4runtimeapi_p.h
new file mode 100644
index 0000000000..040a545b83
--- /dev/null
+++ b/src/qml/jsruntime/qv4runtimeapi_p.h
@@ -0,0 +1,348 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml 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 QV4RUNTIMEAPI_P_H
+#define QV4RUNTIMEAPI_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/qv4global_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+struct NoThrowEngine;
+
+namespace {
+template <typename T>
+struct ExceptionCheck {
+ enum { NeedsCheck = 1 };
+};
+// push_catch and pop context methods shouldn't check for exceptions
+template <>
+struct ExceptionCheck<void (*)(QV4::ExecutionEngine *)> {
+ enum { NeedsCheck = 0 };
+};
+template <typename A>
+struct ExceptionCheck<void (*)(A, QV4::NoThrowEngine)> {
+ enum { NeedsCheck = 0 };
+};
+template <>
+struct ExceptionCheck<QV4::ReturnedValue (*)(QV4::NoThrowEngine *)> {
+ enum { NeedsCheck = 0 };
+};
+template <typename A>
+struct ExceptionCheck<QV4::ReturnedValue (*)(QV4::NoThrowEngine *, A)> {
+ enum { NeedsCheck = 0 };
+};
+template <typename A, typename B>
+struct ExceptionCheck<QV4::ReturnedValue (*)(QV4::NoThrowEngine *, A, B)> {
+ enum { NeedsCheck = 0 };
+};
+template <typename A, typename B, typename C>
+struct ExceptionCheck<void (*)(QV4::NoThrowEngine *, A, B, C)> {
+ enum { NeedsCheck = 0 };
+};
+} // anonymous namespace
+
+#define RUNTIME_METHOD(returnvalue, name, args) \
+ typedef returnvalue (*Method_##name)args; \
+ enum { Method_##name##_NeedsExceptionCheck = ExceptionCheck<Method_##name>::NeedsCheck }; \
+ static returnvalue method_##name args; \
+ const Method_##name name
+
+#define INIT_RUNTIME_METHOD(name) \
+ name(method_##name)
+
+struct Q_QML_PRIVATE_EXPORT Runtime {
+ Runtime()
+ : INIT_RUNTIME_METHOD(callGlobalLookup)
+ , INIT_RUNTIME_METHOD(callActivationProperty)
+ , INIT_RUNTIME_METHOD(callQmlScopeObjectProperty)
+ , INIT_RUNTIME_METHOD(callQmlContextObjectProperty)
+ , INIT_RUNTIME_METHOD(callProperty)
+ , INIT_RUNTIME_METHOD(callPropertyLookup)
+ , INIT_RUNTIME_METHOD(callElement)
+ , INIT_RUNTIME_METHOD(callValue)
+ , INIT_RUNTIME_METHOD(constructGlobalLookup)
+ , INIT_RUNTIME_METHOD(constructActivationProperty)
+ , INIT_RUNTIME_METHOD(constructProperty)
+ , INIT_RUNTIME_METHOD(constructPropertyLookup)
+ , INIT_RUNTIME_METHOD(constructValue)
+ , INIT_RUNTIME_METHOD(setActivationProperty)
+ , INIT_RUNTIME_METHOD(setProperty)
+ , INIT_RUNTIME_METHOD(setElement)
+ , INIT_RUNTIME_METHOD(getProperty)
+ , INIT_RUNTIME_METHOD(getActivationProperty)
+ , INIT_RUNTIME_METHOD(getElement)
+ , INIT_RUNTIME_METHOD(typeofValue)
+ , INIT_RUNTIME_METHOD(typeofName)
+ , INIT_RUNTIME_METHOD(typeofScopeObjectProperty)
+ , INIT_RUNTIME_METHOD(typeofContextObjectProperty)
+ , INIT_RUNTIME_METHOD(typeofMember)
+ , INIT_RUNTIME_METHOD(typeofElement)
+ , INIT_RUNTIME_METHOD(deleteElement)
+ , INIT_RUNTIME_METHOD(deleteMember)
+ , INIT_RUNTIME_METHOD(deleteMemberString)
+ , INIT_RUNTIME_METHOD(deleteName)
+ , INIT_RUNTIME_METHOD(throwException)
+ , INIT_RUNTIME_METHOD(unwindException)
+ , INIT_RUNTIME_METHOD(pushWithScope)
+ , INIT_RUNTIME_METHOD(pushCatchScope)
+ , INIT_RUNTIME_METHOD(popScope)
+ , INIT_RUNTIME_METHOD(closure)
+ , INIT_RUNTIME_METHOD(declareVar)
+ , INIT_RUNTIME_METHOD(setupArgumentsObject)
+ , INIT_RUNTIME_METHOD(convertThisToObject)
+ , INIT_RUNTIME_METHOD(arrayLiteral)
+ , INIT_RUNTIME_METHOD(objectLiteral)
+ , INIT_RUNTIME_METHOD(regexpLiteral)
+ , INIT_RUNTIME_METHOD(foreachIterator)
+ , INIT_RUNTIME_METHOD(foreachNextPropertyName)
+ , INIT_RUNTIME_METHOD(uPlus)
+ , INIT_RUNTIME_METHOD(uMinus)
+ , INIT_RUNTIME_METHOD(uNot)
+ , INIT_RUNTIME_METHOD(complement)
+ , INIT_RUNTIME_METHOD(increment)
+ , INIT_RUNTIME_METHOD(decrement)
+ , INIT_RUNTIME_METHOD(instanceof)
+ , INIT_RUNTIME_METHOD(in)
+ , INIT_RUNTIME_METHOD(add)
+ , INIT_RUNTIME_METHOD(addString)
+ , INIT_RUNTIME_METHOD(bitOr)
+ , INIT_RUNTIME_METHOD(bitXor)
+ , INIT_RUNTIME_METHOD(bitAnd)
+ , INIT_RUNTIME_METHOD(sub)
+ , INIT_RUNTIME_METHOD(mul)
+ , INIT_RUNTIME_METHOD(div)
+ , INIT_RUNTIME_METHOD(mod)
+ , INIT_RUNTIME_METHOD(shl)
+ , INIT_RUNTIME_METHOD(shr)
+ , INIT_RUNTIME_METHOD(ushr)
+ , INIT_RUNTIME_METHOD(greaterThan)
+ , INIT_RUNTIME_METHOD(lessThan)
+ , INIT_RUNTIME_METHOD(greaterEqual)
+ , INIT_RUNTIME_METHOD(lessEqual)
+ , INIT_RUNTIME_METHOD(equal)
+ , INIT_RUNTIME_METHOD(notEqual)
+ , INIT_RUNTIME_METHOD(strictEqual)
+ , INIT_RUNTIME_METHOD(strictNotEqual)
+ , INIT_RUNTIME_METHOD(compareGreaterThan)
+ , INIT_RUNTIME_METHOD(compareLessThan)
+ , INIT_RUNTIME_METHOD(compareGreaterEqual)
+ , INIT_RUNTIME_METHOD(compareLessEqual)
+ , INIT_RUNTIME_METHOD(compareEqual)
+ , INIT_RUNTIME_METHOD(compareNotEqual)
+ , INIT_RUNTIME_METHOD(compareStrictEqual)
+ , INIT_RUNTIME_METHOD(compareStrictNotEqual)
+ , INIT_RUNTIME_METHOD(compareInstanceof)
+ , INIT_RUNTIME_METHOD(compareIn)
+ , INIT_RUNTIME_METHOD(toBoolean)
+ , INIT_RUNTIME_METHOD(toDouble)
+ , INIT_RUNTIME_METHOD(toInt)
+ , INIT_RUNTIME_METHOD(doubleToInt)
+ , INIT_RUNTIME_METHOD(toUInt)
+ , INIT_RUNTIME_METHOD(doubleToUInt)
+ , INIT_RUNTIME_METHOD(getQmlContext)
+ , INIT_RUNTIME_METHOD(getQmlImportedScripts)
+ , INIT_RUNTIME_METHOD(getQmlSingleton)
+ , INIT_RUNTIME_METHOD(getQmlAttachedProperty)
+ , INIT_RUNTIME_METHOD(getQmlScopeObjectProperty)
+ , INIT_RUNTIME_METHOD(getQmlContextObjectProperty)
+ , INIT_RUNTIME_METHOD(getQmlQObjectProperty)
+ , INIT_RUNTIME_METHOD(getQmlSingletonQObjectProperty)
+ , INIT_RUNTIME_METHOD(getQmlIdObject)
+ , INIT_RUNTIME_METHOD(setQmlScopeObjectProperty)
+ , INIT_RUNTIME_METHOD(setQmlContextObjectProperty)
+ , INIT_RUNTIME_METHOD(setQmlQObjectProperty)
+ { }
+
+ // call
+ RUNTIME_METHOD(ReturnedValue, callGlobalLookup, (ExecutionEngine *engine, uint index, CallData *callData));
+ RUNTIME_METHOD(ReturnedValue, callActivationProperty, (ExecutionEngine *engine, int nameIndex, CallData *callData));
+ RUNTIME_METHOD(ReturnedValue, callQmlScopeObjectProperty, (ExecutionEngine *engine, int propertyIndex, CallData *callData));
+ RUNTIME_METHOD(ReturnedValue, callQmlContextObjectProperty, (ExecutionEngine *engine, int propertyIndex, CallData *callData));
+ RUNTIME_METHOD(ReturnedValue, callProperty, (ExecutionEngine *engine, int nameIndex, CallData *callData));
+ RUNTIME_METHOD(ReturnedValue, callPropertyLookup, (ExecutionEngine *engine, uint index, CallData *callData));
+ RUNTIME_METHOD(ReturnedValue, callElement, (ExecutionEngine *engine, const Value &index, CallData *callData));
+ RUNTIME_METHOD(ReturnedValue, callValue, (ExecutionEngine *engine, const Value &func, CallData *callData));
+
+ // construct
+ RUNTIME_METHOD(ReturnedValue, constructGlobalLookup, (ExecutionEngine *engine, uint index, CallData *callData));
+ RUNTIME_METHOD(ReturnedValue, constructActivationProperty, (ExecutionEngine *engine, int nameIndex, CallData *callData));
+ RUNTIME_METHOD(ReturnedValue, constructProperty, (ExecutionEngine *engine, int nameIndex, CallData *callData));
+ RUNTIME_METHOD(ReturnedValue, constructPropertyLookup, (ExecutionEngine *engine, uint index, CallData *callData));
+ RUNTIME_METHOD(ReturnedValue, constructValue, (ExecutionEngine *engine, const Value &func, CallData *callData));
+
+ // set & get
+ RUNTIME_METHOD(void, setActivationProperty, (ExecutionEngine *engine, int nameIndex, const Value &value));
+ RUNTIME_METHOD(void, setProperty, (ExecutionEngine *engine, const Value &object, int nameIndex, const Value &value));
+ RUNTIME_METHOD(void, setElement, (ExecutionEngine *engine, const Value &object, const Value &index, const Value &value));
+ RUNTIME_METHOD(ReturnedValue, getProperty, (ExecutionEngine *engine, const Value &object, int nameIndex));
+ RUNTIME_METHOD(ReturnedValue, getActivationProperty, (ExecutionEngine *engine, int nameIndex));
+ RUNTIME_METHOD(ReturnedValue, getElement, (ExecutionEngine *engine, const Value &object, const Value &index));
+
+ // typeof
+ RUNTIME_METHOD(ReturnedValue, typeofValue, (ExecutionEngine *engine, const Value &val));
+ RUNTIME_METHOD(ReturnedValue, typeofName, (ExecutionEngine *engine, int nameIndex));
+ RUNTIME_METHOD(ReturnedValue, typeofScopeObjectProperty, (ExecutionEngine *engine, const Value &context, int propertyIndex));
+ RUNTIME_METHOD(ReturnedValue, typeofContextObjectProperty, (ExecutionEngine *engine, const Value &context, int propertyIndex));
+ RUNTIME_METHOD(ReturnedValue, typeofMember, (ExecutionEngine *engine, const Value &base, int nameIndex));
+ RUNTIME_METHOD(ReturnedValue, typeofElement, (ExecutionEngine *engine, const Value &base, const Value &index));
+
+ // delete
+ RUNTIME_METHOD(ReturnedValue, deleteElement, (ExecutionEngine *engine, const Value &base, const Value &index));
+ RUNTIME_METHOD(ReturnedValue, deleteMember, (ExecutionEngine *engine, const Value &base, int nameIndex));
+ RUNTIME_METHOD(ReturnedValue, deleteMemberString, (ExecutionEngine *engine, const Value &base, String *name));
+ RUNTIME_METHOD(ReturnedValue, deleteName, (ExecutionEngine *engine, int nameIndex));
+
+ // exceptions & scopes
+ RUNTIME_METHOD(void, throwException, (ExecutionEngine *engine, const Value &value));
+ RUNTIME_METHOD(ReturnedValue, unwindException, (ExecutionEngine *engine));
+ RUNTIME_METHOD(void, pushWithScope, (const Value &o, ExecutionEngine *engine));
+ RUNTIME_METHOD(void, pushCatchScope, (NoThrowEngine *engine, int exceptionVarNameIndex));
+ RUNTIME_METHOD(void, popScope, (ExecutionEngine *engine));
+
+ // closures
+ RUNTIME_METHOD(ReturnedValue, closure, (ExecutionEngine *engine, int functionId));
+
+ // function header
+ RUNTIME_METHOD(void, declareVar, (ExecutionEngine *engine, bool deletable, int nameIndex));
+ RUNTIME_METHOD(ReturnedValue, setupArgumentsObject, (ExecutionEngine *engine));
+ RUNTIME_METHOD(void, convertThisToObject, (ExecutionEngine *engine));
+
+ // literals
+ RUNTIME_METHOD(ReturnedValue, arrayLiteral, (ExecutionEngine *engine, Value *values, uint length));
+ RUNTIME_METHOD(ReturnedValue, objectLiteral, (ExecutionEngine *engine, const Value *args, int classId, int arrayValueCount, int arrayGetterSetterCountAndFlags));
+ RUNTIME_METHOD(ReturnedValue, regexpLiteral, (ExecutionEngine *engine, int id));
+
+ // foreach
+ RUNTIME_METHOD(ReturnedValue, foreachIterator, (ExecutionEngine *engine, const Value &in));
+ RUNTIME_METHOD(ReturnedValue, foreachNextPropertyName, (const Value &foreach_iterator));
+
+ // unary operators
+ typedef ReturnedValue (*UnaryOperation)(const Value &value);
+ RUNTIME_METHOD(ReturnedValue, uPlus, (const Value &value));
+ RUNTIME_METHOD(ReturnedValue, uMinus, (const Value &value));
+ RUNTIME_METHOD(ReturnedValue, uNot, (const Value &value));
+ RUNTIME_METHOD(ReturnedValue, complement, (const Value &value));
+ RUNTIME_METHOD(ReturnedValue, increment, (const Value &value));
+ RUNTIME_METHOD(ReturnedValue, decrement, (const Value &value));
+
+ // binary operators
+ typedef ReturnedValue (*BinaryOperation)(const Value &left, const Value &right);
+ typedef ReturnedValue (*BinaryOperationContext)(ExecutionEngine *engine, const Value &left, const Value &right);
+
+ RUNTIME_METHOD(ReturnedValue, instanceof, (ExecutionEngine *engine, const Value &left, const Value &right));
+ RUNTIME_METHOD(ReturnedValue, in, (ExecutionEngine *engine, const Value &left, const Value &right));
+ RUNTIME_METHOD(ReturnedValue, add, (ExecutionEngine *engine, const Value &left, const Value &right));
+ RUNTIME_METHOD(ReturnedValue, addString, (ExecutionEngine *engine, const Value &left, const Value &right));
+ RUNTIME_METHOD(ReturnedValue, bitOr, (const Value &left, const Value &right));
+ RUNTIME_METHOD(ReturnedValue, bitXor, (const Value &left, const Value &right));
+ RUNTIME_METHOD(ReturnedValue, bitAnd, (const Value &left, const Value &right));
+ RUNTIME_METHOD(ReturnedValue, sub, (const Value &left, const Value &right));
+ RUNTIME_METHOD(ReturnedValue, mul, (const Value &left, const Value &right));
+ RUNTIME_METHOD(ReturnedValue, div, (const Value &left, const Value &right));
+ RUNTIME_METHOD(ReturnedValue, mod, (const Value &left, const Value &right));
+ RUNTIME_METHOD(ReturnedValue, shl, (const Value &left, const Value &right));
+ RUNTIME_METHOD(ReturnedValue, shr, (const Value &left, const Value &right));
+ RUNTIME_METHOD(ReturnedValue, ushr, (const Value &left, const Value &right));
+ RUNTIME_METHOD(ReturnedValue, greaterThan, (const Value &left, const Value &right));
+ RUNTIME_METHOD(ReturnedValue, lessThan, (const Value &left, const Value &right));
+ RUNTIME_METHOD(ReturnedValue, greaterEqual, (const Value &left, const Value &right));
+ RUNTIME_METHOD(ReturnedValue, lessEqual, (const Value &left, const Value &right));
+ RUNTIME_METHOD(ReturnedValue, equal, (const Value &left, const Value &right));
+ RUNTIME_METHOD(ReturnedValue, notEqual, (const Value &left, const Value &right));
+ RUNTIME_METHOD(ReturnedValue, strictEqual, (const Value &left, const Value &right));
+ RUNTIME_METHOD(ReturnedValue, strictNotEqual, (const Value &left, const Value &right));
+
+ // comparisons
+ RUNTIME_METHOD(Bool, compareGreaterThan, (const Value &l, const Value &r));
+ RUNTIME_METHOD(Bool, compareLessThan, (const Value &l, const Value &r));
+ RUNTIME_METHOD(Bool, compareGreaterEqual, (const Value &l, const Value &r));
+ RUNTIME_METHOD(Bool, compareLessEqual, (const Value &l, const Value &r));
+ RUNTIME_METHOD(Bool, compareEqual, (const Value &left, const Value &right));
+ RUNTIME_METHOD(Bool, compareNotEqual, (const Value &left, const Value &right));
+ RUNTIME_METHOD(Bool, compareStrictEqual, (const Value &left, const Value &right));
+ RUNTIME_METHOD(Bool, compareStrictNotEqual, (const Value &left, const Value &right));
+
+ RUNTIME_METHOD(Bool, compareInstanceof, (ExecutionEngine *engine, const Value &left, const Value &right));
+ RUNTIME_METHOD(Bool, compareIn, (ExecutionEngine *engine, const Value &left, const Value &right));
+
+ // conversions
+ RUNTIME_METHOD(Bool, toBoolean, (const Value &value));
+ RUNTIME_METHOD(ReturnedValue, toDouble, (const Value &value));
+ RUNTIME_METHOD(int, toInt, (const Value &value));
+ RUNTIME_METHOD(int, doubleToInt, (const double &d));
+ RUNTIME_METHOD(unsigned, toUInt, (const Value &value));
+ RUNTIME_METHOD(unsigned, doubleToUInt, (const double &d));
+
+ // qml
+ RUNTIME_METHOD(ReturnedValue, getQmlContext, (NoThrowEngine *engine));
+ RUNTIME_METHOD(ReturnedValue, getQmlImportedScripts, (NoThrowEngine *engine));
+ RUNTIME_METHOD(ReturnedValue, getQmlSingleton, (NoThrowEngine *engine, int nameIndex));
+ RUNTIME_METHOD(ReturnedValue, getQmlAttachedProperty, (ExecutionEngine *engine, int attachedPropertiesId, int propertyIndex));
+ RUNTIME_METHOD(ReturnedValue, getQmlScopeObjectProperty, (ExecutionEngine *engine, const Value &context, int propertyIndex, bool captureRequired));
+ RUNTIME_METHOD(ReturnedValue, getQmlContextObjectProperty, (ExecutionEngine *engine, const Value &context, int propertyIndex, bool captureRequired));
+ RUNTIME_METHOD(ReturnedValue, getQmlQObjectProperty, (ExecutionEngine *engine, const Value &object, int propertyIndex, bool captureRequired));
+ RUNTIME_METHOD(ReturnedValue, getQmlSingletonQObjectProperty, (ExecutionEngine *engine, const Value &object, int propertyIndex, bool captureRequired));
+ RUNTIME_METHOD(ReturnedValue, getQmlIdObject, (ExecutionEngine *engine, const Value &context, uint index));
+
+ RUNTIME_METHOD(void, setQmlScopeObjectProperty, (ExecutionEngine *engine, const Value &context, int propertyIndex, const Value &value));
+ RUNTIME_METHOD(void, setQmlContextObjectProperty, (ExecutionEngine *engine, const Value &context, int propertyIndex, const Value &value));
+ RUNTIME_METHOD(void, setQmlQObjectProperty, (ExecutionEngine *engine, const Value &object, int propertyIndex, const Value &value));
+};
+
+#undef RUNTIME_METHOD
+#undef INIT_RUNTIME_METHOD
+
+} // namespace QV4
+
+QT_END_NAMESPACE
+
+#endif // QV4RUNTIMEAPI_P_H
diff --git a/src/qml/jsruntime/qv4scopedvalue_p.h b/src/qml/jsruntime/qv4scopedvalue_p.h
index 5bc17f741b..5022d7c3bc 100644
--- a/src/qml/jsruntime/qv4scopedvalue_p.h
+++ b/src/qml/jsruntime/qv4scopedvalue_p.h
@@ -71,14 +71,18 @@ struct ScopedValue;
struct Scope {
inline Scope(ExecutionContext *ctx)
: engine(ctx->d()->engine)
+ , mark(engine->jsStackTop)
+ , result(*engine->jsAlloca(1))
{
- mark = engine->jsStackTop;
+ result = Encode::undefined();
}
explicit Scope(ExecutionEngine *e)
: engine(e)
+ , mark(engine->jsStackTop)
+ , result(*engine->jsAlloca(1))
{
- mark = engine->jsStackTop;
+ result = Encode::undefined();
}
~Scope() {
@@ -93,7 +97,7 @@ struct Scope {
engine->jsStackTop = mark;
}
- Value *alloc(int nValues) {
+ Value *alloc(int nValues) const {
return engine->jsAlloca(nValues);
}
@@ -103,6 +107,7 @@ struct Scope {
ExecutionEngine *engine;
Value *mark;
+ Value &result;
private:
Q_DISABLE_COPY(Scope)
@@ -190,59 +195,59 @@ struct Scoped
Scoped(const Scope &scope)
{
- ptr = scope.engine->jsStackTop++;
- ptr->setM(0);
+ ptr = scope.engine->jsAlloca(1);
}
Scoped(const Scope &scope, const Value &v)
{
- ptr = scope.engine->jsStackTop++;
+ ptr = scope.engine->jsAlloca(1);
setPointer(v.as<T>());
}
Scoped(const Scope &scope, Heap::Base *o)
{
Value v;
v = o;
- ptr = scope.engine->jsStackTop++;
+ ptr = scope.engine->jsAlloca(1);
setPointer(v.as<T>());
}
Scoped(const Scope &scope, const ScopedValue &v)
{
- ptr = scope.engine->jsStackTop++;
+ ptr = scope.engine->jsAlloca(1);
setPointer(v.ptr->as<T>());
}
Scoped(const Scope &scope, const Value &v, ConvertType)
{
- ptr = scope.engine->jsStackTop++;
+ ptr = scope.engine->jsAlloca(1);
ptr->setRawValue(value_convert<T>(scope.engine, v));
}
Scoped(const Scope &scope, const Value *v)
{
- ptr = scope.engine->jsStackTop++;
+ ptr = scope.engine->jsAlloca(1);
setPointer(v ? v->as<T>() : 0);
}
Scoped(const Scope &scope, T *t)
{
- ptr = scope.engine->jsStackTop++;
+ ptr = scope.engine->jsAlloca(1);
setPointer(t);
}
Scoped(const Scope &scope, typename T::Data *t)
{
- ptr = scope.engine->jsStackTop++;
+ ptr = scope.engine->jsAlloca(1);
*ptr = t;
}
Scoped(const Scope &scope, const ReturnedValue &v)
{
- ptr = scope.engine->jsStackTop++;
+ ptr = scope.engine->jsAlloca(1);
setPointer(QV4::Value::fromReturnedValue(v).as<T>());
}
+
Scoped(const Scope &scope, const ReturnedValue &v, ConvertType)
{
- ptr = scope.engine->jsStackTop++;
+ ptr = scope.engine->jsAlloca(1);
ptr->setRawValue(value_convert<T>(scope.engine, QV4::Value::fromReturnedValue(v)));
}
@@ -289,6 +294,10 @@ struct Scoped
return ptr->cast<T>();
}
+ const T *operator->() const {
+ return ptr->cast<T>();
+ }
+
bool operator!() const {
return !ptr->m();
}
@@ -311,7 +320,7 @@ struct Scoped
};
struct ScopedCallData {
- ScopedCallData(Scope &scope, int argc = 0)
+ ScopedCallData(const Scope &scope, int argc = 0)
{
int size = qMax(argc, (int)QV4::Global::ReservedArgumentCount) + qOffsetOf(QV4::CallData, args)/sizeof(QV4::Value);
ptr = reinterpret_cast<CallData *>(scope.alloc(size));
@@ -362,19 +371,19 @@ struct ScopedProperty
struct ExecutionContextSaver
{
- ExecutionEngine *engine;
+ Scope scope; // this makes sure that a reference to context on the JS stack goes out of scope as soon as the context is not used anymore.
ExecutionContext *savedContext;
- ExecutionContextSaver(Scope &scope)
- : engine(scope.engine)
+ ExecutionContextSaver(const Scope &scope)
+ : scope(scope.engine)
{
- savedContext = engine->currentContext;
+ savedContext = scope.engine->currentContext;
}
~ExecutionContextSaver()
{
- Q_ASSERT(engine->jsStackTop > engine->currentContext);
- engine->currentContext = savedContext;
- engine->current = savedContext->d();
+ Q_ASSERT(scope.engine->jsStackTop > scope.engine->currentContext);
+ scope.engine->currentContext = savedContext;
+ scope.engine->current = savedContext->d();
}
};
diff --git a/src/qml/jsruntime/qv4script.cpp b/src/qml/jsruntime/qv4script.cpp
index 4b847600b4..787047806a 100644
--- a/src/qml/jsruntime/qv4script.cpp
+++ b/src/qml/jsruntime/qv4script.cpp
@@ -64,34 +64,16 @@ QT_BEGIN_NAMESPACE
namespace QV4 {
namespace Heap {
-struct CompilationUnitHolder : Object {
- inline CompilationUnitHolder(CompiledData::CompilationUnit *unit);
-
- QQmlRefPointer<CompiledData::CompilationUnit> unit;
-};
-
struct QmlBindingWrapper : FunctionObject {
- QmlBindingWrapper(QV4::QmlContext *scope, Function *f);
+ void init(QV4::QmlContext *scope, Function *f);
};
}
-struct CompilationUnitHolder : public Object
-{
- V4_OBJECT2(CompilationUnitHolder, Object)
- V4_NEEDS_DESTROY
-};
-
-inline
-Heap::CompilationUnitHolder::CompilationUnitHolder(CompiledData::CompilationUnit *unit)
- : unit(unit)
-{
-}
-
struct QmlBindingWrapper : FunctionObject {
V4_OBJECT2(QmlBindingWrapper, FunctionObject)
- static ReturnedValue call(const Managed *that, CallData *callData);
+ static void call(const Managed *that, Scope &scope, CallData *callData);
};
}
@@ -101,11 +83,11 @@ QT_END_NAMESPACE
using namespace QV4;
DEFINE_OBJECT_VTABLE(QmlBindingWrapper);
-DEFINE_OBJECT_VTABLE(CompilationUnitHolder);
-Heap::QmlBindingWrapper::QmlBindingWrapper(QV4::QmlContext *scope, Function *f)
- : Heap::FunctionObject(scope, scope->d()->engine->id_eval(), /*createProto = */ false)
+void Heap::QmlBindingWrapper::init(QV4::QmlContext *scope, Function *f)
{
+ Heap::FunctionObject::init(scope, scope->d()->engine->id_eval(), /*createProto = */ false);
+
Q_ASSERT(scope->inUse());
function = f;
@@ -113,32 +95,33 @@ Heap::QmlBindingWrapper::QmlBindingWrapper(QV4::QmlContext *scope, Function *f)
function->compilationUnit->addref();
}
-ReturnedValue QmlBindingWrapper::call(const Managed *that, CallData *callData)
+void QmlBindingWrapper::call(const Managed *that, Scope &scope, CallData *callData)
{
const QmlBindingWrapper *This = static_cast<const QmlBindingWrapper *>(that);
ExecutionEngine *v4 = static_cast<const Object *>(that)->engine();
- if (v4->hasException)
- return Encode::undefined();
- CHECK_STACK_LIMITS(v4);
+ if (v4->hasException) {
+ scope.result = Encode::undefined();
+ return;
+ }
+ CHECK_STACK_LIMITS(v4, scope);
- Scope scope(v4);
ExecutionContextSaver ctxSaver(scope);
QV4::Function *f = This->function();
- if (!f)
- return QV4::Encode::undefined();
+ if (!f) {
+ scope.result = QV4::Encode::undefined();
+ return;
+ }
Scoped<CallContext> ctx(scope, v4->currentContext->newCallContext(This, callData));
v4->pushContext(ctx);
- ScopedValue result(scope, Q_V4_PROFILE(v4, f));
-
- return result->asReturnedValue();
+ scope.result = Q_V4_PROFILE(v4, f);
}
Script::Script(ExecutionEngine *v4, QmlContext *qml, CompiledData::CompilationUnit *compilationUnit)
: line(0), column(0), scope(v4->rootContext()), strictMode(false), inheritContext(true), parsed(false)
- , vmFunction(0), parseAsBinding(true)
+ , compilationUnit(compilationUnit), vmFunction(0), parseAsBinding(true)
{
if (qml)
qmlContext.set(v4, *qml);
@@ -146,11 +129,6 @@ Script::Script(ExecutionEngine *v4, QmlContext *qml, CompiledData::CompilationUn
parsed = true;
vmFunction = compilationUnit ? compilationUnit->linkToEngine(v4) : 0;
- if (vmFunction) {
- Scope valueScope(v4);
- ScopedObject holder(valueScope, v4->memoryManager->allocObject<CompilationUnitHolder>(compilationUnit));
- compilationUnitHolder.set(v4, holder);
- }
}
Script::~Script()
@@ -171,7 +149,7 @@ void Script::parse()
MemoryManager::GCBlocker gcBlocker(v4->memoryManager);
- IR::Module module(v4->debugger != 0);
+ IR::Module module(v4->debugger() != 0);
QQmlJS::Engine ee, *engine = &ee;
Lexer lexer(engine);
@@ -217,10 +195,8 @@ void Script::parse()
QScopedPointer<EvalInstructionSelection> isel(v4->iselFactory->create(QQmlEnginePrivate::get(v4), v4->executableAllocator, &module, &jsGenerator));
if (inheritContext)
isel->setUseFastLookups(false);
- QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit = isel->compile();
+ compilationUnit = isel->compile();
vmFunction = compilationUnit->linkToEngine(v4);
- ScopedObject holder(valueScope, v4->memoryManager->allocObject<CompilationUnitHolder>(compilationUnit));
- compilationUnitHolder.set(v4, holder);
}
if (!vmFunction) {
@@ -247,6 +223,7 @@ ReturnedValue Script::run()
ContextStateSaver stateSaver(valueScope, scope);
scope->d()->strictMode = vmFunction->isStrict();
scope->d()->lookups = vmFunction->compilationUnit->runtimeLookups;
+ scope->d()->constantTable = vmFunction->compilationUnit->constants;
scope->d()->compilationUnit = vmFunction->compilationUnit;
return Q_V4_PROFILE(engine, vmFunction);
@@ -255,7 +232,8 @@ ReturnedValue Script::run()
ScopedFunctionObject f(valueScope, engine->memoryManager->allocObject<QmlBindingWrapper>(qml, vmFunction));
ScopedCallData callData(valueScope);
callData->thisObject = Primitive::undefinedValue();
- return f->call(callData);
+ f->call(valueScope, callData);
+ return valueScope.result.asReturnedValue();
}
}
diff --git a/src/qml/jsruntime/qv4script_p.h b/src/qml/jsruntime/qv4script_p.h
index d7b82218e7..2e87a7692b 100644
--- a/src/qml/jsruntime/qv4script_p.h
+++ b/src/qml/jsruntime/qv4script_p.h
@@ -71,22 +71,25 @@ struct ContextStateSaver {
Value *savedContext;
bool strictMode;
Lookup *lookups;
+ const QV4::Value *constantTable;
CompiledData::CompilationUnit *compilationUnit;
int lineNumber;
- ContextStateSaver(Scope &scope, ExecutionContext *context)
+ ContextStateSaver(const Scope &scope, ExecutionContext *context)
: savedContext(scope.alloc(1))
, strictMode(context->d()->strictMode)
, lookups(context->d()->lookups)
+ , constantTable(context->d()->constantTable)
, compilationUnit(context->d()->compilationUnit)
, lineNumber(context->d()->lineNumber)
{
savedContext->setM(context->d());
}
- ContextStateSaver(Scope &scope, Heap::ExecutionContext *context)
+ ContextStateSaver(const Scope &scope, Heap::ExecutionContext *context)
: savedContext(scope.alloc(1))
, strictMode(context->strictMode)
, lookups(context->lookups)
+ , constantTable(context->constantTable)
, compilationUnit(context->compilationUnit)
, lineNumber(context->lineNumber)
{
@@ -98,6 +101,7 @@ struct ContextStateSaver {
Heap::ExecutionContext *ctx = static_cast<Heap::ExecutionContext *>(savedContext->m());
ctx->strictMode = strictMode;
ctx->lookups = lookups;
+ ctx->constantTable = constantTable;
ctx->compilationUnit = compilationUnit;
ctx->lineNumber = lineNumber;
}
@@ -126,7 +130,7 @@ struct Q_QML_EXPORT Script {
bool inheritContext;
bool parsed;
QV4::PersistentValue qmlContext;
- QV4::PersistentValue compilationUnitHolder;
+ QQmlRefPointer<CompiledData::CompilationUnit> compilationUnit;
Function *vmFunction;
bool parseAsBinding;
diff --git a/src/qml/jsruntime/qv4sequenceobject.cpp b/src/qml/jsruntime/qv4sequenceobject.cpp
index 0f2acd6bbb..58da7b9f68 100644
--- a/src/qml/jsruntime/qv4sequenceobject.cpp
+++ b/src/qml/jsruntime/qv4sequenceobject.cpp
@@ -216,11 +216,16 @@ namespace Heap {
template <typename Container>
struct QQmlSequence : Object {
- QQmlSequence(const Container &container);
- QQmlSequence(QObject *object, int propertyIndex);
+ void init(const Container &container);
+ void init(QObject *object, int propertyIndex);
+ void destroy() {
+ delete container;
+ object.destroy();
+ Object::destroy();
+ }
- mutable Container container;
- QPointer<QObject> object;
+ mutable Container *container;
+ QQmlQPointer<QObject> object;
int propertyIndex;
bool isReference;
};
@@ -259,10 +264,10 @@ public:
loadReference();
}
qint32 signedIdx = static_cast<qint32>(index);
- if (signedIdx < d()->container.count()) {
+ if (signedIdx < d()->container->count()) {
if (hasProperty)
*hasProperty = true;
- return convertElementToValue(engine(), d()->container.at(signedIdx));
+ return convertElementToValue(engine(), d()->container->at(signedIdx));
}
if (hasProperty)
*hasProperty = false;
@@ -288,22 +293,22 @@ public:
qint32 signedIdx = static_cast<qint32>(index);
- int count = d()->container.count();
+ int count = d()->container->count();
typename Container::value_type element = convertValueToElement<typename Container::value_type>(value);
if (signedIdx == count) {
- d()->container.append(element);
+ d()->container->append(element);
} else if (signedIdx < count) {
- d()->container[signedIdx] = element;
+ (*d()->container)[signedIdx] = element;
} else {
/* according to ECMA262r3 we need to insert */
/* the value at the given index, increasing length to index+1. */
- d()->container.reserve(signedIdx + 1);
+ d()->container->reserve(signedIdx + 1);
while (signedIdx > count++) {
- d()->container.append(typename Container::value_type());
+ d()->container->append(typename Container::value_type());
}
- d()->container.append(element);
+ d()->container->append(element);
}
if (d()->isReference)
@@ -323,7 +328,7 @@ public:
loadReference();
}
qint32 signedIdx = static_cast<qint32>(index);
- return (signedIdx < d()->container.count()) ? QV4::Attr_Data : QV4::Attr_Invalid;
+ return (signedIdx < d()->container->count()) ? QV4::Attr_Data : QV4::Attr_Invalid;
}
void containerAdvanceIterator(ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attrs)
@@ -339,11 +344,11 @@ public:
loadReference();
}
- if (it->arrayIndex < static_cast<uint>(d()->container.count())) {
+ if (it->arrayIndex < static_cast<uint>(d()->container->count())) {
*index = it->arrayIndex;
++it->arrayIndex;
*attrs = QV4::Attr_Data;
- p->value = convertElementToValue(engine(), d()->container.at(*index));
+ p->value = convertElementToValue(engine(), d()->container->at(*index));
return;
}
QV4::Object::advanceIterator(this, it, name, index, p, attrs);
@@ -361,12 +366,12 @@ public:
}
qint32 signedIdx = static_cast<qint32>(index);
- if (signedIdx >= d()->container.count())
+ if (signedIdx >= d()->container->count())
return false;
/* according to ECMA262r3 it should be Undefined, */
/* but we cannot, so we insert a default-value instead. */
- d()->container.replace(signedIdx, typename Container::value_type());
+ d()->container->replace(signedIdx, typename Container::value_type());
if (d()->isReference)
storeReference();
@@ -411,8 +416,8 @@ public:
callData->args[0] = convertElementToValue(this->m_ctx->d()->engine, lhs);
callData->args[1] = convertElementToValue(this->m_ctx->d()->engine, rhs);
callData->thisObject = this->m_ctx->d()->engine->globalObject;
- QV4::ScopedValue result(scope, compare->call(callData));
- return result->toNumber() < 0;
+ compare->call(scope, callData);
+ return scope.result.toNumber() < 0;
}
private:
@@ -431,10 +436,10 @@ public:
QV4::Scope scope(ctx);
if (ctx->argc() == 1 && ctx->args()[0].as<FunctionObject>()) {
CompareFunctor cf(ctx, ctx->args()[0]);
- std::sort(d()->container.begin(), d()->container.end(), cf);
+ std::sort(d()->container->begin(), d()->container->end(), cf);
} else {
DefaultCompareFunctor cf;
- std::sort(d()->container.begin(), d()->container.end(), cf);
+ std::sort(d()->container->begin(), d()->container->end(), cf);
}
if (d()->isReference)
@@ -453,7 +458,7 @@ public:
return QV4::Encode(0);
This->loadReference();
}
- return QV4::Encode(This->d()->container.count());
+ return QV4::Encode(This->d()->container->count());
}
static QV4::ReturnedValue method_set_length(QV4::CallContext* ctx)
@@ -477,23 +482,23 @@ public:
}
/* Determine whether we need to modify the sequence */
qint32 newCount = static_cast<qint32>(newLength);
- qint32 count = This->d()->container.count();
+ qint32 count = This->d()->container->count();
if (newCount == count) {
return QV4::Encode::undefined();
} else if (newCount > count) {
/* according to ECMA262r3 we need to insert */
/* undefined values increasing length to newLength. */
/* We cannot, so we insert default-values instead. */
- This->d()->container.reserve(newCount);
+ This->d()->container->reserve(newCount);
while (newCount > count++) {
- This->d()->container.append(typename Container::value_type());
+ This->d()->container->append(typename Container::value_type());
}
} else {
/* according to ECMA262r3 we need to remove */
/* elements until the sequence is the required length. */
while (newCount < count) {
count--;
- This->d()->container.removeAt(count);
+ This->d()->container->removeAt(count);
}
}
/* write back if required. */
@@ -505,7 +510,7 @@ public:
}
QVariant toVariant() const
- { return QVariant::fromValue<Container>(d()->container); }
+ { return QVariant::fromValue<Container>(*d()->container); }
static QVariant toVariant(QV4::ArrayObject *array)
{
@@ -522,7 +527,7 @@ public:
{
Q_ASSERT(d()->object);
Q_ASSERT(d()->isReference);
- void *a[] = { &d()->container, 0 };
+ void *a[] = { d()->container, 0 };
QMetaObject::metacall(d()->object, QMetaObject::ReadProperty, d()->propertyIndex, a);
}
@@ -531,8 +536,8 @@ public:
Q_ASSERT(d()->object);
Q_ASSERT(d()->isReference);
int status = -1;
- QQmlPropertyPrivate::WriteFlags flags = QQmlPropertyPrivate::DontRemoveBinding;
- void *a[] = { &d()->container, 0, &status, &flags };
+ QQmlPropertyData::WriteFlags flags = QQmlPropertyData::DontRemoveBinding;
+ void *a[] = { d()->container, 0, &status, &flags };
QMetaObject::metacall(d()->object, QMetaObject::WriteProperty, d()->propertyIndex, a);
}
@@ -553,11 +558,14 @@ public:
template <typename Container>
-Heap::QQmlSequence<Container>::QQmlSequence(const Container &container)
- : container(container)
- , propertyIndex(-1)
- , isReference(false)
+void Heap::QQmlSequence<Container>::init(const Container &container)
{
+ Object::init();
+ this->container = new Container(container);
+ propertyIndex = -1;
+ isReference = false;
+ object.init();
+
QV4::Scope scope(internalClass->engine);
QV4::Scoped<QV4::QQmlSequence<Container> > o(scope, this);
o->setArrayType(Heap::ArrayData::Custom);
@@ -565,11 +573,13 @@ Heap::QQmlSequence<Container>::QQmlSequence(const Container &container)
}
template <typename Container>
-Heap::QQmlSequence<Container>::QQmlSequence(QObject *object, int propertyIndex)
- : object(object)
- , propertyIndex(propertyIndex)
- , isReference(true)
+void Heap::QQmlSequence<Container>::init(QObject *object, int propertyIndex)
{
+ Object::init();
+ this->container = new Container;
+ this->propertyIndex = propertyIndex;
+ isReference = true;
+ this->object.init(object);
QV4::Scope scope(internalClass->engine);
QV4::Scoped<QV4::QQmlSequence<Container> > o(scope, this);
o->setArrayType(Heap::ArrayData::Custom);
diff --git a/src/qml/jsruntime/qv4string.cpp b/src/qml/jsruntime/qv4string.cpp
index abef885249..3365ffe637 100644
--- a/src/qml/jsruntime/qv4string.cpp
+++ b/src/qml/jsruntime/qv4string.cpp
@@ -49,57 +49,8 @@
using namespace QV4;
-static uint toArrayIndex(const QChar *ch, const QChar *end)
-{
- uint i = ch->unicode() - '0';
- if (i > 9)
- return UINT_MAX;
- ++ch;
- // reject "01", "001", ...
- if (i == 0 && ch != end)
- return UINT_MAX;
-
- while (ch < end) {
- uint x = ch->unicode() - '0';
- if (x > 9)
- return UINT_MAX;
- uint n = i*10 + x;
- if (n < i)
- // overflow
- return UINT_MAX;
- i = n;
- ++ch;
- }
- return i;
-}
-
#ifndef V4_BOOTSTRAP
-static uint toArrayIndex(const char *ch, const char *end)
-{
- uint i = *ch - '0';
- if (i > 9)
- return UINT_MAX;
- ++ch;
- // reject "01", "001", ...
- if (i == 0 && ch != end)
- return UINT_MAX;
-
- while (ch < end) {
- uint x = *ch - '0';
- if (x > 9)
- return UINT_MAX;
- uint n = i*10 + x;
- if (n < i)
- // overflow
- return UINT_MAX;
- i = n;
- ++ch;
- }
- return i;
-}
-
-
DEFINE_MANAGED_VTABLE(String);
void String::markObjects(Heap::Base *that, ExecutionEngine *e)
@@ -123,9 +74,11 @@ bool String::isEqualTo(Managed *t, Managed *o)
}
-Heap::String::String(MemoryManager *mm, const QString &t)
- : mm(mm)
+void Heap::String::init(MemoryManager *mm, const QString &t)
{
+ Base::init();
+ this->mm = mm;
+
subtype = String::StringType_Unknown;
text = const_cast<QString &>(t).data_ptr();
@@ -136,9 +89,11 @@ Heap::String::String(MemoryManager *mm, const QString &t)
len = text->size;
}
-Heap::String::String(MemoryManager *mm, String *l, String *r)
- : mm(mm)
+void Heap::String::init(MemoryManager *mm, String *l, String *r)
{
+ Base::init();
+ this->mm = mm;
+
subtype = String::StringType_Unknown;
left = l;
@@ -198,31 +153,6 @@ void Heap::String::simplifyString() const
mm->growUnmanagedHeapSizeUsage(size_t(text->size) * sizeof(QChar));
}
-void Heap::String::createHashValue() const
-{
- if (largestSubLength)
- simplifyString();
- Q_ASSERT(!largestSubLength);
- const QChar *ch = reinterpret_cast<const QChar *>(text->data());
- const QChar *end = ch + text->size;
-
- // array indices get their number as hash value
- stringHash = ::toArrayIndex(ch, end);
- if (stringHash != UINT_MAX) {
- subtype = Heap::String::StringType_ArrayIndex;
- return;
- }
-
- uint h = 0xffffffff;
- while (ch < end) {
- h = 31 * h + ch->unicode();
- ++ch;
- }
-
- stringHash = h;
- subtype = Heap::String::StringType_Regular;
-}
-
void Heap::String::append(const String *data, QChar *ch)
{
std::vector<const String *> worklist;
@@ -243,45 +173,14 @@ void Heap::String::append(const String *data, QChar *ch)
}
}
-
-
-
-uint String::createHashValue(const QChar *ch, int length)
-{
- const QChar *end = ch + length;
-
- // array indices get their number as hash value
- uint stringHash = ::toArrayIndex(ch, end);
- if (stringHash != UINT_MAX)
- return stringHash;
-
- uint h = 0xffffffff;
- while (ch < end) {
- h = 31 * h + ch->unicode();
- ++ch;
- }
-
- return h;
-}
-
-uint String::createHashValue(const char *ch, int length)
+void Heap::String::createHashValue() const
{
- const char *end = ch + length;
-
- // array indices get their number as hash value
- uint stringHash = ::toArrayIndex(ch, end);
- if (stringHash != UINT_MAX)
- return stringHash;
-
- uint h = 0xffffffff;
- while (ch < end) {
- if ((uchar)(*ch) >= 0x80)
- return UINT_MAX;
- h = 31 * h + *ch;
- ++ch;
- }
-
- return h;
+ if (largestSubLength)
+ simplifyString();
+ Q_ASSERT(!largestSubLength);
+ const QChar *ch = reinterpret_cast<const QChar *>(text->data());
+ const QChar *end = ch + text->size;
+ stringHash = QV4::String::calculateHashValue(ch, end, &subtype);
}
uint String::getLength(const Managed *m)
@@ -293,6 +192,6 @@ uint String::getLength(const Managed *m)
uint String::toArrayIndex(const QString &str)
{
- return ::toArrayIndex(str.constData(), str.constData() + str.length());
+ return QV4::String::toArrayIndex(str.constData(), str.constData() + str.length());
}
diff --git a/src/qml/jsruntime/qv4string_p.h b/src/qml/jsruntime/qv4string_p.h
index 3196f49896..23ec3349b9 100644
--- a/src/qml/jsruntime/qv4string_p.h
+++ b/src/qml/jsruntime/qv4string_p.h
@@ -52,6 +52,7 @@
#include <QtCore/qstring.h>
#include "qv4managed_p.h"
+#include <QtCore/private/qnumeric_p.h>
QT_BEGIN_NAMESPACE
@@ -62,7 +63,6 @@ struct Identifier;
namespace Heap {
-#ifndef V4_BOOTSTRAP
struct Q_QML_PRIVATE_EXPORT String : Base {
enum StringType {
StringType_Unknown,
@@ -70,11 +70,13 @@ struct Q_QML_PRIVATE_EXPORT String : Base {
StringType_ArrayIndex
};
- String(MemoryManager *mm, const QString &text);
- String(MemoryManager *mm, String *l, String *n);
- ~String() {
+#ifndef V4_BOOTSTRAP
+ void init(MemoryManager *mm, const QString &text);
+ void init(MemoryManager *mm, String *l, String *n);
+ void destroy() {
if (!largestSubLength && !text->ref.deref())
QStringData::deallocate(text);
+ Base::destroy();
}
void simplifyString() const;
int length() const {
@@ -130,8 +132,9 @@ struct Q_QML_PRIVATE_EXPORT String : Base {
MemoryManager *mm;
private:
static void append(const String *data, QChar *ch);
-};
#endif
+};
+V4_ASSERT_IS_TRIVIAL(String)
}
@@ -183,8 +186,17 @@ struct Q_QML_PRIVATE_EXPORT String : public Managed {
void makeIdentifierImpl(ExecutionEngine *e) const;
- static uint createHashValue(const QChar *ch, int length);
- static uint createHashValue(const char *ch, int length);
+ static uint createHashValue(const QChar *ch, int length, uint *subtype)
+ {
+ const QChar *end = ch + length;
+ return calculateHashValue(ch, end, subtype);
+ }
+
+ static uint createHashValue(const char *ch, int length, uint *subtype)
+ {
+ const char *end = ch + length;
+ return calculateHashValue(ch, end, subtype);
+ }
bool startsWithUpper() const {
const String::Data *l = d();
@@ -203,6 +215,54 @@ protected:
public:
static uint toArrayIndex(const QString &str);
+
+private:
+ static inline uint toUInt(const QChar *ch) { return ch->unicode(); }
+ static inline uint toUInt(const char *ch) { return *ch; }
+
+ template <typename T>
+ static inline uint toArrayIndex(const T *ch, const T *end)
+ {
+ uint i = toUInt(ch) - '0';
+ if (i > 9)
+ return UINT_MAX;
+ ++ch;
+ // reject "01", "001", ...
+ if (i == 0 && ch != end)
+ return UINT_MAX;
+
+ while (ch < end) {
+ uint x = toUInt(ch) - '0';
+ if (x > 9)
+ return UINT_MAX;
+ if (mul_overflow(i, uint(10), &i) || add_overflow(i, x, &i)) // i = i * 10 + x
+ return UINT_MAX;
+ ++ch;
+ }
+ return i;
+ }
+
+public:
+ template <typename T>
+ static inline uint calculateHashValue(const T *ch, const T* end, uint *subtype)
+ {
+ // array indices get their number as hash value
+ uint h = toArrayIndex(ch, end);
+ if (h != UINT_MAX) {
+ if (subtype)
+ *subtype = Heap::String::StringType_ArrayIndex;
+ return h;
+ }
+
+ while (ch < end) {
+ h = 31 * h + toUInt(ch);
+ ++ch;
+ }
+
+ if (subtype)
+ *subtype = Heap::String::StringType_Regular;
+ return h;
+ }
};
template<>
diff --git a/src/qml/jsruntime/qv4stringobject.cpp b/src/qml/jsruntime/qv4stringobject.cpp
index b874766655..829ada0c1a 100644
--- a/src/qml/jsruntime/qv4stringobject.cpp
+++ b/src/qml/jsruntime/qv4stringobject.cpp
@@ -73,15 +73,17 @@ using namespace QV4;
DEFINE_OBJECT_VTABLE(StringObject);
-Heap::StringObject::StringObject()
+void Heap::StringObject::init()
{
+ Object::init();
Q_ASSERT(vtable() == QV4::StringObject::staticVTable());
string = internalClass->engine->id_empty()->d();
*propertyData(LengthPropertyIndex) = Primitive::fromInt32(0);
}
-Heap::StringObject::StringObject(const QV4::String *str)
+void Heap::StringObject::init(const QV4::String *str)
{
+ Object::init();
string = str->d();
*propertyData(LengthPropertyIndex) = Primitive::fromInt32(length());
}
@@ -152,33 +154,29 @@ void StringObject::markObjects(Heap::Base *that, ExecutionEngine *e)
DEFINE_OBJECT_VTABLE(StringCtor);
-Heap::StringCtor::StringCtor(QV4::ExecutionContext *scope)
- : Heap::FunctionObject(scope, QStringLiteral("String"))
+void Heap::StringCtor::init(QV4::ExecutionContext *scope)
{
+ Heap::FunctionObject::init(scope, QStringLiteral("String"));
}
-ReturnedValue StringCtor::construct(const Managed *m, CallData *callData)
+void StringCtor::construct(const Managed *m, Scope &scope, CallData *callData)
{
ExecutionEngine *v4 = static_cast<const Object *>(m)->engine();
- Scope scope(v4);
ScopedString value(scope);
if (callData->argc)
value = callData->args[0].toString(v4);
else
value = v4->newString();
- return Encode(v4->newStringObject(value));
+ scope.result = Encode(v4->newStringObject(value));
}
-ReturnedValue StringCtor::call(const Managed *m, CallData *callData)
+void StringCtor::call(const Managed *, Scope &scope, CallData *callData)
{
- ExecutionEngine *v4 = static_cast<const Object *>(m)->engine();
- Scope scope(v4);
- ScopedValue value(scope);
+ ExecutionEngine *v4 = scope.engine;
if (callData->argc)
- value = callData->args[0].toString(v4);
+ scope.result = callData->args[0].toString(v4);
else
- value = v4->newString();
- return value->asReturnedValue();
+ scope.result = v4->newString();
}
void StringPrototype::init(ExecutionEngine *engine, Object *ctor)
@@ -196,7 +194,9 @@ void StringPrototype::init(ExecutionEngine *engine, Object *ctor)
defineDefaultProperty(QStringLiteral("charAt"), method_charAt, 1);
defineDefaultProperty(QStringLiteral("charCodeAt"), method_charCodeAt, 1);
defineDefaultProperty(QStringLiteral("concat"), method_concat, 1);
+ defineDefaultProperty(QStringLiteral("endsWith"), method_endsWith, 1);
defineDefaultProperty(QStringLiteral("indexOf"), method_indexOf, 1);
+ defineDefaultProperty(QStringLiteral("includes"), method_includes, 1);
defineDefaultProperty(QStringLiteral("lastIndexOf"), method_lastIndexOf, 1);
defineDefaultProperty(QStringLiteral("localeCompare"), method_localeCompare, 1);
defineDefaultProperty(QStringLiteral("match"), method_match, 1);
@@ -204,6 +204,7 @@ void StringPrototype::init(ExecutionEngine *engine, Object *ctor)
defineDefaultProperty(QStringLiteral("search"), method_search, 1);
defineDefaultProperty(QStringLiteral("slice"), method_slice, 2);
defineDefaultProperty(QStringLiteral("split"), method_split, 2);
+ defineDefaultProperty(QStringLiteral("startsWith"), method_startsWith, 1);
defineDefaultProperty(QStringLiteral("substr"), method_substr, 2);
defineDefaultProperty(QStringLiteral("substring"), method_substring, 2);
defineDefaultProperty(QStringLiteral("toLowerCase"), method_toLowerCase);
@@ -293,6 +294,30 @@ ReturnedValue StringPrototype::method_concat(CallContext *context)
return context->d()->engine->newString(value)->asReturnedValue();
}
+ReturnedValue StringPrototype::method_endsWith(CallContext *context)
+{
+ QString value = getThisString(context);
+ if (context->d()->engine->hasException)
+ return Encode::undefined();
+
+ QString searchString;
+ if (context->argc()) {
+ if (context->args()[0].as<RegExpObject>())
+ return context->engine()->throwTypeError();
+ searchString = context->args()[0].toQString();
+ }
+
+ int pos = value.length();
+ if (context->argc() > 1)
+ pos = (int) context->args()[1].toInteger();
+
+ if (pos == value.length())
+ return Encode(value.endsWith(searchString));
+
+ QStringRef stringToSearch = value.leftRef(pos);
+ return Encode(stringToSearch.endsWith(searchString));
+}
+
ReturnedValue StringPrototype::method_indexOf(CallContext *context)
{
QString value = getThisString(context);
@@ -314,6 +339,35 @@ ReturnedValue StringPrototype::method_indexOf(CallContext *context)
return Encode(index);
}
+ReturnedValue StringPrototype::method_includes(CallContext *context)
+{
+ QString value = getThisString(context);
+ if (context->d()->engine->hasException)
+ return Encode::undefined();
+
+ QString searchString;
+ if (context->argc()) {
+ if (context->args()[0].as<RegExpObject>())
+ return context->engine()->throwTypeError();
+ searchString = context->args()[0].toQString();
+ }
+
+ int pos = 0;
+ if (context->argc() > 1) {
+ Scope scope(context);
+ ScopedValue posArg(scope, context->argument(1));
+ pos = (int) posArg->toInteger();
+ if (!posArg->isInteger() && posArg->isNumber() && qIsInf(posArg->toNumber()))
+ pos = value.length();
+ }
+
+ if (pos == 0)
+ return Encode(value.contains(searchString));
+
+ QStringRef stringToSearch = value.midRef(pos);
+ return Encode(stringToSearch.contains(searchString));
+}
+
ReturnedValue StringPrototype::method_lastIndexOf(CallContext *context)
{
Scope scope(context);
@@ -367,7 +421,8 @@ ReturnedValue StringPrototype::method_match(CallContext *context)
if (!rx) {
ScopedCallData callData(scope, 1);
callData->args[0] = regexp;
- rx = context->d()->engine->regExpCtor()->construct(callData);
+ context->d()->engine->regExpCtor()->construct(scope, callData);
+ rx = scope.result.asReturnedValue();
}
if (!rx)
@@ -383,8 +438,10 @@ ReturnedValue StringPrototype::method_match(CallContext *context)
ScopedCallData callData(scope, 1);
callData->thisObject = rx;
callData->args[0] = s;
- if (!global)
- return exec->call(callData);
+ if (!global) {
+ exec->call(scope, callData);
+ return scope.result.asReturnedValue();
+ }
ScopedString lastIndex(scope, context->d()->engine->newString(QStringLiteral("lastIndex")));
rx->put(lastIndex, ScopedValue(scope, Primitive::fromInt32(0)));
@@ -392,14 +449,13 @@ ReturnedValue StringPrototype::method_match(CallContext *context)
double previousLastIndex = 0;
uint n = 0;
- ScopedValue result(scope);
ScopedValue matchStr(scope);
ScopedValue index(scope);
while (1) {
- result = exec->call(callData);
- if (result->isNull())
+ exec->call(scope, callData);
+ if (scope.result.isNull())
break;
- assert(result->isObject());
+ assert(scope.result.isObject());
index = rx->get(lastIndex, 0);
double thisIndex = index->toInteger();
if (previousLastIndex == thisIndex) {
@@ -408,7 +464,7 @@ ReturnedValue StringPrototype::method_match(CallContext *context)
} else {
previousLastIndex = thisIndex;
}
- matchStr = result->objectValue()->getIndexed(0);
+ matchStr = scope.result.objectValue()->getIndexed(0);
a->arraySet(n, matchStr);
++n;
}
@@ -524,7 +580,6 @@ ReturnedValue StringPrototype::method_replace(CallContext *ctx)
}
QString result;
- ScopedValue replacement(scope);
ScopedValue replaceValue(scope, ctx->argument(1));
ScopedFunctionObject searchCallback(scope, replaceValue);
if (!!searchCallback) {
@@ -549,9 +604,9 @@ ReturnedValue StringPrototype::method_replace(CallContext *ctx)
callData->args[numCaptures] = Primitive::fromUInt32(matchStart);
callData->args[numCaptures + 1] = ctx->d()->engine->newString(string);
- replacement = searchCallback->call(callData);
+ searchCallback->call(scope, callData);
result += string.midRef(lastEnd, matchStart - lastEnd);
- result += replacement->toQString();
+ result += scope.result.toQString();
lastEnd = matchEnd;
}
result += string.midRef(lastEnd);
@@ -584,17 +639,17 @@ ReturnedValue StringPrototype::method_search(CallContext *ctx)
{
Scope scope(ctx);
QString string = getThisString(ctx);
- ScopedValue regExpValue(scope, ctx->argument(0));
+ scope.result = ctx->argument(0);
if (scope.engine->hasException)
return Encode::undefined();
- Scoped<RegExpObject> regExp(scope, regExpValue->as<RegExpObject>());
+ Scoped<RegExpObject> regExp(scope, scope.result.as<RegExpObject>());
if (!regExp) {
ScopedCallData callData(scope, 1);
- callData->args[0] = regExpValue;
- regExpValue = ctx->d()->engine->regExpCtor()->construct(callData);
+ callData->args[0] = scope.result;
+ ctx->d()->engine->regExpCtor()->construct(scope, callData);
if (scope.engine->hasException)
return Encode::undefined();
- regExp = regExpValue->as<RegExpObject>();
+ regExp = scope.result.as<RegExpObject>();
Q_ASSERT(regExp);
}
Scoped<RegExp> re(scope, regExp->value());
@@ -662,7 +717,7 @@ ReturnedValue StringPrototype::method_split(CallContext *ctx)
Scoped<RegExpObject> re(scope, separatorValue);
if (re) {
- if (re->value()->pattern.isEmpty()) {
+ if (re->value()->pattern->isEmpty()) {
re = (RegExpObject *)0;
separatorValue = ctx->d()->engine->newString();
}
@@ -716,6 +771,30 @@ ReturnedValue StringPrototype::method_split(CallContext *ctx)
return array.asReturnedValue();
}
+ReturnedValue StringPrototype::method_startsWith(CallContext *context)
+{
+ QString value = getThisString(context);
+ if (context->d()->engine->hasException)
+ return Encode::undefined();
+
+ QString searchString;
+ if (context->argc()) {
+ if (context->args()[0].as<RegExpObject>())
+ return context->engine()->throwTypeError();
+ searchString = context->args()[0].toQString();
+ }
+
+ int pos = 0;
+ if (context->argc() > 1)
+ pos = (int) context->args()[1].toInteger();
+
+ if (pos == 0)
+ return Encode(value.startsWith(searchString));
+
+ QStringRef stringToSearch = value.midRef(pos);
+ return Encode(stringToSearch.startsWith(searchString));
+}
+
ReturnedValue StringPrototype::method_substr(CallContext *context)
{
const QString value = getThisString(context);
diff --git a/src/qml/jsruntime/qv4stringobject_p.h b/src/qml/jsruntime/qv4stringobject_p.h
index 3930a011e6..b9f9d44fe8 100644
--- a/src/qml/jsruntime/qv4stringobject_p.h
+++ b/src/qml/jsruntime/qv4stringobject_p.h
@@ -65,8 +65,8 @@ struct StringObject : Object {
LengthPropertyIndex = 0
};
- StringObject();
- StringObject(const QV4::String *string);
+ void init();
+ void init(const QV4::String *string);
String *string;
Heap::String *getIndex(uint index) const;
@@ -74,7 +74,7 @@ struct StringObject : Object {
};
struct StringCtor : FunctionObject {
- StringCtor(QV4::ExecutionContext *scope);
+ void init(QV4::ExecutionContext *scope);
};
}
@@ -103,8 +103,8 @@ struct StringCtor: FunctionObject
{
V4_OBJECT2(StringCtor, FunctionObject)
- static ReturnedValue construct(const Managed *m, CallData *callData);
- static ReturnedValue call(const Managed *that, CallData *callData);
+ static void construct(const Managed *m, Scope &scope, CallData *callData);
+ static void call(const Managed *, Scope &scope, CallData *callData);
};
struct StringPrototype: StringObject
@@ -115,7 +115,9 @@ struct StringPrototype: StringObject
static ReturnedValue method_charAt(CallContext *context);
static ReturnedValue method_charCodeAt(CallContext *context);
static ReturnedValue method_concat(CallContext *context);
+ static ReturnedValue method_endsWith(CallContext *ctx);
static ReturnedValue method_indexOf(CallContext *context);
+ static ReturnedValue method_includes(CallContext *context);
static ReturnedValue method_lastIndexOf(CallContext *context);
static ReturnedValue method_localeCompare(CallContext *context);
static ReturnedValue method_match(CallContext *context);
@@ -123,6 +125,7 @@ struct StringPrototype: StringObject
static ReturnedValue method_search(CallContext *ctx);
static ReturnedValue method_slice(CallContext *ctx);
static ReturnedValue method_split(CallContext *ctx);
+ static ReturnedValue method_startsWith(CallContext *ctx);
static ReturnedValue method_substr(CallContext *context);
static ReturnedValue method_substring(CallContext *context);
static ReturnedValue method_toLowerCase(CallContext *ctx);
diff --git a/src/qml/jsruntime/qv4typedarray.cpp b/src/qml/jsruntime/qv4typedarray.cpp
index c86f252353..009c573bf8 100644
--- a/src/qml/jsruntime/qv4typedarray.cpp
+++ b/src/qml/jsruntime/qv4typedarray.cpp
@@ -202,36 +202,40 @@ const TypedArrayOperations operations[Heap::TypedArray::NTypes] = {
};
-Heap::TypedArrayCtor::TypedArrayCtor(QV4::ExecutionContext *scope, TypedArray::Type t)
- : Heap::FunctionObject(scope, QLatin1String(operations[t].name))
- , type(t)
+void Heap::TypedArrayCtor::init(QV4::ExecutionContext *scope, TypedArray::Type t)
{
+ Heap::FunctionObject::init(scope, QLatin1String(operations[t].name));
+ type = t;
}
-ReturnedValue TypedArrayCtor::construct(const Managed *m, CallData *callData)
+void TypedArrayCtor::construct(const Managed *m, Scope &scope, CallData *callData)
{
- Scope scope(static_cast<const Object *>(m)->engine());
Scoped<TypedArrayCtor> that(scope, static_cast<const TypedArrayCtor *>(m));
if (!callData->argc || !callData->args[0].isObject()) {
// ECMA 6 22.2.1.1
double l = callData->argc ? callData->args[0].toNumber() : 0;
- if (scope.engine->hasException)
- return Encode::undefined();
+ if (scope.engine->hasException) {
+ scope.result = Encode::undefined();
+ return;
+ }
uint len = (uint)l;
if (l != len)
scope.engine->throwRangeError(QStringLiteral("Non integer length for typed array."));
uint byteLength = len * operations[that->d()->type].bytesPerElement;
Scoped<ArrayBuffer> buffer(scope, scope.engine->newArrayBuffer(byteLength));
- if (scope.engine->hasException)
- return Encode::undefined();
+ if (scope.engine->hasException) {
+ scope.result = Encode::undefined();
+ return;
+ }
Scoped<TypedArray > array(scope, TypedArray::create(scope.engine, that->d()->type));
array->d()->buffer = buffer->d();
array->d()->byteLength = byteLength;
array->d()->byteOffset = 0;
- return array.asReturnedValue();
+ scope.result = array.asReturnedValue();
+ return;
}
Scoped<TypedArray> typedArray(scope, callData->argument(0));
if (!!typedArray) {
@@ -243,8 +247,10 @@ ReturnedValue TypedArrayCtor::construct(const Managed *m, CallData *callData)
uint destByteLength = byteLength*destElementSize/srcElementSize;
Scoped<ArrayBuffer> newBuffer(scope, scope.engine->newArrayBuffer(destByteLength));
- if (scope.engine->hasException)
- return Encode::undefined();
+ if (scope.engine->hasException) {
+ scope.result = Encode::undefined();
+ return;
+ }
Scoped<TypedArray > array(scope, TypedArray::create(scope.engine, that->d()->type));
array->d()->buffer = newBuffer->d();
@@ -269,7 +275,8 @@ ReturnedValue TypedArrayCtor::construct(const Managed *m, CallData *callData)
}
}
- return array.asReturnedValue();
+ scope.result = array.asReturnedValue();
+ return;
}
Scoped<ArrayBuffer> buffer(scope, callData->argument(0));
if (!!buffer) {
@@ -278,21 +285,29 @@ ReturnedValue TypedArrayCtor::construct(const Managed *m, CallData *callData)
double dbyteOffset = callData->argc > 1 ? callData->args[1].toInteger() : 0;
uint byteOffset = (uint)dbyteOffset;
uint elementSize = operations[that->d()->type].bytesPerElement;
- if (dbyteOffset < 0 || (byteOffset % elementSize) || dbyteOffset > buffer->byteLength())
- return scope.engine->throwRangeError(QStringLiteral("new TypedArray: invalid byteOffset"));
+ if (dbyteOffset < 0 || (byteOffset % elementSize) || dbyteOffset > buffer->byteLength()) {
+ scope.result = scope.engine->throwRangeError(QStringLiteral("new TypedArray: invalid byteOffset"));
+ return;
+ }
uint byteLength;
if (callData->argc < 3 || callData->args[2].isUndefined()) {
byteLength = buffer->byteLength() - byteOffset;
- if (buffer->byteLength() < byteOffset || byteLength % elementSize)
- return scope.engine->throwRangeError(QStringLiteral("new TypedArray: invalid length"));
+ if (buffer->byteLength() < byteOffset || byteLength % elementSize) {
+ scope.result = scope.engine->throwRangeError(QStringLiteral("new TypedArray: invalid length"));
+ return;
+ }
} else {
double l = qBound(0., callData->args[2].toInteger(), (double)UINT_MAX);
- if (scope.engine->hasException)
- return Encode::undefined();
+ if (scope.engine->hasException) {
+ scope.result = Encode::undefined();
+ return;
+ }
l *= elementSize;
- if (buffer->byteLength() - byteOffset < l)
- return scope.engine->throwRangeError(QStringLiteral("new TypedArray: invalid length"));
+ if (buffer->byteLength() - byteOffset < l) {
+ scope.result = scope.engine->throwRangeError(QStringLiteral("new TypedArray: invalid length"));
+ return;
+ }
byteLength = (uint)l;
}
@@ -300,20 +315,25 @@ ReturnedValue TypedArrayCtor::construct(const Managed *m, CallData *callData)
array->d()->buffer = buffer->d();
array->d()->byteLength = byteLength;
array->d()->byteOffset = byteOffset;
- return array.asReturnedValue();
+ scope.result = array.asReturnedValue();
+ return;
}
// ECMA 6 22.2.1.3
ScopedObject o(scope, callData->argument(0));
uint l = (uint) qBound(0., ScopedValue(scope, o->get(scope.engine->id_length()))->toInteger(), (double)UINT_MAX);
- if (scope.engine->hasException)
- return scope.engine->throwTypeError();
+ if (scope.engine->hasException) {
+ scope.result = scope.engine->throwTypeError();
+ return;
+ }
uint elementSize = operations[that->d()->type].bytesPerElement;
Scoped<ArrayBuffer> newBuffer(scope, scope.engine->newArrayBuffer(l * elementSize));
- if (scope.engine->hasException)
- return Encode::undefined();
+ if (scope.engine->hasException) {
+ scope.result = Encode::undefined();
+ return;
+ }
Scoped<TypedArray > array(scope, TypedArray::create(scope.engine, that->d()->type));
array->d()->buffer = newBuffer->d();
@@ -326,25 +346,28 @@ ReturnedValue TypedArrayCtor::construct(const Managed *m, CallData *callData)
while (idx < l) {
val = o->getIndexed(idx);
array->d()->type->write(scope.engine, b, 0, val);
- if (scope.engine->hasException)
- return Encode::undefined();
+ if (scope.engine->hasException) {
+ scope.result = Encode::undefined();
+ return;
+ }
++idx;
b += elementSize;
}
- return array.asReturnedValue();
+ scope.result = array.asReturnedValue();
}
-ReturnedValue TypedArrayCtor::call(const Managed *that, CallData *callData)
+void TypedArrayCtor::call(const Managed *that, Scope &scope, CallData *callData)
{
- return construct(that, callData);
+ construct(that, scope, callData);
}
-Heap::TypedArray::TypedArray(Type t)
- : type(operations + t),
- arrayType(t)
+void Heap::TypedArray::init(Type t)
{
+ Object::init();
+ type = operations + t;
+ arrayType = t;
}
Heap::TypedArray *TypedArray::create(ExecutionEngine *e, Heap::TypedArray::Type t)
@@ -582,5 +605,6 @@ ReturnedValue TypedArrayPrototype::method_subarray(CallContext *ctx)
callData->args[0] = buffer;
callData->args[1] = Encode(a->d()->byteOffset + begin*a->d()->type->bytesPerElement);
callData->args[2] = Encode(newLen);
- return constructor->construct(callData);
+ constructor->construct(scope, callData);
+ return scope.result.asReturnedValue();
}
diff --git a/src/qml/jsruntime/qv4typedarray_p.h b/src/qml/jsruntime/qv4typedarray_p.h
index 757273e4ed..0112d2e4a1 100644
--- a/src/qml/jsruntime/qv4typedarray_p.h
+++ b/src/qml/jsruntime/qv4typedarray_p.h
@@ -86,7 +86,7 @@ struct TypedArray : Object {
NTypes
};
- TypedArray(Type t);
+ void init(Type t);
const TypedArrayOperations *type;
Pointer<ArrayBuffer> buffer;
@@ -96,13 +96,13 @@ struct TypedArray : Object {
};
struct TypedArrayCtor : FunctionObject {
- TypedArrayCtor(QV4::ExecutionContext *scope, TypedArray::Type t);
+ void init(QV4::ExecutionContext *scope, TypedArray::Type t);
TypedArray::Type type;
};
struct TypedArrayPrototype : Object {
- inline TypedArrayPrototype(TypedArray::Type t);
+ inline void init(TypedArray::Type t);
TypedArray::Type type;
};
@@ -140,8 +140,8 @@ struct TypedArrayCtor: FunctionObject
{
V4_OBJECT2(TypedArrayCtor, FunctionObject)
- static ReturnedValue construct(const Managed *m, CallData *callData);
- static ReturnedValue call(const Managed *that, CallData *callData);
+ static void construct(const Managed *m, Scope &scope, CallData *callData);
+ static void call(const Managed *that, Scope &scope, CallData *callData);
};
@@ -160,10 +160,11 @@ struct TypedArrayPrototype : Object
static ReturnedValue method_subarray(CallContext *ctx);
};
-inline
-Heap::TypedArrayPrototype::TypedArrayPrototype(TypedArray::Type t)
- : type(t)
+inline void
+Heap::TypedArrayPrototype::init(TypedArray::Type t)
{
+ Object::init();
+ type = t;
}
} // namespace QV4
diff --git a/src/qml/jsruntime/qv4value_p.h b/src/qml/jsruntime/qv4value_p.h
index 9eee34aff2..6d5cff4ecc 100644
--- a/src/qml/jsruntime/qv4value_p.h
+++ b/src/qml/jsruntime/qv4value_p.h
@@ -372,16 +372,22 @@ public:
}
Q_ALWAYS_INLINE String *stringValue() const {
+ if (!isString())
+ return nullptr;
return m() ? reinterpret_cast<String*>(const_cast<Value *>(this)) : 0;
}
Q_ALWAYS_INLINE Object *objectValue() const {
+ if (!isObject())
+ return nullptr;
return m() ? reinterpret_cast<Object*>(const_cast<Value *>(this)) : 0;
}
Q_ALWAYS_INLINE Managed *managed() const {
+ if (!isManaged())
+ return nullptr;
return m() ? reinterpret_cast<Managed*>(const_cast<Value *>(this)) : 0;
}
Q_ALWAYS_INLINE Heap::Base *heapObject() const {
- return m();
+ return isManaged() ? m() : nullptr;
}
static inline Value fromHeapObject(Heap::Base *m)
@@ -431,7 +437,10 @@ public:
}
template <typename T>
T *as() {
- return const_cast<T *>(const_cast<const Value *>(this)->as<T>());
+ if (isManaged())
+ return const_cast<T *>(const_cast<const Value *>(this)->as<T>());
+ else
+ return nullptr;
}
template<typename T> inline T *cast() {
@@ -471,11 +480,8 @@ public:
template<typename T>
Value &operator=(const Scoped<T> &t);
- Value &operator=(const Value &v) {
- _val = v._val;
- return *this;
- }
};
+V4_ASSERT_IS_TRIVIAL(Value)
inline bool Value::isString() const
{
diff --git a/src/qml/jsruntime/qv4variantobject.cpp b/src/qml/jsruntime/qv4variantobject.cpp
index 444c0a37e0..b26dd27913 100644
--- a/src/qml/jsruntime/qv4variantobject.cpp
+++ b/src/qml/jsruntime/qv4variantobject.cpp
@@ -50,20 +50,23 @@ using namespace QV4;
DEFINE_OBJECT_VTABLE(VariantObject);
-Heap::VariantObject::VariantObject()
+void Heap::VariantObject::init()
{
+ Object::init();
+ scarceData = new ExecutionEngine::ScarceResourceData;
}
-Heap::VariantObject::VariantObject(const QVariant &value)
+void Heap::VariantObject::init(const QVariant &value)
{
- data = value;
+ Object::init();
+ scarceData = new ExecutionEngine::ScarceResourceData(value);
if (isScarce())
- internalClass->engine->scarceResources.insert(this);
+ removeVmePropertyReference();
}
bool VariantObject::Data::isScarce() const
{
- QVariant::Type t = data.type();
+ QVariant::Type t = data().type();
return t == QVariant::Pixmap || t == QVariant::Image;
}
@@ -73,10 +76,10 @@ bool VariantObject::isEqualTo(Managed *m, Managed *other)
QV4::VariantObject *lv = static_cast<QV4::VariantObject *>(m);
if (QV4::VariantObject *rv = other->as<QV4::VariantObject>())
- return lv->d()->data == rv->d()->data;
+ return lv->d()->data() == rv->d()->data();
if (QV4::QQmlValueTypeWrapper *v = other->as<QQmlValueTypeWrapper>())
- return v->isEqual(lv->d()->data);
+ return v->isEqual(lv->d()->data());
return false;
}
@@ -87,7 +90,7 @@ void VariantObject::addVmePropertyReference()
// remove from the ep->scarceResources list
// since it is now no longer eligible to be
// released automatically by the engine.
- d()->node.remove();
+ d()->addVmePropertyReference();
}
}
@@ -97,7 +100,7 @@ void VariantObject::removeVmePropertyReference()
// and add to the ep->scarceResources list
// since it is now eligible to be released
// automatically by the engine.
- internalClass()->engine->scarceResources.insert(d());
+ d()->removeVmePropertyReference();
}
}
@@ -115,7 +118,7 @@ QV4::ReturnedValue VariantPrototype::method_preserve(CallContext *ctx)
Scope scope(ctx);
Scoped<VariantObject> o(scope, ctx->thisObject().as<QV4::VariantObject>());
if (o && o->d()->isScarce())
- o->d()->node.remove();
+ o->d()->addVmePropertyReference();
return Encode::undefined();
}
@@ -125,8 +128,8 @@ QV4::ReturnedValue VariantPrototype::method_destroy(CallContext *ctx)
Scoped<VariantObject> o(scope, ctx->thisObject().as<QV4::VariantObject>());
if (o) {
if (o->d()->isScarce())
- o->d()->node.remove();
- o->d()->data = QVariant();
+ o->d()->addVmePropertyReference();
+ o->d()->data() = QVariant();
}
return Encode::undefined();
}
@@ -137,9 +140,9 @@ QV4::ReturnedValue VariantPrototype::method_toString(CallContext *ctx)
Scoped<VariantObject> o(scope, ctx->thisObject().as<QV4::VariantObject>());
if (!o)
return Encode::undefined();
- QString result = o->d()->data.toString();
- if (result.isEmpty() && !o->d()->data.canConvert(QVariant::String))
- result = QStringLiteral("QVariant(%0)").arg(QString::fromLatin1(o->d()->data.typeName()));
+ QString result = o->d()->data().toString();
+ if (result.isEmpty() && !o->d()->data().canConvert(QVariant::String))
+ result = QStringLiteral("QVariant(%0)").arg(QString::fromLatin1(o->d()->data().typeName()));
return Encode(ctx->d()->engine->newString(result));
}
@@ -148,7 +151,7 @@ QV4::ReturnedValue VariantPrototype::method_valueOf(CallContext *ctx)
Scope scope(ctx);
Scoped<VariantObject> o(scope, ctx->thisObject().as<QV4::VariantObject>());
if (o) {
- QVariant v = o->d()->data;
+ QVariant v = o->d()->data();
switch (v.type()) {
case QVariant::Invalid:
return Encode::undefined();
diff --git a/src/qml/jsruntime/qv4variantobject_p.h b/src/qml/jsruntime/qv4variantobject_p.h
index e50706ef94..9a04069c12 100644
--- a/src/qml/jsruntime/qv4variantobject_p.h
+++ b/src/qml/jsruntime/qv4variantobject_p.h
@@ -64,16 +64,28 @@ namespace QV4 {
namespace Heap {
-struct VariantObject : Object, public ExecutionEngine::ScarceResourceData
+struct VariantObject : Object
{
- VariantObject();
- VariantObject(const QVariant &value);
- ~VariantObject() {
+ void init();
+ void init(const QVariant &value);
+ void destroy() {
+ Q_ASSERT(scarceData);
if (isScarce())
- node.remove();
+ addVmePropertyReference();
+ delete scarceData;
+ Object::destroy();
}
bool isScarce() const;
int vmePropertyReferenceCount;
+
+ const QVariant &data() const { return scarceData->data; }
+ QVariant &data() { return scarceData->data; }
+
+ void addVmePropertyReference() { scarceData->node.remove(); }
+ void removeVmePropertyReference() { internalClass->engine->scarceResources.insert(scarceData); }
+
+private:
+ ExecutionEngine::ScarceResourceData *scarceData;
};
}
diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp
index fbb26dc571..0f7f6b1f75 100644
--- a/src/qml/jsruntime/qv4vme_moth.cpp
+++ b/src/qml/jsruntime/qv4vme_moth.cpp
@@ -142,6 +142,7 @@ Q_QML_EXPORT int qt_v4DebuggerHook(const char *json);
} // extern "C"
+#ifndef QT_NO_QML_DEBUGGER
static int qt_v4BreakpointCount = 0;
static bool qt_v4IsDebugging = true;
static bool qt_v4IsStepping = false;
@@ -203,7 +204,7 @@ int qt_v4DebuggerHook(const char *json)
QJsonDocument doc = QJsonDocument::fromJson(json);
QJsonObject ob = doc.object();
- QByteArray command = ob.value(QStringLiteral("command")).toString().toUtf8();
+ QByteArray command = ob.value(QLatin1String("command")).toString().toUtf8();
if (command == "protocolVersion") {
return ProtocolVersion; // Version number.
@@ -217,17 +218,17 @@ int qt_v4DebuggerHook(const char *json)
if (command == "insertBreakpoint") {
Breakpoint bp;
bp.bpNumber = ++qt_v4BreakpointCount;
- bp.lineNumber = ob.value(QStringLiteral("lineNumber")).toString().toInt();
- bp.engineName = ob.value(QStringLiteral("engineName")).toString();
- bp.fullName = ob.value(QStringLiteral("fullName")).toString();
- bp.condition = ob.value(QStringLiteral("condition")).toString();
+ bp.lineNumber = ob.value(QLatin1String("lineNumber")).toString().toInt();
+ bp.engineName = ob.value(QLatin1String("engineName")).toString();
+ bp.fullName = ob.value(QLatin1String("fullName")).toString();
+ bp.condition = ob.value(QLatin1String("condition")).toString();
qt_v4Breakpoints.append(bp);
return bp.bpNumber;
}
if (command == "removeBreakpoint") {
- int lineNumber = ob.value(QStringLiteral("lineNumber")).toString().toInt();
- QString fullName = ob.value(QStringLiteral("fullName")).toString();
+ int lineNumber = ob.value(QLatin1String("lineNumber")).toString().toInt();
+ QString fullName = ob.value(QLatin1String("fullName")).toString();
if (qt_v4Breakpoints.last().matches(fullName, lineNumber)) {
qt_v4Breakpoints.removeLast();
return Success;
@@ -285,6 +286,7 @@ static void qt_v4CheckForBreak(QV4::ExecutionContext *context, QV4::Value **scop
}
}
+#endif // QT_NO_QML_DEBUGGER
// End of debugger interface
using namespace QV4;
@@ -400,7 +402,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code
QV4::Value **scopes = static_cast<QV4::Value **>(alloca(sizeof(QV4::Value *)*(2 + 2*scopeDepth)));
{
- scopes[0] = const_cast<QV4::Value *>(context->d()->compilationUnit->data->constants());
+ scopes[0] = const_cast<QV4::Value *>(context->d()->compilationUnit->constants);
// stack gets setup in push instruction
scopes[1] = 0;
QV4::Heap::ExecutionContext *scope = context->d();
@@ -451,12 +453,12 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code
MOTH_END_INSTR(LoadRegExp)
MOTH_BEGIN_INSTR(LoadClosure)
- STOREVALUE(instr.result, Runtime::closure(engine, instr.value));
+ STOREVALUE(instr.result, engine->runtime.closure(engine, instr.value));
MOTH_END_INSTR(LoadClosure)
MOTH_BEGIN_INSTR(LoadName)
TRACE(inline, "property name = %s", runtimeStrings[instr.name]->toQString().toUtf8().constData());
- STOREVALUE(instr.result, Runtime::getActivationProperty(engine, instr.name));
+ STOREVALUE(instr.result, engine->runtime.getActivationProperty(engine, instr.name));
MOTH_END_INSTR(LoadName)
MOTH_BEGIN_INSTR(GetGlobalLookup)
@@ -466,12 +468,12 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code
MOTH_BEGIN_INSTR(StoreName)
TRACE(inline, "property name = %s", runtimeStrings[instr.name]->toQString().toUtf8().constData());
- Runtime::setActivationProperty(engine, instr.name, VALUE(instr.source));
+ engine->runtime.setActivationProperty(engine, instr.name, VALUE(instr.source));
CHECK_EXCEPTION;
MOTH_END_INSTR(StoreName)
MOTH_BEGIN_INSTR(LoadElement)
- STOREVALUE(instr.result, Runtime::getElement(engine, VALUE(instr.base), VALUE(instr.index)));
+ STOREVALUE(instr.result, engine->runtime.getElement(engine, VALUE(instr.base), VALUE(instr.index)));
MOTH_END_INSTR(LoadElement)
MOTH_BEGIN_INSTR(LoadElementLookup)
@@ -480,7 +482,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code
MOTH_END_INSTR(LoadElementLookup)
MOTH_BEGIN_INSTR(StoreElement)
- Runtime::setElement(engine, VALUE(instr.base), VALUE(instr.index), VALUE(instr.source));
+ engine->runtime.setElement(engine, VALUE(instr.base), VALUE(instr.index), VALUE(instr.source));
CHECK_EXCEPTION;
MOTH_END_INSTR(StoreElement)
@@ -491,7 +493,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code
MOTH_END_INSTR(StoreElementLookup)
MOTH_BEGIN_INSTR(LoadProperty)
- STOREVALUE(instr.result, Runtime::getProperty(engine, VALUE(instr.base), instr.name));
+ STOREVALUE(instr.result, engine->runtime.getProperty(engine, VALUE(instr.base), instr.name));
MOTH_END_INSTR(LoadProperty)
MOTH_BEGIN_INSTR(GetLookup)
@@ -500,7 +502,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code
MOTH_END_INSTR(GetLookup)
MOTH_BEGIN_INSTR(StoreProperty)
- Runtime::setProperty(engine, VALUE(instr.base), instr.name, VALUE(instr.source));
+ engine->runtime.setProperty(engine, VALUE(instr.base), instr.name, VALUE(instr.source));
CHECK_EXCEPTION;
MOTH_END_INSTR(StoreProperty)
@@ -511,42 +513,42 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code
MOTH_END_INSTR(SetLookup)
MOTH_BEGIN_INSTR(StoreQObjectProperty)
- Runtime::setQmlQObjectProperty(engine, VALUE(instr.base), instr.propertyIndex, VALUE(instr.source));
+ engine->runtime.setQmlQObjectProperty(engine, VALUE(instr.base), instr.propertyIndex, VALUE(instr.source));
CHECK_EXCEPTION;
MOTH_END_INSTR(StoreQObjectProperty)
MOTH_BEGIN_INSTR(LoadQObjectProperty)
- STOREVALUE(instr.result, Runtime::getQmlQObjectProperty(engine, VALUE(instr.base), instr.propertyIndex, instr.captureRequired));
+ STOREVALUE(instr.result, engine->runtime.getQmlQObjectProperty(engine, VALUE(instr.base), instr.propertyIndex, instr.captureRequired));
MOTH_END_INSTR(LoadQObjectProperty)
MOTH_BEGIN_INSTR(StoreScopeObjectProperty)
- Runtime::setQmlScopeObjectProperty(engine, VALUE(instr.base), instr.propertyIndex, VALUE(instr.source));
+ engine->runtime.setQmlScopeObjectProperty(engine, VALUE(instr.base), instr.propertyIndex, VALUE(instr.source));
CHECK_EXCEPTION;
MOTH_END_INSTR(StoreScopeObjectProperty)
MOTH_BEGIN_INSTR(LoadScopeObjectProperty)
- STOREVALUE(instr.result, Runtime::getQmlScopeObjectProperty(engine, VALUE(instr.base), instr.propertyIndex));
+ STOREVALUE(instr.result, engine->runtime.getQmlScopeObjectProperty(engine, VALUE(instr.base), instr.propertyIndex, instr.captureRequired));
MOTH_END_INSTR(LoadScopeObjectProperty)
MOTH_BEGIN_INSTR(StoreContextObjectProperty)
- Runtime::setQmlContextObjectProperty(engine, VALUE(instr.base), instr.propertyIndex, VALUE(instr.source));
+ engine->runtime.setQmlContextObjectProperty(engine, VALUE(instr.base), instr.propertyIndex, VALUE(instr.source));
CHECK_EXCEPTION;
MOTH_END_INSTR(StoreContextObjectProperty)
MOTH_BEGIN_INSTR(LoadContextObjectProperty)
- STOREVALUE(instr.result, Runtime::getQmlContextObjectProperty(engine, VALUE(instr.base), instr.propertyIndex));
+ STOREVALUE(instr.result, engine->runtime.getQmlContextObjectProperty(engine, VALUE(instr.base), instr.propertyIndex, instr.captureRequired));
MOTH_END_INSTR(LoadContextObjectProperty)
MOTH_BEGIN_INSTR(LoadIdObject)
- STOREVALUE(instr.result, Runtime::getQmlIdObject(engine, VALUE(instr.base), instr.index));
+ STOREVALUE(instr.result, engine->runtime.getQmlIdObject(engine, VALUE(instr.base), instr.index));
MOTH_END_INSTR(LoadIdObject)
MOTH_BEGIN_INSTR(LoadAttachedQObjectProperty)
- STOREVALUE(instr.result, Runtime::getQmlAttachedProperty(engine, instr.attachedPropertiesId, instr.propertyIndex));
+ STOREVALUE(instr.result, engine->runtime.getQmlAttachedProperty(engine, instr.attachedPropertiesId, instr.propertyIndex));
MOTH_END_INSTR(LoadAttachedQObjectProperty)
MOTH_BEGIN_INSTR(LoadSingletonQObjectProperty)
- STOREVALUE(instr.result, Runtime::getQmlSingletonQObjectProperty(engine, VALUE(instr.base), instr.propertyIndex, instr.captureRequired));
+ STOREVALUE(instr.result, engine->runtime.getQmlSingletonQObjectProperty(engine, VALUE(instr.base), instr.propertyIndex, instr.captureRequired));
MOTH_END_INSTR(LoadSingletonQObjectProperty)
MOTH_BEGIN_INSTR(Push)
@@ -572,7 +574,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code
callData->tag = QV4::Value::Integer_Type_Internal;
callData->argc = instr.argc;
callData->thisObject = QV4::Primitive::undefinedValue();
- STOREVALUE(instr.result, Runtime::callValue(engine, VALUE(instr.dest), callData));
+ STOREVALUE(instr.result, engine->runtime.callValue(engine, VALUE(instr.dest), callData));
MOTH_END_INSTR(CallValue)
MOTH_BEGIN_INSTR(CallProperty)
@@ -582,7 +584,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code
callData->tag = QV4::Value::Integer_Type_Internal;
callData->argc = instr.argc;
callData->thisObject = VALUE(instr.base);
- STOREVALUE(instr.result, Runtime::callProperty(engine, instr.name, callData));
+ STOREVALUE(instr.result, engine->runtime.callProperty(engine, instr.name, callData));
MOTH_END_INSTR(CallProperty)
MOTH_BEGIN_INSTR(CallPropertyLookup)
@@ -591,7 +593,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code
callData->tag = QV4::Value::Integer_Type_Internal;
callData->argc = instr.argc;
callData->thisObject = VALUE(instr.base);
- STOREVALUE(instr.result, Runtime::callPropertyLookup(engine, instr.lookupIndex, callData));
+ STOREVALUE(instr.result, engine->runtime.callPropertyLookup(engine, instr.lookupIndex, callData));
MOTH_END_INSTR(CallPropertyLookup)
MOTH_BEGIN_INSTR(CallScopeObjectProperty)
@@ -601,7 +603,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code
callData->tag = QV4::Value::Integer_Type_Internal;
callData->argc = instr.argc;
callData->thisObject = VALUE(instr.base);
- STOREVALUE(instr.result, Runtime::callQmlScopeObjectProperty(engine, instr.index, callData));
+ STOREVALUE(instr.result, engine->runtime.callQmlScopeObjectProperty(engine, instr.index, callData));
MOTH_END_INSTR(CallScopeObjectProperty)
MOTH_BEGIN_INSTR(CallContextObjectProperty)
@@ -611,7 +613,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code
callData->tag = QV4::Value::Integer_Type_Internal;
callData->argc = instr.argc;
callData->thisObject = VALUE(instr.base);
- STOREVALUE(instr.result, Runtime::callQmlContextObjectProperty(engine, instr.index, callData));
+ STOREVALUE(instr.result, engine->runtime.callQmlContextObjectProperty(engine, instr.index, callData));
MOTH_END_INSTR(CallContextObjectProperty)
MOTH_BEGIN_INSTR(CallElement)
@@ -620,7 +622,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code
callData->tag = QV4::Value::Integer_Type_Internal;
callData->argc = instr.argc;
callData->thisObject = VALUE(instr.base);
- STOREVALUE(instr.result, Runtime::callElement(engine, VALUE(instr.index), callData));
+ STOREVALUE(instr.result, engine->runtime.callElement(engine, VALUE(instr.index), callData));
MOTH_END_INSTR(CallElement)
MOTH_BEGIN_INSTR(CallActivationProperty)
@@ -629,7 +631,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code
callData->tag = QV4::Value::Integer_Type_Internal;
callData->argc = instr.argc;
callData->thisObject = QV4::Primitive::undefinedValue();
- STOREVALUE(instr.result, Runtime::callActivationProperty(engine, instr.name, callData));
+ STOREVALUE(instr.result, engine->runtime.callActivationProperty(engine, instr.name, callData));
MOTH_END_INSTR(CallActivationProperty)
MOTH_BEGIN_INSTR(CallGlobalLookup)
@@ -638,7 +640,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code
callData->tag = QV4::Value::Integer_Type_Internal;
callData->argc = instr.argc;
callData->thisObject = QV4::Primitive::undefinedValue();
- STOREVALUE(instr.result, Runtime::callGlobalLookup(engine, instr.index, callData));
+ STOREVALUE(instr.result, Runtime::method_callGlobalLookup(engine, instr.index, callData));
MOTH_END_INSTR(CallGlobalLookup)
MOTH_BEGIN_INSTR(SetExceptionHandler)
@@ -646,95 +648,95 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code
MOTH_END_INSTR(SetExceptionHandler)
MOTH_BEGIN_INSTR(CallBuiltinThrow)
- Runtime::throwException(engine, VALUE(instr.arg));
+ engine->runtime.throwException(engine, VALUE(instr.arg));
CHECK_EXCEPTION;
MOTH_END_INSTR(CallBuiltinThrow)
MOTH_BEGIN_INSTR(CallBuiltinUnwindException)
- STOREVALUE(instr.result, Runtime::unwindException(engine));
+ STOREVALUE(instr.result, engine->runtime.unwindException(engine));
MOTH_END_INSTR(CallBuiltinUnwindException)
MOTH_BEGIN_INSTR(CallBuiltinPushCatchScope)
- Runtime::pushCatchScope(static_cast<QV4::NoThrowEngine*>(engine), instr.name);
+ engine->runtime.pushCatchScope(static_cast<QV4::NoThrowEngine*>(engine), instr.name);
context = engine->currentContext;
MOTH_END_INSTR(CallBuiltinPushCatchScope)
MOTH_BEGIN_INSTR(CallBuiltinPushScope)
- Runtime::pushWithScope(VALUE(instr.arg), engine);
+ engine->runtime.pushWithScope(VALUE(instr.arg), engine);
context = engine->currentContext;
CHECK_EXCEPTION;
MOTH_END_INSTR(CallBuiltinPushScope)
MOTH_BEGIN_INSTR(CallBuiltinPopScope)
- Runtime::popScope(engine);
+ engine->runtime.popScope(engine);
context = engine->currentContext;
MOTH_END_INSTR(CallBuiltinPopScope)
MOTH_BEGIN_INSTR(CallBuiltinForeachIteratorObject)
- STOREVALUE(instr.result, Runtime::foreachIterator(engine, VALUE(instr.arg)));
+ STOREVALUE(instr.result, engine->runtime.foreachIterator(engine, VALUE(instr.arg)));
MOTH_END_INSTR(CallBuiltinForeachIteratorObject)
MOTH_BEGIN_INSTR(CallBuiltinForeachNextPropertyName)
- STOREVALUE(instr.result, Runtime::foreachNextPropertyName(VALUE(instr.arg)));
+ STOREVALUE(instr.result, engine->runtime.foreachNextPropertyName(VALUE(instr.arg)));
MOTH_END_INSTR(CallBuiltinForeachNextPropertyName)
MOTH_BEGIN_INSTR(CallBuiltinDeleteMember)
- STOREVALUE(instr.result, Runtime::deleteMember(engine, VALUE(instr.base), instr.member));
+ STOREVALUE(instr.result, engine->runtime.deleteMember(engine, VALUE(instr.base), instr.member));
MOTH_END_INSTR(CallBuiltinDeleteMember)
MOTH_BEGIN_INSTR(CallBuiltinDeleteSubscript)
- STOREVALUE(instr.result, Runtime::deleteElement(engine, VALUE(instr.base), VALUE(instr.index)));
+ STOREVALUE(instr.result, engine->runtime.deleteElement(engine, VALUE(instr.base), VALUE(instr.index)));
MOTH_END_INSTR(CallBuiltinDeleteSubscript)
MOTH_BEGIN_INSTR(CallBuiltinDeleteName)
- STOREVALUE(instr.result, Runtime::deleteName(engine, instr.name));
+ STOREVALUE(instr.result, engine->runtime.deleteName(engine, instr.name));
MOTH_END_INSTR(CallBuiltinDeleteName)
MOTH_BEGIN_INSTR(CallBuiltinTypeofScopeObjectProperty)
- STOREVALUE(instr.result, Runtime::typeofScopeObjectProperty(engine, VALUE(instr.base), instr.index));
+ STOREVALUE(instr.result, engine->runtime.typeofScopeObjectProperty(engine, VALUE(instr.base), instr.index));
MOTH_END_INSTR(CallBuiltinTypeofMember)
MOTH_BEGIN_INSTR(CallBuiltinTypeofContextObjectProperty)
- STOREVALUE(instr.result, Runtime::typeofContextObjectProperty(engine, VALUE(instr.base), instr.index));
+ STOREVALUE(instr.result, engine->runtime.typeofContextObjectProperty(engine, VALUE(instr.base), instr.index));
MOTH_END_INSTR(CallBuiltinTypeofMember)
MOTH_BEGIN_INSTR(CallBuiltinTypeofMember)
- STOREVALUE(instr.result, Runtime::typeofMember(engine, VALUE(instr.base), instr.member));
+ STOREVALUE(instr.result, engine->runtime.typeofMember(engine, VALUE(instr.base), instr.member));
MOTH_END_INSTR(CallBuiltinTypeofMember)
MOTH_BEGIN_INSTR(CallBuiltinTypeofSubscript)
- STOREVALUE(instr.result, Runtime::typeofElement(engine, VALUE(instr.base), VALUE(instr.index)));
+ STOREVALUE(instr.result, engine->runtime.typeofElement(engine, VALUE(instr.base), VALUE(instr.index)));
MOTH_END_INSTR(CallBuiltinTypeofSubscript)
MOTH_BEGIN_INSTR(CallBuiltinTypeofName)
- STOREVALUE(instr.result, Runtime::typeofName(engine, instr.name));
+ STOREVALUE(instr.result, engine->runtime.typeofName(engine, instr.name));
MOTH_END_INSTR(CallBuiltinTypeofName)
MOTH_BEGIN_INSTR(CallBuiltinTypeofValue)
- STOREVALUE(instr.result, Runtime::typeofValue(engine, VALUE(instr.value)));
+ STOREVALUE(instr.result, engine->runtime.typeofValue(engine, VALUE(instr.value)));
MOTH_END_INSTR(CallBuiltinTypeofValue)
MOTH_BEGIN_INSTR(CallBuiltinDeclareVar)
- Runtime::declareVar(engine, instr.isDeletable, instr.varName);
+ engine->runtime.declareVar(engine, instr.isDeletable, instr.varName);
MOTH_END_INSTR(CallBuiltinDeclareVar)
MOTH_BEGIN_INSTR(CallBuiltinDefineArray)
Q_ASSERT(instr.args + instr.argc <= stackSize);
QV4::Value *args = stack + instr.args;
- STOREVALUE(instr.result, Runtime::arrayLiteral(engine, args, instr.argc));
+ STOREVALUE(instr.result, engine->runtime.arrayLiteral(engine, args, instr.argc));
MOTH_END_INSTR(CallBuiltinDefineArray)
MOTH_BEGIN_INSTR(CallBuiltinDefineObjectLiteral)
QV4::Value *args = stack + instr.args;
- STOREVALUE(instr.result, Runtime::objectLiteral(engine, args, instr.internalClassId, instr.arrayValueCount, instr.arrayGetterSetterCountAndFlags));
+ STOREVALUE(instr.result, engine->runtime.objectLiteral(engine, args, instr.internalClassId, instr.arrayValueCount, instr.arrayGetterSetterCountAndFlags));
MOTH_END_INSTR(CallBuiltinDefineObjectLiteral)
MOTH_BEGIN_INSTR(CallBuiltinSetupArgumentsObject)
- STOREVALUE(instr.result, Runtime::setupArgumentsObject(engine));
+ STOREVALUE(instr.result, engine->runtime.setupArgumentsObject(engine));
MOTH_END_INSTR(CallBuiltinSetupArgumentsObject)
MOTH_BEGIN_INSTR(CallBuiltinConvertThisToObject)
- Runtime::convertThisToObject(engine);
+ engine->runtime.convertThisToObject(engine);
CHECK_EXCEPTION;
MOTH_END_INSTR(CallBuiltinConvertThisToObject)
@@ -744,7 +746,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code
callData->tag = QV4::Value::Integer_Type_Internal;
callData->argc = instr.argc;
callData->thisObject = QV4::Primitive::undefinedValue();
- STOREVALUE(instr.result, Runtime::constructValue(engine, VALUE(instr.func), callData));
+ STOREVALUE(instr.result, engine->runtime.constructValue(engine, VALUE(instr.func), callData));
MOTH_END_INSTR(CreateValue)
MOTH_BEGIN_INSTR(CreateProperty)
@@ -753,7 +755,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code
callData->tag = QV4::Value::Integer_Type_Internal;
callData->argc = instr.argc;
callData->thisObject = VALUE(instr.base);
- STOREVALUE(instr.result, Runtime::constructProperty(engine, instr.name, callData));
+ STOREVALUE(instr.result, engine->runtime.constructProperty(engine, instr.name, callData));
MOTH_END_INSTR(CreateProperty)
MOTH_BEGIN_INSTR(ConstructPropertyLookup)
@@ -762,7 +764,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code
callData->tag = QV4::Value::Integer_Type_Internal;
callData->argc = instr.argc;
callData->thisObject = VALUE(instr.base);
- STOREVALUE(instr.result, Runtime::constructPropertyLookup(engine, instr.index, callData));
+ STOREVALUE(instr.result, engine->runtime.constructPropertyLookup(engine, instr.index, callData));
MOTH_END_INSTR(ConstructPropertyLookup)
MOTH_BEGIN_INSTR(CreateActivationProperty)
@@ -771,7 +773,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code
callData->tag = QV4::Value::Integer_Type_Internal;
callData->argc = instr.argc;
callData->thisObject = QV4::Primitive::undefinedValue();
- STOREVALUE(instr.result, Runtime::constructActivationProperty(engine, instr.name, callData));
+ STOREVALUE(instr.result, engine->runtime.constructActivationProperty(engine, instr.name, callData));
MOTH_END_INSTR(CreateActivationProperty)
MOTH_BEGIN_INSTR(ConstructGlobalLookup)
@@ -780,7 +782,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code
callData->tag = QV4::Value::Integer_Type_Internal;
callData->argc = instr.argc;
callData->thisObject = QV4::Primitive::undefinedValue();
- STOREVALUE(instr.result, Runtime::constructGlobalLookup(engine, instr.index, callData));
+ STOREVALUE(instr.result, engine->runtime.constructGlobalLookup(engine, instr.index, callData));
MOTH_END_INSTR(ConstructGlobalLookup)
MOTH_BEGIN_INSTR(Jump)
@@ -802,7 +804,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code
MOTH_END_INSTR(JumpNe)
MOTH_BEGIN_INSTR(UNot)
- STOREVALUE(instr.result, Runtime::uNot(VALUE(instr.source)));
+ STOREVALUE(instr.result, engine->runtime.uNot(VALUE(instr.source)));
MOTH_END_INSTR(UNot)
MOTH_BEGIN_INSTR(UNotBool)
@@ -811,15 +813,15 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code
MOTH_END_INSTR(UNotBool)
MOTH_BEGIN_INSTR(UPlus)
- STOREVALUE(instr.result, Runtime::uPlus(VALUE(instr.source)));
+ STOREVALUE(instr.result, engine->runtime.uPlus(VALUE(instr.source)));
MOTH_END_INSTR(UPlus)
MOTH_BEGIN_INSTR(UMinus)
- STOREVALUE(instr.result, Runtime::uMinus(VALUE(instr.source)));
+ STOREVALUE(instr.result, engine->runtime.uMinus(VALUE(instr.source)));
MOTH_END_INSTR(UMinus)
MOTH_BEGIN_INSTR(UCompl)
- STOREVALUE(instr.result, Runtime::complement(VALUE(instr.source)));
+ STOREVALUE(instr.result, engine->runtime.complement(VALUE(instr.source)));
MOTH_END_INSTR(UCompl)
MOTH_BEGIN_INSTR(UComplInt)
@@ -827,31 +829,32 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code
MOTH_END_INSTR(UComplInt)
MOTH_BEGIN_INSTR(Increment)
- STOREVALUE(instr.result, Runtime::increment(VALUE(instr.source)));
+ STOREVALUE(instr.result, engine->runtime.increment(VALUE(instr.source)));
MOTH_END_INSTR(Increment)
MOTH_BEGIN_INSTR(Decrement)
- STOREVALUE(instr.result, Runtime::decrement(VALUE(instr.source)));
+ STOREVALUE(instr.result, engine->runtime.decrement(VALUE(instr.source)));
MOTH_END_INSTR(Decrement)
MOTH_BEGIN_INSTR(Binop)
- STOREVALUE(instr.result, instr.alu(VALUE(instr.lhs), VALUE(instr.rhs)));
+ QV4::Runtime::BinaryOperation op = *reinterpret_cast<QV4::Runtime::BinaryOperation *>(reinterpret_cast<char *>(&engine->runtime) + instr.alu);
+ STOREVALUE(instr.result, op(VALUE(instr.lhs), VALUE(instr.rhs)));
MOTH_END_INSTR(Binop)
MOTH_BEGIN_INSTR(Add)
- STOREVALUE(instr.result, Runtime::add(engine, VALUE(instr.lhs), VALUE(instr.rhs)));
+ STOREVALUE(instr.result, engine->runtime.add(engine, VALUE(instr.lhs), VALUE(instr.rhs)));
MOTH_END_INSTR(Add)
MOTH_BEGIN_INSTR(BitAnd)
- STOREVALUE(instr.result, Runtime::bitAnd(VALUE(instr.lhs), VALUE(instr.rhs)));
+ STOREVALUE(instr.result, engine->runtime.bitAnd(VALUE(instr.lhs), VALUE(instr.rhs)));
MOTH_END_INSTR(BitAnd)
MOTH_BEGIN_INSTR(BitOr)
- STOREVALUE(instr.result, Runtime::bitOr(VALUE(instr.lhs), VALUE(instr.rhs)));
+ STOREVALUE(instr.result, engine->runtime.bitOr(VALUE(instr.lhs), VALUE(instr.rhs)));
MOTH_END_INSTR(BitOr)
MOTH_BEGIN_INSTR(BitXor)
- STOREVALUE(instr.result, Runtime::bitXor(VALUE(instr.lhs), VALUE(instr.rhs)));
+ STOREVALUE(instr.result, engine->runtime.bitXor(VALUE(instr.lhs), VALUE(instr.rhs)));
MOTH_END_INSTR(BitXor)
MOTH_BEGIN_INSTR(Shr)
@@ -886,15 +889,16 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code
MOTH_END_INSTR(ShlConst)
MOTH_BEGIN_INSTR(Mul)
- STOREVALUE(instr.result, Runtime::mul(VALUE(instr.lhs), VALUE(instr.rhs)));
+ STOREVALUE(instr.result, engine->runtime.mul(VALUE(instr.lhs), VALUE(instr.rhs)));
MOTH_END_INSTR(Mul)
MOTH_BEGIN_INSTR(Sub)
- STOREVALUE(instr.result, Runtime::sub(VALUE(instr.lhs), VALUE(instr.rhs)));
+ STOREVALUE(instr.result, engine->runtime.sub(VALUE(instr.lhs), VALUE(instr.rhs)));
MOTH_END_INSTR(Sub)
MOTH_BEGIN_INSTR(BinopContext)
- STOREVALUE(instr.result, instr.alu(engine, VALUE(instr.lhs), VALUE(instr.rhs)));
+ QV4::Runtime::BinaryOperationContext op = *reinterpret_cast<QV4::Runtime::BinaryOperationContext *>(reinterpret_cast<char *>(&engine->runtime) + instr.alu);
+ STOREVALUE(instr.result, op(engine, VALUE(instr.lhs), VALUE(instr.rhs)));
MOTH_END_INSTR(BinopContext)
MOTH_BEGIN_INSTR(Ret)
@@ -902,9 +906,10 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code
return VALUE(instr.result).asReturnedValue();
MOTH_END_INSTR(Ret)
+#ifndef QT_NO_QML_DEBUGGER
MOTH_BEGIN_INSTR(Debug)
engine->current->lineNumber = instr.lineNumber;
- QV4::Debugging::Debugger *debugger = context->engine()->debugger;
+ QV4::Debugging::Debugger *debugger = context->engine()->debugger();
if (debugger && debugger->pauseAtNextOpportunity())
debugger->maybeBreakAtInstruction();
if (qt_v4IsDebugging)
@@ -916,21 +921,22 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code
if (qt_v4IsDebugging)
qt_v4CheckForBreak(context, scopes, scopeDepth);
MOTH_END_INSTR(Line)
+#endif // QT_NO_QML_DEBUGGER
MOTH_BEGIN_INSTR(LoadThis)
VALUE(instr.result) = context->thisObject();
MOTH_END_INSTR(LoadThis)
MOTH_BEGIN_INSTR(LoadQmlContext)
- VALUE(instr.result) = Runtime::getQmlContext(static_cast<QV4::NoThrowEngine*>(engine));
+ VALUE(instr.result) = engine->runtime.getQmlContext(static_cast<QV4::NoThrowEngine*>(engine));
MOTH_END_INSTR(LoadQmlContext)
MOTH_BEGIN_INSTR(LoadQmlImportedScripts)
- VALUE(instr.result) = Runtime::getQmlImportedScripts(static_cast<QV4::NoThrowEngine*>(engine));
+ VALUE(instr.result) = engine->runtime.getQmlImportedScripts(static_cast<QV4::NoThrowEngine*>(engine));
MOTH_END_INSTR(LoadQmlImportedScripts)
MOTH_BEGIN_INSTR(LoadQmlSingleton)
- VALUE(instr.result) = Runtime::getQmlSingleton(static_cast<QV4::NoThrowEngine*>(engine), instr.name);
+ VALUE(instr.result) = engine->runtime.getQmlSingleton(static_cast<QV4::NoThrowEngine*>(engine), instr.name);
MOTH_END_INSTR(LoadQmlSingleton)
#ifdef MOTH_THREADED_INTERPRETER
@@ -968,7 +974,7 @@ void **VME::instructionJumpTable()
QV4::ReturnedValue VME::exec(ExecutionEngine *engine, const uchar *code)
{
VME vme;
- QV4::Debugging::Debugger *debugger = engine->debugger;
+ QV4::Debugging::Debugger *debugger = engine->debugger();
if (debugger)
debugger->enteringFunction();
QV4::ReturnedValue retVal = vme.run(engine, code);
diff --git a/src/qml/jsruntime/qv4vme_moth_p.h b/src/qml/jsruntime/qv4vme_moth_p.h
index cd8d335f1c..f8893509d9 100644
--- a/src/qml/jsruntime/qv4vme_moth_p.h
+++ b/src/qml/jsruntime/qv4vme_moth_p.h
@@ -51,9 +51,12 @@
// We mean it.
//
+#include <private/qv4global_p.h>
#include <private/qv4runtime_p.h>
#include <private/qv4instr_moth_p.h>
+QT_REQUIRE_CONFIG(qml_interpreter);
+
QT_BEGIN_NAMESPACE
namespace QV4 {
diff --git a/src/qml/memory/qv4heap_p.h b/src/qml/memory/qv4heap_p.h
index ed7a531766..5a3797f397 100644
--- a/src/qml/memory/qv4heap_p.h
+++ b/src/qml/memory/qv4heap_p.h
@@ -52,6 +52,17 @@
#include <QtCore/QString>
#include <private/qv4global_p.h>
+#include <QSharedPointer>
+
+// To check if Heap::Base::init is called (meaning, all subclasses did their init and called their
+// parent's init all up the inheritance chain), define QML_CHECK_INIT_DESTROY_CALLS below.
+#undef QML_CHECK_INIT_DESTROY_CALLS
+
+#if defined(_MSC_VER) && (_MSC_VER < 1900) // broken compilers:
+# define V4_ASSERT_IS_TRIVIAL(x)
+#else // working compilers:
+# define V4_ASSERT_IS_TRIVIAL(x) Q_STATIC_ASSERT(std::is_trivial< x >::value);
+#endif
QT_BEGIN_NAMESPACE
@@ -77,6 +88,8 @@ struct VTable
namespace Heap {
struct Q_QML_EXPORT Base {
+ void *operator new(size_t) = delete;
+
quintptr mm_data; // vtable and markbit
inline ReturnedValue asReturnedValue() const;
@@ -119,13 +132,48 @@ struct Q_QML_EXPORT Base {
void *operator new(size_t, Managed *m) { return m; }
void *operator new(size_t, Heap::Base *m) { return m; }
void operator delete(void *, Heap::Base *) {}
+
+ void init() { _setInitialized(); }
+ void destroy() { _setDestroyed(); }
+#ifdef QML_CHECK_INIT_DESTROY_CALLS
+ enum { Uninitialized = 0, Initialized, Destroyed } _livenessStatus;
+ void _checkIsInitialized() {
+ if (_livenessStatus == Uninitialized)
+ fprintf(stderr, "ERROR: use of object '%s' before call to init() !!\n",
+ vtable()->className);
+ else if (_livenessStatus == Destroyed)
+ fprintf(stderr, "ERROR: use of object '%s' after call to destroy() !!\n",
+ vtable()->className);
+ Q_ASSERT(_livenessStatus = Initialized);
+ }
+ void _checkIsDestroyed() {
+ if (_livenessStatus == Initialized)
+ fprintf(stderr, "ERROR: object '%s' was never destroyed completely !!\n",
+ vtable()->className);
+ Q_ASSERT(_livenessStatus == Destroyed);
+ }
+ void _setInitialized() { Q_ASSERT(_livenessStatus == Uninitialized); _livenessStatus = Initialized; }
+ void _setDestroyed() {
+ if (_livenessStatus == Uninitialized)
+ fprintf(stderr, "ERROR: attempting to destroy an uninitialized object '%s' !!\n",
+ vtable()->className);
+ else if (_livenessStatus == Destroyed)
+ fprintf(stderr, "ERROR: attempting to destroy repeatedly object '%s' !!\n",
+ vtable()->className);
+ Q_ASSERT(_livenessStatus == Initialized);
+ _livenessStatus = Destroyed;
+ }
+#else
+ Q_ALWAYS_INLINE void _checkIsInitialized() {}
+ Q_ALWAYS_INLINE void _checkIsDestroyed() {}
+ Q_ALWAYS_INLINE void _setInitialized() {}
+ Q_ALWAYS_INLINE void _setDestroyed() {}
+#endif
};
+V4_ASSERT_IS_TRIVIAL(Base)
template <typename T>
struct Pointer {
- Pointer() {}
- Pointer(T *t) : ptr(t) {}
-
T *operator->() const { return ptr; }
operator T *() const { return ptr; }
@@ -136,9 +184,65 @@ struct Pointer {
T *ptr;
};
+V4_ASSERT_IS_TRIVIAL(Pointer<void>)
}
+#ifdef QT_NO_QOBJECT
+template <class T>
+struct QQmlQPointer {
+};
+#else
+template <class T>
+struct QQmlQPointer {
+ void init()
+ {
+ d = nullptr;
+ qObject = nullptr;
+ }
+
+ void init(T *o)
+ {
+ Q_ASSERT(d == nullptr);
+ Q_ASSERT(qObject == nullptr);
+ if (o) {
+ d = QtSharedPointer::ExternalRefCountData::getAndRef(o);
+ qObject = o;
+ }
+ }
+
+ void destroy()
+ {
+ if (d && !d->weakref.deref())
+ delete d;
+ d = nullptr;
+ qObject = nullptr;
+ }
+
+ T *data() const {
+ return d == nullptr || d->strongref.load() == 0 ? nullptr : qObject;
+ }
+ operator T*() const { return data(); }
+ inline T* operator->() const { return data(); }
+ QQmlQPointer &operator=(T *o)
+ {
+ if (d)
+ destroy();
+ init(o);
+ return *this;
+ }
+ bool isNull() const Q_DECL_NOTHROW
+ {
+ return d == nullptr || qObject == nullptr || d->strongref.load() == 0;
+ }
+
+private:
+ QtSharedPointer::ExternalRefCountData *d;
+ QObject *qObject;
+};
+V4_ASSERT_IS_TRIVIAL(QQmlQPointer<QObject>)
+#endif
+
}
QT_END_NAMESPACE
diff --git a/src/qml/memory/qv4mm.cpp b/src/qml/memory/qv4mm.cpp
index 9924d37fdc..6ef2380561 100644
--- a/src/qml/memory/qv4mm.cpp
+++ b/src/qml/memory/qv4mm.cpp
@@ -61,6 +61,10 @@
#include <valgrind/memcheck.h>
#endif
+#ifdef V4_USE_HEAPTRACK
+#include <heaptrack_api.h>
+#endif
+
#if OS(QNX)
#include <sys/storage.h> // __tls()
#endif
@@ -69,7 +73,7 @@
#include <pthread_np.h>
#endif
-#define MIN_UNMANAGED_HEAPSIZE_GC_LIMIT (std::size_t)128*1024
+#define MIN_UNMANAGED_HEAPSIZE_GC_LIMIT std::size_t(128 * 1024)
using namespace WTF;
@@ -80,9 +84,9 @@ static uint maxShiftValue()
static uint result = 0;
if (!result) {
result = 6;
- if (Q_UNLIKELY(qEnvironmentVariableIsSet("QV4_MM_MAXBLOCK_SHIFT"))) {
+ if (Q_UNLIKELY(qEnvironmentVariableIsSet(QV4_MM_MAXBLOCK_SHIFT))) {
bool ok;
- const uint overrideValue = qgetenv("QV4_MM_MAXBLOCK_SHIFT").toUInt(&ok);
+ const uint overrideValue = qgetenv(QV4_MM_MAXBLOCK_SHIFT).toUInt(&ok);
if (ok && overrideValue <= 11 && overrideValue > 0)
result = overrideValue;
}
@@ -95,9 +99,9 @@ static std::size_t maxChunkSizeValue()
static std::size_t result = 0;
if (!result) {
result = 32 * 1024;
- if (Q_UNLIKELY(qEnvironmentVariableIsSet("QV4_MM_MAX_CHUNK_SIZE"))) {
+ if (Q_UNLIKELY(qEnvironmentVariableIsSet(QV4_MM_MAX_CHUNK_SIZE))) {
bool ok;
- const std::size_t overrideValue = qgetenv("QV4_MM_MAX_CHUNK_SIZE").toUInt(&ok);
+ const std::size_t overrideValue = qgetenv(QV4_MM_MAX_CHUNK_SIZE).toUInt(&ok);
if (ok)
result = overrideValue;
}
@@ -109,29 +113,20 @@ using namespace QV4;
struct MemoryManager::Data
{
+ const size_t pageSize;
+
struct ChunkHeader {
Heap::Base freeItems;
ChunkHeader *nextNonFull;
char *itemStart;
char *itemEnd;
- int itemSize;
+ unsigned itemSize;
};
- bool gcBlocked;
- bool aggressiveGC;
- bool gcStats;
ExecutionEngine *engine;
- enum { MaxItemSize = 512 };
- ChunkHeader *nonFullChunks[MaxItemSize/16];
- uint nChunks[MaxItemSize/16];
- uint availableItems[MaxItemSize/16];
- uint allocCount[MaxItemSize/16];
- int totalItems;
- int totalAlloc;
- uint maxShift;
std::size_t maxChunkSize;
- QVector<PageAllocation> heapChunks;
+ std::vector<PageAllocation> heapChunks;
std::size_t unmanagedHeapSize; // the amount of bytes of heap that is not managed by the memory manager, but which is held onto by managed items.
std::size_t unmanagedHeapSizeGCLimit;
@@ -148,24 +143,39 @@ struct MemoryManager::Data
LargeItem *largeItems;
std::size_t totalLargeItemsAllocated;
+ enum { MaxItemSize = 512 };
+ ChunkHeader *nonFullChunks[MaxItemSize/16];
+ uint nChunks[MaxItemSize/16];
+ uint availableItems[MaxItemSize/16];
+ uint allocCount[MaxItemSize/16];
+ int totalItems;
+ int totalAlloc;
+ uint maxShift;
+
+ bool gcBlocked;
+ bool aggressiveGC;
+ bool gcStats;
+ bool unused; // suppress padding warning
+
// statistics:
#ifdef DETAILED_MM_STATS
QVector<unsigned> allocSizeCounters;
#endif // DETAILED_MM_STATS
Data()
- : gcBlocked(false)
- , aggressiveGC(!qEnvironmentVariableIsEmpty("QV4_MM_AGGRESSIVE_GC"))
- , gcStats(!qEnvironmentVariableIsEmpty("QV4_MM_STATS"))
+ : pageSize(WTF::pageSize())
, engine(0)
- , totalItems(0)
- , totalAlloc(0)
- , maxShift(maxShiftValue())
, maxChunkSize(maxChunkSizeValue())
, unmanagedHeapSize(0)
, unmanagedHeapSizeGCLimit(MIN_UNMANAGED_HEAPSIZE_GC_LIMIT)
, largeItems(0)
, totalLargeItemsAllocated(0)
+ , totalItems(0)
+ , totalAlloc(0)
+ , maxShift(maxShiftValue())
+ , gcBlocked(false)
+ , aggressiveGC(!qEnvironmentVariableIsEmpty("QV4_MM_AGGRESSIVE_GC"))
+ , gcStats(!qEnvironmentVariableIsEmpty(QV4_MM_STATS))
{
memset(nonFullChunks, 0, sizeof(nonFullChunks));
memset(nChunks, 0, sizeof(nChunks));
@@ -175,8 +185,8 @@ struct MemoryManager::Data
~Data()
{
- for (QVector<PageAllocation>::iterator i = heapChunks.begin(), ei = heapChunks.end(); i != ei; ++i) {
- Q_V4_PROFILE_DEALLOC(engine, 0, i->size(), Profiling::HeapPage);
+ for (std::vector<PageAllocation>::iterator i = heapChunks.begin(), ei = heapChunks.end(); i != ei; ++i) {
+ Q_V4_PROFILE_DEALLOC(engine, i->size(), Profiling::HeapPage);
i->deallocate();
}
}
@@ -199,7 +209,7 @@ bool sweepChunk(MemoryManager::Data::ChunkHeader *header, uint *itemsInUse, Exec
// qDebug("chunk @ %p, in use: %s, mark bit: %s",
// item, (m->inUse() ? "yes" : "no"), (m->isMarked() ? "true" : "false"));
- Q_ASSERT((qintptr) item % 16 == 0);
+ Q_ASSERT(qintptr(item) % 16 == 0);
if (m->isMarked()) {
Q_ASSERT(m->inUse());
@@ -219,15 +229,20 @@ bool sweepChunk(MemoryManager::Data::ChunkHeader *header, uint *itemsInUse, Exec
*unmanagedHeapSize -= heapBytes;
}
- if (m->vtable()->destroy)
+ if (m->vtable()->destroy) {
m->vtable()->destroy(m);
+ m->_checkIsDestroyed();
+ }
- memset(m, 0, header->itemSize);
+ memset(m, 0, sizeof(Heap::Base));
#ifdef V4_USE_VALGRIND
VALGRIND_DISABLE_ERROR_REPORTING;
VALGRIND_MEMPOOL_FREE(engine->memoryManager, m);
#endif
- Q_V4_PROFILE_DEALLOC(engine, m, header->itemSize, Profiling::SmallItem);
+#ifdef V4_USE_HEAPTRACK
+ heaptrack_report_free(m);
+#endif
+ Q_V4_PROFILE_DEALLOC(engine, header->itemSize, Profiling::SmallItem);
++(*itemsInUse);
}
// Relink all free blocks to rewrite references to any released chunk.
@@ -290,10 +305,11 @@ Heap::Base *MemoryManager::allocData(std::size_t size, std::size_t unmanagedSize
runGC();
// we use malloc for this
- MemoryManager::Data::LargeItem *item = static_cast<MemoryManager::Data::LargeItem *>(
- malloc(Q_V4_PROFILE_ALLOC(engine, size + sizeof(MemoryManager::Data::LargeItem),
- Profiling::LargeItem)));
- memset(item, 0, size + sizeof(MemoryManager::Data::LargeItem));
+ const size_t totalSize = size + sizeof(MemoryManager::Data::LargeItem);
+ Q_V4_PROFILE_ALLOC(engine, totalSize, Profiling::LargeItem);
+ MemoryManager::Data::LargeItem *item =
+ static_cast<MemoryManager::Data::LargeItem *>(malloc(totalSize));
+ memset(item, 0, totalSize);
item->next = m_d->largeItems;
item->size = size;
m_d->largeItems = item;
@@ -325,14 +341,14 @@ Heap::Base *MemoryManager::allocData(std::size_t size, std::size_t unmanagedSize
if (shift > m_d->maxShift)
shift = m_d->maxShift;
std::size_t allocSize = m_d->maxChunkSize*(size_t(1) << shift);
- allocSize = roundUpToMultipleOf(WTF::pageSize(), allocSize);
- PageAllocation allocation = PageAllocation::allocate(
- Q_V4_PROFILE_ALLOC(engine, allocSize, Profiling::HeapPage),
- OSAllocator::JSGCHeapPages);
- m_d->heapChunks.append(allocation);
+ allocSize = roundUpToMultipleOf(m_d->pageSize, allocSize);
+ Q_V4_PROFILE_ALLOC(engine, allocSize, Profiling::HeapPage);
+ PageAllocation allocation = PageAllocation::allocate(allocSize, OSAllocator::JSGCHeapPages);
+ m_d->heapChunks.push_back(allocation);
header = reinterpret_cast<Data::ChunkHeader *>(allocation.base());
- header->itemSize = int(size);
+ Q_ASSERT(size <= UINT_MAX);
+ header->itemSize = unsigned(size);
header->itemStart = reinterpret_cast<char *>(allocation.base()) + roundUpToMultipleOf(16, sizeof(Data::ChunkHeader));
header->itemEnd = reinterpret_cast<char *>(allocation.base()) + allocation.size() - header->itemSize;
@@ -348,19 +364,26 @@ Heap::Base *MemoryManager::allocData(std::size_t size, std::size_t unmanagedSize
}
last->setNextFree(0);
m = header->freeItems.nextFree();
- const size_t increase = (header->itemEnd - header->itemStart) / header->itemSize;
+ Q_ASSERT(header->itemEnd >= header->itemStart);
+ const size_t increase = quintptr(header->itemEnd - header->itemStart) / header->itemSize;
m_d->availableItems[pos] += uint(increase);
m_d->totalItems += int(increase);
#ifdef V4_USE_VALGRIND
VALGRIND_MAKE_MEM_NOACCESS(allocation.base(), allocSize);
VALGRIND_MEMPOOL_ALLOC(this, header, sizeof(Data::ChunkHeader));
#endif
+#ifdef V4_USE_HEAPTRACK
+ heaptrack_report_alloc(header, sizeof(Data::ChunkHeader));
+#endif
}
found:
#ifdef V4_USE_VALGRIND
VALGRIND_MEMPOOL_ALLOC(this, m, size);
#endif
+#ifdef V4_USE_HEAPTRACK
+ heaptrack_report_alloc(m, size);
+#endif
Q_V4_PROFILE_ALLOC(engine, size, Profiling::SmallItem);
++m_d->allocCount[pos];
@@ -375,6 +398,7 @@ static void drainMarkStack(QV4::ExecutionEngine *engine, Value *markBase)
{
while (engine->jsStackTop > markBase) {
Heap::Base *h = engine->popForGC();
+ Q_ASSERT(h); // at this point we should only have Heap::Base objects in this area on the stack. If not, weird things might happen.
Q_ASSERT (h->vtable()->markObjects);
h->vtable()->markObjects(h, engine);
}
@@ -431,7 +455,7 @@ void MemoryManager::sweep(bool lastSweep)
for (PersistentValueStorage::Iterator it = m_weakValues->begin(); it != m_weakValues->end(); ++it) {
if (!(*it).isManaged())
continue;
- Managed *m = (*it).as<Managed>();
+ Managed *m = (*it).managed();
if (m->markBit())
continue;
// we need to call destroyObject on qobjectwrappers now, so that they can emit the destroyed
@@ -477,29 +501,33 @@ void MemoryManager::sweep(bool lastSweep)
}
}
- bool *chunkIsEmpty = (bool *)alloca(m_d->heapChunks.size() * sizeof(bool));
+ bool *chunkIsEmpty = static_cast<bool *>(alloca(m_d->heapChunks.size() * sizeof(bool)));
uint itemsInUse[MemoryManager::Data::MaxItemSize/16];
memset(itemsInUse, 0, sizeof(itemsInUse));
memset(m_d->nonFullChunks, 0, sizeof(m_d->nonFullChunks));
- for (int i = 0; i < m_d->heapChunks.size(); ++i) {
+ for (size_t i = 0; i < m_d->heapChunks.size(); ++i) {
Data::ChunkHeader *header = reinterpret_cast<Data::ChunkHeader *>(m_d->heapChunks[i].base());
chunkIsEmpty[i] = sweepChunk(header, &itemsInUse[header->itemSize >> 4], engine, &m_d->unmanagedHeapSize);
}
- QVector<PageAllocation>::iterator chunkIter = m_d->heapChunks.begin();
- for (int i = 0; i < m_d->heapChunks.size(); ++i) {
+ std::vector<PageAllocation>::iterator chunkIter = m_d->heapChunks.begin();
+ for (size_t i = 0; i < m_d->heapChunks.size(); ++i) {
Q_ASSERT(chunkIter != m_d->heapChunks.end());
Data::ChunkHeader *header = reinterpret_cast<Data::ChunkHeader *>(chunkIter->base());
const size_t pos = header->itemSize >> 4;
- const size_t decrease = (header->itemEnd - header->itemStart) / header->itemSize;
+ Q_ASSERT(header->itemEnd >= header->itemStart);
+ const size_t decrease = quintptr(header->itemEnd - header->itemStart) / header->itemSize;
// Release that chunk if it could have been spared since the last GC run without any difference.
if (chunkIsEmpty[i] && m_d->availableItems[pos] - decrease >= itemsInUse[pos]) {
- Q_V4_PROFILE_DEALLOC(engine, 0, chunkIter->size(), Profiling::HeapPage);
+ Q_V4_PROFILE_DEALLOC(engine, chunkIter->size(), Profiling::HeapPage);
#ifdef V4_USE_VALGRIND
VALGRIND_MEMPOOL_FREE(this, header);
#endif
+#ifdef V4_USE_HEAPTRACK
+ heaptrack_report_free(header);
+#endif
--m_d->nChunks[pos];
m_d->availableItems[pos] -= uint(decrease);
m_d->totalItems -= int(decrease);
@@ -528,8 +556,8 @@ void MemoryManager::sweep(bool lastSweep)
m->vtable()->destroy(m);
*last = i->next;
- free(Q_V4_PROFILE_DEALLOC(engine, i, i->size + sizeof(Data::LargeItem),
- Profiling::LargeItem));
+ Q_V4_PROFILE_DEALLOC(engine, i->size + sizeof(Data::LargeItem), Profiling::LargeItem);
+ free(i);
i = *last;
}
@@ -574,7 +602,7 @@ void MemoryManager::runGC()
qint64 markTime = t.restart();
const size_t usedBefore = getUsedMem();
const size_t largeItemsBefore = getLargeItemsMem();
- int chunksBefore = m_d->heapChunks.size();
+ size_t chunksBefore = m_d->heapChunks.size();
sweep();
const size_t usedAfter = getUsedMem();
const size_t largeItemsAfter = getLargeItemsMem();
@@ -602,11 +630,11 @@ void MemoryManager::runGC()
size_t MemoryManager::getUsedMem() const
{
size_t usedMem = 0;
- for (QVector<PageAllocation>::const_iterator i = m_d->heapChunks.cbegin(), ei = m_d->heapChunks.cend(); i != ei; ++i) {
+ for (std::vector<PageAllocation>::const_iterator i = m_d->heapChunks.cbegin(), ei = m_d->heapChunks.cend(); i != ei; ++i) {
Data::ChunkHeader *header = reinterpret_cast<Data::ChunkHeader *>(i->base());
for (char *item = header->itemStart; item <= header->itemEnd; item += header->itemSize) {
Heap::Base *m = reinterpret_cast<Heap::Base *>(item);
- Q_ASSERT((qintptr) item % 16 == 0);
+ Q_ASSERT(qintptr(item) % 16 == 0);
if (m->inUse())
usedMem += header->itemSize;
}
@@ -617,7 +645,7 @@ size_t MemoryManager::getUsedMem() const
size_t MemoryManager::getAllocatedMem() const
{
size_t total = 0;
- for (int i = 0; i < m_d->heapChunks.size(); ++i)
+ for (size_t i = 0; i < m_d->heapChunks.size(); ++i)
total += m_d->heapChunks.at(i).size();
return total;
}
@@ -680,7 +708,7 @@ void MemoryManager::collectFromJSStack() const
Value *v = engine->jsStackBase;
Value *top = engine->jsStackTop;
while (v < top) {
- Managed *m = v->as<Managed>();
+ Managed *m = v->managed();
if (m && m->inUse())
// Skip pointers to already freed objects, they are bogus as well
m->mark(engine);
diff --git a/src/qml/memory/qv4mm_p.h b/src/qml/memory/qv4mm_p.h
index e169675f7d..dfa0d85dff 100644
--- a/src/qml/memory/qv4mm_p.h
+++ b/src/qml/memory/qv4mm_p.h
@@ -59,6 +59,10 @@
//#define DETAILED_MM_STATS
+#define QV4_MM_MAXBLOCK_SHIFT "QV4_MM_MAXBLOCK_SHIFT"
+#define QV4_MM_MAX_CHUNK_SIZE "QV4_MM_MAX_CHUNK_SIZE"
+#define QV4_MM_STATS "QV4_MM_STATS"
+
QT_BEGIN_NAMESPACE
namespace QV4 {
@@ -104,8 +108,10 @@ public:
template<typename ManagedType>
inline typename ManagedType::Data *allocManaged(std::size_t size, std::size_t unmanagedSize = 0)
{
+ V4_ASSERT_IS_TRIVIAL(typename ManagedType::Data)
size = align(size);
Heap::Base *o = allocData(size, unmanagedSize);
+ memset(o, 0, size);
o->setVtable(ManagedType::staticVTable());
return static_cast<typename ManagedType::Data *>(o);
}
@@ -140,7 +146,7 @@ public:
{
Scope scope(engine);
Scoped<ManagedType> t(scope, allocManaged<ManagedType>(sizeof(typename ManagedType::Data), unmanagedSize));
- (void)new (t->d()) typename ManagedType::Data(this, arg1);
+ t->d_unchecked()->init(this, arg1);
return t->d();
}
@@ -149,7 +155,7 @@ public:
{
Scope scope(engine);
Scoped<ObjectType> t(scope, allocateObject<ObjectType>(ic));
- (void)new (t->d()) typename ObjectType::Data();
+ t->d_unchecked()->init();
return t->d();
}
@@ -158,8 +164,8 @@ public:
{
Scope scope(engine);
Scoped<ObjectType> t(scope, allocateObject<ObjectType>(ic));
- t->d()->prototype = prototype->d();
- (void)new (t->d()) typename ObjectType::Data();
+ t->d_unchecked()->prototype = prototype->d();
+ t->d_unchecked()->init();
return t->d();
}
@@ -168,8 +174,8 @@ public:
{
Scope scope(engine);
Scoped<ObjectType> t(scope, allocateObject<ObjectType>(ic));
- t->d()->prototype = prototype->d();
- (void)new (t->d()) typename ObjectType::Data(arg1);
+ t->d_unchecked()->prototype = prototype->d();
+ t->d_unchecked()->init(arg1);
return t->d();
}
@@ -178,8 +184,8 @@ public:
{
Scope scope(engine);
Scoped<ObjectType> t(scope, allocateObject<ObjectType>(ic));
- t->d()->prototype = prototype->d();
- (void)new (t->d()) typename ObjectType::Data(arg1, arg2);
+ t->d_unchecked()->prototype = prototype->d();
+ t->d_unchecked()->init(arg1, arg2);
return t->d();
}
@@ -188,8 +194,8 @@ public:
{
Scope scope(engine);
Scoped<ObjectType> t(scope, allocateObject<ObjectType>(ic));
- t->d()->prototype = prototype->d();
- (void)new (t->d()) typename ObjectType::Data(arg1, arg2, arg3);
+ t->d_unchecked()->prototype = prototype->d();
+ t->d_unchecked()->init(arg1, arg2, arg3);
return t->d();
}
@@ -198,8 +204,8 @@ public:
{
Scope scope(engine);
Scoped<ObjectType> t(scope, allocateObject<ObjectType>(ic));
- t->d()->prototype = prototype->d();
- (void)new (t->d()) typename ObjectType::Data(arg1, arg2, arg3, arg4);
+ t->d_unchecked()->prototype = prototype->d();
+ t->d_unchecked()->init(arg1, arg2, arg3, arg4);
return t->d();
}
@@ -208,7 +214,7 @@ public:
{
Scope scope(engine);
Scoped<ObjectType> t(scope, allocateObject<ObjectType>());
- (void)new (t->d()) typename ObjectType::Data();
+ t->d_unchecked()->init();
return t->d();
}
@@ -217,7 +223,7 @@ public:
{
Scope scope(engine);
Scoped<ObjectType> t(scope, allocateObject<ObjectType>());
- (void)new (t->d()) typename ObjectType::Data(arg1);
+ t->d_unchecked()->init(arg1);
return t->d();
}
@@ -226,7 +232,7 @@ public:
{
Scope scope(engine);
Scoped<ObjectType> t(scope, allocateObject<ObjectType>());
- (void)new (t->d()) typename ObjectType::Data(arg1, arg2);
+ t->d_unchecked()->init(arg1, arg2);
return t->d();
}
@@ -235,7 +241,7 @@ public:
{
Scope scope(engine);
Scoped<ObjectType> t(scope, allocateObject<ObjectType>());
- (void)new (t->d()) typename ObjectType::Data(arg1, arg2, arg3);
+ t->d_unchecked()->init(arg1, arg2, arg3);
return t->d();
}
@@ -244,7 +250,7 @@ public:
{
Scope scope(engine);
Scoped<ObjectType> t(scope, allocateObject<ObjectType>());
- (void)new (t->d()) typename ObjectType::Data(arg1, arg2, arg3, arg4);
+ t->d_unchecked()->init(arg1, arg2, arg3, arg4);
return t->d();
}
@@ -254,7 +260,7 @@ public:
{
Scope scope(engine);
Scoped<ManagedType> t(scope, allocManaged<ManagedType>(sizeof(typename ManagedType::Data)));
- (void)new (t->d()) typename ManagedType::Data();
+ t->d_unchecked()->init();
return t->d();
}
@@ -263,7 +269,7 @@ public:
{
Scope scope(engine);
Scoped<ManagedType> t(scope, allocManaged<ManagedType>(sizeof(typename ManagedType::Data)));
- (void)new (t->d()) typename ManagedType::Data(arg1);
+ t->d_unchecked()->init(arg1);
return t->d();
}
@@ -272,7 +278,7 @@ public:
{
Scope scope(engine);
Scoped<ManagedType> t(scope, allocManaged<ManagedType>(sizeof(typename ManagedType::Data)));
- (void)new (t->d()) typename ManagedType::Data(arg1, arg2);
+ t->d_unchecked()->init(arg1, arg2);
return t->d();
}
@@ -281,7 +287,7 @@ public:
{
Scope scope(engine);
Scoped<ManagedType> t(scope, allocManaged<ManagedType>(sizeof(typename ManagedType::Data)));
- (void)new (t->d()) typename ManagedType::Data(arg1, arg2, arg3);
+ t->d_unchecked()->init(arg1, arg2, arg3);
return t->d();
}
@@ -290,7 +296,7 @@ public:
{
Scope scope(engine);
Scoped<ManagedType> t(scope, allocManaged<ManagedType>(sizeof(typename ManagedType::Data)));
- (void)new (t->d()) typename ManagedType::Data(arg1, arg2, arg3, arg4);
+ t->d_unchecked()->init(arg1, arg2, arg3, arg4);
return t->d();
}
@@ -299,7 +305,7 @@ public:
{
Scope scope(engine);
Scoped<ManagedType> t(scope, allocManaged<ManagedType>(sizeof(typename ManagedType::Data)));
- (void)new (t->d()) typename ManagedType::Data(arg1, arg2, arg3, arg4, arg5);
+ t->d_unchecked()->init(arg1, arg2, arg3, arg4, arg5);
return t->d();
}
diff --git a/src/qml/parser/qqmljsengine_p.cpp b/src/qml/parser/qqmljsengine_p.cpp
index 07064a4889..7a6d9c3826 100644
--- a/src/qml/parser/qqmljsengine_p.cpp
+++ b/src/qml/parser/qqmljsengine_p.cpp
@@ -114,7 +114,7 @@ double integerFromString(const char *buf, int size, int radix)
double integerFromString(const QString &str, int radix)
{
- QByteArray ba = str.trimmed().toLatin1();
+ QByteArray ba = QStringRef(&str).trimmed().toLatin1();
return integerFromString(ba.constData(), ba.size(), radix);
}
diff --git a/src/qml/qml.pro b/src/qml/qml.pro
index 7c9eef6df1..826a074701 100644
--- a/src/qml/qml.pro
+++ b/src/qml/qml.pro
@@ -1,11 +1,14 @@
TARGET = QtQml
-QT = core-private network
+QT = core-private
+
+qtConfig(qml-network): \
+ QT += network
DEFINES += QT_NO_URL_CAST_FROM_STRING QT_NO_INTEGER_EVENT_COORDINATES
win32-msvc*|win32-icc:QMAKE_LFLAGS += /BASE:0x66000000
win32-msvc*:DEFINES *= _CRT_SECURE_NO_WARNINGS
-win32:!wince*:!winrt:LIBS += -lshell32
+win32:!winrt:LIBS += -lshell32
solaris-cc*:QMAKE_CXXFLAGS_RELEASE -= -O2
# Ensure this gcc optimization is switched off for mips platforms to avoid trouble with JIT.
@@ -16,11 +19,6 @@ exists("qqml_enable_gcov") {
LIBS_PRIVATE += -lgcov
}
-greaterThan(QT_GCC_MAJOR_VERSION, 5) {
- # Our code is bad. Temporary workaround.
- QMAKE_CXXFLAGS += -fno-delete-null-pointer-checks -fno-lifetime-dse
-}
-
# QTBUG-55238, disable new optimizer for MSVC 2015/Update 3.
release:win32-msvc*:equals(QT_CL_MAJOR_VERSION, 19):equals(QT_CL_MINOR_VERSION, 00): \
greaterThan(QT_CL_PATCH_VERSION, 24212):QMAKE_CXXFLAGS += -d2SSAOptimizer-
diff --git a/src/qml/qml/ftw/ftw.pri b/src/qml/qml/ftw/ftw.pri
index a671cfa12d..87d80d04bc 100644
--- a/src/qml/qml/ftw/ftw.pri
+++ b/src/qml/qml/ftw/ftw.pri
@@ -8,12 +8,11 @@ HEADERS += \
$$PWD/qqmlthread_p.h \
$$PWD/qfinitestack_p.h \
$$PWD/qrecursionwatcher_p.h \
- $$PWD/qdeletewatcher_p.h \
$$PWD/qrecyclepool_p.h \
$$PWD/qflagpointer_p.h \
- $$PWD/qpointervaluepair_p.h \
$$PWD/qlazilyallocated_p.h \
$$PWD/qqmlnullablevalue_p.h \
+ $$PWD/qdeferredcleanup_p.h \
SOURCES += \
$$PWD/qintrusivelist.cpp \
@@ -22,4 +21,4 @@ SOURCES += \
# mirrors logic in $$QT_SOURCE_TREE/config.tests/unix/clock-gettime/clock-gettime.pri
# clock_gettime() is implemented in librt on these systems
-contains(QT_CONFIG, clock-gettime):linux-*|hpux-*|solaris-*:LIBS_PRIVATE *= -lrt
+qtConfig(clock-gettime):linux-*|hpux-*|solaris-*:LIBS_PRIVATE *= -lrt
diff --git a/src/qml/qml/ftw/qdeletewatcher_p.h b/src/qml/qml/ftw/qdeferredcleanup_p.h
index d4c0c6dfb2..6b59f04a77 100644
--- a/src/qml/qml/ftw/qdeletewatcher_p.h
+++ b/src/qml/qml/ftw/qdeferredcleanup_p.h
@@ -37,8 +37,8 @@
**
****************************************************************************/
-#ifndef QDELETEWATCHER_P_H
-#define QDELETEWATCHER_P_H
+#ifndef QDEFERREDCLEANUP_P_H
+#define QDEFERREDCLEANUP_P_H
//
// W A R N I N G
@@ -53,59 +53,22 @@
#include <QtCore/qglobal.h>
+#include <functional>
+
QT_BEGIN_NAMESPACE
-class QDeleteWatchable
+struct QDeferredCleanup
{
-public:
- inline QDeleteWatchable();
- inline ~QDeleteWatchable();
-private:
- friend class QDeleteWatcher;
- bool *_w;
-};
-
-class QDeleteWatcher {
-public:
- inline QDeleteWatcher(QDeleteWatchable *data);
- inline ~QDeleteWatcher();
- inline bool wasDeleted() const;
-private:
- void *operator new(size_t);
- bool *_w;
- bool _s;
- QDeleteWatchable *m_d;
+ std::function<void()> callback;
+ template <typename Callback>
+ QDeferredCleanup(Callback &&cb)
+ : callback(cb)
+ {}
+ ~QDeferredCleanup() { callback(); }
+ QDeferredCleanup(const QDeferredCleanup &) = delete;
+ QDeferredCleanup &operator=(const QDeferredCleanup &) = delete;
};
-QDeleteWatchable::QDeleteWatchable()
-: _w(0)
-{
-}
-
-QDeleteWatchable::~QDeleteWatchable()
-{
- if (_w) *_w = true;
-}
-
-QDeleteWatcher::QDeleteWatcher(QDeleteWatchable *data)
-: _s(false), m_d(data)
-{
- if (!m_d->_w)
- m_d->_w = &_s;
- _w = m_d->_w;
-}
-
-QDeleteWatcher::~QDeleteWatcher()
-{
- if (false == *_w && &_s == m_d->_w)
- m_d->_w = 0;
-}
-
-bool QDeleteWatcher::wasDeleted() const
-{
- return *_w;
-}
-
QT_END_NAMESPACE
-#endif // QDELETEWATCHER_P_H
+#endif // QDEFERREDCLEANUP_P_H
diff --git a/src/qml/qml/ftw/qhashedstring.cpp b/src/qml/qml/ftw/qhashedstring.cpp
index 37c1003748..117670dbfc 100644
--- a/src/qml/qml/ftw/qhashedstring.cpp
+++ b/src/qml/qml/ftw/qhashedstring.cpp
@@ -39,30 +39,7 @@
#include "qhashedstring_p.h"
-inline quint32 stringHash(const QChar* data, int length)
-{
- return QV4::String::createHashValue(data, length);
-}
-inline quint32 stringHash(const char *data, int length)
-{
- return QV4::String::createHashValue(data, length);
-}
-
-void QHashedString::computeHash() const
-{
- m_hash = stringHash(constData(), length());
-}
-
-void QHashedStringRef::computeHash() const
-{
- m_hash = stringHash(m_data, m_length);
-}
-
-void QHashedCStringRef::computeHash() const
-{
- m_hash = stringHash(m_data, m_length);
-}
/*
A QHash has initially around pow(2, MinNumBits) buckets. For
diff --git a/src/qml/qml/ftw/qhashedstring_p.h b/src/qml/qml/ftw/qhashedstring_p.h
index 6ff3e4a11b..9ee50ec931 100644
--- a/src/qml/qml/ftw/qhashedstring_p.h
+++ b/src/qml/qml/ftw/qhashedstring_p.h
@@ -67,7 +67,7 @@ QT_BEGIN_NAMESPACE
// #define QSTRINGHASH_LINK_DEBUG
class QHashedStringRef;
-class Q_AUTOTEST_EXPORT QHashedString : public QString
+class Q_QML_PRIVATE_EXPORT QHashedString : public QString
{
public:
inline QHashedString();
@@ -85,16 +85,20 @@ public:
static bool compare(const QChar *lhs, const QChar *rhs, int length);
static inline bool compare(const QChar *lhs, const char *rhs, int length);
static inline bool compare(const char *lhs, const char *rhs, int length);
+
+ static inline quint32 stringHash(const QChar* data, int length);
+ static inline quint32 stringHash(const char *data, int length);
+
private:
friend class QHashedStringRef;
friend class QStringHashNode;
- void computeHash() const;
+ inline void computeHash() const;
mutable quint32 m_hash;
};
class QHashedCStringRef;
-class Q_AUTOTEST_EXPORT QHashedStringRef
+class Q_QML_PRIVATE_EXPORT QHashedStringRef
{
public:
inline QHashedStringRef();
@@ -136,7 +140,7 @@ public:
private:
friend class QHashedString;
- void computeHash() const;
+ inline void computeHash() const;
const QChar *m_data;
int m_length;
@@ -163,7 +167,7 @@ public:
private:
friend class QHashedStringRef;
- void computeHash() const;
+ inline void computeHash() const;
const char *m_data;
int m_length;
@@ -1214,6 +1218,11 @@ bool QHashedStringRef::isLatin1() const
return true;
}
+void QHashedStringRef::computeHash() const
+{
+ m_hash = QHashedString::stringHash(m_data, m_length);
+}
+
bool QHashedStringRef::startsWithUpper() const
{
if (m_length < 1) return false;
@@ -1280,6 +1289,11 @@ void QHashedCStringRef::writeUtf16(quint16 *output) const
*output++ = *d++;
}
+void QHashedCStringRef::computeHash() const
+{
+ m_hash = QHashedString::stringHash(m_data, m_length);
+}
+
bool QHashedString::compare(const QChar *lhs, const char *rhs, int length)
{
Q_ASSERT(lhs && rhs);
@@ -1295,6 +1309,21 @@ bool QHashedString::compare(const char *lhs, const char *rhs, int length)
return 0 == ::memcmp(lhs, rhs, length);
}
+quint32 QHashedString::stringHash(const QChar *data, int length)
+{
+ return QV4::String::createHashValue(data, length, Q_NULLPTR);
+}
+
+quint32 QHashedString::stringHash(const char *data, int length)
+{
+ return QV4::String::createHashValue(data, length, Q_NULLPTR);
+}
+
+void QHashedString::computeHash() const
+{
+ m_hash = stringHash(constData(), length());
+}
+
QT_END_NAMESPACE
#endif // QHASHEDSTRING_P_H
diff --git a/src/qml/qml/ftw/qpointervaluepair_p.h b/src/qml/qml/ftw/qpointervaluepair_p.h
deleted file mode 100644
index 3d0644039f..0000000000
--- a/src/qml/qml/ftw/qpointervaluepair_p.h
+++ /dev/null
@@ -1,194 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml 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 QPOINTERVALUEPAIR_P_H
-#define QPOINTERVALUEPAIR_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/qglobal.h>
-#include <private/qflagpointer_p.h>
-
-QT_BEGIN_NAMESPACE
-
-// QPointerValuePair is intended to help reduce the memory consumption of a class.
-// In the common case, QPointerValuePair behaves like a pointer. In this mode, it
-// consumes the same memory as a regular pointer.
-// Additionally, QPointerValuePair can store an arbitrary value type in *addition*
-// to the pointer. In this case, it uses slightly more memory than the pointer and
-// value type combined.
-// Consequently, this class is most useful in cases where a pointer is always stored
-// and a value type is rarely stored.
-template<typename P, typename V>
-class QPointerValuePair {
-public:
- inline QPointerValuePair();
- inline QPointerValuePair(P *);
- inline ~QPointerValuePair();
-
- inline bool isNull() const;
-
- inline bool flag() const;
- inline void setFlag();
- inline void clearFlag();
- inline void setFlagValue(bool);
-
- inline QPointerValuePair<P, V> &operator=(P *);
-
- inline P *operator->() const;
- inline P *operator*() const;
-
- inline bool hasValue() const;
- inline V &value();
- inline const V *constValue() const;
-
-private:
- struct Value { P *pointer; V value; };
- QBiPointer<P, Value> d;
-};
-
-template<typename P, typename V>
-QPointerValuePair<P, V>::QPointerValuePair()
-{
-}
-
-template<typename P, typename V>
-QPointerValuePair<P, V>::QPointerValuePair(P *p)
-: d(p)
-{
-}
-
-template<typename P, typename V>
-QPointerValuePair<P, V>::~QPointerValuePair()
-{
- if (d.isT2()) delete d.asT2();
-}
-
-template<typename P, typename V>
-bool QPointerValuePair<P, V>::isNull() const
-{
- if (d.isT1()) return 0 == d.asT1();
- else return d.asT2()->pointer == 0;
-}
-
-template<typename P, typename V>
-bool QPointerValuePair<P, V>::flag() const
-{
- return d.flag();
-}
-
-template<typename P, typename V>
-void QPointerValuePair<P, V>::setFlag()
-{
- d.setFlag();
-}
-
-template<typename P, typename V>
-void QPointerValuePair<P, V>::clearFlag()
-{
- d.clearFlag();
-}
-
-template<typename P, typename V>
-void QPointerValuePair<P, V>::setFlagValue(bool v)
-{
- d.setFlagValue(v);
-}
-
-template<typename P, typename V>
-QPointerValuePair<P, V> &QPointerValuePair<P, V>::operator=(P *o)
-{
- if (d.isT1()) d = o;
- else d.asT2()->pointer = o;
- return *this;
-}
-
-template<typename P, typename V>
-P *QPointerValuePair<P, V>::operator->() const
-{
- if (d.isT1()) return d.asT1();
- else return d.asT2()->pointer;
-}
-
-template<typename P, typename V>
-P *QPointerValuePair<P, V>::operator*() const
-{
- if (d.isT1()) return d.asT1();
- else return d.asT2()->pointer;
-}
-
-template<typename P, typename V>
-bool QPointerValuePair<P, V>::hasValue() const
-{
- return d.isT2();
-}
-
-template<typename P, typename V>
-V &QPointerValuePair<P, V>::value()
-{
- if (d.isT1()) {
- P *p = d.asT1();
- Value *value = new Value;
- value->pointer = p;
- d = value;
- }
-
- return d.asT2()->value;
-}
-
-// Will return null if hasValue() == false
-template<typename P, typename V>
-const V *QPointerValuePair<P, V>::constValue() const
-{
- if (d.isT2()) return &d.asT2()->value;
- else return 0;
-}
-
-QT_END_NAMESPACE
-
-#endif // QPOINTERVALUEPAIR_P_H
diff --git a/src/qml/qml/qml.pri b/src/qml/qml/qml.pri
index 4d84cc82ae..8d8da3742d 100644
--- a/src/qml/qml/qml.pri
+++ b/src/qml/qml/qml.pri
@@ -12,7 +12,6 @@ SOURCES += \
$$PWD/qqmlpropertyvalueinterceptor.cpp \
$$PWD/qqmlproxymetaobject.cpp \
$$PWD/qqmlvme.cpp \
- $$PWD/qqmlcompileddata.cpp \
$$PWD/qqmlboundsignal.cpp \
$$PWD/qqmlmetatype.cpp \
$$PWD/qqmlstringconverters.cpp \
@@ -21,7 +20,6 @@ SOURCES += \
$$PWD/qqmlinfo.cpp \
$$PWD/qqmlerror.cpp \
$$PWD/qqmlvaluetype.cpp \
- $$PWD/qqmlaccessors.cpp \
$$PWD/qqmlxmlhttprequest.cpp \
$$PWD/qqmlcleanup.cpp \
$$PWD/qqmlpropertycache.cpp \
@@ -39,7 +37,6 @@ SOURCES += \
$$PWD/qqmlvaluetypeproxybinding.cpp \
$$PWD/qqmlglobal.cpp \
$$PWD/qqmlfile.cpp \
- $$PWD/qqmlmemoryprofiler.cpp \
$$PWD/qqmlplatform.cpp \
$$PWD/qqmlbinding.cpp \
$$PWD/qqmlabstracturlinterceptor.cpp \
@@ -50,7 +47,9 @@ SOURCES += \
$$PWD/qqmltypewrapper.cpp \
$$PWD/qqmlfileselector.cpp \
$$PWD/qqmlobjectcreator.cpp \
- $$PWD/qqmldirparser.cpp
+ $$PWD/qqmldirparser.cpp \
+ $$PWD/qqmldelayedcallqueue.cpp \
+ $$PWD/qqmlloggingcategory.cpp
HEADERS += \
$$PWD/qqmlglobal_p.h \
@@ -70,7 +69,6 @@ HEADERS += \
$$PWD/qqmlparserstatus.h \
$$PWD/qqmlproxymetaobject_p.h \
$$PWD/qqmlvme_p.h \
- $$PWD/qqmlcompiler_p.h \
$$PWD/qqmlengine_p.h \
$$PWD/qqmlexpression_p.h \
$$PWD/qqmlprivate.h \
@@ -88,10 +86,10 @@ HEADERS += \
$$PWD/qqmldata_p.h \
$$PWD/qqmlerror.h \
$$PWD/qqmlvaluetype_p.h \
- $$PWD/qqmlaccessors_p.h \
$$PWD/qqmlxmlhttprequest_p.h \
$$PWD/qqmlcleanup_p.h \
$$PWD/qqmlpropertycache_p.h \
+ $$PWD/qqmlpropertyindex_p.h \
$$PWD/qqmlnotifier_p.h \
$$PWD/qqmltypenotavailable_p.h \
$$PWD/qqmltypenamecache_p.h \
@@ -108,7 +106,6 @@ HEADERS += \
$$PWD/qqmlabstractbinding_p.h \
$$PWD/qqmlvaluetypeproxybinding_p.h \
$$PWD/qqmlfile.h \
- $$PWD/qqmlmemoryprofiler_p.h \
$$PWD/qqmlplatform_p.h \
$$PWD/qqmlbinding_p.h \
$$PWD/qqmlextensionplugin_p.h \
@@ -122,7 +119,9 @@ HEADERS += \
$$PWD/qqmlfileselector_p.h \
$$PWD/qqmlfileselector.h \
$$PWD/qqmlobjectcreator_p.h \
- $$PWD/qqmldirparser_p.h
+ $$PWD/qqmldirparser_p.h \
+ $$PWD/qqmldelayedcallqueue_p.h \
+ $$PWD/qqmlloggingcategory_p.h
include(ftw/ftw.pri)
include(v8/v8.pri)
diff --git a/src/qml/qml/qqml.h b/src/qml/qml/qqml.h
index 8fb710ad9d..39764b8001 100644
--- a/src/qml/qml/qqml.h
+++ b/src/qml/qml/qqml.h
@@ -238,6 +238,9 @@ int qmlRegisterExtendedUncreatableType(const char *uri, int versionMajor, int ve
return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type);
}
+
+Q_QML_EXPORT int qmlRegisterUncreatableMetaObject(const QMetaObject &staticMetaObject, const char *uri, int versionMajor, int versionMinor, const char *qmlName, const QString& reason);
+
template<typename T>
int qmlRegisterType(const char *uri, int versionMajor, int versionMinor, const char *qmlName)
{
diff --git a/src/qml/qml/qqmlabstractbinding.cpp b/src/qml/qml/qqmlabstractbinding.cpp
index abaf95fa11..39d609454f 100644
--- a/src/qml/qml/qqmlabstractbinding.cpp
+++ b/src/qml/qml/qqmlabstractbinding.cpp
@@ -78,24 +78,26 @@ void QQmlAbstractBinding::addToObject()
QQmlData *data = QQmlData::get(obj, true);
- int coreIndex;
- if (QQmlPropertyData::decodeValueTypePropertyIndex(targetPropertyIndex(), &coreIndex) != -1) {
+ int coreIndex = targetPropertyIndex().coreIndex();
+ if (targetPropertyIndex().hasValueTypeIndex()) {
// Value type
// Find the value type proxy (if there is one)
QQmlValueTypeProxyBinding *proxy = 0;
if (data->hasBindingBit(coreIndex)) {
QQmlAbstractBinding *b = data->bindings;
- while (b && b->targetPropertyIndex() != coreIndex)
+ while (b && (b->targetPropertyIndex().coreIndex() != coreIndex ||
+ b->targetPropertyIndex().hasValueTypeIndex()))
b = b->nextBinding();
Q_ASSERT(b && b->isValueTypeProxy());
proxy = static_cast<QQmlValueTypeProxyBinding *>(b);
}
if (!proxy) {
- proxy = new QQmlValueTypeProxyBinding(obj, coreIndex);
+ proxy = new QQmlValueTypeProxyBinding(obj, QQmlPropertyIndex(coreIndex));
- Q_ASSERT(proxy->targetPropertyIndex() == coreIndex);
+ Q_ASSERT(proxy->targetPropertyIndex().coreIndex() == coreIndex);
+ Q_ASSERT(!proxy->targetPropertyIndex().hasValueTypeIndex());
Q_ASSERT(proxy->targetObject() == obj);
proxy->addToObject();
@@ -137,12 +139,13 @@ void QQmlAbstractBinding::removeFromObject()
next = nextBinding();
setNextBinding(0);
- int coreIndex;
- if (QQmlPropertyData::decodeValueTypePropertyIndex(targetPropertyIndex(), &coreIndex) != -1) {
+ int coreIndex = targetPropertyIndex().coreIndex();
+ if (targetPropertyIndex().hasValueTypeIndex()) {
// Find the value type binding
QQmlAbstractBinding *vtbinding = data->bindings;
- while (vtbinding->targetPropertyIndex() != coreIndex) {
+ while (vtbinding && (vtbinding->targetPropertyIndex().coreIndex() != coreIndex ||
+ vtbinding->targetPropertyIndex().hasValueTypeIndex())) {
vtbinding = vtbinding->nextBinding();
Q_ASSERT(vtbinding);
}
diff --git a/src/qml/qml/qqmlabstractbinding_p.h b/src/qml/qml/qqmlabstractbinding_p.h
index 674178153a..bea2d253e4 100644
--- a/src/qml/qml/qqmlabstractbinding_p.h
+++ b/src/qml/qml/qqmlabstractbinding_p.h
@@ -55,7 +55,6 @@
#include <QtCore/qshareddata.h>
#include <private/qtqmlglobal_p.h>
#include <private/qqmlproperty_p.h>
-#include <private/qpointervaluepair_p.h>
QT_BEGIN_NAMESPACE
@@ -77,13 +76,13 @@ public:
// Should return the encoded property index for the binding. Should return this value
// even if the binding is not enabled or added to an object.
// Encoding is: coreIndex | (valueTypeIndex << 16)
- int targetPropertyIndex() const { return m_targetIndex; }
+ QQmlPropertyIndex targetPropertyIndex() const { return m_targetIndex; }
// Should return the object for the binding. Should return this object even if the
// binding is not enabled or added to the object.
QObject *targetObject() const { return m_target.data(); }
- virtual void setEnabled(bool e, QQmlPropertyPrivate::WriteFlags f = QQmlPropertyPrivate::DontRemoveBinding) = 0;
+ virtual void setEnabled(bool e, QQmlPropertyData::WriteFlags f = QQmlPropertyData::DontRemoveBinding) = 0;
void addToObject();
void removeFromObject();
@@ -92,6 +91,8 @@ public:
inline QQmlAbstractBinding *nextBinding() const;
+ inline bool canUseAccessor() const
+ { return m_nextBinding.flag2(); }
struct RefCount {
RefCount() : refCount(0) {}
@@ -112,9 +113,16 @@ protected:
inline void setNextBinding(QQmlAbstractBinding *);
- int m_targetIndex;
+ QQmlPropertyIndex m_targetIndex;
+
+ // Pointer is the target object to which the binding binds
+ // flag1 is the updating flag
+ // flag2 is the enabled flag
QFlagPointer<QObject> m_target;
+
// Pointer to the next binding in the linked list of bindings.
+ // flag1 is used for addedToObject
+ // flag2 indicates if an accessor is can be used (i.e. there is no interceptor on the target)
QFlagPointer<QQmlAbstractBinding> m_nextBinding;
};
diff --git a/src/qml/qml/qqmlaccessors_p.h b/src/qml/qml/qqmlaccessors_p.h
deleted file mode 100644
index 55562a5307..0000000000
--- a/src/qml/qml/qqmlaccessors_p.h
+++ /dev/null
@@ -1,177 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml 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 QQMLACCESSORS_P_H
-#define QQMLACCESSORS_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/qtqmlglobal_p.h>
-#include <QtCore/qbytearray.h>
-#include <QtCore/qvector.h>
-#include <QtCore/qhash.h>
-#include <QtCore/QReadWriteLock>
-
-#if defined(Q_OS_QNX) || defined(Q_OS_LINUX)
-#include <stdint.h>
-#endif
-
-QT_BEGIN_NAMESPACE
-
-class QObject;
-struct QMetaObject;
-class QQmlNotifier;
-
-// QML "accessor properties" allow V4 and V8 to bypass Qt's meta system to read and, more
-// importantly, subscribe to properties directly. Any property that is primarily read
-// from bindings is a candidate for inclusion as an accessor property.
-//
-// To define accessor properties, use the QML_DECLARE_PROPERTIES() and QML_DEFINE_PROPERTIES()
-// macros. The QML_DECLARE_PROPERTIES() macro is used to specify the properties, and the
-// QML_DEFINE_PROPERTIES() macro to register the properties with the
-// QQmlAccessorProperties singleton.
-//
-// A class with accessor properties must also add the Q_CLASSINFO("qt_HasQmlAccessors", "true")
-// tag to its declaration. This is essential for QML to maintain internal consistency,
-// and forgetting to do so will probably cause your application to qFatal() with a
-// helpful reminder of this requirement.
-//
-// It is important that QML_DEFINE_PROPERTIES() has been called before QML ever sees
-// the type with the accessor properties. As QML_DEFINE_PROPERTIES() is idempotent, it is
-// recommended to call it in the type's constructor as well as when the type is registered
-// as a QML element (if it ever is). QML_DEFINE_PROPERTIES() is a very cheap operation
-// if registration has already occurred.
-
-#define QML_DECLARE_PROPERTIES(type) \
- static volatile bool qqml_accessor_properties_isregistered_ ## type = false; \
- static QQmlAccessorProperties::Property qqml_accessor_properties_ ## type[] =
-
-#define QML_DEFINE_PROPERTIES(type) \
- do { \
- if (!qqml_accessor_properties_isregistered_ ## type) { \
- int count = sizeof(qqml_accessor_properties_ ## type) / \
- sizeof(QQmlAccessorProperties::Property); \
- QQmlAccessorProperties::registerProperties(&type::staticMetaObject, count, \
- qqml_accessor_properties_ ## type);\
- qqml_accessor_properties_isregistered_ ## type = true; \
- } \
- } while (false);
-
-#define QML_PRIVATE_ACCESSOR(clazz, cpptype, name, variable) \
- static void clazz ## _ ## name ## Read(QObject *o, void *rv) \
- { \
- clazz ## Private *d = clazz ## Private::get(static_cast<clazz *>(o)); \
- *static_cast<cpptype *>(rv) = d->variable; \
- }
-
-#define QML_PROPERTY_NAME(name) #name, sizeof #name - 1
-
-class QQmlAccessors
-{
-public:
- void (*read)(QObject *object, void *output);
- void (*notifier)(QObject *object, QQmlNotifier **notifier);
-};
-
-namespace QQmlAccessorProperties {
- struct Property {
- const char *name;
- unsigned int nameLength;
- qintptr data;
- QQmlAccessors *accessors;
- };
-
- struct Properties {
- inline Properties();
- Properties(Property *, int);
-
- bool operator==(const Properties &o) const {
- return count == o.count && properties == o.properties;
- }
-
- inline Property *property(const char *name);
-
- int count;
- Property *properties;
- quint32 nameMask;
- };
-
- Properties properties(const QMetaObject *);
- void Q_QML_PRIVATE_EXPORT registerProperties(const QMetaObject *, int, Property *);
-};
-
-QQmlAccessorProperties::Property *
-QQmlAccessorProperties::Properties::property(const char *name)
-{
- if (count == 0)
- return 0;
-
- const unsigned int length = (unsigned int)strlen(name);
-
- Q_ASSERT(length);
-
- if (nameMask & (1 << qMin(31U, length - 1))) {
-
- for (int ii = 0; ii < count; ++ii) {
- if (properties[ii].nameLength == length && 0 == qstrcmp(name, properties[ii].name))
- return &properties[ii];
- }
-
- }
-
- return 0;
-}
-
-QQmlAccessorProperties::Properties::Properties()
-: count(0), properties(0), nameMask(0)
-{
-}
-
-QT_END_NAMESPACE
-
-#endif // QQMLACCESSORS_P_H
diff --git a/src/qml/qml/qqmlapplicationengine.cpp b/src/qml/qml/qqmlapplicationengine.cpp
index 57cdd3f47f..fef2da753b 100644
--- a/src/qml/qml/qqmlapplicationengine.cpp
+++ b/src/qml/qml/qqmlapplicationengine.cpp
@@ -69,6 +69,7 @@ void QQmlApplicationEnginePrivate::init()
q->connect(&statusMapper, SIGNAL(mapped(QObject*)),
q, SLOT(_q_finishLoad(QObject*)));
q->connect(q, SIGNAL(quit()), QCoreApplication::instance(), SLOT(quit()));
+ q->connect(q, &QQmlApplicationEngine::exit, QCoreApplication::instance(), &QCoreApplication::exit);
#ifndef QT_NO_TRANSLATION
QTranslator* qtTranslator = new QTranslator;
if (qtTranslator->load(QLatin1String("qt_") + QLocale::system().name(), QLibraryInfo::location(QLibraryInfo::TranslationsPath)))
@@ -134,7 +135,7 @@ void QQmlApplicationEnginePrivate::_q_finishLoad(QObject *o)
break;
case QQmlComponent::Ready:
objects << c->create();
- q->objectCreated(objects.last(), c->url());
+ q->objectCreated(objects.constLast(), c->url());
break;
case QQmlComponent::Loading:
case QQmlComponent::Null:
@@ -211,11 +212,8 @@ QQmlApplicationEngine::QQmlApplicationEngine(QObject *parent)
This is provided as a convenience, and is the same as using the empty constructor and calling load afterwards.
*/
QQmlApplicationEngine::QQmlApplicationEngine(const QUrl &url, QObject *parent)
- : QQmlEngine(*(new QQmlApplicationEnginePrivate(this)), parent)
+ : QQmlApplicationEngine(parent)
{
- Q_D(QQmlApplicationEngine);
- d->init();
- QJSEnginePrivate::addToDebugServer(this);
load(url);
}
@@ -228,12 +226,8 @@ QQmlApplicationEngine::QQmlApplicationEngine(const QUrl &url, QObject *parent)
This is provided as a convenience, and is the same as using the empty constructor and calling load afterwards.
*/
QQmlApplicationEngine::QQmlApplicationEngine(const QString &filePath, QObject *parent)
- : QQmlEngine(*(new QQmlApplicationEnginePrivate(this)), parent)
+ : QQmlApplicationEngine(QUrl::fromLocalFile(filePath), parent)
{
- Q_D(QQmlApplicationEngine);
- d->init();
- QJSEnginePrivate::addToDebugServer(this);
- load(QUrl::fromLocalFile(filePath));
}
/*!
diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp
index 1249e1b6c8..203bfec838 100644
--- a/src/qml/qml/qqmlbinding.cpp
+++ b/src/qml/qml/qqmlbinding.cpp
@@ -42,7 +42,6 @@
#include "qqml.h"
#include "qqmlcontext.h"
#include "qqmlinfo.h"
-#include "qqmlcompiler_p.h"
#include "qqmldata_p.h"
#include <private/qqmlprofiler_p.h>
#include <private/qqmlexpression_p.h>
@@ -51,33 +50,36 @@
#include <private/qqmlbuiltinfunctions_p.h>
#include <private/qqmlvmemetaobject_p.h>
#include <private/qqmlvaluetypewrapper_p.h>
+#include <private/qv4qobjectwrapper_p.h>
+#include <private/qv4variantobject_p.h>
#include <QVariant>
#include <QtCore/qdebug.h>
QT_BEGIN_NAMESPACE
-QQmlBinding::QQmlBinding(const QString &str, QObject *obj, QQmlContext *ctxt)
- : QQmlJavaScriptExpression(),
- QQmlAbstractBinding()
+QQmlBinding *QQmlBinding::create(const QQmlPropertyData *property, const QString &str, QObject *obj, QQmlContext *ctxt)
{
- setNotifyOnValueChanged(true);
- QQmlJavaScriptExpression::setContext(QQmlContextData::get(ctxt));
- setScopeObject(obj);
+ QQmlBinding *b = newBinding(QQmlEnginePrivate::get(ctxt), property);
+ b->setNotifyOnValueChanged(true);
+ b->QQmlJavaScriptExpression::setContext(QQmlContextData::get(ctxt));
+ b->setScopeObject(obj);
- createQmlBinding(context(), obj, str, QString(), 0);
+ b->createQmlBinding(b->context(), obj, str, QString(), 0);
+
+ return b;
}
-QQmlBinding::QQmlBinding(const QQmlScriptString &script, QObject *obj, QQmlContext *ctxt)
- : QQmlJavaScriptExpression(),
- QQmlAbstractBinding()
+QQmlBinding *QQmlBinding::create(const QQmlPropertyData *property, const QQmlScriptString &script, QObject *obj, QQmlContext *ctxt)
{
+ QQmlBinding *b = newBinding(QQmlEnginePrivate::get(ctxt), property);
+
if (ctxt && !ctxt->isValid())
- return;
+ return b;
const QQmlScriptStringPrivate *scriptPrivate = script.d.data();
if (!ctxt && (!scriptPrivate->context || !scriptPrivate->context->isValid()))
- return;
+ return b;
QString url;
QV4::Function *runtimeFunction = 0;
@@ -90,53 +92,61 @@ QQmlBinding::QQmlBinding(const QQmlScriptString &script, QObject *obj, QQmlConte
runtimeFunction = ctxtdata->typeCompilationUnit->runtimeFunctions.at(scriptPrivate->bindingId);
}
- setNotifyOnValueChanged(true);
- QQmlJavaScriptExpression::setContext(QQmlContextData::get(ctxt ? ctxt : scriptPrivate->context));
- setScopeObject(obj ? obj : scriptPrivate->scope);
+ b->setNotifyOnValueChanged(true);
+ b->QQmlJavaScriptExpression::setContext(QQmlContextData::get(ctxt ? ctxt : scriptPrivate->context));
+ b->setScopeObject(obj ? obj : scriptPrivate->scope);
- QV4::ExecutionEngine *v4 = QQmlEnginePrivate::get(context()->engine)->v4engine();
+ QV4::ExecutionEngine *v4 = QQmlEnginePrivate::get(b->context()->engine)->v4engine();
if (runtimeFunction) {
- m_function.set(v4, QV4::FunctionObject::createQmlFunction(ctxtdata, scopeObject(), runtimeFunction));
+ b->m_function.set(v4, QV4::FunctionObject::createQmlFunction(ctxtdata, b->scopeObject(), runtimeFunction));
} else {
QString code = scriptPrivate->script;
- createQmlBinding(context(), scopeObject(), code, url, scriptPrivate->lineNumber);
+ b->createQmlBinding(b->context(), b->scopeObject(), code, url, scriptPrivate->lineNumber);
}
+
+ return b;
}
-QQmlBinding::QQmlBinding(const QString &str, QObject *obj, QQmlContextData *ctxt)
- : QQmlJavaScriptExpression(),
- QQmlAbstractBinding()
+QQmlBinding *QQmlBinding::create(const QQmlPropertyData *property, const QString &str, QObject *obj, QQmlContextData *ctxt)
{
- setNotifyOnValueChanged(true);
- QQmlJavaScriptExpression::setContext(ctxt);
- setScopeObject(obj);
+ QQmlBinding *b = newBinding(QQmlEnginePrivate::get(ctxt), property);
+
+ b->setNotifyOnValueChanged(true);
+ b->QQmlJavaScriptExpression::setContext(ctxt);
+ b->setScopeObject(obj);
- createQmlBinding(ctxt, obj, str, QString(), 0);
+ b->createQmlBinding(ctxt, obj, str, QString(), 0);
+
+ return b;
}
-QQmlBinding::QQmlBinding(const QString &str, QObject *obj,
- QQmlContextData *ctxt,
- const QString &url, quint16 lineNumber, quint16 columnNumber)
- : QQmlJavaScriptExpression(),
- QQmlAbstractBinding()
+QQmlBinding *QQmlBinding::create(const QQmlPropertyData *property, const QString &str, QObject *obj,
+ QQmlContextData *ctxt, const QString &url, quint16 lineNumber,
+ quint16 columnNumber)
{
+ QQmlBinding *b = newBinding(QQmlEnginePrivate::get(ctxt), property);
+
Q_UNUSED(columnNumber);
- setNotifyOnValueChanged(true);
- QQmlJavaScriptExpression::setContext(ctxt);
- setScopeObject(obj);
+ b->setNotifyOnValueChanged(true);
+ b->QQmlJavaScriptExpression::setContext(ctxt);
+ b->setScopeObject(obj);
- createQmlBinding(ctxt, obj, str, url, lineNumber);
+ b->createQmlBinding(ctxt, obj, str, url, lineNumber);
+
+ return b;
}
-QQmlBinding::QQmlBinding(const QV4::Value &functionPtr, QObject *obj, QQmlContextData *ctxt)
- : QQmlJavaScriptExpression(),
- QQmlAbstractBinding()
+QQmlBinding *QQmlBinding::create(const QQmlPropertyData *property, const QV4::Value &functionPtr, QObject *obj, QQmlContextData *ctxt)
{
- setNotifyOnValueChanged(true);
- QQmlJavaScriptExpression::setContext(ctxt);
- setScopeObject(obj);
+ QQmlBinding *b = newBinding(QQmlEnginePrivate::get(ctxt), property);
+
+ b->setNotifyOnValueChanged(true);
+ b->QQmlJavaScriptExpression::setContext(ctxt);
+ b->setScopeObject(obj);
- m_function.set(functionPtr.as<QV4::Object>()->engine(), functionPtr);
+ b->m_function.set(functionPtr.as<QV4::Object>()->engine(), functionPtr);
+
+ return b;
}
QQmlBinding::~QQmlBinding()
@@ -148,7 +158,7 @@ void QQmlBinding::setNotifyOnValueChanged(bool v)
QQmlJavaScriptExpression::setNotifyOnValueChanged(v);
}
-void QQmlBinding::update(QQmlPropertyPrivate::WriteFlags flags)
+void QQmlBinding::update(QQmlPropertyData::WriteFlags flags)
{
if (!enabledFlag() || !context() || !context()->isValid())
return;
@@ -157,44 +167,75 @@ void QQmlBinding::update(QQmlPropertyPrivate::WriteFlags flags)
if (QQmlData::wasDeleted(targetObject()))
return;
- QQmlEnginePrivate *ep = QQmlEnginePrivate::get(context()->engine);
- QV4::Scope scope(ep->v4engine());
- QV4::ScopedFunctionObject f(scope, m_function.value());
- Q_ASSERT(f);
-
- if (updatingFlag()) {
- QQmlProperty p = QQmlPropertyPrivate::restore(targetObject(), getPropertyData(), 0);
+ // Check for a binding update loop
+ if (Q_UNLIKELY(updatingFlag())) {
+ QQmlPropertyData *d = nullptr;
+ QQmlPropertyData vtd;
+ getPropertyData(&d, &vtd);
+ Q_ASSERT(d);
+ QQmlProperty p = QQmlPropertyPrivate::restore(targetObject(), *d, &vtd, 0);
QQmlAbstractBinding::printBindingLoopError(p);
return;
}
-
- QQmlBindingProfiler prof(ep->profiler, this, f);
setUpdatingFlag(true);
- QQmlJavaScriptExpression::DeleteWatcher watcher(this);
+ DeleteWatcher watcher(this);
+
+ QQmlEnginePrivate *ep = QQmlEnginePrivate::get(context()->engine);
+ QV4::Scope scope(ep->v4engine());
+ QV4::ScopedFunctionObject f(scope, m_function.value());
+ Q_ASSERT(f);
- QQmlPropertyData pd = getPropertyData();
+ if (canUseAccessor())
+ flags.setFlag(QQmlPropertyData::BypassInterceptor);
- if (pd.propType == qMetaTypeId<QQmlBinding *>()) {
+ QQmlBindingProfiler prof(ep->profiler, this, f);
+ doUpdate(watcher, flags, scope, f);
- int idx = pd.coreIndex;
- Q_ASSERT(idx != -1);
+ if (!watcher.wasDeleted())
+ setUpdatingFlag(false);
+}
- QQmlBinding *t = this;
- int status = -1;
- void *a[] = { &t, 0, &status, &flags };
- QMetaObject::metacall(*m_target, QMetaObject::WriteProperty, idx, a);
+// QQmlBindingBinding is for target properties which are of type "binding" (instead of, say, int or
+// double). The reason for being is that GenericBinding::fastWrite needs a compile-time constant
+// expression for the switch for the compiler to generate the optimal code, but
+// qMetaTypeId<QQmlBinding *>() needs to be used for the ID. So QQmlBinding::newBinding uses that
+// to instantiate this class.
+class QQmlBindingBinding: public QQmlBinding
+{
+protected:
+ void doUpdate(const DeleteWatcher &,
+ QQmlPropertyData::WriteFlags flags, QV4::Scope &,
+ const QV4::ScopedFunctionObject &) Q_DECL_OVERRIDE Q_DECL_FINAL
+ {
+ Q_ASSERT(!m_targetIndex.hasValueTypeIndex());
+ QQmlPropertyData *pd = nullptr;
+ getPropertyData(&pd, nullptr);
+ QQmlBinding *thisPtr = this;
+ pd->writeProperty(*m_target, &thisPtr, flags);
+ }
+};
- } else {
+// For any target that's not a binding, we have a common doUpdate. However, depending on the type
+// of the target property, there is a specialized write method.
+class QQmlNonbindingBinding: public QQmlBinding
+{
+protected:
+ void doUpdate(const DeleteWatcher &watcher,
+ QQmlPropertyData::WriteFlags flags, QV4::Scope &scope,
+ const QV4::ScopedFunctionObject &f) Q_DECL_OVERRIDE Q_DECL_FINAL
+ {
+ auto ep = QQmlEnginePrivate::get(scope.engine);
ep->referenceScarceResources();
bool isUndefined = false;
- QV4::ScopedValue result(scope, QQmlJavaScriptExpression::evaluate(&isUndefined));
+ QV4::ScopedCallData callData(scope);
+ QQmlJavaScriptExpression::evaluate(callData, &isUndefined, scope);
bool error = false;
if (!watcher.wasDeleted() && isAddedToObject() && !hasError())
- error = !write(pd, result, isUndefined, flags);
+ error = !write(scope.result, isUndefined, flags);
if (!watcher.wasDeleted()) {
@@ -211,66 +252,88 @@ void QQmlBinding::update(QQmlPropertyPrivate::WriteFlags flags)
}
+ cancelPermanentGuards();
+
ep->dereferenceScarceResources();
}
- if (!watcher.wasDeleted())
- setUpdatingFlag(false);
-}
+ virtual bool write(const QV4::Value &result, bool isUndefined, QQmlPropertyData::WriteFlags flags) = 0;
+};
-// Returns true if successful, false if an error description was set on expression
-bool QQmlBinding::write(const QQmlPropertyData &core,
- const QV4::Value &result, bool isUndefined,
- QQmlPropertyPrivate::WriteFlags flags)
+template<int StaticPropType>
+class GenericBinding: public QQmlNonbindingBinding
{
- Q_ASSERT(m_target.data());
- Q_ASSERT(core.coreIndex != -1);
-
-#define QUICK_STORE(cpptype, conversion) \
- { \
- cpptype o = (conversion); \
- int status = -1; \
- void *argv[] = { &o, 0, &status, &flags }; \
- QMetaObject::metacall(m_target.data(), QMetaObject::WriteProperty, core.coreIndex, argv); \
- return true; \
- } \
-
-
- if (Q_LIKELY(!isUndefined && !core.isValueTypeVirtual())) {
- switch (core.propType) {
- case QMetaType::Int:
- if (result.isInteger())
- QUICK_STORE(int, result.integerValue())
- else if (result.isNumber())
- QUICK_STORE(int, result.doubleValue())
- break;
- case QMetaType::Double:
- if (result.isNumber())
- QUICK_STORE(double, result.asDouble())
- break;
- case QMetaType::Float:
- if (result.isNumber())
- QUICK_STORE(float, result.asDouble())
- break;
- case QMetaType::QString:
- if (result.isString())
- QUICK_STORE(QString, result.toQStringNoThrow())
- break;
- default:
- if (const QV4::QQmlValueTypeWrapper *vtw = result.as<const QV4::QQmlValueTypeWrapper>()) {
- if (vtw->d()->valueType->typeId == core.propType) {
- return vtw->write(m_target.data(), core.coreIndex);
+protected:
+ // Returns true if successful, false if an error description was set on expression
+ Q_ALWAYS_INLINE bool write(const QV4::Value &result, bool isUndefined,
+ QQmlPropertyData::WriteFlags flags) Q_DECL_OVERRIDE Q_DECL_FINAL
+ {
+ Q_ASSERT(targetObject());
+
+ QQmlPropertyData *pd;
+ QQmlPropertyData vpd;
+ getPropertyData(&pd, &vpd);
+ Q_ASSERT(pd);
+
+ int propertyType = StaticPropType; // If the binding is specialized to a type, the if and switch below will be constant-folded.
+ if (propertyType == QMetaType::UnknownType)
+ propertyType = pd->propType();
+
+ if (Q_LIKELY(!isUndefined && !vpd.isValid())) {
+ switch (propertyType) {
+ case QMetaType::Bool:
+ if (result.isBoolean())
+ return doStore<bool>(result.booleanValue(), pd, flags);
+ else
+ return doStore<bool>(result.toBoolean(), pd, flags);
+ case QMetaType::Int:
+ if (result.isInteger())
+ return doStore<int>(result.integerValue(), pd, flags);
+ else if (result.isNumber())
+ return doStore<int>(result.doubleValue(), pd, flags);
+ break;
+ case QMetaType::Double:
+ if (result.isNumber())
+ return doStore<double>(result.asDouble(), pd, flags);
+ break;
+ case QMetaType::Float:
+ if (result.isNumber())
+ return doStore<float>(result.asDouble(), pd, flags);
+ break;
+ case QMetaType::QString:
+ if (result.isString())
+ return doStore<QString>(result.toQStringNoThrow(), pd, flags);
+ break;
+ default:
+ if (const QV4::QQmlValueTypeWrapper *vtw = result.as<const QV4::QQmlValueTypeWrapper>()) {
+ if (vtw->d()->valueType->typeId == pd->propType()) {
+ return vtw->write(m_target.data(), pd->coreIndex());
+ }
}
+ break;
}
- break;
}
+
+ return slowWrite(*pd, vpd, result, isUndefined, flags);
+ }
+
+ template <typename T>
+ Q_ALWAYS_INLINE bool doStore(T value, const QQmlPropertyData *pd, QQmlPropertyData::WriteFlags flags) const
+ {
+ void *o = &value;
+ return pd->writeProperty(targetObject(), o, flags);
}
-#undef QUICK_STORE
+};
+Q_NEVER_INLINE bool QQmlBinding::slowWrite(const QQmlPropertyData &core,
+ const QQmlPropertyData &valueTypeData,
+ const QV4::Value &result,
+ bool isUndefined, QQmlPropertyData::WriteFlags flags)
+{
QQmlEngine *engine = context()->engine;
QV8Engine *v8engine = QQmlEnginePrivate::getV8Engine(engine);
- int type = core.isValueTypeVirtual() ? core.valueTypePropType : core.propType;
+ int type = valueTypeData.isValid() ? valueTypeData.propType() : core.propType();
QQmlJavaScriptExpression::DeleteWatcher watcher(this);
@@ -282,7 +345,7 @@ bool QQmlBinding::write(const QQmlPropertyData &core,
value = QV8Engine::getV4(v8engine)->toVariant(result, qMetaTypeId<QList<QObject *> >());
} else if (result.isNull() && core.isQObject()) {
value = QVariant::fromValue((QObject *)0);
- } else if (core.propType == qMetaTypeId<QList<QUrl> >()) {
+ } else if (core.propType() == qMetaTypeId<QList<QUrl> >()) {
value = QQmlPropertyPrivate::resolvedUrlSequence(QV8Engine::getV4(v8engine)->toVariant(result, qMetaTypeId<QList<QUrl> >()), context());
} else if (!isVarProperty && type != qMetaTypeId<QJSValue>()) {
value = QV8Engine::getV4(v8engine)->toVariant(result, type);
@@ -301,28 +364,27 @@ bool QQmlBinding::write(const QQmlPropertyData &core,
QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(m_target.data());
Q_ASSERT(vmemo);
- vmemo->setVMEProperty(core.coreIndex, result);
+ vmemo->setVMEProperty(core.coreIndex(), result);
} else if (isUndefined && core.isResettable()) {
void *args[] = { 0 };
- QMetaObject::metacall(m_target.data(), QMetaObject::ResetProperty, core.coreIndex, args);
+ QMetaObject::metacall(m_target.data(), QMetaObject::ResetProperty, core.coreIndex(), args);
} else if (isUndefined && type == qMetaTypeId<QVariant>()) {
- QQmlPropertyPrivate::writeValueProperty(m_target.data(), core, QVariant(), context(), flags);
+ QQmlPropertyPrivate::writeValueProperty(m_target.data(), core, valueTypeData, QVariant(), context(), flags);
} else if (type == qMetaTypeId<QJSValue>()) {
const QV4::FunctionObject *f = result.as<QV4::FunctionObject>();
if (f && f->isBinding()) {
delayedError()->setErrorDescription(QLatin1String("Invalid use of Qt.binding() in a binding declaration."));
return false;
}
- QQmlPropertyPrivate::writeValueProperty(m_target.data(), core, QVariant::fromValue(
+ QQmlPropertyPrivate::writeValueProperty(m_target.data(), core, valueTypeData, QVariant::fromValue(
QJSValue(QV8Engine::getV4(v8engine), result.asReturnedValue())),
context(), flags);
} else if (isUndefined) {
- QString errorStr = QLatin1String("Unable to assign [undefined] to ");
- if (!QMetaType::typeName(type))
- errorStr += QLatin1String("[unknown property type]");
- else
- errorStr += QLatin1String(QMetaType::typeName(type));
- delayedError()->setErrorDescription(errorStr);
+ const QLatin1String typeName(QMetaType::typeName(type)
+ ? QMetaType::typeName(type)
+ : "[unknown property type]");
+ delayedError()->setErrorDescription(QLatin1String("Unable to assign [undefined] to ")
+ + typeName);
return false;
} else if (const QV4::FunctionObject *f = result.as<QV4::FunctionObject>()) {
if (f->isBinding())
@@ -330,7 +392,7 @@ bool QQmlBinding::write(const QQmlPropertyData &core,
else
delayedError()->setErrorDescription(QLatin1String("Unable to assign a function to a property of any type other than var."));
return false;
- } else if (!QQmlPropertyPrivate::writeValueProperty(m_target.data(), core, value, context(), flags)) {
+ } else if (!QQmlPropertyPrivate::writeValueProperty(m_target.data(), core, valueTypeData, value, context(), flags)) {
if (watcher.wasDeleted())
return true;
@@ -338,7 +400,8 @@ bool QQmlBinding::write(const QQmlPropertyData &core,
const char *valueType = 0;
const char *propertyType = 0;
- if (value.userType() == QMetaType::QObjectStar) {
+ const int userType = value.userType();
+ if (userType == QMetaType::QObjectStar) {
if (QObject *o = *(QObject *const *)value.constData()) {
valueType = o->metaObject()->className();
@@ -346,11 +409,11 @@ bool QQmlBinding::write(const QQmlPropertyData &core,
if (!propertyMetaObject.isNull())
propertyType = propertyMetaObject.className();
}
- } else if (value.userType() != QVariant::Invalid) {
- if (value.userType() == QMetaType::VoidStar)
+ } else if (userType != QVariant::Invalid) {
+ if (userType == QMetaType::Nullptr || userType == QMetaType::VoidStar)
valueType = "null";
else
- valueType = QMetaType::typeName(value.userType());
+ valueType = QMetaType::typeName(userType);
}
if (!valueType)
@@ -378,11 +441,12 @@ QVariant QQmlBinding::evaluate()
bool isUndefined = false;
QV4::Scope scope(ep->v4engine());
- QV4::ScopedValue result(scope, QQmlJavaScriptExpression::evaluate(&isUndefined));
+ QV4::ScopedCallData callData(scope);
+ QQmlJavaScriptExpression::evaluate(callData, &isUndefined, scope);
ep->dereferenceScarceResources();
- return scope.engine->toVariant(result, qMetaTypeId<QList<QObject*> >());
+ return scope.engine->toVariant(scope.result, qMetaTypeId<QList<QObject*> >());
}
QString QQmlBinding::expressionIdentifier()
@@ -395,8 +459,7 @@ QString QQmlBinding::expressionIdentifier()
QString url = function->sourceFile();
quint16 lineNumber = function->compiledFunction->location.line;
quint16 columnNumber = function->compiledFunction->location.column;
-
- return url + QLatin1Char(':') + QString::number(lineNumber) + QLatin1Char(':') + QString::number(columnNumber);
+ return url + QString::asprintf(":%u:%u", uint(lineNumber), uint(columnNumber));
}
void QQmlBinding::expressionChanged()
@@ -409,11 +472,17 @@ void QQmlBinding::refresh()
update();
}
-void QQmlBinding::setEnabled(bool e, QQmlPropertyPrivate::WriteFlags flags)
+void QQmlBinding::setEnabled(bool e, QQmlPropertyData::WriteFlags flags)
{
setEnabledFlag(e);
setNotifyOnValueChanged(e);
+ m_nextBinding.setFlag2(); // Always use accessors, only not when:
+ if (auto interceptorMetaObject = QQmlInterceptorMetaObject::get(targetObject())) {
+ if (!m_targetIndex.isValid() || interceptorMetaObject->intercepts(m_targetIndex))
+ m_nextBinding.clearFlag2();
+ }
+
if (e)
update(flags);
}
@@ -427,29 +496,28 @@ QString QQmlBinding::expression() const
void QQmlBinding::setTarget(const QQmlProperty &prop)
{
- setTarget(prop.object(), QQmlPropertyPrivate::get(prop)->core);
+ auto pd = QQmlPropertyPrivate::get(prop);
+ setTarget(prop.object(), pd->core, &pd->valueTypeData);
}
-void QQmlBinding::setTarget(QObject *object, const QQmlPropertyData &core)
+void QQmlBinding::setTarget(QObject *object, const QQmlPropertyData &core, const QQmlPropertyData *valueType)
{
m_target = object;
if (!object) {
- m_targetIndex = -1;
+ m_targetIndex = QQmlPropertyIndex();
return;
}
- QQmlPropertyData pd = core;
-
- while (pd.isAlias()) {
- int coreIndex = pd.coreIndex;
- int valueTypeIndex = pd.getValueTypeCoreIndex();
+ int coreIndex = core.coreIndex();
+ int valueTypeIndex = valueType ? valueType->coreIndex() : -1;
+ for (bool isAlias = core.isAlias(); isAlias; ) {
QQmlVMEMetaObject *vme = QQmlVMEMetaObject::getForProperty(object, coreIndex);
int aValueTypeIndex;
if (!vme->aliasTarget(coreIndex, &object, &coreIndex, &aValueTypeIndex)) {
m_target = 0;
- m_targetIndex = -1;
+ m_targetIndex = QQmlPropertyIndex();
return;
}
if (valueTypeIndex == -1)
@@ -458,25 +526,17 @@ void QQmlBinding::setTarget(QObject *object, const QQmlPropertyData &core)
QQmlData *data = QQmlData::get(object, false);
if (!data || !data->propertyCache) {
m_target = 0;
- m_targetIndex = -1;
+ m_targetIndex = QQmlPropertyIndex();
return;
}
QQmlPropertyData *propertyData = data->propertyCache->property(coreIndex);
Q_ASSERT(propertyData);
m_target = object;
- pd = *propertyData;
- if (valueTypeIndex != -1) {
- const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(pd.propType);
- Q_ASSERT(valueTypeMetaObject);
- QMetaProperty vtProp = valueTypeMetaObject->property(valueTypeIndex);
- pd.setFlags(pd.getFlags() | QQmlPropertyData::IsValueTypeVirtual);
- pd.valueTypeFlags = QQmlPropertyData::flagsForProperty(vtProp);
- pd.valueTypePropType = vtProp.userType();
- pd.valueTypeCoreIndex = valueTypeIndex;
- }
+ isAlias = propertyData->isAlias();
+ coreIndex = propertyData->coreIndex();
}
- m_targetIndex = pd.encodedIndex();
+ m_targetIndex = QQmlPropertyIndex(coreIndex, valueTypeIndex);
QQmlData *data = QQmlData::get(*m_target, true);
if (!data->propertyCache) {
@@ -485,28 +545,110 @@ void QQmlBinding::setTarget(QObject *object, const QQmlPropertyData &core)
}
}
-QQmlPropertyData QQmlBinding::getPropertyData() const
+void QQmlBinding::getPropertyData(QQmlPropertyData **propertyData, QQmlPropertyData *valueTypeData) const
{
- int coreIndex;
- int valueTypeIndex = QQmlPropertyData::decodeValueTypePropertyIndex(m_targetIndex, &coreIndex);
+ Q_ASSERT(propertyData);
QQmlData *data = QQmlData::get(*m_target, false);
Q_ASSERT(data && data->propertyCache);
- QQmlPropertyData *propertyData = data->propertyCache->property(coreIndex);
- Q_ASSERT(propertyData);
+ *propertyData = data->propertyCache->property(m_targetIndex.coreIndex());
+ Q_ASSERT(*propertyData);
- QQmlPropertyData d = *propertyData;
- if (Q_UNLIKELY(valueTypeIndex != -1)) {
- const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(d.propType);
+ if (Q_UNLIKELY(m_targetIndex.hasValueTypeIndex() && valueTypeData)) {
+ const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType((*propertyData)->propType());
Q_ASSERT(valueTypeMetaObject);
- QMetaProperty vtProp = valueTypeMetaObject->property(valueTypeIndex);
- d.setFlags(d.getFlags() | QQmlPropertyData::IsValueTypeVirtual);
- d.valueTypeFlags = QQmlPropertyData::flagsForProperty(vtProp);
- d.valueTypePropType = vtProp.userType();
- d.valueTypeCoreIndex = valueTypeIndex;
+ QMetaProperty vtProp = valueTypeMetaObject->property(m_targetIndex.valueTypeIndex());
+ valueTypeData->setFlags(QQmlPropertyData::flagsForProperty(vtProp));
+ valueTypeData->setPropType(vtProp.userType());
+ valueTypeData->setCoreIndex(m_targetIndex.valueTypeIndex());
+ }
+}
+
+class QObjectPointerBinding: public QQmlNonbindingBinding
+{
+ QQmlMetaObject targetMetaObject;
+
+public:
+ QObjectPointerBinding(QQmlEnginePrivate *engine, int propertyType)
+ : targetMetaObject(QQmlPropertyPrivate::rawMetaObjectForType(engine, propertyType))
+ {}
+
+protected:
+ Q_ALWAYS_INLINE bool write(const QV4::Value &result, bool isUndefined,
+ QQmlPropertyData::WriteFlags flags) Q_DECL_OVERRIDE Q_DECL_FINAL
+ {
+ QQmlPropertyData *pd;
+ QQmlPropertyData vtpd;
+ getPropertyData(&pd, &vtpd);
+ if (Q_UNLIKELY(isUndefined || vtpd.isValid()))
+ return slowWrite(*pd, vtpd, result, isUndefined, flags);
+
+ // Check if the result is a QObject:
+ QObject *resultObject = nullptr;
+ QQmlMetaObject resultMo;
+ if (result.isNull()) {
+ // Special case: we can always write a nullptr. Don't bother checking anything else.
+ return pd->writeProperty(targetObject(), &resultObject, flags);
+ } else if (auto wrapper = result.as<QV4::QObjectWrapper>()) {
+ resultObject = wrapper->object();
+ if (!resultObject)
+ return pd->writeProperty(targetObject(), &resultObject, flags);
+ if (QQmlData *ddata = QQmlData::get(resultObject, false))
+ resultMo = ddata->propertyCache;
+ if (resultMo.isNull()) {
+ resultMo = resultObject->metaObject();
+ }
+ } else if (auto variant = result.as<QV4::VariantObject>()) {
+ QVariant value = variant->d()->data();
+ QQmlEnginePrivate *ep = QQmlEnginePrivate::get(context());
+ resultMo = QQmlPropertyPrivate::rawMetaObjectForType(ep, value.userType());
+ if (resultMo.isNull())
+ return slowWrite(*pd, vtpd, result, isUndefined, flags);
+ resultObject = *static_cast<QObject *const *>(value.constData());
+ } else {
+ return slowWrite(*pd, vtpd, result, isUndefined, flags);
+ }
+
+ // Compare & set:
+ if (QQmlMetaObject::canConvert(resultMo, targetMetaObject)) {
+ return pd->writeProperty(targetObject(), &resultObject, flags);
+ } else if (!resultObject && QQmlMetaObject::canConvert(targetMetaObject, resultMo)) {
+ // In the case of a null QObject, we assign the null if there is
+ // any change that the null variant type could be up or down cast to
+ // the property type.
+ return pd->writeProperty(targetObject(), &resultObject, flags);
+ } else {
+ return slowWrite(*pd, vtpd, result, isUndefined, flags);
+ }
+ }
+};
+
+QQmlBinding *QQmlBinding::newBinding(QQmlEnginePrivate *engine, const QQmlPropertyData *property)
+{
+ if (property && property->isQObject())
+ return new QObjectPointerBinding(engine, property->propType());
+
+ const int type = (property && property->isFullyResolved()) ? property->propType() : QMetaType::UnknownType;
+
+ if (type == qMetaTypeId<QQmlBinding *>()) {
+ return new QQmlBindingBinding;
+ }
+
+ switch (type) {
+ case QMetaType::Bool:
+ return new GenericBinding<QMetaType::Bool>;
+ case QMetaType::Int:
+ return new GenericBinding<QMetaType::Int>;
+ case QMetaType::Double:
+ return new GenericBinding<QMetaType::Double>;
+ case QMetaType::Float:
+ return new GenericBinding<QMetaType::Float>;
+ case QMetaType::QString:
+ return new GenericBinding<QMetaType::QString>;
+ default:
+ return new GenericBinding<QMetaType::UnknownType>;
}
- return d;
}
QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmlbinding_p.h b/src/qml/qml/qqmlbinding_p.h
index 7814b9dee8..6d42a8ea8a 100644
--- a/src/qml/qml/qqmlbinding_p.h
+++ b/src/qml/qml/qqmlbinding_p.h
@@ -61,7 +61,6 @@
#include <QtCore/QObject>
#include <QtCore/QMetaProperty>
-#include <private/qpointervaluepair_p.h>
#include <private/qqmlabstractbinding_p.h>
#include <private/qqmljavascriptexpression_p.h>
@@ -73,26 +72,24 @@ class Q_QML_PRIVATE_EXPORT QQmlBinding : public QQmlJavaScriptExpression,
{
friend class QQmlAbstractBinding;
public:
- QQmlBinding(const QString &, QObject *, QQmlContext *);
- QQmlBinding(const QQmlScriptString &, QObject *, QQmlContext *);
- QQmlBinding(const QString &, QObject *, QQmlContextData *);
- QQmlBinding(const QString &, QObject *, QQmlContextData *,
- const QString &url, quint16 lineNumber, quint16 columnNumber);
- QQmlBinding(const QV4::Value &, QObject *, QQmlContextData *);
+ static QQmlBinding *create(const QQmlPropertyData *, const QString &, QObject *, QQmlContext *);
+ static QQmlBinding *create(const QQmlPropertyData *, const QQmlScriptString &, QObject *, QQmlContext *);
+ static QQmlBinding *create(const QQmlPropertyData *, const QString &, QObject *, QQmlContextData *);
+ static QQmlBinding *create(const QQmlPropertyData *, const QString &, QObject *, QQmlContextData *,
+ const QString &url, quint16 lineNumber, quint16 columnNumber);
+ static QQmlBinding *create(const QQmlPropertyData *, const QV4::Value &, QObject *, QQmlContextData *);
~QQmlBinding();
void setTarget(const QQmlProperty &);
- void setTarget(QObject *, const QQmlPropertyData &);
+ void setTarget(QObject *, const QQmlPropertyData &, const QQmlPropertyData *valueType);
void setNotifyOnValueChanged(bool);
- // Inherited from QQmlJavaScriptExpression
- virtual void refresh();
+ void refresh() Q_DECL_OVERRIDE;
- // Inherited from QQmlAbstractBinding
- virtual void setEnabled(bool, QQmlPropertyPrivate::WriteFlags flags = QQmlPropertyPrivate::DontRemoveBinding);
- virtual QString expression() const;
- void update(QQmlPropertyPrivate::WriteFlags flags = QQmlPropertyPrivate::DontRemoveBinding);
+ void setEnabled(bool, QQmlPropertyData::WriteFlags flags = QQmlPropertyData::DontRemoveBinding) Q_DECL_OVERRIDE;
+ QString expression() const Q_DECL_OVERRIDE;
+ void update(QQmlPropertyData::WriteFlags flags = QQmlPropertyData::DontRemoveBinding);
typedef int Identifier;
enum {
@@ -101,20 +98,27 @@ public:
QVariant evaluate();
- virtual QString expressionIdentifier();
- virtual void expressionChanged();
+ QString expressionIdentifier() Q_DECL_OVERRIDE;
+ void expressionChanged() Q_DECL_OVERRIDE;
+
+protected:
+ virtual void doUpdate(const DeleteWatcher &watcher,
+ QQmlPropertyData::WriteFlags flags, QV4::Scope &scope,
+ const QV4::ScopedFunctionObject &f) = 0;
+
+ void getPropertyData(QQmlPropertyData **propertyData, QQmlPropertyData *valueTypeData) const;
+ int getPropertyType() const;
+
+ bool slowWrite(const QQmlPropertyData &core, const QQmlPropertyData &valueTypeData,
+ const QV4::Value &result, bool isUndefined, QQmlPropertyData::WriteFlags flags);
private:
inline bool updatingFlag() const;
inline void setUpdatingFlag(bool);
inline bool enabledFlag() const;
inline void setEnabledFlag(bool);
- QQmlPropertyData getPropertyData() const;
-
- bool write(const QQmlPropertyData &core,
- const QV4::Value &result, bool isUndefined,
- QQmlPropertyPrivate::WriteFlags flags);
+ static QQmlBinding *newBinding(QQmlEnginePrivate *engine, const QQmlPropertyData *property);
};
bool QQmlBinding::updatingFlag() const
diff --git a/src/qml/qml/qqmlboundsignal.cpp b/src/qml/qml/qqmlboundsignal.cpp
index c6a7cde240..4e63790290 100644
--- a/src/qml/qml/qqmlboundsignal.cpp
+++ b/src/qml/qml/qqmlboundsignal.cpp
@@ -51,14 +51,12 @@
#include <private/qqmlprofiler_p.h>
#include <private/qqmldebugconnector_p.h>
#include <private/qqmldebugserviceinterfaces_p.h>
-#include <private/qqmlcompiler_p.h>
#include "qqmlinfo.h"
#include <private/qjsvalue_p.h>
#include <private/qv4value_p.h>
#include <private/qv4qobjectwrapper_p.h>
-#include <QtCore/qstringbuilder.h>
#include <QtCore/qdebug.h>
@@ -82,10 +80,8 @@ QQmlBoundSignalExpression::QQmlBoundSignalExpression(QObject *target, int index,
// Add some leading whitespace to account for the binding's column offset.
// It's 2 off because a, we start counting at 1 and b, the '(' below is not counted.
- function.fill(QChar(QChar::Space), qMax(column, (quint16)2) - 2);
- function += QStringLiteral("(function ");
- function += handlerName;
- function += QLatin1Char('(');
+ function += QString(qMax(column, (quint16)2) - 2, QChar(QChar::Space))
+ + QLatin1String("(function ") + handlerName + QLatin1Char('(');
if (parameterString.isEmpty()) {
QString error;
@@ -101,10 +97,7 @@ QQmlBoundSignalExpression::QQmlBoundSignalExpression(QObject *target, int index,
} else
function += parameterString;
- function += QStringLiteral(") { ");
- function += expression;
- function += QStringLiteral(" })");
-
+ function += QLatin1String(") { ") + expression + QLatin1String(" })");
m_function.set(v4, evalFunction(context(), scopeObject(), function, fileName, line));
if (m_function.isNullOrUndefined())
@@ -213,10 +206,10 @@ void QQmlBoundSignalExpression::evaluate(void **a)
ep->referenceScarceResources(); // "hold" scarce resources in memory during evaluation.
- QVarLengthArray<int, 9> dummy;
+ QQmlMetaObject::ArgTypeStorage storage;
//TODO: lookup via signal index rather than method index as an optimization
int methodIndex = QMetaObjectPrivate::signal(m_target->metaObject(), m_index).methodIndex();
- int *argsTypes = QQmlMetaObject(m_target).methodParameterTypes(methodIndex, dummy, 0);
+ int *argsTypes = QQmlMetaObject(m_target).methodParameterTypes(methodIndex, &storage, 0);
int argCount = argsTypes ? *argsTypes : 0;
QV4::ScopedCallData callData(scope, argCount);
@@ -244,7 +237,7 @@ void QQmlBoundSignalExpression::evaluate(void **a)
}
}
- QQmlJavaScriptExpression::evaluate(callData, 0);
+ QQmlJavaScriptExpression::evaluate(callData, 0, scope);
ep->dereferenceScarceResources(); // "release" scarce resources if top-level expression evaluation is complete.
}
@@ -266,7 +259,7 @@ void QQmlBoundSignalExpression::evaluate(const QList<QVariant> &args)
callData->args[ii] = scope.engine->fromVariant(args[ii]);
}
- QQmlJavaScriptExpression::evaluate(callData, 0);
+ QQmlJavaScriptExpression::evaluate(callData, 0, scope);
ep->dereferenceScarceResources(); // "release" scarce resources if top-level expression evaluation is complete.
}
diff --git a/src/qml/qml/qqmlcompileddata.cpp b/src/qml/qml/qqmlcompileddata.cpp
deleted file mode 100644
index 28d599a593..0000000000
--- a/src/qml/qml/qqmlcompileddata.cpp
+++ /dev/null
@@ -1,165 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml 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 "qqmlcompiler_p.h"
-#include "qqmlengine.h"
-#include "qqmlcomponent.h"
-#include "qqmlcomponent_p.h"
-#include "qqmlcontext.h"
-#include "qqmlcontext_p.h"
-#include "qqmlpropertymap.h"
-#ifdef QML_THREADED_VME_INTERPRETER
-#include "qqmlvme_p.h"
-#endif
-
-#include <QtCore/qdebug.h>
-
-#include <private/qobject_p.h>
-
-QT_BEGIN_NAMESPACE
-
-QQmlCompiledData::QQmlCompiledData(QQmlEngine *engine)
-: engine(engine), importCache(0), metaTypeId(-1), listMetaTypeId(-1), isRegisteredWithEngine(false),
- rootPropertyCache(0), totalBindingsCount(0), totalParserStatusCount(0)
-{
- Q_ASSERT(engine);
-}
-
-void QQmlCompiledData::destroy()
-{
- if (engine && hasEngine())
- QQmlEnginePrivate::deleteInEngineThread(engine, this);
- else
- delete this;
-}
-
-QQmlCompiledData::~QQmlCompiledData()
-{
- if (isRegisteredWithEngine)
- QQmlEnginePrivate::get(engine)->unregisterInternalCompositeType(this);
-
- clear();
-
- for (QHash<int, TypeReference*>::Iterator resolvedType = resolvedTypes.begin(), end = resolvedTypes.end();
- resolvedType != end; ++resolvedType) {
- if ((*resolvedType)->component)
- (*resolvedType)->component->release();
- if ((*resolvedType)->typePropertyCache)
- (*resolvedType)->typePropertyCache->release();
- }
- qDeleteAll(resolvedTypes);
- resolvedTypes.clear();
-
- for (int ii = 0; ii < propertyCaches.count(); ++ii)
- if (propertyCaches.at(ii))
- propertyCaches.at(ii)->release();
-
- for (int ii = 0; ii < scripts.count(); ++ii)
- scripts.at(ii)->release();
-
- if (importCache)
- importCache->release();
-
- if (rootPropertyCache)
- rootPropertyCache->release();
-}
-
-void QQmlCompiledData::clear()
-{
-}
-
-/*!
-Returns the property cache, if one alread exists. The cache is not referenced.
-*/
-QQmlPropertyCache *QQmlCompiledData::TypeReference::propertyCache() const
-{
- if (type)
- return typePropertyCache;
- else
- return component->rootPropertyCache;
-}
-
-/*!
-Returns the property cache, creating one if it doesn't already exist. The cache is not referenced.
-*/
-QQmlPropertyCache *QQmlCompiledData::TypeReference::createPropertyCache(QQmlEngine *engine)
-{
- if (typePropertyCache) {
- return typePropertyCache;
- } else if (type) {
- typePropertyCache = QQmlEnginePrivate::get(engine)->cache(type->metaObject());
- typePropertyCache->addref();
- return typePropertyCache;
- } else {
- return component->rootPropertyCache;
- }
-}
-
-template <typename T>
-bool qtTypeInherits(const QMetaObject *mo) {
- while (mo) {
- if (mo == &T::staticMetaObject)
- return true;
- mo = mo->superClass();
- }
- return false;
-}
-
-void QQmlCompiledData::TypeReference::doDynamicTypeCheck()
-{
- const QMetaObject *mo = 0;
- if (typePropertyCache)
- mo = typePropertyCache->firstCppMetaObject();
- else if (type)
- mo = type->metaObject();
- else if (component)
- mo = component->rootPropertyCache->firstCppMetaObject();
- isFullyDynamicType = qtTypeInherits<QQmlPropertyMap>(mo);
-}
-
-void QQmlCompiledData::initialize(QQmlEngine *engine)
-{
- Q_ASSERT(!hasEngine());
- QQmlCleanup::addToEngine(engine);
- QV4::ExecutionEngine *v4 = QV8Engine::getV4(engine);
- if (compilationUnit && !compilationUnit->engine)
- compilationUnit->linkToEngine(v4);
-}
-
-QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmlcompiler_p.h b/src/qml/qml/qqmlcompiler_p.h
deleted file mode 100644
index 1c43a85de3..0000000000
--- a/src/qml/qml/qqmlcompiler_p.h
+++ /dev/null
@@ -1,160 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml 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 QQMLCOMPILER_P_H
-#define QQMLCOMPILER_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 "qqml.h"
-#include "qqmlerror.h"
-#include "qqmlengine_p.h"
-#include <private/qbitfield_p.h>
-#include "qqmlpropertycache_p.h"
-#include "qqmltypenamecache_p.h"
-#include "qqmltypeloader_p.h"
-#include "private/qv4identifier_p.h"
-#include <private/qqmljsastfwd_p.h>
-#include "qqmlcustomparser_p.h"
-
-#include <QtCore/qbytearray.h>
-#include <QtCore/qset.h>
-#include <QtCore/QCoreApplication>
-
-QT_BEGIN_NAMESPACE
-
-namespace QV4 {
-namespace CompiledData {
-struct CompilationUnit;
-struct Unit;
-}
-}
-
-class QQmlEngine;
-class QQmlComponent;
-class QQmlContext;
-class QQmlContextData;
-
-// ### Merge with QV4::CompiledData::CompilationUnit
-class Q_AUTOTEST_EXPORT QQmlCompiledData : public QQmlRefCount, public QQmlCleanup
-{
-public:
- QQmlCompiledData(QQmlEngine *engine);
- virtual ~QQmlCompiledData();
-
- QQmlEngine *engine;
-
- QString fileName() const { return compilationUnit->fileName(); }
- QUrl url() const { return compilationUnit->url(); }
- QQmlTypeNameCache *importCache;
-
- int metaTypeId;
- int listMetaTypeId;
- bool isRegisteredWithEngine;
-
- struct TypeReference
- {
- TypeReference()
- : type(0), typePropertyCache(0), component(0)
- , majorVersion(0)
- , minorVersion(0)
- , isFullyDynamicType(false)
- {}
-
- QQmlType *type;
- QQmlPropertyCache *typePropertyCache;
- QQmlCompiledData *component;
-
- int majorVersion;
- int minorVersion;
- // Types such as QQmlPropertyMap can add properties dynamically at run-time and
- // therefore cannot have a property cache installed when instantiated.
- bool isFullyDynamicType;
-
- QQmlPropertyCache *propertyCache() const;
- QQmlPropertyCache *createPropertyCache(QQmlEngine *);
-
- void doDynamicTypeCheck();
- };
- // map from name index
- QHash<int, TypeReference*> resolvedTypes;
-
- QQmlPropertyCache *rootPropertyCache;
- QVector<QByteArray> metaObjects;
- QVector<QQmlPropertyCache *> propertyCaches;
- QList<QQmlScriptData *> scripts;
-
- QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit;
- // index in first hash is component index, hash inside maps from object index in that scope to integer id
- QHash<int, QHash<int, int> > objectIndexToIdPerComponent;
- QHash<int, int> objectIndexToIdForRoot;
- // hash key is object index, value is indicies of bindings covered by custom parser
- QHash<int, QBitArray> customParserBindings;
- QHash<int, QBitArray> deferredBindingsPerObject; // index is object index
- int totalBindingsCount; // Number of bindings used in this type
- int totalParserStatusCount; // Number of instantiated types that are QQmlParserStatus subclasses
- int totalObjectCount; // Number of objects explicitly instantiated
-
- bool isComponent(int objectIndex) const { return objectIndexToIdPerComponent.contains(objectIndex); }
- bool isCompositeType() const { return !metaObjects.at(compilationUnit->data->indexOfRootObject).isEmpty(); }
-
- bool isInitialized() const { return hasEngine(); }
- void initialize(QQmlEngine *);
-
-protected:
- virtual void destroy(); // From QQmlRefCount
- virtual void clear(); // From QQmlCleanup
-
-private:
- QQmlCompiledData(const QQmlCompiledData &other);
- QQmlCompiledData &operator=(const QQmlCompiledData &other);
-};
-
-QT_END_NAMESPACE
-
-#endif // QQMLCOMPILER_P_H
diff --git a/src/qml/qml/qqmlcomponent.cpp b/src/qml/qml/qqmlcomponent.cpp
index 747be3cb7f..8be5172cd4 100644
--- a/src/qml/qml/qqmlcomponent.cpp
+++ b/src/qml/qml/qqmlcomponent.cpp
@@ -41,7 +41,6 @@
#include "qqmlcomponent_p.h"
#include "qqmlcomponentattached_p.h"
-#include "qqmlcompiler_p.h"
#include "qqmlcontext_p.h"
#include "qqmlengine_p.h"
#include "qqmlvme_p.h"
@@ -336,14 +335,11 @@ void QQmlComponentPrivate::typeDataProgress(QQmlTypeData *, qreal p)
void QQmlComponentPrivate::fromTypeData(QQmlTypeData *data)
{
url = data->finalUrl();
- QQmlCompiledData *c = data->compiledData();
+ compilationUnit = data->compilationUnit();
- if (!c) {
+ if (!compilationUnit) {
Q_ASSERT(data->isError());
state.errors = data->errors();
- } else {
- cc = c;
- cc->addref();
}
data->release();
@@ -357,10 +353,7 @@ void QQmlComponentPrivate::clear()
typeData = 0;
}
- if (cc) {
- cc->release();
- cc = 0;
- }
+ compilationUnit = nullptr;
}
/*!
@@ -394,8 +387,6 @@ QQmlComponent::~QQmlComponent()
d->typeData->unregisterCallback(d);
d->typeData->release();
}
- if (d->cc)
- d->cc->release();
}
/*!
@@ -423,7 +414,7 @@ QQmlComponent::Status QQmlComponent::status() const
return Loading;
else if (!d->state.errors.isEmpty())
return Error;
- else if (d->engine && d->cc)
+ else if (d->engine && d->compilationUnit)
return Ready;
else
return Null;
@@ -513,11 +504,8 @@ QQmlComponent::QQmlComponent(QQmlEngine *engine, QObject *parent)
\sa loadUrl()
*/
QQmlComponent::QQmlComponent(QQmlEngine *engine, const QUrl &url, QObject *parent)
-: QObject(*(new QQmlComponentPrivate), parent)
+ : QQmlComponent(engine, url, QQmlComponent::PreferSynchronous, parent)
{
- Q_D(QQmlComponent);
- d->engine = engine;
- d->loadUrl(url);
}
/*!
@@ -532,10 +520,9 @@ QQmlComponent::QQmlComponent(QQmlEngine *engine, const QUrl &url, QObject *paren
*/
QQmlComponent::QQmlComponent(QQmlEngine *engine, const QUrl &url, CompilationMode mode,
QObject *parent)
-: QObject(*(new QQmlComponentPrivate), parent)
+ : QQmlComponent(engine, parent)
{
Q_D(QQmlComponent);
- d->engine = engine;
d->loadUrl(url, mode);
}
@@ -547,12 +534,8 @@ QQmlComponent::QQmlComponent(QQmlEngine *engine, const QUrl &url, CompilationMod
*/
QQmlComponent::QQmlComponent(QQmlEngine *engine, const QString &fileName,
QObject *parent)
-: QObject(*(new QQmlComponentPrivate), parent)
+ : QQmlComponent(engine, fileName, QQmlComponent::PreferSynchronous, parent)
{
- Q_D(QQmlComponent);
- d->engine = engine;
- const QUrl url = QDir::isAbsolutePath(fileName) ? QUrl::fromLocalFile(fileName) : d->engine->baseUrl().resolved(QUrl(fileName));
- d->loadUrl(url);
}
/*!
@@ -564,10 +547,9 @@ QQmlComponent::QQmlComponent(QQmlEngine *engine, const QString &fileName,
*/
QQmlComponent::QQmlComponent(QQmlEngine *engine, const QString &fileName,
CompilationMode mode, QObject *parent)
-: QObject(*(new QQmlComponentPrivate), parent)
+ : QQmlComponent(engine, parent)
{
Q_D(QQmlComponent);
- d->engine = engine;
const QUrl url = QDir::isAbsolutePath(fileName) ? QUrl::fromLocalFile(fileName) : d->engine->baseUrl().resolved(QUrl(fileName));
d->loadUrl(url, mode);
}
@@ -575,15 +557,13 @@ QQmlComponent::QQmlComponent(QQmlEngine *engine, const QString &fileName,
/*!
\internal
*/
-QQmlComponent::QQmlComponent(QQmlEngine *engine, QQmlCompiledData *cc, int start, QObject *parent)
- : QObject(*(new QQmlComponentPrivate), parent)
+QQmlComponent::QQmlComponent(QQmlEngine *engine, QV4::CompiledData::CompilationUnit *compilationUnit, int start, QObject *parent)
+ : QQmlComponent(engine, parent)
{
Q_D(QQmlComponent);
- d->engine = engine;
- d->cc = cc;
- cc->addref();
+ d->compilationUnit = compilationUnit;
d->start = start;
- d->url = cc->url();
+ d->url = compilationUnit->url();
d->progress = 1.0;
}
@@ -876,7 +856,7 @@ QQmlComponentPrivate::beginCreate(QQmlContextData *context)
enginePriv->referenceScarceResources();
QObject *rv = 0;
- state.creator.reset(new QQmlObjectCreator(context, cc, creationContext));
+ state.creator.reset(new QQmlObjectCreator(context, compilationUnit, creationContext));
rv = state.creator->create(start);
if (!rv)
state.errors = state.creator->errors;
@@ -896,11 +876,13 @@ QQmlComponentPrivate::beginCreate(QQmlContextData *context)
depthIncreased = false;
}
- QQmlEngineDebugService *service = QQmlDebugConnector::service<QQmlEngineDebugService>();
- if (service && rv) {
- if (!context->isInternal)
- context->asQQmlContextPrivate()->instances.append(rv);
- service->objectCreated(engine, rv);
+ if (rv) {
+ if (QQmlEngineDebugService *service =
+ QQmlDebugConnector::service<QQmlEngineDebugService>()) {
+ if (!context->isInternal)
+ context->asQQmlContextPrivate()->instances.append(rv);
+ service->objectCreated(engine, rv);
+ }
}
return rv;
@@ -917,7 +899,7 @@ void QQmlComponentPrivate::beginDeferred(QQmlEnginePrivate *enginePriv,
Q_ASSERT(ddata->deferredData);
QQmlData::DeferredData *deferredData = ddata->deferredData;
QQmlContextData *creationContext = 0;
- state->creator.reset(new QQmlObjectCreator(deferredData->context->parent, deferredData->compiledData, creationContext));
+ state->creator.reset(new QQmlObjectCreator(deferredData->context->parent, deferredData->compilationUnit, creationContext));
if (!state->creator->populateDeferredProperties(object))
state->errors << state->creator->errors;
}
@@ -1061,9 +1043,9 @@ void QQmlComponent::create(QQmlIncubator &incubator, QQmlContext *context,
QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(d->engine);
- p->compiledData = d->cc;
- p->compiledData->addref();
- p->creator.reset(new QQmlObjectCreator(contextData, d->cc, d->creationContext, p.data()));
+ p->compilationUnit = d->compilationUnit;
+ p->enginePriv = enginePriv;
+ p->creator.reset(new QQmlObjectCreator(contextData, d->compilationUnit, d->creationContext, p.data()));
p->subComponentToCreate = d->start;
enginePriv->incubate(incubator, forContextData);
@@ -1076,9 +1058,10 @@ namespace QV4 {
namespace Heap {
struct QmlIncubatorObject : Object {
- QmlIncubatorObject(QQmlIncubator::IncubationMode = QQmlIncubator::Asynchronous);
- QScopedPointer<QQmlComponentIncubator> incubator;
- QPointer<QObject> parent;
+ void init(QQmlIncubator::IncubationMode = QQmlIncubator::Asynchronous);
+ inline void destroy();
+ QQmlComponentIncubator *incubator;
+ QQmlQPointer<QObject> parent;
QV4::Value valuemap;
QV4::Value statusChanged;
Pointer<Heap::QmlContext> qmlContext;
@@ -1196,7 +1179,7 @@ static void QQmlComponent_setQmlParent(QObject *me, QObject *parent)
*/
-static void setInitialProperties(QV4::ExecutionEngine *engine, QV4::QmlContext *qmlContext, const QV4::Value &o, const QV4::Value &v)
+void QQmlComponentPrivate::setInitialProperties(QV4::ExecutionEngine *engine, QV4::QmlContext *qmlContext, const QV4::Value &o, const QV4::Value &v)
{
QV4::Scope scope(engine);
QV4::ScopedObject object(scope);
@@ -1285,7 +1268,7 @@ void QQmlComponent::createObject(QQmlV4Function *args)
if (!valuemap->isUndefined()) {
QV4::Scoped<QV4::QmlContext> qmlContext(scope, v4->qmlContext());
- setInitialProperties(v4, qmlContext, object, valuemap);
+ QQmlComponentPrivate::setInitialProperties(v4, qmlContext, object, valuemap);
}
d->completeCreate();
@@ -1406,7 +1389,7 @@ void QQmlComponent::incubateObject(QQmlV4Function *args)
r->d()->qmlContext = v4->qmlContext();
r->d()->parent = parent;
- QQmlIncubator *incubator = r->d()->incubator.data();
+ QQmlIncubator *incubator = r->d()->incubator;
create(*incubator, creationContext());
if (incubator->status() == QQmlIncubator::Null) {
@@ -1501,12 +1484,20 @@ QQmlComponentExtension::~QQmlComponentExtension()
{
}
-QV4::Heap::QmlIncubatorObject::QmlIncubatorObject(QQmlIncubator::IncubationMode m)
- : valuemap(QV4::Primitive::undefinedValue())
- , statusChanged(QV4::Primitive::undefinedValue())
- , qmlContext(0)
+void QV4::Heap::QmlIncubatorObject::init(QQmlIncubator::IncubationMode m)
{
- incubator.reset(new QQmlComponentIncubator(this, m));
+ Object::init();
+ valuemap = QV4::Primitive::undefinedValue();
+ statusChanged = QV4::Primitive::undefinedValue();
+ parent.init();
+ qmlContext = nullptr;
+ incubator = new QQmlComponentIncubator(this, m);
+}
+
+void QV4::Heap::QmlIncubatorObject::destroy() {
+ delete incubator;
+ parent.destroy();
+ Object::destroy();
}
void QV4::QmlIncubatorObject::setInitialState(QObject *o)
@@ -1518,7 +1509,7 @@ void QV4::QmlIncubatorObject::setInitialState(QObject *o)
QV4::Scope scope(v4);
QV4::ScopedObject obj(scope, QV4::QObjectWrapper::wrap(v4, o));
QV4::Scoped<QV4::QmlContext> qmlCtxt(scope, d()->qmlContext);
- setInitialProperties(v4, qmlCtxt, obj, d()->valuemap);
+ QQmlComponentPrivate::setInitialProperties(v4, qmlCtxt, obj, d()->valuemap);
}
}
@@ -1549,7 +1540,7 @@ void QV4::QmlIncubatorObject::statusChanged(QQmlIncubator::Status s)
QV4::ScopedCallData callData(scope, 1);
callData->thisObject = this;
callData->args[0] = QV4::Primitive::fromUInt32(s);
- f->call(callData);
+ f->call(scope, callData);
if (scope.hasException()) {
QQmlError error = scope.engine->catchExceptionAsQmlError();
QQmlEnginePrivate::warning(QQmlEnginePrivate::get(scope.engine->qmlEngine()), error);
diff --git a/src/qml/qml/qqmlcomponent.h b/src/qml/qml/qqmlcomponent.h
index aefbf20aff..ca60f01eb5 100644
--- a/src/qml/qml/qqmlcomponent.h
+++ b/src/qml/qml/qqmlcomponent.h
@@ -55,10 +55,15 @@ class QQmlEngine;
class QQmlComponent;
class QQmlIncubator;
class QQmlV4Function;
-class QQmlCompiledData;
class QQmlComponentPrivate;
class QQmlComponentAttached;
+namespace QV4 {
+namespace CompiledData {
+struct CompilationUnit;
+}
+}
+
class Q_QML_EXPORT QQmlComponent : public QObject
{
Q_OBJECT
@@ -122,7 +127,7 @@ protected:
Q_INVOKABLE void incubateObject(QQmlV4Function *);
private:
- QQmlComponent(QQmlEngine *, QQmlCompiledData *, int, QObject *parent);
+ QQmlComponent(QQmlEngine *, QV4::CompiledData::CompilationUnit *compilationUnit, int, QObject *parent);
Q_DISABLE_COPY(QQmlComponent)
friend class QQmlTypeData;
diff --git a/src/qml/qml/qqmlcomponent_p.h b/src/qml/qml/qqmlcomponent_p.h
index 039b267433..3a84e724da 100644
--- a/src/qml/qml/qqmlcomponent_p.h
+++ b/src/qml/qml/qqmlcomponent_p.h
@@ -71,7 +71,6 @@ QT_BEGIN_NAMESPACE
class QQmlComponent;
class QQmlEngine;
-class QQmlCompiledData;
class QQmlComponentAttached;
class Q_QML_PRIVATE_EXPORT QQmlComponentPrivate : public QObjectPrivate, public QQmlTypeData::TypeDataCallback
@@ -80,13 +79,14 @@ class Q_QML_PRIVATE_EXPORT QQmlComponentPrivate : public QObjectPrivate, public
public:
QQmlComponentPrivate()
- : typeData(0), progress(0.), start(-1), cc(0), engine(0), creationContext(0), depthIncreased(false) {}
+ : typeData(0), progress(0.), start(-1), engine(0), creationContext(0), depthIncreased(false) {}
void loadUrl(const QUrl &newUrl, QQmlComponent::CompilationMode mode = QQmlComponent::PreferSynchronous);
QObject *beginCreate(QQmlContextData *);
void completeCreate();
void initializeObjectWithInitialProperties(QV4::QmlContext *qmlContext, const QV4::Value &valuemap, QObject *toCreate);
+ static void setInitialProperties(QV4::ExecutionEngine *engine, QV4::QmlContext *qmlContext, const QV4::Value &o, const QV4::Value &v);
QQmlTypeData *typeData;
virtual void typeDataReady(QQmlTypeData *);
@@ -98,7 +98,7 @@ public:
qreal progress;
int start;
- QQmlCompiledData *cc;
+ QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit;
struct ConstructionState {
ConstructionState()
diff --git a/src/qml/qml/qqmlcontext.cpp b/src/qml/qml/qqmlcontext.cpp
index 65a337f4e5..018841b9b1 100644
--- a/src/qml/qml/qqmlcontext.cpp
+++ b/src/qml/qml/qqmlcontext.cpp
@@ -310,7 +310,7 @@ void QQmlContext::setContextProperty(const QString &name, const QVariant &value)
}
}
- QV4::IdentifierHash<int> &properties = data->propertyNames();
+ QV4::IdentifierHash<int> &properties = data->detachedPropertyNames();
int idx = properties.value(name);
if (idx == -1) {
properties.add(name, data->idValueCount + d->propertyValues.count());
@@ -346,7 +346,7 @@ void QQmlContext::setContextProperty(const QString &name, QObject *value)
return;
}
- QV4::IdentifierHash<int> &properties = data->propertyNames();
+ QV4::IdentifierHash<int> &properties = data->detachedPropertyNames();
int idx = properties.value(name);
if (idx == -1) {
@@ -383,7 +383,7 @@ QVariant QQmlContext::contextProperty(const QString &name) const
QQmlPropertyData *property =
QQmlPropertyCache::property(data->engine, obj, name, data, local);
- if (property) value = obj->metaObject()->property(property->coreIndex).read(obj);
+ if (property) value = obj->metaObject()->property(property->coreIndex()).read(obj);
}
if (!value.isValid() && parentContext())
value = parentContext()->contextProperty(name);
@@ -516,20 +516,15 @@ QObject *QQmlContextPrivate::context_at(QQmlListProperty<QObject> *prop, int ind
QQmlContextData::QQmlContextData()
-: parent(0), engine(0), isInternal(false), ownedByParent(false), isJSContext(false),
- isPragmaLibraryContext(false), unresolvedNames(false), hasEmittedDestruction(false), isRootObjectInCreation(false),
- publicContext(0), activeVMEData(0),
- contextObject(0), imports(0), childContexts(0), nextChild(0), prevChild(0),
- expressions(0), contextObjects(0), contextGuards(0), idValues(0), idValueCount(0), linkedContext(0),
- componentAttached(0)
+ : QQmlContextData(nullptr)
{
}
QQmlContextData::QQmlContextData(QQmlContext *ctxt)
: parent(0), engine(0), isInternal(false), ownedByParent(false), isJSContext(false),
isPragmaLibraryContext(false), unresolvedNames(false), hasEmittedDestruction(false), isRootObjectInCreation(false),
- publicContext(ctxt), activeVMEData(0),
- contextObject(0), imports(0), childContexts(0), nextChild(0), prevChild(0),
+ publicContext(ctxt), activeVMEData(0), componentObjectIndex(-1),
+ contextObject(0), childContexts(0), nextChild(0), prevChild(0),
expressions(0), contextObjects(0), contextGuards(0), idValues(0), idValueCount(0), linkedContext(0),
componentAttached(0)
{
@@ -633,9 +628,6 @@ void QQmlContextData::destroy()
}
contextGuards = 0;
- if (imports)
- imports->release();
-
delete [] idValues;
if (isInternal)
@@ -765,15 +757,6 @@ void QQmlContextData::setIdProperty(int idx, QObject *obj)
idValues[idx].context = this;
}
-void QQmlContextData::setIdPropertyData(const QHash<int, int> &data)
-{
- Q_ASSERT(objectIndexToId.isEmpty());
- objectIndexToId = data;
- Q_ASSERT(propertyNameCache.isEmpty());
- idValueCount = data.count();
- idValues = new ContextGuard[idValueCount];
-}
-
QString QQmlContextData::findObjectId(const QObject *obj) const
{
const QV4::IdentifierHash<int> &properties = propertyNames();
@@ -809,21 +792,33 @@ QQmlContextPrivate *QQmlContextData::asQQmlContextPrivate()
return QQmlContextPrivate::get(asQQmlContext());
}
-QV4::IdentifierHash<int> &QQmlContextData::propertyNames() const
+void QQmlContextData::initFromTypeCompilationUnit(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &unit, int subComponentIndex)
+{
+ typeCompilationUnit = unit;
+ componentObjectIndex = subComponentIndex == -1 ? typeCompilationUnit->data->indexOfRootObject : subComponentIndex;
+ Q_ASSERT(!idValues);
+ idValueCount = typeCompilationUnit->data->objectAt(componentObjectIndex)->nNamedObjectsInComponent;
+ idValues = new ContextGuard[idValueCount];
+}
+
+const QV4::IdentifierHash<int> &QQmlContextData::propertyNames() const
{
if (propertyNameCache.isEmpty()) {
- propertyNameCache = QV4::IdentifierHash<int>(QV8Engine::getV4(engine->handle()));
- for (QHash<int, int>::ConstIterator it = objectIndexToId.cbegin(), end = objectIndexToId.cend();
- it != end; ++it) {
- const QV4::CompiledData::Object *obj = typeCompilationUnit->data->objectAt(it.key());
- const QString name = typeCompilationUnit->data->stringAt(obj->idIndex);
- propertyNameCache.add(name, it.value());
- }
- objectIndexToId.clear();
+ if (typeCompilationUnit)
+ propertyNameCache = typeCompilationUnit->namedObjectsPerComponent(componentObjectIndex);
+ else
+ propertyNameCache = QV4::IdentifierHash<int>(QV8Engine::getV4(engine));
}
return propertyNameCache;
}
+QV4::IdentifierHash<int> &QQmlContextData::detachedPropertyNames()
+{
+ propertyNames();
+ propertyNameCache.detach();
+ return propertyNameCache;
+}
+
QUrl QQmlContextData::url() const
{
if (typeCompilationUnit)
diff --git a/src/qml/qml/qqmlcontext_p.h b/src/qml/qml/qqmlcontext_p.h
index 48d596418d..62cd3d4877 100644
--- a/src/qml/qml/qqmlcontext_p.h
+++ b/src/qml/qml/qqmlcontext_p.h
@@ -151,9 +151,15 @@ public:
// Compilation unit for contexts that belong to a compiled type.
QQmlRefPointer<QV4::CompiledData::CompilationUnit> typeCompilationUnit;
- mutable QHash<int, int> objectIndexToId;
+ // object index in CompiledData::Unit to component that created this context
+ int componentObjectIndex;
+
+ void initFromTypeCompilationUnit(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &unit, int subComponentIndex);
+
+ // flag indicates whether the context owns the cache (after mutation) or not.
mutable QV4::IdentifierHash<int> propertyNameCache;
- QV4::IdentifierHash<int> &propertyNames() const;
+ const QV4::IdentifierHash<int> &propertyNames() const;
+ QV4::IdentifierHash<int> &detachedPropertyNames();
// Context object
QObject *contextObject;
@@ -168,7 +174,7 @@ public:
QString urlString() const;
// List of imports that apply to this context
- QQmlTypeNameCache *imports;
+ QQmlRefPointer<QQmlTypeNameCache> imports;
// My children
QQmlContextData *childContexts;
@@ -201,7 +207,6 @@ public:
ContextGuard *idValues;
int idValueCount;
void setIdProperty(int, QObject *);
- void setIdPropertyData(const QHash<int, int> &);
// Linked contexts. this owns linkedContext.
QQmlContextData *linkedContext;
diff --git a/src/qml/qml/qqmlcontextwrapper.cpp b/src/qml/qml/qqmlcontextwrapper.cpp
index 2d0ebad764..2418003519 100644
--- a/src/qml/qml/qqmlcontextwrapper.cpp
+++ b/src/qml/qml/qqmlcontextwrapper.cpp
@@ -61,19 +61,23 @@ using namespace QV4;
DEFINE_OBJECT_VTABLE(QmlContextWrapper);
-Heap::QmlContextWrapper::QmlContextWrapper(QQmlContextData *context, QObject *scopeObject, bool ownsContext)
- : readOnly(true)
- , ownsContext(ownsContext)
- , isNullWrapper(false)
- , context(context)
- , scopeObject(scopeObject)
+void Heap::QmlContextWrapper::init(QQmlContextData *context, QObject *scopeObject, bool ownsContext)
{
+ Object::init();
+ readOnly = true;
+ this->ownsContext = ownsContext;
+ isNullWrapper = false;
+ this->context = new QQmlGuardedContextData(context);
+ this->scopeObject.init(scopeObject);
}
-Heap::QmlContextWrapper::~QmlContextWrapper()
+void Heap::QmlContextWrapper::destroy()
{
- if (context && ownsContext)
- context->destroy();
+ if (*context && ownsContext)
+ (*context)->destroy();
+ delete context;
+ scopeObject.destroy();
+ Object::destroy();
}
ReturnedValue QmlContextWrapper::qmlScope(ExecutionEngine *v4, QQmlContextData *ctxt, QObject *scope)
@@ -119,7 +123,7 @@ ReturnedValue QmlContextWrapper::get(const Managed *m, String *name, bool *hasPr
if (resource->d()->isNullWrapper)
return Object::get(m, name, hasProperty);
- if (v4->callingQmlContext() != resource->d()->context)
+ if (v4->callingQmlContext() != *resource->d()->context)
return Object::get(m, name, hasProperty);
result = Object::get(m, name, &hasProp);
diff --git a/src/qml/qml/qqmlcontextwrapper_p.h b/src/qml/qml/qqmlcontextwrapper_p.h
index ca7fcf1d75..126ffecf0d 100644
--- a/src/qml/qml/qqmlcontextwrapper_p.h
+++ b/src/qml/qml/qqmlcontextwrapper_p.h
@@ -64,14 +64,14 @@ namespace QV4 {
namespace Heap {
struct QmlContextWrapper : Object {
- QmlContextWrapper(QQmlContextData *context, QObject *scopeObject, bool ownsContext = false);
- ~QmlContextWrapper();
+ void init(QQmlContextData *context, QObject *scopeObject, bool ownsContext = false);
+ void destroy();
bool readOnly;
bool ownsContext;
bool isNullWrapper;
- QQmlGuardedContextData context;
- QPointer<QObject> scopeObject;
+ QQmlGuardedContextData *context;
+ QQmlQPointer<QObject> scopeObject;
};
}
@@ -89,7 +89,7 @@ struct Q_QML_EXPORT QmlContextWrapper : Object
}
inline QObject *getScopeObject() const { return d()->scopeObject; }
- inline QQmlContextData *getContext() const { return d()->context; }
+ inline QQmlContextData *getContext() const { return *d()->context; }
void setReadOnly(bool b) { d()->readOnly = b; }
diff --git a/src/qml/qml/qqmlcustomparser.cpp b/src/qml/qml/qqmlcustomparser.cpp
index 134002cf33..85c91a592a 100644
--- a/src/qml/qml/qqmlcustomparser.cpp
+++ b/src/qml/qml/qqmlcustomparser.cpp
@@ -39,7 +39,6 @@
#include "qqmlcustomparser_p.h"
-#include "qqmlcompiler_p.h"
#include <private/qqmltypecompiler_p.h>
#include <QtCore/qdebug.h>
@@ -101,11 +100,7 @@ void QQmlCustomParser::clearErrors()
*/
void QQmlCustomParser::error(const QV4::CompiledData::Location &location, const QString &description)
{
- QQmlError error;
- error.setLine(location.line);
- error.setColumn(location.column);
- error.setDescription(description);
- exceptions << error;
+ exceptions << QQmlCompileError(location, description);
}
struct StaticQtMetaObject : public QObject
@@ -166,11 +161,13 @@ int QQmlCustomParser::evaluateEnum(const QByteArray& script, bool *ok) const
*/
const QMetaObject *QQmlCustomParser::resolveType(const QString& name) const
{
+ if (!imports.isT1())
+ return nullptr;
QQmlType *qmltype = 0;
- if (!validator->imports().resolveType(name, &qmltype, 0, 0, 0))
- return 0;
+ if (!imports.asT1()->resolveType(name, &qmltype, 0, 0, 0))
+ return nullptr;
if (!qmltype)
- return 0;
+ return nullptr;
return qmltype->metaObject();
}
diff --git a/src/qml/qml/qqmlcustomparser_p.h b/src/qml/qml/qqmlcustomparser_p.h
index d8e25c55fd..5eb409990d 100644
--- a/src/qml/qml/qqmlcustomparser_p.h
+++ b/src/qml/qml/qqmlcustomparser_p.h
@@ -54,13 +54,13 @@
#include "qqmlmetatype_p.h"
#include "qqmlerror.h"
#include "qqmlbinding_p.h"
+#include <private/qqmltypecompiler_p.h>
#include <QtCore/qbytearray.h>
#include <QtCore/qxmlstream.h>
QT_BEGIN_NAMESPACE
-class QQmlCompiledData;
class QQmlPropertyValidator;
class QQmlEnginePrivate;
@@ -82,9 +82,9 @@ public:
Flags flags() const { return m_flags; }
virtual void verifyBindings(const QV4::CompiledData::Unit *, const QList<const QV4::CompiledData::Binding *> &) = 0;
- virtual void applyBindings(QObject *, QQmlCompiledData *, const QList<const QV4::CompiledData::Binding *> &) = 0;
+ virtual void applyBindings(QObject *, QV4::CompiledData::CompilationUnit *, const QList<const QV4::CompiledData::Binding *> &) = 0;
- QList<QQmlError> errors() const { return exceptions; }
+ QVector<QQmlCompileError> errors() const { return exceptions; }
protected:
void error(const QV4::CompiledData::Binding *binding, const QString& description)
@@ -98,7 +98,7 @@ protected:
const QMetaObject *resolveType(const QString&) const;
private:
- QList<QQmlError> exceptions;
+ QVector<QQmlCompileError> exceptions;
QQmlEnginePrivate *engine;
const QQmlPropertyValidator *validator;
Flags m_flags;
diff --git a/src/qml/qml/qqmldata_p.h b/src/qml/qml/qqmldata_p.h
index 7ccab5746d..e271598c2d 100644
--- a/src/qml/qml/qqmldata_p.h
+++ b/src/qml/qml/qqmldata_p.h
@@ -53,7 +53,7 @@
#include <private/qtqmlglobal_p.h>
#include <private/qobject_p.h>
-
+#include <private/qqmlpropertyindex_p.h>
#include <private/qv4value_p.h>
#include <private/qv4persistent_p.h>
#include <qjsengine.h>
@@ -63,7 +63,6 @@ QT_BEGIN_NAMESPACE
template <class Key, class T> class QHash;
class QQmlEngine;
class QQmlGuardImpl;
-class QQmlCompiledData;
class QQmlAbstractBinding;
class QQmlBoundSignal;
class QQmlContext;
@@ -72,6 +71,13 @@ class QQmlContextData;
class QQmlNotifier;
class QQmlDataExtended;
class QQmlNotifierEndpoint;
+
+namespace QV4 {
+namespace CompiledData {
+struct CompilationUnit;
+}
+}
+
// This class is structured in such a way, that simply zero'ing it is the
// default state for elemental object allocations. This is crucial in the
// workings of the QQmlInstruction::CreateSimpleObject instruction.
@@ -123,14 +129,16 @@ public:
quint32 parentFrozen:1;
quint32 dummy:21;
- // When bindingBitsSize < 32, we store the binding bit flags inside
- // bindingBitsValue. When we need more than 32 bits, we allocated
+ // When bindingBitsSize < sizeof(ptr), we store the binding bit flags inside
+ // bindingBitsValue. When we need more than sizeof(ptr) bits, we allocated
// sufficient space and use bindingBits to point to it.
int bindingBitsSize;
+ typedef quintptr BindingBitsType;
union {
- quint32 *bindingBits;
- quint32 bindingBitsValue;
+ BindingBitsType *bindingBits;
+ BindingBitsType bindingBitsValue;
};
+ enum { MaxInlineBits = sizeof(BindingBitsType) * 8 };
struct NotifyList {
quint64 connectionMask;
@@ -168,7 +176,7 @@ public:
void clearBindingBit(int);
void setBindingBit(QObject *obj, int);
- inline bool hasPendingBindingBit(int) const;
+ inline bool hasPendingBindingBit(int index) const;
void setPendingBindingBit(QObject *obj, int);
void clearPendingBindingBit(int);
@@ -179,10 +187,10 @@ public:
struct DeferredData {
unsigned int deferredIdx;
- QQmlCompiledData *compiledData;//Not always the same as the other compiledData
+ QV4::CompiledData::CompilationUnit *compilationUnit;//Not always the same as the other compilation unit
QQmlContextData *context;//Could be either context or outerContext
};
- QQmlCompiledData *compiledData;
+ QV4::CompiledData::CompilationUnit *compilationUnit;
DeferredData *deferredData;
QV4::WeakValue jsWrapper;
@@ -199,8 +207,7 @@ public:
} else if (priv->declarativeData) {
return static_cast<QQmlData *>(priv->declarativeData);
} else if (create) {
- priv->declarativeData = new QQmlData;
- return static_cast<QQmlData *>(priv->declarativeData);
+ return createQQmlData(priv);
} else {
return 0;
}
@@ -221,15 +228,36 @@ public:
static void markAsDeleted(QObject *);
static void setQueuedForDeletion(QObject *);
- static inline void flushPendingBinding(QObject *, int coreIndex);
+ static inline void flushPendingBinding(QObject *, QQmlPropertyIndex propertyIndex);
- static QQmlPropertyCache *ensurePropertyCache(QJSEngine *engine, QObject *object);
+ static QQmlPropertyCache *ensurePropertyCache(QJSEngine *engine, QObject *object)
+ {
+ Q_ASSERT(engine);
+ QQmlData *ddata = QQmlData::get(object, /*create*/true);
+ if (Q_LIKELY(ddata->propertyCache))
+ return ddata->propertyCache;
+ return createPropertyCache(engine, object);
+ }
private:
// For attachedProperties
mutable QQmlDataExtended *extendedData;
- void flushPendingBindingImpl(int coreIndex);
+ Q_NEVER_INLINE static QQmlData *createQQmlData(QObjectPrivate *priv);
+ Q_NEVER_INLINE static QQmlPropertyCache *createPropertyCache(QJSEngine *engine, QObject *object);
+
+ void flushPendingBindingImpl(QQmlPropertyIndex index);
+
+ Q_ALWAYS_INLINE bool hasBitSet(int bit) const
+ {
+ if (bindingBitsSize <= bit)
+ return false;
+
+ if (bindingBitsSize == MaxInlineBits)
+ return bindingBitsValue & (BindingBitsType(1) << bit);
+ else
+ return bindingBits[bit / MaxInlineBits] & (BindingBitsType(1) << (bit % MaxInlineBits));
+ }
};
bool QQmlData::wasDeleted(QObject *object)
@@ -275,27 +303,25 @@ inline bool QQmlData::signalHasEndpoint(int index) const
bool QQmlData::hasBindingBit(int coreIndex) const
{
- int bit = coreIndex * 2;
+ Q_ASSERT(coreIndex >= 0);
+ Q_ASSERT(coreIndex <= 0xffff);
- return bindingBitsSize > bit &&
- ((bindingBitsSize == 32) ? (bindingBitsValue & (1 << bit)) :
- (bindingBits[bit / 32] & (1 << (bit % 32))));
+ return hasBitSet(coreIndex * 2);
}
bool QQmlData::hasPendingBindingBit(int coreIndex) const
{
- int bit = coreIndex * 2 + 1;
+ Q_ASSERT(coreIndex >= 0);
+ Q_ASSERT(coreIndex <= 0xffff);
- return bindingBitsSize > bit &&
- ((bindingBitsSize == 32) ? (bindingBitsValue & (1 << bit)) :
- (bindingBits[bit / 32] & (1 << (bit % 32))));
+ return hasBitSet(coreIndex * 2 + 1);
}
-void QQmlData::flushPendingBinding(QObject *o, int coreIndex)
+void QQmlData::flushPendingBinding(QObject *o, QQmlPropertyIndex propertyIndex)
{
QQmlData *data = QQmlData::get(o, false);
- if (data && data->hasPendingBindingBit(coreIndex))
- data->flushPendingBindingImpl(coreIndex);
+ if (data && data->hasPendingBindingBit(propertyIndex.coreIndex()))
+ data->flushPendingBindingImpl(propertyIndex);
}
QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmldelayedcallqueue.cpp b/src/qml/qml/qqmldelayedcallqueue.cpp
new file mode 100644
index 0000000000..d10a8c7718
--- /dev/null
+++ b/src/qml/qml/qqmldelayedcallqueue.cpp
@@ -0,0 +1,215 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml 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 "qqmldelayedcallqueue_p.h"
+#include <private/qv8engine_p.h>
+#include <private/qqmlengine_p.h>
+#include <private/qqmljavascriptexpression_p.h>
+#include <private/qv4value_p.h>
+#include <private/qv4qobjectwrapper_p.h>
+#include <private/qqmlcontextwrapper_p.h>
+
+#include <QQmlError>
+
+QT_BEGIN_NAMESPACE
+
+//
+// struct QQmlDelayedCallQueue::DelayedFunctionCall
+//
+
+void QQmlDelayedCallQueue::DelayedFunctionCall::execute(QV4::ExecutionEngine *engine) const
+{
+ if (!m_guarded ||
+ (!m_objectGuard.isNull() &&
+ !QQmlData::wasDeleted(m_objectGuard) &&
+ QQmlData::get(m_objectGuard) &&
+ !QQmlData::get(m_objectGuard)->isQueuedForDeletion)) {
+
+ QV4::Scope scope(engine);
+
+ QV4::ArrayObject *array = m_args.as<QV4::ArrayObject>();
+ const int argCount = array ? array->getLength() : 0;
+ QV4::ScopedCallData callData(scope, argCount);
+ callData->thisObject = QV4::Encode::undefined();
+
+ for (int i = 0; i < argCount; i++) {
+ callData->args[i] = array->getIndexed(i);
+ }
+
+ const QV4::FunctionObject *callback = m_function.as<QV4::FunctionObject>();
+ Q_ASSERT(callback);
+ callback->call(scope, callData);
+
+ if (scope.engine->hasException) {
+ QQmlError error = scope.engine->catchExceptionAsQmlError();
+ error.setDescription(error.description() + QLatin1String(" (exception occurred during delayed function evaluation)"));
+ QQmlEnginePrivate::warning(QQmlEnginePrivate::get(scope.engine->qmlEngine()), error);
+ }
+ }
+}
+
+//
+// class QQmlDelayedCallQueue
+//
+
+QQmlDelayedCallQueue::QQmlDelayedCallQueue()
+ : QObject(0), m_engine(0), m_callbackOutstanding(false)
+{
+}
+
+QQmlDelayedCallQueue::~QQmlDelayedCallQueue()
+{
+}
+
+void QQmlDelayedCallQueue::init(QV4::ExecutionEngine* engine)
+{
+ m_engine = engine;
+
+ const QMetaObject &metaObject = QQmlDelayedCallQueue::staticMetaObject;
+ int methodIndex = metaObject.indexOfSlot("ticked()");
+ m_tickedMethod = metaObject.method(methodIndex);
+}
+
+QV4::ReturnedValue QQmlDelayedCallQueue::addUniquelyAndExecuteLater(QV4::CallContext *ctx)
+{
+ const QV4::CallData *callData = ctx->d()->callData;
+
+ if (callData->argc == 0)
+ V4THROW_ERROR("Qt.callLater: no arguments given");
+
+ const QV4::FunctionObject *func = callData->args[0].as<QV4::FunctionObject>();
+
+ if (!func)
+ V4THROW_ERROR("Qt.callLater: first argument not a function or signal");
+
+ QPair<QObject *, int> functionData = QV4::QObjectMethod::extractQtMethod(func);
+
+ QVector<DelayedFunctionCall>::Iterator iter;
+ if (functionData.second != -1) {
+ // This is a QObject function wrapper
+ iter = m_delayedFunctionCalls.begin();
+ while (iter != m_delayedFunctionCalls.end()) {
+ DelayedFunctionCall& dfc = *iter;
+ QPair<QObject *, int> storedFunctionData = QV4::QObjectMethod::extractQtMethod(dfc.m_function.as<QV4::FunctionObject>());
+ if (storedFunctionData == functionData) {
+ break; // Already stored!
+ }
+ ++iter;
+ }
+ } else {
+ // This is a JavaScript function (dynamic slot on VMEMO)
+ iter = m_delayedFunctionCalls.begin();
+ while (iter != m_delayedFunctionCalls.end()) {
+ DelayedFunctionCall& dfc = *iter;
+ if (callData->argument(0) == dfc.m_function.value()) {
+ break; // Already stored!
+ }
+ ++iter;
+ }
+ }
+
+ const bool functionAlreadyStored = (iter != m_delayedFunctionCalls.end());
+ if (functionAlreadyStored) {
+ DelayedFunctionCall dfc = *iter;
+ m_delayedFunctionCalls.erase(iter);
+ m_delayedFunctionCalls.append(dfc);
+ } else {
+ m_delayedFunctionCalls.append(QV4::PersistentValue(m_engine, callData->argument(0)));
+ }
+
+ DelayedFunctionCall& dfc = m_delayedFunctionCalls.last();
+ if (dfc.m_objectGuard.isNull()) {
+ if (functionData.second != -1) {
+ // if it's a qobject function wrapper, guard against qobject deletion
+ dfc.m_objectGuard = QQmlGuard<QObject>(functionData.first);
+ dfc.m_guarded = true;
+ } else if (func->scope()->type == QV4::Heap::ExecutionContext::Type_QmlContext) {
+ QV4::QmlContext::Data *g = static_cast<QV4::QmlContext::Data *>(func->scope());
+ Q_ASSERT(g->qml->scopeObject);
+ dfc.m_objectGuard = QQmlGuard<QObject>(g->qml->scopeObject);
+ dfc.m_guarded = true;
+ }
+ }
+ storeAnyArguments(dfc, callData, 1, m_engine);
+
+ if (!m_callbackOutstanding) {
+ m_tickedMethod.invoke(this, Qt::QueuedConnection);
+ m_callbackOutstanding = true;
+ }
+ return QV4::Encode::undefined();
+}
+
+void QQmlDelayedCallQueue::storeAnyArguments(DelayedFunctionCall &dfc, const QV4::CallData *callData, int offset, QV4::ExecutionEngine *engine)
+{
+ const int length = callData->argc - offset;
+ if (length == 0) {
+ dfc.m_args.clear();
+ return;
+ }
+ QV4::Scope scope(engine);
+ QV4::ScopedArrayObject array(scope, engine->newArrayObject(length));
+ int i = 0;
+ for (int j = offset; j < callData->argc; ++i, ++j) {
+ array->putIndexed(i, callData->args[j]);
+ }
+ dfc.m_args.set(engine, array);
+}
+
+void QQmlDelayedCallQueue::executeAllExpired_Later()
+{
+ // Make a local copy of the list and clear m_delayedFunctionCalls
+ // This ensures correct behavior in the case of recursive calls to Qt.callLater()
+ QVector<DelayedFunctionCall> delayedCalls = m_delayedFunctionCalls;
+ m_delayedFunctionCalls.clear();
+
+ QVector<DelayedFunctionCall>::Iterator iter = delayedCalls.begin();
+ while (iter != delayedCalls.end()) {
+ DelayedFunctionCall& dfc = *iter;
+ dfc.execute(m_engine);
+ ++iter;
+ }
+}
+
+void QQmlDelayedCallQueue::ticked()
+{
+ m_callbackOutstanding = false;
+ executeAllExpired_Later();
+}
+
+QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmldelayedcallqueue_p.h b/src/qml/qml/qqmldelayedcallqueue_p.h
new file mode 100644
index 0000000000..ef899170a2
--- /dev/null
+++ b/src/qml/qml/qqmldelayedcallqueue_p.h
@@ -0,0 +1,104 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml 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 QQMLDELAYEDCALLQUEUE_P_H
+#define QQMLDELAYEDCALLQUEUE_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/qglobal.h>
+#include <QtCore/qobject.h>
+#include <QtCore/qmetaobject.h>
+#include <QtCore/qmetatype.h>
+#include <private/qqmlguard_p.h>
+#include <private/qv4context_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QV8Engine;
+class QQmlDelayedCallQueue : public QObject
+{
+ Q_OBJECT
+public:
+ QQmlDelayedCallQueue();
+ ~QQmlDelayedCallQueue();
+
+ void init(QV4::ExecutionEngine *);
+
+ QV4::ReturnedValue addUniquelyAndExecuteLater(QV4::CallContext *ctx);
+
+public Q_SLOTS:
+ void ticked();
+
+private:
+ struct DelayedFunctionCall
+ {
+ DelayedFunctionCall() {}
+ DelayedFunctionCall(QV4::PersistentValue function)
+ : m_function(function), m_guarded(false) { }
+
+ void execute(QV4::ExecutionEngine *engine) const;
+
+ QV4::PersistentValue m_function;
+ QV4::PersistentValue m_args;
+ QQmlGuard<QObject> m_objectGuard;
+ bool m_guarded;
+ };
+
+ void storeAnyArguments(DelayedFunctionCall& dfc, const QV4::CallData *callData, int offset, QV4::ExecutionEngine *engine);
+ void executeAllExpired_Later();
+
+ QV4::ExecutionEngine *m_engine;
+ QVector<DelayedFunctionCall> m_delayedFunctionCalls;
+ QMetaMethod m_tickedMethod;
+ bool m_callbackOutstanding;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLDELAYEDCALLQUEUE_P_H
diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp
index 71795a2539..7877ee7e21 100644
--- a/src/qml/qml/qqmlengine.cpp
+++ b/src/qml/qml/qqmlengine.cpp
@@ -42,7 +42,6 @@
#include "qqmlcomponentattached_p.h"
#include "qqmlcontext_p.h"
-#include "qqmlcompiler_p.h"
#include "qqml.h"
#include "qqmlcontext.h"
#include "qqmlexpression.h"
@@ -53,35 +52,33 @@
#include "qqmlscriptstring.h"
#include "qqmlglobal_p.h"
#include "qqmlcomponent_p.h"
-#include "qqmlnetworkaccessmanagerfactory.h"
#include "qqmldirparser_p.h"
#include "qqmlextensioninterface.h"
#include "qqmllist_p.h"
#include "qqmltypenamecache_p.h"
#include "qqmlnotifier_p.h"
-#include <private/qqmldebugconnector_p.h>
#include "qqmlincubator.h"
#include "qqmlabstracturlinterceptor.h"
#include <private/qqmlboundsignal_p.h>
-
#include <QtCore/qstandardpaths.h>
#include <QtCore/qsettings.h>
-
#include <QtCore/qmetaobject.h>
-#include <QNetworkAccessManager>
#include <QDebug>
#include <QtCore/qcoreapplication.h>
#include <QtCore/qdir.h>
#include <QtCore/qmutex.h>
#include <QtCore/qthread.h>
#include <private/qthread_p.h>
+
+#if QT_CONFIG(qml_network)
+#include "qqmlnetworkaccessmanagerfactory.h"
+#include <QNetworkAccessManager>
#include <QtNetwork/qnetworkconfigmanager.h>
+#endif
#include <private/qobject_p.h>
#include <private/qmetaobject_p.h>
-
#include <private/qqmllocale_p.h>
-
#include <private/qqmlbind_p.h>
#include <private/qqmlconnections_p.h>
#include <private/qqmltimer_p.h>
@@ -92,24 +89,26 @@
#include <private/qqmlobjectmodel_p.h>
#include <private/qquickworkerscript_p.h>
#include <private/qqmlinstantiator_p.h>
+#include <private/qqmlloggingcategory_p.h>
#ifdef Q_OS_WIN // for %APPDATA%
-#include <qt_windows.h>
-# if !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
+# include <qt_windows.h>
+# ifndef Q_OS_WINRT
# include <shlobj.h>
# endif
-#include <qlibrary.h>
-#include <windows.h>
-
-#ifndef CSIDL_APPDATA
-# define CSIDL_APPDATA 0x001a // <username>\Application Data
-#endif
-#endif
+# include <qlibrary.h>
+# ifndef CSIDL_APPDATA
+# define CSIDL_APPDATA 0x001a // <username>\Application Data
+# endif
+#endif // Q_OS_WIN
Q_DECLARE_METATYPE(QQmlProperty)
QT_BEGIN_NAMESPACE
+typedef QQmlData::BindingBitsType BindingBitsType;
+enum { MaxInlineBits = QQmlData::MaxInlineBits };
+
void qmlRegisterBaseTypes(const char *uri, int versionMajor, int versionMinor)
{
QQmlEnginePrivate::registerBaseTypes(uri, versionMajor, versionMinor);
@@ -117,6 +116,39 @@ void qmlRegisterBaseTypes(const char *uri, int versionMajor, int versionMinor)
QQmlValueTypeFactory::registerValueTypes(uri, versionMajor, versionMinor);
}
+// Declared in qqml.h
+int qmlRegisterUncreatableMetaObject(const QMetaObject &staticMetaObject,
+ const char *uri, int versionMajor,
+ int versionMinor, const char *qmlName,
+ const QString& reason)
+{
+ QQmlPrivate::RegisterType type = {
+ 0,
+
+ 0,
+ 0,
+ 0,
+ Q_NULLPTR,
+ reason,
+
+ uri, versionMajor, versionMinor, qmlName, &staticMetaObject,
+
+ QQmlAttachedPropertiesFunc(),
+ Q_NULLPTR,
+
+ 0,
+ 0,
+ 0,
+
+ Q_NULLPTR, Q_NULLPTR,
+
+ Q_NULLPTR,
+ 0
+ };
+
+ return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type);
+}
+
/*!
\qmltype QtObject
\instantiates QObject
@@ -182,12 +214,14 @@ void QQmlEnginePrivate::registerBaseTypes(const char *uri, int versionMajor, int
qmlRegisterType<QQmlComponent>(uri,versionMajor,versionMinor,"Component");
qmlRegisterType<QObject>(uri,versionMajor,versionMinor,"QtObject");
qmlRegisterType<QQmlBind>(uri, versionMajor, versionMinor,"Binding");
+ qmlRegisterType<QQmlBind,8>(uri, versionMajor, (versionMinor < 8 ? 8 : versionMinor), "Binding"); //Only available in >=2.8
qmlRegisterType<QQmlConnections,1>(uri, versionMajor, (versionMinor < 3 ? 3 : versionMinor), "Connections"); //Only available in >=2.3
qmlRegisterType<QQmlConnections>(uri, versionMajor, versionMinor,"Connections");
qmlRegisterType<QQmlTimer>(uri, versionMajor, versionMinor,"Timer");
qmlRegisterType<QQmlInstantiator>(uri, versionMajor, (versionMinor < 1 ? 1 : versionMinor), "Instantiator"); //Only available in >=2.1
qmlRegisterCustomType<QQmlConnections>(uri, versionMajor, versionMinor,"Connections", new QQmlConnectionsParser);
qmlRegisterType<QQmlInstanceModel>();
+ qmlRegisterType<QQmlLoggingCategory>(uri, versionMajor, (versionMinor < 8 ? 8 : versionMinor), "LoggingCategory"); //Only available in >=2.8
}
@@ -492,6 +526,10 @@ The following functions are also on the Qt object.
from right to left.
\endlist
\row
+ \li \c application.font
+ \li This read-only property holds the default application font as
+ returned by \l QGuiApplication::font().
+ \row
\li \c application.arguments
\li This is a string list of the arguments the executable was invoked with.
\row
@@ -530,6 +568,7 @@ The following functions are also on the Qt object.
\li application.active
\li application.state
\li application.layoutDirection
+ \li application.font
\endlist
*/
@@ -600,12 +639,17 @@ the same object as is returned from the Qt.include() call.
QQmlEnginePrivate::QQmlEnginePrivate(QQmlEngine *e)
: propertyCapture(0), rootContext(0),
- profiler(0), outputWarningsToMsgLog(true),
+#ifndef QT_NO_QML_DEBUGGER
+ profiler(0),
+#endif
+ outputWarningsToMsgLog(true),
cleanup(0), erroredBindings(0), inProgressCreations(0),
workerScriptEngine(0),
activeObjectCreator(0),
- networkAccessManager(0), networkAccessManagerFactory(0), urlInterceptor(0),
- scarceResourcesRefCount(0), importDatabase(e), typeLoader(e),
+#if QT_CONFIG(qml_network)
+ networkAccessManager(0), networkAccessManagerFactory(0),
+#endif
+ urlInterceptor(0), scarceResourcesRefCount(0), importDatabase(e), typeLoader(e),
uniqueId(1), incubatorCount(0), incubationController(0)
{
}
@@ -613,7 +657,6 @@ QQmlEnginePrivate::QQmlEnginePrivate(QQmlEngine *e)
QQmlEnginePrivate::~QQmlEnginePrivate()
{
typedef QHash<QPair<QQmlType *, int>, QQmlPropertyCache *>::const_iterator TypePropertyCacheIt;
- typedef QHash<int, QQmlCompiledData *>::const_iterator CompositeTypesIt;
if (inProgressCreations)
qWarning() << QQmlEngine::tr("There are still \"%1\" items in the process of being created at engine destruction.").arg(inProgressCreations);
@@ -634,7 +677,7 @@ QQmlEnginePrivate::~QQmlEnginePrivate()
for (TypePropertyCacheIt iter = typePropertyCache.cbegin(), end = typePropertyCache.cend(); iter != end; ++iter)
(*iter)->release();
- for (CompositeTypesIt iter = m_compositeTypes.cbegin(), end = m_compositeTypes.cend(); iter != end; ++iter) {
+ for (auto iter = m_compositeTypes.cbegin(), end = m_compositeTypes.cend(); iter != end; ++iter) {
iter.value()->isRegisteredWithEngine = false;
// since unregisterInternalCompositeType() will not be called in this
@@ -642,12 +685,9 @@ QQmlEnginePrivate::~QQmlEnginePrivate()
QMetaType::unregisterType(iter.value()->metaTypeId);
QMetaType::unregisterType(iter.value()->listMetaTypeId);
}
+#ifndef QT_NO_QML_DEBUGGER
delete profiler;
-}
-
-void QQmlEnginePrivate::enableProfiler()
-{
- profiler = new QQmlProfiler();
+#endif
}
void QQmlPrivate::qdeclarativeelement_destructor(QObject *o)
@@ -674,9 +714,10 @@ void QQmlPrivate::qdeclarativeelement_destructor(QObject *o)
QQmlData::QQmlData()
: ownedByQml1(false), ownMemory(true), ownContext(false), indestructible(true), explicitIndestructibleSet(false),
hasTaintedV4Object(false), isQueuedForDeletion(false), rootObjectInCreation(false),
- hasInterceptorMetaObject(false), hasVMEMetaObject(false), parentFrozen(false), bindingBitsSize(0), bindingBits(0), notifyList(0), context(0), outerContext(0),
+ hasInterceptorMetaObject(false), hasVMEMetaObject(false), parentFrozen(false),
+ bindingBitsSize(MaxInlineBits), bindingBitsValue(0), notifyList(0), context(0), outerContext(0),
bindings(0), signalHandlers(0), nextContextObject(0), prevContextObject(0),
- lineNumber(0), columnNumber(0), jsEngineId(0), compiledData(0), deferredData(0),
+ lineNumber(0), columnNumber(0), jsEngineId(0), compilationUnit(0), deferredData(0),
propertyCache(0), guards(0), extendedData(0)
{
init();
@@ -834,18 +875,20 @@ void QQmlData::setQueuedForDeletion(QObject *object)
}
}
-void QQmlData::flushPendingBindingImpl(int coreIndex)
+void QQmlData::flushPendingBindingImpl(QQmlPropertyIndex index)
{
- clearPendingBindingBit(coreIndex);
+ clearPendingBindingBit(index.coreIndex());
// Find the binding
QQmlAbstractBinding *b = bindings;
- while (b && b->targetPropertyIndex() != coreIndex)
+ while (b && (b->targetPropertyIndex().coreIndex() != index.coreIndex() ||
+ b->targetPropertyIndex().hasValueTypeIndex()))
b = b->nextBinding();
- if (b && b->targetPropertyIndex() == coreIndex)
- b->setEnabled(true, QQmlPropertyPrivate::BypassInterceptor |
- QQmlPropertyPrivate::DontRemoveBinding);
+ if (b && b->targetPropertyIndex().coreIndex() == index.coreIndex() &&
+ !b->targetPropertyIndex().hasValueTypeIndex())
+ b->setEnabled(true, QQmlPropertyData::BypassInterceptor |
+ QQmlPropertyData::DontRemoveBinding);
}
bool QQmlEnginePrivate::baseModulesUninitialized = true;
@@ -973,8 +1016,19 @@ QQmlEngine::~QQmlEngine()
/*! \fn void QQmlEngine::quit()
This signal is emitted when the QML loaded by the engine would like to quit.
+
+ \sa exit()
*/
+/*! \fn void QQmlEngine::exit(int retCode)
+ This signal is emitted when the QML loaded by the engine would like to exit
+ from the event loop with the specified return code.
+
+ \since 5.8
+ \sa quit()
+ */
+
+
/*! \fn void QQmlEngine::warnings(const QList<QQmlError> &warnings)
This signal is emitted when \a warnings messages are generated by QML.
*/
@@ -1065,7 +1119,17 @@ QQmlAbstractUrlInterceptor *QQmlEngine::urlInterceptor() const
return d->urlInterceptor;
}
+void QQmlEnginePrivate::registerFinalizeCallback(QObject *obj, int index)
+{
+ if (activeObjectCreator) {
+ activeObjectCreator->finalizeCallbacks()->append(qMakePair(QPointer<QObject>(obj), index));
+ } else {
+ void *args[] = { 0 };
+ QMetaObject::metacall(obj, QMetaObject::InvokeMetaMethod, index, args);
+ }
+}
+#if QT_CONFIG(qml_network)
/*!
Sets the \a factory to use for creating QNetworkAccessManager(s).
@@ -1094,16 +1158,6 @@ QQmlNetworkAccessManagerFactory *QQmlEngine::networkAccessManagerFactory() const
return d->networkAccessManagerFactory;
}
-void QQmlEnginePrivate::registerFinalizeCallback(QObject *obj, int index)
-{
- if (activeObjectCreator) {
- activeObjectCreator->finalizeCallbacks()->append(qMakePair(QPointer<QObject>(obj), index));
- } else {
- void *args[] = { 0 };
- QMetaObject::metacall(obj, QMetaObject::InvokeMetaMethod, index, args);
- }
-}
-
QNetworkAccessManager *QQmlEnginePrivate::createNetworkAccessManager(QObject *parent) const
{
QMutexLocker locker(&networkAccessManagerMutex);
@@ -1142,6 +1196,7 @@ QNetworkAccessManager *QQmlEngine::networkAccessManager() const
Q_D(const QQmlEngine);
return d->getNetworkAccessManager();
}
+#endif // qml_network
/*!
@@ -1404,7 +1459,7 @@ void qmlExecuteDeferred(QObject *object)
QQmlComponentPrivate::beginDeferred(ep, object, &state);
// Release the reference for the deferral action (we still have one from construction)
- data->deferredData->compiledData->release();
+ data->deferredData->compilationUnit->release();
delete data->deferredData;
data->deferredData = 0;
@@ -1635,13 +1690,13 @@ void QQmlData::destroyed(QObject *object)
if (bindings && !bindings->ref.deref())
delete bindings;
- if (compiledData) {
- compiledData->release();
- compiledData = 0;
+ if (compilationUnit) {
+ compilationUnit->release();
+ compilationUnit = 0;
}
if (deferredData) {
- deferredData->compiledData->release();
+ deferredData->compilationUnit->release();
delete deferredData;
deferredData = 0;
}
@@ -1664,7 +1719,7 @@ void QQmlData::destroyed(QObject *object)
QString source = expr->expression();
if (source.size() > 100) {
source.truncate(96);
- source.append(QStringLiteral(" ..."));
+ source.append(QLatin1String(" ..."));
}
locationString.append(source);
} else {
@@ -1684,7 +1739,7 @@ void QQmlData::destroyed(QObject *object)
signalHandler = next;
}
- if (bindingBitsSize > 32)
+ if (bindingBitsSize > MaxInlineBits)
free(bindingBits);
if (propertyCache)
@@ -1709,6 +1764,8 @@ void QQmlData::destroyed(QObject *object)
if (ownMemory)
delete this;
+ else
+ this->~QQmlData();
}
DEFINE_BOOL_CONFIG_OPTION(parentTest, QML_PARENT_TEST);
@@ -1732,31 +1789,27 @@ void QQmlData::parentChanged(QObject *object, QObject *parent)
static void QQmlData_setBit(QQmlData *data, QObject *obj, int bit)
{
- if (data->bindingBitsSize == 0 && bit < 32) {
- data->bindingBitsSize = 32;
- }
-
- if (data->bindingBitsSize <= bit) {
+ if (Q_UNLIKELY(data->bindingBitsSize <= bit)) {
int props = QQmlMetaObject(obj).propertyCount();
Q_ASSERT(bit < 2 * props);
- int arraySize = (2 * props + 31) / 32;
+ int arraySize = (2 * props + MaxInlineBits - 1) / MaxInlineBits;
Q_ASSERT(arraySize > 1);
// special handling for 32 here is to make sure we wipe the first byte
// when going from bindingBitsValue to bindingBits, and preserve the old
// set bits so we can restore them after the allocation
- int oldArraySize = data->bindingBitsSize > 32 ? data->bindingBitsSize / 32 : 0;
- quint32 oldValue = data->bindingBitsSize == 32 ? data->bindingBitsValue : 0;
+ int oldArraySize = data->bindingBitsSize > MaxInlineBits ? data->bindingBitsSize / MaxInlineBits : 0;
+ quintptr oldValue = data->bindingBitsSize == MaxInlineBits ? data->bindingBitsValue : 0;
- data->bindingBits = (quint32 *)realloc((data->bindingBitsSize == 32) ? 0 : data->bindingBits,
- arraySize * sizeof(quint32));
+ data->bindingBits = static_cast<BindingBitsType *>(realloc((data->bindingBitsSize == MaxInlineBits) ? 0 : data->bindingBits,
+ arraySize * sizeof(BindingBitsType)));
memset(data->bindingBits + oldArraySize,
0x00,
- sizeof(quint32) * (arraySize - oldArraySize));
+ sizeof(BindingBitsType) * (arraySize - oldArraySize));
- data->bindingBitsSize = arraySize * 32;
+ data->bindingBitsSize = arraySize * MaxInlineBits;
// reinstate bindingBitsValue after we dropped it
if (oldValue) {
@@ -1764,50 +1817,63 @@ static void QQmlData_setBit(QQmlData *data, QObject *obj, int bit)
}
}
- if (data->bindingBitsSize == 32)
- data->bindingBitsValue |= (1 << (bit % 32));
+ if (data->bindingBitsSize == MaxInlineBits)
+ data->bindingBitsValue |= BindingBitsType(1) << bit;
else
- data->bindingBits[bit / 32] |= (1 << (bit % 32));
+ data->bindingBits[bit / MaxInlineBits] |= (BindingBitsType(1) << (bit % MaxInlineBits));
}
static void QQmlData_clearBit(QQmlData *data, int bit)
{
if (data->bindingBitsSize > bit) {
- if (data->bindingBitsSize == 32)
- data->bindingBitsValue &= ~(1 << (bit % 32));
+ if (data->bindingBitsSize == MaxInlineBits)
+ data->bindingBitsValue &= ~(BindingBitsType(1) << (bit % MaxInlineBits));
else
- data->bindingBits[bit / 32] &= ~(1 << (bit % 32));
+ data->bindingBits[bit / MaxInlineBits] &= ~(BindingBitsType(1) << (bit % MaxInlineBits));
}
}
void QQmlData::clearBindingBit(int coreIndex)
{
+ Q_ASSERT(coreIndex >= 0);
+ Q_ASSERT(coreIndex <= 0xffff);
QQmlData_clearBit(this, coreIndex * 2);
}
void QQmlData::setBindingBit(QObject *obj, int coreIndex)
{
+ Q_ASSERT(coreIndex >= 0);
+ Q_ASSERT(coreIndex <= 0xffff);
QQmlData_setBit(this, obj, coreIndex * 2);
}
void QQmlData::clearPendingBindingBit(int coreIndex)
{
+ Q_ASSERT(coreIndex >= 0);
+ Q_ASSERT(coreIndex <= 0xffff);
QQmlData_clearBit(this, coreIndex * 2 + 1);
}
void QQmlData::setPendingBindingBit(QObject *obj, int coreIndex)
{
+ Q_ASSERT(coreIndex >= 0);
+ Q_ASSERT(coreIndex <= 0xffff);
QQmlData_setBit(this, obj, coreIndex * 2 + 1);
}
-QQmlPropertyCache *QQmlData::ensurePropertyCache(QJSEngine *engine, QObject *object)
+QQmlData *QQmlData::createQQmlData(QObjectPrivate *priv)
+{
+ Q_ASSERT(priv);
+ priv->declarativeData = new QQmlData;
+ return static_cast<QQmlData *>(priv->declarativeData);
+}
+
+QQmlPropertyCache *QQmlData::createPropertyCache(QJSEngine *engine, QObject *object)
{
- Q_ASSERT(engine);
QQmlData *ddata = QQmlData::get(object, /*create*/true);
- if (!ddata->propertyCache){
- ddata->propertyCache = QJSEnginePrivate::get(engine)->cache(object);
- if (ddata->propertyCache) ddata->propertyCache->addref();
- }
+ ddata->propertyCache = QJSEnginePrivate::get(engine)->cache(object);
+ if (ddata->propertyCache)
+ ddata->propertyCache->addref();
return ddata->propertyCache;
}
@@ -1820,6 +1886,14 @@ void QQmlEnginePrivate::sendQuit()
}
}
+void QQmlEnginePrivate::sendExit(int retCode)
+{
+ Q_Q(QQmlEngine);
+ if (q->receivers(SIGNAL(exit(int))) == 0)
+ qWarning("Signal QQmlEngine::exit() emitted, but no receivers connected to handle it.");
+ emit q->exit(retCode);
+}
+
static void dumpwarning(const QQmlError &error)
{
QMessageLogger(error.url().toString().toLatin1().constData(),
@@ -2219,9 +2293,9 @@ int QQmlEnginePrivate::listType(int t) const
QQmlMetaObject QQmlEnginePrivate::rawMetaObjectForType(int t) const
{
Locker locker(this);
- QHash<int, QQmlCompiledData *>::ConstIterator iter = m_compositeTypes.constFind(t);
+ auto iter = m_compositeTypes.constFind(t);
if (iter != m_compositeTypes.cend()) {
- return QQmlMetaObject((*iter)->rootPropertyCache);
+ return QQmlMetaObject((*iter)->rootPropertyCache());
} else {
QQmlType *type = QQmlMetaType::qmlType(t);
return QQmlMetaObject(type?type->baseMetaObject():0);
@@ -2231,9 +2305,9 @@ QQmlMetaObject QQmlEnginePrivate::rawMetaObjectForType(int t) const
QQmlMetaObject QQmlEnginePrivate::metaObjectForType(int t) const
{
Locker locker(this);
- QHash<int, QQmlCompiledData *>::ConstIterator iter = m_compositeTypes.constFind(t);
+ auto iter = m_compositeTypes.constFind(t);
if (iter != m_compositeTypes.cend()) {
- return QQmlMetaObject((*iter)->rootPropertyCache);
+ return QQmlMetaObject((*iter)->rootPropertyCache());
} else {
QQmlType *type = QQmlMetaType::qmlType(t);
return QQmlMetaObject(type?type->metaObject():0);
@@ -2243,9 +2317,9 @@ QQmlMetaObject QQmlEnginePrivate::metaObjectForType(int t) const
QQmlPropertyCache *QQmlEnginePrivate::propertyCacheForType(int t)
{
Locker locker(this);
- QHash<int, QQmlCompiledData*>::ConstIterator iter = m_compositeTypes.constFind(t);
+ auto iter = m_compositeTypes.constFind(t);
if (iter != m_compositeTypes.cend()) {
- return (*iter)->rootPropertyCache;
+ return (*iter)->rootPropertyCache();
} else {
QQmlType *type = QQmlMetaType::qmlType(t);
locker.unlock();
@@ -2256,9 +2330,9 @@ QQmlPropertyCache *QQmlEnginePrivate::propertyCacheForType(int t)
QQmlPropertyCache *QQmlEnginePrivate::rawPropertyCacheForType(int t)
{
Locker locker(this);
- QHash<int, QQmlCompiledData*>::ConstIterator iter = m_compositeTypes.constFind(t);
+ auto iter = m_compositeTypes.constFind(t);
if (iter != m_compositeTypes.cend()) {
- return (*iter)->rootPropertyCache;
+ return (*iter)->rootPropertyCache();
} else {
QQmlType *type = QQmlMetaType::qmlType(t);
locker.unlock();
@@ -2266,9 +2340,9 @@ QQmlPropertyCache *QQmlEnginePrivate::rawPropertyCacheForType(int t)
}
}
-void QQmlEnginePrivate::registerInternalCompositeType(QQmlCompiledData *data)
+void QQmlEnginePrivate::registerInternalCompositeType(QV4::CompiledData::CompilationUnit *compilationUnit)
{
- QByteArray name = data->rootPropertyCache->className();
+ QByteArray name = compilationUnit->rootPropertyCache()->className();
QByteArray ptr = name + '*';
QByteArray lst = "QQmlListProperty<" + name + '>';
@@ -2286,21 +2360,21 @@ void QQmlEnginePrivate::registerInternalCompositeType(QQmlCompiledData *data)
static_cast<QFlags<QMetaType::TypeFlag> >(QtPrivate::QMetaTypeTypeFlags<QQmlListProperty<QObject> >::Flags),
static_cast<QMetaObject*>(0));
- data->metaTypeId = ptr_type;
- data->listMetaTypeId = lst_type;
- data->isRegisteredWithEngine = true;
+ compilationUnit->metaTypeId = ptr_type;
+ compilationUnit->listMetaTypeId = lst_type;
+ compilationUnit->isRegisteredWithEngine = true;
Locker locker(this);
m_qmlLists.insert(lst_type, ptr_type);
// The QQmlCompiledData is not referenced here, but it is removed from this
// hash in the QQmlCompiledData destructor
- m_compositeTypes.insert(ptr_type, data);
+ m_compositeTypes.insert(ptr_type, compilationUnit);
}
-void QQmlEnginePrivate::unregisterInternalCompositeType(QQmlCompiledData *data)
+void QQmlEnginePrivate::unregisterInternalCompositeType(QV4::CompiledData::CompilationUnit *compilationUnit)
{
- int ptr_type = data->metaTypeId;
- int lst_type = data->listMetaTypeId;
+ int ptr_type = compilationUnit->metaTypeId;
+ int lst_type = compilationUnit->listMetaTypeId;
Locker locker(this);
m_qmlLists.remove(lst_type);
@@ -2320,7 +2394,7 @@ bool QQmlEnginePrivate::isScriptLoaded(const QUrl &url) const
return typeLoader.isScriptLoaded(url);
}
-#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
// Normalize a file name using Shell API. As opposed to converting it
// to a short 8.3 name and back, this also works for drives where 8.3 notation
// is disabled (see 8dot3name options of fsutil.exe).
@@ -2352,7 +2426,7 @@ static inline QString shellNormalizeFileName(const QString &name)
canonicalName[0] = canonicalName.at(0).toUpper();
return QDir::cleanPath(canonicalName);
}
-#endif // Q_OS_WIN && !Q_OS_WINCE && !Q_OS_WINRT
+#endif // Q_OS_WIN && !Q_OS_WINRT
bool QQml_isFileCaseCorrect(const QString &fileName, int lengthIn /* = -1 */)
{
@@ -2360,7 +2434,7 @@ bool QQml_isFileCaseCorrect(const QString &fileName, int lengthIn /* = -1 */)
QFileInfo info(fileName);
const QString absolute = info.absoluteFilePath();
-#if defined(Q_OS_MAC) || defined(Q_OS_WINCE) || defined(Q_OS_WINRT)
+#if defined(Q_OS_DARWIN) || defined(Q_OS_WINRT)
const QString canonical = info.canonicalFilePath();
#elif defined(Q_OS_WIN)
// No difference if the path is qrc based
diff --git a/src/qml/qml/qqmlengine.h b/src/qml/qml/qqmlengine.h
index bd2a3cfc48..2c0c39d0b4 100644
--- a/src/qml/qml/qqmlengine.h
+++ b/src/qml/qml/qqmlengine.h
@@ -87,8 +87,10 @@ class QQmlExpression;
class QQmlContext;
class QQmlType;
class QUrl;
+#if QT_CONFIG(qml_network)
class QNetworkAccessManager;
class QQmlNetworkAccessManagerFactory;
+#endif
class QQmlIncubationController;
class Q_QML_EXPORT QQmlEngine : public QJSEngine
{
@@ -115,10 +117,12 @@ public:
bool importPlugin(const QString &filePath, const QString &uri, QList<QQmlError> *errors);
+#if QT_CONFIG(qml_network)
void setNetworkAccessManagerFactory(QQmlNetworkAccessManagerFactory *);
QQmlNetworkAccessManagerFactory *networkAccessManagerFactory() const;
QNetworkAccessManager *networkAccessManager() const;
+#endif
void setUrlInterceptor(QQmlAbstractUrlInterceptor* urlInterceptor);
QQmlAbstractUrlInterceptor* urlInterceptor() const;
@@ -151,6 +155,7 @@ protected:
Q_SIGNALS:
void quit();
+ void exit(int retCode);
void warnings(const QList<QQmlError> &warnings);
private:
diff --git a/src/qml/qml/qqmlengine_p.h b/src/qml/qml/qqmlengine_p.h
index 003cfa0112..916566b6c7 100644
--- a/src/qml/qml/qqmlengine_p.h
+++ b/src/qml/qml/qqmlengine_p.h
@@ -134,8 +134,12 @@ public:
QRecyclePool<QQmlJavaScriptExpressionGuard> jsExpressionGuardPool;
QQmlContext *rootContext;
+
+#ifdef QT_NO_QML_DEBUGGER
+ static const quintptr profiler = 0;
+#else
QQmlProfiler *profiler;
- void enableProfiler();
+#endif
bool outputWarningsToMsgLog;
@@ -158,12 +162,12 @@ public:
void registerFinalizeCallback(QObject *obj, int index);
QQmlObjectCreator *activeObjectCreator;
-
+#if QT_CONFIG(qml_network)
QNetworkAccessManager *createNetworkAccessManager(QObject *parent) const;
QNetworkAccessManager *getNetworkAccessManager() const;
mutable QNetworkAccessManager *networkAccessManager;
mutable QQmlNetworkAccessManagerFactory *networkAccessManagerFactory;
-
+#endif
QHash<QString,QSharedPointer<QQmlImageProviderBase> > imageProviders;
QQmlAbstractUrlInterceptor* urlInterceptor;
@@ -216,13 +220,14 @@ public:
QQmlMetaObject metaObjectForType(int) const;
QQmlPropertyCache *propertyCacheForType(int);
QQmlPropertyCache *rawPropertyCacheForType(int);
- void registerInternalCompositeType(QQmlCompiledData *);
- void unregisterInternalCompositeType(QQmlCompiledData *);
+ void registerInternalCompositeType(QV4::CompiledData::CompilationUnit *compilationUnit);
+ void unregisterInternalCompositeType(QV4::CompiledData::CompilationUnit *compilationUnit);
bool isTypeLoaded(const QUrl &url) const;
bool isScriptLoaded(const QUrl &url) const;
void sendQuit();
+ void sendExit(int retCode = 0);
void warning(const QQmlError &);
void warning(const QList<QQmlError> &);
void warning(QQmlDelayedError *);
@@ -260,7 +265,7 @@ private:
// the threaded loader. Only access them through their respective accessor methods.
QHash<QPair<QQmlType *, int>, QQmlPropertyCache *> typePropertyCache;
QHash<int, int> m_qmlLists;
- QHash<int, QQmlCompiledData *> m_compositeTypes;
+ QHash<int, QV4::CompiledData::CompilationUnit *> m_compositeTypes;
static bool s_designerMode;
// These members is protected by the full QQmlEnginePrivate::mutex mutex
diff --git a/src/qml/qml/qqmlerror.cpp b/src/qml/qml/qqmlerror.cpp
index 74ceeabeb4..b309550ca8 100644
--- a/src/qml/qml/qqmlerror.cpp
+++ b/src/qml/qml/qqmlerror.cpp
@@ -249,9 +249,9 @@ QString QQmlError::toString() const
int l(line());
if (u.isEmpty() || (u.isLocalFile() && u.path().isEmpty()))
- rv = QLatin1String("<Unknown File>");
+ rv += QLatin1String("<Unknown File>");
else
- rv = u.toString();
+ rv += u.toString();
if (l != -1) {
rv += QLatin1Char(':') + QString::number(l);
diff --git a/src/qml/qml/qqmlexpression.cpp b/src/qml/qml/qqmlexpression.cpp
index 50880e70ea..6afbd05e3e 100644
--- a/src/qml/qml/qqmlexpression.cpp
+++ b/src/qml/qml/qqmlexpression.cpp
@@ -44,7 +44,7 @@
#include "qqmlengine_p.h"
#include "qqmlcontext_p.h"
#include "qqmlscriptstring_p.h"
-#include "qqmlcompiler_p.h"
+#include "qqmlbinding_p.h"
#include <private/qv8engine_p.h>
#include <QtCore/qdebug.h>
@@ -245,14 +245,15 @@ void QQmlExpression::setExpression(const QString &expression)
}
// Must be called with a valid handle scope
-QV4::ReturnedValue QQmlExpressionPrivate::v4value(bool *isUndefined)
+void QQmlExpressionPrivate::v4value(bool *isUndefined, QV4::Scope &scope)
{
if (!expressionFunctionValid) {
createQmlBinding(context(), scopeObject(), expression, url, line);
expressionFunctionValid = true;
}
- return evaluate(isUndefined);
+ QV4::ScopedCallData callData(scope);
+ evaluate(callData, isUndefined, scope);
}
QVariant QQmlExpressionPrivate::value(bool *isUndefined)
@@ -271,9 +272,9 @@ QVariant QQmlExpressionPrivate::value(bool *isUndefined)
{
QV4::Scope scope(QV8Engine::getV4(ep->v8engine()));
- QV4::ScopedValue result(scope, v4value(isUndefined));
+ v4value(isUndefined, scope);
if (!hasError())
- rv = scope.engine->toVariant(result, -1);
+ rv = scope.engine->toVariant(scope.result, -1);
}
ep->dereferenceScarceResources(); // "release" scarce resources if top-level expression evaluation is complete.
diff --git a/src/qml/qml/qqmlexpression_p.h b/src/qml/qml/qqmlexpression_p.h
index 3d1df55a2d..809a57b169 100644
--- a/src/qml/qml/qqmlexpression_p.h
+++ b/src/qml/qml/qqmlexpression_p.h
@@ -56,8 +56,6 @@
#include <private/qqmlengine_p.h>
#include <private/qfieldlist_p.h>
#include <private/qflagpointer_p.h>
-#include <private/qdeletewatcher_p.h>
-#include <private/qpointervaluepair_p.h>
#include <private/qqmljavascriptexpression_p.h>
QT_BEGIN_NAMESPACE
@@ -77,7 +75,7 @@ public:
QVariant value(bool *isUndefined = 0);
- QV4::ReturnedValue v4value(bool *isUndefined = 0);
+ void v4value(bool *isUndefined, QV4::Scope &scope);
static inline QQmlExpressionPrivate *get(QQmlExpression *expr);
static inline QQmlExpression *get(QQmlExpressionPrivate *expr);
diff --git a/src/qml/qml/qqmlfile.cpp b/src/qml/qml/qqmlfile.cpp
index 5fe3cba5c3..4e4db086b0 100644
--- a/src/qml/qml/qqmlfile.cpp
+++ b/src/qml/qml/qqmlfile.cpp
@@ -67,6 +67,8 @@ static char assets_string[] = "assets";
#endif
class QQmlFilePrivate;
+
+#if QT_CONFIG(qml_network)
class QQmlFileNetworkReply : public QObject
{
Q_OBJECT
@@ -97,6 +99,7 @@ private:
int m_redirectCount;
QNetworkReply *m_reply;
};
+#endif
class QQmlFilePrivate
{
@@ -114,10 +117,12 @@ public:
Error error;
QString errorString;
-
+#if QT_CONFIG(qml_network)
QQmlFileNetworkReply *reply;
+#endif
};
+#if QT_CONFIG(qml_network)
int QQmlFileNetworkReply::finishedIndex = -1;
int QQmlFileNetworkReply::downloadProgressIndex = -1;
int QQmlFileNetworkReply::networkFinishedIndex = -1;
@@ -200,9 +205,13 @@ void QQmlFileNetworkReply::networkDownloadProgress(qint64 a, qint64 b)
{
emit downloadProgress(a, b);
}
+#endif // qml_network
QQmlFilePrivate::QQmlFilePrivate()
-: error(None), reply(0)
+: error(None)
+#if QT_CONFIG(qml_network)
+, reply(0)
+#endif
{
}
@@ -218,14 +227,15 @@ QQmlFile::QQmlFile(QQmlEngine *e, const QUrl &url)
}
QQmlFile::QQmlFile(QQmlEngine *e, const QString &url)
-: d(new QQmlFilePrivate)
+ : QQmlFile(e, QUrl(url))
{
- load(e, url);
}
QQmlFile::~QQmlFile()
{
+#if QT_CONFIG(qml_network)
delete d->reply;
+#endif
delete d;
d = 0;
}
@@ -263,8 +273,10 @@ QQmlFile::Status QQmlFile::status() const
{
if (d->url.isEmpty() && d->urlString.isEmpty())
return Null;
+#if QT_CONFIG(qml_network)
else if (d->reply)
return Loading;
+#endif
else if (d->error != QQmlFilePrivate::None)
return Error;
else
@@ -321,7 +333,11 @@ void QQmlFile::load(QQmlEngine *engine, const QUrl &url)
d->error = QQmlFilePrivate::NotFound;
}
} else {
+#if QT_CONFIG(qml_network)
d->reply = new QQmlFileNetworkReply(engine, d, url);
+#else
+ d->error = QQmlFilePrivate::NotFound;
+#endif
}
}
@@ -348,10 +364,14 @@ void QQmlFile::load(QQmlEngine *engine, const QString &url)
d->error = QQmlFilePrivate::NotFound;
}
} else {
+#if QT_CONFIG(qml_network)
QUrl qurl(url);
d->url = qurl;
d->urlString = QString();
d->reply = new QQmlFileNetworkReply(engine, d, qurl);
+#else
+ d->error = QQmlFilePrivate::NotFound;
+#endif
}
}
@@ -368,6 +388,7 @@ void QQmlFile::clear(QObject *)
clear();
}
+#if QT_CONFIG(qml_network)
bool QQmlFile::connectFinished(QObject *object, const char *method)
{
if (!d || !d->reply) {
@@ -411,6 +432,7 @@ bool QQmlFile::connectDownloadProgress(QObject *object, int method)
return QMetaObject::connect(d->reply, QQmlFileNetworkReply::downloadProgressIndex,
object, method);
}
+#endif
/*!
Returns true if QQmlFile will open \a url synchronously.
diff --git a/src/qml/qml/qqmlfile.h b/src/qml/qml/qqmlfile.h
index b0910cc0f4..aec0981a95 100644
--- a/src/qml/qml/qqmlfile.h
+++ b/src/qml/qml/qqmlfile.h
@@ -80,10 +80,12 @@ public:
void clear();
void clear(QObject *);
+#if QT_CONFIG(qml_network)
bool connectFinished(QObject *, const char *);
bool connectFinished(QObject *, int);
bool connectDownloadProgress(QObject *, const char *);
bool connectDownloadProgress(QObject *, int);
+#endif
static bool isSynchronous(const QString &url);
static bool isSynchronous(const QUrl &url);
diff --git a/src/qml/qml/qqmlimport.cpp b/src/qml/qml/qqmlimport.cpp
index c1f5e75369..98e2f9eefd 100644
--- a/src/qml/qml/qqmlimport.cpp
+++ b/src/qml/qml/qqmlimport.cpp
@@ -191,7 +191,7 @@ void qmlClearEnginePlugins()
{
StringRegisteredPluginMap *plugins = qmlEnginePluginsWithRegisteredTypes();
QMutexLocker lock(&plugins->mutex);
- foreach (RegisteredPlugin plugin, plugins->values()) {
+ for (auto &plugin : qAsConst(*plugins)) {
QPluginLoader* loader = plugin.loader;
if (loader && !loader->unload())
qWarning("Unloading %s failed: %s", qPrintable(plugin.uri), qPrintable(loader->errorString()));
@@ -399,9 +399,7 @@ bool excludeBaseUrl(const QString &importUrl, const QString &fileName, const QSt
if (baseUrl.startsWith(importUrl))
{
- QString typeUrl(importUrl);
- typeUrl.append(fileName);
- if (typeUrl == baseUrl)
+ if (fileName == baseUrl.midRef(importUrl.size()))
return false;
}
@@ -540,10 +538,10 @@ QString QQmlImports::versionString(int vmaj, int vmin, ImportVersion version)
{
if (version == QQmlImports::FullyVersioned) {
// extension with fully encoded version number (eg. MyModule.3.2)
- return QString(QLatin1String(".%1.%2")).arg(vmaj).arg(vmin);
+ return QString::asprintf(".%d.%d", vmaj, vmin);
} else if (version == QQmlImports::PartiallyVersioned) {
// extension with encoded version major (eg. MyModule.3)
- return QString(QLatin1String(".%1")).arg(vmaj);
+ return QString::asprintf(".%d", vmaj);
} // else extension without version number (eg. MyModule)
return QString();
}
@@ -913,7 +911,7 @@ bool QQmlImportsPrivate::populatePluginPairVector(QVector<StaticPluginPair> &res
// To avoid traversing all static plugins for all imports, we cut down
// the list the first time called to only contain QML plugins:
foreach (const QStaticPlugin &plugin, QPluginLoader::staticPlugins()) {
- if (qobject_cast<QQmlExtensionPlugin *>(plugin.instance()))
+ if (plugin.metaData().value(QLatin1String("IID")).toString() == QLatin1String(QQmlExtensionInterface_iid))
plugins.append(plugin);
}
}
@@ -921,7 +919,7 @@ bool QQmlImportsPrivate::populatePluginPairVector(QVector<StaticPluginPair> &res
foreach (const QStaticPlugin &plugin, plugins) {
// Since a module can list more than one plugin, we keep iterating even after we found a match.
if (QQmlExtensionPlugin *instance = qobject_cast<QQmlExtensionPlugin *>(plugin.instance())) {
- const QJsonArray metaTagsUriList = plugin.metaData().value(QStringLiteral("uri")).toArray();
+ const QJsonArray metaTagsUriList = plugin.metaData().value(QLatin1String("uri")).toArray();
if (metaTagsUriList.isEmpty()) {
if (errors) {
QQmlError error;
@@ -1395,15 +1393,9 @@ bool QQmlImportsPrivate::addFileImport(const QString& uri, const QString &prefix
// The uri for this import. For library imports this is the same as uri
// specified by the user, but it may be different in the case of file imports.
QString importUri = uri;
-
- QString qmldirPath = importUri;
- if (importUri.endsWith(Slash))
- qmldirPath += String_qmldir;
- else
- qmldirPath += Slash_qmldir;
-
- QString qmldirUrl = resolveLocalUrl(base, qmldirPath);
-
+ QString qmldirUrl = resolveLocalUrl(base, importUri + (importUri.endsWith(Slash)
+ ? String_qmldir
+ : Slash_qmldir));
QString qmldirIdentifier;
if (QQmlFile::isLocalFile(qmldirUrl)) {
@@ -1701,13 +1693,9 @@ QString QQmlImportDatabase::resolvePlugin(QQmlTypeLoader *typeLoader,
if (!resolvedPath.endsWith(Slash))
resolvedPath += Slash;
+ resolvedPath += prefix + baseName;
foreach (const QString &suffix, suffixes) {
- QString pluginFileName = prefix;
-
- pluginFileName += baseName;
- pluginFileName += suffix;
-
- QString absolutePath = typeLoader->absoluteFilePath(resolvedPath + pluginFileName);
+ const QString absolutePath = typeLoader->absoluteFilePath(resolvedPath + suffix);
if (!absolutePath.isEmpty())
return absolutePath;
}
@@ -1739,29 +1727,32 @@ QString QQmlImportDatabase::resolvePlugin(QQmlTypeLoader *typeLoader,
const QString &baseName)
{
#if defined(Q_OS_WIN)
- return resolvePlugin(typeLoader, qmldirPath, qmldirPluginPath, baseName,
- QStringList()
+ static const QString prefix;
+ static const QStringList suffixes = {
# ifdef QT_DEBUG
- << QLatin1String("d.dll") // try a qmake-style debug build first
+ QLatin1String("d.dll"), // try a qmake-style debug build first
# endif
- << QLatin1String(".dll"));
+ QLatin1String(".dll")
+ };
#elif defined(Q_OS_DARWIN)
-
- return resolvePlugin(typeLoader, qmldirPath, qmldirPluginPath, baseName,
- QStringList()
+ static const QString prefix = QLatin1String("lib");
+ static const QStringList suffixes = {
# ifdef QT_DEBUG
- << QLatin1String("_debug.dylib") // try a qmake-style debug build first
- << QLatin1String(".dylib")
+ QLatin1String("_debug.dylib"), // try a qmake-style debug build first
+ QLatin1String(".dylib"),
# else
- << QLatin1String(".dylib")
- << QLatin1String("_debug.dylib") // try a qmake-style debug build after
+ QLatin1String(".dylib"),
+ QLatin1String("_debug.dylib"), // try a qmake-style debug build after
# endif
- << QLatin1String(".so")
- << QLatin1String(".bundle"),
- QLatin1String("lib"));
+ QLatin1String(".so"),
+ QLatin1String(".bundle")
+ };
# else // Unix
- return resolvePlugin(typeLoader, qmldirPath, qmldirPluginPath, baseName, QStringList() << QLatin1String(".so"), QLatin1String("lib"));
+ static const QString prefix = QLatin1String("lib");
+ static const QStringList suffixes = { QLatin1String(".so") };
#endif
+
+ return resolvePlugin(typeLoader, qmldirPath, qmldirPluginPath, baseName, suffixes, prefix);
}
/*!
@@ -1820,7 +1811,7 @@ void QQmlImportDatabase::addImportPath(const QString& path)
} else if (path.startsWith(QLatin1Char(':'))) {
// qrc directory, e.g. :/foo
// need to convert to a qrc url, e.g. qrc:/foo
- cPath = QStringLiteral("qrc") + path;
+ cPath = QLatin1String("qrc") + path;
cPath.replace(Backslash, Slash);
} else if (url.isRelative() ||
(url.scheme().length() == 1 && QFile::exists(path)) ) { // windows path
@@ -1960,7 +1951,7 @@ bool QQmlImportDatabase::importStaticPlugin(QObject *instance, const QString &ba
#ifndef QT_NO_LIBRARY
// Dynamic plugins are differentiated by their filepath. For static plugins we
// don't have that information so we use their address as key instead.
- QString uniquePluginID = QString().sprintf("%p", instance);
+ const QString uniquePluginID = QString::asprintf("%p", instance);
StringRegisteredPluginMap *plugins = qmlEnginePluginsWithRegisteredTypes();
QMutexLocker lock(&plugins->mutex);
diff --git a/src/qml/qml/qqmlincubator.cpp b/src/qml/qml/qqmlincubator.cpp
index f0f160e6f6..0ce769aaea 100644
--- a/src/qml/qml/qqmlincubator.cpp
+++ b/src/qml/qml/qqmlincubator.cpp
@@ -41,7 +41,6 @@
#include "qqmlcomponent.h"
#include "qqmlincubator_p.h"
-#include "qqmlcompiler_p.h"
#include "qqmlexpression_p.h"
#include "qqmlmemoryprofiler_p.h"
#include "qqmlobjectcreator_p.h"
@@ -132,7 +131,7 @@ QQmlIncubationController *QQmlEngine::incubationController() const
QQmlIncubatorPrivate::QQmlIncubatorPrivate(QQmlIncubator *q, QQmlIncubator::IncubationMode m)
: q(q), status(QQmlIncubator::Null), mode(m), isAsynchronous(false), progress(Execute),
- result(0), compiledData(0), waitingOnMe(0)
+ result(0), enginePriv(0), waitingOnMe(0)
{
}
@@ -143,20 +142,15 @@ QQmlIncubatorPrivate::~QQmlIncubatorPrivate()
void QQmlIncubatorPrivate::clear()
{
+ compilationUnit = nullptr;
if (next.isInList()) {
next.remove();
- Q_ASSERT(compiledData);
- QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(compiledData->engine);
- compiledData->release();
- compiledData = 0;
enginePriv->incubatorCount--;
QQmlIncubationController *controller = enginePriv->incubationController;
if (controller)
controller->incubatingObjectCountChanged(enginePriv->incubatorCount);
- } else if (compiledData) {
- compiledData->release();
- compiledData = 0;
}
+ enginePriv = 0;
if (!rootContext.isNull()) {
rootContext->activeVMEData = 0;
rootContext = 0;
@@ -278,21 +272,20 @@ void QQmlIncubatorPrivate::forceCompletion(QQmlInstantiationInterrupt &i)
void QQmlIncubatorPrivate::incubate(QQmlInstantiationInterrupt &i)
{
- if (!compiledData)
+ if (!compilationUnit)
return;
- QML_MEMORY_SCOPE_URL(compiledData->url());
+ QML_MEMORY_SCOPE_URL(compilationUnit->url());
QExplicitlySharedDataPointer<QQmlIncubatorPrivate> protectThis(this);
QRecursionWatcher<QQmlIncubatorPrivate, &QQmlIncubatorPrivate::recursion> watcher(this);
-
- QQmlEngine *engine = compiledData->engine;
- QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(engine);
+ // get a copy of the engine pointer as it might get reset;
+ QQmlEnginePrivate *enginePriv = this->enginePriv;
if (!vmeGuard.isOK()) {
QQmlError error;
- error.setUrl(compiledData->url());
+ error.setUrl(compilationUnit->url());
error.setDescription(QQmlComponent::tr("Object destroyed during incubation"));
errors << error;
progress = QQmlIncubatorPrivate::Completed;
@@ -563,17 +556,16 @@ void QQmlIncubator::clear()
if (s == Null)
return;
- QQmlEnginePrivate *enginePriv = 0;
+ QQmlEnginePrivate *enginePriv = d->enginePriv;
if (s == Loading) {
- Q_ASSERT(d->compiledData);
- enginePriv = QQmlEnginePrivate::get(d->compiledData->engine);
+ Q_ASSERT(d->compilationUnit);
if (d->result) d->result->deleteLater();
d->result = 0;
}
d->clear();
- Q_ASSERT(d->compiledData == 0);
+ Q_ASSERT(d->compilationUnit.isNull());
Q_ASSERT(d->waitingOnMe.data() == 0);
Q_ASSERT(d->waitingFor.isEmpty());
@@ -713,7 +705,7 @@ QQmlIncubator::Status QQmlIncubatorPrivate::calculateStatus() const
return QQmlIncubator::Error;
else if (result && progress == QQmlIncubatorPrivate::Completed && waitingFor.isEmpty())
return QQmlIncubator::Ready;
- else if (compiledData)
+ else if (compilationUnit)
return QQmlIncubator::Loading;
else
return QQmlIncubator::Null;
diff --git a/src/qml/qml/qqmlincubator_p.h b/src/qml/qml/qqmlincubator_p.h
index a12ff9c5e2..ecf3b6d2ca 100644
--- a/src/qml/qml/qqmlincubator_p.h
+++ b/src/qml/qml/qqmlincubator_p.h
@@ -59,7 +59,6 @@
QT_BEGIN_NAMESPACE
-class QQmlCompiledData;
class QQmlIncubator;
class QQmlIncubatorPrivate : public QQmlEnginePrivate::Incubator
{
@@ -85,7 +84,8 @@ public:
QPointer<QObject> result;
QQmlGuardedContextData rootContext;
- QQmlCompiledData *compiledData;
+ QQmlEnginePrivate *enginePriv;
+ QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit;
QScopedPointer<QQmlObjectCreator> creator;
int subComponentToCreate;
QQmlVMEGuard vmeGuard;
diff --git a/src/qml/qml/qqmljavascriptexpression.cpp b/src/qml/qml/qqmljavascriptexpression.cpp
index cf0b823612..8020bdb2be 100644
--- a/src/qml/qml/qqmljavascriptexpression.cpp
+++ b/src/qml/qml/qqmljavascriptexpression.cpp
@@ -106,7 +106,8 @@ QQmlJavaScriptExpression::~QQmlJavaScriptExpression()
m_nextExpression->m_prevExpression = m_prevExpression;
}
- clearGuards();
+ clearActiveGuards();
+ clearPermanentGuards();
if (m_scopeObject.isT2()) // notify DeleteWatcher of our deletion.
m_scopeObject.asT2()->_s = 0;
}
@@ -114,12 +115,17 @@ QQmlJavaScriptExpression::~QQmlJavaScriptExpression()
void QQmlJavaScriptExpression::setNotifyOnValueChanged(bool v)
{
activeGuards.setFlagValue(v);
- if (!v) clearGuards();
+ permanentGuards.setFlagValue(v);
+ if (!v) {
+ clearActiveGuards();
+ clearPermanentGuards();
+ m_permanentDependenciesRegistered = false;
+ }
}
void QQmlJavaScriptExpression::resetNotifyOnValueChanged()
{
- clearGuards();
+ setNotifyOnValueChanged(false);
}
void QQmlJavaScriptExpression::setContext(QQmlContextData *context)
@@ -147,16 +153,9 @@ void QQmlJavaScriptExpression::refresh()
{
}
-QV4::ReturnedValue QQmlJavaScriptExpression::evaluate(bool *isUndefined)
-{
- QV4::ExecutionEngine *v4 = QV8Engine::getV4(m_context->engine);
- QV4::Scope scope(v4);
- QV4::ScopedCallData callData(scope);
- return evaluate(callData, isUndefined);
-}
-QV4::ReturnedValue QQmlJavaScriptExpression::evaluate(QV4::CallData *callData, bool *isUndefined)
+void QQmlJavaScriptExpression::evaluate(QV4::CallData *callData, bool *isUndefined, QV4::Scope &scope)
{
Q_ASSERT(m_context && m_context->engine);
@@ -164,7 +163,7 @@ QV4::ReturnedValue QQmlJavaScriptExpression::evaluate(QV4::CallData *callData, b
if (!f || f->isUndefined()) {
if (isUndefined)
*isUndefined = true;
- return QV4::Encode::undefined();
+ return;
}
QQmlEnginePrivate *ep = QQmlEnginePrivate::get(m_context->engine);
@@ -184,8 +183,7 @@ QV4::ReturnedValue QQmlJavaScriptExpression::evaluate(QV4::CallData *callData, b
capture.guards.copyAndClearPrepend(activeGuards);
QV4::ExecutionEngine *v4 = QV8Engine::getV4(ep->v8engine());
- QV4::Scope scope(v4);
- QV4::ScopedValue result(scope, QV4::Primitive::undefinedValue());
+ scope.result = QV4::Primitive::undefinedValue();
callData->thisObject = v4->globalObject;
if (scopeObject()) {
QV4::ScopedValue value(scope, QV4::QObjectWrapper::wrap(v4, scopeObject()));
@@ -193,7 +191,7 @@ QV4::ReturnedValue QQmlJavaScriptExpression::evaluate(QV4::CallData *callData, b
callData->thisObject = value;
}
- result = f->as<QV4::FunctionObject>()->call(callData);
+ f->as<QV4::FunctionObject>()->call(scope, callData);
if (scope.hasException()) {
if (watcher.wasDeleted())
scope.engine->catchException(); // ignore exception
@@ -203,7 +201,7 @@ QV4::ReturnedValue QQmlJavaScriptExpression::evaluate(QV4::CallData *callData, b
*isUndefined = true;
} else {
if (isUndefined)
- *isUndefined = result->isUndefined();
+ *isUndefined = scope.result.isUndefined();
if (!watcher.wasDeleted() && hasDelayedError())
delayedError()->clearError();
@@ -220,11 +218,9 @@ QV4::ReturnedValue QQmlJavaScriptExpression::evaluate(QV4::CallData *callData, b
g->Delete();
ep->propertyCapture = lastPropertyCapture;
-
- return result->asReturnedValue();
}
-void QQmlPropertyCapture::captureProperty(QQmlNotifier *n)
+void QQmlPropertyCapture::captureProperty(QQmlNotifier *n, Duration duration)
{
if (watcher->wasDeleted())
return;
@@ -244,14 +240,17 @@ void QQmlPropertyCapture::captureProperty(QQmlNotifier *n)
g->connect(n);
}
- expression->activeGuards.prepend(g);
+ if (duration == Permanently)
+ expression->permanentGuards.prepend(g);
+ else
+ expression->activeGuards.prepend(g);
}
/*! \internal
\a n is in the signal index range (see QObjectPrivate::signalIndex()).
*/
-void QQmlPropertyCapture::captureProperty(QObject *o, int c, int n)
+void QQmlPropertyCapture::captureProperty(QObject *o, int c, int n, Duration duration)
{
if (watcher->wasDeleted())
return;
@@ -290,15 +289,19 @@ void QQmlPropertyCapture::captureProperty(QObject *o, int c, int n)
g->connect(o, n, engine);
}
- expression->activeGuards.prepend(g);
+ if (duration == Permanently)
+ expression->permanentGuards.prepend(g);
+ else
+ expression->activeGuards.prepend(g);
}
}
-void QQmlPropertyCapture::registerQmlDependencies(QV4::ExecutionEngine *engine, const QV4::CompiledData::Function *compiledFunction)
+void QQmlPropertyCapture::registerQmlDependencies(const QV4::CompiledData::Function *compiledFunction, const QV4::Scope &scope)
{
// Let the caller check and avoid the function call :)
Q_ASSERT(compiledFunction->hasQmlDependencies());
+ QV4::ExecutionEngine *engine = scope.engine;
QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine->qmlEngine());
if (!ep)
return;
@@ -306,33 +309,40 @@ void QQmlPropertyCapture::registerQmlDependencies(QV4::ExecutionEngine *engine,
if (!capture || capture->watcher->wasDeleted())
return;
- QV4::Scope scope(engine);
+ if (capture->expression->m_permanentDependenciesRegistered)
+ return;
+
+ capture->expression->m_permanentDependenciesRegistered = true;
+
QV4::Scoped<QV4::QmlContext> context(scope, engine->qmlContext());
QQmlContextData *qmlContext = context->qmlContext();
- const quint32 *idObjectDependency = compiledFunction->qmlIdObjectDependencyTable();
+ const QV4::CompiledData::LEUInt32 *idObjectDependency = compiledFunction->qmlIdObjectDependencyTable();
const int idObjectDependencyCount = compiledFunction->nDependingIdObjects;
for (int i = 0; i < idObjectDependencyCount; ++i, ++idObjectDependency) {
Q_ASSERT(int(*idObjectDependency) < qmlContext->idValueCount);
- capture->captureProperty(&qmlContext->idValues[*idObjectDependency].bindings);
+ capture->captureProperty(&qmlContext->idValues[*idObjectDependency].bindings,
+ QQmlPropertyCapture::Permanently);
}
Q_ASSERT(qmlContext->contextObject);
- const quint32 *contextPropertyDependency = compiledFunction->qmlContextPropertiesDependencyTable();
+ const QV4::CompiledData::LEUInt32 *contextPropertyDependency = compiledFunction->qmlContextPropertiesDependencyTable();
const int contextPropertyDependencyCount = compiledFunction->nDependingContextProperties;
for (int i = 0; i < contextPropertyDependencyCount; ++i) {
const int propertyIndex = *contextPropertyDependency++;
const int notifyIndex = *contextPropertyDependency++;
- capture->captureProperty(qmlContext->contextObject, propertyIndex, notifyIndex);
+ capture->captureProperty(qmlContext->contextObject, propertyIndex, notifyIndex,
+ QQmlPropertyCapture::Permanently);
}
QObject *scopeObject = context->qmlScope();
- const quint32 *scopePropertyDependency = compiledFunction->qmlScopePropertiesDependencyTable();
+ const QV4::CompiledData::LEUInt32 *scopePropertyDependency = compiledFunction->qmlScopePropertiesDependencyTable();
const int scopePropertyDependencyCount = compiledFunction->nDependingScopeProperties;
for (int i = 0; i < scopePropertyDependencyCount; ++i) {
const int propertyIndex = *scopePropertyDependency++;
const int notifyIndex = *scopePropertyDependency++;
- capture->captureProperty(scopeObject, propertyIndex, notifyIndex);
+ capture->captureProperty(scopeObject, propertyIndex, notifyIndex,
+ QQmlPropertyCapture::Permanently);
}
}
@@ -416,12 +426,19 @@ void QQmlJavaScriptExpression::createQmlBinding(QQmlContextData *ctxt, QObject *
}
-void QQmlJavaScriptExpression::clearGuards()
+void QQmlJavaScriptExpression::clearActiveGuards()
{
while (QQmlJavaScriptExpressionGuard *g = activeGuards.takeFirst())
g->Delete();
}
+void QQmlJavaScriptExpression::clearPermanentGuards()
+{
+ m_permanentDependenciesRegistered = false;
+ while (QQmlJavaScriptExpressionGuard *g = permanentGuards.takeFirst())
+ g->Delete();
+}
+
void QQmlJavaScriptExpressionGuard_callback(QQmlNotifierEndpoint *e, void **)
{
QQmlJavaScriptExpression *expression =
diff --git a/src/qml/qml/qqmljavascriptexpression_p.h b/src/qml/qml/qqmljavascriptexpression_p.h
index 64cb1bb242..5f9cffb56d 100644
--- a/src/qml/qml/qqmljavascriptexpression_p.h
+++ b/src/qml/qml/qqmljavascriptexpression_p.h
@@ -54,7 +54,6 @@
#include <QtCore/qglobal.h>
#include <QtQml/qqmlerror.h>
#include <private/qqmlengine_p.h>
-#include <private/qpointervaluepair_p.h>
QT_BEGIN_NAMESPACE
@@ -104,8 +103,7 @@ public:
virtual QString expressionIdentifier() = 0;
virtual void expressionChanged() = 0;
- QV4::ReturnedValue evaluate(bool *isUndefined);
- QV4::ReturnedValue evaluate(QV4::CallData *callData, bool *isUndefined);
+ void evaluate(QV4::CallData *callData, bool *isUndefined, QV4::Scope &scope);
inline bool notifyOnValueChanged() const;
@@ -138,7 +136,8 @@ public:
inline bool hasDelayedError() const;
QQmlError error(QQmlEngine *) const;
void clearError();
- void clearGuards();
+ void clearActiveGuards();
+ void clearPermanentGuards();
QQmlDelayedError *delayedError();
static QV4::ReturnedValue evalFunction(QQmlContextData *ctxt, QObject *scope,
@@ -147,6 +146,14 @@ public:
protected:
void createQmlBinding(QQmlContextData *ctxt, QObject *scope, const QString &code, const QString &filename, quint16 line);
+ void cancelPermanentGuards() const
+ {
+ if (m_permanentDependenciesRegistered) {
+ for (QQmlJavaScriptExpressionGuard *it = permanentGuards.first(); it; it = permanentGuards.next(it))
+ it->cancelNotify();
+ }
+ }
+
private:
friend class QQmlContextData;
friend class QQmlPropertyCapture;
@@ -159,10 +166,12 @@ private:
// activeGuards:flag2 - useSharedContext
QBiPointer<QObject, DeleteWatcher> m_scopeObject;
QForwardFieldList<QQmlJavaScriptExpressionGuard, &QQmlJavaScriptExpressionGuard::next> activeGuards;
+ QForwardFieldList<QQmlJavaScriptExpressionGuard, &QQmlJavaScriptExpressionGuard::next> permanentGuards;
QQmlContextData *m_context;
QQmlJavaScriptExpression **m_prevExpression;
QQmlJavaScriptExpression *m_nextExpression;
+ bool m_permanentDependenciesRegistered = false;
protected:
QV4::PersistentValue m_function;
@@ -179,10 +188,14 @@ public:
Q_ASSERT(errorString == 0);
}
- void captureProperty(QQmlNotifier *);
- void captureProperty(QObject *, int, int);
+ enum Duration {
+ OnlyOnce,
+ Permanently
+ };
- static void registerQmlDependencies(QV4::ExecutionEngine *engine, const QV4::CompiledData::Function *compiledFunction);
+ static void registerQmlDependencies(const QV4::CompiledData::Function *compiledFunction, const QV4::Scope &scope);
+ void captureProperty(QQmlNotifier *, Duration duration = OnlyOnce);
+ void captureProperty(QObject *, int, int, Duration duration = OnlyOnce);
QQmlEngine *engine;
QQmlJavaScriptExpression *expression;
diff --git a/src/qml/qml/qqmllist.cpp b/src/qml/qml/qqmllist.cpp
index d0b7fb4853..a719956483 100644
--- a/src/qml/qml/qqmllist.cpp
+++ b/src/qml/qml/qqmllist.cpp
@@ -143,16 +143,16 @@ QQmlListReference::QQmlListReference(QObject *object, const char *property, QQml
QQmlEnginePrivate *p = engine?QQmlEnginePrivate::get(engine):0;
- int listType = p?p->listType(data->propType):QQmlMetaType::listType(data->propType);
+ int listType = p?p->listType(data->propType()):QQmlMetaType::listType(data->propType());
if (listType == -1) return;
d = new QQmlListReferencePrivate;
d->object = object;
d->elementType = p?p->rawMetaObjectForType(listType):QQmlMetaType::qmlType(listType)->baseMetaObject();
- d->propertyType = data->propType;
+ d->propertyType = data->propType();
void *args[] = { &d->property, 0 };
- QMetaObject::metacall(object, QMetaObject::ReadProperty, data->coreIndex, args);
+ QMetaObject::metacall(object, QMetaObject::ReadProperty, data->coreIndex(), args);
}
/*! \internal */
diff --git a/src/qml/qml/qqmllistwrapper.cpp b/src/qml/qml/qqmllistwrapper.cpp
index 5c35866274..8aa107dc17 100644
--- a/src/qml/qml/qqmllistwrapper.cpp
+++ b/src/qml/qml/qqmllistwrapper.cpp
@@ -52,15 +52,19 @@ using namespace QV4;
DEFINE_OBJECT_VTABLE(QmlListWrapper);
-Heap::QmlListWrapper::QmlListWrapper()
+void Heap::QmlListWrapper::init()
{
+ Object::init();
+ object.init();
QV4::Scope scope(internalClass->engine);
QV4::ScopedObject o(scope, this);
o->setArrayType(Heap::ArrayData::Custom);
}
-Heap::QmlListWrapper::~QmlListWrapper()
+void Heap::QmlListWrapper::destroy()
{
+ object.destroy();
+ Object::destroy();
}
ReturnedValue QmlListWrapper::create(ExecutionEngine *engine, QObject *object, int propId, int propType)
@@ -73,7 +77,7 @@ ReturnedValue QmlListWrapper::create(ExecutionEngine *engine, QObject *object, i
Scoped<QmlListWrapper> r(scope, engine->memoryManager->allocObject<QmlListWrapper>());
r->d()->object = object;
r->d()->propertyType = propType;
- void *args[] = { &r->d()->property, 0 };
+ void *args[] = { &r->d()->property(), 0 };
QMetaObject::metacall(object, QMetaObject::ReadProperty, propId, args);
return r.asReturnedValue();
}
@@ -84,7 +88,7 @@ ReturnedValue QmlListWrapper::create(ExecutionEngine *engine, const QQmlListProp
Scoped<QmlListWrapper> r(scope, engine->memoryManager->allocObject<QmlListWrapper>());
r->d()->object = prop.object;
- r->d()->property = prop;
+ r->d()->property() = prop;
r->d()->propertyType = propType;
return r.asReturnedValue();
}
@@ -94,7 +98,7 @@ QVariant QmlListWrapper::toVariant() const
if (!d()->object)
return QVariant();
- return QVariant::fromValue(QQmlListReferencePrivate::init(d()->property, d()->propertyType, engine()->qmlEngine()));
+ return QVariant::fromValue(QQmlListReferencePrivate::init(d()->property(), d()->propertyType, engine()->qmlEngine()));
}
@@ -105,7 +109,7 @@ ReturnedValue QmlListWrapper::get(const Managed *m, String *name, bool *hasPrope
QV4::ExecutionEngine *v4 = w->engine();
if (name->equals(v4->id_length()) && !w->d()->object.isNull()) {
- quint32 count = w->d()->property.count ? w->d()->property.count(&w->d()->property) : 0;
+ quint32 count = w->d()->property().count ? w->d()->property().count(&w->d()->property()) : 0;
return Primitive::fromUInt32(count).asReturnedValue();
}
@@ -124,11 +128,11 @@ ReturnedValue QmlListWrapper::getIndexed(const Managed *m, uint index, bool *has
const QmlListWrapper *w = static_cast<const QmlListWrapper *>(m);
QV4::ExecutionEngine *v4 = w->engine();
- quint32 count = w->d()->property.count ? w->d()->property.count(&w->d()->property) : 0;
- if (index < count && w->d()->property.at) {
+ quint32 count = w->d()->property().count ? w->d()->property().count(&w->d()->property()) : 0;
+ if (index < count && w->d()->property().at) {
if (hasProperty)
*hasProperty = true;
- return QV4::QObjectWrapper::wrap(v4, w->d()->property.at(&w->d()->property, index));
+ return QV4::QObjectWrapper::wrap(v4, w->d()->property().at(&w->d()->property(), index));
}
if (hasProperty)
@@ -150,12 +154,12 @@ void QmlListWrapper::advanceIterator(Managed *m, ObjectIterator *it, Value *name
*index = UINT_MAX;
Q_ASSERT(m->as<QmlListWrapper>());
QmlListWrapper *w = static_cast<QmlListWrapper *>(m);
- quint32 count = w->d()->property.count ? w->d()->property.count(&w->d()->property) : 0;
+ quint32 count = w->d()->property().count ? w->d()->property().count(&w->d()->property()) : 0;
if (it->arrayIndex < count) {
*index = it->arrayIndex;
++it->arrayIndex;
*attrs = QV4::Attr_Data;
- p->value = QV4::QObjectWrapper::wrap(w->engine(), w->d()->property.at(&w->d()->property, *index));
+ p->value = QV4::QObjectWrapper::wrap(w->engine(), w->d()->property().at(&w->d()->property(), *index));
return;
}
return QV4::Object::advanceIterator(m, it, name, index, p, attrs);
diff --git a/src/qml/qml/qqmllistwrapper_p.h b/src/qml/qml/qqmllistwrapper_p.h
index 26e1e5faaf..d01b332159 100644
--- a/src/qml/qml/qqmllistwrapper_p.h
+++ b/src/qml/qml/qqmllistwrapper_p.h
@@ -66,11 +66,18 @@ namespace QV4 {
namespace Heap {
struct QmlListWrapper : Object {
- QmlListWrapper();
- ~QmlListWrapper();
- QPointer<QObject> object;
- QQmlListProperty<QObject> property;
+ void init();
+ void destroy();
+ QQmlQPointer<QObject> object;
+
+ QQmlListProperty<QObject> &property() {
+ return *reinterpret_cast<QQmlListProperty<QObject>*>(propertyData);
+ }
+
int propertyType;
+
+private:
+ void *propertyData[sizeof(QQmlListProperty<QObject>)/sizeof(void*)];
};
}
diff --git a/src/qml/qml/qqmllocale.cpp b/src/qml/qml/qqmllocale.cpp
index 76752f509c..6f66475aa5 100644
--- a/src/qml/qml/qqmllocale.cpp
+++ b/src/qml/qml/qqmllocale.cpp
@@ -109,16 +109,16 @@ QV4::ReturnedValue QQmlDateExtension::method_toLocaleString(QV4::CallContext *ct
if (ctx->argc() == 2) {
if (ctx->args()[1].isString()) {
QString format = ctx->args()[1].stringValue()->toQString();
- formattedDt = r->d()->locale.toString(dt, format);
+ formattedDt = r->d()->locale->toString(dt, format);
} else if (ctx->args()[1].isNumber()) {
quint32 intFormat = ctx->args()[1].toNumber();
QLocale::FormatType format = QLocale::FormatType(intFormat);
- formattedDt = r->d()->locale.toString(dt, format);
+ formattedDt = r->d()->locale->toString(dt, format);
} else {
V4THROW_ERROR("Locale: Date.toLocaleString(): Invalid datetime format");
}
} else {
- formattedDt = r->d()->locale.toString(dt, enumFormat);
+ formattedDt = r->d()->locale->toString(dt, enumFormat);
}
return ctx->d()->engine->newString(formattedDt)->asReturnedValue();
@@ -154,16 +154,16 @@ QV4::ReturnedValue QQmlDateExtension::method_toLocaleTimeString(QV4::CallContext
if (ctx->argc() == 2) {
if (ctx->args()[1].isString()) {
QString format = ctx->args()[1].stringValue()->toQString();
- formattedTime = r->d()->locale.toString(time, format);
+ formattedTime = r->d()->locale->toString(time, format);
} else if (ctx->args()[1].isNumber()) {
quint32 intFormat = ctx->args()[1].toNumber();
QLocale::FormatType format = QLocale::FormatType(intFormat);
- formattedTime = r->d()->locale.toString(time, format);
+ formattedTime = r->d()->locale->toString(time, format);
} else {
V4THROW_ERROR("Locale: Date.toLocaleTimeString(): Invalid time format");
}
} else {
- formattedTime = r->d()->locale.toString(time, enumFormat);
+ formattedTime = r->d()->locale->toString(time, enumFormat);
}
return ctx->d()->engine->newString(formattedTime)->asReturnedValue();
@@ -199,16 +199,16 @@ QV4::ReturnedValue QQmlDateExtension::method_toLocaleDateString(QV4::CallContext
if (ctx->argc() == 2) {
if (ctx->args()[1].isString()) {
QString format = ctx->args()[1].stringValue()->toQString();
- formattedDate = r->d()->locale.toString(date, format);
+ formattedDate = r->d()->locale->toString(date, format);
} else if (ctx->args()[1].isNumber()) {
quint32 intFormat = ctx->args()[1].toNumber();
QLocale::FormatType format = QLocale::FormatType(intFormat);
- formattedDate = r->d()->locale.toString(date, format);
+ formattedDate = r->d()->locale->toString(date, format);
} else {
V4THROW_ERROR("Locale: Date.loLocaleDateString(): Invalid date format");
}
} else {
- formattedDate = r->d()->locale.toString(date, enumFormat);
+ formattedDate = r->d()->locale->toString(date, enumFormat);
}
return ctx->d()->engine->newString(formattedDate)->asReturnedValue();
@@ -237,16 +237,16 @@ QV4::ReturnedValue QQmlDateExtension::method_fromLocaleString(QV4::CallContext *
if (ctx->argc() == 3) {
if (ctx->args()[2].isString()) {
QString format = ctx->args()[2].stringValue()->toQString();
- dt = r->d()->locale.toDateTime(dateString, format);
+ dt = r->d()->locale->toDateTime(dateString, format);
} else if (ctx->args()[2].isNumber()) {
quint32 intFormat = ctx->args()[2].toNumber();
QLocale::FormatType format = QLocale::FormatType(intFormat);
- dt = r->d()->locale.toDateTime(dateString, format);
+ dt = r->d()->locale->toDateTime(dateString, format);
} else {
V4THROW_ERROR("Locale: Date.fromLocaleString(): Invalid datetime format");
}
} else {
- dt = r->d()->locale.toDateTime(dateString, enumFormat);
+ dt = r->d()->locale->toDateTime(dateString, enumFormat);
}
return QV4::Encode(engine->newDateObject(dt));
@@ -278,16 +278,16 @@ QV4::ReturnedValue QQmlDateExtension::method_fromLocaleTimeString(QV4::CallConte
if (ctx->argc() == 3) {
if (ctx->args()[2].isString()) {
QString format = ctx->args()[2].stringValue()->toQString();
- tm = r->d()->locale.toTime(dateString, format);
+ tm = r->d()->locale->toTime(dateString, format);
} else if (ctx->args()[2].isNumber()) {
quint32 intFormat = ctx->args()[2].toNumber();
QLocale::FormatType format = QLocale::FormatType(intFormat);
- tm = r->d()->locale.toTime(dateString, format);
+ tm = r->d()->locale->toTime(dateString, format);
} else {
V4THROW_ERROR("Locale: Date.fromLocaleTimeString(): Invalid datetime format");
}
} else {
- tm = r->d()->locale.toTime(dateString, enumFormat);
+ tm = r->d()->locale->toTime(dateString, enumFormat);
}
QDateTime dt;
@@ -323,16 +323,16 @@ QV4::ReturnedValue QQmlDateExtension::method_fromLocaleDateString(QV4::CallConte
if (ctx->argc() == 3) {
if (ctx->args()[2].isString()) {
QString format = ctx->args()[2].stringValue()->toQString();
- dt = r->d()->locale.toDate(dateString, format);
+ dt = r->d()->locale->toDate(dateString, format);
} else if (ctx->args()[2].isNumber()) {
quint32 intFormat = ctx->args()[2].toNumber();
QLocale::FormatType format = QLocale::FormatType(intFormat);
- dt = r->d()->locale.toDate(dateString, format);
+ dt = r->d()->locale->toDate(dateString, format);
} else {
V4THROW_ERROR("Locale: Date.fromLocaleDateString(): Invalid datetime format");
}
} else {
- dt = r->d()->locale.toDate(dateString, enumFormat);
+ dt = r->d()->locale->toDate(dateString, enumFormat);
}
return QV4::Encode(engine->newDateObject(QDateTime(dt)));
@@ -393,7 +393,7 @@ QV4::ReturnedValue QQmlNumberExtension::method_toLocaleString(QV4::CallContext *
prec = ctx->args()[2].toInt32();
}
- return ctx->d()->engine->newString(r->d()->locale.toString(number, (char)format, prec))->asReturnedValue();
+ return ctx->d()->engine->newString(r->d()->locale->toString(number, (char)format, prec))->asReturnedValue();
}
QV4::ReturnedValue QQmlNumberExtension::method_toLocaleCurrencyString(QV4::CallContext *ctx)
@@ -423,7 +423,7 @@ QV4::ReturnedValue QQmlNumberExtension::method_toLocaleCurrencyString(QV4::CallC
symbol = ctx->args()[1].toQStringNoThrow();
}
- return ctx->d()->engine->newString(r->d()->locale.toCurrencyString(number, symbol))->asReturnedValue();
+ return ctx->d()->engine->newString(r->d()->locale->toCurrencyString(number, symbol))->asReturnedValue();
}
QV4::ReturnedValue QQmlNumberExtension::method_fromLocaleString(QV4::CallContext *ctx)
@@ -441,7 +441,7 @@ QV4::ReturnedValue QQmlNumberExtension::method_fromLocaleString(QV4::CallContext
V4THROW_ERROR("Locale: Number.fromLocaleString(): Invalid arguments");
GET_LOCALE_DATA_RESOURCE(ctx->args()[0]);
- locale = r->d()->locale;
+ locale = *r->d()->locale;
numberIdx = 1;
}
@@ -813,7 +813,7 @@ QV4::ReturnedValue QQmlLocale::wrap(ExecutionEngine *v4, const QLocale &locale)
QV4::Scope scope(v4);
QV4LocaleDataDeletable *d = localeV4Data(scope.engine);
QV4::Scoped<QQmlLocaleData> wrapper(scope, v4->memoryManager->allocObject<QQmlLocaleData>());
- wrapper->d()->locale = locale;
+ *wrapper->d()->locale = locale;
QV4::ScopedObject p(scope, d->prototype.value());
wrapper->setPrototype(p);
return wrapper.asReturnedValue();
diff --git a/src/qml/qml/qqmllocale_p.h b/src/qml/qml/qqmllocale_p.h
index 652a3ca0d4..275f58db7d 100644
--- a/src/qml/qml/qqmllocale_p.h
+++ b/src/qml/qml/qqmllocale_p.h
@@ -143,8 +143,12 @@ namespace QV4 {
namespace Heap {
struct QQmlLocaleData : Object {
- inline QQmlLocaleData() {}
- QLocale locale;
+ inline void init() { locale = new QLocale; }
+ void destroy() {
+ delete locale;
+ Object::destroy();
+ }
+ QLocale *locale;
};
}
@@ -161,7 +165,7 @@ struct QQmlLocaleData : public QV4::Object
ctx->engine()->throwTypeError();
return 0;
}
- return &thisObject->d()->locale;
+ return thisObject->d()->locale;
}
static QV4::ReturnedValue method_currencySymbol(QV4::CallContext *ctx);
diff --git a/src/qml/qml/qqmlloggingcategory.cpp b/src/qml/qml/qqmlloggingcategory.cpp
new file mode 100644
index 0000000000..fd8fb477c7
--- /dev/null
+++ b/src/qml/qml/qqmlloggingcategory.cpp
@@ -0,0 +1,128 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Pelagicore AG
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml 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 "qqmlloggingcategory_p.h"
+
+#include <QtQml/qqmlinfo.h>
+
+/*!
+ \qmltype LoggingCategory
+ \ingroup qml-utility-elements
+ \inqmlmodule QtQml
+ \brief Defines a logging category in QML
+ \since 5.8
+
+ A logging category can be passed to console.log() and friends as the first argument.
+ If supplied to to the logger the LoggingCategory's name will be used as Logging Category
+ otherwise the default logging category will be used.
+
+ \qml
+ import QtQuick 2.8
+
+ Item {
+ LoggingCategory {
+ id: category
+ name: "com.qt.category"
+ }
+
+ Component.onCompleted: {
+ console.log(category, "message");
+ }
+ }
+ \endqml
+
+ \note As the creation of objects is expensive, it is encouraged to put the needed
+ LoggingCategory definitions into a singleton and import this where needed.
+
+ \sa QLoggingCategory
+*/
+
+/*!
+ \qmlproperty string QtQml::LoggingCategory::name
+
+ Holds the name of the logging category.
+
+ \note This property needs to be set when declaring the LoggingCategory
+ and cannot be changed later.
+
+ \sa QLoggingCategory::categoryName()
+*/
+
+QQmlLoggingCategory::QQmlLoggingCategory(QObject *parent)
+ : QObject(parent)
+ , m_initialized(false)
+{
+}
+
+QQmlLoggingCategory::~QQmlLoggingCategory()
+{
+}
+
+QString QQmlLoggingCategory::name() const
+{
+ return QString::fromUtf8(m_name);
+}
+
+QLoggingCategory *QQmlLoggingCategory::category() const
+{
+ return m_category.data();
+}
+
+void QQmlLoggingCategory::classBegin()
+{
+}
+
+void QQmlLoggingCategory::componentComplete()
+{
+ m_initialized = true;
+ if (m_name.isNull())
+ qmlInfo(this) << QString(QLatin1String("Declaring the name of the LoggingCategory is mandatory and cannot be changed later !"));
+}
+
+void QQmlLoggingCategory::setName(const QString &name)
+{
+ if (m_initialized) {
+ qmlInfo(this) << QString(QLatin1String("The name of a LoggingCategory cannot be changed after the Item is created"));
+ return;
+ }
+
+ m_name = name.toUtf8();
+ QScopedPointer<QLoggingCategory> category(new QLoggingCategory(m_name.constData()));
+ m_category.swap(category);
+}
diff --git a/src/qml/qml/qqmlloggingcategory_p.h b/src/qml/qml/qqmlloggingcategory_p.h
new file mode 100644
index 0000000000..2b7f2f5b53
--- /dev/null
+++ b/src/qml/qml/qqmlloggingcategory_p.h
@@ -0,0 +1,89 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Pelagicore AG
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml 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 QQMLLOGGINGCATEGORY_P_H
+#define QQMLLOGGINGCATEGORY_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/qstring.h>
+#include <QtCore/qloggingcategory.h>
+
+#include <QtQml/qqmlparserstatus.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQmlLoggingCategory : public QObject, public QQmlParserStatus
+{
+ Q_OBJECT
+ Q_INTERFACES(QQmlParserStatus)
+
+ Q_PROPERTY(QString name READ name WRITE setName)
+
+public:
+ QQmlLoggingCategory(QObject *parent = 0);
+ virtual ~QQmlLoggingCategory();
+
+ QString name() const;
+ void setName(const QString &name);
+
+ QLoggingCategory *category() const;
+
+ void classBegin() override;
+ void componentComplete() override;
+
+private:
+ QByteArray m_name;
+ QScopedPointer<QLoggingCategory> m_category;
+ bool m_initialized;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLLOGGINGCATEGORY_H
diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp
index 7b129e2b57..ce0f4b798a 100644
--- a/src/qml/qml/qqmlmetatype.cpp
+++ b/src/qml/qml/qqmlmetatype.cpp
@@ -44,7 +44,6 @@
#include <private/qqmlcustomparser_p.h>
#include <private/qhashedstring_p.h>
#include <private/qqmlimport_p.h>
-#include <private/qqmlcompiler_p.h>
#include <QtCore/qdebug.h>
#include <QtCore/qstringlist.h>
@@ -493,8 +492,8 @@ QQmlType *QQmlType::resolveCompositeBaseType(QQmlEnginePrivate *engine) const
QQmlRefPointer<QQmlTypeData> td(engine->typeLoader.getType(sourceUrl()), QQmlRefPointer<QQmlTypeData>::Adopt);
if (td.isNull() || !td->isComplete())
return 0;
- QQmlCompiledData *cd = td->compiledData();
- const QMetaObject *mo = cd->rootPropertyCache->firstCppMetaObject();
+ QV4::CompiledData::CompilationUnit *compilationUnit = td->compilationUnit();
+ const QMetaObject *mo = compilationUnit->rootPropertyCache()->firstCppMetaObject();
return QQmlMetaType::qmlType(mo);
}
@@ -635,7 +634,7 @@ void QQmlTypePrivate::init() const
QMetaObject *mmo = builder.toMetaObject();
mmo->d.superdata = baseMetaObject;
if (!metaObjects.isEmpty())
- metaObjects.last().metaObject->d.superdata = mmo;
+ metaObjects.constLast().metaObject->d.superdata = mmo;
QQmlProxyMetaObject::ProxyData data = { mmo, t->d->extraData.cd->extFunc, 0, 0 };
metaObjects << data;
}
@@ -657,7 +656,7 @@ void QQmlTypePrivate::init() const
if (metaObjects.isEmpty())
mo = baseMetaObject;
else
- mo = metaObjects.first().metaObject;
+ mo = metaObjects.constFirst().metaObject;
for (int ii = 0; !containsRevisionedAttributes && ii < mo->propertyCount(); ++ii) {
if (isPropertyRevisioned(mo, ii))
@@ -852,7 +851,7 @@ const QMetaObject *QQmlType::metaObject() const
if (d->metaObjects.isEmpty())
return d->baseMetaObject;
else
- return d->metaObjects.first().metaObject;
+ return d->metaObjects.constFirst().metaObject;
}
@@ -1862,7 +1861,7 @@ QQmlType *QQmlMetaType::qmlTypeFromIndex(int idx)
if (idx < 0 || idx >= data->types.count())
return 0;
- return data->types[idx];
+ return data->types.at(idx);
}
/*!
@@ -1914,14 +1913,11 @@ QList<QQmlType*> QQmlMetaType::qmlSingletonTypes()
QMutexLocker lock(metaTypeDataLock());
QQmlMetaTypeData *data = metaTypeData();
- QList<QQmlType*> alltypes = data->nameToType.values();
QList<QQmlType*> retn;
- foreach (QQmlType* t, alltypes) {
- if (t->isSingleton()) {
- retn.append(t);
- }
+ for (const auto type : qAsConst(data->nameToType)) {
+ if (type->isSingleton())
+ retn.append(type);
}
-
return retn;
}
@@ -1929,9 +1925,9 @@ const QQmlPrivate::CachedQmlUnit *QQmlMetaType::findCachedCompilationUnit(const
{
QMutexLocker lock(metaTypeDataLock());
QQmlMetaTypeData *data = metaTypeData();
- for (QVector<QQmlPrivate::QmlUnitCacheLookupFunction>::ConstIterator it = data->lookupCachedQmlUnit.constBegin(), end = data->lookupCachedQmlUnit.constEnd();
- it != end; ++it) {
- if (const QQmlPrivate::CachedQmlUnit *unit = (*it)(uri))
+
+ for (const auto lookup : qAsConst(data->lookupCachedQmlUnit)) {
+ if (const QQmlPrivate::CachedQmlUnit *unit = lookup(uri))
return unit;
}
return 0;
@@ -1961,8 +1957,7 @@ QString QQmlMetaType::prettyTypeName(const QObject *object)
marker = typeName.indexOf(QLatin1String("_QML_"));
if (marker != -1) {
- typeName = typeName.left(marker);
- typeName += QLatin1Char('*');
+ typeName = typeName.left(marker) + QLatin1Char('*');
type = QQmlMetaType::qmlType(QMetaType::type(typeName.toLatin1()));
if (type) {
typeName = type->qmlTypeName();
diff --git a/src/qml/qml/qqmlnetworkaccessmanagerfactory.cpp b/src/qml/qml/qqmlnetworkaccessmanagerfactory.cpp
index f9fea0279e..1680253d73 100644
--- a/src/qml/qml/qqmlnetworkaccessmanagerfactory.cpp
+++ b/src/qml/qml/qqmlnetworkaccessmanagerfactory.cpp
@@ -41,6 +41,8 @@
QT_BEGIN_NAMESPACE
+#if QT_CONFIG(qml_network)
+
/*!
\class QQmlNetworkAccessManagerFactory
\since 5.0
@@ -101,4 +103,6 @@ QQmlNetworkAccessManagerFactory::~QQmlNetworkAccessManagerFactory()
implementation of this method is reentrant.
*/
+#endif // qml_network
+
QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmlnetworkaccessmanagerfactory.h b/src/qml/qml/qqmlnetworkaccessmanagerfactory.h
index 8e3b94fad3..57dec1da29 100644
--- a/src/qml/qml/qqmlnetworkaccessmanagerfactory.h
+++ b/src/qml/qml/qqmlnetworkaccessmanagerfactory.h
@@ -45,6 +45,7 @@
QT_BEGIN_NAMESPACE
+#if QT_CONFIG(qml_network)
class QNetworkAccessManager;
class Q_QML_EXPORT QQmlNetworkAccessManagerFactory
@@ -55,6 +56,8 @@ public:
};
+#endif // qml_network
+
QT_END_NAMESPACE
#endif // QQMLNETWORKACCESSMANAGERFACTORY_H
diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp
index 19e259207c..2218f277d6 100644
--- a/src/qml/qml/qqmlobjectcreator.cpp
+++ b/src/qml/qml/qqmlobjectcreator.cpp
@@ -69,12 +69,11 @@ struct ActiveOCRestorer
};
}
-QQmlObjectCreator::QQmlObjectCreator(QQmlContextData *parentContext, QQmlCompiledData *compiledData, QQmlContextData *creationContext, void *activeVMEDataForRootContext)
+QQmlObjectCreator::QQmlObjectCreator(QQmlContextData *parentContext, QV4::CompiledData::CompilationUnit *compilationUnit, QQmlContextData *creationContext, void *activeVMEDataForRootContext)
: phase(Startup)
- , compiledData(compiledData)
- , resolvedTypes(compiledData->resolvedTypes)
- , propertyCaches(compiledData->propertyCaches)
- , vmeMetaObjectData(compiledData->metaObjects)
+ , compilationUnit(compilationUnit)
+ , resolvedTypes(compilationUnit->resolvedTypes)
+ , propertyCaches(&compilationUnit->propertyCaches)
, sharedState(new QQmlObjectCreatorSharedState)
, topLevelCreator(true)
, activeVMEDataForRootContext(activeVMEDataForRootContext)
@@ -82,24 +81,26 @@ QQmlObjectCreator::QQmlObjectCreator(QQmlContextData *parentContext, QQmlCompile
init(parentContext);
sharedState->componentAttached = 0;
- sharedState->allCreatedBindings.allocate(compiledData->totalBindingsCount);
- sharedState->allParserStatusCallbacks.allocate(compiledData->totalParserStatusCount);
- sharedState->allCreatedObjects.allocate(compiledData->totalObjectCount);
+ sharedState->allCreatedBindings.allocate(compilationUnit->totalBindingsCount);
+ sharedState->allParserStatusCallbacks.allocate(compilationUnit->totalParserStatusCount);
+ sharedState->allCreatedObjects.allocate(compilationUnit->totalObjectCount);
sharedState->allJavaScriptObjects = 0;
sharedState->creationContext = creationContext;
sharedState->rootContext = 0;
- QQmlProfiler *profiler = QQmlEnginePrivate::get(engine)->profiler;
- Q_QML_PROFILE_IF_ENABLED(QQmlProfilerDefinitions::ProfileCreating, profiler,
- sharedState->profiler.init(profiler, compiledData->totalParserStatusCount));
+ if (auto profiler = QQmlEnginePrivate::get(engine)->profiler) {
+ Q_QML_PROFILE_IF_ENABLED(QQmlProfilerDefinitions::ProfileCreating, profiler,
+ sharedState->profiler.init(profiler, compilationUnit->totalParserStatusCount));
+ } else {
+ Q_UNUSED(profiler);
+ }
}
-QQmlObjectCreator::QQmlObjectCreator(QQmlContextData *parentContext, QQmlCompiledData *compiledData, QQmlObjectCreatorSharedState *inheritedSharedState)
+QQmlObjectCreator::QQmlObjectCreator(QQmlContextData *parentContext, QV4::CompiledData::CompilationUnit *compilationUnit, QQmlObjectCreatorSharedState *inheritedSharedState)
: phase(Startup)
- , compiledData(compiledData)
- , resolvedTypes(compiledData->resolvedTypes)
- , propertyCaches(compiledData->propertyCaches)
- , vmeMetaObjectData(compiledData->metaObjects)
+ , compilationUnit(compilationUnit)
+ , resolvedTypes(compilationUnit->resolvedTypes)
+ , propertyCaches(&compilationUnit->propertyCaches)
, sharedState(inheritedSharedState)
, topLevelCreator(false)
, activeVMEDataForRootContext(0)
@@ -113,10 +114,10 @@ void QQmlObjectCreator::init(QQmlContextData *providedParentContext)
engine = parentContext->engine;
v4 = QV8Engine::getV4(engine);
- if (!compiledData->isInitialized())
- compiledData->initialize(engine);
+ if (compilationUnit && !compilationUnit->engine)
+ compilationUnit->linkToEngine(v4);
- qmlUnit = compiledData->compilationUnit->data;
+ qmlUnit = compilationUnit->data;
context = 0;
_qobject = 0;
_scopeObject = 0;
@@ -160,19 +161,16 @@ QObject *QQmlObjectCreator::create(int subComponentIndex, QObject *parent, QQmlI
int objectToCreate;
if (subComponentIndex == -1) {
- objectIndexToId = compiledData->objectIndexToIdForRoot;
objectToCreate = qmlUnit->indexOfRootObject;
} else {
- objectIndexToId = compiledData->objectIndexToIdPerComponent[subComponentIndex];
const QV4::CompiledData::Object *compObj = qmlUnit->objectAt(subComponentIndex);
objectToCreate = compObj->bindingTable()->value.objectIndex;
}
context = new QQmlContextData;
context->isInternal = true;
- context->imports = compiledData->importCache;
- context->imports->addref();
- context->typeCompilationUnit = compiledData->compilationUnit;
+ context->imports = compilationUnit->importCache;
+ context->initFromTypeCompilationUnit(compilationUnit, subComponentIndex);
context->setParent(parentContext);
if (!sharedState->rootContext) {
@@ -185,16 +183,14 @@ QObject *QQmlObjectCreator::create(int subComponentIndex, QObject *parent, QQmlI
Q_ASSERT(sharedState->allJavaScriptObjects || topLevelCreator);
if (topLevelCreator)
- sharedState->allJavaScriptObjects = scope.alloc(compiledData->totalObjectCount);
+ sharedState->allJavaScriptObjects = scope.alloc(compilationUnit->totalObjectCount);
- context->setIdPropertyData(objectIndexToId);
-
- if (subComponentIndex == -1 && compiledData->scripts.count()) {
- QV4::ScopedObject scripts(scope, v4->newArrayObject(compiledData->scripts.count()));
+ if (subComponentIndex == -1 && compilationUnit->dependentScripts.count()) {
+ QV4::ScopedObject scripts(scope, v4->newArrayObject(compilationUnit->dependentScripts.count()));
context->importedScripts.set(v4, scripts);
QV4::ScopedValue v(scope);
- for (int i = 0; i < compiledData->scripts.count(); ++i) {
- QQmlScriptData *s = compiledData->scripts.at(i);
+ for (int i = 0; i < compilationUnit->dependentScripts.count(); ++i) {
+ QQmlScriptData *s = compilationUnit->dependentScripts.at(i);
scripts->putIndexed(i, (v = s->scriptValueForContext(context)));
}
} else if (sharedState->creationContext) {
@@ -205,10 +201,10 @@ QObject *QQmlObjectCreator::create(int subComponentIndex, QObject *parent, QQmlI
if (instance) {
QQmlData *ddata = QQmlData::get(instance);
Q_ASSERT(ddata);
- if (ddata->compiledData)
- ddata->compiledData->release();
- ddata->compiledData = compiledData;
- ddata->compiledData->addref();
+ if (ddata->compilationUnit)
+ ddata->compilationUnit->release();
+ ddata->compilationUnit = compilationUnit;
+ ddata->compilationUnit->addref();
}
if (topLevelCreator)
@@ -242,7 +238,7 @@ bool QQmlObjectCreator::populateDeferredProperties(QObject *instance)
Q_ASSERT(topLevelCreator);
Q_ASSERT(!sharedState->allJavaScriptObjects);
- sharedState->allJavaScriptObjects = valueScope.alloc(compiledData->totalObjectCount);
+ sharedState->allJavaScriptObjects = valueScope.alloc(compilationUnit->totalObjectCount);
QV4::QmlContext *qmlContext = static_cast<QV4::QmlContext *>(valueScope.alloc(1));
@@ -261,11 +257,7 @@ bool QQmlObjectCreator::populateDeferredProperties(QObject *instance)
qSwap(_bindingTarget, bindingTarget);
qSwap(_vmeMetaObject, vmeMetaObject);
- QBitArray bindingSkipList = compiledData->deferredBindingsPerObject.value(_compiledObjectIndex);
- for (int i = 0; i < bindingSkipList.count(); ++i)
- bindingSkipList.setBit(i, !bindingSkipList.testBit(i));
-
- setupBindings(bindingSkipList);
+ setupBindings(/*applyDeferredBindings=*/true);
qSwap(_vmeMetaObject, vmeMetaObject);
qSwap(_bindingTarget, bindingTarget);
@@ -285,14 +277,10 @@ bool QQmlObjectCreator::populateDeferredProperties(QObject *instance)
void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const QV4::CompiledData::Binding *binding)
{
- QQmlPropertyPrivate::WriteFlags propertyWriteFlags = QQmlPropertyPrivate::BypassInterceptor |
- QQmlPropertyPrivate::RemoveBindingOnAliasWrite;
- int propertyWriteStatus = -1;
- void *argv[] = { 0, 0, &propertyWriteStatus, &propertyWriteFlags };
-
+ QQmlPropertyData::WriteFlags propertyWriteFlags = QQmlPropertyData::BypassInterceptor | QQmlPropertyData::RemoveBindingOnAliasWrite;
QV4::Scope scope(v4);
- int propertyType = property->propType;
+ int propertyType = property->propType();
if (property->isEnum()) {
if (binding->flags & QV4::CompiledData::Binding::IsResolvedEnum) {
@@ -313,39 +301,35 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
double n = binding->valueAsNumber();
if (double(int(n)) == n) {
if (property->isVarProperty()) {
- _vmeMetaObject->setVMEProperty(property->coreIndex, QV4::Primitive::fromInt32(int(n)));
+ _vmeMetaObject->setVMEProperty(property->coreIndex(), QV4::Primitive::fromInt32(int(n)));
} else {
int i = int(n);
QVariant value(i);
- argv[0] = &value;
- QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv);
+ property->writeProperty(_qobject, &value, propertyWriteFlags);
}
} else {
if (property->isVarProperty()) {
- _vmeMetaObject->setVMEProperty(property->coreIndex, QV4::Primitive::fromDouble(n));
+ _vmeMetaObject->setVMEProperty(property->coreIndex(), QV4::Primitive::fromDouble(n));
} else {
QVariant value(n);
- argv[0] = &value;
- QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv);
+ property->writeProperty(_qobject, &value, propertyWriteFlags);
}
}
} else if (binding->type == QV4::CompiledData::Binding::Type_Boolean) {
if (property->isVarProperty()) {
- _vmeMetaObject->setVMEProperty(property->coreIndex, QV4::Primitive::fromBoolean(binding->valueAsBoolean()));
+ _vmeMetaObject->setVMEProperty(property->coreIndex(), QV4::Primitive::fromBoolean(binding->valueAsBoolean()));
} else {
QVariant value(binding->valueAsBoolean());
- argv[0] = &value;
- QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv);
+ property->writeProperty(_qobject, &value, propertyWriteFlags);
}
} else {
QString stringValue = binding->valueAsString(qmlUnit);
if (property->isVarProperty()) {
QV4::ScopedString s(scope, v4->newString(stringValue));
- _vmeMetaObject->setVMEProperty(property->coreIndex, s);
+ _vmeMetaObject->setVMEProperty(property->coreIndex(), s);
} else {
QVariant value = QQmlStringConverters::variantFromString(stringValue);
- argv[0] = &value;
- QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv);
+ property->writeProperty(_qobject, &value, propertyWriteFlags);
}
}
}
@@ -353,22 +337,19 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
case QVariant::String: {
Q_ASSERT(binding->evaluatesToString());
QString value = binding->valueAsString(qmlUnit);
- argv[0] = &value;
- QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv);
+ property->writeProperty(_qobject, &value, propertyWriteFlags);
}
break;
case QVariant::StringList: {
Q_ASSERT(binding->evaluatesToString());
QStringList value(binding->valueAsString(qmlUnit));
- argv[0] = &value;
- QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv);
+ property->writeProperty(_qobject, &value, propertyWriteFlags);
}
break;
case QVariant::ByteArray: {
Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_String);
QByteArray value(binding->valueAsString(qmlUnit).toUtf8());
- argv[0] = &value;
- QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv);
+ property->writeProperty(_qobject, &value, propertyWriteFlags);
}
break;
case QVariant::Url: {
@@ -376,20 +357,18 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
QString string = binding->valueAsString(qmlUnit);
// Encoded dir-separators defeat QUrl processing - decode them first
string.replace(QLatin1String("%2f"), QLatin1String("/"), Qt::CaseInsensitive);
- QUrl value = string.isEmpty() ? QUrl() : compiledData->url().resolved(QUrl(string));
+ QUrl value = string.isEmpty() ? QUrl() : compilationUnit->url().resolved(QUrl(string));
// Apply URL interceptor
if (engine->urlInterceptor())
value = engine->urlInterceptor()->intercept(value, QQmlAbstractUrlInterceptor::UrlString);
- argv[0] = &value;
- QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv);
+ property->writeProperty(_qobject, &value, propertyWriteFlags);
}
break;
case QVariant::UInt: {
Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Number);
double d = binding->valueAsNumber();
uint value = uint(d);
- argv[0] = &value;
- QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv);
+ property->writeProperty(_qobject, &value, propertyWriteFlags);
break;
}
break;
@@ -397,23 +376,20 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Number);
double d = binding->valueAsNumber();
int value = int(d);
- argv[0] = &value;
- QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv);
+ property->writeProperty(_qobject, &value, propertyWriteFlags);
break;
}
break;
case QMetaType::Float: {
Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Number);
float value = float(binding->valueAsNumber());
- argv[0] = &value;
- QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv);
+ property->writeProperty(_qobject, &value, propertyWriteFlags);
}
break;
case QVariant::Double: {
Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Number);
double value = binding->valueAsNumber();
- argv[0] = &value;
- QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv);
+ property->writeProperty(_qobject, &value, propertyWriteFlags);
}
break;
case QVariant::Color: {
@@ -421,9 +397,8 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
uint colorValue = QQmlStringConverters::rgbaFromString(binding->valueAsString(qmlUnit), &ok);
Q_ASSERT(ok);
struct { void *data[4]; } buffer;
- if (QQml_valueTypeProvider()->storeValueType(property->propType, &colorValue, &buffer, sizeof(buffer))) {
- argv[0] = reinterpret_cast<void *>(&buffer);
- QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv);
+ if (QQml_valueTypeProvider()->storeValueType(property->propType(), &colorValue, &buffer, sizeof(buffer))) {
+ property->writeProperty(_qobject, &buffer, propertyWriteFlags);
}
}
break;
@@ -432,16 +407,14 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
bool ok = false;
QDate value = QQmlStringConverters::dateFromString(binding->valueAsString(qmlUnit), &ok);
Q_ASSERT(ok);
- argv[0] = &value;
- QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv);
+ property->writeProperty(_qobject, &value, propertyWriteFlags);
}
break;
case QVariant::Time: {
bool ok = false;
QTime value = QQmlStringConverters::timeFromString(binding->valueAsString(qmlUnit), &ok);
Q_ASSERT(ok);
- argv[0] = &value;
- QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv);
+ property->writeProperty(_qobject, &value, propertyWriteFlags);
}
break;
case QVariant::DateTime: {
@@ -454,8 +427,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
value = QDateTime(QDate::fromJulianDay(date), QTime::fromMSecsSinceStartOfDay(msecsSinceStartOfDay));
}
Q_ASSERT(ok);
- argv[0] = &value;
- QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv);
+ property->writeProperty(_qobject, &value, propertyWriteFlags);
}
break;
#endif // QT_NO_DATESTRING
@@ -463,55 +435,48 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
bool ok = false;
QPoint value = QQmlStringConverters::pointFFromString(binding->valueAsString(qmlUnit), &ok).toPoint();
Q_ASSERT(ok);
- argv[0] = &value;
- QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv);
+ property->writeProperty(_qobject, &value, propertyWriteFlags);
}
break;
case QVariant::PointF: {
bool ok = false;
QPointF value = QQmlStringConverters::pointFFromString(binding->valueAsString(qmlUnit), &ok);
Q_ASSERT(ok);
- argv[0] = &value;
- QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv);
+ property->writeProperty(_qobject, &value, propertyWriteFlags);
}
break;
case QVariant::Size: {
bool ok = false;
QSize value = QQmlStringConverters::sizeFFromString(binding->valueAsString(qmlUnit), &ok).toSize();
Q_ASSERT(ok);
- argv[0] = &value;
- QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv);
+ property->writeProperty(_qobject, &value, propertyWriteFlags);
}
break;
case QVariant::SizeF: {
bool ok = false;
QSizeF value = QQmlStringConverters::sizeFFromString(binding->valueAsString(qmlUnit), &ok);
Q_ASSERT(ok);
- argv[0] = &value;
- QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv);
+ property->writeProperty(_qobject, &value, propertyWriteFlags);
}
break;
case QVariant::Rect: {
bool ok = false;
QRect value = QQmlStringConverters::rectFFromString(binding->valueAsString(qmlUnit), &ok).toRect();
Q_ASSERT(ok);
- argv[0] = &value;
- QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv);
+ property->writeProperty(_qobject, &value, propertyWriteFlags);
}
break;
case QVariant::RectF: {
bool ok = false;
QRectF value = QQmlStringConverters::rectFFromString(binding->valueAsString(qmlUnit), &ok);
Q_ASSERT(ok);
- argv[0] = &value;
- QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv);
+ property->writeProperty(_qobject, &value, propertyWriteFlags);
}
break;
case QVariant::Bool: {
Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Boolean);
bool value = binding->valueAsBoolean();
- argv[0] = &value;
- QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv);
+ property->writeProperty(_qobject, &value, propertyWriteFlags);
}
break;
case QVariant::Vector2D: {
@@ -522,8 +487,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
bool ok = QQmlStringConverters::createFromString(QMetaType::QVector2D, binding->valueAsString(qmlUnit), &vec, sizeof(vec));
Q_ASSERT(ok);
Q_UNUSED(ok);
- argv[0] = reinterpret_cast<void *>(&vec);
- QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv);
+ property->writeProperty(_qobject, &vec, propertyWriteFlags);
}
break;
case QVariant::Vector3D: {
@@ -535,8 +499,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
bool ok = QQmlStringConverters::createFromString(QMetaType::QVector3D, binding->valueAsString(qmlUnit), &vec, sizeof(vec));
Q_ASSERT(ok);
Q_UNUSED(ok);
- argv[0] = reinterpret_cast<void *>(&vec);
- QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv);
+ property->writeProperty(_qobject, &vec, propertyWriteFlags);
}
break;
case QVariant::Vector4D: {
@@ -549,8 +512,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
bool ok = QQmlStringConverters::createFromString(QMetaType::QVector4D, binding->valueAsString(qmlUnit), &vec, sizeof(vec));
Q_ASSERT(ok);
Q_UNUSED(ok);
- argv[0] = reinterpret_cast<void *>(&vec);
- QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv);
+ property->writeProperty(_qobject, &vec, propertyWriteFlags);
}
break;
case QVariant::Quaternion: {
@@ -563,8 +525,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
bool ok = QQmlStringConverters::createFromString(QMetaType::QQuaternion, binding->valueAsString(qmlUnit), &vec, sizeof(vec));
Q_ASSERT(ok);
Q_UNUSED(ok);
- argv[0] = reinterpret_cast<void *>(&vec);
- QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv);
+ property->writeProperty(_qobject, &vec, propertyWriteFlags);
}
break;
case QVariant::RegExp:
@@ -572,45 +533,40 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
break;
default: {
// generate single literal value assignment to a list property if required
- if (property->propType == qMetaTypeId<QList<qreal> >()) {
+ if (property->propType() == qMetaTypeId<QList<qreal> >()) {
Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Number);
QList<qreal> value;
value.append(binding->valueAsNumber());
- argv[0] = reinterpret_cast<void *>(&value);
- QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv);
+ property->writeProperty(_qobject, &value, propertyWriteFlags);
break;
- } else if (property->propType == qMetaTypeId<QList<int> >()) {
+ } else if (property->propType() == qMetaTypeId<QList<int> >()) {
Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Number);
double n = binding->valueAsNumber();
QList<int> value;
value.append(int(n));
- argv[0] = reinterpret_cast<void *>(&value);
- QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv);
+ property->writeProperty(_qobject, &value, propertyWriteFlags);
break;
- } else if (property->propType == qMetaTypeId<QList<bool> >()) {
+ } else if (property->propType() == qMetaTypeId<QList<bool> >()) {
Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Boolean);
QList<bool> value;
value.append(binding->valueAsBoolean());
- argv[0] = reinterpret_cast<void *>(&value);
- QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv);
+ property->writeProperty(_qobject, &value, propertyWriteFlags);
break;
- } else if (property->propType == qMetaTypeId<QList<QUrl> >()) {
+ } else if (property->propType() == qMetaTypeId<QList<QUrl> >()) {
Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_String);
QString urlString = binding->valueAsString(qmlUnit);
- QUrl u = urlString.isEmpty() ? QUrl() : compiledData->url().resolved(QUrl(urlString));
+ QUrl u = urlString.isEmpty() ? QUrl() : compilationUnit->url().resolved(QUrl(urlString));
QList<QUrl> value;
value.append(u);
- argv[0] = reinterpret_cast<void *>(&value);
- QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv);
+ property->writeProperty(_qobject, &value, propertyWriteFlags);
break;
- } else if (property->propType == qMetaTypeId<QList<QString> >()) {
+ } else if (property->propType() == qMetaTypeId<QList<QString> >()) {
Q_ASSERT(binding->evaluatesToString());
QList<QString> value;
value.append(binding->valueAsString(qmlUnit));
- argv[0] = reinterpret_cast<void *>(&value);
- QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv);
+ property->writeProperty(_qobject, &value, propertyWriteFlags);
break;
- } else if (property->propType == qMetaTypeId<QJSValue>()) {
+ } else if (property->propType() == qMetaTypeId<QJSValue>()) {
QJSValue value;
if (binding->type == QV4::CompiledData::Binding::Type_Boolean) {
value = QJSValue(binding->valueAsBoolean());
@@ -623,25 +579,23 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
} else {
value = QJSValue(binding->valueAsString(qmlUnit));
}
- argv[0] = reinterpret_cast<void *>(&value);
- QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv);
+ property->writeProperty(_qobject, &value, propertyWriteFlags);
break;
}
// otherwise, try a custom type assignment
QString stringValue = binding->valueAsString(qmlUnit);
- QQmlMetaType::StringConverter converter = QQmlMetaType::customStringConverter(property->propType);
+ QQmlMetaType::StringConverter converter = QQmlMetaType::customStringConverter(property->propType());
Q_ASSERT(converter);
QVariant value = (*converter)(stringValue);
- QMetaProperty metaProperty = _qobject->metaObject()->property(property->coreIndex);
- if (value.isNull() || ((int)metaProperty.type() != property->propType && metaProperty.userType() != property->propType)) {
+ QMetaProperty metaProperty = _qobject->metaObject()->property(property->coreIndex());
+ if (value.isNull() || ((int)metaProperty.type() != property->propType() && metaProperty.userType() != property->propType())) {
recordError(binding->location, tr("Cannot assign value %1 to property %2").arg(stringValue).arg(QString::fromUtf8(metaProperty.name())));
break;
}
- argv[0] = value.data();
- QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv);
+ property->writeProperty(_qobject, value.data(), propertyWriteFlags);
}
break;
}
@@ -658,22 +612,22 @@ static QQmlType *qmlTypeForObject(QObject *object)
return type;
}
-void QQmlObjectCreator::setupBindings(const QBitArray &bindingsToSkip)
+void QQmlObjectCreator::setupBindings(bool applyDeferredBindings)
{
QQmlListProperty<void> savedList;
qSwap(_currentList, savedList);
- const QV4::CompiledData::BindingPropertyData &propertyData = compiledData->compilationUnit->bindingPropertyDataPerObject.at(_compiledObjectIndex);
+ const QV4::CompiledData::BindingPropertyData &propertyData = compilationUnit->bindingPropertyDataPerObject.at(_compiledObjectIndex);
- if (_compiledObject->idIndex) {
+ if (_compiledObject->idNameIndex) {
const QQmlPropertyData *idProperty = propertyData.last();
Q_ASSERT(!idProperty || !idProperty->isValid() || idProperty->name(_qobject) == QLatin1String("id"));
- if (idProperty && idProperty->isValid() && idProperty->isWritable() && idProperty->propType == QMetaType::QString) {
+ if (idProperty && idProperty->isValid() && idProperty->isWritable() && idProperty->propType() == QMetaType::QString) {
QV4::CompiledData::Binding idBinding;
idBinding.propertyNameIndex = 0; // Not used
idBinding.flags = 0;
idBinding.type = QV4::CompiledData::Binding::Type_String;
- idBinding.stringIndex = _compiledObject->idIndex;
+ idBinding.stringIndex = _compiledObject->idNameIndex;
idBinding.location = _compiledObject->location; // ###
setPropertyValue(idProperty, &idBinding);
}
@@ -681,23 +635,23 @@ void QQmlObjectCreator::setupBindings(const QBitArray &bindingsToSkip)
// ### this is best done through type-compile-time binding skip lists.
if (_valueTypeProperty) {
- QQmlAbstractBinding *binding = QQmlPropertyPrivate::binding(_bindingTarget, _valueTypeProperty->coreIndex);
+ QQmlAbstractBinding *binding = QQmlPropertyPrivate::binding(_bindingTarget, QQmlPropertyIndex(_valueTypeProperty->coreIndex()));
if (binding && !binding->isValueTypeProxy()) {
- QQmlPropertyPrivate::removeBinding(_bindingTarget, _valueTypeProperty->coreIndex);
+ QQmlPropertyPrivate::removeBinding(_bindingTarget, QQmlPropertyIndex(_valueTypeProperty->coreIndex()));
} else if (binding) {
QQmlValueTypeProxyBinding *proxy = static_cast<QQmlValueTypeProxyBinding *>(binding);
if (qmlTypeForObject(_bindingTarget)) {
quint32 bindingSkipList = 0;
- QQmlPropertyData *defaultProperty = _compiledObject->indexOfDefaultProperty != -1 ? _propertyCache->parent()->defaultProperty() : _propertyCache->defaultProperty();
+ QQmlPropertyData *defaultProperty = _compiledObject->indexOfDefaultPropertyOrAlias != -1 ? _propertyCache->parent()->defaultProperty() : _propertyCache->defaultProperty();
const QV4::CompiledData::Binding *binding = _compiledObject->bindingTable();
for (quint32 i = 0; i < _compiledObject->nBindings; ++i, ++binding) {
QQmlPropertyData *property = binding->propertyNameIndex != 0 ? _propertyCache->property(stringAt(binding->propertyNameIndex), _qobject, context) : defaultProperty;
if (property)
- bindingSkipList |= (1 << property->coreIndex);
+ bindingSkipList |= (1 << property->coreIndex());
}
proxy->removeBindings(bindingSkipList);
@@ -709,16 +663,24 @@ void QQmlObjectCreator::setupBindings(const QBitArray &bindingsToSkip)
const QV4::CompiledData::Binding *binding = _compiledObject->bindingTable();
for (quint32 i = 0; i < _compiledObject->nBindings; ++i, ++binding) {
- if (static_cast<int>(i) < bindingsToSkip.size() && bindingsToSkip.testBit(i))
+ if (binding->flags & QV4::CompiledData::Binding::IsCustomParserBinding)
continue;
+ if (binding->flags & QV4::CompiledData::Binding::IsDeferredBinding) {
+ if (!applyDeferredBindings)
+ continue;
+ } else {
+ if (applyDeferredBindings)
+ continue;
+ }
+
const QQmlPropertyData *property = propertyData.at(i);
if (property && property->isQList()) {
- if (property->coreIndex != currentListPropertyIndex) {
+ if (property->coreIndex() != currentListPropertyIndex) {
void *argv[1] = { (void*)&_currentList };
- QMetaObject::metacall(_qobject, QMetaObject::ReadProperty, property->coreIndex, argv);
- currentListPropertyIndex = property->coreIndex;
+ QMetaObject::metacall(_qobject, QMetaObject::ReadProperty, property->coreIndex(), argv);
+ currentListPropertyIndex = property->coreIndex();
}
} else if (_currentList.object) {
_currentList = QQmlListProperty<void>();
@@ -736,7 +698,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con
{
if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) {
Q_ASSERT(stringAt(qmlUnit->objectAt(binding->value.objectIndex)->inheritedTypeNameIndex).isEmpty());
- QQmlCompiledData::TypeReference *tr = resolvedTypes.value(binding->propertyNameIndex);
+ QV4::CompiledData::ResolvedTypeReference *tr = resolvedTypes.value(binding->propertyNameIndex);
Q_ASSERT(tr);
QQmlType *attachedType = tr->type;
if (!attachedType) {
@@ -754,7 +716,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con
}
// ### resolve this at compile time
- if (property && property->propType == qMetaTypeId<QQmlScriptString>()) {
+ if (property && property->propType() == qMetaTypeId<QQmlScriptString>()) {
QQmlScriptString ss(binding->valueAsScriptString(qmlUnit), context->asQQmlContext(), _scopeObject);
ss.d.data()->bindingId = binding->type == QV4::CompiledData::Binding::Type_Script ? binding->value.compiledScriptIndex : (quint32)QQmlBinding::Invalid;
ss.d.data()->lineNumber = binding->location.line;
@@ -763,11 +725,11 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con
ss.d.data()->isNumberLiteral = binding->type == QV4::CompiledData::Binding::Type_Number;
ss.d.data()->numberValue = binding->valueAsNumber();
- QQmlPropertyPrivate::WriteFlags propertyWriteFlags = QQmlPropertyPrivate::BypassInterceptor |
- QQmlPropertyPrivate::RemoveBindingOnAliasWrite;
+ QQmlPropertyData::WriteFlags propertyWriteFlags = QQmlPropertyData::BypassInterceptor |
+ QQmlPropertyData::RemoveBindingOnAliasWrite;
int propertyWriteStatus = -1;
void *argv[] = { &ss, 0, &propertyWriteStatus, &propertyWriteFlags };
- QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv);
+ QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex(), argv);
return true;
}
@@ -790,20 +752,20 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con
const QQmlPropertyData *valueTypeProperty = 0;
QObject *bindingTarget = _bindingTarget;
- if (QQmlValueTypeFactory::isValueType(property->propType)) {
- valueType = QQmlValueTypeFactory::valueType(property->propType);
+ if (QQmlValueTypeFactory::isValueType(property->propType())) {
+ valueType = QQmlValueTypeFactory::valueType(property->propType());
if (!valueType) {
recordError(binding->location, tr("Cannot set properties on %1 as it is null").arg(stringAt(binding->propertyNameIndex)));
return false;
}
- valueType->read(_qobject, property->coreIndex);
+ valueType->read(_qobject, property->coreIndex());
groupObject = valueType;
valueTypeProperty = property;
} else {
void *argv[1] = { &groupObject };
- QMetaObject::metacall(_qobject, QMetaObject::ReadProperty, property->coreIndex, argv);
+ QMetaObject::metacall(_qobject, QMetaObject::ReadProperty, property->coreIndex(), argv);
if (!groupObject) {
recordError(binding->location, tr("Cannot set properties on %1 as it is null").arg(stringAt(binding->propertyNameIndex)));
return false;
@@ -816,48 +778,49 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con
return false;
if (valueType)
- valueType->write(_qobject, property->coreIndex, QQmlPropertyPrivate::BypassInterceptor);
+ valueType->write(_qobject, property->coreIndex(), QQmlPropertyData::BypassInterceptor);
return true;
}
}
- if (_ddata->hasBindingBit(property->coreIndex) && !(binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression)
+ if (_ddata->hasBindingBit(property->coreIndex()) && !(binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression)
&& !(binding->flags & QV4::CompiledData::Binding::IsOnAssignment)
&& !_valueTypeProperty)
- QQmlPropertyPrivate::removeBinding(_bindingTarget, property->coreIndex);
+ QQmlPropertyPrivate::removeBinding(_bindingTarget, QQmlPropertyIndex(property->coreIndex()));
if (binding->type == QV4::CompiledData::Binding::Type_Script) {
- QV4::Function *runtimeFunction = compiledData->compilationUnit->runtimeFunctions[binding->value.compiledScriptIndex];
+ QV4::Function *runtimeFunction = compilationUnit->runtimeFunctions[binding->value.compiledScriptIndex];
QV4::Scope scope(v4);
QV4::ScopedContext qmlContext(scope, currentQmlContext());
QV4::ScopedFunctionObject function(scope, QV4::FunctionObject::createScriptFunction(qmlContext, runtimeFunction, /*createProto*/ false));
if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression) {
- int signalIndex = _propertyCache->methodIndexToSignalIndex(property->coreIndex);
+ int signalIndex = _propertyCache->methodIndexToSignalIndex(property->coreIndex());
QQmlBoundSignal *bs = new QQmlBoundSignal(_bindingTarget, signalIndex, _scopeObject, engine);
QQmlBoundSignalExpression *expr = new QQmlBoundSignalExpression(_bindingTarget, signalIndex,
context, _scopeObject, function);
bs->takeExpression(expr);
} else {
- QQmlBinding *qmlBinding = new QQmlBinding(function, _scopeObject, context);
-
// When writing bindings to grouped properties implemented as value types,
// such as point.x: { someExpression; }, then the binding is installed on
// the point property (_qobjectForBindings) and after evaluating the expression,
// the result is written to a value type virtual property, that contains the sub-index
// of the "x" property.
- QQmlPropertyData targetCorePropertyData = *property;
- if (_valueTypeProperty)
- targetCorePropertyData = QQmlPropertyPrivate::saveValueType(*_valueTypeProperty, _qobject->metaObject(), property->coreIndex, engine);
+ QQmlBinding *qmlBinding;
+ if (_valueTypeProperty) {
+ qmlBinding = QQmlBinding::create(_valueTypeProperty, function, _scopeObject, context);
+ qmlBinding->setTarget(_bindingTarget, *_valueTypeProperty, property);
+ } else {
+ qmlBinding = QQmlBinding::create(property, function, _scopeObject, context);
+ qmlBinding->setTarget(_bindingTarget, *property, nullptr);
+ }
sharedState->allCreatedBindings.push(QQmlAbstractBinding::Ptr(qmlBinding));
- qmlBinding->setTarget(_bindingTarget, targetCorePropertyData);
-
- if (targetCorePropertyData.isAlias()) {
+ if (property->isAlias()) {
QQmlPropertyPrivate::setBinding(qmlBinding, QQmlPropertyPrivate::DontEnable);
} else {
qmlBinding->addToObject();
@@ -865,7 +828,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con
if (!_valueTypeProperty) {
QQmlData *targetDeclarativeData = QQmlData::get(_bindingTarget);
Q_ASSERT(targetDeclarativeData);
- targetDeclarativeData->setPendingBindingBit(_bindingTarget, property->coreIndex);
+ targetDeclarativeData->setPendingBindingBit(_bindingTarget, property->coreIndex());
}
}
}
@@ -878,15 +841,16 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con
QQmlType *type = qmlTypeForObject(createdSubObject);
Q_ASSERT(type);
- QQmlPropertyData targetCorePropertyData = *property;
- if (_valueTypeProperty)
- targetCorePropertyData = QQmlPropertyPrivate::saveValueType(*_valueTypeProperty, _qobject->metaObject(), property->coreIndex, engine);
-
int valueSourceCast = type->propertyValueSourceCast();
if (valueSourceCast != -1) {
QQmlPropertyValueSource *vs = reinterpret_cast<QQmlPropertyValueSource *>(reinterpret_cast<char *>(createdSubObject) + valueSourceCast);
QObject *target = createdSubObject->parent();
- vs->setTarget(QQmlPropertyPrivate::restore(target, targetCorePropertyData, context));
+ QQmlProperty prop;
+ if (_valueTypeProperty)
+ prop = QQmlPropertyPrivate::restore(target, *_valueTypeProperty, property, context);
+ else
+ prop = QQmlPropertyPrivate::restore(target, *property, nullptr, context);
+ vs->setTarget(prop);
return true;
}
int valueInterceptorCast = type->propertyValueInterceptorCast();
@@ -894,25 +858,36 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con
QQmlPropertyValueInterceptor *vi = reinterpret_cast<QQmlPropertyValueInterceptor *>(reinterpret_cast<char *>(createdSubObject) + valueInterceptorCast);
QObject *target = createdSubObject->parent();
- if (targetCorePropertyData.isAlias()) {
- int propIndex;
- QQmlPropertyPrivate::findAliasTarget(target, targetCorePropertyData.coreIndex, &target, &propIndex);
+ QQmlPropertyIndex propertyIndex;
+ if (property->isAlias()) {
+ QQmlPropertyIndex originalIndex(property->coreIndex(), _valueTypeProperty ? _valueTypeProperty->coreIndex() : -1);
+ QQmlPropertyIndex propIndex;
+ QQmlPropertyPrivate::findAliasTarget(target, originalIndex, &target, &propIndex);
QQmlData *data = QQmlData::get(target);
if (!data || !data->propertyCache) {
qWarning() << "can't resolve property alias for 'on' assignment";
return false;
}
- targetCorePropertyData = *data->propertyCache->property(propIndex);
- }
- QQmlProperty prop =
- QQmlPropertyPrivate::restore(target, targetCorePropertyData, context);
+ // we can't have aliasses on subproperties of value types, so:
+ QQmlPropertyData targetPropertyData = *data->propertyCache->property(propIndex.coreIndex());
+ auto prop = QQmlPropertyPrivate::restore(target, targetPropertyData, nullptr, context);
+ vi->setTarget(prop);
+ propertyIndex = QQmlPropertyPrivate::propertyIndex(prop);
+ } else {
+ QQmlProperty prop;
+ if (_valueTypeProperty)
+ prop = QQmlPropertyPrivate::restore(target, *_valueTypeProperty, property, context);
+ else
+ prop = QQmlPropertyPrivate::restore(target, *property, nullptr, context);
+ vi->setTarget(prop);
+ propertyIndex = QQmlPropertyPrivate::propertyIndex(prop);
+ }
- vi->setTarget(prop);
QQmlInterceptorMetaObject *mo = QQmlInterceptorMetaObject::get(target);
if (!mo)
mo = new QQmlInterceptorMetaObject(target, QQmlData::get(target)->propertyCache);
- mo->registerInterceptor(prop.index(), QQmlPropertyPrivate::valueTypeCoreIndex(prop), vi);
+ mo->registerInterceptor(propertyIndex, vi);
return true;
}
return false;
@@ -930,7 +905,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con
return false;
}
- QMetaMethod signalMethod = _qobject->metaObject()->method(property->coreIndex);
+ QMetaMethod signalMethod = _qobject->metaObject()->method(property->coreIndex());
if (!QMetaObject::checkConnectArgs(signalMethod, method)) {
recordError(binding->valueLocation,
tr("Cannot connect mismatched signal/slot %1 %vs. %2")
@@ -939,33 +914,33 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con
return false;
}
- QQmlPropertyPrivate::connect(_qobject, property->coreIndex, createdSubObject, method.methodIndex());
+ QQmlPropertyPrivate::connect(_qobject, property->coreIndex(), createdSubObject, method.methodIndex());
return true;
}
- QQmlPropertyPrivate::WriteFlags propertyWriteFlags = QQmlPropertyPrivate::BypassInterceptor |
- QQmlPropertyPrivate::RemoveBindingOnAliasWrite;
+ QQmlPropertyData::WriteFlags propertyWriteFlags = QQmlPropertyData::BypassInterceptor |
+ QQmlPropertyData::RemoveBindingOnAliasWrite;
int propertyWriteStatus = -1;
void *argv[] = { 0, 0, &propertyWriteStatus, &propertyWriteFlags };
- if (const char *iid = QQmlMetaType::interfaceIId(property->propType)) {
+ if (const char *iid = QQmlMetaType::interfaceIId(property->propType())) {
void *ptr = createdSubObject->qt_metacast(iid);
if (ptr) {
argv[0] = &ptr;
- QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv);
+ QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex(), argv);
} else {
recordError(binding->location, tr("Cannot assign object to interface property"));
return false;
}
- } else if (property->propType == QMetaType::QVariant) {
+ } else if (property->propType() == QMetaType::QVariant) {
if (property->isVarProperty()) {
QV4::Scope scope(v4);
QV4::ScopedValue wrappedObject(scope, QV4::QObjectWrapper::wrap(QV8Engine::getV4(engine), createdSubObject));
- _vmeMetaObject->setVMEProperty(property->coreIndex, wrappedObject);
+ _vmeMetaObject->setVMEProperty(property->coreIndex(), wrappedObject);
} else {
QVariant value = QVariant::fromValue(createdSubObject);
argv[0] = &value;
- QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv);
+ QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex(), argv);
}
} else if (property->isQList()) {
Q_ASSERT(_currentList.object);
@@ -973,7 +948,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con
void *itemToAdd = createdSubObject;
const char *iid = 0;
- int listItemType = QQmlEnginePrivate::get(engine)->listType(property->propType);
+ int listItemType = QQmlEnginePrivate::get(engine)->listType(property->propType());
if (listItemType != -1)
iid = QQmlMetaType::interfaceIId(listItemType);
if (iid)
@@ -989,7 +964,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con
} else {
// pointer compatibility was tested in QQmlPropertyValidator at type compile time
argv[0] = &createdSubObject;
- QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv);
+ QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex(), argv);
}
return true;
}
@@ -1009,9 +984,9 @@ void QQmlObjectCreator::setupFunctions()
QV4::ScopedValue function(scope);
QV4::ScopedContext qmlContext(scope, currentQmlContext());
- const quint32 *functionIdx = _compiledObject->functionOffsetTable();
+ const QV4::CompiledData::LEUInt32 *functionIdx = _compiledObject->functionOffsetTable();
for (quint32 i = 0; i < _compiledObject->nFunctions; ++i, ++functionIdx) {
- QV4::Function *runtimeFunction = compiledData->compilationUnit->runtimeFunctions[*functionIdx];
+ QV4::Function *runtimeFunction = compilationUnit->runtimeFunctions[*functionIdx];
const QString name = runtimeFunction->name()->toQString();
QQmlPropertyData *property = _propertyCache->property(name, _qobject, context);
@@ -1019,25 +994,24 @@ void QQmlObjectCreator::setupFunctions()
continue;
function = QV4::FunctionObject::createScriptFunction(qmlContext, runtimeFunction);
- _vmeMetaObject->setVmeMethod(property->coreIndex, function);
+ _vmeMetaObject->setVmeMethod(property->coreIndex(), function);
}
}
void QQmlObjectCreator::recordError(const QV4::CompiledData::Location &location, const QString &description)
{
QQmlError error;
- error.setUrl(compiledData->url());
+ error.setUrl(compilationUnit->url());
error.setLine(location.line);
error.setColumn(location.column);
error.setDescription(description);
errors << error;
}
-void QQmlObjectCreator::registerObjectWithContextById(int objectIndex, QObject *instance) const
+void QQmlObjectCreator::registerObjectWithContextById(const QV4::CompiledData::Object *object, QObject *instance) const
{
- QHash<int, int>::ConstIterator idEntry = objectIndexToId.find(objectIndex);
- if (idEntry != objectIndexToId.constEnd())
- context->setIdProperty(idEntry.value(), instance);
+ if (object->id >= 0)
+ context->setIdProperty(object->id, instance);
}
QV4::Heap::QmlContext *QQmlObjectCreator::currentQmlContext()
@@ -1050,7 +1024,9 @@ QV4::Heap::QmlContext *QQmlObjectCreator::currentQmlContext()
QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isContextObject)
{
- QQmlObjectCreationProfiler profiler(sharedState->profiler.profiler);
+ const QV4::CompiledData::Object *obj = qmlUnit->objectAt(index);
+ QQmlObjectCreationProfiler profiler(sharedState->profiler.profiler, obj);
+
ActiveOCRestorer ocRestorer(this, QQmlEnginePrivate::get(engine));
bool isComponent = false;
@@ -1060,29 +1036,36 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo
QQmlParserStatus *parserStatus = 0;
bool installPropertyCache = true;
- const QV4::CompiledData::Object *obj = qmlUnit->objectAt(index);
- if (compiledData->isComponent(index)) {
+ if (obj->flags & QV4::CompiledData::Object::IsComponent) {
isComponent = true;
- QQmlComponent *component = new QQmlComponent(engine, compiledData, index, parent);
+ QQmlComponent *component = new QQmlComponent(engine, compilationUnit, index, parent);
Q_QML_OC_PROFILE(sharedState->profiler, profiler.update(
- compiledData, obj, QStringLiteral("<component>"), context->url()));
+ compilationUnit, obj, QStringLiteral("<component>"), context->url()));
QQmlComponentPrivate::get(component)->creationContext = context;
instance = component;
ddata = QQmlData::get(instance, /*create*/true);
} else {
- QQmlCompiledData::TypeReference *typeRef = resolvedTypes.value(obj->inheritedTypeNameIndex);
+ QV4::CompiledData::ResolvedTypeReference *typeRef = resolvedTypes.value(obj->inheritedTypeNameIndex);
Q_ASSERT(typeRef);
installPropertyCache = !typeRef->isFullyDynamicType;
QQmlType *type = typeRef->type;
if (type) {
Q_QML_OC_PROFILE(sharedState->profiler, profiler.update(
- compiledData, obj, type->qmlTypeName(), context->url()));
- instance = type->create();
+ compilationUnit, obj, type->qmlTypeName(), context->url()));
+
+ void *ddataMemory = 0;
+ type->create(&instance, &ddataMemory, sizeof(QQmlData));
if (!instance) {
recordError(obj->location, tr("Unable to create object of type %1").arg(stringAt(obj->inheritedTypeNameIndex)));
return 0;
}
+ {
+ QQmlData *ddata = new (ddataMemory) QQmlData;
+ ddata->ownMemory = false;
+ QObjectPrivate::get(instance)->declarativeData = ddata;
+ }
+
const int parserStatusCast = type->parserStatusCast();
if (parserStatusCast != -1)
parserStatus = reinterpret_cast<QQmlParserStatus*>(reinterpret_cast<char *>(instance) + parserStatusCast);
@@ -1097,17 +1080,17 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo
sharedState->allCreatedObjects.push(instance);
} else {
- Q_ASSERT(typeRef->component);
+ Q_ASSERT(typeRef->compilationUnit);
Q_QML_OC_PROFILE(sharedState->profiler, profiler.update(
- compiledData, obj, typeRef->component->fileName(),
+ compilationUnit, obj, typeRef->compilationUnit->fileName(),
context->url()));
- if (typeRef->component->compilationUnit->data->isSingleton())
+ if (typeRef->compilationUnit->data->isSingleton())
{
recordError(obj->location, tr("Composite Singleton Type %1 is not creatable").arg(stringAt(obj->inheritedTypeNameIndex)));
return 0;
}
- QQmlObjectCreator subCreator(context, typeRef->component, sharedState.data());
+ QQmlObjectCreator subCreator(context, typeRef->compilationUnit, sharedState.data());
instance = subCreator.create();
if (!instance) {
errors += subCreator.errors;
@@ -1153,32 +1136,30 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo
if (isContextObject)
context->contextObject = instance;
- QBitArray bindingsToSkip;
- if (customParser) {
- QHash<int, QBitArray>::ConstIterator customParserBindings = compiledData->customParserBindings.constFind(index);
- if (customParserBindings != compiledData->customParserBindings.constEnd()) {
- customParser->engine = QQmlEnginePrivate::get(engine);
- customParser->imports = compiledData->importCache;
-
- QList<const QV4::CompiledData::Binding *> bindings;
- const QV4::CompiledData::Object *obj = qmlUnit->objectAt(index);
- for (int i = 0; i < customParserBindings->count(); ++i)
- if (customParserBindings->testBit(i))
- bindings << obj->bindingTable() + i;
- customParser->applyBindings(instance, compiledData, bindings);
-
- customParser->engine = 0;
- customParser->imports = (QQmlTypeNameCache*)0;
- bindingsToSkip = *customParserBindings;
+ if (customParser && obj->flags & QV4::CompiledData::Object::HasCustomParserBindings) {
+ customParser->engine = QQmlEnginePrivate::get(engine);
+ customParser->imports = compilationUnit->importCache;
+
+ QList<const QV4::CompiledData::Binding *> bindings;
+ const QV4::CompiledData::Object *obj = qmlUnit->objectAt(index);
+ const QV4::CompiledData::Binding *binding = obj->bindingTable();
+ for (quint32 i = 0; i < obj->nBindings; ++i, ++binding) {
+ if (binding->flags & QV4::CompiledData::Binding::IsCustomParserBinding) {
+ bindings << binding;
+ }
}
+ customParser->applyBindings(instance, compilationUnit, bindings);
+
+ customParser->engine = 0;
+ customParser->imports = (QQmlTypeNameCache*)0;
}
if (isComponent) {
- registerObjectWithContextById(index, instance);
+ registerObjectWithContextById(obj, instance);
return instance;
}
- QQmlRefPointer<QQmlPropertyCache> cache = propertyCaches.at(index);
+ QQmlRefPointer<QQmlPropertyCache> cache = propertyCaches->at(index);
Q_ASSERT(!cache.isNull());
if (installPropertyCache) {
if (ddata->propertyCache)
@@ -1199,7 +1180,7 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo
qSwap(_qmlContext, qmlContext);
- bool result = populateInstance(index, instance, /*binding target*/instance, /*value type property*/0, bindingsToSkip);
+ bool result = populateInstance(index, instance, /*binding target*/instance, /*value type property*/0);
qSwap(_qmlContext, qmlContext);
qSwap(_scopeObject, scopeObject);
@@ -1223,9 +1204,9 @@ QQmlContextData *QQmlObjectCreator::finalize(QQmlInstantiationInterrupt &interru
continue;
QQmlData *data = QQmlData::get(b->targetObject());
Q_ASSERT(data);
- data->clearPendingBindingBit(b->targetPropertyIndex());
- b->setEnabled(true, QQmlPropertyPrivate::BypassInterceptor |
- QQmlPropertyPrivate::DontRemoveBinding);
+ data->clearPendingBindingBit(b->targetPropertyIndex().coreIndex());
+ b->setEnabled(true, QQmlPropertyData::BypassInterceptor |
+ QQmlPropertyData::DontRemoveBinding);
if (watcher.hasRecursed() || interrupt.shouldInterrupt())
return 0;
@@ -1294,7 +1275,7 @@ void QQmlObjectCreator::clear()
phase = Done;
}
-bool QQmlObjectCreator::populateInstance(int index, QObject *instance, QObject *bindingTarget, const QQmlPropertyData *valueTypeProperty, const QBitArray &bindingsToSkip)
+bool QQmlObjectCreator::populateInstance(int index, QObject *instance, QObject *bindingTarget, const QQmlPropertyData *valueTypeProperty)
{
QQmlData *declarativeData = QQmlData::get(instance, /*create*/true);
@@ -1309,14 +1290,13 @@ bool QQmlObjectCreator::populateInstance(int index, QObject *instance, QObject *
QV4::Scope valueScope(v4);
QV4::ScopedValue scopeObjectProtector(valueScope);
- QQmlRefPointer<QQmlPropertyCache> cache = propertyCaches.at(_compiledObjectIndex);
+ QQmlRefPointer<QQmlPropertyCache> cache = propertyCaches->at(_compiledObjectIndex);
QQmlVMEMetaObject *vmeMetaObject = 0;
- const QByteArray data = vmeMetaObjectData.value(_compiledObjectIndex);
- if (!data.isEmpty()) {
+ if (propertyCaches->needsVMEMetaObject(_compiledObjectIndex)) {
Q_ASSERT(!cache.isNull());
// install on _object
- vmeMetaObject = new QQmlVMEMetaObject(_qobject, cache, reinterpret_cast<const QQmlVMEMetaData*>(data.constData()));
+ vmeMetaObject = new QQmlVMEMetaObject(_qobject, cache, compilationUnit, _compiledObjectIndex);
if (_ddata->propertyCache)
_ddata->propertyCache->release();
_ddata->propertyCache = cache;
@@ -1326,33 +1306,23 @@ bool QQmlObjectCreator::populateInstance(int index, QObject *instance, QObject *
vmeMetaObject = QQmlVMEMetaObject::get(_qobject);
}
- registerObjectWithContextById(_compiledObjectIndex, _qobject);
+ registerObjectWithContextById(_compiledObject, _qobject);
qSwap(_propertyCache, cache);
qSwap(_vmeMetaObject, vmeMetaObject);
- QBitArray bindingSkipList = bindingsToSkip;
- {
- QHash<int, QBitArray>::ConstIterator deferredBindings = compiledData->deferredBindingsPerObject.constFind(_compiledObjectIndex);
- if (deferredBindings != compiledData->deferredBindingsPerObject.constEnd()) {
- if (bindingSkipList.isEmpty())
- bindingSkipList.resize(deferredBindings->count());
-
- for (int i = 0; i < deferredBindings->count(); ++i)
- if (deferredBindings->testBit(i))
- bindingSkipList.setBit(i);
- QQmlData::DeferredData *deferData = new QQmlData::DeferredData;
- deferData->deferredIdx = _compiledObjectIndex;
- deferData->compiledData = compiledData;
- deferData->compiledData->addref();
- deferData->context = context;
- _ddata->deferredData = deferData;
- }
+ if (_compiledObject->flags & QV4::CompiledData::Object::HasDeferredBindings) {
+ QQmlData::DeferredData *deferData = new QQmlData::DeferredData;
+ deferData->deferredIdx = _compiledObjectIndex;
+ deferData->compilationUnit = compilationUnit;
+ deferData->compilationUnit->addref();
+ deferData->context = context;
+ _ddata->deferredData = deferData;
}
if (_compiledObject->nFunctions > 0)
setupFunctions();
- setupBindings(bindingSkipList);
+ setupBindings();
qSwap(_vmeMetaObject, vmeMetaObject);
qSwap(_bindingTarget, bindingTarget);
diff --git a/src/qml/qml/qqmlobjectcreator_p.h b/src/qml/qml/qqmlobjectcreator_p.h
index 3d743954c9..e3312f9df5 100644
--- a/src/qml/qml/qqmlobjectcreator_p.h
+++ b/src/qml/qml/qqmlobjectcreator_p.h
@@ -53,7 +53,6 @@
#include <private/qqmlimport_p.h>
#include <private/qqmltypenamecache_p.h>
#include <private/qv4compileddata_p.h>
-#include <private/qqmlcompiler_p.h>
#include <private/qqmltypecompiler_p.h>
#include <private/qfinitestack_p.h>
#include <private/qrecursionwatcher_p.h>
@@ -66,7 +65,6 @@ QT_BEGIN_NAMESPACE
class QQmlAbstractBinding;
struct QQmlTypeCompiler;
class QQmlInstantiationInterrupt;
-struct QQmlVmeProfiler;
struct QQmlObjectCreatorSharedState : public QSharedData
{
@@ -86,7 +84,7 @@ class QQmlObjectCreator
{
Q_DECLARE_TR_FUNCTIONS(QQmlObjectCreator)
public:
- QQmlObjectCreator(QQmlContextData *parentContext, QQmlCompiledData *compiledData, QQmlContextData *creationContext, void *activeVMEDataForRootContext = 0);
+ QQmlObjectCreator(QQmlContextData *parentContext, QV4::CompiledData::CompilationUnit *compilationUnit, QQmlContextData *creationContext, void *activeVMEDataForRootContext = 0);
~QQmlObjectCreator();
QObject *create(int subComponentIndex = -1, QObject *parent = 0, QQmlInstantiationInterrupt *interrupt = 0);
@@ -104,17 +102,16 @@ public:
QFiniteStack<QPointer<QObject> > &allCreatedObjects() const { return sharedState->allCreatedObjects; }
private:
- QQmlObjectCreator(QQmlContextData *contextData, QQmlCompiledData *compiledData, QQmlObjectCreatorSharedState *inheritedSharedState);
+ QQmlObjectCreator(QQmlContextData *contextData, QV4::CompiledData::CompilationUnit *compilationUnit, QQmlObjectCreatorSharedState *inheritedSharedState);
void init(QQmlContextData *parentContext);
QObject *createInstance(int index, QObject *parent = 0, bool isContextObject = false);
bool populateInstance(int index, QObject *instance,
- QObject *bindingTarget, const QQmlPropertyData *valueTypeProperty,
- const QBitArray &bindingsToSkip = QBitArray());
+ QObject *bindingTarget, const QQmlPropertyData *valueTypeProperty);
- void setupBindings(const QBitArray &bindingsToSkip);
+ void setupBindings(bool applyDeferredBindings = false);
bool setPropertyBinding(const QQmlPropertyData *property, const QV4::CompiledData::Binding *binding);
void setPropertyValue(const QQmlPropertyData *property, const QV4::CompiledData::Binding *binding);
void setupFunctions();
@@ -122,7 +119,7 @@ private:
QString stringAt(int idx) const { return qmlUnit->stringAt(idx); }
void recordError(const QV4::CompiledData::Location &location, const QString &description);
- void registerObjectWithContextById(int objectIndex, QObject *instance) const;
+ void registerObjectWithContextById(const QV4::CompiledData::Object *object, QObject *instance) const;
QV4::Heap::QmlContext *currentQmlContext();
@@ -137,14 +134,12 @@ private:
QQmlEngine *engine;
QV4::ExecutionEngine *v4;
- QQmlCompiledData *compiledData;
+ QV4::CompiledData::CompilationUnit *compilationUnit;
const QV4::CompiledData::Unit *qmlUnit;
QQmlGuardedContextData parentContext;
QQmlContextData *context;
- const QHash<int, QQmlCompiledData::TypeReference*> &resolvedTypes;
- const QVector<QQmlPropertyCache *> &propertyCaches;
- const QVector<QByteArray> &vmeMetaObjectData;
- QHash<int, int> objectIndexToId;
+ const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypes;
+ const QQmlPropertyCacheVector *propertyCaches;
QExplicitlySharedDataPointer<QQmlObjectCreatorSharedState> sharedState;
bool topLevelCreator;
void *activeVMEDataForRootContext;
diff --git a/src/qml/qml/qqmlopenmetaobject.cpp b/src/qml/qml/qqmlopenmetaobject.cpp
index 0fd9e63bde..49f02476a2 100644
--- a/src/qml/qml/qqmlopenmetaobject.cpp
+++ b/src/qml/qml/qqmlopenmetaobject.cpp
@@ -278,7 +278,7 @@ int QQmlOpenMetaObject::metaCall(QObject *o, QMetaObject::Call c, int id, void *
propertyRead(propId);
*reinterpret_cast<QVariant *>(a[0]) = d->getData(propId);
} else if (c == QMetaObject::WriteProperty) {
- if (propId >= d->data.count() || d->data[propId].first != *reinterpret_cast<QVariant *>(a[0])) {
+ if (propId >= d->data.count() || d->data.at(propId).first != *reinterpret_cast<QVariant *>(a[0])) {
propertyWrite(propId);
QPair<QVariant, bool> &prop = d->getDataRef(propId);
prop.first = propertyWriteValue(propId, *reinterpret_cast<QVariant *>(a[0]));
diff --git a/src/qml/qml/qqmlplatform.cpp b/src/qml/qml/qqmlplatform.cpp
index 37d8d4748a..a47a0ab4a4 100644
--- a/src/qml/qml/qqmlplatform.cpp
+++ b/src/qml/qml/qqmlplatform.cpp
@@ -67,8 +67,6 @@ QString QQmlPlatform::os()
return QStringLiteral("tvos");
#elif defined(Q_OS_MAC)
return QStringLiteral("osx");
-#elif defined(Q_OS_WINCE)
- return QStringLiteral("wince");
#elif defined(Q_OS_WINPHONE)
return QStringLiteral("winphone");
#elif defined(Q_OS_WINRT)
diff --git a/src/qml/qml/qqmlproperty.cpp b/src/qml/qml/qqmlproperty.cpp
index 0ef0a5b16e..c62fef7c3d 100644
--- a/src/qml/qml/qqmlproperty.cpp
+++ b/src/qml/qml/qqmlproperty.cpp
@@ -42,6 +42,7 @@
#include "qqml.h"
#include "qqmlbinding_p.h"
+#include "qqmlboundsignal_p.h"
#include "qqmlcontext.h"
#include "qqmlcontext_p.h"
#include "qqmlboundsignal_p.h"
@@ -50,7 +51,6 @@
#include "qqmldata_p.h"
#include "qqmlstringconverters_p.h"
#include "qqmllist_p.h"
-#include "qqmlcompiler_p.h"
#include "qqmlvmemetaobject_p.h"
#include "qqmlexpression_p.h"
#include "qqmlvaluetypeproxybinding_p.h"
@@ -291,9 +291,9 @@ void QQmlPropertyPrivate::initProperty(QObject *obj, const QString &name)
if (property->isFunction())
return; // Not an object property
- if (ii == (path.count() - 2) && QQmlValueTypeFactory::isValueType(property->propType)) {
+ if (ii == (path.count() - 2) && QQmlValueTypeFactory::isValueType(property->propType())) {
// We're now at a value type property
- const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(property->propType);
+ const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(property->propType());
if (!valueTypeMetaObject) return; // Not a value type
int idx = valueTypeMetaObject->indexOfProperty(path.last().toUtf8().constData());
@@ -301,24 +301,21 @@ void QQmlPropertyPrivate::initProperty(QObject *obj, const QString &name)
QMetaProperty vtProp = valueTypeMetaObject->property(idx);
- Q_ASSERT(QQmlPropertyData::flagsForProperty(vtProp) <= QQmlPropertyData::ValueTypeFlagMask);
Q_ASSERT(vtProp.userType() <= 0x0000FFFF);
Q_ASSERT(idx <= 0x0000FFFF);
object = currentObject;
core = *property;
- core.setFlags(core.getFlags() | QQmlPropertyData::IsValueTypeVirtual);
- core.valueTypeFlags = QQmlPropertyData::flagsForProperty(vtProp);
- core.valueTypePropType = vtProp.userType();
- core.valueTypeCoreIndex = idx;
+ valueTypeData.setFlags(QQmlPropertyData::flagsForProperty(vtProp));
+ valueTypeData.setPropType(vtProp.userType());
+ valueTypeData.setCoreIndex(idx);
return;
} else {
if (!property->isQObject())
return; // Not an object property
- void *args[] = { &currentObject, 0 };
- QMetaObject::metacall(currentObject, QMetaObject::ReadProperty, property->coreIndex, args);
+ property->readProperty(currentObject, &currentObject);
if (!currentObject) return; // No value
}
@@ -358,9 +355,9 @@ void QQmlPropertyPrivate::initProperty(QObject *obj, const QString &name)
while (d && d->isFunction())
d = ddata->propertyCache->overrideData(d);
- if (d && d->notifyIndex != -1) {
+ if (d && d->notifyIndex() != -1) {
object = currentObject;
- core = *ddata->propertyCache->signal(d->notifyIndex);
+ core = *ddata->propertyCache->signal(d->notifyIndex());
return;
}
}
@@ -397,7 +394,7 @@ void QQmlPropertyPrivate::initProperty(QObject *obj, const QString &name)
int QQmlPropertyPrivate::signalIndex() const
{
Q_ASSERT(type() == QQmlProperty::SignalProperty);
- QMetaMethod m = object->metaObject()->method(core.coreIndex);
+ QMetaMethod m = object->metaObject()->method(core.coreIndex());
return QMetaObjectPrivate::signalIndex(m);
}
@@ -473,11 +470,11 @@ const char *QQmlProperty::propertyTypeName() const
if (!d)
return 0;
if (d->isValueType()) {
- const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(d->core.propType);
+ const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(d->core.propType());
Q_ASSERT(valueTypeMetaObject);
- return valueTypeMetaObject->property(d->core.valueTypeCoreIndex).typeName();
+ return valueTypeMetaObject->property(d->valueTypeData.coreIndex()).typeName();
} else if (d->object && type() & Property && d->core.isValid()) {
- return d->object->metaObject()->property(d->core.coreIndex).typeName();
+ return d->object->metaObject()->property(d->core.coreIndex()).typeName();
} else {
return 0;
}
@@ -494,11 +491,8 @@ bool QQmlProperty::operator==(const QQmlProperty &other) const
// category is intentially omitted here as it is generated
// from the other members
return d->object == other.d->object &&
- d->core.coreIndex == other.d->core.coreIndex &&
- d->core.isValueTypeVirtual() == other.d->core.isValueTypeVirtual() &&
- (!d->core.isValueTypeVirtual() ||
- (d->core.valueTypeCoreIndex == other.d->core.valueTypeCoreIndex &&
- d->core.valueTypePropType == other.d->core.valueTypePropType));
+ d->core.coreIndex() == other.d->core.coreIndex() &&
+ d->valueTypeData.coreIndex() == other.d->valueTypeData.coreIndex();
}
/*!
@@ -512,16 +506,16 @@ int QQmlProperty::propertyType() const
bool QQmlPropertyPrivate::isValueType() const
{
- return core.isValueTypeVirtual();
+ return valueTypeData.isValid();
}
int QQmlPropertyPrivate::propertyType() const
{
uint type = this->type();
if (isValueType()) {
- return core.valueTypePropType;
+ return valueTypeData.propType();
} else if (type & QQmlProperty::Property) {
- return core.propType;
+ return core.propType();
} else {
return QVariant::Invalid;
}
@@ -610,7 +604,7 @@ bool QQmlProperty::isDesignable() const
if (!d)
return false;
if (type() & Property && d->core.isValid() && d->object)
- return d->object->metaObject()->property(d->core.coreIndex).isDesignable();
+ return d->object->metaObject()->property(d->core.coreIndex()).isDesignable();
else
return false;
}
@@ -650,15 +644,11 @@ QString QQmlProperty::name() const
// ###
if (!d->object) {
} else if (d->isValueType()) {
- QString rv = d->core.name(d->object) + QLatin1Char('.');
-
- const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(d->core.propType);
+ const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(d->core.propType());
Q_ASSERT(valueTypeMetaObject);
- const char *vtName = valueTypeMetaObject->property(d->core.valueTypeCoreIndex).name();
- rv += QString::fromUtf8(vtName);
-
- d->nameCache = rv;
+ const char *vtName = valueTypeMetaObject->property(d->valueTypeData.coreIndex()).name();
+ d->nameCache = d->core.name(d->object) + QLatin1Char('.') + QString::fromUtf8(vtName);
} else if (type() & SignalProperty) {
QString name = QLatin1String("on") + d->core.name(d->object);
name[2] = name.at(2).toUpper();
@@ -681,7 +671,7 @@ QMetaProperty QQmlProperty::property() const
if (!d)
return QMetaProperty();
if (type() & Property && d->core.isValid() && d->object)
- return d->object->metaObject()->property(d->core.coreIndex);
+ return d->object->metaObject()->property(d->core.coreIndex());
else
return QMetaProperty();
}
@@ -695,7 +685,7 @@ QMetaMethod QQmlProperty::method() const
if (!d)
return QMetaMethod();
if (type() & SignalProperty && d->object)
- return d->object->metaObject()->method(d->core.coreIndex);
+ return d->object->metaObject()->method(d->core.coreIndex());
else
return QMetaMethod();
}
@@ -710,7 +700,8 @@ QQmlPropertyPrivate::binding(const QQmlProperty &that)
if (!that.d || !that.isProperty() || !that.d->object)
return 0;
- return binding(that.d->object, that.d->core.encodedIndex());
+ QQmlPropertyIndex thatIndex(that.d->core.coreIndex(), that.d->valueTypeData.coreIndex());
+ return binding(that.d->object, thatIndex);
}
/*!
@@ -742,10 +733,10 @@ QQmlPropertyPrivate::setBinding(const QQmlProperty &that, QQmlAbstractBinding *n
setBinding(newBinding);
}
-static void removeOldBinding(QObject *object, int index, QQmlPropertyPrivate::BindingFlags flags = QQmlPropertyPrivate::None)
+static void removeOldBinding(QObject *object, QQmlPropertyIndex index, QQmlPropertyPrivate::BindingFlags flags = QQmlPropertyPrivate::None)
{
- int coreIndex;
- int valueTypeIndex = QQmlPropertyData::decodeValueTypePropertyIndex(index, &coreIndex);
+ int coreIndex = index.coreIndex();
+ int valueTypeIndex = index.valueTypeIndex();
QQmlData *data = QQmlData::get(object, false);
@@ -755,7 +746,8 @@ static void removeOldBinding(QObject *object, int index, QQmlPropertyPrivate::Bi
QQmlAbstractBinding::Ptr oldBinding;
oldBinding = data->bindings;
- while (oldBinding && oldBinding->targetPropertyIndex() != coreIndex)
+ while (oldBinding && (oldBinding->targetPropertyIndex().coreIndex() != coreIndex ||
+ oldBinding->targetPropertyIndex().hasValueTypeIndex()))
oldBinding = oldBinding->nextBinding();
if (!oldBinding)
@@ -777,12 +769,12 @@ void QQmlPropertyPrivate::removeBinding(QQmlAbstractBinding *b)
removeBinding(b->targetObject(), b->targetPropertyIndex());
}
-void QQmlPropertyPrivate::removeBinding(QObject *o, int index)
+void QQmlPropertyPrivate::removeBinding(QObject *o, QQmlPropertyIndex index)
{
Q_ASSERT(o);
QObject *target;
- int targetIndex;
+ QQmlPropertyIndex targetIndex;
findAliasTarget(o, index, &target, &targetIndex);
removeOldBinding(target, targetIndex);
}
@@ -792,11 +784,11 @@ void QQmlPropertyPrivate::removeBinding(const QQmlProperty &that)
if (!that.d || !that.isProperty() || !that.d->object)
return;
- removeBinding(that.d->object, that.d->core.encodedIndex());
+ removeBinding(that.d->object, that.d->encodedIndex());
}
QQmlAbstractBinding *
-QQmlPropertyPrivate::binding(QObject *object, int index)
+QQmlPropertyPrivate::binding(QObject *object, QQmlPropertyIndex index)
{
QQmlData *data = QQmlData::get(object);
if (!data)
@@ -804,19 +796,19 @@ QQmlPropertyPrivate::binding(QObject *object, int index)
findAliasTarget(object, index, &object, &index);
- int coreIndex;
- int valueTypeIndex = QQmlPropertyData::decodeValueTypePropertyIndex(index, &coreIndex);
+ const int coreIndex = index.coreIndex();
+ const int valueTypeIndex = index.valueTypeIndex();
if (!data->hasBindingBit(coreIndex))
return 0;
QQmlAbstractBinding *binding = data->bindings;
- while (binding && binding->targetPropertyIndex() != coreIndex)
+ while (binding && (binding->targetPropertyIndex().coreIndex() != coreIndex ||
+ binding->targetPropertyIndex().hasValueTypeIndex()))
binding = binding->nextBinding();
if (binding && valueTypeIndex != -1) {
if (binding->isValueTypeProxy()) {
- int index = QQmlPropertyData::encodeValueTypePropertyIndex(coreIndex, valueTypeIndex);
binding = static_cast<QQmlValueTypeProxyBinding *>(binding)->binding(index);
}
}
@@ -824,13 +816,14 @@ QQmlPropertyPrivate::binding(QObject *object, int index)
return binding;
}
-void QQmlPropertyPrivate::findAliasTarget(QObject *object, int bindingIndex,
- QObject **targetObject, int *targetBindingIndex)
+void QQmlPropertyPrivate::findAliasTarget(QObject *object, QQmlPropertyIndex bindingIndex,
+ QObject **targetObject,
+ QQmlPropertyIndex *targetBindingIndex)
{
QQmlData *data = QQmlData::get(object, false);
if (data) {
- int coreIndex;
- int valueTypeIndex = QQmlPropertyData::decodeValueTypePropertyIndex(bindingIndex, &coreIndex);
+ int coreIndex = bindingIndex.coreIndex();
+ int valueTypeIndex = bindingIndex.valueTypeIndex();
QQmlPropertyData *propertyData =
data->propertyCache?data->propertyCache->property(coreIndex):0;
@@ -842,11 +835,12 @@ void QQmlPropertyPrivate::findAliasTarget(QObject *object, int bindingIndex,
// This will either be a value type sub-reference or an alias to a value-type sub-reference not both
Q_ASSERT(valueTypeIndex == -1 || aValueTypeIndex == -1);
- int aBindingIndex = aCoreIndex;
- if (aValueTypeIndex != -1)
- aBindingIndex = QQmlPropertyData::encodeValueTypePropertyIndex(aBindingIndex, aValueTypeIndex);
- else if (valueTypeIndex != -1)
- aBindingIndex = QQmlPropertyData::encodeValueTypePropertyIndex(aBindingIndex, valueTypeIndex);
+ QQmlPropertyIndex aBindingIndex(aCoreIndex);
+ if (aValueTypeIndex != -1) {
+ aBindingIndex = QQmlPropertyIndex(aCoreIndex, aValueTypeIndex);
+ } else if (valueTypeIndex != -1) {
+ aBindingIndex = QQmlPropertyIndex(aCoreIndex, valueTypeIndex);
+ }
findAliasTarget(aObject, aBindingIndex, targetObject, targetBindingIndex);
return;
@@ -859,16 +853,15 @@ void QQmlPropertyPrivate::findAliasTarget(QObject *object, int bindingIndex,
}
-void QQmlPropertyPrivate::setBinding(QQmlAbstractBinding *binding, BindingFlags flags, WriteFlags writeFlags)
+void QQmlPropertyPrivate::setBinding(QQmlAbstractBinding *binding, BindingFlags flags, QQmlPropertyData::WriteFlags writeFlags)
{
Q_ASSERT(binding);
QObject *object = binding->targetObject();
- int index = binding->targetPropertyIndex();
+ const QQmlPropertyIndex index = binding->targetPropertyIndex();
#ifndef QT_NO_DEBUG
- int coreIndex;
- QQmlPropertyData::decodeValueTypePropertyIndex(index, &coreIndex);
+ int coreIndex = index.coreIndex();
QQmlData *data = QQmlData::get(object, true);
if (data->propertyCache) {
QQmlPropertyData *propertyData = data->propertyCache->property(coreIndex);
@@ -1027,42 +1020,40 @@ QVariant QQmlPropertyPrivate::readValueProperty()
{
if (isValueType()) {
- QQmlValueType *valueType = QQmlValueTypeFactory::valueType(core.propType);
+ QQmlValueType *valueType = QQmlValueTypeFactory::valueType(core.propType());
Q_ASSERT(valueType);
- valueType->read(object, core.coreIndex);
- return valueType->metaObject()->property(core.valueTypeCoreIndex).read(valueType);
+ valueType->read(object, core.coreIndex());
+ return valueType->metaObject()->property(valueTypeData.coreIndex()).read(valueType);
} else if (core.isQList()) {
QQmlListProperty<QObject> prop;
- void *args[] = { &prop, 0 };
- QMetaObject::metacall(object, QMetaObject::ReadProperty, core.coreIndex, args);
- return QVariant::fromValue(QQmlListReferencePrivate::init(prop, core.propType, engine));
+ core.readProperty(object, &prop);
+ return QVariant::fromValue(QQmlListReferencePrivate::init(prop, core.propType(), engine));
} else if (core.isQObject()) {
QObject *rv = 0;
- void *args[] = { &rv, 0 };
- QMetaObject::metacall(object, QMetaObject::ReadProperty, core.coreIndex, args);
+ core.readProperty(object, &rv);
return QVariant::fromValue(rv);
} else {
- if (!core.propType) // Unregistered type
- return object->metaObject()->property(core.coreIndex).read(object);
+ if (!core.propType()) // Unregistered type
+ return object->metaObject()->property(core.coreIndex()).read(object);
QVariant value;
int status = -1;
void *args[] = { 0, &value, &status };
- if (core.propType == QMetaType::QVariant) {
+ if (core.propType() == QMetaType::QVariant) {
args[0] = &value;
} else {
- value = QVariant(core.propType, (void*)0);
+ value = QVariant(core.propType(), (void*)0);
args[0] = value.data();
}
- QMetaObject::metacall(object, QMetaObject::ReadProperty, core.coreIndex, args);
- if (core.propType != QMetaType::QVariant && args[0] != value.data())
- return QVariant((QVariant::Type)core.propType, args[0]);
+ core.readPropertyWithArgs(object, args);
+ if (core.propType() != QMetaType::QVariant && args[0] != value.data())
+ return QVariant((QVariant::Type)core.propType(), args[0]);
return value;
}
@@ -1148,40 +1139,30 @@ bool QQmlPropertyPrivate::writeEnumProperty(const QMetaProperty &prop, int idx,
return status;
}
-bool QQmlPropertyPrivate::writeValueProperty(const QVariant &value, WriteFlags flags)
+bool QQmlPropertyPrivate::writeValueProperty(const QVariant &value, QQmlPropertyData::WriteFlags flags)
{
- return writeValueProperty(object, core, value, effectiveContext(), flags);
+ return writeValueProperty(object, core, valueTypeData, value, effectiveContext(), flags);
}
bool
QQmlPropertyPrivate::writeValueProperty(QObject *object,
const QQmlPropertyData &core,
+ const QQmlPropertyData &valueTypeData,
const QVariant &value,
- QQmlContextData *context, WriteFlags flags)
+ QQmlContextData *context,QQmlPropertyData::WriteFlags flags)
{
// Remove any existing bindings on this property
- if (!(flags & DontRemoveBinding) && object)
- removeBinding(object, core.encodedIndex());
+ if (!(flags & QQmlPropertyData::DontRemoveBinding) && object)
+ removeBinding(object, encodedIndex(core, valueTypeData));
bool rv = false;
- if (core.isValueTypeVirtual()) {
-
- QQmlValueType *writeBack = QQmlValueTypeFactory::valueType(core.propType);
- writeBack->read(object, core.coreIndex);
-
- QQmlPropertyData data = core;
- data.setFlags(QQmlPropertyData::Flag(core.valueTypeFlags));
- data.coreIndex = core.valueTypeCoreIndex;
- data.propType = core.valueTypePropType;
-
- rv = write(writeBack, data, value, context, flags);
-
- writeBack->write(object, core.coreIndex, flags);
-
+ if (valueTypeData.isValid()) {
+ QQmlValueType *writeBack = QQmlValueTypeFactory::valueType(core.propType());
+ writeBack->read(object, core.coreIndex());
+ rv = write(writeBack, valueTypeData, value, context, flags);
+ writeBack->write(object, core.coreIndex(), flags);
} else {
-
rv = write(object, core, value, context, flags);
-
}
return rv;
@@ -1190,145 +1171,151 @@ QQmlPropertyPrivate::writeValueProperty(QObject *object,
bool QQmlPropertyPrivate::write(QObject *object,
const QQmlPropertyData &property,
const QVariant &value, QQmlContextData *context,
- WriteFlags flags)
+ QQmlPropertyData::WriteFlags flags)
{
- int coreIdx = property.coreIndex;
- int status = -1; //for dbus
+ const int propertyType = property.propType();
+ const int variantType = value.userType();
if (property.isEnum()) {
- QMetaProperty prop = object->metaObject()->property(property.coreIndex);
+ QMetaProperty prop = object->metaObject()->property(property.coreIndex());
QVariant v = value;
// Enum values come through the script engine as doubles
- if (value.userType() == QVariant::Double) {
+ if (variantType == QVariant::Double) {
double integral;
double fractional = std::modf(value.toDouble(), &integral);
if (qFuzzyIsNull(fractional))
v.convert(QVariant::Int);
}
- return writeEnumProperty(prop, coreIdx, object, v, flags);
+ return writeEnumProperty(prop, property.coreIndex(), object, v, flags);
}
- int propertyType = property.propType;
- int variantType = value.userType();
-
QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(context);
+ const bool isUrl = propertyType == QVariant::Url; // handled separately
+
+ // The cases below are in approximate order of likelyhood:
+ if (propertyType == variantType && !isUrl && propertyType != qMetaTypeId<QList<QUrl>>() && !property.isQList()) {
+ return property.writeProperty(object, const_cast<void *>(value.constData()), flags);
+ } else if (property.isQObject()) {
+ QQmlMetaObject valMo = rawMetaObjectForType(enginePriv, variantType);
+ if (valMo.isNull())
+ return false;
+ QObject *o = *static_cast<QObject *const *>(value.constData());
+ QQmlMetaObject propMo = rawMetaObjectForType(enginePriv, propertyType);
- if (propertyType == QVariant::Url) {
+ if (o)
+ valMo = o;
+ if (QQmlMetaObject::canConvert(valMo, propMo)) {
+ return property.writeProperty(object, &o, flags);
+ } else if (!o && QQmlMetaObject::canConvert(propMo, valMo)) {
+ // In the case of a null QObject, we assign the null if there is
+ // any change that the null variant type could be up or down cast to
+ // the property type.
+ return property.writeProperty(object, &o, flags);
+ } else {
+ return false;
+ }
+ } else if (value.canConvert(propertyType) && !isUrl && variantType != QVariant::String && propertyType != qMetaTypeId<QList<QUrl>>() && !property.isQList()) {
+ // common cases:
+ switch (propertyType) {
+ case QMetaType::Bool: {
+ bool b = value.toBool();
+ return property.writeProperty(object, &b, flags);
+ }
+ case QMetaType::Int: {
+ int i = value.toInt();
+ return property.writeProperty(object, &i, flags);
+ }
+ case QMetaType::Double: {
+ double d = value.toDouble();
+ return property.writeProperty(object, &d, flags);
+ }
+ case QMetaType::Float: {
+ float f = value.toFloat();
+ return property.writeProperty(object, &f, flags);
+ }
+ case QMetaType::QString: {
+ QString s = value.toString();
+ return property.writeProperty(object, &s, flags);
+ }
+ default: { // "fallback":
+ QVariant v = value;
+ v.convert(propertyType);
+ return property.writeProperty(object, const_cast<void *>(v.constData()), flags);
+ }
+ }
+ } else if (propertyType == qMetaTypeId<QVariant>()) {
+ return property.writeProperty(object, const_cast<QVariant *>(&value), flags);
+ } else if (isUrl) {
QUrl u;
- bool found = false;
if (variantType == QVariant::Url) {
u = value.toUrl();
- found = true;
} else if (variantType == QVariant::ByteArray) {
QString input(QString::fromUtf8(value.toByteArray()));
// Encoded dir-separators defeat QUrl processing - decode them first
input.replace(QLatin1String("%2f"), QLatin1String("/"), Qt::CaseInsensitive);
u = QUrl(input);
- found = true;
} else if (variantType == QVariant::String) {
QString input(value.toString());
// Encoded dir-separators defeat QUrl processing - decode them first
input.replace(QLatin1String("%2f"), QLatin1String("/"), Qt::CaseInsensitive);
u = QUrl(input);
- found = true;
- }
-
- if (!found)
- return false;
-
- if (context && u.isRelative() && !u.isEmpty())
- u = context->resolvedUrl(u);
- int status = -1;
- void *argv[] = { &u, 0, &status, &flags };
- QMetaObject::metacall(object, QMetaObject::WriteProperty, coreIdx, argv);
-
- } else if (propertyType == qMetaTypeId<QList<QUrl> >()) {
- QList<QUrl> urlSeq = resolvedUrlSequence(value, context).value<QList<QUrl> >();
- int status = -1;
- void *argv[] = { &urlSeq, 0, &status, &flags };
- QMetaObject::metacall(object, QMetaObject::WriteProperty, coreIdx, argv);
- } else if (variantType == propertyType) {
-
- void *a[] = { const_cast<void *>(value.constData()), 0, &status, &flags };
- QMetaObject::metacall(object, QMetaObject::WriteProperty, coreIdx, a);
-
- } else if (qMetaTypeId<QVariant>() == propertyType) {
-
- void *a[] = { const_cast<QVariant *>(&value), 0, &status, &flags };
- QMetaObject::metacall(object, QMetaObject::WriteProperty, coreIdx, a);
-
- } else if (property.isQObject()) {
-
- QQmlMetaObject valMo = rawMetaObjectForType(enginePriv, value.userType());
-
- if (valMo.isNull())
- return false;
-
- QObject *o = *(QObject *const *)value.constData();
- QQmlMetaObject propMo = rawMetaObjectForType(enginePriv, propertyType);
-
- if (o) valMo = o;
-
- if (QQmlMetaObject::canConvert(valMo, propMo)) {
- void *args[] = { &o, 0, &status, &flags };
- QMetaObject::metacall(object, QMetaObject::WriteProperty, coreIdx, args);
- } else if (!o && QQmlMetaObject::canConvert(propMo, valMo)) {
- // In the case of a null QObject, we assign the null if there is
- // any change that the null variant type could be up or down cast to
- // the property type.
- void *args[] = { &o, 0, &status, &flags };
- QMetaObject::metacall(object, QMetaObject::WriteProperty, coreIdx, args);
} else {
return false;
}
+ if (context && u.isRelative() && !u.isEmpty())
+ u = context->resolvedUrl(u);
+ return property.writeProperty(object, &u, flags);
+ } else if (propertyType == qMetaTypeId<QList<QUrl>>()) {
+ QList<QUrl> urlSeq = resolvedUrlSequence(value, context).value<QList<QUrl>>();
+ return property.writeProperty(object, &urlSeq, flags);
} else if (property.isQList()) {
-
QQmlMetaObject listType;
if (enginePriv) {
- listType = enginePriv->rawMetaObjectForType(enginePriv->listType(property.propType));
+ listType = enginePriv->rawMetaObjectForType(enginePriv->listType(property.propType()));
} else {
- QQmlType *type = QQmlMetaType::qmlType(QQmlMetaType::listType(property.propType));
- if (!type) return false;
+ QQmlType *type = QQmlMetaType::qmlType(QQmlMetaType::listType(property.propType()));
+ if (!type)
+ return false;
listType = type->baseMetaObject();
}
- if (listType.isNull()) return false;
+ if (listType.isNull())
+ return false;
QQmlListProperty<void> prop;
- void *args[] = { &prop, 0 };
- QMetaObject::metacall(object, QMetaObject::ReadProperty, coreIdx, args);
+ property.readProperty(object, &prop);
- if (!prop.clear) return false;
+ if (!prop.clear)
+ return false;
prop.clear(&prop);
- if (value.userType() == qMetaTypeId<QQmlListReference>()) {
+ if (variantType == qMetaTypeId<QQmlListReference>()) {
QQmlListReference qdlr = value.value<QQmlListReference>();
for (int ii = 0; ii < qdlr.count(); ++ii) {
QObject *o = qdlr.at(ii);
if (o && !QQmlMetaObject::canConvert(o, listType))
- o = 0;
- prop.append(&prop, (void *)o);
+ o = nullptr;
+ prop.append(&prop, o);
}
- } else if (value.userType() == qMetaTypeId<QList<QObject *> >()) {
+ } else if (variantType == qMetaTypeId<QList<QObject *> >()) {
const QList<QObject *> &list = qvariant_cast<QList<QObject *> >(value);
for (int ii = 0; ii < list.count(); ++ii) {
QObject *o = list.at(ii);
if (o && !QQmlMetaObject::canConvert(o, listType))
- o = 0;
- prop.append(&prop, (void *)o);
+ o = nullptr;
+ prop.append(&prop, o);
}
} else {
QObject *o = enginePriv?enginePriv->toQObject(value):QQmlMetaType::toQObject(value);
if (o && !QQmlMetaObject::canConvert(o, listType))
- o = 0;
- prop.append(&prop, (void *)o);
+ o = nullptr;
+ prop.append(&prop, o);
}
-
} else {
Q_ASSERT(variantType != propertyType);
@@ -1347,7 +1334,8 @@ bool QQmlPropertyPrivate::write(QObject *object,
// successful conversion.
Q_ASSERT(v.userType() == propertyType);
ok = true;
- } else if ((uint)propertyType >= QVariant::UserType && variantType == QVariant::String) {
+ } else if (static_cast<uint>(propertyType) >= QVariant::UserType &&
+ variantType == QVariant::String) {
QQmlMetaType::StringConverter con = QQmlMetaType::customStringConverter(propertyType);
if (con) {
v = con(value.toString());
@@ -1390,8 +1378,7 @@ bool QQmlPropertyPrivate::write(QObject *object,
}
if (ok) {
- void *a[] = { const_cast<void *>(v.constData()), 0, &status, &flags};
- QMetaObject::metacall(object, QMetaObject::WriteProperty, coreIdx, a);
+ return property.writeProperty(object, const_cast<void *>(v.constData()), flags);
} else {
return false;
}
@@ -1407,10 +1394,9 @@ QQmlMetaObject QQmlPropertyPrivate::rawMetaObjectForType(QQmlEnginePrivate *engi
return metaType.metaObject();
if (engine)
return engine->rawMetaObjectForType(userType);
- QQmlType *type = QQmlMetaType::qmlType(userType);
- if (type)
+ if (QQmlType *type = QQmlMetaType::qmlType(userType))
return QQmlMetaObject(type->baseMetaObject());
- return QQmlMetaObject((QObject*)0);
+ return QQmlMetaObject();
}
/*!
@@ -1484,7 +1470,7 @@ bool QQmlProperty::reset() const
{
if (isResettable()) {
void *args[] = { 0 };
- QMetaObject::metacall(d->object, QMetaObject::ResetProperty, d->core.coreIndex, args);
+ QMetaObject::metacall(d->object, QMetaObject::ResetProperty, d->core.coreIndex(), args);
return true;
} else {
return false;
@@ -1492,7 +1478,7 @@ bool QQmlProperty::reset() const
}
bool QQmlPropertyPrivate::write(const QQmlProperty &that,
- const QVariant &value, WriteFlags flags)
+ const QVariant &value, QQmlPropertyData::WriteFlags flags)
{
if (!that.d)
return false;
@@ -1509,7 +1495,7 @@ bool QQmlPropertyPrivate::write(const QQmlProperty &that,
bool QQmlProperty::hasNotifySignal() const
{
if (type() & Property && d->object) {
- return d->object->metaObject()->property(d->core.coreIndex).hasNotifySignal();
+ return d->object->metaObject()->property(d->core.coreIndex()).hasNotifySignal();
}
return false;
}
@@ -1539,7 +1525,7 @@ bool QQmlProperty::connectNotifySignal(QObject *dest, int method) const
if (!(type() & Property) || !d->object)
return false;
- QMetaProperty prop = d->object->metaObject()->property(d->core.coreIndex);
+ QMetaProperty prop = d->object->metaObject()->property(d->core.coreIndex());
if (prop.hasNotifySignal()) {
return QQmlPropertyPrivate::connect(d->object, prop.notifySignalIndex(), dest, method, Qt::DirectConnection);
} else {
@@ -1560,7 +1546,7 @@ bool QQmlProperty::connectNotifySignal(QObject *dest, const char *slot) const
if (!(type() & Property) || !d->object)
return false;
- QMetaProperty prop = d->object->metaObject()->property(d->core.coreIndex);
+ QMetaProperty prop = d->object->metaObject()->property(d->core.coreIndex());
if (prop.hasNotifySignal()) {
QByteArray signal('2' + prop.notifySignal().methodSignature());
return QObject::connect(d->object, signal.constData(), dest, slot);
@@ -1574,61 +1560,28 @@ bool QQmlProperty::connectNotifySignal(QObject *dest, const char *slot) const
*/
int QQmlProperty::index() const
{
- return d ? d->core.coreIndex : -1;
-}
-
-int QQmlPropertyPrivate::valueTypeCoreIndex(const QQmlProperty &that)
-{
- return that.d ? that.d->core.getValueTypeCoreIndex() : -1;
-}
-
-/*!
- Returns the "property index" for use in bindings. The top 16 bits are the value type
- offset, and 0 otherwise. The bottom 16 bits are the regular property index.
-*/
-int QQmlPropertyPrivate::bindingIndex(const QQmlProperty &that)
-{
- if (!that.d)
- return -1;
- return bindingIndex(that.d->core);
-}
-
-int QQmlPropertyPrivate::bindingIndex(const QQmlPropertyData &that)
-{
- int rv = that.coreIndex;
- if (rv != -1 && that.isValueTypeVirtual())
- rv = rv | (that.valueTypeCoreIndex << 16);
- return rv;
+ return d ? d->core.coreIndex() : -1;
}
-QQmlPropertyData
-QQmlPropertyPrivate::saveValueType(const QQmlPropertyData &base,
- const QMetaObject *subObject, int subIndex,
- QQmlEngine *)
+QQmlPropertyIndex QQmlPropertyPrivate::propertyIndex(const QQmlProperty &that)
{
- QMetaProperty subProp = subObject->property(subIndex);
-
- QQmlPropertyData core = base;
- core.setFlags(core.getFlags() | QQmlPropertyData::IsValueTypeVirtual);
- core.valueTypeFlags = QQmlPropertyData::flagsForProperty(subProp);
- core.valueTypeCoreIndex = subIndex;
- core.valueTypePropType = subProp.userType();
-
- return core;
+ return that.d ? that.d->encodedIndex() : QQmlPropertyIndex();
}
QQmlProperty
QQmlPropertyPrivate::restore(QObject *object, const QQmlPropertyData &data,
- QQmlContextData *ctxt)
+ const QQmlPropertyData *valueTypeData, QQmlContextData *ctxt)
{
QQmlProperty prop;
prop.d = new QQmlPropertyPrivate;
prop.d->object = object;
prop.d->context = ctxt;
- prop.d->engine = ctxt?ctxt->engine:0;
+ prop.d->engine = ctxt ? ctxt->engine : nullptr;
prop.d->core = data;
+ if (valueTypeData)
+ prop.d->valueTypeData = *valueTypeData;
return prop;
}
diff --git a/src/qml/qml/qqmlproperty_p.h b/src/qml/qml/qqmlproperty_p.h
index 58fea9c239..2565ec0ce6 100644
--- a/src/qml/qml/qqmlproperty_p.h
+++ b/src/qml/qml/qqmlproperty_p.h
@@ -68,24 +68,23 @@ class QQmlJavaScriptExpression;
class Q_QML_PRIVATE_EXPORT QQmlPropertyPrivate : public QQmlRefCount
{
public:
- enum WriteFlag {
- BypassInterceptor = 0x01,
- DontRemoveBinding = 0x02,
- RemoveBindingOnAliasWrite = 0x04
- };
- Q_DECLARE_FLAGS(WriteFlags, WriteFlag)
-
QQmlContextData *context;
QPointer<QQmlEngine> engine;
QPointer<QObject> object;
QQmlPropertyData core;
+ QQmlPropertyData valueTypeData;
bool isNameCached:1;
QString nameCache;
QQmlPropertyPrivate();
+ QQmlPropertyIndex encodedIndex() const
+ { return encodedIndex(core, valueTypeData); }
+ static QQmlPropertyIndex encodedIndex(const QQmlPropertyData &core, const QQmlPropertyData &valueTypeData)
+ { return QQmlPropertyIndex(core.coreIndex(), valueTypeData.coreIndex()); }
+
inline QQmlContextData *effectiveContext() const;
void initProperty(QObject *obj, const QString &name);
@@ -97,18 +96,18 @@ public:
QQmlProperty::PropertyTypeCategory propertyTypeCategory() const;
QVariant readValueProperty();
- bool writeValueProperty(const QVariant &, WriteFlags);
+ bool writeValueProperty(const QVariant &, QQmlPropertyData::WriteFlags);
static QQmlMetaObject rawMetaObjectForType(QQmlEnginePrivate *, int);
static bool writeEnumProperty(const QMetaProperty &prop, int idx, QObject *object,
const QVariant &value, int flags);
static bool writeValueProperty(QObject *,
- const QQmlPropertyData &,
+ const QQmlPropertyData &, const QQmlPropertyData &valueTypeData,
const QVariant &, QQmlContextData *,
- WriteFlags flags = 0);
+ QQmlPropertyData::WriteFlags flags = 0);
static bool write(QObject *, const QQmlPropertyData &, const QVariant &,
- QQmlContextData *, WriteFlags flags = 0);
- static void findAliasTarget(QObject *, int, QObject **, int *);
+ QQmlContextData *, QQmlPropertyData::WriteFlags flags = 0);
+ static void findAliasTarget(QObject *, QQmlPropertyIndex, QObject **, QQmlPropertyIndex *);
enum BindingFlag {
None = 0,
@@ -116,19 +115,15 @@ public:
};
Q_DECLARE_FLAGS(BindingFlags, BindingFlag)
- static void setBinding(QQmlAbstractBinding *binding, BindingFlags flags = None, WriteFlags writeFlags = DontRemoveBinding);
+ static void setBinding(QQmlAbstractBinding *binding, BindingFlags flags = None, QQmlPropertyData::WriteFlags writeFlags = QQmlPropertyData::DontRemoveBinding);
static void removeBinding(const QQmlProperty &that);
- static void removeBinding(QObject *o, int index);
+ static void removeBinding(QObject *o, QQmlPropertyIndex index);
static void removeBinding(QQmlAbstractBinding *b);
- static QQmlAbstractBinding *binding(QObject *, int index);
+ static QQmlAbstractBinding *binding(QObject *, QQmlPropertyIndex index);
- static QQmlPropertyData saveValueType(const QQmlPropertyData &,
- const QMetaObject *, int,
- QQmlEngine *);
- static QQmlProperty restore(QObject *,
- const QQmlPropertyData &,
- QQmlContextData *);
+ static QQmlProperty restore(QObject *, const QQmlPropertyData &, const QQmlPropertyData *,
+ QQmlContextData *);
int signalIndex() const;
@@ -144,10 +139,8 @@ public:
QQmlBoundSignalExpression *);
static void takeSignalExpression(const QQmlProperty &that,
QQmlBoundSignalExpression *);
- static bool write(const QQmlProperty &that, const QVariant &, WriteFlags);
- static int valueTypeCoreIndex(const QQmlProperty &that);
- static int bindingIndex(const QQmlProperty &that);
- static int bindingIndex(const QQmlPropertyData &that);
+ static bool write(const QQmlProperty &that, const QVariant &, QQmlPropertyData::WriteFlags);
+ static QQmlPropertyIndex propertyIndex(const QQmlProperty &that);
static QMetaMethod findSignalByName(const QMetaObject *mo, const QByteArray &);
static bool connect(const QObject *sender, int signal_index,
const QObject *receiver, int method_index,
@@ -157,7 +150,6 @@ public:
static QVariant resolvedUrlSequence(const QVariant &value, QQmlContextData *context);
};
-Q_DECLARE_OPERATORS_FOR_FLAGS(QQmlPropertyPrivate::WriteFlags)
Q_DECLARE_OPERATORS_FOR_FLAGS(QQmlPropertyPrivate::BindingFlags)
QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmlpropertycache.cpp b/src/qml/qml/qqmlpropertycache.cpp
index 9c535b8ce8..2610a807b5 100644
--- a/src/qml/qml/qqmlpropertycache.cpp
+++ b/src/qml/qml/qqmlpropertycache.cpp
@@ -45,12 +45,12 @@
#include <private/qv8engine_p.h>
#include <private/qmetaobject_p.h>
-#include <private/qqmlaccessors_p.h>
#include <private/qmetaobjectbuilder_p.h>
#include <private/qv4value_p.h>
#include <QtCore/qdebug.h>
+#include <QtCore/QCryptographicHash>
#include <ctype.h> // for toupper
#include <limits.h>
@@ -85,51 +85,45 @@ static QQmlPropertyData::Flags fastFlagsForProperty(const QMetaProperty &p)
{
QQmlPropertyData::Flags flags;
- if (p.isConstant())
- flags |= QQmlPropertyData::IsConstant;
- if (p.isWritable())
- flags |= QQmlPropertyData::IsWritable;
- if (p.isResettable())
- flags |= QQmlPropertyData::IsResettable;
- if (p.isFinal())
- flags |= QQmlPropertyData::IsFinal;
+ flags.isConstant = p.isConstant();
+ flags.isWritable = p.isWritable();
+ flags.isResettable = p.isResettable();
+ flags.isFinal = p.isFinal();
+
if (p.isEnumType())
- flags |= QQmlPropertyData::IsEnumType;
+ flags.type = QQmlPropertyData::Flags::EnumType;
return flags;
}
// Flags that do depend on the property's QMetaProperty::userType() and thus are slow to
// load
-static QQmlPropertyData::Flags flagsForPropertyType(int propType, QQmlEngine *engine)
+static void flagsForPropertyType(int propType, QQmlEngine *engine, QQmlPropertyData::Flags &flags)
{
Q_ASSERT(propType != -1);
- QQmlPropertyData::Flags flags;
-
if (propType == QMetaType::QObjectStar) {
- flags |= QQmlPropertyData::IsQObjectDerived;
+ flags.type = QQmlPropertyData::Flags::QObjectDerivedType;
} else if (propType == QMetaType::QVariant) {
- flags |= QQmlPropertyData::IsQVariant;
- } else if (propType < (int)QVariant::UserType) {
+ flags.type = QQmlPropertyData::Flags::QVariantType;
+ } else if (propType < static_cast<int>(QVariant::UserType)) {
+ // nothing to do
} else if (propType == qMetaTypeId<QQmlBinding *>()) {
- flags |= QQmlPropertyData::IsQmlBinding;
+ flags.type = QQmlPropertyData::Flags::QmlBindingType;
} else if (propType == qMetaTypeId<QJSValue>()) {
- flags |= QQmlPropertyData::IsQJSValue;
+ flags.type = QQmlPropertyData::Flags::QJSValueType;
} else if (propType == qMetaTypeId<QQmlV4Handle>()) {
- flags |= QQmlPropertyData::IsV4Handle;
+ flags.type = QQmlPropertyData::Flags::V4HandleType;
} else {
QQmlMetaType::TypeCategory cat =
engine ? QQmlEnginePrivate::get(engine)->typeCategory(propType)
: QQmlMetaType::typeCategory(propType);
if (cat == QQmlMetaType::Object || QMetaType::typeFlags(propType) & QMetaType::PointerToQObject)
- flags |= QQmlPropertyData::IsQObjectDerived;
+ flags.type = QQmlPropertyData::Flags::QObjectDerivedType;
else if (cat == QQmlMetaType::List)
- flags |= QQmlPropertyData::IsQList;
+ flags.type = QQmlPropertyData::Flags::QListType;
}
-
- return flags;
}
static int metaObjectSignalCount(const QMetaObject *metaObject)
@@ -143,95 +137,107 @@ static int metaObjectSignalCount(const QMetaObject *metaObject)
QQmlPropertyData::Flags
QQmlPropertyData::flagsForProperty(const QMetaProperty &p, QQmlEngine *engine)
{
- return fastFlagsForProperty(p) | flagsForPropertyType(p.userType(), engine);
+ auto flags = fastFlagsForProperty(p);
+ flagsForPropertyType(p.userType(), engine, flags);
+ return flags;
}
void QQmlPropertyData::lazyLoad(const QMetaProperty &p)
{
- coreIndex = p.propertyIndex();
- notifyIndex = QMetaObjectPrivate::signalIndex(p.notifySignal());
+ setCoreIndex(p.propertyIndex());
+ setNotifyIndex(QMetaObjectPrivate::signalIndex(p.notifySignal()));
Q_ASSERT(p.revision() <= Q_INT16_MAX);
- revision = p.revision();
+ setRevision(p.revision());
- flags = fastFlagsForProperty(p);
+ setFlags(fastFlagsForProperty(p));
- int type = p.type();
+ int type = static_cast<int>(p.type());
if (type == QMetaType::QObjectStar) {
- propType = type;
- flags |= QQmlPropertyData::IsQObjectDerived;
+ setPropType(type);
+ _flags.type = Flags::QObjectDerivedType;
} else if (type == QMetaType::QVariant) {
- propType = type;
- flags |= QQmlPropertyData::IsQVariant;
+ setPropType(type);
+ _flags.type = Flags::QVariantType;
} else if (type == QVariant::UserType || type == -1) {
- propTypeName = p.typeName();
- flags |= QQmlPropertyData::NotFullyResolved;
+ _flags.notFullyResolved = true;
} else {
- propType = type;
+ setPropType(type);
}
}
void QQmlPropertyData::load(const QMetaProperty &p, QQmlEngine *engine)
{
- propType = p.userType();
- coreIndex = p.propertyIndex();
- notifyIndex = QMetaObjectPrivate::signalIndex(p.notifySignal());
- flags = fastFlagsForProperty(p) | flagsForPropertyType(propType, engine);
+ setPropType(p.userType());
+ setCoreIndex(p.propertyIndex());
+ setNotifyIndex(QMetaObjectPrivate::signalIndex(p.notifySignal()));
+ setFlags(fastFlagsForProperty(p));
+ flagsForPropertyType(propType(), engine, _flags);
Q_ASSERT(p.revision() <= Q_INT16_MAX);
- revision = p.revision();
+ setRevision(p.revision());
}
void QQmlPropertyData::load(const QMetaMethod &m)
{
- coreIndex = m.methodIndex();
- arguments = 0;
- flags |= IsFunction;
+ setCoreIndex(m.methodIndex());
+ setArguments(nullptr);
+
+ setPropType(m.returnType());
+
+ _flags.type = Flags::FunctionType;
if (m.methodType() == QMetaMethod::Signal)
- flags |= IsSignal;
- propType = m.returnType();
+ _flags.isSignal = true;
+ else if (m.methodType() == QMetaMethod::Constructor) {
+ _flags.isConstructor = true;
+ setPropType(QMetaType::QObjectStar);
+ }
if (m.parameterCount()) {
- flags |= HasArguments;
+ _flags.hasArguments = true;
if ((m.parameterCount() == 1) && (m.parameterTypes().first() == "QQmlV4Function*")) {
- flags |= IsV4Function;
+ _flags.isV4Function = true;
}
}
if (m.attributes() & QMetaMethod::Cloned)
- flags |= IsCloned;
+ _flags.isCloned = true;
Q_ASSERT(m.revision() <= Q_INT16_MAX);
- revision = m.revision();
+ setRevision(m.revision());
}
void QQmlPropertyData::lazyLoad(const QMetaMethod &m)
{
- coreIndex = m.methodIndex();
- arguments = 0;
- flags |= IsFunction;
+ setCoreIndex(m.methodIndex());
+ setPropType(QMetaType::Void);
+ setArguments(nullptr);
+ _flags.type = Flags::FunctionType;
if (m.methodType() == QMetaMethod::Signal)
- flags |= IsSignal;
- propType = QMetaType::Void;
+ _flags.isSignal = true;
+ else if (m.methodType() == QMetaMethod::Constructor) {
+ _flags.isConstructor = true;
+ setPropType(QMetaType::QObjectStar);
+ }
const char *returnType = m.typeName();
if (!returnType)
returnType = "\0";
if ((*returnType != 'v') || (qstrcmp(returnType+1, "oid") != 0)) {
- propTypeName = returnType;
- flags |= NotFullyResolved;
+ _flags.notFullyResolved = true;
}
- if (m.parameterCount()) {
- flags |= HasArguments;
- if ((m.parameterCount() == 1) && (m.parameterTypes().first() == "QQmlV4Function*")) {
- flags |= IsV4Function;
+ const int paramCount = m.parameterCount();
+ if (paramCount) {
+ _flags.hasArguments = true;
+ if ((paramCount == 1) && (m.parameterTypes().first() == "QQmlV4Function*")) {
+ _flags.isV4Function = true;
}
}
if (m.attributes() & QMetaMethod::Cloned)
- flags |= IsCloned;
+ _flags.isCloned = true;
Q_ASSERT(m.revision() <= Q_INT16_MAX);
- revision = m.revision();
+ setRevision(m.revision());
}
/*!
@@ -249,11 +255,8 @@ QQmlPropertyCache::QQmlPropertyCache(QV4::ExecutionEngine *e)
Creates a new QQmlPropertyCache of \a metaObject.
*/
QQmlPropertyCache::QQmlPropertyCache(QV4::ExecutionEngine *e, const QMetaObject *metaObject)
- : engine(e), _parent(0), propertyIndexCacheStart(0), methodIndexCacheStart(0),
- signalHandlerIndexCacheStart(0), _hasPropertyOverrides(false), _ownMetaObject(false),
- _metaObject(0), argumentsCache(0), _jsFactoryMethodIndex(-1)
+ : QQmlPropertyCache(e)
{
- Q_ASSERT(engine);
Q_ASSERT(metaObject);
update(metaObject);
@@ -333,14 +336,14 @@ QQmlPropertyCache *QQmlPropertyCache::copyAndReserve(int propertyCount, int meth
\a notifyIndex MUST be in the signal index range (see QObjectPrivate::signalIndex()).
This is different from QMetaMethod::methodIndex().
*/
-void QQmlPropertyCache::appendProperty(const QString &name,
- quint32 flags, int coreIndex, int propType, int notifyIndex)
+void QQmlPropertyCache::appendProperty(const QString &name, QQmlPropertyData::Flags flags,
+ int coreIndex, int propType, int notifyIndex)
{
QQmlPropertyData data;
- data.propType = propType;
- data.coreIndex = coreIndex;
- data.notifyIndex = notifyIndex;
- data.flags = flags;
+ data.setPropType(propType);
+ data.setCoreIndex(coreIndex);
+ data.setNotifyIndex(notifyIndex);
+ data.setFlags(flags);
QQmlPropertyData *old = findNamedProperty(name);
if (old)
@@ -352,24 +355,25 @@ void QQmlPropertyCache::appendProperty(const QString &name,
setNamedProperty(name, index + propertyOffset(), propertyIndexCache.data() + index, (old != 0));
}
-void QQmlPropertyCache::appendSignal(const QString &name, quint32 flags, int coreIndex,
- const int *types, const QList<QByteArray> &names)
+void QQmlPropertyCache::appendSignal(const QString &name, QQmlPropertyData::Flags flags,
+ int coreIndex, const int *types,
+ const QList<QByteArray> &names)
{
QQmlPropertyData data;
- data.propType = QVariant::Invalid;
- data.coreIndex = coreIndex;
- data.flags = flags;
- data.arguments = 0;
+ data.setPropType(QVariant::Invalid);
+ data.setCoreIndex(coreIndex);
+ data.setFlags(flags);
+ data.setArguments(nullptr);
QQmlPropertyData handler = data;
- handler.flags |= QQmlPropertyData::IsSignalHandler;
+ handler._flags.isSignalHandler = true;
if (types) {
int argumentCount = *types;
QQmlPropertyCacheMethodArguments *args = createArgumentsObject(argumentCount, names);
::memcpy(args->arguments, types, (argumentCount + 1) * sizeof(int));
args->argumentsValid = true;
- data.arguments = args;
+ data.setArguments(args);
}
QQmlPropertyData *old = findNamedProperty(name);
@@ -383,28 +387,28 @@ void QQmlPropertyCache::appendSignal(const QString &name, quint32 flags, int cor
signalHandlerIndexCache.append(handler);
QString handlerName = QLatin1String("on") + name;
- handlerName[2] = handlerName[2].toUpper();
+ handlerName[2] = handlerName.at(2).toUpper();
setNamedProperty(name, methodIndex + methodOffset(), methodIndexCache.data() + methodIndex, (old != 0));
setNamedProperty(handlerName, signalHandlerIndex + signalOffset(), signalHandlerIndexCache.data() + signalHandlerIndex, (old != 0));
}
-void QQmlPropertyCache::appendMethod(const QString &name, quint32 flags, int coreIndex,
- const QList<QByteArray> &names)
+void QQmlPropertyCache::appendMethod(const QString &name, QQmlPropertyData::Flags flags,
+ int coreIndex, const QList<QByteArray> &names)
{
int argumentCount = names.count();
QQmlPropertyData data;
- data.propType = QMetaType::QVariant;
- data.coreIndex = coreIndex;
+ data.setPropType(QMetaType::QVariant);
+ data.setCoreIndex(coreIndex);
QQmlPropertyCacheMethodArguments *args = createArgumentsObject(argumentCount, names);
for (int ii = 0; ii < argumentCount; ++ii)
args->arguments[ii + 1] = QMetaType::QVariant;
args->argumentsValid = true;
- data.arguments = args;
+ data.setArguments(args);
- data.flags = flags;
+ data.setFlags(flags);
QQmlPropertyData *old = findNamedProperty(name);
if (old)
@@ -416,12 +420,6 @@ void QQmlPropertyCache::appendMethod(const QString &name, quint32 flags, int cor
setNamedProperty(name, methodIndex + methodOffset(), methodIndexCache.data() + methodIndex, (old != 0));
}
-// Returns this property cache's metaObject. May be null if it hasn't been created yet.
-const QMetaObject *QQmlPropertyCache::metaObject() const
-{
- return _metaObject;
-}
-
// Returns this property cache's metaObject, creating it if necessary.
const QMetaObject *QQmlPropertyCache::createMetaObject()
{
@@ -437,51 +435,36 @@ const QMetaObject *QQmlPropertyCache::createMetaObject()
return _metaObject;
}
-// Returns the name of the default property for this cache
-QString QQmlPropertyCache::defaultPropertyName() const
-{
- return _defaultPropertyName;
-}
-
QQmlPropertyData *QQmlPropertyCache::defaultProperty() const
{
return property(defaultPropertyName(), 0, 0);
}
-QQmlPropertyCache *QQmlPropertyCache::parent() const
-{
- return _parent;
-}
-
void QQmlPropertyCache::setParent(QQmlPropertyCache *newParent)
{
+ if (_parent == newParent)
+ return;
+ if (_parent)
+ _parent->release();
_parent = newParent;
-}
-
-// Returns the first C++ type's QMetaObject - that is, the first QMetaObject not created by
-// QML
-const QMetaObject *QQmlPropertyCache::firstCppMetaObject() const
-{
- while (_parent && (_metaObject == 0 || _ownMetaObject))
- return _parent->firstCppMetaObject();
- return _metaObject;
+ _parent->addref();
}
QQmlPropertyCache *
QQmlPropertyCache::copyAndAppend(const QMetaObject *metaObject,
- QQmlPropertyData::Flag propertyFlags,
- QQmlPropertyData::Flag methodFlags,
- QQmlPropertyData::Flag signalFlags)
+ QQmlPropertyData::Flags propertyFlags,
+ QQmlPropertyData::Flags methodFlags,
+ QQmlPropertyData::Flags signalFlags)
{
return copyAndAppend(metaObject, -1, propertyFlags, methodFlags, signalFlags);
}
QQmlPropertyCache *
QQmlPropertyCache::copyAndAppend(const QMetaObject *metaObject,
- int revision,
- QQmlPropertyData::Flag propertyFlags,
- QQmlPropertyData::Flag methodFlags,
- QQmlPropertyData::Flag signalFlags)
+ int revision,
+ QQmlPropertyData::Flags propertyFlags,
+ QQmlPropertyData::Flags methodFlags,
+ QQmlPropertyData::Flags signalFlags)
{
Q_ASSERT(QMetaObjectPrivate::get(metaObject)->revision >= 4);
@@ -498,10 +481,10 @@ QQmlPropertyCache::copyAndAppend(const QMetaObject *metaObject,
}
void QQmlPropertyCache::append(const QMetaObject *metaObject,
- int revision,
- QQmlPropertyData::Flag propertyFlags,
- QQmlPropertyData::Flag methodFlags,
- QQmlPropertyData::Flag signalFlags)
+ int revision,
+ QQmlPropertyData::Flags propertyFlags,
+ QQmlPropertyData::Flags methodFlags,
+ QQmlPropertyData::Flags signalFlags)
{
Q_UNUSED(revision);
@@ -516,40 +499,21 @@ void QQmlPropertyCache::append(const QMetaObject *metaObject,
int signalCount = metaObjectSignalCount(metaObject);
int classInfoCount = QMetaObjectPrivate::get(metaObject)->classInfoCount;
- QQmlAccessorProperties::Properties accessorProperties;
-
if (classInfoCount) {
int classInfoOffset = metaObject->classInfoOffset();
- bool hasFastProperty = false;
for (int ii = 0; ii < classInfoCount; ++ii) {
int idx = ii + classInfoOffset;
-
- const char * const classInfoName = metaObject->classInfo(idx).name();
- if (0 == qstrcmp(classInfoName, "qt_HasQmlAccessors")) {
- hasFastProperty = true;
- } else if (0 == qstrcmp(classInfoName, "DefaultProperty")) {
- _defaultPropertyName = QString::fromUtf8(metaObject->classInfo(idx).value());
- } else if (0 == qstrcmp(classInfoName, "qt_QmlJSWrapperFactoryMethod")) {
- const char * const factoryMethod = metaObject->classInfo(idx).value();
+ QMetaClassInfo mci = metaObject->classInfo(idx);
+ const char *name = mci.name();
+ if (0 == qstrcmp(name, "DefaultProperty")) {
+ _defaultPropertyName = QString::fromUtf8(mci.value());
+ } else if (0 == qstrcmp(name, "qt_QmlJSWrapperFactoryMethod")) {
+ const char * const factoryMethod = mci.value();
_jsFactoryMethodIndex = metaObject->indexOfSlot(factoryMethod);
if (_jsFactoryMethodIndex != -1)
_jsFactoryMethodIndex -= metaObject->methodOffset();
}
}
-
- if (hasFastProperty) {
- accessorProperties = QQmlAccessorProperties::properties(metaObject);
- if (accessorProperties.count == 0)
- qFatal("QQmlPropertyCache: %s has FastProperty class info, but has not "
- "installed property accessors", metaObject->className());
- } else {
-#ifndef QT_NO_DEBUG
- accessorProperties = QQmlAccessorProperties::properties(metaObject);
- if (accessorProperties.count != 0)
- qFatal("QQmlPropertyCache: %s has fast property accessors, but is missing "
- "FastProperty class info", metaObject->className());
-#endif
- }
}
//Used to block access to QObject::destroyed() and QObject::deleteLater() from QML
@@ -588,23 +552,21 @@ void QQmlPropertyCache::append(const QMetaObject *metaObject,
QQmlPropertyData *data = &methodIndexCache[ii - methodIndexCacheStart];
QQmlPropertyData *sigdata = 0;
- data->lazyLoad(m);
-
- if (data->isSignal())
- data->flags |= signalFlags;
+ if (m.methodType() == QMetaMethod::Signal)
+ data->setFlags(signalFlags);
else
- data->flags |= methodFlags;
+ data->setFlags(methodFlags);
- if (!dynamicMetaObject)
- data->flags |= QQmlPropertyData::IsDirect;
+ data->lazyLoad(m);
+ data->_flags.isDirect = !dynamicMetaObject;
Q_ASSERT((allowedRevisionCache.count() - 1) < Q_INT16_MAX);
- data->metaObjectOffset = allowedRevisionCache.count() - 1;
+ data->setMetaObjectOffset(allowedRevisionCache.count() - 1);
if (data->isSignal()) {
sigdata = &signalHandlerIndexCache[signalHandlerIndex - signalHandlerIndexCacheStart];
*sigdata = *data;
- sigdata->flags |= QQmlPropertyData::IsSignalHandler;
+ sigdata->_flags.isSignalHandler = true;
}
QQmlPropertyData *old = 0;
@@ -616,7 +578,7 @@ void QQmlPropertyCache::append(const QMetaObject *metaObject,
setNamedProperty(methodName, ii, data, (old != 0));
if (data->isSignal()) {
- QHashedString on(QStringLiteral("on") % methodName.at(0).toUpper() % methodName.midRef(1));
+ QHashedString on(QLatin1String("on") % methodName.at(0).toUpper() % methodName.midRef(1));
setNamedProperty(on, ii, sigdata, (old != 0));
++signalHandlerIndex;
}
@@ -645,8 +607,8 @@ void QQmlPropertyCache::append(const QMetaObject *metaObject,
if (old) {
// We only overload methods in the same class, exactly like C++
- if (old->isFunction() && old->coreIndex >= methodOffset)
- data->flags |= QQmlPropertyData::IsOverload;
+ if (old->isFunction() && old->coreIndex() >= methodOffset)
+ data->_flags.isOverload = true;
data->markAsOverrideOf(old);
}
@@ -673,14 +635,13 @@ void QQmlPropertyCache::append(const QMetaObject *metaObject,
QQmlPropertyData *data = &propertyIndexCache[ii - propertyIndexCacheStart];
+ data->setFlags(propertyFlags);
data->lazyLoad(p);
- data->flags |= propertyFlags;
- if (!dynamicMetaObject)
- data->flags |= QQmlPropertyData::IsDirect;
+ data->_flags.isDirect = !dynamicMetaObject;
Q_ASSERT((allowedRevisionCache.count() - 1) < Q_INT16_MAX);
- data->metaObjectOffset = allowedRevisionCache.count() - 1;
+ data->setMetaObjectOffset(allowedRevisionCache.count() - 1);
QQmlPropertyData *old = 0;
@@ -696,29 +657,40 @@ void QQmlPropertyCache::append(const QMetaObject *metaObject,
setNamedProperty(propName, ii, data, (old != 0));
}
- QQmlAccessorProperties::Property *accessorProperty = accessorProperties.property(str);
-
- // Fast properties may not be overrides or revisioned
- Q_ASSERT(accessorProperty == 0 || (old == 0 && data->revision == 0));
+ bool isGadget = true;
+ for (const QMetaObject *it = metaObject; it != nullptr; it = it->superClass()) {
+ if (it == &QObject::staticMetaObject)
+ isGadget = false;
+ }
- if (accessorProperty) {
- data->flags |= QQmlPropertyData::HasAccessors;
- data->accessors = accessorProperty->accessors;
- } else if (old) {
+ if (isGadget) // always dispatch over a 'normal' meta-call so the QQmlValueType can intercept
+ data->_flags.isDirect = false;
+ else
+ data->trySetStaticMetaCallFunction(metaObject->d.static_metacall, ii - propOffset);
+ if (old)
data->markAsOverrideOf(old);
- }
}
}
void QQmlPropertyCache::resolve(QQmlPropertyData *data) const
{
Q_ASSERT(data->notFullyResolved());
-
- data->propType = QMetaType::type(data->propTypeName);
+ data->_flags.notFullyResolved = false;
+
+ const QMetaObject *mo = firstCppMetaObject();
+ if (data->isFunction()) {
+ auto metaMethod = mo->method(data->coreIndex());
+ const char *retTy = metaMethod.typeName();
+ if (!retTy)
+ retTy = "\0";
+ data->setPropType(QMetaType::type(retTy));
+ } else {
+ auto metaProperty = mo->property(data->coreIndex());
+ data->setPropType(QMetaType::type(metaProperty.typeName()));
+ }
if (!data->isFunction()) {
- if (data->propType == QMetaType::UnknownType) {
- const QMetaObject *mo = _metaObject;
+ if (data->propType() == QMetaType::UnknownType) {
QQmlPropertyCache *p = _parent;
while (p && (!mo || _ownMetaObject)) {
mo = p->_metaObject;
@@ -726,22 +698,20 @@ void QQmlPropertyCache::resolve(QQmlPropertyData *data) const
}
int propOffset = mo->propertyOffset();
- if (mo && data->coreIndex < propOffset + mo->propertyCount()) {
- while (data->coreIndex < propOffset) {
+ if (mo && data->coreIndex() < propOffset + mo->propertyCount()) {
+ while (data->coreIndex() < propOffset) {
mo = mo->superClass();
propOffset = mo->propertyOffset();
}
int registerResult = -1;
void *argv[] = { &registerResult };
- mo->static_metacall(QMetaObject::RegisterPropertyMetaType, data->coreIndex - propOffset, argv);
- data->propType = registerResult == -1 ? QMetaType::UnknownType : registerResult;
+ mo->static_metacall(QMetaObject::RegisterPropertyMetaType, data->coreIndex() - propOffset, argv);
+ data->setPropType(registerResult == -1 ? QMetaType::UnknownType : registerResult);
}
}
- data->flags |= flagsForPropertyType(data->propType, engine->qmlEngine());
+ flagsForPropertyType(data->propType(), engine->qmlEngine(), data->_flags);
}
-
- data->flags &= ~QQmlPropertyData::NotFullyResolved;
}
void QQmlPropertyCache::updateRecur(const QMetaObject *metaObject)
@@ -808,48 +778,6 @@ void QQmlPropertyCache::invalidate(const QMetaObject *metaObject)
}
}
-/*! \internal
- \a index MUST be in the signal index range (see QObjectPrivate::signalIndex()).
- This is different from QMetaMethod::methodIndex().
-*/
-QQmlPropertyData *
-QQmlPropertyCache::signal(int index) const
-{
- if (index < 0 || index >= (signalHandlerIndexCacheStart + signalHandlerIndexCache.count()))
- return 0;
-
- if (index < signalHandlerIndexCacheStart)
- return _parent->signal(index);
-
- QQmlPropertyData *rv = const_cast<QQmlPropertyData *>(&methodIndexCache.at(index - signalHandlerIndexCacheStart));
- Q_ASSERT(rv->isSignal() || rv->coreIndex == -1);
- return ensureResolved(rv);
-}
-
-int QQmlPropertyCache::methodIndexToSignalIndex(int index) const
-{
- if (index < 0 || index >= (methodIndexCacheStart + methodIndexCache.count()))
- return index;
-
- if (index < methodIndexCacheStart)
- return _parent->methodIndexToSignalIndex(index);
-
- return index - methodIndexCacheStart + signalHandlerIndexCacheStart;
-}
-
-QQmlPropertyData *
-QQmlPropertyCache::method(int index) const
-{
- if (index < 0 || index >= (methodIndexCacheStart + methodIndexCache.count()))
- return 0;
-
- if (index < methodIndexCacheStart)
- return _parent->method(index);
-
- QQmlPropertyData *rv = const_cast<QQmlPropertyData *>(&methodIndexCache.at(index - methodIndexCacheStart));
- return ensureResolved(rv);
-}
-
QQmlPropertyData *QQmlPropertyCache::findProperty(StringCache::ConstIterator it, QObject *object, QQmlContextData *context) const
{
QQmlData *data = (object ? QQmlData::get(object) : 0);
@@ -866,11 +794,11 @@ inline bool contextHasNoExtensions(QQmlContextData *context)
return (!context->parent || !context->parent->imports);
}
-inline int maximumIndexForProperty(QQmlPropertyData *prop, const QQmlVMEMetaObject *vmemo)
+inline int maximumIndexForProperty(QQmlPropertyData *prop, const int methodCount, const int signalCount, const int propertyCount)
{
- return (prop->isFunction() ? vmemo->methodCount()
- : prop->isSignalHandler() ? vmemo->signalCount()
- : vmemo->propertyCount());
+ return prop->isFunction() ? methodCount
+ : prop->isSignalHandler() ? signalCount
+ : propertyCount;
}
}
@@ -897,11 +825,15 @@ QQmlPropertyData *QQmlPropertyCache::findProperty(StringCache::ConstIterator it,
}
if (vmemo) {
+ const int methodCount = vmemo->methodCount();
+ const int signalCount = vmemo->signalCount();
+ const int propertyCount = vmemo->propertyCount();
+
// Ensure that the property we resolve to is accessible from this meta-object
do {
const StringCache::mapped_type &property(it.value());
- if (property.first < maximumIndexForProperty(property.second, vmemo)) {
+ if (property.first < maximumIndexForProperty(property.second, methodCount, signalCount, propertyCount)) {
// This property is available in the specified context
if (property.second->isFunction() || property.second->isSignalHandler()) {
// Prefer the earlier resolution
@@ -933,33 +865,25 @@ QString QQmlPropertyData::name(QObject *object) const
QString QQmlPropertyData::name(const QMetaObject *metaObject) const
{
- if (!metaObject || coreIndex == -1)
+ if (!metaObject || coreIndex() == -1)
return QString();
- if (flags & IsFunction) {
- QMetaMethod m = metaObject->method(coreIndex);
+ if (isFunction()) {
+ QMetaMethod m = metaObject->method(coreIndex());
return QString::fromUtf8(m.name().constData());
} else {
- QMetaProperty p = metaObject->property(coreIndex);
+ QMetaProperty p = metaObject->property(coreIndex());
return QString::fromUtf8(p.name());
}
}
void QQmlPropertyData::markAsOverrideOf(QQmlPropertyData *predecessor)
{
- overrideIndexIsProperty = !predecessor->isFunction();
- overrideIndex = predecessor->coreIndex;
+ setOverrideIndexIsProperty(!predecessor->isFunction());
+ setOverrideIndex(predecessor->coreIndex());
- predecessor->flags |= QQmlPropertyData::IsOverridden;
-}
-
-QStringList QQmlPropertyCache::propertyNames() const
-{
- QStringList keys;
- for (StringCache::ConstIterator iter = stringCache.begin(), cend = stringCache.end(); iter != cend; ++iter)
- keys.append(iter.key());
- return keys;
+ predecessor->_flags.isOverridden = true;
}
struct StaticQtMetaObject : public QObject
@@ -1010,7 +934,6 @@ QString QQmlPropertyCache::signalParameterStringForJS(QV4::ExecutionEngine *engi
{
bool unnamedParameter = false;
const QSet<QString> &illegalNames = engine->v8Engine->illegalNames();
- QString error;
QString parameters;
for (int i = 0; i < parameterNameList.count(); ++i) {
@@ -1173,9 +1096,21 @@ QQmlPropertyCache::property(QJSEngine *engine, QObject *obj,
return qQmlPropertyCacheProperty<const QString &>(engine, obj, name, context, local);
}
+// these two functions are copied from qmetaobject.cpp
static inline const QMetaObjectPrivate *priv(const uint* data)
{ return reinterpret_cast<const QMetaObjectPrivate*>(data); }
+static inline const QByteArray stringData(const QMetaObject *mo, int index)
+{
+ Q_ASSERT(priv(mo->d.data)->revision >= 7);
+ const QByteArrayDataPtr data = { const_cast<QByteArrayData*>(&mo->d.stringdata[index]) };
+ Q_ASSERT(data.ptr->ref.isStatic());
+ Q_ASSERT(data.ptr->alloc == 0);
+ Q_ASSERT(data.ptr->capacityReserved == 0);
+ Q_ASSERT(data.ptr->size >= 0);
+ return data;
+}
+
bool QQmlPropertyCache::isDynamicMetaObject(const QMetaObject *mo)
{
return priv(mo->d.data)->revision >= 3 && priv(mo->d.data)->flags & DynamicMetaObject;
@@ -1193,7 +1128,7 @@ void QQmlPropertyCache::toMetaObjectBuilder(QMetaObjectBuilder &builder)
{
struct Sort { static bool lt(const QPair<QString, QQmlPropertyData *> &lhs,
const QPair<QString, QQmlPropertyData *> &rhs) {
- return lhs.second->coreIndex < rhs.second->coreIndex;
+ return lhs.second->coreIndex() < rhs.second->coreIndex();
} };
struct Insert { static void in(QQmlPropertyCache *This,
@@ -1204,7 +1139,7 @@ void QQmlPropertyCache::toMetaObjectBuilder(QMetaObjectBuilder &builder)
return;
if (data->isFunction()) {
- if (data->coreIndex < This->methodIndexCacheStart)
+ if (data->coreIndex() < This->methodIndexCacheStart)
return;
QPair<QString, QQmlPropertyData *> entry = qMakePair((QString)iter.key(), data);
@@ -1214,7 +1149,7 @@ void QQmlPropertyCache::toMetaObjectBuilder(QMetaObjectBuilder &builder)
data = This->overrideData(data);
if (data && !data->isFunction()) Insert::in(This, properties, methods, iter, data);
} else {
- if (data->coreIndex < This->propertyIndexCacheStart)
+ if (data->coreIndex() < This->propertyIndexCacheStart)
return;
QPair<QString, QQmlPropertyData *> entry = qMakePair((QString)iter.key(), data);
@@ -1245,11 +1180,11 @@ void QQmlPropertyCache::toMetaObjectBuilder(QMetaObjectBuilder &builder)
QQmlPropertyData *data = properties.at(ii).second;
int notifierId = -1;
- if (data->notifyIndex != -1)
- notifierId = data->notifyIndex - signalHandlerIndexCacheStart;
+ if (data->notifyIndex() != -1)
+ notifierId = data->notifyIndex() - signalHandlerIndexCacheStart;
QMetaPropertyBuilder property = builder.addProperty(properties.at(ii).first.toUtf8(),
- QMetaType::typeName(data->propType),
+ QMetaType::typeName(data->propType()),
notifierId);
property.setReadable(true);
@@ -1261,14 +1196,16 @@ void QQmlPropertyCache::toMetaObjectBuilder(QMetaObjectBuilder &builder)
QQmlPropertyData *data = methods.at(ii).second;
QByteArray returnType;
- if (data->propType != 0)
- returnType = QMetaType::typeName(data->propType);
+ if (data->propType() != 0)
+ returnType = QMetaType::typeName(data->propType());
- QByteArray signature = methods.at(ii).first.toUtf8() + '(';
+ QByteArray signature;
+ // '+=' reserves extra capacity. Follow-up appending will be probably free.
+ signature += methods.at(ii).first.toUtf8() + '(';
QQmlPropertyCacheMethodArguments *arguments = 0;
if (data->hasArguments()) {
- arguments = (QQmlPropertyCacheMethodArguments *)data->arguments;
+ arguments = (QQmlPropertyCacheMethodArguments *)data->arguments();
Q_ASSERT(arguments->argumentsValid);
for (int ii = 0; ii < arguments->arguments[0]; ++ii) {
if (ii != 0) signature.append(',');
@@ -1295,13 +1232,227 @@ void QQmlPropertyCache::toMetaObjectBuilder(QMetaObjectBuilder &builder)
if (!_defaultPropertyName.isEmpty()) {
QQmlPropertyData *dp = property(_defaultPropertyName, 0, 0);
- if (dp && dp->coreIndex >= propertyIndexCacheStart) {
+ if (dp && dp->coreIndex() >= propertyIndexCacheStart) {
Q_ASSERT(!dp->isFunction());
builder.addClassInfo("DefaultProperty", _defaultPropertyName.toUtf8());
}
}
}
+namespace {
+template <typename StringVisitor, typename TypeInfoVisitor>
+int visitMethods(const QMetaObject &mo, int methodOffset, int methodCount,
+ StringVisitor visitString, TypeInfoVisitor visitTypeInfo)
+{
+ const int intsPerMethod = 5;
+
+ int fieldsForParameterData = 0;
+
+ bool hasRevisionedMethods = false;
+
+ for (int i = 0; i < methodCount; ++i) {
+ const int handle = methodOffset + i * intsPerMethod;
+
+ const uint flags = mo.d.data[handle + 4];
+ if (flags & MethodRevisioned)
+ hasRevisionedMethods = true;
+
+ visitString(mo.d.data[handle + 0]); // name
+ visitString(mo.d.data[handle + 3]); // tag
+
+ const int argc = mo.d.data[handle + 1];
+ const int paramIndex = mo.d.data[handle + 2];
+
+ fieldsForParameterData += argc * 2; // type and name
+ fieldsForParameterData += 1; // + return type
+
+ // return type + args
+ for (int i = 0; i < 1 + argc; ++i) {
+ // type name (maybe)
+ visitTypeInfo(mo.d.data[paramIndex + i]);
+
+ // parameter name
+ if (i > 0)
+ visitString(mo.d.data[paramIndex + argc + i]);
+ }
+ }
+
+ int fieldsForRevisions = 0;
+ if (hasRevisionedMethods)
+ fieldsForRevisions = methodCount;
+
+ return methodCount * intsPerMethod + fieldsForRevisions + fieldsForParameterData;
+}
+
+template <typename StringVisitor, typename TypeInfoVisitor>
+int visitProperties(const QMetaObject &mo, StringVisitor visitString, TypeInfoVisitor visitTypeInfo)
+{
+ const QMetaObjectPrivate *const priv = reinterpret_cast<const QMetaObjectPrivate*>(mo.d.data);
+ const int intsPerProperty = 3;
+
+ bool hasRevisionedProperties = false;
+ bool hasNotifySignals = false;
+
+ for (int i = 0; i < priv->propertyCount; ++i) {
+ const int handle = priv->propertyData + i * intsPerProperty;
+
+ const auto flags = mo.d.data[handle + 2];
+ if (flags & Revisioned) {
+ hasRevisionedProperties = true;
+ }
+ if (flags & Notify)
+ hasNotifySignals = true;
+
+ visitString(mo.d.data[handle]); // name
+ visitTypeInfo(mo.d.data[handle + 1]);
+ }
+
+ int fieldsForPropertyRevisions = 0;
+ if (hasRevisionedProperties)
+ fieldsForPropertyRevisions = priv->propertyCount;
+
+ int fieldsForNotifySignals = 0;
+ if (hasNotifySignals)
+ fieldsForNotifySignals = priv->propertyCount;
+
+ return priv->propertyCount * intsPerProperty + fieldsForPropertyRevisions
+ + fieldsForNotifySignals;
+}
+
+template <typename StringVisitor>
+int visitClassInfo(const QMetaObject &mo, StringVisitor visitString)
+{
+ const QMetaObjectPrivate *const priv = reinterpret_cast<const QMetaObjectPrivate*>(mo.d.data);
+ const int intsPerClassInfo = 2;
+
+ for (int i = 0; i < priv->classInfoCount; ++i) {
+ const int handle = priv->classInfoData + i * intsPerClassInfo;
+
+ visitString(mo.d.data[handle]); // key
+ visitString(mo.d.data[handle + 1]); // value
+ }
+
+ return priv->classInfoCount * intsPerClassInfo;
+}
+
+template <typename StringVisitor>
+int visitEnumerations(const QMetaObject &mo, StringVisitor visitString)
+{
+ const QMetaObjectPrivate *const priv = reinterpret_cast<const QMetaObjectPrivate*>(mo.d.data);
+ const int intsPerEnumerator = 4;
+
+ int fieldCount = priv->enumeratorCount * intsPerEnumerator;
+
+ for (int i = 0; i < priv->enumeratorCount; ++i) {
+ const uint *enumeratorData = mo.d.data + priv->enumeratorData + i * intsPerEnumerator;
+
+ const uint keyCount = enumeratorData[2];
+ fieldCount += keyCount * 2;
+
+ visitString(enumeratorData[0]); // name
+
+ const uint keyOffset = enumeratorData[3];
+
+ for (uint j = 0; j < keyCount; ++j) {
+ visitString(mo.d.data[keyOffset + 2 * j]);
+ }
+ }
+
+ return fieldCount;
+}
+
+template <typename StringVisitor>
+int countMetaObjectFields(const QMetaObject &mo, StringVisitor stringVisitor)
+{
+ const QMetaObjectPrivate *const priv = reinterpret_cast<const QMetaObjectPrivate*>(mo.d.data);
+
+ const auto typeInfoVisitor = [&stringVisitor](uint typeInfo) {
+ if (typeInfo & IsUnresolvedType)
+ stringVisitor(typeInfo & TypeNameIndexMask);
+ };
+
+ int fieldCount = MetaObjectPrivateFieldCount;
+
+ fieldCount += visitMethods(mo, priv->methodData, priv->methodCount, stringVisitor,
+ typeInfoVisitor);
+ fieldCount += visitMethods(mo, priv->constructorData, priv->constructorCount, stringVisitor,
+ typeInfoVisitor);
+
+ fieldCount += visitProperties(mo, stringVisitor, typeInfoVisitor);
+ fieldCount += visitClassInfo(mo, stringVisitor);
+ fieldCount += visitEnumerations(mo, stringVisitor);
+
+ return fieldCount;
+}
+
+} // anonymous namespace
+
+bool QQmlPropertyCache::determineMetaObjectSizes(const QMetaObject &mo, int *fieldCount,
+ int *stringCount)
+{
+ const QMetaObjectPrivate *priv = reinterpret_cast<const QMetaObjectPrivate*>(mo.d.data);
+ if (priv->revision != 7) {
+ return false;
+ }
+
+ uint highestStringIndex = 0;
+ const auto stringIndexVisitor = [&highestStringIndex](uint index) {
+ highestStringIndex = qMax(highestStringIndex, index);
+ };
+
+ *fieldCount = countMetaObjectFields(mo, stringIndexVisitor);
+ *stringCount = highestStringIndex + 1;
+
+ return true;
+}
+
+bool QQmlPropertyCache::addToHash(QCryptographicHash &hash, const QMetaObject &mo)
+{
+ int fieldCount = 0;
+ int stringCount = 0;
+ if (!determineMetaObjectSizes(mo, &fieldCount, &stringCount)) {
+ return false;
+ }
+
+ hash.addData(reinterpret_cast<const char *>(mo.d.data), fieldCount * sizeof(uint));
+ for (int i = 0; i < stringCount; ++i) {
+ hash.addData(stringData(&mo, i));
+ }
+
+ return true;
+}
+
+QByteArray QQmlPropertyCache::checksum(bool *ok)
+{
+ if (!_checksum.isEmpty()) {
+ *ok = true;
+ return _checksum;
+ }
+
+ // Generate a checksum on the meta-object data only on C++ types.
+ if (!_metaObject || _ownMetaObject) {
+ *ok = false;
+ return _checksum;
+ }
+
+ QCryptographicHash hash(QCryptographicHash::Md5);
+
+ if (_parent) {
+ hash.addData(_parent->checksum(ok));
+ if (!*ok)
+ return QByteArray();
+ }
+
+ if (!addToHash(hash, *createMetaObject())) {
+ *ok = false;
+ return QByteArray();
+ }
+
+ _checksum = hash.result();
+ *ok = !_checksum.isEmpty();
+ return _checksum;
+}
+
/*! \internal
\a index MUST be in the signal index range (see QObjectPrivate::signalIndex()).
This is different from QMetaMethod::methodIndex().
@@ -1310,7 +1461,7 @@ QList<QByteArray> QQmlPropertyCache::signalParameterNames(int index) const
{
QQmlPropertyData *signalData = signal(index);
if (signalData && signalData->hasArguments()) {
- QQmlPropertyCacheMethodArguments *args = (QQmlPropertyCacheMethodArguments *)signalData->arguments;
+ QQmlPropertyCacheMethodArguments *args = (QQmlPropertyCacheMethodArguments *)signalData->arguments();
if (args && args->names)
return *args->names;
const QMetaMethod &method = QMetaObjectPrivate::signal(firstCppMetaObject(), index);
@@ -1412,9 +1563,9 @@ QQmlPropertyCache *QQmlMetaObject::propertyCache(QQmlEnginePrivate *e) const
int QQmlMetaObject::methodReturnType(const QQmlPropertyData &data, QByteArray *unknownTypeError) const
{
- Q_ASSERT(!_m.isNull() && data.coreIndex >= 0);
+ Q_ASSERT(!_m.isNull() && data.coreIndex() >= 0);
- int type = data.propType;
+ int type = data.propType();
const char *propTypeName = 0;
@@ -1424,16 +1575,16 @@ int QQmlMetaObject::methodReturnType(const QQmlPropertyData &data, QByteArray *u
if (_m.isT1()) {
QQmlPropertyCache *c = _m.asT1();
- Q_ASSERT(data.coreIndex < c->methodIndexCacheStart + c->methodIndexCache.count());
+ Q_ASSERT(data.coreIndex() < c->methodIndexCacheStart + c->methodIndexCache.count());
- while (data.coreIndex < c->methodIndexCacheStart)
+ while (data.coreIndex() < c->methodIndexCacheStart)
c = c->_parent;
const QMetaObject *metaObject = c->createMetaObject();
Q_ASSERT(metaObject);
- m = metaObject->method(data.coreIndex);
+ m = metaObject->method(data.coreIndex());
} else {
- m = _m.asT2()->method(data.coreIndex);
+ m = _m.asT2()->method(data.coreIndex());
}
type = m.returnType();
@@ -1457,7 +1608,8 @@ int QQmlMetaObject::methodReturnType(const QQmlPropertyData &data, QByteArray *u
return type;
}
-int *QQmlMetaObject::methodParameterTypes(int index, QVarLengthArray<int, 9> &dummy, QByteArray *unknownTypeError) const
+int *QQmlMetaObject::methodParameterTypes(int index, ArgTypeStorage *argStorage,
+ QByteArray *unknownTypeError) const
{
Q_ASSERT(!_m.isNull() && index >= 0);
@@ -1472,19 +1624,19 @@ int *QQmlMetaObject::methodParameterTypes(int index, QVarLengthArray<int, 9> &du
QQmlPropertyData *rv = const_cast<QQmlPropertyData *>(&c->methodIndexCache.at(index - c->methodIndexCacheStart));
- if (rv->arguments && static_cast<A *>(rv->arguments)->argumentsValid)
- return static_cast<A *>(rv->arguments)->arguments;
+ if (rv->arguments() && static_cast<A *>(rv->arguments())->argumentsValid)
+ return static_cast<A *>(rv->arguments())->arguments;
const QMetaObject *metaObject = c->createMetaObject();
Q_ASSERT(metaObject);
QMetaMethod m = metaObject->method(index);
int argc = m.parameterCount();
- if (!rv->arguments) {
+ if (!rv->arguments()) {
A *args = c->createArgumentsObject(argc, m.parameterNames());
- rv->arguments = args;
+ rv->setArguments(args);
}
- A *args = static_cast<A *>(rv->arguments);
+ A *args = static_cast<A *>(rv->arguments());
QList<QByteArray> argTypeNames; // Only loaded if needed
@@ -1508,43 +1660,57 @@ int *QQmlMetaObject::methodParameterTypes(int index, QVarLengthArray<int, 9> &du
args->arguments[ii + 1] = type;
}
args->argumentsValid = true;
- return static_cast<A *>(rv->arguments)->arguments;
+ return static_cast<A *>(rv->arguments())->arguments;
} else {
QMetaMethod m = _m.asT2()->method(index);
- int argc = m.parameterCount();
- dummy.resize(argc + 1);
- dummy[0] = argc;
- QList<QByteArray> argTypeNames; // Only loaded if needed
+ return methodParameterTypes(m, argStorage, unknownTypeError);
- for (int ii = 0; ii < argc; ++ii) {
- int type = m.parameterType(ii);
- QMetaType::TypeFlags flags = QMetaType::typeFlags(type);
- if (flags & QMetaType::IsEnumeration)
- type = QVariant::Int;
- else if (type == QMetaType::UnknownType ||
- (type >= (int)QVariant::UserType && !(flags & QMetaType::PointerToQObject) &&
- type != qMetaTypeId<QJSValue>())) {
- //the UserType clause is to catch registered QFlags)
- if (argTypeNames.isEmpty())
- argTypeNames = m.parameterTypes();
- type = EnumType(_m.asT2(), argTypeNames.at(ii), type);
- }
- if (type == QMetaType::UnknownType) {
- if (unknownTypeError) *unknownTypeError = argTypeNames.at(ii);
- return 0;
- }
- dummy[ii + 1] = type;
- }
+ }
+}
- return dummy.data();
+int *QQmlMetaObject::methodParameterTypes(const QMetaMethod &m, ArgTypeStorage *argStorage,
+ QByteArray *unknownTypeError) const
+{
+ Q_ASSERT(argStorage);
+
+ int argc = m.parameterCount();
+ argStorage->resize(argc + 1);
+ argStorage->operator[](0) = argc;
+ QList<QByteArray> argTypeNames; // Only loaded if needed
+
+ for (int ii = 0; ii < argc; ++ii) {
+ int type = m.parameterType(ii);
+ QMetaType::TypeFlags flags = QMetaType::typeFlags(type);
+ if (flags & QMetaType::IsEnumeration)
+ type = QVariant::Int;
+ else if (type == QMetaType::UnknownType ||
+ (type >= (int)QVariant::UserType && !(flags & QMetaType::PointerToQObject) &&
+ type != qMetaTypeId<QJSValue>())) {
+ //the UserType clause is to catch registered QFlags)
+ if (argTypeNames.isEmpty())
+ argTypeNames = m.parameterTypes();
+ type = EnumType(_m.asT2(), argTypeNames.at(ii), type);
+ }
+ if (type == QMetaType::UnknownType) {
+ if (unknownTypeError) *unknownTypeError = argTypeNames.at(ii);
+ return 0;
+ }
+ argStorage->operator[](ii + 1) = type;
}
+
+ return argStorage->data();
}
void QQmlObjectOrGadget::metacall(QMetaObject::Call type, int index, void **argv) const
{
- if (ptr.isT1())
+ if (ptr.isNull()) {
+ const QMetaObject *metaObject = _m.asT2();
+ metaObject->d.static_metacall(0, type, index, argv);
+ }
+ else if (ptr.isT1()) {
QMetaObject::metacall(ptr.asT1(), type, index, argv);
+ }
else {
const QMetaObject *metaObject = _m.asT1()->metaObject();
QQmlMetaObject::resolveGadgetMethodOrPropertyIndex(type, &metaObject, &index);
@@ -1552,4 +1718,11 @@ void QQmlObjectOrGadget::metacall(QMetaObject::Call type, int index, void **argv
}
}
+int *QQmlStaticMetaObject::constructorParameterTypes(int index, ArgTypeStorage *dummy,
+ QByteArray *unknownTypeError) const
+{
+ QMetaMethod m = _m.asT2()->constructor(index);
+ return methodParameterTypes(m, dummy, unknownTypeError);
+}
+
QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmlpropertycache_p.h b/src/qml/qml/qqmlpropertycache_p.h
index 830b8398b5..6281b9c05e 100644
--- a/src/qml/qml/qqmlpropertycache_p.h
+++ b/src/qml/qml/qqmlpropertycache_p.h
@@ -55,6 +55,7 @@
#include <private/qflagpointer_p.h>
#include "qqmlcleanup_p.h"
#include "qqmlnotifier_p.h"
+#include <private/qqmlpropertyindex_p.h>
#include <private/qhashedstring_p.h>
#include <QtCore/qvarlengtharray.h>
@@ -62,17 +63,20 @@
#include <private/qv4value_p.h>
+#include <limits>
+
QT_BEGIN_NAMESPACE
+class QCryptographicHash;
class QMetaProperty;
class QQmlEngine;
class QJSEngine;
class QQmlPropertyData;
-class QQmlAccessors;
class QMetaObjectBuilder;
class QQmlPropertyCacheMethodArguments;
class QQmlVMEMetaObject;
-class QQmlPropertyCacheCreator;
+template <typename T> class QQmlPropertyCacheCreator;
+template <typename T> class QQmlPropertyCacheAliasCreator;
// We have this somewhat awful split between RawData and Data so that RawData can be
// used in unions. In normal code, you should always use Data which initializes RawData
@@ -82,149 +86,203 @@ class QQmlPropertyRawData
public:
typedef QObjectPrivate::StaticMetaCallFunction StaticMetaCallFunction;
- enum Flag {
- NoFlags = 0x00000000,
- ValueTypeFlagMask = 0x0000FFFF, // Flags in valueTypeFlags must fit in this mask
+ struct Flags {
+ enum Types {
+ OtherType = 0,
+ FunctionType = 1, // Is an invokable
+ QObjectDerivedType = 2, // Property type is a QObject* derived type
+ EnumType = 3, // Property type is an enum
+ QListType = 4, // Property type is a QML list
+ QmlBindingType = 5, // Property type is a QQmlBinding*
+ QJSValueType = 6, // Property type is a QScriptValue
+ V4HandleType = 7, // Property type is a QQmlV4Handle
+ VarPropertyType = 8, // Property type is a "var" property of VMEMO
+ QVariantType = 9 // Property is a QVariant
+ };
+
+ // The _otherBits (which "pad" the Flags struct to align it nicely) are used
+ // to store the relative property index. It will only get used when said index fits. See
+ // trySetStaticMetaCallFunction for details.
+ // (Note: this padding is done here, because certain compilers have surprising behavior
+ // when an enum is declared in-between two bit fields.)
+ enum { BitsLeftInFlags = 10 };
+ unsigned _otherBits : BitsLeftInFlags; // align to 32 bits
// Can apply to all properties, except IsFunction
- IsConstant = 0x00000001, // Has CONST flag
- IsWritable = 0x00000002, // Has WRITE function
- IsResettable = 0x00000004, // Has RESET function
- IsAlias = 0x00000008, // Is a QML alias to another property
- IsFinal = 0x00000010, // Has FINAL flag
- IsOverridden = 0x00000020, // Is overridden by a extension property
- IsDirect = 0x00000040, // Exists on a C++ QMetaObject
- HasAccessors = 0x00000080, // Has property accessors
-
- // These are mutualy exclusive
- IsFunction = 0x00000100, // Is an invokable
- IsQObjectDerived = 0x00000200, // Property type is a QObject* derived type
- IsEnumType = 0x00000400, // Property type is an enum
- IsQList = 0x00000800, // Property type is a QML list
- IsQmlBinding = 0x00001000, // Property type is a QQmlBinding*
- IsQJSValue = 0x00002000, // Property type is a QScriptValue
- IsV4Handle = 0x00004000, // Property type is a QQmlV4Handle
- IsVarProperty = 0x00008000, // Property type is a "var" property of VMEMO
- IsValueTypeVirtual = 0x00010000, // Property is a value type "virtual" property
- IsQVariant = 0x00020000, // Property is a QVariant
+ unsigned isConstant : 1; // Has CONST flag
+ unsigned isWritable : 1; // Has WRITE function
+ unsigned isResettable : 1; // Has RESET function
+ unsigned isAlias : 1; // Is a QML alias to another property
+ unsigned isFinal : 1; // Has FINAL flag
+ unsigned isOverridden : 1; // Is overridden by a extension property
+ unsigned isDirect : 1; // Exists on a C++ QMetaObject
+
+ unsigned type : 4; // stores an entry of Types
// Apply only to IsFunctions
- IsVMEFunction = 0x00040000, // Function was added by QML
- HasArguments = 0x00080000, // Function takes arguments
- IsSignal = 0x00100000, // Function is a signal
- IsVMESignal = 0x00200000, // Signal was added by QML
- IsV4Function = 0x00400000, // Function takes QQmlV4Function* args
- IsSignalHandler = 0x00800000, // Function is a signal handler
- IsOverload = 0x01000000, // Function is an overload of another function
- IsCloned = 0x02000000, // The function was marked as cloned
+ unsigned isVMEFunction : 1; // Function was added by QML
+ unsigned hasArguments : 1; // Function takes arguments
+ unsigned isSignal : 1; // Function is a signal
+ unsigned isVMESignal : 1; // Signal was added by QML
+ unsigned isV4Function : 1; // Function takes QQmlV4Function* args
+ unsigned isSignalHandler : 1; // Function is a signal handler
+ unsigned isOverload : 1; // Function is an overload of another function
+ unsigned isCloned : 1; // The function was marked as cloned
+ unsigned isConstructor : 1; // The function was marked is a constructor
// Internal QQmlPropertyCache flags
- NotFullyResolved = 0x04000000, // True if the type data is to be lazily resolved
+ unsigned notFullyResolved : 1; // True if the type data is to be lazily resolved
+ unsigned overrideIndexIsProperty: 1;
- // Flags that are set based on the propType field
- PropTypeFlagMask = IsQObjectDerived | IsEnumType | IsQList | IsQmlBinding | IsQJSValue |
- IsV4Handle | IsQVariant,
+ inline Flags();
+ inline bool operator==(const Flags &other) const;
+ inline void copyPropertyTypeFlags(Flags from);
};
- Q_DECLARE_FLAGS(Flags, Flag)
-
- Flags getFlags() const { return Flag(flags); }
- void setFlags(Flags f) { flags = f; }
-
- bool isValid() const { return coreIndex != -1; }
-
- bool isConstant() const { return flags & IsConstant; }
- bool isWritable() const { return flags & IsWritable; }
- bool isResettable() const { return flags & IsResettable; }
- bool isAlias() const { return flags & IsAlias; }
- bool isFinal() const { return flags & IsFinal; }
- bool isOverridden() const { return flags & IsOverridden; }
- bool isDirect() const { return flags & IsDirect; }
- bool hasAccessors() const { return flags & HasAccessors; }
- bool isFunction() const { return flags & IsFunction; }
- bool isQObject() const { return flags & IsQObjectDerived; }
- bool isEnum() const { return flags & IsEnumType; }
- bool isQList() const { return flags & IsQList; }
- bool isQmlBinding() const { return flags & IsQmlBinding; }
- bool isQJSValue() const { return flags & IsQJSValue; }
- bool isV4Handle() const { return flags & IsV4Handle; }
- bool isVarProperty() const { return flags & IsVarProperty; }
- bool isValueTypeVirtual() const { return flags & IsValueTypeVirtual; }
- bool isQVariant() const { return flags & IsQVariant; }
- bool isVMEFunction() const { return flags & IsVMEFunction; }
- bool hasArguments() const { return flags & HasArguments; }
- bool isSignal() const { return flags & IsSignal; }
- bool isVMESignal() const { return flags & IsVMESignal; }
- bool isV4Function() const { return flags & IsV4Function; }
- bool isSignalHandler() const { return flags & IsSignalHandler; }
- bool isOverload() const { return flags & IsOverload; }
- bool isCloned() const { return flags & IsCloned; }
-
- bool hasOverride() const { return !(flags & IsValueTypeVirtual) &&
- !(flags & HasAccessors) &&
- overrideIndex >= 0; }
- bool hasRevision() const { return !(flags & HasAccessors) && revision != 0; }
-
- // Returns -1 if not a value type virtual property
- inline int getValueTypeCoreIndex() const;
-
- // Returns the "encoded" index for use with bindings. Encoding is:
- // coreIndex | ((valueTypeCoreIndex + 1) << 16)
- inline int encodedIndex() const;
- static int encodeValueTypePropertyIndex(int coreIndex, int valueTypeCoreIndex)
- { return coreIndex | ((valueTypeCoreIndex + 1) << 16); }
- static int decodeValueTypePropertyIndex(int index, int *coreIndex = 0) {
- if (coreIndex) *coreIndex = index & 0xffff;
- return (index >> 16) - 1;
+
+ Flags flags() const { return _flags; }
+ void setFlags(Flags f)
+ {
+ unsigned otherBits = _flags._otherBits;
+ _flags = f;
+ _flags._otherBits = otherBits;
}
- union {
- int propType; // When !NotFullyResolved
- const char *propTypeName; // When NotFullyResolved
- };
- union {
- // The notify index is in the range returned by QObjectPrivate::signalIndex().
- // This is different from QMetaMethod::methodIndex().
- int notifyIndex; // When !IsFunction
- void *arguments; // When IsFunction && HasArguments
- };
+ bool isValid() const { return coreIndex() != -1; }
+
+ bool isConstant() const { return _flags.isConstant; }
+ bool isWritable() const { return _flags.isWritable; }
+ void setWritable(bool onoff) { _flags.isWritable = onoff; }
+ bool isResettable() const { return _flags.isResettable; }
+ bool isAlias() const { return _flags.isAlias; }
+ bool isFinal() const { return _flags.isFinal; }
+ bool isOverridden() const { return _flags.isOverridden; }
+ bool isDirect() const { return _flags.isDirect; }
+ bool hasStaticMetaCallFunction() const { return staticMetaCallFunction() != nullptr; }
+ bool isFunction() const { return _flags.type == Flags::FunctionType; }
+ bool isQObject() const { return _flags.type == Flags::QObjectDerivedType; }
+ bool isEnum() const { return _flags.type == Flags::EnumType; }
+ bool isQList() const { return _flags.type == Flags::QListType; }
+ bool isQmlBinding() const { return _flags.type == Flags::QmlBindingType; }
+ bool isQJSValue() const { return _flags.type == Flags::QJSValueType; }
+ bool isV4Handle() const { return _flags.type == Flags::V4HandleType; }
+ bool isVarProperty() const { return _flags.type == Flags::VarPropertyType; }
+ bool isQVariant() const { return _flags.type == Flags::QVariantType; }
+ bool isVMEFunction() const { return _flags.isVMEFunction; }
+ bool hasArguments() const { return _flags.hasArguments; }
+ bool isSignal() const { return _flags.isSignal; }
+ bool isVMESignal() const { return _flags.isVMESignal; }
+ bool isV4Function() const { return _flags.isV4Function; }
+ bool isSignalHandler() const { return _flags.isSignalHandler; }
+ bool isOverload() const { return _flags.isOverload; }
+ void setOverload(bool onoff) { _flags.isOverload = onoff; }
+ bool isCloned() const { return _flags.isCloned; }
+ bool isConstructor() const { return _flags.isConstructor; }
+
+ bool hasOverride() const { return overrideIndex() >= 0; }
+ bool hasRevision() const { return revision() != 0; }
+
+ bool isFullyResolved() const { return !_flags.notFullyResolved; }
+
+ int propType() const { Q_ASSERT(isFullyResolved()); return _propType; }
+ void setPropType(int pt)
+ {
+ Q_ASSERT(pt >= 0);
+ Q_ASSERT(pt <= std::numeric_limits<qint16>::max());
+ _propType = quint16(pt);
+ }
- union {
- struct { // When !HasAccessors
- qint16 revision;
- qint16 metaObjectOffset;
-
- union {
- struct { // When IsValueTypeVirtual
- quint16 valueTypeFlags; // flags of the access property on the value type proxy
- // object
- quint16 valueTypePropType; // The QVariant::Type of access property on the value
- // type proxy object
- quint16 valueTypeCoreIndex; // The prop index of the access property on the value
- // type proxy object
- };
-
- struct { // When !IsValueTypeVirtual
- uint overrideIndexIsProperty : 1;
- signed int overrideIndex : 31;
- };
- };
- };
- struct { // When HasAccessors
- QQmlAccessors *accessors;
- };
- };
- int coreIndex;
+ int notifyIndex() const { return _notifyIndex; }
+ void setNotifyIndex(int idx)
+ {
+ Q_ASSERT(idx >= std::numeric_limits<qint16>::min());
+ Q_ASSERT(idx <= std::numeric_limits<qint16>::max());
+ _notifyIndex = qint16(idx);
+ }
+
+ bool overrideIndexIsProperty() const { return _flags.overrideIndexIsProperty; }
+ void setOverrideIndexIsProperty(bool onoff) { _flags.overrideIndexIsProperty = onoff; }
+
+ int overrideIndex() const { return _overrideIndex; }
+ void setOverrideIndex(int idx)
+ {
+ Q_ASSERT(idx >= std::numeric_limits<qint16>::min());
+ Q_ASSERT(idx <= std::numeric_limits<qint16>::max());
+ _overrideIndex = qint16(idx);
+ }
+
+ int coreIndex() const { return _coreIndex; }
+ void setCoreIndex(int idx)
+ {
+ Q_ASSERT(idx >= std::numeric_limits<qint16>::min());
+ Q_ASSERT(idx <= std::numeric_limits<qint16>::max());
+ _coreIndex = qint16(idx);
+ }
+
+ int revision() const { return _revision; }
+ void setRevision(int rev)
+ {
+ Q_ASSERT(rev >= std::numeric_limits<qint16>::min());
+ Q_ASSERT(rev <= std::numeric_limits<qint16>::max());
+ _revision = qint16(rev);
+ }
+
+ QQmlPropertyCacheMethodArguments *arguments() const { return _arguments; }
+ void setArguments(QQmlPropertyCacheMethodArguments *args) { _arguments = args; }
+
+ int metaObjectOffset() const { return _metaObjectOffset; }
+ void setMetaObjectOffset(int off)
+ {
+ Q_ASSERT(off >= std::numeric_limits<qint16>::min());
+ Q_ASSERT(off <= std::numeric_limits<qint16>::max());
+ _metaObjectOffset = qint16(off);
+ }
+
+ StaticMetaCallFunction staticMetaCallFunction() const { return _staticMetaCallFunction; }
+ void trySetStaticMetaCallFunction(StaticMetaCallFunction f, unsigned relativePropertyIndex)
+ {
+ if (relativePropertyIndex < (1 << Flags::BitsLeftInFlags) - 1) {
+ _flags._otherBits = relativePropertyIndex;
+ _staticMetaCallFunction = f;
+ }
+ }
+ quint16 relativePropertyIndex() const { Q_ASSERT(hasStaticMetaCallFunction()); return _flags._otherBits; }
private:
+ Flags _flags;
+ qint16 _coreIndex;
+ quint16 _propType;
+
+ // The notify index is in the range returned by QObjectPrivate::signalIndex().
+ // This is different from QMetaMethod::methodIndex().
+ qint16 _notifyIndex;
+ qint16 _overrideIndex;
+
+ qint16 _revision;
+ qint16 _metaObjectOffset;
+
+ QQmlPropertyCacheMethodArguments *_arguments;
+ StaticMetaCallFunction _staticMetaCallFunction;
+
friend class QQmlPropertyData;
friend class QQmlPropertyCache;
- quint32 flags;
};
-Q_DECLARE_OPERATORS_FOR_FLAGS(QQmlPropertyRawData::Flags)
+
+#if QT_POINTER_SIZE == 4
+Q_STATIC_ASSERT(sizeof(QQmlPropertyRawData) == 24);
+#else // QT_POINTER_SIZE == 8
+Q_STATIC_ASSERT(sizeof(QQmlPropertyRawData) == 32);
+#endif
class QQmlPropertyData : public QQmlPropertyRawData
{
public:
+ enum WriteFlag {
+ BypassInterceptor = 0x01,
+ DontRemoveBinding = 0x02,
+ RemoveBindingOnAliasWrite = 0x04
+ };
+ Q_DECLARE_FLAGS(WriteFlags, WriteFlag)
+
inline QQmlPropertyData();
inline QQmlPropertyData(const QQmlPropertyRawData &);
@@ -238,11 +296,57 @@ public:
void markAsOverrideOf(QQmlPropertyData *predecessor);
+ inline void readProperty(QObject *target, void *property) const
+ {
+ void *args[] = { property, 0 };
+ readPropertyWithArgs(target, args);
+ }
+
+ inline void readPropertyWithArgs(QObject *target, void *args[]) const
+ {
+ if (hasStaticMetaCallFunction())
+ staticMetaCallFunction()(target, QMetaObject::ReadProperty, relativePropertyIndex(), args);
+ else if (isDirect())
+ target->qt_metacall(QMetaObject::ReadProperty, coreIndex(), args);
+ else
+ QMetaObject::metacall(target, QMetaObject::ReadProperty, coreIndex(), args);
+ }
+
+ bool writeProperty(QObject *target, void *value, WriteFlags flags) const
+ {
+ int status = -1;
+ void *argv[] = { value, 0, &status, &flags };
+ if (flags.testFlag(BypassInterceptor) && hasStaticMetaCallFunction())
+ staticMetaCallFunction()(target, QMetaObject::WriteProperty, relativePropertyIndex(), argv);
+ else if (flags.testFlag(BypassInterceptor) && isDirect())
+ target->qt_metacall(QMetaObject::WriteProperty, coreIndex(), argv);
+ else
+ QMetaObject::metacall(target, QMetaObject::WriteProperty, coreIndex(), argv);
+ return true;
+ }
+
+ static Flags defaultSignalFlags()
+ {
+ Flags f;
+ f.isSignal = true;
+ f.type = Flags::FunctionType;
+ f.isVMESignal = true;
+ return f;
+ }
+
+ static Flags defaultSlotFlags()
+ {
+ Flags f;
+ f.type = Flags::FunctionType;
+ f.isVMEFunction = true;
+ return f;
+ }
+
private:
friend class QQmlPropertyCache;
void lazyLoad(const QMetaProperty &);
void lazyLoad(const QMetaMethod &);
- bool notFullyResolved() const { return flags & NotFullyResolved; }
+ bool notFullyResolved() const { return _flags.notFullyResolved; }
};
class QQmlPropertyCacheMethodArguments;
@@ -261,21 +365,21 @@ public:
QQmlPropertyCache *copy();
QQmlPropertyCache *copyAndAppend(const QMetaObject *,
- QQmlPropertyData::Flag propertyFlags = QQmlPropertyData::NoFlags,
- QQmlPropertyData::Flag methodFlags = QQmlPropertyData::NoFlags,
- QQmlPropertyData::Flag signalFlags = QQmlPropertyData::NoFlags);
+ QQmlPropertyRawData::Flags propertyFlags = QQmlPropertyData::Flags(),
+ QQmlPropertyRawData::Flags methodFlags = QQmlPropertyData::Flags(),
+ QQmlPropertyRawData::Flags signalFlags = QQmlPropertyData::Flags());
QQmlPropertyCache *copyAndAppend(const QMetaObject *, int revision,
- QQmlPropertyData::Flag propertyFlags = QQmlPropertyData::NoFlags,
- QQmlPropertyData::Flag methodFlags = QQmlPropertyData::NoFlags,
- QQmlPropertyData::Flag signalFlags = QQmlPropertyData::NoFlags);
+ QQmlPropertyRawData::Flags propertyFlags = QQmlPropertyData::Flags(),
+ QQmlPropertyRawData::Flags methodFlags = QQmlPropertyData::Flags(),
+ QQmlPropertyRawData::Flags signalFlags = QQmlPropertyData::Flags());
QQmlPropertyCache *copyAndReserve(int propertyCount,
int methodCount, int signalCount);
- void appendProperty(const QString &,
- quint32 flags, int coreIndex, int propType, int notifyIndex);
- void appendSignal(const QString &, quint32, int coreIndex, const int *types = 0,
- const QList<QByteArray> &names = QList<QByteArray>());
- void appendMethod(const QString &, quint32 flags, int coreIndex,
+ void appendProperty(const QString &, QQmlPropertyRawData::Flags flags, int coreIndex,
+ int propType, int notifyIndex);
+ void appendSignal(const QString &, QQmlPropertyRawData::Flags, int coreIndex,
+ const int *types = 0, const QList<QByteArray> &names = QList<QByteArray>());
+ void appendMethod(const QString &, QQmlPropertyData::Flags flags, int coreIndex,
const QList<QByteArray> &names = QList<QByteArray>());
const QMetaObject *metaObject() const;
@@ -292,7 +396,6 @@ public:
QQmlPropertyData *method(int) const;
QQmlPropertyData *signal(int index) const;
int methodIndexToSignalIndex(int) const;
- QStringList propertyNames() const;
QString defaultPropertyName() const;
QQmlPropertyData *defaultProperty() const;
@@ -330,6 +433,11 @@ public:
inline bool callJSFactoryMethod(QObject *object, void **args) const;
+ static bool determineMetaObjectSizes(const QMetaObject &mo, int *fieldCount, int *stringCount);
+ static bool addToHash(QCryptographicHash &hash, const QMetaObject &mo);
+
+ QByteArray checksum(bool *ok);
+
protected:
virtual void destroy();
virtual void clear();
@@ -337,16 +445,17 @@ protected:
private:
friend class QQmlEnginePrivate;
friend class QQmlCompiler;
- friend class QQmlPropertyCacheCreator;
+ template <typename T> friend class QQmlPropertyCacheCreator;
+ template <typename T> friend class QQmlPropertyCacheAliasCreator;
friend class QQmlComponentAndAliasResolver;
friend class QQmlMetaObject;
inline QQmlPropertyCache *copy(int reserve);
void append(const QMetaObject *, int revision,
- QQmlPropertyData::Flag propertyFlags = QQmlPropertyData::NoFlags,
- QQmlPropertyData::Flag methodFlags = QQmlPropertyData::NoFlags,
- QQmlPropertyData::Flag signalFlags = QQmlPropertyData::NoFlags);
+ QQmlPropertyRawData::Flags propertyFlags = QQmlPropertyRawData::Flags(),
+ QQmlPropertyRawData::Flags methodFlags = QQmlPropertyData::Flags(),
+ QQmlPropertyRawData::Flags signalFlags = QQmlPropertyData::Flags());
QQmlPropertyCacheMethodArguments *createArgumentsObject(int count, const QList<QByteArray> &names);
@@ -359,7 +468,7 @@ private:
QQmlPropertyData *ensureResolved(QQmlPropertyData*) const;
- void resolve(QQmlPropertyData *) const;
+ Q_NEVER_INLINE void resolve(QQmlPropertyData *) const;
void updateRecur(const QMetaObject *);
template<typename K>
@@ -399,6 +508,7 @@ private:
QString _defaultPropertyName;
QQmlPropertyCacheMethodArguments *argumentsCache;
int _jsFactoryMethodIndex;
+ QByteArray _checksum;
};
// QQmlMetaObject serves as a wrapper around either QMetaObject or QQmlPropertyCache.
@@ -411,6 +521,8 @@ class QQmlEnginePrivate;
class Q_QML_EXPORT QQmlMetaObject
{
public:
+ typedef QVarLengthArray<int, 9> ArgTypeStorage;
+
inline QQmlMetaObject();
inline QQmlMetaObject(QObject *);
inline QQmlMetaObject(const QMetaObject *);
@@ -430,7 +542,8 @@ public:
QQmlPropertyCache *propertyCache(QQmlEnginePrivate *) const;
int methodReturnType(const QQmlPropertyData &data, QByteArray *unknownTypeError) const;
- int *methodParameterTypes(int index, QVarLengthArray<int, 9> &dummy, QByteArray *unknownTypeError) const;
+ int *methodParameterTypes(int index, ArgTypeStorage *argStorage,
+ QByteArray *unknownTypeError) const;
static bool canConvert(const QQmlMetaObject &from, const QQmlMetaObject &to);
@@ -440,6 +553,9 @@ public:
protected:
QBiPointer<QQmlPropertyCache, const QMetaObject> _m;
+ int *methodParameterTypes(const QMetaMethod &method, ArgTypeStorage *argStorage,
+ QByteArray *unknownTypeError) const;
+
};
class QQmlObjectOrGadget: public QQmlMetaObject
@@ -458,18 +574,91 @@ public:
private:
QBiPointer<QObject, void> ptr;
+
+protected:
+ QQmlObjectOrGadget(const QMetaObject* metaObject)
+ : QQmlMetaObject(metaObject)
+ {}
+
+};
+
+class QQmlStaticMetaObject : public QQmlObjectOrGadget {
+public:
+ QQmlStaticMetaObject(const QMetaObject* metaObject)
+ : QQmlObjectOrGadget(metaObject)
+ {}
+ int *constructorParameterTypes(int index, ArgTypeStorage *dummy, QByteArray *unknownTypeError) const;
};
+QQmlPropertyRawData::Flags::Flags()
+ : _otherBits(0)
+ , isConstant(false)
+ , isWritable(false)
+ , isResettable(false)
+ , isAlias(false)
+ , isFinal(false)
+ , isOverridden(false)
+ , isDirect(false)
+ , type(OtherType)
+ , isVMEFunction(false)
+ , hasArguments(false)
+ , isSignal(false)
+ , isVMESignal(false)
+ , isV4Function(false)
+ , isSignalHandler(false)
+ , isOverload(false)
+ , isCloned(false)
+ , isConstructor(false)
+ , notFullyResolved(false)
+ , overrideIndexIsProperty(false)
+{}
+
+bool QQmlPropertyRawData::Flags::operator==(const QQmlPropertyRawData::Flags &other) const
+{
+ return isConstant == other.isConstant &&
+ isWritable == other.isWritable &&
+ isResettable == other.isResettable &&
+ isAlias == other.isAlias &&
+ isFinal == other.isFinal &&
+ isOverridden == other.isOverridden &&
+ type == other.type &&
+ isVMEFunction == other.isVMEFunction &&
+ hasArguments == other.hasArguments &&
+ isSignal == other.isSignal &&
+ isVMESignal == other.isVMESignal &&
+ isV4Function == other.isV4Function &&
+ isSignalHandler == other.isSignalHandler &&
+ isOverload == other.isOverload &&
+ isCloned == other.isCloned &&
+ isConstructor == other.isConstructor &&
+ notFullyResolved == other.notFullyResolved &&
+ overrideIndexIsProperty == other.overrideIndexIsProperty;
+}
+
+void QQmlPropertyRawData::Flags::copyPropertyTypeFlags(QQmlPropertyRawData::Flags from)
+{
+ switch (from.type) {
+ case QObjectDerivedType:
+ case EnumType:
+ case QListType:
+ case QmlBindingType:
+ case QJSValueType:
+ case V4HandleType:
+ case QVariantType:
+ type = from.type;
+ }
+}
+
QQmlPropertyData::QQmlPropertyData()
{
- propType = 0;
- coreIndex = -1;
- notifyIndex = -1;
- overrideIndexIsProperty = false;
- overrideIndex = -1;
- revision = 0;
- metaObjectOffset = -1;
- flags = 0;
+ setCoreIndex(-1);
+ setPropType(0);
+ setNotifyIndex(-1);
+ setOverrideIndex(-1);
+ setRevision(0);
+ setMetaObjectOffset(-1);
+ setArguments(nullptr);
+ trySetStaticMetaCallFunction(nullptr, 0);
}
QQmlPropertyData::QQmlPropertyData(const QQmlPropertyRawData &d)
@@ -479,32 +668,34 @@ QQmlPropertyData::QQmlPropertyData(const QQmlPropertyRawData &d)
bool QQmlPropertyData::operator==(const QQmlPropertyRawData &other)
{
- return flags == other.flags &&
- propType == other.propType &&
- coreIndex == other.coreIndex &&
- notifyIndex == other.notifyIndex &&
- revision == other.revision &&
- (!isValueTypeVirtual() ||
- (valueTypeCoreIndex == other.valueTypeCoreIndex &&
- valueTypePropType == other.valueTypePropType));
+ return flags() == other.flags() &&
+ propType() == other.propType() &&
+ coreIndex() == other.coreIndex() &&
+ notifyIndex() == other.notifyIndex() &&
+ revision() == other.revision();
}
-int QQmlPropertyRawData::getValueTypeCoreIndex() const
+inline QQmlPropertyData *QQmlPropertyCache::ensureResolved(QQmlPropertyData *p) const
{
- return isValueTypeVirtual()?valueTypeCoreIndex:-1;
+ if (p && Q_UNLIKELY(p->notFullyResolved()))
+ resolve(p);
+
+ return p;
}
-int QQmlPropertyRawData::encodedIndex() const
+// Returns this property cache's metaObject. May be null if it hasn't been created yet.
+inline const QMetaObject *QQmlPropertyCache::metaObject() const
{
- return isValueTypeVirtual()?QQmlPropertyData::encodeValueTypePropertyIndex(coreIndex, valueTypeCoreIndex):coreIndex;
+ return _metaObject;
}
-inline QQmlPropertyData *QQmlPropertyCache::ensureResolved(QQmlPropertyData *p) const
+// Returns the first C++ type's QMetaObject - that is, the first QMetaObject not created by
+// QML
+inline const QMetaObject *QQmlPropertyCache::firstCppMetaObject() const
{
- if (p && p->notFullyResolved())
- resolve(p);
-
- return p;
+ while (_parent && (_metaObject == 0 || _ownMetaObject))
+ return _parent->firstCppMetaObject();
+ return _metaObject;
}
inline QQmlPropertyData *QQmlPropertyCache::property(int index) const
@@ -519,22 +710,73 @@ inline QQmlPropertyData *QQmlPropertyCache::property(int index) const
return ensureResolved(rv);
}
+inline QQmlPropertyData *QQmlPropertyCache::method(int index) const
+{
+ if (index < 0 || index >= (methodIndexCacheStart + methodIndexCache.count()))
+ return 0;
+
+ if (index < methodIndexCacheStart)
+ return _parent->method(index);
+
+ QQmlPropertyData *rv = const_cast<QQmlPropertyData *>(&methodIndexCache.at(index - methodIndexCacheStart));
+ return ensureResolved(rv);
+}
+
+/*! \internal
+ \a index MUST be in the signal index range (see QObjectPrivate::signalIndex()).
+ This is different from QMetaMethod::methodIndex().
+*/
+inline QQmlPropertyData *QQmlPropertyCache::signal(int index) const
+{
+ if (index < 0 || index >= (signalHandlerIndexCacheStart + signalHandlerIndexCache.count()))
+ return 0;
+
+ if (index < signalHandlerIndexCacheStart)
+ return _parent->signal(index);
+
+ QQmlPropertyData *rv = const_cast<QQmlPropertyData *>(&methodIndexCache.at(index - signalHandlerIndexCacheStart));
+ Q_ASSERT(rv->isSignal() || rv->coreIndex() == -1);
+ return ensureResolved(rv);
+}
+
+inline int QQmlPropertyCache::methodIndexToSignalIndex(int index) const
+{
+ if (index < 0 || index >= (methodIndexCacheStart + methodIndexCache.count()))
+ return index;
+
+ if (index < methodIndexCacheStart)
+ return _parent->methodIndexToSignalIndex(index);
+
+ return index - methodIndexCacheStart + signalHandlerIndexCacheStart;
+}
+
+// Returns the name of the default property for this cache
+inline QString QQmlPropertyCache::defaultPropertyName() const
+{
+ return _defaultPropertyName;
+}
+
+inline QQmlPropertyCache *QQmlPropertyCache::parent() const
+{
+ return _parent;
+}
+
QQmlPropertyData *
QQmlPropertyCache::overrideData(QQmlPropertyData *data) const
{
if (!data->hasOverride())
return 0;
- if (data->overrideIndexIsProperty)
- return property(data->overrideIndex);
+ if (data->overrideIndexIsProperty())
+ return property(data->overrideIndex());
else
- return method(data->overrideIndex);
+ return method(data->overrideIndex());
}
bool QQmlPropertyCache::isAllowedInRevision(QQmlPropertyData *data) const
{
- return (data->hasAccessors() || (data->metaObjectOffset == -1 && data->revision == 0)) ||
- (allowedRevisionCache[data->metaObjectOffset] >= data->revision);
+ return (data->metaObjectOffset() == -1 && data->revision() == 0) ||
+ (allowedRevisionCache[data->metaObjectOffset()] >= data->revision());
}
int QQmlPropertyCache::propertyCount() const
@@ -651,6 +893,51 @@ const QMetaObject *QQmlMetaObject::metaObject() const
else return _m.asT2();
}
+class QQmlPropertyCacheVector
+{
+public:
+ QQmlPropertyCacheVector() {}
+ QQmlPropertyCacheVector(QQmlPropertyCacheVector &&other)
+ : data(std::move(other.data)) {}
+ QQmlPropertyCacheVector &operator=(QQmlPropertyCacheVector &&other) {
+ QVector<QFlagPointer<QQmlPropertyCache>> moved(std::move(other.data));
+ data.swap(moved);
+ return *this;
+ }
+
+ ~QQmlPropertyCacheVector() { clear(); }
+ void resize(int size) { return data.resize(size); }
+ int count() const { return data.count(); }
+ void clear()
+ {
+ for (int i = 0; i < data.count(); ++i) {
+ if (QQmlPropertyCache *cache = data.at(i).data())
+ cache->release();
+ }
+ data.clear();
+ }
+
+ void append(QQmlPropertyCache *cache) { cache->addref(); data.append(cache); }
+ QQmlPropertyCache *at(int index) const { return data.at(index).data(); }
+ void set(int index, QQmlPropertyCache *replacement) {
+ if (QQmlPropertyCache *oldCache = data.at(index).data()) {
+ if (replacement == oldCache)
+ return;
+ oldCache->release();
+ }
+ data[index] = replacement;
+ replacement->addref();
+ }
+
+ void setNeedsVMEMetaObject(int index) { data[index].setFlag(); }
+ bool needsVMEMetaObject(int index) const { return data.at(index).flag(); }
+private:
+ Q_DISABLE_COPY(QQmlPropertyCacheVector)
+ QVector<QFlagPointer<QQmlPropertyCache>> data;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QQmlPropertyData::WriteFlags)
+
QT_END_NAMESPACE
#endif // QQMLPROPERTYCACHE_P_H
diff --git a/src/qml/qml/qqmlpropertyindex_p.h b/src/qml/qml/qqmlpropertyindex_p.h
new file mode 100644
index 0000000000..ebc1828efb
--- /dev/null
+++ b/src/qml/qml/qqmlpropertyindex_p.h
@@ -0,0 +1,133 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml 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 QQMLPROPERTYINDEX_P_H
+#define QQMLPROPERTYINDEX_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/qglobal_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQmlPropertyIndex
+{
+ qint32 index;
+
+public:
+ QQmlPropertyIndex()
+ { index = -1; }
+
+ static QQmlPropertyIndex fromEncoded(qint32 encodedIndex)
+ {
+ QQmlPropertyIndex idx;
+ idx.index = encodedIndex;
+ return idx;
+ }
+
+ explicit QQmlPropertyIndex(int coreIndex)
+ { index = encode(coreIndex, -1); }
+
+ explicit QQmlPropertyIndex(int coreIndex, int valueTypeIndex)
+ : index(encode(coreIndex, valueTypeIndex))
+ {}
+
+ bool isValid() const
+ { return index != -1; }
+
+ int coreIndex() const
+ {
+ if (index == -1)
+ return -1;
+ return index & 0xffff;
+ }
+
+ int valueTypeIndex() const
+ {
+ if (index == -1)
+ return -1;
+ return (index >> 16) - 1;
+ }
+
+ bool hasValueTypeIndex() const
+ {
+ if (index == -1)
+ return false;
+ return index >> 16;
+ }
+
+ qint32 toEncoded() const
+ { return index; }
+
+ int intValue() const
+ { return index; }
+
+ bool operator==(const QQmlPropertyIndex &other) const
+ { return index == other.index; }
+
+ bool operator!=(const QQmlPropertyIndex &other) const
+ { return !operator==(other); }
+
+private:
+ static qint32 encode(int coreIndex, int valueTypeIndex)
+ {
+ Q_ASSERT(coreIndex >= -1);
+ Q_ASSERT(coreIndex <= 0xffff);
+ Q_ASSERT(valueTypeIndex >= -1);
+ Q_ASSERT(valueTypeIndex < 0xffff);
+
+ if (coreIndex == -1)
+ return -1;
+ else
+ return coreIndex | ((valueTypeIndex + 1) << 16);
+ }
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLPROPERTYINDEX_P_H
diff --git a/src/qml/qml/qqmlpropertyvalueinterceptor_p.h b/src/qml/qml/qqmlpropertyvalueinterceptor_p.h
index 0c10d13aea..a3d6b0c8c7 100644
--- a/src/qml/qml/qqmlpropertyvalueinterceptor_p.h
+++ b/src/qml/qml/qqmlpropertyvalueinterceptor_p.h
@@ -52,6 +52,7 @@
//
#include <private/qtqmlglobal_p.h>
+#include <private/qqmlpropertyindex_p.h>
#include <QtCore/qobject.h>
QT_BEGIN_NAMESPACE
@@ -68,8 +69,7 @@ public:
private:
friend class QQmlInterceptorMetaObject;
- int m_coreIndex;
- int m_valueTypeCoreIndex;
+ QQmlPropertyIndex m_propertyIndex;
QQmlPropertyValueInterceptor *m_next;
};
diff --git a/src/qml/qml/qqmlproxymetaobject.cpp b/src/qml/qml/qqmlproxymetaobject.cpp
index bbaab1e155..27e3c13ff8 100644
--- a/src/qml/qml/qqmlproxymetaobject.cpp
+++ b/src/qml/qml/qqmlproxymetaobject.cpp
@@ -45,7 +45,7 @@ QT_BEGIN_NAMESPACE
QQmlProxyMetaObject::QQmlProxyMetaObject(QObject *obj, QList<ProxyData> *mList)
: metaObjects(mList), proxies(0), parent(0), object(obj)
{
- *static_cast<QMetaObject *>(this) = *metaObjects->first().metaObject;
+ *static_cast<QMetaObject *>(this) = *metaObjects->constFirst().metaObject;
QObjectPrivate *op = QObjectPrivate::get(obj);
if (op->metaObject)
@@ -71,7 +71,7 @@ int QQmlProxyMetaObject::metaCall(QObject *o, QMetaObject::Call c, int id, void
if ((c == QMetaObject::ReadProperty ||
c == QMetaObject::WriteProperty) &&
- id >= metaObjects->last().propertyOffset) {
+ id >= metaObjects->constLast().propertyOffset) {
for (int ii = 0; ii < metaObjects->count(); ++ii) {
const ProxyData &data = metaObjects->at(ii);
@@ -107,7 +107,7 @@ int QQmlProxyMetaObject::metaCall(QObject *o, QMetaObject::Call c, int id, void
}
}
} else if (c == QMetaObject::InvokeMetaMethod &&
- id >= metaObjects->last().methodOffset) {
+ id >= metaObjects->constLast().methodOffset) {
QMetaMethod m = object->metaObject()->method(id);
if (m.methodType() == QMetaMethod::Signal) {
QMetaObject::activate(object, id, a);
diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp
index f2f5cffbf8..09b9dcf452 100644
--- a/src/qml/qml/qqmltypeloader.cpp
+++ b/src/qml/qml/qqmltypeloader.cpp
@@ -45,14 +45,17 @@
#include <private/qqmlengine_p.h>
#include <private/qqmlglobal_p.h>
#include <private/qqmlthread_p.h>
-#include <private/qqmlcompiler_p.h>
#include <private/qqmlcomponent_p.h>
#include <private/qqmlprofiler_p.h>
#include <private/qqmlmemoryprofiler_p.h>
#include <private/qqmltypecompiler_p.h>
+#include <private/qqmlpropertyvalidator_p.h>
+#include <private/qqmlpropertycachecreator_p.h>
+#include <private/qdeferredcleanup_p.h>
#include <QtCore/qdir.h>
#include <QtCore/qfile.h>
+#include <QtCore/qdatetime.h>
#include <QtCore/qdebug.h>
#include <QtCore/qmutex.h>
#include <QtCore/qthread.h>
@@ -60,8 +63,11 @@
#include <QtCore/qdiriterator.h>
#include <QtQml/qqmlcomponent.h>
#include <QtCore/qwaitcondition.h>
+#include <QtCore/qloggingcategory.h>
#include <QtQml/qqmlextensioninterface.h>
+#include <functional>
+
#if defined (Q_OS_UNIX)
#include <sys/types.h>
#include <sys/stat.h>
@@ -98,6 +104,11 @@
#endif
DEFINE_BOOL_CONFIG_OPTION(dumpErrors, QML_DUMP_ERRORS);
+DEFINE_BOOL_CONFIG_OPTION(disableDiskCache, QML_DISABLE_DISK_CACHE);
+DEFINE_BOOL_CONFIG_OPTION(forceDiskCache, QML_FORCE_DISK_CACHE);
+
+Q_DECLARE_LOGGING_CATEGORY(DBG_DISK_CACHE)
+Q_LOGGING_CATEGORY(DBG_DISK_CACHE, "qt.qml.diskcache")
QT_BEGIN_NAMESPACE
@@ -112,6 +123,7 @@ namespace {
};
}
+#if QT_CONFIG(qml_network)
// This is a lame object that we need to ensure that slots connected to
// QNetworkReply get called in the correct thread (the loader thread).
// As QQmlTypeLoader lives in the main thread, and we can't use
@@ -131,6 +143,7 @@ public slots:
private:
QQmlTypeLoader *l;
};
+#endif // qml_network
class QQmlTypeLoaderThread : public QQmlThread
{
@@ -138,9 +151,10 @@ class QQmlTypeLoaderThread : public QQmlThread
public:
QQmlTypeLoaderThread(QQmlTypeLoader *loader);
+#if QT_CONFIG(qml_network)
QNetworkAccessManager *networkAccessManager() const;
QQmlTypeLoaderNetworkReplyProxy *networkReplyProxy() const;
-
+#endif // qml_network
void load(QQmlDataBlob *b);
void loadAsync(QQmlDataBlob *b);
void loadWithStaticData(QQmlDataBlob *b, const QByteArray &);
@@ -163,11 +177,13 @@ private:
void initializeEngineMain(QQmlExtensionInterface *iface, const char *uri);
QQmlTypeLoader *m_loader;
+#if QT_CONFIG(qml_network)
mutable QNetworkAccessManager *m_networkAccessManager;
mutable QQmlTypeLoaderNetworkReplyProxy *m_networkReplyProxy;
+#endif // qml_network
};
-
+#if QT_CONFIG(qml_network)
QQmlTypeLoaderNetworkReplyProxy::QQmlTypeLoaderNetworkReplyProxy(QQmlTypeLoader *l)
: l(l)
{
@@ -196,7 +212,7 @@ void QQmlTypeLoaderNetworkReplyProxy::manualFinished(QNetworkReply *reply)
l->networkReplyProgress(reply, replySize, replySize);
l->networkReplyFinished(reply);
}
-
+#endif // qml_network
/*!
\class QQmlDataBlob
@@ -430,6 +446,39 @@ void QQmlDataBlob::setError(const QList<QQmlError> &errors)
tryDone();
}
+void QQmlDataBlob::setError(const QQmlCompileError &error)
+{
+ QQmlError e;
+ e.setColumn(error.location.column);
+ e.setLine(error.location.line);
+ e.setDescription(error.description);
+ e.setUrl(url());
+ setError(e);
+}
+
+void QQmlDataBlob::setError(const QVector<QQmlCompileError> &errors)
+{
+ QList<QQmlError> finalErrors;
+ finalErrors.reserve(errors.count());
+ for (const QQmlCompileError &error: errors) {
+ QQmlError e;
+ e.setColumn(error.location.column);
+ e.setLine(error.location.line);
+ e.setDescription(error.description);
+ e.setUrl(url());
+ finalErrors << e;
+ }
+ setError(finalErrors);
+}
+
+void QQmlDataBlob::setError(const QString &description)
+{
+ QQmlError e;
+ e.setDescription(description);
+ e.setUrl(finalUrl());
+ setError(e);
+}
+
/*!
Wait for \a blob to become complete or to error. If \a blob is already
complete or in error, or this blob is already complete, this has no effect.
@@ -480,6 +529,7 @@ void QQmlDataBlob::done()
{
}
+#if QT_CONFIG(qml_network)
/*!
Invoked if there is a network error while fetching this blob.
@@ -532,6 +582,7 @@ void QQmlDataBlob::networkError(QNetworkReply::NetworkError networkError)
setError(error);
}
+#endif // qml_network
/*!
Called if \a blob, which was previously waited for, has an error.
@@ -730,12 +781,16 @@ void QQmlDataBlob::ThreadData::setProgress(quint8 v)
}
QQmlTypeLoaderThread::QQmlTypeLoaderThread(QQmlTypeLoader *loader)
-: m_loader(loader), m_networkAccessManager(0), m_networkReplyProxy(0)
+: m_loader(loader)
+#if QT_CONFIG(qml_network)
+, m_networkAccessManager(0), m_networkReplyProxy(0)
+#endif // qml_network
{
// Do that after initializing all the members.
startup();
}
+#if QT_CONFIG(qml_network)
QNetworkAccessManager *QQmlTypeLoaderThread::networkAccessManager() const
{
Q_ASSERT(isThisThread());
@@ -753,6 +808,7 @@ QQmlTypeLoaderNetworkReplyProxy *QQmlTypeLoaderThread::networkReplyProxy() const
Q_ASSERT(m_networkReplyProxy); // Must call networkAccessManager() first
return m_networkReplyProxy;
}
+#endif // qml_network
void QQmlTypeLoaderThread::load(QQmlDataBlob *b)
{
@@ -810,10 +866,12 @@ void QQmlTypeLoaderThread::initializeEngine(QQmlExtensionInterface *iface,
void QQmlTypeLoaderThread::shutdownThread()
{
+#if QT_CONFIG(qml_network)
delete m_networkAccessManager;
m_networkAccessManager = 0;
delete m_networkReplyProxy;
m_networkReplyProxy = 0;
+#endif // qml_network
}
void QQmlTypeLoaderThread::loadThread(QQmlDataBlob *b)
@@ -899,12 +957,14 @@ void QQmlTypeLoader::invalidate()
m_thread = 0;
}
+#if QT_CONFIG(qml_network)
// Need to delete the network replies after
// the loader thread is shutdown as it could be
// getting new replies while we clear them
for (NetworkReplies::Iterator iter = m_networkReplies.begin(); iter != m_networkReplies.end(); ++iter)
(*iter)->release();
m_networkReplies.clear();
+#endif // qml_network
}
void QQmlTypeLoader::lock()
@@ -1065,13 +1125,9 @@ void QQmlTypeLoader::loadThread(QQmlDataBlob *blob)
QML_MEMORY_SCOPE_URL(blob->m_url);
if (QQmlFile::isSynchronous(blob->m_url)) {
- QQmlFile file(m_engine, blob->m_url);
-
- if (file.isError()) {
- QQmlError error;
- error.setUrl(blob->m_url);
- error.setDescription(file.error());
- blob->setError(error);
+ const QString fileName = QQmlFile::urlToLocalFileOrQrc(blob->m_url);
+ if (!QQml_isFileCaseCorrect(fileName)) {
+ blob->setError(QLatin1String("File name case mismatch"));
return;
}
@@ -1079,10 +1135,10 @@ void QQmlTypeLoader::loadThread(QQmlDataBlob *blob)
if (blob->m_data.isAsync())
m_thread->callDownloadProgressChanged(blob, 1.);
- setData(blob, &file);
+ setData(blob, fileName);
} else {
-
+#if QT_CONFIG(qml_network)
QNetworkReply *reply = m_thread->networkAccessManager()->get(QNetworkRequest(blob->m_url));
QQmlTypeLoaderNetworkReplyProxy *nrp = m_thread->networkReplyProxy();
blob->addref();
@@ -1099,14 +1155,15 @@ void QQmlTypeLoader::loadThread(QQmlDataBlob *blob)
#ifdef DATABLOB_DEBUG
qWarning("QQmlDataBlob: requested %s", qPrintable(blob->url().toString()));
-#endif
-
+#endif // DATABLOB_DEBUG
+#endif // qml_network
}
}
#define DATALOADER_MAXIMUM_REDIRECT_RECURSION 16
#define TYPELOADER_MINIMUM_TRIM_THRESHOLD 64
+#if QT_CONFIG(qml_network)
void QQmlTypeLoader::networkReplyFinished(QNetworkReply *reply)
{
Q_ASSERT(m_thread->isThisThread());
@@ -1162,6 +1219,7 @@ void QQmlTypeLoader::networkReplyProgress(QNetworkReply *reply,
m_thread->callDownloadProgressChanged(blob, blob->m_data.progress());
}
}
+#endif // qml_network
/*!
Return the QQmlEngine associated with this loader
@@ -1197,11 +1255,11 @@ void QQmlTypeLoader::setData(QQmlDataBlob *blob, const QByteArray &data)
setData(blob, d);
}
-void QQmlTypeLoader::setData(QQmlDataBlob *blob, QQmlFile *file)
+void QQmlTypeLoader::setData(QQmlDataBlob *blob, const QString &fileName)
{
QML_MEMORY_SCOPE_URL(blob->url());
QQmlDataBlob::Data d;
- d.d = file;
+ d.d = &fileName;
setData(blob, d);
}
@@ -1252,7 +1310,7 @@ void QQmlTypeLoader::shutdownThread()
}
QQmlTypeLoader::Blob::Blob(const QUrl &url, QQmlDataBlob::Type type, QQmlTypeLoader *loader)
- : QQmlDataBlob(url, type, loader), m_importCache(loader), m_isSingleton(false)
+ : QQmlDataBlob(url, type, loader), m_importCache(loader)
{
}
@@ -1394,13 +1452,10 @@ bool QQmlTypeLoader::Blob::addImport(const QV4::CompiledData::Import *import, QL
bool incomplete = false;
- QUrl qmldirUrl;
- if (importQualifier.isEmpty()) {
- qmldirUrl = finalUrl().resolved(QUrl(importUri + QLatin1String("/qmldir")));
- if (!QQmlImports::isLocal(qmldirUrl)) {
- // This is a remote file; the import is currently incomplete
- incomplete = true;
- }
+ QUrl qmldirUrl = finalUrl().resolved(QUrl(importUri + QLatin1String("/qmldir")));
+ if (!QQmlImports::isLocal(qmldirUrl)) {
+ // This is a remote file; the import is currently incomplete
+ incomplete = true;
}
if (!m_importCache.addFileImport(importDatabase, importUri, importQualifier, import->majorVersion,
@@ -1416,51 +1471,6 @@ bool QQmlTypeLoader::Blob::addImport(const QV4::CompiledData::Import *import, QL
return true;
}
-bool QQmlTypeLoader::Blob::addPragma(const QmlIR::Pragma &pragma, QList<QQmlError> *errors)
-{
- Q_ASSERT(errors);
-
- if (pragma.type == QmlIR::Pragma::PragmaSingleton) {
- QUrl myUrl = finalUrl();
-
- QQmlType *ret = QQmlMetaType::qmlType(myUrl, true);
- if (!ret) {
- QQmlError error;
- error.setDescription(QQmlTypeLoader::tr("No matching type found, pragma Singleton files cannot be used by QQmlComponent."));
- error.setUrl(myUrl);
- error.setLine(pragma.location.line);
- error.setColumn(pragma.location.column);
- errors->prepend(error);
- return false;
- }
-
- if (!ret->isCompositeSingleton()) {
- QQmlError error;
- error.setDescription(QQmlTypeLoader::tr("pragma Singleton used with a non composite singleton type %1").arg(ret->qmlTypeName()));
- error.setUrl(myUrl);
- error.setLine(pragma.location.line);
- error.setColumn(pragma.location.column);
- errors->prepend(error);
- return false;
- }
- // This flag is used for error checking when a qmldir file marks a type as
- // composite singleton, but there is no pragma Singleton defined in QML.
- m_isSingleton = true;
- } else {
- QQmlError error;
- error.setDescription(QLatin1String("Invalid pragma"));
- error.setUrl(finalUrl());
- error.setLine(pragma.location.line);
- error.setColumn(pragma.location.column);
- errors->prepend(error);
- return false;
- }
-
- return true;
-}
-
-
-
void QQmlTypeLoader::Blob::dependencyError(QQmlDataBlob *blob)
{
if (blob->type() == QQmlDataBlob::QmldirFile) {
@@ -1489,6 +1499,11 @@ void QQmlTypeLoader::Blob::dependencyComplete(QQmlDataBlob *blob)
}
}
+bool QQmlTypeLoader::Blob::isDebugging() const
+{
+ return QV8Engine::getV4(typeLoader()->engine())->debugger() != 0;
+}
+
bool QQmlTypeLoader::Blob::qmldirDataAvailable(QQmlQmldirData *data, QList<QQmlError> *errors)
{
bool resolve = true;
@@ -1951,9 +1966,11 @@ void QQmlTypeLoader::trimCache()
for (TypeCache::Iterator iter = m_typeCache.begin(), end = m_typeCache.end(); iter != end; ++iter) {
QQmlTypeData *typeData = iter.value();
- const bool hasError = !typeData->m_compiledData && !typeData->m_errors.isEmpty();
- const bool isNotReferenced = typeData->m_compiledData && typeData->m_compiledData->count() == 1;
- if (typeData->count() == 1 && (hasError || isNotReferenced)) {
+ // typeData->m_compiledData may be set early on in the proccess of loading a file, so
+ // it's important to check the general loading status of the typeData before making any
+ // other decisions.
+ if (typeData->count() == 1 && (typeData->isError() || typeData->isComplete())
+ && (!typeData->m_compiledData || typeData->m_compiledData->count() == 1)) {
// There are no live objects of this type
unneededTypes.append(iter);
}
@@ -1963,8 +1980,7 @@ void QQmlTypeLoader::trimCache()
break;
while (!unneededTypes.isEmpty()) {
- TypeCache::Iterator iter = unneededTypes.last();
- unneededTypes.removeLast();
+ TypeCache::Iterator iter = unneededTypes.takeLast();
iter.value()->release();
m_typeCache.erase(iter);
@@ -1994,7 +2010,7 @@ QQmlTypeData::TypeDataCallback::~TypeDataCallback()
QQmlTypeData::QQmlTypeData(const QUrl &url, QQmlTypeLoader *manager)
: QQmlTypeLoader::Blob(url, QmlFile, manager),
- m_typesResolved(false), m_compiledData(0), m_implicitImport(0), m_implicitImportLoaded(false)
+ m_typesResolved(false), m_implicitImportLoaded(false)
{
}
@@ -2007,14 +2023,11 @@ QQmlTypeData::~QQmlTypeData()
if (QQmlTypeData *tdata = m_compositeSingletons.at(ii).typeData)
tdata->release();
}
- for (QHash<int, TypeReference>::ConstIterator it = m_resolvedTypes.constBegin(), end = m_resolvedTypes.constEnd();
+ for (auto it = m_resolvedTypes.constBegin(), end = m_resolvedTypes.constEnd();
it != end; ++it) {
if (QQmlTypeData *tdata = it->typeData)
tdata->release();
}
-
- if (m_compiledData)
- m_compiledData->release();
}
const QList<QQmlTypeData::ScriptReference> &QQmlTypeData::resolvedScripts() const
@@ -2022,19 +2035,9 @@ const QList<QQmlTypeData::ScriptReference> &QQmlTypeData::resolvedScripts() cons
return m_scripts;
}
-const QSet<QString> &QQmlTypeData::namespaces() const
-{
- return m_namespaces;
-}
-
-const QList<QQmlTypeData::TypeReference> &QQmlTypeData::compositeSingletons() const
-{
- return m_compositeSingletons;
-}
-
-QQmlCompiledData *QQmlTypeData::compiledData() const
+QV4::CompiledData::CompilationUnit *QQmlTypeData::compilationUnit() const
{
- return m_compiledData;
+ return m_compiledData.data();
}
void QQmlTypeData::registerCallback(TypeDataCallback *callback)
@@ -2050,10 +2053,115 @@ void QQmlTypeData::unregisterCallback(TypeDataCallback *callback)
Q_ASSERT(!m_callbacks.contains(callback));
}
+bool QQmlTypeData::tryLoadFromDiskCache()
+{
+ if (disableDiskCache() && !forceDiskCache())
+ return false;
+
+ if (isDebugging())
+ return false;
+
+ QV4::ExecutionEngine *v4 = QQmlEnginePrivate::getV4Engine(typeLoader()->engine());
+ if (!v4)
+ return false;
+
+ QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit = v4->iselFactory->createUnitForLoading();
+ {
+ QString error;
+ if (!unit->loadFromDisk(url(), v4->iselFactory.data(), &error)) {
+ qCDebug(DBG_DISK_CACHE) << "Error loading" << url().toString() << "from disk cache:" << error;
+ return false;
+ }
+ }
+
+ m_compiledData = unit;
+
+ for (int i = 0, count = m_compiledData->objectCount(); i < count; ++i)
+ m_typeReferences.collectFromObject(m_compiledData->objectAt(i));
+
+ m_importCache.setBaseUrl(finalUrl(), finalUrlString());
+
+ // For remote URLs, we don't delay the loading of the implicit import
+ // because the loading probably requires an asynchronous fetch of the
+ // qmldir (so we can't load it just in time).
+ if (!finalUrl().scheme().isEmpty()) {
+ QUrl qmldirUrl = finalUrl().resolved(QUrl(QLatin1String("qmldir")));
+ if (!QQmlImports::isLocal(qmldirUrl)) {
+ if (!loadImplicitImport())
+ return false;
+
+ // find the implicit import
+ for (quint32 i = 0; i < m_compiledData->data->nImports; ++i) {
+ const QV4::CompiledData::Import *import = m_compiledData->data->importAt(i);
+ if (m_compiledData->stringAt(import->uriIndex) == QLatin1String(".")
+ && import->qualifierIndex == 0
+ && import->majorVersion == -1
+ && import->minorVersion == -1) {
+ QList<QQmlError> errors;
+ if (!fetchQmldir(qmldirUrl, import, 1, &errors)) {
+ setError(errors);
+ return false;
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ for (int i = 0, count = m_compiledData->data->nImports; i < count; ++i) {
+ const QV4::CompiledData::Import *import = m_compiledData->data->importAt(i);
+ QList<QQmlError> errors;
+ if (!addImport(import, &errors)) {
+ Q_ASSERT(errors.size());
+ QQmlError error(errors.takeFirst());
+ error.setUrl(m_importCache.baseUrl());
+ error.setLine(import->location.line);
+ error.setColumn(import->location.column);
+ errors.prepend(error); // put it back on the list after filling out information.
+ setError(errors);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void QQmlTypeData::createTypeAndPropertyCaches(const QQmlRefPointer<QQmlTypeNameCache> &importCache,
+ const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypeCache)
+{
+ Q_ASSERT(m_compiledData);
+ m_compiledData->importCache = importCache;
+ m_compiledData->resolvedTypes = resolvedTypeCache;
+
+ QQmlEnginePrivate * const engine = QQmlEnginePrivate::get(typeLoader()->engine());
+
+ {
+ QQmlPropertyCacheCreator<QV4::CompiledData::CompilationUnit> propertyCacheCreator(&m_compiledData->propertyCaches, engine, m_compiledData, &m_importCache);
+ QQmlCompileError error = propertyCacheCreator.buildMetaObjects();
+ if (error.isSet()) {
+ setError(error);
+ return;
+ }
+ }
+
+ QQmlPropertyCacheAliasCreator<QV4::CompiledData::CompilationUnit> aliasCreator(&m_compiledData->propertyCaches, m_compiledData);
+ aliasCreator.appendAliasPropertiesToMetaObjects();
+}
+
void QQmlTypeData::done()
{
+ QDeferredCleanup cleanup([this]{
+ m_document.reset();
+ m_typeReferences.clear();
+ if (isError())
+ m_compiledData = nullptr;
+ });
+
+ if (isError())
+ return;
+
// Check all script dependencies for errors
- for (int ii = 0; !isError() && ii < m_scripts.count(); ++ii) {
+ for (int ii = 0; ii < m_scripts.count(); ++ii) {
const ScriptReference &script = m_scripts.at(ii);
Q_ASSERT(script.script->isCompleteOrError());
if (script.script->isError()) {
@@ -2065,16 +2173,17 @@ void QQmlTypeData::done()
error.setDescription(QQmlTypeLoader::tr("Script %1 unavailable").arg(script.script->url().toString()));
errors.prepend(error);
setError(errors);
+ return;
}
}
// Check all type dependencies for errors
- for (QHash<int, TypeReference>::ConstIterator it = m_resolvedTypes.constBegin(), end = m_resolvedTypes.constEnd();
- !isError() && it != end; ++it) {
+ for (auto it = m_resolvedTypes.constBegin(), end = m_resolvedTypes.constEnd(); it != end;
+ ++it) {
const TypeReference &type = *it;
Q_ASSERT(!type.typeData || type.typeData->isCompleteOrError());
if (type.typeData && type.typeData->isError()) {
- QString typeName = m_document->stringAt(it.key());
+ const QString typeName = stringAt(it.key());
QList<QQmlError> errors = type.typeData->errors();
QQmlError error;
@@ -2084,11 +2193,12 @@ void QQmlTypeData::done()
error.setDescription(QQmlTypeLoader::tr("Type %1 unavailable").arg(typeName));
errors.prepend(error);
setError(errors);
+ return;
}
}
// Check all composite singleton type dependencies for errors
- for (int ii = 0; !isError() && ii < m_compositeSingletons.count(); ++ii) {
+ for (int ii = 0; ii < m_compositeSingletons.count(); ++ii) {
const TypeReference &type = m_compositeSingletons.at(ii);
Q_ASSERT(!type.typeData || type.typeData->isCompleteOrError());
if (type.typeData && type.typeData->isError()) {
@@ -2102,27 +2212,102 @@ void QQmlTypeData::done()
error.setDescription(QQmlTypeLoader::tr("Type %1 unavailable").arg(typeName));
errors.prepend(error);
setError(errors);
+ return;
+ }
+ }
+
+ QQmlRefPointer<QQmlTypeNameCache> importCache;
+ QV4::CompiledData::ResolvedTypeReferenceMap resolvedTypeCache;
+ {
+ QQmlCompileError error = buildTypeResolutionCaches(&importCache, &resolvedTypeCache);
+ if (error.isSet()) {
+ setError(error);
+ return;
}
}
- // If the type is CompositeSingleton but there was no pragma Singleton in the
- // QML file, lets report an error.
- QQmlType *type = QQmlMetaType::qmlType(url(), true);
- if (!isError() && type && type->isCompositeSingleton() && !m_isSingleton) {
- QString typeName = type->qmlTypeName();
+ QQmlEngine *const engine = typeLoader()->engine();
- QQmlError error;
- error.setDescription(QQmlTypeLoader::tr("qmldir defines type as singleton, but no pragma Singleton found in type %1.").arg(typeName));
- error.setUrl(finalUrl());
- setError(error);
+ // verify if any dependencies changed if we're using a cache
+ if (m_document.isNull() && !m_compiledData->verifyChecksum(engine, resolvedTypeCache)) {
+ qCDebug(DBG_DISK_CACHE) << "Checksum mismatch for cached version of" << m_compiledData->url().toString();
+ if (!loadFromSource())
+ return;
+ m_backupSourceCode.clear();
+ m_compiledData = nullptr;
+ }
+
+ if (!m_document.isNull()) {
+ // Compile component
+ compile(importCache, resolvedTypeCache);
+ } else {
+ createTypeAndPropertyCaches(importCache, resolvedTypeCache);
}
- // Compile component
- if (!isError())
- compile();
+ if (isError())
+ return;
- m_document.reset();
- m_implicitImport = 0;
+ {
+ QQmlEnginePrivate *const enginePrivate = QQmlEnginePrivate::get(engine);
+ {
+ // Sanity check property bindings
+ QQmlPropertyValidator validator(enginePrivate, m_importCache, m_compiledData);
+ QVector<QQmlCompileError> errors = validator.validate();
+ if (!errors.isEmpty()) {
+ setError(errors);
+ return;
+ }
+ }
+
+ m_compiledData->finalize(enginePrivate);
+ }
+
+ {
+ QQmlType *type = QQmlMetaType::qmlType(finalUrl(), true);
+ if (m_compiledData && m_compiledData->data->flags & QV4::CompiledData::Unit::IsSingleton) {
+ if (!type) {
+ QQmlError error;
+ error.setDescription(QQmlTypeLoader::tr("No matching type found, pragma Singleton files cannot be used by QQmlComponent."));
+ setError(error);
+ return;
+ } else if (!type->isCompositeSingleton()) {
+ QQmlError error;
+ error.setDescription(QQmlTypeLoader::tr("pragma Singleton used with a non composite singleton type %1").arg(type->qmlTypeName()));
+ setError(error);
+ return;
+ }
+ } else {
+ // If the type is CompositeSingleton but there was no pragma Singleton in the
+ // QML file, lets report an error.
+ if (type && type->isCompositeSingleton()) {
+ QString typeName = type->qmlTypeName();
+ setError(QQmlTypeLoader::tr("qmldir defines type as singleton, but no pragma Singleton found in type %1.").arg(typeName));
+ return;
+ }
+ }
+ }
+
+ {
+ // Collect imported scripts
+ m_compiledData->dependentScripts.reserve(m_scripts.count());
+ for (int scriptIndex = 0; scriptIndex < m_scripts.count(); ++scriptIndex) {
+ const QQmlTypeData::ScriptReference &script = m_scripts.at(scriptIndex);
+
+ QStringRef qualifier(&script.qualifier);
+ QString enclosingNamespace;
+
+ const int lastDotIndex = qualifier.lastIndexOf(QLatin1Char('.'));
+ if (lastDotIndex != -1) {
+ enclosingNamespace = qualifier.left(lastDotIndex).toString();
+ qualifier = qualifier.mid(lastDotIndex+1);
+ }
+
+ m_compiledData->importCache->add(qualifier.toString(), scriptIndex, enclosingNamespace);
+ QQmlScriptData *scriptData = script.script->scriptData();
+ scriptData->addref();
+ m_compiledData->dependentScripts << scriptData;
+ }
+ }
}
void QQmlTypeData::completed()
@@ -2157,9 +2342,42 @@ bool QQmlTypeData::loadImplicitImport()
void QQmlTypeData::dataReceived(const Data &data)
{
- QString code = QString::fromUtf8(data.data(), data.size());
+ QString error;
+ m_backupSourceCode = data.readAll(&error, &m_sourceTimeStamp);
+ // if we failed to read the source code, process it _after_ we've tried
+ // to use the disk cache, in order to support scenarios where the source
+ // was removed deliberately.
+
+ if (tryLoadFromDiskCache())
+ return;
+
+ if (isError())
+ return;
+
+ if (!error.isEmpty()) {
+ setError(error);
+ return;
+ }
+
+ if (!loadFromSource())
+ return;
+
+ continueLoadFromIR();
+}
+
+void QQmlTypeData::initializeFromCachedUnit(const QQmlPrivate::CachedQmlUnit *unit)
+{
+ m_document.reset(new QmlIR::Document(isDebugging()));
+ unit->loadIR(m_document.data(), unit);
+ continueLoadFromIR();
+}
+
+bool QQmlTypeData::loadFromSource()
+{
+ QString code = QString::fromUtf8(m_backupSourceCode);
+ m_document.reset(new QmlIR::Document(isDebugging()));
+ m_document->jsModule.sourceTimeStamp = m_sourceTimeStamp;
QQmlEngine *qmlEngine = typeLoader()->engine();
- m_document.reset(new QmlIR::Document(QV8Engine::getV4(qmlEngine)->debugger != 0));
QmlIR::IRBuilder compiler(QV8Engine::get(qmlEngine)->illegalNames());
if (!compiler.generateFromQml(code, finalUrlString(), m_document.data())) {
QList<QQmlError> errors;
@@ -2173,23 +2391,14 @@ void QQmlTypeData::dataReceived(const Data &data)
errors << e;
}
setError(errors);
- return;
+ return false;
}
-
- continueLoadFromIR();
-}
-
-void QQmlTypeData::initializeFromCachedUnit(const QQmlPrivate::CachedQmlUnit *unit)
-{
- QQmlEngine *qmlEngine = typeLoader()->engine();
- m_document.reset(new QmlIR::Document(QV8Engine::getV4(qmlEngine)->debugger != 0));
- unit->loadIR(m_document.data(), unit);
- continueLoadFromIR();
+ return true;
}
void QQmlTypeData::continueLoadFromIR()
{
- m_document->collectTypeReferences();
+ m_typeReferences.collectFromObjects(m_document->objects.constBegin(), m_document->objects.constEnd());
m_importCache.setBaseUrl(finalUrl(), finalUrlString());
// For remote URLs, we don't delay the loading of the implicit import
@@ -2202,14 +2411,14 @@ void QQmlTypeData::continueLoadFromIR()
return;
// This qmldir is for the implicit import
QQmlJS::MemoryPool *pool = m_document->jsParserEngine.pool();
- m_implicitImport = pool->New<QV4::CompiledData::Import>();
- m_implicitImport->uriIndex = m_document->registerString(QLatin1String("."));
- m_implicitImport->qualifierIndex = 0; // empty string
- m_implicitImport->majorVersion = -1;
- m_implicitImport->minorVersion = -1;
+ auto implicitImport = pool->New<QV4::CompiledData::Import>();
+ implicitImport->uriIndex = m_document->registerString(QLatin1String("."));
+ implicitImport->qualifierIndex = 0; // empty string
+ implicitImport->majorVersion = -1;
+ implicitImport->minorVersion = -1;
QList<QQmlError> errors;
- if (!fetchQmldir(qmldirUrl, m_implicitImport, 1, &errors)) {
+ if (!fetchQmldir(qmldirUrl, implicitImport, 1, &errors)) {
setError(errors);
return;
}
@@ -2230,14 +2439,6 @@ void QQmlTypeData::continueLoadFromIR()
return;
}
}
-
- foreach (QmlIR::Pragma *pragma, m_document->pragmas) {
- if (!addPragma(*pragma, &errors)) {
- Q_ASSERT(errors.size());
- setError(errors);
- return;
- }
- }
}
void QQmlTypeData::allDependenciesDone()
@@ -2282,20 +2483,34 @@ void QQmlTypeData::downloadProgressChanged(qreal p)
QString QQmlTypeData::stringAt(int index) const
{
+ if (m_compiledData)
+ return m_compiledData->stringAt(index);
return m_document->jsGenerator.stringTable.stringForIndex(index);
}
-void QQmlTypeData::compile()
+void QQmlTypeData::compile(const QQmlRefPointer<QQmlTypeNameCache> &importCache, const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypeCache)
{
- Q_ASSERT(m_compiledData == 0);
+ Q_ASSERT(m_compiledData.isNull());
- m_compiledData = new QQmlCompiledData(typeLoader()->engine());
-
- QQmlTypeCompiler compiler(QQmlEnginePrivate::get(typeLoader()->engine()), m_compiledData, this, m_document.data());
- if (!compiler.compile()) {
+ QQmlEnginePrivate * const enginePrivate = QQmlEnginePrivate::get(typeLoader()->engine());
+ QQmlTypeCompiler compiler(enginePrivate, this, m_document.data(), importCache, resolvedTypeCache);
+ m_compiledData = compiler.compile();
+ if (!m_compiledData) {
setError(compiler.compilationErrors());
- m_compiledData->release();
- m_compiledData = 0;
+ return;
+ }
+
+ const bool trySaveToDisk = (!disableDiskCache() || forceDiskCache()) && !m_document->jsModule.debugMode;
+ if (trySaveToDisk) {
+ QString errorString;
+ if (m_compiledData->saveToDisk(url(), &errorString)) {
+ QString error;
+ if (!m_compiledData->loadFromDisk(url(), enginePrivate->v4engine()->iselFactory.data(), &error)) {
+ // ignore error, keep using the in-memory compilation unit.
+ }
+ } else {
+ qCDebug(DBG_DISK_CACHE) << "Error saving cached version of" << m_compiledData->url().toString() << "to disk:" << errorString;
+ }
}
}
@@ -2309,13 +2524,13 @@ void QQmlTypeData::resolveTypes()
ScriptReference ref;
//ref.location = ...
- ref.qualifier = script.nameSpace;
if (!script.qualifier.isEmpty())
{
- ref.qualifier.prepend(script.qualifier + QLatin1Char('.'));
-
+ ref.qualifier = script.qualifier + QLatin1Char('.') + script.nameSpace;
// Add a reference to the enclosing namespace
m_namespaces.insert(script.qualifier);
+ } else {
+ ref.qualifier = script.nameSpace;
}
ref.script = blob;
@@ -2325,12 +2540,13 @@ void QQmlTypeData::resolveTypes()
// Lets handle resolved composite singleton types
foreach (const QQmlImports::CompositeSingletonReference &csRef, m_importCache.resolvedCompositeSingletons()) {
TypeReference ref;
- QString typeName = csRef.typeName;
-
+ QString typeName;
if (!csRef.prefix.isEmpty()) {
- typeName.prepend(csRef.prefix + QLatin1Char('.'));
+ typeName = csRef.prefix + QLatin1Char('.') + csRef.typeName;
// Add a reference to the enclosing namespace
m_namespaces.insert(csRef.prefix);
+ } else {
+ typeName = csRef.typeName;
}
int majorVersion = csRef.majorVersion > -1 ? csRef.majorVersion : -1;
@@ -2348,7 +2564,7 @@ void QQmlTypeData::resolveTypes()
}
}
- for (QV4::CompiledData::TypeReferenceMap::ConstIterator unresolvedRef = m_document->typeReferences.constBegin(), end = m_document->typeReferences.constEnd();
+ for (QV4::CompiledData::TypeReferenceMap::ConstIterator unresolvedRef = m_typeReferences.constBegin(), end = m_typeReferences.constEnd();
unresolvedRef != end; ++unresolvedRef) {
TypeReference ref; // resolved reference
@@ -2418,6 +2634,57 @@ void QQmlTypeData::resolveTypes()
}
}
+QQmlCompileError QQmlTypeData::buildTypeResolutionCaches(
+ QQmlRefPointer<QQmlTypeNameCache> *importCache,
+ QV4::CompiledData::ResolvedTypeReferenceMap *resolvedTypeCache
+ ) const
+{
+ importCache->adopt(new QQmlTypeNameCache);
+
+ for (const QString &ns: m_namespaces)
+ (*importCache)->add(ns);
+
+ // Add any Composite Singletons that were used to the import cache
+ for (const QQmlTypeData::TypeReference &singleton: m_compositeSingletons)
+ (*importCache)->add(singleton.type->qmlTypeName(), singleton.type->sourceUrl(), singleton.prefix);
+
+ m_importCache.populateCache(*importCache);
+
+ QQmlEnginePrivate * const engine = QQmlEnginePrivate::get(typeLoader()->engine());
+
+ for (auto resolvedType = m_resolvedTypes.constBegin(), end = m_resolvedTypes.constEnd(); resolvedType != end; ++resolvedType) {
+ QScopedPointer<QV4::CompiledData::ResolvedTypeReference> ref(new QV4::CompiledData::ResolvedTypeReference);
+ QQmlType *qmlType = resolvedType->type;
+ if (resolvedType->typeData) {
+ if (resolvedType->needsCreation && qmlType->isCompositeSingleton()) {
+ return QQmlCompileError(resolvedType->location, tr("Composite Singleton Type %1 is not creatable.").arg(qmlType->qmlTypeName()));
+ }
+ ref->compilationUnit = resolvedType->typeData->compilationUnit();
+ } else if (qmlType) {
+ ref->type = qmlType;
+ Q_ASSERT(ref->type);
+
+ if (resolvedType->needsCreation && !ref->type->isCreatable()) {
+ QString reason = ref->type->noCreationReason();
+ if (reason.isEmpty())
+ reason = tr("Element is not creatable.");
+ return QQmlCompileError(resolvedType->location, reason);
+ }
+
+ if (ref->type->containsRevisionedAttributes()) {
+ ref->typePropertyCache = engine->cache(ref->type,
+ resolvedType->minorVersion);
+ }
+ }
+ ref->majorVersion = resolvedType->majorVersion;
+ ref->minorVersion = resolvedType->minorVersion;
+ ref->doDynamicTypeCheck();
+ resolvedTypeCache->insert(resolvedType.key(), ref.take());
+ }
+ QQmlCompileError noError;
+ return noError;
+}
+
bool QQmlTypeData::resolveType(const QString &typeName, int &majorVersion, int &minorVersion, TypeReference &ref)
{
QQmlImportNamespace *typeNamespace = 0;
@@ -2539,10 +2806,6 @@ QV4::ReturnedValue QQmlScriptData::scriptValueForContext(QQmlContextData *parent
ctxt->importedScripts = effectiveCtxt->importedScripts;
}
- if (ctxt->imports) {
- ctxt->imports->addref();
- }
-
if (effectiveCtxt) {
ctxt->setParent(effectiveCtxt, true);
} else {
@@ -2630,10 +2893,29 @@ struct EmptyCompilationUnit : public QV4::CompiledData::CompilationUnit
void QQmlScriptBlob::dataReceived(const Data &data)
{
- QString source = QString::fromUtf8(data.data(), data.size());
-
QV4::ExecutionEngine *v4 = QV8Engine::getV4(m_typeLoader->engine());
- QmlIR::Document irUnit(v4->debugger != 0);
+
+ if (!disableDiskCache() || forceDiskCache()) {
+ QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit = v4->iselFactory->createUnitForLoading();
+ QString error;
+ if (unit->loadFromDisk(url(), v4->iselFactory.data(), &error)) {
+ initializeFromCompilationUnit(unit);
+ return;
+ } else {
+ qCDebug(DBG_DISK_CACHE()) << "Error loading" << url().toString() << "from disk cache:" << error;
+ }
+ }
+
+
+ QmlIR::Document irUnit(isDebugging());
+
+ QString error;
+ QString source = QString::fromUtf8(data.readAll(&error, &irUnit.jsModule.sourceTimeStamp));
+ if (!error.isEmpty()) {
+ setError(error);
+ return;
+ }
+
QmlIR::ScriptDirectivesCollector collector(&irUnit.jsParserEngine, &irUnit.jsGenerator);
QList<QQmlError> errors;
@@ -2650,14 +2932,22 @@ void QQmlScriptBlob::dataReceived(const Data &data)
irUnit.javaScriptCompilationUnit = unit;
irUnit.imports = collector.imports;
if (collector.hasPragmaLibrary)
- irUnit.unitFlags |= QV4::CompiledData::Unit::IsSharedLibrary;
+ irUnit.jsModule.unitFlags |= QV4::CompiledData::Unit::IsSharedLibrary;
QmlIR::QmlUnitGenerator qmlGenerator;
- QV4::CompiledData::Unit *unitData = qmlGenerator.generate(irUnit);
+ QV4::CompiledData::ResolvedTypeReferenceMap emptyDependencies;
+ QV4::CompiledData::Unit *unitData = qmlGenerator.generate(irUnit, m_typeLoader->engine(), emptyDependencies);
Q_ASSERT(!unit->data);
// The js unit owns the data and will free the qml unit.
unit->data = unitData;
+ if (!disableDiskCache() || forceDiskCache()) {
+ QString errorString;
+ if (!unit->saveToDisk(url(), &errorString)) {
+ qCDebug(DBG_DISK_CACHE()) << "Error saving cached version of" << unit->url().toString() << "to disk:" << errorString;
+ }
+ }
+
initializeFromCompilationUnit(unit);
}
@@ -2668,8 +2958,11 @@ void QQmlScriptBlob::initializeFromCachedUnit(const QQmlPrivate::CachedQmlUnit *
void QQmlScriptBlob::done()
{
+ if (isError())
+ return;
+
// Check all script dependencies for errors
- for (int ii = 0; !isError() && ii < m_scripts.count(); ++ii) {
+ for (int ii = 0; ii < m_scripts.count(); ++ii) {
const ScriptReference &script = m_scripts.at(ii);
Q_ASSERT(script.script->isCompleteOrError());
if (script.script->isError()) {
@@ -2681,17 +2974,15 @@ void QQmlScriptBlob::done()
error.setDescription(QQmlTypeLoader::tr("Script %1 unavailable").arg(script.script->url().toString()));
errors.prepend(error);
setError(errors);
+ return;
}
}
- if (isError())
- return;
-
m_scriptData->importCache = new QQmlTypeNameCache();
QSet<QString> ns;
- for (int scriptIndex = 0; !isError() && scriptIndex < m_scripts.count(); ++scriptIndex) {
+ for (int scriptIndex = 0; scriptIndex < m_scripts.count(); ++scriptIndex) {
const ScriptReference &script = m_scripts.at(scriptIndex);
m_scriptData->scripts.append(script.script);
@@ -2785,7 +3076,12 @@ void QQmlQmldirData::setPriority(int priority)
void QQmlQmldirData::dataReceived(const Data &data)
{
- m_content = QString::fromUtf8(data.data(), data.size());
+ QString error;
+ m_content = QString::fromUtf8(data.readAll(&error));
+ if (!error.isEmpty()) {
+ setError(error);
+ return;
+ }
}
void QQmlQmldirData::initializeFromCachedUnit(const QQmlPrivate::CachedQmlUnit *)
@@ -2793,6 +3089,36 @@ void QQmlQmldirData::initializeFromCachedUnit(const QQmlPrivate::CachedQmlUnit *
Q_UNIMPLEMENTED();
}
+QByteArray QQmlDataBlob::Data::readAll(QString *error, qint64 *sourceTimeStamp) const
+{
+ Q_ASSERT(!d.isNull());
+ error->clear();
+ if (d.isT1()) {
+ if (sourceTimeStamp)
+ *sourceTimeStamp = 0;
+ return *d.asT1();
+ }
+ QFile f(*d.asT2());
+ if (!f.open(QIODevice::ReadOnly)) {
+ *error = f.errorString();
+ return QByteArray();
+ }
+ if (sourceTimeStamp) {
+ QDateTime timeStamp = QFileInfo(f).lastModified();
+ // Files from the resource system do not have any time stamps, so fall back to the application
+ // executable.
+ if (!timeStamp.isValid())
+ timeStamp = QFileInfo(QCoreApplication::applicationFilePath()).lastModified();
+ *sourceTimeStamp = timeStamp.toMSecsSinceEpoch();
+ }
+ QByteArray data(f.size(), Qt::Uninitialized);
+ if (f.read(data.data(), data.length()) != data.length()) {
+ *error = f.errorString();
+ return QByteArray();
+ }
+ return data;
+}
+
QT_END_NAMESPACE
#include "qqmltypeloader.moc"
diff --git a/src/qml/qml/qqmltypeloader_p.h b/src/qml/qml/qqmltypeloader_p.h
index 51228eacc7..53cf4234e1 100644
--- a/src/qml/qml/qqmltypeloader_p.h
+++ b/src/qml/qml/qqmltypeloader_p.h
@@ -51,9 +51,12 @@
// We mean it.
//
+#include <QtQml/qtqmlglobal.h>
#include <QtCore/qobject.h>
#include <QtCore/qatomic.h>
+#if QT_CONFIG(qml_network)
#include <QtNetwork/qnetworkreply.h>
+#endif
#include <QtQml/qqmlerror.h>
#include <QtQml/qqmlengine.h>
#include <QtQml/qqmlfile.h>
@@ -75,11 +78,11 @@ class QQmlScriptData;
class QQmlScriptBlob;
class QQmlQmldirData;
class QQmlTypeLoader;
-class QQmlCompiledData;
class QQmlComponentPrivate;
class QQmlTypeData;
class QQmlTypeLoader;
class QQmlExtensionInterface;
+struct QQmlCompileError;
namespace QmlIR {
struct Document;
@@ -129,34 +132,32 @@ public:
class Data {
public:
- inline const char *data() const;
- inline int size() const;
-
- inline QByteArray asByteArray() const;
-
- inline bool isFile() const;
- inline QQmlFile *asFile() const;
-
+ QByteArray readAll(QString *error, qint64 *sourceTimeStamp = 0) const;
private:
friend class QQmlDataBlob;
friend class QQmlTypeLoader;
inline Data();
Data(const Data &);
Data &operator=(const Data &);
- QBiPointer<const QByteArray, QQmlFile> d;
+ QBiPointer<const QByteArray, const QString> d;
};
protected:
// Can be called from within callbacks
void setError(const QQmlError &);
void setError(const QList<QQmlError> &errors);
+ void setError(const QQmlCompileError &error);
+ void setError(const QVector<QQmlCompileError> &errors);
+ void setError(const QString &description);
void addDependency(QQmlDataBlob *);
// Callbacks made in load thread
virtual void dataReceived(const Data &) = 0;
virtual void initializeFromCachedUnit(const QQmlPrivate::CachedQmlUnit*) = 0;
virtual void done();
+#if QT_CONFIG(qml_network)
virtual void networkError(QNetworkReply::NetworkError);
+#endif
virtual void dependencyError(QQmlDataBlob *);
virtual void dependencyComplete(QQmlDataBlob *);
virtual void allDependenciesDone();
@@ -233,7 +234,6 @@ public:
protected:
bool addImport(const QV4::CompiledData::Import *import, QList<QQmlError> *errors);
- bool addPragma(const QmlIR::Pragma &pragma, QList<QQmlError> *errors);
bool fetchQmldir(const QUrl &url, const QV4::CompiledData::Import *import, int priority, QList<QQmlError> *errors);
bool updateQmldir(QQmlQmldirData *data, const QV4::CompiledData::Import *import, QList<QQmlError> *errors);
@@ -249,8 +249,9 @@ public:
protected:
virtual QString stringAt(int) const { return QString(); }
+ bool isDebugging() const;
+
QQmlImports m_importCache;
- bool m_isSingleton;
QHash<const QV4::CompiledData::Import*, int> m_unresolvedImports;
QList<QQmlQmldirData *> m_qmldirs;
};
@@ -320,20 +321,24 @@ public:
private:
friend class QQmlDataBlob;
friend class QQmlTypeLoaderThread;
+#if QT_CONFIG(qml_network)
friend class QQmlTypeLoaderNetworkReplyProxy;
+#endif // qml_network
void shutdownThread();
void loadThread(QQmlDataBlob *);
void loadWithStaticDataThread(QQmlDataBlob *, const QByteArray &);
void loadWithCachedUnitThread(QQmlDataBlob *blob, const QQmlPrivate::CachedQmlUnit *unit);
+#if QT_CONFIG(qml_network)
void networkReplyFinished(QNetworkReply *);
void networkReplyProgress(QNetworkReply *, qint64, qint64);
typedef QHash<QNetworkReply *, QQmlDataBlob *> NetworkReplies;
+#endif
void setData(QQmlDataBlob *, const QByteArray &);
- void setData(QQmlDataBlob *, QQmlFile *);
+ void setData(QQmlDataBlob *, const QString &fileName);
void setData(QQmlDataBlob *, const QQmlDataBlob::Data &);
void setCachedUnit(QQmlDataBlob *blob, const QQmlPrivate::CachedQmlUnit *unit);
@@ -362,7 +367,9 @@ private:
QQmlEngine *m_engine;
QQmlTypeLoaderThread *m_thread;
+#if QT_CONFIG(qml_network)
NetworkReplies m_networkReplies;
+#endif
TypeCache m_typeCache;
int m_typeCacheTrimThreshold;
ScriptCache m_scriptCache;
@@ -381,6 +388,7 @@ private:
class Q_AUTOTEST_EXPORT QQmlTypeData : public QQmlTypeLoader::Blob
{
+ Q_DECLARE_TR_FUNCTIONS(QQmlTypeData)
public:
struct TypeReference
{
@@ -412,13 +420,9 @@ private:
public:
~QQmlTypeData();
- const QHash<int, TypeReference> &resolvedTypeRefs() const { return m_resolvedTypes; }
-
const QList<ScriptReference> &resolvedScripts() const;
- const QSet<QString> &namespaces() const;
- const QList<TypeReference> &compositeSingletons() const;
- QQmlCompiledData *compiledData() const;
+ QV4::CompiledData::CompilationUnit *compilationUnit() const;
// Used by QQmlComponent to get notifications
struct TypeDataCallback {
@@ -440,14 +444,27 @@ protected:
virtual QString stringAt(int index) const;
private:
+ bool tryLoadFromDiskCache();
+ bool loadFromSource();
void continueLoadFromIR();
void resolveTypes();
- void compile();
+ QQmlCompileError buildTypeResolutionCaches(
+ QQmlRefPointer<QQmlTypeNameCache> *importCache,
+ QV4::CompiledData::ResolvedTypeReferenceMap *resolvedTypeCache
+ ) const;
+ void compile(const QQmlRefPointer<QQmlTypeNameCache> &importCache,
+ const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypeCache);
+ void createTypeAndPropertyCaches(const QQmlRefPointer<QQmlTypeNameCache> &importCache,
+ const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypeCache);
bool resolveType(const QString &typeName, int &majorVersion, int &minorVersion, TypeReference &ref);
virtual void scriptImported(QQmlScriptBlob *blob, const QV4::CompiledData::Location &location, const QString &qualifier, const QString &nameSpace);
+
+ qint64 m_sourceTimeStamp = 0;
+ QByteArray m_backupSourceCode; // used when cache verification fails.
QScopedPointer<QmlIR::Document> m_document;
+ QV4::CompiledData::TypeReferenceMap m_typeReferences;
QList<ScriptReference> m_scripts;
@@ -455,14 +472,15 @@ private:
QList<TypeReference> m_compositeSingletons;
// map from name index to resolved type
- QHash<int, TypeReference> m_resolvedTypes;
+ // While this could be a hash, a map is chosen here to provide a stable
+ // order, which is used to calculating a check-sum on dependent meta-objects.
+ QMap<int, TypeReference> m_resolvedTypes;
bool m_typesResolved:1;
- QQmlCompiledData *m_compiledData;
+ QQmlRefPointer<QV4::CompiledData::CompilationUnit> m_compiledData;
QList<TypeDataCallback *> m_callbacks;
- QV4::CompiledData::Import *m_implicitImport;
bool m_implicitImportLoaded;
bool loadImplicitImport();
};
@@ -572,40 +590,7 @@ QQmlDataBlob::Data::Data()
{
}
-const char *QQmlDataBlob::Data::data() const
-{
- Q_ASSERT(!d.isNull());
-
- if (d.isT1()) return d.asT1()->constData();
- else return d.asT2()->data();
-}
-
-int QQmlDataBlob::Data::size() const
-{
- Q_ASSERT(!d.isNull());
-
- if (d.isT1()) return d.asT1()->size();
- else return d.asT2()->size();
-}
-
-bool QQmlDataBlob::Data::isFile() const
-{
- return d.isT2();
-}
-QByteArray QQmlDataBlob::Data::asByteArray() const
-{
- Q_ASSERT(!d.isNull());
-
- if (d.isT1()) return *d.asT1();
- else return d.asT2()->dataByteArray();
-}
-
-QQmlFile *QQmlDataBlob::Data::asFile() const
-{
- if (d.isT2()) return d.asT2();
- else return 0;
-}
QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmltypewrapper.cpp b/src/qml/qml/qqmltypewrapper.cpp
index 7892555f08..5c3ad6b2a6 100644
--- a/src/qml/qml/qqmltypewrapper.cpp
+++ b/src/qml/qml/qqmltypewrapper.cpp
@@ -55,15 +55,19 @@ using namespace QV4;
DEFINE_OBJECT_VTABLE(QmlTypeWrapper);
-Heap::QmlTypeWrapper::QmlTypeWrapper()
- : mode(IncludeEnums)
+void Heap::QmlTypeWrapper::init()
{
+ Object::init();
+ mode = IncludeEnums;
+ object.init();
}
-Heap::QmlTypeWrapper::~QmlTypeWrapper()
+void Heap::QmlTypeWrapper::destroy()
{
if (typeNamespace)
typeNamespace->release();
+ object.destroy();
+ Object::destroy();
}
bool QmlTypeWrapper::isSingleton() const
diff --git a/src/qml/qml/qqmltypewrapper_p.h b/src/qml/qml/qqmltypewrapper_p.h
index 8216526cb5..3b0ae04cc1 100644
--- a/src/qml/qml/qqmltypewrapper_p.h
+++ b/src/qml/qml/qqmltypewrapper_p.h
@@ -71,10 +71,10 @@ struct QmlTypeWrapper : Object {
ExcludeEnums
};
- QmlTypeWrapper();
- ~QmlTypeWrapper();
+ void init();
+ void destroy();
TypeNameMode mode;
- QPointer<QObject> object;
+ QQmlQPointer<QObject> object;
QQmlType *type;
QQmlTypeNameCache *typeNamespace;
diff --git a/src/qml/qml/qqmlvaluetype.cpp b/src/qml/qml/qqmlvaluetype.cpp
index 44fd47244d..bcefad0ee3 100644
--- a/src/qml/qml/qqmlvaluetype.cpp
+++ b/src/qml/qml/qqmlvaluetype.cpp
@@ -88,6 +88,7 @@ bool QQmlValueTypeFactoryImpl::isValueType(int idx)
&& idx != QVariant::StringList
&& idx != QMetaType::QObjectStar
&& idx != QMetaType::VoidStar
+ && idx != QMetaType::Nullptr
&& idx != QMetaType::QVariant
&& idx != QMetaType::QLocale) {
return true;
@@ -219,7 +220,7 @@ void QQmlValueType::read(QObject *obj, int idx)
QMetaObject::metacall(obj, QMetaObject::ReadProperty, idx, a);
}
-void QQmlValueType::write(QObject *obj, int idx, QQmlPropertyPrivate::WriteFlags flags)
+void QQmlValueType::write(QObject *obj, int idx, QQmlPropertyData::WriteFlags flags)
{
Q_ASSERT(gadgetPtr);
int status = -1;
@@ -259,7 +260,7 @@ int QQmlValueType::metaCall(QObject *, QMetaObject::Call type, int _id, void **a
QString QQmlPointFValueType::toString() const
{
- return QString(QLatin1String("QPointF(%1, %2)")).arg(v.x()).arg(v.y());
+ return QString::asprintf("QPointF(%g, %g)", v.x(), v.y());
}
qreal QQmlPointFValueType::x() const
@@ -306,7 +307,7 @@ void QQmlPointValueType::setY(int y)
QString QQmlSizeFValueType::toString() const
{
- return QString(QLatin1String("QSizeF(%1, %2)")).arg(v.width()).arg(v.height());
+ return QString::asprintf("QSizeF(%g, %g)", v.width(), v.height());
}
qreal QQmlSizeFValueType::width() const
@@ -352,7 +353,7 @@ void QQmlSizeValueType::setHeight(int h)
QString QQmlRectFValueType::toString() const
{
- return QString(QLatin1String("QRectF(%1, %2, %3, %4)")).arg(v.x()).arg(v.y()).arg(v.width()).arg(v.height());
+ return QString::asprintf("QRectF(%g, %g, %g, %g)", v.x(), v.y(), v.width(), v.height());
}
qreal QQmlRectFValueType::x() const
diff --git a/src/qml/qml/qqmlvaluetype_p.h b/src/qml/qml/qqmlvaluetype_p.h
index 910d39cf0a..11e1dfdb00 100644
--- a/src/qml/qml/qqmlvaluetype_p.h
+++ b/src/qml/qml/qqmlvaluetype_p.h
@@ -69,7 +69,7 @@ public:
QQmlValueType(int userType, const QMetaObject *metaObject);
~QQmlValueType();
void read(QObject *, int);
- void write(QObject *, int, QQmlPropertyPrivate::WriteFlags flags);
+ void write(QObject *, int, QQmlPropertyData::WriteFlags flags);
QVariant value();
void setValue(const QVariant &);
diff --git a/src/qml/qml/qqmlvaluetypeproxybinding.cpp b/src/qml/qml/qqmlvaluetypeproxybinding.cpp
index 6858215a79..56f073121e 100644
--- a/src/qml/qml/qqmlvaluetypeproxybinding.cpp
+++ b/src/qml/qml/qqmlvaluetypeproxybinding.cpp
@@ -41,7 +41,7 @@
QT_BEGIN_NAMESPACE
-QQmlValueTypeProxyBinding::QQmlValueTypeProxyBinding(QObject *o, int index)
+QQmlValueTypeProxyBinding::QQmlValueTypeProxyBinding(QObject *o, QQmlPropertyIndex index)
: QQmlAbstractBinding(),
m_bindings(0)
{
@@ -58,7 +58,7 @@ QQmlValueTypeProxyBinding::~QQmlValueTypeProxyBinding()
}
}
-void QQmlValueTypeProxyBinding::setEnabled(bool e, QQmlPropertyPrivate::WriteFlags flags)
+void QQmlValueTypeProxyBinding::setEnabled(bool e, QQmlPropertyData::WriteFlags flags)
{
QQmlAbstractBinding *b = m_bindings.data();
while (b) {
@@ -72,7 +72,7 @@ bool QQmlValueTypeProxyBinding::isValueTypeProxy() const
return true;
}
-QQmlAbstractBinding *QQmlValueTypeProxyBinding::binding(int propertyIndex)
+QQmlAbstractBinding *QQmlValueTypeProxyBinding::binding(QQmlPropertyIndex propertyIndex)
{
QQmlAbstractBinding *binding = m_bindings.data();
@@ -91,7 +91,7 @@ void QQmlValueTypeProxyBinding::removeBindings(quint32 mask)
QQmlAbstractBinding *lastBinding = 0;
while (binding) {
- int valueTypeIndex = QQmlPropertyData::decodeValueTypePropertyIndex(binding->targetPropertyIndex());
+ const int valueTypeIndex = binding->targetPropertyIndex().valueTypeIndex();
if (valueTypeIndex != -1 && (mask & (1 << valueTypeIndex))) {
QQmlAbstractBinding *remove = binding;
remove->setAddedToObject(false);
diff --git a/src/qml/qml/qqmlvaluetypeproxybinding_p.h b/src/qml/qml/qqmlvaluetypeproxybinding_p.h
index de5acc2984..9a487d6992 100644
--- a/src/qml/qml/qqmlvaluetypeproxybinding_p.h
+++ b/src/qml/qml/qqmlvaluetypeproxybinding_p.h
@@ -58,12 +58,12 @@ QT_BEGIN_NAMESPACE
class QQmlValueTypeProxyBinding : public QQmlAbstractBinding
{
public:
- QQmlValueTypeProxyBinding(QObject *o, int coreIndex);
+ QQmlValueTypeProxyBinding(QObject *o, QQmlPropertyIndex coreIndex);
- QQmlAbstractBinding *binding(int targetPropertyIndex);
+ QQmlAbstractBinding *binding(QQmlPropertyIndex targetPropertyIndex);
void removeBindings(quint32 mask);
- virtual void setEnabled(bool, QQmlPropertyPrivate::WriteFlags);
+ virtual void setEnabled(bool, QQmlPropertyData::WriteFlags);
virtual bool isValueTypeProxy() const;
protected:
diff --git a/src/qml/qml/qqmlvaluetypewrapper.cpp b/src/qml/qml/qqmlvaluetypewrapper.cpp
index 04a556f46c..b23bc033d1 100644
--- a/src/qml/qml/qqmlvaluetypewrapper.cpp
+++ b/src/qml/qml/qqmlvaluetypewrapper.cpp
@@ -50,6 +50,7 @@
#include <private/qv4functionobject_p.h>
#include <private/qv4variantobject_p.h>
#include <private/qv4alloca_p.h>
+#include <private/qv4objectiterator_p.h>
#include <private/qv4qobjectwrapper_p.h>
QT_BEGIN_NAMESPACE
@@ -62,8 +63,15 @@ namespace Heap {
struct QQmlValueTypeReference : QQmlValueTypeWrapper
{
- QQmlValueTypeReference() {}
- QPointer<QObject> object;
+ void init() {
+ QQmlValueTypeWrapper::init();
+ object.init();
+ }
+ void destroy() {
+ object.destroy();
+ QQmlValueTypeWrapper::destroy();
+ }
+ QQmlQPointer<QObject> object;
int property;
};
@@ -72,8 +80,7 @@ struct QQmlValueTypeReference : QQmlValueTypeWrapper
struct QQmlValueTypeReference : public QQmlValueTypeWrapper
{
V4_OBJECT2(QQmlValueTypeReference, QQmlValueTypeWrapper)
-
- static void destroy(Heap::Base *that);
+ V4_NEEDS_DESTROY
bool readReferenceValue() const;
};
@@ -84,12 +91,13 @@ DEFINE_OBJECT_VTABLE(QV4::QQmlValueTypeReference);
using namespace QV4;
-Heap::QQmlValueTypeWrapper::~QQmlValueTypeWrapper()
+void Heap::QQmlValueTypeWrapper::destroy()
{
if (gadgetPtr) {
valueType->metaType.destruct(gadgetPtr);
::operator delete(gadgetPtr);
}
+ Object::destroy();
}
void Heap::QQmlValueTypeWrapper::setValue(const QVariant &value) const
@@ -138,7 +146,7 @@ bool QQmlValueTypeReference::readReferenceValue() const
::operator delete(d()->gadgetPtr);
}
d()->gadgetPtr =0;
- d()->propertyCache = cache;
+ d()->setPropertyCache(cache);
d()->valueType = QQmlValueTypeFactory::valueType(variantReferenceType);
if (!cache)
return false;
@@ -161,7 +169,7 @@ bool QQmlValueTypeReference::readReferenceValue() const
void QQmlValueTypeWrapper::initProto(ExecutionEngine *v4)
{
- if (v4->valueTypeWrapperPrototype()->d())
+ if (v4->valueTypeWrapperPrototype()->d_unchecked())
return;
Scope scope(v4);
@@ -178,7 +186,7 @@ ReturnedValue QQmlValueTypeWrapper::create(ExecutionEngine *engine, QObject *obj
Scoped<QQmlValueTypeReference> r(scope, engine->memoryManager->allocObject<QQmlValueTypeReference>());
r->d()->object = object;
r->d()->property = property;
- r->d()->propertyCache = QJSEnginePrivate::get(engine)->cache(metaObject);
+ r->d()->setPropertyCache(QJSEnginePrivate::get(engine)->cache(metaObject));
r->d()->valueType = QQmlValueTypeFactory::valueType(typeId);
r->d()->gadgetPtr = 0;
return r->asReturnedValue();
@@ -190,7 +198,7 @@ ReturnedValue QQmlValueTypeWrapper::create(ExecutionEngine *engine, const QVaria
initProto(engine);
Scoped<QQmlValueTypeWrapper> r(scope, engine->memoryManager->allocObject<QQmlValueTypeWrapper>());
- r->d()->propertyCache = QJSEnginePrivate::get(engine)->cache(metaObject);
+ r->d()->setPropertyCache(QJSEnginePrivate::get(engine)->cache(metaObject));
r->d()->valueType = QQmlValueTypeFactory::valueType(typeId);
r->d()->gadgetPtr = 0;
r->d()->setValue(value);
@@ -216,19 +224,13 @@ bool QQmlValueTypeWrapper::toGadget(void *data) const
return true;
}
-void QQmlValueTypeWrapper::destroy(Heap::Base *that)
-{
- Heap::QQmlValueTypeWrapper *w = static_cast<Heap::QQmlValueTypeWrapper *>(that);
- w->Heap::QQmlValueTypeWrapper::~QQmlValueTypeWrapper();
-}
-
bool QQmlValueTypeWrapper::isEqualTo(Managed *m, Managed *other)
{
Q_ASSERT(m && m->as<QQmlValueTypeWrapper>() && other);
QV4::QQmlValueTypeWrapper *lv = static_cast<QQmlValueTypeWrapper *>(m);
if (QV4::VariantObject *rv = other->as<VariantObject>())
- return lv->isEqual(rv->d()->data);
+ return lv->isEqual(rv->d()->data());
if (QV4::QQmlValueTypeWrapper *v = other->as<QQmlValueTypeWrapper>())
return lv->isEqual(v->toVariant());
@@ -241,10 +243,38 @@ PropertyAttributes QQmlValueTypeWrapper::query(const Managed *m, String *name)
Q_ASSERT(m->as<const QQmlValueTypeWrapper>());
const QQmlValueTypeWrapper *r = static_cast<const QQmlValueTypeWrapper *>(m);
- QQmlPropertyData *result = r->d()->propertyCache->property(name, 0, 0);
+ QQmlPropertyData *result = r->d()->propertyCache()->property(name, 0, 0);
return result ? Attr_Data : Attr_Invalid;
}
+void QQmlValueTypeWrapper::advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes)
+{
+ name->setM(0);
+ *index = UINT_MAX;
+
+ QQmlValueTypeWrapper *that = static_cast<QQmlValueTypeWrapper*>(m);
+
+ if (QQmlValueTypeReference *ref = that->as<QQmlValueTypeReference>()) {
+ if (!ref->readReferenceValue())
+ return;
+ }
+
+ if (that->d()->propertyCache()) {
+ const QMetaObject *mo = that->d()->propertyCache()->createMetaObject();
+ const int propertyCount = mo->propertyCount();
+ if (it->arrayIndex < static_cast<uint>(propertyCount)) {
+ Scope scope(that->engine());
+ ScopedString propName(scope, that->engine()->newString(QString::fromUtf8(mo->property(it->arrayIndex).name())));
+ name->setM(propName->d());
+ ++it->arrayIndex;
+ *attributes = QV4::Attr_Data;
+ p->value = that->QV4::Object::get(propName);
+ return;
+ }
+ }
+ QV4::Object::advanceIterator(m, it, name, index, p, attributes);
+}
+
bool QQmlValueTypeWrapper::isEqual(const QVariant& value)
{
if (QQmlValueTypeReference *ref = as<QQmlValueTypeReference>())
@@ -303,9 +333,9 @@ ReturnedValue QQmlValueTypeWrapper::method_toString(CallContext *ctx)
if (QMetaType::convert(w->d()->gadgetPtr, w->d()->valueType->typeId, &convertResult, QMetaType::QString)) {
result = convertResult;
} else {
- result = QString::fromUtf8(QMetaType::typeName(w->d()->valueType->typeId));
- result += QLatin1Char('(');
- const QMetaObject *mo = w->d()->propertyCache->metaObject();
+ result += QString::fromUtf8(QMetaType::typeName(w->d()->valueType->typeId))
+ + QLatin1Char('(');
+ const QMetaObject *mo = w->d()->propertyCache()->metaObject();
const int propCount = mo->propertyCount();
for (int i = 0; i < propCount; ++i) {
if (mo->property(i).isDesignable()) {
@@ -332,7 +362,7 @@ ReturnedValue QQmlValueTypeWrapper::get(const Managed *m, String *name, bool *ha
return Primitive::undefinedValue().asReturnedValue();
}
- QQmlPropertyData *result = r->d()->propertyCache->property(name, 0, 0);
+ QQmlPropertyData *result = r->d()->propertyCache()->property(name, 0, 0);
if (!result)
return Object::get(m, name, hasProperty);
@@ -341,19 +371,19 @@ ReturnedValue QQmlValueTypeWrapper::get(const Managed *m, String *name, bool *ha
if (result->isFunction())
// calling a Q_INVOKABLE function of a value type
- return QV4::QObjectMethod::create(v4->rootContext(), r, result->coreIndex);
+ return QV4::QObjectMethod::create(v4->rootContext(), r, result->coreIndex());
#define VALUE_TYPE_LOAD(metatype, cpptype, constructor) \
- if (result->propType == metatype) { \
+ if (result->propType() == metatype) { \
cpptype v; \
void *args[] = { &v, 0 }; \
metaObject->d.static_metacall(reinterpret_cast<QObject*>(gadget), QMetaObject::ReadProperty, index, args); \
return QV4::Encode(constructor(v)); \
}
- const QMetaObject *metaObject = r->d()->propertyCache->metaObject();
+ const QMetaObject *metaObject = r->d()->propertyCache()->metaObject();
- int index = result->coreIndex;
+ int index = result->coreIndex();
QQmlMetaObject::resolveGadgetMethodOrPropertyIndex(QMetaObject::ReadProperty, &metaObject, &index);
void *gadget = r->d()->gadgetPtr;
@@ -366,10 +396,10 @@ ReturnedValue QQmlValueTypeWrapper::get(const Managed *m, String *name, bool *ha
QVariant v;
void *args[] = { Q_NULLPTR, Q_NULLPTR };
- if (result->propType == QMetaType::QVariant) {
+ if (result->propType() == QMetaType::QVariant) {
args[0] = &v;
} else {
- v = QVariant(result->propType, static_cast<void *>(Q_NULLPTR));
+ v = QVariant(result->propType(), static_cast<void *>(Q_NULLPTR));
args[0] = v.data();
}
metaObject->d.static_metacall(reinterpret_cast<QObject*>(gadget), QMetaObject::ReadProperty, index, args);
@@ -399,12 +429,10 @@ void QQmlValueTypeWrapper::put(Managed *m, String *name, const Value &value)
writeBackPropertyType = writebackProperty.userType();
}
- const QMetaObject *metaObject = r->d()->propertyCache->metaObject();
- const QQmlPropertyData *pd = r->d()->propertyCache->property(name, 0, 0);
+ const QMetaObject *metaObject = r->d()->propertyCache()->metaObject();
+ const QQmlPropertyData *pd = r->d()->propertyCache()->property(name, 0, 0);
if (!pd)
return;
- QMetaProperty property = metaObject->property(pd->coreIndex);
- Q_ASSERT(property.isValid());
if (reference) {
QV4::ScopedFunctionObject f(scope, value);
@@ -420,27 +448,24 @@ void QQmlValueTypeWrapper::put(Managed *m, String *name, const Value &value)
QQmlContextData *context = v4->callingQmlContext();
QQmlPropertyData cacheData;
- cacheData.setFlags(QQmlPropertyData::IsWritable |
- QQmlPropertyData::IsValueTypeVirtual);
- cacheData.propType = writeBackPropertyType;
- cacheData.coreIndex = reference->d()->property;
- cacheData.valueTypeFlags = 0;
- cacheData.valueTypeCoreIndex = pd->coreIndex;
- cacheData.valueTypePropType = property.userType();
+ cacheData.setWritable(true);
+ cacheData.setPropType(writeBackPropertyType);
+ cacheData.setCoreIndex(reference->d()->property);
QV4::Scoped<QQmlBindingFunction> bindingFunction(scope, (const Value &)f);
bindingFunction->initBindingLocation();
- QQmlBinding *newBinding = new QQmlBinding(value, reference->d()->object, context);
- newBinding->setTarget(reference->d()->object, cacheData);
+ QQmlBinding *newBinding = QQmlBinding::create(&cacheData, value, reference->d()->object, context);
+ newBinding->setTarget(reference->d()->object, cacheData, pd);
QQmlPropertyPrivate::setBinding(newBinding);
return;
} else {
- QQmlPropertyPrivate::removeBinding(reference->d()->object, QQmlPropertyData::encodeValueTypePropertyIndex(reference->d()->property, pd->coreIndex));
-
+ QQmlPropertyPrivate::removeBinding(reference->d()->object, QQmlPropertyIndex(reference->d()->property, pd->coreIndex()));
}
}
+ QMetaProperty property = metaObject->property(pd->coreIndex());
+ Q_ASSERT(property.isValid());
QVariant v = v4->toVariant(value, property.userType());
@@ -469,9 +494,4 @@ void QQmlValueTypeWrapper::put(Managed *m, String *name, const Value &value)
}
}
-void QQmlValueTypeReference::destroy(Heap::Base *that)
-{
- static_cast<Heap::QQmlValueTypeReference*>(that)->Heap::QQmlValueTypeReference::~QQmlValueTypeReference();
-}
-
QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmlvaluetypewrapper_p.h b/src/qml/qml/qqmlvaluetypewrapper_p.h
index c2861f5bfa..b8ca5a16f4 100644
--- a/src/qml/qml/qqmlvaluetypewrapper_p.h
+++ b/src/qml/qml/qqmlvaluetypewrapper_p.h
@@ -66,14 +66,24 @@ namespace QV4 {
namespace Heap {
struct QQmlValueTypeWrapper : Object {
- QQmlValueTypeWrapper() {}
- ~QQmlValueTypeWrapper();
- QQmlRefPointer<QQmlPropertyCache> propertyCache;
+ void init() { Object::init(); }
+ void destroy();
+ QQmlPropertyCache *propertyCache() const { return _propertyCache; }
+ void setPropertyCache(QQmlPropertyCache *c) {
+ if (c)
+ c->addref();
+ if (_propertyCache)
+ _propertyCache->release();
+ _propertyCache = c;
+ }
mutable void *gadgetPtr;
QQmlValueType *valueType;
void setValue(const QVariant &value) const;
QVariant toVariant() const;
+
+private:
+ QQmlPropertyCache *_propertyCache;
};
}
@@ -82,7 +92,7 @@ struct Q_QML_EXPORT QQmlValueTypeWrapper : Object
{
V4_OBJECT2(QQmlValueTypeWrapper, Object)
V4_PROTOTYPE(valueTypeWrapperPrototype)
- static void destroy(Heap::Base *b);
+ V4_NEEDS_DESTROY
public:
@@ -99,6 +109,7 @@ public:
static void put(Managed *m, String *name, const Value &value);
static bool isEqualTo(Managed *m, Managed *other);
static PropertyAttributes query(const Managed *, String *name);
+ static void advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes);
static QV4::ReturnedValue method_toString(CallContext *ctx);
diff --git a/src/qml/qml/qqmlvme.cpp b/src/qml/qml/qqmlvme.cpp
index 5f9fa69944..01c4f476d6 100644
--- a/src/qml/qml/qqmlvme.cpp
+++ b/src/qml/qml/qqmlvme.cpp
@@ -39,7 +39,6 @@
#include "qqmlvme_p.h"
-#include "qqmlcompiler_p.h"
#include "qqmlboundsignal_p.h"
#include "qqmlstringconverters_p.h"
#include <private/qmetaobjectbuilder_p.h>
diff --git a/src/qml/qml/qqmlvme_p.h b/src/qml/qml/qqmlvme_p.h
index ac9db5c046..99d63380ad 100644
--- a/src/qml/qml/qqmlvme_p.h
+++ b/src/qml/qml/qqmlvme_p.h
@@ -69,7 +69,6 @@ QT_BEGIN_NAMESPACE
class QObject;
class QJSValue;
class QQmlScriptData;
-class QQmlCompiledData;
class QQmlContextData;
namespace QQmlVMETypes {
@@ -84,10 +83,9 @@ namespace QQmlVMETypes {
struct State {
enum Flag { Deferred = 0x00000001 };
- State() : flags(0), context(0), compiledData(0), instructionStream(0) {}
+ State() : flags(0), context(0), instructionStream(0) {}
quint32 flags;
QQmlContextData *context;
- QQmlCompiledData *compiledData;
const char *instructionStream;
QBitField bindingSkipList;
};
diff --git a/src/qml/qml/qqmlvmemetaobject.cpp b/src/qml/qml/qqmlvmemetaobject.cpp
index 775309d04a..b08a0e5087 100644
--- a/src/qml/qml/qqmlvmemetaobject.cpp
+++ b/src/qml/qml/qqmlvmemetaobject.cpp
@@ -59,6 +59,32 @@
QT_BEGIN_NAMESPACE
+static void list_append(QQmlListProperty<QObject> *prop, QObject *o)
+{
+ QList<QObject *> *list = static_cast<QList<QObject *> *>(prop->data);
+ list->append(o);
+ static_cast<QQmlVMEMetaObject *>(prop->dummy1)->activate(prop->object, reinterpret_cast<quintptr>(prop->dummy2), 0);
+}
+
+static int list_count(QQmlListProperty<QObject> *prop)
+{
+ QList<QObject *> *list = static_cast<QList<QObject *> *>(prop->data);
+ return list->count();
+}
+
+static QObject *list_at(QQmlListProperty<QObject> *prop, int index)
+{
+ QList<QObject *> *list = static_cast<QList<QObject *> *>(prop->data);
+ return list->at(index);
+}
+
+static void list_clear(QQmlListProperty<QObject> *prop)
+{
+ QList<QObject *> *list = static_cast<QList<QObject *> *>(prop->data);
+ list->clear();
+ static_cast<QQmlVMEMetaObject *>(prop->dummy1)->activate(prop->object, reinterpret_cast<quintptr>(prop->dummy2), 0);
+}
+
QQmlVMEVariantQObjectPtr::QQmlVMEVariantQObjectPtr()
: QQmlGuard<QObject>(0), m_target(0), m_index(-1)
{
@@ -74,10 +100,10 @@ void QQmlVMEVariantQObjectPtr::objectDestroyed(QObject *)
return;
if (m_index >= 0) {
- QV4::ExecutionEngine *v4 = m_target->properties.engine();
+ QV4::ExecutionEngine *v4 = m_target->propertyAndMethodStorage.engine();
if (v4) {
QV4::Scope scope(v4);
- QV4::Scoped<QV4::MemberData> sp(scope, m_target->properties.value());
+ QV4::Scoped<QV4::MemberData> sp(scope, m_target->propertyAndMethodStorage.value());
if (sp)
*(sp->data() + m_index) = QV4::Primitive::nullValue();
}
@@ -115,22 +141,31 @@ void QQmlVMEMetaObjectEndpoint_callback(QQmlNotifierEndpoint *e, void **)
void QQmlVMEMetaObjectEndpoint::tryConnect()
{
+ Q_ASSERT(metaObject->compiledObject);
int aliasId = this - metaObject->aliasEndpoints;
if (metaObject.flag()) {
// This is actually notify
- int sigIdx = metaObject->methodOffset() + aliasId + metaObject->metaData->propertyCount;
+ int sigIdx = metaObject->methodOffset() + aliasId + metaObject->compiledObject->nProperties;
metaObject->activate(metaObject->object, sigIdx, 0);
} else {
- QQmlVMEMetaData::AliasData *d = metaObject->metaData->aliasData() + aliasId;
- if (!d->isObjectAlias()) {
+ const QV4::CompiledData::Alias *aliasData = &metaObject->compiledObject->aliasTable()[aliasId];
+ if (!aliasData->isObjectAlias()) {
QQmlContextData *ctxt = metaObject->ctxt;
- QObject *target = ctxt->idValues[d->contextIdx].data();
+ QObject *target = ctxt->idValues[aliasData->targetObjectId].data();
if (!target)
return;
- if (d->notifySignal != -1)
- connect(target, d->notifySignal, ctxt->engine);
+ QQmlData *targetDData = QQmlData::get(target, /*create*/false);
+ if (!targetDData)
+ return;
+ int coreIndex = QQmlPropertyIndex::fromEncoded(aliasData->encodedMetaPropertyIndex).coreIndex();
+ const QQmlPropertyData *pd = targetDData->propertyCache->property(coreIndex);
+ if (!pd)
+ return;
+
+ if (pd->notifyIndex() != -1)
+ connect(target, pd->notifyIndex(), ctxt->engine);
}
metaObject.setFlag();
@@ -163,10 +198,9 @@ QQmlInterceptorMetaObject::~QQmlInterceptorMetaObject()
}
-void QQmlInterceptorMetaObject::registerInterceptor(int index, int valueIndex, QQmlPropertyValueInterceptor *interceptor)
+void QQmlInterceptorMetaObject::registerInterceptor(QQmlPropertyIndex index, QQmlPropertyValueInterceptor *interceptor)
{
- interceptor->m_coreIndex = index;
- interceptor->m_valueTypeCoreIndex = valueIndex;
+ interceptor->m_propertyIndex = index;
interceptor->m_next = interceptors;
interceptors = interceptor;
}
@@ -184,14 +218,14 @@ int QQmlInterceptorMetaObject::metaCall(QObject *o, QMetaObject::Call c, int id,
bool QQmlInterceptorMetaObject::intercept(QMetaObject::Call c, int id, void **a)
{
if (c == QMetaObject::WriteProperty && interceptors &&
- !(*reinterpret_cast<int*>(a[3]) & QQmlPropertyPrivate::BypassInterceptor)) {
+ !(*reinterpret_cast<int*>(a[3]) & QQmlPropertyData::BypassInterceptor)) {
for (QQmlPropertyValueInterceptor *vi = interceptors; vi; vi = vi->m_next) {
- if (vi->m_coreIndex != id)
+ if (vi->m_propertyIndex.coreIndex() != id)
continue;
- int valueIndex = vi->m_valueTypeCoreIndex;
- int type = QQmlData::get(object)->propertyCache->property(id)->propType;
+ const int valueIndex = vi->m_propertyIndex.valueTypeIndex();
+ int type = QQmlData::get(object)->propertyCache->property(id)->propType();
if (type != QVariant::Invalid) {
if (valueIndex != -1) {
@@ -242,7 +276,7 @@ bool QQmlInterceptorMetaObject::intercept(QMetaObject::Call c, int id, void **a)
bool updated = false;
if (newComponentValue != prevComponentValue) {
valueProp.write(valueType, prevComponentValue);
- valueType->write(object, id, QQmlPropertyPrivate::DontRemoveBinding | QQmlPropertyPrivate::BypassInterceptor);
+ valueType->write(object, id, QQmlPropertyData::DontRemoveBinding | QQmlPropertyData::BypassInterceptor);
vi->write(newComponentValue);
updated = true;
@@ -278,136 +312,124 @@ QAbstractDynamicMetaObject *QQmlInterceptorMetaObject::toDynamicMetaObject(QObje
}
QQmlVMEMetaObject::QQmlVMEMetaObject(QObject *obj,
- QQmlPropertyCache *cache,
- const QQmlVMEMetaData *meta)
+ QQmlPropertyCache *cache, QV4::CompiledData::CompilationUnit *qmlCompilationUnit, int qmlObjectId)
: QQmlInterceptorMetaObject(obj, cache),
- ctxt(QQmlData::get(obj, true)->outerContext), metaData(meta),
- aliasEndpoints(0),
- methods(0)
+ ctxt(QQmlData::get(obj, true)->outerContext),
+ aliasEndpoints(0), compilationUnit(qmlCompilationUnit), compiledObject(0)
{
- cache->addref();
-
QQmlData::get(obj)->hasVMEMetaObject = true;
- int qobject_type = qMetaTypeId<QObject*>();
- int variant_type = qMetaTypeId<QVariant>();
- // Need JS wrapper to ensure properties are marked.
- // ### FIXME: I hope that this can be removed once we have the proper scope chain
- // set up and the JS wrappers always exist.
- bool needsJSWrapper = (metaData->propertyCount > 0);
-
- // ### Optimize
- for (int ii = 0; ii < metaData->propertyCount; ++ii) {
- int t = (metaData->propertyData() + ii)->propertyType;
- if (t == qobject_type || t == variant_type) {
- needsJSWrapper = true;
- break;
+ if (compilationUnit && qmlObjectId >= 0) {
+ compiledObject = compilationUnit->data->objectAt(qmlObjectId);
+
+ if (compiledObject->nProperties || compiledObject->nFunctions) {
+ Q_ASSERT(cache && cache->engine);
+ QV4::ExecutionEngine *v4 = cache->engine;
+ QV4::Heap::MemberData *data = QV4::MemberData::allocate(v4, compiledObject->nProperties + compiledObject->nFunctions);
+ propertyAndMethodStorage.set(v4, data);
+ std::fill(data->data, data->data + data->size, QV4::Encode::undefined());
+
+ // Need JS wrapper to ensure properties/methods are marked.
+ ensureQObjectWrapper();
}
}
-
- if (needsJSWrapper)
- ensureQObjectWrapper();
}
QQmlVMEMetaObject::~QQmlVMEMetaObject()
{
if (parent.isT1()) parent.asT1()->objectDestroyed(object);
delete [] aliasEndpoints;
- delete [] methods;
qDeleteAll(varObjectGuards);
-
- cache->release();
}
-QV4::MemberData *QQmlVMEMetaObject::propertiesAsMemberData()
+QV4::MemberData *QQmlVMEMetaObject::propertyAndMethodStorageAsMemberData()
{
- if (properties.isUndefined()) {
- if (properties.valueRef())
+ if (propertyAndMethodStorage.isUndefined()) {
+ if (propertyAndMethodStorage.valueRef())
// in some situations, the QObject wrapper (and associated data,
// such as the varProperties array) will have been cleaned up, but the
// QObject ptr will not yet have been deleted (eg, waiting on deleteLater).
// In this situation, return 0.
return 0;
- allocateProperties();
}
- return static_cast<QV4::MemberData*>(properties.asManaged());
+ return static_cast<QV4::MemberData*>(propertyAndMethodStorage.asManaged());
}
void QQmlVMEMetaObject::writeProperty(int id, int v)
{
- QV4::MemberData *md = propertiesAsMemberData();
+ QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
if (md)
*(md->data() + id) = QV4::Primitive::fromInt32(v);
}
void QQmlVMEMetaObject::writeProperty(int id, bool v)
{
- QV4::MemberData *md = propertiesAsMemberData();
+ QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
if (md)
*(md->data() + id) = QV4::Primitive::fromBoolean(v);
}
void QQmlVMEMetaObject::writeProperty(int id, double v)
{
- QV4::MemberData *md = propertiesAsMemberData();
+ QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
if (md)
*(md->data() + id) = QV4::Primitive::fromDouble(v);
}
void QQmlVMEMetaObject::writeProperty(int id, const QString& v)
{
- QV4::MemberData *md = propertiesAsMemberData();
+ QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
if (md)
*(md->data() + id) = cache->engine->newString(v);
}
void QQmlVMEMetaObject::writeProperty(int id, const QUrl& v)
{
- QV4::MemberData *md = propertiesAsMemberData();
+ QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
if (md)
*(md->data() + id) = cache->engine->newVariantObject(QVariant::fromValue(v));
}
void QQmlVMEMetaObject::writeProperty(int id, const QDate& v)
{
- QV4::MemberData *md = propertiesAsMemberData();
+ QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
if (md)
*(md->data() + id) = cache->engine->newVariantObject(QVariant::fromValue(v));
}
void QQmlVMEMetaObject::writeProperty(int id, const QDateTime& v)
{
- QV4::MemberData *md = propertiesAsMemberData();
+ QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
if (md)
*(md->data() + id) = cache->engine->newVariantObject(QVariant::fromValue(v));
}
void QQmlVMEMetaObject::writeProperty(int id, const QPointF& v)
{
- QV4::MemberData *md = propertiesAsMemberData();
+ QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
if (md)
*(md->data() + id) = cache->engine->newVariantObject(QVariant::fromValue(v));
}
void QQmlVMEMetaObject::writeProperty(int id, const QSizeF& v)
{
- QV4::MemberData *md = propertiesAsMemberData();
+ QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
if (md)
*(md->data() + id) = cache->engine->newVariantObject(QVariant::fromValue(v));
}
void QQmlVMEMetaObject::writeProperty(int id, const QRectF& v)
{
- QV4::MemberData *md = propertiesAsMemberData();
+ QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
if (md)
*(md->data() + id) = cache->engine->newVariantObject(QVariant::fromValue(v));
}
void QQmlVMEMetaObject::writeProperty(int id, QObject* v)
{
- QV4::MemberData *md = propertiesAsMemberData();
+ QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
if (md)
*(md->data() + id) = QV4::QObjectWrapper::wrap(cache->engine, v);
@@ -422,7 +444,7 @@ void QQmlVMEMetaObject::writeProperty(int id, QObject* v)
int QQmlVMEMetaObject::readPropertyAsInt(int id)
{
- QV4::MemberData *md = propertiesAsMemberData();
+ QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
if (!md)
return 0;
@@ -435,7 +457,7 @@ int QQmlVMEMetaObject::readPropertyAsInt(int id)
bool QQmlVMEMetaObject::readPropertyAsBool(int id)
{
- QV4::MemberData *md = propertiesAsMemberData();
+ QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
if (!md)
return false;
@@ -448,7 +470,7 @@ bool QQmlVMEMetaObject::readPropertyAsBool(int id)
double QQmlVMEMetaObject::readPropertyAsDouble(int id)
{
- QV4::MemberData *md = propertiesAsMemberData();
+ QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
if (!md)
return 0.0;
@@ -461,7 +483,7 @@ double QQmlVMEMetaObject::readPropertyAsDouble(int id)
QString QQmlVMEMetaObject::readPropertyAsString(int id)
{
- QV4::MemberData *md = propertiesAsMemberData();
+ QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
if (!md)
return QString();
@@ -474,77 +496,77 @@ QString QQmlVMEMetaObject::readPropertyAsString(int id)
QUrl QQmlVMEMetaObject::readPropertyAsUrl(int id)
{
- QV4::MemberData *md = propertiesAsMemberData();
+ QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
if (!md)
return QUrl();
QV4::Scope scope(cache->engine);
QV4::ScopedValue sv(scope, *(md->data() + id));
const QV4::VariantObject *v = sv->as<QV4::VariantObject>();
- if (!v || v->d()->data.type() != QVariant::Url)
+ if (!v || v->d()->data().type() != QVariant::Url)
return QUrl();
- return v->d()->data.value<QUrl>();
+ return v->d()->data().value<QUrl>();
}
QDate QQmlVMEMetaObject::readPropertyAsDate(int id)
{
- QV4::MemberData *md = propertiesAsMemberData();
+ QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
if (!md)
return QDate();
QV4::Scope scope(cache->engine);
QV4::ScopedValue sv(scope, *(md->data() + id));
const QV4::VariantObject *v = sv->as<QV4::VariantObject>();
- if (!v || v->d()->data.type() != QVariant::Date)
+ if (!v || v->d()->data().type() != QVariant::Date)
return QDate();
- return v->d()->data.value<QDate>();
+ return v->d()->data().value<QDate>();
}
QDateTime QQmlVMEMetaObject::readPropertyAsDateTime(int id)
{
- QV4::MemberData *md = propertiesAsMemberData();
+ QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
if (!md)
return QDateTime();
QV4::Scope scope(cache->engine);
QV4::ScopedValue sv(scope, *(md->data() + id));
const QV4::VariantObject *v = sv->as<QV4::VariantObject>();
- if (!v || v->d()->data.type() != QVariant::DateTime)
+ if (!v || v->d()->data().type() != QVariant::DateTime)
return QDateTime();
- return v->d()->data.value<QDateTime>();
+ return v->d()->data().value<QDateTime>();
}
QSizeF QQmlVMEMetaObject::readPropertyAsSizeF(int id)
{
- QV4::MemberData *md = propertiesAsMemberData();
+ QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
if (!md)
return QSizeF();
QV4::Scope scope(cache->engine);
QV4::ScopedValue sv(scope, *(md->data() + id));
const QV4::VariantObject *v = sv->as<QV4::VariantObject>();
- if (!v || v->d()->data.type() != QVariant::SizeF)
+ if (!v || v->d()->data().type() != QVariant::SizeF)
return QSizeF();
- return v->d()->data.value<QSizeF>();
+ return v->d()->data().value<QSizeF>();
}
QPointF QQmlVMEMetaObject::readPropertyAsPointF(int id)
{
- QV4::MemberData *md = propertiesAsMemberData();
+ QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
if (!md)
return QPointF();
QV4::Scope scope(cache->engine);
QV4::ScopedValue sv(scope, *(md->data() + id));
const QV4::VariantObject *v = sv->as<QV4::VariantObject>();
- if (!v || v->d()->data.type() != QVariant::PointF)
+ if (!v || v->d()->data().type() != QVariant::PointF)
return QPointF();
- return v->d()->data.value<QPointF>();
+ return v->d()->data().value<QPointF>();
}
QObject* QQmlVMEMetaObject::readPropertyAsQObject(int id)
{
- QV4::MemberData *md = propertiesAsMemberData();
+ QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
if (!md)
return 0;
@@ -558,34 +580,37 @@ QObject* QQmlVMEMetaObject::readPropertyAsQObject(int id)
QList<QObject *> *QQmlVMEMetaObject::readPropertyAsList(int id)
{
- QV4::MemberData *md = propertiesAsMemberData();
+ QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
if (!md)
return 0;
QV4::Scope scope(cache->engine);
QV4::Scoped<QV4::VariantObject> v(scope, *(md->data() + id));
- if (!v || (int)v->d()->data.userType() != qMetaTypeId<QList<QObject *> >()) {
+ if (!v || (int)v->d()->data().userType() != qMetaTypeId<QList<QObject *> >()) {
QVariant variant(qVariantFromValue(QList<QObject*>()));
v = cache->engine->newVariantObject(variant);
*(md->data() + id) = v;
}
- return static_cast<QList<QObject *> *>(v->d()->data.data());
+ return static_cast<QList<QObject *> *>(v->d()->data().data());
}
QRectF QQmlVMEMetaObject::readPropertyAsRectF(int id)
{
- QV4::MemberData *md = propertiesAsMemberData();
+ QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
if (!md)
return QRectF();
QV4::Scope scope(cache->engine);
QV4::ScopedValue sv(scope, *(md->data() + id));
const QV4::VariantObject *v = sv->as<QV4::VariantObject>();
- if (!v || v->d()->data.type() != QVariant::RectF)
+ if (!v || v->d()->data().type() != QVariant::RectF)
return QRectF();
- return v->d()->data.value<QRectF>();
+ return v->d()->data().value<QRectF>();
}
+#if defined(Q_OS_WINRT) && defined(_M_ARM)
+#pragma optimize("", off)
+#endif
int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void **a)
{
Q_ASSERT(o == object);
@@ -596,15 +621,20 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void *
if (intercept(c, _id, a))
return -1;
+ const int propertyCount = compiledObject ? int(compiledObject->nProperties) : 0;
+ const int aliasCount = compiledObject ? int(compiledObject->nAliases) : 0;
+ const int signalCount = compiledObject ? int(compiledObject->nSignals) : 0;
+ const int methodCount = compiledObject ? int(compiledObject->nFunctions) : 0;
+
if (c == QMetaObject::ReadProperty || c == QMetaObject::WriteProperty || c == QMetaObject::ResetProperty) {
if (id >= propOffset()) {
id -= propOffset();
- if (id < metaData->propertyCount) {
- int t = (metaData->propertyData() + id)->propertyType;
+ if (id < propertyCount) {
+ const QV4::CompiledData::Property::Type t = static_cast<QV4::CompiledData::Property::Type>(qint32(compiledObject->propertyTable()[id].type));
bool needActivate = false;
- if (t == QQmlVMEMetaData::VarPropertyType) {
+ if (t == QV4::CompiledData::Property::Var) {
// the context can be null if accessing var properties from cpp after re-parenting an item.
QQmlEnginePrivate *ep = (ctxt == 0 || ctxt->engine == 0) ? 0 : QQmlEnginePrivate::get(ctxt->engine);
QV8Engine *v8e = (ep == 0) ? 0 : ep->v8engine();
@@ -620,132 +650,180 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void *
}
} else {
+ int fallbackMetaType = QMetaType::UnknownType;
+ switch (t) {
+ case QV4::CompiledData::Property::Font:
+ fallbackMetaType = QMetaType::QFont;
+ break;
+ case QV4::CompiledData::Property::Time:
+ fallbackMetaType = QMetaType::QTime;
+ break;
+ case QV4::CompiledData::Property::Color:
+ fallbackMetaType = QMetaType::QColor;
+ break;
+ case QV4::CompiledData::Property::Vector2D:
+ fallbackMetaType = QMetaType::QVector2D;
+ break;
+ case QV4::CompiledData::Property::Vector3D:
+ fallbackMetaType = QMetaType::QVector3D;
+ break;
+ case QV4::CompiledData::Property::Vector4D:
+ fallbackMetaType = QMetaType::QVector4D;
+ break;
+ case QV4::CompiledData::Property::Matrix4x4:
+ fallbackMetaType = QMetaType::QMatrix4x4;
+ break;
+ case QV4::CompiledData::Property::Quaternion:
+ fallbackMetaType = QMetaType::QQuaternion;
+ break;
+ default: break;
+ }
+
if (c == QMetaObject::ReadProperty) {
- switch(t) {
- case QVariant::Int:
+ switch (t) {
+ case QV4::CompiledData::Property::Int:
*reinterpret_cast<int *>(a[0]) = readPropertyAsInt(id);
break;
- case QVariant::Bool:
+ case QV4::CompiledData::Property::Bool:
*reinterpret_cast<bool *>(a[0]) = readPropertyAsBool(id);
break;
- case QVariant::Double:
+ case QV4::CompiledData::Property::Real:
*reinterpret_cast<double *>(a[0]) = readPropertyAsDouble(id);
break;
- case QVariant::String:
+ case QV4::CompiledData::Property::String:
*reinterpret_cast<QString *>(a[0]) = readPropertyAsString(id);
break;
- case QVariant::Url:
+ case QV4::CompiledData::Property::Url:
*reinterpret_cast<QUrl *>(a[0]) = readPropertyAsUrl(id);
break;
- case QVariant::Date:
+ case QV4::CompiledData::Property::Date:
*reinterpret_cast<QDate *>(a[0]) = readPropertyAsDate(id);
break;
- case QVariant::DateTime:
+ case QV4::CompiledData::Property::DateTime:
*reinterpret_cast<QDateTime *>(a[0]) = readPropertyAsDateTime(id);
break;
- case QVariant::RectF:
+ case QV4::CompiledData::Property::Rect:
*reinterpret_cast<QRectF *>(a[0]) = readPropertyAsRectF(id);
break;
- case QVariant::SizeF:
+ case QV4::CompiledData::Property::Size:
*reinterpret_cast<QSizeF *>(a[0]) = readPropertyAsSizeF(id);
break;
- case QVariant::PointF:
+ case QV4::CompiledData::Property::Point:
*reinterpret_cast<QPointF *>(a[0]) = readPropertyAsPointF(id);
break;
- case QMetaType::QObjectStar:
+ case QV4::CompiledData::Property::Custom:
*reinterpret_cast<QObject **>(a[0]) = readPropertyAsQObject(id);
break;
- case QMetaType::QVariant:
+ case QV4::CompiledData::Property::Variant:
*reinterpret_cast<QVariant *>(a[0]) = readPropertyAsVariant(id);
break;
- default:
- {
- if (t == qMetaTypeId<QQmlListProperty<QObject> >()) {
- QList<QObject *> *list = readPropertyAsList(id);
- QQmlListProperty<QObject> *p = static_cast<QQmlListProperty<QObject> *>(a[0]);
- *p = QQmlListProperty<QObject>(object, list,
- list_append, list_count, list_at,
- list_clear);
- p->dummy1 = this;
- p->dummy2 = reinterpret_cast<void *>(quintptr(methodOffset() + id));
- } else {
- QV4::MemberData *md = propertiesAsMemberData();
- if (md) {
- QVariant propertyAsVariant;
- if (QV4::VariantObject *v = (md->data() + id)->as<QV4::VariantObject>())
- propertyAsVariant = v->d()->data;
- QQml_valueTypeProvider()->readValueType(propertyAsVariant, a[0], t);
- }
- }
+ case QV4::CompiledData::Property::CustomList: {
+ QList<QObject *> *list = readPropertyAsList(id);
+ QQmlListProperty<QObject> *p = static_cast<QQmlListProperty<QObject> *>(a[0]);
+ *p = QQmlListProperty<QObject>(object, list,
+ list_append, list_count, list_at,
+ list_clear);
+ p->dummy1 = this;
+ p->dummy2 = reinterpret_cast<void *>(quintptr(methodOffset() + id));
break;
}
+ case QV4::CompiledData::Property::Font:
+ case QV4::CompiledData::Property::Time:
+ case QV4::CompiledData::Property::Color:
+ case QV4::CompiledData::Property::Vector2D:
+ case QV4::CompiledData::Property::Vector3D:
+ case QV4::CompiledData::Property::Vector4D:
+ case QV4::CompiledData::Property::Matrix4x4:
+ case QV4::CompiledData::Property::Quaternion:
+ Q_ASSERT(fallbackMetaType != QMetaType::UnknownType);
+ if (QV4::MemberData *md = propertyAndMethodStorageAsMemberData()) {
+ QVariant propertyAsVariant;
+ if (QV4::VariantObject *v = (md->data() + id)->as<QV4::VariantObject>())
+ propertyAsVariant = v->d()->data();
+ QQml_valueTypeProvider()->readValueType(propertyAsVariant, a[0], fallbackMetaType);
+ }
+ break;
+ case QV4::CompiledData::Property::Var:
+ Q_UNREACHABLE();
}
} else if (c == QMetaObject::WriteProperty) {
switch(t) {
- case QVariant::Int:
+ case QV4::CompiledData::Property::Int:
needActivate = *reinterpret_cast<int *>(a[0]) != readPropertyAsInt(id);
writeProperty(id, *reinterpret_cast<int *>(a[0]));
break;
- case QVariant::Bool:
+ case QV4::CompiledData::Property::Bool:
needActivate = *reinterpret_cast<bool *>(a[0]) != readPropertyAsBool(id);
writeProperty(id, *reinterpret_cast<bool *>(a[0]));
break;
- case QVariant::Double:
+ case QV4::CompiledData::Property::Real:
needActivate = *reinterpret_cast<double *>(a[0]) != readPropertyAsDouble(id);
writeProperty(id, *reinterpret_cast<double *>(a[0]));
break;
- case QVariant::String:
+ case QV4::CompiledData::Property::String:
needActivate = *reinterpret_cast<QString *>(a[0]) != readPropertyAsString(id);
writeProperty(id, *reinterpret_cast<QString *>(a[0]));
break;
- case QVariant::Url:
+ case QV4::CompiledData::Property::Url:
needActivate = *reinterpret_cast<QUrl *>(a[0]) != readPropertyAsUrl(id);
writeProperty(id, *reinterpret_cast<QUrl *>(a[0]));
break;
- case QVariant::Date:
+ case QV4::CompiledData::Property::Date:
needActivate = *reinterpret_cast<QDate *>(a[0]) != readPropertyAsDate(id);
writeProperty(id, *reinterpret_cast<QDate *>(a[0]));
break;
- case QVariant::DateTime:
+ case QV4::CompiledData::Property::DateTime:
needActivate = *reinterpret_cast<QDateTime *>(a[0]) != readPropertyAsDateTime(id);
writeProperty(id, *reinterpret_cast<QDateTime *>(a[0]));
break;
- case QVariant::RectF:
+ case QV4::CompiledData::Property::Rect:
needActivate = *reinterpret_cast<QRectF *>(a[0]) != readPropertyAsRectF(id);
writeProperty(id, *reinterpret_cast<QRectF *>(a[0]));
break;
- case QVariant::SizeF:
+ case QV4::CompiledData::Property::Size:
needActivate = *reinterpret_cast<QSizeF *>(a[0]) != readPropertyAsSizeF(id);
writeProperty(id, *reinterpret_cast<QSizeF *>(a[0]));
break;
- case QVariant::PointF:
+ case QV4::CompiledData::Property::Point:
needActivate = *reinterpret_cast<QPointF *>(a[0]) != readPropertyAsPointF(id);
writeProperty(id, *reinterpret_cast<QPointF *>(a[0]));
break;
- case QMetaType::QObjectStar:
+ case QV4::CompiledData::Property::Custom:
needActivate = *reinterpret_cast<QObject **>(a[0]) != readPropertyAsQObject(id);
writeProperty(id, *reinterpret_cast<QObject **>(a[0]));
break;
- case QMetaType::QVariant:
+ case QV4::CompiledData::Property::Variant:
writeProperty(id, *reinterpret_cast<QVariant *>(a[0]));
break;
- default: {
- QV4::MemberData *md = propertiesAsMemberData();
- if (md) {
+ case QV4::CompiledData::Property::CustomList:
+ // Writing such a property is not supported. Content is added through the list property
+ // methods.
+ break;
+ case QV4::CompiledData::Property::Font:
+ case QV4::CompiledData::Property::Time:
+ case QV4::CompiledData::Property::Color:
+ case QV4::CompiledData::Property::Vector2D:
+ case QV4::CompiledData::Property::Vector3D:
+ case QV4::CompiledData::Property::Vector4D:
+ case QV4::CompiledData::Property::Matrix4x4:
+ case QV4::CompiledData::Property::Quaternion:
+ Q_ASSERT(fallbackMetaType != QMetaType::UnknownType);
+ if (QV4::MemberData *md = propertyAndMethodStorageAsMemberData()) {
QV4::VariantObject *v = (md->data() + id)->as<QV4::VariantObject>();
if (!v) {
*(md->data() + id) = cache->engine->newVariantObject(QVariant());
v = (md->data() + id)->as<QV4::VariantObject>();
- QQml_valueTypeProvider()->initValueType(t, v->d()->data);
+ QQml_valueTypeProvider()->initValueType(fallbackMetaType, v->d()->data());
}
- needActivate = !QQml_valueTypeProvider()->equalValueType(t, a[0], v->d()->data);
- QQml_valueTypeProvider()->writeValueType(t, a[0], v->d()->data);
+ needActivate = !QQml_valueTypeProvider()->equalValueType(fallbackMetaType, a[0], v->d()->data());
+ QQml_valueTypeProvider()->writeValueType(fallbackMetaType, a[0], v->d()->data());
}
break;
- }
+ case QV4::CompiledData::Property::Var:
+ Q_UNREACHABLE();
}
}
@@ -758,56 +836,69 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void *
return -1;
}
- id -= metaData->propertyCount;
-
- if (id < metaData->aliasCount) {
+ id -= propertyCount;
- QQmlVMEMetaData::AliasData *d = metaData->aliasData() + id;
+ if (id < aliasCount) {
+ const QV4::CompiledData::Alias *aliasData = &compiledObject->aliasTable()[id];
- if (d->flags & QML_ALIAS_FLAG_PTR && c == QMetaObject::ReadProperty)
+ if ((aliasData->flags & QV4::CompiledData::Alias::AliasPointsToPointerObject) && c == QMetaObject::ReadProperty)
*reinterpret_cast<void **>(a[0]) = 0;
if (!ctxt) return -1;
+ while (aliasData->aliasToLocalAlias)
+ aliasData = &compiledObject->aliasTable()[aliasData->localAliasIndex];
+
QQmlContext *context = ctxt->asQQmlContext();
QQmlContextPrivate *ctxtPriv = QQmlContextPrivate::get(context);
- QObject *target = ctxtPriv->data->idValues[d->contextIdx].data();
+ QObject *target = ctxtPriv->data->idValues[aliasData->targetObjectId].data();
if (!target)
return -1;
connectAlias(id);
- if (d->isObjectAlias()) {
+ if (aliasData->isObjectAlias()) {
*reinterpret_cast<QObject **>(a[0]) = target;
return -1;
}
+ QQmlData *targetDData = QQmlData::get(target, /*create*/false);
+ if (!targetDData)
+ return -1;
+
+ QQmlPropertyIndex encodedIndex = QQmlPropertyIndex::fromEncoded(aliasData->encodedMetaPropertyIndex);
+ int coreIndex = encodedIndex.coreIndex();
+ const int valueTypePropertyIndex = encodedIndex.valueTypeIndex();
+
// Remove binding (if any) on write
if(c == QMetaObject::WriteProperty) {
int flags = *reinterpret_cast<int*>(a[3]);
- if (flags & QQmlPropertyPrivate::RemoveBindingOnAliasWrite) {
+ if (flags & QQmlPropertyData::RemoveBindingOnAliasWrite) {
QQmlData *targetData = QQmlData::get(target);
- if (targetData && targetData->hasBindingBit(d->propertyIndex()))
- QQmlPropertyPrivate::removeBinding(target, d->propertyIdx);
+ if (targetData && targetData->hasBindingBit(coreIndex))
+ QQmlPropertyPrivate::removeBinding(target, encodedIndex);
}
}
- if (d->isValueTypeAlias()) {
+ if (valueTypePropertyIndex != -1) {
+ if (!targetDData->propertyCache)
+ return -1;
+ const QQmlPropertyData *pd = targetDData->propertyCache->property(coreIndex);
// Value type property
- QQmlValueType *valueType = QQmlValueTypeFactory::valueType(d->valueType());
+ QQmlValueType *valueType = QQmlValueTypeFactory::valueType(pd->propType());
Q_ASSERT(valueType);
- valueType->read(target, d->propertyIndex());
- int rv = QMetaObject::metacall(valueType, c, d->valueTypeIndex(), a);
+ valueType->read(target, coreIndex);
+ int rv = QMetaObject::metacall(valueType, c, valueTypePropertyIndex, a);
if (c == QMetaObject::WriteProperty)
- valueType->write(target, d->propertyIndex(), 0x00);
+ valueType->write(target, coreIndex, 0x00);
return rv;
} else {
- return QMetaObject::metacall(target, c, d->propertyIndex(), a);
+ return QMetaObject::metacall(target, c, coreIndex, a);
}
}
@@ -820,8 +911,7 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void *
if (id >= methodOffset()) {
id -= methodOffset();
- int plainSignals = metaData->signalCount + metaData->propertyCount +
- metaData->aliasCount;
+ int plainSignals = signalCount + propertyCount + aliasCount;
if (id < plainSignals) {
activate(object, _id, a);
return -1;
@@ -829,7 +919,7 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void *
id -= plainSignals;
- if (id < metaData->methodCount) {
+ if (id < methodCount) {
if (!ctxt->engine)
return -1; // We can't run the method
@@ -845,31 +935,29 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void *
// are not rewritten correctly but this bug is deemed out-of-scope to fix for
// performance reasons; see QTBUG-24064) and thus compilation will have failed.
QQmlError e;
- e.setDescription(QString::fromLatin1("Exception occurred during compilation of "
- "function: %1")
- .arg(QString::fromUtf8(QMetaObject::method(_id)
- .methodSignature())));
+ e.setDescription(QLatin1String("Exception occurred during compilation of "
+ "function: ")
+ + QString::fromUtf8(QMetaObject::method(_id)
+ .methodSignature()));
ep->warning(e);
return -1; // The dynamic method with that id is not available.
}
- QQmlVMEMetaData::MethodData *data = metaData->methodData() + id;
-
- QV4::ScopedCallData callData(scope, data->parameterCount);
+ const unsigned int parameterCount = function->formalParameterCount();
+ QV4::ScopedCallData callData(scope, parameterCount);
callData->thisObject = ep->v8engine()->global();
- for (int ii = 0; ii < data->parameterCount; ++ii)
+ for (uint ii = 0; ii < parameterCount; ++ii)
callData->args[ii] = scope.engine->fromVariant(*(QVariant *)a[ii + 1]);
- QV4::ScopedValue result(scope);
- result = function->call(callData);
+ function->call(scope, callData);
if (scope.hasException()) {
QQmlError error = scope.engine->catchExceptionAsQmlError();
if (error.isValid())
ep->warning(error);
if (a[0]) *(QVariant *)a[0] = QVariant();
} else {
- if (a[0]) *(QVariant *)a[0] = scope.engine->toVariant(result, 0);
+ if (a[0]) *(QVariant *)a[0] = scope.engine->toVariant(scope.result, 0);
}
ep->dereferenceScarceResources(); // "release" scarce resources if top-level expression evaluation is complete.
@@ -884,25 +972,29 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void *
else
return object->qt_metacall(c, _id, a);
}
+#if defined(Q_OS_WINRT) && defined(_M_ARM)
+#pragma optimize("", on)
+#endif
QV4::ReturnedValue QQmlVMEMetaObject::method(int index)
{
- if (!ctxt || !ctxt->isValid()) {
+ if (!ctxt || !ctxt->isValid() || !compiledObject) {
qWarning("QQmlVMEMetaObject: Internal error - attempted to evaluate a function in an invalid context");
return QV4::Encode::undefined();
}
- if (!methods)
+ QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
+ if (!md)
return QV4::Encode::undefined();
- return methods[index].value();
+ return (md->data() + index + compiledObject->nProperties)->asReturnedValue();
}
QV4::ReturnedValue QQmlVMEMetaObject::readVarProperty(int id)
{
- Q_ASSERT((metaData->propertyData() + id)->propertyType == QQmlVMEMetaData::VarPropertyType);
+ Q_ASSERT(compiledObject && compiledObject->propertyTable()[id].type == QV4::CompiledData::Property::Var);
- QV4::MemberData *md = propertiesAsMemberData();
+ QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
if (md)
return (md->data() + id)->asReturnedValue();
return QV4::Primitive::undefinedValue().asReturnedValue();
@@ -910,14 +1002,14 @@ QV4::ReturnedValue QQmlVMEMetaObject::readVarProperty(int id)
QVariant QQmlVMEMetaObject::readPropertyAsVariant(int id)
{
- QV4::MemberData *md = propertiesAsMemberData();
+ QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
if (md) {
const QV4::QObjectWrapper *wrapper = (md->data() + id)->as<QV4::QObjectWrapper>();
if (wrapper)
return QVariant::fromValue(wrapper->object());
const QV4::VariantObject *v = (md->data() + id)->as<QV4::VariantObject>();
if (v)
- return v->d()->data;
+ return v->d()->data();
return cache->engine->toVariant(*(md->data() + id), -1);
}
return QVariant();
@@ -925,9 +1017,9 @@ QVariant QQmlVMEMetaObject::readPropertyAsVariant(int id)
void QQmlVMEMetaObject::writeVarProperty(int id, const QV4::Value &value)
{
- Q_ASSERT((metaData->propertyData() + id)->propertyType == QQmlVMEMetaData::VarPropertyType);
+ Q_ASSERT(compiledObject && compiledObject->propertyTable()[id].type == QV4::CompiledData::Property::Var);
- QV4::MemberData *md = propertiesAsMemberData();
+ QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
if (!md)
return;
@@ -965,8 +1057,8 @@ void QQmlVMEMetaObject::writeVarProperty(int id, const QV4::Value &value)
void QQmlVMEMetaObject::writeProperty(int id, const QVariant &value)
{
- if ((metaData->propertyData() + id)->propertyType == QQmlVMEMetaData::VarPropertyType) {
- QV4::MemberData *md = propertiesAsMemberData();
+ if (compiledObject && compiledObject->propertyTable()[id].type == QV4::CompiledData::Property::Var) {
+ QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
if (!md)
return;
@@ -996,12 +1088,12 @@ void QQmlVMEMetaObject::writeProperty(int id, const QVariant &value)
needActivate = readPropertyAsQObject(id) != o; // TODO: still correct?
writeProperty(id, o);
} else {
- QV4::MemberData *md = propertiesAsMemberData();
+ QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
if (md) {
QV4::VariantObject *v = (md->data() + id)->as<QV4::VariantObject>();
needActivate = (!v ||
- v->d()->data.userType() != value.userType() ||
- v->d()->data != value);
+ v->d()->data().userType() != value.userType() ||
+ v->d()->data() != value);
if (v)
v->removeVmePropertyReference();
*(md->data() + id) = cache->engine->newVariantObject(value);
@@ -1015,61 +1107,16 @@ void QQmlVMEMetaObject::writeProperty(int id, const QVariant &value)
}
}
-void QQmlVMEMetaObject::listChanged(int id)
-{
- activate(object, methodOffset() + id, 0);
-}
-
-void QQmlVMEMetaObject::list_append(QQmlListProperty<QObject> *prop, QObject *o)
-{
- QList<QObject *> *list = static_cast<QList<QObject *> *>(prop->data);
- list->append(o);
- static_cast<QQmlVMEMetaObject *>(prop->dummy1)->activate(prop->object, reinterpret_cast<quintptr>(prop->dummy2), 0);
-}
-
-int QQmlVMEMetaObject::list_count(QQmlListProperty<QObject> *prop)
-{
- QList<QObject *> *list = static_cast<QList<QObject *> *>(prop->data);
- return list->count();
-}
-
-QObject *QQmlVMEMetaObject::list_at(QQmlListProperty<QObject> *prop, int index)
-{
- QList<QObject *> *list = static_cast<QList<QObject *> *>(prop->data);
- return list->at(index);
-}
-
-void QQmlVMEMetaObject::list_clear(QQmlListProperty<QObject> *prop)
-{
- QList<QObject *> *list = static_cast<QList<QObject *> *>(prop->data);
- list->clear();
- static_cast<QQmlVMEMetaObject *>(prop->dummy1)->activate(prop->object, reinterpret_cast<quintptr>(prop->dummy2), 0);
-}
-
-quint16 QQmlVMEMetaObject::vmeMethodLineNumber(int index)
-{
- if (index < methodOffset()) {
- Q_ASSERT(parentVMEMetaObject());
- return parentVMEMetaObject()->vmeMethodLineNumber(index);
- }
-
- int plainSignals = metaData->signalCount + metaData->propertyCount + metaData->aliasCount;
- Q_ASSERT(index >= (methodOffset() + plainSignals) && index < (methodOffset() + plainSignals + metaData->methodCount));
-
- int rawIndex = index - methodOffset() - plainSignals;
-
- QQmlVMEMetaData::MethodData *data = metaData->methodData() + rawIndex;
- return data->lineNumber;
-}
-
QV4::ReturnedValue QQmlVMEMetaObject::vmeMethod(int index)
{
if (index < methodOffset()) {
Q_ASSERT(parentVMEMetaObject());
return parentVMEMetaObject()->vmeMethod(index);
}
- int plainSignals = metaData->signalCount + metaData->propertyCount + metaData->aliasCount;
- Q_ASSERT(index >= (methodOffset() + plainSignals) && index < (methodOffset() + plainSignals + metaData->methodCount));
+ if (!compiledObject)
+ return QV4::Primitive::undefinedValue().asReturnedValue();
+ const int plainSignals = compiledObject->nSignals + compiledObject->nProperties + compiledObject->nAliases;
+ Q_ASSERT(index >= (methodOffset() + plainSignals) && index < (methodOffset() + plainSignals + int(compiledObject->nFunctions)));
return method(index - methodOffset() - plainSignals);
}
@@ -1080,14 +1127,16 @@ void QQmlVMEMetaObject::setVmeMethod(int index, const QV4::Value &function)
Q_ASSERT(parentVMEMetaObject());
return parentVMEMetaObject()->setVmeMethod(index, function);
}
- int plainSignals = metaData->signalCount + metaData->propertyCount + metaData->aliasCount;
- Q_ASSERT(index >= (methodOffset() + plainSignals) && index < (methodOffset() + plainSignals + metaData->methodCount));
-
- if (!methods)
- methods = new QV4::PersistentValue[metaData->methodCount];
+ if (!compiledObject)
+ return;
+ const int plainSignals = compiledObject->nSignals + compiledObject->nProperties + compiledObject->nAliases;
+ Q_ASSERT(index >= (methodOffset() + plainSignals) && index < (methodOffset() + plainSignals + int(compiledObject->nFunctions)));
int methodIndex = index - methodOffset() - plainSignals;
- methods[methodIndex].set(function.as<QV4::Object>()->engine(), function);
+ QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
+ if (!md)
+ return;
+ *(md->data() + methodIndex + compiledObject->nProperties) = function;
}
QV4::ReturnedValue QQmlVMEMetaObject::vmeProperty(int index)
@@ -1122,26 +1171,15 @@ void QQmlVMEMetaObject::mark(QV4::ExecutionEngine *e)
if (v4 != e)
return;
- properties.markOnce(e);
+ propertyAndMethodStorage.markOnce(e);
if (QQmlVMEMetaObject *parent = parentVMEMetaObject())
parent->mark(e);
}
-void QQmlVMEMetaObject::allocateProperties()
-{
- Q_ASSERT(cache && cache->engine);
- Q_ASSERT(!properties.valueRef());
- QV4::ExecutionEngine *v4 = cache->engine;
- QV4::Heap::MemberData *data = QV4::MemberData::allocate(v4, metaData->propertyCount);
- properties.set(v4, data);
- for (uint i = 0; i < data->size; ++i)
- data->data[i] = QV4::Encode::undefined();
-}
-
bool QQmlVMEMetaObject::aliasTarget(int index, QObject **target, int *coreIndex, int *valueTypeIndex) const
{
- Q_ASSERT(index >= propOffset() + metaData->propertyCount);
+ Q_ASSERT(compiledObject && (index >= propOffset() + int(compiledObject->nProperties)));
*target = 0;
*coreIndex = -1;
@@ -1150,31 +1188,27 @@ bool QQmlVMEMetaObject::aliasTarget(int index, QObject **target, int *coreIndex,
if (!ctxt)
return false;
- QQmlVMEMetaData::AliasData *d = metaData->aliasData() + (index - propOffset() - metaData->propertyCount);
- QQmlContext *context = ctxt->asQQmlContext();
- QQmlContextPrivate *ctxtPriv = QQmlContextPrivate::get(context);
-
- *target = ctxtPriv->data->idValues[d->contextIdx].data();
+ const int aliasId = index - propOffset() - compiledObject->nProperties;
+ const QV4::CompiledData::Alias *aliasData = &compiledObject->aliasTable()[aliasId];
+ *target = ctxt->idValues[aliasData->targetObjectId].data();
if (!*target)
return false;
- if (d->isObjectAlias()) {
- } else if (d->isValueTypeAlias()) {
- *coreIndex = d->propertyIndex();
- *valueTypeIndex = d->valueTypeIndex();
- } else {
- *coreIndex = d->propertyIndex();
+ if (!aliasData->isObjectAlias()) {
+ QQmlPropertyIndex encodedIndex = QQmlPropertyIndex::fromEncoded(aliasData->encodedMetaPropertyIndex);
+ *coreIndex = encodedIndex.coreIndex();
+ *valueTypeIndex = encodedIndex.valueTypeIndex();
}
-
return true;
}
void QQmlVMEMetaObject::connectAlias(int aliasId)
{
+ Q_ASSERT(compiledObject);
if (!aliasEndpoints)
- aliasEndpoints = new QQmlVMEMetaObjectEndpoint[metaData->aliasCount];
+ aliasEndpoints = new QQmlVMEMetaObjectEndpoint[compiledObject->nAliases];
- QQmlVMEMetaData::AliasData *d = metaData->aliasData() + aliasId;
+ const QV4::CompiledData::Alias *aliasData = &compiledObject->aliasTable()[aliasId];
QQmlVMEMetaObjectEndpoint *endpoint = aliasEndpoints + aliasId;
if (endpoint->metaObject.data()) {
@@ -1184,14 +1218,15 @@ void QQmlVMEMetaObject::connectAlias(int aliasId)
}
endpoint->metaObject = this;
- endpoint->connect(&ctxt->idValues[d->contextIdx].bindings);
+ endpoint->connect(&ctxt->idValues[aliasData->targetObjectId].bindings);
endpoint->tryConnect();
}
void QQmlVMEMetaObject::connectAliasSignal(int index, bool indexInSignalRange)
{
- int aliasId = (index - (indexInSignalRange ? signalOffset() : methodOffset())) - metaData->propertyCount;
- if (aliasId < 0 || aliasId >= metaData->aliasCount)
+ Q_ASSERT(compiledObject);
+ int aliasId = (index - (indexInSignalRange ? signalOffset() : methodOffset())) - compiledObject->nProperties;
+ if (aliasId < 0 || aliasId >= int(compiledObject->nAliases))
return;
connectAlias(aliasId);
diff --git a/src/qml/qml/qqmlvmemetaobject_p.h b/src/qml/qml/qqmlvmemetaobject_p.h
index 98fdae60ee..031bfdfb9b 100644
--- a/src/qml/qml/qqmlvmemetaobject_p.h
+++ b/src/qml/qml/qqmlvmemetaobject_p.h
@@ -64,82 +64,18 @@
#include <private/qobject_p.h>
#include "qqmlguard_p.h"
-#include "qqmlcompiler_p.h"
#include "qqmlcontext_p.h"
+#include "qqmlpropertycache_p.h"
#include <private/qv8engine_p.h>
#include <private/qflagpointer_p.h>
+#include <private/qv4object_p.h>
#include <private/qv4value_p.h>
+#include <private/qqmlpropertyvalueinterceptor_p.h>
QT_BEGIN_NAMESPACE
-#define QML_ALIAS_FLAG_PTR 0x00000001
-
-struct QQmlVMEMetaData
-{
- short propertyCount;
- short aliasCount;
- short signalCount;
- short methodCount;
- // Make sure this structure is always aligned to int
-
- struct AliasData {
- int contextIdx;
- int propertyIdx;
- int propType;
- int flags;
- int notifySignal;
-
- bool isObjectAlias() const {
- return propertyIdx == -1;
- }
- bool isPropertyAlias() const {
- return !isObjectAlias() && valueTypeIndex() == -1;
- }
- bool isValueTypeAlias() const {
- return !isObjectAlias() && valueTypeIndex() != -1;
- }
- int propertyIndex() const {
- int index;
- QQmlPropertyData::decodeValueTypePropertyIndex(propertyIdx, &index);
- return index;
- }
- int valueTypeIndex() const {
- return QQmlPropertyData::decodeValueTypePropertyIndex(propertyIdx);
- }
- int valueType() const {
- return (valueTypeIndex() != -1) ? propType : 0;
- }
- };
-
- enum {
- VarPropertyType = -1
- };
-
- struct PropertyData {
- int propertyType;
- };
-
- struct MethodData {
- int runtimeFunctionIndex;
- int parameterCount;
- quint16 lineNumber;
- };
-
- PropertyData *propertyData() const {
- return (PropertyData *)(((char *)const_cast<QQmlVMEMetaData *>(this)) + sizeof(QQmlVMEMetaData));
- }
-
- AliasData *aliasData() const {
- return (AliasData *)(propertyData() + propertyCount);
- }
-
- MethodData *methodData() const {
- return (MethodData *)(aliasData() + aliasCount);
- }
-};
-
class QQmlVMEMetaObject;
class QQmlVMEVariantQObjectPtr : public QQmlGuard<QObject>
{
@@ -161,22 +97,31 @@ public:
QQmlInterceptorMetaObject(QObject *obj, QQmlPropertyCache *cache);
~QQmlInterceptorMetaObject();
- void registerInterceptor(int index, int valueIndex, QQmlPropertyValueInterceptor *interceptor);
+ void registerInterceptor(QQmlPropertyIndex index, QQmlPropertyValueInterceptor *interceptor);
static QQmlInterceptorMetaObject *get(QObject *obj);
- virtual QAbstractDynamicMetaObject *toDynamicMetaObject(QObject *o);
+ QAbstractDynamicMetaObject *toDynamicMetaObject(QObject *o) Q_DECL_OVERRIDE;
// Used by auto-tests for inspection
QQmlPropertyCache *propertyCache() const { return cache; }
+ bool intercepts(QQmlPropertyIndex propertyIndex) const
+ {
+ for (auto it = interceptors; it; it = it->m_next) {
+ if (it->m_propertyIndex == propertyIndex)
+ return true;
+ }
+ return false;
+ }
+
protected:
- virtual int metaCall(QObject *o, QMetaObject::Call c, int id, void **a);
+ int metaCall(QObject *o, QMetaObject::Call c, int id, void **a) Q_DECL_OVERRIDE;
bool intercept(QMetaObject::Call c, int id, void **a);
public:
QObject *object;
- QQmlPropertyCache *cache;
+ QQmlRefPointer<QQmlPropertyCache> cache;
QBiPointer<QDynamicMetaObjectData, const QMetaObject> parent;
QQmlPropertyValueInterceptor *interceptors;
@@ -195,18 +140,15 @@ inline QQmlInterceptorMetaObject *QQmlInterceptorMetaObject::get(QObject *obj)
return 0;
}
-class QQmlVMEVariant;
-class QQmlRefCount;
class QQmlVMEMetaObjectEndpoint;
class Q_QML_PRIVATE_EXPORT QQmlVMEMetaObject : public QQmlInterceptorMetaObject
{
public:
- QQmlVMEMetaObject(QObject *obj, QQmlPropertyCache *cache, const QQmlVMEMetaData *data);
+ QQmlVMEMetaObject(QObject *obj, QQmlPropertyCache *cache, QV4::CompiledData::CompilationUnit *qmlCompilationUnit, int qmlObjectId);
~QQmlVMEMetaObject();
bool aliasTarget(int index, QObject **target, int *coreIndex, int *valueTypeIndex) const;
QV4::ReturnedValue vmeMethod(int index);
- quint16 vmeMethodLineNumber(int index);
void setVmeMethod(int index, const QV4::Value &function);
QV4::ReturnedValue vmeProperty(int index);
void setVMEProperty(int index, const QV4::Value &v);
@@ -219,16 +161,11 @@ public:
static QQmlVMEMetaObject *getForSignal(QObject *o, int coreIndex);
protected:
- virtual int metaCall(QObject *o, QMetaObject::Call _c, int _id, void **_a);
+ int metaCall(QObject *o, QMetaObject::Call _c, int _id, void **_a) Q_DECL_OVERRIDE;
public:
- friend class QQmlVMEMetaObjectEndpoint;
- friend class QQmlVMEVariantQObjectPtr;
- friend class QQmlPropertyCache;
-
QQmlGuardedContextData ctxt;
- const QQmlVMEMetaData *metaData;
inline int propOffset() const;
inline int methodOffset() const;
inline int signalOffset() const;
@@ -236,9 +173,8 @@ public:
QQmlVMEMetaObjectEndpoint *aliasEndpoints;
- QV4::WeakValue properties;
- inline void allocateProperties();
- QV4::MemberData *propertiesAsMemberData();
+ QV4::WeakValue propertyAndMethodStorage;
+ QV4::MemberData *propertyAndMethodStorageAsMemberData();
int readPropertyAsInt(int id);
bool readPropertyAsBool(int id);
@@ -271,7 +207,6 @@ public:
void connectAlias(int aliasId);
- QV4::PersistentValue *methods;
QV4::ReturnedValue method(int);
QV4::ReturnedValue readVarProperty(int);
@@ -281,18 +216,17 @@ public:
inline QQmlVMEMetaObject *parentVMEMetaObject() const;
- void listChanged(int);
-
- static void list_append(QQmlListProperty<QObject> *, QObject *);
- static int list_count(QQmlListProperty<QObject> *);
- static QObject *list_at(QQmlListProperty<QObject> *, int);
- static void list_clear(QQmlListProperty<QObject> *);
-
void activate(QObject *, int, void **);
QList<QQmlVMEVariantQObjectPtr *> varObjectGuards;
QQmlVMEVariantQObjectPtr *getQObjectGuardForProperty(int) const;
+
+
+ // keep a reference to the compilation unit in order to still
+ // do property access when the context has been invalidated.
+ QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit;
+ const QV4::CompiledData::Object *compiledObject;
};
QQmlVMEMetaObject *QQmlVMEMetaObject::get(QObject *obj)
diff --git a/src/qml/qml/qqmlxmlhttprequest.cpp b/src/qml/qml/qqmlxmlhttprequest.cpp
index 24f49eb6e7..10b1cbcfd4 100644
--- a/src/qml/qml/qqmlxmlhttprequest.cpp
+++ b/src/qml/qml/qqmlxmlhttprequest.cpp
@@ -70,7 +70,7 @@
using namespace QV4;
-#ifndef QT_NO_XMLSTREAMREADER
+#if !defined(QT_NO_XMLSTREAMREADER) && QT_CONFIG(qml_network)
#define V4THROW_REFERENCE(string) { \
ScopedObject error(scope, ctx->engine()->newReferenceErrorObject(QStringLiteral(string))); \
@@ -158,7 +158,7 @@ class DocumentImpl : public QQmlRefCount, public NodeImpl
public:
DocumentImpl() : root(0) { type = Document; }
virtual ~DocumentImpl() {
- if (root) delete root;
+ delete root;
}
QString version;
@@ -174,33 +174,43 @@ public:
namespace Heap {
struct NamedNodeMap : Object {
- NamedNodeMap(NodeImpl *data, const QList<NodeImpl *> &list);
- ~NamedNodeMap() {
+ void init(NodeImpl *data, const QList<NodeImpl *> &list);
+ void destroy() {
+ delete listPtr;
if (d)
d->release();
+ Object::destroy();
}
- QList<NodeImpl *> list; // Only used in NamedNodeMap
+ QList<NodeImpl *> &list() {
+ if (listPtr == nullptr)
+ listPtr = new QList<NodeImpl *>;
+ return *listPtr;
+ }
+
+ QList<NodeImpl *> *listPtr; // Only used in NamedNodeMap
NodeImpl *d;
};
struct NodeList : Object {
- NodeList(NodeImpl *data);
- ~NodeList() {
+ void init(NodeImpl *data);
+ void destroy() {
if (d)
d->release();
+ Object::destroy();
}
NodeImpl *d;
};
struct NodePrototype : Object {
- NodePrototype();
+ void init();
};
struct Node : Object {
- Node(NodeImpl *data);
- ~Node() {
+ void init(NodeImpl *data);
+ void destroy() {
if (d)
d->release();
+ Object::destroy();
}
NodeImpl *d;
};
@@ -221,10 +231,11 @@ public:
static ReturnedValue getIndexed(const Managed *m, uint index, bool *hasProperty);
};
-Heap::NamedNodeMap::NamedNodeMap(NodeImpl *data, const QList<NodeImpl *> &list)
- : list(list)
- , d(data)
+void Heap::NamedNodeMap::init(NodeImpl *data, const QList<NodeImpl *> &list)
{
+ Object::init();
+ d = data;
+ this->list() = list;
if (d)
d->addref();
}
@@ -246,9 +257,10 @@ public:
};
-Heap::NodeList::NodeList(NodeImpl *data)
- : d(data)
+void Heap::NodeList::init(NodeImpl *data)
{
+ Object::init();
+ d = data;
if (d)
d->addref();
}
@@ -287,8 +299,9 @@ public:
};
-Heap::NodePrototype::NodePrototype()
+void Heap::NodePrototype::init()
{
+ Object::init();
Scope scope(internalClass->engine);
ScopedObject o(scope, this);
@@ -320,9 +333,10 @@ struct Node : public Object
bool isNull() const;
};
-Heap::Node::Node(NodeImpl *data)
- : d(data)
+void Heap::Node::init(NodeImpl *data)
{
+ Object::init();
+ d = data;
if (d)
d->addref();
}
@@ -502,7 +516,7 @@ ReturnedValue NodePrototype::method_get_firstChild(CallContext *ctx)
if (r->d()->d->children.isEmpty())
return Encode::null();
else
- return Node::create(scope.engine, r->d()->d->children.first());
+ return Node::create(scope.engine, r->d()->d->children.constFirst());
}
ReturnedValue NodePrototype::method_get_lastChild(CallContext *ctx)
@@ -515,7 +529,7 @@ ReturnedValue NodePrototype::method_get_lastChild(CallContext *ctx)
if (r->d()->d->children.isEmpty())
return Encode::null();
else
- return Node::create(scope.engine, r->d()->d->children.last());
+ return Node::create(scope.engine, r->d()->d->children.constLast());
}
ReturnedValue NodePrototype::method_get_previousSibling(CallContext *ctx)
@@ -715,7 +729,7 @@ ReturnedValue Text::method_isElementContentWhitespace(CallContext *ctx)
Scoped<Node> r(scope, ctx->thisObject().as<Node>());
if (!r) return Encode::undefined();
- return Encode(r->d()->d->data.trimmed().isEmpty());
+ return Encode(QStringRef(&r->d()->d->data).trimmed().isEmpty());
}
ReturnedValue Text::method_wholeText(CallContext *ctx)
@@ -877,10 +891,10 @@ ReturnedValue NamedNodeMap::getIndexed(const Managed *m, uint index, bool *hasPr
const NamedNodeMap *r = static_cast<const NamedNodeMap *>(m);
QV4::ExecutionEngine *v4 = r->engine();
- if ((int)index < r->d()->list.count()) {
+ if ((int)index < r->d()->list().count()) {
if (hasProperty)
*hasProperty = true;
- return Node::create(v4, r->d()->list.at(index));
+ return Node::create(v4, r->d()->list().at(index));
}
if (hasProperty)
*hasProperty = false;
@@ -895,14 +909,14 @@ ReturnedValue NamedNodeMap::get(const Managed *m, String *name, bool *hasPropert
name->makeIdentifier(v4);
if (name->equals(v4->id_length()))
- return Primitive::fromInt32(r->d()->list.count()).asReturnedValue();
+ return Primitive::fromInt32(r->d()->list().count()).asReturnedValue();
QString str = name->toQString();
- for (int ii = 0; ii < r->d()->list.count(); ++ii) {
- if (r->d()->list.at(ii)->name == str) {
+ for (int ii = 0; ii < r->d()->list().count(); ++ii) {
+ if (r->d()->list().at(ii)->name == str) {
if (hasProperty)
*hasProperty = true;
- return Node::create(v4, r->d()->list.at(ii));
+ return Node::create(v4, r->d()->list().at(ii));
}
}
@@ -1016,8 +1030,8 @@ public:
ReturnedValue abort(Object *thisObject, QQmlContextData *context);
void addHeader(const QString &, const QString &);
- QString header(const QString &name);
- QString headers();
+ QString header(const QString &name) const;
+ QString headers() const;
QString responseBody();
const QByteArray & rawResponseBody() const;
@@ -1144,26 +1158,27 @@ void QQmlXMLHttpRequest::addHeader(const QString &name, const QString &value)
}
}
-QString QQmlXMLHttpRequest::header(const QString &name)
+QString QQmlXMLHttpRequest::header(const QString &name) const
{
- QByteArray utfname = name.toLower().toUtf8();
-
- foreach (const HeaderPair &header, m_headersList) {
- if (header.first == utfname)
- return QString::fromUtf8(header.second);
+ if (!m_headersList.isEmpty()) {
+ const QByteArray utfname = name.toLower().toUtf8();
+ for (const HeaderPair &header : m_headersList) {
+ if (header.first == utfname)
+ return QString::fromUtf8(header.second);
+ }
}
return QString();
}
-QString QQmlXMLHttpRequest::headers()
+QString QQmlXMLHttpRequest::headers() const
{
QString ret;
- foreach (const HeaderPair &header, m_headersList) {
+ for (const HeaderPair &header : m_headersList) {
if (ret.length())
ret.append(QLatin1String("\r\n"));
- ret = ret % QString::fromUtf8(header.first) % QLatin1String(": ")
- % QString::fromUtf8(header.second);
+ ret += QString::fromUtf8(header.first) + QLatin1String(": ")
+ + QString::fromUtf8(header.second);
}
return ret;
}
@@ -1235,7 +1250,8 @@ void QQmlXMLHttpRequest::requestFromUrl(const QUrl &url)
} else if (m_method == QLatin1String("DELETE")) {
m_network = networkAccessManager()->deleteResource(request);
} else if ((m_method == QLatin1String("OPTIONS")) ||
- m_method == QLatin1String("PROPFIND")) {
+ m_method == QLatin1String("PROPFIND") ||
+ m_method == QLatin1String("PATCH")) {
QBuffer *buffer = new QBuffer;
buffer->setData(m_data);
buffer->open(QIODevice::ReadOnly);
@@ -1559,7 +1575,7 @@ void QQmlXMLHttpRequest::dispatchCallback(Object *thisObj, QQmlContextData *cont
QV4::ScopedCallData callData(scope);
callData->thisObject = Encode::undefined();
- callback->call(callData);
+ callback->call(scope, callData);
if (scope.engine->hasException) {
QQmlError error = scope.engine->catchExceptionAsQmlError();
@@ -1585,15 +1601,20 @@ namespace QV4 {
namespace Heap {
struct QQmlXMLHttpRequestWrapper : Object {
- QQmlXMLHttpRequestWrapper(QQmlXMLHttpRequest *request);
- ~QQmlXMLHttpRequestWrapper() {
+ void init(QQmlXMLHttpRequest *request) {
+ Object::init();
+ this->request = request;
+ }
+
+ void destroy() {
delete request;
+ Object::destroy();
}
QQmlXMLHttpRequest *request;
};
struct QQmlXMLHttpRequestCtor : FunctionObject {
- QQmlXMLHttpRequestCtor(ExecutionEngine *engine);
+ void init(ExecutionEngine *engine);
Pointer<Object> proto;
};
@@ -1606,11 +1627,6 @@ struct QQmlXMLHttpRequestWrapper : public Object
V4_NEEDS_DESTROY
};
-Heap::QQmlXMLHttpRequestWrapper::QQmlXMLHttpRequestWrapper(QQmlXMLHttpRequest *request)
- : request(request)
-{
-}
-
struct QQmlXMLHttpRequestCtor : public FunctionObject
{
V4_OBJECT2(QQmlXMLHttpRequestCtor, FunctionObject)
@@ -1620,22 +1636,23 @@ struct QQmlXMLHttpRequestCtor : public FunctionObject
c->proto->mark(e);
FunctionObject::markObjects(that, e);
}
- static ReturnedValue construct(const Managed *that, QV4::CallData *)
+ static void construct(const Managed *that, Scope &scope, QV4::CallData *)
{
- Scope scope(static_cast<const QQmlXMLHttpRequestCtor *>(that)->engine());
Scoped<QQmlXMLHttpRequestCtor> ctor(scope, that->as<QQmlXMLHttpRequestCtor>());
- if (!ctor)
- return scope.engine->throwTypeError();
+ if (!ctor) {
+ scope.result = scope.engine->throwTypeError();
+ return;
+ }
QQmlXMLHttpRequest *r = new QQmlXMLHttpRequest(scope.engine->v8Engine->networkAccessManager());
Scoped<QQmlXMLHttpRequestWrapper> w(scope, scope.engine->memoryManager->allocObject<QQmlXMLHttpRequestWrapper>(r));
ScopedObject proto(scope, ctor->d()->proto);
w->setPrototype(proto);
- return w.asReturnedValue();
+ scope.result = w.asReturnedValue();
}
- static ReturnedValue call(const Managed *, QV4::CallData *) {
- return Primitive::undefinedValue().asReturnedValue();
+ static void call(const Managed *, Scope &scope, QV4::CallData *) {
+ scope.result = Primitive::undefinedValue();
}
void setupProto();
@@ -1661,9 +1678,9 @@ struct QQmlXMLHttpRequestCtor : public FunctionObject
DEFINE_OBJECT_VTABLE(QQmlXMLHttpRequestWrapper);
-Heap::QQmlXMLHttpRequestCtor::QQmlXMLHttpRequestCtor(ExecutionEngine *engine)
- : Heap::FunctionObject(engine->rootContext(), QStringLiteral("XMLHttpRequest"))
+void Heap::QQmlXMLHttpRequestCtor::init(ExecutionEngine *engine)
{
+ Heap::FunctionObject::init(engine->rootContext(), QStringLiteral("XMLHttpRequest"));
Scope scope(engine);
Scoped<QV4::QQmlXMLHttpRequestCtor> ctor(scope, this);
@@ -1735,7 +1752,8 @@ ReturnedValue QQmlXMLHttpRequestCtor::method_open(CallContext *ctx)
method != QLatin1String("POST") &&
method != QLatin1String("DELETE") &&
method != QLatin1String("OPTIONS") &&
- method != QLatin1String("PROPFIND"))
+ method != QLatin1String("PROPFIND") &&
+ method != QLatin1String("PATCH"))
V4THROW_DOM(DOMEXCEPTION_SYNTAX_ERR, "Unsupported HTTP method type");
// Argument 1 - URL
@@ -2038,6 +2056,6 @@ void *qt_add_qmlxmlhttprequest(ExecutionEngine *v4)
QT_END_NAMESPACE
-#endif // QT_NO_XMLSTREAMREADER
+#endif // QT_NO_XMLSTREAMREADER && qml_network
#include <qqmlxmlhttprequest.moc>
diff --git a/src/qml/qml/qqmlxmlhttprequest_p.h b/src/qml/qml/qqmlxmlhttprequest_p.h
index 7bbfb5243c..fdb6194537 100644
--- a/src/qml/qml/qqmlxmlhttprequest_p.h
+++ b/src/qml/qml/qqmlxmlhttprequest_p.h
@@ -55,7 +55,7 @@
#include <QtCore/qglobal.h>
#include <private/qqmlglobal_p.h>
-#ifndef QT_NO_XMLSTREAMREADER
+#if !defined(QT_NO_XMLSTREAMREADER) && QT_CONFIG(qml_network)
QT_BEGIN_NAMESPACE
@@ -64,7 +64,7 @@ void qt_rem_qmlxmlhttprequest(QV4::ExecutionEngine *engine, void *);
QT_END_NAMESPACE
-#endif // QT_NO_XMLSTREAMREADER
+#endif // QT_NO_XMLSTREAMREADER && qml_network
#endif // QQMLXMLHTTPREQUEST_P_H
diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp
index b9fb1f4ffe..cf0fd57773 100644
--- a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp
+++ b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp
@@ -42,9 +42,11 @@
#include <QtQml/qqmlcomponent.h>
#include <private/qqmlengine_p.h>
#include <private/qqmlcomponent_p.h>
+#include <private/qqmlloggingcategory_p.h>
#include <private/qqmlstringconverters_p.h>
#include <private/qqmllocale_p.h>
#include <private/qv8engine_p.h>
+#include <private/qqmldelayedcallqueue_p.h>
#include <QFileInfo>
#include <private/qqmldebugconnector_p.h>
@@ -75,6 +77,8 @@
#include <QtCore/qcoreapplication.h>
#include <QtCore/qloggingcategory.h>
+#include <QDebug>
+
QT_BEGIN_NAMESPACE
using namespace QV4;
@@ -87,10 +91,11 @@ struct StaticQtMetaObject : public QObject
{ return &staticQtMetaObject; }
};
-Heap::QtObject::QtObject(QQmlEngine *qmlEngine)
- : enumeratorIterator(0)
- , keyIterator(0)
+void Heap::QtObject::init(QQmlEngine *qmlEngine)
{
+ Heap::Object::init();
+ enumeratorIterator = 0;
+ keyIterator = 0;
Scope scope(internalClass->engine);
ScopedObject o(scope, this);
@@ -136,6 +141,7 @@ Heap::QtObject::QtObject(QQmlEngine *qmlEngine)
o->defineDefaultProperty(QStringLiteral("darker"), QV4::QtObject::method_darker);
o->defineDefaultProperty(QStringLiteral("tint"), QV4::QtObject::method_tint);
o->defineDefaultProperty(QStringLiteral("quit"), QV4::QtObject::method_quit);
+ o->defineDefaultProperty(QStringLiteral("exit"), QV4::QtObject::method_exit);
o->defineDefaultProperty(QStringLiteral("createQmlObject"), QV4::QtObject::method_createQmlObject);
o->defineDefaultProperty(QStringLiteral("createComponent"), QV4::QtObject::method_createComponent);
}
@@ -146,6 +152,8 @@ Heap::QtObject::QtObject(QQmlEngine *qmlEngine)
o->defineAccessorProperty(QStringLiteral("inputMethod"), QV4::QtObject::method_get_inputMethod, 0);
#endif
o->defineAccessorProperty(QStringLiteral("styleHints"), QV4::QtObject::method_get_styleHints, 0);
+
+ o->defineDefaultProperty(QStringLiteral("callLater"), QV4::QtObject::method_callLater);
}
void QtObject::addAll()
@@ -989,6 +997,8 @@ This function causes the QQmlEngine::quit() signal to be emitted.
Within the \l {Prototyping with qmlscene}, this causes the launcher application to exit;
to quit a C++ application when this method is called, connect the
QQmlEngine::quit() signal to the QCoreApplication::quit() slot.
+
+\sa exit()
*/
ReturnedValue QtObject::method_quit(CallContext *ctx)
{
@@ -997,6 +1007,28 @@ ReturnedValue QtObject::method_quit(CallContext *ctx)
}
/*!
+ \qmlmethod Qt::exit(int retCode)
+
+ This function causes the QQmlEngine::exit(int) signal to be emitted.
+ Within the \l {Prototyping with qmlscene}, this causes the launcher application to exit
+ the specified return code. To exit from the event loop with a specified return code when this
+ method is called, a C++ application can connect the QQmlEngine::exit(int) signal
+ to the QCoreApplication::exit(int) slot.
+
+ \sa quit()
+*/
+ReturnedValue QtObject::method_exit(CallContext *ctx)
+{
+ if (ctx->argc() != 1)
+ V4THROW_ERROR("Qt.exit(): Invalid arguments");
+
+ int retCode = ctx->args()[0].toNumber();
+
+ QQmlEnginePrivate::get(ctx->engine()->qmlEngine())->sendExit(retCode);
+ return QV4::Encode::undefined();
+}
+
+/*!
\qmlmethod object Qt::createQmlObject(string qml, object parent, string filepath)
Returns a new object created from the given \a string of QML which will have the specified \a parent,
@@ -1029,7 +1061,9 @@ ReturnedValue QtObject::method_createQmlObject(CallContext *ctx)
struct Error {
static ReturnedValue create(QV4::ExecutionEngine *v4, const QList<QQmlError> &errors) {
Scope scope(v4);
- QString errorstr = QLatin1String("Qt.createQmlObject(): failed to create object: ");
+ QString errorstr;
+ // '+=' reserves extra capacity. Follow-up appending will be probably free.
+ errorstr += QLatin1String("Qt.createQmlObject(): failed to create object: ");
QV4::ScopedArrayObject qmlerrors(scope, v4->newArrayObject());
QV4::ScopedObject qmlerror(scope);
@@ -1269,24 +1303,24 @@ ReturnedValue QtObject::method_locale(CallContext *ctx)
return QQmlLocale::locale(ctx->engine(), code);
}
-Heap::QQmlBindingFunction::QQmlBindingFunction(const QV4::FunctionObject *originalFunction)
- : QV4::Heap::FunctionObject(originalFunction->scope(), originalFunction->name())
- , originalFunction(originalFunction->d())
+void Heap::QQmlBindingFunction::init(const QV4::FunctionObject *originalFunction)
{
+ QV4::Heap::FunctionObject::init(originalFunction->scope(), originalFunction->name());
+ bindingLocation = new QQmlSourceLocation;
+ this->originalFunction = originalFunction->d();
}
void QQmlBindingFunction::initBindingLocation()
{
QV4::StackFrame frame = engine()->currentStackFrame();
- d()->bindingLocation.sourceFile = frame.source;
- d()->bindingLocation.line = frame.line;
+ d()->bindingLocation->sourceFile = frame.source;
+ d()->bindingLocation->line = frame.line;
}
-ReturnedValue QQmlBindingFunction::call(const Managed *that, CallData *callData)
+void QQmlBindingFunction::call(const Managed *that, Scope &scope, CallData *callData)
{
- Scope scope(static_cast<const QQmlBindingFunction*>(that)->engine());
ScopedFunctionObject function(scope, static_cast<const QQmlBindingFunction*>(that)->d()->originalFunction);
- return function->call(callData);
+ function->call(scope, callData);
}
void QQmlBindingFunction::markObjects(Heap::Base *that, ExecutionEngine *e)
@@ -1404,8 +1438,9 @@ ReturnedValue QtObject::method_get_styleHints(CallContext *ctx)
}
-QV4::Heap::ConsoleObject::ConsoleObject()
+void QV4::Heap::ConsoleObject::init()
{
+ Object::init();
QV4::Scope scope(internalClass->engine);
QV4::ScopedObject o(scope, this);
@@ -1462,28 +1497,42 @@ static QString jsStack(QV4::ExecutionEngine *engine) {
static QV4::ReturnedValue writeToConsole(ConsoleLogTypes logType, CallContext *ctx,
bool printStack = false)
{
+ QLoggingCategory *loggingCategory = 0;
QString result;
QV4::ExecutionEngine *v4 = ctx->d()->engine;
- for (int i = 0; i < ctx->argc(); ++i) {
- if (i != 0)
+ int start = 0;
+ if (ctx->argc() > 0) {
+ if (const QObjectWrapper* wrapper = ctx->args()[0].as<QObjectWrapper>()) {
+ if (QQmlLoggingCategory* category = qobject_cast<QQmlLoggingCategory*>(wrapper->object())) {
+ if (category->category())
+ loggingCategory = category->category();
+ else
+ V4THROW_ERROR("A QmlLoggingCatgory was provided without a valid name");
+ start = 1;
+ }
+ }
+ }
+
+
+ for (int i = start; i < ctx->argc(); ++i) {
+ if (i != start)
result.append(QLatin1Char(' '));
if (ctx->args()[i].as<ArrayObject>())
- result.append(QLatin1Char('[') + ctx->args()[i].toQStringNoThrow() + QLatin1Char(']'));
+ result += QLatin1Char('[') + ctx->args()[i].toQStringNoThrow() + QLatin1Char(']');
else
result.append(ctx->args()[i].toQStringNoThrow());
}
- if (printStack) {
- result.append(QLatin1Char('\n'));
- result.append(jsStack(v4));
- }
+ if (printStack)
+ result += QLatin1Char('\n') + jsStack(v4);
static QLoggingCategory qmlLoggingCategory("qml");
static QLoggingCategory jsLoggingCategory("js");
- QLoggingCategory *loggingCategory = v4->qmlEngine() ? &qmlLoggingCategory : &jsLoggingCategory;
+ if (!loggingCategory)
+ loggingCategory = v4->qmlEngine() ? &qmlLoggingCategory : &jsLoggingCategory;
QV4::StackFrame frame = v4->currentStackFrame();
const QByteArray baSource = frame.source.toUtf8();
const QByteArray baFunction = frame.function.toUtf8();
@@ -1513,6 +1562,8 @@ static QV4::ReturnedValue writeToConsole(ConsoleLogTypes logType, CallContext *c
return QV4::Encode::undefined();
}
+DEFINE_OBJECT_VTABLE(ConsoleObject);
+
QV4::ReturnedValue ConsoleObject::method_error(CallContext *ctx)
{
return writeToConsole(Error, ctx);
@@ -1995,6 +2046,31 @@ ReturnedValue GlobalExtensions::method_string_arg(CallContext *ctx)
return ctx->d()->engine->newString(value.arg(arg->toQString()))->asReturnedValue();
}
+/*!
+\qmlmethod Qt::callLater(function)
+\qmlmethod Qt::callLater(function, argument1, argument2, ...)
+\since 5.8
+Use this function to eliminate redundant calls to a function or signal.
+
+The function passed as the first argument to Qt.callLater()
+will be called later, once the QML engine returns to the event loop.
+
+When this function is called multiple times in quick succession with the
+same function as its first argument, that function will be called only once.
+
+For example:
+\snippet qml/qtLater.qml 0
+
+Any additional arguments passed to Qt.callLater() will
+be passed on to the function invoked. Note that if redundant calls
+are eliminated, then only the last set of arguments will be passed to the
+function.
+*/
+ReturnedValue QtObject::method_callLater(CallContext *ctx)
+{
+ QV8Engine *v8engine = ctx->engine()->v8Engine;
+ return v8engine->delayedCallQueue()->addUniquelyAndExecuteLater(ctx);
+}
QT_END_NAMESPACE
diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions_p.h b/src/qml/qml/v8/qqmlbuiltinfunctions_p.h
index dc806c6031..7602a92582 100644
--- a/src/qml/qml/v8/qqmlbuiltinfunctions_p.h
+++ b/src/qml/qml/v8/qqmlbuiltinfunctions_p.h
@@ -64,7 +64,7 @@ namespace QV4 {
namespace Heap {
struct QtObject : Object {
- QtObject(QQmlEngine *qmlEngine);
+ void init(QQmlEngine *qmlEngine);
QObject *platform;
QObject *application;
@@ -77,14 +77,18 @@ struct QtObject : Object {
};
struct ConsoleObject : Object {
- ConsoleObject();
+ void init();
};
struct QQmlBindingFunction : FunctionObject {
- QQmlBindingFunction(const QV4::FunctionObject *originalFunction);
+ void init(const QV4::FunctionObject *originalFunction);
+ void destroy() {
+ delete bindingLocation;
+ Object::destroy();
+ }
Pointer<FunctionObject> originalFunction;
// Set when the binding is created later
- QQmlSourceLocation bindingLocation;
+ QQmlSourceLocation *bindingLocation;
};
}
@@ -122,6 +126,7 @@ struct QtObject : Object
static ReturnedValue method_btoa(CallContext *ctx);
static ReturnedValue method_atob(CallContext *ctx);
static ReturnedValue method_quit(CallContext *ctx);
+ static ReturnedValue method_exit(CallContext *ctx);
static ReturnedValue method_resolvedUrl(CallContext *ctx);
static ReturnedValue method_createQmlObject(CallContext *ctx);
static ReturnedValue method_createComponent(CallContext *ctx);
@@ -135,6 +140,8 @@ struct QtObject : Object
#endif
static ReturnedValue method_get_styleHints(CallContext *ctx);
+ static ReturnedValue method_callLater(CallContext *ctx);
+
private:
void addAll();
ReturnedValue findAndAdd(const QString *name, bool &foundProperty) const;
@@ -142,9 +149,7 @@ private:
struct ConsoleObject : Object
{
- typedef Heap::ConsoleObject Data;
- const Data *d() const { return static_cast<const Data *>(Object::d()); }
- Data *d() { return static_cast<Data *>(Object::d()); }
+ V4_OBJECT2(ConsoleObject, Object)
static ReturnedValue method_error(CallContext *ctx);
static ReturnedValue method_log(CallContext *ctx);
@@ -186,7 +191,7 @@ struct QQmlBindingFunction : public QV4::FunctionObject
void initBindingLocation(); // from caller stack trace
- static ReturnedValue call(const Managed *that, CallData *callData);
+ static void call(const Managed *that, Scope &scope, CallData *callData);
static void markObjects(Heap::Base *that, ExecutionEngine *e);
};
diff --git a/src/qml/qml/v8/qv8engine.cpp b/src/qml/qml/v8/qv8engine.cpp
index 46fd4fbbeb..b0599dd0a2 100644
--- a/src/qml/qml/v8/qv8engine.cpp
+++ b/src/qml/qml/v8/qv8engine.cpp
@@ -150,6 +150,7 @@ QV8Engine::QV8Engine(QJSEngine* qq)
m_v4Engine = new QV4::ExecutionEngine;
m_v4Engine->v8Engine = this;
+ m_delayedCallQueue.init(m_v4Engine);
QV4::QObjectWrapper::initializeBindings(m_v4Engine);
}
@@ -159,18 +160,23 @@ QV8Engine::~QV8Engine()
qDeleteAll(m_extensionData);
m_extensionData.clear();
+#if !defined(QT_NO_XMLSTREAMREADER) && QT_CONFIG(qml_network)
qt_rem_qmlxmlhttprequest(m_v4Engine, m_xmlHttpRequestData);
m_xmlHttpRequestData = 0;
+#endif
+
delete m_listModelData;
m_listModelData = 0;
delete m_v4Engine;
}
+#if QT_CONFIG(qml_network)
QNetworkAccessManager *QV8Engine::networkAccessManager()
{
return QQmlEnginePrivate::get(m_engine)->getNetworkAccessManager();
}
+#endif
const QSet<QString> &QV8Engine::illegalNames() const
{
@@ -189,8 +195,10 @@ void QV8Engine::initializeGlobal()
QQmlDateExtension::registerExtension(m_v4Engine);
QQmlNumberExtension::registerExtension(m_v4Engine);
+#if !defined(QT_NO_XMLSTREAMREADER) && QT_CONFIG(qml_network)
qt_add_domexceptions(m_v4Engine);
m_xmlHttpRequestData = qt_add_qmlxmlhttprequest(m_v4Engine);
+#endif
qt_add_sqlexceptions(m_v4Engine);
diff --git a/src/qml/qml/v8/qv8engine_p.h b/src/qml/qml/v8/qv8engine_p.h
index 2cfa996409..0cbe34fd30 100644
--- a/src/qml/qml/v8/qv8engine_p.h
+++ b/src/qml/qml/v8/qv8engine_p.h
@@ -67,6 +67,7 @@
#include <private/qv4value_p.h>
#include <private/qv4identifier_p.h>
#include <private/qv4context_p.h>
+#include <private/qqmldelayedcallqueue_p.h>
QT_BEGIN_NAMESPACE
@@ -76,12 +77,6 @@ namespace QV4 {
struct QObjectMethod;
}
-// Uncomment the following line to enable global handle debugging. When enabled, all the persistent
-// handles allocated using qPersistentNew() (or registered with qPersistentRegsiter()) and disposed
-// with qPersistentDispose() are tracked. If you try and do something illegal, like double disposing
-// a handle, qFatal() is called.
-// #define QML_GLOBAL_HANDLE_DEBUGGING
-
#define V4THROW_ERROR(string) \
return ctx->engine()->throwError(QString::fromUtf8(string));
@@ -184,6 +179,7 @@ public:
QQmlEngine *engine() { return m_engine; }
QJSEngine *publicEngine() { return q; }
QV4::ReturnedValue global();
+ QQmlDelayedCallQueue *delayedCallQueue() { return &m_delayedCallQueue; }
void *xmlHttpRequestData() { return m_xmlHttpRequestData; }
@@ -192,10 +188,12 @@ public:
void freezeObject(const QV4::Value &value);
+#if QT_CONFIG(qml_network)
// Return the network access manager for this engine. By default this returns the network
// access manager of the QQmlEngine. It is overridden in the case of a threaded v8
// instance (like in WorkerScript).
virtual QNetworkAccessManager *networkAccessManager();
+#endif
// Return the list of illegal id names (the names of the properties on the global object)
const QSet<QString> &illegalNames() const;
@@ -217,6 +215,7 @@ public:
protected:
QJSEngine* q;
QQmlEngine *m_engine;
+ QQmlDelayedCallQueue m_delayedCallQueue;
QV4::ExecutionEngine *m_v4Engine;
diff --git a/src/qml/qml/v8/v8.pri b/src/qml/qml/v8/v8.pri
index 3d6a012481..4592022939 100644
--- a/src/qml/qml/v8/v8.pri
+++ b/src/qml/qml/v8/v8.pri
@@ -9,4 +9,3 @@ SOURCES += \
$$PWD/qv4domerrors.cpp \
$$PWD/qv4sqlerrors.cpp \
$$PWD/qqmlbuiltinfunctions.cpp
-
diff --git a/src/qml/qtqmlglobal.h b/src/qml/qtqmlglobal.h
index 34191d06b2..89228b7777 100644
--- a/src/qml/qtqmlglobal.h
+++ b/src/qml/qtqmlglobal.h
@@ -41,6 +41,10 @@
#define QTQMLGLOBAL_H
#include <QtCore/qglobal.h>
+#include <QtQml/qtqml-config.h>
+#if QT_CONFIG(qml_network)
+#include <QtNetwork/qtnetworkglobal.h>
+#endif
QT_BEGIN_NAMESPACE
diff --git a/src/qml/qtqmlglobal_p.h b/src/qml/qtqmlglobal_p.h
index 1b0872298d..63585fd62e 100644
--- a/src/qml/qtqmlglobal_p.h
+++ b/src/qml/qtqmlglobal_p.h
@@ -51,7 +51,9 @@
// We mean it.
//
-#include "qtqmlglobal.h"
+#include <QtCore/private/qglobal_p.h>
+#include <QtQml/private/qtqml-config_p.h>
+#include <QtQml/qtqmlglobal.h>
#if defined(QT_BUILD_QMLDEVTOOLS_LIB) || defined(QT_QMLDEVTOOLS_LIB)
# define Q_QML_PRIVATE_EXPORT
diff --git a/src/qml/types/qqmlbind.cpp b/src/qml/types/qqmlbind.cpp
index ed9a8533c0..a545fe57ca 100644
--- a/src/qml/types/qqmlbind.cpp
+++ b/src/qml/types/qqmlbind.cpp
@@ -51,6 +51,7 @@
#include <QtCore/qfile.h>
#include <QtCore/qdebug.h>
+#include <QtCore/qtimer.h>
#include <private/qobject_p.h>
@@ -59,16 +60,18 @@ QT_BEGIN_NAMESPACE
class QQmlBindPrivate : public QObjectPrivate
{
public:
- QQmlBindPrivate() : componentComplete(true), obj(0) {}
+ QQmlBindPrivate() : obj(0), componentComplete(true), delayed(false), pendingEval(false) {}
~QQmlBindPrivate() { }
QQmlNullableValue<bool> when;
- bool componentComplete;
QPointer<QObject> obj;
QString propName;
QQmlNullableValue<QVariant> value;
QQmlProperty prop;
QQmlAbstractBinding::Ptr prevBind;
+ bool componentComplete:1;
+ bool delayed:1;
+ bool pendingEval:1;
void validate(QObject *binding) const;
};
@@ -281,7 +284,43 @@ void QQmlBind::setValue(const QVariant &v)
{
Q_D(QQmlBind);
d->value = v;
- eval();
+ prepareEval();
+}
+
+/*!
+ \qmlproperty bool QtQml::Binding::delayed
+ \since 5.8
+
+ This property holds whether the binding should be delayed.
+
+ A delayed binding will not immediately update the target, but rather wait
+ until the event queue has been cleared. This can be used as an optimization,
+ or to prevent intermediary values from being assigned.
+
+ \code
+ Binding {
+ target: contactName; property: 'text'
+ value: givenName + " " + familyName; when: list.ListView.isCurrentItem
+ delayed: true
+ }
+ \endcode
+*/
+bool QQmlBind::delayed() const
+{
+ Q_D(const QQmlBind);
+ return d->delayed;
+}
+
+void QQmlBind::setDelayed(bool delayed)
+{
+ Q_D(QQmlBind);
+ if (d->delayed == delayed)
+ return;
+
+ d->delayed = delayed;
+
+ if (!d->delayed)
+ eval();
}
void QQmlBind::setTarget(const QQmlProperty &p)
@@ -307,9 +346,22 @@ void QQmlBind::componentComplete()
eval();
}
+void QQmlBind::prepareEval()
+{
+ Q_D(QQmlBind);
+ if (d->delayed) {
+ if (!d->pendingEval)
+ QTimer::singleShot(0, this, &QQmlBind::eval);
+ d->pendingEval = true;
+ } else {
+ eval();
+ }
+}
+
void QQmlBind::eval()
{
Q_D(QQmlBind);
+ d->pendingEval = false;
if (!d->prop.isValid() || d->value.isNull || !d->componentComplete)
return;
diff --git a/src/qml/types/qqmlbind_p.h b/src/qml/types/qqmlbind_p.h
index 581995af56..77939a40bc 100644
--- a/src/qml/types/qqmlbind_p.h
+++ b/src/qml/types/qqmlbind_p.h
@@ -68,6 +68,7 @@ class Q_AUTOTEST_EXPORT QQmlBind : public QObject, public QQmlPropertyValueSourc
Q_PROPERTY(QString property READ property WRITE setProperty)
Q_PROPERTY(QVariant value READ value WRITE setValue)
Q_PROPERTY(bool when READ when WRITE setWhen)
+ Q_PROPERTY(bool delayed READ delayed WRITE setDelayed REVISION 8)
public:
QQmlBind(QObject *parent=0);
@@ -85,12 +86,16 @@ public:
QVariant value() const;
void setValue(const QVariant &);
+ bool delayed() const;
+ void setDelayed(bool);
+
protected:
virtual void setTarget(const QQmlProperty &);
virtual void classBegin();
virtual void componentComplete();
private:
+ void prepareEval();
void eval();
};
diff --git a/src/qml/types/qqmlconnections.cpp b/src/qml/types/qqmlconnections.cpp
index a16acac3ab..84782114ac 100644
--- a/src/qml/types/qqmlconnections.cpp
+++ b/src/qml/types/qqmlconnections.cpp
@@ -44,7 +44,6 @@
#include <private/qqmlboundsignal_p.h>
#include <qqmlcontext.h>
#include <private/qqmlcontext_p.h>
-#include <private/qqmlcompiler_p.h>
#include <qqmlinfo.h>
#include <QtCore/qdebug.h>
@@ -67,7 +66,7 @@ public:
bool ignoreUnknownSignals;
bool componentcomplete;
- QQmlRefPointer<QQmlCompiledData> cdata;
+ QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit;
QList<const QV4::CompiledData::Binding *> bindings;
};
@@ -258,11 +257,11 @@ void QQmlConnectionsParser::verifyBindings(const QV4::CompiledData::Unit *qmlUni
}
}
-void QQmlConnectionsParser::applyBindings(QObject *object, QQmlCompiledData *cdata, const QList<const QV4::CompiledData::Binding *> &bindings)
+void QQmlConnectionsParser::applyBindings(QObject *object, QV4::CompiledData::CompilationUnit *compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings)
{
QQmlConnectionsPrivate *p =
static_cast<QQmlConnectionsPrivate *>(QObjectPrivate::get(object));
- p->cdata = cdata;
+ p->compilationUnit = compilationUnit;
p->bindings = bindings;
}
@@ -278,7 +277,7 @@ void QQmlConnections::connectSignals()
QQmlData *ddata = QQmlData::get(this);
QQmlContextData *ctxtdata = ddata ? ddata->outerContext : 0;
- const QV4::CompiledData::Unit *qmlUnit = d->cdata->compilationUnit->data;
+ const QV4::CompiledData::Unit *qmlUnit = d->compilationUnit->data;
foreach (const QV4::CompiledData::Binding *binding, d->bindings) {
Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Script);
QString propName = qmlUnit->stringAt(binding->propertyNameIndex);
@@ -291,7 +290,7 @@ void QQmlConnections::connectSignals()
QQmlBoundSignalExpression *expression = ctxtdata ?
new QQmlBoundSignalExpression(target, signalIndex,
- ctxtdata, this, d->cdata->compilationUnit->runtimeFunctions[binding->value.compiledScriptIndex]) : 0;
+ ctxtdata, this, d->compilationUnit->runtimeFunctions[binding->value.compiledScriptIndex]) : 0;
signal->takeExpression(expression);
d->boundsignals += signal;
} else {
diff --git a/src/qml/types/qqmlconnections_p.h b/src/qml/types/qqmlconnections_p.h
index ff128a0ca6..d454affba8 100644
--- a/src/qml/types/qqmlconnections_p.h
+++ b/src/qml/types/qqmlconnections_p.h
@@ -99,7 +99,7 @@ class QQmlConnectionsParser : public QQmlCustomParser
{
public:
virtual void verifyBindings(const QV4::CompiledData::Unit *qmlUnit, const QList<const QV4::CompiledData::Binding *> &props);
- virtual void applyBindings(QObject *object, QQmlCompiledData *cdata, const QList<const QV4::CompiledData::Binding *> &bindings);
+ virtual void applyBindings(QObject *object, QV4::CompiledData::CompilationUnit *compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings);
};
diff --git a/src/qml/types/qqmldelegatemodel.cpp b/src/qml/types/qqmldelegatemodel.cpp
index 405242767f..9b58ec35f8 100644
--- a/src/qml/types/qqmldelegatemodel.cpp
+++ b/src/qml/types/qqmldelegatemodel.cpp
@@ -48,7 +48,6 @@
#include <private/qqmlengine_p.h>
#include <private/qqmlcomponent_p.h>
#include <private/qqmlincubator_p.h>
-#include <private/qqmlcompiler_p.h>
#include <private/qv4value_p.h>
#include <private/qv4functionobject_p.h>
@@ -63,21 +62,26 @@ namespace QV4 {
namespace Heap {
struct DelegateModelGroupFunction : FunctionObject {
- DelegateModelGroupFunction(QV4::ExecutionContext *scope, uint flag, QV4::ReturnedValue (*code)(QQmlDelegateModelItem *item, uint flag, const QV4::Value &arg));
+ void init(QV4::ExecutionContext *scope, uint flag, QV4::ReturnedValue (*code)(QQmlDelegateModelItem *item, uint flag, const QV4::Value &arg));
- uint flag;
QV4::ReturnedValue (*code)(QQmlDelegateModelItem *item, uint flag, const QV4::Value &arg);
+ uint flag;
};
struct QQmlDelegateModelGroupChange : Object {
- QQmlDelegateModelGroupChange() {}
+ void init() { Object::init(); }
- QQmlChangeSet::Change change;
+ QQmlChangeSet::ChangeData change;
};
struct QQmlDelegateModelGroupChangeArray : Object {
- QQmlDelegateModelGroupChangeArray(const QVector<QQmlChangeSet::Change> &changes);
- QVector<QQmlChangeSet::Change> changes;
+ void init(const QVector<QQmlChangeSet::Change> &changes);
+ void destroy() {
+ delete changes;
+ Object::destroy();
+ }
+
+ QVector<QQmlChangeSet::Change> *changes;
};
@@ -92,25 +96,25 @@ struct DelegateModelGroupFunction : QV4::FunctionObject
return scope->engine()->memoryManager->allocObject<DelegateModelGroupFunction>(scope, flag, code);
}
- static QV4::ReturnedValue call(const QV4::Managed *that, QV4::CallData *callData)
+ static void call(const QV4::Managed *that, QV4::Scope &scope, QV4::CallData *callData)
{
- QV4::ExecutionEngine *v4 = static_cast<const DelegateModelGroupFunction *>(that)->engine();
- QV4::Scope scope(v4);
QV4::Scoped<DelegateModelGroupFunction> f(scope, static_cast<const DelegateModelGroupFunction *>(that));
QV4::Scoped<QQmlDelegateModelItemObject> o(scope, callData->thisObject);
- if (!o)
- return v4->throwTypeError(QStringLiteral("Not a valid VisualData object"));
+ if (!o) {
+ scope.result = scope.engine->throwTypeError(QStringLiteral("Not a valid VisualData object"));
+ return;
+ }
QV4::ScopedValue v(scope, callData->argument(0));
- return f->d()->code(o->d()->item, f->d()->flag, v);
+ scope.result = f->d()->code(o->d()->item, f->d()->flag, v);
}
};
-Heap::DelegateModelGroupFunction::DelegateModelGroupFunction(QV4::ExecutionContext *scope, uint flag, QV4::ReturnedValue (*code)(QQmlDelegateModelItem *item, uint flag, const QV4::Value &arg))
- : QV4::Heap::FunctionObject(scope, QStringLiteral("DelegateModelGroupFunction"))
- , flag(flag)
- , code(code)
+void Heap::DelegateModelGroupFunction::init(QV4::ExecutionContext *scope, uint flag, QV4::ReturnedValue (*code)(QQmlDelegateModelItem *item, uint flag, const QV4::Value &arg))
{
+ QV4::Heap::FunctionObject::init(scope, QStringLiteral("DelegateModelGroupFunction"));
+ this->flag = flag;
+ this->code = code;
}
}
@@ -235,10 +239,8 @@ void QQmlDelegateModelPrivate::init()
}
QQmlDelegateModel::QQmlDelegateModel()
-: QQmlInstanceModel(*(new QQmlDelegateModelPrivate(0)))
+ : QQmlDelegateModel(nullptr, nullptr)
{
- Q_D(QQmlDelegateModel);
- d->init();
}
QQmlDelegateModel::QQmlDelegateModel(QQmlContext *ctxt, QObject *parent)
@@ -1671,7 +1673,7 @@ void QQmlDelegateModelItemMetaType::initializeMetaObject()
int notifierId = 0;
for (int i = 0; i < groupNames.count(); ++i, ++notifierId) {
- QString propertyName = QStringLiteral("in") + groupNames.at(i);
+ QString propertyName = QLatin1String("in") + groupNames.at(i);
propertyName.replace(2, 1, propertyName.at(2).toUpper());
builder.addSignal("__" + propertyName.toUtf8() + "Changed()");
QMetaPropertyBuilder propertyBuilder = builder.addProperty(
@@ -1679,7 +1681,7 @@ void QQmlDelegateModelItemMetaType::initializeMetaObject()
propertyBuilder.setWritable(true);
}
for (int i = 0; i < groupNames.count(); ++i, ++notifierId) {
- const QString propertyName = groupNames.at(i) + QStringLiteral("Index");
+ const QString propertyName = groupNames.at(i) + QLatin1String("Index");
builder.addSignal("__" + propertyName.toUtf8() + "Changed()");
QMetaPropertyBuilder propertyBuilder = builder.addProperty(
propertyName.toUtf8(), "int", notifierId);
@@ -1727,7 +1729,7 @@ void QQmlDelegateModelItemMetaType::initializePrototype()
proto->insertMember(s, p, QV4::Attr_Accessor|QV4::Attr_NotConfigurable|QV4::Attr_NotEnumerable);
for (int i = 2; i < groupNames.count(); ++i) {
- QString propertyName = QStringLiteral("in") + groupNames.at(i);
+ QString propertyName = QLatin1String("in") + groupNames.at(i);
propertyName.replace(2, 1, propertyName.at(2).toUpper());
s = v4->newString(propertyName);
p->setGetter((f = QV4::DelegateModelGroupFunction::create(global, i + 1, QQmlDelegateModelItem::get_member)));
@@ -1735,7 +1737,7 @@ void QQmlDelegateModelItemMetaType::initializePrototype()
proto->insertMember(s, p, QV4::Attr_Accessor|QV4::Attr_NotConfigurable|QV4::Attr_NotEnumerable);
}
for (int i = 2; i < groupNames.count(); ++i) {
- const QString propertyName = groupNames.at(i) + QStringLiteral("Index");
+ const QString propertyName = groupNames.at(i) + QLatin1String("Index");
s = v4->newString(propertyName);
p->setGetter((f = QV4::DelegateModelGroupFunction::create(global, i + 1, QQmlDelegateModelItem::get_index)));
p->setSetter(0);
@@ -1868,9 +1870,10 @@ QV4::ReturnedValue QQmlDelegateModelItem::get_index(QQmlDelegateModelItem *thisI
DEFINE_OBJECT_VTABLE(QQmlDelegateModelItemObject);
-QV4::Heap::QQmlDelegateModelItemObject::~QQmlDelegateModelItemObject()
+void QV4::Heap::QQmlDelegateModelItemObject::destroy()
{
item->Dispose();
+ Object::destroy();
}
@@ -1935,9 +1938,10 @@ void QQmlDelegateModelItem::incubateObject(
QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(engine);
QQmlComponentPrivate *componentPriv = QQmlComponentPrivate::get(component);
- incubatorPriv->compiledData = componentPriv->cc;
- incubatorPriv->compiledData->addref();
- incubatorPriv->creator.reset(new QQmlObjectCreator(context, componentPriv->cc, componentPriv->creationContext));
+ incubatorPriv->compilationUnit = componentPriv->compilationUnit;
+ incubatorPriv->compilationUnit->addref();
+ incubatorPriv->enginePriv = enginePriv;
+ incubatorPriv->creator.reset(new QQmlObjectCreator(context, componentPriv->compilationUnit, componentPriv->creationContext));
incubatorPriv->subComponentToCreate = componentPriv->start;
enginePriv->incubate(*incubationTask, forContext);
@@ -2336,7 +2340,7 @@ QQmlDelegateModelGroup::QQmlDelegateModelGroup(QObject *parent)
QQmlDelegateModelGroup::QQmlDelegateModelGroup(
const QString &name, QQmlDelegateModel *model, int index, QObject *parent)
- : QObject(*new QQmlDelegateModelGroupPrivate, parent)
+ : QQmlDelegateModelGroup(parent)
{
Q_D(QQmlDelegateModelGroup);
d->name = name;
@@ -3258,8 +3262,8 @@ public:
return engine->memoryManager->allocObject<QQmlDelegateModelGroupChangeArray>(changes);
}
- quint32 count() const { return d()->changes.count(); }
- const QQmlChangeSet::Change &at(int index) const { return d()->changes.at(index); }
+ quint32 count() const { return d()->changes->count(); }
+ const QQmlChangeSet::Change &at(int index) const { return d()->changes->at(index); }
static QV4::ReturnedValue getIndexed(const QV4::Managed *m, uint index, bool *hasProperty)
{
@@ -3301,9 +3305,10 @@ public:
}
};
-QV4::Heap::QQmlDelegateModelGroupChangeArray::QQmlDelegateModelGroupChangeArray(const QVector<QQmlChangeSet::Change> &changes)
- : changes(changes)
+void QV4::Heap::QQmlDelegateModelGroupChangeArray::init(const QVector<QQmlChangeSet::Change> &changes)
{
+ Object::init();
+ this->changes = new QVector<QQmlChangeSet::Change>(changes);
QV4::Scope scope(internalClass->engine);
QV4::ScopedObject o(scope, this);
o->setArrayType(QV4::Heap::ArrayData::Custom);
diff --git a/src/qml/types/qqmldelegatemodel_p_p.h b/src/qml/types/qqmldelegatemodel_p_p.h
index bf9fd99f19..6cb82fddfc 100644
--- a/src/qml/types/qqmldelegatemodel_p_p.h
+++ b/src/qml/types/qqmldelegatemodel_p_p.h
@@ -160,8 +160,8 @@ protected:
namespace QV4 {
namespace Heap {
struct QQmlDelegateModelItemObject : Object {
- inline QQmlDelegateModelItemObject(QQmlDelegateModelItem *item);
- ~QQmlDelegateModelItemObject();
+ inline void init(QQmlDelegateModelItem *item);
+ void destroy();
QQmlDelegateModelItem *item;
};
@@ -174,9 +174,10 @@ struct QQmlDelegateModelItemObject : QV4::Object
V4_NEEDS_DESTROY
};
-QV4::Heap::QQmlDelegateModelItemObject::QQmlDelegateModelItemObject(QQmlDelegateModelItem *item)
- : item(item)
+void QV4::Heap::QQmlDelegateModelItemObject::init(QQmlDelegateModelItem *item)
{
+ Object::init();
+ this->item = item;
}
diff --git a/src/qml/types/qqmllistmodel.cpp b/src/qml/types/qqmllistmodel.cpp
index 1e1ebc4939..f7fdbf0d80 100644
--- a/src/qml/types/qqmllistmodel.cpp
+++ b/src/qml/types/qqmllistmodel.cpp
@@ -42,7 +42,6 @@
#include <private/qqmlopenmetaobject_p.h>
#include <private/qqmljsast_p.h>
#include <private/qqmljsengine_p.h>
-#include <private/qqmlcompiler_p.h>
#include <private/qqmlcustomparser_p.h>
#include <private/qqmlengine_p.h>
@@ -79,13 +78,16 @@ static bool isMemoryUsed(const char *mem)
static QString roleTypeName(ListLayout::Role::DataType t)
{
- QString result;
- const char *roleTypeNames[] = { "String", "Number", "Bool", "List", "QObject", "VariantMap", "DateTime" };
+ static const QString roleTypeNames[] = {
+ QStringLiteral("String"), QStringLiteral("Number"), QStringLiteral("Bool"),
+ QStringLiteral("List"), QStringLiteral("QObject"), QStringLiteral("VariantMap"),
+ QStringLiteral("DateTime")
+ };
if (t > ListLayout::Role::Invalid && t < ListLayout::Role::MaxDataType)
- result = QString::fromLatin1(roleTypeNames[t]);
+ return roleTypeNames[t];
- return result;
+ return QString();
}
const ListLayout::Role &ListLayout::getRoleOrCreate(const QString &key, Role::DataType type)
@@ -1440,8 +1442,7 @@ void DynamicRoleModelNode::updateValues(const QVariantMap &object, QVector<int>
const QByteArray &keyUtf8 = key.toUtf8();
QQmlListModel *existingModel = qobject_cast<QQmlListModel *>(m_meta->value(keyUtf8).value<QObject *>());
- if (existingModel)
- delete existingModel;
+ delete existingModel;
if (m_meta->setValue(keyUtf8, value))
roles << roleIndex;
@@ -1457,8 +1458,7 @@ DynamicRoleModelNodeMetaObject::~DynamicRoleModelNodeMetaObject()
{
for (int i=0 ; i < count() ; ++i) {
QQmlListModel *subModel = qobject_cast<QQmlListModel *>(value(i).value<QObject *>());
- if (subModel)
- delete subModel;
+ delete subModel;
}
}
@@ -1469,8 +1469,7 @@ void DynamicRoleModelNodeMetaObject::propertyWrite(int index)
QVariant v = value(index);
QQmlListModel *model = qobject_cast<QQmlListModel *>(v.value<QObject *>());
- if (model)
- delete model;
+ delete model;
}
void DynamicRoleModelNodeMetaObject::propertyWritten(int index)
@@ -1981,15 +1980,7 @@ void QQmlListModel::setDynamicRoles(bool enableDynamicRoles)
*/
int QQmlListModel::count() const
{
- int count;
-
- if (m_dynamicRoles)
- count = m_modelObjects.count();
- else {
- count = m_listModel->elementCount();
- }
-
- return count;
+ return m_dynamicRoles ? m_modelObjects.count() : m_listModel->elementCount();
}
/*!
@@ -2001,7 +1992,7 @@ int QQmlListModel::count() const
*/
void QQmlListModel::clear()
{
- int cleared = count();
+ const int cleared = count();
emitItemsAboutToBeRemoved(0, cleared);
@@ -2411,7 +2402,7 @@ bool QQmlListModelParser::verifyProperty(const QV4::CompiledData::Unit *qmlUnit,
listElementTypeName = objName; // cache right name for next time
}
- if (!qmlUnit->stringAt(target->idIndex).isEmpty()) {
+ if (!qmlUnit->stringAt(target->idNameIndex).isEmpty()) {
error(target->locationOfIdProperty, QQmlListModel::tr("ListElement: cannot use reserved \"id\" property"));
return false;
}
@@ -2518,13 +2509,13 @@ void QQmlListModelParser::verifyBindings(const QV4::CompiledData::Unit *qmlUnit,
}
}
-void QQmlListModelParser::applyBindings(QObject *obj, QQmlCompiledData *cdata, const QList<const QV4::CompiledData::Binding *> &bindings)
+void QQmlListModelParser::applyBindings(QObject *obj, QV4::CompiledData::CompilationUnit *compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings)
{
QQmlListModel *rv = static_cast<QQmlListModel *>(obj);
rv->m_engine = QV8Engine::getV4(qmlEngine(rv));
- const QV4::CompiledData::Unit *qmlUnit = cdata->compilationUnit->data;
+ const QV4::CompiledData::Unit *qmlUnit = compilationUnit->data;
bool setRoles = false;
diff --git a/src/qml/types/qqmllistmodel_p.h b/src/qml/types/qqmllistmodel_p.h
index b35a5a1224..220b0e54b5 100644
--- a/src/qml/types/qqmllistmodel_p.h
+++ b/src/qml/types/qqmllistmodel_p.h
@@ -187,8 +187,8 @@ public:
QQmlListModelParser() : QQmlCustomParser(QQmlCustomParser::AcceptsSignalHandlers) {}
- virtual void verifyBindings(const QV4::CompiledData::Unit *qmlUnit, const QList<const QV4::CompiledData::Binding *> &bindings);
- virtual void applyBindings(QObject *obj, QQmlCompiledData *cdata, const QList<const QV4::CompiledData::Binding *> &bindings);
+ void verifyBindings(const QV4::CompiledData::Unit *qmlUnit, const QList<const QV4::CompiledData::Binding *> &bindings) Q_DECL_OVERRIDE;
+ void applyBindings(QObject *obj, QV4::CompiledData::CompilationUnit *compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings) Q_DECL_OVERRIDE;
private:
bool verifyProperty(const QV4::CompiledData::Unit *qmlUnit, const QV4::CompiledData::Binding *binding);
diff --git a/src/qml/types/qqmllistmodel_p_p.h b/src/qml/types/qqmllistmodel_p_p.h
index f519b5ff61..6e17b55e79 100644
--- a/src/qml/types/qqmllistmodel_p_p.h
+++ b/src/qml/types/qqmllistmodel_p_p.h
@@ -162,11 +162,13 @@ namespace QV4 {
namespace Heap {
struct ModelObject : public QObjectWrapper {
- ModelObject(QObject *object, QQmlListModel *model, int elementIndex)
- : QObjectWrapper(object)
- , m_model(model)
- , m_elementIndex(elementIndex)
- {}
+ void init(QObject *object, QQmlListModel *model, int elementIndex)
+ {
+ QObjectWrapper::init(object);
+ m_model = model;
+ m_elementIndex = elementIndex;
+ }
+ void destroy() { QObjectWrapper::destroy(); }
QQmlListModel *m_model;
int m_elementIndex;
};
@@ -180,6 +182,7 @@ struct ModelObject : public QObjectWrapper
static void advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes);
V4_OBJECT2(ModelObject, QObjectWrapper)
+ V4_NEEDS_DESTROY
};
} // namespace QV4
@@ -199,7 +202,7 @@ public:
explicit Role(const Role *other);
~Role();
- // This enum must be kept in sync with the roleTypeNames variable in qdeclarativelistmodel.cpp
+ // This enum must be kept in sync with the roleTypeNames variable in qqmllistmodel.cpp
enum DataType
{
Invalid = -1,
diff --git a/src/qml/types/qquickworkerscript.cpp b/src/qml/types/qquickworkerscript.cpp
index e819e4ee7d..78e7776c9b 100644
--- a/src/qml/types/qquickworkerscript.cpp
+++ b/src/qml/types/qquickworkerscript.cpp
@@ -52,10 +52,12 @@
#include <QtCore/qwaitcondition.h>
#include <QtCore/qfile.h>
#include <QtCore/qdatetime.h>
-#include <QtNetwork/qnetworkaccessmanager.h>
#include <QtQml/qqmlinfo.h>
#include <QtQml/qqmlfile.h>
+#if QT_CONFIG(qml_network)
+#include <QtNetwork/qnetworkaccessmanager.h>
#include "qqmlnetworkaccessmanagerfactory.h"
+#endif
#include <private/qv8engine_p.h>
#include <private/qv4serialize_p.h>
@@ -141,7 +143,10 @@ public:
~WorkerEngine();
void init();
+
+#if QT_CONFIG(qml_network)
virtual QNetworkAccessManager *networkAccessManager();
+#endif
QQuickWorkerScriptEnginePrivate *p;
@@ -150,7 +155,9 @@ public:
QV4::PersistentValue onmessage;
private:
QV4::PersistentValue createsend;
+#if QT_CONFIG(qml_network)
QNetworkAccessManager *accessManager;
+#endif
};
WorkerEngine *workerEngine;
@@ -194,14 +201,19 @@ private:
};
QQuickWorkerScriptEnginePrivate::WorkerEngine::WorkerEngine(QQuickWorkerScriptEnginePrivate *parent)
-: QV8Engine(0), p(parent), accessManager(0)
+: QV8Engine(0), p(parent)
+#if QT_CONFIG(qml_network)
+, accessManager(0)
+#endif
{
m_v4Engine->v8Engine = this;
}
QQuickWorkerScriptEnginePrivate::WorkerEngine::~WorkerEngine()
{
+#if QT_CONFIG(qml_network)
delete accessManager;
+#endif
}
void QQuickWorkerScriptEnginePrivate::WorkerEngine::init()
@@ -239,7 +251,8 @@ void QQuickWorkerScriptEnginePrivate::WorkerEngine::init()
QV4::ScopedCallData callData(scope, 1);
callData->args[0] = function;
callData->thisObject = global();
- createsend.set(scope.engine, createsendconstructor->call(callData));
+ createsendconstructor->call(scope, callData);
+ createsend.set(scope.engine, scope.result.asReturnedValue());
}
// Requires handle and context scope
@@ -252,16 +265,16 @@ QV4::ReturnedValue QQuickWorkerScriptEnginePrivate::WorkerEngine::sendFunction(i
QV4::Scope scope(v4);
QV4::ScopedFunctionObject f(scope, createsend.value());
- QV4::ScopedValue v(scope);
QV4::ScopedCallData callData(scope, 1);
callData->args[0] = QV4::Primitive::fromInt32(id);
callData->thisObject = global();
- v = f->call(callData);
+ f->call(scope, callData);
if (scope.hasException())
- v = scope.engine->catchException();
- return v->asReturnedValue();
+ scope.result = scope.engine->catchException();
+ return scope.result.asReturnedValue();
}
+#if QT_CONFIG(qml_network)
QNetworkAccessManager *QQuickWorkerScriptEnginePrivate::WorkerEngine::networkAccessManager()
{
if (!accessManager) {
@@ -273,6 +286,7 @@ QNetworkAccessManager *QQuickWorkerScriptEnginePrivate::WorkerEngine::networkAcc
}
return accessManager;
}
+#endif
QQuickWorkerScriptEnginePrivate::QQuickWorkerScriptEnginePrivate(QQmlEngine *engine)
: workerEngine(0), qmlengine(engine), m_nextId(0)
@@ -366,7 +380,7 @@ void QQuickWorkerScriptEnginePrivate::processMessage(int id, const QByteArray &d
callData->thisObject = workerEngine->global();
callData->args[0] = qmlContext->d()->qml; // ###
callData->args[1] = value;
- f->call(callData);
+ f->call(scope, callData);
if (scope.hasException()) {
QQmlError error = scope.engine->catchExceptionAsQmlError();
reportScriptException(script, error);
diff --git a/src/qml/util/qqmlchangeset_p.h b/src/qml/util/qqmlchangeset_p.h
index b7a637fb2e..8e1fa3f9f2 100644
--- a/src/qml/util/qqmlchangeset_p.h
+++ b/src/qml/util/qqmlchangeset_p.h
@@ -68,16 +68,31 @@ public:
int offset;
};
- struct Change
+ // The storrage for Change (below). This struct is trivial, which it has to be in order to store
+ // it in a QV4::Heap::Base object. The Change struct doesn't add any storage fields, so it is
+ // safe to cast ChangeData to/from Change.
+ struct ChangeData
{
- Change() : index(0), count(0), moveId(-1) {}
- Change(int index, int count, int moveId = -1, int offset = 0)
- : index(index), count(count), moveId(moveId), offset(offset) {}
-
int index;
int count;
int moveId;
int offset;
+ };
+
+ struct Change: ChangeData
+ {
+ Change() {
+ index = 0;
+ count = 0;
+ moveId = -1;
+ offset = 0;
+ }
+ Change(int index, int count, int moveId = -1, int offset = 0) {
+ this->index = index;
+ this->count = count;
+ this->moveId = moveId;
+ this->offset = offset;
+ }
bool isMove() const { return moveId >= 0; }
diff --git a/src/qmldebug/qqmldebugconnection.cpp b/src/qmldebug/qqmldebugconnection.cpp
index 35a540bff8..b5db71557f 100644
--- a/src/qmldebug/qqmldebugconnection.cpp
+++ b/src/qmldebug/qqmldebugconnection.cpp
@@ -77,7 +77,7 @@ public:
QStringList removedPlugins;
void advertisePlugins();
- void connectDeviceSignals();
+ void createProtocol();
void flush();
};
@@ -159,7 +159,7 @@ void QQmlDebugConnection::protocolReadyRead()
if (!validHello) {
qWarning("QQmlDebugConnection: Invalid hello message");
- QObject::disconnect(d->protocol, SIGNAL(protocolReadyRead()), this, SLOT(protocolReadyRead()));
+ close();
return;
}
d->gotHello = true;
@@ -254,7 +254,7 @@ QQmlDebugConnection::QQmlDebugConnection(QObject *parent) :
QObject(*(new QQmlDebugConnectionPrivate), parent)
{
Q_D(QQmlDebugConnection);
- connect(&d->handshakeTimer, SIGNAL(timeout()), this, SLOT(handshakeTimeout()));
+ connect(&d->handshakeTimer, &QTimer::timeout, this, &QQmlDebugConnection::handshakeTimeout);
}
QQmlDebugConnection::~QQmlDebugConnection()
@@ -374,8 +374,9 @@ bool QQmlDebugConnection::sendMessage(const QString &name, const QByteArray &mes
void QQmlDebugConnectionPrivate::flush()
{
- QAbstractSocket *socket = qobject_cast<QAbstractSocket*>(device);
- if (socket)
+ if (QAbstractSocket *socket = qobject_cast<QAbstractSocket *>(device))
+ socket->flush();
+ else if (QLocalSocket *socket = qobject_cast<QLocalSocket *>(device))
socket->flush();
}
@@ -386,12 +387,12 @@ void QQmlDebugConnection::connectToHost(const QString &hostName, quint16 port)
close();
QTcpSocket *socket = new QTcpSocket(this);
d->device = socket;
- d->connectDeviceSignals();
- connect(socket, SIGNAL(connected()), this, SLOT(socketConnected()));
- connect(socket, SIGNAL(error(QAbstractSocket::SocketError)),
- this, SIGNAL(socketError(QAbstractSocket::SocketError)));
- connect(socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)),
- this, SIGNAL(socketStateChanged(QAbstractSocket::SocketState)));
+ d->createProtocol();
+ connect(socket, &QAbstractSocket::disconnected, this, &QQmlDebugConnection::socketDisconnected);
+ connect(socket, &QAbstractSocket::connected, this, &QQmlDebugConnection::socketConnected);
+ connect(socket, static_cast<void(QAbstractSocket::*)(QAbstractSocket::SocketError)>(
+ &QAbstractSocket::error), this, &QQmlDebugConnection::socketError);
+ connect(socket, &QAbstractSocket::stateChanged, this, &QQmlDebugConnection::socketStateChanged);
socket->connectToHost(hostName, port);
}
@@ -404,7 +405,8 @@ void QQmlDebugConnection::startLocalServer(const QString &fileName)
d->server->deleteLater();
d->server = new QLocalServer(this);
// QueuedConnection so that waitForNewConnection() returns true.
- connect(d->server, SIGNAL(newConnection()), this, SLOT(newConnection()), Qt::QueuedConnection);
+ connect(d->server, &QLocalServer::newConnection,
+ this, &QQmlDebugConnection::newConnection, Qt::QueuedConnection);
d->server->listen(fileName);
}
@@ -414,17 +416,12 @@ class LocalSocketSignalTranslator : public QObject
public:
LocalSocketSignalTranslator(QLocalSocket *parent) : QObject(parent)
{
- connect(parent, SIGNAL(stateChanged(QLocalSocket::LocalSocketState)),
- this, SLOT(onStateChanged(QLocalSocket::LocalSocketState)));
- connect(parent, SIGNAL(error(QLocalSocket::LocalSocketError)),
- this, SLOT(onError(QLocalSocket::LocalSocketError)));
+ connect(parent, &QLocalSocket::stateChanged,
+ this, &LocalSocketSignalTranslator::onStateChanged);
+ connect(parent, static_cast<void(QLocalSocket::*)(QLocalSocket::LocalSocketError)>(
+ &QLocalSocket::error), this, &LocalSocketSignalTranslator::onError);
}
-signals:
- void socketError(QAbstractSocket::SocketError error);
- void socketStateChanged(QAbstractSocket::SocketState state);
-
-public slots:
void onError(QLocalSocket::LocalSocketError error)
{
emit socketError(static_cast<QAbstractSocket::SocketError>(error));
@@ -434,6 +431,10 @@ public slots:
{
emit socketStateChanged(static_cast<QAbstractSocket::SocketState>(state));
}
+
+signals:
+ void socketError(QAbstractSocket::SocketError error);
+ void socketStateChanged(QAbstractSocket::SocketState state);
};
void QQmlDebugConnection::newConnection()
@@ -443,23 +444,24 @@ void QQmlDebugConnection::newConnection()
QLocalSocket *socket = d->server->nextPendingConnection();
d->server->close();
d->device = socket;
- d->connectDeviceSignals();
+ d->createProtocol();
+ connect(socket, &QLocalSocket::disconnected, this, &QQmlDebugConnection::socketDisconnected);
LocalSocketSignalTranslator *translator = new LocalSocketSignalTranslator(socket);
- QObject::connect(translator, SIGNAL(socketError(QAbstractSocket::SocketError)),
- this, SIGNAL(socketError(QAbstractSocket::SocketError)));
- QObject::connect(translator, SIGNAL(socketStateChanged(QAbstractSocket::SocketState)),
- this, SIGNAL(socketStateChanged(QAbstractSocket::SocketState)));
+ connect(translator, &LocalSocketSignalTranslator::socketError,
+ this, &QQmlDebugConnection::socketError);
+ connect(translator, &LocalSocketSignalTranslator::socketStateChanged,
+ this, &QQmlDebugConnection::socketStateChanged);
socketConnected();
}
-void QQmlDebugConnectionPrivate::connectDeviceSignals()
+void QQmlDebugConnectionPrivate::createProtocol()
{
Q_Q(QQmlDebugConnection);
delete protocol;
protocol = new QPacketProtocol(device, q);
- QObject::connect(protocol, SIGNAL(readyRead()), q, SLOT(protocolReadyRead()));
- QObject::connect(device, SIGNAL(disconnected()), q, SLOT(socketDisconnected()));
+ QObject::connect(protocol, &QPacketProtocol::readyRead,
+ q, &QQmlDebugConnection::protocolReadyRead);
}
QT_END_NAMESPACE
diff --git a/src/qmldebug/qqmldebugconnection_p.h b/src/qmldebug/qqmldebugconnection_p.h
index 40753fc998..be425b6cbf 100644
--- a/src/qmldebug/qqmldebugconnection_p.h
+++ b/src/qmldebug/qqmldebugconnection_p.h
@@ -92,7 +92,7 @@ signals:
void socketError(QAbstractSocket::SocketError socketError);
void socketStateChanged(QAbstractSocket::SocketState socketState);
-private Q_SLOTS:
+private:
void newConnection();
void socketConnected();
void socketDisconnected();
diff --git a/src/qmldebug/qqmlprofilerclient.cpp b/src/qmldebug/qqmlprofilerclient.cpp
index 6f6b04ade9..29ccbb33b6 100644
--- a/src/qmldebug/qqmlprofilerclient.cpp
+++ b/src/qmldebug/qqmlprofilerclient.cpp
@@ -70,7 +70,7 @@ void QQmlProfilerClient::sendRecordingStatus(bool record, int engineId, quint32
Q_D(const QQmlProfilerClient);
QPacket stream(d->connection->currentDataStreamVersion());
- stream << record << engineId << d->features << flushInterval;
+ stream << record << engineId << d->features << flushInterval << true;
sendMessage(stream.data());
}
@@ -205,7 +205,7 @@ inline QQmlProfilerDefinitions::ProfileFeature featureFromRangeType(
void QQmlProfilerClient::messageReceived(const QByteArray &data)
{
- Q_D(const QQmlProfilerClient);
+ Q_D(QQmlProfilerClient);
QPacket stream(d->connection->currentDataStreamVersion(), data);
@@ -333,12 +333,25 @@ void QQmlProfilerClient::messageReceived(const QByteArray &data)
!(d->features & one << featureFromRangeType(rangeType)))
return;
+ qint64 typeId = 0;
if (messageType == QQmlProfilerDefinitions::RangeStart) {
rangeStart(rangeType, time);
+ if (!stream.atEnd()) {
+ stream >> typeId;
+ auto i = d->types.constFind(typeId);
+ if (i != d->types.constEnd()) {
+ rangeLocation(rangeType, time, i->location);
+ rangeData(rangeType, time, i->name);
+ }
+ }
} else if (messageType == QQmlProfilerDefinitions::RangeData) {
QString data;
stream >> data;
rangeData(rangeType, time, data);
+ if (!stream.atEnd()) {
+ stream >> typeId;
+ d->types[typeId].name = data;
+ }
} else if (messageType == QQmlProfilerDefinitions::RangeLocation) {
QQmlEventLocation location;
stream >> location.filename >> location.line;
@@ -347,6 +360,10 @@ void QQmlProfilerClient::messageReceived(const QByteArray &data)
stream >> location.column;
rangeLocation(rangeType, time, location);
+ if (!stream.atEnd()) {
+ stream >> typeId;
+ d->types[typeId].location = location;
+ }
} else if (messageType == QQmlProfilerDefinitions::RangeEnd) {
rangeEnd(rangeType, time);
} else {
diff --git a/src/qmldebug/qqmlprofilerclient_p.h b/src/qmldebug/qqmlprofilerclient_p.h
index 832c05fef7..b4054ed0d9 100644
--- a/src/qmldebug/qqmlprofilerclient_p.h
+++ b/src/qmldebug/qqmlprofilerclient_p.h
@@ -67,8 +67,6 @@ class QQmlProfilerClient : public QQmlDebugClient
public:
QQmlProfilerClient(QQmlDebugConnection *connection);
void setFeatures(quint64 features);
-
-public slots:
void sendRecordingStatus(bool record, int engineId = -1, quint32 flushInterval = 0);
protected:
diff --git a/src/qmldebug/qqmlprofilerclient_p_p.h b/src/qmldebug/qqmlprofilerclient_p_p.h
index 8238c97dd8..9c44113aa8 100644
--- a/src/qmldebug/qqmlprofilerclient_p_p.h
+++ b/src/qmldebug/qqmlprofilerclient_p_p.h
@@ -56,12 +56,20 @@
QT_BEGIN_NAMESPACE
+struct QQmlProfilerRangeType
+{
+ QQmlEventLocation location;
+ QString name;
+};
+
class QQmlProfilerClientPrivate : public QQmlDebugClientPrivate
{
Q_DECLARE_PUBLIC(QQmlProfilerClient)
public:
QQmlProfilerClientPrivate(QQmlDebugConnection *connection);
quint64 features;
+
+ QHash<qint64, QQmlProfilerRangeType> types;
};
QT_END_NAMESPACE
diff --git a/src/qmltest/qmltest.pro b/src/qmltest/qmltest.pro
index 42224e9751..9852861334 100644
--- a/src/qmltest/qmltest.pro
+++ b/src/qmltest/qmltest.pro
@@ -1,6 +1,6 @@
TARGET = QtQuickTest
-DEFINES += QT_NO_URL_CAST_FROM_STRING
+DEFINES += QT_NO_URL_CAST_FROM_STRING QT_NO_FOREACH
QT = core testlib-private
QT_PRIVATE = quick qml-private gui core-private
@@ -30,6 +30,6 @@ HEADERS += \
$$PWD/quicktestresult_p.h \
$$PWD/qtestoptions_p.h
-DEFINES += QT_QML_DEBUG_NO_WARNING
+!contains(QT_CONFIG, no-qml-debug): DEFINES += QT_QML_DEBUG_NO_WARNING
load(qt_module)
diff --git a/src/qmltest/quicktest.cpp b/src/qmltest/quicktest.cpp
index bc26a19033..0e348eee11 100644
--- a/src/qmltest/quicktest.cpp
+++ b/src/qmltest/quicktest.cpp
@@ -144,7 +144,7 @@ void handleCompileErrors(const QFileInfo &fi, QQuickView *view)
QTextStream str(&message);
str << "\n " << QDir::toNativeSeparators(fi.absoluteFilePath()) << " produced "
<< errors.size() << " error(s):\n";
- foreach (const QQmlError &e, errors) {
+ for (const QQmlError &e : errors) {
str << " ";
if (e.url().isLocalFile()) {
str << QDir::toNativeSeparators(e.url().toLocalFile());
@@ -158,11 +158,12 @@ void handleCompileErrors(const QFileInfo &fi, QQuickView *view)
str << " Working directory: " << QDir::toNativeSeparators(QDir::current().absolutePath()) << '\n';
if (QQmlEngine *engine = view->engine()) {
str << " View: " << view->metaObject()->className() << ", import paths:\n";
- foreach (const QString &i, engine->importPathList())
+ const auto importPaths = engine->importPathList();
+ for (const QString &i : importPaths)
str << " '" << QDir::toNativeSeparators(i) << "'\n";
const QStringList pluginPaths = engine->pluginPathList();
str << " Plugin paths:\n";
- foreach (const QString &p, pluginPaths)
+ for (const QString &p : pluginPaths)
str << " '" << QDir::toNativeSeparators(p) << "'\n";
}
qWarning("%s", qPrintable(message));
@@ -338,11 +339,11 @@ int quick_test_main(int argc, char **argv, const char *name, const char *sourceD
&eventLoop, SLOT(quit()));
view->rootContext()->setContextProperty
(QLatin1String("qtest"), QTestRootObject::instance()); // Deprecated. Use QTestRootObject from Qt.test.qtestroot instead
- foreach (const QString &path, imports)
+ for (const QString &path : qAsConst(imports))
view->engine()->addImportPath(path);
- foreach (const QString &path, pluginPaths)
+ for (const QString &path : qAsConst(pluginPaths))
view->engine()->addPluginPath(path);
- foreach (const QString &file, files) {
+ for (const QString &file : qAsConst(files)) {
const QFileInfo fi(file);
if (!fi.exists())
continue;
diff --git a/src/qmltest/quicktestresult.cpp b/src/qmltest/quicktestresult.cpp
index 0a820b79db..dd78b82dcb 100644
--- a/src/qmltest/quicktestresult.cpp
+++ b/src/qmltest/quicktestresult.cpp
@@ -46,6 +46,11 @@
#include <QtTest/private/qtestlog_p.h>
#include "qtestoptions_p.h"
#include <QtTest/qbenchmark.h>
+// qbenchmark_p.h pulls windows.h via 3rd party; prevent it from defining
+// the min/max macros which would clash with qnumeric_p.h's usage of min()/max().
+#if defined(Q_OS_WIN32) && !defined(NOMINMAX)
+# define NOMINMAX
+#endif
#include <QtTest/private/qbenchmark_p.h>
#include <QtCore/qset.h>
#include <QtCore/qmap.h>
@@ -248,7 +253,7 @@ void QuickTestResult::setDataTag(const QString &tag)
if (!tag.isEmpty()) {
QTestData *data = &(QTest::newRow(tag.toUtf8().constData()));
QTestResult::setCurrentTestData(data);
- QTestPrivate::checkBlackLists((testCaseName() + QStringLiteral("::") + functionName()).toUtf8().constData(), tag.toUtf8().constData());
+ QTestPrivate::checkBlackLists((testCaseName() + QLatin1String("::") + functionName()).toUtf8().constData(), tag.toUtf8().constData());
emit dataTagChanged();
} else {
QTestResult::setCurrentTestData(0);
diff --git a/src/quick/accessible/qaccessiblequickitem.cpp b/src/quick/accessible/qaccessiblequickitem.cpp
index 49cbc15829..1626aaac2d 100644
--- a/src/quick/accessible/qaccessiblequickitem.cpp
+++ b/src/quick/accessible/qaccessiblequickitem.cpp
@@ -156,9 +156,9 @@ int QAccessibleQuickItem::indexOfChild(const QAccessibleInterface *iface) const
static void unignoredChildren(QQuickItem *item, QList<QQuickItem *> *items, bool paintOrder)
{
- QList<QQuickItem*> childItems = paintOrder ? QQuickItemPrivate::get(item)->paintOrderChildItems()
+ const QList<QQuickItem*> childItems = paintOrder ? QQuickItemPrivate::get(item)->paintOrderChildItems()
: item->childItems();
- Q_FOREACH (QQuickItem *child, childItems) {
+ for (QQuickItem *child : childItems) {
if (QQuickItemPrivate::get(child)->isAccessible) {
items->append(child);
} else {
diff --git a/src/quick/configure.json b/src/quick/configure.json
new file mode 100644
index 0000000000..4ed11e8318
--- /dev/null
+++ b/src/quick/configure.json
@@ -0,0 +1,157 @@
+{
+ "module": "quick",
+ "depends": [
+ "qml-private",
+ "gui-private"
+ ],
+ "testDir": "../../config.tests",
+
+ "commandline": {
+ "options": {
+ "d3d12": "boolean",
+ "quick-animatedimage": "boolean",
+ "quick-canvas": "boolean",
+ "quick-designer": "boolean",
+ "quick-flipable": "boolean",
+ "quick-gridview": "boolean",
+ "quick-listview": "boolean",
+ "quick-path": "boolean",
+ "quick-pathview": "boolean",
+ "quick-positioners": "boolean",
+ "quick-shadereffect": "boolean",
+ "quick-sprite": "boolean"
+ }
+ },
+
+ "tests": {
+ "d3d12": {
+ "label": "Direct3D 12",
+ "type": "compile",
+ "test": "d3d12"
+ }
+ },
+
+ "features": {
+ "d3d12": {
+ "label": "Direct3D 12",
+ "purpose": "Provides a Direct3D 12 backend for the Qt Quick Scenegraph",
+ "condition": "tests.d3d12",
+ "output": [
+ "publicFeature"
+ ]
+ },
+ "quick-animatedimage": {
+ "label": "AnimatedImage item",
+ "purpose": "Provides the Qt Quick AnimatedImage Item",
+ "condition": "features.movie",
+ "output": [
+ "privateFeature"
+ ]
+ },
+ "quick-canvas": {
+ "label": "Canvas item",
+ "purpose": "Provides the Qt Quick Canvas Item",
+ "output": [
+ "privateFeature"
+ ]
+ },
+ "quick-designer": {
+ "label": "Support for Quick Designer",
+ "purpose": "Provides support for the Qt Quick Designer in Qt Creator",
+ "output": [
+ "privateFeature"
+ ]
+ },
+ "quick-flipable": {
+ "label": "Flipable item",
+ "purpose": "Provides the Qt Quick Flipable Item",
+ "output": [
+ "privateFeature"
+ ]
+ },
+ "quick-gridview": {
+ "label": "GridView item",
+ "purpose": "Provides the Qt Quick GridView item",
+ "output": [
+ "privateFeature"
+ ]
+ },
+ "quick-itemview": {
+ "label": "ItemView item",
+ "condition": "features.quick-gridview || features.quick-listview",
+ "output": [
+ "privateFeature"
+ ]
+ },
+ "quick-viewtransitions": {
+ "label": "Transitions required for ItemViews and Positioners",
+ "condition": "features.quick-itemview || features.quick-positioners",
+ "output": [
+ "privateFeature"
+ ]
+ },
+ "quick-listview": {
+ "label": "ListView item",
+ "purpose": "Provides the Qt Quick ListView item",
+ "output": [
+ "privateFeature"
+ ]
+ },
+ "quick-path": {
+ "label": "Path support",
+ "purpose": "Provides Path elements in Qt Quick",
+ "output": [
+ "privateFeature"
+ ]
+ },
+ "quick-pathview": {
+ "label": "PathView item",
+ "purpose": "Provides the Qt Quick PathView item",
+ "condition": "features.quick-path",
+ "output": [
+ "privateFeature"
+ ]
+ },
+ "quick-positioners": {
+ "label": "Positioner items",
+ "purpose": "Provides Positioner items in Qt Quick",
+ "output": [
+ "privateFeature"
+ ]
+ },
+ "quick-shadereffect": {
+ "label": "ShaderEffect item",
+ "purpose": "Provides Shader effects in Qt Quick",
+ "output": [
+ "privateFeature"
+ ]
+ },
+ "quick-sprite": {
+ "label": "Sprite item",
+ "purpose": "Provides the Qt Quick Sprite Item",
+ "output": [
+ "privateFeature"
+ ]
+ }
+ },
+
+ "summary": [
+ {
+ "section": "Qt Quick",
+ "entries": [
+ "d3d12",
+ "quick-animatedimage",
+ "quick-canvas",
+ "quick-designer",
+ "quick-flipable",
+ "quick-gridview",
+ "quick-listview",
+ "quick-path",
+ "quick-pathview",
+ "quick-positioners",
+ "quick-shadereffect",
+ "quick-sprite"
+ ]
+ }
+ ]
+}
diff --git a/src/quick/designer/designer.pri b/src/quick/designer/designer.pri
index eb2141134d..f87ea4da04 100644
--- a/src/quick/designer/designer.pri
+++ b/src/quick/designer/designer.pri
@@ -7,7 +7,8 @@ HEADERS += \
designer/qquickdesignersupportproperties_p.h \
designer/qquickdesignersupportmetainfo_p.h \
designer/qqmldesignermetaobject_p.h \
- designer/qquickdesignersupport_p.h
+ designer/qquickdesignersupport_p.h \
+ designer/qquickdesignercustomparserobject_p.h
SOURCES += \
designer/qquickdesignercustomobjectdata.cpp \
@@ -18,4 +19,5 @@ SOURCES += \
designer/qquickdesignersupportpropertychanges.cpp \
designer/qquickdesignersupportstates.cpp \
designer/qquickdesignerwindowmanager.cpp \
- designer/qqmldesignermetaobject.cpp
+ designer/qqmldesignermetaobject.cpp \
+ designer/qquickdesignercustomparserobject.cpp
diff --git a/src/quick/designer/qqmldesignermetaobject.cpp b/src/quick/designer/qqmldesignermetaobject.cpp
index 0bfa72e9d4..5e897218c5 100644
--- a/src/quick/designer/qqmldesignermetaobject.cpp
+++ b/src/quick/designer/qqmldesignermetaobject.cpp
@@ -79,34 +79,6 @@ struct MetaPropertyData {
QVector<QPair<QVariant, bool> > m_data;
};
-static bool constructedMetaData(const QQmlVMEMetaData* data)
-{
- return data->propertyCount == 0
- && data->aliasCount == 0
- && data->signalCount == 0
- && data->methodCount == 0;
-}
-
-static QQmlVMEMetaData* fakeMetaData()
-{
- QQmlVMEMetaData* data = new QQmlVMEMetaData;
- data->propertyCount = 0;
- data->aliasCount = 0;
- data->signalCount = 0;
- data->methodCount = 0;
-
- return data;
-}
-
-static const QQmlVMEMetaData* vMEMetaDataForObject(QObject *object)
-{
- QQmlVMEMetaObject *metaObject = QQmlVMEMetaObject::get(object);
- if (metaObject)
- return metaObject->metaData;
-
- return fakeMetaData();
-}
-
static QQmlPropertyCache *cacheForObject(QObject *object, QQmlEngine *engine)
{
QQmlVMEMetaObject *metaObject = QQmlVMEMetaObject::get(object);
@@ -125,7 +97,15 @@ QQmlDesignerMetaObject* QQmlDesignerMetaObject::getNodeInstanceMetaObject(QObjec
return static_cast<QQmlDesignerMetaObject *>(parent);
// we just create one and the ownership goes automatically to the object in nodeinstance see init method
- return new QQmlDesignerMetaObject(object, engine);
+
+ QQmlData *ddata = QQmlData::get(object, false);
+
+ const bool hadVMEMetaObject = ddata ? ddata->hasVMEMetaObject : false;
+ QQmlDesignerMetaObject *mo = new QQmlDesignerMetaObject(object, engine);
+ //If our parent is not a VMEMetaObject we just set the flag to false again
+ if (ddata)
+ ddata->hasVMEMetaObject = hadVMEMetaObject;
+ return mo;
}
void QQmlDesignerMetaObject::init(QObject *object, QQmlEngine *engine)
@@ -140,39 +120,27 @@ void QQmlDesignerMetaObject::init(QObject *object, QQmlEngine *engine)
QObjectPrivate *op = QObjectPrivate::get(object);
op->metaObject = this;
- m_cache = QQmlEnginePrivate::get(engine)->cache(this);
-
- if (m_cache != cache) {
- m_cache->addref();
- cache->release();
- cache = m_cache;
- }
-
- //If our parent is not a VMEMetaObject we just se the flag to false again
- if (constructedMetaData(metaData))
- QQmlData::get(object)->hasVMEMetaObject = false;
+ cache = QQmlEnginePrivate::get(engine)->cache(this);
nodeInstanceMetaObjectList.insert(this, true);
hasAssignedMetaObjectData = true;
}
QQmlDesignerMetaObject::QQmlDesignerMetaObject(QObject *object, QQmlEngine *engine)
- : QQmlVMEMetaObject(object, cacheForObject(object, engine), vMEMetaDataForObject(object)),
+ : QQmlVMEMetaObject(object, cacheForObject(object, engine), /*qml compilation unit*/nullptr, /*qmlObjectId*/-1),
m_context(engine->contextForObject(object)),
- m_data(new MetaPropertyData),
- m_cache(0)
+ m_data(new MetaPropertyData)
{
init(object, engine);
QQmlData *ddata = QQmlData::get(object, false);
-
//Assign cache to object
if (ddata && ddata->propertyCache) {
cache->setParent(ddata->propertyCache);
cache->invalidate(engine, this);
ddata->propertyCache->release();
- ddata->propertyCache = m_cache;
- m_cache->addref();
+ ddata->propertyCache = cache;
+ ddata->propertyCache->addref();
}
}
@@ -193,9 +161,9 @@ void QQmlDesignerMetaObject::createNewDynamicProperty(const QString &name)
Q_UNUSED(id);
//Updating cache
- QQmlPropertyCache *oldParent = m_cache->parent();
+ QQmlPropertyCache *oldParent = cache->parent();
QQmlEnginePrivate::get(m_context->engine())->cache(this)->invalidate(m_context->engine(), this);
- m_cache->setParent(oldParent);
+ cache->setParent(oldParent);
QQmlProperty property(myObject(), name, m_context);
Q_ASSERT(property.isValid());
diff --git a/src/quick/designer/qqmldesignermetaobject_p.h b/src/quick/designer/qqmldesignermetaobject_p.h
index c45f83dc1e..01512f6af0 100644
--- a/src/quick/designer/qqmldesignermetaobject_p.h
+++ b/src/quick/designer/qqmldesignermetaobject_p.h
@@ -70,7 +70,6 @@ public:
static void registerNotifyPropertyChangeCallBack(void (*callback)(QObject*, const QQuickDesignerSupport::PropertyName &propertyName));
protected:
- QQmlDesignerMetaObject(QObject *object, QQmlEngine *engine);
static QQmlDesignerMetaObject* getNodeInstanceMetaObject(QObject *object, QQmlEngine *engine);
void createNewDynamicProperty(const QString &name);
@@ -95,13 +94,13 @@ protected:
void copyTypeMetaObject();
private:
+ QQmlDesignerMetaObject(QObject *object, QQmlEngine *engine);
void init(QObject *, QQmlEngine *engine);
QPointer<QQmlContext> m_context;
QQmlOpenMetaObjectType *m_type;
QScopedPointer<MetaPropertyData> m_data;
//QAbstractDynamicMetaObject *m_parent;
- QQmlPropertyCache *m_cache;
friend class QQuickDesignerSupportProperties;
};
diff --git a/src/quick/designer/qquickdesignercustomobjectdata.cpp b/src/quick/designer/qquickdesignercustomobjectdata.cpp
index 93e7b6133f..e37254d165 100644
--- a/src/quick/designer/qquickdesignercustomobjectdata.cpp
+++ b/src/quick/designer/qquickdesignercustomobjectdata.cpp
@@ -145,10 +145,10 @@ void QQuickDesignerCustomObjectData::keepBindingFromGettingDeleted(QObject *obje
void QQuickDesignerCustomObjectData::populateResetHashes()
{
- QQuickDesignerSupport::PropertyNameList propertyNameList =
+ const QQuickDesignerSupport::PropertyNameList propertyNameList =
QQuickDesignerSupportProperties::propertyNameListForWritableProperties(object());
- Q_FOREACH (const QQuickDesignerSupport::PropertyName &propertyName, propertyNameList) {
+ for (const QQuickDesignerSupport::PropertyName &propertyName : propertyNameList) {
QQmlProperty property(object(), QString::fromUtf8(propertyName), QQmlEngine::contextForObject(object()));
QQmlAbstractBinding::Ptr binding = QQmlAbstractBinding::Ptr(QQmlPropertyPrivate::binding(property));
@@ -194,7 +194,7 @@ void QQuickDesignerCustomObjectData::doResetProperty(QQmlContext *context, const
#endif
if (qmlBinding)
qmlBinding->setTarget(property);
- QQmlPropertyPrivate::setBinding(binding, QQmlPropertyPrivate::None, QQmlPropertyPrivate::DontRemoveBinding);
+ QQmlPropertyPrivate::setBinding(binding, QQmlPropertyPrivate::None, QQmlPropertyData::DontRemoveBinding);
if (qmlBinding)
qmlBinding->update();
@@ -257,11 +257,12 @@ void QQuickDesignerCustomObjectData::setPropertyBinding(QQmlContext *context,
return;
if (property.isProperty()) {
- QQmlBinding *binding = new QQmlBinding(expression, object(), context);
+ QQmlBinding *binding = QQmlBinding::create(&QQmlPropertyPrivate::get(property)->core,
+ expression, object(), context);
binding->setTarget(property);
binding->setNotifyOnValueChanged(true);
- QQmlPropertyPrivate::setBinding(binding, QQmlPropertyPrivate::None, QQmlPropertyPrivate::DontRemoveBinding);
+ QQmlPropertyPrivate::setBinding(binding, QQmlPropertyPrivate::None, QQmlPropertyData::DontRemoveBinding);
//Refcounting is taking take care of deletion
binding->update();
if (binding->hasError()) {
diff --git a/src/quick/designer/qquickdesignercustomparserobject.cpp b/src/quick/designer/qquickdesignercustomparserobject.cpp
new file mode 100644
index 0000000000..b785abe361
--- /dev/null
+++ b/src/quick/designer/qquickdesignercustomparserobject.cpp
@@ -0,0 +1,59 @@
+/****************************************************************************
+**
+** 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 "qquickdesignercustomparserobject_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QQuickDesignerCustomParserObject::QQuickDesignerCustomParserObject()
+{
+
+}
+
+void QQuickDesignerCustomParser::verifyBindings(const QV4::CompiledData::Unit *, const QList<const QV4::CompiledData::Binding *> &)
+{
+ /* Nothing to do we accept anything */
+}
+
+void QQuickDesignerCustomParser::applyBindings(QObject *, QV4::CompiledData::CompilationUnit *, const QList<const QV4::CompiledData::Binding *> &)
+{
+ /* Nothing to do we accept anything */
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/designer/qquickdesignercustomparserobject_p.h b/src/quick/designer/qquickdesignercustomparserobject_p.h
new file mode 100644
index 0000000000..e4d0765277
--- /dev/null
+++ b/src/quick/designer/qquickdesignercustomparserobject_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 QUICKDESIGNERCUSTOMPARSEROBJECT_H
+#define QUICKDESIGNERCUSTOMPARSEROBJECT_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 <QObject>
+#include <private/qqmlcustomparser_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQuickDesignerCustomParserObject : public QObject
+{
+ Q_OBJECT
+
+public:
+ QQuickDesignerCustomParserObject();
+};
+
+class QQuickDesignerCustomParser : public QQmlCustomParser
+{
+public:
+ QQuickDesignerCustomParser()
+ : QQmlCustomParser(AcceptsAttachedProperties | AcceptsSignalHandlers) {}
+
+ void verifyBindings(const QV4::CompiledData::Unit *qmlUnit, const QList<const QV4::CompiledData::Binding *> &props) override;
+ void applyBindings(QObject *obj, QV4::CompiledData::CompilationUnit *compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings) override;
+};
+
+QT_END_NAMESPACE
+
+#endif // QUICKDESIGNERCUSTOMPARSEROBJECT_H
diff --git a/src/quick/designer/qquickdesignersupport.cpp b/src/quick/designer/qquickdesignersupport.cpp
index 78ed89a107..3ff5a3ce96 100644
--- a/src/quick/designer/qquickdesignersupport.cpp
+++ b/src/quick/designer/qquickdesignersupport.cpp
@@ -40,7 +40,9 @@
#include "qquickdesignersupport_p.h"
#include <private/qquickitem_p.h>
+#if QT_CONFIG(quick_shadereffect)
#include <QtQuick/private/qquickshadereffectsource_p.h>
+#endif
#include <QtQuick/private/qquickrectangle_p.h>
#include <QtQml/private/qabstractanimationjob_p.h>
#include <private/qqmlengine_p.h>
@@ -51,6 +53,7 @@
#include <private/qqmlvme_p.h>
#include <private/qqmlcomponentattached_p.h>
#include <private/qqmldata_p.h>
+#include <private/qsgadaptationlayer_p.h>
#include "qquickdesignerwindowmanager_p.h"
@@ -92,6 +95,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 +104,7 @@ void QQuickDesignerSupport::refFromEffectItem(QQuickItem *referencedItem, bool h
#else
texture->setFormat(GL_RGBA);
#endif
+#endif
texture->setHasMipmaps(false);
m_itemTextureHash.insert(referencedItem, texture);
@@ -234,7 +239,8 @@ bool QQuickDesignerSupport::isAnchoredTo(QQuickItem *fromItem, QQuickItem *toIte
bool QQuickDesignerSupport::areChildrenAnchoredTo(QQuickItem *fromItem, QQuickItem *toItem)
{
- Q_FOREACH (QQuickItem *childItem, fromItem->childItems()) {
+ const auto childItems = fromItem->childItems();
+ for (QQuickItem *childItem : childItems) {
if (childItem) {
if (isAnchoredTo(childItem, toItem))
return true;
@@ -390,10 +396,10 @@ void QQuickDesignerSupport::emitComponentCompleteSignalForAttachedProperty(QQuic
QList<QObject*> QQuickDesignerSupport::statesForItem(QQuickItem *item)
{
QList<QObject*> objectList;
- QList<QQuickState *> stateList = QQuickItemPrivate::get(item)->_states()->states();
+ const QList<QQuickState *> stateList = QQuickItemPrivate::get(item)->_states()->states();
objectList.reserve(stateList.size());
- Q_FOREACH (QQuickState* state, stateList)
+ for (QQuickState* state : stateList)
objectList.append(state);
return objectList;
diff --git a/src/quick/designer/qquickdesignersupportitems.cpp b/src/quick/designer/qquickdesignersupportitems.cpp
index 544ca04754..2003b484ad 100644
--- a/src/quick/designer/qquickdesignersupportitems.cpp
+++ b/src/quick/designer/qquickdesignersupportitems.cpp
@@ -118,16 +118,16 @@ static void allSubObjects(QObject *object, QObjectList &objectList)
}
// search recursive in object children list
- Q_FOREACH (QObject *childObject, object->children()) {
+ for (QObject *childObject : object->children()) {
allSubObjects(childObject, objectList);
}
// search recursive in quick item childItems list
QQuickItem *quickItem = qobject_cast<QQuickItem*>(object);
if (quickItem) {
- Q_FOREACH (QQuickItem *childItem, quickItem->childItems()) {
+ const auto childItems = quickItem->childItems();
+ for (QQuickItem *childItem : childItems)
allSubObjects(childItem, objectList);
- }
}
}
@@ -135,7 +135,7 @@ void QQuickDesignerSupportItems::tweakObjects(QObject *object)
{
QObjectList objectList;
allSubObjects(object, objectList);
- Q_FOREACH (QObject* childObject, objectList) {
+ for (QObject* childObject : qAsConst(objectList)) {
stopAnimation(childObject);
if (fixResourcePathsForObjectCallBack)
fixResourcePathsForObjectCallBack(childObject);
@@ -254,7 +254,8 @@ QObject *QQuickDesignerSupportItems::createComponent(const QUrl &componentUrl, Q
if (component.isError()) {
qWarning() << "Error in:" << Q_FUNC_INFO << componentUrl;
- Q_FOREACH (const QQmlError &error, component.errors())
+ const auto errors = component.errors();
+ for (const QQmlError &error : errors)
qWarning() << error;
}
return object;
@@ -282,7 +283,8 @@ void QQuickDesignerSupportItems::disableNativeTextRendering(QQuickItem *item)
void QQuickDesignerSupportItems::disableTextCursor(QQuickItem *item)
{
- Q_FOREACH (QQuickItem *childItem, item->childItems())
+ const auto childItems = item->childItems();
+ for (QQuickItem *childItem : childItems)
disableTextCursor(childItem);
QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(item);
diff --git a/src/quick/designer/qquickdesignersupportmetainfo.cpp b/src/quick/designer/qquickdesignersupportmetainfo.cpp
index 27c9814ef1..332ae26bd4 100644
--- a/src/quick/designer/qquickdesignersupportmetainfo.cpp
+++ b/src/quick/designer/qquickdesignersupportmetainfo.cpp
@@ -37,6 +37,7 @@
**
****************************************************************************/
+#include "qquickdesignercustomparserobject_p.h"
#include "qquickdesignersupportmetainfo_p.h"
#include "qqmldesignermetaobject_p.h"
@@ -70,5 +71,10 @@ void QQuickDesignerSupportMetaInfo::registerNotifyPropertyChangeCallBack(void (*
QQmlDesignerMetaObject::registerNotifyPropertyChangeCallBack(callback);
}
+void QQuickDesignerSupportMetaInfo::registerMockupObject(const char *uri, int versionMajor, int versionMinor, const char *qmlName)
+{
+ qmlRegisterCustomType<QQuickDesignerCustomParserObject>(uri, versionMajor, versionMinor, qmlName, new QQuickDesignerCustomParser);
+}
+
QT_END_NAMESPACE
diff --git a/src/quick/designer/qquickdesignersupportmetainfo_p.h b/src/quick/designer/qquickdesignersupportmetainfo_p.h
index 43cd8e8fb1..344d720d98 100644
--- a/src/quick/designer/qquickdesignersupportmetainfo_p.h
+++ b/src/quick/designer/qquickdesignersupportmetainfo_p.h
@@ -63,6 +63,7 @@ class Q_QUICK_EXPORT QQuickDesignerSupportMetaInfo
public:
static bool isSubclassOf(QObject *object, const QByteArray &superTypeName);
static void registerNotifyPropertyChangeCallBack(void (*callback)(QObject *, const QQuickDesignerSupport::PropertyName &));
+ static void registerMockupObject(const char *uri, int versionMajor, int versionMinor, const char *qmlName);
};
QT_END_NAMESPACE
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/snippets/qml/externaldrag.qml b/src/quick/doc/snippets/qml/externaldrag.qml
new file mode 100644
index 0000000000..096a7702b4
--- /dev/null
+++ b/src/quick/doc/snippets/qml/externaldrag.qml
@@ -0,0 +1,76 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the documentation 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$
+**
+****************************************************************************/
+//![0]
+import QtQuick 2.8
+
+Item {
+ width: 200; height: 200
+
+ Rectangle {
+ anchors.centerIn: parent
+ width: text.implicitWidth + 20; height: text.implicitHeight + 10
+ color: "green"
+ radius: 5
+
+ Drag.active: dragArea.drag.active
+ Drag.dragType: Drag.Automatic
+ Drag.supportedActions: Qt.CopyAction
+ Drag.mimeData: {
+ "text/plain": "Copied text"
+ }
+
+ Text {
+ id: text
+ anchors.centerIn: parent
+ text: "Drag me"
+ }
+
+ MouseArea {
+ id: dragArea
+ anchors.fill: parent
+
+ drag.target: parent
+ onPressed: parent.grabToImage(function(result) {
+ parent.Drag.imageSource = result.url
+ })
+ }
+ }
+}
+//![0]
diff --git a/src/quick/doc/snippets/qml/localstorage/dbtransaction.js b/src/quick/doc/snippets/qml/localstorage/dbtransaction.js
index 07d8a5618b..40eb6d2804 100644
--- a/src/quick/doc/snippets/qml/localstorage/dbtransaction.js
+++ b/src/quick/doc/snippets/qml/localstorage/dbtransaction.js
@@ -39,30 +39,47 @@
****************************************************************************/
//![0]
+var db = LocalStorage.openDatabaseSync("ActivityTrackDB", "", "Database tracking sports activities", 1000000);
db.transaction(
try {
function(tx) {
- tx.executeSql('INSERT INTO Greeting VALUES(?, ?)', [ 'hello', 'world' ]);
+ tx.executeSql('INSERT INTO trip_log VALUES(?, ?, ?)',
+ [ '01/10/2016','Sylling - Vikersund', '53' ]);
}
} catch (err) {
- console.log("Error inserting into table Greeting");
+ console.log("Error inserting into table Greeting: " + err);
}
)
//![0]
//![1]
+// Retrieve activity date, description and distance based on minimum
+// distance parameter Pdistance
+function db_distance_select(Pdistance)
+{
+var db = LocalStorage.openDatabaseSync("ActivityTrackDB", "", "Database tracking sports activities", 1000000);
db.transaction(
function(tx) {
- var results = tx.executeSql('SELECT salutation FROM Greeting WHERE salutee=?;', 'world');
+ var results = tx.executeSql('SELECT rowid,
+ date,
+ trip_desc,
+ distance FROM trip_log
+ where distance >= ?',[Pdistance]');
+ for (var i = 0; i < results.rows.length; i++) {
+ listModel.append({"id": results.rows.item(i).rowid,
+ "date": results.rows.item(i).date,
+ "trip_desc": results.rows.item(i).trip_desc,
+ "distance": results.rows.item(i).distance});
+ }
}
- console.log("We greeted in this most respectful way: " + results.rows.item(0).value);
-)
+}
//![1]
//![2]
-var db = LocalStorage.openDatabaseSync("QQmlExampleDB", "", "The Example QML SQL!", 1000000);
+var db = LocalStorage.openDatabaseSync("ActivityTrackDB", "", "Database tracking sports activities", 1000000);
if (db.version == '0.1') {
db.changeVersion('0.1', '0.2', function(tx) {
- tx.executeSql('INSERT INTO Greeting VALUES(?, ?)', [ 'hello', 'world' ]);
+ tx.executeSql('INSERT INTO trip_log VALUES(?, ?, ?)',
+ [ '01/10/2016','Sylling - Vikersund', '53' ]);
}
});
//![2]
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..9ce26e1bb8
--- /dev/null
+++ b/src/quick/doc/src/concepts/visualcanvas/adaptations.qdoc
@@ -0,0 +1,389 @@
+/****************************************************************************
+**
+** 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 (with framebuffer object extensions) APIs. The Qt Quick APIs have
+originally been designed with the assumption that OpenGL is always available.
+However, it is now possible to use other graphics API's to render Qt Quick
+scenes using the scene graph APIs.
+
+\section1 Switching between the adaptation used by the application
+
+The default of the OpenGL, or - in Qt builds with disabled OpenGL support - the
+software adaptation, can be overridden either by using an environment variable
+or a C++ API. The former consists of setting the \c{QT_QUICK_BACKEND} or the
+legacy \c{QMLSCENE_DEVICE} environment variable before launching applications.
+The latter is done by calling QQuickWindow::setSceneGraphBackend() early in the
+application's main() function.
+
+The supported backends are the following
+
+\list
+
+\li OpenGL - Requested by the string \c{""} or the enum value QSGRendererInterface::OpenGL.
+
+\li Software - Requested by the string \c{"software"} or the enum value QSGRendererInterface::Software.
+
+\li Direct3D 12 - Requested by the string \c{"d3d12"} or the enum value QSGRendererInterface::Direct3D12.
+
+\endlist
+
+When in doubt which backend is in use, enable basic scenegraph information
+logging via the \c{QSG_INFO} environment variable or the
+\c{qt.scenegraph.general} logging category. This will result in printing some
+information during application startup onto the debug output.
+
+\note Adaptations other than OpenGL will typically come with a set of
+limitations since they are unlikely to provide a feature set 100% compatible
+with OpenGL. However, they may provide their own specific advantages in certain
+areas. Refer to the sections below for more information on the various
+adaptations.
+
+\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. The
+details for this adaptation are available here:
+\l{qtquick-visualcanvas-adaptations-software.html}{Software Adaptation}
+
+\section1 Direct3D 12 (experimental)
+
+The Direct3D 12 adaptation is an alternative renderer for \l {Qt Quick} 2 when
+running on Windows 10, both for Win32 and UWP applications. The details for
+this adaptation are available here:
+\l{qtquick-visualcanvas-adaptations-d3d12.html}{Direct3D 12 Adaptation}
+
+*/
+
+
+/*!
+\title Qt Quick Software Adaptation
+\page qtquick-visualcanvas-adaptations-software.html
+
+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 adaptation 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.
+However, unlike the 2D Renderer, the new, integrated version supports partial
+updates. This means that the full update of the window or screen contents is
+now avoided, and only the changed areas get flushed. This can significantly
+improve performance for many applications.
+
+\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 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.
+
+*/
+
+
+/*!
+\title Qt Quick Direct3D 12 Adaptation
+\page qtquick-visualcanvas-adaptations-d3d12.html
+
+The Direct3D 12 adaptation for Windows 10 (both Win32 (\c windows platform
+plugin) and UWP (\c winrt platform plugin)) is shipped as a dynamically loaded
+plugin. It will not be functional on earlier Windows versions. The building of
+the plugin is enabled automatically whenever the necessary D3D and DXGI
+develpoment files are present. In practice this currently means Visual Studio
+2015 and newer.
+
+The adaptation is available both in normal, OpenGL-enabled Qt builds and also
+when Qt was configured with \c{-no-opengl}. However, it is never the default,
+meaning the user or the application has to explicitly request it by setting the
+\c{QT_QUICK_BACKEND} environment variable to \c{d3d12} or by calling
+QQuickWindow::setSceneGraphBackend().
+
+\section2 Motivation
+
+This experimental adaptation is the first Qt Quick backend focusing on a
+modern, lower-level graphics API in combination with a windowing system
+interface different from the traditional approaches used in combination with
+OpenGL.
+
+It also allows better integration with Windows, Direct3D being the primary
+vendor-supported solution. This means that there are fewer problems anticipated
+with drivers, operations like window resizes, and special events like graphics
+device loss caused by device resets or graphics driver updates.
+
+Performance-wise the general expectation is a somewhat lower CPU usage compared
+to OpenGL due to lower driver overhead, and a higher GPU utilization with less
+wasted idle time. The backend does not heavily utilize threads yet, which means
+there are opportunities for further improvements in the future, for example to
+further optimize image loading.
+
+The D3D12 backend also introduces support for pre-compiled shaders. All the
+backend's own shaders (used by the built-in materials on which the Rectangle,
+Image, Text, etc. QML types are built) are compiled to D3D shader bytecode when
+compiling Qt. Applications using ShaderEffect items can chose to ship bytecode
+either in regular files or via the Qt resource system, or use HLSL source
+strings. Unlike OpenGL, the compilation for the latter is properly threaded,
+meaning shader compilation will not block the application and its user
+interface.
+
+\section2 Graphics Adapters
+
+The plugin does not necessarily require hardware acceleration. Using WARP, the
+Direct3D software rasterizer, is also an option. By default the first adapter
+providing hardware acceleration is chosen. To override this, in order to use
+another graphics adapter or to force the usage of the software rasterizer, set
+the environment variable \c{QT_D3D_ADAPTER_INDEX} to the index of the adapter.
+The discovered adapters are printed at startup when \c{QSG_INFO} or the logging
+category \c{qt.scenegraph.general} is enabled.
+
+\section2 Troubleshooting
+
+When encountering issues, always set the \c{QSG_INFO} and \c{QT_D3D_DEBUG}
+environment variables to 1 in order to get debug and warning messages printed
+on the debug output. The latter enables the Direct3D debug layer. Note that the
+debug layer should not be enabled in production use since it can significantly
+impact performance (CPU load) due to increased API overhead.
+
+\section2 Render Loops
+
+By default the D3D12 adaptation uses a single-threaded render loop similar to
+OpenGL's \c windows render loop. There is also a threaded variant available, that
+can be requested by setting the \c{QSG_RENDER_LOOP} environment variable to \c
+threaded. However, due to conceptual limitations in DXGI, the windowing system
+interface, the threaded loop is prone to deadlocks when multiple QQuickWindow
+or QQuickView instances are shown. Therefore the default is the single-threaded
+loop for the time being. This means that with the D3D12 backend applications
+are expected to move their work from the main (GUI) thread out to worker
+threads, instead of expecting Qt to keep the GUI thread responsive and suitable
+for heavy, blocking operations.
+
+See the \l{qtquick-visualcanvas-scenegraph.html}{Scene Graph page} for more
+information on render loops and
+\l{https://msdn.microsoft.com/en-us/library/windows/desktop/ee417025(v=vs.85).aspx#multithreading_and_dxgi}{the
+MSDN page for DXGI} regarding the issues with multithreading.
+
+\section2 Renderer
+
+The scenegraph renderer in the D3D12 adaptation does not currently perform any
+batching. This is less of an issue, unlike OpenGL, because state changes are
+not presenting any problems in the first place. The simpler renderer logic can
+also lead to lower CPU overhead in some cases. The trade-offs between the
+various approaches are currently under research.
+
+\section2 Shader Effects
+
+The ShaderEffect QML type is fully functional with the D3D12 adaptation as well.
+However, the interpretation of the fragmentShader and vertexShader properties is
+different than with OpenGL.
+
+With D3D12, these strings can either be an URL for a local file or a file in
+the resource system, or a HLSL source string. The former indicates that the
+file in question contains pre-compiled D3D shader bytecode generated by the
+\c fxc tool, or, alternatively, HLSL source code. The type of the file is detected
+automatically. This means that the D3D12 backend supports all options from
+GraphicsInfo.shaderCompilationType and GraphicsInfo.shaderSourceType.
+
+Unlike OpenGL, there is a QFileSelector with the extra selector \c hlsl used
+whenever opening a file. This allows easy creation of ShaderEffect items that
+are functional across both backends, for example by placing the GLSL source
+code into \c{shaders/effect.frag}, the HLSL source code or - preferably -
+pre-compiled bytecode into \c{shaders/+hlsl/effect.frag}, while simply writing
+\c{fragmentShader: "qrc:shaders/effect.frag"} in QML.
+
+See the ShaderEffect documentation for more details.
+
+\section2 Multisample Render Targets
+
+The Direct3D 12 adaptation ignores the QSurfaceFormat set on the QQuickWindow
+or QQuickView (or set via QSurfaceFormat::setDefaultFormat()), with two
+exceptions: QSurfaceFormat::samples() and QSurfaceFormat::alphaBufferSize() are
+still taken into account. When the samples value is greater than 1, multisample
+offscreen render targets will be created with the specified sample count and a
+quality of the maximum supported quality level. The backend automatically
+performs resolving into the non-multisample swapchain buffers after each frame.
+
+\section2 Semi-transparent windows
+
+When the alpha channel is enabled either via
+QQuickWindow::setDefaultAlphaBuffer() or by setting alphaBufferSize to a
+non-zero value in the window's QSurfaceFormat or in the global format managed
+by QSurfaceFormat::setDefaultFormat(), the D3D12 backend will create a
+swapchain for composition and go through DirectComposition since the flip model
+swapchain (which is mandatory) would not support transparency otherwise.
+
+It is therefore important not to unneccessarily request an alpha channel. When
+the alphaBufferSize is 0 or the default -1, all these extra steps can be
+avoided and the traditional window-based swapchain is sufficient.
+
+This is not relevant on WinRT because there the backend always uses a
+composition swapchain which is associated with the ISwapChainPanel that backs
+QWindow on that platform.
+
+\section2 Mipmaps
+
+Mipmap generation is supported and handled transparently to the applications
+via a built-in compute shader, but is experimental and only supports
+power-of-two images at the moment. Textures of other size will work too, but
+this involves a QImage-based scaling on the CPU first. Therefore avoid enabling
+mipmapping for NPOT images whenever possible.
+
+\section2 Image formats
+
+When creating textures via the C++ scenegraph APIs like
+QQuickWindow::createTextureFromImage(), 32-bit formats will not involve any
+conversion, they will map directly to the corresponding \c{R8G8B8A8_UNORM} or
+\c{B8G8R8A8_UNORM} format. Everything else will trigger a QImage-based format
+conversion on the CPU first.
+
+\section2 Unsupported Features
+
+Particles and some other OpenGL-dependent utilities, like
+QQuickFramebufferObject, are not currently supported.
+
+Like with the \l{qtquick-visualcanvas-adaptations-software.html}{Software
+adaptation}, text is always rendered using the native method. Distance
+field-based text rendering is not currently implemented.
+
+The shader sources in the \l {Qt Graphical Effects} module have not been ported
+to any format other than the OpenGL 2.0 compatible one, meaning the QML types
+provided by that module are not currently functional with the D3D12 backend.
+
+Texture atlases are not currently in use.
+
+The renderer may lack support for certain minor features, for example drawing
+points and lines with a width other than 1.
+
+Custom Qt Quick items using custom scenegraph nodes can be problematic.
+Materials are inherently tied to the graphics API. Therefore only items using
+the utility rectangle and image nodes are functional across all adaptations.
+
+QQuickWidget and its underlying OpenGL-based compositing architecture is not
+supported. If mixing with QWidget-based user interfaces is desired, use
+QWidget::createWindowContainer() to embed the native window of the QQuickWindow
+or QQuickView.
+
+Finally, rendering via QSGEngine and QSGAbstractRenderer is not feasible with
+the D3D12 adaptation at the moment.
+
+\section2 Related APIs
+
+To integrate custom Direct3D 12 rendering, use QSGRenderNode in combination
+with QSGRendererInterface. This approach does not rely on OpenGL contexts or
+API specifics like framebuffers, and allows exposing the graphics device and
+command buffer from the adaptation. It is not necessarily suitable for easy
+integration of all types of content, in particular true 3D, so it will likely
+get complemented by an alternative to QQuickFramebufferObject in future
+releases.
+
+To perform runtime decisions based on the adaptation in use, use
+QSGRendererInterface from C++ and GraphicsInfo from QML. They can also be used
+to check the level of shader support (shading language, compilation approach).
+
+When creating custom items, use the new QSGRectangleNode and QSGImageNode
+classes. These replace the now deprecated QSGSimpleRectNode and
+QSGSimpleTextureNode. Unlike their predecessors, the new classes are
+interfaces, and implementations are created via the factory functions
+QQuickWindow::createRectangleNode() and QQuickWindow::createImageNode().
+
+\section2 Advanced Configuration
+
+The D3D12 adaptation can keep multiple frames in flight, similarly to modern
+game engines. This is somewhat different from the traditional render - swap -
+wait for vsync model and allows better GPU utilization at the expense of higher
+resource usage. This means that the renderer will be a number of frames ahead
+of what is displayed on the screen.
+
+For a discussion of flip model swap chains and the typical configuration
+parameters, refer to
+\l{https://software.intel.com/en-us/articles/sample-application-for-direct3d-12-flip-model-swap-chains}{this
+article}.
+
+Vertical synchronization is always enabled, meaning Present() is invoked with
+an interval of 1.
+
+The configuration can be changed by setting the following environment variables:
+
+\list
+
+\li \c{QT_D3D_BUFFER_COUNT} - The number of swap chain buffers in range 2 - 4.
+The default value is 3.
+
+\li \c{QT_D3D_FRAME_COUNT} - The number of frames prepared without blocking in
+range 1 - 4. Note that Present will start blocking after queuing 3 frames
+(regardless of \c{QT_D3D_BUFFER_COUNT}), unless the waitable object is in use.
+Note that every additional frame increases GPU resource usage since geometry
+and constant buffer data will have to be duplicated, and involves more
+bookkeeping on the CPU side. The default value is 2.
+
+\li \c{QT_D3D_WAITABLE_SWAP_CHAIN_MAX_LATENCY} - When set to a value between 1
+and 16, the frame latency is set to the specified value. This changes the limit
+for Present() and will trigger a wait for an available swap chain buffer when
+beginning each frame. Refer to the article above for a detailed discussion.
+This is considered experimental for now and the default value is 0 (disabled).
+
+\li \c{QT_D3D_BLOCKING_PRESENT} - When set to a non-zero value, there will be
+CPU-side wait for the GPU to finish its work after each call to Present. This
+effectively kills all parallelism but makes the behavior resemble the
+traditional swap-blocks-for-vsync model, and can therefore be useful in some
+special cases. This is not the same as setting the frame count to 1 because
+that still avoids blocking after Present, and may block only when starting to
+prepare the next frame (or may not block at all depending on the time gap
+between the frames). By default blocking present is disabled.
+
+\endlist
+
+*/
diff --git a/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc b/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc
index e5cd7e2424..2e41c85873 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/doc/src/qmltypereference.qdoc b/src/quick/doc/src/qmltypereference.qdoc
index 6e6e66e026..2406722dbc 100644
--- a/src/quick/doc/src/qmltypereference.qdoc
+++ b/src/quick/doc/src/qmltypereference.qdoc
@@ -100,9 +100,9 @@ available when you import \c QtQuick.
\li By a hexadecimal triplet or quad in the form \c "#RRGGBB" and \c "#AARRGGBB"
respectively. For example, the color red corresponds to a triplet of \c "#FF0000"
and a slightly transparent blue to a quad of \c "#800000FF".
- \li Using the \l{QtQml::Qt::rgba()}{Qt.rgba()}, \l{QtQml::Qt::hsla()}{Qt.hsla()},
- \l{QtQml::Qt::darker()}{Qt.darker()}, \l{QtQml::Qt::lighter()}{Qt.lighter()} or
- \l{QtQml::Qt::tint()}{Qt.tint()} functions.
+ \li Using the \l{QtQml::Qt::rgba()}{Qt.rgba()}, \l{QtQml::Qt::hsva()}{Qt.hsva()},
+ \l{QtQml::Qt::hsla()}{Qt.hsla()}, \l{QtQml::Qt::darker()}{Qt.darker()},
+ \l{QtQml::Qt::lighter()}{Qt.lighter()} or \l{QtQml::Qt::tint()}{Qt.tint()} functions.
\endlist
Example:
@@ -112,8 +112,11 @@ available when you import \c QtQuick.
\enddiv
\snippet qml/colors.qml colors
- Additionally, a color type has \c r, \c g, \c b and \c a properties that refer to the
- red, green, blue and alpha values of the color, respectively:
+ A color type has \c r, \c g, \c b and \c a properties that refer to the red,
+ green, blue and alpha values of the color, respectively. Additionally it has
+ \c hsvHue, \c hsvSaturation, \c hsvValue and \c hslHue, \c hslSaturation,
+ \c hslLightness properties, which allow access to color values in HSV and HSL
+ color models accordingly:
\qml
Text {
diff --git a/src/quick/items/context2d/qquickcanvascontext_p.h b/src/quick/items/context2d/qquickcanvascontext_p.h
index 4f71770e1a..0746b7dcd3 100644
--- a/src/quick/items/context2d/qquickcanvascontext_p.h
+++ b/src/quick/items/context2d/qquickcanvascontext_p.h
@@ -51,10 +51,13 @@
// We mean it.
//
+#include <private/qtquickglobal_p.h>
+
+QT_REQUIRE_CONFIG(quick_canvas);
+
#include <QtQuick/qquickitem.h>
#include <private/qv8engine_p.h>
-
QT_BEGIN_NAMESPACE
class QQuickCanvasItem;
diff --git a/src/quick/items/context2d/qquickcanvasitem.cpp b/src/quick/items/context2d/qquickcanvasitem.cpp
index 89a68e4f33..3b2b125c63 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;
+ QSGInternalImageNode *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)
@@ -712,7 +709,7 @@ void QQuickCanvasItem::updatePolish()
for (auto it = animationCallbacks.cbegin(), end = animationCallbacks.cend(); it != end; ++it) {
QV4::ScopedFunctionObject f(scope, it.value().value());
callData->args[0] = QV4::Primitive::fromUInt32(QDateTime::currentMSecsSinceEpoch() / 1000);
- f->call(callData);
+ f->call(scope, callData);
}
}
else {
@@ -739,16 +736,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);
+ QSGInternalImageNode *node = static_cast<QSGInternalImageNode *>(oldNode);
if (!node) {
- node = new QQuickCanvasNode();
+ node = QQuickWindowPrivate::get(window())->context->sceneGraphContext()->createInternalImageNode();
d->node = node;
}
@@ -765,22 +762,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;
@@ -800,14 +802,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/qquickcanvasitem_p.h b/src/quick/items/context2d/qquickcanvasitem_p.h
index 4f94393a45..8196debef1 100644
--- a/src/quick/items/context2d/qquickcanvasitem_p.h
+++ b/src/quick/items/context2d/qquickcanvasitem_p.h
@@ -51,6 +51,10 @@
// We mean it.
//
+#include <private/qtquickglobal_p.h>
+
+QT_REQUIRE_CONFIG(quick_canvas);
+
#include <QtQuick/qquickitem.h>
#include <private/qv8engine_p.h>
#include <private/qqmlrefcount_p.h>
diff --git a/src/quick/items/context2d/qquickcontext2d.cpp b/src/quick/items/context2d/qquickcontext2d.cpp
index b2117d3eb9..2483a8eadb 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>
@@ -484,34 +489,43 @@ namespace QV4 {
namespace Heap {
struct QQuickJSContext2D : Object {
- QQuickJSContext2D() {}
+ void init() { Object::init(); }
QQuickContext2D* context;
};
struct QQuickJSContext2DPrototype : Object {
- QQuickJSContext2DPrototype() {}
+ void init() { Object::init(); }
};
struct QQuickContext2DStyle : Object {
- QQuickContext2DStyle()
+ void init()
{
+ brush = new QBrush;
patternRepeatX = false;
patternRepeatY = false;
}
+ void destroy() {
+ delete brush;
+ Object::destroy();
+ }
- QBrush brush;
+ QBrush *brush;
bool patternRepeatX:1;
bool patternRepeatY:1;
};
struct QQuickJSContext2DPixelData : Object {
- QQuickJSContext2DPixelData();
+ void init();
+ void destroy() {
+ delete image;
+ Object::destroy();
+ }
- QImage image;
+ QImage *image;
};
struct QQuickJSContext2DImageData : Object {
- QQuickJSContext2DImageData();
+ void init();
QV4::Value pixelData;
};
@@ -789,7 +803,7 @@ static QPainter::CompositionMode qt_composite_mode_from_string(const QString &co
} else if (compositeOperator == QLatin1String("destination-over")) {
return QPainter::CompositionMode_DestinationOver;
} else if (compositeOperator == QLatin1String("lighter")) {
- return QPainter::CompositionMode_Lighten;
+ return QPainter::CompositionMode_Plus;
} else if (compositeOperator == QLatin1String("copy")) {
return QPainter::CompositionMode_Source;
} else if (compositeOperator == QLatin1String("xor")) {
@@ -852,7 +866,7 @@ static QString qt_composite_mode_to_string(QPainter::CompositionMode op)
case QPainter::CompositionMode_Xor:
return QStringLiteral("xor");
case QPainter::CompositionMode_Plus:
- return QStringLiteral("plus");
+ return QStringLiteral("lighter");
case QPainter::CompositionMode_Multiply:
return QStringLiteral("qt-multiply");
case QPainter::CompositionMode_Screen:
@@ -892,8 +906,10 @@ struct QQuickJSContext2DPixelData : public QV4::Object
static QV4::ReturnedValue proto_get_length(QV4::CallContext *ctx);
};
-QV4::Heap::QQuickJSContext2DPixelData::QQuickJSContext2DPixelData()
+void QV4::Heap::QQuickJSContext2DPixelData::init()
{
+ Object::init();
+ image = new QImage;
QV4::Scope scope(internalClass->engine);
QV4::ScopedObject o(scope, this);
o->setArrayType(QV4::Heap::ArrayData::Custom);
@@ -915,8 +931,9 @@ struct QQuickJSContext2DImageData : public QV4::Object
}
};
-QV4::Heap::QQuickJSContext2DImageData::QQuickJSContext2DImageData()
+void QV4::Heap::QQuickJSContext2DImageData::init()
{
+ Object::init();
pixelData = QV4::Primitive::undefinedValue();
QV4::Scope scope(internalClass->engine);
@@ -938,11 +955,11 @@ static QV4::ReturnedValue qt_create_image_data(qreal w, qreal h, QV4::ExecutionE
pixelData->setPrototype(p);
if (image.isNull()) {
- pixelData->d()->image = QImage(w, h, QImage::Format_ARGB32);
- pixelData->d()->image.fill(0x00000000);
+ *pixelData->d()->image = QImage(w, h, QImage::Format_ARGB32);
+ pixelData->d()->image->fill(0x00000000);
} else {
Q_ASSERT(image.width() == qRound(w) && image.height() == qRound(h));
- pixelData->d()->image = image.format() == QImage::Format_ARGB32 ? image : image.convertToFormat(QImage::Format_ARGB32);
+ *pixelData->d()->image = image.format() == QImage::Format_ARGB32 ? image : image.convertToFormat(QImage::Format_ARGB32);
}
QV4::Scoped<QQuickJSContext2DImageData> imageData(scope, scope.engine->memoryManager->allocObject<QQuickJSContext2DImageData>());
@@ -1387,9 +1404,9 @@ QV4::ReturnedValue QQuickJSContext2D::method_set_fillStyle(QV4::CallContext *ctx
r->d()->context->m_fillStyle.set(scope.engine, value);
} else {
QV4::Scoped<QQuickContext2DStyle> style(scope, value->as<QQuickContext2DStyle>());
- if (style && style->d()->brush != r->d()->context->state.fillStyle) {
- r->d()->context->state.fillStyle = style->d()->brush;
- r->d()->context->buffer()->setFillStyle(style->d()->brush, style->d()->patternRepeatX, style->d()->patternRepeatY);
+ if (style && *style->d()->brush != r->d()->context->state.fillStyle) {
+ r->d()->context->state.fillStyle = *style->d()->brush;
+ r->d()->context->buffer()->setFillStyle(*style->d()->brush, style->d()->patternRepeatX, style->d()->patternRepeatY);
r->d()->context->m_fillStyle.set(scope.engine, value);
r->d()->context->state.fillPatternRepeatX = style->d()->patternRepeatX;
r->d()->context->state.fillPatternRepeatY = style->d()->patternRepeatY;
@@ -1496,9 +1513,9 @@ QV4::ReturnedValue QQuickJSContext2D::method_set_strokeStyle(QV4::CallContext *c
r->d()->context->m_strokeStyle.set(scope.engine, value);
} else {
QV4::Scoped<QQuickContext2DStyle> style(scope, value->as<QQuickContext2DStyle>());
- if (style && style->d()->brush != r->d()->context->state.strokeStyle) {
- r->d()->context->state.strokeStyle = style->d()->brush;
- r->d()->context->buffer()->setStrokeStyle(style->d()->brush, style->d()->patternRepeatX, style->d()->patternRepeatY);
+ if (style && *style->d()->brush != r->d()->context->state.strokeStyle) {
+ r->d()->context->state.strokeStyle = *style->d()->brush;
+ r->d()->context->buffer()->setStrokeStyle(*style->d()->brush, style->d()->patternRepeatX, style->d()->patternRepeatY);
r->d()->context->m_strokeStyle.set(scope.engine, value);
r->d()->context->state.strokePatternRepeatX = style->d()->patternRepeatX;
r->d()->context->state.strokePatternRepeatY = style->d()->patternRepeatY;
@@ -1556,7 +1573,7 @@ QV4::ReturnedValue QQuickJSContext2DPrototype::method_createLinearGradient(QV4::
QV4::Scoped<QQuickContext2DStyle> gradient(scope, scope.engine->memoryManager->allocObject<QQuickContext2DStyle>());
QV4::ScopedObject p(scope, ed->gradientProto.value());
gradient->setPrototype(p);
- gradient->d()->brush = QLinearGradient(x0, y0, x1, y1);
+ *gradient->d()->brush = QLinearGradient(x0, y0, x1, y1);
return gradient.asReturnedValue();
}
@@ -1607,7 +1624,7 @@ QV4::ReturnedValue QQuickJSContext2DPrototype::method_createRadialGradient(QV4::
QV4::Scoped<QQuickContext2DStyle> gradient(scope, scope.engine->memoryManager->allocObject<QQuickContext2DStyle>());
QV4::ScopedObject p(scope, ed->gradientProto.value());
gradient->setPrototype(p);
- gradient->d()->brush = QRadialGradient(QPointF(x1, y1), r0+r1, QPointF(x0, y0));
+ *gradient->d()->brush = QRadialGradient(QPointF(x1, y1), r0+r1, QPointF(x0, y0));
return gradient.asReturnedValue();
}
@@ -1650,7 +1667,7 @@ QV4::ReturnedValue QQuickJSContext2DPrototype::method_createConicalGradient(QV4:
QV4::Scoped<QQuickContext2DStyle> gradient(scope, scope.engine->memoryManager->allocObject<QQuickContext2DStyle>());
QV4::ScopedObject p(scope, ed->gradientProto.value());
gradient->setPrototype(p);
- gradient->d()->brush = QConicalGradient(x, y, angle);
+ *gradient->d()->brush = QConicalGradient(x, y, angle);
return gradient.asReturnedValue();
}
@@ -1715,7 +1732,7 @@ QV4::ReturnedValue QQuickJSContext2DPrototype::method_createPattern(QV4::CallCon
if (patternMode >= 0 && patternMode < Qt::LinearGradientPattern) {
style = static_cast<Qt::BrushStyle>(patternMode);
}
- pattern->d()->brush = QBrush(color, style);
+ *pattern->d()->brush = QBrush(color, style);
} else {
QImage patternTexture;
@@ -1723,14 +1740,14 @@ QV4::ReturnedValue QQuickJSContext2DPrototype::method_createPattern(QV4::CallCon
QV4::ScopedString s(scope, scope.engine->newString(QStringLiteral("data")));
QV4::Scoped<QQuickJSContext2DPixelData> pixelData(scope, o->get(s));
if (!!pixelData) {
- patternTexture = pixelData->d()->image;
+ patternTexture = *pixelData->d()->image;
}
} else {
patternTexture = r->d()->context->createPixmap(QUrl(ctx->args()[0].toQStringNoThrow()))->image();
}
if (!patternTexture.isNull()) {
- pattern->d()->brush.setTextureImage(patternTexture);
+ pattern->d()->brush->setTextureImage(patternTexture);
QString repetition = ctx->args()[1].toQStringNoThrow();
if (repetition == QLatin1String("repeat") || repetition.isEmpty()) {
@@ -2669,15 +2686,15 @@ QV4::ReturnedValue QQuickJSContext2D::method_set_textAlign(QV4::CallContext *ctx
QString textAlign = s->toQString();
QQuickContext2D::TextAlignType ta;
- if (textAlign == QStringLiteral("start"))
+ if (textAlign == QLatin1String("start"))
ta = QQuickContext2D::Start;
- else if (textAlign == QStringLiteral("end"))
+ else if (textAlign == QLatin1String("end"))
ta = QQuickContext2D::End;
- else if (textAlign == QStringLiteral("left"))
+ else if (textAlign == QLatin1String("left"))
ta = QQuickContext2D::Left;
- else if (textAlign == QStringLiteral("right"))
+ else if (textAlign == QLatin1String("right"))
ta = QQuickContext2D::Right;
- else if (textAlign == QStringLiteral("center"))
+ else if (textAlign == QLatin1String("center"))
ta = QQuickContext2D::Center;
else
return QV4::Encode::undefined();
@@ -2920,8 +2937,8 @@ QV4::ReturnedValue QQuickJSContext2DPrototype::method_drawImage(QV4::CallContext
QV4::Scoped<QQuickJSContext2DImageData> imageData(scope, arg);
if (!!imageData) {
QV4::Scoped<QQuickJSContext2DPixelData> pix(scope, imageData->d()->pixelData.as<QQuickJSContext2DPixelData>());
- if (pix && !pix->d()->image.isNull()) {
- pixmap.adopt(new QQuickCanvasPixmap(pix->d()->image));
+ if (pix && !pix->d()->image->isNull()) {
+ pixmap.adopt(new QQuickCanvasPixmap(*pix->d()->image));
} else {
V4THROW_DOM(DOMEXCEPTION_TYPE_MISMATCH_ERR, "drawImage(), type mismatch");
}
@@ -3029,7 +3046,7 @@ QV4::ReturnedValue QQuickJSContext2DImageData::method_get_width(QV4::CallContext
QV4::Scoped<QQuickJSContext2DPixelData> r(scope, imageData->d()->pixelData.as<QQuickJSContext2DPixelData>());
if (!r)
return QV4::Encode(0);
- return QV4::Encode(r->d()->image.width());
+ return QV4::Encode(r->d()->image->width());
}
/*!
@@ -3045,7 +3062,7 @@ QV4::ReturnedValue QQuickJSContext2DImageData::method_get_height(QV4::CallContex
QV4::Scoped<QQuickJSContext2DPixelData> r(scope, imageData->d()->pixelData.as<QQuickJSContext2DPixelData>());
if (!r)
return QV4::Encode(0);
- return QV4::Encode(r->d()->image.height());
+ return QV4::Encode(r->d()->image->height());
}
/*!
@@ -3083,10 +3100,10 @@ QV4::ReturnedValue QQuickJSContext2DPixelData::proto_get_length(QV4::CallContext
{
QV4::Scope scope(ctx);
QV4::Scoped<QQuickJSContext2DPixelData> r(scope, ctx->thisObject().as<QQuickJSContext2DPixelData>());
- if (!r || r->d()->image.isNull())
+ if (!r || r->d()->image->isNull())
return QV4::Encode::undefined();
- return QV4::Encode(r->d()->image.width() * r->d()->image.height() * 4);
+ return QV4::Encode(r->d()->image->width() * r->d()->image->height() * 4);
}
QV4::ReturnedValue QQuickJSContext2DPixelData::getIndexed(const QV4::Managed *m, uint index, bool *hasProperty)
@@ -3096,13 +3113,13 @@ QV4::ReturnedValue QQuickJSContext2DPixelData::getIndexed(const QV4::Managed *m,
QV4::Scope scope(v4);
QV4::Scoped<QQuickJSContext2DPixelData> r(scope, static_cast<const QQuickJSContext2DPixelData *>(m));
- if (index < static_cast<quint32>(r->d()->image.width() * r->d()->image.height() * 4)) {
+ if (index < static_cast<quint32>(r->d()->image->width() * r->d()->image->height() * 4)) {
if (hasProperty)
*hasProperty = true;
- const quint32 w = r->d()->image.width();
+ const quint32 w = r->d()->image->width();
const quint32 row = (index / 4) / w;
const quint32 col = (index / 4) % w;
- const QRgb* pixel = reinterpret_cast<const QRgb*>(r->d()->image.constScanLine(row));
+ const QRgb* pixel = reinterpret_cast<const QRgb*>(r->d()->image->constScanLine(row));
pixel += col;
switch (index % 4) {
case 0:
@@ -3131,12 +3148,12 @@ void QQuickJSContext2DPixelData::putIndexed(QV4::Managed *m, uint index, const Q
QV4::Scoped<QQuickJSContext2DPixelData> r(scope, static_cast<QQuickJSContext2DPixelData *>(m));
const int v = value.toInt32();
- if (r && index < static_cast<quint32>(r->d()->image.width() * r->d()->image.height() * 4) && v >= 0 && v <= 255) {
- const quint32 w = r->d()->image.width();
+ if (r && index < static_cast<quint32>(r->d()->image->width() * r->d()->image->height() * 4) && v >= 0 && v <= 255) {
+ const quint32 w = r->d()->image->width();
const quint32 row = (index / 4) / w;
const quint32 col = (index / 4) % w;
- QRgb* pixel = reinterpret_cast<QRgb*>(r->d()->image.scanLine(row));
+ QRgb* pixel = reinterpret_cast<QRgb*>(r->d()->image->scanLine(row));
pixel += col;
switch (index % 4) {
case 0:
@@ -3187,8 +3204,8 @@ QV4::ReturnedValue QQuickJSContext2DPrototype::method_createImageData(QV4::CallC
if (!!imgData) {
QV4::Scoped<QQuickJSContext2DPixelData> pa(scope, imgData->d()->pixelData.as<QQuickJSContext2DPixelData>());
if (pa) {
- qreal w = pa->d()->image.width();
- qreal h = pa->d()->image.height();
+ qreal w = pa->d()->image->width();
+ qreal h = pa->d()->image->height();
return qt_create_image_data(w, h, scope.engine, QImage());
}
} else if (arg0->isString()) {
@@ -3266,8 +3283,8 @@ QV4::ReturnedValue QQuickJSContext2DPrototype::method_putImageData(QV4::CallCont
QV4::Scoped<QQuickJSContext2DPixelData> pixelArray(scope, imageData->d()->pixelData.as<QQuickJSContext2DPixelData>());
if (pixelArray) {
- w = pixelArray->d()->image.width();
- h = pixelArray->d()->image.height();
+ w = pixelArray->d()->image->width();
+ h = pixelArray->d()->image->height();
if (ctx->argc() == 7) {
dirtyX = ctx->args()[3].toNumber();
@@ -3316,7 +3333,7 @@ QV4::ReturnedValue QQuickJSContext2DPrototype::method_putImageData(QV4::CallCont
dirtyHeight = h;
}
- QImage image = pixelArray->d()->image.copy(dirtyX, dirtyY, dirtyWidth, dirtyHeight);
+ QImage image = pixelArray->d()->image->copy(dirtyX, dirtyY, dirtyWidth, dirtyHeight);
r->d()->context->buffer()->drawImage(image, QRectF(dirtyX, dirtyY, dirtyWidth, dirtyHeight), QRectF(dx, dy, dirtyWidth, dirtyHeight));
}
return ctx->thisObject().asReturnedValue();
@@ -3351,9 +3368,9 @@ QV4::ReturnedValue QQuickContext2DStyle::gradient_proto_addColorStop(QV4::CallCo
if (ctx->argc() == 2) {
- if (!style->d()->brush.gradient())
+ if (!style->d()->brush->gradient())
V4THROW_ERROR("Not a valid CanvasGradient object, can't get the gradient information");
- QGradient gradient = *(style->d()->brush.gradient());
+ QGradient gradient = *(style->d()->brush->gradient());
qreal pos = ctx->args()[0].toNumber();
QColor color;
@@ -3371,7 +3388,7 @@ QV4::ReturnedValue QQuickContext2DStyle::gradient_proto_addColorStop(QV4::CallCo
} else {
V4THROW_DOM(DOMEXCEPTION_SYNTAX_ERR, "CanvasGradient: parameter color is not a valid color string");
}
- style->d()->brush = gradient;
+ *style->d()->brush = gradient;
}
return ctx->thisObject().asReturnedValue();
@@ -3980,10 +3997,12 @@ public:
~QQuickContext2DThreadCleanup()
{
+#ifndef QT_NO_OPENGL
context->makeCurrent(surface);
delete texture;
context->doneCurrent();
delete context;
+#endif
surface->deleteLater();
}
@@ -4019,6 +4038,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 +4059,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 +4085,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 +4103,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 +4134,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 +4165,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 +4212,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 +4228,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/qquickcontext2d_p.h b/src/quick/items/context2d/qquickcontext2d_p.h
index cfb62ee052..e897263b6f 100644
--- a/src/quick/items/context2d/qquickcontext2d_p.h
+++ b/src/quick/items/context2d/qquickcontext2d_p.h
@@ -51,7 +51,10 @@
// We mean it.
//
-#include <QtQuick/qtquickglobal.h>
+#include <private/qtquickglobal_p.h>
+
+QT_REQUIRE_CONFIG(quick_canvas);
+
#include <QtQml/qqml.h>
#include <QtQml/qqmlcomponent.h>
#include <private/qquickcanvascontext_p.h>
@@ -126,8 +129,8 @@ public:
struct State {
State()
- : strokeStyle(QColor("#000000"))
- , fillStyle(QColor("#000000"))
+ : strokeStyle(QColor(Qt::black))
+ , fillStyle(QColor(Qt::black))
, fillPatternRepeatX(false)
, fillPatternRepeatY(false)
, strokePatternRepeatX(false)
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/qquickcontext2dcommandbuffer_p.h b/src/quick/items/context2d/qquickcontext2dcommandbuffer_p.h
index a82b88f36f..3663e49f10 100644
--- a/src/quick/items/context2d/qquickcontext2dcommandbuffer_p.h
+++ b/src/quick/items/context2d/qquickcontext2dcommandbuffer_p.h
@@ -51,6 +51,10 @@
// We mean it.
//
+#include <private/qtquickglobal_p.h>
+
+QT_REQUIRE_CONFIG(quick_canvas);
+
#include <QtCore/qmutex.h>
#include "qquickcontext2d_p.h"
@@ -70,7 +74,7 @@ public:
inline int size() {return commands.size();}
inline bool isEmpty() const {return commands.isEmpty(); }
inline bool hasNext() const {return cmdIdx < commands.size(); }
- inline QQuickContext2D::PaintCommand takeNextCommand() { return commands[cmdIdx++]; }
+ inline QQuickContext2D::PaintCommand takeNextCommand() { return commands.at(cmdIdx++); }
inline qreal takeGlobalAlpha() { return takeReal(); }
inline QPainter::CompositionMode takeGlobalCompositeOperation(){ return static_cast<QPainter::CompositionMode>(takeInt()); }
@@ -227,20 +231,20 @@ public:
colors << color;
}
- inline QTransform takeMatrix() { return matrixes[matrixIdx++]; }
+ inline QTransform takeMatrix() { return matrixes.at(matrixIdx++); }
- inline QRectF takeRect() { return rects[rectIdx++]; }
+ inline QRectF takeRect() { return rects.at(rectIdx++); }
- inline QPainterPath takePath() { return pathes[pathIdx++]; }
+ inline QPainterPath takePath() { return pathes.at(pathIdx++); }
- inline const QImage& takeImage() { return images[imageIdx++]; }
- inline QQmlRefPointer<QQuickCanvasPixmap> takePixmap() { return pixmaps[pixmapIdx++]; }
+ inline const QImage& takeImage() { return images.at(imageIdx++); }
+ inline QQmlRefPointer<QQuickCanvasPixmap> takePixmap() { return pixmaps.at(pixmapIdx++); }
- inline int takeInt() { return ints[intIdx++]; }
- inline bool takeBool() {return bools[boolIdx++]; }
- inline qreal takeReal() { return reals[realIdx++]; }
- inline QColor takeColor() { return colors[colorIdx++]; }
- inline QBrush takeBrush() { return brushes[brushIdx++]; }
+ inline int takeInt() { return ints.at(intIdx++); }
+ inline bool takeBool() {return bools.at(boolIdx++); }
+ inline qreal takeReal() { return reals.at(realIdx++); }
+ inline QColor takeColor() { return colors.at(colorIdx++); }
+ inline QBrush takeBrush() { return brushes.at(brushIdx++); }
void replay(QPainter* painter, QQuickContext2D::State& state, const QVector2D &scaleFactor);
diff --git a/src/quick/items/context2d/qquickcontext2dtexture.cpp b/src/quick/items/context2d/qquickcontext2dtexture.cpp
index 375f537b9b..4435c0c37b 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;
@@ -650,6 +654,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..6a5d4e8b09 100644
--- a/src/quick/items/context2d/qquickcontext2dtexture_p.h
+++ b/src/quick/items/context2d/qquickcontext2dtexture_p.h
@@ -51,13 +51,17 @@
// We mean it.
//
+#include <private/qtquickglobal_p.h>
+
+QT_REQUIRE_CONFIG(quick_canvas);
+
#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 +125,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 +157,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 +180,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 +215,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..2f3fdeb54f 100644
--- a/src/quick/items/context2d/qquickcontext2dtile_p.h
+++ b/src/quick/items/context2d/qquickcontext2dtile_p.h
@@ -51,9 +51,14 @@
// We mean it.
//
-#include "qquickcontext2d_p.h"
-#include <QOpenGLFramebufferObject>
+#include <private/qtquickglobal_p.h>
+
+QT_REQUIRE_CONFIG(quick_canvas);
+#include "qquickcontext2d_p.h"
+#ifndef QT_NO_OPENGL
+# include <QOpenGLFramebufferObject>
+#endif
QT_BEGIN_NAMESPACE
class QQuickContext2DTexture;
@@ -82,7 +87,7 @@ protected:
QPainter m_painter;
};
-
+#ifndef QT_NO_OPENGL
class QQuickContext2DFBOTile : public QQuickContext2DTile
{
public:
@@ -99,7 +104,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..0f8061b5ef 100644
--- a/src/quick/items/items.pri
+++ b/src/quick/items/items.pri
@@ -41,19 +41,10 @@ HEADERS += \
$$PWD/qquickflickable_p.h \
$$PWD/qquickflickable_p_p.h \
$$PWD/qquickflickablebehavior_p.h \
- $$PWD/qquicklistview_p.h \
$$PWD/qquickrepeater_p.h \
$$PWD/qquickrepeater_p_p.h \
- $$PWD/qquickgridview_p.h \
- $$PWD/qquickpathview_p.h \
- $$PWD/qquickpathview_p_p.h \
- $$PWD/qquickpositioners_p.h \
- $$PWD/qquickpositioners_p_p.h \
$$PWD/qquickloader_p.h \
$$PWD/qquickloader_p_p.h \
- $$PWD/qquickanimatedimage_p.h \
- $$PWD/qquickanimatedimage_p_p.h \
- $$PWD/qquickflipable_p.h \
$$PWD/qquicktranslate_p.h \
$$PWD/qquickclipnode_p.h \
$$PWD/qquickview.h \
@@ -63,24 +54,16 @@ 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 \
- $$PWD/qquickitemview_p.h \
- $$PWD/qquickitemview_p_p.h \
- $$PWD/qquickitemviewtransition_p.h \
$$PWD/qquickscreen_p.h \
$$PWD/qquickwindowattached_p.h \
$$PWD/qquickwindowmodule_p.h \
- $$PWD/qquickframebufferobject.h \
- $$PWD/qquickitemgrabresult.h \
$$PWD/qquickrendercontrol.h \
$$PWD/qquickrendercontrol_p.h \
- $$PWD/qquickopenglinfo_p.h
+ $$PWD/qquickgraphicsinfo_p.h \
+ $$PWD/qquickitemgrabresult.h
SOURCES += \
$$PWD/qquickevents.cpp \
@@ -106,65 +89,145 @@ SOURCES += \
$$PWD/qquickmousearea.cpp \
$$PWD/qquickpincharea.cpp \
$$PWD/qquickflickable.cpp \
- $$PWD/qquicklistview.cpp \
$$PWD/qquickrepeater.cpp \
- $$PWD/qquickgridview.cpp \
- $$PWD/qquickpathview.cpp \
- $$PWD/qquickpositioners.cpp \
$$PWD/qquickloader.cpp \
- $$PWD/qquickanimatedimage.cpp \
- $$PWD/qquickflipable.cpp \
$$PWD/qquicktranslate.cpp \
$$PWD/qquickclipnode.cpp \
$$PWD/qquickview.cpp \
$$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 \
$$PWD/qquickmultipointtoucharea.cpp \
- $$PWD/qquickitemview.cpp \
- $$PWD/qquickitemviewtransition.cpp \
$$PWD/qquickwindowmodule.cpp \
$$PWD/qquickscreen.cpp \
$$PWD/qquickwindowattached.cpp \
- $$PWD/qquickframebufferobject.cpp \
- $$PWD/qquickitemgrabresult.cpp \
$$PWD/qquickrendercontrol.cpp \
- $$PWD/qquickopenglinfo.cpp
+ $$PWD/qquickgraphicsinfo.cpp \
+ $$PWD/qquickitemgrabresult.cpp
-SOURCES += \
- $$PWD/qquickshadereffect.cpp \
- $$PWD/qquickshadereffectmesh.cpp \
- $$PWD/qquickshadereffectnode.cpp \
- $$PWD/qquickshadereffectsource.cpp \
+qtConfig(quick-animatedimage) {
+ HEADERS += \
+ $$PWD/qquickanimatedimage_p.h \
+ $$PWD/qquickanimatedimage_p_p.h
+ SOURCES += \
+ $$PWD/qquickanimatedimage.cpp
+}
-HEADERS += \
- $$PWD/qquickshadereffect_p.h \
- $$PWD/qquickshadereffectmesh_p.h \
- $$PWD/qquickshadereffectnode_p.h \
- $$PWD/qquickshadereffectsource_p.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
+qtConfig(quick-gridview) {
+ HEADERS += \
+ $$PWD/qquickgridview_p.h
+ SOURCES += \
+ $$PWD/qquickgridview.cpp
+}
+
+qtConfig(quick-itemview) {
+ HEADERS += \
+ $$PWD/qquickitemview_p.h \
+ $$PWD/qquickitemview_p_p.h
+ SOURCES += \
+ $$PWD/qquickitemview.cpp
+}
+
+qtConfig(quick-viewtransitions) {
+ HEADERS += \
+ $$PWD/qquickitemviewtransition_p.h
+ SOURCES += \
+ $$PWD/qquickitemviewtransition.cpp
+}
+
+qtConfig(quick-listview) {
+ HEADERS += \
+ $$PWD/qquicklistview_p.h
+ SOURCES += \
+ $$PWD/qquicklistview.cpp
+}
+
+qtConfig(quick-pathview) {
+ HEADERS += \
+ $$PWD/qquickpathview_p.h \
+ $$PWD/qquickpathview_p_p.h
+ SOURCES += \
+ $$PWD/qquickpathview.cpp
+}
+
+qtConfig(quick-positioners) {
+ HEADERS += \
+ $$PWD/qquickpositioners_p.h \
+ $$PWD/qquickpositioners_p_p.h
+ SOURCES += \
+ $$PWD/qquickpositioners.cpp
+}
+
+qtConfig(quick-flipable) {
+ HEADERS += \
+ $$PWD/qquickflipable_p.h
+ SOURCES += \
+ $$PWD/qquickflipable.cpp
+}
+
+qtConfig(quick-shadereffect) {
+ HEADERS += \
+ $$PWD/qquickshadereffectsource_p.h \
+ $$PWD/qquickshadereffectmesh_p.h \
+ $$PWD/qquickshadereffect_p.h \
+ $$PWD/qquickgenericshadereffect_p.h
+ SOURCES += \
+ $$PWD/qquickshadereffectsource.cpp \
+ $$PWD/qquickshadereffectmesh.cpp \
+ $$PWD/qquickshadereffect.cpp \
+ $$PWD/qquickgenericshadereffect.cpp
+
+ qtConfig(opengl) {
+ SOURCES += \
+ $$PWD/qquickopenglshadereffect.cpp \
+ $$PWD/qquickopenglshadereffectnode.cpp
+ HEADERS += \
+ $$PWD/qquickopenglshadereffect_p.h \
+ $$PWD/qquickopenglshadereffectnode_p.h
+
+ OTHER_FILES += \
+ $$PWD/shaders/shadereffect.vert \
+ $$PWD/shaders/shadereffect.frag \
+ $$PWD/shaders/shadereffectfallback.vert \
+ $$PWD/shaders/shadereffectfallback.frag \
+ $$PWD/shaders/shadereffect_core.vert \
+ $$PWD/shaders/shadereffect_core.frag \
+ $$PWD/shaders/shadereffectfallback_core.vert \
+ $$PWD/shaders/shadereffectfallback_core.frag
+ }
+}
+
+qtConfig(quick-sprite) {
+ HEADERS += \
+ $$PWD/qquickspriteengine_p.h \
+ $$PWD/qquicksprite_p.h \
+ $$PWD/qquickspritesequence_p.h \
+ $$PWD/qquickanimatedsprite_p.h \
+ $$PWD/qquickanimatedsprite_p_p.h \
+ $$PWD/qquickspritesequence_p_p.h
+ SOURCES += \
+ $$PWD/qquickspriteengine.cpp \
+ $$PWD/qquicksprite.cpp \
+ $$PWD/qquickspritesequence.cpp \
+ $$PWD/qquickanimatedsprite.cpp
+}
+
+# Items that depend on OpenGL Renderer
+qtConfig(opengl(es1|es2)?) {
+ SOURCES += \
+ $$PWD/qquickopenglinfo.cpp \
+ $$PWD/qquickframebufferobject.cpp
+
+ HEADERS += \
+ $$PWD/qquickopenglinfo_p.h \
+ $$PWD/qquickframebufferobject.h
+}
RESOURCES += \
$$PWD/items.qrc
-include(context2d/context2d.pri)
+qtConfig(quick-canvas): \
+ include(context2d/context2d.pri)
diff --git a/src/quick/items/items.qrc b/src/quick/items/items.qrc
index 99f9b5224f..6aaf757c29 100644
--- a/src/quick/items/items.qrc
+++ b/src/quick/items/items.qrc
@@ -1,7 +1,5 @@
<RCC>
<qresource prefix="/qt-project.org/items">
- <file>shaders/sprite.frag</file>
- <file>shaders/sprite.vert</file>
<file>shaders/shadereffect.vert</file>
<file>shaders/shadereffect.frag</file>
<file>shaders/shadereffectfallback.frag</file>
@@ -10,7 +8,5 @@
<file>shaders/shadereffect_core.vert</file>
<file>shaders/shadereffectfallback_core.frag</file>
<file>shaders/shadereffectfallback_core.vert</file>
- <file>shaders/sprite_core.frag</file>
- <file>shaders/sprite_core.vert</file>
</qresource>
</RCC>
diff --git a/src/quick/items/qquickanchors.cpp b/src/quick/items/qquickanchors.cpp
index 4cbd41106e..b6978e534e 100644
--- a/src/quick/items/qquickanchors.cpp
+++ b/src/quick/items/qquickanchors.cpp
@@ -285,26 +285,26 @@ void QQuickAnchorsPrivate::clearItem(QQuickItem *item)
}
}
-int QQuickAnchorsPrivate::calculateDependency(QQuickItem *controlItem)
+QQuickGeometryChange QQuickAnchorsPrivate::calculateDependency(QQuickItem *controlItem)
{
- QQuickItemPrivate::GeometryChangeTypes dependency = QQuickItemPrivate::NoChange;
+ QQuickGeometryChange dependency;
if (!controlItem || inDestructor)
return dependency;
if (fill == controlItem) {
if (controlItem == readParentItem(item))
- dependency |= QQuickItemPrivate::SizeChange;
+ dependency.setSizeChange(true);
else //sibling
- dependency |= QQuickItemPrivate::GeometryChange;
+ dependency.setAllChanged(true);
return dependency; //exit early
}
if (centerIn == controlItem) {
if (controlItem == readParentItem(item))
- dependency |= QQuickItemPrivate::SizeChange;
+ dependency.setSizeChange(true);
else //sibling
- dependency |= QQuickItemPrivate::GeometryChange;
+ dependency.setAllChanged(true);
return dependency; //exit early
}
@@ -312,9 +312,9 @@ int QQuickAnchorsPrivate::calculateDependency(QQuickItem *controlItem)
(usedAnchors & QQuickAnchors::RightAnchor && rightAnchorItem == controlItem) ||
(usedAnchors & QQuickAnchors::HCenterAnchor && hCenterAnchorItem == controlItem)) {
if (controlItem == readParentItem(item))
- dependency |= QQuickItemPrivate::WidthChange;
+ dependency.setWidthChange(true);
else //sibling
- dependency |= QFlags<QQuickItemPrivate::GeometryChangeType>(QQuickItemPrivate::XChange | QQuickItemPrivate::WidthChange);
+ dependency.setHorizontalChange(true);
}
if ((usedAnchors & QQuickAnchors::TopAnchor && topAnchorItem == controlItem) ||
@@ -322,9 +322,9 @@ int QQuickAnchorsPrivate::calculateDependency(QQuickItem *controlItem)
(usedAnchors & QQuickAnchors::VCenterAnchor && vCenterAnchorItem == controlItem) ||
(usedAnchors & QQuickAnchors::BaselineAnchor && baselineAnchorItem == controlItem)) {
if (controlItem == readParentItem(item))
- dependency |= QQuickItemPrivate::HeightChange;
+ dependency.setHeightChange(true);
else //sibling
- dependency |= QFlags<QQuickItemPrivate::GeometryChangeType>(QQuickItemPrivate::YChange | QQuickItemPrivate::HeightChange);
+ dependency.setVerticalChange(true);
}
return dependency;
@@ -336,7 +336,7 @@ void QQuickAnchorsPrivate::addDepend(QQuickItem *item)
return;
QQuickItemPrivate *p = QQuickItemPrivate::get(item);
- p->updateOrAddGeometryChangeListener(this, QFlags<QQuickItemPrivate::GeometryChangeType>(calculateDependency(item)));
+ p->updateOrAddGeometryChangeListener(this, calculateDependency(item));
}
void QQuickAnchorsPrivate::remDepend(QQuickItem *item)
@@ -345,7 +345,7 @@ void QQuickAnchorsPrivate::remDepend(QQuickItem *item)
return;
QQuickItemPrivate *p = QQuickItemPrivate::get(item);
- p->updateOrRemoveGeometryChangeListener(this, QFlags<QQuickItemPrivate::GeometryChangeType>(calculateDependency(item)));
+ p->updateOrRemoveGeometryChangeListener(this, calculateDependency(item));
}
bool QQuickAnchors::mirrored()
@@ -492,7 +492,7 @@ void QQuickAnchorsPrivate::update()
}
}
-void QQuickAnchorsPrivate::itemGeometryChanged(QQuickItem *, const QRectF &newG, const QRectF &oldG)
+void QQuickAnchorsPrivate::itemGeometryChanged(QQuickItem *, QQuickGeometryChange change, const QRectF &)
{
if (!isItemComplete())
return;
@@ -502,11 +502,9 @@ void QQuickAnchorsPrivate::itemGeometryChanged(QQuickItem *, const QRectF &newG,
} else if (centerIn) {
centerInChanged();
} else {
- if ((usedAnchors & QQuickAnchors::Horizontal_Mask)
- && (newG.x() != oldG.x() || newG.width() != oldG.width()))
+ if ((usedAnchors & QQuickAnchors::Horizontal_Mask) && change.horizontalChange())
updateHorizontalAnchors();
- if ((usedAnchors & QQuickAnchors::Vertical_Mask)
- && (newG.y() != oldG.y() || newG.height() != oldG.height()))
+ if ((usedAnchors & QQuickAnchors::Vertical_Mask) && change.verticalChange())
updateVerticalAnchors();
}
}
diff --git a/src/quick/items/qquickanchors_p_p.h b/src/quick/items/qquickanchors_p_p.h
index da659946c0..3357e134bf 100644
--- a/src/quick/items/qquickanchors_p_p.h
+++ b/src/quick/items/qquickanchors_p_p.h
@@ -124,7 +124,7 @@ public:
void clearItem(QQuickItem *);
- int calculateDependency(QQuickItem *);
+ QQuickGeometryChange calculateDependency(QQuickItem *);
void addDepend(QQuickItem *);
void remDepend(QQuickItem *);
bool isItemComplete() const;
@@ -141,7 +141,7 @@ public:
void updateMe();
// QQuickItemGeometryListener interface
- void itemGeometryChanged(QQuickItem *, const QRectF &, const QRectF &) Q_DECL_OVERRIDE;
+ void itemGeometryChanged(QQuickItem *, QQuickGeometryChange, const QRectF &) Q_DECL_OVERRIDE;
QQuickAnchorsPrivate *anchorPrivate() Q_DECL_OVERRIDE { return this; }
bool checkHValid() const;
diff --git a/src/quick/items/qquickanimatedimage.cpp b/src/quick/items/qquickanimatedimage.cpp
index ab5170cd65..81c649dbd5 100644
--- a/src/quick/items/qquickanimatedimage.cpp
+++ b/src/quick/items/qquickanimatedimage.cpp
@@ -40,15 +40,15 @@
#include "qquickanimatedimage_p.h"
#include "qquickanimatedimage_p_p.h"
-#ifndef QT_NO_MOVIE
-
#include <QtGui/qguiapplication.h>
#include <QtQml/qqmlinfo.h>
#include <QtQml/qqmlfile.h>
#include <QtQml/qqmlengine.h>
#include <QtGui/qmovie.h>
+#if QT_CONFIG(qml_network)
#include <QtNetwork/qnetworkrequest.h>
#include <QtNetwork/qnetworkreply.h>
+#endif
QT_BEGIN_NAMESPACE
@@ -144,8 +144,10 @@ QQuickAnimatedImage::QQuickAnimatedImage(QQuickItem *parent)
QQuickAnimatedImage::~QQuickAnimatedImage()
{
Q_D(QQuickAnimatedImage);
+#if QT_CONFIG(qml_network)
if (d->reply)
d->reply->deleteLater();
+#endif
delete d->_movie;
qDeleteAll(d->frameMap);
d->frameMap.clear();
@@ -264,10 +266,12 @@ void QQuickAnimatedImage::setSource(const QUrl &url)
if (url == d->url)
return;
+#if QT_CONFIG(qml_network)
if (d->reply) {
d->reply->deleteLater();
d->reply = 0;
}
+#endif
d->setImage(QImage());
qDeleteAll(d->frameMap);
@@ -319,6 +323,7 @@ void QQuickAnimatedImage::load()
d->_movie = new QMovie(lf);
movieRequestFinished();
} else {
+#if QT_CONFIG(qml_network)
if (d->status != Loading) {
d->status = Loading;
emit statusChanged(d->status);
@@ -335,6 +340,7 @@ void QQuickAnimatedImage::load()
this, SLOT(movieRequestFinished()));
QObject::connect(d->reply, SIGNAL(downloadProgress(qint64,qint64)),
this, SLOT(requestProgress(qint64,qint64)));
+#endif
}
}
}
@@ -343,8 +349,10 @@ void QQuickAnimatedImage::load()
void QQuickAnimatedImage::movieRequestFinished()
{
+
Q_D(QQuickAnimatedImage);
+#if QT_CONFIG(qml_network)
if (d->reply) {
d->redirectCount++;
if (d->redirectCount < ANIMATEDIMAGE_MAXIMUM_REDIRECT_RECURSION) {
@@ -360,6 +368,7 @@ void QQuickAnimatedImage::movieRequestFinished()
d->redirectCount=0;
d->_movie = new QMovie(d->reply);
}
+#endif
if (!d->_movie->isValid()) {
qmlInfo(this) << "Error Reading Animated Image File " << d->url.toString();
@@ -482,5 +491,3 @@ void QQuickAnimatedImage::componentComplete()
}
QT_END_NAMESPACE
-
-#endif // QT_NO_MOVIE
diff --git a/src/quick/items/qquickanimatedimage_p.h b/src/quick/items/qquickanimatedimage_p.h
index 288a8379da..143fe8904d 100644
--- a/src/quick/items/qquickanimatedimage_p.h
+++ b/src/quick/items/qquickanimatedimage_p.h
@@ -51,9 +51,11 @@
// We mean it.
//
-#include "qquickimage_p.h"
+#include <private/qtquickglobal_p.h>
+
+QT_REQUIRE_CONFIG(quick_animatedimage);
-#ifndef QT_NO_MOVIE
+#include "qquickimage_p.h"
QT_BEGIN_NAMESPACE
@@ -116,6 +118,4 @@ QT_END_NAMESPACE
QML_DECLARE_TYPE(QQuickAnimatedImage)
-#endif // QT_NO_MOVIE
-
#endif // QQUICKANIMATEDIMAGE_P_H
diff --git a/src/quick/items/qquickanimatedimage_p_p.h b/src/quick/items/qquickanimatedimage_p_p.h
index 6d32f1071f..9eff6a44e3 100644
--- a/src/quick/items/qquickanimatedimage_p_p.h
+++ b/src/quick/items/qquickanimatedimage_p_p.h
@@ -51,14 +51,18 @@
// We mean it.
//
-#include "qquickimage_p_p.h"
+#include <QtQuick/qtquickglobal.h>
+
+QT_REQUIRE_CONFIG(quick_animatedimage);
-#ifndef QT_NO_MOVIE
+#include "qquickimage_p_p.h"
QT_BEGIN_NAMESPACE
class QMovie;
+#if QT_CONFIG(qml_network)
class QNetworkReply;
+#endif
class QQuickAnimatedImagePrivate : public QQuickImagePrivate
{
@@ -66,7 +70,11 @@ class QQuickAnimatedImagePrivate : public QQuickImagePrivate
public:
QQuickAnimatedImagePrivate()
- : playing(true), paused(false), preset_currentframe(0), _movie(0), reply(0), redirectCount(0), oldPlaying(false), currentSourceSize(0, 0)
+ : playing(true), paused(false), preset_currentframe(0), _movie(0), oldPlaying(false)
+#if QT_CONFIG(qml_network)
+ , reply(0), redirectCount(0)
+#endif
+ , currentSourceSize(0, 0)
{
}
@@ -76,15 +84,15 @@ public:
bool paused;
int preset_currentframe;
QMovie *_movie;
+ bool oldPlaying;
+#if QT_CONFIG(qml_network)
QNetworkReply *reply;
int redirectCount;
- bool oldPlaying;
+#endif
QMap<int, QQuickPixmap *> frameMap;
QSize currentSourceSize;
};
QT_END_NAMESPACE
-#endif // QT_NO_MOVIE
-
#endif // QQUICKANIMATEDIMAGE_P_P_H
diff --git a/src/quick/items/qquickanimatedsprite.cpp b/src/quick/items/qquickanimatedsprite.cpp
index 6207a86877..8aeef4ef4a 100644
--- a/src/quick/items/qquickanimatedsprite.cpp
+++ b/src/quick/items/qquickanimatedsprite.cpp
@@ -38,6 +38,7 @@
****************************************************************************/
#include "qquickanimatedsprite_p.h"
+#include "qquickanimatedsprite_p_p.h"
#include "qquicksprite_p.h"
#include "qquickspriteengine_p.h"
#include <QtQuick/private/qsgcontext_p.h>
@@ -56,111 +57,6 @@
QT_BEGIN_NAMESPACE
-class QQuickAnimatedSpriteMaterial : public QSGMaterial
-{
-public:
- QQuickAnimatedSpriteMaterial();
- ~QQuickAnimatedSpriteMaterial();
- QSGMaterialType *type() const Q_DECL_OVERRIDE { static QSGMaterialType type; return &type; }
- QSGMaterialShader *createShader() const Q_DECL_OVERRIDE;
- int compare(const QSGMaterial *other) const Q_DECL_OVERRIDE
- {
- return this - static_cast<const QQuickAnimatedSpriteMaterial *>(other);
- }
-
- QSGTexture *texture;
-
- float animT;
- float animX1;
- float animY1;
- float animX2;
- float animY2;
- float animW;
- float animH;
-};
-
-QQuickAnimatedSpriteMaterial::QQuickAnimatedSpriteMaterial()
- : texture(0)
- , animT(0.0f)
- , animX1(0.0f)
- , animY1(0.0f)
- , animX2(0.0f)
- , animY2(0.0f)
- , animW(1.0f)
- , animH(1.0f)
-{
- setFlag(Blending, true);
-}
-
-QQuickAnimatedSpriteMaterial::~QQuickAnimatedSpriteMaterial()
-{
- delete texture;
-}
-
-class AnimatedSpriteMaterialData : public QSGMaterialShader
-{
-public:
- AnimatedSpriteMaterialData()
- : QSGMaterialShader()
- {
- setShaderSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/qt-project.org/items/shaders/sprite.vert"));
- setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/items/shaders/sprite.frag"));
- }
-
- void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *) Q_DECL_OVERRIDE
- {
- QQuickAnimatedSpriteMaterial *m = static_cast<QQuickAnimatedSpriteMaterial *>(newEffect);
- m->texture->bind();
-
- program()->setUniformValue(m_opacity_id, state.opacity());
- program()->setUniformValue(m_animData_id, m->animW, m->animH, m->animT);
- program()->setUniformValue(m_animPos_id, m->animX1, m->animY1, m->animX2, m->animY2);
-
- if (state.isMatrixDirty())
- program()->setUniformValue(m_matrix_id, state.combinedMatrix());
- }
-
- void initialize() Q_DECL_OVERRIDE {
- m_matrix_id = program()->uniformLocation("qt_Matrix");
- m_opacity_id = program()->uniformLocation("qt_Opacity");
- m_animData_id = program()->uniformLocation("animData");
- m_animPos_id = program()->uniformLocation("animPos");
- }
-
- char const *const *attributeNames() const Q_DECL_OVERRIDE {
- static const char *attr[] = {
- "vPos",
- "vTex",
- 0
- };
- return attr;
- }
-
- int m_matrix_id;
- int m_opacity_id;
- int m_animData_id;
- int m_animPos_id;
-};
-
-QSGMaterialShader *QQuickAnimatedSpriteMaterial::createShader() const
-{
- return new AnimatedSpriteMaterialData;
-}
-
-struct AnimatedSpriteVertex {
- float x;
- float y;
- float tx;
- float ty;
-};
-
-struct AnimatedSpriteVertices {
- AnimatedSpriteVertex v1;
- AnimatedSpriteVertex v2;
- AnimatedSpriteVertex v3;
- AnimatedSpriteVertex v4;
-};
-
/*!
\qmltype AnimatedSprite
\instantiates QQuickAnimatedSprite
@@ -316,18 +212,11 @@ struct AnimatedSpriteVertices {
//TODO: Implicitly size element to size of sprite
QQuickAnimatedSprite::QQuickAnimatedSprite(QQuickItem *parent) :
- QQuickItem(parent)
- , m_sprite(new QQuickSprite(this))
- , m_spriteEngine(0)
- , m_curFrame(0)
- , m_pleaseReset(false)
- , m_running(true)
- , m_paused(false)
- , m_interpolate(true)
- , m_loops(-1)
- , m_curLoop(0)
- , m_pauseOffset(0)
+ QQuickItem(*(new QQuickAnimatedSpritePrivate), parent)
{
+ Q_D(QQuickAnimatedSprite);
+ d->m_sprite = new QQuickSprite(this);
+
setFlag(ItemHasContents);
connect(this, SIGNAL(widthChanged()),
this, SLOT(reset()));
@@ -335,6 +224,96 @@ QQuickAnimatedSprite::QQuickAnimatedSprite(QQuickItem *parent) :
this, SLOT(reset()));
}
+bool QQuickAnimatedSprite::running() const
+{
+ Q_D(const QQuickAnimatedSprite);
+ return d->m_running;
+}
+
+bool QQuickAnimatedSprite::interpolate() const
+{
+ Q_D(const QQuickAnimatedSprite);
+ return d->m_interpolate;
+}
+
+QUrl QQuickAnimatedSprite::source() const
+{
+ Q_D(const QQuickAnimatedSprite);
+ return d->m_sprite->source();
+}
+
+bool QQuickAnimatedSprite::reverse() const
+{
+ Q_D(const QQuickAnimatedSprite);
+ return d->m_sprite->reverse();
+}
+
+bool QQuickAnimatedSprite::frameSync() const
+{
+ Q_D(const QQuickAnimatedSprite);
+ return d->m_sprite->frameSync();
+}
+
+int QQuickAnimatedSprite::frameCount() const
+{
+ Q_D(const QQuickAnimatedSprite);
+ return d->m_sprite->frames();
+}
+
+int QQuickAnimatedSprite::frameHeight() const
+{
+ Q_D(const QQuickAnimatedSprite);
+ return d->m_sprite->frameHeight();
+}
+
+int QQuickAnimatedSprite::frameWidth() const
+{
+ Q_D(const QQuickAnimatedSprite);
+ return d->m_sprite->frameWidth();
+}
+
+int QQuickAnimatedSprite::frameX() const
+{
+ Q_D(const QQuickAnimatedSprite);
+ return d->m_sprite->frameX();
+}
+
+int QQuickAnimatedSprite::frameY() const
+{
+ Q_D(const QQuickAnimatedSprite);
+ return d->m_sprite->frameY();
+}
+
+qreal QQuickAnimatedSprite::frameRate() const
+{
+ Q_D(const QQuickAnimatedSprite);
+ return d->m_sprite->frameRate();
+}
+
+int QQuickAnimatedSprite::frameDuration() const
+{
+ Q_D(const QQuickAnimatedSprite);
+ return d->m_sprite->frameDuration();
+}
+
+int QQuickAnimatedSprite::loops() const
+{
+ Q_D(const QQuickAnimatedSprite);
+ return d->m_loops;
+}
+
+bool QQuickAnimatedSprite::paused() const
+{
+ Q_D(const QQuickAnimatedSprite);
+ return d->m_paused;
+}
+
+int QQuickAnimatedSprite::currentFrame() const
+{
+ Q_D(const QQuickAnimatedSprite);
+ return d->m_curFrame;
+}
+
bool QQuickAnimatedSprite::isCurrentFrameChangedConnected()
{
IS_SIGNAL_CONNECTED(this, QQuickAnimatedSprite, currentFrameChanged, (int));
@@ -349,23 +328,25 @@ void QQuickAnimatedSprite::reloadImage()
void QQuickAnimatedSprite::componentComplete()
{
+ Q_D(const QQuickAnimatedSprite);
createEngine();
QQuickItem::componentComplete();
- if (m_running)
+ if (d->m_running)
start();
}
void QQuickAnimatedSprite::start()
{
- m_running = true;
+ Q_D(QQuickAnimatedSprite);
+ d->m_running = true;
if (!isComponentComplete())
return;
- m_curLoop = 0;
- m_timestamp.start();
- if (m_spriteEngine) {
- m_spriteEngine->stop(0);
- m_spriteEngine->updateSprites(0);
- m_spriteEngine->start(0);
+ d->m_curLoop = 0;
+ d->m_timestamp.start();
+ if (d->m_spriteEngine) {
+ d->m_spriteEngine->stop(0);
+ d->m_spriteEngine->updateSprites(0);
+ d->m_spriteEngine->start(0);
}
emit currentFrameChanged(0);
emit runningChanged(true);
@@ -374,10 +355,11 @@ void QQuickAnimatedSprite::start()
void QQuickAnimatedSprite::stop()
{
- m_running = false;
+ Q_D(QQuickAnimatedSprite);
+ d->m_running = false;
if (!isComponentComplete())
return;
- m_pauseOffset = 0;
+ d->m_pauseOffset = 0;
emit runningChanged(false);
maybeUpdate();
}
@@ -389,14 +371,15 @@ void QQuickAnimatedSprite::stop()
*/
void QQuickAnimatedSprite::advance(int frames)
{
+ Q_D(QQuickAnimatedSprite);
if (!frames)
return;
//TODO-C: May not work when running - only when paused
- m_curFrame += frames;
- while (m_curFrame < 0)
- m_curFrame += m_spriteEngine->maxFrames();
- m_curFrame = m_curFrame % m_spriteEngine->maxFrames();
- emit currentFrameChanged(m_curFrame);
+ d->m_curFrame += frames;
+ while (d->m_curFrame < 0)
+ d->m_curFrame += d->m_spriteEngine->maxFrames();
+ d->m_curFrame = d->m_curFrame % d->m_spriteEngine->maxFrames();
+ emit currentFrameChanged(d->m_curFrame);
maybeUpdate();
}
@@ -418,10 +401,12 @@ void QQuickAnimatedSprite::maybeUpdate()
*/
void QQuickAnimatedSprite::pause()
{
- if (m_paused)
+ Q_D(QQuickAnimatedSprite);
+
+ if (d->m_paused)
return;
- m_pauseOffset = m_timestamp.elapsed();
- m_paused = true;
+ d->m_pauseOffset = d->m_timestamp.elapsed();
+ d->m_paused = true;
emit pausedChanged(true);
maybeUpdate();
}
@@ -436,245 +421,362 @@ void QQuickAnimatedSprite::pause()
*/
void QQuickAnimatedSprite::resume()
{
- if (!m_paused)
+ Q_D(QQuickAnimatedSprite);
+
+ if (!d->m_paused)
return;
- m_pauseOffset = m_pauseOffset - m_timestamp.elapsed();
- m_paused = false;
+ d->m_pauseOffset = d->m_pauseOffset - d->m_timestamp.elapsed();
+ d->m_paused = false;
emit pausedChanged(false);
maybeUpdate();
}
-void QQuickAnimatedSprite::createEngine()
+void QQuickAnimatedSprite::setRunning(bool arg)
{
- if (m_spriteEngine)
- delete m_spriteEngine;
- QList<QQuickSprite*> spriteList;
- spriteList << m_sprite;
- m_spriteEngine = new QQuickSpriteEngine(QList<QQuickSprite*>(spriteList), this);
- m_spriteEngine->startAssemblingImage();
- reset();
+ Q_D(QQuickAnimatedSprite);
+
+ if (d->m_running != arg) {
+ if (d->m_running)
+ stop();
+ else
+ start();
+ }
}
-static QSGGeometry::Attribute AnimatedSprite_Attributes[] = {
- QSGGeometry::Attribute::create(0, 2, GL_FLOAT, true), // pos
- QSGGeometry::Attribute::create(1, 2, GL_FLOAT), // tex
-};
+void QQuickAnimatedSprite::setPaused(bool arg)
+{
+ Q_D(const QQuickAnimatedSprite);
-static QSGGeometry::AttributeSet AnimatedSprite_AttributeSet =
+ if (d->m_paused != arg) {
+ if (d->m_paused)
+ resume();
+ else
+ pause();
+ }
+}
+
+void QQuickAnimatedSprite::setInterpolate(bool arg)
+{
+ Q_D(QQuickAnimatedSprite);
+
+ if (d->m_interpolate != arg) {
+ d->m_interpolate = arg;
+ Q_EMIT interpolateChanged(arg);
+ }
+}
+
+void QQuickAnimatedSprite::setSource(QUrl arg)
+{
+ Q_D(QQuickAnimatedSprite);
+
+ if (d->m_sprite->m_source != arg) {
+ d->m_sprite->setSource(arg);
+ Q_EMIT sourceChanged(arg);
+ reloadImage();
+ }
+}
+
+void QQuickAnimatedSprite::setReverse(bool arg)
+{
+ Q_D(QQuickAnimatedSprite);
+
+ if (d->m_sprite->m_reverse != arg) {
+ d->m_sprite->setReverse(arg);
+ Q_EMIT reverseChanged(arg);
+ }
+}
+
+void QQuickAnimatedSprite::setFrameSync(bool arg)
+{
+ Q_D(QQuickAnimatedSprite);
+
+ if (d->m_sprite->m_frameSync != arg) {
+ d->m_sprite->setFrameSync(arg);
+ Q_EMIT frameSyncChanged(arg);
+ if (d->m_running)
+ restart();
+ }
+}
+
+void QQuickAnimatedSprite::setFrameCount(int arg)
+{
+ Q_D(QQuickAnimatedSprite);
+
+ if (d->m_sprite->m_frames != arg) {
+ d->m_sprite->setFrameCount(arg);
+ Q_EMIT frameCountChanged(arg);
+ reloadImage();
+ }
+}
+
+void QQuickAnimatedSprite::setFrameHeight(int arg)
+{
+ Q_D(QQuickAnimatedSprite);
+
+ if (d->m_sprite->m_frameHeight != arg) {
+ d->m_sprite->setFrameHeight(arg);
+ Q_EMIT frameHeightChanged(arg);
+ reloadImage();
+ }
+}
+
+void QQuickAnimatedSprite::setFrameWidth(int arg)
+{
+ Q_D(QQuickAnimatedSprite);
+
+ if (d->m_sprite->m_frameWidth != arg) {
+ d->m_sprite->setFrameWidth(arg);
+ Q_EMIT frameWidthChanged(arg);
+ reloadImage();
+ }
+}
+
+void QQuickAnimatedSprite::setFrameX(int arg)
+{
+ Q_D(QQuickAnimatedSprite);
+
+ if (d->m_sprite->m_frameX != arg) {
+ d->m_sprite->setFrameX(arg);
+ Q_EMIT frameXChanged(arg);
+ reloadImage();
+ }
+}
+
+void QQuickAnimatedSprite::setFrameY(int arg)
{
- 2, // Attribute Count
- (2+2) * sizeof(float),
- AnimatedSprite_Attributes
-};
+ Q_D(QQuickAnimatedSprite);
+
+ if (d->m_sprite->m_frameY != arg) {
+ d->m_sprite->setFrameY(arg);
+ Q_EMIT frameYChanged(arg);
+ reloadImage();
+ }
+}
-void QQuickAnimatedSprite::sizeVertices(QSGGeometryNode *node)
+void QQuickAnimatedSprite::setFrameRate(qreal arg)
{
- AnimatedSpriteVertices *p = (AnimatedSpriteVertices *) node->geometry()->vertexData();
- p->v1.x = 0;
- p->v1.y = 0;
+ Q_D(QQuickAnimatedSprite);
- p->v2.x = width();
- p->v2.y = 0;
+ if (d->m_sprite->m_frameRate != arg) {
+ d->m_sprite->setFrameRate(arg);
+ Q_EMIT frameRateChanged(arg);
+ if (d->m_running)
+ restart();
+ }
+}
- p->v3.x = 0;
- p->v3.y = height();
+void QQuickAnimatedSprite::setFrameDuration(int arg)
+{
+ Q_D(QQuickAnimatedSprite);
- p->v4.x = width();
- p->v4.y = height();
+ if (d->m_sprite->m_frameDuration != arg) {
+ d->m_sprite->setFrameDuration(arg);
+ Q_EMIT frameDurationChanged(arg);
+ if (d->m_running)
+ restart();
+ }
+}
+
+void QQuickAnimatedSprite::resetFrameRate()
+{
+ setFrameRate(-1.0);
}
-QSGGeometryNode* QQuickAnimatedSprite::buildNode()
+void QQuickAnimatedSprite::resetFrameDuration()
{
- if (!m_spriteEngine) {
+ setFrameDuration(-1);
+}
+
+void QQuickAnimatedSprite::setLoops(int arg)
+{
+ Q_D(QQuickAnimatedSprite);
+
+ if (d->m_loops != arg) {
+ d->m_loops = arg;
+ Q_EMIT loopsChanged(arg);
+ }
+}
+
+void QQuickAnimatedSprite::setCurrentFrame(int arg) //TODO-C: Probably only works when paused
+{
+ Q_D(QQuickAnimatedSprite);
+
+ if (d->m_curFrame != arg) {
+ d->m_curFrame = arg;
+ Q_EMIT currentFrameChanged(arg); //TODO-C Only emitted on manual advance!
+ update();
+ }
+}
+
+void QQuickAnimatedSprite::createEngine()
+{
+ Q_D(QQuickAnimatedSprite);
+
+ if (d->m_spriteEngine)
+ delete d->m_spriteEngine;
+ QList<QQuickSprite*> spriteList;
+ spriteList << d->m_sprite;
+ d->m_spriteEngine = new QQuickSpriteEngine(QList<QQuickSprite*>(spriteList), this);
+ d->m_spriteEngine->startAssemblingImage();
+ reset();
+}
+
+QSGSpriteNode* QQuickAnimatedSprite::initNode()
+{
+ Q_D(QQuickAnimatedSprite);
+
+ if (!d->m_spriteEngine) {
qmlInfo(this) << "No sprite engine...";
- return 0;
- } else if (m_spriteEngine->status() == QQuickPixmap::Null) {
- m_spriteEngine->startAssemblingImage();
+ return nullptr;
+ } else if (d->m_spriteEngine->status() == QQuickPixmap::Null) {
+ d->m_spriteEngine->startAssemblingImage();
maybeUpdate();//Schedule another update, where we will check again
- return 0;
- } else if (m_spriteEngine->status() == QQuickPixmap::Loading) {
+ return nullptr;
+ } else if (d->m_spriteEngine->status() == QQuickPixmap::Loading) {
maybeUpdate();//Schedule another update, where we will check again
- return 0;
+ return nullptr;
}
- QQuickAnimatedSpriteMaterial *material = new QQuickAnimatedSpriteMaterial();
-
- QImage image = m_spriteEngine->assembledImage(); //Engine prints errors if there are any
+ QImage image = d->m_spriteEngine->assembledImage(d->sceneGraphRenderContext()->maxTextureSize()); //Engine prints errors if there are any
if (image.isNull())
- return 0;
- m_sheetSize = QSizeF(image.size());
- material->texture = window()->createTextureFromImage(image);
- m_spriteEngine->start(0);
- material->animT = 0;
- material->animX1 = m_spriteEngine->spriteX() / m_sheetSize.width();
- material->animY1 = m_spriteEngine->spriteY() / m_sheetSize.height();
- material->animX2 = material->animX1;
- material->animY2 = material->animY1;
- material->animW = m_spriteEngine->spriteWidth() / m_sheetSize.width();
- material->animH = m_spriteEngine->spriteHeight() / m_sheetSize.height();
-
- int vCount = 4;
- int iCount = 6;
- QSGGeometry *g = new QSGGeometry(AnimatedSprite_AttributeSet, vCount, iCount);
- g->setDrawingMode(GL_TRIANGLES);
-
- AnimatedSpriteVertices *p = (AnimatedSpriteVertices *) g->vertexData();
-
- QRectF texRect = material->texture->normalizedTextureSubRect();
-
- p->v1.tx = texRect.topLeft().x();
- p->v1.ty = texRect.topLeft().y();
-
- p->v2.tx = texRect.topRight().x();
- p->v2.ty = texRect.topRight().y();
-
- p->v3.tx = texRect.bottomLeft().x();
- p->v3.ty = texRect.bottomLeft().y();
-
- p->v4.tx = texRect.bottomRight().x();
- p->v4.ty = texRect.bottomRight().y();
-
- quint16 *indices = g->indexDataAsUShort();
- indices[0] = 0;
- indices[1] = 1;
- indices[2] = 2;
- indices[3] = 1;
- indices[4] = 3;
- indices[5] = 2;
-
-
- QSGGeometryNode *node = new QSGGeometryNode();
- node->setGeometry(g);
- node->setMaterial(material);
- node->setFlag(QSGGeometryNode::OwnsMaterial);
- node->setFlag(QSGGeometryNode::OwnsGeometry);
- sizeVertices(node);
+ return nullptr;
+
+ QSGSpriteNode *node = d->sceneGraphContext()->createSpriteNode();
+
+ d->m_sheetSize = QSize(image.size());
+ node->setTexture(window()->createTextureFromImage(image));
+ d->m_spriteEngine->start(0);
+ node->setTime(0.0f);
+ node->setSourceA(QPoint(d->m_spriteEngine->spriteX(), d->m_spriteEngine->spriteY()));
+ node->setSourceB(QPoint(d->m_spriteEngine->spriteX(), d->m_spriteEngine->spriteY()));
+ node->setSpriteSize(QSize(d->m_spriteEngine->spriteWidth(), d->m_spriteEngine->spriteHeight()));
+ node->setSheetSize(d->m_sheetSize);
+ node->setSize(QSizeF(width(), height()));
return node;
}
void QQuickAnimatedSprite::reset()
{
- m_pleaseReset = true;
+ Q_D(QQuickAnimatedSprite);
+ d->m_pleaseReset = true;
maybeUpdate();
}
QSGNode *QQuickAnimatedSprite::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
{
- if (m_pleaseReset) {
+ Q_D(QQuickAnimatedSprite);
+
+ if (d->m_pleaseReset) {
delete oldNode;
- oldNode = 0;
- m_pleaseReset = false;
+ oldNode = nullptr;
+ d->m_pleaseReset = false;
}
- QSGGeometryNode *node = static_cast<QSGGeometryNode *>(oldNode);
+ QSGSpriteNode *node = static_cast<QSGSpriteNode *>(oldNode);
if (!node)
- node = buildNode();
+ node = initNode();
if (node)
prepareNextFrame(node);
- if (m_running) {
- if (!m_paused)
- maybeUpdate();
-
- if (node) {
- node->markDirty(QSGNode::DirtyMaterial);
- }
- }
+ if (d->m_running && !d->m_paused)
+ maybeUpdate();
return node;
}
-void QQuickAnimatedSprite::prepareNextFrame(QSGGeometryNode *node)
+void QQuickAnimatedSprite::prepareNextFrame(QSGSpriteNode *node)
{
- int timeInt = m_timestamp.elapsed() + m_pauseOffset;
+ Q_D(QQuickAnimatedSprite);
+
+ int timeInt = d->m_timestamp.elapsed() + d->m_pauseOffset;
qreal time = timeInt / 1000.;
int frameAt;
qreal progress = 0.0;
- int lastFrame = m_curFrame;
- if (m_running && !m_paused) {
- const int nColumns = int(m_sheetSize.width()) / m_spriteEngine->spriteWidth();
+ int lastFrame = d->m_curFrame;
+ if (d->m_running && !d->m_paused) {
+ const int nColumns = d->m_sheetSize.width() / d->m_spriteEngine->spriteWidth();
//Advance State (keeps time for psuedostates)
- m_spriteEngine->updateSprites(timeInt);
+ d->m_spriteEngine->updateSprites(timeInt);
//Advance AnimatedSprite
- qreal animT = m_spriteEngine->spriteStart()/1000.0;
- const int frameCountInRow = m_spriteEngine->spriteFrames();
- const qreal frameDuration = m_spriteEngine->spriteDuration()/frameCountInRow;
+ qreal animT = d->m_spriteEngine->spriteStart()/1000.0;
+ const int frameCountInRow = d->m_spriteEngine->spriteFrames();
+ const qreal frameDuration = d->m_spriteEngine->spriteDuration() / frameCountInRow;
if (frameDuration > 0) {
qreal frame = (time - animT)/(frameDuration / 1000.0);
- bool lastLoop = m_loops > 0 && m_curLoop == m_loops-1;
+ bool lastLoop = d->m_loops > 0 && d->m_curLoop == d->m_loops-1;
//don't visually interpolate for the last frame of the last loop
const int max = lastLoop ? frameCountInRow - 1 : frameCountInRow;
frame = qBound(qreal(0.0), frame, qreal(max));
double intpart;
progress = std::modf(frame,&intpart);
frameAt = (int)intpart;
- const int rowIndex = m_spriteEngine->spriteY()/frameHeight();
+ const int rowIndex = d->m_spriteEngine->spriteY()/frameHeight();
const int newFrame = rowIndex * nColumns + frameAt;
- if (m_curFrame > newFrame) //went around
- m_curLoop++;
- m_curFrame = newFrame;
+ if (d->m_curFrame > newFrame) //went around
+ d->m_curLoop++;
+ d->m_curFrame = newFrame;
} else {
- m_curFrame++;
- if (m_curFrame >= m_spriteEngine->maxFrames()) { // maxFrames: total number of frames including all rows
- m_curFrame = 0;
- m_curLoop++;
+ d->m_curFrame++;
+ if (d->m_curFrame >= d->m_spriteEngine->maxFrames()) { // maxFrames: total number of frames including all rows
+ d->m_curFrame = 0;
+ d->m_curLoop++;
}
- frameAt = m_curFrame % nColumns;
+ frameAt = d->m_curFrame % nColumns;
if (frameAt == 0)
- m_spriteEngine->advance();
+ d->m_spriteEngine->advance();
progress = 0;
}
- if (m_loops > 0 && m_curLoop >= m_loops) {
+ if (d->m_loops > 0 && d->m_curLoop >= d->m_loops) {
frameAt = 0;
- m_running = false;
+ d->m_running = false;
emit runningChanged(false);
maybeUpdate();
}
} else {
- frameAt = m_curFrame;
+ frameAt = d->m_curFrame;
}
- if (m_curFrame != lastFrame) {
+ if (d->m_curFrame != lastFrame) {
if (isCurrentFrameChangedConnected())
- emit currentFrameChanged(m_curFrame);
+ emit currentFrameChanged(d->m_curFrame);
maybeUpdate();
}
- qreal frameCount = m_spriteEngine->spriteFrames();
- bool reverse = m_spriteEngine->sprite()->reverse();
+ qreal frameCount = d->m_spriteEngine->spriteFrames();
+ bool reverse = d->m_spriteEngine->sprite()->reverse();
if (reverse)
frameAt = (frameCount - 1) - frameAt;
- qreal w = m_spriteEngine->spriteWidth() / m_sheetSize.width();
- qreal h = m_spriteEngine->spriteHeight() / m_sheetSize.height();
- qreal x1;
- qreal y1;
- if (m_paused) {
- int spriteY = m_spriteEngine->spriteY();
+ int w = d->m_spriteEngine->spriteWidth();
+ int h = d->m_spriteEngine->spriteHeight();
+ int x1;
+ int y1;
+ if (d->m_paused) {
+ int spriteY = d->m_spriteEngine->spriteY();
if (reverse) {
- int rows = m_spriteEngine->maxFrames() * m_spriteEngine->spriteWidth() / m_sheetSize.width();
- spriteY -= rows * m_spriteEngine->spriteHeight();
+ int rows = d->m_spriteEngine->maxFrames() * d->m_spriteEngine->spriteWidth() / d->m_sheetSize.width();
+ spriteY -= rows * d->m_spriteEngine->spriteHeight();
frameAt = (frameCount - 1) - frameAt;
}
- int position = frameAt * m_spriteEngine->spriteWidth() + m_spriteEngine->spriteX();
- int row = position / m_sheetSize.width();
+ int position = frameAt * d->m_spriteEngine->spriteWidth() + d->m_spriteEngine->spriteX();
+ int row = position / d->m_sheetSize.width();
- x1 = (position - (row * m_sheetSize.width())) / m_sheetSize.width();
- y1 = (row * m_spriteEngine->spriteHeight() + spriteY) / m_sheetSize.height();
+ x1 = (position - (row * d->m_sheetSize.width()));
+ y1 = (row * d->m_spriteEngine->spriteHeight() + spriteY);
} else {
- x1 = m_spriteEngine->spriteX() / m_sheetSize.width() + frameAt * w;
- y1 = m_spriteEngine->spriteY() / m_sheetSize.height();
+ x1 = d->m_spriteEngine->spriteX() + frameAt * w;
+ y1 = d->m_spriteEngine->spriteY();
}
//### hard-coded 0/1 work because we are the only
// images in the sprite sheet (without this we cannot assume
// where in the sheet we begin/end).
- qreal x2;
- qreal y2;
+ int x2;
+ int y2;
if (reverse) {
if (frameAt > 0) {
x2 = x1 - w;
@@ -684,9 +786,9 @@ void QQuickAnimatedSprite::prepareNextFrame(QSGGeometryNode *node)
y2 = y1 - h;
if (y2 < 0.0) {
//the last row may not fill the entire width
- int maxRowFrames = m_sheetSize.width() / m_spriteEngine->spriteWidth();
- if (m_spriteEngine->maxFrames() % maxRowFrames)
- x2 = ((m_spriteEngine->maxFrames() % maxRowFrames) - 1) * w;
+ int maxRowFrames = d->m_sheetSize.width() / d->m_spriteEngine->spriteWidth();
+ if (d->m_spriteEngine->maxFrames() % maxRowFrames)
+ x2 = ((d->m_spriteEngine->maxFrames() % maxRowFrames) - 1) * w;
y2 = 1.0 - h;
}
@@ -703,15 +805,13 @@ void QQuickAnimatedSprite::prepareNextFrame(QSGGeometryNode *node)
}
}
- QQuickAnimatedSpriteMaterial *material = static_cast<QQuickAnimatedSpriteMaterial *>(node->material());
- material->animX1 = x1;
- material->animY1 = y1;
- material->animX2 = x2;
- material->animY2 = y2;
- material->animW = w;
- material->animH = h;
- material->animT = m_interpolate ? progress : 0.0;
- material->texture->setFiltering(smooth() ? QSGTexture::Linear : QSGTexture::Nearest);
+ node->setSourceA(QPoint(x1, y1));
+ node->setSourceB(QPoint(x2, y2));
+ node->setSpriteSize(QSize(w, h));
+ node->setTime(d->m_interpolate ? progress : 0.0);
+ node->setSize(QSizeF(width(), height()));
+ node->setFiltering(smooth() ? QSGTexture::Linear : QSGTexture::Nearest);
+ node->update();
}
QT_END_NAMESPACE
diff --git a/src/quick/items/qquickanimatedsprite_p.h b/src/quick/items/qquickanimatedsprite_p.h
index 9d8b4dad40..850461a011 100644
--- a/src/quick/items/qquickanimatedsprite_p.h
+++ b/src/quick/items/qquickanimatedsprite_p.h
@@ -51,6 +51,10 @@
// We mean it.
//
+#include <private/qtquickglobal_p.h>
+
+QT_REQUIRE_CONFIG(quick_sprite);
+
#include <QtQuick/QQuickItem>
#include <private/qquicksprite_p.h>
#include <QtCore/qelapsedtimer.h>
@@ -62,6 +66,8 @@ class QQuickSprite;
class QQuickSpriteEngine;
class QSGGeometryNode;
class QQuickAnimatedSpriteMaterial;
+class QQuickAnimatedSpritePrivate;
+class QSGSpriteNode;
class Q_AUTOTEST_EXPORT QQuickAnimatedSprite : public QQuickItem
{
Q_OBJECT
@@ -94,80 +100,21 @@ public:
};
Q_ENUM(LoopParameters)
- bool running() const
- {
- return m_running;
- }
-
- bool interpolate() const
- {
- return m_interpolate;
- }
-
- QUrl source() const
- {
- return m_sprite->source();
- }
-
- bool reverse() const
- {
- return m_sprite->reverse();
- }
-
- bool frameSync() const
- {
- return m_sprite->frameSync();
- }
-
- int frameCount() const
- {
- return m_sprite->frames();
- }
-
- int frameHeight() const
- {
- return m_sprite->frameHeight();
- }
-
- int frameWidth() const
- {
- return m_sprite->frameWidth();
- }
-
- int frameX() const
- {
- return m_sprite->frameX();
- }
-
- int frameY() const
- {
- return m_sprite->frameY();
- }
-
- qreal frameRate() const
- {
- return m_sprite->frameRate();
- }
-
- int frameDuration() const
- {
- return m_sprite->frameDuration();
- }
-
- int loops() const
- {
- return m_loops;
- }
-
- bool paused() const
- {
- return m_paused;
- }
-
- int currentFrame() const
- {
- return m_curFrame;
- }
+ bool running() const;
+ bool interpolate() const;
+ QUrl source() const;
+ bool reverse() const;
+ bool frameSync() const;
+ int frameCount() const;
+ int frameHeight() const;
+ int frameWidth() const;
+ int frameX() const;
+ int frameY() const;
+ qreal frameRate() const;
+ int frameDuration() const;
+ int loops() const;
+ bool paused() const;
+ int currentFrame() const;
Q_SIGNALS:
@@ -176,27 +123,16 @@ Q_SIGNALS:
void interpolateChanged(bool arg);
void sourceChanged(QUrl arg);
-
void reverseChanged(bool arg);
-
void frameSyncChanged(bool arg);
-
void frameCountChanged(int arg);
-
void frameHeightChanged(int arg);
-
void frameWidthChanged(int arg);
-
void frameXChanged(int arg);
-
void frameYChanged(int arg);
-
void frameRateChanged(qreal arg);
-
void frameDurationChanged(int arg);
-
void loopsChanged(int arg);
-
void currentFrameChanged(int arg);
public Q_SLOTS:
@@ -207,157 +143,27 @@ public Q_SLOTS:
void pause();
void resume();
- void setRunning(bool arg)
- {
- if (m_running != arg) {
- if (m_running)
- stop();
- else
- start();
- }
- }
-
- void setPaused(bool arg)
- {
- if (m_paused != arg) {
- if (m_paused)
- resume();
- else
- pause();
- }
- }
-
- void setInterpolate(bool arg)
- {
- if (m_interpolate != arg) {
- m_interpolate = arg;
- Q_EMIT interpolateChanged(arg);
- }
- }
-
- void setSource(QUrl arg)
- {
- if (m_sprite->m_source != arg) {
- m_sprite->setSource(arg);
- Q_EMIT sourceChanged(arg);
- reloadImage();
- }
- }
-
- void setReverse(bool arg)
- {
- if (m_sprite->m_reverse != arg) {
- m_sprite->setReverse(arg);
- Q_EMIT reverseChanged(arg);
- }
- }
-
- void setFrameSync(bool arg)
- {
- if (m_sprite->m_frameSync != arg) {
- m_sprite->setFrameSync(arg);
- Q_EMIT frameSyncChanged(arg);
- if (m_running)
- restart();
- }
- }
-
- void setFrameCount(int arg)
- {
- if (m_sprite->m_frames != arg) {
- m_sprite->setFrameCount(arg);
- Q_EMIT frameCountChanged(arg);
- reloadImage();
- }
- }
-
- void setFrameHeight(int arg)
- {
- if (m_sprite->m_frameHeight != arg) {
- m_sprite->setFrameHeight(arg);
- Q_EMIT frameHeightChanged(arg);
- reloadImage();
- }
- }
-
- void setFrameWidth(int arg)
- {
- if (m_sprite->m_frameWidth != arg) {
- m_sprite->setFrameWidth(arg);
- Q_EMIT frameWidthChanged(arg);
- reloadImage();
- }
- }
-
- void setFrameX(int arg)
- {
- if (m_sprite->m_frameX != arg) {
- m_sprite->setFrameX(arg);
- Q_EMIT frameXChanged(arg);
- reloadImage();
- }
- }
-
- void setFrameY(int arg)
- {
- if (m_sprite->m_frameY != arg) {
- m_sprite->setFrameY(arg);
- Q_EMIT frameYChanged(arg);
- reloadImage();
- }
- }
-
- void setFrameRate(qreal arg)
- {
- if (m_sprite->m_frameRate != arg) {
- m_sprite->setFrameRate(arg);
- Q_EMIT frameRateChanged(arg);
- if (m_running)
- restart();
- }
- }
-
- void setFrameDuration(int arg)
- {
- if (m_sprite->m_frameDuration != arg) {
- m_sprite->setFrameDuration(arg);
- Q_EMIT frameDurationChanged(arg);
- if (m_running)
- restart();
- }
- }
-
- void resetFrameRate()
- {
- setFrameRate(-1.0);
- }
-
- void resetFrameDuration()
- {
- setFrameDuration(-1);
- }
-
- void setLoops(int arg)
- {
- if (m_loops != arg) {
- m_loops = arg;
- Q_EMIT loopsChanged(arg);
- }
- }
-
- void setCurrentFrame(int arg) //TODO-C: Probably only works when paused
- {
- if (m_curFrame != arg) {
- m_curFrame = arg;
- Q_EMIT currentFrameChanged(arg); //TODO-C Only emitted on manual advance!
- update();
- }
- }
+ void setRunning(bool arg);
+ void setPaused(bool arg);
+ void setInterpolate(bool arg);
+ void setSource(QUrl arg);
+ void setReverse(bool arg);
+ void setFrameSync(bool arg);
+ void setFrameCount(int arg);
+ void setFrameHeight(int arg);
+ void setFrameWidth(int arg);
+ void setFrameX(int arg);
+ void setFrameY(int arg);
+ void setFrameRate(qreal arg);
+ void setFrameDuration(int arg);
+ void resetFrameRate();
+ void resetFrameDuration();
+ void setLoops(int arg);
+ void setCurrentFrame(int arg);
private Q_SLOTS:
void createEngine();
- void sizeVertices(QSGGeometryNode *node);
protected Q_SLOTS:
void reset();
@@ -368,21 +174,13 @@ protected:
private:
void maybeUpdate();
bool isCurrentFrameChangedConnected();
- void prepareNextFrame(QSGGeometryNode *node);
+ void prepareNextFrame(QSGSpriteNode *node);
void reloadImage();
- QSGGeometryNode* buildNode();
- QQuickSprite* m_sprite;
- QQuickSpriteEngine* m_spriteEngine;
- QElapsedTimer m_timestamp;
- int m_curFrame;
- bool m_pleaseReset;
- bool m_running;
- bool m_paused;
- bool m_interpolate;
- QSizeF m_sheetSize;
- int m_loops;
- int m_curLoop;
- int m_pauseOffset;
+ QSGSpriteNode* initNode();
+
+private:
+ Q_DISABLE_COPY(QQuickAnimatedSprite)
+ Q_DECLARE_PRIVATE(QQuickAnimatedSprite)
};
QT_END_NAMESPACE
diff --git a/src/quick/items/qquickanimatedsprite_p_p.h b/src/quick/items/qquickanimatedsprite_p_p.h
new file mode 100644
index 0000000000..3610e58861
--- /dev/null
+++ b/src/quick/items/qquickanimatedsprite_p_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 QQUICKANIMATEDSPRITE_P_P_H
+#define QQUICKANIMATEDSPRITE_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 <QtQuick/qtquickglobal.h>
+
+QT_REQUIRE_CONFIG(quick_sprite);
+
+#include "qquickitem_p.h"
+#include "qquicksprite_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QQuickAnimatedSprite;
+
+class QQuickAnimatedSpritePrivate : public QQuickItemPrivate
+{
+ Q_DECLARE_PUBLIC(QQuickAnimatedSprite)
+
+public:
+ QQuickAnimatedSpritePrivate()
+ : m_sprite(nullptr)
+ , m_spriteEngine(nullptr)
+ , m_curFrame(0)
+ , m_pleaseReset(false)
+ , m_running(true)
+ , m_paused(false)
+ , m_interpolate(true)
+ , m_loops(-1)
+ , m_curLoop(0)
+ , m_pauseOffset(0)
+ {
+ }
+
+ QQuickSprite* m_sprite;
+ QQuickSpriteEngine* m_spriteEngine;
+ QElapsedTimer m_timestamp;
+ int m_curFrame;
+ bool m_pleaseReset;
+ bool m_running;
+ bool m_paused;
+ bool m_interpolate;
+ QSize m_sheetSize;
+ int m_loops;
+ int m_curLoop;
+ int m_pauseOffset;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKANIMATEDSPRITE_P_P_H
diff --git a/src/quick/items/qquickborderimage.cpp b/src/quick/items/qquickborderimage.cpp
index 66f414d816..67b99bfbc6 100644
--- a/src/quick/items/qquickborderimage.cpp
+++ b/src/quick/items/qquickborderimage.cpp
@@ -43,12 +43,15 @@
#include <QtQml/qqmlinfo.h>
#include <QtQml/qqmlfile.h>
#include <QtQml/qqmlengine.h>
+#if QT_CONFIG(qml_network)
#include <QtNetwork/qnetworkreply.h>
+#endif
#include <QtCore/qfile.h>
#include <QtCore/qmath.h>
#include <QtGui/qguiapplication.h>
#include <private/qqmlglobal_p.h>
+#include <private/qsgadaptationlayer_p.h>
QT_BEGIN_NAMESPACE
@@ -169,9 +172,11 @@ QQuickBorderImage::QQuickBorderImage(QQuickItem *parent)
QQuickBorderImage::~QQuickBorderImage()
{
+#if QT_CONFIG(qml_network)
Q_D(QQuickBorderImage);
if (d->sciReply)
d->sciReply->deleteLater();
+#endif
}
/*!
@@ -270,10 +275,12 @@ void QQuickBorderImage::setSource(const QUrl &url)
if (url == d->url)
return;
+#if QT_CONFIG(qml_network)
if (d->sciReply) {
d->sciReply->deleteLater();
d->sciReply = 0;
}
+#endif
d->url = url;
d->sciurl = QUrl();
@@ -311,6 +318,7 @@ void QQuickBorderImage::load()
setGridScaledImage(QQuickGridScaledImage(&file));
return;
} else {
+#if QT_CONFIG(qml_network)
if (d->progress != 0.0) {
d->progress = 0.0;
emit progressChanged(d->progress);
@@ -320,6 +328,7 @@ void QQuickBorderImage::load()
d->sciReply = qmlEngine(this)->networkAccessManager()->get(req);
qmlobject_connect(d->sciReply, QNetworkReply, SIGNAL(finished()),
this, QQuickBorderImage, SLOT(sciRequestFinished()))
+#endif
}
} else {
QQuickPixmap::Options options;
@@ -529,6 +538,7 @@ void QQuickBorderImage::requestFinished()
pixmapChange();
}
+#if QT_CONFIG(qml_network)
#define BORDERIMAGE_MAX_REDIRECT 16
void QQuickBorderImage::sciRequestFinished()
@@ -558,12 +568,59 @@ void QQuickBorderImage::sciRequestFinished()
setGridScaledImage(sci);
}
}
+#endif // qml_network
void QQuickBorderImage::doUpdate()
{
update();
}
+void QQuickBorderImagePrivate::calculateRects(const QQuickScaleGrid *border,
+ const QSize &sourceSize,
+ const QSizeF &targetSize,
+ int horizontalTileMode,
+ int verticalTileMode,
+ qreal devicePixelRatio,
+ QRectF *targetRect,
+ QRectF *innerTargetRect,
+ QRectF *innerSourceRect,
+ QRectF *subSourceRect)
+{
+ *innerSourceRect = QRectF(0, 0, 1, 1);
+ *targetRect = QRectF(0, 0, targetSize.width(), targetSize.height());
+ *innerTargetRect = *targetRect;
+
+ if (border) {
+ *innerSourceRect = QRectF(border->left() * devicePixelRatio / qreal(sourceSize.width()),
+ border->top() * devicePixelRatio / qreal(sourceSize.height()),
+ qMax<qreal>(0, sourceSize.width() - (border->right() + border->left()) * devicePixelRatio) / sourceSize.width(),
+ qMax<qreal>(0, sourceSize.height() - (border->bottom() + border->top()) * devicePixelRatio) / sourceSize.height());
+ *innerTargetRect = QRectF(border->left(),
+ border->top(),
+ qMax<qreal>(0, targetSize.width() - (border->right() + border->left())),
+ qMax<qreal>(0, targetSize.height() - (border->bottom() + border->top())));
+ }
+
+ qreal hTiles = 1;
+ qreal vTiles = 1;
+ const QSizeF innerTargetSize = innerTargetRect->size() * devicePixelRatio;
+ if (innerSourceRect->width() != 0
+ && horizontalTileMode != QQuickBorderImage::Stretch) {
+ hTiles = innerTargetSize.width() / qreal(innerSourceRect->width() * sourceSize.width());
+ if (horizontalTileMode == QQuickBorderImage::Round)
+ hTiles = qCeil(hTiles);
+ }
+ if (innerSourceRect->height() != 0
+ && verticalTileMode != QQuickBorderImage::Stretch) {
+ vTiles = innerTargetSize.height() / qreal(innerSourceRect->height() * sourceSize.height());
+ if (verticalTileMode == QQuickBorderImage::Round)
+ vTiles = qCeil(vTiles);
+ }
+
+ *subSourceRect = QRectF(0, 0, hTiles, vTiles);
+}
+
+
QSGNode *QQuickBorderImage::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
{
Q_D(QQuickBorderImage);
@@ -575,12 +632,12 @@ QSGNode *QQuickBorderImage::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeDat
return 0;
}
- QSGImageNode *node = static_cast<QSGImageNode *>(oldNode);
+ QSGInternalImageNode *node = static_cast<QSGInternalImageNode *>(oldNode);
bool updatePixmap = d->pixmapChanged;
d->pixmapChanged = false;
if (!node) {
- node = d->sceneGraphContext()->createImageNode();
+ node = d->sceneGraphContext()->createInternalImageNode();
updatePixmap = true;
}
@@ -588,45 +645,25 @@ QSGNode *QQuickBorderImage::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeDat
node->setTexture(texture);
// Don't implicitly create the scalegrid in the rendering thread...
- QRectF innerSourceRect(0, 0, 1, 1);
- QRectF targetRect(0, 0, width(), height());
- QRectF innerTargetRect = targetRect;
- if (d->border) {
- const QQuickScaleGrid *border = d->getScaleGrid();
- innerSourceRect = QRectF(border->left() * d->devicePixelRatio / qreal(d->pix.width()),
- border->top() * d->devicePixelRatio / qreal(d->pix.height()),
- qMax<qreal>(0, d->pix.width() - (border->right() + border->left()) * d->devicePixelRatio) / d->pix.width(),
- qMax<qreal>(0, d->pix.height() - (border->bottom() + border->top()) * d->devicePixelRatio) / d->pix.height());
- innerTargetRect = QRectF(border->left(),
- border->top(),
- qMax<qreal>(0, width() - (border->right() + border->left())),
- qMax<qreal>(0, height() - (border->bottom() + border->top())));
- }
- qreal hTiles = 1;
- qreal vTiles = 1;
- const QSizeF innerTargetSize = innerTargetRect.size() * d->devicePixelRatio;
- if (innerSourceRect.width() != 0
- && d->horizontalTileMode != QQuickBorderImage::Stretch) {
- hTiles = innerTargetSize.width() / qreal(innerSourceRect.width() * d->pix.width());
- if (d->horizontalTileMode == QQuickBorderImage::Round)
- hTiles = qCeil(hTiles);
- }
- if (innerSourceRect.height() != 0
- && d->verticalTileMode != QQuickBorderImage::Stretch) {
- vTiles = innerTargetSize.height() / qreal(innerSourceRect.height() * d->pix.height());
- if (d->verticalTileMode == QQuickBorderImage::Round)
- vTiles = qCeil(vTiles);
- }
+ QRectF targetRect;
+ QRectF innerTargetRect;
+ QRectF innerSourceRect;
+ QRectF subSourceRect;
+ d->calculateRects(d->border,
+ QSize(d->pix.width(), d->pix.height()), QSizeF(width(), height()),
+ d->horizontalTileMode, d->verticalTileMode, d->devicePixelRatio,
+ &targetRect, &innerTargetRect,
+ &innerSourceRect, &subSourceRect);
node->setTargetRect(targetRect);
node->setInnerSourceRect(innerSourceRect);
node->setInnerTargetRect(innerTargetRect);
- node->setSubSourceRect(QRectF(0, 0, hTiles, vTiles));
+ node->setSubSourceRect(subSourceRect);
node->setMirror(d->mirror);
node->setMipmapFiltering(QSGTexture::None);
node->setFiltering(d->smooth ? QSGTexture::Linear : QSGTexture::Nearest);
- if (innerSourceRect == QRectF(0, 0, 1, 1) && (vTiles > 1 || hTiles > 1)) {
+ if (innerSourceRect == QRectF(0, 0, 1, 1) && (subSourceRect.width() > 1 || subSourceRect.height() > 1)) {
node->setHorizontalWrapMode(QSGTexture::Repeat);
node->setVerticalWrapMode(QSGTexture::Repeat);
} else {
diff --git a/src/quick/items/qquickborderimage_p.h b/src/quick/items/qquickborderimage_p.h
index 7f8b172a21..844f71e2c9 100644
--- a/src/quick/items/qquickborderimage_p.h
+++ b/src/quick/items/qquickborderimage_p.h
@@ -101,7 +101,9 @@ private:
private Q_SLOTS:
void doUpdate();
void requestFinished() Q_DECL_OVERRIDE;
+#if QT_CONFIG(qml_network)
void sciRequestFinished();
+#endif
private:
Q_DISABLE_COPY(QQuickBorderImage)
diff --git a/src/quick/items/qquickborderimage_p_p.h b/src/quick/items/qquickborderimage_p_p.h
index 478de88a52..0f4e7acc05 100644
--- a/src/quick/items/qquickborderimage_p_p.h
+++ b/src/quick/items/qquickborderimage_p_p.h
@@ -58,17 +58,20 @@
QT_BEGIN_NAMESPACE
+#if QT_CONFIG(qml_network)
class QNetworkReply;
+#endif
class QQuickBorderImagePrivate : public QQuickImageBasePrivate
{
Q_DECLARE_PUBLIC(QQuickBorderImage)
public:
QQuickBorderImagePrivate()
- : border(0), sciReply(0),
- horizontalTileMode(QQuickBorderImage::Stretch),
- verticalTileMode(QQuickBorderImage::Stretch),
- redirectCount(0), pixmapChanged(false)
+ : border(0), horizontalTileMode(QQuickBorderImage::Stretch),
+ verticalTileMode(QQuickBorderImage::Stretch), pixmapChanged(false)
+#if QT_CONFIG(qml_network)
+ , sciReply(0), redirectCount(0)
+#endif
{
}
@@ -88,14 +91,27 @@ public:
return border;
}
+ static void calculateRects(const QQuickScaleGrid *border,
+ const QSize &sourceSize,
+ const QSizeF &targetSize,
+ int horizontalTileMode,
+ int verticalTileMode,
+ qreal devicePixelRatio,
+ QRectF *targetRect,
+ QRectF *innerTargetRect,
+ QRectF *innerSourceRect,
+ QRectF *subSourceRect);
+
QQuickScaleGrid *border;
QUrl sciurl;
- QNetworkReply *sciReply;
QQuickBorderImage::TileMode horizontalTileMode;
QQuickBorderImage::TileMode verticalTileMode;
- int redirectCount;
-
bool pixmapChanged : 1;
+
+#if QT_CONFIG(qml_network)
+ QNetworkReply *sciReply;
+ int redirectCount;
+#endif
};
QT_END_NAMESPACE
diff --git a/src/quick/items/qquickclipnode.cpp b/src/quick/items/qquickclipnode.cpp
index 56f8227c05..747e844172 100644
--- a/src/quick/items/qquickclipnode.cpp
+++ b/src/quick/items/qquickclipnode.cpp
@@ -113,7 +113,7 @@ void QQuickDefaultClipNode::updateGeometry()
}
}
- markDirty(DirtyGeometry);
setClipRect(m_rect);
+ markDirty(DirtyGeometry);
}
diff --git a/src/quick/items/qquickdrag.cpp b/src/quick/items/qquickdrag.cpp
index c81cb0c5fc..cbb052856e 100644
--- a/src/quick/items/qquickdrag.cpp
+++ b/src/quick/items/qquickdrag.cpp
@@ -45,6 +45,7 @@
#include <private/qquickitem_p.h>
#include <QtQuick/private/qquickevents_p_p.h>
#include <private/qquickitemchangelistener_p.h>
+#include <private/qquickpixmapcache_p.h>
#include <private/qv8engine_p.h>
#include <private/qv4scopedvalue_p.h>
#include <QtCore/qmimedata.h>
@@ -81,7 +82,7 @@ public:
{
}
- void itemGeometryChanged(QQuickItem *, const QRectF &, const QRectF &) Q_DECL_OVERRIDE;
+ void itemGeometryChanged(QQuickItem *, QQuickGeometryChange, const QRectF &) Q_DECL_OVERRIDE;
void itemParentChanged(QQuickItem *, QQuickItem *parent) Q_DECL_OVERRIDE;
void updatePosition();
void restartDrag();
@@ -110,6 +111,8 @@ public:
bool eventQueued : 1;
bool overrideActions : 1;
QPointF hotSpot;
+ QUrl imageSource;
+ QQuickPixmap pixmapLoader;
QStringList keys;
QVariantMap externalMimeData;
QQuickDrag::DragType dragType;
@@ -145,9 +148,10 @@ public:
\sa {Qt Quick Examples - Drag and Drop}, {Qt Quick Examples - externaldraganddrop}
*/
-void QQuickDragAttachedPrivate::itemGeometryChanged(QQuickItem *, const QRectF &newGeometry, const QRectF &oldGeometry)
+void QQuickDragAttachedPrivate::itemGeometryChanged(QQuickItem *, QQuickGeometryChange change,
+ const QRectF &)
{
- if (newGeometry.topLeft() == oldGeometry.topLeft() || !active || itemMoved)
+ if (!change.positionChange() || !active || itemMoved)
return;
updatePosition();
}
@@ -409,6 +413,43 @@ void QQuickDragAttached::setHotSpot(const QPointF &hotSpot)
}
/*!
+ \qmlattachedproperty QUrl QtQuick::Drag::imageSource
+ \since 5.8
+
+ This property holds the URL of the image which will be used to represent
+ the data during the drag and drop operation. Changing this property after
+ the drag operation has started will have no effect.
+
+ The example below uses an item's contents as a drag image:
+
+ \snippet qml/externaldrag.qml 0
+
+ \sa Item::grabToImage()
+*/
+
+QUrl QQuickDragAttached::imageSource() const
+{
+ Q_D(const QQuickDragAttached);
+ return d->imageSource;
+}
+
+void QQuickDragAttached::setImageSource(const QUrl &url)
+{
+ Q_D(QQuickDragAttached);
+ if (d->imageSource != url) {
+ d->imageSource = url;
+
+ if (url.isEmpty()) {
+ d->pixmapLoader.clear();
+ } else {
+ d->pixmapLoader.load(qmlEngine(this), url);
+ }
+
+ Q_EMIT imageSourceChanged();
+ }
+}
+
+/*!
\qmlattachedproperty stringlist QtQuick::Drag::keys
This property holds a list of keys that can be used by a DropArea to filter drag events.
@@ -727,9 +768,9 @@ Qt::DropAction QQuickDragAttachedPrivate::startDrag(Qt::DropActions supportedAct
mimeData->setData(it.key(), it.value().toString().toUtf8());
drag->setMimeData(mimeData);
-
- // TODO: how to handle drag image?
- // drag->setPixmap(iconPixmap);
+ if (pixmapLoader.isReady()) {
+ drag->setPixmap(QPixmap::fromImage(pixmapLoader.image()));
+ }
emit q->dragStarted();
diff --git a/src/quick/items/qquickdrag_p.h b/src/quick/items/qquickdrag_p.h
index c1695e49f0..17721251d9 100644
--- a/src/quick/items/qquickdrag_p.h
+++ b/src/quick/items/qquickdrag_p.h
@@ -58,6 +58,7 @@
#include <QtCore/qmimedata.h>
#include <QtCore/qstringlist.h>
+#include <QtCore/qurl.h>
#ifndef QT_NO_DRAGANDDROP
@@ -247,6 +248,7 @@ class QQuickDragAttached : public QObject
Q_PROPERTY(QObject *source READ source WRITE setSource NOTIFY sourceChanged RESET resetSource)
Q_PROPERTY(QObject *target READ target NOTIFY targetChanged)
Q_PROPERTY(QPointF hotSpot READ hotSpot WRITE setHotSpot NOTIFY hotSpotChanged)
+ Q_PROPERTY(QUrl imageSource READ imageSource WRITE setImageSource NOTIFY imageSourceChanged REVISION 8)
Q_PROPERTY(QStringList keys READ keys WRITE setKeys NOTIFY keysChanged)
Q_PROPERTY(QVariantMap mimeData READ mimeData WRITE setMimeData NOTIFY mimeDataChanged)
Q_PROPERTY(Qt::DropActions supportedActions READ supportedActions WRITE setSupportedActions NOTIFY supportedActionsChanged)
@@ -268,6 +270,9 @@ public:
QPointF hotSpot() const;
void setHotSpot(const QPointF &hotSpot);
+ QUrl imageSource() const;
+ void setImageSource(const QUrl &url);
+
QStringList keys() const;
void setKeys(const QStringList &keys);
@@ -300,6 +305,7 @@ Q_SIGNALS:
void sourceChanged();
void targetChanged();
void hotSpotChanged();
+ void imageSourceChanged();
void keysChanged();
void mimeDataChanged();
void supportedActionsChanged();
diff --git a/src/quick/items/qquickdroparea.cpp b/src/quick/items/qquickdroparea.cpp
index 1701441240..8dcc13971e 100644
--- a/src/quick/items/qquickdroparea.cpp
+++ b/src/quick/items/qquickdroparea.cpp
@@ -235,7 +235,7 @@ bool QQuickDropAreaPrivate::hasMatchingKey(const QStringList &keys) const
return true;
QRegExp copy = keyRegExp;
- foreach (const QString &key, keys) {
+ for (const QString &key : keys) {
if (copy.exactMatch(key))
return true;
}
diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp
index cc45891189..a5497f4627 100644
--- a/src/quick/items/qquickevents.cpp
+++ b/src/quick/items/qquickevents.cpp
@@ -38,9 +38,16 @@
****************************************************************************/
#include "qquickevents_p_p.h"
+#include <QtGui/private/qguiapplication_p.h>
+#include <QtQuick/private/qquickitem_p.h>
+#include <QtQuick/private/qquickwindow_p.h>
+#include <private/qdebug_p.h>
QT_BEGIN_NAMESPACE
+Q_LOGGING_CATEGORY(lcPointerEvents, "qt.quick.pointer.events")
+Q_LOGGING_CATEGORY(lcPointerGrab, "qt.quick.pointer.grab")
+
/*!
\qmltype KeyEvent
\instantiates QQuickKeyEvent
@@ -415,4 +422,534 @@ Item {
\endqml
*/
+/*!
+ \qmlproperty int QtQuick::WheelEvent::inverted
+
+ Returns whether the delta values delivered with the event are inverted.
+
+ Normally, a vertical wheel will produce a WheelEvent with positive delta
+ values if the top of the wheel is rotating away from the hand operating it.
+ Similarly, a horizontal wheel movement will produce a QWheelEvent with
+ positive delta values if the top of the wheel is moved to the left.
+
+ However, on some platforms this is configurable, so that the same
+ operations described above will produce negative delta values (but with the
+ same magnitude). For instance, in a QML component (such as a tumbler or a
+ slider) where it is appropriate to synchronize the movement or rotation of
+ an item with the direction of the wheel, regardless of the system settings,
+ the wheel event handler can use the inverted property to decide whether to
+ negate the angleDelta or pixelDelta values.
+
+ \note Many platforms provide no such information. On such platforms
+ \l inverted always returns false.
+*/
+
+typedef QHash<QTouchDevice *, QQuickPointerDevice *> PointerDeviceForTouchDeviceHash;
+Q_GLOBAL_STATIC(PointerDeviceForTouchDeviceHash, g_touchDevices)
+
+Q_GLOBAL_STATIC_WITH_ARGS(QQuickPointerDevice, g_genericMouseDevice,
+ (QQuickPointerDevice::Mouse,
+ QQuickPointerDevice::GenericPointer,
+ QQuickPointerDevice::Position | QQuickPointerDevice::Scroll | QQuickPointerDevice::Hover,
+ 1, 3, QLatin1String("core pointer"), 0))
+
+typedef QHash<qint64, QQuickPointerDevice *> PointerDeviceForDeviceIdHash;
+Q_GLOBAL_STATIC(PointerDeviceForDeviceIdHash, g_tabletDevices)
+
+QQuickPointerDevice *QQuickPointerDevice::touchDevice(QTouchDevice *d)
+{
+ if (g_touchDevices->contains(d))
+ return g_touchDevices->value(d);
+
+ QQuickPointerDevice::DeviceType type = QQuickPointerDevice::TouchScreen;
+ QString name;
+ int maximumTouchPoints = 10;
+ QQuickPointerDevice::Capabilities caps = QQuickPointerDevice::Capabilities(QTouchDevice::Position);
+ if (d) {
+ QQuickPointerDevice::Capabilities caps =
+ static_cast<QQuickPointerDevice::Capabilities>(static_cast<int>(d->capabilities()) & 0x0F);
+ if (d->type() == QTouchDevice::TouchPad) {
+ type = QQuickPointerDevice::TouchPad;
+ caps |= QQuickPointerDevice::Scroll;
+ }
+ name = d->name();
+ maximumTouchPoints = d->maximumTouchPoints();
+ } else {
+ qWarning() << "QQuickWindowPrivate::touchDevice: creating touch device from nullptr device in QTouchEvent";
+ }
+
+ QQuickPointerDevice *dev = new QQuickPointerDevice(type, QQuickPointerDevice::Finger,
+ caps, maximumTouchPoints, 0, name, 0);
+ g_touchDevices->insert(d, dev);
+ return dev;
+}
+
+QList<QQuickPointerDevice*> QQuickPointerDevice::touchDevices()
+{
+ return g_touchDevices->values();
+}
+
+QQuickPointerDevice *QQuickPointerDevice::genericMouseDevice()
+{
+ return g_genericMouseDevice;
+}
+
+QQuickPointerDevice *QQuickPointerDevice::tabletDevice(qint64 id)
+{
+ auto it = g_tabletDevices->find(id);
+ if (it != g_tabletDevices->end())
+ return it.value();
+
+ // ### Figure out how to populate the tablet devices
+ return nullptr;
+}
+
+void QQuickEventPoint::reset(Qt::TouchPointState state, QPointF scenePos, quint64 pointId, ulong timestamp)
+{
+ m_scenePos = scenePos;
+ m_pointId = pointId;
+ m_valid = true;
+ m_accept = false;
+ m_state = static_cast<QQuickEventPoint::State>(state);
+ m_timestamp = timestamp;
+ if (state == Qt::TouchPointPressed)
+ m_pressTimestamp = timestamp;
+ // TODO calculate velocity
+}
+
+QQuickItem *QQuickEventPoint::grabber() const
+{
+ return m_grabber.data();
+}
+
+void QQuickEventPoint::setGrabber(QQuickItem *grabber)
+{
+ if (Q_UNLIKELY(lcPointerGrab().isDebugEnabled()) && m_grabber.data() != grabber) {
+ auto device = static_cast<const QQuickPointerEvent *>(parent())->device();
+ static const QMetaEnum stateMetaEnum = metaObject()->enumerator(metaObject()->indexOfEnumerator("State"));
+ QString deviceName = (device ? device->name() : QLatin1String("null device"));
+ deviceName.resize(16, ' '); // shorten, and align in case of sequential output
+ qCDebug(lcPointerGrab) << deviceName << "point" << hex << m_pointId << stateMetaEnum.valueToKey(state())
+ << ": grab" << m_grabber << "->" << grabber;
+ }
+ m_grabber = QPointer<QQuickItem>(grabber);
+}
+
+void QQuickEventPoint::setAccepted(bool accepted)
+{
+ if (m_accept != accepted) {
+ qCDebug(lcPointerEvents) << this << m_accept << "->" << accepted;
+ m_accept = accepted;
+ }
+}
+
+QQuickEventTouchPoint::QQuickEventTouchPoint(QQuickPointerTouchEvent *parent)
+ : QQuickEventPoint(parent), m_rotation(0), m_pressure(0)
+{}
+
+void QQuickEventTouchPoint::reset(const QTouchEvent::TouchPoint &tp, ulong timestamp)
+{
+ QQuickEventPoint::reset(tp.state(), tp.scenePos(), tp.id(), timestamp);
+ m_rotation = tp.rotation();
+ m_pressure = tp.pressure();
+ m_uniqueId = tp.uniqueId();
+}
+
+/*!
+ \internal
+ \class QQuickPointerEvent
+
+ QQuickPointerEvent is used as a long-lived object to store data related to
+ an event from a pointing device, such as a mouse, touch or tablet event,
+ during event delivery. It also provides properties which may be used later
+ to expose the event to QML, the same as is done with QQuickMouseEvent,
+ QQuickTouchPoint, QQuickKeyEvent, etc. Since only one event can be
+ delivered at a time, this class is effectively a singleton. We don't worry
+ about the QObject overhead because the instances are long-lived: we don't
+ dynamically create and destroy objects of this type for each event.
+*/
+
+QQuickPointerEvent::~QQuickPointerEvent()
+{}
+
+QQuickPointerEvent *QQuickPointerMouseEvent::reset(QEvent *event)
+{
+ auto ev = static_cast<QMouseEvent*>(event);
+ m_event = ev;
+ if (!event)
+ return this;
+
+ m_device = QQuickPointerDevice::genericMouseDevice();
+ m_button = ev->button();
+ m_pressedButtons = ev->buttons();
+ Qt::TouchPointState state = Qt::TouchPointStationary;
+ switch (ev->type()) {
+ case QEvent::MouseButtonPress:
+ case QEvent::MouseButtonDblClick:
+ state = Qt::TouchPointPressed;
+ break;
+ case QEvent::MouseButtonRelease:
+ state = Qt::TouchPointReleased;
+ break;
+ case QEvent::MouseMove:
+ state = Qt::TouchPointMoved;
+ break;
+ default:
+ break;
+ }
+ m_mousePoint->reset(state, ev->windowPos(), 0, ev->timestamp()); // mouse is 0
+ return this;
+}
+
+QQuickPointerEvent *QQuickPointerTouchEvent::reset(QEvent *event)
+{
+ auto ev = static_cast<QTouchEvent*>(event);
+ m_event = ev;
+ if (!event)
+ return this;
+
+ m_device = QQuickPointerDevice::touchDevice(ev->device());
+ m_button = Qt::NoButton;
+ m_pressedButtons = Qt::NoButton;
+
+ const QList<QTouchEvent::TouchPoint> &tps = ev->touchPoints();
+ int newPointCount = tps.count();
+ m_touchPoints.reserve(newPointCount);
+
+ for (int i = m_touchPoints.size(); i < newPointCount; ++i)
+ m_touchPoints.insert(i, new QQuickEventTouchPoint(this));
+
+ // Make sure the grabbers are right from one event to the next
+ QVector<QQuickItem*> grabbers;
+ // Copy all grabbers, because the order of points might have changed in the event.
+ // The ID is all that we can rely on (release might remove the first point etc).
+ for (int i = 0; i < newPointCount; ++i) {
+ QQuickItem *grabber = nullptr;
+ if (auto point = pointById(tps.at(i).id()))
+ grabber = point->grabber();
+ grabbers.append(grabber);
+ }
+
+ for (int i = 0; i < newPointCount; ++i) {
+ auto point = m_touchPoints.at(i);
+ point->reset(tps.at(i), ev->timestamp());
+ if (point->state() == QQuickEventPoint::Pressed) {
+ if (grabbers.at(i))
+ qWarning() << "TouchPointPressed without previous release event" << point;
+ point->setGrabber(nullptr);
+ } else {
+ point->setGrabber(grabbers.at(i));
+ }
+ }
+ m_pointCount = newPointCount;
+ return this;
+}
+
+QQuickEventPoint *QQuickPointerMouseEvent::point(int i) const {
+ if (i == 0)
+ return m_mousePoint;
+ return nullptr;
+}
+
+QQuickEventPoint *QQuickPointerTouchEvent::point(int i) const {
+ if (i >= 0 && i < m_pointCount)
+ return m_touchPoints.at(i);
+ return nullptr;
+}
+
+QQuickEventPoint::QQuickEventPoint(QQuickPointerEvent *parent)
+ : QObject(parent), m_pointId(0), m_grabber(nullptr), m_timestamp(0), m_pressTimestamp(0),
+ m_state(QQuickEventPoint::Released), m_valid(false), m_accept(false)
+{
+ Q_UNUSED(m_reserved);
+}
+
+QQuickPointerEvent *QQuickEventPoint::pointerEvent() const
+{
+ return static_cast<QQuickPointerEvent *>(parent());
+}
+
+bool QQuickPointerMouseEvent::allPointsAccepted() const {
+ return m_mousePoint->isAccepted();
+}
+
+QMouseEvent *QQuickPointerMouseEvent::asMouseEvent(const QPointF &localPos) const
+{
+ auto event = static_cast<QMouseEvent *>(m_event);
+ event->setLocalPos(localPos);
+ return event;
+}
+
+QVector<QQuickItem *> QQuickPointerMouseEvent::grabbers() const
+{
+ QVector<QQuickItem *> result;
+ if (QQuickItem *grabber = m_mousePoint->grabber())
+ result << grabber;
+ return result;
+}
+
+void QQuickPointerMouseEvent::clearGrabbers() const {
+ m_mousePoint->setGrabber(nullptr);
+}
+
+bool QQuickPointerMouseEvent::isPressEvent() const
+{
+ auto me = static_cast<QMouseEvent*>(m_event);
+ return ((me->type() == QEvent::MouseButtonPress || me->type() == QEvent::MouseButtonDblClick) &&
+ (me->buttons() & me->button()) == me->buttons());
+}
+
+bool QQuickPointerTouchEvent::allPointsAccepted() const {
+ for (int i = 0; i < m_pointCount; ++i) {
+ if (!m_touchPoints.at(i)->isAccepted())
+ return false;
+ }
+ return true;
+}
+
+QVector<QQuickItem *> QQuickPointerTouchEvent::grabbers() const
+{
+ QVector<QQuickItem *> result;
+ for (int i = 0; i < m_pointCount; ++i) {
+ auto point = m_touchPoints.at(i);
+ if (QQuickItem *grabber = point->grabber()) {
+ if (!result.contains(grabber))
+ result << grabber;
+ }
+ }
+ return result;
+}
+
+void QQuickPointerTouchEvent::clearGrabbers() const {
+ for (auto point: m_touchPoints)
+ point->setGrabber(nullptr);
+}
+
+bool QQuickPointerTouchEvent::isPressEvent() const
+{
+ return static_cast<QTouchEvent*>(m_event)->touchPointStates() & Qt::TouchPointPressed;
+}
+
+QVector<QPointF> QQuickPointerEvent::unacceptedPressedPointScenePositions() const
+{
+ QVector<QPointF> points;
+ for (int i = 0; i < pointCount(); ++i) {
+ if (!point(i)->isAccepted() && point(i)->state() == QQuickEventPoint::Pressed)
+ points << point(i)->scenePos();
+ }
+ return points;
+}
+
+/*!
+ \internal
+ Populate the reusable synth-mouse event from one touchpoint.
+ It's required that isTouchEvent() be true when this is called.
+ If the touchpoint cannot be found, this returns nullptr.
+ Ownership of the event is NOT transferred to the caller.
+*/
+QMouseEvent *QQuickPointerTouchEvent::syntheticMouseEvent(int pointID, QQuickItem *relativeTo) const {
+ const QTouchEvent::TouchPoint *p = touchPointById(pointID);
+ if (!p)
+ return nullptr;
+ QEvent::Type type;
+ Qt::MouseButton buttons = Qt::LeftButton;
+ switch (p->state()) {
+ case Qt::TouchPointPressed:
+ type = QEvent::MouseButtonPress;
+ break;
+ case Qt::TouchPointMoved:
+ case Qt::TouchPointStationary:
+ type = QEvent::MouseMove;
+ break;
+ case Qt::TouchPointReleased:
+ type = QEvent::MouseButtonRelease;
+ buttons = Qt::NoButton;
+ break;
+ default:
+ Q_ASSERT(false);
+ return nullptr;
+ }
+ m_synthMouseEvent = QMouseEvent(type, relativeTo->mapFromScene(p->scenePos()),
+ p->scenePos(), p->screenPos(), Qt::LeftButton, buttons, m_event->modifiers());
+ m_synthMouseEvent.setAccepted(true);
+ m_synthMouseEvent.setTimestamp(m_event->timestamp());
+ // In the future we will try to always have valid velocity in every QQuickEventPoint.
+ // QQuickFlickablePrivate::handleMouseMoveEvent() checks for QTouchDevice::Velocity
+ // and if it is set, then it does not need to do its own velocity calculations.
+ // That's probably the only usecase for this, so far. Some day Flickable should handle
+ // pointer events, and then passing touchpoint velocity via QMouseEvent will be obsolete.
+ // Conveniently (by design), QTouchDevice::Velocity == QQuickPointerDevice.Velocity
+ // so that we don't need to convert m_device->capabilities().
+ if (m_device)
+ QGuiApplicationPrivate::setMouseEventCapsAndVelocity(&m_synthMouseEvent, m_device->capabilities(), p->velocity());
+ QGuiApplicationPrivate::setMouseEventSource(&m_synthMouseEvent, Qt::MouseEventSynthesizedByQt);
+ return &m_synthMouseEvent;
+}
+
+/*!
+ \internal
+ Returns a pointer to the QQuickEventPoint which has the \a pointId as
+ \l {QQuickEventPoint::pointId}{pointId}.
+ Returns nullptr if there is no point with that ID.
+
+ \fn QQuickPointerEvent::pointById(quint64 pointId) const
+*/
+QQuickEventPoint *QQuickPointerMouseEvent::pointById(quint64 pointId) const {
+ if (m_mousePoint && pointId == m_mousePoint->pointId())
+ return m_mousePoint;
+ return nullptr;
+}
+
+QQuickEventPoint *QQuickPointerTouchEvent::pointById(quint64 pointId) const {
+ auto it = std::find_if(m_touchPoints.constBegin(), m_touchPoints.constEnd(),
+ [pointId](const QQuickEventTouchPoint *tp) { return tp->pointId() == pointId; } );
+ if (it != m_touchPoints.constEnd())
+ return *it;
+ return nullptr;
+}
+
+
+/*!
+ \internal
+ Returns a pointer to the original TouchPoint which has the same
+ \l {QTouchEvent::TouchPoint::id}{id} as \a pointId, if the original event is a
+ QTouchEvent, and if that point is found. Otherwise, returns nullptr.
+*/
+const QTouchEvent::TouchPoint *QQuickPointerTouchEvent::touchPointById(int pointId) const {
+ const QTouchEvent *ev = asTouchEvent();
+ if (!ev)
+ return nullptr;
+ const QList<QTouchEvent::TouchPoint> &tps = ev->touchPoints();
+ auto it = std::find_if(tps.constBegin(), tps.constEnd(),
+ [pointId](QTouchEvent::TouchPoint const& tp) { return tp.id() == pointId; } );
+ // return the pointer to the actual TP in QTouchEvent::_touchPoints
+ return (it == tps.constEnd() ? nullptr : it.operator->());
+}
+
+/*!
+ \internal
+ Make a new QTouchEvent, giving it a subset of the original touch points.
+
+ Returns a nullptr if all points are stationary or there are no points inside the item.
+*/
+QTouchEvent *QQuickPointerTouchEvent::touchEventForItem(QQuickItem *item, bool isFiltering) const
+{
+ QList<QTouchEvent::TouchPoint> touchPoints;
+ Qt::TouchPointStates eventStates;
+ // TODO maybe add QQuickItem::mapVector2DFromScene(QVector2D) to avoid needing QQuickItemPrivate here
+ // Or else just document that velocity is always scene-relative and is not scaled and rotated with the item
+ // but that would require changing tst_qquickwindow::touchEvent_velocity(): it expects transformed velocity
+
+ QMatrix4x4 transformMatrix(QQuickItemPrivate::get(item)->windowToItemTransform());
+ for (int i = 0; i < m_pointCount; ++i) {
+ auto p = m_touchPoints.at(i);
+ if (p->isAccepted())
+ continue;
+ bool isGrabber = p->grabber() == item;
+ bool isPressInside = p->state() == QQuickEventPoint::Pressed && item->contains(item->mapFromScene(p->scenePos()));
+ if (!(isGrabber || isPressInside || isFiltering))
+ continue;
+
+ const QTouchEvent::TouchPoint *tp = touchPointById(p->pointId());
+ if (tp) {
+ eventStates |= tp->state();
+ QTouchEvent::TouchPoint tpCopy = *tp;
+ tpCopy.setPos(item->mapFromScene(tpCopy.scenePos()));
+ tpCopy.setLastPos(item->mapFromScene(tpCopy.lastScenePos()));
+ tpCopy.setStartPos(item->mapFromScene(tpCopy.startScenePos()));
+ tpCopy.setRect(item->mapRectFromScene(tpCopy.sceneRect()));
+ tpCopy.setVelocity(transformMatrix.mapVector(tpCopy.velocity()).toVector2D());
+ touchPoints << tpCopy;
+ }
+ }
+
+ if (eventStates == Qt::TouchPointStationary || touchPoints.isEmpty())
+ return nullptr;
+
+ // if all points have the same state, set the event type accordingly
+ const QTouchEvent &event = *asTouchEvent();
+ QEvent::Type eventType = event.type();
+ switch (eventStates) {
+ case Qt::TouchPointPressed:
+ eventType = QEvent::TouchBegin;
+ break;
+ case Qt::TouchPointReleased:
+ eventType = QEvent::TouchEnd;
+ break;
+ default:
+ eventType = QEvent::TouchUpdate;
+ break;
+ }
+
+ QTouchEvent *touchEvent = new QTouchEvent(eventType);
+ touchEvent->setWindow(event.window());
+ touchEvent->setTarget(item);
+ touchEvent->setDevice(event.device());
+ touchEvent->setModifiers(event.modifiers());
+ touchEvent->setTouchPoints(touchPoints);
+ touchEvent->setTouchPointStates(eventStates);
+ touchEvent->setTimestamp(event.timestamp());
+ touchEvent->accept();
+ return touchEvent;
+}
+
+QTouchEvent *QQuickPointerTouchEvent::asTouchEvent() const
+{
+ return static_cast<QTouchEvent *>(m_event);
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+
+Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug dbg, const QQuickPointerDevice *dev) {
+ QDebugStateSaver saver(dbg);
+ dbg.nospace();
+ if (!dev) {
+ dbg << "QQuickPointerDevice(0)";
+ return dbg;
+ }
+ dbg << "QQuickPointerDevice("<< dev->name() << ' ';
+ QtDebugUtils::formatQEnum(dbg, dev->type());
+ dbg << ' ';
+ QtDebugUtils::formatQEnum(dbg, dev->pointerType());
+ dbg << " caps:";
+ QtDebugUtils::formatQFlags(dbg, dev->capabilities());
+ if (dev->type() == QQuickPointerDevice::TouchScreen ||
+ dev->type() == QQuickPointerDevice::TouchPad)
+ dbg << " maxTouchPoints:" << dev->maximumTouchPoints();
+ else
+ dbg << " buttonCount:" << dev->buttonCount();
+ dbg << ')';
+ return dbg;
+}
+
+Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug dbg, const QQuickPointerEvent *event) {
+ QDebugStateSaver saver(dbg);
+ dbg.nospace();
+ dbg << "QQuickPointerEvent(dev:";
+ QtDebugUtils::formatQEnum(dbg, event->device()->type());
+ if (event->buttons() != Qt::NoButton) {
+ dbg << " buttons:";
+ QtDebugUtils::formatQEnum(dbg, event->buttons());
+ }
+ dbg << " [";
+ int c = event->pointCount();
+ for (int i = 0; i < c; ++i)
+ dbg << event->point(i) << ' ';
+ dbg << "])";
+ return dbg;
+}
+
+Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug dbg, const QQuickEventPoint *event) {
+ QDebugStateSaver saver(dbg);
+ dbg.nospace();
+ dbg << "QQuickEventPoint(valid:" << event->isValid() << " accepted:" << event->isAccepted()
+ << " state:";
+ QtDebugUtils::formatQEnum(dbg, event->state());
+ dbg << " scenePos:" << event->scenePos() << " id:" << event->pointId()
+ << " timeHeld:" << event->timeHeld() << ')';
+ return dbg;
+}
+
+#endif
+
QT_END_NAMESPACE
diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h
index b28ab555b0..61bbb4ecda 100644
--- a/src/quick/items/qquickevents_p_p.h
+++ b/src/quick/items/qquickevents_p_p.h
@@ -55,12 +55,20 @@
#include <qqml.h>
#include <QtCore/qobject.h>
+#include <QtCore/qpointer.h>
#include <QtGui/qvector2d.h>
#include <QtGui/qevent.h>
#include <QtGui/qkeysequence.h>
+#include <QtQuick/qquickitem.h>
QT_BEGIN_NAMESPACE
+class QQuickPointerDevice;
+class QQuickPointerEvent;
+class QQuickPointerMouseEvent;
+class QQuickPointerTabletEvent;
+class QQuickPointerTouchEvent;
+
class QQuickKeyEvent : public QObject
{
Q_OBJECT
@@ -73,10 +81,22 @@ class QQuickKeyEvent : public QObject
Q_PROPERTY(bool accepted READ isAccepted WRITE setAccepted)
public:
- QQuickKeyEvent(QEvent::Type type, int key, Qt::KeyboardModifiers modifiers, const QString &text=QString(), bool autorep=false, ushort count=1)
- : event(type, key, modifiers, text, autorep, count) { event.setAccepted(false); }
- QQuickKeyEvent(const QKeyEvent &ke)
- : event(ke) { event.setAccepted(false); }
+ QQuickKeyEvent()
+ : event(QEvent::None, 0, 0)
+ {}
+
+ void reset(QEvent::Type type, int key, Qt::KeyboardModifiers modifiers,
+ const QString &text = QString(), bool autorep = false, ushort count = 1)
+ {
+ event = QKeyEvent(type, key, modifiers, text, autorep, count);
+ event.setAccepted(false);
+ }
+
+ void reset(const QKeyEvent &ke)
+ {
+ event = ke;
+ event.setAccepted(false);
+ }
int key() const { return event.key(); }
QString text() const { return event.text(); }
@@ -109,10 +129,24 @@ class Q_QUICK_PRIVATE_EXPORT QQuickMouseEvent : public QObject
Q_PROPERTY(bool accepted READ isAccepted WRITE setAccepted)
public:
- QQuickMouseEvent(qreal x, qreal y, Qt::MouseButton button, Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers
- , bool isClick=false, bool wasHeld=false)
- : _x(x), _y(y), _button(button), _buttons(buttons), _modifiers(modifiers)
- , _source(Qt::MouseEventNotSynthesized), _wasHeld(wasHeld), _isClick(isClick), _accepted(true) {}
+ QQuickMouseEvent()
+ : _x(0), _y(0), _button(Qt::NoButton), _buttons(Qt::NoButton), _modifiers(Qt::NoModifier)
+ , _source(Qt::MouseEventNotSynthesized), _wasHeld(false), _isClick(false), _accepted(false)
+ {}
+
+ void reset(qreal x, qreal y, Qt::MouseButton button, Qt::MouseButtons buttons,
+ Qt::KeyboardModifiers modifiers, bool isClick = false, bool wasHeld = false)
+ {
+ _x = x;
+ _y = y;
+ _button = button;
+ _buttons = buttons;
+ _modifiers = modifiers;
+ _source = Qt::MouseEventNotSynthesized;
+ _wasHeld = wasHeld;
+ _isClick = isClick;
+ _accepted = true;
+ }
qreal x() const { return _x; }
qreal y() const { return _y; }
@@ -139,9 +173,9 @@ private:
Qt::MouseButtons _buttons;
Qt::KeyboardModifiers _modifiers;
Qt::MouseEventSource _source;
- bool _wasHeld;
- bool _isClick;
- bool _accepted;
+ bool _wasHeld : 1;
+ bool _isClick : 1;
+ bool _accepted : 1;
};
class QQuickWheelEvent : public QObject
@@ -153,13 +187,27 @@ class QQuickWheelEvent : public QObject
Q_PROPERTY(QPoint pixelDelta READ pixelDelta)
Q_PROPERTY(int buttons READ buttons)
Q_PROPERTY(int modifiers READ modifiers)
+ Q_PROPERTY(bool inverted READ inverted)
Q_PROPERTY(bool accepted READ isAccepted WRITE setAccepted)
public:
- QQuickWheelEvent(qreal x, qreal y, const QPoint& angleDelta, const QPoint& pixelDelta,
- Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers)
- : _x(x), _y(y), _angleDelta(angleDelta), _pixelDelta(pixelDelta), _buttons(buttons),
- _modifiers(modifiers), _accepted(true) {}
+ QQuickWheelEvent()
+ : _x(0), _y(0), _buttons(Qt::NoButton), _modifiers(Qt::NoModifier)
+ , _inverted(false), _accepted(false)
+ {}
+
+ void reset(qreal x, qreal y, const QPoint &angleDelta, const QPoint &pixelDelta,
+ Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers, bool inverted)
+ {
+ _x = x;
+ _y = y;
+ _angleDelta = angleDelta;
+ _pixelDelta = pixelDelta;
+ _buttons = buttons;
+ _modifiers = modifiers;
+ _accepted = true;
+ _inverted = inverted;
+ }
qreal x() const { return _x; }
qreal y() const { return _y; }
@@ -167,7 +215,7 @@ public:
QPoint pixelDelta() const { return _pixelDelta; }
int buttons() const { return _buttons; }
int modifiers() const { return _modifiers; }
-
+ bool inverted() const { return _inverted; }
bool isAccepted() { return _accepted; }
void setAccepted(bool accepted) { _accepted = accepted; }
@@ -178,13 +226,335 @@ private:
QPoint _pixelDelta;
Qt::MouseButtons _buttons;
Qt::KeyboardModifiers _modifiers;
+ bool _inverted;
+ bool _accepted;
+};
+
+class Q_QUICK_PRIVATE_EXPORT QQuickCloseEvent : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(bool accepted READ isAccepted WRITE setAccepted)
+
+public:
+ QQuickCloseEvent()
+ : _accepted(true) {}
+
+ bool isAccepted() { return _accepted; }
+ void setAccepted(bool accepted) { _accepted = accepted; }
+
+private:
bool _accepted;
};
+class Q_QUICK_PRIVATE_EXPORT QQuickEventPoint : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QPointF scenePos READ scenePos)
+ Q_PROPERTY(State state READ state)
+ Q_PROPERTY(quint64 pointId READ pointId)
+ Q_PROPERTY(qreal timeHeld READ timeHeld)
+ Q_PROPERTY(bool accepted READ isAccepted WRITE setAccepted)
+ Q_PROPERTY(QQuickItem *grabber READ grabber WRITE setGrabber)
+
+public:
+ enum State {
+ Pressed = Qt::TouchPointPressed,
+ Updated = Qt::TouchPointMoved,
+ Stationary = Qt::TouchPointStationary,
+ Released = Qt::TouchPointReleased
+ // Canceled = Qt::TouchPointReleased << 1 // 0x10 // TODO maybe
+ };
+ Q_ENUM(State)
+
+ QQuickEventPoint(QQuickPointerEvent *parent);
+
+ void reset(Qt::TouchPointState state, QPointF scenePos, quint64 pointId, ulong timestamp);
+
+ void invalidate() { m_valid = false; }
+
+ QQuickPointerEvent *pointerEvent() const;
+ QPointF scenePos() const { return m_scenePos; }
+ State state() const { return m_state; }
+ quint64 pointId() const { return m_pointId; }
+ bool isValid() const { return m_valid; }
+ qreal timeHeld() const { return (m_timestamp - m_pressTimestamp) / 1000.0; }
+ bool isAccepted() const { return m_accept; }
+ void setAccepted(bool accepted = true);
+ QQuickItem *grabber() const;
+ void setGrabber(QQuickItem *grabber);
+
+private:
+ QPointF m_scenePos;
+ quint64 m_pointId;
+ QPointer<QQuickItem> m_grabber;
+ ulong m_timestamp;
+ ulong m_pressTimestamp;
+ State m_state;
+ bool m_valid : 1;
+ bool m_accept : 1;
+ int m_reserved : 30;
+
+ Q_DISABLE_COPY(QQuickEventPoint)
+};
+
+class Q_QUICK_PRIVATE_EXPORT QQuickEventTouchPoint : public QQuickEventPoint
+{
+ Q_OBJECT
+ Q_PROPERTY(qreal rotation READ rotation)
+ Q_PROPERTY(qreal pressure READ pressure)
+ Q_PROPERTY(QPointerUniqueId uniqueId READ uniqueId)
+
+public:
+ QQuickEventTouchPoint(QQuickPointerTouchEvent *parent);
+
+ void reset(const QTouchEvent::TouchPoint &tp, ulong timestamp);
+
+ qreal rotation() const { return m_rotation; }
+ qreal pressure() const { return m_pressure; }
+ QPointerUniqueId uniqueId() const { return m_uniqueId; }
+
+private:
+ qreal m_rotation;
+ qreal m_pressure;
+ QPointerUniqueId m_uniqueId;
+
+ Q_DISABLE_COPY(QQuickEventTouchPoint)
+};
+
+class Q_QUICK_PRIVATE_EXPORT QQuickPointerEvent : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(const QQuickPointerDevice *device READ device)
+ Q_PROPERTY(Qt::KeyboardModifiers modifiers READ modifiers)
+ Q_PROPERTY(Qt::MouseButtons button READ button)
+ Q_PROPERTY(Qt::MouseButtons buttons READ buttons)
+
+public:
+ QQuickPointerEvent(QObject *parent = nullptr)
+ : QObject(parent)
+ , m_device(nullptr)
+ , m_event(nullptr)
+ , m_button(Qt::NoButton)
+ , m_pressedButtons(Qt::NoButton)
+ { }
+
+ virtual ~QQuickPointerEvent();
+
+public: // property accessors
+ QQuickPointerDevice *device() const { return m_device; }
+ Qt::KeyboardModifiers modifiers() const { return m_event ? m_event->modifiers() : Qt::NoModifier; }
+ Qt::MouseButton button() const { return m_button; }
+ Qt::MouseButtons buttons() const { return m_pressedButtons; }
+
+public: // helpers for C++ only (during event delivery)
+ virtual QQuickPointerEvent *reset(QEvent *ev) = 0;
+
+ virtual bool isPressEvent() const = 0;
+ virtual QQuickPointerMouseEvent *asPointerMouseEvent() { return nullptr; }
+ virtual QQuickPointerTouchEvent *asPointerTouchEvent() { return nullptr; }
+ virtual QQuickPointerTabletEvent *asPointerTabletEvent() { return nullptr; }
+ virtual const QQuickPointerMouseEvent *asPointerMouseEvent() const { return nullptr; }
+ virtual const QQuickPointerTouchEvent *asPointerTouchEvent() const { return nullptr; }
+ virtual const QQuickPointerTabletEvent *asPointerTabletEvent() const { return nullptr; }
+ bool isValid() const { return m_event != nullptr; }
+ virtual bool allPointsAccepted() const = 0;
+ bool isAccepted() { return m_event->isAccepted(); }
+ void setAccepted(bool accepted) { m_event->setAccepted(accepted); }
+ QVector<QPointF> unacceptedPressedPointScenePositions() const;
+
+ virtual int pointCount() const = 0;
+ virtual QQuickEventPoint *point(int i) const = 0;
+ virtual QQuickEventPoint *pointById(quint64 pointId) const = 0;
+ virtual QVector<QQuickItem *> grabbers() const = 0;
+ virtual void clearGrabbers() const = 0;
+
+ ulong timestamp() const { return m_event->timestamp(); }
+
+protected:
+ QQuickPointerDevice *m_device;
+ QInputEvent *m_event; // original event as received by QQuickWindow
+ Qt::MouseButton m_button;
+ Qt::MouseButtons m_pressedButtons;
+
+ Q_DISABLE_COPY(QQuickPointerEvent)
+};
+
+class Q_QUICK_PRIVATE_EXPORT QQuickPointerMouseEvent : public QQuickPointerEvent
+{
+ Q_OBJECT
+public:
+ QQuickPointerMouseEvent(QObject *parent = nullptr)
+ : QQuickPointerEvent(parent), m_mousePoint(new QQuickEventPoint(this)) { }
+
+ QQuickPointerEvent *reset(QEvent *) override;
+ bool isPressEvent() const override;
+ QQuickPointerMouseEvent *asPointerMouseEvent() override { return this; }
+ const QQuickPointerMouseEvent *asPointerMouseEvent() const override { return this; }
+ int pointCount() const override { return 1; }
+ QQuickEventPoint *point(int i) const override;
+ QQuickEventPoint *pointById(quint64 pointId) const override;
+ bool allPointsAccepted() const override;
+ QVector<QQuickItem *> grabbers() const override;
+ void clearGrabbers() const override;
+
+ QMouseEvent *asMouseEvent(const QPointF& localPos) const;
+
+private:
+ QQuickEventPoint *m_mousePoint;
+
+ Q_DISABLE_COPY(QQuickPointerMouseEvent)
+};
+
+class Q_QUICK_PRIVATE_EXPORT QQuickPointerTouchEvent : public QQuickPointerEvent
+{
+ Q_OBJECT
+public:
+ QQuickPointerTouchEvent(QObject *parent = nullptr)
+ : QQuickPointerEvent(parent)
+ , m_pointCount(0)
+ , m_synthMouseEvent(QEvent::MouseMove, QPointF(), Qt::NoButton, Qt::NoButton, Qt::NoModifier)
+ { }
+
+ QQuickPointerEvent *reset(QEvent *) override;
+ bool isPressEvent() const override;
+ QQuickPointerTouchEvent *asPointerTouchEvent() override { return this; }
+ const QQuickPointerTouchEvent *asPointerTouchEvent() const override { return this; }
+ int pointCount() const override { return m_pointCount; }
+ QQuickEventPoint *point(int i) const override;
+ QQuickEventPoint *pointById(quint64 pointId) const override;
+ const QTouchEvent::TouchPoint *touchPointById(int pointId) const;
+ bool allPointsAccepted() const override;
+ QVector<QQuickItem *> grabbers() const override;
+ void clearGrabbers() const override;
+
+ QMouseEvent *syntheticMouseEvent(int pointID, QQuickItem *relativeTo) const;
+ QTouchEvent *touchEventForItem(QQuickItem *item, bool isFiltering = false) const;
+
+ QTouchEvent *asTouchEvent() const;
+
+private:
+ int m_pointCount;
+ QVector<QQuickEventTouchPoint *> m_touchPoints;
+ mutable QMouseEvent m_synthMouseEvent;
+
+ Q_DISABLE_COPY(QQuickPointerTouchEvent)
+};
+
+// ### Qt 6: move this to qtbase, replace QTouchDevice and the enums in QTabletEvent
+class Q_QUICK_PRIVATE_EXPORT QQuickPointerDevice : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(DeviceType type READ type CONSTANT)
+ Q_PROPERTY(PointerType pointerType READ pointerType CONSTANT)
+ Q_PROPERTY(Capabilities capabilities READ capabilities CONSTANT)
+ Q_PROPERTY(int maximumTouchPoints READ maximumTouchPoints CONSTANT)
+ Q_PROPERTY(int buttonCount READ buttonCount CONSTANT)
+ Q_PROPERTY(QString name READ name CONSTANT)
+ Q_PROPERTY(qint64 uniqueId READ uniqueId CONSTANT)
+
+public:
+ enum DeviceType {
+ UnknownDevice = 0x0000,
+ Mouse = 0x0001,
+ TouchScreen = 0x0002,
+ TouchPad = 0x0004,
+ Puck = 0x0008,
+ Stylus = 0x0010,
+ Airbrush = 0x0020,
+ AllDevices = 0x003F
+ };
+ Q_DECLARE_FLAGS(DeviceTypes, DeviceType)
+ Q_ENUM(DeviceType)
+ Q_FLAG(DeviceTypes)
+
+ enum PointerType {
+ GenericPointer = 0x0001,
+ Finger = 0x0002,
+ Pen = 0x0004,
+ Eraser = 0x0008,
+ Cursor = 0x0010,
+ AllPointerTypes = 0x001F
+ };
+ Q_DECLARE_FLAGS(PointerTypes, PointerType)
+ Q_ENUM(PointerType)
+ Q_FLAG(PointerTypes)
+
+ enum CapabilityFlag {
+ Position = QTouchDevice::Position,
+ Area = QTouchDevice::Area,
+ Pressure = QTouchDevice::Pressure,
+ Velocity = QTouchDevice::Velocity,
+ // some bits reserved in case we need more of QTouchDevice::Capabilities
+ Scroll = 0x0100, // mouse has a wheel, or there is OS-level scroll gesture recognition (dubious?)
+ Hover = 0x0200,
+ Rotation = 0x0400,
+ XTilt = 0x0800,
+ YTilt = 0x1000
+ };
+ Q_DECLARE_FLAGS(Capabilities, CapabilityFlag)
+ Q_ENUM(CapabilityFlag)
+ Q_FLAG(Capabilities)
+
+ QQuickPointerDevice(DeviceType devType, PointerType pType, Capabilities caps, int maxPoints, int buttonCount, const QString &name, qint64 uniqueId = 0)
+ : m_deviceType(devType), m_pointerType(pType), m_capabilities(caps)
+ , m_maximumTouchPoints(maxPoints), m_buttonCount(buttonCount), m_name(name), m_uniqueId(uniqueId), m_event(nullptr)
+ {
+ if (m_deviceType == Mouse) {
+ m_event = new QQuickPointerMouseEvent;
+ } else if (m_deviceType == TouchScreen || m_deviceType == TouchPad) {
+ m_event = new QQuickPointerTouchEvent;
+ } else {
+ Q_ASSERT(false);
+ }
+ }
+
+ ~QQuickPointerDevice() { delete m_event; }
+ DeviceType type() const { return m_deviceType; }
+ PointerType pointerType() const { return m_pointerType; }
+ Capabilities capabilities() const { return m_capabilities; }
+ bool hasCapability(CapabilityFlag cap) { return m_capabilities & cap; }
+ int maximumTouchPoints() const { return m_maximumTouchPoints; }
+ int buttonCount() const { return m_buttonCount; }
+ QString name() const { return m_name; }
+ qint64 uniqueId() const { return m_uniqueId; }
+ QQuickPointerEvent *pointerEvent() const { return m_event; }
+
+ static QQuickPointerDevice *touchDevice(QTouchDevice *d);
+ static QList<QQuickPointerDevice *> touchDevices();
+ static QQuickPointerDevice *genericMouseDevice();
+ static QQuickPointerDevice *tabletDevice(qint64);
+
+private:
+ DeviceType m_deviceType;
+ PointerType m_pointerType;
+ Capabilities m_capabilities;
+ int m_maximumTouchPoints;
+ int m_buttonCount;
+ QString m_name;
+ qint64 m_uniqueId;
+ // the device-specific event instance which is reused during event delivery
+ QQuickPointerEvent *m_event;
+
+ Q_DISABLE_COPY(QQuickPointerDevice)
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickPointerDevice::DeviceTypes)
+Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickPointerDevice::PointerTypes)
+Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickPointerDevice::Capabilities)
+
+Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug, const QQuickPointerDevice *);
+Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug, const QQuickPointerEvent *);
+Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug, const QQuickEventPoint *);
+//Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug, const QQuickEventTouchPoint *); TODO maybe
+
QT_END_NAMESPACE
QML_DECLARE_TYPE(QQuickKeyEvent)
QML_DECLARE_TYPE(QQuickMouseEvent)
QML_DECLARE_TYPE(QQuickWheelEvent)
+QML_DECLARE_TYPE(QQuickCloseEvent)
+QML_DECLARE_TYPE(QQuickPointerDevice)
+QML_DECLARE_TYPE(QPointerUniqueId)
+QML_DECLARE_TYPE(QQuickPointerEvent)
#endif // QQUICKEVENTS_P_P_H
diff --git a/src/quick/items/qquickflickable.cpp b/src/quick/items/qquickflickable.cpp
index 49117d27d1..12b01b8aaf 100644
--- a/src/quick/items/qquickflickable.cpp
+++ b/src/quick/items/qquickflickable.cpp
@@ -55,6 +55,8 @@
#include <QtCore/qmath.h>
#include "qplatformdefs.h"
+#include <cmath>
+
QT_BEGIN_NAMESPACE
// FlickThreshold determines how far the "mouse" must have moved
@@ -69,6 +71,21 @@ static const int RetainGrabVelocity = 100;
static const int MovementEndingTimerInterval = 100;
#endif
+// Currently std::round can't be used on Android when using ndk g++, so
+// use C version instead. We could just define two versions of Round, one
+// for float and one for double, but then only one of them would be used
+// and compiler would trigger a warning about unused function.
+//
+// See https://code.google.com/p/android/issues/detail?id=54418
+template<typename T>
+static T Round(T t) {
+ return round(t);
+}
+template<>
+Q_DECL_UNUSED float Round<float>(float f) {
+ return roundf(f);
+}
+
static qreal EaseOvershoot(qreal t) {
return qAtan(t);
}
@@ -292,14 +309,14 @@ void QQuickFlickablePrivate::AxisData::updateVelocity()
}
}
-void QQuickFlickablePrivate::itemGeometryChanged(QQuickItem *item, const QRectF &newGeom, const QRectF &oldGeom)
+void QQuickFlickablePrivate::itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change, const QRectF &)
{
Q_Q(QQuickFlickable);
if (item == contentItem) {
Qt::Orientations orient = 0;
- if (newGeom.x() != oldGeom.x())
+ if (change.xChange())
orient |= Qt::Horizontal;
- if (newGeom.y() != oldGeom.y())
+ if (change.yChange())
orient |= Qt::Vertical;
if (orient)
q->viewportMoved(orient);
@@ -351,7 +368,7 @@ bool QQuickFlickablePrivate::flick(AxisData &data, qreal minExtent, qreal maxExt
qreal dist = v2 / (accel * 2.0);
if (v > 0)
dist = -dist;
- qreal target = -qRound(-(data.move.value() - dist));
+ qreal target = -Round(-(data.move.value() - dist));
dist = -target + data.move.value();
accel = v2 / (2.0f * qAbs(dist));
@@ -455,18 +472,18 @@ void QQuickFlickablePrivate::fixup(AxisData &data, qreal minExtent, qreal maxExt
} else if (data.move.value() <= maxExtent) {
resetTimeline(data);
adjustContentPos(data, maxExtent);
- } else if (-qRound(-data.move.value()) != data.move.value()) {
+ } else if (-Round(-data.move.value()) != data.move.value()) {
// We could animate, but since it is less than 0.5 pixel it's probably not worthwhile.
resetTimeline(data);
qreal val = data.move.value();
- if (qAbs(-qRound(-val) - val) < 0.25) // round small differences
- val = -qRound(-val);
+ if (std::abs(-Round(-val) - val) < 0.25) // round small differences
+ val = -Round(-val);
else if (data.smoothVelocity.value() > 0) // continue direction of motion for larger
- val = -qFloor(-val);
+ val = -std::floor(-val);
else if (data.smoothVelocity.value() < 0)
- val = -qCeil(-val);
+ val = -std::ceil(-val);
else // otherwise round
- val = -qRound(-val);
+ val = -Round(-val);
timeline.set(data.move, val);
}
data.inOvershoot = false;
@@ -1553,12 +1570,12 @@ void QQuickFlickablePrivate::replayDelayedPress()
//XXX pixelAligned ignores the global position of the Flickable, i.e. assumes Flickable itself is pixel aligned.
void QQuickFlickablePrivate::setViewportX(qreal x)
{
- contentItem->setX(pixelAligned ? -qRound(-x) : x);
+ contentItem->setX(pixelAligned ? -Round(-x) : x);
}
void QQuickFlickablePrivate::setViewportY(qreal y)
{
- contentItem->setY(pixelAligned ? -qRound(-y) : y);
+ contentItem->setY(pixelAligned ? -Round(-y) : y);
}
void QQuickFlickable::timerEvent(QTimerEvent *event)
@@ -2219,21 +2236,28 @@ void QQuickFlickablePrivate::cancelInteraction()
}
}
-bool QQuickFlickable::sendMouseEvent(QQuickItem *item, QMouseEvent *event)
+/*!
+ QQuickFlickable::filterMouseEvent checks filtered mouse events and potentially steals them.
+
+ This is how flickable takes over events from other items (\a receiver) that are on top of it.
+ It filters their events and may take over (grab) the \a event.
+ Return true if the mouse event will be stolen.
+ \internal
+*/
+bool QQuickFlickable::filterMouseEvent(QQuickItem *receiver, QMouseEvent *event)
{
Q_D(QQuickFlickable);
QPointF localPos = mapFromScene(event->windowPos());
- QQuickWindow *c = window();
- QQuickItem *grabber = c ? c->mouseGrabberItem() : 0;
- if (grabber == this && d->stealMouse) {
+ Q_ASSERT_X(receiver != this, "", "Flickable received a filter event for itself");
+ if (receiver == this && d->stealMouse) {
// we are already the grabber and we do want the mouse event to ourselves.
return true;
}
- bool grabberDisabled = grabber && !grabber->isEnabled();
+ bool receiverDisabled = receiver && !receiver->isEnabled();
bool stealThisEvent = d->stealMouse;
- if ((stealThisEvent || contains(localPos)) && (!grabber || !grabber->keepMouseGrab() || grabberDisabled)) {
+ if ((stealThisEvent || contains(localPos)) && (!receiver || !receiver->keepMouseGrab() || receiverDisabled)) {
QScopedPointer<QMouseEvent> mouseEvent(QQuickWindowPrivate::cloneMouseEvent(event, &localPos));
mouseEvent->setAccepted(false);
@@ -2243,7 +2267,7 @@ bool QQuickFlickable::sendMouseEvent(QQuickItem *item, QMouseEvent *event)
break;
case QEvent::MouseButtonPress:
d->handleMousePressEvent(mouseEvent.data());
- d->captureDelayedPress(item, event);
+ d->captureDelayedPress(receiver, event);
stealThisEvent = d->stealMouse; // Update stealThisEvent in case changed by function call above
break;
case QEvent::MouseButtonRelease:
@@ -2253,15 +2277,14 @@ bool QQuickFlickable::sendMouseEvent(QQuickItem *item, QMouseEvent *event)
default:
break;
}
- grabber = qobject_cast<QQuickItem*>(c->mouseGrabberItem());
- if ((grabber && stealThisEvent && !grabber->keepMouseGrab() && grabber != this) || grabberDisabled) {
+ if ((receiver && stealThisEvent && !receiver->keepMouseGrab() && receiver != this) || receiverDisabled) {
d->clearDelayedPress();
grabMouse();
} else if (d->delayedPressEvent) {
grabMouse();
}
- const bool filtered = stealThisEvent || d->delayedPressEvent || grabberDisabled;
+ const bool filtered = stealThisEvent || d->delayedPressEvent || receiverDisabled;
if (filtered) {
event->setAccepted(true);
}
@@ -2270,7 +2293,7 @@ bool QQuickFlickable::sendMouseEvent(QQuickItem *item, QMouseEvent *event)
d->lastPosTime = -1;
returnToBounds();
}
- if (event->type() == QEvent::MouseButtonRelease || (grabber && grabber->keepMouseGrab() && !grabberDisabled)) {
+ if (event->type() == QEvent::MouseButtonRelease || (receiver && receiver->keepMouseGrab() && !receiverDisabled)) {
// mouse released, or another item has claimed the grab
d->lastPosTime = -1;
d->clearDelayedPress();
@@ -2290,7 +2313,7 @@ bool QQuickFlickable::childMouseEventFilter(QQuickItem *i, QEvent *e)
case QEvent::MouseButtonPress:
case QEvent::MouseMove:
case QEvent::MouseButtonRelease:
- return sendMouseEvent(i, static_cast<QMouseEvent *>(e));
+ return filterMouseEvent(i, static_cast<QMouseEvent *>(e));
case QEvent::UngrabMouse:
if (d->window && d->window->mouseGrabberItem() && d->window->mouseGrabberItem() != this) {
// The grab has been taken away from a child and given to some other item.
diff --git a/src/quick/items/qquickflickable_p.h b/src/quick/items/qquickflickable_p.h
index 318b8ce473..610bfd1427 100644
--- a/src/quick/items/qquickflickable_p.h
+++ b/src/quick/items/qquickflickable_p.h
@@ -275,7 +275,7 @@ protected:
void geometryChanged(const QRectF &newGeometry,
const QRectF &oldGeometry) Q_DECL_OVERRIDE;
void mouseUngrabEvent() Q_DECL_OVERRIDE;
- bool sendMouseEvent(QQuickItem *item, QMouseEvent *event);
+ bool filterMouseEvent(QQuickItem *receiver, QMouseEvent *event);
bool xflick() const;
bool yflick() const;
diff --git a/src/quick/items/qquickflickable_p_p.h b/src/quick/items/qquickflickable_p_p.h
index 3c59b19ec2..ac1e39d829 100644
--- a/src/quick/items/qquickflickable_p_p.h
+++ b/src/quick/items/qquickflickable_p_p.h
@@ -62,6 +62,7 @@
#include <private/qquicktimeline_p_p.h>
#include <private/qquickanimation_p_p.h>
#include <private/qquicktransitionmanager_p_p.h>
+#include <private/qpodvector_p.h>
QT_BEGIN_NAMESPACE
@@ -193,7 +194,7 @@ public:
qreal overShootDistance(qreal size);
- void itemGeometryChanged(QQuickItem *, const QRectF &, const QRectF &) Q_DECL_OVERRIDE;
+ void itemGeometryChanged(QQuickItem *, QQuickGeometryChange, const QRectF &) Q_DECL_OVERRIDE;
void draggingStarting();
void draggingEnding();
diff --git a/src/quick/items/qquickflipable_p.h b/src/quick/items/qquickflipable_p.h
index 189d94775a..17a74d1f7a 100644
--- a/src/quick/items/qquickflipable_p.h
+++ b/src/quick/items/qquickflipable_p.h
@@ -51,6 +51,10 @@
// We mean it.
//
+#include <private/qtquickglobal_p.h>
+
+QT_REQUIRE_CONFIG(quick_flipable);
+
#include "qquickitem.h"
#include <QtGui/qtransform.h>
diff --git a/src/quick/items/qquickframebufferobject.cpp b/src/quick/items/qquickframebufferobject.cpp
index 0356b72e1d..18a6a58467 100644
--- a/src/quick/items/qquickframebufferobject.cpp
+++ b/src/quick/items/qquickframebufferobject.cpp
@@ -40,10 +40,13 @@
#include "qquickframebufferobject.h"
#include <QtGui/QOpenGLFramebufferObject>
-
+#include <QtGui/QOpenGLFunctions>
#include <private/qquickitem_p.h>
+#include <private/qsgadaptationlayer_p.h>
+#include <qsgtextureprovider.h>
#include <QSGSimpleTextureNode>
+#include <QSGRendererInterface>
QT_BEGIN_NAMESPACE
@@ -260,6 +263,12 @@ public:
int devicePixelRatio;
};
+static inline bool isOpenGL(QSGRenderContext *rc)
+{
+ QSGRendererInterface *rif = rc->sceneGraphContext()->rendererInterface(rc);
+ return !rif || rif->graphicsApi() == QSGRendererInterface::OpenGL;
+}
+
/*!
* \internal
*/
@@ -278,6 +287,8 @@ QSGNode *QQuickFramebufferObject::updatePaintNode(QSGNode *node, UpdatePaintNode
Q_D(QQuickFramebufferObject);
if (!n) {
+ if (!isOpenGL(d->sceneGraphRenderContext()))
+ return 0;
if (!d->node)
d->node = new QSGFramebufferObjectNode;
n = d->node;
@@ -360,6 +371,8 @@ QSGTextureProvider *QQuickFramebufferObject::textureProvider() const
qWarning("QQuickFramebufferObject::textureProvider: can only be queried on the rendering thread of an exposed window");
return 0;
}
+ if (!isOpenGL(d->sceneGraphRenderContext()))
+ return 0;
if (!d->node)
d->node = new QSGFramebufferObjectNode;
return d->node;
diff --git a/src/quick/items/qquickgenericshadereffect.cpp b/src/quick/items/qquickgenericshadereffect.cpp
new file mode 100644
index 0000000000..9714f39663
--- /dev/null
+++ b/src/quick/items/qquickgenericshadereffect.cpp
@@ -0,0 +1,658 @@
+/****************************************************************************
+**
+** 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_fragNeedsUpdate(true)
+ , m_vertNeedsUpdate(true)
+ , m_dirty(0)
+{
+ qRegisterMetaType<QSGGuiThreadShaderEffectManager::ShaderInfo::Type>("ShaderInfo::Type");
+ for (int i = 0; i < NShader; ++i)
+ m_inProgress[i] = nullptr;
+}
+
+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_fragNeedsUpdate = true;
+ if (m_item->isComponentComplete())
+ maybeUpdateShaders();
+
+ emit m_item->fragmentShaderChanged();
+}
+
+void QQuickGenericShaderEffect::setVertexShader(const QByteArray &src)
+{
+ if (m_vertShader == src)
+ return;
+
+ m_vertShader = src;
+
+ m_vertNeedsUpdate = true;
+ if (m_item->isComponentComplete())
+ maybeUpdateShaders();
+
+ 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::parseLog()
+{
+ maybeUpdateShaders();
+ return log();
+}
+
+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());
+}
+
+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;
+ }
+
+ // Do not change anything while a new shader is being reflected or compiled.
+ if (m_inProgress[Vertex] || m_inProgress[Fragment])
+ return node;
+
+ // 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::maybeUpdateShaders()
+{
+ if (m_vertNeedsUpdate)
+ m_vertNeedsUpdate = !updateShader(Vertex, m_vertShader);
+ if (m_fragNeedsUpdate)
+ m_fragNeedsUpdate = !updateShader(Fragment, m_fragShader);
+ if (m_vertNeedsUpdate || m_fragNeedsUpdate) {
+ // This function is invoked either from componentComplete or in a
+ // response to a previous invocation's polish() request. If this is
+ // case #1 then updateShader can fail due to not having a window or
+ // scenegraph ready. Schedule the polish to try again later. In case #2
+ // the backend probably does not have shadereffect support so there is
+ // nothing to do for us here.
+ if (!m_item->window() || !m_item->window()->isSceneGraphInitialized())
+ m_item->polish();
+ }
+}
+
+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;
+ QQuickWindow *w = m_item->window();
+ if (w) { // note: just the window, don't care about isSceneGraphInitialized() here
+ 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()));
+ connect(m_mgr, &QSGGuiThreadShaderEffectManager::shaderCodePrepared, this, &QQuickGenericShaderEffect::shaderCodePrepared);
+ }
+ }
+ }
+
+ return m_mgr;
+}
+
+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 ShaderInfoCache
+{
+ bool contains(const QByteArray &key) const
+ {
+ return m_shaderInfoCache.contains(key);
+ }
+
+ QSGGuiThreadShaderEffectManager::ShaderInfo value(const QByteArray &key) const
+ {
+ return m_shaderInfoCache.value(key);
+ }
+
+ void insert(const QByteArray &key, const QSGGuiThreadShaderEffectManager::ShaderInfo &value)
+ {
+ m_shaderInfoCache.insert(key, value);
+ }
+
+ QHash<QByteArray, QSGGuiThreadShaderEffectManager::ShaderInfo> m_shaderInfoCache;
+};
+
+Q_GLOBAL_STATIC(ShaderInfoCache, shaderInfoCache)
+
+bool QQuickGenericShaderEffect::updateShader(Shader shaderType, const QByteArray &src)
+{
+ QSGGuiThreadShaderEffectManager *mgr = shaderEffectManager();
+ if (!mgr)
+ return false;
+
+ const bool texturesSeparate = mgr->hasSeparateSamplerAndTextureObjects();
+
+ disconnectSignals(shaderType);
+
+ m_shaders[shaderType].shaderInfo = QSGGuiThreadShaderEffectManager::ShaderInfo();
+ m_shaders[shaderType].varData.clear();
+
+ if (!src.isEmpty()) {
+ if (shaderInfoCache()->contains(src)) {
+ m_shaders[shaderType].shaderInfo = shaderInfoCache()->value(src);
+ m_shaders[shaderType].hasShaderCode = true;
+ } else {
+ // Each prepareShaderCode call needs its own work area, hence the
+ // dynamic alloc. If there are calls in progress, let those run to
+ // finish, their results can then simply be ignored because
+ // m_inProgress indicates what we care about.
+ m_inProgress[shaderType] = new QSGGuiThreadShaderEffectManager::ShaderInfo;
+ const QSGGuiThreadShaderEffectManager::ShaderInfo::Type typeHint =
+ shaderType == Vertex ? QSGGuiThreadShaderEffectManager::ShaderInfo::TypeVertex
+ : QSGGuiThreadShaderEffectManager::ShaderInfo::TypeFragment;
+ // 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. Some backends may choose to do
+ // source->bytecode compilation as well in this step.
+ mgr->prepareShaderCode(typeHint, src, m_inProgress[shaderType]);
+ // the rest is handled in shaderCodePrepared()
+ return 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);
+ }
+ }
+
+ updateShaderVars(shaderType);
+ m_dirty |= QSGShaderEffectNode::DirtyShaders;
+ m_item->update();
+ return true;
+}
+
+void QQuickGenericShaderEffect::shaderCodePrepared(bool ok, QSGGuiThreadShaderEffectManager::ShaderInfo::Type typeHint,
+ const QByteArray &src, QSGGuiThreadShaderEffectManager::ShaderInfo *result)
+{
+ const Shader shaderType = typeHint == QSGGuiThreadShaderEffectManager::ShaderInfo::TypeVertex ? Vertex : Fragment;
+
+ // If another call was made to updateShader() for the same shader type in
+ // the meantime then our results are useless, just drop them.
+ if (result != m_inProgress[shaderType]) {
+ delete result;
+ return;
+ }
+
+ m_shaders[shaderType].shaderInfo = *result;
+ delete result;
+ m_inProgress[shaderType] = nullptr;
+
+ if (!ok) {
+ qWarning("ShaderEffect: shader preparation failed for %s\n%s\n", src.constData(), qPrintable(log()));
+ m_shaders[shaderType].hasShaderCode = false;
+ return;
+ }
+
+ m_shaders[shaderType].hasShaderCode = true;
+ shaderInfoCache()->insert(src, m_shaders[shaderType].shaderInfo);
+ updateShaderVars(shaderType);
+ m_dirty |= QSGShaderEffectNode::DirtyShaders;
+ m_item->update();
+}
+
+void QQuickGenericShaderEffect::updateShaderVars(Shader shaderType)
+{
+ QSGGuiThreadShaderEffectManager *mgr = shaderEffectManager();
+ if (!mgr)
+ return;
+
+ const bool texturesSeparate = mgr->hasSeparateSamplerAndTextureObjects();
+
+ 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..ab19816493
--- /dev/null
+++ b/src/quick/items/qquickgenericshadereffect_p.h
@@ -0,0 +1,153 @@
+/****************************************************************************
+**
+** 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);
+
+ 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);
+ void maybeUpdateShaders();
+
+private slots:
+ void propertyChanged(int mappedId);
+ void sourceDestroyed(QObject *object);
+ void markGeometryDirtyAndUpdate();
+ void markGeometryDirtyAndUpdateIfSupportsAtlas();
+ void shaderCodePrepared(bool ok, QSGGuiThreadShaderEffectManager::ShaderInfo::Type typeHint,
+ const QByteArray &src, QSGGuiThreadShaderEffectManager::ShaderInfo *result);
+
+private:
+ QSGGuiThreadShaderEffectManager *shaderEffectManager() const;
+
+ enum Shader {
+ Vertex,
+ Fragment,
+
+ NShader
+ };
+ bool updateShader(Shader shaderType, const QByteArray &src);
+ void updateShaderVars(Shader shaderType);
+ void disconnectSignals(Shader shaderType);
+ 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;
+ bool m_fragNeedsUpdate;
+ QByteArray m_vertShader;
+ bool m_vertNeedsUpdate;
+
+ QSGShaderEffectNode::ShaderData m_shaders[NShader];
+ QSGShaderEffectNode::DirtyShaderFlags m_dirty;
+ QSet<int> m_dirtyConstants[NShader];
+ QSet<int> m_dirtyTextures[NShader];
+ QSGGuiThreadShaderEffectManager::ShaderInfo *m_inProgress[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/qquickgraphicsinfo.cpp b/src/quick/items/qquickgraphicsinfo.cpp
new file mode 100644
index 0000000000..761d0c3cad
--- /dev/null
+++ b/src/quick/items/qquickgraphicsinfo.cpp
@@ -0,0 +1,303 @@
+/****************************************************************************
+**
+** 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 "qquickgraphicsinfo_p.h"
+#include "qquickwindow.h"
+#include "qquickitem.h"
+#include <QtGui/qopenglcontext.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \qmltype GraphicsInfo
+ \instantiates QQuickGraphicsInfo
+ \inqmlmodule QtQuick
+ \ingroup qtquick-visual
+ \since 5.8
+ \since QtQuick 2.8
+ \brief Provides information about the used Qt Quick backend
+
+ The GraphicsInfo 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.
+ */
+
+QQuickGraphicsInfo::QQuickGraphicsInfo(QQuickItem *item)
+ : QObject(item)
+ , m_window(0)
+ , m_api(Unknown)
+ , m_shaderType(UnknownShadingLanguage)
+ , m_shaderCompilationType(ShaderCompilationType(0))
+ , m_shaderSourceType(ShaderSourceType(0))
+ , m_majorVersion(2)
+ , m_minorVersion(0)
+ , m_profile(OpenGLNoProfile)
+ , m_renderableType(SurfaceFormatUnspecified)
+{
+ connect(item, SIGNAL(windowChanged(QQuickWindow*)), this, SLOT(setWindow(QQuickWindow*)));
+ setWindow(item->window());
+}
+
+QQuickGraphicsInfo *QQuickGraphicsInfo::qmlAttachedProperties(QObject *object)
+{
+ if (QQuickItem *item = qobject_cast<QQuickItem *>(object))
+ return new QQuickGraphicsInfo(item);
+
+ return nullptr;
+}
+
+/*!
+ \qmlproperty enumeration QtQuick::GraphicsInfo::api
+
+ This property describes the graphics API that is currently in use.
+
+ The possible values are:
+ \list
+ \li GraphicsInfo.Unknown - the default value when no active scenegraph is associated with the item
+ \li GraphicsInfo.Software - Qt Quick's software renderer based on QPainter with the raster paint engine
+ \li GraphicsInfo.OpenGL - OpenGL or OpenGL ES
+ \li GraphicsInfo.Direct3D12 - Direct3D 12
+ \endlist
+ */
+
+/*!
+ \qmlproperty enumeration QtQuick::GraphicsInfo::shaderType
+
+ This property contains the shading language supported by the Qt Quick
+ backend the application is using.
+
+ \list
+ \li GraphicsInfo.UnknownShadingLanguage - Not yet known due to no window and scenegraph associated
+ \li GraphicsInfo.GLSL - GLSL or GLSL ES
+ \li GraphicsInfo.HLSL - HLSL
+ \endlist
+
+ \note The value is only up-to-date once the item is associated with a
+ window. Bindings relying on the value have to keep this in mind since the
+ value may change from GraphicsInfo.UnknownShadingLanguage to the actual
+ value after component initialization is complete. This is particularly
+ relevant for ShaderEffect items inside ShaderEffectSource items set as
+ property values.
+
+ \since 5.8
+ \since QtQuick 2.8
+
+ \sa shaderCompilationType, shaderSourceType
+*/
+
+/*!
+ \qmlproperty enumeration QtQuick::GraphicsInfo::shaderCompilationType
+
+ This property contains a bitmask of the shader compilation approaches
+ supported by the Qt Quick backend the application is using.
+
+ \list
+ \li GraphicsInfo.RuntimeCompilation
+ \li GraphicsInfo.OfflineCompilation
+ \endlist
+
+ With OpenGL the value is GraphicsInfo.RuntimeCompilation, which corresponds
+ to the traditional way of using ShaderEffect. Non-OpenGL backends are
+ expected to focus more on GraphicsInfo.OfflineCompilation, however.
+
+ \note The value is only up-to-date once the item is associated with a
+ window. 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.
+
+ \since 5.8
+ \since QtQuick 2.8
+
+ \sa shaderType, shaderSourceType
+*/
+
+/*!
+ \qmlproperty enumeration QtQuick::GraphicsInfo::shaderSourceType
+
+ This property contains a bitmask of the supported ways of providing shader
+ sources.
+
+ \list
+ \li GraphicsInfo.ShaderSourceString
+ \li GraphicsInfo.ShaderSourceFile
+ \li GraphicsInfo.ShaderByteCode
+ \endlist
+
+ With OpenGL the value is GraphicsInfo.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.
+
+ \note The value is only up-to-date once the item is associated with a
+ window. 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.
+
+ \since 5.8
+ \since QtQuick 2.8
+
+ \sa shaderType, shaderCompilationType
+*/
+
+/*!
+ \qmlproperty int QtQuick::GraphicsInfo::majorVersion
+
+ This property holds the major version of the graphics API in use.
+
+ With OpenGL the default version is \c 2.0.
+
+ \sa minorVersion, profile
+ */
+
+/*!
+ \qmlproperty int QtQuick::GraphicsInfo::minorVersion
+
+ This property holds the minor version of the graphics API in use.
+
+ With OpenGL the default version is \c 2.0.
+
+ \sa majorVersion, profile
+ */
+
+/*!
+ \qmlproperty enumeration QtQuick::GraphicsInfo::profile
+
+ This property holds the configured OpenGL context profile.
+
+ The possible values are:
+ \list
+ \li GraphicsInfo.OpenGLNoProfile (default) - OpenGL version is lower than 3.2 or OpenGL is not in use.
+ \li GraphicsInfo.OpenGLCoreProfile - Functionality deprecated in OpenGL version 3.0 is not available.
+ \li GraphicsInfo.OpenGLCompatibilityProfile - Functionality from earlier OpenGL versions is available.
+ \endlist
+
+ Reusable QML components will typically use this property in bindings in order to
+ choose between core and non core profile compatible shader sources.
+
+ \sa majorVersion, minorVersion, QSurfaceFormat
+ */
+
+/*!
+ \qmlproperty enumeration QtQuick::GraphicsInfo::renderableType
+
+ This property holds the renderable type. The value has no meaning for APIs
+ other than OpenGL.
+
+ The possible values are:
+ \list
+ \li GraphicsInfo.SurfaceFormatUnspecified (default) - Unspecified rendering method
+ \li GraphicsInfo.SurfaceFormatOpenGL - Desktop OpenGL or other graphics API
+ \li GraphicsInfo.SurfaceFormatOpenGLES - OpenGL ES
+ \endlist
+
+ \sa QSurfaceFormat
+ */
+
+void QQuickGraphicsInfo::updateInfo()
+{
+ // The queries via the RIF do not depend on isSceneGraphInitialized(), they only need a window.
+ if (m_window) {
+ QSGRendererInterface *rif = m_window->rendererInterface();
+ if (rif) {
+ GraphicsApi newAPI = GraphicsApi(rif->graphicsApi());
+ if (m_api != newAPI) {
+ m_api = newAPI;
+ emit apiChanged();
+ m_shaderType = ShaderType(rif->shaderType());
+ emit shaderTypeChanged();
+ m_shaderCompilationType = ShaderCompilationType(int(rif->shaderCompilationType()));
+ emit shaderCompilationTypeChanged();
+ m_shaderSourceType = ShaderSourceType(int(rif->shaderSourceType()));
+ emit shaderSourceTypeChanged();
+ }
+ }
+ }
+
+ QSurfaceFormat format = QSurfaceFormat::defaultFormat();
+#ifndef QT_NO_OPENGL
+ if (m_window && m_window->isSceneGraphInitialized()) {
+ QOpenGLContext *context = m_window->openglContext();
+ if (context)
+ format = context->format();
+ }
+#endif
+ if (m_majorVersion != format.majorVersion()) {
+ m_majorVersion = format.majorVersion();
+ emit majorVersionChanged();
+ }
+ if (m_minorVersion != format.minorVersion()) {
+ m_minorVersion = format.minorVersion();
+ emit minorVersionChanged();
+ }
+ OpenGLContextProfile profile = static_cast<OpenGLContextProfile>(format.profile());
+ if (m_profile != profile) {
+ m_profile = profile;
+ emit profileChanged();
+ }
+ RenderableType renderableType = static_cast<RenderableType>(format.renderableType());
+ if (m_renderableType != renderableType) {
+ m_renderableType = renderableType;
+ emit renderableTypeChanged();
+ }
+}
+
+void QQuickGraphicsInfo::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/qquickgraphicsinfo_p.h b/src/quick/items/qquickgraphicsinfo_p.h
new file mode 100644
index 0000000000..9ef7bacb3e
--- /dev/null
+++ b/src/quick/items/qquickgraphicsinfo_p.h
@@ -0,0 +1,166 @@
+/****************************************************************************
+**
+** 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 QQUICKGRAPHICSINFO_P_H
+#define QQUICKGRAPHICSINFO_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>
+#include <QtGui/qsurfaceformat.h>
+#include <QtQuick/qsgrendererinterface.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQuickItem;
+class QQuickWindow;
+
+class QQuickGraphicsInfo : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(GraphicsApi api READ api NOTIFY apiChanged FINAL)
+ Q_PROPERTY(ShaderType shaderType READ shaderType NOTIFY shaderTypeChanged FINAL)
+ Q_PROPERTY(ShaderCompilationType shaderCompilationType READ shaderCompilationType NOTIFY shaderCompilationTypeChanged FINAL)
+ Q_PROPERTY(ShaderSourceType shaderSourceType READ shaderSourceType NOTIFY shaderSourceTypeChanged FINAL)
+
+ Q_PROPERTY(int majorVersion READ majorVersion NOTIFY majorVersionChanged FINAL)
+ Q_PROPERTY(int minorVersion READ minorVersion NOTIFY minorVersionChanged FINAL)
+ Q_PROPERTY(OpenGLContextProfile profile READ profile NOTIFY profileChanged FINAL)
+ Q_PROPERTY(RenderableType renderableType READ renderableType NOTIFY renderableTypeChanged FINAL)
+
+public:
+ enum GraphicsApi {
+ Unknown = QSGRendererInterface::Unknown,
+ Software = QSGRendererInterface::Software,
+ OpenGL = QSGRendererInterface::OpenGL,
+ Direct3D12 = QSGRendererInterface::Direct3D12
+ };
+ Q_ENUM(GraphicsApi)
+
+ enum ShaderType {
+ UnknownShadingLanguage = QSGRendererInterface::UnknownShadingLanguage,
+ GLSL = QSGRendererInterface::GLSL,
+ HLSL = QSGRendererInterface::HLSL
+ };
+ Q_ENUM(ShaderType)
+
+ enum ShaderCompilationType {
+ RuntimeCompilation = QSGRendererInterface::RuntimeCompilation,
+ OfflineCompilation = QSGRendererInterface::OfflineCompilation
+ };
+ Q_ENUM(ShaderCompilationType)
+
+ enum ShaderSourceType {
+ ShaderSourceString = QSGRendererInterface::ShaderSourceString,
+ ShaderSourceFile = QSGRendererInterface::ShaderSourceFile,
+ ShaderByteCode = QSGRendererInterface::ShaderByteCode
+ };
+ Q_ENUM(ShaderSourceType)
+
+ enum OpenGLContextProfile {
+ OpenGLNoProfile = QSurfaceFormat::NoProfile,
+ OpenGLCoreProfile = QSurfaceFormat::CoreProfile,
+ OpenGLCompatibilityProfile = QSurfaceFormat::CompatibilityProfile
+ };
+ Q_ENUM(OpenGLContextProfile)
+
+ enum RenderableType {
+ SurfaceFormatUnspecified = QSurfaceFormat::DefaultRenderableType,
+ SurfaceFormatOpenGL = QSurfaceFormat::OpenGL,
+ SurfaceFormatOpenGLES = QSurfaceFormat::OpenGLES
+ };
+ Q_ENUM(RenderableType)
+
+ QQuickGraphicsInfo(QQuickItem *item = 0);
+
+ static QQuickGraphicsInfo *qmlAttachedProperties(QObject *object);
+
+ GraphicsApi api() const { return m_api; }
+ ShaderType shaderType() const { return m_shaderType; }
+ ShaderCompilationType shaderCompilationType() const { return m_shaderCompilationType; }
+ ShaderSourceType shaderSourceType() const { return m_shaderSourceType; }
+
+ int majorVersion() const { return m_majorVersion; }
+ int minorVersion() const { return m_minorVersion; }
+ OpenGLContextProfile profile() const { return m_profile; }
+ RenderableType renderableType() const { return m_renderableType; }
+
+Q_SIGNALS:
+ void apiChanged();
+ void shaderTypeChanged();
+ void shaderCompilationTypeChanged();
+ void shaderSourceTypeChanged();
+
+ void majorVersionChanged();
+ void minorVersionChanged();
+ void profileChanged();
+ void renderableTypeChanged();
+
+private Q_SLOTS:
+ void updateInfo();
+ void setWindow(QQuickWindow *window);
+
+private:
+ QPointer<QQuickWindow> m_window;
+ GraphicsApi m_api;
+ ShaderType m_shaderType;
+ ShaderCompilationType m_shaderCompilationType;
+ ShaderSourceType m_shaderSourceType;
+ int m_majorVersion;
+ int m_minorVersion;
+ OpenGLContextProfile m_profile;
+ RenderableType m_renderableType;
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPEINFO(QQuickGraphicsInfo, QML_HAS_ATTACHED_PROPERTIES)
+
+#endif // QQUICKGRAPHICSINFO_P_H
diff --git a/src/quick/items/qquickgridview.cpp b/src/quick/items/qquickgridview.cpp
index fe33019d27..14ea43f123 100644
--- a/src/quick/items/qquickgridview.cpp
+++ b/src/quick/items/qquickgridview.cpp
@@ -478,7 +478,7 @@ bool QQuickGridViewPrivate::addVisibleItems(qreal fillFrom, qreal fillTo, qreal
qreal colPos = colPosAt(visibleIndex);
qreal rowPos = rowPosAt(visibleIndex);
if (visibleItems.count()) {
- FxGridItemSG *lastItem = static_cast<FxGridItemSG*>(visibleItems.last());
+ FxGridItemSG *lastItem = static_cast<FxGridItemSG*>(visibleItems.constLast());
rowPos = lastItem->rowPos();
int colNum = qFloor((lastItem->colPos()+colSize()/2) / colSize());
if (++colNum >= columns) {
@@ -536,7 +536,7 @@ bool QQuickGridViewPrivate::addVisibleItems(qreal fillFrom, qreal fillTo, qreal
// Find first column
if (visibleItems.count()) {
- FxGridItemSG *firstItem = static_cast<FxGridItemSG*>(visibleItems.first());
+ FxGridItemSG *firstItem = static_cast<FxGridItemSG*>(visibleItems.constFirst());
rowPos = firstItem->rowPos();
colNum = qFloor((firstItem->colPos()+colSize()/2) / colSize());
if (--colNum < 0) {
@@ -586,7 +586,7 @@ bool QQuickGridViewPrivate::removeNonVisibleItems(qreal bufferFrom, qreal buffer
bool changed = false;
while (visibleItems.count() > 1
- && (item = static_cast<FxGridItemSG*>(visibleItems.first()))
+ && (item = static_cast<FxGridItemSG*>(visibleItems.constFirst()))
&& item->rowPos()+rowSize()-1 < bufferFrom - rowSize()*(item->colPos()/colSize()+1)/(columns+1)) {
if (item->attached->delayRemove())
break;
@@ -598,7 +598,7 @@ bool QQuickGridViewPrivate::removeNonVisibleItems(qreal bufferFrom, qreal buffer
changed = true;
}
while (visibleItems.count() > 1
- && (item = static_cast<FxGridItemSG*>(visibleItems.last()))
+ && (item = static_cast<FxGridItemSG*>(visibleItems.constLast()))
&& item->rowPos() > bufferTo + rowSize()*(columns - item->colPos()/colSize())/(columns+1)) {
if (item->attached->delayRemove())
break;
@@ -623,7 +623,7 @@ void QQuickGridViewPrivate::layoutVisibleItems(int fromModelIndex)
const qreal from = isContentFlowReversed() ? -position()-displayMarginBeginning-size() : position()-displayMarginBeginning;
const qreal to = isContentFlowReversed() ? -position()+displayMarginEnd : position()+size()+displayMarginEnd;
- FxGridItemSG *firstItem = static_cast<FxGridItemSG*>(visibleItems.first());
+ FxGridItemSG *firstItem = static_cast<FxGridItemSG*>(visibleItems.constFirst());
qreal rowPos = firstItem->rowPos();
qreal colPos = firstItem->colPos();
int col = visibleIndex % columns;
@@ -679,7 +679,7 @@ void QQuickGridViewPrivate::repositionPackageItemAt(QQuickItem *item, int index)
void QQuickGridViewPrivate::resetFirstItemPosition(qreal pos)
{
- FxGridItemSG *item = static_cast<FxGridItemSG*>(visibleItems.first());
+ FxGridItemSG *item = static_cast<FxGridItemSG*>(visibleItems.constFirst());
item->setPosition(0, pos);
}
@@ -692,7 +692,7 @@ void QQuickGridViewPrivate::adjustFirstItem(qreal forwards, qreal backwards, int
if (moveCount == 0 && changeBeforeVisible != 0)
moveCount += (changeBeforeVisible % columns) - (columns - 1);
- FxGridItemSG *gridItem = static_cast<FxGridItemSG*>(visibleItems.first());
+ FxGridItemSG *gridItem = static_cast<FxGridItemSG*>(visibleItems.constFirst());
gridItem->setPosition(gridItem->colPos(), gridItem->rowPos() + ((moveCount / columns) * rowSize()));
}
@@ -2517,7 +2517,7 @@ void QQuickGridViewPrivate::translateAndTransitionItemsAfter(int afterModelIndex
int markerItemIndex = -1;
for (int i=0; i<visibleItems.count(); i++) {
- if (visibleItems[i]->index == afterModelIndex) {
+ if (visibleItems.at(i)->index == afterModelIndex) {
markerItemIndex = i;
break;
}
@@ -2536,7 +2536,7 @@ void QQuickGridViewPrivate::translateAndTransitionItemsAfter(int afterModelIndex
countItemsRemoved -= removalResult.countChangeAfterVisibleItems;
for (int i=markerItemIndex+1; i<visibleItems.count() && visibleItems.at(i)->position() < viewEndPos; i++) {
- FxGridItemSG *gridItem = static_cast<FxGridItemSG *>(visibleItems[i]);
+ FxGridItemSG *gridItem = static_cast<FxGridItemSG *>(visibleItems.at(i));
if (!gridItem->transitionScheduledOrRunning()) {
qreal origRowPos = gridItem->colPos();
qreal origColPos = gridItem->rowPos();
diff --git a/src/quick/items/qquickgridview_p.h b/src/quick/items/qquickgridview_p.h
index 44bc8444f2..aaf6e4a75b 100644
--- a/src/quick/items/qquickgridview_p.h
+++ b/src/quick/items/qquickgridview_p.h
@@ -51,8 +51,11 @@
// We mean it.
//
-#include "qquickitemview_p.h"
+#include <QtQuick/private/qtquickglobal_p.h>
+
+QT_REQUIRE_CONFIG(quick_gridview);
+#include "qquickitemview_p.h"
QT_BEGIN_NAMESPACE
diff --git a/src/quick/items/qquickimage.cpp b/src/quick/items/qquickimage.cpp
index a168b43fd1..e36c070248 100644
--- a/src/quick/items/qquickimage.cpp
+++ b/src/quick/items/qquickimage.cpp
@@ -614,10 +614,10 @@ QSGNode *QQuickImage::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
return 0;
}
- QSGImageNode *node = static_cast<QSGImageNode *>(oldNode);
+ QSGInternalImageNode *node = static_cast<QSGInternalImageNode *>(oldNode);
if (!node) {
d->pixmapChanged = true;
- node = d->sceneGraphContext()->createImageNode();
+ node = d->sceneGraphContext()->createInternalImageNode();
}
QRectF targetRect;
diff --git a/src/quick/items/qquickimagebase.cpp b/src/quick/items/qquickimagebase.cpp
index 60e31631c0..a2b99b6395 100644
--- a/src/quick/items/qquickimagebase.cpp
+++ b/src/quick/items/qquickimagebase.cpp
@@ -355,7 +355,7 @@ void QQuickImageBase::resolve2xLocalFile(const QUrl &url, qreal targetDevicePixe
if (disable2xImageLoading)
return;
- QString localFile = QQmlFile::urlToLocalFileOrQrc(url);
+ const QString localFile = QQmlFile::urlToLocalFileOrQrc(url);
// Non-local file path: @2x loading is not supported.
if (localFile.isEmpty())
diff --git a/src/quick/items/qquickimplicitsizeitem.cpp b/src/quick/items/qquickimplicitsizeitem.cpp
index 5dadf81ce4..08886329fd 100644
--- a/src/quick/items/qquickimplicitsizeitem.cpp
+++ b/src/quick/items/qquickimplicitsizeitem.cpp
@@ -42,6 +42,19 @@
QT_BEGIN_NAMESPACE
+/*!
+ \internal
+
+ The purpose of QQuickImplicitSizeItem is not immediately clear, as both
+ the implicit size properties and signals exist on QQuickItem. However,
+ for some items - where the implicit size has an underlying meaning (such as
+ Image, where the implicit size represents the real size of the image)
+ having implicit size writable is an undesirable thing.
+
+ QQuickImplicitSizeItem redefines the properties as being readonly.
+ Unfortunately, this also means they need to redefine the change signals.
+ See QTBUG-30258 for more information.
+*/
void QQuickImplicitSizeItemPrivate::implicitWidthChanged()
{
Q_Q(QQuickImplicitSizeItem);
diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp
index f371138ea3..e6069d692f 100644
--- a/src/quick/items/qquickitem.cpp
+++ b/src/quick/items/qquickitem.cpp
@@ -66,7 +66,6 @@
#include <private/qqmlopenmetaobject_p.h>
#include <QtQuick/private/qquickstate_p.h>
#include <private/qquickitem_p.h>
-#include <private/qqmlaccessors_p.h>
#include <QtQuick/private/qquickaccessibleattached_p.h>
#include <private/qv4engine_p.h>
@@ -85,9 +84,8 @@
QT_BEGIN_NAMESPACE
-#ifndef QT_NO_DEBUG
-static const bool qsg_leak_check = !qEnvironmentVariableIsEmpty("QML_LEAK_CHECK");
-#endif
+Q_DECLARE_LOGGING_CATEGORY(DBG_MOUSE_TARGET)
+Q_DECLARE_LOGGING_CATEGORY(DBG_HOVER_TRACE)
void debugFocusTree(QQuickItem *item, QQuickItem *scope = 0, int depth = 1)
{
@@ -99,7 +97,8 @@ void debugFocusTree(QQuickItem *item, QQuickItem *scope = 0, int depth = 1)
<< item->hasActiveFocus()
<< item->isFocusScope()
<< item;
- foreach (QQuickItem *child, item->childItems()) {
+ const auto childItems = item->childItems();
+ for (QQuickItem *child : childItems) {
debugFocusTree(
child,
item->isFocusScope() || !scope ? item : scope,
@@ -108,37 +107,6 @@ void debugFocusTree(QQuickItem *item, QQuickItem *scope = 0, int depth = 1)
}
}
-static void QQuickItem_parentNotifier(QObject *o, QQmlNotifier **n)
-{
- QQuickItemPrivate *d = QQuickItemPrivate::get(static_cast<QQuickItem *>(o));
- *n = &d->parentNotifier;
-}
-
-QML_PRIVATE_ACCESSOR(QQuickItem, QQuickItem *, parent, parentItem)
-QML_PRIVATE_ACCESSOR(QQuickItem, qreal, x, x)
-QML_PRIVATE_ACCESSOR(QQuickItem, qreal, y, y)
-QML_PRIVATE_ACCESSOR(QQuickItem, qreal, width, width)
-QML_PRIVATE_ACCESSOR(QQuickItem, qreal, height, height)
-
-static QQmlAccessors QQuickItem_parent = { QQuickItem_parentRead, QQuickItem_parentNotifier };
-static QQmlAccessors QQuickItem_x = { QQuickItem_xRead, 0 };
-static QQmlAccessors QQuickItem_y = { QQuickItem_yRead, 0 };
-static QQmlAccessors QQuickItem_width = { QQuickItem_widthRead, 0 };
-static QQmlAccessors QQuickItem_height = { QQuickItem_heightRead, 0 };
-
-QML_DECLARE_PROPERTIES(QQuickItem) {
- { QML_PROPERTY_NAME(parent), 0, &QQuickItem_parent },
- { QML_PROPERTY_NAME(x), 0, &QQuickItem_x },
- { QML_PROPERTY_NAME(y), 0, &QQuickItem_y },
- { QML_PROPERTY_NAME(width), 0, &QQuickItem_width },
- { QML_PROPERTY_NAME(height), 0, &QQuickItem_height }
-};
-
-void QQuickItemPrivate::registerAccessorProperties()
-{
- QML_DEFINE_PROPERTIES(QQuickItem);
-}
-
/*!
\qmltype Transform
\instantiates QQuickTransform
@@ -178,8 +146,8 @@ QQuickTransform::QQuickTransform(QQuickTransformPrivate &dd, QObject *parent)
QQuickTransform::~QQuickTransform()
{
Q_D(QQuickTransform);
- for (int ii = 0; ii < d->items.count(); ++ii) {
- QQuickItemPrivate *p = QQuickItemPrivate::get(d->items.at(ii));
+ for (QQuickItem *item : qAsConst(d->items)) {
+ QQuickItemPrivate *p = QQuickItemPrivate::get(item);
p->transforms.removeOne(this);
p->dirty(QQuickItemPrivate::Transform);
}
@@ -188,8 +156,8 @@ QQuickTransform::~QQuickTransform()
void QQuickTransform::update()
{
Q_D(QQuickTransform);
- for (int ii = 0; ii < d->items.count(); ++ii) {
- QQuickItemPrivate *p = QQuickItemPrivate::get(d->items.at(ii));
+ for (QQuickItem *item : qAsConst(d->items)) {
+ QQuickItemPrivate *p = QQuickItemPrivate::get(item);
p->dirty(QQuickItemPrivate::Transform);
}
}
@@ -201,9 +169,7 @@ QQuickContents::QQuickContents(QQuickItem *item)
QQuickContents::~QQuickContents()
{
- QList<QQuickItem *> children = m_item->childItems();
- for (int i = 0; i < children.count(); ++i) {
- QQuickItem *child = children.at(i);
+ for (QQuickItem *child : m_item->childItems()) {
QQuickItemPrivate::get(child)->removeItemChangeListener(this, QQuickItemPrivate::Geometry | QQuickItemPrivate::Destroyed);
}
}
@@ -226,9 +192,8 @@ bool QQuickContents::calcHeight(QQuickItem *changed)
} else {
qreal top = std::numeric_limits<qreal>::max();
qreal bottom = -std::numeric_limits<qreal>::max();
- QList<QQuickItem *> children = m_item->childItems();
- for (int i = 0; i < children.count(); ++i) {
- QQuickItem *child = children.at(i);
+ const QList<QQuickItem*> children = m_item->childItems();
+ for (QQuickItem *child : qAsConst(children)) {
qreal y = child->y();
if (y + child->height() > bottom)
bottom = y + child->height();
@@ -261,9 +226,8 @@ bool QQuickContents::calcWidth(QQuickItem *changed)
} else {
qreal left = std::numeric_limits<qreal>::max();
qreal right = -std::numeric_limits<qreal>::max();
- QList<QQuickItem *> children = m_item->childItems();
- for (int i = 0; i < children.count(); ++i) {
- QQuickItem *child = children.at(i);
+ const QList<QQuickItem*> children = m_item->childItems();
+ for (QQuickItem *child : qAsConst(children)) {
qreal x = child->x();
if (x + child->width() > right)
right = x + child->width();
@@ -282,9 +246,7 @@ void QQuickContents::complete()
{
QQuickItemPrivate::get(m_item)->addItemChangeListener(this, QQuickItemPrivate::Children);
- QList<QQuickItem *> children = m_item->childItems();
- for (int i = 0; i < children.count(); ++i) {
- QQuickItem *child = children.at(i);
+ for (QQuickItem *child : m_item->childItems()) {
QQuickItemPrivate::get(child)->addItemChangeListener(this, QQuickItemPrivate::Geometry | QQuickItemPrivate::Destroyed);
//###what about changes to visibility?
}
@@ -296,15 +258,15 @@ void QQuickContents::updateRect()
QQuickItemPrivate::get(m_item)->emitChildrenRectChanged(rectF());
}
-void QQuickContents::itemGeometryChanged(QQuickItem *changed, const QRectF &newGeometry, const QRectF &oldGeometry)
+void QQuickContents::itemGeometryChanged(QQuickItem *changed, QQuickGeometryChange change, const QRectF &)
{
Q_UNUSED(changed)
bool wChanged = false;
bool hChanged = false;
//### we can only pass changed if the left edge has moved left, or the right edge has moved right
- if (newGeometry.width() != oldGeometry.width() || newGeometry.x() != oldGeometry.x())
+ if (change.horizontalChange())
wChanged = calcWidth(/*changed*/);
- if (newGeometry.height() != oldGeometry.height() || newGeometry.y() != oldGeometry.y())
+ if (change.verticalChange())
hChanged = calcHeight(/*changed*/);
if (wChanged || hChanged)
updateRect();
@@ -1339,8 +1301,7 @@ void QQuickKeysAttached::componentComplete()
#ifndef QT_NO_IM
Q_D(QQuickKeysAttached);
if (d->item) {
- for (int ii = 0; ii < d->targets.count(); ++ii) {
- QQuickItem *targetItem = d->targets.at(ii);
+ for (QQuickItem *targetItem : qAsConst(d->targets)) {
if (targetItem && (targetItem->flags() & QQuickItem::ItemAcceptsInputMethod)) {
d->item->setFlag(QQuickItem::ItemAcceptsInputMethod);
break;
@@ -1362,11 +1323,10 @@ void QQuickKeysAttached::keyPressed(QKeyEvent *event, bool post)
// first process forwards
if (d->item && d->item->window()) {
d->inPress = true;
- for (int ii = 0; ii < d->targets.count(); ++ii) {
- QQuickItem *i = d->targets.at(ii);
- if (i && i->isVisible()) {
+ for (QQuickItem *targetItem : qAsConst(d->targets)) {
+ if (targetItem && targetItem->isVisible()) {
event->accept();
- QCoreApplication::sendEvent(i, event);
+ QCoreApplication::sendEvent(targetItem, event);
if (event->isAccepted()) {
d->inPress = false;
return;
@@ -1376,7 +1336,8 @@ void QQuickKeysAttached::keyPressed(QKeyEvent *event, bool post)
d->inPress = false;
}
- QQuickKeyEvent ke(*event);
+ QQuickKeyEvent &ke = d->theKeyEvent;
+ ke.reset(*event);
QByteArray keySignal = keyToSignal(event->key());
if (!keySignal.isEmpty()) {
keySignal += "(QQuickKeyEvent*)";
@@ -1405,11 +1366,10 @@ void QQuickKeysAttached::keyReleased(QKeyEvent *event, bool post)
if (d->item && d->item->window()) {
d->inRelease = true;
- for (int ii = 0; ii < d->targets.count(); ++ii) {
- QQuickItem *i = d->targets.at(ii);
- if (i && i->isVisible()) {
+ for (QQuickItem *targetItem : qAsConst(d->targets)) {
+ if (targetItem && targetItem->isVisible()) {
event->accept();
- QCoreApplication::sendEvent(i, event);
+ QCoreApplication::sendEvent(targetItem, event);
if (event->isAccepted()) {
d->inRelease = false;
return;
@@ -1419,7 +1379,8 @@ void QQuickKeysAttached::keyReleased(QKeyEvent *event, bool post)
d->inRelease = false;
}
- QQuickKeyEvent ke(*event);
+ QQuickKeyEvent &ke = d->theKeyEvent;
+ ke.reset(*event);
emit released(&ke);
event->setAccepted(ke.isAccepted());
@@ -1432,12 +1393,11 @@ void QQuickKeysAttached::inputMethodEvent(QInputMethodEvent *event, bool post)
Q_D(QQuickKeysAttached);
if (post == m_processPost && d->item && !d->inIM && d->item->window()) {
d->inIM = true;
- for (int ii = 0; ii < d->targets.count(); ++ii) {
- QQuickItem *i = d->targets.at(ii);
- if (i && i->isVisible() && (i->flags() & QQuickItem::ItemAcceptsInputMethod)) {
- d->item->window()->sendEvent(i, event);
+ for (QQuickItem *targetItem : qAsConst(d->targets)) {
+ if (targetItem && targetItem->isVisible() && (targetItem->flags() & QQuickItem::ItemAcceptsInputMethod)) {
+ QCoreApplication::sendEvent(targetItem, event);
if (event->isAccepted()) {
- d->imeItem = i;
+ d->imeItem = targetItem;
d->inIM = false;
return;
}
@@ -1452,13 +1412,12 @@ QVariant QQuickKeysAttached::inputMethodQuery(Qt::InputMethodQuery query) const
{
Q_D(const QQuickKeysAttached);
if (d->item) {
- for (int ii = 0; ii < d->targets.count(); ++ii) {
- QQuickItem *i = d->targets.at(ii);
- if (i && i->isVisible() && (i->flags() & QQuickItem::ItemAcceptsInputMethod) && i == d->imeItem) {
- //### how robust is i == d->imeItem check?
- QVariant v = i->inputMethodQuery(query);
+ for (QQuickItem *targetItem : qAsConst(d->targets)) {
+ if (targetItem && targetItem->isVisible() && (targetItem->flags() & QQuickItem::ItemAcceptsInputMethod) && targetItem == d->imeItem) {
+ //### how robust is targetItem == d->imeItem check?
+ QVariant v = targetItem->inputMethodQuery(query);
if (v.userType() == QVariant::RectF)
- v = d->item->mapRectFromItem(i, v.toRectF()); //### cost?
+ v = d->item->mapRectFromItem(targetItem, v.toRectF()); //### cost?
return v;
}
}
@@ -1491,6 +1450,9 @@ QQuickKeysAttached *QQuickKeysAttached::qmlAttachedProperties(QObject *obj)
behavior to all child items as well. If the \c LayoutMirroring attached property has not been defined
for an item, mirroring is not enabled.
+ \note Since Qt 5.8, \c LayoutMirroring can be attached to a \l Window. In practice, it is the same as
+ attaching \c LayoutMirroring to the window's \c contentItem.
+
The following example shows mirroring in action. The \l Row below is specified as being anchored
to the left of its parent. However, since mirroring has been enabled, the anchor is horizontally
reversed and it is now anchored to the right. Also, since items in a \l Row are positioned
@@ -1540,11 +1502,15 @@ QQuickKeysAttached *QQuickKeysAttached::qmlAttachedProperties(QObject *obj)
QQuickLayoutMirroringAttached::QQuickLayoutMirroringAttached(QObject *parent) : QObject(parent), itemPrivate(0)
{
- if (QQuickItem *item = qobject_cast<QQuickItem*>(parent)) {
+ if (QQuickItem *item = qobject_cast<QQuickItem *>(parent))
itemPrivate = QQuickItemPrivate::get(item);
+ else if (QQuickWindow *window = qobject_cast<QQuickWindow *>(parent))
+ itemPrivate = QQuickItemPrivate::get(window->contentItem());
+
+ if (itemPrivate)
itemPrivate->extra.value().layoutDirectionAttached = this;
- } else
- qmlInfo(parent) << tr("LayoutDirection attached property only works with Items");
+ else
+ qmlInfo(parent) << tr("LayoutDirection attached property only works with Items and Windows");
}
QQuickLayoutMirroringAttached * QQuickLayoutMirroringAttached::qmlAttachedProperties(QObject *object)
@@ -1615,11 +1581,9 @@ void QQuickItemPrivate::setImplicitLayoutMirror(bool mirror, bool inherit)
if (isMirrorImplicit)
setLayoutMirror(inherit ? inheritedLayoutMirror : false);
- for (int i = 0; i < childItems.count(); ++i) {
- if (QQuickItem *child = qmlobject_cast<QQuickItem *>(childItems.at(i))) {
- QQuickItemPrivate *childPrivate = QQuickItemPrivate::get(child);
- childPrivate->setImplicitLayoutMirror(inheritedLayoutMirror, inheritMirrorFromParent);
- }
+ for (QQuickItem *child : qAsConst(childItems)) {
+ QQuickItemPrivate *childPrivate = QQuickItemPrivate::get(child);
+ childPrivate->setImplicitLayoutMirror(inheritedLayoutMirror, inheritMirrorFromParent);
}
}
@@ -2326,29 +2290,11 @@ QQuickItem::QQuickItem(QQuickItemPrivate &dd, QQuickItem *parent)
d->init(parent);
}
-#ifndef QT_NO_DEBUG
-static int qt_item_count = 0;
-
-static void qt_print_item_count()
-{
- qDebug("Number of leaked items: %i", qt_item_count);
- qt_item_count = -1;
-}
-#endif
-
/*!
Destroys the QQuickItem.
*/
QQuickItem::~QQuickItem()
{
-#ifndef QT_NO_DEBUG
- if (qsg_leak_check) {
- --qt_item_count;
- if (qt_item_count < 0)
- qDebug("Item destroyed after qt_print_item_count() was called.");
- }
-#endif
-
Q_D(QQuickItem);
if (d->windowRefCount > 1)
@@ -2362,7 +2308,7 @@ QQuickItem::~QQuickItem()
while (!d->childItems.isEmpty())
d->childItems.constFirst()->setParentItem(0);
- const auto listeners = d->changeListeners;
+ const auto listeners = d->changeListeners; // NOTE: intentional copy (QTBUG-54732)
for (const QQuickItemPrivate::ChangeListener &change : listeners) {
QQuickAnchorsPrivate *anchor = change.listener->anchorPrivate();
if (anchor)
@@ -2391,15 +2337,16 @@ QQuickItem::~QQuickItem()
remove themselves from our list of transforms when that list has already
been destroyed after ~QQuickItem() has run.
*/
- for (int ii = 0; ii < d->transforms.count(); ++ii) {
- QQuickTransform *t = d->transforms.at(ii);
+ for (QQuickTransform *t : qAsConst(d->transforms)) {
QQuickTransformPrivate *tp = QQuickTransformPrivate::get(t);
tp->items.removeOne(this);
}
if (d->extra.isAllocated()) {
delete d->extra->contents; d->extra->contents = 0;
+#if QT_CONFIG(quick_shadereffect)
delete d->extra->layer; d->extra->layer = 0;
+#endif
}
delete d->_anchors; d->_anchors = 0;
@@ -2758,8 +2705,6 @@ void QQuickItem::setParentItem(QQuickItem *parentItem)
d->itemChange(ItemParentHasChanged, d->parentItem);
- d->parentNotifier.notify();
-
emit parentChanged(d->parentItem);
if (isVisible() && d->parentItem)
emit d->parentItem->visibleChildrenChanged();
@@ -2883,8 +2828,8 @@ QList<QQuickItem *> QQuickItemPrivate::paintOrderChildItems() const
// If none of the items have set Z then the paint order list is the same as
// the childItems list. This is by far the most common case.
bool haveZ = false;
- for (int i = 0; i < childItems.count(); ++i) {
- if (QQuickItemPrivate::get(childItems.at(i))->z() != 0.) {
+ for (QQuickItem *childItem : qAsConst(childItems)) {
+ if (QQuickItemPrivate::get(childItem)->z() != 0.) {
haveZ = true;
break;
}
@@ -2913,9 +2858,11 @@ void QQuickItemPrivate::addChild(QQuickItem *child)
// if the added child has a cursor and we do not currently have any children
// with cursors, bubble the notification up
- if (childPrivate->hasCursorInChild && !hasCursorInChild)
+ if (childPrivate->subtreeCursorEnabled && !subtreeCursorEnabled)
setHasCursorInChild(true);
#endif
+ if (childPrivate->subtreeHoverEnabled && !subtreeHoverEnabled)
+ setHasHoverInChild(true);
markSortedChildrenDirty(child);
dirty(QQuickItemPrivate::ChildrenChanged);
@@ -2938,9 +2885,11 @@ void QQuickItemPrivate::removeChild(QQuickItem *child)
QQuickItemPrivate *childPrivate = QQuickItemPrivate::get(child);
// turn it off, if nothing else is using it
- if (childPrivate->hasCursorInChild && hasCursorInChild)
+ if (childPrivate->subtreeCursorEnabled && subtreeCursorEnabled)
setHasCursorInChild(false);
#endif
+ if (childPrivate->subtreeHoverEnabled && subtreeHoverEnabled)
+ setHasHoverInChild(false);
markSortedChildrenDirty(child);
dirty(QQuickItemPrivate::ChildrenChanged);
@@ -2979,8 +2928,7 @@ void QQuickItemPrivate::refWindow(QQuickWindow *c)
if (!parentItem)
QQuickWindowPrivate::get(window)->parentlessItems.insert(q);
- for (int ii = 0; ii < childItems.count(); ++ii) {
- QQuickItem *child = childItems.at(ii);
+ for (QQuickItem *child : qAsConst(childItems)) {
QQuickItemPrivate::get(child)->refWindow(c);
}
@@ -3007,13 +2955,7 @@ void QQuickItemPrivate::derefWindow()
QQuickWindowPrivate *c = QQuickWindowPrivate::get(window);
if (polishScheduled)
c->itemsToPolish.removeOne(q);
- QMutableHashIterator<int, QQuickItem *> itemTouchMapIt(c->itemForTouchPointId);
- while (itemTouchMapIt.hasNext()) {
- if (itemTouchMapIt.next().value() == q)
- itemTouchMapIt.remove();
- }
- if (c->mouseGrabberItem == q)
- c->mouseGrabberItem = 0;
+ c->removeGrabber(q);
#ifndef QT_NO_CURSOR
if (c->cursorItem == q) {
c->cursorItem = 0;
@@ -3038,8 +2980,7 @@ void QQuickItemPrivate::derefWindow()
paintNode = 0;
- for (int ii = 0; ii < childItems.count(); ++ii) {
- QQuickItem *child = childItems.at(ii);
+ for (QQuickItem *child : qAsConst(childItems)) {
QQuickItemPrivate::get(child)->derefWindow();
}
@@ -3162,7 +3103,8 @@ QQuickItemPrivate::QQuickItemPrivate()
, isAccessible(false)
, culled(false)
, hasCursor(false)
- , hasCursorInChild(false)
+ , subtreeCursorEnabled(false)
+ , subtreeHoverEnabled(false)
, activeFocusOnTab(false)
, implicitAntialiasing(false)
, antialiasingValid(false)
@@ -3196,21 +3138,8 @@ QQuickItemPrivate::~QQuickItemPrivate()
void QQuickItemPrivate::init(QQuickItem *parent)
{
-#ifndef QT_NO_DEBUG
- if (qsg_leak_check) {
- ++qt_item_count;
- static bool atexit_registered = false;
- if (!atexit_registered) {
- atexit(qt_print_item_count);
- atexit_registered = true;
- }
- }
-#endif
-
Q_Q(QQuickItem);
- registerAccessorProperties();
-
baselineOffset = 0.0;
if (parent) {
@@ -3355,7 +3284,7 @@ void QQuickItemPrivate::resources_clear(QQmlListProperty<QObject> *prop)
QQuickItem *quickItem = static_cast<QQuickItem *>(prop->object);
QQuickItemPrivate *quickItemPrivate = QQuickItemPrivate::get(quickItem);
if (quickItemPrivate->extra.isAllocated()) {//If extra is not allocated resources is empty.
- foreach (QObject *object, quickItemPrivate->extra->resourcesList) {
+ for (QObject *object : qAsConst(quickItemPrivate->extra->resourcesList)) {
qmlobject_disconnect(object, QObject, SIGNAL(destroyed(QObject*)),
quickItem, QQuickItem, SLOT(_q_resourceObjectDeleted(QObject*)));
}
@@ -3496,8 +3425,7 @@ void QQuickItemPrivate::transform_clear(QQmlListProperty<QQuickTransform> *prop)
QQuickItem *that = static_cast<QQuickItem *>(prop->object);
QQuickItemPrivate *p = QQuickItemPrivate::get(that);
- for (int ii = 0; ii < p->transforms.count(); ++ii) {
- QQuickTransform *t = p->transforms.at(ii);
+ for (QQuickTransform *t : qAsConst(p->transforms)) {
QQuickTransformPrivate *tp = QQuickTransformPrivate::get(t);
tp->items.removeOne(that);
}
@@ -3621,7 +3549,7 @@ QQuickAnchors *QQuickItemPrivate::anchors() const
void QQuickItemPrivate::siblingOrderChanged()
{
Q_Q(QQuickItem);
- const auto listeners = changeListeners;
+ const auto listeners = changeListeners; // NOTE: intentional copy (QTBUG-54732)
for (const QQuickItemPrivate::ChangeListener &change : listeners) {
if (change.types & QQuickItemPrivate::SiblingOrder) {
change.listener->itemSiblingOrderChanged(q);
@@ -3725,32 +3653,31 @@ void QQuickItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeo
if (d->_anchors)
QQuickAnchorsPrivate::get(d->_anchors)->updateMe();
- bool xChange = (newGeometry.x() != oldGeometry.x());
- bool yChange = (newGeometry.y() != oldGeometry.y());
- bool widthChange = (newGeometry.width() != oldGeometry.width());
- bool heightChange = (newGeometry.height() != oldGeometry.height());
-
- const auto listeners = d->changeListeners;
- for (const QQuickItemPrivate::ChangeListener &change : listeners) {
- if (change.types & QQuickItemPrivate::Geometry) {
- if (change.gTypes == QQuickItemPrivate::GeometryChange) {
- change.listener->itemGeometryChanged(this, newGeometry, oldGeometry);
- } else if ((xChange && (change.gTypes & QQuickItemPrivate::XChange)) ||
- (yChange && (change.gTypes & QQuickItemPrivate::YChange)) ||
- (widthChange && (change.gTypes & QQuickItemPrivate::WidthChange)) ||
- (heightChange && (change.gTypes & QQuickItemPrivate::HeightChange))) {
- change.listener->itemGeometryChanged(this, newGeometry, oldGeometry);
- }
+ QQuickGeometryChange change;
+ QRectF diff(newGeometry.x() - oldGeometry.x(),
+ newGeometry.y() - oldGeometry.y(),
+ newGeometry.width() - oldGeometry.width(),
+ newGeometry.height() - oldGeometry.height());
+ change.setXChange(diff.x() != 0);
+ change.setYChange(diff.y() != 0);
+ change.setWidthChange(diff.width() != 0);
+ change.setHeightChange(diff.height() != 0);
+
+ const auto listeners = d->changeListeners; // NOTE: intentional copy (QTBUG-54732)
+ for (const QQuickItemPrivate::ChangeListener &listener : listeners) {
+ if (listener.types & QQuickItemPrivate::Geometry) {
+ if (change.matches(listener.gTypes))
+ listener.listener->itemGeometryChanged(this, change, diff);
}
}
- if (xChange)
+ if (change.xChange())
emit xChanged();
- if (yChange)
+ if (change.yChange())
emit yChanged();
- if (widthChange)
+ if (change.widthChange())
emit widthChanged();
- if (heightChange)
+ if (change.heightChange())
emit heightChanged();
}
@@ -3815,6 +3742,11 @@ QSGNode *QQuickItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *upda
return 0;
}
+QQuickItem::UpdatePaintNodeData::UpdatePaintNodeData()
+: transformNode(0)
+{
+}
+
/*!
This function is called when an item should release graphics
resources which are not already managed by the nodes returend from
@@ -3864,7 +3796,8 @@ void QQuickItemPrivate::removeItemChangeListener(QQuickItemChangeListener *liste
changeListeners.removeOne(change);
}
-void QQuickItemPrivate::updateOrAddGeometryChangeListener(QQuickItemChangeListener *listener, GeometryChangeTypes types)
+void QQuickItemPrivate::updateOrAddGeometryChangeListener(QQuickItemChangeListener *listener,
+ QQuickGeometryChange types)
{
ChangeListener change(listener, types);
int index = changeListeners.indexOf(change);
@@ -3875,10 +3808,10 @@ void QQuickItemPrivate::updateOrAddGeometryChangeListener(QQuickItemChangeListen
}
void QQuickItemPrivate::updateOrRemoveGeometryChangeListener(QQuickItemChangeListener *listener,
- GeometryChangeTypes types)
+ QQuickGeometryChange types)
{
ChangeListener change(listener, types);
- if (types == NoChange) {
+ if (types.noChange()) {
changeListeners.removeOne(change);
} else {
int index = changeListeners.indexOf(change);
@@ -4147,7 +4080,8 @@ bool QQuickItem::childMouseEventFilter(QQuickItem *item, QEvent *event)
*/
void QQuickItem::windowDeactivateEvent()
{
- foreach (QQuickItem* item, childItems()) {
+ const auto children = childItems();
+ for (QQuickItem* item : children) {
item->windowDeactivateEvent();
}
}
@@ -4290,7 +4224,7 @@ void QQuickItem::setBaselineOffset(qreal offset)
d->baselineOffset = offset;
- const auto listeners = d->changeListeners;
+ const auto listeners = d->changeListeners; // NOTE: intentional copy (QTBUG-54732)
for (const QQuickItemPrivate::ChangeListener &change : listeners) {
if (change.types & QQuickItemPrivate::Geometry) {
QQuickAnchorsPrivate *anchor = change.listener->anchorPrivate();
@@ -4912,8 +4846,10 @@ void QQuickItem::classBegin()
d->_stateGroup->classBegin();
if (d->_anchors)
d->_anchors->classBegin();
+#if QT_CONFIG(quick_shadereffect)
if (d->extra.isAllocated() && d->extra->layer)
d->extra->layer->classBegin();
+#endif
}
/*!
@@ -4932,14 +4868,18 @@ void QQuickItem::componentComplete()
QQuickAnchorsPrivate::get(d->_anchors)->updateOnComplete();
}
- if (d->extra.isAllocated() && d->extra->layer)
- d->extra->layer->componentComplete();
+ if (d->extra.isAllocated()) {
+#if QT_CONFIG(quick_shadereffect)
+ if (d->extra->layer)
+ d->extra->layer->componentComplete();
+#endif
- if (d->extra.isAllocated() && d->extra->keyHandler)
- d->extra->keyHandler->componentComplete();
+ if (d->extra->keyHandler)
+ d->extra->keyHandler->componentComplete();
- if (d->extra.isAllocated() && d->extra->contents)
- d->extra->contents->complete();
+ if (d->extra->contents)
+ d->extra->contents->complete();
+ }
if (d->window && d->dirtyAttributes) {
d->addToDirtyList();
@@ -4988,8 +4928,10 @@ QPointF QQuickItemPrivate::computeTransformOrigin() const
void QQuickItemPrivate::transformChanged()
{
+#if QT_CONFIG(quick_shadereffect)
if (extra.isAllocated() && extra->layer)
extra->layer->updateMatrix();
+#endif
}
void QQuickItemPrivate::deliverKeyEvent(QKeyEvent *e)
@@ -5371,8 +5313,10 @@ void QQuickItem::setZ(qreal v)
emit zChanged();
+#if QT_CONFIG(quick_shadereffect)
if (d->extra.isAllocated() && d->extra->layer)
d->extra->layer->updateZ();
+#endif
}
/*!
@@ -5833,15 +5777,13 @@ bool QQuickItemPrivate::setEffectiveVisibleRecur(bool newEffectiveVisible)
if (window) {
QQuickWindowPrivate *windowPriv = QQuickWindowPrivate::get(window);
- if (windowPriv->mouseGrabberItem == q)
- q->ungrabMouse();
- if (!effectiveVisible)
- q->ungrabTouchPoints();
+ windowPriv->removeGrabber(q);
}
bool childVisibilityChanged = false;
- for (int ii = 0; ii < childItems.count(); ++ii)
- childVisibilityChanged |= QQuickItemPrivate::get(childItems.at(ii))->setEffectiveVisibleRecur(newEffectiveVisible);
+ for (QQuickItem *childItem : qAsConst(childItems)) {
+ childVisibilityChanged |= QQuickItemPrivate::get(childItem)->setEffectiveVisibleRecur(newEffectiveVisible);
+ }
itemChange(QQuickItem::ItemVisibleHasChanged, effectiveVisible);
#ifndef QT_NO_ACCESSIBILITY
@@ -5883,18 +5825,15 @@ void QQuickItemPrivate::setEffectiveEnableRecur(QQuickItem *scope, bool newEffec
if (window) {
QQuickWindowPrivate *windowPriv = QQuickWindowPrivate::get(window);
- if (windowPriv->mouseGrabberItem == q)
- q->ungrabMouse();
- if (!effectiveEnable)
- q->ungrabTouchPoints();
+ windowPriv->removeGrabber(q);
if (scope && !effectiveEnable && activeFocus) {
windowPriv->clearFocusInScope(
scope, q, Qt::OtherFocusReason, QQuickWindowPrivate::DontChangeFocusProperty | QQuickWindowPrivate::DontChangeSubFocusItem);
}
}
- for (int ii = 0; ii < childItems.count(); ++ii) {
- QQuickItemPrivate::get(childItems.at(ii))->setEffectiveEnableRecur(
+ for (QQuickItem *childItem : qAsConst(childItems)) {
+ QQuickItemPrivate::get(childItem)->setEffectiveEnableRecur(
(flags & QQuickItem::ItemIsFocusScope) && scope ? q : scope, newEffectiveEnable);
}
@@ -6038,7 +5977,7 @@ void QQuickItemPrivate::itemChange(QQuickItem::ItemChange change, const QQuickIt
switch (change) {
case QQuickItem::ItemChildAddedChange: {
q->itemChange(change, data);
- const auto listeners = changeListeners;
+ const auto listeners = changeListeners; // NOTE: intentional copy (QTBUG-54732)
for (const QQuickItemPrivate::ChangeListener &change : listeners) {
if (change.types & QQuickItemPrivate::Children) {
change.listener->itemChildAdded(q, data.item);
@@ -6048,7 +5987,7 @@ void QQuickItemPrivate::itemChange(QQuickItem::ItemChange change, const QQuickIt
}
case QQuickItem::ItemChildRemovedChange: {
q->itemChange(change, data);
- const auto listeners = changeListeners;
+ const auto listeners = changeListeners; // NOTE: intentional copy (QTBUG-54732)
for (const QQuickItemPrivate::ChangeListener &change : listeners) {
if (change.types & QQuickItemPrivate::Children) {
change.listener->itemChildRemoved(q, data.item);
@@ -6061,7 +6000,7 @@ void QQuickItemPrivate::itemChange(QQuickItem::ItemChange change, const QQuickIt
break;
case QQuickItem::ItemVisibleHasChanged: {
q->itemChange(change, data);
- const auto listeners = changeListeners;
+ const auto listeners = changeListeners; // NOTE: intentional copy (QTBUG-54732)
for (const QQuickItemPrivate::ChangeListener &change : listeners) {
if (change.types & QQuickItemPrivate::Visibility) {
change.listener->itemVisibilityChanged(q);
@@ -6071,7 +6010,7 @@ void QQuickItemPrivate::itemChange(QQuickItem::ItemChange change, const QQuickIt
}
case QQuickItem::ItemParentHasChanged: {
q->itemChange(change, data);
- const auto listeners = changeListeners;
+ const auto listeners = changeListeners; // NOTE: intentional copy (QTBUG-54732)
for (const QQuickItemPrivate::ChangeListener &change : listeners) {
if (change.types & QQuickItemPrivate::Parent) {
change.listener->itemParentChanged(q, data.item);
@@ -6081,7 +6020,7 @@ void QQuickItemPrivate::itemChange(QQuickItem::ItemChange change, const QQuickIt
}
case QQuickItem::ItemOpacityHasChanged: {
q->itemChange(change, data);
- const auto listeners = changeListeners;
+ const auto listeners = changeListeners; // NOTE: intentional copy (QTBUG-54732)
for (const QQuickItemPrivate::ChangeListener &change : listeners) {
if (change.types & QQuickItemPrivate::Opacity) {
change.listener->itemOpacityChanged(q);
@@ -6094,7 +6033,7 @@ void QQuickItemPrivate::itemChange(QQuickItem::ItemChange change, const QQuickIt
break;
case QQuickItem::ItemRotationHasChanged: {
q->itemChange(change, data);
- const auto listeners = changeListeners;
+ const auto listeners = changeListeners; // NOTE: intentional copy (QTBUG-54732)
for (const QQuickItemPrivate::ChangeListener &change : listeners) {
if (change.types & QQuickItemPrivate::Rotation) {
change.listener->itemRotationChanged(q);
@@ -6456,7 +6395,7 @@ void QQuickItem::resetWidth()
void QQuickItemPrivate::implicitWidthChanged()
{
Q_Q(QQuickItem);
- const auto listeners = changeListeners;
+ const auto listeners = changeListeners; // NOTE: intentional copy (QTBUG-54732)
for (const QQuickItemPrivate::ChangeListener &change : listeners) {
if (change.types & QQuickItemPrivate::ImplicitWidth) {
change.listener->itemImplicitWidthChanged(q);
@@ -6620,7 +6559,7 @@ void QQuickItem::resetHeight()
void QQuickItemPrivate::implicitHeightChanged()
{
Q_Q(QQuickItem);
- const auto listeners = changeListeners;
+ const auto listeners = changeListeners; // NOTE: intentional copy (QTBUG-54732)
for (const QQuickItemPrivate::ChangeListener &change : listeners) {
if (change.types & QQuickItemPrivate::ImplicitHeight) {
change.listener->itemImplicitHeightChanged(q);
@@ -7047,7 +6986,7 @@ void QQuickItem::setAcceptedMouseButtons(Qt::MouseButtons buttons)
}
/*!
- Returns whether mouse events of this item's children should be filtered
+ Returns whether mouse and touch events of this item's children should be filtered
through this item.
\sa setFiltersChildMouseEvents(), childMouseEventFilter()
@@ -7059,7 +6998,7 @@ bool QQuickItem::filtersChildMouseEvents() const
}
/*!
- Sets whether mouse events of this item's children should be filtered
+ Sets whether mouse and touch events of this item's children should be filtered
through this item.
If \a filter is true, childMouseEventFilter() will be called when
@@ -7110,6 +7049,7 @@ void QQuickItem::setAcceptHoverEvents(bool enabled)
{
Q_D(QQuickItem);
d->hoverEnabled = enabled;
+ d->setHasHoverInChild(enabled);
}
void QQuickItemPrivate::setHasCursorInChild(bool hasCursor)
@@ -7118,17 +7058,18 @@ void QQuickItemPrivate::setHasCursorInChild(bool hasCursor)
Q_Q(QQuickItem);
// if we're asked to turn it off (because of an unsetcursor call, or a node
- // removal) then we should check our children and make sure it's really ok
- // to turn it off.
- if (!hasCursor && hasCursorInChild) {
- foreach (QQuickItem *otherChild, childItems) {
+ // removal) then we should make sure it's really ok to turn it off.
+ if (!hasCursor && subtreeCursorEnabled) {
+ if (hasCursor)
+ return; // nope! sorry, I have a cursor myself
+ for (QQuickItem *otherChild : qAsConst(childItems)) {
QQuickItemPrivate *otherChildPrivate = QQuickItemPrivate::get(otherChild);
- if (otherChildPrivate->hasCursorInChild)
+ if (otherChildPrivate->subtreeCursorEnabled || otherChildPrivate->hasCursor)
return; // nope! sorry, something else wants it kept on.
}
}
- hasCursorInChild = hasCursor;
+ subtreeCursorEnabled = hasCursor;
QQuickItem *parent = q->parentItem();
if (parent) {
QQuickItemPrivate *parentPrivate = QQuickItemPrivate::get(parent);
@@ -7137,6 +7078,31 @@ void QQuickItemPrivate::setHasCursorInChild(bool hasCursor)
#endif
}
+void QQuickItemPrivate::setHasHoverInChild(bool hasHover)
+{
+ Q_Q(QQuickItem);
+
+ // if we're asked to turn it off (because of a setAcceptHoverEvents call, or a node
+ // removal) then we should make sure it's really ok to turn it off.
+ if (!hasHover && subtreeHoverEnabled) {
+ if (hoverEnabled)
+ return; // nope! sorry, I need hover myself
+ for (QQuickItem *otherChild : qAsConst(childItems)) {
+ QQuickItemPrivate *otherChildPrivate = QQuickItemPrivate::get(otherChild);
+ if (otherChildPrivate->subtreeHoverEnabled || otherChildPrivate->hoverEnabled)
+ return; // nope! sorry, something else wants it kept on.
+ }
+ }
+
+ qCDebug(DBG_HOVER_TRACE) << q << subtreeHoverEnabled << "->" << hasHover;
+ subtreeHoverEnabled = hasHover;
+ QQuickItem *parent = q->parentItem();
+ if (parent) {
+ QQuickItemPrivate *parentPrivate = QQuickItemPrivate::get(parent);
+ parentPrivate->setHasHoverInChild(hasHover);
+ }
+}
+
#ifndef QT_NO_CURSOR
/*!
@@ -7227,6 +7193,11 @@ void QQuickItem::unsetCursor()
Grabs the mouse input.
This item will receive all mouse events until ungrabMouse() is called.
+ Usually this function should not be called, since accepting for example
+ a mouse press event makes sure that the following events are delivered
+ to the item.
+ If an item wants to take over mouse events from the current receiver,
+ it needs to call this function.
\warning This function should be used with caution.
*/
@@ -7241,6 +7212,12 @@ void QQuickItem::grabMouse()
/*!
Releases the mouse grab following a call to grabMouse().
+
+ Note that this function should only be called when the item wants
+ to stop handling further events. There is no need to call this function
+ after a release or cancel event since no future events will be received
+ in any case. No move or release events will be delivered after this
+ function was called.
*/
void QQuickItem::ungrabMouse()
{
@@ -7248,15 +7225,7 @@ void QQuickItem::ungrabMouse()
if (!d->window)
return;
QQuickWindowPrivate *windowPriv = QQuickWindowPrivate::get(d->window);
- if (windowPriv->mouseGrabberItem != this) {
- qWarning("QQuickItem::ungrabMouse(): Item is not the mouse grabber.");
- return;
- }
-
- windowPriv->mouseGrabberItem = 0;
-
- QEvent ev(QEvent::UngrabMouse);
- d->window->sendEvent(this, &ev);
+ windowPriv->removeGrabber(this, true, false);
}
@@ -7309,31 +7278,16 @@ void QQuickItem::grabTouchPoints(const QVector<int> &ids)
if (!d->window)
return;
QQuickWindowPrivate *windowPriv = QQuickWindowPrivate::get(d->window);
-
- QSet<QQuickItem*> ungrab;
- for (int i = 0; i < ids.count(); ++i) {
- QQuickItem *oldGrabber = windowPriv->itemForTouchPointId.value(ids.at(i));
- if (oldGrabber == this)
- return;
-
- windowPriv->itemForTouchPointId[ids.at(i)] = this;
- if (oldGrabber)
- ungrab.insert(oldGrabber);
-
- QQuickItem *mouseGrabber = windowPriv->mouseGrabberItem;
- if (windowPriv->touchMouseId == ids.at(i) && mouseGrabber && mouseGrabber != this) {
- windowPriv->mouseGrabberItem = 0;
- QEvent ev(QEvent::UngrabMouse);
- d->window->sendEvent(mouseGrabber, &ev);
- }
- }
- foreach (QQuickItem *oldGrabber, ungrab)
- oldGrabber->touchUngrabEvent();
+ windowPriv->grabTouchPoints(this, ids);
}
/*!
Ungrabs the touch points owned by this item.
+ \note there is hardly any reason to call this function. It should only be
+ called when an item does not want to receive any further events, so no
+ move or release events will be delivered after calling this function.
+
\sa grabTouchPoints()
*/
void QQuickItem::ungrabTouchPoints()
@@ -7342,14 +7296,7 @@ void QQuickItem::ungrabTouchPoints()
if (!d->window)
return;
QQuickWindowPrivate *windowPriv = QQuickWindowPrivate::get(d->window);
-
- QMutableHashIterator<int, QQuickItem*> i(windowPriv->itemForTouchPointId);
- while (i.hasNext()) {
- i.next();
- if (i.value() == this)
- i.remove();
- }
- touchUngrabEvent();
+ windowPriv->removeGrabber(this, false, true);
}
/*!
@@ -7408,7 +7355,9 @@ void QQuickItem::setKeepTouchGrab(bool keep)
bool QQuickItem::contains(const QPointF &point) const
{
Q_D(const QQuickItem);
- return QRectF(0, 0, d->width, d->height).contains(point);
+ qreal x = point.x();
+ qreal y = point.y();
+ return x >= 0 && y >= 0 && x <= d->width && y <= d->height;
}
/*!
@@ -7769,9 +7718,13 @@ QDebug operator<<(QDebug debug, QQuickItem *item)
bool QQuickItem::isTextureProvider() const
{
+#if QT_CONFIG(quick_shadereffect)
Q_D(const QQuickItem);
return d->extra.isAllocated() && d->extra->layer && d->extra->layer->effectSource() ?
d->extra->layer->effectSource()->isTextureProvider() : false;
+#else
+ return false;
+#endif
}
/*!
@@ -7785,9 +7738,13 @@ bool QQuickItem::isTextureProvider() const
QSGTextureProvider *QQuickItem::textureProvider() const
{
+#if QT_CONFIG(quick_shadereffect)
Q_D(const QQuickItem);
return d->extra.isAllocated() && d->extra->layer && d->extra->layer->effectSource() ?
d->extra->layer->effectSource()->textureProvider() : 0;
+#else
+ return 0;
+#endif
}
/*!
@@ -7796,14 +7753,19 @@ QSGTextureProvider *QQuickItem::textureProvider() const
*/
QQuickItemLayer *QQuickItemPrivate::layer() const
{
+#if QT_CONFIG(quick_shadereffect)
if (!extra.isAllocated() || !extra->layer) {
extra.value().layer = new QQuickItemLayer(const_cast<QQuickItem *>(q_func()));
if (!componentComplete)
extra->layer->classBegin();
}
return extra->layer;
+#else
+ return 0;
+#endif
}
+#if QT_CONFIG(quick_shadereffect)
QQuickItemLayer::QQuickItemLayer(QQuickItem *item)
: m_item(item)
, m_enabled(false)
@@ -8211,7 +8173,7 @@ void QQuickItemLayer::itemOpacityChanged(QQuickItem *item)
updateOpacity();
}
-void QQuickItemLayer::itemGeometryChanged(QQuickItem *, const QRectF &, const QRectF &)
+void QQuickItemLayer::itemGeometryChanged(QQuickItem *, QQuickGeometryChange, const QRectF &)
{
updateGeometry();
}
@@ -8291,12 +8253,16 @@ void QQuickItemLayer::updateMatrix()
ld->extra.value().origin = QQuickItemPrivate::get(m_item)->origin();
ld->dirty(QQuickItemPrivate::Transform);
}
+#endif // quick_shadereffect
QQuickItemPrivate::ExtraData::ExtraData()
: z(0), scale(1), rotation(0), opacity(1),
contents(0), screenAttached(0), layoutDirectionAttached(0),
enterKeyAttached(0),
- keyHandler(0), layer(0),
+ keyHandler(0),
+#if QT_CONFIG(quick_shadereffect)
+ layer(0),
+#endif
effectRefCount(0), hideRefCount(0),
opacityNode(0), clipNode(0), rootNode(0),
acceptedMouseButtons(0), origin(QQuickItem::Center),
@@ -8322,7 +8288,6 @@ QAccessible::Role QQuickItemPrivate::accessibleRole() const
namespace QV4 {
namespace Heap {
struct QQuickItemWrapper : public QObjectWrapper {
- QQuickItemWrapper(QQuickItem *item) : QObjectWrapper(item) {}
};
}
}
@@ -8337,7 +8302,7 @@ DEFINE_OBJECT_VTABLE(QQuickItemWrapper);
void QQuickItemWrapper::markObjects(QV4::Heap::Base *that, QV4::ExecutionEngine *e)
{
QObjectWrapper::Data *This = static_cast<QObjectWrapper::Data *>(that);
- if (QQuickItem *item = static_cast<QQuickItem*>(This->object.data())) {
+ if (QQuickItem *item = static_cast<QQuickItem*>(This->object())) {
foreach (QQuickItem *child, QQuickItemPrivate::get(item)->childItems)
QV4::QObjectWrapper::markWrapper(child, e);
}
diff --git a/src/quick/items/qquickitem.h b/src/quick/items/qquickitem.h
index 5f5a141922..b1b1d627b1 100644
--- a/src/quick/items/qquickitem.h
+++ b/src/quick/items/qquickitem.h
@@ -148,7 +148,6 @@ class Q_QUICK_EXPORT QQuickItem : public QObject, public QQmlParserStatus
Q_PRIVATE_PROPERTY(QQuickItem::d_func(), QQuickItemLayer *layer READ layer DESIGNABLE false CONSTANT FINAL)
Q_CLASSINFO("DefaultProperty", "data")
- Q_CLASSINFO("qt_HasQmlAccessors", "true")
Q_CLASSINFO("qt_QmlJSWrapperFactoryMethod", "_q_createJSWrapper(QV4::ExecutionEngine*)")
public:
@@ -372,7 +371,6 @@ Q_SIGNALS:
void clipChanged(bool);
Q_REVISION(1) void windowChanged(QQuickWindow* window);
- // XXX todo
void childrenChanged();
void opacityChanged();
void enabledChanged();
@@ -458,7 +456,6 @@ private:
Q_DECLARE_PRIVATE(QQuickItem)
};
-// XXX todo
Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickItem::Flags)
#ifndef QT_NO_DEBUG_STREAM
diff --git a/src/quick/items/qquickitem_p.h b/src/quick/items/qquickitem_p.h
index 8cda5f89c6..4bff293657 100644
--- a/src/quick/items/qquickitem_p.h
+++ b/src/quick/items/qquickitem_p.h
@@ -56,6 +56,7 @@
#include "qquickanchors_p.h"
#include "qquickanchors_p_p.h"
#include "qquickitemchangelistener_p.h"
+#include "qquickevents_p_p.h"
#include "qquickwindow_p.h"
@@ -75,8 +76,9 @@
#include <QtCore/qdebug.h>
#include <QtCore/qelapsedtimer.h>
+#if QT_CONFIG(quick_shadereffect)
#include <QtQuick/private/qquickshadereffectsource_p.h>
-#include <QtQuick/private/qquickshadereffect_p.h>
+#endif
QT_BEGIN_NAMESPACE
@@ -98,7 +100,7 @@ public:
void complete();
protected:
- void itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry) Q_DECL_OVERRIDE;
+ void itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change, const QRectF &) Q_DECL_OVERRIDE;
void itemDestroyed(QQuickItem *item) Q_DECL_OVERRIDE;
void itemChildAdded(QQuickItem *, QQuickItem *) Q_DECL_OVERRIDE;
void itemChildRemoved(QQuickItem *, QQuickItem *) Q_DECL_OVERRIDE;
@@ -135,6 +137,7 @@ public:
QList<QQuickItem *> items;
};
+#if QT_CONFIG(quick_shadereffect)
class QQuickItemLayer : public QObject, public QQuickItemChangeListener
{
@@ -188,7 +191,7 @@ public:
QQuickShaderEffectSource *effectSource() const { return m_effectSource; }
- void itemGeometryChanged(QQuickItem *, const QRectF &, const QRectF &) Q_DECL_OVERRIDE;
+ void itemGeometryChanged(QQuickItem *, QQuickGeometryChange, const QRectF &) Q_DECL_OVERRIDE;
void itemOpacityChanged(QQuickItem *) Q_DECL_OVERRIDE;
void itemParentChanged(QQuickItem *, QQuickItem *) Q_DECL_OVERRIDE;
void itemSiblingOrderChanged(QQuickItem *) Q_DECL_OVERRIDE;
@@ -236,6 +239,8 @@ private:
QQuickShaderEffectSource::TextureMirroring m_textureMirroring;
};
+#endif
+
class Q_QUICK_PRIVATE_EXPORT QQuickItemPrivate : public QObjectPrivate
{
Q_DECLARE_PUBLIC(QQuickItem)
@@ -244,8 +249,6 @@ public:
static QQuickItemPrivate* get(QQuickItem *item) { return item->d_func(); }
static const QQuickItemPrivate* get(const QQuickItem *item) { return item->d_func(); }
- static void registerAccessorProperties();
-
QQuickItemPrivate();
~QQuickItemPrivate();
void init(QQuickItem *parent);
@@ -319,24 +322,12 @@ public:
Q_DECLARE_FLAGS(ChangeTypes, ChangeType)
- enum GeometryChangeType {
- NoChange = 0,
- XChange = 0x01,
- YChange = 0x02,
- WidthChange = 0x04,
- HeightChange = 0x08,
- SizeChange = WidthChange | HeightChange,
- GeometryChange = XChange | YChange | SizeChange
- };
-
- Q_DECLARE_FLAGS(GeometryChangeTypes, GeometryChangeType)
-
struct ChangeListener {
- ChangeListener(QQuickItemChangeListener *l = Q_NULLPTR, QQuickItemPrivate::ChangeTypes t = 0) : listener(l), types(t), gTypes(GeometryChange) {}
- ChangeListener(QQuickItemChangeListener *l, QQuickItemPrivate::GeometryChangeTypes gt) : listener(l), types(Geometry), gTypes(gt) {}
+ ChangeListener(QQuickItemChangeListener *l = nullptr, QQuickItemPrivate::ChangeTypes t = 0) : listener(l), types(t), gTypes(QQuickGeometryChange::All) {}
+ ChangeListener(QQuickItemChangeListener *l, QQuickGeometryChange gt) : listener(l), types(Geometry), gTypes(gt) {}
QQuickItemChangeListener *listener;
QQuickItemPrivate::ChangeTypes types;
- QQuickItemPrivate::GeometryChangeTypes gTypes; //NOTE: not used for ==
+ QQuickGeometryChange gTypes; //NOTE: not used for ==
bool operator==(const ChangeListener &other) const { return listener == other.listener && types == other.types; }
};
@@ -353,7 +344,9 @@ public:
QQuickLayoutMirroringAttached* layoutDirectionAttached;
QQuickEnterKeyAttached *enterKeyAttached;
QQuickItemKeyFilter *keyHandler;
+#if QT_CONFIG(quick_shadereffect)
mutable QQuickItemLayer *layer;
+#endif
#ifndef QT_NO_CURSOR
QCursor cursor;
#endif
@@ -390,8 +383,8 @@ public:
void addItemChangeListener(QQuickItemChangeListener *listener, ChangeTypes types);
void removeItemChangeListener(QQuickItemChangeListener *, ChangeTypes types);
- void updateOrAddGeometryChangeListener(QQuickItemChangeListener *listener, GeometryChangeTypes types);
- void updateOrRemoveGeometryChangeListener(QQuickItemChangeListener *listener, GeometryChangeTypes types);
+ void updateOrAddGeometryChangeListener(QQuickItemChangeListener *listener, QQuickGeometryChange types);
+ void updateOrRemoveGeometryChangeListener(QQuickItemChangeListener *listener, QQuickGeometryChange types);
QQuickStateGroup *_states();
QQuickStateGroup *_stateGroup;
@@ -427,8 +420,9 @@ public:
bool isAccessible:1;
bool culled:1;
bool hasCursor:1;
- bool hasCursorInChild:1;
+ bool subtreeCursorEnabled:1;
// Bit 32
+ bool subtreeHoverEnabled:1;
bool activeFocusOnTab:1;
bool implicitAntialiasing:1;
bool antialiasingValid:1;
@@ -488,7 +482,6 @@ public:
inline QSGRenderContext *sceneGraphRenderContext() const;
QQuickItem *parentItem;
- QQmlNotifier parentNotifier;
QList<QQuickItem *> childItems;
mutable QList<QQuickItem *> *sortedChildItems;
@@ -606,6 +599,9 @@ public:
virtual void mirrorChange() {}
void setHasCursorInChild(bool hasCursor);
+ void setHasHoverInChild(bool hasHover);
+
+ virtual void updatePolish() { }
};
/*
@@ -773,6 +769,7 @@ public:
QQuickItem *imeItem;
QList<QQuickItem *> targets;
QQuickItem *item;
+ QQuickKeyEvent theKeyEvent;
};
class QQuickKeysAttached : public QObject, public QQuickItemKeyFilter
@@ -933,7 +930,9 @@ Q_DECLARE_TYPEINFO(QQuickItemPrivate::ChangeListener, Q_PRIMITIVE_TYPE);
QT_END_NAMESPACE
+#if QT_CONFIG(quick_shadereffect)
QML_DECLARE_TYPE(QQuickItemLayer)
+#endif
QML_DECLARE_TYPE(QQuickKeysAttached)
QML_DECLARE_TYPEINFO(QQuickKeysAttached, QML_HAS_ATTACHED_PROPERTIES)
QML_DECLARE_TYPE(QQuickKeyNavigationAttached)
diff --git a/src/quick/items/qquickitemanimation.cpp b/src/quick/items/qquickitemanimation.cpp
index 027c07ec07..fd4a7d733f 100644
--- a/src/quick/items/qquickitemanimation.cpp
+++ b/src/quick/items/qquickitemanimation.cpp
@@ -42,7 +42,9 @@
#include "qquickstateoperations_p.h"
#include <private/qqmlproperty_p.h>
+#if QT_CONFIG(quick_path)
#include <private/qquickpath_p.h>
+#endif
#include "private/qparallelanimationgroupjob_p.h"
#include "private/qsequentialanimationgroupjob_p.h"
@@ -339,9 +341,9 @@ QAbstractAnimationJob* QQuickParentAnimation::transition(QQuickStateActions &act
qreal w = target->width();
qreal h = target->height();
if (pc->widthIsSet() && i < actions.size() - 1)
- w = actions[++i].toValue.toReal();
+ w = actions.at(++i).toValue.toReal();
if (pc->heightIsSet() && i < actions.size() - 1)
- h = actions[++i].toValue.toReal();
+ h = actions.at(++i).toValue.toReal();
const QPointF &transformOrigin
= d->computeTransformOrigin(target->transformOrigin(), w,h);
qreal tempxt = transformOrigin.x();
@@ -554,6 +556,8 @@ QAbstractAnimationJob* QQuickAnchorAnimation::transition(QQuickStateActions &act
return initInstance(animator);
}
+
+#if QT_CONFIG(quick_path)
/*!
\qmltype PathAnimation
\instantiates QQuickPathAnimation
@@ -1044,4 +1048,6 @@ QQuickPathAnimationAnimator::~QQuickPathAnimationAnimator()
}
}
+#endif // quick_path
+
QT_END_NAMESPACE
diff --git a/src/quick/items/qquickitemanimation_p.h b/src/quick/items/qquickitemanimation_p.h
index df80272eaa..a503cff223 100644
--- a/src/quick/items/qquickitemanimation_p.h
+++ b/src/quick/items/qquickitemanimation_p.h
@@ -124,6 +124,8 @@ protected:
QObject *defaultTarget = 0) Q_DECL_OVERRIDE;
};
+#if QT_CONFIG(quick_path)
+
class QQuickItem;
class QQuickPath;
class QQuickPathAnimationPrivate;
@@ -199,10 +201,14 @@ Q_SIGNALS:
void endRotationChanged(qreal);
};
+#endif
+
QT_END_NAMESPACE
QML_DECLARE_TYPE(QQuickParentAnimation)
QML_DECLARE_TYPE(QQuickAnchorAnimation)
+#if QT_CONFIG(quick_path)
QML_DECLARE_TYPE(QQuickPathAnimation)
+#endif
#endif // QQUICKITEMANIMATION_H
diff --git a/src/quick/items/qquickitemanimation_p_p.h b/src/quick/items/qquickitemanimation_p_p.h
index 5b18e4b0ae..92dd84e4a9 100644
--- a/src/quick/items/qquickitemanimation_p_p.h
+++ b/src/quick/items/qquickitemanimation_p_p.h
@@ -53,7 +53,9 @@
#include "qquickitemanimation_p.h"
+#if QT_CONFIG(quick_path)
#include <private/qquickpath_p.h>
+#endif
#include <private/qquickanimation_p_p.h>
QT_BEGIN_NAMESPACE
@@ -84,6 +86,8 @@ public:
QList<QQuickItem*> targets;
};
+#if QT_CONFIG(quick_path)
+
class QQuickPathAnimationUpdater : public QQuickBulkValueUpdater
{
public:
@@ -153,6 +157,7 @@ public:
QHash<QQuickItem*, QQuickPathAnimationAnimator* > activeAnimations;
};
+#endif
QT_END_NAMESPACE
diff --git a/src/quick/items/qquickitemchangelistener_p.h b/src/quick/items/qquickitemchangelistener_p.h
index 6e3ef25506..19ff73056b 100644
--- a/src/quick/items/qquickitemchangelistener_p.h
+++ b/src/quick/items/qquickitemchangelistener_p.h
@@ -58,12 +58,69 @@ QT_BEGIN_NAMESPACE
class QRectF;
class QQuickItem;
class QQuickAnchorsPrivate;
+
+class QQuickGeometryChange
+{
+public:
+ enum Kind: int {
+ Nothing = 0x00,
+ X = 0x01,
+ Y = 0x02,
+ Width = 0x04,
+ Height = 0x08,
+
+ Size = Width | Height,
+ All = X | Y | Size
+ };
+
+ QQuickGeometryChange(int change = Nothing)
+ : kind(change)
+ {}
+
+ bool noChange() const { return kind == Nothing; }
+ bool anyChange() const { return !noChange(); }
+
+ bool xChange() const { return kind & X; }
+ bool yChange() const { return kind & Y; }
+ bool widthChange() const { return kind & Width; }
+ bool heightChange() const { return kind & Height; }
+
+ bool positionChange() const { return xChange() || yChange(); }
+ bool sizeChange() const { return widthChange() || heightChange(); }
+
+ bool horizontalChange() const { return xChange() || widthChange(); }
+ bool verticalChange() const { return yChange() || heightChange(); }
+
+ void setXChange(bool enabled) { set(X, enabled); }
+ void setYChange(bool enabled) { set(Y, enabled); }
+ void setWidthChange(bool enabled) { set(Width, enabled); }
+ void setHeightChange(bool enabled) { set(Height, enabled); }
+ void setSizeChange(bool enabled) { set(Size, enabled); }
+ void setAllChanged(bool enabled) { set(All, enabled); }
+ void setHorizontalChange(bool enabled) { set(X | Width, enabled); }
+ void setVerticalChange(bool enabled) { set(Y | Height, enabled); }
+
+ void set(int bits, bool enabled)
+ {
+ if (enabled) {
+ kind |= bits;
+ } else {
+ kind &= ~bits;
+ }
+ }
+
+ bool matches(QQuickGeometryChange other) const { return kind & other.kind; }
+
+private:
+ int kind;
+};
+
class QQuickItemChangeListener
{
public:
virtual ~QQuickItemChangeListener() {}
- virtual void itemGeometryChanged(QQuickItem *, const QRectF & /* new */, const QRectF & /* old */ ) {}
+ virtual void itemGeometryChanged(QQuickItem *, QQuickGeometryChange, const QRectF & /* diff */) {}
virtual void itemSiblingOrderChanged(QQuickItem *) {}
virtual void itemVisibilityChanged(QQuickItem *) {}
virtual void itemOpacityChanged(QQuickItem *) {}
diff --git a/src/quick/items/qquickitemgrabresult.cpp b/src/quick/items/qquickitemgrabresult.cpp
index f8327a1c6e..1b0e1f07f6 100644
--- a/src/quick/items/qquickitemgrabresult.cpp
+++ b/src/quick/items/qquickitemgrabresult.cpp
@@ -37,17 +37,21 @@
**
****************************************************************************/
+#include <private/qtquickglobal_p.h>
#include "qquickitemgrabresult.h"
#include "qquickwindow.h"
#include "qquickitem.h"
+#if QT_CONFIG(quick_shadereffect)
#include "qquickshadereffectsource_p.h"
+#endif
#include <QtQml/QQmlEngine>
#include <private/qquickpixmapcache_p.h>
#include <private/qquickitem_p.h>
#include <private/qsgcontext_p.h>
+#include <private/qsgadaptationlayer_p.h>
QT_BEGIN_NAMESPACE
@@ -240,8 +244,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 efb86aff11..0705d9b504 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"
@@ -56,32 +55,59 @@
#include "qquickpincharea_p.h"
#include "qquickflickable_p.h"
#include "qquickflickable_p_p.h"
+#if QT_CONFIG(quick_listview)
#include "qquicklistview_p.h"
+#endif
+#if QT_CONFIG(quick_gridview)
#include "qquickgridview_p.h"
+#endif
+#if QT_CONFIG(quick_pathview)
#include "qquickpathview_p.h"
+#endif
+#if QT_CONFIG(quick_viewtransitions)
#include "qquickitemviewtransition_p.h"
+#endif
+#if QT_CONFIG(quick_path)
#include <private/qquickpath_p.h>
#include <private/qquickpathinterpolator_p.h>
+#endif
+#if QT_CONFIG(quick_positioners)
#include "qquickpositioners_p.h"
+#endif
#include "qquickrepeater_p.h"
#include "qquickloader_p.h"
+#if QT_CONFIG(quick_animatedimage)
#include "qquickanimatedimage_p.h"
+#endif
+#if QT_CONFIG(quick_flipable)
#include "qquickflipable_p.h"
+#endif
#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>
+#if QT_CONFIG(quick_canvas)
#include <QtQuick/private/qquickcanvasitem_p.h>
#include <QtQuick/private/qquickcontext2d_p.h>
+#endif
+#include "qquickitemgrabresult.h"
+#if QT_CONFIG(quick_sprite)
#include "qquicksprite_p.h"
#include "qquickspritesequence_p.h"
#include "qquickanimatedsprite_p.h"
+#endif
+#ifndef QT_NO_OPENGL
+# include "qquickopenglinfo_p.h"
+#endif
+#include "qquickgraphicsinfo_p.h"
+#if QT_CONFIG(quick_shadereffect)
+#include <QtQuick/private/qquickshadereffectsource_p.h>
+#include "qquickshadereffect_p.h"
+#include "qquickshadereffectmesh_p.h"
+#endif
#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>
@@ -129,29 +155,41 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor)
{
QQmlPrivate::RegisterAutoParent autoparent = { 0, &qquickitem_autoParent };
QQmlPrivate::qmlregister(QQmlPrivate::AutoParentRegistration, &autoparent);
- QQuickItemPrivate::registerAccessorProperties();
-#ifdef QT_NO_MOVIE
+#if !QT_CONFIG(quick_animatedimage)
qmlRegisterTypeNotAvailable(uri,major,minor,"AnimatedImage", QCoreApplication::translate("QQuickAnimatedImage","Qt was built without support for QMovie"));
#else
qmlRegisterType<QQuickAnimatedImage>(uri,major,minor,"AnimatedImage");
#endif
qmlRegisterType<QQuickBorderImage>(uri,major,minor,"BorderImage");
- qmlRegisterType<QQuickColumn>(uri,major,minor,"Column");
qmlRegisterType<QQuickFlickable>(uri,major,minor,"Flickable");
+#if QT_CONFIG(quick_flipable)
qmlRegisterType<QQuickFlipable>(uri,major,minor,"Flipable");
- qmlRegisterType<QQuickFlow>(uri,major,minor,"Flow");
+#endif
// qmlRegisterType<QQuickFocusPanel>(uri,major,minor,"FocusPanel");
qmlRegisterType<QQuickFocusScope>(uri,major,minor,"FocusScope");
qmlRegisterType<QQuickGradient>(uri,major,minor,"Gradient");
qmlRegisterType<QQuickGradientStop>(uri,major,minor,"GradientStop");
+#if QT_CONFIG(quick_positioners)
+ qmlRegisterType<QQuickColumn>(uri,major,minor,"Column");
+ qmlRegisterType<QQuickFlow>(uri,major,minor,"Flow");
qmlRegisterType<QQuickGrid>(uri,major,minor,"Grid");
+ qmlRegisterUncreatableType<QQuickBasePositioner>(uri,major,minor,"Positioner",
+ QStringLiteral("Positioner is an abstract type that is only available as an attached property."));
+ qmlRegisterType<QQuickRow>(uri,major,minor,"Row");
+#endif
+#if QT_CONFIG(quick_gridview)
qmlRegisterType<QQuickGridView>(uri,major,minor,"GridView");
+#endif
qmlRegisterType<QQuickImage>(uri,major,minor,"Image");
qmlRegisterType<QQuickItem>(uri,major,minor,"Item");
+#if QT_CONFIG(quick_listview)
qmlRegisterType<QQuickListView>(uri,major,minor,"ListView");
+ qmlRegisterType<QQuickViewSection>(uri,major,minor,"ViewSection");
+#endif
qmlRegisterType<QQuickLoader>(uri,major,minor,"Loader");
qmlRegisterType<QQuickMouseArea>(uri,major,minor,"MouseArea");
+#if QT_CONFIG(quick_path)
qmlRegisterType<QQuickPath>(uri,major,minor,"Path");
qmlRegisterType<QQuickPathAttribute>(uri,major,minor,"PathAttribute");
qmlRegisterType<QQuickPathCubic>(uri,major,minor,"PathCubic");
@@ -161,12 +199,12 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor)
qmlRegisterType<QQuickPathCatmullRomCurve>("QtQuick",2,0,"PathCurve");
qmlRegisterType<QQuickPathArc>("QtQuick",2,0,"PathArc");
qmlRegisterType<QQuickPathSvg>("QtQuick",2,0,"PathSvg");
+#endif
+#if QT_CONFIG(quick_pathview)
qmlRegisterType<QQuickPathView>(uri,major,minor,"PathView");
- qmlRegisterUncreatableType<QQuickBasePositioner>(uri,major,minor,"Positioner",
- QStringLiteral("Positioner is an abstract type that is only available as an attached property."));
+#endif
qmlRegisterType<QQuickRectangle>(uri,major,minor,"Rectangle");
qmlRegisterType<QQuickRepeater>(uri,major,minor,"Repeater");
- qmlRegisterType<QQuickRow>(uri,major,minor,"Row");
qmlRegisterType<QQuickTranslate>(uri,major,minor,"Translate");
qmlRegisterType<QQuickRotation>(uri,major,minor,"Rotation");
qmlRegisterType<QQuickScale>(uri,major,minor,"Scale");
@@ -177,18 +215,20 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor)
qmlRegisterType<QQuickTextInput>(uri,major,minor,"TextInput");
qmlRegisterType<QQuickTextInput,2>(uri,2,2,"TextInput");
qmlRegisterType<QQuickTextInput,3>(uri,2,4,"TextInput");
- qmlRegisterType<QQuickViewSection>(uri,major,minor,"ViewSection");
-
qmlRegisterType<QQuickItemGrabResult>();
+#if QT_CONFIG(quick_shadereffect)
qmlRegisterType<QQuickItemLayer>();
+#endif
qmlRegisterType<QQuickAnchors>();
qmlRegisterType<QQuickKeyEvent>();
qmlRegisterType<QQuickMouseEvent>();
qmlRegisterType<QQuickWheelEvent>();
qmlRegisterType<QQuickCloseEvent>();
qmlRegisterType<QQuickTransform>();
+#if QT_CONFIG(quick_path)
qmlRegisterType<QQuickPathElement>();
qmlRegisterType<QQuickCurve>();
+#endif
qmlRegisterType<QQuickScaleGrid>();
qmlRegisterType<QQuickTextLine>();
qmlRegisterType<QQuickPen>();
@@ -201,32 +241,42 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor)
qmlRegisterUncreatableType<QQuickKeyNavigationAttached>(uri,major,minor,"KeyNavigation",QQuickKeyNavigationAttached::tr("KeyNavigation is only available via attached properties"));
qmlRegisterUncreatableType<QQuickKeysAttached>(uri,major,minor,"Keys",QQuickKeysAttached::tr("Keys is only available via attached properties"));
qmlRegisterUncreatableType<QQuickLayoutMirroringAttached>(uri,major,minor,"LayoutMirroring", QQuickLayoutMirroringAttached::tr("LayoutMirroring is only available via attached properties"));
+#if QT_CONFIG(quick_viewtransitions)
qmlRegisterUncreatableType<QQuickViewTransitionAttached>(uri,major,minor,"ViewTransition",QQuickViewTransitionAttached::tr("ViewTransition is only available via attached properties"));
+#endif
qmlRegisterType<QQuickPinchArea>(uri,major,minor,"PinchArea");
qmlRegisterType<QQuickPinch>(uri,major,minor,"Pinch");
qmlRegisterType<QQuickPinchEvent>();
- qmlRegisterType<QQuickShaderEffect>("QtQuick", 2, 0, "ShaderEffect");
+#if QT_CONFIG(quick_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<QQuickShaderEffect>("QtQuick", 2, 0, "ShaderEffect");
+#endif
qmlRegisterUncreatableType<QQuickPaintedItem>("QtQuick", 2, 0, "PaintedItem", QQuickPaintedItem::tr("Cannot create instance of abstract class PaintedItem"));
+#if QT_CONFIG(quick_canvas)
qmlRegisterType<QQuickCanvasItem>("QtQuick", 2, 0, "Canvas");
+#endif
+#if QT_CONFIG(quick_sprite)
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>();
qmlRegisterType<QQuickAnchorAnimation>(uri, major, minor,"AnchorAnimation");
qmlRegisterType<QQuickParentAnimation>(uri, major, minor,"ParentAnimation");
+#if QT_CONFIG(quick_canvas)
qmlRegisterType<QQuickPathAnimation>("QtQuick",2,0,"PathAnimation");
qmlRegisterType<QQuickPathInterpolator>("QtQuick",2,0,"PathInterpolator");
+#endif
#ifndef QT_NO_DRAGANDDROP
qmlRegisterType<QQuickDropArea>("QtQuick", 2, 0, "DropArea");
@@ -244,11 +294,19 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor)
#endif
qmlRegisterType<QQuickItem, 1>(uri, 2, 1,"Item");
+#if QT_CONFIG(quick_positioners)
qmlRegisterType<QQuickGrid, 1>(uri, 2, 1, "Grid");
+#endif
+#if QT_CONFIG(quick_itemview)
qmlRegisterUncreatableType<QQuickItemView, 1>(uri, 2, 1, "ItemView", QQuickItemView::tr("ItemView is an abstract base class"));
qmlRegisterUncreatableType<QQuickItemView, 2>(uri, 2, 3, "ItemView", QQuickItemView::tr("ItemView is an abstract base class"));
+#endif
+#if QT_CONFIG(quick_listview)
qmlRegisterType<QQuickListView, 1>(uri, 2, 1, "ListView");
+#endif
+#if QT_CONFIG(quick_gridview)
qmlRegisterType<QQuickGridView, 1>(uri, 2, 1, "GridView");
+#endif
qmlRegisterType<QQuickTextEdit, 1>(uri, 2, 1, "TextEdit");
qmlRegisterType<QQuickText, 2>(uri, 2, 2, "Text");
@@ -259,11 +317,17 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor)
qmlRegisterType<QQuickImage, 1>(uri, 2, 3,"Image");
qmlRegisterType<QQuickItem, 2>(uri, 2, 4, "Item");
+#if QT_CONFIG(quick_listview)
qmlRegisterType<QQuickListView, 2>(uri, 2, 4, "ListView");
+#endif
qmlRegisterType<QQuickMouseArea, 1>(uri, 2, 4, "MouseArea");
+#if QT_CONFIG(quick_shadereffect)
qmlRegisterType<QQuickShaderEffect, 1>(uri, 2, 4, "ShaderEffect");
- qmlRegisterUncreatableType<QQuickOpenGLInfo>(uri, 2, 4,"OpenGLInfo", QQuickOpenGLInfo::tr("OpenGLInfo is only available via attached properties"));
+#endif
+#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");
@@ -271,24 +335,39 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor)
qmlRegisterType<QQuickText, 6>(uri, 2, 6, "Text");
qmlRegisterType<QQuickTextEdit, 6>(uri, 2, 6, "TextEdit");
qmlRegisterType<QQuickTextInput, 6>(uri, 2, 6, "TextInput");
+#if QT_CONFIG(quick_positioners)
qmlRegisterUncreatableType<QQuickBasePositioner, 6>(uri, 2, 6, "Positioner",
QStringLiteral("Positioner is an abstract type that is only available as an attached property."));
qmlRegisterType<QQuickColumn, 6>(uri, 2, 6, "Column");
qmlRegisterType<QQuickRow, 6>(uri, 2, 6, "Row");
qmlRegisterType<QQuickGrid, 6>(uri, 2, 6, "Grid");
qmlRegisterType<QQuickFlow, 6>(uri, 2, 6, "Flow");
+#endif
qmlRegisterUncreatableType<QQuickEnterKeyAttached, 6>(uri, 2, 6, "EnterKey",
QQuickEnterKeyAttached::tr("EnterKey is only available via attached properties"));
+#if QT_CONFIG(quick_shadereffect)
qmlRegisterType<QQuickShaderEffectSource, 1>(uri, 2, 6, "ShaderEffectSource");
+#endif
qmlRegisterType<QQuickItem, 7>(uri, 2, 7, "Item");
+#if QT_CONFIG(quick_listview)
qmlRegisterType<QQuickListView, 7>(uri, 2, 7, "ListView");
+#endif
+#if QT_CONFIG(quick_gridview)
qmlRegisterType<QQuickGridView, 7>(uri, 2, 7, "GridView");
+#endif
qmlRegisterType<QQuickTextInput, 7>(uri, 2, 7, "TextInput");
qmlRegisterType<QQuickTextEdit, 7>(uri, 2, 7, "TextEdit");
+#if QT_CONFIG(quick_pathview)
qmlRegisterType<QQuickPathView, 7>(uri, 2, 7, "PathView");
+#endif
qmlRegisterUncreatableType<QQuickMouseEvent, 7>(uri, 2, 7, nullptr, QQuickMouseEvent::tr("MouseEvent is only available within handlers in MouseArea"));
+
+ qmlRegisterUncreatableType<QQuickGraphicsInfo>(uri, 2, 8,"GraphicsInfo", QQuickGraphicsInfo::tr("GraphicsInfo is only available via attached properties"));
+#if QT_CONFIG(quick_shadereffect)
+ qmlRegisterType<QQuickBorderImageMesh>("QtQuick", 2, 8, "BorderImageMesh");
+#endif
}
static void initResources()
diff --git a/src/quick/items/qquickitemview.cpp b/src/quick/items/qquickitemview.cpp
index aff03b7539..e017d6564a 100644
--- a/src/quick/items/qquickitemview.cpp
+++ b/src/quick/items/qquickitemview.cpp
@@ -181,7 +181,7 @@ void QQuickItemViewChangeSet::applyChanges(const QQmlChangeSet &changeSet)
int moveId = -1;
int moveOffset = 0;
- foreach (const QQmlChangeSet::Change &r, changeSet.removes()) {
+ for (const QQmlChangeSet::Change &r : changeSet.removes()) {
itemCount -= r.count;
if (moveId == -1 && newCurrentIndex >= r.index + r.count) {
newCurrentIndex -= r.count;
@@ -200,7 +200,7 @@ void QQuickItemViewChangeSet::applyChanges(const QQmlChangeSet &changeSet)
currentChanged = true;
}
}
- foreach (const QQmlChangeSet::Change &i, changeSet.inserts()) {
+ for (const QQmlChangeSet::Change &i : changeSet.inserts()) {
if (moveId == -1) {
if (itemCount && newCurrentIndex >= i.index) {
newCurrentIndex += i.count;
@@ -1199,16 +1199,17 @@ void QQuickItemViewPrivate::showVisibleItems() const
{
qDebug() << "Visible items:";
for (int i = 0; i < visibleItems.count(); ++i) {
- qDebug() << "\t" << visibleItems[i]->index
- << visibleItems[i]->item->objectName()
- << visibleItems[i]->position();
+ qDebug() << "\t" << visibleItems.at(i)->index
+ << visibleItems.at(i)->item->objectName()
+ << visibleItems.at(i)->position();
}
}
-void QQuickItemViewPrivate::itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry)
+void QQuickItemViewPrivate::itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change,
+ const QRectF &diff)
{
Q_Q(QQuickItemView);
- QQuickFlickablePrivate::itemGeometryChanged(item, newGeometry, oldGeometry);
+ QQuickFlickablePrivate::itemGeometryChanged(item, change, diff);
if (!q->isComponentComplete())
return;
@@ -1621,12 +1622,10 @@ qreal QQuickItemViewPrivate::contentStartOffset() const
int QQuickItemViewPrivate::findLastVisibleIndex(int defaultValue) const
{
- if (visibleItems.count()) {
- int i = visibleItems.count() - 1;
- while (i > 0 && visibleItems.at(i)->index == -1)
- --i;
- if (visibleItems.at(i)->index != -1)
- return visibleItems.at(i)->index;
+ for (auto it = visibleItems.rbegin(), end = visibleItems.rend(); it != end; ++it) {
+ auto item = *it;
+ if (item->index != -1)
+ return item->index;
}
return defaultValue;
}
@@ -1657,9 +1656,10 @@ FxViewItem *QQuickItemViewPrivate::firstVisibleItem() const {
int QQuickItemViewPrivate::findLastIndexInView() const
{
const qreal viewEndPos = isContentFlowReversed() ? -position() : position() + size();
- for (int i=visibleItems.count() - 1; i>=0; i--) {
- if (visibleItems.at(i)->position() <= viewEndPos && visibleItems.at(i)->index != -1)
- return visibleItems.at(i)->index;
+ for (auto it = visibleItems.rbegin(), end = visibleItems.rend(); it != end; ++it) {
+ auto item = *it;
+ if (item->index != -1 && item->position() <= viewEndPos)
+ return item->index;
}
return -1;
}
@@ -1997,7 +1997,7 @@ bool QQuickItemViewPrivate::applyModelChanges(ChangeResult *totalInsertionResult
prevViewPos = prevFirstVisible->position();
prevFirstVisibleIndex = prevFirstVisible->index;
}
- qreal prevVisibleItemsFirstPos = visibleItems.count() ? visibleItems.first()->position() : 0.0;
+ qreal prevVisibleItemsFirstPos = visibleItems.count() ? visibleItems.constFirst()->position() : 0.0;
totalInsertionResult->visiblePos = prevViewPos;
totalRemovalResult->visiblePos = prevViewPos;
@@ -2076,13 +2076,13 @@ bool QQuickItemViewPrivate::applyModelChanges(ChangeResult *totalInsertionResult
// can transition it from this "original" position to its new position in the view
if (transitioner && transitioner->canTransition(QQuickItemViewTransitioner::MoveTransition, true)) {
for (int i=0; i<movingIntoView.count(); i++) {
- int fromIndex = findMoveKeyIndex(movingIntoView[i].moveKey, removals);
+ int fromIndex = findMoveKeyIndex(movingIntoView.at(i).moveKey, removals);
if (fromIndex >= 0) {
if (prevFirstVisibleIndex >= 0 && fromIndex < prevFirstVisibleIndex)
- repositionItemAt(movingIntoView[i].item, fromIndex, -totalInsertionResult->sizeChangesAfterVisiblePos);
+ repositionItemAt(movingIntoView.at(i).item, fromIndex, -totalInsertionResult->sizeChangesAfterVisiblePos);
else
- repositionItemAt(movingIntoView[i].item, fromIndex, totalInsertionResult->sizeChangesAfterVisiblePos);
- movingIntoView[i].item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::MoveTransition, true);
+ repositionItemAt(movingIntoView.at(i).item, fromIndex, totalInsertionResult->sizeChangesAfterVisiblePos);
+ movingIntoView.at(i).item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::MoveTransition, true);
}
}
}
@@ -2128,11 +2128,11 @@ bool QQuickItemViewPrivate::applyRemovalChange(const QQmlChangeSet::Change &remo
Q_Q(QQuickItemView);
bool visibleAffected = false;
- if (visibleItems.count() && removal.index + removal.count > visibleItems.last()->index) {
- if (removal.index > visibleItems.last()->index)
+ if (visibleItems.count() && removal.index + removal.count > visibleItems.constLast()->index) {
+ if (removal.index > visibleItems.constLast()->index)
removeResult->countChangeAfterVisibleItems += removal.count;
else
- removeResult->countChangeAfterVisibleItems += ((removal.index + removal.count - 1) - visibleItems.last()->index);
+ removeResult->countChangeAfterVisibleItems += ((removal.index + removal.count - 1) - visibleItems.constLast()->index);
}
QList<FxViewItem*>::Iterator it = visibleItems.begin();
@@ -2222,10 +2222,11 @@ void QQuickItemViewPrivate::repositionFirstItem(FxViewItem *prevVisibleItemsFirs
qreal moveBackwardsBy = 0;
// shift visibleItems.first() relative to the number of added/removed items
- if (visibleItems.first()->position() > prevViewPos) {
+ const auto pos = visibleItems.constFirst()->position();
+ if (pos > prevViewPos) {
moveForwardsBy = insertionResult->sizeChangesAfterVisiblePos;
moveBackwardsBy = removalResult->sizeChangesAfterVisiblePos;
- } else if (visibleItems.first()->position() < prevViewPos) {
+ } else if (pos < prevViewPos) {
moveForwardsBy = removalResult->sizeChangesBeforeVisiblePos;
moveBackwardsBy = insertionResult->sizeChangesBeforeVisiblePos;
}
@@ -2303,7 +2304,7 @@ bool QQuickItemViewPrivate::prepareNonVisibleItemTransition(FxViewItem *item, co
void QQuickItemViewPrivate::viewItemTransitionFinished(QQuickItemViewTransitionableItem *item)
{
for (int i=0; i<releasePendingTransition.count(); i++) {
- if (releasePendingTransition[i]->transitionableItem == item) {
+ if (releasePendingTransition.at(i)->transitionableItem == item) {
releaseItem(releasePendingTransition.takeAt(i));
return;
}
@@ -2323,8 +2324,8 @@ FxViewItem *QQuickItemViewPrivate::createItem(int modelIndex, bool asynchronous)
return 0;
for (int i=0; i<releasePendingTransition.count(); i++) {
- if (releasePendingTransition[i]->index == modelIndex
- && !releasePendingTransition[i]->isPendingRemoval()) {
+ if (releasePendingTransition.at(i)->index == modelIndex
+ && !releasePendingTransition.at(i)->isPendingRemoval()) {
releasePendingTransition[i]->releaseAfterTransition = false;
return releasePendingTransition.takeAt(i);
}
diff --git a/src/quick/items/qquickitemview_p.h b/src/quick/items/qquickitemview_p.h
index 1a28fc212b..c289ace408 100644
--- a/src/quick/items/qquickitemview_p.h
+++ b/src/quick/items/qquickitemview_p.h
@@ -51,6 +51,10 @@
// We mean it.
//
+#include <QtQuick/private/qtquickglobal_p.h>
+
+QT_REQUIRE_CONFIG(quick_itemview);
+
#include "qquickflickable_p.h"
#include <qpointer.h>
#include <QtCore/QLoggingCategory>
diff --git a/src/quick/items/qquickitemview_p_p.h b/src/quick/items/qquickitemview_p_p.h
index 5e104cf208..62851c9a89 100644
--- a/src/quick/items/qquickitemview_p_p.h
+++ b/src/quick/items/qquickitemview_p_p.h
@@ -51,6 +51,10 @@
// We mean it.
//
+#include <QtQuick/private/qtquickglobal_p.h>
+
+QT_REQUIRE_CONFIG(quick_itemview);
+
#include "qquickitemview_p.h"
#include "qquickitemviewtransition_p.h"
#include "qquickflickable_p_p.h"
@@ -378,7 +382,7 @@ protected:
virtual void updateSectionCriteria() {}
virtual void updateSections() {}
- void itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry) Q_DECL_OVERRIDE;
+ void itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change, const QRectF &) Q_DECL_OVERRIDE;
};
diff --git a/src/quick/items/qquickitemviewtransition_p.h b/src/quick/items/qquickitemviewtransition_p.h
index 6641dada29..ff0e82ac7b 100644
--- a/src/quick/items/qquickitemviewtransition_p.h
+++ b/src/quick/items/qquickitemviewtransition_p.h
@@ -52,6 +52,9 @@
//
#include <QtQuick/private/qtquickglobal_p.h>
+
+QT_REQUIRE_CONFIG(quick_viewtransitions);
+
#include <QtCore/qobject.h>
#include <QtCore/qpoint.h>
#include <QtQml/qqml.h>
diff --git a/src/quick/items/qquicklistview.cpp b/src/quick/items/qquicklistview.cpp
index b9f4fc5d42..30625c7ea8 100644
--- a/src/quick/items/qquicklistview.cpp
+++ b/src/quick/items/qquicklistview.cpp
@@ -131,7 +131,7 @@ public:
void updateAverage();
- void itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry) Q_DECL_OVERRIDE;
+ void itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change, const QRectF &diff) Q_DECL_OVERRIDE;
void fixupPosition() Q_DECL_OVERRIDE;
void fixup(AxisData &data, qreal minExtent, qreal maxExtent) Q_DECL_OVERRIDE;
bool flick(QQuickItemViewPrivate::AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
@@ -400,7 +400,7 @@ FxViewItem *QQuickListViewPrivate::itemBefore(int modelIndex) const
++idx;
}
if (lastIndex == modelIndex-1)
- return visibleItems.last();
+ return visibleItems.constLast();
return 0;
}
@@ -752,7 +752,7 @@ bool QQuickListViewPrivate::removeNonVisibleItems(qreal bufferFrom, qreal buffer
}
}
- while (visibleItems.count() > 1 && (item = visibleItems.last()) && item->position() > bufferTo) {
+ while (visibleItems.count() > 1 && (item = visibleItems.constLast()) && item->position() > bufferTo) {
if (item->attached->delayRemove())
break;
qCDebug(lcItemViewDelegateLifecycle) << "refill: remove last" << visibleIndex+visibleItems.count()-1 << item->position() << (QObject *)(item->item);
@@ -839,7 +839,7 @@ void QQuickListViewPrivate::repositionPackageItemAt(QQuickItem *item, int index)
void QQuickListViewPrivate::resetFirstItemPosition(qreal pos)
{
- FxListItemSG *item = static_cast<FxListItemSG*>(visibleItems.first());
+ FxListItemSG *item = static_cast<FxListItemSG*>(visibleItems.constFirst());
item->setPosition(pos);
}
@@ -848,12 +848,12 @@ void QQuickListViewPrivate::adjustFirstItem(qreal forwards, qreal backwards, int
if (!visibleItems.count())
return;
qreal diff = forwards - backwards;
- static_cast<FxListItemSG*>(visibleItems.first())->setPosition(visibleItems.first()->position() + diff);
+ static_cast<FxListItemSG*>(visibleItems.constFirst())->setPosition(visibleItems.constFirst()->position() + diff);
}
void QQuickListViewPrivate::updateSizeChangesBeforeVisiblePos(FxViewItem *item, ChangeResult *removeResult)
{
- if (item != visibleItems.first())
+ if (item != visibleItems.constFirst())
QQuickItemViewPrivate::updateSizeChangesBeforeVisiblePos(item, removeResult);
}
@@ -1270,7 +1270,7 @@ void QQuickListViewPrivate::initializeCurrentItem()
if (!actualItem) {
if (currentIndex == visibleIndex - 1 && visibleItems.count()) {
// We can calculate exact postion in this case
- listItem->setPosition(visibleItems.first()->position() - currentItem->size() - spacing);
+ listItem->setPosition(visibleItems.constFirst()->position() - currentItem->size() - spacing);
} else {
// Create current item now and position as best we can.
// Its position will be corrected when it becomes visible.
@@ -1400,10 +1400,12 @@ bool QQuickListViewPrivate::hasStickyFooter() const
return footer && footerPositioning != QQuickListView::InlineFooter;
}
-void QQuickListViewPrivate::itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry)
+void QQuickListViewPrivate::itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change,
+ const QRectF &diff)
{
Q_Q(QQuickListView);
- QQuickItemViewPrivate::itemGeometryChanged(item, newGeometry, oldGeometry);
+
+ QQuickItemViewPrivate::itemGeometryChanged(item, change, diff);
if (!q->isComponentComplete())
return;
@@ -1417,29 +1419,31 @@ void QQuickListViewPrivate::itemGeometryChanged(QQuickItem *item, const QRectF &
}
if (item != contentItem && (!highlight || item != highlight->item)) {
- if ((orient == QQuickListView::Vertical && newGeometry.height() != oldGeometry.height())
- || (orient == QQuickListView::Horizontal && newGeometry.width() != oldGeometry.width())) {
+ if ((orient == QQuickListView::Vertical && change.heightChange())
+ || (orient == QQuickListView::Horizontal && change.widthChange())) {
// if visibleItems.first() has resized, adjust its pos since it is used to
// position all subsequent items
- if (visibleItems.count() && item == visibleItems.first()->item) {
- FxListItemSG *listItem = static_cast<FxListItemSG*>(visibleItems.first());
+ if (visibleItems.count() && item == visibleItems.constFirst()->item) {
+ FxListItemSG *listItem = static_cast<FxListItemSG*>(visibleItems.constFirst());
+ const QRectF oldGeometry(item->x() - diff.x(),
+ item->y() - diff.y(),
+ item->width() - diff.width(),
+ item->height() - diff.height());
if (listItem->transitionScheduledOrRunning())
return;
if (orient == QQuickListView::Vertical) {
const qreal oldItemEndPosition = verticalLayoutDirection == QQuickItemView::BottomToTop ? -oldGeometry.y() : oldGeometry.y() + oldGeometry.height();
- qreal diff = newGeometry.height() - oldGeometry.height();
if (verticalLayoutDirection == QQuickListView::TopToBottom && oldItemEndPosition < q->contentY())
- listItem->setPosition(listItem->position() - diff, true);
+ listItem->setPosition(listItem->position() - diff.height(), true);
else if (verticalLayoutDirection == QQuickListView::BottomToTop && oldItemEndPosition > q->contentY())
- listItem->setPosition(listItem->position() + diff, true);
+ listItem->setPosition(listItem->position() + diff.height(), true);
} else {
const qreal oldItemEndPosition = q->effectiveLayoutDirection() == Qt::RightToLeft ? -oldGeometry.x() : oldGeometry.x() + oldGeometry.width();
- qreal diff = newGeometry.width() - oldGeometry.width();
if (q->effectiveLayoutDirection() == Qt::LeftToRight && oldItemEndPosition < q->contentX())
- listItem->setPosition(listItem->position() - diff, true);
+ listItem->setPosition(listItem->position() - diff.width(), true);
else if (q->effectiveLayoutDirection() == Qt::RightToLeft && oldItemEndPosition > q->contentX())
- listItem->setPosition(listItem->position() + diff, true);
+ listItem->setPosition(listItem->position() + diff.width(), true);
}
}
forceLayoutPolish();
@@ -3107,7 +3111,7 @@ bool QQuickListViewPrivate::applyInsertionChange(const QQmlChangeSet::Change &ch
int i = visibleItems.count() - 1;
while (i > 0 && visibleItems.at(i)->index == -1)
--i;
- if (i == 0 && visibleItems.first()->index == -1) {
+ if (i == 0 && visibleItems.constFirst()->index == -1) {
// there are no visible items except items marked for removal
index = visibleItems.count();
} else if (visibleItems.at(i)->index + 1 == modelIndex
@@ -3132,7 +3136,7 @@ bool QQuickListViewPrivate::applyInsertionChange(const QQmlChangeSet::Change &ch
qreal pos = 0;
if (visibleItems.count()) {
pos = index < visibleItems.count() ? visibleItems.at(index)->position()
- : visibleItems.last()->endPosition()+spacing;
+ : visibleItems.constLast()->endPosition() + spacing;
}
// Update the indexes of the following visible items.
@@ -3267,7 +3271,7 @@ void QQuickListViewPrivate::translateAndTransitionItemsAfter(int afterModelIndex
int markerItemIndex = -1;
for (int i=0; i<visibleItems.count(); i++) {
- if (visibleItems[i]->index == afterModelIndex) {
+ if (visibleItems.at(i)->index == afterModelIndex) {
markerItemIndex = i;
break;
}
@@ -3280,7 +3284,7 @@ void QQuickListViewPrivate::translateAndTransitionItemsAfter(int afterModelIndex
- (removalResult.countChangeAfterVisibleItems * (averageSize + spacing));
for (int i=markerItemIndex+1; i<visibleItems.count() && visibleItems.at(i)->position() < viewEndPos; i++) {
- FxListItemSG *listItem = static_cast<FxListItemSG *>(visibleItems[i]);
+ FxListItemSG *listItem = static_cast<FxListItemSG *>(visibleItems.at(i));
if (!listItem->transitionScheduledOrRunning()) {
qreal pos = listItem->position();
listItem->setPosition(pos - sizeRemoved);
diff --git a/src/quick/items/qquicklistview_p.h b/src/quick/items/qquicklistview_p.h
index b8ab38bbba..8d0ad7f618 100644
--- a/src/quick/items/qquicklistview_p.h
+++ b/src/quick/items/qquicklistview_p.h
@@ -51,13 +51,19 @@
// We mean it.
//
+#include <private/qtquickglobal_p.h>
+
+QT_REQUIRE_CONFIG(quick_listview);
+
#include "qquickitemview_p.h"
+#include <private/qtquickglobal_p.h>
+
QT_BEGIN_NAMESPACE
class QQuickListView;
class QQuickListViewPrivate;
-class Q_AUTOTEST_EXPORT QQuickViewSection : public QObject
+class Q_QUICK_PRIVATE_EXPORT QQuickViewSection : public QObject
{
Q_OBJECT
Q_PROPERTY(QString property READ property WRITE setProperty NOTIFY propertyChanged)
@@ -103,7 +109,7 @@ private:
class QQmlInstanceModel;
class QQuickListViewAttached;
-class Q_AUTOTEST_EXPORT QQuickListView : public QQuickItemView
+class Q_QUICK_PRIVATE_EXPORT QQuickListView : public QQuickItemView
{
Q_OBJECT
Q_DECLARE_PRIVATE(QQuickListView)
diff --git a/src/quick/items/qquickloader.cpp b/src/quick/items/qquickloader.cpp
index 63c9558d7a..9aea9c50df 100644
--- a/src/quick/items/qquickloader.cpp
+++ b/src/quick/items/qquickloader.cpp
@@ -65,11 +65,12 @@ QQuickLoaderPrivate::~QQuickLoaderPrivate()
disposeInitialPropertyValues();
}
-void QQuickLoaderPrivate::itemGeometryChanged(QQuickItem *resizeItem, const QRectF &newGeometry, const QRectF &oldGeometry)
+void QQuickLoaderPrivate::itemGeometryChanged(QQuickItem *resizeItem, QQuickGeometryChange change
+ , const QRectF &diff)
{
if (resizeItem == item)
_q_updateSize(false);
- QQuickItemChangeListener::itemGeometryChanged(resizeItem, newGeometry, oldGeometry);
+ QQuickItemChangeListener::itemGeometryChanged(resizeItem, change, diff);
}
void QQuickLoaderPrivate::itemImplicitWidthChanged(QQuickItem *)
diff --git a/src/quick/items/qquickloader_p_p.h b/src/quick/items/qquickloader_p_p.h
index 9ef89a74d6..fcccbfe4f5 100644
--- a/src/quick/items/qquickloader_p_p.h
+++ b/src/quick/items/qquickloader_p_p.h
@@ -84,7 +84,7 @@ public:
QQuickLoaderPrivate();
~QQuickLoaderPrivate();
- void itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry) Q_DECL_OVERRIDE;
+ void itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change, const QRectF &diff) Q_DECL_OVERRIDE;
void itemImplicitWidthChanged(QQuickItem *) Q_DECL_OVERRIDE;
void itemImplicitHeightChanged(QQuickItem *) Q_DECL_OVERRIDE;
void clear();
diff --git a/src/quick/items/qquickmousearea.cpp b/src/quick/items/qquickmousearea.cpp
index 609666d32b..6bd83dd808 100644
--- a/src/quick/items/qquickmousearea.cpp
+++ b/src/quick/items/qquickmousearea.cpp
@@ -40,10 +40,10 @@
#include "qquickmousearea_p.h"
#include "qquickmousearea_p_p.h"
#include "qquickwindow.h"
-#include "qquickevents_p_p.h"
#include "qquickdrag_p.h"
#include <private/qqmldata_p.h>
+#include <private/qsgadaptationlayer_p.h>
#include <QtGui/private/qguiapplication_p.h>
#include <QtGui/qevent.h>
@@ -55,6 +55,8 @@ QT_BEGIN_NAMESPACE
DEFINE_BOOL_CONFIG_OPTION(qmlVisualTouchDebugging, QML_VISUAL_TOUCH_DEBUGGING)
+Q_DECLARE_LOGGING_CATEGORY(DBG_HOVER_TRACE)
+
QQuickMouseAreaPrivate::QQuickMouseAreaPrivate()
: enabled(true), scrollGestureEnabled(true), hovered(false), longPress(false),
moved(false), stealMouse(false), doubleClick(false), preventStealing(false),
@@ -404,8 +406,7 @@ bool QQuickMouseAreaPrivate::propagateHelper(QQuickMouseEvent *ev, QQuickItem *i
/*!
\qmlsignal QtQuick::MouseArea::canceled()
- This signal is emitted when mouse events have been canceled, either because an event was not accepted, or
- because another item stole the mouse event handling.
+ This signal is emitted when mouse events have been canceled, because another item stole the mouse event handling.
This signal is for advanced use: it is useful when there is more than one MouseArea
that is handling input, or when there is a MouseArea inside a \l Flickable. In the latter
@@ -765,7 +766,8 @@ void QQuickMouseArea::mouseMoveEvent(QMouseEvent *event)
}
#endif
- QQuickMouseEvent me(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, false, d->longPress);
+ QQuickMouseEvent &me = d->quickMouseEvent;
+ me.reset(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, false, d->longPress);
me.setSource(event->source());
emit mouseXChanged(&me);
me.setPosition(d->lastPos);
@@ -807,7 +809,8 @@ void QQuickMouseArea::mouseDoubleClickEvent(QMouseEvent *event)
Q_D(QQuickMouseArea);
if (d->enabled) {
d->saveEvent(event);
- QQuickMouseEvent me(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, true, false);
+ QQuickMouseEvent &me = d->quickMouseEvent;
+ me.reset(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, true, false);
me.setSource(event->source());
me.setAccepted(d->isDoubleClickConnected());
emit this->doubleClicked(&me);
@@ -827,7 +830,8 @@ void QQuickMouseArea::hoverEnterEvent(QHoverEvent *event)
d->lastPos = event->posF();
d->lastModifiers = event->modifiers();
setHovered(true);
- QQuickMouseEvent me(d->lastPos.x(), d->lastPos.y(), Qt::NoButton, Qt::NoButton, d->lastModifiers, false, false);
+ QQuickMouseEvent &me = d->quickMouseEvent;
+ me.reset(d->lastPos.x(), d->lastPos.y(), Qt::NoButton, Qt::NoButton, d->lastModifiers, false, false);
emit mouseXChanged(&me);
me.setPosition(d->lastPos);
emit mouseYChanged(&me);
@@ -840,10 +844,11 @@ void QQuickMouseArea::hoverMoveEvent(QHoverEvent *event)
Q_D(QQuickMouseArea);
if (!d->enabled && !d->pressed) {
QQuickItem::hoverMoveEvent(event);
- } else {
+ } else if (d->lastPos != event->posF()) {
d->lastPos = event->posF();
d->lastModifiers = event->modifiers();
- QQuickMouseEvent me(d->lastPos.x(), d->lastPos.y(), Qt::NoButton, Qt::NoButton, d->lastModifiers, false, false);
+ QQuickMouseEvent &me = d->quickMouseEvent;
+ me.reset(d->lastPos.x(), d->lastPos.y(), Qt::NoButton, Qt::NoButton, d->lastModifiers, false, false);
emit mouseXChanged(&me);
me.setPosition(d->lastPos);
emit mouseYChanged(&me);
@@ -870,8 +875,9 @@ void QQuickMouseArea::wheelEvent(QWheelEvent *event)
return;
}
- QQuickWheelEvent we(event->posF().x(), event->posF().y(), event->angleDelta(),
- event->pixelDelta(), event->buttons(), event->modifiers());
+ QQuickWheelEvent &we = d->quickWheelEvent;
+ we.reset(event->posF().x(), event->posF().y(), event->angleDelta(), event->pixelDelta(),
+ event->buttons(), event->modifiers(), event->inverted());
we.setAccepted(d->isWheelConnected());
emit wheel(&we);
if (!we.isAccepted())
@@ -1004,7 +1010,8 @@ void QQuickMouseArea::timerEvent(QTimerEvent *event)
#endif
if (d->pressed && dragged == false && d->hovered == true) {
d->longPress = true;
- QQuickMouseEvent me(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, false, d->longPress);
+ QQuickMouseEvent &me = d->quickMouseEvent;
+ me.reset(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, false, d->longPress);
me.setSource(Qt::MouseEventSynthesizedByQt);
me.setAccepted(d->isPressAndHoldConnected());
emit pressAndHold(&me);
@@ -1085,8 +1092,7 @@ void QQuickMouseArea::setHoverEnabled(bool h)
\qmlproperty bool QtQuick::MouseArea::containsMouse
This property holds whether the mouse is currently inside the mouse area.
- \warning This property is not updated if the area moves under the mouse: \e containsMouse will not change.
- In addition, if hoverEnabled is false, containsMouse will only be valid
+ \warning If hoverEnabled is false, containsMouse will only be valid
when the mouse is pressed while the mouse cursor is inside the MouseArea.
*/
bool QQuickMouseArea::hovered() const
@@ -1127,6 +1133,7 @@ void QQuickMouseArea::setHovered(bool h)
{
Q_D(QQuickMouseArea);
if (d->hovered != h) {
+ qCDebug(DBG_HOVER_TRACE) << this << d->hovered << "->" << h;
d->hovered = h;
emit hoveredChanged();
d->hovered ? emit entered() : emit exited();
@@ -1182,7 +1189,8 @@ bool QQuickMouseArea::setPressed(Qt::MouseButton button, bool p, Qt::MouseEventS
Qt::MouseButtons oldPressed = d->pressed;
if (wasPressed != p) {
- QQuickMouseEvent me(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, isclick, d->longPress);
+ QQuickMouseEvent &me = d->quickMouseEvent;
+ me.reset(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, isclick, d->longPress);
me.setSource(source);
if (p) {
d->pressed |= button;
@@ -1192,6 +1200,11 @@ bool QQuickMouseArea::setPressed(Qt::MouseButton button, bool p, Qt::MouseEventS
emit mouseXChanged(&me);
me.setPosition(d->lastPos);
emit mouseYChanged(&me);
+
+ if (!me.isAccepted()) {
+ d->pressed = Qt::NoButton;
+ }
+
if (!oldPressed) {
emit pressedChanged();
emit containsPressChanged();
@@ -1348,8 +1361,8 @@ QSGNode *QQuickMouseArea::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData
if (!qmlVisualTouchDebugging())
return 0;
- QSGRectangleNode *rectangle = static_cast<QSGRectangleNode *>(oldNode);
- if (!rectangle) rectangle = d->sceneGraphContext()->createRectangleNode();
+ QSGInternalRectangleNode *rectangle = static_cast<QSGInternalRectangleNode *>(oldNode);
+ if (!rectangle) rectangle = d->sceneGraphContext()->createInternalRectangleNode();
rectangle->setRect(QRectF(0, 0, width(), height()));
rectangle->setColor(QColor(255, 0, 0, 50));
diff --git a/src/quick/items/qquickmousearea_p_p.h b/src/quick/items/qquickmousearea_p_p.h
index dc00ffe52c..b59e02910f 100644
--- a/src/quick/items/qquickmousearea_p_p.h
+++ b/src/quick/items/qquickmousearea_p_p.h
@@ -52,6 +52,7 @@
//
#include "qquickitem_p.h"
+#include "qquickevents_p_p.h"
#include <QtGui/qevent.h>
#include <QtCore/qbasictimer.h>
@@ -108,6 +109,8 @@ public:
#ifndef QT_NO_CURSOR
QCursor *cursor;
#endif
+ QQuickMouseEvent quickMouseEvent;
+ QQuickWheelEvent quickWheelEvent;
};
QT_END_NAMESPACE
diff --git a/src/quick/items/qquickmultipointtoucharea.cpp b/src/quick/items/qquickmultipointtoucharea.cpp
index 9d8e7aedd4..706980cd13 100644
--- a/src/quick/items/qquickmultipointtoucharea.cpp
+++ b/src/quick/items/qquickmultipointtoucharea.cpp
@@ -362,7 +362,7 @@ QQuickMultiPointTouchArea::QQuickMultiPointTouchArea(QQuickItem *parent)
QQuickMultiPointTouchArea::~QQuickMultiPointTouchArea()
{
clearTouchLists();
- foreach (QObject *obj, _touchPoints) {
+ for (QObject *obj : qAsConst(_touchPoints)) {
QQuickTouchPoint *dtp = static_cast<QQuickTouchPoint*>(obj);
if (!dtp->isQmlDefined())
delete dtp;
@@ -448,20 +448,12 @@ void QQuickMultiPointTouchArea::touchEvent(QTouchEvent *event)
}
}
updateTouchData(event);
- if (event->type() == QEvent::TouchEnd) {
- //TODO: move to window
- _stealMouse = false;
- setKeepMouseGrab(false);
- setKeepTouchGrab(false);
- ungrabTouchPoints();
- }
+ if (event->type() == QEvent::TouchEnd)
+ ungrab();
break;
}
case QEvent::TouchCancel:
- _stealMouse = false;
- setKeepMouseGrab(false);
- setKeepTouchGrab(false);
- ungrabTouchPoints();
+ ungrab();
break;
default:
QQuickItem::touchEvent(event);
@@ -478,8 +470,10 @@ void QQuickMultiPointTouchArea::grabGesture()
QVector<int> ids;
ids.reserve(_touchPoints.size());
- for (auto it = _touchPoints.keyBegin(), end = _touchPoints.keyEnd(); it != end; ++it)
- ids.append(*it);
+ for (auto it = _touchPoints.keyBegin(), end = _touchPoints.keyEnd(); it != end; ++it) {
+ if (*it != -1) // -1 might be the mouse-point, but we already grabbed the mouse above.
+ ids.append(*it);
+ }
grabTouchPoints(ids);
setKeepTouchGrab(true);
}
@@ -532,7 +526,7 @@ void QQuickMultiPointTouchArea::updateTouchData(QEvent *event)
}
int numTouchPoints = touchPoints.count();
//always remove released touches, and make sure we handle all releases before adds.
- foreach (const QTouchEvent::TouchPoint &p, touchPoints) {
+ for (const QTouchEvent::TouchPoint &p : qAsConst(touchPoints)) {
Qt::TouchPointState touchPointState = p.state();
int id = p.id();
if (touchPointState & Qt::TouchPointReleased) {
@@ -547,7 +541,7 @@ void QQuickMultiPointTouchArea::updateTouchData(QEvent *event)
}
}
if (numTouchPoints >= _minimumTouchPoints && numTouchPoints <= _maximumTouchPoints) {
- foreach (const QTouchEvent::TouchPoint &p, touchPoints) {
+ for (const QTouchEvent::TouchPoint &p : qAsConst(touchPoints)) {
Qt::TouchPointState touchPointState = p.state();
int id = p.id();
if (touchPointState & Qt::TouchPointReleased) {
@@ -557,13 +551,13 @@ void QQuickMultiPointTouchArea::updateTouchData(QEvent *event)
addTouchPoint(&p);
started = true;
} else if (touchPointState & Qt::TouchPointMoved) {
- QQuickTouchPoint* dtp = static_cast<QQuickTouchPoint*>(_touchPoints[id]);
+ QQuickTouchPoint* dtp = static_cast<QQuickTouchPoint*>(_touchPoints.value(id));
Q_ASSERT(dtp);
_movedTouchPoints.append(dtp);
updateTouchPoint(dtp,&p);
moved = true;
} else {
- QQuickTouchPoint* dtp = static_cast<QQuickTouchPoint*>(_touchPoints[id]);
+ QQuickTouchPoint* dtp = static_cast<QQuickTouchPoint*>(_touchPoints.value(id));
Q_ASSERT(dtp);
updateTouchPoint(dtp,&p);
}
@@ -573,7 +567,7 @@ void QQuickMultiPointTouchArea::updateTouchData(QEvent *event)
if (!_stealMouse /* !ignoring gesture*/) {
bool offerGrab = false;
const int dragThreshold = QGuiApplication::styleHints()->startDragDistance();
- foreach (const QTouchEvent::TouchPoint &p, touchPoints) {
+ for (const QTouchEvent::TouchPoint &p : qAsConst(touchPoints)) {
if (p.state() == Qt::TouchPointReleased)
continue;
const QPointF &currentPos = p.scenePos();
@@ -607,7 +601,7 @@ void QQuickMultiPointTouchArea::updateTouchData(QEvent *event)
void QQuickMultiPointTouchArea::clearTouchLists()
{
- foreach (QObject *obj, _releasedTouchPoints) {
+ for (QObject *obj : qAsConst(_releasedTouchPoints)) {
QQuickTouchPoint *dtp = static_cast<QQuickTouchPoint*>(obj);
if (!dtp->isQmlDefined()) {
_touchPoints.remove(dtp->pointId());
@@ -624,7 +618,7 @@ void QQuickMultiPointTouchArea::clearTouchLists()
void QQuickMultiPointTouchArea::addTouchPoint(const QTouchEvent::TouchPoint *p)
{
QQuickTouchPoint *dtp = 0;
- foreach (QQuickTouchPoint* tp, _touchPrototypes) {
+ for (QQuickTouchPoint* tp : qAsConst(_touchPrototypes)) {
if (!tp->inUse()) {
tp->setInUse(true);
dtp = tp;
@@ -644,7 +638,7 @@ void QQuickMultiPointTouchArea::addTouchPoint(const QTouchEvent::TouchPoint *p)
void QQuickMultiPointTouchArea::addTouchPoint(const QMouseEvent *e)
{
QQuickTouchPoint *dtp = 0;
- foreach (QQuickTouchPoint *tp, _touchPrototypes)
+ for (QQuickTouchPoint *tp : qAsConst(_touchPrototypes))
if (!tp->inUse()) {
tp->setInUse(true);
dtp = tp;
@@ -784,18 +778,17 @@ void QQuickMultiPointTouchArea::mouseReleaseEvent(QMouseEvent *event)
void QQuickMultiPointTouchArea::ungrab()
{
+ _stealMouse = false;
+ setKeepMouseGrab(false);
+ setKeepTouchGrab(false);
+ ungrabTouchPoints();
+
if (_touchPoints.count()) {
- QQuickWindow *c = window();
- if (c && c->mouseGrabberItem() == this) {
- _stealMouse = false;
- setKeepMouseGrab(false);
- }
- setKeepTouchGrab(false);
- foreach (QObject *obj, _touchPoints)
+ for (QObject *obj : qAsConst(_touchPoints))
static_cast<QQuickTouchPoint*>(obj)->setPressed(false);
emit canceled(_touchPoints.values());
clearTouchLists();
- foreach (QObject *obj, _touchPoints) {
+ for (QObject *obj : qAsConst(_touchPoints)) {
QQuickTouchPoint *dtp = static_cast<QQuickTouchPoint*>(obj);
if (!dtp->isQmlDefined())
delete dtp;
@@ -861,10 +854,10 @@ bool QQuickMultiPointTouchArea::sendMouseEvent(QMouseEvent *event)
return false;
}
-bool QQuickMultiPointTouchArea::childMouseEventFilter(QQuickItem *i, QEvent *event)
+bool QQuickMultiPointTouchArea::childMouseEventFilter(QQuickItem *receiver, QEvent *event)
{
if (!isEnabled() || !isVisible())
- return QQuickItem::childMouseEventFilter(i, event);
+ return QQuickItem::childMouseEventFilter(receiver, event);
switch (event->type()) {
case QEvent::MouseButtonPress:
case QEvent::MouseMove:
@@ -881,17 +874,13 @@ bool QQuickMultiPointTouchArea::childMouseEventFilter(QQuickItem *i, QEvent *eve
if (!shouldFilter(event))
return false;
updateTouchData(event);
- //TODO: verify this behavior
- _stealMouse = false;
- setKeepMouseGrab(false);
- setKeepTouchGrab(false);
- ungrabTouchPoints();
+ ungrab();
}
break;
default:
break;
}
- return QQuickItem::childMouseEventFilter(i, event);
+ return QQuickItem::childMouseEventFilter(receiver, event);
}
bool QQuickMultiPointTouchArea::shouldFilter(QEvent *event)
@@ -914,7 +903,7 @@ bool QQuickMultiPointTouchArea::shouldFilter(QEvent *event)
case QEvent::TouchUpdate:
case QEvent::TouchEnd: {
QTouchEvent *te = static_cast<QTouchEvent*>(event);
- foreach (const QTouchEvent::TouchPoint &point, te->touchPoints()) {
+ for (const QTouchEvent::TouchPoint &point : te->touchPoints()) {
if (contains(mapFromScene(point.scenePos()))) {
containsPoint = true;
break;
@@ -940,8 +929,8 @@ QSGNode *QQuickMultiPointTouchArea::updatePaintNode(QSGNode *oldNode, UpdatePain
if (!qmlVisualTouchDebugging())
return 0;
- QSGRectangleNode *rectangle = static_cast<QSGRectangleNode *>(oldNode);
- if (!rectangle) rectangle = QQuickItemPrivate::get(this)->sceneGraphContext()->createRectangleNode();
+ QSGInternalRectangleNode *rectangle = static_cast<QSGInternalRectangleNode *>(oldNode);
+ if (!rectangle) rectangle = QQuickItemPrivate::get(this)->sceneGraphContext()->createInternalRectangleNode();
rectangle->setRect(QRectF(0, 0, width(), height()));
rectangle->setColor(QColor(255, 0, 0, 50));
diff --git a/src/quick/items/qquickmultipointtoucharea_p.h b/src/quick/items/qquickmultipointtoucharea_p.h
index 52913f53f6..541eb04764 100644
--- a/src/quick/items/qquickmultipointtoucharea_p.h
+++ b/src/quick/items/qquickmultipointtoucharea_p.h
@@ -231,7 +231,7 @@ public:
static QQuickTouchPoint* touchPoint_at(QQmlListProperty<QQuickTouchPoint> *list, int index) {
QQuickMultiPointTouchArea *q = static_cast<QQuickMultiPointTouchArea*>(list->object);
- return q->_touchPrototypes[index];
+ return q->_touchPrototypes.value(index);
}
Q_SIGNALS:
@@ -247,7 +247,7 @@ Q_SIGNALS:
protected:
void touchEvent(QTouchEvent *) Q_DECL_OVERRIDE;
- bool childMouseEventFilter(QQuickItem *i, QEvent *event) Q_DECL_OVERRIDE;
+ bool childMouseEventFilter(QQuickItem *receiver, QEvent *event) Q_DECL_OVERRIDE;
void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
void mouseReleaseEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
void mouseMoveEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
diff --git a/src/quick/items/qquickopenglinfo.cpp b/src/quick/items/qquickopenglinfo.cpp
index b380a93e76..4bb13b84aa 100644
--- a/src/quick/items/qquickopenglinfo.cpp
+++ b/src/quick/items/qquickopenglinfo.cpp
@@ -61,6 +61,10 @@ QT_BEGIN_NAMESPACE
format. When it becomes associated with a surface, all properties
will update.
+ \deprecated
+
+ \warning This type is deprecated. Use GraphicsInfo instead.
+
\sa ShaderEffect
*/
QQuickOpenGLInfo::QQuickOpenGLInfo(QQuickItem *item)
diff --git a/src/quick/items/qquickopenglshadereffect.cpp b/src/quick/items/qquickopenglshadereffect.cpp
new file mode 100644
index 0000000000..b974641cca
--- /dev/null
+++ b/src/quick/items/qquickopenglshadereffect.cpp
@@ -0,0 +1,973 @@
+/****************************************************************************
+**
+** 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 <QtQml/qqmlfile.h>
+#include <QtCore/qsignalmapper.h>
+#include <QtCore/qfileselector.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;
+ }
+}
+
+namespace QtPrivate {
+class MappedSlotObject: public QtPrivate::QSlotObjectBase
+{
+public:
+ typedef std::function<void()> PropChangedFunc;
+
+ explicit MappedSlotObject(PropChangedFunc func)
+ : QSlotObjectBase(&impl), _signalIndex(-1), func(func)
+ { ref(); }
+
+ void setSignalIndex(int idx) { _signalIndex = idx; }
+ int signalIndex() const { return _signalIndex; }
+
+private:
+ int _signalIndex;
+ PropChangedFunc func;
+
+ static void impl(int which, QSlotObjectBase *this_, QObject *, void **a, bool *ret)
+ {
+ auto thiz = static_cast<MappedSlotObject*>(this_);
+ switch (which) {
+ case Destroy:
+ delete thiz;
+ break;
+ case Call:
+ thiz->func();
+ break;
+ case Compare:
+ *ret = thiz == reinterpret_cast<MappedSlotObject *>(a[0]);
+ break;
+ case NumOperations: ;
+ }
+ }
+};
+}
+
+QQuickOpenGLShaderEffectCommon::~QQuickOpenGLShaderEffectCommon()
+{
+ for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType)
+ clearSignalMappers(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);
+ auto mapper = signalMappers[shaderType].at(i);
+ void *a = mapper;
+ QObjectPrivate::disconnect(item, mapper->signalIndex(), &a);
+ 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,
+ const QMetaObject *itemMetaObject,
+ Key::ShaderType shaderType)
+{
+ QQmlPropertyCache *propCache = QQmlData::ensurePropertyCache(qmlEngine(item), item);
+ for (int i = 0; i < uniformData[shaderType].size(); ++i) {
+ if (signalMappers[shaderType].at(i) == 0)
+ continue;
+ const UniformData &d = uniformData[shaderType].at(i);
+ QQmlPropertyData *pd = propCache->property(QString::fromUtf8(d.name), nullptr, nullptr);
+ if (pd && !pd->isFunction()) {
+ if (pd->notifyIndex() == -1) {
+ qWarning("QQuickOpenGLShaderEffect: property '%s' does not have notification method!", d.name.constData());
+ } else {
+ auto *mapper = signalMappers[shaderType].at(i);
+ mapper->setSignalIndex(pd->notifyIndex());
+ Q_ASSERT(item->metaObject() == itemMetaObject);
+ QObjectPrivate::connectImpl(item, mapper->signalIndex(), item, nullptr, mapper,
+ Qt::AutoConnection, nullptr, itemMetaObject);
+ }
+ } 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).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,
+ const QMetaObject *itemMetaObject,
+ Key::ShaderType shaderType,
+ const QByteArray &code)
+{
+ QQmlPropertyCache *propCache = QQmlData::ensurePropertyCache(qmlEngine(item), item);
+ 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;
+ QtPrivate::MappedSlotObject *mapper = nullptr;
+ 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 {
+ if (QQmlPropertyData *pd = propCache->property(QString::fromUtf8(d.name), nullptr, nullptr)) {
+ if (!pd->isFunction())
+ d.propertyIndex = pd->coreIndex();
+ }
+ const int mappedId = uniformData[shaderType].size() | (shaderType << 16);
+ mapper = new QtPrivate::MappedSlotObject([this, mappedId](){
+ this->mappedPropertyChanged(mappedId);
+ });
+ bool sampler = typeLength == sampLen && qstrncmp("sampler2D", s + typeIndex, sampLen) == 0;
+ d.specialType = sampler ? UniformData::Sampler : UniformData::None;
+ d.setValueFromProperty(item, itemMetaObject);
+ }
+ uniformData[shaderType].append(d);
+ signalMappers[shaderType].append(mapper);
+ }
+ }
+}
+
+void QQuickOpenGLShaderEffectCommon::updateShader(QQuickItem *item,
+ const QMetaObject *itemMetaObject,
+ Key::ShaderType shaderType)
+{
+ disconnectPropertySignals(item, shaderType);
+ uniformData[shaderType].clear();
+ clearSignalMappers(shaderType);
+ if (shaderType == Key::VertexShader)
+ attributes.clear();
+
+ // A qrc or file URL means the shader source is to be read from the specified file.
+ QUrl srcUrl(QString::fromUtf8(source.sourceCode[shaderType]));
+ if (!srcUrl.scheme().compare(QLatin1String("qrc"), Qt::CaseInsensitive) || srcUrl.isLocalFile()) {
+ if (!fileSelector) {
+ fileSelector = new QFileSelector(item);
+ // There may not be an OpenGL context accessible here. So rely on
+ // the window's requestedFormat().
+ if (item->window()
+ && item->window()->requestedFormat().profile() == QSurfaceFormat::CoreProfile) {
+ fileSelector->setExtraSelectors(QStringList() << QStringLiteral("glslcore"));
+ }
+ }
+ const QString fn = fileSelector->select(QQmlFile::urlToLocalFileOrQrc(srcUrl));
+ QFile f(fn);
+ if (f.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ source.sourceCode[shaderType] = f.readAll();
+ f.close();
+ } else {
+ qWarning("ShaderEffect: Failed to read %s", qPrintable(fn));
+ source.sourceCode[shaderType] = QByteArray();
+ }
+ }
+
+ 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);
+ const int mappedId = 1 | (Key::FragmentShader << 16);
+ auto mapper = new QtPrivate::MappedSlotObject([this, mappedId](){mappedPropertyChanged(mappedId);});
+ const char *sourceName = "source";
+ d.name = sourceName;
+ d.setValueFromProperty(item, itemMetaObject);
+ d.specialType = UniformData::Sampler;
+ uniformData[Key::FragmentShader].append(d);
+ signalMappers[Key::FragmentShader].append(mapper);
+ }
+ } else {
+ lookThroughShaderCode(item, itemMetaObject, shaderType, code);
+ }
+
+ connectPropertySignals(item, itemMetaObject, 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,
+ const QMetaObject *itemMetaObject,
+ 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.setValueFromProperty(item, itemMetaObject);
+
+ 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.setValueFromProperty(item, itemMetaObject);
+ if (textureProviderChanged)
+ *textureProviderChanged = false;
+ }
+}
+
+void QQuickOpenGLShaderEffectCommon::clearSignalMappers(int shader)
+{
+ for (auto mapper : qAsConst(signalMappers[shader])) {
+ if (mapper)
+ mapper->destroyIfLastRef();
+ }
+ signalMappers[shader].clear();
+}
+
+QQuickOpenGLShaderEffect::QQuickOpenGLShaderEffect(QQuickShaderEffect *item, QObject *parent)
+ : QObject(parent)
+ , m_item(item)
+ , m_itemMetaObject(nullptr)
+ , m_meshResolution(1, 1)
+ , m_mesh(0)
+ , m_cullMode(QQuickShaderEffect::NoCulling)
+ , m_status(QQuickShaderEffect::Uncompiled)
+ , m_common(this, [this](int mappedId){this->propertyChanged(mappedId);})
+ , 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)
+ , m_vertNeedsUpdate(true)
+ , m_fragNeedsUpdate(true)
+{
+}
+
+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;
+
+ m_fragNeedsUpdate = true;
+ if (m_item->isComponentComplete())
+ maybeUpdateShaders();
+
+ 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;
+
+ m_vertNeedsUpdate = true;
+ if (m_item->isComponentComplete())
+ maybeUpdateShaders();
+
+ 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()
+{
+ maybeUpdateShaders(true);
+
+ 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, m_itemMetaObject,
+ (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, m_itemMetaObject, 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::maybeUpdateShaders(bool force)
+{
+ if (!m_itemMetaObject)
+ m_itemMetaObject = m_item->metaObject();
+
+ // Defer processing if a window is not yet associated with the item. This
+ // is because the actual scenegraph backend is not known so conditions
+ // based on GraphicsInfo.shaderType and similar evaluate to wrong results.
+ if (!m_item->window() && !force) {
+ m_item->polish();
+ return;
+ }
+
+ if (m_vertNeedsUpdate) {
+ m_vertNeedsUpdate = false;
+ m_common.updateShader(m_item, m_itemMetaObject, Key::VertexShader);
+ }
+
+ if (m_fragNeedsUpdate) {
+ m_fragNeedsUpdate = false;
+ m_common.updateShader(m_item, m_itemMetaObject, 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..64e79a9343
--- /dev/null
+++ b/src/quick/items/qquickopenglshadereffect_p.h
@@ -0,0 +1,198 @@
+/****************************************************************************
+**
+** 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 <private/qtquickglobal_p.h>
+
+QT_REQUIRE_CONFIG(quick_shadereffect);
+
+#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>
+#include <functional>
+
+QT_BEGIN_NAMESPACE
+
+class QSGContext;
+class QSignalMapper;
+class QFileSelector;
+class QQuickOpenGLCustomMaterialShader;
+
+namespace QtPrivate {
+class MappedSlotObject;
+}
+
+// Common class for QQuickOpenGLShaderEffect and QQuickCustomParticle.
+struct Q_QUICK_PRIVATE_EXPORT QQuickOpenGLShaderEffectCommon
+{
+ typedef QQuickOpenGLShaderEffectMaterialKey Key;
+ typedef QQuickOpenGLShaderEffectMaterial::UniformData UniformData;
+
+ QQuickOpenGLShaderEffectCommon(QObject *host, std::function<void(int)> mappedPropertyChanged)
+ : host(host), mappedPropertyChanged(mappedPropertyChanged), fileSelector(nullptr)
+ { }
+
+ ~QQuickOpenGLShaderEffectCommon();
+
+ void disconnectPropertySignals(QQuickItem *item, Key::ShaderType shaderType);
+ void connectPropertySignals(QQuickItem *item, const QMetaObject *itemMetaObject, Key::ShaderType shaderType);
+ void updateParseLog(bool ignoreAttributes);
+ void lookThroughShaderCode(QQuickItem *item, const QMetaObject *itemMetaObject, Key::ShaderType shaderType, const QByteArray &code);
+ void updateShader(QQuickItem *item, const QMetaObject *itemMetaObject, 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, const QMetaObject *itemMetaObject, int mappedId, bool *textureProviderChanged);
+
+ void clearSignalMappers(int shader);
+
+ QObject *host;
+ std::function<void(int)> mappedPropertyChanged;
+ Key source;
+ QVector<QByteArray> attributes;
+ QVector<UniformData> uniformData[Key::ShaderTypeCount];
+ QVector<QtPrivate::MappedSlotObject *> signalMappers[Key::ShaderTypeCount];
+ QString parseLog;
+ QFileSelector *fileSelector;
+};
+
+
+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 handleItemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value);
+ void maybeUpdateShaders(bool force = false);
+
+private Q_SLOTS:
+ void updateGeometry();
+ void updateGeometryIfAtlased();
+ void updateLogAndStatus(const QString &log, int status);
+ void sourceDestroyed(QObject *object);
+
+private:
+ void propertyChanged(int mappedId);
+
+ friend class QQuickCustomMaterialShader;
+ friend class QQuickOpenGLShaderEffectNode;
+
+ typedef QQuickOpenGLShaderEffectMaterialKey Key;
+ typedef QQuickOpenGLShaderEffectMaterial::UniformData UniformData;
+
+ QQuickShaderEffect *m_item;
+ const QMetaObject *m_itemMetaObject;
+ 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;
+ uint m_vertNeedsUpdate : 1;
+ uint m_fragNeedsUpdate : 1;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKOPENGLSHADEREFFECT_P_H
diff --git a/src/quick/items/qquickshadereffectnode.cpp b/src/quick/items/qquickopenglshadereffectnode.cpp
index 246a713dca..02b76b2dbc 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());
+ qDeleteAll(cache->cache);
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..aea28e6612 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
@@ -51,18 +51,23 @@
// We mean it.
//
+#include <private/qtquickglobal_p.h>
+
+QT_REQUIRE_CONFIG(quick_shadereffect);
+
#include <QtQuick/qsgnode.h>
#include <QtQuick/qsgmaterial.h>
#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 +77,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
@@ -89,30 +94,33 @@ public:
QByteArray name;
QVariant value;
+ int propertyIndex = -1;
SpecialType specialType;
bool operator == (const UniformData &other) const;
- };
- enum CullMode
- {
- NoCulling,
- BackFaceCulling,
- FrontFaceCulling
+ void setValueFromProperty(QObject *item, const QMetaObject *itemMetaObject)
+ {
+ if (propertyIndex == -1) {
+ value = item->property(name);
+ } else {
+ value = itemMetaObject->property(propertyIndex).read(item);
+ }
+ }
};
- 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 +135,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 +164,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..1ef8de753d 100644
--- a/src/quick/items/qquickpainteditem.cpp
+++ b/src/quick/items/qquickpainteditem.cpp
@@ -43,6 +43,7 @@
#include <QtQuick/private/qsgdefaultpainternode_p.h>
#include <QtQuick/private/qsgcontext_p.h>
#include <private/qsgadaptationlayer_p.h>
+#include <qsgtextureprovider.h>
#include <qmath.h>
@@ -63,13 +64,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 +80,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 +178,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 +503,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 +662,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/qquickpathview.cpp b/src/quick/items/qquickpathview.cpp
index c975207dad..e54ee66fe5 100644
--- a/src/quick/items/qquickpathview.cpp
+++ b/src/quick/items/qquickpathview.cpp
@@ -191,7 +191,8 @@ void QQuickPathView::initItem(int index, QObject *object)
att->m_view = this;
qreal percent = d->positionOfIndex(index);
if (percent < 1.0 && d->path) {
- foreach (const QString &attr, d->path->attributes())
+ const auto attributes = d->path->attributes();
+ for (const QString &attr : attributes)
att->setValue(attr.toUtf8(), d->path->attributeAt(attr, percent));
item->setZ(d->requestedZ);
}
@@ -230,7 +231,8 @@ QQmlOpenMetaObjectType *QQuickPathViewPrivate::attachedType()
// pre-create one metatype to share with all attached objects
attType = new QQmlOpenMetaObjectType(&QQuickPathViewAttached::staticMetaObject, qmlEngine(q));
if (path) {
- foreach (const QString &attr, path->attributes())
+ const auto attributes = path->attributes();
+ for (const QString &attr : attributes)
attType->createProperty(attr.toUtf8());
}
}
@@ -244,10 +246,9 @@ void QQuickPathViewPrivate::clear()
releaseItem(currentItem);
currentItem = 0;
}
- for (int i=0; i<items.count(); i++){
- QQuickItem *p = items[i];
+ for (QQuickItem *p : qAsConst(items))
releaseItem(p);
- }
+
if (requestedIndex >= 0) {
if (model)
model->cancel(requestedIndex);
@@ -417,12 +418,9 @@ void QQuickPathViewPrivate::setHighlightPosition(qreal pos)
void QQuickPathView::pathUpdated()
{
Q_D(QQuickPathView);
- QList<QQuickItem*>::iterator it = d->items.begin();
- while (it != d->items.end()) {
- QQuickItem *item = *it;
+ for (QQuickItem *item : qAsConst(d->items)) {
if (QQuickPathViewAttached *att = d->attached(item))
att->m_percent = -1;
- ++it;
}
refill();
}
@@ -435,7 +433,8 @@ void QQuickPathViewPrivate::updateItem(QQuickItem *item, qreal percent)
if (qFuzzyCompare(att->m_percent, percent))
return;
att->m_percent = percent;
- foreach (const QString &attr, path->attributes())
+ const auto attributes = path->attributes();
+ for (const QString &attr : attributes)
att->setValue(attr.toUtf8(), path->attributeAt(attr, percent));
att->setOnPath(percent < 1.0);
}
@@ -1518,17 +1517,8 @@ void QQuickPathView::positionViewAtIndex(int index, int mode)
int QQuickPathView::indexAt(qreal x, qreal y) const
{
Q_D(const QQuickPathView);
- if (!d->isValid())
- return -1;
-
- for (int idx = 0; idx < d->items.count(); ++idx) {
- QQuickItem *item = d->items.at(idx);
- QPointF p = item->mapFromItem(this, QPointF(x, y));
- if (item->contains(p))
- return d->model->indexOf(item, 0);
- }
-
- return -1;
+ QQuickItem *item = itemAt(x, y);
+ return item ? d->model->indexOf(item, 0) : -1;
}
/*!
@@ -1545,8 +1535,7 @@ QQuickItem *QQuickPathView::itemAt(qreal x, qreal y) const
if (!d->isValid())
return 0;
- for (int idx = 0; idx < d->items.count(); ++idx) {
- QQuickItem *item = d->items.at(idx);
+ for (QQuickItem *item : d->items) {
QPointF p = item->mapFromItem(this, QPointF(x, y));
if (item->contains(p))
return item;
@@ -1557,8 +1546,9 @@ QQuickItem *QQuickPathView::itemAt(qreal x, qreal y) const
QPointF QQuickPathViewPrivate::pointNear(const QPointF &point, qreal *nearPercent) const
{
- qreal samples = qMin(path->path().length()/5, qreal(500.0));
- qreal res = path->path().length()/samples;
+ const auto pathLength = path->path().length();
+ qreal samples = qMin(pathLength / 5, qreal(500.0));
+ qreal res = pathLength / samples;
qreal mindist = 1e10; // big number
QPointF nearPoint = path->pointAt(0);
@@ -1755,12 +1745,13 @@ void QQuickPathViewPrivate::handleMouseReleaseEvent(QMouseEvent *)
qreal velocity = calcVelocity();
qreal count = pathItems == -1 ? modelCount : qMin(pathItems, modelCount);
- qreal pixelVelocity = (path->path().length()/count) * velocity;
+ const auto averageItemLength = path->path().length() / count;
+ qreal pixelVelocity = averageItemLength * velocity;
if (qAbs(pixelVelocity) > MinimumFlickVelocity) {
if (qAbs(pixelVelocity) > maximumFlickVelocity || snapMode == QQuickPathView::SnapOneItem) {
// limit velocity
qreal maxVel = velocity < 0 ? -maximumFlickVelocity : maximumFlickVelocity;
- velocity = maxVel / (path->path().length()/count);
+ velocity = maxVel / averageItemLength;
}
// Calculate the distance to be travelled
qreal v2 = velocity*velocity;
@@ -2004,8 +1995,8 @@ void QQuickPathView::refill()
endPos = -1.0;
startPos = 2.0;
- for (int i = 0; i < d->items.count(); i++) {
- int idx = d->model->indexOf(d->items[i], 0);
+ for (QQuickItem * item : qAsConst(d->items)) {
+ int idx = d->model->indexOf(item, 0);
qreal curPos = d->positionOfIndex(idx);
if (curPos > endPos) {
endPos = curPos;
@@ -2096,7 +2087,7 @@ void QQuickPathView::refill()
if (!waiting && d->items.count() < count+d->cacheSize) {
qCDebug(lcItemViewDelegateLifecycle) << "Checking for pathview middle inserts, items count was" << d->items.count();
idx = startIdx;
- QQuickItem *lastItem = d->items[0];
+ QQuickItem *lastItem = d->items.at(0);
while (idx != endIdx) {
nextPos = d->positionOfIndex(idx);
if (d->isInBound(nextPos, d->mappedRange - d->mappedCache, 1.0 + d->mappedCache)) {
@@ -2164,8 +2155,9 @@ void QQuickPathView::refill()
if (QQuickPathViewAttached *att = d->attached(d->highlightItem))
att->setOnPath(currentVisible);
}
- while (d->itemCache.count())
- d->releaseItem(d->itemCache.takeLast());
+ for (QQuickItem *item : qAsConst(d->itemCache))
+ d->releaseItem(item);
+ d->itemCache.clear();
d->inRefill = false;
if (currentChanged)
@@ -2193,7 +2185,7 @@ void QQuickPathView::modelUpdated(const QQmlChangeSet &changeSet, bool reset)
int moveOffset = 0;
bool currentChanged = false;
bool changedOffset = false;
- foreach (const QQmlChangeSet::Change &r, changeSet.removes()) {
+ for (const QQmlChangeSet::Change &r : changeSet.removes()) {
if (moveId == -1 && d->currentIndex >= r.index + r.count) {
d->currentIndex -= r.count;
currentChanged = true;
@@ -2219,7 +2211,7 @@ void QQuickPathView::modelUpdated(const QQmlChangeSet &changeSet, bool reset)
}
d->modelCount -= r.count;
}
- foreach (const QQmlChangeSet::Change &i, changeSet.inserts()) {
+ for (const QQmlChangeSet::Change &i : changeSet.inserts()) {
if (d->modelCount) {
if (moveId == -1 && i.index <= d->currentIndex) {
d->currentIndex += i.count;
@@ -2249,8 +2241,9 @@ void QQuickPathView::modelUpdated(const QQmlChangeSet &changeSet, bool reset)
d->items.clear();
if (!d->modelCount) {
- while (d->itemCache.count())
- d->releaseItem(d->itemCache.takeLast());
+ for (QQuickItem * item : qAsConst(d->itemCache))
+ d->releaseItem(item);
+ d->itemCache.clear();
d->offset = 0;
changedOffset = true;
d->tl.reset(d->moveOffset);
@@ -2318,8 +2311,8 @@ void QQuickPathViewPrivate::createCurrentItem()
return;
bool inItems = false;
- for (int i = 0; i < items.count(); i++) {
- if (model->indexOf(items[i], 0) == currentIndex) {
+ for (QQuickItem *item : qAsConst(items)) {
+ if (model->indexOf(item, 0) == currentIndex) {
inItems = true;
break;
}
diff --git a/src/quick/items/qquickpathview_p.h b/src/quick/items/qquickpathview_p.h
index daec965f02..a44d1be5c4 100644
--- a/src/quick/items/qquickpathview_p.h
+++ b/src/quick/items/qquickpathview_p.h
@@ -51,8 +51,13 @@
// We mean it.
//
+#include <private/qtquickglobal_p.h>
+
+QT_REQUIRE_CONFIG(quick_pathview);
+
#include "qquickitem.h"
+#include <private/qtquickglobal_p.h>
#include <private/qquickpath_p.h>
QT_BEGIN_NAMESPACE
@@ -61,7 +66,7 @@ class QQmlChangeSet;
class QQuickPathViewPrivate;
class QQuickPathViewAttached;
-class Q_AUTOTEST_EXPORT QQuickPathView : public QQuickItem
+class Q_QUICK_PRIVATE_EXPORT QQuickPathView : public QQuickItem
{
Q_OBJECT
diff --git a/src/quick/items/qquickpathview_p_p.h b/src/quick/items/qquickpathview_p_p.h
index d9c4baf572..64abe3d1dc 100644
--- a/src/quick/items/qquickpathview_p_p.h
+++ b/src/quick/items/qquickpathview_p_p.h
@@ -51,6 +51,10 @@
// We mean it.
//
+#include <private/qtquickglobal_p.h>
+
+QT_REQUIRE_CONFIG(quick_pathview);
+
#include "qquickpathview_p.h"
#include "qquickitem_p.h"
@@ -61,6 +65,7 @@
#include <private/qquickanimation_p_p.h>
#include <private/qqmldelegatemodel_p.h>
#include <private/qquicktimeline_p_p.h>
+#include <private/qpodvector_p.h>
QT_BEGIN_NAMESPACE
@@ -75,9 +80,8 @@ public:
void init();
- void itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry) Q_DECL_OVERRIDE {
- if ((newGeometry.size() != oldGeometry.size())
- && (!highlightItem || item != highlightItem)) {
+ void itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change, const QRectF &) Q_DECL_OVERRIDE {
+ if (change.sizeChange() && (!highlightItem || item != highlightItem)) {
if (QQuickPathViewAttached *att = attached(item))
att->m_percent = -1;
scheduleLayout();
diff --git a/src/quick/items/qquickpositioners_p.h b/src/quick/items/qquickpositioners_p.h
index f6388f111d..c25ecd6dbc 100644
--- a/src/quick/items/qquickpositioners_p.h
+++ b/src/quick/items/qquickpositioners_p.h
@@ -51,6 +51,10 @@
// We mean it.
//
+#include <QtQuick/private/qtquickglobal_p.h>
+
+QT_REQUIRE_CONFIG(quick_positioners);
+
#include "qquickimplicitsizeitem_p.h"
#include "qquickitemviewtransition_p.h"
diff --git a/src/quick/items/qquickpositioners_p_p.h b/src/quick/items/qquickpositioners_p_p.h
index f0fbac2df7..6dd84e6098 100644
--- a/src/quick/items/qquickpositioners_p_p.h
+++ b/src/quick/items/qquickpositioners_p_p.h
@@ -51,6 +51,10 @@
// We mean it.
//
+#include <QtQuick/private/qtquickglobal_p.h>
+
+QT_REQUIRE_CONFIG(quick_positioners);
+
#include "qquickpositioners_p.h"
#include "qquickimplicitsizeitem_p_p.h"
@@ -137,9 +141,9 @@ public:
setPositioningDirty();
}
- void itemGeometryChanged(QQuickItem *, const QRectF &newGeometry, const QRectF &oldGeometry) Q_DECL_OVERRIDE
+ void itemGeometryChanged(QQuickItem *, QQuickGeometryChange change, const QRectF &) Q_DECL_OVERRIDE
{
- if (newGeometry.size() != oldGeometry.size())
+ if (change.sizeChange())
setPositioningDirty();
}
diff --git a/src/quick/items/qquickrectangle.cpp b/src/quick/items/qquickrectangle.cpp
index 05ff0c0fda..7ba2421d62 100644
--- a/src/quick/items/qquickrectangle.cpp
+++ b/src/quick/items/qquickrectangle.cpp
@@ -44,7 +44,6 @@
#include <private/qsgadaptationlayer_p.h>
#include <QtGui/qpixmapcache.h>
-#include <QtCore/qstringbuilder.h>
#include <QtCore/qmath.h>
#include <QtCore/qmetaobject.h>
@@ -73,7 +72,7 @@ QT_BEGIN_NAMESPACE
QQuickPen::QQuickPen(QObject *parent)
: QObject(parent)
, m_width(1)
- , m_color("#000000")
+ , m_color(Qt::black)
, m_aligned(true)
, m_valid(false)
{
@@ -483,8 +482,8 @@ QSGNode *QQuickRectangle::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData
return 0;
}
- QSGRectangleNode *rectangle = static_cast<QSGRectangleNode *>(oldNode);
- if (!rectangle) rectangle = d->sceneGraphContext()->createRectangleNode();
+ QSGInternalRectangleNode *rectangle = static_cast<QSGInternalRectangleNode *>(oldNode);
+ if (!rectangle) rectangle = d->sceneGraphContext()->createInternalRectangleNode();
rectangle->setRect(QRectF(0, 0, width(), height()));
rectangle->setColor(d->color);
diff --git a/src/quick/items/qquickrendercontrol.cpp b/src/quick/items/qquickrendercontrol.cpp
index e36df53d38..74aa2da9e0 100644
--- a/src/quick/items/qquickrendercontrol.cpp
+++ b/src/quick/items/qquickrendercontrol.cpp
@@ -44,7 +44,13 @@
#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>
+#if QT_CONFIG(quick_shadereffect)
+# include <QtQuick/private/qquickopenglshadereffectnode_p.h>
+#endif
+#endif
#include <QtGui/private/qguiapplication_p.h>
#include <qpa/qplatformintegration.h>
@@ -52,14 +58,13 @@
#include <QtQuick/QQuickWindow>
#include <QtQuick/private/qquickwindow_p.h>
+#include <QtQuick/private/qsgsoftwarerenderer_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
@@ -121,6 +126,10 @@ extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_
To send events, for example mouse or keyboard events, to the scene, use
QCoreApplication::sendEvent() with the QQuickWindow instance as the receiver.
+ \note In general QQuickRenderControl is supported in combination with all Qt
+ Quick backends. However, some functionality, in particular grab(), may not be
+ available in all cases.
+
\inmodule QtQuick
*/
@@ -134,7 +143,7 @@ QQuickRenderControlPrivate::QQuickRenderControlPrivate()
qAddPostRoutine(cleanup);
sg = QSGContext::createDefaultContext();
}
- rc = new QSGRenderContext(sg);
+ rc = sg->createRenderContext();
}
void QQuickRenderControlPrivate::cleanup()
@@ -183,7 +192,9 @@ void QQuickRenderControlPrivate::windowDestroyed()
delete QQuickWindowPrivate::get(window)->animationController;
QQuickWindowPrivate::get(window)->animationController = 0;
- QQuickShaderEffectMaterial::cleanupMaterialCache();
+#if QT_CONFIG(quick_shadereffect) && QT_CONFIG(opengl)
+ QQuickOpenGLShaderEffectMaterial::cleanupMaterialCache();
+#endif
window = 0;
}
@@ -204,8 +215,9 @@ void QQuickRenderControl::prepareThread(QThread *targetThread)
}
/*!
- Initializes the scene graph resources. The context \a gl has to
- be the current context.
+ Initializes the scene graph resources. The context \a gl has to be the
+ current OpenGL context or null if it is not relevant because a Qt Quick
+ backend other than OpenGL is in use.
\note Qt Quick does not take ownership of the context. It is up to the
application to destroy it after a call to invalidate() or after the
@@ -213,8 +225,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 +242,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;
}
@@ -247,7 +261,7 @@ void QQuickRenderControl::polishItems()
return;
QQuickWindowPrivate *cd = QQuickWindowPrivate::get(d->window);
- cd->flushDelayedTouchEvent();
+ cd->flushFrameSynchronousEvents();
if (!d->window)
return;
cd->polishItems();
@@ -362,8 +376,31 @@ QImage QQuickRenderControl::grab()
if (!d->window)
return QImage();
- render();
- QImage grabContent = qt_gl_read_framebuffer(d->window->size() * d->window->effectiveDevicePixelRatio(), false, false);
+ QImage grabContent;
+
+ if (d->window->rendererInterface()->graphicsApi() == QSGRendererInterface::OpenGL) {
+#ifndef QT_NO_OPENGL
+ render();
+ grabContent = qt_gl_read_framebuffer(d->window->size() * d->window->effectiveDevicePixelRatio(), false, false);
+#endif
+ } else if (d->window->rendererInterface()->graphicsApi() == QSGRendererInterface::Software) {
+ QQuickWindowPrivate *cd = QQuickWindowPrivate::get(d->window);
+ QSGSoftwareRenderer *softwareRenderer = static_cast<QSGSoftwareRenderer *>(cd->renderer);
+ if (softwareRenderer) {
+ const qreal dpr = d->window->effectiveDevicePixelRatio();
+ const QSize imageSize = d->window->size() * dpr;
+ grabContent = QImage(imageSize, QImage::Format_ARGB32_Premultiplied);
+ grabContent.setDevicePixelRatio(dpr);
+ QPaintDevice *prevDev = softwareRenderer->currentPaintDevice();
+ softwareRenderer->setCurrentPaintDevice(&grabContent);
+ softwareRenderer->markDirty();
+ render();
+ softwareRenderer->setCurrentPaintDevice(prevDev);
+ }
+ } else {
+ qWarning("QQuickRenderControl: grabs are not supported with the current Qt Quick backend");
+ }
+
return grabContent;
}
diff --git a/src/quick/items/qquickrepeater.cpp b/src/quick/items/qquickrepeater.cpp
index 198573fda5..4f46f41b0d 100644
--- a/src/quick/items/qquickrepeater.cpp
+++ b/src/quick/items/qquickrepeater.cpp
@@ -461,7 +461,7 @@ void QQuickRepeater::modelUpdated(const QQmlChangeSet &changeSet, bool reset)
int difference = 0;
QHash<int, QVector<QPointer<QQuickItem> > > moved;
- foreach (const QQmlChangeSet::Change &remove, changeSet.removes()) {
+ for (const QQmlChangeSet::Change &remove : changeSet.removes()) {
int index = qMin(remove.index, d->deletables.count());
int count = qMin(remove.index + remove.count, d->deletables.count()) - index;
if (remove.isMove()) {
@@ -483,7 +483,7 @@ void QQuickRepeater::modelUpdated(const QQmlChangeSet &changeSet, bool reset)
difference -= remove.count;
}
- foreach (const QQmlChangeSet::Change &insert, changeSet.inserts()) {
+ for (const QQmlChangeSet::Change &insert : changeSet.inserts()) {
int index = qMin(insert.index, d->deletables.count());
if (insert.isMove()) {
QVector<QPointer<QQuickItem> > items = moved.value(insert.moveId);
diff --git a/src/quick/items/qquickshadereffect.cpp b/src/quick/items/qquickshadereffect.cpp
index 5b337baac4..5670696ce2 100644
--- a/src/quick/items/qquickshadereffect.cpp
+++ b/src/quick/items/qquickshadereffect.cpp
@@ -38,506 +38,15 @@
****************************************************************************/
#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>
+#include <private/qquickitem_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 +55,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 +166,293 @@ 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::GraphicsInfo.
+
+ Starting from Qt 5.8 ShaderEffect also supports reading the GLSL source
+ code from files. Whenever the fragmentShader or vertexShader property value
+ is a URL with the \c file or \c qrc schema, it is treated as a file
+ reference and the source code is read from the specified file.
+
+ \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, GraphicsInfo.shaderType 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 OpenGL, backends for modern APIs will typically prefer offline
+ compilation and shipping pre-compiled bytecode with applications instead of
+ inlined shader source strings. In this case the string properties for
+ vertex and fragment shaders are treated as URLs referring to local files or
+ files shipped via the Qt resource system.
+
+ To check at runtime what is supported, use the
+ GraphicsInfo.shaderSourceType and GraphicsInfo.shaderCompilationType
+ properties. Note that these are bitmasks, because some backends may support
+ multiple approaches.
+
+ In case of Direct3D 12, all combinations are supported. If the vertexShader
+ and fragmentShader properties form a valid URL with the \c file or \c qrc
+ schema, the bytecode or HLSL source code is read from the specified file.
+ The type of the file contents is detected automatically. Otherwise, the
+ string is treated as HLSL source code and is compiled at runtime, assuming
+ Shader Model 5.0 and an entry point of \c{"main"}. This allows dynamically
+ constructing shader strings. However, whenever the shader source code is
+ static, it is strongly recommended to pre-compile to bytecode using the
+ \c fxc tool and refer to these files from QML. This will be a lot more
+ efficient at runtime and allows catching syntax errors in the shaders at
+ compile time.
+
+ Unlike OpenGL, the Direct3D backend is able to perform runtime shader
+ compilation on dedicated threads. This is managed transparently to the
+ applications, and means that ShaderEffect items that contain HLSL source
+ strings do not block the rendering or other parts of the application until
+ the bytecode is ready.
+
+ Using files with bytecode is more flexible also when it comes to the entry
+ point name (it can be anything, not limited to \c main) and the shader
+ model (it can be something newer than 5.0, for instance 5.1).
+
+ \table 70%
+ \row
+ \li \qml
+ import QtQuick 2.0
+
+ 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.
+
+ If desired, the HLSL source code can be placed directly into the QML
+ source, similarly to how its done with GLSL. The only difference in this
+ case is the entry point name, which must be \c main when using inline
+ source strings.
+
+ Alternatively, we could also have referred to a file containing the source
+ of the effect instead of the compiled bytecode version.
+
+ 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 \qml
+ import QtQuick 2.0
+
+ 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 Cross-platform, Cross-API ShaderEffect Items
+
+ Some applications will want to be functional with multiple accelerated
+ graphics backends. This has consequences for ShaderEffect items because the
+ supported shading languages may vary from backend to backend.
+
+ There are two approaches to handle this: either write conditional property
+ values based on GraphicsInfo.shaderType, or use file selectors. In practice
+ the latter is strongly recommended as it leads to more concise and cleaner
+ application code. The only case it is not suitable is when the source
+ strings are constructed dynamically.
+
+ \table 70%
+ \row
+ \li \qml
+ import QtQuick 2.8 // for GraphicsInfo
+
+ 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)
+ fragmentShader: GraphicsInfo.shaderType === GraphicsInfo.GLSL ?
+ "varying highp vec2 coord;
+ uniform sampler2D src;
+ uniform lowp float qt_Opacity;
+ void main() {
+ lowp vec4 tex = texture2D(src, coord);
+ gl_FragColor = vec4(vec3(dot(tex.rgb,
+ vec3(0.344, 0.5, 0.156))),
+ tex.a) * qt_Opacity;"
+ : GraphicsInfo.shaderType === GraphicsInfo.HLSL ?
+ "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;
+ }"
+ : ""
+ }
+ }
+ }
+ \endqml
+ \row
+
+ \li This is the first approach based on GraphicsInfo. Note that the value
+ reported by GraphicsInfo is not up-to-date until the ShaderEffect item gets
+ associated with a QQuickWindow. Before that, the reported value is
+ GraphicsInfo.UnknownShadingLanguage. The alternative is to place the GLSL
+ source code and the compiled D3D bytecode into the files
+ \c{shaders/effect.frag} and \c{shaders/+hlsl/effect.frag}, include them in
+ the Qt resource system, and let the ShaderEffect's internal QFileSelector
+ do its job. The selector-less version is the GLSL source, while the \c hlsl
+ selector is used when running on the D3D12 backend. The file under
+ \c{+hlsl} can then contain either HLSL source code or compiled bytecode
+ from the \c fxc tool. Additionally, when using a version 3.2 or newer core
+ profile context with OpenGL, GLSL sources with a core profile compatible
+ syntax can be placed under \c{+glslcore}.
+ \qml
+ import QtQuick 2.8 // for GraphicsInfo
+
+ 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)
+ fragmentShader: "qrc:shaders/effect.frag" // selects the correct variant automatically
+ }
+ }
+ }
+ \endqml
+ \endtable
\section1 ShaderEffect and Item Layers
@@ -675,97 +475,125 @@ 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}
*/
+class QQuickShaderEffectPrivate : public QQuickItemPrivate
+{
+ Q_DECLARE_PUBLIC(QQuickShaderEffect)
+
+public:
+ void updatePolish() override;
+};
+
+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(*new QQuickShaderEffectPrivate, 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, GraphicsInfo
*/
+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, GraphicsInfo
*/
+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 +605,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 +640,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 +672,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 +713,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 +744,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 +762,115 @@ 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)
+bool QQuickShaderEffect::event(QEvent *e)
{
- m_log = parseLog() + log;
- m_status = Status(status);
- emit logChanged();
- emit statusChanged();
+#ifndef QT_NO_OPENGL
+ if (m_glImpl) {
+ m_glImpl->handleEvent(e);
+ return QQuickItem::event(e);
+ }
+#endif
+ m_impl->handleEvent(e);
+ return QQuickItem::event(e);
}
-void QQuickShaderEffect::sourceDestroyed(QObject *object)
+void QQuickShaderEffect::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
{
- m_common.sourceDestroyed(object);
+#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);
}
-
-void QQuickShaderEffect::propertyChanged(int mappedId)
+QSGNode *QQuickShaderEffect::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *updatePaintNodeData)
{
- bool textureProviderChanged;
- m_common.propertyChanged(this, mappedId, &textureProviderChanged);
- m_dirtyTextureProviders |= textureProviderChanged;
- m_dirtyUniformValues = true;
- update();
+#ifndef QT_NO_OPENGL
+ if (m_glImpl)
+ return m_glImpl->handleUpdatePaintNode(oldNode, updatePaintNodeData);
+#endif
+ return m_impl->handleUpdatePaintNode(oldNode, updatePaintNodeData);
}
-void QQuickShaderEffect::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
+void QQuickShaderEffect::componentComplete()
{
- m_dirtyGeometry = true;
- QQuickItem::geometryChanged(newGeometry, oldGeometry);
+#ifndef QT_NO_OPENGL
+ if (m_glImpl) {
+ m_glImpl->maybeUpdateShaders();
+ QQuickItem::componentComplete();
+ return;
+ }
+#endif
+ m_impl->maybeUpdateShaders();
+ QQuickItem::componentComplete();
}
-QSGNode *QQuickShaderEffect::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
+void QQuickShaderEffect::itemChange(ItemChange change, const ItemChangeData &value)
{
- QQuickShaderEffectNode *node = static_cast<QQuickShaderEffectNode *>(oldNode);
-
- // 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;
- }
-
- 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);
- }
-
- QQuickShaderEffectMaterial *material = static_cast<QQuickShaderEffectMaterial *>(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 = QQuickShaderEffectMaterial::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 && 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, 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;
- }
-
- node->setGeometry(geometry);
- node->setFlag(QSGNode::OwnsGeometry, true);
-
- m_dirtyGeometry = false;
+#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);
+}
- return node;
+bool QQuickShaderEffect::isComponentComplete() const
+{
+ return QQuickItem::isComponentComplete();
}
-void QQuickShaderEffect::componentComplete()
+QString QQuickShaderEffect::parseLog() // for OpenGL-based autotests
{
- m_common.updateShader(this, Key::VertexShader);
- m_common.updateShader(this, Key::FragmentShader);
- QQuickItem::componentComplete();
+#ifndef QT_NO_OPENGL
+ if (m_glImpl)
+ return m_glImpl->parseLog();
+#endif
+ return m_impl->parseLog();
}
-void QQuickShaderEffect::itemChange(ItemChange change, const ItemChangeData &value)
+void QQuickShaderEffectPrivate::updatePolish()
{
- if (change == QQuickItem::ItemSceneChange)
- m_common.updateWindow(value.window);
- QQuickItem::itemChange(change, value);
+ Q_Q(QQuickShaderEffect);
+#ifndef QT_NO_OPENGL
+ if (q->m_glImpl) {
+ q->m_glImpl->maybeUpdateShaders();
+ return;
+ }
+#endif
+ q->m_impl->maybeUpdateShaders();
}
QT_END_NAMESPACE
diff --git a/src/quick/items/qquickshadereffect_p.h b/src/quick/items/qquickshadereffect_p.h
index fb266d4c44..2b7ff4cf6e 100644
--- a/src/quick/items/qquickshadereffect_p.h
+++ b/src/quick/items/qquickshadereffect_p.h
@@ -51,52 +51,18 @@
// We mean it.
//
-#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
+QT_REQUIRE_CONFIG(quick_shadereffect);
-const char *qtPositionAttributeName();
-const char *qtTexCoordAttributeName();
-
-class QSGContext;
-class QSignalMapper;
-class QQuickCustomMaterialShader;
+#include <QtQuick/qquickitem.h>
+#include <private/qtquickglobal_p.h>
-// 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;
-};
+QT_BEGIN_NAMESPACE
+class QQuickOpenGLShaderEffect;
+class QQuickGenericShaderEffect;
+class QQuickShaderEffectPrivate;
class Q_QUICK_PRIVATE_EXPORT QQuickShaderEffect : public QQuickItem
{
@@ -111,16 +77,14 @@ class Q_QUICK_PRIVATE_EXPORT QQuickShaderEffect : public QQuickItem
Q_PROPERTY(bool supportsAtlasTextures READ supportsAtlasTextures WRITE setSupportsAtlasTextures NOTIFY supportsAtlasTexturesChanged REVISION 1)
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
@@ -128,32 +92,30 @@ public:
Q_ENUM(Status)
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;
+ bool isComponentComplete() const;
+ QString parseLog();
Q_SIGNALS:
void fragmentShaderChanged();
@@ -166,44 +128,19 @@ Q_SIGNALS:
void supportsAtlasTexturesChanged();
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;
+
+ Q_DECLARE_PRIVATE(QQuickShaderEffect)
};
QT_END_NAMESPACE
diff --git a/src/quick/items/qquickshadereffectmesh.cpp b/src/quick/items/qquickshadereffectmesh.cpp
index 0811025654..b572feb5ee 100644
--- a/src/quick/items/qquickshadereffectmesh.cpp
+++ b/src/quick/items/qquickshadereffectmesh.cpp
@@ -40,9 +40,25 @@
#include "qquickshadereffectmesh_p.h"
#include <QtQuick/qsggeometry.h>
#include "qquickshadereffect_p.h"
+#include "qquickscalegrid_p_p.h"
+#include "qquickborderimage_p_p.h"
+#include <QtQuick/private/qsgbasicinternalimagenode_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)
{
@@ -67,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::UnsignedShortType);
} else {
geometry->allocate((vmesh + 1) * (hmesh + 1), vmesh * 2 * (hmesh + 2));
@@ -129,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;
@@ -217,4 +243,197 @@ QSize QQuickGridMesh::resolution() const
return m_resolution;
}
+/*!
+ \qmltype BorderImageMesh
+ \instantiates QQuickBorderImageMesh
+ \inqmlmodule QtQuick
+ \since 5.8
+ \ingroup qtquick-effects
+ \brief Defines a mesh with vertices arranged like those of a BorderImage.
+
+ BorderImageMesh provides BorderImage-like capabilities to a ShaderEffect
+ without the need for a potentially costly ShaderEffectSource.
+
+ The following are functionally equivalent:
+ \qml
+ BorderImage {
+ id: borderImage
+ border {
+ left: 10
+ right: 10
+ top: 10
+ bottom: 10
+ }
+ source: "myImage.png"
+ visible: false
+ }
+ ShaderEffectSource {
+ id: effectSource
+ sourceItem: borderImage
+ visible: false
+ }
+ ShaderEffect {
+ property var source: effectSource
+ ...
+ }
+ \endqml
+
+ \qml
+ Image {
+ id: image
+ source: "myImage.png"
+ visible: false
+ }
+ ShaderEffect {
+ property var source: image
+ mesh: BorderImageMesh {
+ border {
+ left: 10
+ right: 10
+ top: 10
+ bottom: 10
+ }
+ size: image.sourceSize
+ }
+ ...
+ }
+ \endqml
+
+ But the BorderImageMesh version can typically be better optimized.
+*/
+QQuickBorderImageMesh::QQuickBorderImageMesh(QObject *parent)
+ : QQuickShaderEffectMesh(parent), m_border(new QQuickScaleGrid(this)),
+ m_horizontalTileMode(QQuickBorderImageMesh::Stretch),
+ m_verticalTileMode(QQuickBorderImageMesh::Stretch)
+{
+}
+
+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);
+
+ QRectF sourceRect = srcRect;
+ QRectF modifiedInnerSourceRect(sourceRect.x() + innerSourceRect.x() * sourceRect.width(),
+ sourceRect.y() + innerSourceRect.y() * sourceRect.height(),
+ innerSourceRect.width() * sourceRect.width(),
+ innerSourceRect.height() * sourceRect.height());
+
+ geometry = QSGBasicInternalImageNode::updateGeometry(targetRect, innerTargetRect, sourceRect,
+ modifiedInnerSourceRect, subSourceRect, geometry);
+
+ return geometry;
+}
+
+/*!
+ \qmlpropertygroup QtQuick::BorderImageMesh::border
+ \qmlproperty int QtQuick::BorderImageMesh::border.left
+ \qmlproperty int QtQuick::BorderImageMesh::border.right
+ \qmlproperty int QtQuick::BorderImageMesh::border.top
+ \qmlproperty int QtQuick::BorderImageMesh::border.bottom
+
+ The 4 border lines (2 horizontal and 2 vertical) break the image into 9 sections,
+ as shown below:
+
+ \image declarative-scalegrid.png
+
+ Each border line (left, right, top, and bottom) specifies an offset in pixels
+ from the respective edge of the mesh. By default, each border line has
+ a value of 0.
+
+ For example, the following definition sets the bottom line 10 pixels up from
+ the bottom of the mesh:
+
+ \qml
+ BorderImageMesh {
+ border.bottom: 10
+ // ...
+ }
+ \endqml
+*/
+QQuickScaleGrid *QQuickBorderImageMesh::border()
+{
+ return m_border;
+}
+
+/*!
+ \qmlproperty size QtQuick::BorderImageMesh::size
+
+ The base size of the mesh. This generally corresponds to the \l {Image::}{sourceSize}
+ of the image being used by the ShaderEffect.
+*/
+QSize QQuickBorderImageMesh::size() const
+{
+ return m_size;
+}
+
+void QQuickBorderImageMesh::setSize(const QSize &size)
+{
+ if (size == m_size)
+ return;
+ m_size = size;
+ Q_EMIT sizeChanged();
+ Q_EMIT geometryChanged();
+}
+
+/*!
+ \qmlproperty enumeration QtQuick::BorderImageMesh::horizontalTileMode
+ \qmlproperty enumeration QtQuick::BorderImageMesh::verticalTileMode
+
+ This property describes how to repeat or stretch the middle parts of an image.
+
+ \list
+ \li BorderImage.Stretch - Scales the image to fit to the available area.
+ \li BorderImage.Repeat - Tile the image until there is no more space. May crop the last image.
+ \li BorderImage.Round - Like Repeat, but scales the images down to ensure that the last image is not cropped.
+ \endlist
+
+ The default tile mode for each property is BorderImage.Stretch.
+*/
+
+QQuickBorderImageMesh::TileMode QQuickBorderImageMesh::horizontalTileMode() const
+{
+ return m_horizontalTileMode;
+}
+
+void QQuickBorderImageMesh::setHorizontalTileMode(TileMode t)
+{
+ if (t == m_horizontalTileMode)
+ return;
+ m_horizontalTileMode = t;
+ Q_EMIT horizontalTileModeChanged();
+ Q_EMIT geometryChanged();
+}
+
+QQuickBorderImageMesh::TileMode QQuickBorderImageMesh::verticalTileMode() const
+{
+ return m_verticalTileMode;
+}
+
+void QQuickBorderImageMesh::setVerticalTileMode(TileMode t)
+{
+ if (t == m_verticalTileMode)
+ return;
+
+ m_verticalTileMode = t;
+ Q_EMIT verticalTileModeChanged();
+ Q_EMIT geometryChanged();
+}
+
QT_END_NAMESPACE
diff --git a/src/quick/items/qquickshadereffectmesh_p.h b/src/quick/items/qquickshadereffectmesh_p.h
index 7f9c95888c..cbf33b795f 100644
--- a/src/quick/items/qquickshadereffectmesh_p.h
+++ b/src/quick/items/qquickshadereffectmesh_p.h
@@ -37,14 +37,18 @@
**
****************************************************************************/
+#include <private/qtquickglobal_p.h>
+
+QT_REQUIRE_CONFIG(quick_shadereffect);
+
#include "qqmlparserstatus.h"
#include <QtQuick/qtquickglobal.h>
#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 +66,9 @@
QT_BEGIN_NAMESPACE
+const char *qtPositionAttributeName();
+const char *qtTexCoordAttributeName();
+
class QSGGeometry;
class QRectF;
@@ -70,8 +77,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 +95,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);
@@ -100,6 +111,48 @@ private:
QString m_log;
};
+class QQuickScaleGrid;
+class QQuickBorderImageMesh : public QQuickShaderEffectMesh
+{
+ Q_OBJECT
+
+ Q_PROPERTY(QQuickScaleGrid *border READ border CONSTANT)
+ Q_PROPERTY(QSize size READ size WRITE setSize NOTIFY sizeChanged)
+ Q_PROPERTY(TileMode horizontalTileMode READ horizontalTileMode WRITE setHorizontalTileMode NOTIFY horizontalTileModeChanged)
+ Q_PROPERTY(TileMode verticalTileMode READ verticalTileMode WRITE setVerticalTileMode NOTIFY verticalTileModeChanged)
+public:
+ QQuickBorderImageMesh(QObject *parent = 0);
+
+ 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();
+
+ enum TileMode { Stretch = Qt::StretchTile, Repeat = Qt::RepeatTile, Round = Qt::RoundTile };
+ Q_ENUM(TileMode)
+
+ QSize size() const;
+ void setSize(const QSize &size);
+
+ TileMode horizontalTileMode() const;
+ void setHorizontalTileMode(TileMode);
+
+ TileMode verticalTileMode() const;
+ void setVerticalTileMode(TileMode);
+
+Q_SIGNALS:
+ void sizeChanged();
+ void horizontalTileModeChanged();
+ void verticalTileModeChanged();
+
+private:
+ QQuickScaleGrid *m_border;
+ QSize m_size;
+ TileMode m_horizontalTileMode;
+ TileMode m_verticalTileMode;
+};
+
inline QColor qt_premultiply_color(const QColor &c)
{
return QColor::fromRgbF(c.redF() * c.alphaF(), c.greenF() * c.alphaF(), c.blueF() * c.alphaF(), c.alphaF());
diff --git a/src/quick/items/qquickshadereffectsource.cpp b/src/quick/items/qquickshadereffectsource.cpp
index a8cf6155a0..a60a06f59a 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>
@@ -309,11 +308,11 @@ QQuickItem *QQuickShaderEffectSource::sourceItem() const
return m_sourceItem;
}
-void QQuickShaderEffectSource::itemGeometryChanged(QQuickItem *item, const QRectF &newRect, const QRectF &oldRect)
+void QQuickShaderEffectSource::itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change, const QRectF &)
{
Q_ASSERT(item == m_sourceItem);
Q_UNUSED(item);
- if (newRect.size() != oldRect.size())
+ if (change.sizeChange())
update();
}
@@ -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);
@@ -709,9 +708,9 @@ QSGNode *QQuickShaderEffectSource::updatePaintNode(QSGNode *oldNode, UpdatePaint
return 0;
}
- QSGImageNode *node = static_cast<QSGImageNode *>(oldNode);
+ QSGInternalImageNode *node = static_cast<QSGInternalImageNode *>(oldNode);
if (!node) {
- node = d->sceneGraphContext()->createImageNode();
+ node = d->sceneGraphContext()->createInternalImageNode();
node->setFlag(QSGNode::UsePreprocess);
node->setTexture(m_texture);
QQuickShaderSourceAttachedNode *attached = new QQuickShaderSourceAttachedNode;
diff --git a/src/quick/items/qquickshadereffectsource_p.h b/src/quick/items/qquickshadereffectsource_p.h
index 92b04f4ca7..5e7e354feb 100644
--- a/src/quick/items/qquickshadereffectsource_p.h
+++ b/src/quick/items/qquickshadereffectsource_p.h
@@ -51,11 +51,15 @@
// We mean it.
//
+#include <QtQuick/private/qtquickglobal_p.h>
+
+QT_REQUIRE_CONFIG(quick_shadereffect);
+
#include "qquickitem.h"
#include <QtQuick/qsgtextureprovider.h>
#include <private/qsgadaptationlayer_p.h>
#include <QtQuick/private/qsgcontext_p.h>
-#include <private/qsgdefaultimagenode_p.h>
+#include <private/qsgdefaultinternalimagenode_p.h>
#include <private/qquickitemchangelistener_p.h>
#include "qpointer.h"
@@ -93,11 +97,11 @@ public:
Repeat
};
Q_ENUM(WrapMode)
-
+ // Equivalents to GL_ALPHA and similar type constants.
enum Format {
- Alpha = GL_ALPHA,
- RGB = GL_RGB,
- RGBA = GL_RGBA
+ Alpha = 0x1906,
+ RGB = 0x1907,
+ RGBA = 0x1908
};
Q_ENUM(Format)
@@ -168,7 +172,7 @@ protected:
void releaseResources() Q_DECL_OVERRIDE;
QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *) Q_DECL_OVERRIDE;
- void itemGeometryChanged(QQuickItem *item, const QRectF &newRect, const QRectF &oldRect) Q_DECL_OVERRIDE;
+ void itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change, const QRectF &) Q_DECL_OVERRIDE;
void itemChange(ItemChange change, const ItemChangeData &value) Q_DECL_OVERRIDE;
private:
diff --git a/src/quick/items/qquicksprite_p.h b/src/quick/items/qquicksprite_p.h
index 1b8c8cee84..d68a45ecc0 100644
--- a/src/quick/items/qquicksprite_p.h
+++ b/src/quick/items/qquicksprite_p.h
@@ -51,6 +51,10 @@
// We mean it.
//
+#include <private/qtquickglobal_p.h>
+
+QT_REQUIRE_CONFIG(quick_sprite);
+
#include <QObject>
#include <QUrl>
#include <QVariantMap>
@@ -300,7 +304,7 @@ private Q_SLOTS:
private:
friend class QQuickImageParticle;
- friend class QQuickSpriteSequence;
+ //friend class QQuickSpriteSequence;
friend class QQuickAnimatedSprite;
friend class QQuickSpriteEngine;
friend class QQuickStochasticEngine;
@@ -325,4 +329,5 @@ private:
};
QT_END_NAMESPACE
+
#endif // QQUICKSPRITE_P_H
diff --git a/src/quick/items/qquickspriteengine.cpp b/src/quick/items/qquickspriteengine.cpp
index df044988e0..10e0d51709 100644
--- a/src/quick/items/qquickspriteengine.cpp
+++ b/src/quick/items/qquickspriteengine.cpp
@@ -94,7 +94,7 @@ QQuickStochasticEngine::QQuickStochasticEngine(QObject *parent) :
setCount(1);
}
-QQuickStochasticEngine::QQuickStochasticEngine(QList<QQuickStochasticState*> states, QObject *parent) :
+QQuickStochasticEngine::QQuickStochasticEngine(const QList<QQuickStochasticState *> &states, QObject *parent) :
QObject(parent), m_states(states), m_timeOffset(0), m_addAdvance(false)
{
//Default size 1
@@ -110,10 +110,10 @@ QQuickSpriteEngine::QQuickSpriteEngine(QObject *parent)
{
}
-QQuickSpriteEngine::QQuickSpriteEngine(QList<QQuickSprite*> sprites, QObject *parent)
- : QQuickStochasticEngine(parent), m_startedImageAssembly(false), m_loaded(false)
+QQuickSpriteEngine::QQuickSpriteEngine(const QList<QQuickSprite *> &sprites, QObject *parent)
+ : QQuickSpriteEngine(parent)
{
- foreach (QQuickSprite* sprite, sprites)
+ for (QQuickSprite* sprite : sprites)
m_states << (QQuickStochasticState*)sprite;
}
@@ -122,7 +122,7 @@ QQuickSpriteEngine::~QQuickSpriteEngine()
}
-int QQuickSpriteEngine::maxFrames()
+int QQuickSpriteEngine::maxFrames() const
{
return m_maxFrames;
}
@@ -140,7 +140,7 @@ TODO: Above idea needs to have the varying duration offset added to it
m_startTimes will be set in advance/restart to 0->(m_framesPerRow-1) and can be used directly as extra.
This makes it 'frame' instead, but is more memory efficient than two arrays and less hideous than a vector of unions.
*/
-int QQuickSpriteEngine::pseudospriteProgress(int sprite, int state, int* rowDuration)
+int QQuickSpriteEngine::pseudospriteProgress(int sprite, int state, int* rowDuration) const
{
int myRowDuration = m_duration[sprite] * m_sprites[state]->m_framesPerRow / m_sprites[state]->m_frames;
if (rowDuration)
@@ -153,7 +153,7 @@ int QQuickSpriteEngine::pseudospriteProgress(int sprite, int state, int* rowDura
return (m_timeOffset - m_startTimes[sprite]) / myRowDuration;
}
-int QQuickSpriteEngine::spriteState(int sprite)
+int QQuickSpriteEngine::spriteState(int sprite) const
{
if (!m_loaded)
return 0;
@@ -174,7 +174,7 @@ int QQuickSpriteEngine::spriteState(int sprite)
return state + extra;
}
-int QQuickSpriteEngine::spriteStart(int sprite)
+int QQuickSpriteEngine::spriteStart(int sprite) const
{
if (!m_duration[sprite] || !m_loaded)
return m_timeOffset;
@@ -188,7 +188,7 @@ int QQuickSpriteEngine::spriteStart(int sprite)
return m_startTimes[sprite] + extra*rowDuration;
}
-int QQuickSpriteEngine::spriteFrames(int sprite)
+int QQuickSpriteEngine::spriteFrames(int sprite) const
{
if (!m_loaded)
return 1;
@@ -215,7 +215,7 @@ int QQuickSpriteEngine::spriteFrames(int sprite)
return m_sprites[state]->m_framesPerRow;
}
-int QQuickSpriteEngine::spriteDuration(int sprite)//Full duration, not per frame
+int QQuickSpriteEngine::spriteDuration(int sprite) const //Full duration, not per frame
{
if (!m_duration[sprite] || !m_loaded)
return m_duration[sprite];
@@ -235,7 +235,7 @@ int QQuickSpriteEngine::spriteDuration(int sprite)//Full duration, not per frame
return rowDuration;
}
-int QQuickSpriteEngine::spriteY(int sprite)
+int QQuickSpriteEngine::spriteY(int sprite) const
{
if (!m_loaded)
return 0;
@@ -257,7 +257,7 @@ int QQuickSpriteEngine::spriteY(int sprite)
return m_sprites[state]->m_rowY + m_sprites[state]->m_frameHeight * extra;
}
-int QQuickSpriteEngine::spriteX(int sprite)
+int QQuickSpriteEngine::spriteX(int sprite) const
{
if (!m_loaded)
return 0;
@@ -280,24 +280,24 @@ int QQuickSpriteEngine::spriteX(int sprite)
return m_sprites[state]->m_rowStartX;
}
-QQuickSprite* QQuickSpriteEngine::sprite(int sprite)
+QQuickSprite* QQuickSpriteEngine::sprite(int sprite) const
{
return m_sprites[m_things[sprite]];
}
-int QQuickSpriteEngine::spriteWidth(int sprite)
+int QQuickSpriteEngine::spriteWidth(int sprite) const
{
int state = m_things[sprite];
return m_sprites[state]->m_frameWidth;
}
-int QQuickSpriteEngine::spriteHeight(int sprite)
+int QQuickSpriteEngine::spriteHeight(int sprite) const
{
int state = m_things[sprite];
return m_sprites[state]->m_frameHeight;
}
-int QQuickSpriteEngine::spriteCount()//TODO: Actually image state count, need to rename these things to make sense together
+int QQuickSpriteEngine::spriteCount() const //TODO: Actually image state count, need to rename these things to make sense together
{
return m_imageStateCount;
}
@@ -312,14 +312,14 @@ void QQuickStochasticEngine::setGoal(int state, int sprite, bool jump)
return;
}
- if (m_things[sprite] == state)
+ if (m_things.at(sprite) == state)
return;//Already there
m_things[sprite] = state;
- m_duration[sprite] = m_states[state]->variedDuration();
+ m_duration[sprite] = m_states.at(state)->variedDuration();
m_goals[sprite] = -1;
restart(sprite);
emit stateChanged(sprite);
- emit m_states[state]->entered();
+ emit m_states.at(state)->entered();
return;
}
@@ -329,7 +329,7 @@ QQuickPixmap::Status QQuickSpriteEngine::status()//Composed status of all Sprite
return QQuickPixmap::Null;
int null, loading, ready;
null = loading = ready = 0;
- foreach (QQuickSprite* s, m_sprites) {
+ for (QQuickSprite* s : qAsConst(m_sprites)) {
switch (s->m_pix.status()) {
// ### Maybe add an error message here, because this null shouldn't be reached but when it does, the image fails without an error message.
case QQuickPixmap::Null : null++; break;
@@ -358,7 +358,7 @@ void QQuickSpriteEngine::startAssemblingImage()
QList<QQuickStochasticState*> removals;
- foreach (QQuickStochasticState* s, m_states){
+ for (QQuickStochasticState* s : qAsConst(m_states)) {
QQuickSprite* sprite = qobject_cast<QQuickSprite*>(s);
if (sprite) {
m_sprites << sprite;
@@ -367,16 +367,16 @@ void QQuickSpriteEngine::startAssemblingImage()
qDebug() << "Error: Non-sprite in QQuickSpriteEngine";
}
}
- foreach (QQuickStochasticState* s, removals)
+ for (QQuickStochasticState* s : qAsConst(removals))
m_states.removeAll(s);
m_startedImageAssembly = true;
}
-QImage QQuickSpriteEngine::assembledImage()
+QImage QQuickSpriteEngine::assembledImage(int maxSize)
{
QQuickPixmap::Status stat = status();
if (!m_errorsPrinted && stat == QQuickPixmap::Error) {
- foreach (QQuickSprite* s, m_sprites)
+ for (QQuickSprite* s : qAsConst(m_sprites))
if (s->m_pix.isError())
qmlInfo(s) << s->m_pix.error();
m_errorsPrinted = true;
@@ -389,17 +389,8 @@ QImage QQuickSpriteEngine::assembledImage()
int w = 0;
m_maxFrames = 0;
m_imageStateCount = 0;
- int maxSize = 0;
-
- //If there is no current OpenGL Context
- if (!QOpenGLContext::currentContext())
- return QImage();
- QOpenGLContext::currentContext()->functions()->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxSize);
-#ifdef SPRITE_IMAGE_DEBUG
- qDebug() << "MAX TEXTURE SIZE" << maxSize;
-#endif
- foreach (QQuickSprite* state, m_sprites){
+ for (QQuickSprite* state : qAsConst(m_sprites)) {
if (state->frames() > m_maxFrames)
m_maxFrames = state->frames();
@@ -450,7 +441,7 @@ QImage QQuickSpriteEngine::assembledImage()
image.fill(0);
QPainter p(&image);
int y = 0;
- foreach (QQuickSprite* state, m_sprites){
+ for (QQuickSprite* state : qAsConst(m_sprites)) {
QImage img(state->m_pix.image());
int frameWidth = state->m_frameWidth;
int frameHeight = state->m_frameHeight;
@@ -525,8 +516,8 @@ void QQuickStochasticEngine::start(int index, int state)
if (index >= m_things.count())
return;
m_things[index] = state;
- m_duration[index] = m_states[state]->variedDuration();
- if (m_states[state]->randomStart())
+ m_duration[index] = m_states.at(state)->variedDuration();
+ if (m_states.at(state)->randomStart())
m_startTimes[index] = NINF;
else
m_startTimes[index] = 0;
@@ -547,33 +538,33 @@ void QQuickStochasticEngine::stop(int index)
void QQuickStochasticEngine::restart(int index)
{
- bool randomStart = (m_startTimes[index] == NINF);
+ bool randomStart = (m_startTimes.at(index) == NINF);
m_startTimes[index] = m_timeOffset;
if (m_addAdvance)
m_startTimes[index] += m_advanceTime.elapsed();
if (randomStart)
- m_startTimes[index] -= qrand() % m_duration[index];
- int time = m_duration[index] + m_startTimes[index];
+ m_startTimes[index] -= qrand() % m_duration.at(index);
+ int time = m_duration.at(index) + m_startTimes.at(index);
for (int i=0; i<m_stateUpdates.count(); i++)
m_stateUpdates[i].second.removeAll(index);
- if (m_duration[index] >= 0)
+ if (m_duration.at(index) >= 0)
addToUpdateList(time, index);
}
void QQuickSpriteEngine::restart(int index) //Reimplemented to recognize and handle pseudostates
{
- bool randomStart = (m_startTimes[index] == NINF);
- if (m_loaded && m_sprites[m_things[index]]->frameSync()) {//Manually advanced
+ bool randomStart = (m_startTimes.at(index) == NINF);
+ if (m_loaded && m_sprites.at(m_things.at(index))->frameSync()) {//Manually advanced
m_startTimes[index] = 0;
- if (randomStart && m_sprites[m_things[index]]->m_generatedCount)
- m_startTimes[index] += qrand() % m_sprites[m_things[index]]->m_generatedCount;
+ if (randomStart && m_sprites.at(m_things.at(index))->m_generatedCount)
+ m_startTimes[index] += qrand() % m_sprites.at(m_things.at(index))->m_generatedCount;
} else {
m_startTimes[index] = m_timeOffset;
if (m_addAdvance)
m_startTimes[index] += m_advanceTime.elapsed();
if (randomStart)
- m_startTimes[index] -= qrand() % m_duration[index];
- int time = spriteDuration(index) + m_startTimes[index];
+ m_startTimes[index] -= qrand() % m_duration.at(index);
+ int time = spriteDuration(index) + m_startTimes.at(index);
if (randomStart) {
int curTime = m_timeOffset + (m_addAdvance ? m_advanceTime.elapsed() : 0);
while (time < curTime) //Fast forward through psuedostates as needed
@@ -590,11 +581,11 @@ void QQuickStochasticEngine::advance(int idx)
{
if (idx >= m_things.count())
return;//TODO: Proper fix(because this has happened and I just ignored it)
- int nextIdx = nextState(m_things[idx],idx);
+ int nextIdx = nextState(m_things.at(idx), idx);
m_things[idx] = nextIdx;
- m_duration[idx] = m_states[nextIdx]->variedDuration();
+ m_duration[idx] = m_states.at(nextIdx)->variedDuration();
restart(idx);
- emit m_states[nextIdx]->entered();
+ emit m_states.at(nextIdx)->entered();
emit stateChanged(idx);
}
@@ -607,29 +598,29 @@ void QQuickSpriteEngine::advance(int idx) //Reimplemented to recognize and handl
if (idx >= m_things.count())
return;//TODO: Proper fix(because this has happened and I just ignored it)
- if (m_duration[idx] == 0) {
- if (m_sprites[m_things[idx]]->frameSync()) {
+ if (m_duration.at(idx) == 0) {
+ if (m_sprites.at(m_things.at(idx))->frameSync()) {
//Manually called, advance inner substate count
m_startTimes[idx]++;
- if (m_startTimes[idx] < m_sprites[m_things[idx]]->m_generatedCount) {
+ if (m_startTimes.at(idx) < m_sprites.at(m_things.at(idx))->m_generatedCount) {
//only a pseudostate ended
emit stateChanged(idx);
return;
}
}
//just go past the pseudostate logic
- } else if (m_startTimes[idx] + m_duration[idx]
+ } else if (m_startTimes.at(idx) + m_duration.at(idx)
> int(m_timeOffset + (m_addAdvance ? m_advanceTime.elapsed() : 0))) {
//only a pseduostate ended
emit stateChanged(idx);
addToUpdateList(spriteStart(idx) + spriteDuration(idx) + (m_addAdvance ? m_advanceTime.elapsed() : 0), idx);
return;
}
- int nextIdx = nextState(m_things[idx],idx);
+ int nextIdx = nextState(m_things.at(idx), idx);
m_things[idx] = nextIdx;
- m_duration[idx] = m_states[nextIdx]->variedDuration();
+ m_duration[idx] = m_states.at(nextIdx)->variedDuration();
restart(idx);
- emit m_states[nextIdx]->entered();
+ emit m_states.at(nextIdx)->entered();
emit stateChanged(idx);
}
@@ -640,16 +631,16 @@ int QQuickStochasticEngine::nextState(int curState, int curThing)
if (goalPath == -1){//Random
qreal r =(qreal) qrand() / (qreal) RAND_MAX;
qreal total = 0.0;
- for (QVariantMap::const_iterator iter=m_states[curState]->m_to.constBegin();
- iter!=m_states[curState]->m_to.constEnd(); ++iter)
+ for (QVariantMap::const_iterator iter=m_states.at(curState)->m_to.constBegin();
+ iter!=m_states.at(curState)->m_to.constEnd(); ++iter)
total += (*iter).toReal();
r*=total;
- for (QVariantMap::const_iterator iter= m_states[curState]->m_to.constBegin();
- iter!=m_states[curState]->m_to.constEnd(); ++iter){
+ for (QVariantMap::const_iterator iter= m_states.at(curState)->m_to.constBegin();
+ iter!=m_states.at(curState)->m_to.constEnd(); ++iter){
if (r < (*iter).toReal()){
bool superBreak = false;
for (int i=0; i<m_states.count(); i++){
- if (m_states[i]->name() == iter.key()){
+ if (m_states.at(i)->name() == iter.key()){
nextIdx = i;
superBreak = true;
break;
@@ -673,8 +664,9 @@ uint QQuickStochasticEngine::updateSprites(uint time)//### would returning a lis
//Sprite State Update;
m_timeOffset = time;
m_addAdvance = false;
- while (!m_stateUpdates.isEmpty() && time >= m_stateUpdates.first().first){
- foreach (int idx, m_stateUpdates.first().second)
+ while (!m_stateUpdates.isEmpty() && time >= m_stateUpdates.constFirst().first){
+ const auto copy = m_stateUpdates.constFirst().second;
+ for (int idx : copy)
advance(idx);
m_stateUpdates.pop_front();
}
@@ -683,14 +675,14 @@ uint QQuickStochasticEngine::updateSprites(uint time)//### would returning a lis
m_addAdvance = true;
if (m_stateUpdates.isEmpty())
return uint(-1);
- return m_stateUpdates.first().first;
+ return m_stateUpdates.constFirst().first;
}
int QQuickStochasticEngine::goalSeek(int curIdx, int spriteIdx, int dist)
{
QString goalName;
- if (m_goals[spriteIdx] != -1)
- goalName = m_states[m_goals[spriteIdx]]->name();
+ if (m_goals.at(spriteIdx) != -1)
+ goalName = m_states.at(m_goals.at(spriteIdx))->name();
else
goalName = m_globalGoal;
if (goalName.isEmpty())
@@ -698,16 +690,16 @@ int QQuickStochasticEngine::goalSeek(int curIdx, int spriteIdx, int dist)
//TODO: caching instead of excessively redoing iterative deepening (which was chosen arbitrarily anyways)
// Paraphrased - implement in an *efficient* manner
for (int i=0; i<m_states.count(); i++)
- if (m_states[curIdx]->name() == goalName)
+ if (m_states.at(curIdx)->name() == goalName)
return curIdx;
if (dist < 0)
dist = m_states.count();
- QQuickStochasticState* curState = m_states[curIdx];
+ QQuickStochasticState* curState = m_states.at(curIdx);
for (QVariantMap::const_iterator iter = curState->m_to.constBegin();
iter!=curState->m_to.constEnd(); ++iter){
if (iter.key() == goalName)
for (int i=0; i<m_states.count(); i++)
- if (m_states[i]->name() == goalName)
+ if (m_states.at(i)->name() == goalName)
return i;
}
QSet<int> options;
@@ -716,7 +708,7 @@ int QQuickStochasticEngine::goalSeek(int curIdx, int spriteIdx, int dist)
iter!=curState->m_to.constEnd(); ++iter){
int option = -1;
for (int j=0; j<m_states.count(); j++)//One place that could be a lot more efficient...
- if (m_states[j]->name() == iter.key())
+ if (m_states.at(j)->name() == iter.key())
if (goalSeek(j, spriteIdx, i) != -1)
option = j;
if (option != -1)
@@ -730,13 +722,13 @@ int QQuickStochasticEngine::goalSeek(int curIdx, int spriteIdx, int dist)
qreal total = 0;
for (QSet<int>::const_iterator iter=options.constBegin();
iter!=options.constEnd(); ++iter)
- total += curState->m_to.value(m_states[(*iter)]->name()).toReal();
+ total += curState->m_to.value(m_states.at((*iter))->name()).toReal();
r *= total;
for (QVariantMap::const_iterator iter = curState->m_to.constBegin();
iter!=curState->m_to.constEnd(); ++iter){
bool superContinue = true;
for (int j=0; j<m_states.count(); j++)
- if (m_states[j]->name() == iter.key())
+ if (m_states.at(j)->name() == iter.key())
if (options.contains(j))
superContinue = false;
if (superContinue)
@@ -744,7 +736,7 @@ int QQuickStochasticEngine::goalSeek(int curIdx, int spriteIdx, int dist)
if (r < (*iter).toReal()){
bool superBreak = false;
for (int j=0; j<m_states.count(); j++){
- if (m_states[j]->name() == iter.key()){
+ if (m_states.at(j)->name() == iter.key()){
option = j;
superBreak = true;
break;
@@ -764,10 +756,10 @@ int QQuickStochasticEngine::goalSeek(int curIdx, int spriteIdx, int dist)
void QQuickStochasticEngine::addToUpdateList(uint t, int idx)
{
for (int i=0; i<m_stateUpdates.count(); i++){
- if (m_stateUpdates[i].first==t){
+ if (m_stateUpdates.at(i).first == t){
m_stateUpdates[i].second << idx;
return;
- }else if (m_stateUpdates[i].first > t){
+ } else if (m_stateUpdates.at(i).first > t) {
QList<int> tmpList;
tmpList << idx;
m_stateUpdates.insert(i, qMakePair(t, tmpList));
diff --git a/src/quick/items/qquickspriteengine_p.h b/src/quick/items/qquickspriteengine_p.h
index 65f58fafcb..cf50cd2d84 100644
--- a/src/quick/items/qquickspriteengine_p.h
+++ b/src/quick/items/qquickspriteengine_p.h
@@ -51,6 +51,10 @@
// We mean it.
//
+#include <private/qtquickglobal_p.h>
+
+QT_REQUIRE_CONFIG(quick_sprite);
+
#include <QObject>
#include <QVector>
#include <QTimer>
@@ -189,7 +193,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickStochasticEngine : public QObject
Q_PROPERTY(QQmlListProperty<QQuickStochasticState> states READ states)
public:
explicit QQuickStochasticEngine(QObject *parent = 0);
- QQuickStochasticEngine(QList<QQuickStochasticState*> states, QObject *parent=0);
+ QQuickStochasticEngine(const QList<QQuickStochasticState*> &states, QObject *parent = 0);
~QQuickStochasticEngine();
QQmlListProperty<QQuickStochasticState> states()
@@ -210,11 +214,11 @@ public:
virtual void restart(int index=0);
virtual void advance(int index=0);//Sends state to the next chosen state, unlike goal.
void stop(int index=0);
- int curState(int index=0) {return m_things[index];}
+ int curState(int index=0) const {return m_things[index];}
- QQuickStochasticState* state(int idx){return m_states[idx];}
- int stateIndex(QQuickStochasticState* s){return m_states.indexOf(s);}
- int stateIndex(const QString& s) {
+ QQuickStochasticState* state(int idx) const {return m_states[idx];}
+ int stateIndex(QQuickStochasticState* s) const {return m_states.indexOf(s);}
+ int stateIndex(const QString& s) const {
for (int i=0; i<m_states.count(); i++)
if (m_states[i]->name() == s)
return i;
@@ -266,24 +270,24 @@ class Q_QUICK_PRIVATE_EXPORT QQuickSpriteEngine : public QQuickStochasticEngine
Q_PROPERTY(QQmlListProperty<QQuickSprite> sprites READ sprites)
public:
explicit QQuickSpriteEngine(QObject *parent = 0);
- QQuickSpriteEngine(QList<QQuickSprite*> sprites, QObject *parent=0);
+ QQuickSpriteEngine(const QList<QQuickSprite*> &sprites, QObject *parent = 0);
~QQuickSpriteEngine();
QQmlListProperty<QQuickSprite> sprites()
{
return QQmlListProperty<QQuickSprite>(this, m_sprites);
}
- QQuickSprite* sprite(int sprite=0);
- int spriteState(int sprite=0);
- int spriteStart(int sprite=0);
- int spriteFrames(int sprite=0);
- int spriteDuration(int sprite=0);
- int spriteX(int sprite=0);
- int spriteY(int sprite=0);
- int spriteWidth(int sprite=0);
- int spriteHeight(int sprite=0);
- int spriteCount();//Like state count
- int maxFrames();
+ QQuickSprite* sprite(int sprite = 0) const;
+ int spriteState(int sprite = 0) const;
+ int spriteStart(int sprite = 0) const;
+ int spriteFrames(int sprite = 0) const;
+ int spriteDuration(int sprite = 0) const;
+ int spriteX(int sprite = 0) const;
+ int spriteY(int sprite = 0) const;
+ int spriteWidth(int sprite = 0) const;
+ int spriteHeight(int sprite = 0) const;
+ int spriteCount() const;//Like state count
+ int maxFrames() const;
void restart(int index=0) Q_DECL_OVERRIDE;
void advance(int index=0) Q_DECL_OVERRIDE;
@@ -295,10 +299,10 @@ public:
bool isError() { return status() == QQuickPixmap::Error; }
QQuickPixmap::Status status();//Composed status of all Sprites
void startAssemblingImage();
- QImage assembledImage();
+ QImage assembledImage(int maxSize = 2048);
private:
- int pseudospriteProgress(int,int,int*rd=0);
+ int pseudospriteProgress(int, int, int *rd = 0) const;
QList<QQuickSprite*> m_sprites;
bool m_startedImageAssembly;
bool m_loaded;
diff --git a/src/quick/items/qquickspritesequence.cpp b/src/quick/items/qquickspritesequence.cpp
index f32e1afd50..25a39f951a 100644
--- a/src/quick/items/qquickspritesequence.cpp
+++ b/src/quick/items/qquickspritesequence.cpp
@@ -38,6 +38,7 @@
****************************************************************************/
#include "qquickspritesequence_p.h"
+#include "qquickspritesequence_p_p.h"
#include "qquicksprite_p.h"
#include "qquickspriteengine_p.h"
#include <QtQuick/private/qsgcontext_p.h>
@@ -54,111 +55,6 @@
QT_BEGIN_NAMESPACE
-class QQuickSpriteSequenceMaterial : public QSGMaterial
-{
-public:
- QQuickSpriteSequenceMaterial();
- ~QQuickSpriteSequenceMaterial();
- QSGMaterialType *type() const Q_DECL_OVERRIDE{ static QSGMaterialType type; return &type; }
- QSGMaterialShader *createShader() const Q_DECL_OVERRIDE;
- int compare(const QSGMaterial *other) const Q_DECL_OVERRIDE
- {
- return this - static_cast<const QQuickSpriteSequenceMaterial *>(other);
- }
-
- QSGTexture *texture;
-
- float animT;
- float animX1;
- float animY1;
- float animX2;
- float animY2;
- float animW;
- float animH;
-};
-
-QQuickSpriteSequenceMaterial::QQuickSpriteSequenceMaterial()
- : texture(0)
- , animT(0.0f)
- , animX1(0.0f)
- , animY1(0.0f)
- , animX2(0.0f)
- , animY2(0.0f)
- , animW(1.0f)
- , animH(1.0f)
-{
- setFlag(Blending, true);
-}
-
-QQuickSpriteSequenceMaterial::~QQuickSpriteSequenceMaterial()
-{
- delete texture;
-}
-
-class SpriteSequenceMaterialData : public QSGMaterialShader
-{
-public:
- SpriteSequenceMaterialData()
- : QSGMaterialShader()
- {
- setShaderSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/qt-project.org/items/shaders/sprite.vert"));
- setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/items/shaders/sprite.frag"));
- }
-
- void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *) Q_DECL_OVERRIDE
- {
- QQuickSpriteSequenceMaterial *m = static_cast<QQuickSpriteSequenceMaterial *>(newEffect);
- m->texture->bind();
-
- program()->setUniformValue(m_opacity_id, state.opacity());
- program()->setUniformValue(m_animData_id, m->animW, m->animH, m->animT);
- program()->setUniformValue(m_animPos_id, m->animX1, m->animY1, m->animX2, m->animY2);
-
- if (state.isMatrixDirty())
- program()->setUniformValue(m_matrix_id, state.combinedMatrix());
- }
-
- void initialize() Q_DECL_OVERRIDE {
- m_matrix_id = program()->uniformLocation("qt_Matrix");
- m_opacity_id = program()->uniformLocation("qt_Opacity");
- m_animData_id = program()->uniformLocation("animData");
- m_animPos_id = program()->uniformLocation("animPos");
- }
-
- char const *const *attributeNames() const Q_DECL_OVERRIDE {
- static const char *attr[] = {
- "vPos",
- "vTex",
- 0
- };
- return attr;
- }
-
- int m_matrix_id;
- int m_opacity_id;
- int m_animData_id;
- int m_animPos_id;
-};
-
-QSGMaterialShader *QQuickSpriteSequenceMaterial::createShader() const
-{
- return new SpriteSequenceMaterialData;
-}
-
-struct SpriteVertex {
- float x;
- float y;
- float tx;
- float ty;
-};
-
-struct SpriteVertices {
- SpriteVertex v1;
- SpriteVertex v2;
- SpriteVertex v3;
- SpriteVertex v4;
-};
-
/*!
\qmltype SpriteSequence
\instantiates QQuickSpriteSequence
@@ -218,213 +114,185 @@ struct SpriteVertices {
//TODO: Implicitly size element to size of first sprite?
QQuickSpriteSequence::QQuickSpriteSequence(QQuickItem *parent) :
- QQuickItem(parent)
- , m_node(0)
- , m_material(0)
- , m_spriteEngine(0)
- , m_curFrame(0)
- , m_pleaseReset(false)
- , m_running(true)
- , m_interpolate(true)
- , m_curStateIdx(0)
+ QQuickItem(*(new QQuickSpriteSequencePrivate), parent)
{
setFlag(ItemHasContents);
connect(this, SIGNAL(runningChanged(bool)),
this, SLOT(update()));
- connect(this, SIGNAL(widthChanged()),
- this, SLOT(sizeVertices()));
- connect(this, SIGNAL(heightChanged()),
- this, SLOT(sizeVertices()));
}
void QQuickSpriteSequence::jumpTo(const QString &sprite)
{
- if (!m_spriteEngine)
+ Q_D(QQuickSpriteSequence);
+ if (!d->m_spriteEngine)
return;
- m_spriteEngine->setGoal(m_spriteEngine->stateIndex(sprite), 0, true);
+ d->m_spriteEngine->setGoal(d->m_spriteEngine->stateIndex(sprite), 0, true);
}
void QQuickSpriteSequence::setGoalSprite(const QString &sprite)
{
- if (m_goalState != sprite){
- m_goalState = sprite;
+ Q_D(QQuickSpriteSequence);
+ if (d->m_goalState != sprite){
+ d->m_goalState = sprite;
emit goalSpriteChanged(sprite);
- if (m_spriteEngine)
- m_spriteEngine->setGoal(m_spriteEngine->stateIndex(sprite));
+ if (d->m_spriteEngine)
+ d->m_spriteEngine->setGoal(d->m_spriteEngine->stateIndex(sprite));
}
}
-QQmlListProperty<QQuickSprite> QQuickSpriteSequence::sprites()
+void QQuickSpriteSequence::setRunning(bool arg)
{
- return QQmlListProperty<QQuickSprite>(this, &m_sprites, spriteAppend, spriteCount, spriteAt, spriteClear);
+ Q_D(QQuickSpriteSequence);
+ if (d->m_running != arg) {
+ d->m_running = arg;
+ Q_EMIT runningChanged(arg);
+ }
}
-void QQuickSpriteSequence::createEngine()
+void QQuickSpriteSequence::setInterpolate(bool arg)
{
- //TODO: delay until component complete
- if (m_spriteEngine)
- delete m_spriteEngine;
- if (m_sprites.count()) {
- m_spriteEngine = new QQuickSpriteEngine(m_sprites, this);
- if (!m_goalState.isEmpty())
- m_spriteEngine->setGoal(m_spriteEngine->stateIndex(m_goalState));
- } else {
- m_spriteEngine = 0;
+ Q_D(QQuickSpriteSequence);
+ if (d->m_interpolate != arg) {
+ d->m_interpolate = arg;
+ Q_EMIT interpolateChanged(arg);
}
- reset();
}
-static QSGGeometry::Attribute SpriteSequence_Attributes[] = {
- QSGGeometry::Attribute::create(0, 2, GL_FLOAT, true), // pos
- QSGGeometry::Attribute::create(1, 2, GL_FLOAT), // tex
-};
-
-static QSGGeometry::AttributeSet SpriteSequence_AttributeSet =
+QQmlListProperty<QQuickSprite> QQuickSpriteSequence::sprites()
{
- 2, // Attribute Count
- (2+2) * sizeof(float),
- SpriteSequence_Attributes
-};
+ Q_D(QQuickSpriteSequence);
+ return QQmlListProperty<QQuickSprite>(this, &d->m_sprites, spriteAppend, spriteCount, spriteAt, spriteClear);
+}
-void QQuickSpriteSequence::sizeVertices()
+bool QQuickSpriteSequence::running() const
{
- if (!m_node)
- return;
+ Q_D(const QQuickSpriteSequence);
+ return d->m_running;
+}
- SpriteVertices *p = (SpriteVertices *) m_node->geometry()->vertexData();
- p->v1.x = 0;
- p->v1.y = 0;
+bool QQuickSpriteSequence::interpolate() const
+{
+ Q_D(const QQuickSpriteSequence);
+ return d->m_interpolate;
+}
- p->v2.x = width();
- p->v2.y = 0;
+QString QQuickSpriteSequence::goalSprite() const
+{
+ Q_D(const QQuickSpriteSequence);
+ return d->m_goalState;
+}
- p->v3.x = 0;
- p->v3.y = height();
+QString QQuickSpriteSequence::currentSprite() const
+{
+ Q_D(const QQuickSpriteSequence);
+ return d->m_curState;
+}
- p->v4.x = width();
- p->v4.y = height();
+void QQuickSpriteSequence::createEngine()
+{
+ Q_D(QQuickSpriteSequence);
+ //TODO: delay until component complete
+ if (d->m_spriteEngine)
+ delete d->m_spriteEngine;
+ if (d->m_sprites.count()) {
+ d->m_spriteEngine = new QQuickSpriteEngine(d->m_sprites, this);
+ if (!d->m_goalState.isEmpty())
+ d->m_spriteEngine->setGoal(d->m_spriteEngine->stateIndex(d->m_goalState));
+ } else {
+ d->m_spriteEngine = 0;
+ }
+ reset();
}
-QSGGeometryNode* QQuickSpriteSequence::buildNode()
+QSGSpriteNode *QQuickSpriteSequence::initNode()
{
- if (!m_spriteEngine) {
+ Q_D(QQuickSpriteSequence);
+
+ if (!d->m_spriteEngine) {
qmlInfo(this) << "No sprite engine...";
- return 0;
- } else if (m_spriteEngine->status() == QQuickPixmap::Null) {
- m_spriteEngine->startAssemblingImage();
+ return nullptr;
+ } else if (d->m_spriteEngine->status() == QQuickPixmap::Null) {
+ d->m_spriteEngine->startAssemblingImage();
update();//Schedule another update, where we will check again
- return 0;
- } else if (m_spriteEngine->status() == QQuickPixmap::Loading) {
+ return nullptr;
+ } else if (d->m_spriteEngine->status() == QQuickPixmap::Loading) {
update();//Schedule another update, where we will check again
- return 0;
+ return nullptr;
}
- m_material = new QQuickSpriteSequenceMaterial();
-
- QImage image = m_spriteEngine->assembledImage();
+ QImage image = d->m_spriteEngine->assembledImage(d->sceneGraphRenderContext()->maxTextureSize());
if (image.isNull())
- return 0;
- m_sheetSize = QSizeF(image.size());
- m_material->texture = window()->createTextureFromImage(image);
- m_material->texture->setFiltering(QSGTexture::Linear);
- m_spriteEngine->start(0);
- m_material->animT = 0;
- m_material->animX1 = m_spriteEngine->spriteX() / m_sheetSize.width();
- m_material->animY1 = m_spriteEngine->spriteY() / m_sheetSize.height();
- m_material->animX2 = m_material->animX1;
- m_material->animY2 = m_material->animY1;
- m_material->animW = m_spriteEngine->spriteWidth() / m_sheetSize.width();
- m_material->animH = m_spriteEngine->spriteHeight() / m_sheetSize.height();
- m_curState = m_spriteEngine->state(m_spriteEngine->curState())->name();
- emit currentSpriteChanged(m_curState);
-
- int vCount = 4;
- int iCount = 6;
- QSGGeometry *g = new QSGGeometry(SpriteSequence_AttributeSet, vCount, iCount);
- g->setDrawingMode(GL_TRIANGLES);
-
- SpriteVertices *p = (SpriteVertices *) g->vertexData();
- QRectF texRect = m_material->texture->normalizedTextureSubRect();
-
- p->v1.tx = texRect.topLeft().x();
- p->v1.ty = texRect.topLeft().y();
-
- p->v2.tx = texRect.topRight().x();
- p->v2.ty = texRect.topRight().y();
-
- p->v3.tx = texRect.bottomLeft().x();
- p->v3.ty = texRect.bottomLeft().y();
-
- p->v4.tx = texRect.bottomRight().x();
- p->v4.ty = texRect.bottomRight().y();
-
- quint16 *indices = g->indexDataAsUShort();
- indices[0] = 0;
- indices[1] = 1;
- indices[2] = 2;
- indices[3] = 1;
- indices[4] = 3;
- indices[5] = 2;
-
-
- m_timestamp.start();
- m_node = new QSGGeometryNode();
- m_node->setGeometry(g);
- m_node->setMaterial(m_material);
- m_node->setFlag(QSGGeometryNode::OwnsMaterial);
- sizeVertices();
- return m_node;
+ return nullptr;
+
+ QSGSpriteNode *node = d->sceneGraphContext()->createSpriteNode();
+
+ d->m_sheetSize = QSize(image.size());
+ node->setTexture(window()->createTextureFromImage(image));
+ d->m_spriteEngine->start(0);
+ node->setTime(0.0f);
+ node->setSourceA(QPoint(d->m_spriteEngine->spriteX(), d->m_spriteEngine->spriteY()));
+ node->setSourceB(QPoint(d->m_spriteEngine->spriteX(), d->m_spriteEngine->spriteY()));
+ node->setSpriteSize(QSize(d->m_spriteEngine->spriteWidth(), d->m_spriteEngine->spriteHeight()));
+ node->setSheetSize(d->m_sheetSize);
+ node->setSize(QSizeF(width(), height()));
+
+ d->m_curState = d->m_spriteEngine->state(d->m_spriteEngine->curState())->name();
+ emit currentSpriteChanged(d->m_curState);
+ d->m_timestamp.start();
+ return node;
}
void QQuickSpriteSequence::reset()
{
- m_pleaseReset = true;
+ Q_D(QQuickSpriteSequence);
+ d->m_pleaseReset = true;
}
-QSGNode *QQuickSpriteSequence::updatePaintNode(QSGNode *, UpdatePaintNodeData *)
+QSGNode *QQuickSpriteSequence::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
{
- if (m_pleaseReset) {
- delete m_node;
+ Q_D(QQuickSpriteSequence);
- m_node = 0;
- m_material = 0;
- m_pleaseReset = false;
+ if (d->m_pleaseReset) {
+ delete oldNode;
+
+ oldNode = nullptr;
+ d->m_pleaseReset = false;
}
- prepareNextFrame();
+ QSGSpriteNode *node = static_cast<QSGSpriteNode *>(oldNode);
+ if (!node)
+ node = initNode();
+
+ if (node)
+ prepareNextFrame(node);
- if (m_running) {
+ if (d->m_running) {
update();
- if (m_node)
- m_node->markDirty(QSGNode::DirtyMaterial);
}
- return m_node;
+ return node;
}
-void QQuickSpriteSequence::prepareNextFrame()
+void QQuickSpriteSequence::prepareNextFrame(QSGSpriteNode *node)
{
- if (m_node == 0)
- m_node = buildNode();
- if (m_node == 0) //error creating node
- return;
+ Q_D(QQuickSpriteSequence);
- uint timeInt = m_timestamp.elapsed();
+ uint timeInt = d->m_timestamp.elapsed();
qreal time = timeInt / 1000.;
//Advance State
- m_spriteEngine->updateSprites(timeInt);
- if (m_curStateIdx != m_spriteEngine->curState()) {
- m_curStateIdx = m_spriteEngine->curState();
- m_curState = m_spriteEngine->state(m_spriteEngine->curState())->name();
- emit currentSpriteChanged(m_curState);
- m_curFrame= -1;
+ d->m_spriteEngine->updateSprites(timeInt);
+ if (d->m_curStateIdx != d->m_spriteEngine->curState()) {
+ d->m_curStateIdx = d->m_spriteEngine->curState();
+ d->m_curState = d->m_spriteEngine->state(d->m_spriteEngine->curState())->name();
+ emit currentSpriteChanged(d->m_curState);
+ d->m_curFrame= -1;
}
//Advance Sprite
- qreal animT = m_spriteEngine->spriteStart()/1000.0;
- qreal frameCount = m_spriteEngine->spriteFrames();
- qreal frameDuration = m_spriteEngine->spriteDuration()/frameCount;
+ qreal animT = d->m_spriteEngine->spriteStart()/1000.0;
+ qreal frameCount = d->m_spriteEngine->spriteFrames();
+ qreal frameDuration = d->m_spriteEngine->spriteDuration()/frameCount;
double frameAt;
qreal progress;
if (frameDuration > 0) {
@@ -432,32 +300,32 @@ void QQuickSpriteSequence::prepareNextFrame()
frame = qBound(qreal(0.0), frame, frameCount - qreal(1.0));//Stop at count-1 frames until we have between anim interpolation
progress = std::modf(frame,&frameAt);
} else {
- m_curFrame++;
- if (m_curFrame >= frameCount){
- m_curFrame = 0;
- m_spriteEngine->advance();
+ d->m_curFrame++;
+ if (d->m_curFrame >= frameCount){
+ d->m_curFrame = 0;
+ d->m_spriteEngine->advance();
}
- frameAt = m_curFrame;
+ frameAt = d->m_curFrame;
progress = 0;
}
- if (m_spriteEngine->sprite()->reverse())
- frameAt = (m_spriteEngine->spriteFrames() - 1) - frameAt;
- qreal y = m_spriteEngine->spriteY() / m_sheetSize.height();
- qreal w = m_spriteEngine->spriteWidth() / m_sheetSize.width();
- qreal h = m_spriteEngine->spriteHeight() / m_sheetSize.height();
- qreal x1 = m_spriteEngine->spriteX() / m_sheetSize.width();
+ if (d->m_spriteEngine->sprite()->reverse())
+ frameAt = (d->m_spriteEngine->spriteFrames() - 1) - frameAt;
+ int y = d->m_spriteEngine->spriteY();
+ int w = d->m_spriteEngine->spriteWidth();
+ int h = d->m_spriteEngine->spriteHeight();
+ int x1 = d->m_spriteEngine->spriteX();
x1 += frameAt * w;
- qreal x2 = x1;
+ int x2 = x1;
if (frameAt < (frameCount-1))
x2 += w;
- m_material->animX1 = x1;
- m_material->animY1 = y;
- m_material->animX2 = x2;
- m_material->animY2 = y;
- m_material->animW = w;
- m_material->animH = h;
- m_material->animT = m_interpolate ? progress : 0.0;
+ node->setSourceA(QPoint(x1, y));
+ node->setSourceB(QPoint(x2, y));
+ node->setSpriteSize(QSize(w, h));
+ node->setTime(d->m_interpolate ? progress : 0.0);
+ node->setSize(QSizeF(width(), height()));
+ node->setFiltering(smooth() ? QSGTexture::Linear : QSGTexture::Nearest);
+ node->update();
}
QT_END_NAMESPACE
diff --git a/src/quick/items/qquickspritesequence_p.h b/src/quick/items/qquickspritesequence_p.h
index b4cc133821..b80a8348aa 100644
--- a/src/quick/items/qquickspritesequence_p.h
+++ b/src/quick/items/qquickspritesequence_p.h
@@ -51,6 +51,10 @@
// We mean it.
//
+#include <private/qtquickglobal_p.h>
+
+QT_REQUIRE_CONFIG(quick_sprite);
+
#include <QtQuick/QQuickItem>
#include <QTime>
@@ -59,8 +63,8 @@ QT_BEGIN_NAMESPACE
class QSGContext;
class QQuickSprite;
class QQuickSpriteEngine;
-class QSGGeometryNode;
-class QQuickSpriteSequenceMaterial;
+class QQuickSpriteSequencePrivate;
+class QSGSpriteNode;
class Q_AUTOTEST_EXPORT QQuickSpriteSequence : public QQuickItem
{
Q_OBJECT
@@ -77,25 +81,10 @@ public:
QQmlListProperty<QQuickSprite> sprites();
- bool running() const
- {
- return m_running;
- }
-
- bool interpolate() const
- {
- return m_interpolate;
- }
-
- QString goalSprite() const
- {
- return m_goalState;
- }
-
- QString currentSprite() const
- {
- return m_curState;
- }
+ bool running() const;
+ bool interpolate() const;
+ QString goalSprite() const;
+ QString currentSprite() const;
Q_SIGNALS:
@@ -108,46 +97,23 @@ public Q_SLOTS:
void jumpTo(const QString &sprite);
void setGoalSprite(const QString &sprite);
-
- void setRunning(bool arg)
- {
- if (m_running != arg) {
- m_running = arg;
- Q_EMIT runningChanged(arg);
- }
- }
-
- void setInterpolate(bool arg)
- {
- if (m_interpolate != arg) {
- m_interpolate = arg;
- Q_EMIT interpolateChanged(arg);
- }
- }
+ void setRunning(bool arg);
+ void setInterpolate(bool arg);
private Q_SLOTS:
void createEngine();
- void sizeVertices();
protected:
void reset();
QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *) Q_DECL_OVERRIDE;
private:
- void prepareNextFrame();
- QSGGeometryNode* buildNode();
- QSGGeometryNode *m_node;
- QQuickSpriteSequenceMaterial *m_material;
- QList<QQuickSprite*> m_sprites;
- QQuickSpriteEngine* m_spriteEngine;
- QTime m_timestamp;
- int m_curFrame;
- bool m_pleaseReset;
- bool m_running;
- bool m_interpolate;
- QString m_goalState;
- QString m_curState;
- int m_curStateIdx;
- QSizeF m_sheetSize;
+ void prepareNextFrame(QSGSpriteNode *node);
+ QSGSpriteNode* initNode();
+
+
+private:
+ Q_DISABLE_COPY(QQuickSpriteSequence)
+ Q_DECLARE_PRIVATE(QQuickSpriteSequence)
};
QT_END_NAMESPACE
diff --git a/src/quick/items/qquickspritesequence_p_p.h b/src/quick/items/qquickspritesequence_p_p.h
new file mode 100644
index 0000000000..3579833116
--- /dev/null
+++ b/src/quick/items/qquickspritesequence_p_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 QQUICKSPRITESEQUENCE_P_P_H
+#define QQUICKSPRITESEQUENCE_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 <private/qtquickglobal_p.h>
+
+QT_REQUIRE_CONFIG(quick_sprite);
+
+#include "qquickitem_p.h"
+#include "qquicksprite_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QQuickSpriteSequence;
+
+class QQuickSpriteSequencePrivate :public QQuickItemPrivate
+{
+ Q_DECLARE_PUBLIC(QQuickSpriteSequence)
+public:
+ QQuickSpriteSequencePrivate()
+ : m_spriteEngine(nullptr)
+ , m_curFrame(0)
+ , m_pleaseReset(false)
+ , m_running(true)
+ , m_interpolate(true)
+ , m_curStateIdx(0)
+ {
+
+ }
+ QList<QQuickSprite*> m_sprites;
+ QQuickSpriteEngine* m_spriteEngine;
+ QTime m_timestamp;
+ int m_curFrame;
+ bool m_pleaseReset;
+ bool m_running;
+ bool m_interpolate;
+ QString m_goalState;
+ QString m_curState;
+ int m_curStateIdx;
+ QSize m_sheetSize;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKSPRITESEQUENCE_P_P_H
diff --git a/src/quick/items/qquickstateoperations.cpp b/src/quick/items/qquickstateoperations.cpp
index 73b1680305..7e485c675c 100644
--- a/src/quick/items/qquickstateoperations.cpp
+++ b/src/quick/items/qquickstateoperations.cpp
@@ -359,8 +359,8 @@ QQuickStateOperation::ActionList QQuickParentChange::actions()
QQuickStateAction xa(d->target, QLatin1String("x"), x);
actions << xa;
} else {
- QQmlBinding *newBinding = new QQmlBinding(d->xString.value, d->target, qmlContext(this));
QQmlProperty property(d->target, QLatin1String("x"));
+ QQmlBinding *newBinding = QQmlBinding::create(&QQmlPropertyPrivate::get(property)->core, d->xString.value, d->target, qmlContext(this));
newBinding->setTarget(property);
QQuickStateAction xa;
xa.property = property;
@@ -378,8 +378,8 @@ QQuickStateOperation::ActionList QQuickParentChange::actions()
QQuickStateAction ya(d->target, QLatin1String("y"), y);
actions << ya;
} else {
- QQmlBinding *newBinding = new QQmlBinding(d->yString.value, d->target, qmlContext(this));
QQmlProperty property(d->target, QLatin1String("y"));
+ QQmlBinding *newBinding = QQmlBinding::create(&QQmlPropertyPrivate::get(property)->core, d->yString.value, d->target, qmlContext(this));
newBinding->setTarget(property);
QQuickStateAction ya;
ya.property = property;
@@ -397,8 +397,8 @@ QQuickStateOperation::ActionList QQuickParentChange::actions()
QQuickStateAction sa(d->target, QLatin1String("scale"), scale);
actions << sa;
} else {
- QQmlBinding *newBinding = new QQmlBinding(d->scaleString.value, d->target, qmlContext(this));
QQmlProperty property(d->target, QLatin1String("scale"));
+ QQmlBinding *newBinding = QQmlBinding::create(&QQmlPropertyPrivate::get(property)->core, d->scaleString.value, d->target, qmlContext(this));
newBinding->setTarget(property);
QQuickStateAction sa;
sa.property = property;
@@ -416,8 +416,8 @@ QQuickStateOperation::ActionList QQuickParentChange::actions()
QQuickStateAction ra(d->target, QLatin1String("rotation"), rotation);
actions << ra;
} else {
- QQmlBinding *newBinding = new QQmlBinding(d->rotationString.value, d->target, qmlContext(this));
QQmlProperty property(d->target, QLatin1String("rotation"));
+ QQmlBinding *newBinding = QQmlBinding::create(&QQmlPropertyPrivate::get(property)->core, d->rotationString.value, d->target, qmlContext(this));
newBinding->setTarget(property);
QQuickStateAction ra;
ra.property = property;
@@ -435,8 +435,8 @@ QQuickStateOperation::ActionList QQuickParentChange::actions()
QQuickStateAction wa(d->target, QLatin1String("width"), width);
actions << wa;
} else {
- QQmlBinding *newBinding = new QQmlBinding(d->widthString.value, d->target, qmlContext(this));
QQmlProperty property(d->target, QLatin1String("width"));
+ QQmlBinding *newBinding = QQmlBinding::create(&QQmlPropertyPrivate::get(property)->core, d->widthString.value, d->target, qmlContext(this));
newBinding->setTarget(property);
QQuickStateAction wa;
wa.property = property;
@@ -454,8 +454,8 @@ QQuickStateOperation::ActionList QQuickParentChange::actions()
QQuickStateAction ha(d->target, QLatin1String("height"), height);
actions << ha;
} else {
- QQmlBinding *newBinding = new QQmlBinding(d->heightString.value, d->target, qmlContext(this));
QQmlProperty property(d->target, QLatin1String("height"));
+ QQmlBinding *newBinding = QQmlBinding::create(&QQmlPropertyPrivate::get(property)->core, d->heightString.value, d->target, qmlContext(this));
newBinding->setTarget(property);
QQuickStateAction ha;
ha.property = property;
@@ -866,31 +866,31 @@ QQuickAnchorChanges::ActionList QQuickAnchorChanges::actions()
d->baselineProp = QQmlProperty(d->target, QLatin1String("anchors.baseline"));
if (d->anchorSet->d_func()->usedAnchors & QQuickAnchors::LeftAnchor) {
- d->leftBinding = new QQmlBinding(d->anchorSet->d_func()->leftScript, d->target, qmlContext(this));
+ d->leftBinding = QQmlBinding::create(&QQmlPropertyPrivate::get(d->leftProp)->core, d->anchorSet->d_func()->leftScript, d->target, qmlContext(this));
d->leftBinding->setTarget(d->leftProp);
}
if (d->anchorSet->d_func()->usedAnchors & QQuickAnchors::RightAnchor) {
- d->rightBinding = new QQmlBinding(d->anchorSet->d_func()->rightScript, d->target, qmlContext(this));
+ d->rightBinding = QQmlBinding::create(&QQmlPropertyPrivate::get(d->rightProp)->core, d->anchorSet->d_func()->rightScript, d->target, qmlContext(this));
d->rightBinding->setTarget(d->rightProp);
}
if (d->anchorSet->d_func()->usedAnchors & QQuickAnchors::HCenterAnchor) {
- d->hCenterBinding = new QQmlBinding(d->anchorSet->d_func()->hCenterScript, d->target, qmlContext(this));
+ d->hCenterBinding = QQmlBinding::create(&QQmlPropertyPrivate::get(d->hCenterProp)->core, d->anchorSet->d_func()->hCenterScript, d->target, qmlContext(this));
d->hCenterBinding->setTarget(d->hCenterProp);
}
if (d->anchorSet->d_func()->usedAnchors & QQuickAnchors::TopAnchor) {
- d->topBinding = new QQmlBinding(d->anchorSet->d_func()->topScript, d->target, qmlContext(this));
+ d->topBinding = QQmlBinding::create(&QQmlPropertyPrivate::get(d->topProp)->core, d->anchorSet->d_func()->topScript, d->target, qmlContext(this));
d->topBinding->setTarget(d->topProp);
}
if (d->anchorSet->d_func()->usedAnchors & QQuickAnchors::BottomAnchor) {
- d->bottomBinding = new QQmlBinding(d->anchorSet->d_func()->bottomScript, d->target, qmlContext(this));
+ d->bottomBinding = QQmlBinding::create(&QQmlPropertyPrivate::get(d->bottomProp)->core, d->anchorSet->d_func()->bottomScript, d->target, qmlContext(this));
d->bottomBinding->setTarget(d->bottomProp);
}
if (d->anchorSet->d_func()->usedAnchors & QQuickAnchors::VCenterAnchor) {
- d->vCenterBinding = new QQmlBinding(d->anchorSet->d_func()->vCenterScript, d->target, qmlContext(this));
+ d->vCenterBinding = QQmlBinding::create(&QQmlPropertyPrivate::get(d->vCenterProp)->core, d->anchorSet->d_func()->vCenterScript, d->target, qmlContext(this));
d->vCenterBinding->setTarget(d->vCenterProp);
}
if (d->anchorSet->d_func()->usedAnchors & QQuickAnchors::BaselineAnchor) {
- d->baselineBinding = new QQmlBinding(d->anchorSet->d_func()->baselineScript, d->target, qmlContext(this));
+ d->baselineBinding = QQmlBinding::create(&QQmlPropertyPrivate::get(d->baselineProp)->core, d->anchorSet->d_func()->baselineScript, d->target, qmlContext(this));
d->baselineBinding->setTarget(d->baselineProp);
}
diff --git a/src/quick/items/qquicktext.cpp b/src/quick/items/qquicktext.cpp
index 80321cf5d1..2bfb4501fc 100644
--- a/src/quick/items/qquicktext.cpp
+++ b/src/quick/items/qquicktext.cpp
@@ -69,6 +69,7 @@
QT_BEGIN_NAMESPACE
+Q_DECLARE_LOGGING_CATEGORY(DBG_HOVER_TRACE)
const QChar QQuickTextPrivate::elideChar = QChar(0x2026);
@@ -321,7 +322,7 @@ void QQuickText::imageDownloadFinished()
if (d->extra.isAllocated() && d->extra->nbActiveDownloads == 0) {
bool needToUpdateLayout = false;
- foreach (QQuickStyledTextImgTag *img, d->extra->visibleImgTags) {
+ for (QQuickStyledTextImgTag *img : qAsConst(d->extra->visibleImgTags)) {
if (!img->size.isValid()) {
img->size = img->pix->implicitSize();
needToUpdateLayout = true;
@@ -1114,7 +1115,7 @@ void QQuickTextPrivate::setLineGeometry(QTextLine &line, qreal lineWidth, qreal
QList<QQuickStyledTextImgTag *> imagesInLine;
if (extra.isAllocated()) {
- foreach (QQuickStyledTextImgTag *image, extra->imgTags) {
+ for (QQuickStyledTextImgTag *image : qAsConst(extra->imgTags)) {
if (image->position >= line.textStart() &&
image->position < line.textStart() + line.textLength()) {
@@ -1151,7 +1152,7 @@ void QQuickTextPrivate::setLineGeometry(QTextLine &line, qreal lineWidth, qreal
}
}
- foreach (QQuickStyledTextImgTag *image, imagesInLine) {
+ for (QQuickStyledTextImgTag *image : qAsConst(imagesInLine)) {
totalLineHeight = qMax(totalLineHeight, textTop + image->pos.y() + image->size.height());
const int leadX = line.cursorToX(image->position);
const int trailX = line.cursorToX(image->position, QTextLine::Trailing);
@@ -1431,6 +1432,39 @@ QQuickText::~QQuickText()
Text { text: "Hello"; font.capitalization: Font.AllLowercase }
\endqml
*/
+
+/*!
+ \qmlproperty enumeration QtQuick::Text::font.hintingPreference
+ \since 5.8
+
+ Sets the preferred hinting on the text. This is a hint to the underlying text rendering system
+ to use a certain level of hinting, and has varying support across platforms. See the table in
+ the documentation for QFont::HintingPreference for more details.
+
+ \note This property only has an effect when used together with render type Text.NativeRendering.
+
+ \list
+ \value Font.PreferDefaultHinting - Use the default hinting level for the target platform.
+ \value Font.PreferNoHinting - If possible, render text without hinting the outlines
+ of the glyphs. The text layout will be typographically accurate, using the same metrics
+ as are used e.g. when printing.
+ \value Font.PreferVerticalHinting - If possible, render text with no horizontal hinting,
+ but align glyphs to the pixel grid in the vertical direction. The text will appear
+ crisper on displays where the density is too low to give an accurate rendering
+ of the glyphs. But since the horizontal metrics of the glyphs are unhinted, the text's
+ layout will be scalable to higher density devices (such as printers) without impacting
+ details such as line breaks.
+ \value Font.PreferFullHinting - If possible, render text with hinting in both horizontal and
+ vertical directions. The text will be altered to optimize legibility on the target
+ device, but since the metrics will depend on the target size of the text, the positions
+ of glyphs, line breaks, and other typographical detail will not scale, meaning that a
+ text layout may look different on devices with different pixel densities.
+ \endlist
+
+ \qml
+ Text { text: "Hello"; renderType: Text.NativeRendering; font.hintingPreference: Font.PreferVerticalHinting }
+ \endqml
+*/
QFont QQuickText::font() const
{
Q_D(const QQuickText);
@@ -2308,7 +2342,7 @@ QSGNode *QQuickText::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data
node->addTextLayout(QPointF(dx, dy), d->elideLayout, color, d->style, styleColor, linkColor);
if (d->extra.isAllocated()) {
- foreach (QQuickStyledTextImgTag *img, d->extra->visibleImgTags) {
+ for (QQuickStyledTextImgTag *img : qAsConst(d->extra->visibleImgTags)) {
QQuickPixmap *pix = img->pix;
if (pix && pix->isReady())
node->addImage(QRectF(img->pos.x() + dx, img->pos.y() + dy, pix->width(), pix->height()), pix->image());
@@ -2562,7 +2596,8 @@ QString QQuickTextPrivate::anchorAt(const QTextLayout *layout, const QPointF &mo
QTextLine line = layout->lineAt(i);
if (line.naturalTextRect().contains(mousePos)) {
int charPos = line.xToCursor(mousePos.x(), QTextLine::CursorOnCharacter);
- foreach (const QTextLayout::FormatRange &formatRange, layout->formats()) {
+ const auto formats = layout->formats();
+ for (const QTextLayout::FormatRange &formatRange : formats) {
if (formatRange.format.isAnchor()
&& charPos >= formatRange.start
&& charPos < formatRange.start + formatRange.length) {
@@ -2691,6 +2726,7 @@ QString QQuickText::hoveredLink() const
void QQuickTextPrivate::processHoverEvent(QHoverEvent *event)
{
Q_Q(QQuickText);
+ qCDebug(DBG_HOVER_TRACE) << q;
QString link;
if (isLinkHoveredConnected()) {
if (event->type() != QEvent::HoverLeave)
diff --git a/src/quick/items/qquicktextcontrol.cpp b/src/quick/items/qquicktextcontrol.cpp
index 0cee9c7d27..ef7485a8e9 100644
--- a/src/quick/items/qquicktextcontrol.cpp
+++ b/src/quick/items/qquicktextcontrol.cpp
@@ -44,6 +44,7 @@
#include <qcoreapplication.h>
#include <qfont.h>
+#include <qfontmetrics.h>
#include <qevent.h>
#include <qdebug.h>
#include <qdrag.h>
@@ -57,6 +58,7 @@
#include "qtextlist.h"
#include "qtextdocumentwriter.h"
#include "private/qtextcursor_p.h"
+#include <QtCore/qloggingcategory.h>
#include <qtextformat.h>
#include <qdatetime.h>
@@ -75,6 +77,7 @@
const int textCursorWidth = 1;
QT_BEGIN_NAMESPACE
+Q_DECLARE_LOGGING_CATEGORY(DBG_HOVER_TRACE)
#ifndef QT_NO_CONTEXTMENU
#endif
@@ -982,6 +985,14 @@ process:
{
QString text = e->text();
if (!text.isEmpty() && (text.at(0).isPrint() || text.at(0) == QLatin1Char('\t'))) {
+ if (overwriteMode
+ // no need to call deleteChar() if we have a selection, insertText
+ // does it already
+ && !cursor.hasSelection()
+ && !cursor.atBlockEnd()) {
+ cursor.deleteChar();
+ }
+
cursor.insertText(text);
selectionChanged();
} else {
@@ -1024,6 +1035,12 @@ QRectF QQuickTextControlPrivate::rectForPosition(int position) const
if (line.isValid()) {
qreal x = line.cursorToX(relativePos);
qreal w = 0;
+ if (overwriteMode) {
+ if (relativePos < line.textLength() - line.textStart())
+ w = line.cursorToX(relativePos + 1) - x;
+ else
+ w = QFontMetrics(block.layout()->font()).width(QLatin1Char(' ')); // in sync with QTextLine::draw()
+ }
r = QRectF(layoutPos.x() + x, layoutPos.y() + line.y(), textCursorWidth + w, line.height());
} else {
r = QRectF(layoutPos.x(), layoutPos.y(), textCursorWidth, 10); // #### correct height
@@ -1498,6 +1515,7 @@ void QQuickTextControlPrivate::hoverEvent(QHoverEvent *e, const QPointF &pos)
hoveredLink = link;
emit q->linkHovered(link);
}
+ qCDebug(DBG_HOVER_TRACE) << q << e->type() << pos << "hoveredLink" << hoveredLink;
}
bool QQuickTextControl::hasImState() const
@@ -1506,6 +1524,21 @@ bool QQuickTextControl::hasImState() const
return d->hasImState;
}
+bool QQuickTextControl::overwriteMode() const
+{
+ Q_D(const QQuickTextControl);
+ return d->overwriteMode;
+}
+
+void QQuickTextControl::setOverwriteMode(bool overwrite)
+{
+ Q_D(QQuickTextControl);
+ if (d->overwriteMode == overwrite)
+ return;
+ d->overwriteMode = overwrite;
+ emit overwriteModeChanged(overwrite);
+}
+
bool QQuickTextControl::cursorVisible() const
{
Q_D(const QQuickTextControl);
diff --git a/src/quick/items/qquicktextcontrol_p.h b/src/quick/items/qquicktextcontrol_p.h
index ca5a3d3191..602e457cb8 100644
--- a/src/quick/items/qquicktextcontrol_p.h
+++ b/src/quick/items/qquicktextcontrol_p.h
@@ -94,6 +94,8 @@ public:
#endif
bool hasImState() const;
+ bool overwriteMode() const;
+ void setOverwriteMode(bool overwrite);
bool cursorVisible() const;
void setCursorVisible(bool visible);
QRectF anchorRect() const;
@@ -149,6 +151,7 @@ Q_SIGNALS:
void copyAvailable(bool b);
void selectionChanged();
void cursorPositionChanged();
+ void overwriteModeChanged(bool overwriteMode);
// control signals
void updateCursorRequest();
diff --git a/src/quick/items/qquicktextdocument.cpp b/src/quick/items/qquicktextdocument.cpp
index 3eacfd61bc..1dc54eb107 100644
--- a/src/quick/items/qquicktextdocument.cpp
+++ b/src/quick/items/qquicktextdocument.cpp
@@ -219,7 +219,7 @@ QQuickPixmap *QQuickTextDocumentWithImageResources::loadPixmap(
void QQuickTextDocumentWithImageResources::clearResources()
{
- foreach (QQuickPixmap *pixmap, m_resources)
+ for (QQuickPixmap *pixmap : qAsConst(m_resources))
pixmap->clear(this);
qDeleteAll(m_resources);
m_resources.clear();
diff --git a/src/quick/items/qquicktextedit.cpp b/src/quick/items/qquicktextedit.cpp
index 9b23abc877..c81544cbdb 100644
--- a/src/quick/items/qquicktextedit.cpp
+++ b/src/quick/items/qquicktextedit.cpp
@@ -153,7 +153,7 @@ namespace {
newNode->setFlag(QSGNode::OwnedByParent);
}
- void resetCursorNode(QSGRectangleNode* newNode)
+ void resetCursorNode(QSGInternalRectangleNode* newNode)
{
if (cursorNode)
removeChildNode(cursorNode);
@@ -165,7 +165,7 @@ namespace {
}
}
- QSGRectangleNode *cursorNode;
+ QSGInternalRectangleNode *cursorNode;
QQuickTextNode* frameDecorationsNode;
};
@@ -323,6 +323,39 @@ QString QQuickTextEdit::text() const
*/
/*!
+ \qmlproperty enumeration QtQuick::TextEdit::font.hintingPreference
+ \since 5.8
+
+ Sets the preferred hinting on the text. This is a hint to the underlying text rendering system
+ to use a certain level of hinting, and has varying support across platforms. See the table in
+ the documentation for QFont::HintingPreference for more details.
+
+ \note This property only has an effect when used together with render type TextEdit.NativeRendering.
+
+ \list
+ \value Font.PreferDefaultHinting - Use the default hinting level for the target platform.
+ \value Font.PreferNoHinting - If possible, render text without hinting the outlines
+ of the glyphs. The text layout will be typographically accurate, using the same metrics
+ as are used e.g. when printing.
+ \value Font.PreferVerticalHinting - If possible, render text with no horizontal hinting,
+ but align glyphs to the pixel grid in the vertical direction. The text will appear
+ crisper on displays where the density is too low to give an accurate rendering
+ of the glyphs. But since the horizontal metrics of the glyphs are unhinted, the text's
+ layout will be scalable to higher density devices (such as printers) without impacting
+ details such as line breaks.
+ \value Font.PreferFullHinting - If possible, render text with hinting in both horizontal and
+ vertical directions. The text will be altered to optimize legibility on the target
+ device, but since the metrics will depend on the target size of the text, the positions
+ of glyphs, line breaks, and other typographical detail will not scale, meaning that a
+ text layout may look different on devices with different pixel densities.
+ \endlist
+
+ \qml
+ TextEdit { text: "Hello"; renderType: TextEdit.NativeRendering; font.hintingPreference: Font.PreferVerticalHinting }
+ \endqml
+*/
+
+/*!
\qmlproperty string QtQuick::TextEdit::text
The text to display. If the text format is AutoText the text edit will
@@ -1583,6 +1616,32 @@ bool QQuickTextEdit::event(QEvent *event)
}
/*!
+ \qmlproperty bool QtQuick::TextEdit::overwriteMode
+ \since 5.8
+ Whether text entered by the user will overwrite existing text.
+
+ As with many text editors, the text editor widget can be configured
+ to insert or overwrite existing text with new text entered by the user.
+
+ If this property is \c true, existing text is overwritten, character-for-character
+ by new text; otherwise, text is inserted at the cursor position, displacing
+ existing text.
+
+ By default, this property is \c false (new text does not overwrite existing text).
+*/
+bool QQuickTextEdit::overwriteMode() const
+{
+ Q_D(const QQuickTextEdit);
+ return d->control->overwriteMode();
+}
+
+void QQuickTextEdit::setOverwriteMode(bool overwrite)
+{
+ Q_D(QQuickTextEdit);
+ d->control->setOverwriteMode(overwrite);
+}
+
+/*!
\overload
Handles the given key \a event.
*/
@@ -2006,7 +2065,7 @@ QSGNode *QQuickTextEdit::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *
// Having nodes spanning across frame boundaries will break the current bookkeeping mechanism. We need to prevent that.
QList<int> frameBoundaries;
frameBoundaries.reserve(frames.size());
- Q_FOREACH (QTextFrame *frame, frames)
+ for (QTextFrame *frame : qAsConst(frames))
frameBoundaries.append(frame->firstPosition());
std::sort(frameBoundaries.begin(), frameBoundaries.end());
@@ -2066,9 +2125,9 @@ QSGNode *QQuickTextEdit::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *
}
if (d->cursorComponent == 0) {
- QSGRectangleNode* cursor = 0;
+ QSGInternalRectangleNode* cursor = 0;
if (!isReadOnly() && d->cursorVisible && d->control->cursorOn())
- cursor = d->sceneGraphContext()->createRectangleNode(d->control->cursorRect(), d->color);
+ cursor = d->sceneGraphContext()->createInternalRectangleNode(d->control->cursorRect(), d->color);
rootNode->resetCursorNode(cursor);
}
@@ -2193,6 +2252,7 @@ void QQuickTextEditPrivate::init()
qmlobject_connect(control, QQuickTextControl, SIGNAL(cursorRectangleChanged()), q, QQuickTextEdit, SLOT(moveCursorDelegate()));
qmlobject_connect(control, QQuickTextControl, SIGNAL(linkActivated(QString)), q, QQuickTextEdit, SIGNAL(linkActivated(QString)));
qmlobject_connect(control, QQuickTextControl, SIGNAL(linkHovered(QString)), q, QQuickTextEdit, SIGNAL(linkHovered(QString)));
+ qmlobject_connect(control, QQuickTextControl, SIGNAL(overwriteModeChanged(bool)), q, QQuickTextEdit, SIGNAL(overwriteModeChanged(bool)));
qmlobject_connect(control, QQuickTextControl, SIGNAL(textChanged()), q, QQuickTextEdit, SLOT(q_textChanged()));
qmlobject_connect(control, QQuickTextControl, SIGNAL(preeditTextChanged()), q, QQuickTextEdit, SIGNAL(preeditTextChanged()));
#ifndef QT_NO_CLIPBOARD
@@ -2439,7 +2499,7 @@ void QQuickTextEdit::updateWholeDocument()
{
Q_D(QQuickTextEdit);
if (!d->textNodeMap.isEmpty()) {
- Q_FOREACH (TextNode* node, d->textNodeMap)
+ for (TextNode* node : qAsConst(d->textNodeMap))
node->setDirty();
}
diff --git a/src/quick/items/qquicktextedit_p.h b/src/quick/items/qquicktextedit_p.h
index f6ecb984e3..42c9064860 100644
--- a/src/quick/items/qquicktextedit_p.h
+++ b/src/quick/items/qquicktextedit_p.h
@@ -86,6 +86,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickTextEdit : public QQuickImplicitSizeItem
Q_PROPERTY(int cursorPosition READ cursorPosition WRITE setCursorPosition NOTIFY cursorPositionChanged)
Q_PROPERTY(QRectF cursorRectangle READ cursorRectangle NOTIFY cursorRectangleChanged)
Q_PROPERTY(QQmlComponent* cursorDelegate READ cursorDelegate WRITE setCursorDelegate NOTIFY cursorDelegateChanged)
+ Q_PROPERTY(bool overwriteMode READ overwriteMode WRITE setOverwriteMode NOTIFY overwriteModeChanged)
Q_PROPERTY(int selectionStart READ selectionStart NOTIFY selectionStartChanged)
Q_PROPERTY(int selectionEnd READ selectionEnd NOTIFY selectionEndChanged)
Q_PROPERTY(QString selectedText READ selectedText NOTIFY selectedTextChanged)
@@ -199,6 +200,9 @@ public:
QQmlComponent* cursorDelegate() const;
void setCursorDelegate(QQmlComponent*);
+ bool overwriteMode() const;
+ void setOverwriteMode(bool overwrite);
+
int selectionStart() const;
int selectionEnd() const;
@@ -313,6 +317,7 @@ Q_SIGNALS:
void readOnlyChanged(bool isReadOnly);
void cursorVisibleChanged(bool isCursorVisible);
void cursorDelegateChanged();
+ void overwriteModeChanged(bool overwriteMode);
void activeFocusOnPressChanged(bool activeFocusOnPressed);
void persistentSelectionChanged(bool isPersistentSelection);
void textMarginChanged(qreal textMargin);
diff --git a/src/quick/items/qquicktextinput.cpp b/src/quick/items/qquicktextinput.cpp
index 3d708c8eeb..dc4aecbbeb 100644
--- a/src/quick/items/qquicktextinput.cpp
+++ b/src/quick/items/qquicktextinput.cpp
@@ -345,6 +345,38 @@ QString QQuickTextInputPrivate::realText() const
\endqml
*/
+/*!
+ \qmlproperty enumeration QtQuick::TextInput::font.hintingPreference
+ \since 5.8
+
+ Sets the preferred hinting on the text. This is a hint to the underlying text rendering system
+ to use a certain level of hinting, and has varying support across platforms. See the table in
+ the documentation for QFont::HintingPreference for more details.
+
+ \note This property only has an effect when used together with render type TextInput.NativeRendering.
+
+ \list
+ \value Font.PreferDefaultHinting - Use the default hinting level for the target platform.
+ \value Font.PreferNoHinting - If possible, render text without hinting the outlines
+ of the glyphs. The text layout will be typographically accurate, using the same metrics
+ as are used e.g. when printing.
+ \value Font.PreferVerticalHinting - If possible, render text with no horizontal hinting,
+ but align glyphs to the pixel grid in the vertical direction. The text will appear
+ crisper on displays where the density is too low to give an accurate rendering
+ of the glyphs. But since the horizontal metrics of the glyphs are unhinted, the text's
+ layout will be scalable to higher density devices (such as printers) without impacting
+ details such as line breaks.
+ \value Font.PreferFullHinting - If possible, render text with hinting in both horizontal and
+ vertical directions. The text will be altered to optimize legibility on the target
+ device, but since the metrics will depend on the target size of the text, the positions
+ of glyphs, line breaks, and other typographical detail will not scale, meaning that a
+ text layout may look different on devices with different pixel densities.
+ \endlist
+
+ \qml
+ TextInput { text: "Hello"; renderType: TextInput.NativeRendering; font.hintingPreference: Font.PreferVerticalHinting }
+ \endqml
+*/
QFont QQuickTextInput::font() const
{
Q_D(const QQuickTextInput);
@@ -734,7 +766,7 @@ void QQuickTextInput::setMaxLength(int ml)
(but without actually giving it active focus).
It should not be set directly on the item, like in the below QML,
- as the specified value will be overridden an lost on focus changes.
+ as the specified value will be overridden and lost on focus changes.
\code
TextInput {
@@ -808,7 +840,14 @@ QRectF QQuickTextInput::cursorRectangle() const
return QRectF();
qreal x = l.cursorToX(c) - d->hscroll + leftPadding();
qreal y = l.y() - d->vscroll + topPadding();
- return QRectF(x, y, 1, l.height());
+ qreal w = 1;
+ if (d->overwriteMode) {
+ if (c < text().length())
+ w = l.cursorToX(c + 1) - x;
+ else
+ w = QFontMetrics(font()).width(QLatin1Char(' ')); // in sync with QTextLine::draw()
+ }
+ return QRectF(x, y, w, l.height());
}
/*!
@@ -1289,7 +1328,14 @@ QRectF QQuickTextInput::positionToRectangle(int pos) const
return QRectF();
qreal x = l.cursorToX(pos) - d->hscroll;
qreal y = l.y() - d->vscroll;
- return QRectF(x, y, 1, l.height());
+ qreal w = 1;
+ if (d->overwriteMode) {
+ if (pos < text().length())
+ w = l.cursorToX(pos + 1) - x;
+ else
+ w = QFontMetrics(font()).width(QLatin1Char(' ')); // in sync with QTextLine::draw()
+ }
+ return QRectF(x, y, w, l.height());
}
/*!
@@ -1371,6 +1417,36 @@ int QQuickTextInputPrivate::positionAt(qreal x, qreal y, QTextLine::CursorPositi
return line.isValid() ? line.xToCursor(x, position) : 0;
}
+/*!
+ \qmlproperty bool QtQuick::TextInput::overwriteMode
+ \since 5.8
+
+ Whether text entered by the user will overwrite existing text.
+
+ As with many text editors, the text editor widget can be configured
+ to insert or overwrite existing text with new text entered by the user.
+
+ If this property is \c true, existing text is overwritten, character-for-character
+ by new text; otherwise, text is inserted at the cursor position, displacing
+ existing text.
+
+ By default, this property is \c false (new text does not overwrite existing text).
+*/
+bool QQuickTextInput::overwriteMode() const
+{
+ Q_D(const QQuickTextInput);
+ return d->overwriteMode;
+}
+
+void QQuickTextInput::setOverwriteMode(bool overwrite)
+{
+ Q_D(QQuickTextInput);
+ if (d->overwriteMode == overwrite)
+ return;
+ d->overwriteMode = overwrite;
+ emit overwriteModeChanged(overwrite);
+}
+
void QQuickTextInput::keyPressEvent(QKeyEvent* ev)
{
Q_D(QQuickTextInput);
@@ -2606,8 +2682,8 @@ void QQuickTextInputPrivate::init()
#endif
q->setFlag(QQuickItem::ItemHasContents);
#ifndef QT_NO_CLIPBOARD
- q->connect(QGuiApplication::clipboard(), SIGNAL(dataChanged()),
- q, SLOT(q_canPasteChanged()));
+ qmlobject_connect(QGuiApplication::clipboard(), QClipboard, SIGNAL(dataChanged()),
+ q, QQuickTextInput, SLOT(q_canPasteChanged()));
#endif // QT_NO_CLIPBOARD
lastSelectionStart = 0;
@@ -2768,7 +2844,7 @@ void QQuickTextInputPrivate::updateDisplayText(bool forceUpdate)
// characters)
QChar* uc = str.data();
for (int i = 0; i < (int)str.length(); ++i) {
- if ((uc[i] < 0x20 && uc[i] != 0x09)
+ if ((uc[i].unicode() < 0x20 && uc[i] != QChar::Tabulation)
|| uc[i] == QChar::LineSeparator
|| uc[i] == QChar::ParagraphSeparator
|| uc[i] == QChar::ObjectReplacementCharacter)
@@ -4454,6 +4530,14 @@ void QQuickTextInputPrivate::processKeyEvent(QKeyEvent* event)
if (unknown && !m_readOnly) {
QString t = event->text();
if (!t.isEmpty() && t.at(0).isPrint()) {
+ if (overwriteMode
+ // no need to call del() if we have a selection, insert
+ // does it already
+ && !hasSelectedText()
+ && !(m_cursor == q_func()->text().length())) {
+ del();
+ }
+
insert(t);
event->accept();
return;
diff --git a/src/quick/items/qquicktextinput_p.h b/src/quick/items/qquicktextinput_p.h
index 211d9146fc..d0461f551e 100644
--- a/src/quick/items/qquicktextinput_p.h
+++ b/src/quick/items/qquicktextinput_p.h
@@ -79,6 +79,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickTextInput : public QQuickImplicitSizeItem
Q_PROPERTY(int cursorPosition READ cursorPosition WRITE setCursorPosition NOTIFY cursorPositionChanged)
Q_PROPERTY(QRectF cursorRectangle READ cursorRectangle NOTIFY cursorRectangleChanged)
Q_PROPERTY(QQmlComponent *cursorDelegate READ cursorDelegate WRITE setCursorDelegate NOTIFY cursorDelegateChanged)
+ Q_PROPERTY(bool overwriteMode READ overwriteMode WRITE setOverwriteMode NOTIFY overwriteModeChanged)
Q_PROPERTY(int selectionStart READ selectionStart NOTIFY selectionStartChanged)
Q_PROPERTY(int selectionEnd READ selectionEnd NOTIFY selectionEndChanged)
Q_PROPERTY(QString selectedText READ selectedText NOTIFY selectedTextChanged)
@@ -245,6 +246,9 @@ public:
QQmlComponent* cursorDelegate() const;
void setCursorDelegate(QQmlComponent*);
+ bool overwriteMode() const;
+ void setOverwriteMode(bool overwrite);
+
bool focusOnPress() const;
void setFocusOnPress(bool);
@@ -325,6 +329,7 @@ Q_SIGNALS:
void readOnlyChanged(bool isReadOnly);
void cursorVisibleChanged(bool isCursorVisible);
void cursorDelegateChanged();
+ void overwriteModeChanged(bool overwriteMode);
void maximumLengthChanged(int maximumLength);
void validatorChanged();
void inputMaskChanged(const QString &inputMask);
diff --git a/src/quick/items/qquicktextinput_p_p.h b/src/quick/items/qquicktextinput_p_p.h
index 57a9f6413a..93a8778c40 100644
--- a/src/quick/items/qquicktextinput_p_p.h
+++ b/src/quick/items/qquicktextinput_p_p.h
@@ -158,6 +158,7 @@ public:
, m_passwordEchoEditing(false)
, inLayout(false)
, requireImplicitWidth(false)
+ , overwriteMode(false)
{
}
@@ -299,6 +300,7 @@ public:
bool m_passwordEchoEditing : 1;
bool inLayout:1;
bool requireImplicitWidth:1;
+ bool overwriteMode:1;
static inline QQuickTextInputPrivate *get(QQuickTextInput *t) {
return t->d_func();
diff --git a/src/quick/items/qquicktextnode.cpp b/src/quick/items/qquicktextnode.cpp
index b0bb60f566..8716f98bff 100644
--- a/src/quick/items/qquicktextnode.cpp
+++ b/src/quick/items/qquicktextnode.cpp
@@ -136,7 +136,7 @@ void QQuickTextNode::setCursor(const QRectF &rect, const QColor &color)
delete m_cursorNode;
QSGRenderContext *sg = QQuickItemPrivate::get(m_ownerElement)->sceneGraphRenderContext();
- m_cursorNode = sg->sceneGraphContext()->createRectangleNode(rect, color);
+ m_cursorNode = sg->sceneGraphContext()->createInternalRectangleNode(rect, color);
appendChildNode(m_cursorNode);
}
@@ -151,14 +151,14 @@ void QQuickTextNode::clearCursor()
void QQuickTextNode::addRectangleNode(const QRectF &rect, const QColor &color)
{
QSGRenderContext *sg = QQuickItemPrivate::get(m_ownerElement)->sceneGraphRenderContext();
- appendChildNode(sg->sceneGraphContext()->createRectangleNode(rect, color));
+ appendChildNode(sg->sceneGraphContext()->createInternalRectangleNode(rect, color));
}
void QQuickTextNode::addImage(const QRectF &rect, const QImage &image)
{
QSGRenderContext *sg = QQuickItemPrivate::get(m_ownerElement)->sceneGraphRenderContext();
- QSGImageNode *node = sg->sceneGraphContext()->createImageNode();
+ QSGInternalImageNode *node = sg->sceneGraphContext()->createInternalImageNode();
QSGTexture *texture = sg->createTexture(image);
if (m_ownerElement->smooth()) {
texture->setFiltering(QSGTexture::Linear);
diff --git a/src/quick/items/qquicktextnode_p.h b/src/quick/items/qquicktextnode_p.h
index 0006cf1156..2969ce9dbc 100644
--- a/src/quick/items/qquicktextnode_p.h
+++ b/src/quick/items/qquicktextnode_p.h
@@ -68,7 +68,7 @@ class QColor;
class QTextDocument;
class QSGContext;
class QRawFont;
-class QSGRectangleNode;
+class QSGInternalRectangleNode;
class QSGClipNode;
class QSGTexture;
@@ -77,15 +77,6 @@ class QQuickTextNodeEngine;
class Q_QUICK_PRIVATE_EXPORT QQuickTextNode : public QSGTransformNode
{
public:
- enum Decoration {
- NoDecoration = 0x0,
- Underline = 0x1,
- Overline = 0x2,
- StrikeOut = 0x4,
- Background = 0x8
- };
- Q_DECLARE_FLAGS(Decorations, Decoration)
-
QQuickTextNode(QQuickItem *ownerElement);
~QQuickTextNode();
@@ -106,7 +97,7 @@ public:
void setCursor(const QRectF &rect, const QColor &color);
void clearCursor();
- QSGRectangleNode *cursorNode() const { return m_cursorNode; }
+ QSGInternalRectangleNode *cursorNode() const { return m_cursorNode; }
QSGGlyphNode *addGlyphs(const QPointF &position, const QGlyphRun &glyphs, const QColor &color,
QQuickText::TextStyle style = QQuickText::Normal, const QColor &styleColor = QColor(),
@@ -118,7 +109,7 @@ public:
void setUseNativeRenderer(bool on) { m_useNativeRenderer = on; }
private:
- QSGRectangleNode *m_cursorNode;
+ QSGInternalRectangleNode *m_cursorNode;
QList<QSGTexture *> m_textures;
QQuickItem *m_ownerElement;
bool m_useNativeRenderer;
diff --git a/src/quick/items/qquicktextnodeengine.cpp b/src/quick/items/qquicktextnodeengine.cpp
index ef94b0eef7..4631b2e724 100644
--- a/src/quick/items/qquicktextnodeengine.cpp
+++ b/src/quick/items/qquicktextnodeengine.cpp
@@ -68,7 +68,7 @@ QQuickTextNodeEngine::BinaryTreeNodeKey::BinaryTreeNodeKey(BinaryTreeNode *node)
QQuickTextNodeEngine::BinaryTreeNode::BinaryTreeNode(const QGlyphRun &g,
SelectionState selState,
const QRectF &brect,
- const QQuickTextNode::Decorations &decs,
+ const Decorations &decs,
const QColor &c,
const QColor &bc,
const QPointF &pos, qreal a)
@@ -90,7 +90,7 @@ QQuickTextNodeEngine::BinaryTreeNode::BinaryTreeNode(const QGlyphRun &g,
void QQuickTextNodeEngine::BinaryTreeNode::insert(QVarLengthArray<BinaryTreeNode, 16> *binaryTree, const QGlyphRun &glyphRun, SelectionState selectionState,
- QQuickTextNode::Decorations decorations, const QColor &textColor,
+ Decorations decorations, const QColor &textColor,
const QColor &backgroundColor, const QPointF &position)
{
QRectF searchRect = glyphRun.boundingRect();
@@ -99,10 +99,10 @@ void QQuickTextNodeEngine::BinaryTreeNode::insert(QVarLengthArray<BinaryTreeNode
if (qFuzzyIsNull(searchRect.width()) || qFuzzyIsNull(searchRect.height()))
return;
- decorations |= (glyphRun.underline() ? QQuickTextNode::Underline : QQuickTextNode::NoDecoration);
- decorations |= (glyphRun.overline() ? QQuickTextNode::Overline : QQuickTextNode::NoDecoration);
- decorations |= (glyphRun.strikeOut() ? QQuickTextNode::StrikeOut : QQuickTextNode::NoDecoration);
- decorations |= (backgroundColor.isValid() ? QQuickTextNode::Background : QQuickTextNode::NoDecoration);
+ decorations |= (glyphRun.underline() ? Decoration::Underline : Decoration::NoDecoration);
+ decorations |= (glyphRun.overline() ? Decoration::Overline : Decoration::NoDecoration);
+ decorations |= (glyphRun.strikeOut() ? Decoration::StrikeOut : Decoration::NoDecoration);
+ decorations |= (backgroundColor.isValid() ? Decoration::Background : Decoration::NoDecoration);
qreal ascent = glyphRun.rawFont().ascent();
insert(binaryTree, BinaryTreeNode(glyphRun,
@@ -237,7 +237,7 @@ void QQuickTextNodeEngine::processCurrentLine()
SelectionState currentSelectionState = Unselected;
QRectF currentRect;
- QQuickTextNode::Decorations currentDecorations = QQuickTextNode::NoDecoration;
+ Decorations currentDecorations = Decoration::NoDecoration;
qreal underlineOffset = 0.0;
qreal underlineThickness = 0.0;
@@ -271,7 +271,7 @@ void QQuickTextNodeEngine::processCurrentLine()
currentSelectionState = node->selectionState;
// Update decorations
- if (currentDecorations != QQuickTextNode::NoDecoration) {
+ if (currentDecorations != Decoration::NoDecoration) {
decorationRect.setY(m_position.y() + m_currentLine.y());
decorationRect.setHeight(m_currentLine.height());
@@ -279,16 +279,16 @@ void QQuickTextNodeEngine::processCurrentLine()
decorationRect.setRight(node->boundingRect.left());
TextDecoration textDecoration(currentSelectionState, decorationRect, lastColor);
- if (currentDecorations & QQuickTextNode::Underline)
+ if (currentDecorations & Decoration::Underline)
pendingUnderlines.append(textDecoration);
- if (currentDecorations & QQuickTextNode::Overline)
+ if (currentDecorations & Decoration::Overline)
pendingOverlines.append(textDecoration);
- if (currentDecorations & QQuickTextNode::StrikeOut)
+ if (currentDecorations & Decoration::StrikeOut)
pendingStrikeOuts.append(textDecoration);
- if (currentDecorations & QQuickTextNode::Background)
+ if (currentDecorations & Decoration::Background)
m_backgrounds.append(qMakePair(decorationRect, lastBackgroundColor));
}
@@ -344,7 +344,7 @@ void QQuickTextNodeEngine::processCurrentLine()
// If previous item(s) had underline and current does not, then we add the
// pending lines to the lists and likewise for overlines and strikeouts
if (!pendingUnderlines.isEmpty()
- && !(node->decorations & QQuickTextNode::Underline)) {
+ && !(node->decorations & Decoration::Underline)) {
addTextDecorations(pendingUnderlines, underlineOffset, underlineThickness);
pendingUnderlines.clear();
@@ -377,19 +377,19 @@ void QQuickTextNodeEngine::processCurrentLine()
// Merge current values with previous. Prefer greatest thickness
QRawFont rawFont = node->glyphRun.rawFont();
- if (node->decorations & QQuickTextNode::Underline) {
+ if (node->decorations & Decoration::Underline) {
if (rawFont.lineThickness() > underlineThickness) {
underlineThickness = rawFont.lineThickness();
underlineOffset = rawFont.underlinePosition();
}
}
- if (node->decorations & QQuickTextNode::Overline) {
+ if (node->decorations & Decoration::Overline) {
overlineOffset = -rawFont.ascent();
overlineThickness = rawFont.lineThickness();
}
- if (node->decorations & QQuickTextNode::StrikeOut) {
+ if (node->decorations & Decoration::StrikeOut) {
strikeOutThickness = rawFont.lineThickness();
strikeOutOffset = rawFont.ascent() / -3.0;
}
@@ -495,7 +495,7 @@ void QQuickTextNodeEngine::addUnselectedGlyphs(const QGlyphRun &glyphRun)
BinaryTreeNode::insert(&m_currentLineTree,
glyphRun,
Unselected,
- QQuickTextNode::NoDecoration,
+ Decoration::NoDecoration,
m_textColor,
m_backgroundColor,
m_position);
@@ -507,7 +507,7 @@ void QQuickTextNodeEngine::addSelectedGlyphs(const QGlyphRun &glyphRun)
BinaryTreeNode::insert(&m_currentLineTree,
glyphRun,
Selected,
- QQuickTextNode::NoDecoration,
+ Decoration::NoDecoration,
m_textColor,
m_backgroundColor,
m_position);
@@ -734,10 +734,13 @@ void QQuickTextNodeEngine::mergeProcessedNodes(QList<BinaryTreeNode *> *regularN
QVector<QPointF> glyphPositions = glyphRun.positions();
glyphPositions.reserve(count);
+ QRectF glyphBoundingRect = glyphRun.boundingRect();
+
for (int j = 1; j < nodes.size(); ++j) {
BinaryTreeNode *otherNode = nodes.at(j);
glyphIndexes += otherNode->glyphRun.glyphIndexes();
primaryNode->ranges += otherNode->ranges;
+ glyphBoundingRect = glyphBoundingRect.united(otherNode->boundingRect);
QVector<QPointF> otherPositions = otherNode->glyphRun.positions();
for (int k = 0; k < otherPositions.size(); ++k)
@@ -749,6 +752,7 @@ void QQuickTextNodeEngine::mergeProcessedNodes(QList<BinaryTreeNode *> *regularN
glyphRun.setGlyphIndexes(glyphIndexes);
glyphRun.setPositions(glyphPositions);
+ glyphRun.setBoundingRect(glyphBoundingRect);
}
}
}
diff --git a/src/quick/items/qquicktextnodeengine_p.h b/src/quick/items/qquicktextnodeengine_p.h
index 87235344e6..91ed6f4430 100644
--- a/src/quick/items/qquicktextnodeengine_p.h
+++ b/src/quick/items/qquicktextnodeengine_p.h
@@ -68,8 +68,15 @@ QT_BEGIN_NAMESPACE
// number of nodes, and join decorations in neighbouring items
class QQuickTextNodeEngine {
-
public:
+ enum Decoration {
+ NoDecoration = 0x0,
+ Underline = 0x1,
+ Overline = 0x2,
+ StrikeOut = 0x4,
+ Background = 0x8
+ };
+ Q_DECLARE_FLAGS(Decorations, Decoration)
enum SelectionState {
Unselected,
@@ -79,26 +86,26 @@ public:
struct BinaryTreeNode {
BinaryTreeNode()
- : selectionState(Unselected), clipNode(0), decorations(QQuickTextNode::NoDecoration)
+ : selectionState(Unselected), clipNode(0), decorations(Decoration::NoDecoration)
, ascent(0.0), leftChildIndex(-1), rightChildIndex(-1)
{
}
BinaryTreeNode(const QRectF &brect, const QImage &i, SelectionState selState, qreal a)
- : boundingRect(brect), selectionState(selState), clipNode(0), decorations(QQuickTextNode::NoDecoration)
+ : boundingRect(brect), selectionState(selState), clipNode(0), decorations(Decoration::NoDecoration)
, image(i), ascent(a), leftChildIndex(-1), rightChildIndex(-1)
{
}
BinaryTreeNode(const QGlyphRun &g, SelectionState selState, const QRectF &brect,
- const QQuickTextNode::Decorations &decs, const QColor &c, const QColor &bc,
+ const Decorations &decs, const QColor &c, const QColor &bc,
const QPointF &pos, qreal a);
QGlyphRun glyphRun;
QRectF boundingRect;
SelectionState selectionState;
QQuickDefaultClipNode *clipNode;
- QQuickTextNode::Decorations decorations;
+ Decorations decorations;
QColor color;
QColor backgroundColor;
QPointF position;
@@ -114,7 +121,7 @@ public:
{ insert(binaryTree, BinaryTreeNode(rect, image, selectionState, ascent)); }
static void insert(QVarLengthArray<BinaryTreeNode, 16> *binaryTree, const QGlyphRun &glyphRun, SelectionState selectionState,
- QQuickTextNode::Decorations decorations, const QColor &textColor, const QColor &backgroundColor, const QPointF &position);
+ Decorations decorations, const QColor &textColor, const QColor &backgroundColor, const QPointF &position);
static void insert(QVarLengthArray<BinaryTreeNode, 16> *binaryTree, const BinaryTreeNode &binaryTreeNode);
static void inOrder(const QVarLengthArray<BinaryTreeNode, 16> &binaryTree, QVarLengthArray<int> *sortedIndexes, int currentIndex = 0);
};
diff --git a/src/quick/items/qquickview.cpp b/src/quick/items/qquickview.cpp
index f8973ebfba..a167f01484 100644
--- a/src/quick/items/qquickview.cpp
+++ b/src/quick/items/qquickview.cpp
@@ -44,9 +44,6 @@
#include "qquickitem_p.h"
#include "qquickitemchangelistener_p.h"
-#include <private/qqmldebugconnector_p.h>
-#include <private/qquickprofiler_p.h>
-#include <private/qqmldebugserviceinterfaces_p.h>
#include <private/qqmlmemoryprofiler_p.h>
#include <QtQml/qqmlengine.h>
@@ -113,14 +110,15 @@ void QQuickViewPrivate::execute()
}
}
-void QQuickViewPrivate::itemGeometryChanged(QQuickItem *resizeItem, const QRectF &newGeometry, const QRectF &oldGeometry)
+void QQuickViewPrivate::itemGeometryChanged(QQuickItem *resizeItem, QQuickGeometryChange change,
+ const QRectF &diff)
{
Q_Q(QQuickView);
if (resizeItem == root && resizeMode == QQuickView::SizeViewToRootObject) {
// wait for both width and height to be changed
resizetimer.start(0,q);
}
- QQuickItemChangeListener::itemGeometryChanged(resizeItem, newGeometry, oldGeometry);
+ QQuickItemChangeListener::itemGeometryChanged(resizeItem, change, diff);
}
/*!
@@ -176,9 +174,8 @@ QQuickView::QQuickView(QWindow *parent)
*/
QQuickView::QQuickView(const QUrl &source, QWindow *parent)
-: QQuickWindow(*(new QQuickViewPrivate), parent)
+ : QQuickView(parent)
{
- d_func()->init();
setSource(source);
}
@@ -249,8 +246,8 @@ void QQuickView::setContent(const QUrl& url, QQmlComponent *component, QObject*
d->component = component;
if (d->component && d->component->isError()) {
- QList<QQmlError> errorList = d->component->errors();
- foreach (const QQmlError &error, errorList) {
+ const QList<QQmlError> errorList = d->component->errors();
+ for (const QQmlError &error : errorList) {
QMessageLogger(error.url().toString().toLatin1().constData(), error.line(), 0).warning()
<< error;
}
@@ -415,9 +412,14 @@ void QQuickViewPrivate::updateSize()
q->resize(newSize);
}
} else if (resizeMode == QQuickView::SizeRootObjectToView) {
- if (!qFuzzyCompare(q->width(), root->width()))
+ bool needToUpdateWidth = !qFuzzyCompare(q->width(), root->width());
+ bool needToUpdateHeight = !qFuzzyCompare(q->height(), root->height());
+
+ if (needToUpdateWidth && needToUpdateHeight)
+ root->setSize(QSizeF(q->width(), q->height()));
+ else if (needToUpdateWidth)
root->setWidth(q->width());
- if (!qFuzzyCompare(q->height(), root->height()))
+ else if (needToUpdateHeight)
root->setHeight(q->height());
}
}
@@ -455,8 +457,8 @@ void QQuickView::continueExecute()
disconnect(d->component, SIGNAL(statusChanged(QQmlComponent::Status)), this, SLOT(continueExecute()));
if (d->component->isError()) {
- QList<QQmlError> errorList = d->component->errors();
- foreach (const QQmlError &error, errorList) {
+ const QList<QQmlError> errorList = d->component->errors();
+ for (const QQmlError &error : errorList) {
QMessageLogger(error.url().toString().toLatin1().constData(), error.line(), 0).warning()
<< error;
}
@@ -467,8 +469,8 @@ void QQuickView::continueExecute()
QObject *obj = d->component->create();
if (d->component->isError()) {
- QList<QQmlError> errorList = d->component->errors();
- foreach (const QQmlError &error, errorList) {
+ const QList<QQmlError> errorList = d->component->errors();
+ for (const QQmlError &error : errorList) {
QMessageLogger(error.url().toString().toLatin1().constData(), error.line(), 0).warning()
<< error;
}
diff --git a/src/quick/items/qquickview_p.h b/src/quick/items/qquickview_p.h
index fd033daf2f..a090bdc9f7 100644
--- a/src/quick/items/qquickview_p.h
+++ b/src/quick/items/qquickview_p.h
@@ -88,7 +88,7 @@ public:
~QQuickViewPrivate();
void execute();
- void itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry) Q_DECL_OVERRIDE;
+ void itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change, const QRectF &) Q_DECL_OVERRIDE;
void initResize();
void updateSize();
void setRootObject(QObject *);
diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp
index 83aa58091a..a0a07f43cc 100644
--- a/src/quick/items/qquickwindow.cpp
+++ b/src/quick/items/qquickwindow.cpp
@@ -74,16 +74,20 @@
#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
QT_BEGIN_NAMESPACE
-Q_LOGGING_CATEGORY(DBG_TOUCH, "qt.quick.touch");
-Q_LOGGING_CATEGORY(DBG_TOUCH_TARGET, "qt.quick.touch.target");
-Q_LOGGING_CATEGORY(DBG_MOUSE, "qt.quick.mouse");
-Q_LOGGING_CATEGORY(DBG_FOCUS, "qt.quick.focus");
-Q_LOGGING_CATEGORY(DBG_DIRTY, "qt.quick.dirty");
+Q_LOGGING_CATEGORY(DBG_TOUCH, "qt.quick.touch")
+Q_LOGGING_CATEGORY(DBG_TOUCH_TARGET, "qt.quick.touch.target")
+Q_LOGGING_CATEGORY(DBG_MOUSE, "qt.quick.mouse")
+Q_LOGGING_CATEGORY(DBG_MOUSE_TARGET, "qt.quick.mouse.target")
+Q_LOGGING_CATEGORY(DBG_HOVER_TRACE, "qt.quick.hover.trace")
+Q_LOGGING_CATEGORY(DBG_FOCUS, "qt.quick.focus")
+Q_LOGGING_CATEGORY(DBG_DIRTY, "qt.quick.dirty")
extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha);
@@ -199,11 +203,6 @@ thus the first item that has focus will get it (assuming the scope doesn't alrea
have a scope focused item), and the other items will have their focus cleared.
*/
-QQuickItem::UpdatePaintNodeData::UpdatePaintNodeData()
-: transformNode(0)
-{
-}
-
QQuickRootItem::QQuickRootItem()
{
}
@@ -289,7 +288,9 @@ void QQuickWindowPrivate::polishItems()
int recursionSafeguard = INT_MAX;
while (!itemsToPolish.isEmpty() && --recursionSafeguard > 0) {
QQuickItem *item = itemsToPolish.takeLast();
- QQuickItemPrivate::get(item)->polishScheduled = false;
+ QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
+ itemPrivate->polishScheduled = false;
+ itemPrivate->updatePolish();
item->updatePolish();
}
@@ -470,7 +471,6 @@ void QQuickWindowPrivate::renderSceneGraph(const QSize &size)
QQuickWindowPrivate::QQuickWindowPrivate()
: contentItem(0)
, activeFocusItem(0)
- , mouseGrabberItem(0)
#ifndef QT_NO_CURSOR
, cursorItem(0)
#endif
@@ -478,6 +478,7 @@ QQuickWindowPrivate::QQuickWindowPrivate()
, dragGrabber(0)
#endif
, touchMouseId(-1)
+ , touchMouseDevice(nullptr)
, touchMousePressTimestamp(0)
, dirtyItemList(0)
, devicePixelRatio(0)
@@ -485,7 +486,7 @@ QQuickWindowPrivate::QQuickWindowPrivate()
, renderer(0)
, windowManager(0)
, renderControl(0)
- , touchRecursionGuard(0)
+ , pointerEventRecursionGuard(0)
, customRenderStage(0)
, clearColor(Qt::white)
, clearBeforeRendering(true)
@@ -621,8 +622,17 @@ bool QQuickWindowPrivate::checkIfDoubleClicked(ulong newPressEventTimestamp)
return doubleClicked;
}
-bool QQuickWindowPrivate::translateTouchToMouse(QQuickItem *item, QTouchEvent *event)
+bool QQuickWindowPrivate::deliverTouchAsMouse(QQuickItem *item, QQuickPointerEvent *pointerEvent)
{
+ Q_Q(QQuickWindow);
+ auto device = pointerEvent->device();
+
+ // FIXME: make this work for mouse events too and get rid of the asTouchEvent in here.
+ Q_ASSERT(pointerEvent->asPointerTouchEvent());
+ QTouchEvent *event = pointerEvent->asPointerTouchEvent()->touchEventForItem(item);
+ if (!event)
+ return false;
+
// For each point, check if it is accepted, if not, try the next point.
// Any of the fingers can become the mouse one.
// This can happen because a mouse area might not accept an event at some point but another.
@@ -636,65 +646,45 @@ bool QQuickWindowPrivate::translateTouchToMouse(QQuickItem *item, QTouchEvent *e
if (!item->contains(pos))
break;
- // Store the id already here and restore it to -1 if the event does not get
- // accepted. Cannot defer setting the new value because otherwise if the event
- // handler spins the event loop all subsequent moves and releases get lost.
- touchMouseId = p.id();
- itemForTouchPointId[touchMouseId] = item;
qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << p.id() << "->" << item;
QScopedPointer<QMouseEvent> mousePress(touchToMouseEvent(QEvent::MouseButtonPress, p, event, item, false));
// Send a single press and see if that's accepted
- if (!mouseGrabberItem)
- item->grabMouse();
- item->grabTouchPoints(QVector<int>() << touchMouseId);
-
QCoreApplication::sendEvent(item, mousePress.data());
event->setAccepted(mousePress->isAccepted());
- if (!mousePress->isAccepted()) {
- touchMouseId = -1;
- if (itemForTouchPointId.value(p.id()) == item) {
- qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << p.id() << "disassociated";
- itemForTouchPointId.remove(p.id());
+ if (mousePress->isAccepted()) {
+ touchMouseDevice = device;
+ touchMouseId = p.id();
+ if (!q->mouseGrabberItem())
+ item->grabMouse();
+ auto pointerEventPoint = pointerEvent->pointById(p.id());
+ pointerEventPoint->setGrabber(item);
+
+ if (checkIfDoubleClicked(event->timestamp())) {
+ QScopedPointer<QMouseEvent> mouseDoubleClick(touchToMouseEvent(QEvent::MouseButtonDblClick, p, event, item, false));
+ QCoreApplication::sendEvent(item, mouseDoubleClick.data());
+ event->setAccepted(mouseDoubleClick->isAccepted());
+ if (!mouseDoubleClick->isAccepted()) {
+ touchMouseId = -1;
+ touchMouseDevice = nullptr;
+ }
}
- if (mouseGrabberItem == item)
- item->ungrabMouse();
- }
-
- if (mousePress->isAccepted() && checkIfDoubleClicked(event->timestamp())) {
- QScopedPointer<QMouseEvent> mouseDoubleClick(touchToMouseEvent(QEvent::MouseButtonDblClick, p, event, item, false));
- QCoreApplication::sendEvent(item, mouseDoubleClick.data());
- event->setAccepted(mouseDoubleClick->isAccepted());
- if (mouseDoubleClick->isAccepted()) {
- touchMouseIdCandidates.clear();
- return true;
- } else {
- touchMouseId = -1;
- }
- }
- // The event was accepted, we are done.
- if (mousePress->isAccepted()) {
- touchMouseIdCandidates.clear();
return true;
}
- // The event was not accepted but touchMouseId was set.
- if (touchMouseId != -1)
- return false;
// try the next point
// Touch point was there before and moved
- } else if (p.id() == touchMouseId) {
+ } else if (touchMouseDevice == device && p.id() == touchMouseId) {
if (p.state() & Qt::TouchPointMoved) {
- if (mouseGrabberItem) {
+ if (QQuickItem *mouseGrabberItem = q->mouseGrabberItem()) {
QScopedPointer<QMouseEvent> me(touchToMouseEvent(QEvent::MouseMove, p, event, mouseGrabberItem, false));
QCoreApplication::sendEvent(item, me.data());
event->setAccepted(me->isAccepted());
if (me->isAccepted()) {
qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << p.id() << "->" << mouseGrabberItem;
- itemForTouchPointId[p.id()] = mouseGrabberItem; // N.B. the mouseGrabberItem may be different after returning from sendEvent()
- return true;
}
+ return event->isAccepted();
} else {
// no grabber, check if we care about mouse hover
// FIXME: this should only happen once, not recursively... I'll ignore it just ignore hover now.
@@ -716,8 +706,7 @@ bool QQuickWindowPrivate::translateTouchToMouse(QQuickItem *item, QTouchEvent *e
}
} else if (p.state() & Qt::TouchPointReleased) {
// currently handled point was released
- touchMouseId = -1;
- if (mouseGrabberItem) {
+ if (QQuickItem *mouseGrabberItem = q->mouseGrabberItem()) {
QScopedPointer<QMouseEvent> me(touchToMouseEvent(QEvent::MouseButtonRelease, p, event, mouseGrabberItem, false));
QCoreApplication::sendEvent(item, me.data());
@@ -729,8 +718,11 @@ bool QQuickWindowPrivate::translateTouchToMouse(QQuickItem *item, QTouchEvent *e
Qt::NoButton, Qt::NoButton, event->modifiers());
QCoreApplication::sendEvent(item, &mm);
}
- if (mouseGrabberItem) // might have ungrabbed due to event
- mouseGrabberItem->ungrabMouse();
+ if (q->mouseGrabberItem()) // might have ungrabbed due to event
+ q->mouseGrabberItem()->ungrabMouse();
+
+ touchMouseId = -1;
+ touchMouseDevice = nullptr;
return me->isAccepted();
}
}
@@ -743,39 +735,94 @@ bool QQuickWindowPrivate::translateTouchToMouse(QQuickItem *item, QTouchEvent *e
void QQuickWindowPrivate::setMouseGrabber(QQuickItem *grabber)
{
Q_Q(QQuickWindow);
- if (mouseGrabberItem == grabber)
+ if (q->mouseGrabberItem() == grabber)
return;
- QQuickItem *oldGrabber = mouseGrabberItem;
- mouseGrabberItem = grabber;
+ qCDebug(DBG_MOUSE_TARGET) << "grabber" << q->mouseGrabberItem() << "->" << grabber;
+ QQuickItem *oldGrabber = q->mouseGrabberItem();
- if (touchMouseId != -1) {
+ if (grabber && touchMouseId != -1 && touchMouseDevice) {
// update the touch item for mouse touch id to the new grabber
- itemForTouchPointId.remove(touchMouseId);
- if (grabber) {
- qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << touchMouseId << "->" << mouseGrabberItem;
- itemForTouchPointId[touchMouseId] = grabber;
- }
+ qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << touchMouseId << "->" << q->mouseGrabberItem();
+ auto point = touchMouseDevice->pointerEvent()->pointById(touchMouseId);
+ if (point)
+ point->setGrabber(grabber);
+ } else {
+ QQuickPointerEvent *event = QQuickPointerDevice::genericMouseDevice()->pointerEvent();
+ Q_ASSERT(event->pointCount() == 1);
+ event->point(0)->setGrabber(grabber);
}
if (oldGrabber) {
- QEvent ev(QEvent::UngrabMouse);
- q->sendEvent(oldGrabber, &ev);
+ QEvent e(QEvent::UngrabMouse);
+ QSet<QQuickItem *> hasFiltered;
+ if (!sendFilteredMouseEvent(oldGrabber->parentItem(), oldGrabber, &e, &hasFiltered))
+ oldGrabber->mouseUngrabEvent();
}
}
-void QQuickWindowPrivate::transformTouchPoints(QList<QTouchEvent::TouchPoint> &touchPoints, const QTransform &transform)
+void QQuickWindowPrivate::grabTouchPoints(QQuickItem *grabber, const QVector<int> &ids)
{
- QMatrix4x4 transformMatrix(transform);
- for (int i=0; i<touchPoints.count(); i++) {
- QTouchEvent::TouchPoint &touchPoint = touchPoints[i];
- touchPoint.setRect(transform.mapRect(touchPoint.sceneRect()));
- touchPoint.setStartPos(transform.map(touchPoint.startScenePos()));
- touchPoint.setLastPos(transform.map(touchPoint.lastScenePos()));
- touchPoint.setVelocity(transformMatrix.mapVector(touchPoint.velocity()).toVector2D());
+ QSet<QQuickItem*> ungrab;
+ for (int i = 0; i < ids.count(); ++i) {
+ // FIXME: deprecate this function, we need a device
+ int id = ids.at(i);
+ if (Q_UNLIKELY(id < 0)) {
+ qWarning("ignoring grab of touchpoint %d", id);
+ continue;
+ }
+ if (id == touchMouseId) {
+ auto point = touchMouseDevice->pointerEvent()->pointById(id);
+ auto touchMouseGrabber = point->grabber();
+ if (touchMouseGrabber) {
+ point->setGrabber(nullptr);
+ touchMouseGrabber->mouseUngrabEvent();
+ ungrab.insert(touchMouseGrabber);
+ touchMouseDevice = nullptr;
+ touchMouseId = -1;
+ }
+ qCDebug(DBG_MOUSE_TARGET) << "grabTouchPoints: mouse grabber changed due to grabTouchPoints:" << touchMouseGrabber << "-> null";
+ }
+
+ const auto touchDevices = QQuickPointerDevice::touchDevices();
+ for (auto device : touchDevices) {
+ auto point = device->pointerEvent()->pointById(id);
+ if (!point)
+ continue;
+ QQuickItem *oldGrabber = point->grabber();
+ if (oldGrabber == grabber)
+ continue;
+
+ point->setGrabber(grabber);
+ if (oldGrabber)
+ ungrab.insert(oldGrabber);
+ }
}
+ for (QQuickItem *oldGrabber : qAsConst(ungrab))
+ oldGrabber->touchUngrabEvent();
}
+void QQuickWindowPrivate::removeGrabber(QQuickItem *grabber, bool mouse, bool touch)
+{
+ Q_Q(QQuickWindow);
+ if (Q_LIKELY(touch)) {
+ const auto touchDevices = QQuickPointerDevice::touchDevices();
+ for (auto device : touchDevices) {
+ auto pointerEvent = device->pointerEvent();
+ for (int i = 0; i < pointerEvent->pointCount(); ++i) {
+ if (pointerEvent->point(i)->grabber() == grabber) {
+ pointerEvent->point(i)->setGrabber(nullptr);
+ // FIXME send ungrab event only once
+ grabber->touchUngrabEvent();
+ }
+ }
+ }
+ }
+ if (Q_LIKELY(mouse) && q->mouseGrabberItem() == grabber) {
+ qCDebug(DBG_MOUSE_TARGET) << "removeGrabber" << q->mouseGrabberItem() << "-> null";
+ setMouseGrabber(nullptr);
+ }
+}
/*!
Translates the data in \a touchEvent to this window. This method leaves the item local positions in
@@ -787,16 +834,9 @@ void QQuickWindowPrivate::translateTouchEvent(QTouchEvent *touchEvent)
for (int i = 0; i < touchPoints.count(); ++i) {
QTouchEvent::TouchPoint &touchPoint = touchPoints[i];
- touchPoint.setScreenRect(touchPoint.sceneRect());
- touchPoint.setStartScreenPos(touchPoint.startScenePos());
- touchPoint.setLastScreenPos(touchPoint.lastScenePos());
-
touchPoint.setSceneRect(touchPoint.rect());
touchPoint.setStartScenePos(touchPoint.startPos());
touchPoint.setLastScenePos(touchPoint.lastPos());
-
- if (i == 0)
- lastMousePosition = touchPoint.pos().toPoint();
}
touchEvent->setTouchPoints(touchPoints);
}
@@ -907,17 +947,16 @@ void QQuickWindowPrivate::setFocusInScope(QQuickItem *scope, QQuickItem *item, Q
}
// Now that all the state is changed, emit signals & events
- // We must do this last, as this process may result in further changes to
- // focus.
+ // We must do this last, as this process may result in further changes to focus.
if (oldActiveFocusItem) {
QFocusEvent event(QEvent::FocusOut, reason);
- q->sendEvent(oldActiveFocusItem, &event);
+ QCoreApplication::sendEvent(oldActiveFocusItem, &event);
}
// Make sure that the FocusOut didn't result in another focus change.
if (sendFocusIn && activeFocusItem == newActiveFocusItem) {
QFocusEvent event(QEvent::FocusIn, reason);
- q->sendEvent(newActiveFocusItem, &event);
+ QCoreApplication::sendEvent(newActiveFocusItem, &event);
}
if (activeFocusItem != currentActiveFocusItem)
@@ -1004,13 +1043,13 @@ void QQuickWindowPrivate::clearFocusInScope(QQuickItem *scope, QQuickItem *item,
// focus.
if (oldActiveFocusItem) {
QFocusEvent event(QEvent::FocusOut, reason);
- q->sendEvent(oldActiveFocusItem, &event);
+ QCoreApplication::sendEvent(oldActiveFocusItem, &event);
}
// Make sure that the FocusOut didn't result in another focus change.
if (newActiveFocusItem && activeFocusItem == newActiveFocusItem) {
QFocusEvent event(QEvent::FocusIn, reason);
- q->sendEvent(newActiveFocusItem, &event);
+ QCoreApplication::sendEvent(newActiveFocusItem, &event);
}
if (activeFocusItem != currentActiveFocusItem)
@@ -1120,8 +1159,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
@@ -1135,9 +1174,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.
@@ -1150,10 +1189,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.
@@ -1169,8 +1208,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.
@@ -1193,10 +1233,8 @@ void QQuickWindowPrivate::cleanup(QSGNode *n)
Constructs a window for displaying a QML scene with parent window \a parent.
*/
QQuickWindow::QQuickWindow(QWindow *parent)
- : QWindow(*(new QQuickWindowPrivate), parent)
+ : QQuickWindow(*new QQuickWindowPrivate, parent)
{
- Q_D(QQuickWindow);
- d->init(this);
}
@@ -1306,6 +1344,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()
*/
@@ -1323,7 +1364,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
@@ -1438,7 +1480,16 @@ QQuickItem *QQuickWindow::mouseGrabberItem() const
{
Q_D(const QQuickWindow);
- return d->mouseGrabberItem;
+ if (d->touchMouseId != -1 && d->touchMouseDevice) {
+ QQuickPointerEvent *event = d->touchMouseDevice->pointerEvent();
+ auto point = event->pointById(d->touchMouseId);
+ Q_ASSERT(point);
+ return point->grabber();
+ }
+
+ QQuickPointerEvent *event = QQuickPointerDevice::genericMouseDevice()->pointerEvent();
+ Q_ASSERT(event->pointCount());
+ return event->point(0)->grabber();
}
@@ -1451,7 +1502,7 @@ bool QQuickWindowPrivate::clearHover(ulong timestamp)
QPointF pos = q->mapFromGlobal(QGuiApplicationPrivate::lastCursorPosition.toPoint());
bool accepted = false;
- foreach (QQuickItem* item, hoverItems)
+ for (QQuickItem* item : qAsConst(hoverItems))
accepted = sendHoverEvent(QEvent::HoverLeave, item, pos, pos, QGuiApplication::keyboardModifiers(), timestamp, true) || accepted;
hoverItems.clear();
return accepted;
@@ -1468,8 +1519,7 @@ bool QQuickWindow::event(QEvent *e)
case QEvent::TouchUpdate:
case QEvent::TouchEnd: {
QTouchEvent *touch = static_cast<QTouchEvent*>(e);
- d->translateTouchEvent(touch);
- d->deliverTouchEvent(touch);
+ d->handleTouchEvent(touch);
if (Q_LIKELY(QCoreApplication::testAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents))) {
// we consume all touch events ourselves to avoid duplicate
// mouse delivery by QtGui mouse synthesis
@@ -1493,7 +1543,7 @@ bool QQuickWindow::event(QEvent *e)
break;
case QEvent::Leave:
d->clearHover();
- d->lastMousePosition = QPoint();
+ d->lastMousePosition = QPointF();
break;
#ifndef QT_NO_DRAGANDDROP
case QEvent::DragEnter:
@@ -1519,8 +1569,8 @@ bool QQuickWindow::event(QEvent *e)
if (d->activeFocusItem)
qGuiApp->inputMethod()->commit();
#endif
- if (d->mouseGrabberItem)
- d->mouseGrabberItem->ungrabMouse();
+ if (mouseGrabberItem())
+ mouseGrabberItem()->ungrabMouse();
break;
case QEvent::UpdateRequest: {
if (d->windowManager)
@@ -1534,7 +1584,7 @@ bool QQuickWindow::event(QEvent *e)
#endif
case QEvent::ShortcutOverride:
if (d->activeFocusItem)
- sendEvent(d->activeFocusItem, static_cast<QKeyEvent *>(e));
+ QCoreApplication::sendEvent(d->activeFocusItem, e);
return true;
default:
break;
@@ -1586,143 +1636,44 @@ QMouseEvent *QQuickWindowPrivate::cloneMouseEvent(QMouseEvent *event, QPointF *t
return me;
}
-bool QQuickWindowPrivate::deliverInitialMousePressEvent(QQuickItem *item, QMouseEvent *event)
+void QQuickWindowPrivate::deliverMouseEvent(QQuickPointerMouseEvent *pointerEvent)
{
Q_Q(QQuickWindow);
-
- QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
-
- if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
- QPointF p = item->mapFromScene(event->windowPos());
- if (!item->contains(p))
- return false;
- }
-
- QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
- for (int ii = children.count() - 1; ii >= 0; --ii) {
- QQuickItem *child = children.at(ii);
- if (!child->isVisible() || !child->isEnabled() || QQuickItemPrivate::get(child)->culled)
- continue;
- if (deliverInitialMousePressEvent(child, event))
- return true;
- }
-
- if (itemPrivate->acceptedMouseButtons() & event->button()) {
- QPointF localPos = item->mapFromScene(event->windowPos());
- if (item->contains(localPos)) {
- QScopedPointer<QMouseEvent> me(cloneMouseEvent(event, &localPos));
- me->accept();
- item->grabMouse();
- q->sendEvent(item, me.data());
- event->setAccepted(me->isAccepted());
- if (me->isAccepted())
- return true;
- if (mouseGrabberItem)
- mouseGrabberItem->ungrabMouse();
+ auto point = pointerEvent->point(0);
+ lastMousePosition = point->scenePos();
+ QQuickItem *grabber = point->grabber();
+ if (grabber) {
+ // if the update consists of changing button state, then don't accept it
+ // unless the button is one in which the item is interested
+ if (pointerEvent->button() != Qt::NoButton
+ && grabber->acceptedMouseButtons()
+ && !(grabber->acceptedMouseButtons() & pointerEvent->button())) {
+ pointerEvent->setAccepted(false);
+ return;
}
- }
-
- return false;
-}
-
-bool QQuickWindowPrivate::deliverMouseEvent(QMouseEvent *event)
-{
- Q_Q(QQuickWindow);
-
- lastMousePosition = event->windowPos();
-
- if (!mouseGrabberItem &&
- event->type() == QEvent::MouseButtonPress &&
- (event->buttons() & event->button()) == event->buttons()) {
- if (deliverInitialMousePressEvent(contentItem, event))
- event->accept();
- else
- event->ignore();
- return event->isAccepted();
- }
- if (mouseGrabberItem) {
- if (event->button() != Qt::NoButton
- && mouseGrabberItem->acceptedMouseButtons()
- && !(mouseGrabberItem->acceptedMouseButtons() & event->button())) {
- event->ignore();
- return false;
- }
- QPointF localPos = mouseGrabberItem->mapFromScene(event->windowPos());
- QScopedPointer<QMouseEvent> me(cloneMouseEvent(event, &localPos));
+ // send update
+ QPointF localPos = grabber->mapFromScene(lastMousePosition);
+ auto me = pointerEvent->asMouseEvent(localPos);
me->accept();
- q->sendEvent(mouseGrabberItem, me.data());
- event->setAccepted(me->isAccepted());
- if (me->isAccepted())
- return true;
- }
-
- return false;
-}
+ q->sendEvent(grabber, me);
+ point->setAccepted(me->isAccepted());
-/*! \reimp */
-void QQuickWindow::mousePressEvent(QMouseEvent *event)
-{
- Q_D(QQuickWindow);
- Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMousePress, event->button(),
- event->buttons());
-
- if (event->source() == Qt::MouseEventSynthesizedBySystem) {
- event->accept();
- return;
- }
-
- qCDebug(DBG_MOUSE) << "QQuickWindow::mousePressEvent()" << event->localPos() << event->button() << event->buttons();
- d->deliverMouseEvent(event);
-}
-
-/*! \reimp */
-void QQuickWindow::mouseReleaseEvent(QMouseEvent *event)
-{
- Q_D(QQuickWindow);
- Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseRelease, event->button(),
- event->buttons());
-
- if (event->source() == Qt::MouseEventSynthesizedBySystem) {
- event->accept();
- return;
- }
-
- qCDebug(DBG_MOUSE) << "QQuickWindow::mouseReleaseEvent()" << event->localPos() << event->button() << event->buttons();
-
- if (!d->mouseGrabberItem) {
- QWindow::mouseReleaseEvent(event);
- return;
- }
-
- d->deliverMouseEvent(event);
- if (d->mouseGrabberItem && !event->buttons())
- d->mouseGrabberItem->ungrabMouse();
-}
-
-/*! \reimp */
-void QQuickWindow::mouseDoubleClickEvent(QMouseEvent *event)
-{
- Q_D(QQuickWindow);
- Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseDoubleClick,
- event->button(), event->buttons());
-
- if (event->source() == Qt::MouseEventSynthesizedBySystem) {
- event->accept();
- return;
- }
-
- qCDebug(DBG_MOUSE) << "QQuickWindow::mouseDoubleClickEvent()" << event->localPos() << event->button() << event->buttons();
+ // release event, make sure to ungrab if there still is a grabber
+ if (me->type() == QEvent::MouseButtonRelease && !me->buttons() && q->mouseGrabberItem())
+ q->mouseGrabberItem()->ungrabMouse();
+ } else {
+ // send initial press
+ bool delivered = false;
+ if (pointerEvent->isPressEvent()) {
+ QSet<QQuickItem*> hasFiltered;
+ delivered = deliverPressEvent(pointerEvent, &hasFiltered);
+ }
- if (!d->mouseGrabberItem && (event->buttons() & event->button()) == event->buttons()) {
- if (d->deliverInitialMousePressEvent(d->contentItem, event))
- event->accept();
- else
- event->ignore();
- return;
+ if (!delivered)
+ // make sure not to accept unhandled events
+ pointerEvent->setAccepted(false);
}
-
- d->deliverMouseEvent(event);
}
bool QQuickWindowPrivate::sendHoverEvent(QEvent::Type type, QQuickItem *item,
@@ -1730,7 +1681,6 @@ bool QQuickWindowPrivate::sendHoverEvent(QEvent::Type type, QQuickItem *item,
Qt::KeyboardModifiers modifiers, ulong timestamp,
bool accepted)
{
- Q_Q(QQuickWindow);
const QTransform transform = QQuickItemPrivate::get(item)->windowToItemTransform();
//create copy of event
@@ -1743,48 +1693,11 @@ bool QQuickWindowPrivate::sendHoverEvent(QEvent::Type type, QQuickItem *item,
return true;
}
- q->sendEvent(item, &hoverEvent);
+ QCoreApplication::sendEvent(item, &hoverEvent);
return hoverEvent.isAccepted();
}
-/*! \reimp */
-void QQuickWindow::mouseMoveEvent(QMouseEvent *event)
-{
- Q_D(QQuickWindow);
- Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseMove,
- event->localPos().x(), event->localPos().y());
-
- if (event->source() == Qt::MouseEventSynthesizedBySystem) {
- event->accept();
- return;
- }
-
- qCDebug(DBG_MOUSE) << "QQuickWindow::mouseMoveEvent()" << event->localPos() << event->button() << event->buttons();
-
-#ifndef QT_NO_CURSOR
- d->updateCursor(event->windowPos());
-#endif
-
- if (!d->mouseGrabberItem) {
- if (d->lastMousePosition.isNull())
- d->lastMousePosition = event->windowPos();
- QPointF last = d->lastMousePosition;
- d->lastMousePosition = event->windowPos();
-
- bool accepted = event->isAccepted();
- bool delivered = d->deliverHoverEvent(d->contentItem, event->windowPos(), last, event->modifiers(), event->timestamp(), accepted);
- if (!delivered) {
- //take care of any exits
- accepted = d->clearHover(event->timestamp());
- }
- event->setAccepted(accepted);
- return;
- }
-
- d->deliverMouseEvent(event);
-}
-
bool QQuickWindowPrivate::deliverHoverEvent(QQuickItem *item, const QPointF &scenePos, const QPointF &lastScenePos,
Qt::KeyboardModifiers modifiers, ulong timestamp, bool &accepted)
{
@@ -1797,19 +1710,22 @@ bool QQuickWindowPrivate::deliverHoverEvent(QQuickItem *item, const QPointF &sce
return false;
}
- QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
- for (int ii = children.count() - 1; ii >= 0; --ii) {
- QQuickItem *child = children.at(ii);
- if (!child->isVisible() || !child->isEnabled() || QQuickItemPrivate::get(child)->culled)
- continue;
- if (deliverHoverEvent(child, scenePos, lastScenePos, modifiers, timestamp, accepted))
- return true;
+ qCDebug(DBG_HOVER_TRACE) << q << item << scenePos << lastScenePos << "subtreeHoverEnabled" << itemPrivate->subtreeHoverEnabled;
+ if (itemPrivate->subtreeHoverEnabled) {
+ QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
+ for (int ii = children.count() - 1; ii >= 0; --ii) {
+ QQuickItem *child = children.at(ii);
+ if (!child->isVisible() || !child->isEnabled() || QQuickItemPrivate::get(child)->culled)
+ continue;
+ if (deliverHoverEvent(child, scenePos, lastScenePos, modifiers, timestamp, accepted))
+ return true;
+ }
}
if (itemPrivate->hoverEnabled) {
QPointF p = item->mapFromScene(scenePos);
if (item->contains(p)) {
- if (!hoverItems.isEmpty() && hoverItems[0] == item) {
+ if (!hoverItems.isEmpty() && hoverItems.at(0) == item) {
//move
accepted = sendHoverEvent(QEvent::HoverMove, item, scenePos, lastScenePos, modifiers, timestamp, accepted);
} else {
@@ -1820,24 +1736,24 @@ bool QQuickWindowPrivate::deliverHoverEvent(QQuickItem *item, const QPointF &sce
itemsToHover << parent;
// Leaving from previous hovered items until we reach the item or one of its ancestors.
- while (!hoverItems.isEmpty() && !itemsToHover.contains(hoverItems[0])) {
+ while (!hoverItems.isEmpty() && !itemsToHover.contains(hoverItems.at(0))) {
QQuickItem *hoverLeaveItem = hoverItems.takeFirst();
sendHoverEvent(QEvent::HoverLeave, hoverLeaveItem, scenePos, lastScenePos, modifiers, timestamp, accepted);
}
- if (!hoverItems.isEmpty() && hoverItems[0] == item){//Not entering a new Item
+ if (!hoverItems.isEmpty() && hoverItems.at(0) == item) {//Not entering a new Item
// ### Shouldn't we send moves for the parent items as well?
accepted = sendHoverEvent(QEvent::HoverMove, item, scenePos, lastScenePos, modifiers, timestamp, accepted);
} else {
// Enter items that are not entered yet.
int startIdx = -1;
if (!hoverItems.isEmpty())
- startIdx = itemsToHover.indexOf(hoverItems[0]) - 1;
+ startIdx = itemsToHover.indexOf(hoverItems.at(0)) - 1;
if (startIdx == -1)
startIdx = itemsToHover.count() - 1;
for (int i = startIdx; i >= 0; i--) {
- QQuickItem *itemToHover = itemsToHover[i];
+ QQuickItem *itemToHover = itemsToHover.at(i);
QQuickItemPrivate *itemToHoverPrivate = QQuickItemPrivate::get(itemToHover);
// The item may be about to be deleted or reparented to another window
// due to another hover event delivered in this function. If that is the
@@ -1861,7 +1777,6 @@ bool QQuickWindowPrivate::deliverHoverEvent(QQuickItem *item, const QPointF &sce
#ifndef QT_NO_WHEELEVENT
bool QQuickWindowPrivate::deliverWheelEvent(QQuickItem *item, QWheelEvent *event)
{
- Q_Q(QQuickWindow);
QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
@@ -1886,7 +1801,7 @@ bool QQuickWindowPrivate::deliverWheelEvent(QQuickItem *item, QWheelEvent *event
event->orientation(), event->buttons(), event->modifiers(), event->phase(), event->source(), event->inverted());
wheel.setTimestamp(event->timestamp());
wheel.accept();
- q->sendEvent(item, &wheel);
+ QCoreApplication::sendEvent(item, &wheel);
if (wheel.isAccepted()) {
event->accept();
return true;
@@ -1953,20 +1868,22 @@ bool QQuickWindowPrivate::deliverTouchCancelEvent(QTouchEvent *event)
{
qCDebug(DBG_TOUCH) << event;
Q_Q(QQuickWindow);
+
// A TouchCancel event will typically not contain any points.
// Deliver it to all items that have active touches.
- QSet<QQuickItem *> cancelDelivered;
- foreach (QQuickItem *item, itemForTouchPointId) {
- if (cancelDelivered.contains(item))
- continue;
- cancelDelivered.insert(item);
- q->sendEvent(item, event);
+ QQuickPointerEvent *pointerEvent = QQuickPointerDevice::touchDevice(event->device())->pointerEvent();
+ QVector<QQuickItem *> grabbers = pointerEvent->grabbers();
+
+ for (QQuickItem *grabber: qAsConst(grabbers)) {
+ q->sendEvent(grabber, event);
}
touchMouseId = -1;
- if (mouseGrabberItem)
- mouseGrabberItem->ungrabMouse();
+ touchMouseDevice = nullptr;
+ if (q->mouseGrabberItem())
+ q->mouseGrabberItem()->ungrabMouse();
+
// The next touch event can only be a TouchBegin so clean up.
- itemForTouchPointId.clear();
+ pointerEvent->clearGrabbers();
return true;
}
@@ -1976,88 +1893,185 @@ void QQuickWindowPrivate::deliverDelayedTouchEvent()
// Set delayedTouch to 0 before delivery to avoid redelivery in case of
// event loop recursions (e.g if it the touch starts a dnd session).
QScopedPointer<QTouchEvent> e(delayedTouch.take());
- reallyDeliverTouchEvent(e.data());
+ deliverPointerEvent(pointerEventInstance(e.data()));
}
static bool qquickwindow_no_touch_compression = qEnvironmentVariableIsSet("QML_NO_TOUCH_COMPRESSION");
-// check what kind of touch we have (begin/update) and
-// call deliverTouchPoints to actually dispatch the points
-void QQuickWindowPrivate::deliverTouchEvent(QTouchEvent *event)
+bool QQuickWindowPrivate::compressTouchEvent(QTouchEvent *event)
{
- qCDebug(DBG_TOUCH) << event;
Q_Q(QQuickWindow);
+ Qt::TouchPointStates states = event->touchPointStates();
+ if (((states & (Qt::TouchPointMoved | Qt::TouchPointStationary)) == 0)
+ || ((states & (Qt::TouchPointPressed | Qt::TouchPointReleased)) != 0)) {
+ // we can only compress something that isn't a press or release
+ return false;
+ }
- if (qquickwindow_no_touch_compression || touchRecursionGuard) {
- reallyDeliverTouchEvent(event);
- return;
+ if (!delayedTouch) {
+ delayedTouch.reset(new QTouchEvent(event->type(), event->device(), event->modifiers(), event->touchPointStates(), event->touchPoints()));
+ delayedTouch->setTimestamp(event->timestamp());
+ if (renderControl)
+ QQuickRenderControlPrivate::get(renderControl)->maybeUpdate();
+ else if (windowManager)
+ windowManager->maybeUpdate(q);
+ return true;
}
- Qt::TouchPointStates states = event->touchPointStates();
- if (((states & (Qt::TouchPointMoved | Qt::TouchPointStationary)) != 0)
- && ((states & (Qt::TouchPointPressed | Qt::TouchPointReleased)) == 0)) {
- // we can only compress something that isn't a press or release
- if (!delayedTouch) {
- delayedTouch.reset(new QTouchEvent(event->type(), event->device(), event->modifiers(), event->touchPointStates(), event->touchPoints()));
+ // check if this looks like the last touch event
+ if (delayedTouch->type() == event->type() &&
+ delayedTouch->device() == event->device() &&
+ delayedTouch->modifiers() == event->modifiers() &&
+ delayedTouch->touchPoints().count() == event->touchPoints().count())
+ {
+ // possible match.. is it really the same?
+ bool mismatch = false;
+
+ QList<QTouchEvent::TouchPoint> tpts = event->touchPoints();
+ Qt::TouchPointStates states;
+ for (int i = 0; i < event->touchPoints().count(); ++i) {
+ const QTouchEvent::TouchPoint &tp = tpts.at(i);
+ const QTouchEvent::TouchPoint &tpDelayed = delayedTouch->touchPoints().at(i);
+ if (tp.id() != tpDelayed.id()) {
+ mismatch = true;
+ break;
+ }
+
+ if (tpDelayed.state() == Qt::TouchPointMoved && tp.state() == Qt::TouchPointStationary)
+ tpts[i].setState(Qt::TouchPointMoved);
+ tpts[i].setLastPos(tpDelayed.lastPos());
+ tpts[i].setLastScenePos(tpDelayed.lastScenePos());
+ tpts[i].setLastScreenPos(tpDelayed.lastScreenPos());
+ tpts[i].setLastNormalizedPos(tpDelayed.lastNormalizedPos());
+
+ states |= tpts.at(i).state();
+ }
+
+ // matching touch event? then merge the new event into the old one
+ if (!mismatch) {
+ delayedTouch->setTouchPoints(tpts);
delayedTouch->setTimestamp(event->timestamp());
- if (renderControl)
- QQuickRenderControlPrivate::get(renderControl)->maybeUpdate();
- else if (windowManager)
- windowManager->maybeUpdate(q);
- return;
- } else {
- // check if this looks like the last touch event
- if (delayedTouch->type() == event->type() &&
- delayedTouch->device() == event->device() &&
- delayedTouch->modifiers() == event->modifiers() &&
- delayedTouch->touchPoints().count() == event->touchPoints().count())
- {
- // possible match.. is it really the same?
- bool mismatch = false;
-
- QList<QTouchEvent::TouchPoint> tpts = event->touchPoints();
- Qt::TouchPointStates states;
- for (int i = 0; i < event->touchPoints().count(); ++i) {
- const QTouchEvent::TouchPoint &tp = tpts.at(i);
- const QTouchEvent::TouchPoint &tpDelayed = delayedTouch->touchPoints().at(i);
- if (tp.id() != tpDelayed.id()) {
- mismatch = true;
- break;
- }
+ return true;
+ }
+ }
- if (tpDelayed.state() == Qt::TouchPointMoved && tp.state() == Qt::TouchPointStationary)
- tpts[i].setState(Qt::TouchPointMoved);
- tpts[i].setLastPos(tpDelayed.lastPos());
- tpts[i].setLastScenePos(tpDelayed.lastScenePos());
- tpts[i].setLastScreenPos(tpDelayed.lastScreenPos());
- tpts[i].setLastNormalizedPos(tpDelayed.lastNormalizedPos());
+ // merging wasn't possible, so deliver the delayed event first, and then delay this one
+ deliverDelayedTouchEvent();
+ delayedTouch.reset(new QTouchEvent(event->type(), event->device(), event->modifiers(), event->touchPointStates(), event->touchPoints()));
+ delayedTouch->setTimestamp(event->timestamp());
+ return true;
+}
- states |= tpts.at(i).state();
- }
+// entry point for touch event delivery:
+// - translate the event to window coordinates
+// - compress the event instead of delivering it if applicable
+// - call deliverTouchPoints to actually dispatch the points
+void QQuickWindowPrivate::handleTouchEvent(QTouchEvent *event)
+{
+ translateTouchEvent(event);
+ if (event->touchPoints().size())
+ lastMousePosition = event->touchPoints().at(0).pos();
- // matching touch event? then merge the new event into the old one
- if (!mismatch) {
- delayedTouch->setTouchPoints(tpts);
- delayedTouch->setTimestamp(event->timestamp());
- return;
- }
- }
+ qCDebug(DBG_TOUCH) << event;
+
+ if (qquickwindow_no_touch_compression || pointerEventRecursionGuard) {
+ deliverPointerEvent(pointerEventInstance(event));
+ return;
+ }
- // merging wasn't possible, so deliver the delayed event first, and then delay this one
+ if (!compressTouchEvent(event)) {
+ if (delayedTouch)
deliverDelayedTouchEvent();
- delayedTouch.reset(new QTouchEvent(event->type(), event->device(), event->modifiers(), event->touchPointStates(), event->touchPoints()));
- delayedTouch->setTimestamp(event->timestamp());
+ deliverPointerEvent(pointerEventInstance(event));
+ }
+}
+
+/*! \reimp */
+void QQuickWindow::mousePressEvent(QMouseEvent *event)
+{
+ Q_D(QQuickWindow);
+ d->handleMouseEvent(event);
+}
+/*! \reimp */
+void QQuickWindow::mouseMoveEvent(QMouseEvent *event)
+{
+ Q_D(QQuickWindow);
+ d->handleMouseEvent(event);
+}
+/*! \reimp */
+void QQuickWindow::mouseDoubleClickEvent(QMouseEvent *event)
+{
+ Q_D(QQuickWindow);
+ d->handleMouseEvent(event);
+}
+/*! \reimp */
+void QQuickWindow::mouseReleaseEvent(QMouseEvent *event)
+{
+ Q_D(QQuickWindow);
+ d->handleMouseEvent(event);
+}
+
+void QQuickWindowPrivate::handleMouseEvent(QMouseEvent *event)
+{
+ Q_Q(QQuickWindow);
+
+ if (event->source() == Qt::MouseEventSynthesizedBySystem) {
+ event->accept();
+ return;
+ }
+ qCDebug(DBG_MOUSE) << "QQuickWindow::handleMouseEvent()" << event->type() << event->localPos() << event->button() << event->buttons();
+
+ switch (event->type()) {
+ case QEvent::MouseButtonPress:
+ Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMousePress, event->button(),
+ event->buttons());
+ deliverPointerEvent(pointerEventInstance(event));
+ break;
+ case QEvent::MouseButtonRelease:
+ Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseRelease, event->button(),
+ event->buttons());
+ deliverPointerEvent(pointerEventInstance(event));
+ break;
+ case QEvent::MouseButtonDblClick:
+ Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseDoubleClick,
+ event->button(), event->buttons());
+ deliverPointerEvent(pointerEventInstance(event));
+ break;
+ case QEvent::MouseMove:
+ Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseMove,
+ event->localPos().x(), event->localPos().y());
+
+ qCDebug(DBG_HOVER_TRACE) << this;
+
+ #ifndef QT_NO_CURSOR
+ updateCursor(event->windowPos());
+ #endif
+
+ if (!q->mouseGrabberItem()) {
+ QPointF last = lastMousePosition.isNull() ? event->windowPos() : lastMousePosition;
+ lastMousePosition = event->windowPos();
+
+ bool accepted = event->isAccepted();
+ bool delivered = deliverHoverEvent(contentItem, event->windowPos(), last, event->modifiers(), event->timestamp(), accepted);
+ if (!delivered) {
+ //take care of any exits
+ accepted = clearHover(event->timestamp());
+ }
+ event->setAccepted(accepted);
return;
}
- } else {
- if (delayedTouch)
- deliverDelayedTouchEvent();
- reallyDeliverTouchEvent(event);
+ deliverPointerEvent(pointerEventInstance(event));
+ break;
+ default:
+ Q_ASSERT(false);
+ break;
}
}
-void QQuickWindowPrivate::flushDelayedTouchEvent()
+void QQuickWindowPrivate::flushFrameSynchronousEvents()
{
+ Q_Q(QQuickWindow);
+
if (delayedTouch) {
deliverDelayedTouchEvent();
@@ -2067,169 +2081,232 @@ void QQuickWindowPrivate::flushDelayedTouchEvent()
if (ut && ut->hasStartAnimationPending())
ut->startAnimations();
}
+
+ // Once per frame, send a synthetic hover, in case items have changed position.
+ // For instance, during animation (including the case of a ListView
+ // whose delegates contain MouseAreas), a MouseArea needs to know
+ // whether it has moved into a position where it is now under the cursor.
+ if (!q->mouseGrabberItem() && !lastMousePosition.isNull()) {
+ bool accepted = false;
+ bool delivered = deliverHoverEvent(contentItem, lastMousePosition, lastMousePosition, QGuiApplication::keyboardModifiers(), 0, accepted);
+ if (!delivered)
+ clearHover(); // take care of any exits
+ }
}
-void QQuickWindowPrivate::reallyDeliverTouchEvent(QTouchEvent *event)
-{
- qCDebug(DBG_TOUCH) << " - delivering" << event;
+/*!
+ \internal
+ Returns a QQuickPointerEvent instance suitable for wrapping and delivering \a event.
- // If users spin the eventloop as a result of touch delivery, we disable
- // touch compression and send events directly. This is because we consider
- // the usecase a bit evil, but we at least don't want to lose events.
- ++touchRecursionGuard;
-
- // List of all items that received an event before
- // When we have TouchBegin this is and will stay empty
- QHash<QQuickItem *, QList<QTouchEvent::TouchPoint> > updatedPoints;
-
- // Figure out who accepted a touch point last and put it in updatedPoints
- // Add additional item to newPoints
- const QList<QTouchEvent::TouchPoint> &touchPoints = event->touchPoints();
- QList<QTouchEvent::TouchPoint> newPoints;
- for (int i=0; i<touchPoints.count(); i++) {
- const QTouchEvent::TouchPoint &touchPoint = touchPoints.at(i);
- if (touchPoint.state() == Qt::TouchPointPressed) {
- newPoints << touchPoint;
- } else {
- // TouchPointStationary is relevant only to items which
- // are also receiving touch points with some other state.
- // But we have not yet decided which points go to which item,
- // so for now we must include all non-new points in updatedPoints.
- if (itemForTouchPointId.contains(touchPoint.id())) {
- QQuickItem *item = itemForTouchPointId.value(touchPoint.id());
- if (item)
- updatedPoints[item].append(touchPoint);
- }
- }
+ There is a unique instance per QQuickPointerDevice, which is determined
+ from \a event's device.
+*/
+QQuickPointerEvent *QQuickWindowPrivate::pointerEventInstance(QEvent *event)
+{
+ QQuickPointerDevice *dev = nullptr;
+ switch (event->type()) {
+ case QEvent::MouseButtonPress:
+ case QEvent::MouseButtonRelease:
+ case QEvent::MouseButtonDblClick:
+ case QEvent::MouseMove:
+ dev = QQuickPointerDevice::genericMouseDevice();
+ break;
+ case QEvent::TouchBegin:
+ case QEvent::TouchUpdate:
+ case QEvent::TouchEnd:
+ case QEvent::TouchCancel:
+ dev = QQuickPointerDevice::touchDevice(static_cast<QTouchEvent *>(event)->device());
+ break;
+ // TODO tablet event types
+ default:
+ break;
}
+ Q_ASSERT(dev);
+ return dev->pointerEvent()->reset(event);
+}
- // Deliver the event, but only if there is at least one new point
- // or some item accepted a point and should receive an update
- if (newPoints.count() > 0 || updatedPoints.count() > 0) {
- QSet<int> acceptedNewPoints;
- QSet<QQuickItem *> hasFiltered;
- event->setAccepted(deliverTouchPoints(contentItem, event, newPoints, &acceptedNewPoints, &updatedPoints, &hasFiltered));
- } else
- event->ignore();
+void QQuickWindowPrivate::deliverPointerEvent(QQuickPointerEvent *event)
+{
+ // If users spin the eventloop as a result of event delivery, we disable
+ // event compression and send events directly. This is because we consider
+ // the usecase a bit evil, but we at least don't want to lose events.
+ ++pointerEventRecursionGuard;
- // Remove released points from itemForTouchPointId
- if (event->touchPointStates() & Qt::TouchPointReleased) {
- for (int i=0; i<touchPoints.count(); i++) {
- if (touchPoints[i].state() == Qt::TouchPointReleased) {
- qCDebug(DBG_TOUCH_TARGET) << "TP" << touchPoints[i].id() << "released";
- itemForTouchPointId.remove(touchPoints[i].id());
- if (touchPoints[i].id() == touchMouseId)
- touchMouseId = -1;
- touchMouseIdCandidates.remove(touchPoints[i].id());
- }
- }
+ if (event->asPointerMouseEvent()) {
+ deliverMouseEvent(event->asPointerMouseEvent());
+ } else if (event->asPointerTouchEvent()) {
+ deliverTouchEvent(event->asPointerTouchEvent());
+ } else {
+ Q_ASSERT(false);
}
- if (event->type() == QEvent::TouchEnd) {
- if (!itemForTouchPointId.isEmpty()) {
- qWarning() << "No release received for" << itemForTouchPointId.size()
- << "touch points over" << itemForTouchPointId.begin().value()
- << "on touch end.";
- itemForTouchPointId.clear();
- }
- }
+ event->reset(nullptr);
- --touchRecursionGuard;
+ --pointerEventRecursionGuard;
}
-// This function recurses and sends the events to the individual items
-bool QQuickWindowPrivate::deliverTouchPoints(QQuickItem *item, QTouchEvent *event, const QList<QTouchEvent::TouchPoint> &newPoints,
- QSet<int> *acceptedNewPoints, QHash<QQuickItem *,
- QList<QTouchEvent::TouchPoint> > *updatedPoints, QSet<QQuickItem *> *hasFiltered)
+// check if item or any of its child items contain the point
+// FIXME: should this be iterative instead of recursive?
+QVector<QQuickItem *> QQuickWindowPrivate::pointerTargets(QQuickItem *item, const QPointF &scenePos, bool checkMouseButtons) const
{
- QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
-
+ QVector<QQuickItem *> targets;
+ auto itemPrivate = QQuickItemPrivate::get(item);
+ QPointF itemPos = item->mapFromScene(scenePos);
+ // if the item clips, we can potentially return early
if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
- for (int i=0; i<newPoints.count(); i++) {
- QPointF p = item->mapFromScene(newPoints[i].scenePos());
- if (!item->contains(p))
- return false;
- }
+ if (!item->contains(itemPos))
+ return targets;
}
- // Check if our children want the event (or parts of it)
- // This is the only point where touch event delivery recurses!
+ // recurse for children
QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
for (int ii = children.count() - 1; ii >= 0; --ii) {
QQuickItem *child = children.at(ii);
- if (!child->isEnabled() || !child->isVisible() || QQuickItemPrivate::get(child)->culled)
+ auto childPrivate = QQuickItemPrivate::get(child);
+ if (!child->isVisible() || !child->isEnabled() || childPrivate->culled)
continue;
- if (deliverTouchPoints(child, event, newPoints, acceptedNewPoints, updatedPoints, hasFiltered))
- return true;
+ targets << pointerTargets(child, scenePos, false);
}
- // None of the children accepted the event, so check the given item itself.
- // First, construct matchingPoints as a list of TouchPoints which the
- // given item might be interested in. Any newly-pressed point which is
- // inside the item's bounds will be interesting, and also any updated point
- // which was already accepted by that item when it was first pressed.
- // (A point which was already accepted is effectively "grabbed" by the item.)
-
- // set of IDs of "interesting" new points
- QSet<int> matchingNewPoints;
- // set of points which this item has previously accepted, for starters
- QList<QTouchEvent::TouchPoint> matchingPoints = (*updatedPoints)[item];
- // now add the new points which are inside this item's bounds
- if (newPoints.count() > 0 && acceptedNewPoints->count() < newPoints.count()) {
- for (int i = 0; i < newPoints.count(); i++) {
- if (acceptedNewPoints->contains(newPoints[i].id()))
- continue;
- QPointF p = item->mapFromScene(newPoints[i].scenePos());
- if (item->contains(p)) {
- matchingNewPoints.insert(newPoints[i].id());
- matchingPoints << newPoints[i];
- }
+ if (item->contains(itemPos) && (!checkMouseButtons || itemPrivate->acceptedMouseButtons())) {
+ // add this item last - children take precedence
+ targets << item;
+ }
+ return targets;
+}
+
+// return the joined lists
+// list1 has priority, common items come last
+QVector<QQuickItem *> QQuickWindowPrivate::mergePointerTargets(const QVector<QQuickItem *> &list1, const QVector<QQuickItem *> &list2) const
+{
+ QVector<QQuickItem *> targets = list1;
+ // start at the end of list2
+ // if item not in list, append it
+ // if item found, move to next one, inserting before the last found one
+ int insertPosition = targets.length();
+ for (int i = list2.length() - 1; i >= 0; --i) {
+ int newInsertPosition = targets.lastIndexOf(list2.at(i), insertPosition);
+ if (newInsertPosition >= 0) {
+ Q_ASSERT(newInsertPosition <= insertPosition);
+ insertPosition = newInsertPosition;
}
+ // check for duplicates, only insert if the item isn't there already
+ if (insertPosition == targets.size() || list2.at(i) != targets.at(insertPosition))
+ targets.insert(insertPosition, list2.at(i));
}
- // If there are no matching new points, and the existing points are all stationary,
- // there's no need to send an event to this item. This is required by a test in
- // tst_qquickwindow::touchEvent_basic:
- // a single stationary press on an item shouldn't cause an event
- if (matchingNewPoints.isEmpty()) {
- bool stationaryOnly = true;
-
- foreach (const QTouchEvent::TouchPoint &tp, matchingPoints) {
- if (tp.state() != Qt::TouchPointStationary) {
- stationaryOnly = false;
- break;
+ return targets;
+}
+
+void QQuickWindowPrivate::deliverTouchEvent(QQuickPointerTouchEvent *event)
+{
+ qCDebug(DBG_TOUCH) << " - delivering" << event->asTouchEvent();
+
+ QSet<QQuickItem *> hasFiltered;
+ if (event->isPressEvent())
+ deliverPressEvent(event, &hasFiltered);
+ if (!event->allPointsAccepted())
+ deliverUpdatedTouchPoints(event, &hasFiltered);
+
+ // Remove released points from itemForTouchPointId
+ bool allReleased = true;
+ int pointCount = event->pointCount();
+ for (int i = 0; i < pointCount; ++i) {
+ QQuickEventPoint *point = event->point(i);
+ if (point->state() == QQuickEventPoint::Released) {
+ int id = point->pointId();
+ qCDebug(DBG_TOUCH_TARGET) << "TP" << id << "released";
+ point->setGrabber(nullptr);
+ if (id == touchMouseId) {
+ touchMouseId = -1;
+ touchMouseDevice = nullptr;
}
+ } else {
+ allReleased = false;
}
+ }
- if (stationaryOnly)
- matchingPoints.clear();
+ if (allReleased && !event->grabbers().isEmpty()) {
+ qWarning() << "No release received for some grabbers" << event->grabbers();
+ event->clearGrabbers();
}
+}
- if (!matchingPoints.isEmpty()) {
- // Now we know this item might be interested in the event. Copy and send it, but
- // with only the subset of TouchPoints which are relevant to that item: that's matchingPoints.
- QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
- transformTouchPoints(matchingPoints, itemPrivate->windowToItemTransform());
- deliverMatchingPointsToItem(item, event, acceptedNewPoints, matchingNewPoints, matchingPoints, hasFiltered);
+// Deliver touch points to existing grabbers
+bool QQuickWindowPrivate::deliverUpdatedTouchPoints(QQuickPointerTouchEvent *event, QSet<QQuickItem *> *hasFiltered)
+{
+ const auto grabbers = event->grabbers();
+ for (auto grabber : grabbers)
+ deliverMatchingPointsToItem(grabber, event, hasFiltered);
+
+ return false;
+}
+
+// Deliver newly pressed touch points
+bool QQuickWindowPrivate::deliverPressEvent(QQuickPointerEvent *event, QSet<QQuickItem *> *hasFiltered)
+{
+ const QVector<QPointF> points = event->unacceptedPressedPointScenePositions();
+ QVector<QQuickItem *> targetItems;
+ for (QPointF point: points) {
+ QVector<QQuickItem *> targetItemsForPoint = pointerTargets(contentItem, point, false);
+ if (targetItems.count()) {
+ targetItems = mergePointerTargets(targetItems, targetItemsForPoint);
+ } else {
+ targetItems = targetItemsForPoint;
+ }
}
- // record the fact that this item has been visited already
- updatedPoints->remove(item);
+ for (QQuickItem *item: targetItems) {
+ deliverMatchingPointsToItem(item, event, hasFiltered);
+ if (event->allPointsAccepted())
+ break;
+ }
- // recursion is done only if ALL touch points have been delivered
- return (acceptedNewPoints->count() == newPoints.count() && updatedPoints->isEmpty());
+ return event->allPointsAccepted();
}
-// touchEventForItemBounds has no means to generate a touch event that contains
-// only the points that are relevant for this item. Thus the need for
-// matchingPoints to already be that set of interesting points.
-// They are all pre-transformed, too.
-bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QTouchEvent *event, QSet<int> *acceptedNewPoints, const QSet<int> &matchingNewPoints, const QList<QTouchEvent::TouchPoint> &matchingPoints, QSet<QQuickItem *> *hasFiltered)
+bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPointerEvent *pointerEvent, QSet<QQuickItem *> *hasFiltered)
{
- QScopedPointer<QTouchEvent> touchEvent(touchEventWithPoints(*event, matchingPoints));
- touchEvent.data()->setTarget(item);
- bool touchEventAccepted = false;
+ Q_Q(QQuickWindow);
+
+ // TODO: unite this mouse point delivery with the synthetic mouse event below
+ if (auto event = pointerEvent->asPointerMouseEvent()) {
+ if (item->acceptedMouseButtons() & event->button()) {
+ auto point = event->point(0);
+ if (point->isAccepted())
+ return false;
+
+ // The only reason to already have a mouse grabber here is
+ // synthetic events - flickable sends one when setPressDelay is used.
+ auto oldMouseGrabber = q->mouseGrabberItem();
+ QPointF localPos = item->mapFromScene(point->scenePos());
+ Q_ASSERT(item->contains(localPos)); // transform is checked already
+ QMouseEvent *me = event->asMouseEvent(localPos);
+ me->accept();
+ q->sendEvent(item, me);
+ if (me->isAccepted()) {
+ auto mouseGrabber = q->mouseGrabberItem();
+ if (mouseGrabber && mouseGrabber != item && mouseGrabber != oldMouseGrabber) {
+ item->mouseUngrabEvent();
+ } else {
+ item->grabMouse();
+ }
+ point->setAccepted(true);
+ }
+ return me->isAccepted();
+ }
+ return false;
+ }
+
+ QQuickPointerTouchEvent *event = pointerEvent->asPointerTouchEvent();
+ if (!event)
+ return false;
+
+ QScopedPointer<QTouchEvent> touchEvent(event->touchEventForItem(item));
+ if (!touchEvent)
+ return false;
qCDebug(DBG_TOUCH) << " - considering delivering " << touchEvent.data() << " to " << item;
+ bool eventAccepted = false;
// First check whether the parent wants to be a filter,
// and if the parent accepts the event we are done.
@@ -2237,109 +2314,53 @@ bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QTouchEv
// If the touch was accepted (regardless by whom or in what form),
// update acceptedNewPoints
qCDebug(DBG_TOUCH) << " - can't. intercepted " << touchEvent.data() << " to " << item->parentItem() << " instead of " << item;
- foreach (int id, matchingNewPoints)
- acceptedNewPoints->insert(id);
+ for (auto point: qAsConst(touchEvent->touchPoints())) {
+ event->pointById(point.id())->setAccepted();
+ }
return true;
}
- // Since it can change in sendEvent, update itemForTouchPointId now
- foreach (int id, matchingNewPoints) {
- qCDebug(DBG_TOUCH_TARGET) << "TP" << id << "->" << item;
- itemForTouchPointId[id] = item;
- }
-
// Deliver the touch event to the given item
qCDebug(DBG_TOUCH) << " - actually delivering " << touchEvent.data() << " to " << item;
QCoreApplication::sendEvent(item, touchEvent.data());
- touchEventAccepted = touchEvent->isAccepted();
+ eventAccepted = touchEvent->isAccepted();
// If the touch event wasn't accepted, synthesize a mouse event and see if the item wants it.
QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
- if (!touchEventAccepted && (itemPrivate->acceptedMouseButtons() & Qt::LeftButton)) {
+ if (!eventAccepted && (itemPrivate->acceptedMouseButtons() & Qt::LeftButton)) {
// send mouse event
- event->setAccepted(translateTouchToMouse(item, touchEvent.data()));
- if (event->isAccepted()) {
- touchEventAccepted = true;
- }
+ if (deliverTouchAsMouse(item, event))
+ eventAccepted = true;
}
- if (touchEventAccepted) {
+ if (eventAccepted) {
// If the touch was accepted (regardless by whom or in what form),
- // update acceptedNewPoints.
- foreach (int id, matchingNewPoints)
- acceptedNewPoints->insert(id);
+ // update accepted new points.
+ for (auto point: qAsConst(touchEvent->touchPoints())) {
+ auto pointerEventPoint = event->pointById(point.id());
+ pointerEventPoint->setAccepted();
+ if (point.state() == Qt::TouchPointPressed)
+ pointerEventPoint->setGrabber(item);
+ }
} else {
// But if the event was not accepted then we know this item
// will not be interested in further updates for those touchpoint IDs either.
- foreach (int id, matchingNewPoints)
- if (itemForTouchPointId[id] == item) {
- qCDebug(DBG_TOUCH_TARGET) << "TP" << id << "disassociated";
- itemForTouchPointId.remove(id);
- }
- }
-
- return touchEventAccepted;
-}
-
-QTouchEvent *QQuickWindowPrivate::touchEventForItemBounds(QQuickItem *target, const QTouchEvent &originalEvent)
-{
- const QList<QTouchEvent::TouchPoint> &touchPoints = originalEvent.touchPoints();
- QList<QTouchEvent::TouchPoint> pointsInBounds;
- // if all points are stationary, the list of points should be empty to signal a no-op
- if (originalEvent.touchPointStates() != Qt::TouchPointStationary) {
- for (int i = 0; i < touchPoints.count(); ++i) {
- const QTouchEvent::TouchPoint &tp = touchPoints.at(i);
- if (tp.state() == Qt::TouchPointPressed) {
- QPointF p = target->mapFromScene(tp.scenePos());
- if (target->contains(p))
- pointsInBounds.append(tp);
- } else {
- pointsInBounds.append(tp);
+ for (auto point: qAsConst(touchEvent->touchPoints())) {
+ if (point.state() == Qt::TouchPointPressed) {
+ if (event->pointById(point.id())->grabber() == item) {
+ qCDebug(DBG_TOUCH_TARGET) << "TP" << point.id() << "disassociated";
+ event->pointById(point.id())->setGrabber(nullptr);
+ }
}
}
- transformTouchPoints(pointsInBounds, QQuickItemPrivate::get(target)->windowToItemTransform());
}
- QTouchEvent* touchEvent = touchEventWithPoints(originalEvent, pointsInBounds);
- touchEvent->setTarget(target);
- return touchEvent;
-}
-
-QTouchEvent *QQuickWindowPrivate::touchEventWithPoints(const QTouchEvent &event, const QList<QTouchEvent::TouchPoint> &newPoints)
-{
- Qt::TouchPointStates eventStates;
- for (int i=0; i<newPoints.count(); i++)
- eventStates |= newPoints[i].state();
- // if all points have the same state, set the event type accordingly
- QEvent::Type eventType = event.type();
- switch (eventStates) {
- case Qt::TouchPointPressed:
- eventType = QEvent::TouchBegin;
- break;
- case Qt::TouchPointReleased:
- eventType = QEvent::TouchEnd;
- break;
- default:
- eventType = QEvent::TouchUpdate;
- break;
- }
-
- QTouchEvent *touchEvent = new QTouchEvent(eventType);
- touchEvent->setWindow(event.window());
- touchEvent->setTarget(event.target());
- touchEvent->setDevice(event.device());
- touchEvent->setModifiers(event.modifiers());
- touchEvent->setTouchPoints(newPoints);
- touchEvent->setTouchPointStates(eventStates);
- touchEvent->setTimestamp(event.timestamp());
- touchEvent->accept();
- return touchEvent;
+ return eventAccepted;
}
#ifndef QT_NO_DRAGANDDROP
void QQuickWindowPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QEvent *event)
{
- Q_Q(QQuickWindow);
grabber->resetTarget();
QQuickDragGrabber::iterator grabItem = grabber->begin();
if (grabItem != grabber->end()) {
@@ -2355,7 +2376,7 @@ void QQuickWindowPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QEvent *e
e->mouseButtons(),
e->keyboardModifiers());
QQuickDropEventEx::copyActions(&translatedEvent, *e);
- q->sendEvent(**grabItem, &translatedEvent);
+ QCoreApplication::sendEvent(**grabItem, &translatedEvent);
e->setAccepted(translatedEvent.isAccepted());
e->setDropAction(translatedEvent.dropAction());
grabber->setTarget(**grabItem);
@@ -2364,7 +2385,7 @@ void QQuickWindowPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QEvent *e
if (event->type() != QEvent::DragMove) { // Either an accepted drop or a leave.
QDragLeaveEvent leaveEvent;
for (; grabItem != grabber->end(); grabItem = grabber->release(grabItem))
- q->sendEvent(**grabItem, &leaveEvent);
+ QCoreApplication::sendEvent(**grabItem, &leaveEvent);
return;
} else for (; grabItem != grabber->end(); grabItem = grabber->release(grabItem)) {
QDragMoveEvent *moveEvent = static_cast<QDragMoveEvent *>(event);
@@ -2380,18 +2401,18 @@ void QQuickWindowPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QEvent *e
moveEvent->mouseButtons(),
moveEvent->keyboardModifiers());
QQuickDropEventEx::copyActions(&translatedEvent, *moveEvent);
- q->sendEvent(**grabItem, &translatedEvent);
+ QCoreApplication::sendEvent(**grabItem, &translatedEvent);
++grabItem;
} else {
QDragLeaveEvent leaveEvent;
- q->sendEvent(**grabItem, &leaveEvent);
+ QCoreApplication::sendEvent(**grabItem, &leaveEvent);
grabItem = grabber->release(grabItem);
}
}
return;
} else {
QDragLeaveEvent leaveEvent;
- q->sendEvent(**grabItem, &leaveEvent);
+ QCoreApplication::sendEvent(**grabItem, &leaveEvent);
}
}
}
@@ -2410,7 +2431,6 @@ void QQuickWindowPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QEvent *e
bool QQuickWindowPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QQuickItem *item, QDragMoveEvent *event)
{
- Q_Q(QQuickWindow);
bool accepted = false;
QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
if (!item->isVisible() || !item->isEnabled() || QQuickItemPrivate::get(item)->culled)
@@ -2445,7 +2465,7 @@ bool QQuickWindowPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QQuickIte
event->keyboardModifiers(),
event->type());
QQuickDropEventEx::copyActions(&translatedEvent, *event);
- q->sendEvent(item, &translatedEvent);
+ QCoreApplication::sendEvent(item, &translatedEvent);
if (event->type() == QEvent::DragEnter) {
if (translatedEvent.isAccepted()) {
grabber->grab(item);
@@ -2488,7 +2508,7 @@ QQuickItem *QQuickWindowPrivate::findCursorItem(QQuickItem *item, const QPointF
return 0;
}
- if (itemPrivate->hasCursorInChild) {
+ if (itemPrivate->subtreeCursorEnabled) {
QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
for (int ii = children.count() - 1; ii >= 0; --ii) {
QQuickItem *child = children.at(ii);
@@ -2508,8 +2528,10 @@ QQuickItem *QQuickWindowPrivate::findCursorItem(QQuickItem *item, const QPointF
}
#endif
-bool QQuickWindowPrivate::sendFilteredTouchEvent(QQuickItem *target, QQuickItem *item, QTouchEvent *event, QSet<QQuickItem *> *hasFiltered)
+bool QQuickWindowPrivate::sendFilteredTouchEvent(QQuickItem *target, QQuickItem *item, QQuickPointerTouchEvent *event, QSet<QQuickItem *> *hasFiltered)
{
+ Q_Q(QQuickWindow);
+
if (!target)
return false;
@@ -2518,8 +2540,8 @@ bool QQuickWindowPrivate::sendFilteredTouchEvent(QQuickItem *target, QQuickItem
QQuickItemPrivate *targetPrivate = QQuickItemPrivate::get(target);
if (targetPrivate->filtersChildMouseEvents && !hasFiltered->contains(target)) {
hasFiltered->insert(target);
- QScopedPointer<QTouchEvent> targetEvent(touchEventForItemBounds(target, *event));
- if (!targetEvent->touchPoints().isEmpty()) {
+ QScopedPointer<QTouchEvent> targetEvent(event->touchEventForItem(target, true));
+ if (targetEvent) {
if (target->childMouseEventFilter(item, targetEvent.data())) {
qCDebug(DBG_TOUCH) << " - first chance intercepted on childMouseEventFilter by " << target;
QVector<int> touchIds;
@@ -2528,9 +2550,10 @@ bool QQuickWindowPrivate::sendFilteredTouchEvent(QQuickItem *target, QQuickItem
for (int i = 0; i < touchPointCount; ++i)
touchIds.append(targetEvent->touchPoints().at(i).id());
target->grabTouchPoints(touchIds);
- if (mouseGrabberItem) {
- mouseGrabberItem->ungrabMouse();
+ if (q->mouseGrabberItem()) {
+ q->mouseGrabberItem()->ungrabMouse();
touchMouseId = -1;
+ touchMouseDevice = nullptr;
}
filtered = true;
}
@@ -2542,12 +2565,6 @@ bool QQuickWindowPrivate::sendFilteredTouchEvent(QQuickItem *target, QQuickItem
switch (tp.state()) {
case Qt::TouchPointPressed:
t = QEvent::MouseButtonPress;
- if (touchMouseId == -1) {
- // We don't want to later filter touches as a mouse event if they were pressed
- // while a touchMouseId was already active.
- // Remember this touch as a potential to become the touchMouseId.
- touchMouseIdCandidates.insert(tp.id());
- }
break;
case Qt::TouchPointReleased:
t = QEvent::MouseButtonRelease;
@@ -2560,18 +2577,20 @@ bool QQuickWindowPrivate::sendFilteredTouchEvent(QQuickItem *target, QQuickItem
}
// Only deliver mouse event if it is the touchMouseId or it could become the touchMouseId
- if ((touchMouseIdCandidates.contains(tp.id()) && touchMouseId == -1) || touchMouseId == tp.id()) {
+ if (touchMouseId == -1 || touchMouseId == tp.id()) {
// targetEvent is already transformed wrt local position, velocity, etc.
- QScopedPointer<QMouseEvent> mouseEvent(touchToMouseEvent(t, tp, event, item, false));
+
+ // FIXME: remove asTouchEvent!!!
+ QScopedPointer<QMouseEvent> mouseEvent(touchToMouseEvent(t, tp, event->asTouchEvent(), item, false));
if (target->childMouseEventFilter(item, mouseEvent.data())) {
qCDebug(DBG_TOUCH) << " - second chance intercepted on childMouseEventFilter by " << target;
if (t != QEvent::MouseButtonRelease) {
qCDebug(DBG_TOUCH_TARGET) << "TP" << tp.id() << "->" << target;
- itemForTouchPointId[tp.id()] = target;
touchMouseId = tp.id();
+ touchMouseDevice = event->device();
+ touchMouseDevice->pointerEvent()->pointById(tp.id())->setGrabber(target);
target->grabMouse();
}
- touchMouseIdCandidates.clear();
filtered = true;
}
// Only one event can be filtered as a mouse event.
@@ -2598,6 +2617,7 @@ bool QQuickWindowPrivate::sendFilteredMouseEvent(QQuickItem *target, QQuickItem
hasFiltered->insert(target);
if (target->childMouseEventFilter(item, event))
filtered = true;
+ qCDebug(DBG_MOUSE_TARGET) << target << "childMouseEventFilter ->" << filtered;
}
return sendFilteredMouseEvent(target->parentItem(), item, event, hasFiltered) || filtered;
@@ -2618,6 +2638,15 @@ bool QQuickWindowPrivate::dragOverThreshold(qreal d, Qt::Axis axis, QMouseEvent
return overThreshold;
}
+bool QQuickWindowPrivate::dragOverThreshold(qreal d, Qt::Axis axis, const QTouchEvent::TouchPoint *tp, int startDragThreshold)
+{
+ QStyleHints *styleHints = qApp->styleHints();
+ bool overThreshold = qAbs(d) > (startDragThreshold >= 0 ? startDragThreshold : styleHints->startDragDistance());
+ qreal velocity = axis == Qt::XAxis ? tp->velocity().x() : tp->velocity().y();
+ overThreshold |= qAbs(velocity) > styleHints->startDragVelocity();
+ return overThreshold;
+}
+
/*!
\qmlproperty list<Object> Window::data
\default
@@ -2715,8 +2744,13 @@ void QQuickWindowPrivate::contextCreationFailureMessage(const QSurfaceFormat &fo
/*!
Propagates an event \a e to a QQuickItem \a item on the window.
+ Use \l QCoreApplication::sendEvent() directly instead.
+
The return value is currently not used.
+
+ \deprecated
*/
+// ### Qt6: remove
bool QQuickWindow::sendEvent(QQuickItem *item, QEvent *e)
{
Q_D(QQuickWindow);
@@ -2738,9 +2772,6 @@ bool QQuickWindow::sendEvent(QQuickItem *item, QEvent *e)
QCoreApplication::sendEvent(item, e);
}
break;
- case QEvent::ShortcutOverride:
- QCoreApplication::sendEvent(item, e);
- break;
case QEvent::MouseButtonPress:
case QEvent::MouseButtonRelease:
case QEvent::MouseButtonDblClick:
@@ -2754,41 +2785,8 @@ bool QQuickWindow::sendEvent(QQuickItem *item, QEvent *e)
}
}
break;
- case QEvent::UngrabMouse: {
- QSet<QQuickItem *> hasFiltered;
- if (!d->sendFilteredMouseEvent(item->parentItem(), item, e, &hasFiltered)) {
- e->accept();
- item->mouseUngrabEvent();
- }
- }
- break;
-#ifndef QT_NO_WHEELEVENT
- case QEvent::Wheel:
-#endif
-#ifndef QT_NO_DRAGANDDROP
- case QEvent::DragEnter:
- case QEvent::DragMove:
- case QEvent::DragLeave:
- case QEvent::Drop:
-#endif
- case QEvent::FocusIn:
- case QEvent::FocusOut:
- case QEvent::HoverEnter:
- case QEvent::HoverLeave:
- case QEvent::HoverMove:
- case QEvent::TouchCancel:
- QCoreApplication::sendEvent(item, e);
- break;
- case QEvent::TouchBegin:
- case QEvent::TouchUpdate:
- case QEvent::TouchEnd: {
- QSet<QQuickItem*> hasFiltered;
- QTouchEvent *ev = static_cast<QTouchEvent *>(e);
- qCDebug(DBG_TOUCH) << " - sendEvent for " << ev << " to " << item->parentItem() << " and " << item;
- d->sendFilteredTouchEvent(item->parentItem(), item, ev, &hasFiltered);
- }
- break;
default:
+ QCoreApplication::sendEvent(item, e);
break;
}
@@ -3149,7 +3147,7 @@ void QQuickWindowPrivate::updateDirtyNode(QQuickItem *item)
<< itemPriv->paintNode;
nodes.removeAll(0);
- Q_ASSERT(nodes.first() == itemPriv->itemNodeInstance);
+ Q_ASSERT(nodes.constFirst() == itemPriv->itemNodeInstance);
for (int i=1; i<nodes.size(); ++i) {
QSGNode *n = nodes.at(i);
// Failing this means we messed up reparenting
@@ -3184,10 +3182,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;
@@ -3210,17 +3208,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;
}
/*!
@@ -3235,7 +3246,9 @@ bool QQuickWindow::isSceneGraphInitialized() const
/*!
\fn void QQuickWindow::frameSwapped()
- This signal is emitted when the frame buffers have been swapped.
+ This signal is emitted when a frame has been queued for presenting. With
+ vertical synchronization enabled the signal is emitted at most once per
+ vsync interval in a continuously animating scene.
This signal will be emitted from the scene graph rendering thread.
*/
@@ -3256,14 +3269,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.
*/
@@ -3274,7 +3287,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.
@@ -3336,13 +3349,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.
@@ -3365,7 +3382,7 @@ void QQuickWindow::setRenderTarget(QOpenGLFramebufferObject *fbo)
d->renderTargetSize = QSize();
}
}
-
+#endif
/*!
\overload
@@ -3375,6 +3392,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.
@@ -3416,19 +3437,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.
@@ -3445,33 +3470,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)
@@ -3513,7 +3547,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.
@@ -3528,7 +3562,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
@@ -3545,7 +3579,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
@@ -3566,15 +3600,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()
@@ -3586,16 +3621,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.
@@ -3607,16 +3642,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.
@@ -3651,6 +3686,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
*/
@@ -3663,15 +3702,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
@@ -3682,7 +3721,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.
@@ -3723,7 +3762,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
@@ -3740,9 +3780,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.
@@ -3761,7 +3801,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;
@@ -3773,7 +3813,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.
@@ -3784,15 +3824,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);
@@ -3800,6 +3843,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;
}
@@ -3869,7 +3917,7 @@ void QQuickWindow::setDefaultAlphaBuffer(bool useAlpha)
{
QQuickWindowPrivate::defaultAlphaBuffer = useAlpha;
}
-
+#ifndef QT_NO_OPENGL
/*!
\since 5.2
@@ -3887,6 +3935,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()
@@ -3941,7 +3992,7 @@ void QQuickWindow::resetOpenGLState()
QOpenGLFramebufferObject::bindDefault();
}
-
+#endif
/*!
\qmlproperty string Window::title
@@ -4350,7 +4401,7 @@ void QQuickWindowPrivate::runAndClearJobs(QList<QRunnable *> *jobs)
jobs->clear();
renderJobMutex.unlock();
- foreach (QRunnable *r, jobList) {
+ for (QRunnable *r : qAsConst(jobList)) {
r->run();
delete r;
}
@@ -4380,6 +4431,130 @@ qreal QQuickWindow::effectiveDevicePixelRatio() const
return w ? w->devicePixelRatio() : devicePixelRatio();
}
+/*!
+ \return the current renderer interface. The value is always valid and is never null.
+
+ \note This function can be called at any time after constructing the
+ QQuickWindow, even while isSceneGraphInitialized() is still false. However,
+ some renderer interface functions, in particular
+ QSGRendererInterface::getResource() will not be functional until the
+ scenegraph is up and running. Backend queries, like
+ QSGRendererInterface::graphicsApi() or QSGRendererInterface::shaderType(),
+ will always be functional on the other hand.
+
+ \note The ownership of the returned pointer stays with Qt. The returned
+ instance may or may not be shared between different QQuickWindow instances,
+ depending on the scenegraph backend in use. Therefore applications are
+ expected to query the interface object for each QQuickWindow instead of
+ reusing the already queried pointer.
+
+ \sa QSGRenderNode, QSGRendererInterface
+
+ \since 5.8
+ */
+QSGRendererInterface *QQuickWindow::rendererInterface() const
+{
+ Q_D(const QQuickWindow);
+
+ // no context validity check - it is essential to be able to return a
+ // renderer interface instance before scenegraphInitialized() is emitted
+ // (depending on the backend, that can happen way too late for some of the
+ // rif use cases, like examining the graphics api or shading language in
+ // use)
+
+ return d->context->sceneGraphContext()->rendererInterface(d->context);
+}
+
+/*!
+ Requests a Qt Quick scenegraph backend for the specified graphics \a api.
+ Backends can either be built-in or be installed in form of dynamically
+ loaded plugins.
+
+ \note The call to the function must happen before constructing the first
+ QQuickWindow in the application. It cannot be changed afterwards.
+
+ If \a backend is invalid or an error occurs, the default backend (OpenGL or
+ software, depending on the Qt configuration) is used.
+
+ \since 5.8
+ */
+void QQuickWindow::setSceneGraphBackend(QSGRendererInterface::GraphicsApi api)
+{
+ switch (api) {
+ case QSGRendererInterface::Software:
+ setSceneGraphBackend(QStringLiteral("software"));
+ break;
+ case QSGRendererInterface::Direct3D12:
+ setSceneGraphBackend(QStringLiteral("d3d12"));
+ break;
+ default:
+ break;
+ }
+}
+
+/*!
+ Requests the specified Qt Quick scenegraph \a backend. Backends can either
+ be built-in or be installed in form of dynamically loaded plugins.
+
+ \overload
+
+ \note The call to the function must happen before constructing the first
+ QQuickWindow in the application. It cannot be changed afterwards.
+
+ If \a backend is invalid or an error occurs, the default backend (OpenGL or
+ software, depending on the Qt configuration) is used.
+
+ \note Calling this function is equivalent to setting the
+ \c QT_QUICK_BACKEND or \c QMLSCENE_DEVICE environment variables. However, this
+ API is safer to use in applications that spawn other processes as there is
+ no need to worry about environment inheritance.
+
+ \since 5.8
+ */
+void QQuickWindow::setSceneGraphBackend(const QString &backend)
+{
+ QSGContext::setBackend(backend);
+}
+
+/*!
+ Creates a simple rectangle node. When the scenegraph is not initialized, the return value is null.
+
+ This is cross-backend alternative to constructing a QSGSimpleRectNode directly.
+
+ \since 5.8
+ \sa QSGRectangleNode
+ */
+QSGRectangleNode *QQuickWindow::createRectangleNode() const
+{
+ Q_D(const QQuickWindow);
+ return isSceneGraphInitialized() ? d->context->sceneGraphContext()->createRectangleNode() : nullptr;
+}
+
+/*!
+ Creates a simple image node. When the scenegraph is not initialized, the return value is null.
+
+ This is cross-backend alternative to constructing a QSGSimpleTextureNode directly.
+
+ \since 5.8
+ \sa QSGImageNode
+ */
+QSGImageNode *QQuickWindow::createImageNode() const
+{
+ Q_D(const QQuickWindow);
+ return isSceneGraphInitialized() ? d->context->sceneGraphContext()->createImageNode() : nullptr;
+}
+
+/*!
+ Creates a nine patch node. When the scenegraph is not initialized, the return value is null.
+
+ \since 5.8
+ */
+QSGNinePatchNode *QQuickWindow::createNinePatchNode() const
+{
+ Q_D(const QQuickWindow);
+ return isSceneGraphInitialized() ? d->context->sceneGraphContext()->createNinePatchNode() : nullptr;
+}
+
#include "moc_qquickwindow.cpp"
QT_END_NAMESPACE
diff --git a/src/quick/items/qquickwindow.h b/src/quick/items/qquickwindow.h
index 1024147bb4..cfadadec2d 100644
--- a/src/quick/items/qquickwindow.h
+++ b/src/quick/items/qquickwindow.h
@@ -41,6 +41,7 @@
#define QQUICKWINDOW_H
#include <QtQuick/qtquickglobal.h>
+#include <QtQuick/qsgrendererinterface.h>
#include <QtCore/qmetatype.h>
#include <QtGui/qopengl.h>
#include <QtGui/qwindow.h>
@@ -60,6 +61,9 @@ class QQmlIncubationController;
class QInputMethodEvent;
class QQuickCloseEvent;
class QQuickRenderControl;
+class QSGRectangleNode;
+class QSGImageNode;
+class QSGNinePatchNode;
class Q_QUICK_EXPORT QQuickWindow : public QWindow
{
@@ -110,16 +114,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 +157,15 @@ public:
qreal effectiveDevicePixelRatio() const;
+ QSGRendererInterface *rendererInterface() const;
+
+ static void setSceneGraphBackend(QSGRendererInterface::GraphicsApi api);
+ static void setSceneGraphBackend(const QString &backend);
+
+ QSGRectangleNode *createRectangleNode() const;
+ QSGImageNode *createImageNode() const;
+ QSGNinePatchNode *createNinePatchNode() const;
+
Q_SIGNALS:
void frameSwapped();
Q_REVISION(2) void openglContextCreated(QOpenGLContext *context);
diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h
index 7d2b1af29b..3328eb65c4 100644
--- a/src/quick/items/qquickwindow_p.h
+++ b/src/quick/items/qquickwindow_p.h
@@ -53,6 +53,7 @@
#include "qquickitem.h"
#include "qquickwindow.h"
+#include "qquickevents_p_p.h"
#include <QtQuick/private/qsgcontext_p.h>
@@ -68,13 +69,19 @@
QT_BEGIN_NAMESPACE
-//Make it easy to identify and customize the root item if needed
-
+class QOpenGLVertexArrayObjectHelper;
class QQuickAnimatorController;
-class QSGRenderLoop;
-class QQuickRenderControl;
class QQuickDragGrabber;
+class QQuickItemPrivate;
+class QQuickPointerDevice;
+class QQuickRenderControl;
+class QQuickWindowIncubationController;
+class QQuickWindowPrivate;
+class QQuickWindowRenderLoop;
+class QSGRenderLoop;
+class QTouchEvent;
+//Make it easy to identify and customize the root item if needed
class QQuickRootItem : public QQuickItem
{
Q_OBJECT
@@ -85,15 +92,6 @@ public Q_SLOTS:
void setHeight(int h) {QQuickItem::setHeight(qreal(h));}
};
-class QQuickItemPrivate;
-class QQuickWindowPrivate;
-
-class QTouchEvent;
-class QQuickWindowRenderLoop;
-class QQuickWindowIncubationController;
-
-class QOpenGLVertexArrayObjectHelper;
-
class Q_QUICK_PRIVATE_EXPORT QQuickCustomRenderStage
{
public:
@@ -127,7 +125,6 @@ public:
void deliverKeyEvent(QKeyEvent *e);
// Keeps track of the item currently receiving mouse events
- QQuickItem *mouseGrabberItem;
#ifndef QT_NO_CURSOR
QQuickItem *cursorItem;
#endif
@@ -135,18 +132,19 @@ public:
QQuickDragGrabber *dragGrabber;
#endif
int touchMouseId;
+ QQuickPointerDevice *touchMouseDevice;
bool checkIfDoubleClicked(ulong newPressEventTimestamp);
ulong touchMousePressTimestamp;
// Mouse positions are saved in widget coordinates
QPointF lastMousePosition;
- bool translateTouchToMouse(QQuickItem *item, QTouchEvent *event);
+ bool deliverTouchAsMouse(QQuickItem *item, QQuickPointerEvent *pointerEvent);
void translateTouchEvent(QTouchEvent *touchEvent);
void setMouseGrabber(QQuickItem *grabber);
- static void transformTouchPoints(QList<QTouchEvent::TouchPoint> &touchPoints, const QTransform &transform);
+ void grabTouchPoints(QQuickItem *grabber, const QVector<int> &ids);
+ void removeGrabber(QQuickItem *grabber, bool mouse = true, bool touch = true);
static QMouseEvent *cloneMouseEvent(QMouseEvent *event, QPointF *transformedLocalPos = 0);
- bool deliverInitialMousePressEvent(QQuickItem *, QMouseEvent *);
- bool deliverMouseEvent(QMouseEvent *);
+ void deliverMouseEvent(QQuickPointerMouseEvent *pointerEvent);
bool sendFilteredMouseEvent(QQuickItem *, QQuickItem *, QEvent *, QSet<QQuickItem *> *);
#ifndef QT_NO_WHEELEVENT
bool deliverWheelEvent(QQuickItem *, QWheelEvent *);
@@ -154,21 +152,33 @@ public:
#ifndef QT_NO_GESTURES
bool deliverNativeGestureEvent(QQuickItem *, QNativeGestureEvent *);
#endif
- bool deliverTouchPoints(QQuickItem *, QTouchEvent *, const QList<QTouchEvent::TouchPoint> &, QSet<int> *,
- QHash<QQuickItem *, QList<QTouchEvent::TouchPoint> > *, QSet<QQuickItem*> *filtered);
- void deliverTouchEvent(QTouchEvent *);
- void reallyDeliverTouchEvent(QTouchEvent *);
- bool deliverTouchCancelEvent(QTouchEvent *);
+
+ // entry point of events to the window
+ void handleTouchEvent(QTouchEvent *);
+ void handleMouseEvent(QMouseEvent *);
+ bool compressTouchEvent(QTouchEvent *);
+ void flushFrameSynchronousEvents();
void deliverDelayedTouchEvent();
- void flushDelayedTouchEvent();
+
+ // delivery of pointer events:
+ QQuickPointerEvent *pointerEventInstance(QEvent *ev);
+ void deliverPointerEvent(QQuickPointerEvent *);
+ void deliverTouchEvent(QQuickPointerTouchEvent *);
+ bool deliverTouchCancelEvent(QTouchEvent *);
+ bool deliverPressEvent(QQuickPointerEvent *, QSet<QQuickItem *> *);
+ bool deliverUpdatedTouchPoints(QQuickPointerTouchEvent *event, QSet<QQuickItem *> *hasFiltered);
+ bool deliverMatchingPointsToItem(QQuickItem *item, QQuickPointerEvent *pointerEvent, QSet<QQuickItem*> *filtered);
+ bool sendFilteredTouchEvent(QQuickItem *target, QQuickItem *item, QQuickPointerTouchEvent *event, QSet<QQuickItem*> *filtered);
+
+ QVector<QQuickItem *> pointerTargets(QQuickItem *, const QPointF &, bool checkMouseButtons) const;
+ QVector<QQuickItem *> mergePointerTargets(const QVector<QQuickItem *> &list1, const QVector<QQuickItem *> &list2) const;
+
+ // hover delivery
bool deliverHoverEvent(QQuickItem *, const QPointF &scenePos, const QPointF &lastScenePos, Qt::KeyboardModifiers modifiers, ulong timestamp, bool &accepted);
- bool deliverMatchingPointsToItem(QQuickItem *item, QTouchEvent *event, QSet<int> *acceptedNewPoints, const QSet<int> &matchingNewPoints, const QList<QTouchEvent::TouchPoint> &matchingPoints, QSet<QQuickItem*> *filtered);
- QTouchEvent *touchEventForItemBounds(QQuickItem *target, const QTouchEvent &originalEvent);
- QTouchEvent *touchEventWithPoints(const QTouchEvent &event, const QList<QTouchEvent::TouchPoint> &newPoints);
- bool sendFilteredTouchEvent(QQuickItem *target, QQuickItem *item, QTouchEvent *event, QSet<QQuickItem*> *filtered);
bool sendHoverEvent(QEvent::Type, QQuickItem *, const QPointF &scenePos, const QPointF &lastScenePos,
Qt::KeyboardModifiers modifiers, ulong timestamp, bool accepted);
bool clearHover(ulong timestamp = 0);
+
#ifndef QT_NO_DRAGANDDROP
void deliverDragEvent(QQuickDragGrabber *, QEvent *);
bool deliverDragEvent(QQuickDragGrabber *, QQuickItem *, QDragMoveEvent *);
@@ -233,7 +243,8 @@ public:
QQuickRenderControl *renderControl;
QQuickAnimatorController *animationController;
QScopedPointer<QTouchEvent> delayedTouch;
- int touchRecursionGuard;
+
+ int pointerEventRecursionGuard;
QQuickCustomRenderStage *customRenderStage;
QColor clearColor;
@@ -254,15 +265,12 @@ public:
QOpenGLVertexArrayObjectHelper *vaoHelper;
- // Keeps track of which touch point (int) was last accepted by which item
- QHash<int, QQuickItem *> itemForTouchPointId;
- QSet<int> touchMouseIdCandidates;
-
mutable QQuickWindowIncubationController *incubationController;
static bool defaultAlphaBuffer;
static bool dragOverThreshold(qreal d, Qt::Axis axis, QMouseEvent *event, int startDragThreshold = -1);
+ static bool dragOverThreshold(qreal d, Qt::Axis axis, const QTouchEvent::TouchPoint *tp, int startDragThreshold = -1);
// data property
static void data_append(QQmlListProperty<QObject> *, QObject *);
@@ -288,22 +296,6 @@ private:
static void cleanupNodesOnShutdown(QQuickItem *);
};
-class Q_QUICK_PRIVATE_EXPORT QQuickCloseEvent : public QObject
-{
- Q_OBJECT
- Q_PROPERTY(bool accepted READ isAccepted WRITE setAccepted)
-
-public:
- QQuickCloseEvent()
- : _accepted(true) {}
-
- bool isAccepted() { return _accepted; }
- void setAccepted(bool accepted) { _accepted = accepted; }
-
-private:
- bool _accepted;
-};
-
class QQuickWindowQObjectCleanupJob : public QRunnable
{
public:
@@ -321,6 +313,4 @@ Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickWindowPrivate::FocusOptions)
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickCloseEvent)
-
#endif // QQUICKWINDOW_P_H
diff --git a/src/quick/qtquick2.cpp b/src/quick/qtquick2.cpp
index 41e30a1110..9a50250ebb 100644
--- a/src/quick/qtquick2.cpp
+++ b/src/quick/qtquick2.cpp
@@ -49,7 +49,6 @@
#include <private/qqmldebugstatesdelegate_p.h>
#include <private/qqmlbinding_p.h>
#include <private/qqmlcontext_p.h>
-#include <private/qquickprofiler_p.h>
#include <private/qquickapplication_p.h>
#include <QtQuick/private/qquickpropertychanges_p.h>
#include <QtQuick/private/qquickstate_p.h>
@@ -63,6 +62,12 @@ static void initResources()
QT_BEGIN_NAMESPACE
+#ifdef QT_NO_QML_DEBUGGER
+
+class QQmlQtQuick2DebugStatesDelegate : public QQmlDebugStatesDelegate {};
+
+#else
+
class QQmlQtQuick2DebugStatesDelegate : public QQmlDebugStatesDelegate
{
public:
@@ -128,7 +133,7 @@ void QQmlQtQuick2DebugStatesDelegate::updateBinding(QQmlContext *context,
typedef QPointer<QQuickState> QuickStatePointer;
QObject *object = property.object();
QString propertyName = property.name();
- foreach (const QuickStatePointer& statePointer, m_allStates) {
+ for (const QuickStatePointer& statePointer : qAsConst(m_allStates)) {
if (QQuickState *state = statePointer.data()) {
// here we assume that the revert list on itself defines the base state
if (state->isStateActive() && state->containsPropertyInRevertList(object, propertyName)) {
@@ -136,9 +141,10 @@ void QQmlQtQuick2DebugStatesDelegate::updateBinding(QQmlContext *context,
QQmlBinding *newBinding = 0;
if (!isLiteralValue) {
- newBinding = new QQmlBinding(expression.toString(), object,
- QQmlContextData::get(context), fileName,
- line, column);
+ newBinding = QQmlBinding::create(&QQmlPropertyPrivate::get(property)->core,
+ expression.toString(), object,
+ QQmlContextData::get(context), fileName,
+ line, column);
newBinding->setTarget(property);
}
@@ -174,6 +180,7 @@ void QQmlQtQuick2DebugStatesDelegate::resetBindingForInvalidProperty(QObject *ob
}
}
+#endif // QT_NO_QML_DEBUGGER
void QQmlQtQuick2Module::defineModule()
{
diff --git a/src/quick/qtquickglobal.h b/src/quick/qtquickglobal.h
index f6f8f42e7f..5e83c1db84 100644
--- a/src/quick/qtquickglobal.h
+++ b/src/quick/qtquickglobal.h
@@ -40,7 +40,9 @@
#ifndef QTQUICKGLOBAL_H
#define QTQUICKGLOBAL_H
-#include <QtCore/qglobal.h>
+#include <QtQml/qtqmlglobal.h>
+#include <QtGui/qtguiglobal.h>
+#include <QtQuick/qtquick-config.h>
QT_BEGIN_NAMESPACE
diff --git a/src/quick/qtquickglobal_p.h b/src/quick/qtquickglobal_p.h
index 3c313de0a1..f6376a6d17 100644
--- a/src/quick/qtquickglobal_p.h
+++ b/src/quick/qtquickglobal_p.h
@@ -40,6 +40,10 @@
#ifndef QTQUICKGLOBAL_P_H
#define QTQUICKGLOBAL_P_H
+#include <QtQml/private/qtqmlglobal_p.h>
+#include <QtGui/private/qtguiglobal_p.h>
+#include <QtQuick/private/qtquick-config_p.h>
+
#include <QtCore/qloggingcategory.h>
//
diff --git a/src/quick/quick.pro b/src/quick/quick.pro
index 1c14ff8d57..8f4f9a8290 100644
--- a/src/quick/quick.pro
+++ b/src/quick/quick.pro
@@ -1,12 +1,13 @@
TARGET = QtQuick
QT = core-private gui-private qml-private
-QT_PRIVATE = network
+qtConfig(qml-network): \
+ QT_PRIVATE += network
DEFINES += QT_NO_URL_CAST_FROM_STRING QT_NO_INTEGER_EVENT_COORDINATES
win32-msvc*:DEFINES *= _CRT_SECURE_NO_WARNINGS
solaris-cc*:QMAKE_CXXFLAGS_RELEASE -= -O2
-win32:!wince:!winrt: LIBS += -luser32
+win32:!winrt: LIBS += -luser32
exists("qqml_enable_gcov") {
QMAKE_CXXFLAGS = -fprofile-arcs -ftest-coverage -fno-elide-constructors
@@ -27,8 +28,9 @@ ANDROID_BUNDLED_FILES += \
include(util/util.pri)
include(scenegraph/scenegraph.pri)
include(items/items.pri)
-!wince:include(designer/designer.pri)
-contains(QT_CONFIG, accessibility) {
+qtConfig(quick-designer): \
+ include(designer/designer.pri)
+qtConfig(accessibility) {
include(accessible/accessible.pri)
}
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..2ff180ea99
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer.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 "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;
+
+ qDeleteAll(m_nodes);
+
+ 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());
+}
+
+QRegion 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();
+ }
+
+ QRegion updateRegion = m_dirtyRegion;
+
+ // Empty dirtyRegion
+ m_dirtyRegion = QRegion();
+ m_obscuredRegion = QRegion();
+
+ return updateRegion;
+}
+
+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
+ markDirty();
+}
+
+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);
+}
+
+void QSGAbstractSoftwareRenderer::markDirty()
+{
+ m_dirtyRegion = QRegion(m_background->rect().toRect());
+}
+
+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..905577b92a
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer_p.h
@@ -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$
+**
+****************************************************************************/
+
+#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;
+
+ void markDirty();
+
+protected:
+ QRegion renderNodes(QPainter *painter);
+ void buildRenderList();
+ QRegion 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..8ad9b50b09
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwareadaptation.cpp
@@ -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$
+**
+****************************************************************************/
+
+#include "qsgsoftwareadaptation_p.h"
+#include "qsgsoftwarecontext_p.h"
+#include "qsgsoftwarerenderloop_p.h"
+#include "qsgsoftwarethreadedrenderloop_p.h"
+
+#include <private/qguiapplication_p.h>
+#include <qpa/qplatformintegration.h>
+
+QT_BEGIN_NAMESPACE
+
+QSGSoftwareAdaptation::QSGSoftwareAdaptation(QObject *parent)
+ : QSGContextPlugin(parent)
+{
+}
+
+QStringList QSGSoftwareAdaptation::keys() const
+{
+ return QStringList() << QLatin1String("software") << QLatin1String("softwarecontext");
+}
+
+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()
+{
+ static bool threaded = false;
+ static bool envChecked = false;
+ if (!envChecked) {
+ envChecked = true;
+ threaded = qgetenv("QSG_RENDER_LOOP") == QByteArrayLiteral("threaded");
+ }
+
+ if (threaded)
+ return new QSGSoftwareThreadedRenderLoop;
+
+ return new QSGSoftwareRenderLoop();
+}
+
+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..05d5daa686
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext.cpp
@@ -0,0 +1,224 @@
+/****************************************************************************
+**
+** 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 "qsgsoftwareinternalrectanglenode_p.h"
+#include "qsgsoftwareinternalimagenode_p.h"
+#include "qsgsoftwarepainternode_p.h"
+#include "qsgsoftwarepixmaptexture_p.h"
+#include "qsgsoftwareglyphnode_p.h"
+#include "qsgsoftwarepublicnodes_p.h"
+#include "qsgsoftwarelayer_p.h"
+#include "qsgsoftwarerenderer_p.h"
+#if QT_CONFIG(quick_sprite)
+#include "qsgsoftwarespritenode_p.h"
+#endif
+
+#include <QtCore/QCoreApplication>
+#include <QtCore/QElapsedTimer>
+
+#include <QtGui/QWindow>
+#include <QtQuick/private/qquickwindow_p.h>
+
+// 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)
+ , m_activePainter(nullptr)
+{
+}
+
+QSGSoftwareContext::QSGSoftwareContext(QObject *parent)
+ : QSGContext(parent)
+{
+}
+
+QSGInternalRectangleNode *QSGSoftwareContext::createInternalRectangleNode()
+{
+ return new QSGSoftwareInternalRectangleNode();
+}
+
+QSGInternalImageNode *QSGSoftwareContext::createInternalImageNode()
+{
+ return new QSGSoftwareInternalImageNode();
+}
+
+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();
+}
+
+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()
+{
+ m_sg->renderContextInvalidated(this);
+ emit invalidated();
+}
+
+QSGTexture *QSGSoftwareRenderContext::createTexture(const QImage &image, uint flags) const
+{
+ return new QSGSoftwarePixmapTexture(image, flags);
+}
+
+QSGRenderer *QSGSoftwareRenderContext::createRenderer()
+{
+ return new QSGSoftwareRenderer(this);
+}
+
+
+void QSGSoftwareRenderContext::renderNextFrame(QSGRenderer *renderer, uint fbo)
+{
+ renderer->renderScene(fbo);
+}
+
+int QSGSoftwareRenderContext::maxTextureSize() const
+{
+ return 2048;
+}
+
+QSGRendererInterface *QSGSoftwareContext::rendererInterface(QSGRenderContext *renderContext)
+{
+ Q_UNUSED(renderContext);
+ return this;
+}
+
+QSGRectangleNode *QSGSoftwareContext::createRectangleNode()
+{
+ return new QSGSoftwareRectangleNode;
+}
+
+QSGImageNode *QSGSoftwareContext::createImageNode()
+{
+ return new QSGSoftwareImageNode;
+}
+
+QSGNinePatchNode *QSGSoftwareContext::createNinePatchNode()
+{
+ return new QSGSoftwareNinePatchNode;
+}
+
+#if QT_CONFIG(quick_sprite)
+QSGSpriteNode *QSGSoftwareContext::createSpriteNode()
+{
+ return new QSGSoftwareSpriteNode;
+}
+#endif
+
+QSGRendererInterface::GraphicsApi QSGSoftwareContext::graphicsApi() const
+{
+ return Software;
+}
+
+QSGRendererInterface::ShaderType QSGSoftwareContext::shaderType() const
+{
+ return UnknownShadingLanguage;
+}
+
+QSGRendererInterface::ShaderCompilationTypes QSGSoftwareContext::shaderCompilationType() const
+{
+ return 0;
+}
+
+QSGRendererInterface::ShaderSourceTypes QSGSoftwareContext::shaderSourceType() const
+{
+ return 0;
+}
+
+void *QSGSoftwareContext::getResource(QQuickWindow *window, Resource resource) const
+{
+ if (resource == Painter && window && window->isSceneGraphInitialized())
+ return static_cast<QSGSoftwareRenderContext *>(QQuickWindowPrivate::get(window)->context)->m_activePainter;
+
+ return nullptr;
+}
+
+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..1f14717416
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext_p.h
@@ -0,0 +1,114 @@
+/****************************************************************************
+**
+** 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;
+ int maxTextureSize() const override;
+
+ bool m_initialized;
+ QPainter *m_activePainter;
+};
+
+class QSGSoftwareContext : public QSGContext, public QSGRendererInterface
+{
+ Q_OBJECT
+public:
+ explicit QSGSoftwareContext(QObject *parent = nullptr);
+
+ QSGRenderContext *createRenderContext() override { return new QSGSoftwareRenderContext(this); }
+ QSGInternalRectangleNode *createInternalRectangleNode() override;
+ QSGInternalImageNode *createInternalImageNode() override;
+ QSGPainterNode *createPainterNode(QQuickPaintedItem *item) override;
+ QSGGlyphNode *createGlyphNode(QSGRenderContext *rc, bool preferNativeGlyphNode) override;
+ QSGLayer *createLayer(QSGRenderContext *renderContext) override;
+ QSurfaceFormat defaultSurfaceFormat() const override;
+ QSGRendererInterface *rendererInterface(QSGRenderContext *renderContext) override;
+ QSGRectangleNode *createRectangleNode() override;
+ QSGImageNode *createImageNode() override;
+ QSGNinePatchNode *createNinePatchNode() override;
+#if QT_CONFIG(quick_sprite)
+ QSGSpriteNode *createSpriteNode() override;
+#endif
+
+ GraphicsApi graphicsApi() const override;
+ ShaderType shaderType() const override;
+ ShaderCompilationTypes shaderCompilationType() const override;
+ ShaderSourceTypes shaderSourceType() const override;
+ void *getResource(QQuickWindow *window, Resource resource) 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/qsgsoftwareinternalimagenode.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalimagenode.cpp
new file mode 100644
index 0000000000..10291b9cb5
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalimagenode.cpp
@@ -0,0 +1,501 @@
+/****************************************************************************
+**
+** 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 "qsgsoftwareinternalimagenode_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
+
+QSGSoftwareInternalImageNode::QSGSoftwareInternalImageNode()
+ : 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 QSGSoftwareInternalImageNode::setTargetRect(const QRectF &rect)
+{
+ if (rect == m_targetRect)
+ return;
+ m_targetRect = rect;
+ markDirty(DirtyGeometry);
+}
+
+void QSGSoftwareInternalImageNode::setInnerTargetRect(const QRectF &rect)
+{
+ if (rect == m_innerTargetRect)
+ return;
+ m_innerTargetRect = rect;
+ markDirty(DirtyGeometry);
+}
+
+void QSGSoftwareInternalImageNode::setInnerSourceRect(const QRectF &rect)
+{
+ if (rect == m_innerSourceRect)
+ return;
+ m_innerSourceRect = rect;
+ markDirty(DirtyGeometry);
+}
+
+void QSGSoftwareInternalImageNode::setSubSourceRect(const QRectF &rect)
+{
+ if (rect == m_subSourceRect)
+ return;
+ m_subSourceRect = rect;
+ markDirty(DirtyGeometry);
+}
+
+void QSGSoftwareInternalImageNode::setTexture(QSGTexture *texture)
+{
+ m_texture = texture;
+ m_cachedMirroredPixmapIsDirty = true;
+ markDirty(DirtyMaterial);
+}
+
+void QSGSoftwareInternalImageNode::setMirror(bool mirror)
+{
+ if (m_mirror != mirror) {
+ m_mirror = mirror;
+ m_cachedMirroredPixmapIsDirty = true;
+ markDirty(DirtyMaterial);
+ }
+}
+
+void QSGSoftwareInternalImageNode::setMipmapFiltering(QSGTexture::Filtering /*filtering*/)
+{
+}
+
+void QSGSoftwareInternalImageNode::setFiltering(QSGTexture::Filtering filtering)
+{
+ bool smooth = (filtering == QSGTexture::Linear);
+ if (smooth == m_smooth)
+ return;
+
+ m_smooth = smooth;
+ markDirty(DirtyMaterial);
+}
+
+void QSGSoftwareInternalImageNode::setHorizontalWrapMode(QSGTexture::WrapMode wrapMode)
+{
+ bool tileHorizontal = (wrapMode == QSGTexture::Repeat);
+ if (tileHorizontal == m_tileHorizontal)
+ return;
+
+ m_tileHorizontal = tileHorizontal;
+ markDirty(DirtyMaterial);
+}
+
+void QSGSoftwareInternalImageNode::setVerticalWrapMode(QSGTexture::WrapMode wrapMode)
+{
+ bool tileVertical = (wrapMode == QSGTexture::Repeat);
+ if (tileVertical == m_tileVertical)
+ return;
+
+ m_tileVertical = (wrapMode == QSGTexture::Repeat);
+ markDirty(DirtyMaterial);
+}
+
+void QSGSoftwareInternalImageNode::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 QSGSoftwareInternalImageNode::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 QSGSoftwareInternalImageNode::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 QSGSoftwareInternalImageNode::rect() const
+{
+ return m_targetRect;
+}
+
+const QPixmap &QSGSoftwareInternalImageNode::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/qsgsoftwareinternalimagenode_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalimagenode_p.h
new file mode 100644
index 0000000000..f21667fdf7
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalimagenode_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 QSGSOFTWAREINTERNALIMAGENODE_H
+#define QSGSOFTWAREINTERNALIMAGENODE_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 QSGSoftwareInternalImageNode : public QSGInternalImageNode
+{
+public:
+ QSGSoftwareInternalImageNode();
+
+ 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 // QSGSOFTWAREINTERNALIMAGENODE_H
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode.cpp
new file mode 100644
index 0000000000..f6898b3879
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode.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 "qsgsoftwareinternalrectanglenode_p.h"
+#include <qmath.h>
+
+#include <QtGui/QPainter>
+
+QT_BEGIN_NAMESPACE
+
+QSGSoftwareInternalRectangleNode::QSGSoftwareInternalRectangleNode()
+ : 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 QSGSoftwareInternalRectangleNode::setRect(const QRectF &rect)
+{
+ QRect alignedRect = rect.toAlignedRect();
+ if (m_rect != alignedRect) {
+ m_rect = alignedRect;
+ markDirty(DirtyMaterial);
+ }
+}
+
+void QSGSoftwareInternalRectangleNode::setColor(const QColor &color)
+{
+ if (m_color != color) {
+ m_color = color;
+ m_cornerPixmapIsDirty = true;
+ markDirty(DirtyMaterial);
+ }
+}
+
+void QSGSoftwareInternalRectangleNode::setPenColor(const QColor &color)
+{
+ if (m_penColor != color) {
+ m_penColor = color;
+ m_cornerPixmapIsDirty = true;
+ markDirty(DirtyMaterial);
+ }
+}
+
+void QSGSoftwareInternalRectangleNode::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 QSGSoftwareInternalRectangleNode::setGradientStops(const QGradientStops &stops)
+{
+ //normalize stops
+ bool needsNormalization = false;
+ for (const QGradientStop &stop : qAsConst(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 QSGSoftwareInternalRectangleNode::setRadius(qreal radius)
+{
+ if (m_radius != radius) {
+ m_radius = radius;
+ m_cornerPixmapIsDirty = true;
+ markDirty(DirtyMaterial);
+ }
+}
+
+void QSGSoftwareInternalRectangleNode::setAligned(bool /*aligned*/)
+{
+}
+
+void QSGSoftwareInternalRectangleNode::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 QSGSoftwareInternalRectangleNode::paint(QPainter *painter)
+{
+ //We can only check for a device pixel ratio change when we know what
+ //paint device is being used.
+ if (painter->device()->devicePixelRatio() != m_devicePixelRatio) {
+ m_devicePixelRatio = painter->device()->devicePixelRatio();
+ 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 QSGSoftwareInternalRectangleNode::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) {
+ for (const QGradientStop &stop : qAsConst(m_stops)) {
+ if (stop.second.alpha() < 255)
+ return false;
+ }
+ }
+
+ return true;
+}
+
+QRectF QSGSoftwareInternalRectangleNode::rect() const
+{
+ //TODO: double check that this is correct.
+ return m_rect;
+}
+
+void QSGSoftwareInternalRectangleNode::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 QSGSoftwareInternalRectangleNode::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/qsgsoftwareinternalrectanglenode_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode_p.h
new file mode 100644
index 0000000000..f363e279e1
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode_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 QSGSOFTWAREINTERNALRECTANGLENODE_H
+#define QSGSOFTWAREINTERNALRECTANGLENODE_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 QSGSoftwareInternalRectangleNode : public QSGInternalRectangleNode
+{
+public:
+ QSGSoftwareInternalRectangleNode();
+
+ 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 // QSGSOFTWAREINTERNALRECTANGLENODE_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/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..f8c1a3d90b
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaprenderer.cpp
@@ -0,0 +1,116 @@
+/****************************************************************************
+**
+** 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 "qsgsoftwarecontext_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);
+ auto rc = static_cast<QSGSoftwareRenderContext *>(context());
+ QPainter *prevPainter = rc->m_activePainter;
+ rc->m_activePainter = &painter;
+
+ 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();
+
+ rc->m_activePainter = prevPainter;
+ 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..534a0a4ec6
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaptexture.cpp
@@ -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$
+**
+****************************************************************************/
+
+#include "qsgsoftwarepixmaptexture_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QSGSoftwarePixmapTexture::QSGSoftwarePixmapTexture(const QImage &image, uint flags)
+{
+ // Prevent pixmap format conversion to reduce memory consumption
+ // and surprises in calling code. (See QTBUG-47328)
+ if (flags & QSGRenderContext::CreateTexture_Alpha) {
+ //If texture should have an alpha
+ m_pixmap = QPixmap::fromImage(image, Qt::NoFormatConversion);
+ } else {
+ //Force opaque texture
+ m_pixmap = QPixmap::fromImage(image.convertToFormat(QImage::Format_RGB32), 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..034fa25da9
--- /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, uint flags);
+ 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/qsgsoftwarepublicnodes.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes.cpp
new file mode 100644
index 0000000000..1fa5234377
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes.cpp
@@ -0,0 +1,196 @@
+/****************************************************************************
+**
+** 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 "qsgsoftwarepublicnodes_p.h"
+#include "qsgsoftwarepixmaptexture_p.h"
+#include "qsgsoftwareinternalimagenode_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QSGSoftwareRectangleNode::QSGSoftwareRectangleNode()
+ : m_color(QColor(255, 255, 255))
+{
+ setMaterial((QSGMaterial*)1);
+ setGeometry((QSGGeometry*)1);
+}
+
+void QSGSoftwareRectangleNode::paint(QPainter *painter)
+{
+ painter->fillRect(m_rect, m_color);
+}
+
+QSGSoftwareImageNode::QSGSoftwareImageNode()
+ : m_texture(nullptr),
+ m_owns(false),
+ m_filtering(QSGTexture::None),
+ m_transformMode(NoTransform),
+ m_cachedMirroredPixmapIsDirty(false)
+{
+ setMaterial((QSGMaterial*)1);
+ setGeometry((QSGGeometry*)1);
+}
+
+QSGSoftwareImageNode::~QSGSoftwareImageNode()
+{
+ if (m_owns)
+ delete m_texture;
+}
+
+void QSGSoftwareImageNode::setTexture(QSGTexture *texture)
+{
+ m_texture = texture; markDirty(DirtyMaterial);
+ m_cachedMirroredPixmapIsDirty = true;
+}
+
+void QSGSoftwareImageNode::setTextureCoordinatesTransform(QSGImageNode::TextureCoordinatesTransformMode transformNode)
+{
+ if (m_transformMode == transformNode)
+ return;
+
+ m_transformMode = transformNode;
+ m_cachedMirroredPixmapIsDirty = true;
+
+ markDirty(DirtyGeometry);
+}
+
+void QSGSoftwareImageNode::paint(QPainter *painter)
+{
+ if (m_cachedMirroredPixmapIsDirty)
+ updateCachedMirroredPixmap();
+
+ painter->setRenderHint(QPainter::SmoothPixmapTransform, (m_filtering == QSGTexture::Linear));
+
+ if (!m_cachedPixmap.isNull()) {
+ painter->drawPixmap(m_rect, m_cachedPixmap, m_sourceRect);
+ } else if (QSGSoftwarePixmapTexture *pt = dynamic_cast<QSGSoftwarePixmapTexture *>(m_texture)) {
+ const QPixmap &pm = pt->pixmap();
+ painter->drawPixmap(m_rect, pm, m_sourceRect);
+ } else if (QSGPlainTexture *pt = dynamic_cast<QSGPlainTexture *>(m_texture)) {
+ const QImage &im = pt->image();
+ painter->drawImage(m_rect, im, m_sourceRect);
+ }
+}
+
+void QSGSoftwareImageNode::updateCachedMirroredPixmap()
+{
+ if (m_transformMode == NoTransform) {
+ m_cachedPixmap = QPixmap();
+ } else {
+
+ if (QSGSoftwarePixmapTexture *pt = dynamic_cast<QSGSoftwarePixmapTexture *>(m_texture)) {
+ QTransform mirrorTransform;
+ if (m_transformMode.testFlag(MirrorVertically))
+ mirrorTransform = mirrorTransform.scale(1, -1);
+ if (m_transformMode.testFlag(MirrorHorizontally))
+ mirrorTransform = mirrorTransform.scale(-1, 1);
+ m_cachedPixmap = pt->pixmap().transformed(mirrorTransform);
+ } else if (QSGPlainTexture *pt = dynamic_cast<QSGPlainTexture *>(m_texture)) {
+ m_cachedPixmap = QPixmap::fromImage(pt->image().mirrored(m_transformMode.testFlag(MirrorHorizontally), m_transformMode.testFlag(MirrorVertically)));
+ } else {
+ m_cachedPixmap = QPixmap();
+ }
+ }
+
+ m_cachedMirroredPixmapIsDirty = false;
+}
+
+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/qsgsoftwarepublicnodes_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes_p.h
new file mode 100644
index 0000000000..9f1913205b
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes_p.h
@@ -0,0 +1,145 @@
+/****************************************************************************
+**
+** 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 QSGSOFTWAREPUBLICNODES_H
+#define QSGSOFTWAREPUBLICNODES_H
+
+#include <QtQuick/qsgrectanglenode.h>
+#include <QtQuick/qsgimagenode.h>
+#include <QtQuick/qsgninepatchnode.h>
+#include <QtGui/qpixmap.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 QSGSoftwareRectangleNode : public QSGRectangleNode
+{
+public:
+ QSGSoftwareRectangleNode();
+
+ void setRect(const QRectF &rect) override { m_rect = rect; markDirty(DirtyMaterial); }
+ QRectF rect() const override { return m_rect; }
+
+ void setColor(const QColor &color) override { m_color = color; markDirty(DirtyMaterial); }
+ QColor color() const override { return m_color; }
+
+ void paint(QPainter *painter);
+
+private:
+ QRectF m_rect;
+ QColor m_color;
+};
+
+class QSGSoftwareImageNode : public QSGImageNode
+{
+public:
+ QSGSoftwareImageNode();
+ ~QSGSoftwareImageNode();
+
+ void setRect(const QRectF &rect) override { m_rect = rect; markDirty(DirtyMaterial); }
+ QRectF rect() const override { return m_rect; }
+
+ void setSourceRect(const QRectF &r) override { m_sourceRect = r; }
+ QRectF sourceRect() const override { return m_sourceRect; }
+
+ void setTexture(QSGTexture *texture) override;
+ QSGTexture *texture() const override { return m_texture; }
+
+ void setFiltering(QSGTexture::Filtering filtering) override { m_filtering = filtering; markDirty(DirtyMaterial); }
+ QSGTexture::Filtering filtering() const override { return m_filtering; }
+
+ void setMipmapFiltering(QSGTexture::Filtering) override { }
+ QSGTexture::Filtering mipmapFiltering() const override { return QSGTexture::None; }
+
+ void setTextureCoordinatesTransform(TextureCoordinatesTransformMode transformNode) override;
+ TextureCoordinatesTransformMode textureCoordinatesTransform() const override { return m_transformMode; }
+
+ void setOwnsTexture(bool owns) override { m_owns = owns; }
+ bool ownsTexture() const override { return m_owns; }
+
+ void paint(QPainter *painter);
+
+private:
+ void updateCachedMirroredPixmap();
+
+ QPixmap m_cachedPixmap;
+ QSGTexture *m_texture;
+ QRectF m_rect;
+ QRectF m_sourceRect;
+ bool m_owns;
+ QSGTexture::Filtering m_filtering;
+ TextureCoordinatesTransformMode m_transformMode;
+ bool m_cachedMirroredPixmapIsDirty;
+};
+
+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 // QSGSOFTWAREPUBLICNODES_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..59c47db0c4
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp
@@ -0,0 +1,434 @@
+/****************************************************************************
+**
+** 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 "qsgsoftwareinternalimagenode_p.h"
+#include "qsgsoftwareinternalrectanglenode_p.h"
+#include "qsgsoftwareglyphnode_p.h"
+#include "qsgsoftwarepublicnodes_p.h"
+#include "qsgsoftwarepainternode_p.h"
+#include "qsgsoftwarepixmaptexture_p.h"
+#if QT_CONFIG(quick_sprite)
+#include "qsgsoftwarespritenode_p.h"
+#endif
+
+#include <qsgsimplerectnode.h>
+#include <qsgsimpletexturenode.h>
+#include <private/qsgrendernode_p.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_hasClipRegion(false)
+ , 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<QSGSoftwareInternalImageNode*>(node);
+ break;
+ case QSGSoftwareRenderableNode::Painter:
+ m_handle.painterNode = static_cast<QSGSoftwarePainterNode*>(node);
+ break;
+ case QSGSoftwareRenderableNode::Rectangle:
+ m_handle.rectangleNode = static_cast<QSGSoftwareInternalRectangleNode*>(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::SimpleRectangle:
+ m_handle.simpleRectangleNode = static_cast<QSGRectangleNode*>(node);
+ break;
+ case QSGSoftwareRenderableNode::SimpleImage:
+ m_handle.simpleImageNode = static_cast<QSGImageNode*>(node);
+ break;
+#if QT_CONFIG(quick_sprite)
+ case QSGSoftwareRenderableNode::SpriteNode:
+ m_handle.spriteNode = static_cast<QSGSoftwareSpriteNode*>(node);
+ break;
+#endif
+ case QSGSoftwareRenderableNode::RenderNode:
+ m_handle.renderNode = static_cast<QSGRenderNode*>(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;
+ case QSGSoftwareRenderableNode::SimpleRectangle:
+ if (m_handle.simpleRectangleNode->color().alpha() == 255 && !m_transform.isRotating())
+ m_isOpaque = true;
+ else
+ m_isOpaque = false;
+
+ boundingRect = m_handle.simpleRectangleNode->rect().toRect();
+ break;
+ case QSGSoftwareRenderableNode::SimpleImage:
+ if (!m_handle.simpleImageNode->texture()->hasAlphaChannel() && !m_transform.isRotating())
+ m_isOpaque = true;
+ else
+ m_isOpaque = false;
+
+ boundingRect = m_handle.simpleImageNode->rect().toRect();
+ break;
+#if QT_CONFIG(quick_sprite)
+ case QSGSoftwareRenderableNode::SpriteNode:
+ m_isOpaque = m_handle.spriteNode->isOpaque();
+ boundingRect = m_handle.spriteNode->rect().toRect();
+ break;
+#endif
+ case QSGSoftwareRenderableNode::RenderNode:
+ if (m_handle.renderNode->flags().testFlag(QSGRenderNode::OpaqueRendering) && !m_transform.isRotating())
+ m_isOpaque = true;
+ else
+ m_isOpaque = false;
+
+ boundingRect = m_handle.renderNode->rect().toRect();
+ break;
+ default:
+ break;
+ }
+
+ m_boundingRect = m_transform.mapRect(boundingRect);
+
+ if (m_hasClipRegion && m_clipRegion.rectCount() <= 1) {
+ // If there is a clipRegion, and it is empty, the item wont be rendered
+ if (m_clipRegion.isEmpty())
+ m_boundingRect = QRect();
+ else
+ m_boundingRect = m_boundingRect.intersected(m_clipRegion.rects().first());
+ }
+
+ // Overrides
+ if (m_opacity < 1.0f)
+ m_isOpaque = false;
+
+ m_dirtyRegion = QRegion(m_boundingRect);
+}
+
+struct RenderNodeState : public QSGRenderNode::RenderState
+{
+ const QMatrix4x4 *projectionMatrix() const override { return &ident; }
+ QRect scissorRect() const override { return QRect(); }
+ bool scissorEnabled() const override { return false; }
+ int stencilValue() const override { return 0; }
+ bool stencilEnabled() const override { return false; }
+ const QRegion *clipRegion() const override { return &cr; }
+ QMatrix4x4 ident;
+ QRegion cr;
+};
+
+QRegion QSGSoftwareRenderableNode::renderNode(QPainter *painter, bool forceOpaquePainting)
+{
+ Q_ASSERT(painter);
+
+ // Check for don't paint conditions
+ if (m_nodeType != RenderNode) {
+ if (!m_isDirty || qFuzzyIsNull(m_opacity) || m_dirtyRegion.isEmpty()) {
+ m_isDirty = false;
+ m_dirtyRegion = QRegion();
+ return QRegion();
+ }
+ } else {
+ if (!m_isDirty || qFuzzyIsNull(m_opacity)) {
+ m_isDirty = false;
+ m_dirtyRegion = QRegion();
+ return QRegion();
+ } else {
+ QSGRenderNodePrivate *rd = QSGRenderNodePrivate::get(m_handle.renderNode);
+ QMatrix4x4 m = m_transform;
+ rd->m_matrix = &m;
+ rd->m_opacity = m_opacity;
+ RenderNodeState rs;
+ rs.cr = m_clipRegion;
+
+ const QRect br = m_handle.renderNode->flags().testFlag(QSGRenderNode::BoundedRectRendering)
+ ? m_boundingRect :
+ QRect(0, 0, painter->device()->width(), painter->device()->height());
+
+ painter->save();
+ painter->setClipRegion(br, Qt::ReplaceClip);
+ m_handle.renderNode->render(&rs);
+ painter->restore();
+
+ m_previousDirtyRegion = QRegion(br);
+ m_isDirty = false;
+ m_dirtyRegion = QRegion();
+ return br;
+ }
+ }
+
+ 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, m_handle.simpleTextureNode->sourceRect());
+ } else if (QSGPlainTexture *pt = dynamic_cast<QSGPlainTexture *>(texture)) {
+ const QImage &im = pt->image();
+ painter->drawImage(m_handle.simpleTextureNode->rect(), im, m_handle.simpleTextureNode->sourceRect());
+ }
+ }
+ 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;
+ case QSGSoftwareRenderableNode::SimpleRectangle:
+ static_cast<QSGSoftwareRectangleNode *>(m_handle.simpleRectangleNode)->paint(painter);
+ break;
+ case QSGSoftwareRenderableNode::SimpleImage:
+ static_cast<QSGSoftwareImageNode *>(m_handle.simpleImageNode)->paint(painter);
+ break;
+#if QT_CONFIG(quick_sprite)
+ case QSGSoftwareRenderableNode::SpriteNode:
+ static_cast<QSGSoftwareSpriteNode *>(m_handle.spriteNode)->paint(painter);
+ break;
+#endif
+ 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, bool hasClipRegion)
+{
+ if (m_clipRegion == clipRect && m_hasClipRegion == hasClipRegion)
+ return;
+
+ m_clipRegion = clipRect;
+ m_hasClipRegion = hasClipRegion;
+ 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..473578c185
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode_p.h
@@ -0,0 +1,157 @@
+/****************************************************************************
+**
+** 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 <QtQuick/private/qtquickglobal_p.h>
+
+#include <QtGui/QRegion>
+#include <QtCore/QRect>
+#include <QtGui/QTransform>
+#include <QtQuick/qsgrectanglenode.h>
+#include <QtQuick/qsgimagenode.h>
+#include <QtQuick/qsgninepatchnode.h>
+
+QT_BEGIN_NAMESPACE
+
+class QSGSimpleRectNode;
+class QSGSimpleTextureNode;
+class QSGSoftwareInternalImageNode;
+class QSGSoftwarePainterNode;
+class QSGSoftwareInternalRectangleNode;
+class QSGSoftwareGlyphNode;
+class QSGSoftwareNinePatchNode;
+class QSGSoftwareSpriteNode;
+class QSGRenderNode;
+
+class QSGSoftwareRenderableNode
+{
+public:
+ enum NodeType {
+ Invalid = -1,
+ SimpleRect,
+ SimpleTexture,
+ Image,
+ Painter,
+ Rectangle,
+ Glyph,
+ NinePatch,
+ SimpleRectangle,
+ SimpleImage,
+#if QT_CONFIG(quick_sprite)
+ SpriteNode,
+#endif
+ RenderNode
+ };
+
+ 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, bool hasClipRegion = true);
+ 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;
+ QSGSoftwareInternalImageNode *imageNode;
+ QSGSoftwarePainterNode *painterNode;
+ QSGSoftwareInternalRectangleNode *rectangleNode;
+ QSGSoftwareGlyphNode *glpyhNode;
+ QSGSoftwareNinePatchNode *ninePatchNode;
+ QSGRectangleNode *simpleRectangleNode;
+ QSGImageNode *simpleImageNode;
+ QSGSoftwareSpriteNode *spriteNode;
+ QSGRenderNode *renderNode;
+ };
+
+ 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;
+ bool m_hasClipRegion;
+ 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..4937565aa9
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater.cpp
@@ -0,0 +1,309 @@
+/****************************************************************************
+**
+** 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 "qsgsoftwareinternalimagenode_p.h"
+#include "qsgsoftwareinternalrectanglenode_p.h"
+#include "qsgsoftwareglyphnode_p.h"
+#include "qsgsoftwarepublicnodes_p.h"
+#include "qsgsoftwarepainternode_p.h"
+#include "qsgsoftwarepixmaptexture_p.h"
+
+#include <QtQuick/qsgsimplerectnode.h>
+#include <QtQuick/qsgsimpletexturenode.h>
+#include <QtQuick/qsgrendernode.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_hasClip = false;
+ 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.count() == 1) {
+ m_clipState.push(m_transformState.top().map(QRegion(node->clipRect().toRect())));
+ m_hasClip = true;
+ } else {
+ const QRegion transformedClipRect = m_transformState.top().map(QRegion(node->clipRect().toRect()));
+ m_clipState.push(transformedClipRect.intersected(m_clipState.top()));
+ }
+ m_stateMap[node] = currentState(node);
+ return true;
+}
+
+void QSGSoftwareRenderableNodeUpdater::endVisit(QSGClipNode *)
+{
+ m_clipState.pop();
+ if (m_clipState.count() == 1)
+ m_hasClip = false;
+}
+
+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 if (QSGNinePatchNode *nn = dynamic_cast<QSGNinePatchNode *>(node)) {
+ return updateRenderableNode(QSGSoftwareRenderableNode::NinePatch, nn);
+ } else if (QSGRectangleNode *rn = dynamic_cast<QSGRectangleNode *>(node)) {
+ return updateRenderableNode(QSGSoftwareRenderableNode::SimpleRectangle, rn);
+ } else if (QSGImageNode *n = dynamic_cast<QSGImageNode *>(node)) {
+ return updateRenderableNode(QSGSoftwareRenderableNode::SimpleImage, n);
+ } 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(QSGInternalImageNode *node)
+{
+ return updateRenderableNode(QSGSoftwareRenderableNode::Image, node);
+}
+
+void QSGSoftwareRenderableNodeUpdater::endVisit(QSGInternalImageNode *)
+{
+}
+
+bool QSGSoftwareRenderableNodeUpdater::visit(QSGPainterNode *node)
+{
+ return updateRenderableNode(QSGSoftwareRenderableNode::Painter, node);
+}
+
+void QSGSoftwareRenderableNodeUpdater::endVisit(QSGPainterNode *)
+{
+}
+
+bool QSGSoftwareRenderableNodeUpdater::visit(QSGInternalRectangleNode *node)
+{
+ return updateRenderableNode(QSGSoftwareRenderableNode::Rectangle, node);
+}
+
+void QSGSoftwareRenderableNodeUpdater::endVisit(QSGInternalRectangleNode *)
+{
+}
+
+bool QSGSoftwareRenderableNodeUpdater::visit(QSGGlyphNode *node)
+{
+ return updateRenderableNode(QSGSoftwareRenderableNode::Glyph, node);
+}
+
+void QSGSoftwareRenderableNodeUpdater::endVisit(QSGGlyphNode *)
+{
+}
+
+bool QSGSoftwareRenderableNodeUpdater::visit(QSGRootNode *node)
+{
+ m_stateMap[node] = currentState(node);
+ return true;
+}
+
+void QSGSoftwareRenderableNodeUpdater::endVisit(QSGRootNode *)
+{
+}
+
+#if QT_CONFIG(quick_sprite)
+bool QSGSoftwareRenderableNodeUpdater::visit(QSGSpriteNode *node)
+{
+ return updateRenderableNode(QSGSoftwareRenderableNode::SpriteNode, node);
+}
+
+void QSGSoftwareRenderableNodeUpdater::endVisit(QSGSpriteNode *)
+{
+
+}
+#endif
+
+bool QSGSoftwareRenderableNodeUpdater::visit(QSGRenderNode *node)
+{
+ return updateRenderableNode(QSGSoftwareRenderableNode::RenderNode, node);
+}
+
+void QSGSoftwareRenderableNodeUpdater::endVisit(QSGRenderNode *)
+{
+}
+
+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);
+ m_hasClip = state.hasClip;
+ } else {
+ // There is no parent, and no previous parent, so likely a root node
+ m_opacityState.push(1.0f);
+ m_transformState.push(QTransform());
+ m_clipState.push(QRegion());
+ m_hasClip = false;
+ }
+
+ // If the node is being removed, then cleanup the state data
+ // 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;
+ }
+ case QSGNode::RenderNodeType: {
+ QSGRenderNode *r = static_cast<QSGRenderNode*>(node);
+ if (visit(r))
+ visitChildren(r);
+ endVisit(r);
+ 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.hasClip = m_hasClip;
+ 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..ca16f77c22
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater_p.h
@@ -0,0 +1,143 @@
+/****************************************************************************
+**
+** 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(QSGInternalImageNode *) override;
+ void endVisit(QSGInternalImageNode *) override;
+ bool visit(QSGPainterNode *) override;
+ void endVisit(QSGPainterNode *) override;
+ bool visit(QSGInternalRectangleNode *) override;
+ void endVisit(QSGInternalRectangleNode *) override;
+ bool visit(QSGGlyphNode *) override;
+ void endVisit(QSGGlyphNode *) override;
+ bool visit(QSGRootNode *) override;
+ void endVisit(QSGRootNode *) override;
+#if QT_CONFIG(quick_sprite)
+ bool visit(QSGSpriteNode *) override;
+ void endVisit(QSGSpriteNode *) override;
+#endif
+ bool visit(QSGRenderNode *) override;
+ void endVisit(QSGRenderNode *) override;
+
+ void updateNodes(QSGNode *node, bool isNodeRemoved = false);
+
+private:
+ struct NodeState {
+ float opacity;
+ QRegion clip;
+ bool hasClip;
+ 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;
+ bool m_hasClip;
+ 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(), m_hasClip);
+
+ 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..cad826fb27
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer.cpp
@@ -0,0 +1,165 @@
+/****************************************************************************
+**
+** 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 <QtGui/QBackingStore>
+#include <QElapsedTimer>
+
+Q_LOGGING_CATEGORY(lcRenderer, "qt.scenegraph.softwarecontext.renderer")
+
+QT_BEGIN_NAMESPACE
+
+QSGSoftwareRenderer::QSGSoftwareRenderer(QSGRenderContext *context)
+ : QSGAbstractSoftwareRenderer(context)
+ , m_paintDevice(nullptr)
+ , m_backingStore(nullptr)
+{
+}
+
+QSGSoftwareRenderer::~QSGSoftwareRenderer()
+{
+}
+
+void QSGSoftwareRenderer::setCurrentPaintDevice(QPaintDevice *device)
+{
+ m_paintDevice = device;
+ m_backingStore = nullptr;
+}
+
+QPaintDevice *QSGSoftwareRenderer::currentPaintDevice() const
+{
+ return m_paintDevice;
+}
+
+void QSGSoftwareRenderer::setBackingStore(QBackingStore *backingStore)
+{
+ m_backingStore = backingStore;
+ m_paintDevice = nullptr;
+}
+
+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 && !m_backingStore)
+ return;
+
+ // If there is a backingstore, set the current paint device
+ if (m_backingStore) {
+ // For HiDPI QBackingStores, the paint device is not valid
+ // until begin() has been called. See: QTBUG-55875
+ m_backingStore->beginPaint(QRegion());
+ m_paintDevice = m_backingStore->paintDevice();
+ m_backingStore->endPaint();
+ }
+
+ QElapsedTimer renderTimer;
+
+ setBackgroundColor(clearColor());
+ setBackgroundSize(QSize(m_paintDevice->width() / m_paintDevice->devicePixelRatio(),
+ m_paintDevice->height() / m_paintDevice->devicePixelRatio()));
+
+ // 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.
+ const QRegion updateRegion = optimizeRenderList();
+ qint64 optimizeRenderListTime = renderTimer.restart();
+
+ // If Rendering to a backingstore, prepare it to be updated
+ if (m_backingStore != nullptr) {
+ m_backingStore->beginPaint(updateRegion);
+ // It is possible that a QBackingStore's paintDevice() will change
+ // when begin() is called.
+ m_paintDevice = m_backingStore->paintDevice();
+ }
+
+ QPainter painter(m_paintDevice);
+ painter.setRenderHint(QPainter::Antialiasing);
+ auto rc = static_cast<QSGSoftwareRenderContext *>(context());
+ QPainter *prevPainter = rc->m_activePainter;
+ rc->m_activePainter = &painter;
+
+ // Render the contents Renderlist
+ m_flushRegion = renderNodes(&painter);
+ qint64 renderTime = renderTimer.elapsed();
+
+ if (m_backingStore != nullptr)
+ m_backingStore->endPaint();
+
+ rc->m_activePainter = prevPainter;
+ 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..bb28da4ca5
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer_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 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 QBackingStore;
+
+class Q_QUICK_PRIVATE_EXPORT QSGSoftwareRenderer : public QSGAbstractSoftwareRenderer
+{
+public:
+ QSGSoftwareRenderer(QSGRenderContext *context);
+ virtual ~QSGSoftwareRenderer();
+
+ void setCurrentPaintDevice(QPaintDevice *device);
+ QPaintDevice *currentPaintDevice() const;
+ void setBackingStore(QBackingStore *backingStore);
+ QRegion flushRegion() const;
+
+protected:
+ void renderScene(uint fboId = 0) final;
+ void render() final;
+
+private:
+ QPaintDevice* m_paintDevice;
+ QBackingStore* m_backingStore;
+ 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..70dce71801
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderlistbuilder.cpp
@@ -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$
+**
+****************************************************************************/
+
+#include "qsgsoftwarerenderlistbuilder_p.h"
+
+#include "qsgsoftwarerenderablenode_p.h"
+#include "qsgabstractsoftwarerenderer_p.h"
+#include "qsgsoftwareinternalimagenode_p.h"
+#include "qsgsoftwareinternalrectanglenode_p.h"
+#include "qsgsoftwareglyphnode_p.h"
+#include "qsgsoftwarepublicnodes_p.h"
+#include "qsgsoftwarepainternode_p.h"
+#include "qsgsoftwarepixmaptexture_p.h"
+
+#include <QtQuick/qsgsimplerectnode.h>
+#include <QtQuick/qsgsimpletexturenode.h>
+#include <QtQuick/qsgrendernode.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(QSGInternalImageNode *node)
+{
+ return addRenderableNode(node);
+}
+
+void QSGSoftwareRenderListBuilder::endVisit(QSGInternalImageNode *)
+{
+}
+
+bool QSGSoftwareRenderListBuilder::visit(QSGPainterNode *node)
+{
+ return addRenderableNode(node);
+}
+
+void QSGSoftwareRenderListBuilder::endVisit(QSGPainterNode *)
+{
+}
+
+bool QSGSoftwareRenderListBuilder::visit(QSGInternalRectangleNode *node)
+{
+ return addRenderableNode(node);
+}
+
+void QSGSoftwareRenderListBuilder::endVisit(QSGInternalRectangleNode *)
+{
+}
+
+bool QSGSoftwareRenderListBuilder::visit(QSGGlyphNode *node)
+{
+ return addRenderableNode(node);
+}
+
+void QSGSoftwareRenderListBuilder::endVisit(QSGGlyphNode *)
+{
+}
+
+bool QSGSoftwareRenderListBuilder::visit(QSGRootNode *)
+{
+ return true;
+}
+
+void QSGSoftwareRenderListBuilder::endVisit(QSGRootNode *)
+{
+}
+
+#if QT_CONFIG(quick_sprite)
+bool QSGSoftwareRenderListBuilder::visit(QSGSpriteNode *node)
+{
+ return addRenderableNode(node);
+}
+
+void QSGSoftwareRenderListBuilder::endVisit(QSGSpriteNode *)
+{
+
+}
+#endif
+
+bool QSGSoftwareRenderListBuilder::visit(QSGRenderNode *node)
+{
+ return addRenderableNode(node);
+}
+
+void QSGSoftwareRenderListBuilder::endVisit(QSGRenderNode *)
+{
+}
+
+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..f5db30269f
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderlistbuilder_p.h
@@ -0,0 +1,98 @@
+/****************************************************************************
+**
+** 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(QSGInternalImageNode *) override;
+ void endVisit(QSGInternalImageNode *) override;
+ bool visit(QSGPainterNode *) override;
+ void endVisit(QSGPainterNode *) override;
+ bool visit(QSGInternalRectangleNode *) override;
+ void endVisit(QSGInternalRectangleNode *) override;
+ bool visit(QSGGlyphNode *) override;
+ void endVisit(QSGGlyphNode *) override;
+ bool visit(QSGRootNode *) override;
+ void endVisit(QSGRootNode *) override;
+#if QT_CONFIG(quick_sprite)
+ bool visit(QSGSpriteNode *) override;
+ void endVisit(QSGSpriteNode *) override;
+#endif
+ bool visit(QSGRenderNode *) override;
+ void endVisit(QSGRenderNode *) 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..19a963b403
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop.cpp
@@ -0,0 +1,264 @@
+/****************************************************************************
+**
+** 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->flushFrameSynchronousEvents();
+ // 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->setBackingStore(m_backingStores[window]);
+
+ cd->renderSceneGraph(window->size());
+
+ 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 'software' 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;
+ grabbed.detach();
+ 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/qsgsoftwarespritenode.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarespritenode.cpp
new file mode 100644
index 0000000000..ba7bbc2d11
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarespritenode.cpp
@@ -0,0 +1,139 @@
+/****************************************************************************
+**
+** 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 "qsgsoftwarespritenode_p.h"
+#include "qsgsoftwarepixmaptexture_p.h"
+#include <QtGui/QPainter>
+
+QT_BEGIN_NAMESPACE
+
+QSGSoftwareSpriteNode::QSGSoftwareSpriteNode()
+{
+ setMaterial((QSGMaterial*)1);
+ setGeometry((QSGGeometry*)1);
+}
+
+void QSGSoftwareSpriteNode::setTexture(QSGTexture *texture)
+{
+ m_texture = qobject_cast<QSGSoftwarePixmapTexture*>(texture);
+ markDirty(DirtyMaterial);
+}
+
+void QSGSoftwareSpriteNode::setTime(float time)
+{
+ if (m_time != time) {
+ m_time = time;
+ markDirty(DirtyMaterial);
+ }
+}
+
+void QSGSoftwareSpriteNode::setSourceA(const QPoint &source)
+{
+ if (m_sourceA != source) {
+ m_sourceA = source;
+ markDirty(DirtyMaterial);
+ }
+}
+
+void QSGSoftwareSpriteNode::setSourceB(const QPoint &source)
+{
+ if (m_sourceB != source) {
+ m_sourceB = source;
+ markDirty(DirtyMaterial);
+ }
+}
+
+void QSGSoftwareSpriteNode::setSpriteSize(const QSize &size)
+{
+ if (m_spriteSize != size) {
+ m_spriteSize = size;
+ markDirty(DirtyMaterial);
+ }
+}
+
+void QSGSoftwareSpriteNode::setSheetSize(const QSize &size)
+{
+ if (m_sheetSize != size) {
+ m_sheetSize = size;
+ markDirty(DirtyMaterial);
+ }
+}
+
+void QSGSoftwareSpriteNode::setSize(const QSizeF &size)
+{
+ if (m_size != size) {
+ m_size = size;
+ markDirty(DirtyGeometry);
+ }
+}
+
+void QSGSoftwareSpriteNode::setFiltering(QSGTexture::Filtering filtering)
+{
+ Q_UNUSED(filtering);
+}
+
+void QSGSoftwareSpriteNode::update()
+{
+}
+
+void QSGSoftwareSpriteNode::paint(QPainter *painter)
+{
+ //Get the pixmap handle from the texture
+ if (!m_texture)
+ return;
+
+ const QPixmap &pixmap = m_texture->pixmap();
+
+ // XXX try to do some kind of interpolation between sourceA and sourceB using time
+ painter->drawPixmap(QRectF(0, 0, m_size.width(), m_size.height()),
+ pixmap,
+ QRectF(m_sourceA, m_spriteSize));
+}
+
+bool QSGSoftwareSpriteNode::isOpaque() const
+{
+ return false;
+}
+
+QRectF QSGSoftwareSpriteNode::rect() const
+{
+ return QRectF(0, 0, m_size.width(), m_size.height());
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarespritenode_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarespritenode_p.h
new file mode 100644
index 0000000000..577a30c051
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarespritenode_p.h
@@ -0,0 +1,96 @@
+/****************************************************************************
+**
+** 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 QSGSOFTWARESPRITENODE_H
+#define QSGSOFTWARESPRITENODE_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/qtquickglobal_p.h>
+
+QT_REQUIRE_CONFIG(quick_sprite);
+
+#include <private/qsgadaptationlayer_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QSGSoftwarePixmapTexture;
+class QSGSoftwareSpriteNode : public QSGSpriteNode
+{
+public:
+ QSGSoftwareSpriteNode();
+
+ void setTexture(QSGTexture *texture) override;
+ void setTime(float time) override;
+ void setSourceA(const QPoint &source) override;
+ void setSourceB(const QPoint &source) override;
+ void setSpriteSize(const QSize &size) override;
+ void setSheetSize(const QSize &size) override;
+ void setSize(const QSizeF &size) override;
+ void setFiltering(QSGTexture::Filtering filtering) override;
+ void update() override;
+
+ void paint(QPainter *painter);
+ bool isOpaque() const;
+ QRectF rect() const;
+
+private:
+
+ QSGSoftwarePixmapTexture *m_texture;
+ float m_time;
+ QPoint m_sourceA;
+ QPoint m_sourceB;
+ QSize m_spriteSize;
+ QSize m_sheetSize;
+ QSizeF m_size;
+
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGSOFTWARESPRITENODE_H
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop.cpp
new file mode 100644
index 0000000000..5d5485ed8f
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop.cpp
@@ -0,0 +1,991 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgsoftwarethreadedrenderloop_p.h"
+#include "qsgsoftwarecontext_p.h"
+#include "qsgsoftwarerenderer_p.h"
+
+#include <private/qsgrenderer_p.h>
+#include <private/qquickwindow_p.h>
+#include <private/qquickprofiler_p.h>
+#include <private/qquickanimatorcontroller_p.h>
+#include <private/qquickprofiler_p.h>
+#include <private/qqmldebugserviceinterfaces_p.h>
+#include <private/qqmldebugconnector_p.h>
+
+#include <qpa/qplatformbackingstore.h>
+
+#include <QtCore/QQueue>
+#include <QtCore/QElapsedTimer>
+#include <QtCore/QThread>
+#include <QtCore/QMutex>
+#include <QtCore/QWaitCondition>
+#include <QtGui/QGuiApplication>
+#include <QtGui/QBackingStore>
+#include <QtQuick/QQuickWindow>
+
+QT_BEGIN_NAMESPACE
+
+// Passed from the RL to the RT when a window is removed obscured and should be
+// removed from the render loop.
+const QEvent::Type WM_Obscure = QEvent::Type(QEvent::User + 1);
+
+// Passed from the RL to RT when GUI has been locked, waiting for sync.
+const QEvent::Type WM_RequestSync = QEvent::Type(QEvent::User + 2);
+
+// Passed by the RT to itself to trigger another render pass. This is typically
+// a result of QQuickWindow::update().
+const QEvent::Type WM_RequestRepaint = QEvent::Type(QEvent::User + 3);
+
+// Passed by the RL to the RT to maybe release resource if no windows are
+// rendering.
+const QEvent::Type WM_TryRelease = QEvent::Type(QEvent::User + 4);
+
+// Passed by the RL to the RT when a QQuickWindow::grabWindow() is called.
+const QEvent::Type WM_Grab = QEvent::Type(QEvent::User + 5);
+
+// Passed by the window when there is a render job to run.
+const QEvent::Type WM_PostJob = QEvent::Type(QEvent::User + 6);
+
+class QSGSoftwareWindowEvent : public QEvent
+{
+public:
+ QSGSoftwareWindowEvent(QQuickWindow *c, QEvent::Type type) : QEvent(type), window(c) { }
+ QQuickWindow *window;
+};
+
+class QSGSoftwareTryReleaseEvent : public QSGSoftwareWindowEvent
+{
+public:
+ QSGSoftwareTryReleaseEvent(QQuickWindow *win, bool destroy)
+ : QSGSoftwareWindowEvent(win, WM_TryRelease), destroying(destroy) { }
+ bool destroying;
+};
+
+class QSGSoftwareSyncEvent : public QSGSoftwareWindowEvent
+{
+public:
+ QSGSoftwareSyncEvent(QQuickWindow *c, bool inExpose, bool force)
+ : QSGSoftwareWindowEvent(c, WM_RequestSync)
+ , size(c->size())
+ , dpr(c->effectiveDevicePixelRatio())
+ , syncInExpose(inExpose)
+ , forceRenderPass(force) { }
+ QSize size;
+ float dpr;
+ bool syncInExpose;
+ bool forceRenderPass;
+};
+
+class QSGSoftwareGrabEvent : public QSGSoftwareWindowEvent
+{
+public:
+ QSGSoftwareGrabEvent(QQuickWindow *c, QImage *result)
+ : QSGSoftwareWindowEvent(c, WM_Grab), image(result) { }
+ QImage *image;
+};
+
+class QSGSoftwareJobEvent : public QSGSoftwareWindowEvent
+{
+public:
+ QSGSoftwareJobEvent(QQuickWindow *c, QRunnable *postedJob)
+ : QSGSoftwareWindowEvent(c, WM_PostJob), job(postedJob) { }
+ ~QSGSoftwareJobEvent() { delete job; }
+ QRunnable *job;
+};
+
+class QSGSoftwareEventQueue : public QQueue<QEvent *>
+{
+public:
+ void addEvent(QEvent *e) {
+ mutex.lock();
+ enqueue(e);
+ if (waiting)
+ condition.wakeOne();
+ mutex.unlock();
+ }
+
+ QEvent *takeEvent(bool wait) {
+ mutex.lock();
+ if (isEmpty() && wait) {
+ waiting = true;
+ condition.wait(&mutex);
+ waiting = false;
+ }
+ QEvent *e = dequeue();
+ mutex.unlock();
+ return e;
+ }
+
+ bool hasMoreEvents() {
+ mutex.lock();
+ bool has = !isEmpty();
+ mutex.unlock();
+ return has;
+ }
+
+private:
+ QMutex mutex;
+ QWaitCondition condition;
+ bool waiting = false;
+};
+
+static inline int qsgrl_animation_interval()
+{
+ const qreal refreshRate = QGuiApplication::primaryScreen() ? QGuiApplication::primaryScreen()->refreshRate() : 0;
+ return refreshRate < 1 ? 16 : int(1000 / refreshRate);
+}
+
+class QSGSoftwareRenderThread : public QThread
+{
+ Q_OBJECT
+public:
+ QSGSoftwareRenderThread(QSGSoftwareThreadedRenderLoop *rl, QSGRenderContext *renderContext)
+ : renderLoop(rl)
+ {
+ rc = static_cast<QSGSoftwareRenderContext *>(renderContext);
+ vsyncDelta = qsgrl_animation_interval();
+ }
+
+ ~QSGSoftwareRenderThread()
+ {
+ delete rc;
+ }
+
+ bool event(QEvent *e);
+ void run();
+
+ void syncAndRender();
+ void sync(bool inExpose);
+
+ void requestRepaint()
+ {
+ if (sleeping)
+ stopEventProcessing = true;
+ if (exposedWindow)
+ pendingUpdate |= RepaintRequest;
+ }
+
+ void processEventsAndWaitForMore();
+ void processEvents();
+ void postEvent(QEvent *e);
+
+ enum UpdateRequest {
+ SyncRequest = 0x01,
+ RepaintRequest = 0x02,
+ ExposeRequest = 0x04 | RepaintRequest | SyncRequest
+ };
+
+ QSGSoftwareThreadedRenderLoop *renderLoop;
+ QSGSoftwareRenderContext *rc;
+ QAnimationDriver *rtAnim = nullptr;
+ volatile bool active = false;
+ uint pendingUpdate = 0;
+ bool sleeping = false;
+ bool syncResultedInChanges = false;
+ float vsyncDelta;
+ QMutex mutex;
+ QWaitCondition waitCondition;
+ QQuickWindow *exposedWindow = nullptr;
+ QBackingStore *backingStore = nullptr;
+ bool stopEventProcessing = false;
+ QSGSoftwareEventQueue eventQueue;
+ QElapsedTimer renderThrottleTimer;
+ qint64 syncTime;
+ qint64 renderTime;
+ qint64 sinceLastTime;
+
+public slots:
+ void onSceneGraphChanged() {
+ syncResultedInChanges = true;
+ }
+};
+
+bool QSGSoftwareRenderThread::event(QEvent *e)
+{
+ switch ((int)e->type()) {
+
+ case WM_Obscure:
+ Q_ASSERT(!exposedWindow || exposedWindow == static_cast<QSGSoftwareWindowEvent *>(e)->window);
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "RT - WM_Obscure" << exposedWindow;
+ mutex.lock();
+ if (exposedWindow) {
+ QQuickWindowPrivate::get(exposedWindow)->fireAboutToStop();
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_Obscure - window removed");
+ exposedWindow = nullptr;
+ delete backingStore;
+ backingStore = nullptr;
+ }
+ waitCondition.wakeOne();
+ mutex.unlock();
+ return true;
+
+ case WM_RequestSync: {
+ QSGSoftwareSyncEvent *wme = static_cast<QSGSoftwareSyncEvent *>(e);
+ if (sleeping)
+ stopEventProcessing = true;
+ exposedWindow = wme->window;
+ if (backingStore == nullptr)
+ backingStore = new QBackingStore(exposedWindow);
+ if (backingStore->size() != exposedWindow->size())
+ backingStore->resize(exposedWindow->size());
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "RT - WM_RequestSync" << exposedWindow;
+ pendingUpdate |= SyncRequest;
+ if (wme->syncInExpose) {
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_RequestSync - triggered from expose");
+ pendingUpdate |= ExposeRequest;
+ }
+ if (wme->forceRenderPass) {
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_RequestSync - repaint regardless");
+ pendingUpdate |= RepaintRequest;
+ }
+ return true;
+ }
+
+ case WM_TryRelease: {
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_TryRelease");
+ mutex.lock();
+ renderLoop->lockedForSync = true;
+ QSGSoftwareTryReleaseEvent *wme = static_cast<QSGSoftwareTryReleaseEvent *>(e);
+ // Only when no windows are exposed anymore or we are shutting down.
+ if (!exposedWindow || wme->destroying) {
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_TryRelease - invalidating rc");
+ if (wme->window) {
+ QQuickWindowPrivate *wd = QQuickWindowPrivate::get(wme->window);
+ if (wme->destroying) {
+ // Bye bye nodes...
+ wd->cleanupNodesOnShutdown();
+ }
+ rc->invalidate();
+ QCoreApplication::processEvents();
+ QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
+ if (wme->destroying)
+ delete wd->animationController;
+ }
+ if (wme->destroying)
+ active = false;
+ if (sleeping)
+ stopEventProcessing = true;
+ } else {
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_TryRelease - not releasing because window is still active");
+ }
+ waitCondition.wakeOne();
+ renderLoop->lockedForSync = false;
+ mutex.unlock();
+ return true;
+ }
+
+ case WM_Grab: {
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_Grab");
+ QSGSoftwareGrabEvent *wme = static_cast<QSGSoftwareGrabEvent *>(e);
+ Q_ASSERT(wme->window);
+ Q_ASSERT(wme->window == exposedWindow || !exposedWindow);
+ mutex.lock();
+ if (wme->window) {
+ // Grabbing is generally done by rendering a frame and reading the
+ // color buffer contents back, without presenting, and then
+ // creating a QImage from the returned data. It is terribly
+ // inefficient since it involves a full blocking wait for the GPU.
+ // However, our hands are tied by the existing, synchronous APIs of
+ // QQuickWindow and such.
+ QQuickWindowPrivate *wd = QQuickWindowPrivate::get(wme->window);
+ auto softwareRenderer = static_cast<QSGSoftwareRenderer*>(wd->renderer);
+ if (softwareRenderer)
+ softwareRenderer->setBackingStore(backingStore);
+ rc->initialize(nullptr);
+ wd->syncSceneGraph();
+ wd->renderSceneGraph(wme->window->size());
+ *wme->image = backingStore->handle()->toImage();
+ }
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_Grab - waking gui to handle result");
+ waitCondition.wakeOne();
+ mutex.unlock();
+ return true;
+ }
+
+ case WM_PostJob: {
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_PostJob");
+ QSGSoftwareJobEvent *wme = static_cast<QSGSoftwareJobEvent *>(e);
+ Q_ASSERT(wme->window == exposedWindow);
+ if (exposedWindow) {
+ wme->job->run();
+ delete wme->job;
+ wme->job = nullptr;
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_PostJob - job done");
+ }
+ return true;
+ }
+
+ case WM_RequestRepaint:
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - WM_RequestPaint");
+ // When GUI posts this event, it is followed by a polishAndSync, so we
+ // must not exit the event loop yet.
+ pendingUpdate |= RepaintRequest;
+ break;
+
+ default:
+ break;
+ }
+
+ return QThread::event(e);
+}
+
+void QSGSoftwareRenderThread::postEvent(QEvent *e)
+{
+ eventQueue.addEvent(e);
+}
+
+void QSGSoftwareRenderThread::processEvents()
+{
+ while (eventQueue.hasMoreEvents()) {
+ QEvent *e = eventQueue.takeEvent(false);
+ event(e);
+ delete e;
+ }
+}
+
+void QSGSoftwareRenderThread::processEventsAndWaitForMore()
+{
+ stopEventProcessing = false;
+ while (!stopEventProcessing) {
+ QEvent *e = eventQueue.takeEvent(true);
+ event(e);
+ delete e;
+ }
+}
+
+void QSGSoftwareRenderThread::run()
+{
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - run()");
+
+ rtAnim = rc->sceneGraphContext()->createAnimationDriver(nullptr);
+ rtAnim->install();
+
+ if (QQmlDebugConnector::service<QQmlProfilerService>())
+ QQuickProfiler::registerAnimationCallback();
+
+ renderThrottleTimer.start();
+
+ while (active) {
+ if (exposedWindow)
+ syncAndRender();
+
+ processEvents();
+ QCoreApplication::processEvents();
+
+ if (pendingUpdate == 0 || !exposedWindow) {
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - done drawing, sleep");
+ sleeping = true;
+ processEventsAndWaitForMore();
+ sleeping = false;
+ }
+ }
+
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - run() exiting");
+
+ delete rtAnim;
+ rtAnim = nullptr;
+
+ rc->moveToThread(renderLoop->thread());
+ moveToThread(renderLoop->thread());
+}
+
+void QSGSoftwareRenderThread::sync(bool inExpose)
+{
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - sync");
+
+ mutex.lock();
+ Q_ASSERT_X(renderLoop->lockedForSync, "QSGD3D12RenderThread::sync()", "sync triggered with gui not locked");
+
+ if (exposedWindow) {
+ QQuickWindowPrivate *wd = QQuickWindowPrivate::get(exposedWindow);
+ bool hadRenderer = wd->renderer != nullptr;
+ // If the scene graph was touched since the last sync() make sure it sends the
+ // changed signal.
+ if (wd->renderer)
+ wd->renderer->clearChangedFlag();
+
+ rc->initialize(nullptr);
+ wd->syncSceneGraph();
+
+ if (!hadRenderer && wd->renderer) {
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - created renderer");
+ syncResultedInChanges = true;
+ connect(wd->renderer, &QSGRenderer::sceneGraphChanged, this,
+ &QSGSoftwareRenderThread::onSceneGraphChanged, Qt::DirectConnection);
+ }
+
+ // Process deferred deletes now, directly after the sync as deleteLater
+ // on the GUI must now also have resulted in SG changes and the delete
+ // is a safe operation.
+ QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
+ }
+
+ if (!inExpose) {
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - sync complete, waking gui");
+ waitCondition.wakeOne();
+ mutex.unlock();
+ }
+}
+
+void QSGSoftwareRenderThread::syncAndRender()
+{
+ Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphRenderLoopFrame);
+
+ QElapsedTimer waitTimer;
+ waitTimer.start();
+
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - syncAndRender()");
+
+ syncResultedInChanges = false;
+ QQuickWindowPrivate *wd = QQuickWindowPrivate::get(exposedWindow);
+
+ const bool repaintRequested = (pendingUpdate & RepaintRequest) || wd->customRenderStage;
+ const bool syncRequested = pendingUpdate & SyncRequest;
+ const bool exposeRequested = (pendingUpdate & ExposeRequest) == ExposeRequest;
+ pendingUpdate = 0;
+
+ if (syncRequested)
+ sync(exposeRequested);
+
+ Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame);
+
+ if (!syncResultedInChanges && !repaintRequested) {
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - no changes, render aborted");
+ int waitTime = vsyncDelta - (int) waitTimer.elapsed();
+ if (waitTime > 0)
+ msleep(waitTime);
+ return;
+ }
+
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - rendering started");
+
+ if (rtAnim->isRunning()) {
+ wd->animationController->lock();
+ rtAnim->advance();
+ wd->animationController->unlock();
+ }
+
+ bool canRender = wd->renderer != nullptr;
+
+ if (canRender) {
+ auto softwareRenderer = static_cast<QSGSoftwareRenderer*>(wd->renderer);
+ if (softwareRenderer)
+ softwareRenderer->setBackingStore(backingStore);
+ wd->renderSceneGraph(exposedWindow->size());
+
+ Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame);
+
+ if (softwareRenderer && (!wd->customRenderStage || !wd->customRenderStage->swap()))
+ backingStore->flush(softwareRenderer->flushRegion());
+
+ // Since there is no V-Sync with QBackingStore, throttle rendering the refresh
+ // rate of the current screen the window is on.
+ int blockTime = vsyncDelta - (int) renderThrottleTimer.elapsed();
+ if (blockTime > 0) {
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "RT - blocking for " << blockTime << "ms";
+ msleep(blockTime);
+ }
+ renderThrottleTimer.restart();
+
+ wd->fireFrameSwapped();
+ } else {
+ Q_QUICK_SG_PROFILE_SKIP(QQuickProfiler::SceneGraphRenderLoopFrame, 1);
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - window not ready, skipping render");
+ }
+
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - rendering done");
+
+ if (exposeRequested) {
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - wake gui after initial expose");
+ waitCondition.wakeOne();
+ mutex.unlock();
+ }
+
+ Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphRenderLoopFrame);
+}
+
+template<class T> T *windowFor(const QVector<T> &list, QQuickWindow *window)
+{
+ for (const T &t : list) {
+ if (t.window == window)
+ return const_cast<T *>(&t);
+ }
+ return nullptr;
+}
+
+
+QSGSoftwareThreadedRenderLoop::QSGSoftwareThreadedRenderLoop()
+{
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "software threaded render loop constructor");
+ m_sg = new QSGSoftwareContext;
+ m_anim = m_sg->createAnimationDriver(this);
+ connect(m_anim, &QAnimationDriver::started, this, &QSGSoftwareThreadedRenderLoop::onAnimationStarted);
+ connect(m_anim, &QAnimationDriver::stopped, this, &QSGSoftwareThreadedRenderLoop::onAnimationStopped);
+ m_anim->install();
+}
+
+QSGSoftwareThreadedRenderLoop::~QSGSoftwareThreadedRenderLoop()
+{
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "software threaded render loop destructor");
+ delete m_sg;
+}
+
+void QSGSoftwareThreadedRenderLoop::show(QQuickWindow *window)
+{
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "show" << window;
+}
+
+void QSGSoftwareThreadedRenderLoop::hide(QQuickWindow *window)
+{
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "hide" << window;
+
+ if (window->isExposed())
+ handleObscurity(windowFor(m_windows, window));
+
+ releaseResources(window);
+}
+
+void QSGSoftwareThreadedRenderLoop::resize(QQuickWindow *window)
+{
+ if (!window->isExposed() || window->size().isEmpty())
+ return;
+
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "resize" << window << window->size();
+}
+
+void QSGSoftwareThreadedRenderLoop::windowDestroyed(QQuickWindow *window)
+{
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "window destroyed" << window;
+
+ WindowData *w = windowFor(m_windows, window);
+ if (!w)
+ return;
+
+ handleObscurity(w);
+ handleResourceRelease(w, true);
+
+ QSGSoftwareRenderThread *thread = w->thread;
+ while (thread->isRunning())
+ QThread::yieldCurrentThread();
+
+ Q_ASSERT(thread->thread() == QThread::currentThread());
+ delete thread;
+
+ for (int i = 0; i < m_windows.size(); ++i) {
+ if (m_windows.at(i).window == window) {
+ m_windows.removeAt(i);
+ break;
+ }
+ }
+}
+
+void QSGSoftwareThreadedRenderLoop::exposureChanged(QQuickWindow *window)
+{
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "exposure changed" << window;
+
+ if (window->isExposed()) {
+ handleExposure(window);
+ } else {
+ WindowData *w = windowFor(m_windows, window);
+ if (w)
+ handleObscurity(w);
+ }
+}
+
+QImage QSGSoftwareThreadedRenderLoop::grab(QQuickWindow *window)
+{
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "grab" << window;
+
+ WindowData *w = windowFor(m_windows, window);
+ // Have to support invisible (but created()'ed) windows as well.
+ // Unlike with GL, leaving that case for QQuickWindow to handle is not feasible.
+ const bool tempExpose = !w;
+ if (tempExpose) {
+ handleExposure(window);
+ w = windowFor(m_windows, window);
+ Q_ASSERT(w);
+ }
+
+ if (!w->thread->isRunning())
+ return QImage();
+
+ if (!window->handle())
+ window->create();
+
+ QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window);
+ wd->polishItems();
+
+ QImage result;
+ w->thread->mutex.lock();
+ lockedForSync = true;
+ w->thread->postEvent(new QSGSoftwareGrabEvent(window, &result));
+ w->thread->waitCondition.wait(&w->thread->mutex);
+ lockedForSync = false;
+ w->thread->mutex.unlock();
+
+ result.setDevicePixelRatio(window->effectiveDevicePixelRatio());
+
+ if (tempExpose)
+ handleObscurity(w);
+
+ return result;
+}
+
+void QSGSoftwareThreadedRenderLoop::update(QQuickWindow *window)
+{
+ WindowData *w = windowFor(m_windows, window);
+ if (!w)
+ return;
+
+ if (w->thread == QThread::currentThread()) {
+ w->thread->requestRepaint();
+ return;
+ }
+
+ // We set forceRenderPass because we want to make sure the QQuickWindow
+ // actually does a full render pass after the next sync.
+ w->forceRenderPass = true;
+ scheduleUpdate(w);
+}
+
+void QSGSoftwareThreadedRenderLoop::maybeUpdate(QQuickWindow *window)
+{
+ WindowData *w = windowFor(m_windows, window);
+ if (w)
+ scheduleUpdate(w);
+}
+
+void QSGSoftwareThreadedRenderLoop::handleUpdateRequest(QQuickWindow *window)
+{
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "handleUpdateRequest" << window;
+
+ WindowData *w = windowFor(m_windows, window);
+ if (w)
+ polishAndSync(w, false);
+}
+
+QAnimationDriver *QSGSoftwareThreadedRenderLoop::animationDriver() const
+{
+ return m_anim;
+}
+
+QSGContext *QSGSoftwareThreadedRenderLoop::sceneGraphContext() const
+{
+ return m_sg;
+}
+
+QSGRenderContext *QSGSoftwareThreadedRenderLoop::createRenderContext(QSGContext *) const
+{
+ return m_sg->createRenderContext();
+}
+
+void QSGSoftwareThreadedRenderLoop::releaseResources(QQuickWindow *window)
+{
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "releaseResources" << window;
+
+ WindowData *w = windowFor(m_windows, window);
+ if (w)
+ handleResourceRelease(w, false);
+}
+
+void QSGSoftwareThreadedRenderLoop::postJob(QQuickWindow *window, QRunnable *job)
+{
+ WindowData *w = windowFor(m_windows, window);
+ if (w && w->thread && w->thread->exposedWindow)
+ w->thread->postEvent(new QSGSoftwareJobEvent(window, job));
+ else
+ delete job;
+}
+
+QSurface::SurfaceType QSGSoftwareThreadedRenderLoop::windowSurfaceType() const
+{
+ return QSurface::RasterSurface;
+}
+
+bool QSGSoftwareThreadedRenderLoop::interleaveIncubation() const
+{
+ bool somethingVisible = false;
+ for (const WindowData &w : m_windows) {
+ if (w.window->isVisible() && w.window->isExposed()) {
+ somethingVisible = true;
+ break;
+ }
+ }
+ return somethingVisible && m_anim->isRunning();
+}
+
+int QSGSoftwareThreadedRenderLoop::flags() const
+{
+ return SupportsGrabWithoutExpose;
+}
+
+bool QSGSoftwareThreadedRenderLoop::event(QEvent *e)
+{
+ if (e->type() == QEvent::Timer) {
+ QTimerEvent *te = static_cast<QTimerEvent *>(e);
+ if (te->timerId() == animationTimer) {
+ m_anim->advance();
+ emit timeToIncubate();
+ return true;
+ }
+ }
+
+ return QObject::event(e);
+}
+
+void QSGSoftwareThreadedRenderLoop::onAnimationStarted()
+{
+ startOrStopAnimationTimer();
+
+ for (const WindowData &w : qAsConst(m_windows))
+ w.window->requestUpdate();
+}
+
+void QSGSoftwareThreadedRenderLoop::onAnimationStopped()
+{
+ startOrStopAnimationTimer();
+}
+
+void QSGSoftwareThreadedRenderLoop::startOrStopAnimationTimer()
+{
+ int exposedWindowCount = 0;
+ const WindowData *exposed = nullptr;
+
+ for (int i = 0; i < m_windows.size(); ++i) {
+ const WindowData &w(m_windows[i]);
+ if (w.window->isVisible() && w.window->isExposed()) {
+ ++exposedWindowCount;
+ exposed = &w;
+ }
+ }
+
+ if (animationTimer && (exposedWindowCount == 1 || !m_anim->isRunning())) {
+ killTimer(animationTimer);
+ animationTimer = 0;
+ // If animations are running, make sure we keep on animating
+ if (m_anim->isRunning())
+ exposed->window->requestUpdate();
+ } else if (!animationTimer && exposedWindowCount != 1 && m_anim->isRunning()) {
+ animationTimer = startTimer(qsgrl_animation_interval());
+ }
+}
+
+void QSGSoftwareThreadedRenderLoop::handleExposure(QQuickWindow *window)
+{
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "handleExposure" << window;
+
+ WindowData *w = windowFor(m_windows, window);
+ if (!w) {
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "adding window to list");
+ WindowData win;
+ win.window = window;
+ QSGRenderContext *rc = QQuickWindowPrivate::get(window)->context; // will transfer ownership
+ win.thread = new QSGSoftwareRenderThread(this, rc);
+ win.updateDuringSync = false;
+ win.forceRenderPass = true; // also covered by polishAndSync(inExpose=true), but doesn't hurt
+ m_windows.append(win);
+ w = &m_windows.last();
+ }
+
+ // set this early as we'll be rendering shortly anyway and this avoids
+ // special casing exposure in polishAndSync.
+ w->thread->exposedWindow = window;
+
+ if (w->window->size().isEmpty()
+ || (w->window->isTopLevel() && !w->window->geometry().intersects(w->window->screen()->availableGeometry()))) {
+#ifndef QT_NO_DEBUG
+ qWarning().noquote().nospace() << "QSGSotwareThreadedRenderLoop: expose event received for window "
+ << w->window << " with invalid geometry: " << w->window->geometry()
+ << " on " << w->window->screen();
+#endif
+ }
+
+ if (!w->window->handle())
+ w->window->create();
+
+ // Start render thread if it is not running
+ if (!w->thread->isRunning()) {
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "starting render thread");
+ // Push a few things to the render thread.
+ QQuickAnimatorController *controller = QQuickWindowPrivate::get(w->window)->animationController;
+ if (controller->thread() != w->thread)
+ controller->moveToThread(w->thread);
+ if (w->thread->thread() == QThread::currentThread()) {
+ w->thread->rc->moveToThread(w->thread);
+ w->thread->moveToThread(w->thread);
+ }
+
+ w->thread->active = true;
+ w->thread->start();
+
+ if (!w->thread->isRunning())
+ qFatal("Render thread failed to start, aborting application.");
+ }
+
+ polishAndSync(w, true);
+
+ startOrStopAnimationTimer();
+}
+
+void QSGSoftwareThreadedRenderLoop::handleObscurity(QSGSoftwareThreadedRenderLoop::WindowData *w)
+{
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "handleObscurity" << w->window;
+
+ if (w->thread->isRunning()) {
+ w->thread->mutex.lock();
+ w->thread->postEvent(new QSGSoftwareWindowEvent(w->window, WM_Obscure));
+ w->thread->waitCondition.wait(&w->thread->mutex);
+ w->thread->mutex.unlock();
+ }
+
+ startOrStopAnimationTimer();
+}
+
+void QSGSoftwareThreadedRenderLoop::scheduleUpdate(QSGSoftwareThreadedRenderLoop::WindowData *w)
+{
+ if (!QCoreApplication::instance())
+ return;
+
+ if (!w || !w->thread->isRunning())
+ return;
+
+ QThread *current = QThread::currentThread();
+ if (current != QCoreApplication::instance()->thread() && (current != w->thread || !lockedForSync)) {
+ qWarning() << "Updates can only be scheduled from GUI thread or from QQuickItem::updatePaintNode()";
+ return;
+ }
+
+ if (current == w->thread) {
+ w->updateDuringSync = true;
+ return;
+ }
+
+ w->window->requestUpdate();
+}
+
+void QSGSoftwareThreadedRenderLoop::handleResourceRelease(QSGSoftwareThreadedRenderLoop::WindowData *w, bool destroying)
+{
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "handleResourceRelease" << (destroying ? "destroying" : "hide/releaseResources") << w->window;
+
+ w->thread->mutex.lock();
+ if (w->thread->isRunning() && w->thread->active) {
+ QQuickWindow *window = w->window;
+
+ // Note that window->handle() is typically null by this time because
+ // the platform window is already destroyed. This should not be a
+ // problem for the D3D cleanup.
+
+ w->thread->postEvent(new QSGSoftwareTryReleaseEvent(window, destroying));
+ w->thread->waitCondition.wait(&w->thread->mutex);
+
+ // Avoid a shutdown race condition.
+ // If SG is invalidated and 'active' becomes false, the thread's run()
+ // method will exit. handleExposure() relies on QThread::isRunning() (because it
+ // potentially needs to start the thread again) and our mutex cannot be used to
+ // track the thread stopping, so we wait a few nanoseconds extra so the thread
+ // can exit properly.
+ if (!w->thread->active)
+ w->thread->wait();
+ }
+ w->thread->mutex.unlock();
+}
+
+void QSGSoftwareThreadedRenderLoop::polishAndSync(QSGSoftwareThreadedRenderLoop::WindowData *w, bool inExpose)
+{
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP) << "polishAndSync" << (inExpose ? "(in expose)" : "(normal)") << w->window;
+
+ QQuickWindow *window = w->window;
+ if (!w->thread || !w->thread->exposedWindow) {
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "polishAndSync - not exposed, abort");
+ return;
+ }
+
+ // Flush pending touch events.
+ QQuickWindowPrivate::get(window)->flushFrameSynchronousEvents();
+ // The delivery of the event might have caused the window to stop rendering
+ w = windowFor(m_windows, window);
+ if (!w || !w->thread || !w->thread->exposedWindow) {
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "polishAndSync - removed after touch event flushing, abort");
+ return;
+ }
+
+ Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphPolishAndSync);
+
+ QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window);
+ wd->polishItems();
+
+ Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphPolishAndSync);
+
+ w->updateDuringSync = false;
+
+ emit window->afterAnimating();
+
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "polishAndSync - lock for sync");
+ w->thread->mutex.lock();
+ lockedForSync = true;
+ w->thread->postEvent(new QSGSoftwareSyncEvent(window, inExpose, w->forceRenderPass));
+ w->forceRenderPass = false;
+
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "polishAndSync - wait for sync");
+
+ Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphPolishAndSync);
+ w->thread->waitCondition.wait(&w->thread->mutex);
+ lockedForSync = false;
+ w->thread->mutex.unlock();
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "polishAndSync - unlock after sync");
+
+ Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphPolishAndSync);
+
+ if (!animationTimer && m_anim->isRunning()) {
+ qCDebug(QSG_RASTER_LOG_RENDERLOOP, "polishAndSync - advancing animations");
+ m_anim->advance();
+ // We need to trigger another sync to keep animations running...
+ w->window->requestUpdate();
+ emit timeToIncubate();
+ } else if (w->updateDuringSync) {
+ w->window->requestUpdate();
+ }
+
+ Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphPolishAndSync);
+}
+
+#include "qsgsoftwarethreadedrenderloop.moc"
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop_p.h
new file mode 100644
index 0000000000..99993d651c
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop_p.h
@@ -0,0 +1,118 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGSOFTWARETHREADEDRENDERLOOP_H
+#define QSGSOFTWARETHREADEDRENDERLOOP_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qsgrenderloop_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QSGSoftwareRenderThread;
+class QSGSoftwareContext;
+
+class QSGSoftwareThreadedRenderLoop : public QSGRenderLoop
+{
+ Q_OBJECT
+public:
+ QSGSoftwareThreadedRenderLoop();
+ ~QSGSoftwareThreadedRenderLoop();
+
+ void show(QQuickWindow *window) override;
+ void hide(QQuickWindow *window) override;
+ void resize(QQuickWindow *window) override;
+ void windowDestroyed(QQuickWindow *window) override;
+ void exposureChanged(QQuickWindow *window) override;
+ QImage grab(QQuickWindow *window) override;
+ void update(QQuickWindow *window) override;
+ void maybeUpdate(QQuickWindow *window) override;
+ void handleUpdateRequest(QQuickWindow *window) override;
+ QAnimationDriver *animationDriver() const override;
+ QSGContext *sceneGraphContext() const override;
+ QSGRenderContext *createRenderContext(QSGContext *) const override;
+ void releaseResources(QQuickWindow *window) override;
+ void postJob(QQuickWindow *window, QRunnable *job) override;
+ QSurface::SurfaceType windowSurfaceType() const override;
+ bool interleaveIncubation() const override;
+ int flags() const override;
+
+ bool event(QEvent *e) override;
+
+public Q_SLOTS:
+ void onAnimationStarted();
+ void onAnimationStopped();
+
+private:
+ struct WindowData {
+ QQuickWindow *window;
+ QSGSoftwareRenderThread *thread;
+ uint updateDuringSync : 1;
+ uint forceRenderPass : 1;
+ };
+
+ void startOrStopAnimationTimer();
+ void handleExposure(QQuickWindow *window);
+ void handleObscurity(WindowData *w);
+ void scheduleUpdate(WindowData *w);
+ void handleResourceRelease(WindowData *w, bool destroying);
+ void polishAndSync(WindowData *w, bool inExpose);
+
+ QSGSoftwareContext *m_sg;
+ QAnimationDriver *m_anim;
+ int animationTimer = 0;
+ bool lockedForSync = false;
+ QVector<WindowData> m_windows;
+
+ friend class QSGSoftwareRenderThread;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGSOFTWARETHREADEDRENDERLOOP_H
diff --git a/src/quick/scenegraph/adaptations/software/software.pri b/src/quick/scenegraph/adaptations/software/software.pri
new file mode 100644
index 0000000000..de5f01cdee
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/software.pri
@@ -0,0 +1,48 @@
+QT += gui-private core-private qml-private
+
+#DEFINES += QTQUICK2D_DEBUG_FLUSH
+
+SOURCES += \
+ $$PWD/qsgsoftwarecontext.cpp \
+ $$PWD/qsgabstractsoftwarerenderer.cpp \
+ $$PWD/qsgsoftwareglyphnode.cpp \
+ $$PWD/qsgsoftwareinternalimagenode.cpp \
+ $$PWD/qsgsoftwarepublicnodes.cpp \
+ $$PWD/qsgsoftwarepainternode.cpp \
+ $$PWD/qsgsoftwareinternalrectanglenode.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 \
+ $$PWD/qsgsoftwarethreadedrenderloop.cpp
+
+HEADERS += \
+ $$PWD/qsgsoftwarecontext_p.h \
+ $$PWD/qsgabstractsoftwarerenderer_p.h \
+ $$PWD/qsgsoftwareglyphnode_p.h \
+ $$PWD/qsgsoftwareinternalimagenode_p.h \
+ $$PWD/qsgsoftwarepublicnodes_p.h \
+ $$PWD/qsgsoftwarepainternode_p.h \
+ $$PWD/qsgsoftwarepixmaprenderer_p.h \
+ $$PWD/qsgsoftwarepixmaptexture_p.h \
+ $$PWD/qsgsoftwareinternalrectanglenode_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 \
+ $$PWD/qsgsoftwarethreadedrenderloop_p.h
+
+qtConfig(quick-sprite) {
+ SOURCES += \
+ $$PWD/qsgsoftwarespritenode.cpp
+ HEADERS += \
+ $$PWD/qsgsoftwarespritenode_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..49bbbf0ba8 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;
@@ -356,17 +356,17 @@ void Updater::visitClipNode(Node *n)
if (m_roots.last() && m_added > 0)
renderer->registerBatchRoot(n, m_roots.last());
- cn->m_clip_list = m_current_clip;
+ cn->setRendererClipList(m_current_clip);
m_current_clip = cn;
m_roots << n;
m_rootMatrices.add(m_rootMatrices.last() * *m_combined_matrix_stack.last());
extra->matrix = m_rootMatrices.last();
- cn->m_matrix = &extra->matrix;
+ cn->setRendererMatrix(&extra->matrix);
m_combined_matrix_stack << &m_identityMatrix;
SHADOWNODE_TRAVERSE(n) visitNode(child);
- m_current_clip = cn->m_clip_list;
+ m_current_clip = cn->clipList();
m_rootMatrices.pop_back();
m_combined_matrix_stack.pop_back();
m_roots.pop_back();
@@ -459,8 +459,8 @@ void Updater::visitGeometryNode(Node *n)
{
QSGGeometryNode *gn = static_cast<QSGGeometryNode *>(n->sgNode);
- gn->m_matrix = m_combined_matrix_stack.last();
- gn->m_clip_list = m_current_clip;
+ gn->setRendererMatrix(m_combined_matrix_stack.last());
+ gn->setRendererClipList(m_current_clip);
gn->setInheritedOpacity(m_opacity_stack.last());
if (m_added) {
@@ -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();
}
@@ -845,7 +846,7 @@ Renderer::~Renderer()
for (int i=0; i<m_batchPool.size(); ++i) qsg_wipeBatch(m_batchPool.at(i), this);
}
- foreach (Node *n, m_nodes.values())
+ for (Node *n : qAsConst(m_nodes))
m_nodeAllocator.release(n);
// Remaining elements...
@@ -1032,11 +1033,13 @@ void Renderer::nodeWasAdded(QSGNode *node, Node *shadowParent)
m_rebuild |= FullRebuild;
} else if (node->type() == QSGNode::RenderNodeType) {
- RenderNodeElement *e = new RenderNodeElement(static_cast<QSGRenderNode *>(node));
+ QSGRenderNode *rn = static_cast<QSGRenderNode *>(node);
+ RenderNodeElement *e = new RenderNodeElement(rn);
snode->data = e;
- Q_ASSERT(!m_renderNodeElements.contains(static_cast<QSGRenderNode *>(node)));
+ Q_ASSERT(!m_renderNodeElements.contains(rn));
m_renderNodeElements.insert(e->renderNode, e);
- m_useDepthBuffer = false;
+ if (!rn->flags().testFlag(QSGRenderNode::DepthAwareRendering))
+ m_useDepthBuffer = false;
m_rebuild |= FullRebuild;
}
@@ -1101,7 +1104,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 +2763,22 @@ void Renderer::render()
m_vao->release();
}
+struct RenderNodeState : public QSGRenderNode::RenderState
+{
+ const QMatrix4x4 *projectionMatrix() const override { return m_projectionMatrix; }
+ QRect scissorRect() const override { return m_scissorRect; }
+ bool scissorEnabled() const override { return m_scissorEnabled; }
+ int stencilValue() const override { return m_stencilValue; }
+ bool stencilEnabled() const override { return m_stencilEnabled; }
+ const QRegion *clipRegion() const override { return nullptr; }
+
+ const QMatrix4x4 *m_projectionMatrix;
+ QRect m_scissorRect;
+ int m_stencilValue;
+ bool m_scissorEnabled;
+ bool m_stencilEnabled;
+};
+
void Renderer::renderRenderNode(Batch *batch)
{
if (Q_UNLIKELY(debug_render()))
@@ -2771,24 +2790,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 +2824,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 +2842,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 +2886,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..b43a2bc2ba 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, UnknownAttribute, 0 };
return a;
}
+QSGGeometry::Attribute QSGGeometry::Attribute::createWithAttributeType(int pos, int tupleSize, int primitiveType, AttributeType attributeType)
+{
+ Attribute a;
+ a.position = pos;
+ a.tupleSize = tupleSize;
+ a.type = primitiveType;
+ a.isVertexCoordinate = attributeType == PositionAttribute;
+ a.attributeType = attributeType;
+ 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::createWithAttributeType(0, 2, FloatType, PositionAttribute)
};
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::createWithAttributeType(0, 2, FloatType, PositionAttribute),
+ Attribute::createWithAttributeType(1, 2, FloatType, TexCoordAttribute)
};
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::createWithAttributeType(0, 2, FloatType, PositionAttribute),
+ Attribute::createWithAttributeType(1, 4, UnsignedByteType, ColorAttribute)
};
static AttributeSet attrs = { 2, 2 * sizeof(float) + 4 * sizeof(char), data };
return attrs;
@@ -122,12 +134,31 @@ const QSGGeometry::AttributeSet &QSGGeometry::defaultAttributes_ColoredPoint2D()
\fn QSGGeometry::Attribute QSGGeometry::Attribute::create(int pos, int tupleSize, int primitiveType, bool isPosition)
Creates a new QSGGeometry::Attribute for attribute register \a pos with \a
- tupleSize. The \a primitiveType can be any of the supported OpenGL types,
- such as \c GL_FLOAT or \c GL_UNSIGNED_BYTE.
+ tupleSize. The \a primitiveType can be any of the supported types from
+ QSGGeometry::Type, such as QSGGeometry::FloatType or
+ QSGGeometry::UnsignedByteType.
- If the attribute describes the position for the vertex, the \a isPosition hint
- should be set to \c true. The scene graph renderer may use this information
- to perform optimizations.
+ If the attribute describes the position for the vertex, the \a isPosition
+ hint should be set to \c true. The scene graph renderer may use this
+ information to perform optimizations.
+
+ \note Scene graph backends for APIs other than OpenGL may require an
+ accurate description of attributes' usage, and therefore it is recommended
+ to use createWithAttributeType() instead.
+
+ Use the create function to construct the attribute, rather than an
+ initialization list, to ensure that all fields are initialized.
+ */
+
+/*!
+ \fn QSGGeometry::Attribute QSGGeometry::Attribute::createWithAttributeType(int pos, int tupleSize, int primitiveType, AttributeType attributeType)
+
+ Creates a new QSGGeometry::Attribute for attribute register \a pos with \a
+ tupleSize. The \a primitiveType can be any of the supported types from
+ QSGGeometry::Type, such as QSGGeometry::FloatType or
+ QSGGeometry::UnsignedByteType.
+
+ \a attributeType describes the intended use of the attribute.
Use the create function to construct the attribute, rather than an
initialization list, to ensure that all fields are initialized.
@@ -206,9 +237,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 +425,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 +441,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 != UnsignedByteType
+ && indexType != UnsignedShortType
+ && indexType != UnsignedIntType) {
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 +546,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 ByteType
+ \value UnsignedByteType
+ \value ShortType
+ \value UnsignedShortType
+ \value IntType
+ \value UnsignedIntType
+ \value FloatType
+ */
+
+/*!
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 +611,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 +672,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 == UnsignedIntType || m_index_type == UnsignedShortType);
+ int indexByteSize = indexCount * (m_index_type == UnsignedShortType ? sizeof(quint16) : sizeof(quint32));
m_data = (void *) malloc(vertexByteSize + indexByteSize);
m_index_data_offset = vertexByteSize;
m_owns_data = true;
@@ -674,6 +745,27 @@ void QSGGeometry::updateTexturedRectGeometry(QSGGeometry *g, const QRectF &rect,
v[3].ty = textureRect.bottom();
}
+/*!
+ Updates the geometry \a g with the coordinates in \a rect.
+
+ The function assumes the geometry object contains a single triangle strip
+ of QSGGeometry::ColoredPoint2D vertices
+ */
+void QSGGeometry::updateColoredRectGeometry(QSGGeometry *g, const QRectF &rect)
+{
+ ColoredPoint2D *v = g->vertexDataAsColoredPoint2D();
+ v[0].x = rect.left();
+ v[0].y = rect.top();
+
+ v[1].x = rect.left();
+ v[1].y = rect.bottom();
+
+ v[2].x = rect.right();
+ v[2].y = rect.top();
+
+ v[3].x = rect.right();
+ v[3].y = rect.bottom();
+}
/*!
diff --git a/src/quick/scenegraph/coreapi/qsggeometry.h b/src/quick/scenegraph/coreapi/qsggeometry.h
index 5773b6abd1..7a916610e3 100644
--- a/src/quick/scenegraph/coreapi/qsggeometry.h
+++ b/src/quick/scenegraph/coreapi/qsggeometry.h
@@ -51,6 +51,45 @@ class QSGGeometryData;
class Q_QUICK_EXPORT QSGGeometry
{
public:
+ enum AttributeType {
+ UnknownAttribute,
+ PositionAttribute,
+ ColorAttribute,
+ TexCoordAttribute,
+ TexCoord1Attribute,
+ TexCoord2Attribute
+ };
+
+ enum DataPattern {
+ AlwaysUploadPattern = 0,
+ StreamPattern = 1,
+ DynamicPattern = 2,
+ StaticPattern = 3
+ };
+
+ // Equivalents to GL_* drawing modes.
+ // Keep in sync with GL headers.
+ enum DrawingMode {
+ DrawPoints = 0x0000,
+ DrawLines = 0x0001,
+ DrawLineLoop = 0x0002,
+ DrawLineStrip = 0x0003,
+ DrawTriangles = 0x0004,
+ DrawTriangleStrip = 0x0005,
+ DrawTriangleFan = 0x0006
+ };
+
+ // Equivalents to GL_BYTE and similar type constants.
+ // Keep in sync with GL headers.
+ enum Type {
+ ByteType = 0x1400,
+ UnsignedByteType = 0x1401,
+ ShortType = 0x1402,
+ UnsignedShortType = 0x1403,
+ IntType = 0x1404,
+ UnsignedIntType = 0x1405,
+ FloatType = 0x1406
+ };
struct Q_QUICK_EXPORT Attribute
{
@@ -60,9 +99,12 @@ public:
uint isVertexCoordinate : 1;
- uint reserved : 31;
+ AttributeType attributeType : 4;
+
+ uint reserved : 27;
static Attribute create(int pos, int tupleSize, int primitiveType, bool isPosition = false);
+ static Attribute createWithAttributeType(int pos, int tupleSize, int primitiveType, AttributeType attributeType);
};
struct AttributeSet {
@@ -97,21 +139,15 @@ public:
static const AttributeSet &defaultAttributes_TexturedPoint2D();
static const AttributeSet &defaultAttributes_ColoredPoint2D();
- enum DataPattern {
- AlwaysUploadPattern = 0,
- StreamPattern = 1,
- DynamicPattern = 2,
- StaticPattern = 3
- };
-
QSGGeometry(const QSGGeometry::AttributeSet &attribs,
int vertexCount,
int indexCount = 0,
- int indexType = GL_UNSIGNED_SHORT);
+ int indexType = UnsignedShortType);
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);
@@ -147,6 +183,7 @@ public:
static void updateRectGeometry(QSGGeometry *g, const QRectF &rect);
static void updateTexturedRectGeometry(QSGGeometry *g, const QRectF &rect, const QRectF &sourceRect);
+ static void updateColoredRectGeometry(QSGGeometry *g, const QRectF &rect);
void setIndexDataPattern(DataPattern p);
DataPattern indexDataPattern() const { return DataPattern(m_index_usage_pattern); }
@@ -187,25 +224,25 @@ private:
inline uint *QSGGeometry::indexDataAsUInt()
{
- Q_ASSERT(m_index_type == GL_UNSIGNED_INT);
+ Q_ASSERT(m_index_type == UnsignedIntType);
return static_cast<uint *>(indexData());
}
inline quint16 *QSGGeometry::indexDataAsUShort()
{
- Q_ASSERT(m_index_type == GL_UNSIGNED_SHORT);
+ Q_ASSERT(m_index_type == UnsignedShortType);
return static_cast<quint16 *>(indexData());
}
inline const uint *QSGGeometry::indexDataAsUInt() const
{
- Q_ASSERT(m_index_type == GL_UNSIGNED_INT);
+ Q_ASSERT(m_index_type == UnsignedIntType);
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 == UnsignedShortType);
return static_cast<const quint16 *>(indexData());
}
@@ -214,7 +251,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 == FloatType);
Q_ASSERT(m_attributes.attributes[0].position == 0);
return static_cast<Point2D *>(m_data);
}
@@ -225,10 +262,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 == FloatType);
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 == FloatType);
return static_cast<TexturedPoint2D *>(m_data);
}
@@ -238,10 +275,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 == FloatType);
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 == UnsignedByteType);
return static_cast<ColoredPoint2D *>(m_data);
}
@@ -250,7 +287,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 == FloatType);
Q_ASSERT(m_attributes.attributes[0].position == 0);
return static_cast<const Point2D *>(m_data);
}
@@ -261,10 +298,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 == FloatType);
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 == FloatType);
return static_cast<const TexturedPoint2D *>(m_data);
}
@@ -274,18 +311,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 == FloatType);
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 == UnsignedByteType);
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 == UnsignedShortType) return 2;
+ else if (m_index_type == UnsignedByteType) return 1;
+ else if (m_index_type == UnsignedIntType) return 4;
return 0;
}
diff --git a/src/quick/scenegraph/coreapi/qsgmaterial.cpp b/src/quick/scenegraph/coreapi/qsgmaterial.cpp
index ceb53d0d14..13598bbe1d 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,16 +64,17 @@ 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];
+ const QStringList files = m_sourceFiles[type];
QSGShaderSourceBuilder builder;
- Q_FOREACH (const QString &file, files)
+ for (const QString &file : files)
builder.appendSourceFile(file);
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;
@@ -667,7 +681,6 @@ QSGMaterial::~QSGMaterial()
the full matrix of the geometry nodes for rendering.
\value CustomCompileStep Starting with Qt 5.2, the scene graph will not always call
-
QSGMaterialShader::compile() when its shader program is compiled and linked.
Set this flag to enforce that the function is called.
diff --git a/src/quick/scenegraph/coreapi/qsgmaterial.h b/src/quick/scenegraph/coreapi/qsgmaterial.h
index 0a6a340092..114651653f 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
@@ -59,8 +63,10 @@ public:
public:
enum DirtyState
{
- DirtyMatrix = 0x0001,
- DirtyOpacity = 0x0002
+ DirtyMatrix = 0x0001,
+ DirtyOpacity = 0x0002,
+ DirtyCachedMaterialData = 0x0004,
+ DirtyAll = 0xFFFF
};
Q_DECLARE_FLAGS(DirtyStates, DirtyState)
@@ -68,6 +74,7 @@ public:
inline bool isMatrixDirty() const { return m_dirty & DirtyMatrix; }
inline bool isOpacityDirty() const { return m_dirty & DirtyOpacity; }
+ bool isCachedMaterialDataDirty() const { return m_dirty & DirtyCachedMaterialData; }
float opacity() const;
QMatrix4x4 combinedMatrix() const;
@@ -77,9 +84,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 +101,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 53798f0e07..a1e1ef8c27 100644
--- a/src/quick/scenegraph/coreapi/qsgnode.cpp
+++ b/src/quick/scenegraph/coreapi/qsgnode.cpp
@@ -778,6 +778,18 @@ QSGBasicGeometryNode::~QSGBasicGeometryNode()
\internal
*/
+/*!
+ \fn void QSGBasicGeometryNode::setRendererMatrix(const QMatrix4x4 *m)
+
+ \internal
+ */
+
+/*!
+ \fn void QSGBasicGeometryNode::setRendererClipList(const QSGClipNode *c)
+
+ \internal
+ */
+
/*!
Sets the geometry of this node to \a geometry.
@@ -1256,7 +1268,7 @@ QSGRootNode::QSGRootNode()
QSGRootNode::~QSGRootNode()
{
while (!m_renderers.isEmpty())
- m_renderers.last()->setRootNode(0);
+ m_renderers.constLast()->setRootNode(0);
destroy(); // Must call destroy() here because markDirty() casts this to QSGRootNode.
}
@@ -1454,8 +1466,6 @@ void QSGNodeVisitor::visitChildren(QSGNode *n)
visitNode(c);
}
-
-
#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug d, const QSGGeometryNode *n)
{
@@ -1472,15 +1482,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::FloatType) {
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..f7ea6dbe23 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,13 +204,15 @@ public:
const QMatrix4x4 *matrix() const { return m_matrix; }
const QSGClipNode *clipList() const { return m_clip_list; }
+ void setRendererMatrix(const QMatrix4x4 *m) { m_matrix = m; }
+ void setRendererClipList(const QSGClipNode *c) { m_clip_list = c; }
+
protected:
QSGBasicGeometryNode(NodeType type);
QSGBasicGeometryNode(QSGBasicGeometryNodePrivate &dd, NodeType type);
private:
friend class QSGNodeUpdater;
- friend class QSGBatchRenderer::Updater;
QSGGeometry *m_geometry;
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..fa543aecad
--- /dev/null
+++ b/src/quick/scenegraph/coreapi/qsgrendererinterface.cpp
@@ -0,0 +1,197 @@
+/****************************************************************************
+**
+** 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.
+
+ QSGRendererInterface's functions have varying availability. API and
+ language queries, like graphicsApi() or shaderType() are always available,
+ meaning it is sufficient to construct a QQuickWindow or QQuickView, and the
+ graphics API or shading language in use can be queried right after via
+ QQuickWindow::rendererInterface(). This guarantees that utilities like the
+ GraphicsInfo QML type are able to report the correct values as early as
+ possible, without having conditional property values - depending on for
+ instance shaderType() - evaluate to unexpected values.
+
+ Engine-specific accessors, like getResource(), are however available only
+ after the scenegraph is initialized. Additionally, there may be
+ backend-specific limitations on when such 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 QSGRendererInterface::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
+ */
+
+/*!
+ \enum QSGRendererInterface::Resource
+ \value Device The graphics device, when applicable.
+ \value CommandQueue The graphics command queue used by the scenegraph, when applicable.
+ \value CommandList The command list or buffer used by the scenegraph, when applicable.
+ \value Painter The active QPainter used by the scenegraph, when running with the software backend.
+ */
+
+/*!
+ \enum QSGRendererInterface::ShaderType
+ \value UnknownShadingLanguage Not yet known due to no window and scenegraph associated
+ \value GLSL GLSL or GLSL ES
+ \value HLSL HLSL
+ */
+
+/*!
+ \enum QSGRendererInterface::ShaderCompilationType
+ \value RuntimeCompilation Runtime compilation of shader source code is supported
+ \value OfflineCompilation Pre-compiled bytecode supported
+ */
+
+/*!
+ \enum QSGRendererInterface::ShaderSourceType
+
+ \value ShaderSourceString Shader source can be provided as a string in
+ the corresponding properties of ShaderEffect
+
+ \value ShaderSourceFile Local or resource files containing shader source
+ code are supported
+
+ \value ShaderByteCode Local or resource files containing shader bytecode are
+ supported
+ */
+
+QSGRendererInterface::~QSGRendererInterface()
+{
+}
+
+/*!
+ \fn QSGRendererInterface::GraphicsApi QSGRendererInterface::graphicsApi() const
+
+ Returns the graphics API that is in use by the Qt Quick scenegraph.
+
+ \note This function can be called on any thread.
+ */
+
+/*!
+ 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 The ownership of the returned pointer is never transferred to the caller.
+
+ \note This function must only be called on the render thread.
+ */
+void *QSGRendererInterface::getResource(QQuickWindow *window, Resource resource) const
+{
+ Q_UNUSED(window);
+ 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 The ownership of the returned pointer is never transferred to the caller.
+
+ \note This function must only be called on the render thread.
+ */
+void *QSGRendererInterface::getResource(QQuickWindow *window, const char *resource) const
+{
+ Q_UNUSED(window);
+ Q_UNUSED(resource);
+ return nullptr;
+}
+
+/*!
+ \fn QSGRendererInterface::ShaderType QSGRendererInterface::shaderType() const
+
+ \return the shading language supported by the Qt Quick backend the
+ application is using.
+
+ \note This function can be called on any thread.
+
+ \sa QtQuick::GraphicsInfo
+ */
+
+/*!
+ \fn QSGRendererInterface::ShaderCompilationTypes QSGRendererInterface::shaderCompilationType() const
+
+ \return a bitmask of the shader compilation approaches supported by the Qt
+ Quick backend the application is using.
+
+ \note This function can be called on any thread.
+
+ \sa QtQuick::GraphicsInfo
+ */
+
+/*!
+ \fn QSGRendererInterface::ShaderSourceTypes QSGRendererInterface::shaderSourceType() const
+
+ \return a bitmask of the supported ways of providing shader sources in ShaderEffect items.
+
+ \note This function can be called on any thread.
+
+ \sa QtQuick::GraphicsInfo
+ */
+
+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..a50b362aeb
--- /dev/null
+++ b/src/quick/scenegraph/coreapi/qsgrendererinterface.h
@@ -0,0 +1,102 @@
+/****************************************************************************
+**
+** 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 QQuickWindow;
+
+class Q_QUICK_EXPORT QSGRendererInterface
+{
+public:
+ enum GraphicsApi {
+ Unknown,
+ Software,
+ OpenGL,
+ Direct3D12
+ };
+
+ enum Resource {
+ Device,
+ CommandQueue,
+ CommandList,
+ Painter
+ };
+
+ enum ShaderType {
+ UnknownShadingLanguage,
+ GLSL,
+ HLSL
+ };
+
+ enum ShaderCompilationType {
+ RuntimeCompilation = 0x01,
+ OfflineCompilation = 0x02
+ };
+ Q_DECLARE_FLAGS(ShaderCompilationTypes, ShaderCompilationType)
+
+ enum ShaderSourceType {
+ ShaderSourceString = 0x01,
+ ShaderSourceFile = 0x02,
+ ShaderByteCode = 0x04
+ };
+ Q_DECLARE_FLAGS(ShaderSourceTypes, ShaderSourceType)
+
+ virtual ~QSGRendererInterface();
+
+ virtual GraphicsApi graphicsApi() const = 0;
+
+ virtual void *getResource(QQuickWindow *window, Resource resource) const;
+ virtual void *getResource(QQuickWindow *window, const char *resource) const;
+
+ virtual ShaderType shaderType() const = 0;
+ virtual ShaderCompilationTypes shaderCompilationType() const = 0;
+ virtual ShaderSourceTypes shaderSourceType() const = 0;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QSGRendererInterface::ShaderCompilationTypes)
+Q_DECLARE_OPERATORS_FOR_FLAGS(QSGRendererInterface::ShaderSourceTypes)
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/quick/scenegraph/coreapi/qsgrendernode.cpp b/src/quick/scenegraph/coreapi/qsgrendernode.cpp
index 1a38f6495e..5915d51f2b 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 QSGRenderNode 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,71 @@ 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.
+
+ 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.
- \internal
+ The software backend exposes its QPainter and saves and restores before and
+ after invoking render(). Therefore reporting any changed states from here
+ is not necessary.
+
+ \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.
-
- 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.
+ 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 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:
+ Clip information is calculated before the function is called, it is however
+ not enabled. Implementations wishing to take clipping into account can set
+ up scissoring or stencil based on the information in \a state. Some
+ scenegraph backends, software in particular, use no scissor or stencil.
+ There the clip region is provided as an ordinary QRegion.
+
+ 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 +171,209 @@ 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 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.
+
+ \note Depth writes are disabled when this function is called (for example,
+ glDepthMask(false) in case of OpenGL). Enabling depth writes can lead to
+ unexpected results, depending on the scenegraph backend in use, so nodes
+ should avoid this.
- \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.
+ 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.
- \internal
+ \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()
+{
+}
+
+/*!
+ \enum QSGRenderNode::RenderingFlag
+
+ Possible values for the bitmask returned from flags().
+
+ \value BoundedRectRendering Indicates that the implementation of render()
+ does not render outside the area reported from rect() in item
+ coordinates. Such node implementations can lead to more efficient rendering,
+ depending on the scenegraph backend. For example, the software backend can
+ continue to use the more optimal partial update path when all render nodes
+ in the scene have this flag set.
+
+ \value DepthAwareRendering Indicates that the implementations of render()
+ conforms to scenegraph expectations by only generating a Z value of 0 in
+ scene coordinates which is then transformed by the matrices retrieved from
+ RenderState::projectionMatrix() and matrix(), as described in the notes for
+ render(). Such node implementations can lead to more efficient rendering,
+ depending on the scenegraph backend. For example, the batching OpenGL
+ renderer can continue to use a more optimal path when all render nodes in
+ the scene have this flag set.
+
+ \value OpaqueRendering Indicates that the implementation of render() writes
+ out opaque pixels for the entire area reported from rect(). By default the
+ renderers must assume that render() can also output semi or fully
+ transparent pixels. Setting this flag can improve performance in some
+ cases.
+
+ \sa render(), rect()
+ */
+
+/*!
+ \return flags describing the behavior of this render node.
+
+ The default implementation returns 0.
+
+ \sa RenderingFlag, rect()
+ */
+QSGRenderNode::RenderingFlags QSGRenderNode::flags() const
+{
+ return 0;
+}
+
+/*!
+ \return the bounding rectangle in item coordinates for the area render()
+ touches. The value is only in use when flags() includes
+ BoundedRectRendering, ignored otherwise.
+
+ Reporting the rectangle in combination with BoundedRectRendering is
+ particularly important with the \c software backend because otherwise
+ having a rendernode in the scene would trigger fullscreen updates, skipping
+ all partial update optimizations.
+
+ For rendernodes covering the entire area of a corresponding QQuickItem the
+ return value will be (0, 0, item->width(), item->height()).
+
+ \sa flags()
+*/
+QRectF QSGRenderNode::rect() const
+{
+ return QRectF();
+}
+
+/*!
+ \return pointer to the current model-view matrix.
+ */
+const QMatrix4x4 *QSGRenderNode::matrix() const
+{
+ 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.
+ */
+
+/*!
+ \fn const QRegion *QSGRenderNode::clipRegion() const
+
+ \return the current clip region or null for backends where clipping is
+ implemented via stencil or scissoring.
+
+ The software backend uses no projection, scissor or stencil, meaning most
+ of the render state is not in use. However, the clip region that can be set
+ on the QPainter still has to be communicated since reconstructing this
+ manually in render() is not reasonable. It can therefore be queried via
+ this function.
+ */
+
+/*!
+ \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..f6bc40d3ee
--- /dev/null
+++ b/src/quick/scenegraph/coreapi/qsgrendernode.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 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)
+
+ enum RenderingFlag {
+ BoundedRectRendering = 0x01,
+ DepthAwareRendering = 0x02,
+ OpaqueRendering = 0x04
+ };
+ Q_DECLARE_FLAGS(RenderingFlags, RenderingFlag)
+
+ struct Q_QUICK_EXPORT RenderState {
+ virtual ~RenderState();
+ virtual const QMatrix4x4 *projectionMatrix() const = 0;
+ virtual QRect scissorRect() const = 0;
+ virtual bool scissorEnabled() const = 0;
+ virtual int stencilValue() const = 0;
+ virtual bool stencilEnabled() const = 0;
+ virtual const QRegion *clipRegion() 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();
+ virtual RenderingFlags flags() const;
+ virtual QRectF rect() const;
+
+ const QMatrix4x4 *matrix() const;
+ const QSGClipNode *clipList() const;
+ qreal inheritedOpacity() const;
+
+private:
+ QSGRenderNodePrivate *d;
+ friend class QSGRenderNodePrivate;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QSGRenderNode::StateFlags)
+Q_DECLARE_OPERATORS_FOR_FLAGS(QSGRenderNode::RenderingFlags)
+
+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/coreapi/qsgshaderrewriter.cpp b/src/quick/scenegraph/coreapi/qsgshaderrewriter.cpp
index 97d7e69407..3a35632d5c 100644
--- a/src/quick/scenegraph/coreapi/qsgshaderrewriter.cpp
+++ b/src/quick/scenegraph/coreapi/qsgshaderrewriter.cpp
@@ -89,9 +89,6 @@ void Tokenizer::initialize(const char *input)
identifier = input;
}
-#define foo
-
-
Tokenizer::Token Tokenizer::next()
{
while (*pos != 0) {
diff --git a/src/quick/scenegraph/qsgadaptationlayer.cpp b/src/quick/scenegraph/qsgadaptationlayer.cpp
index 682423726e..9923fa6e24 100644
--- a/src/quick/scenegraph/qsgadaptationlayer.cpp
+++ b/src/quick/scenegraph/qsgadaptationlayer.cpp
@@ -46,6 +46,7 @@
#include <private/qrawfont_p.h>
#include <QtGui/qguiapplication.h>
#include <qdir.h>
+#include <qsgrendernode.h>
#include <private/qquickprofiler_p.h>
#include <QElapsedTimer>
@@ -65,15 +66,18 @@ QSGDistanceFieldGlyphCache::QSGDistanceFieldGlyphCache(QSGDistanceFieldGlyphCach
QRawFontPrivate *fontD = QRawFontPrivate::get(font);
m_glyphCount = fontD->fontEngine->glyphCount();
- m_doubleGlyphResolution = qt_fontHasNarrowOutlines(font) && m_glyphCount < QT_DISTANCEFIELD_HIGHGLYPHCOUNT;
+ m_doubleGlyphResolution = qt_fontHasNarrowOutlines(font) && m_glyphCount < QT_DISTANCEFIELD_HIGHGLYPHCOUNT();
m_referenceFont = font;
// we set the same pixel size as used by the distance field internally.
// 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()
@@ -118,7 +122,7 @@ void QSGDistanceFieldGlyphCache::populate(const QVector<glyph_t> &glyphs)
int count = glyphs.count();
for (int i = 0; i < count; ++i) {
glyph_t glyphIndex = glyphs.at(i);
- if ((int) glyphIndex >= glyphCount()) {
+ if ((int) glyphIndex >= glyphCount() && glyphCount() > 0) {
qWarning("Warning: distance-field glyph is not available with index %d", glyphIndex);
continue;
}
@@ -192,7 +196,7 @@ void QSGDistanceFieldGlyphCache::update()
storeGlyphs(distanceFields);
#if defined(QSG_DISTANCEFIELD_CACHE_DEBUG)
- foreach (Texture texture, m_textures)
+ for (Texture texture : qAsConst(m_textures))
saveTexture(texture.textureId, texture.size.width(), texture.size.height());
#endif
@@ -291,7 +295,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) {
@@ -509,6 +513,13 @@ void QSGNodeVisitorEx::visitChildren(QSGNode *node)
visitChildren(child);
break;
}
+ case QSGNode::RenderNodeType: {
+ QSGRenderNode *r = static_cast<QSGRenderNode*>(child);
+ if (visit(r))
+ visitChildren(r);
+ endVisit(r);
+ break;
+ }
default:
Q_UNREACHABLE();
break;
@@ -516,4 +527,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..5880c67af0 100644
--- a/src/quick/scenegraph/qsgadaptationlayer_p.h
+++ b/src/quick/scenegraph/qsgadaptationlayer_p.h
@@ -76,12 +76,13 @@ class TextureReference;
class QSGDistanceFieldGlyphCacheManager;
class QSGDistanceFieldGlyphNode;
class QOpenGLContext;
-class QSGImageNode;
+class QSGInternalImageNode;
class QSGPainterNode;
-class QSGRectangleNode;
+class QSGInternalRectangleNode;
class QSGGlyphNode;
-class QSGNinePatchNode;
class QSGRootNode;
+class QSGSpriteNode;
+class QSGRenderNode;
class Q_QUICK_PRIVATE_EXPORT QSGNodeVisitorEx
{
@@ -97,18 +98,22 @@ public:
virtual void endVisit(QSGGeometryNode *) = 0;
virtual bool visit(QSGOpacityNode *) = 0;
virtual void endVisit(QSGOpacityNode *) = 0;
- virtual bool visit(QSGImageNode *) = 0;
- virtual void endVisit(QSGImageNode *) = 0;
+ virtual bool visit(QSGInternalImageNode *) = 0;
+ virtual void endVisit(QSGInternalImageNode *) = 0;
virtual bool visit(QSGPainterNode *) = 0;
virtual void endVisit(QSGPainterNode *) = 0;
- virtual bool visit(QSGRectangleNode *) = 0;
- virtual void endVisit(QSGRectangleNode *) = 0;
+ virtual bool visit(QSGInternalRectangleNode *) = 0;
+ virtual void endVisit(QSGInternalRectangleNode *) = 0;
virtual bool visit(QSGGlyphNode *) = 0;
virtual void endVisit(QSGGlyphNode *) = 0;
- virtual bool visit(QSGNinePatchNode *) = 0;
- virtual void endVisit(QSGNinePatchNode *) = 0;
virtual bool visit(QSGRootNode *) = 0;
virtual void endVisit(QSGRootNode *) = 0;
+#if QT_CONFIG(quick_sprite)
+ virtual bool visit(QSGSpriteNode *) = 0;
+ virtual void endVisit(QSGSpriteNode *) = 0;
+#endif
+ virtual bool visit(QSGRenderNode *) = 0;
+ virtual void endVisit(QSGRenderNode *) = 0;
void visitChildren(QSGNode *node);
};
@@ -122,7 +127,7 @@ public:
virtual void accept(QSGNodeVisitorEx *) = 0;
};
-class Q_QUICK_PRIVATE_EXPORT QSGRectangleNode : public QSGVisitableNode
+class Q_QUICK_PRIVATE_EXPORT QSGInternalRectangleNode : public QSGVisitableNode
{
public:
virtual void setRect(const QRectF &rect) = 0;
@@ -140,7 +145,7 @@ public:
};
-class Q_QUICK_PRIVATE_EXPORT QSGImageNode : public QSGVisitableNode
+class Q_QUICK_PRIVATE_EXPORT QSGInternalImageNode : public QSGVisitableNode
{
public:
virtual void setTargetRect(const QRectF &rect) = 0;
@@ -186,19 +191,6 @@ public:
virtual void accept(QSGNodeVisitorEx *visitor) { if (visitor->visit(this)) visitor->visitChildren(this); visitor->endVisit(this); }
};
-class Q_QUICK_PRIVATE_EXPORT QSGNinePatchNode : public QSGVisitableNode
-{
-public:
- virtual void setTexture(QSGTexture *texture) = 0;
- virtual void setBounds(const QRectF &bounds) = 0;
- virtual void setDevicePixelRatio(qreal ratio) = 0;
- virtual void setPadding(qreal left, qreal top, qreal right, qreal bottom) = 0;
-
- virtual void update() = 0;
-
- virtual void accept(QSGNodeVisitorEx *visitor) { if (visitor->visit(this)) visitor->visitChildren(this); visitor->endVisit(this); }
-};
-
class Q_QUICK_EXPORT QSGLayer : public QSGDynamicTexture
{
Q_OBJECT
@@ -210,7 +202,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 +215,152 @@ Q_SIGNALS:
void scheduledUpdateCompleted();
};
+#if QT_CONFIG(quick_sprite)
+
+class Q_QUICK_PRIVATE_EXPORT QSGSpriteNode : public QSGVisitableNode
+{
+public:
+ virtual void setTexture(QSGTexture *texture) = 0;
+ virtual void setTime(float time) = 0;
+ virtual void setSourceA(const QPoint &source) = 0;
+ virtual void setSourceB(const QPoint &source) = 0;
+ virtual void setSpriteSize(const QSize &size) = 0;
+ virtual void setSheetSize(const QSize &size) = 0;
+ virtual void setSize(const QSizeF &size) = 0;
+ virtual void setFiltering(QSGTexture::Filtering filtering) = 0;
+
+ virtual void update() = 0;
+
+ virtual void accept(QSGNodeVisitorEx *visitor) { if (visitor->visit(this)) visitor->visitChildren(this); visitor->endVisit(this); }
+};
+
+#endif
+
+class Q_QUICK_PRIVATE_EXPORT QSGGuiThreadShaderEffectManager : public QObject
+{
+ Q_OBJECT
+
+public:
+ enum Status {
+ Compiled,
+ Uncompiled,
+ Error
+ };
+
+ 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 void prepareShaderCode(ShaderInfo::Type typeHint, const QByteArray &src, ShaderInfo *result) = 0;
+
+Q_SIGNALS:
+ void shaderCodePrepared(bool ok, ShaderInfo::Type typeHint, const QByteArray &src, ShaderInfo *result);
+ 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 +433,7 @@ public:
};
struct Texture {
- GLuint textureId;
+ uint textureId;
QSize size;
Texture() : textureId(0), size(QSize()) { }
@@ -359,10 +497,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);
@@ -412,7 +550,8 @@ inline bool QSGDistanceFieldGlyphCache::containsGlyph(glyph_t glyph)
return glyphData(glyph).texCoord.isValid();
}
-
QT_END_NAMESPACE
+Q_DECLARE_METATYPE(QSGGuiThreadShaderEffectManager::ShaderInfo::Type)
+
#endif
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/qsgbasicinternalimagenode.cpp b/src/quick/scenegraph/qsgbasicinternalimagenode.cpp
new file mode 100644
index 0000000000..c8699218ba
--- /dev/null
+++ b/src/quick/scenegraph/qsgbasicinternalimagenode.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 "qsgbasicinternalimagenode_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::createWithAttributeType(0, 2, QSGGeometry::FloatType, QSGGeometry::PositionAttribute),
+ QSGGeometry::Attribute::createWithAttributeType(1, 2, QSGGeometry::FloatType, QSGGeometry::TexCoordAttribute),
+ QSGGeometry::Attribute::createWithAttributeType(2, 2, QSGGeometry::FloatType, QSGGeometry::TexCoord1Attribute),
+ QSGGeometry::Attribute::createWithAttributeType(3, 2, QSGGeometry::FloatType, QSGGeometry::TexCoord2Attribute)
+ };
+ static QSGGeometry::AttributeSet attrs = { 4, sizeof(SmoothVertex), data };
+ return attrs;
+ }
+}
+
+QSGBasicInternalImageNode::QSGBasicInternalImageNode()
+ : 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("internalimage"));
+#endif
+}
+
+void QSGBasicInternalImageNode::setTargetRect(const QRectF &rect)
+{
+ if (rect == m_targetRect)
+ return;
+ m_targetRect = rect;
+ m_dirtyGeometry = true;
+}
+
+void QSGBasicInternalImageNode::setInnerTargetRect(const QRectF &rect)
+{
+ if (rect == m_innerTargetRect)
+ return;
+ m_innerTargetRect = rect;
+ m_dirtyGeometry = true;
+}
+
+void QSGBasicInternalImageNode::setInnerSourceRect(const QRectF &rect)
+{
+ if (rect == m_innerSourceRect)
+ return;
+ m_innerSourceRect = rect;
+ m_dirtyGeometry = true;
+}
+
+void QSGBasicInternalImageNode::setSubSourceRect(const QRectF &rect)
+{
+ if (rect == m_subSourceRect)
+ return;
+ m_subSourceRect = rect;
+ m_dirtyGeometry = true;
+}
+
+void QSGBasicInternalImageNode::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 QSGBasicInternalImageNode::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 QSGBasicInternalImageNode::setMirror(bool mirror)
+{
+ if (mirror == m_mirror)
+ return;
+ m_mirror = mirror;
+ m_dirtyGeometry = true;
+}
+
+
+void QSGBasicInternalImageNode::update()
+{
+ if (m_dirtyGeometry)
+ updateGeometry();
+}
+
+void QSGBasicInternalImageNode::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 *QSGBasicInternalImageNode::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::UnsignedShortType);
+ } 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 QSGBasicInternalImageNode::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/qsgbasicinternalimagenode_p.h b/src/quick/scenegraph/qsgbasicinternalimagenode_p.h
new file mode 100644
index 0000000000..a5689b20aa
--- /dev/null
+++ b/src/quick/scenegraph/qsgbasicinternalimagenode_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 QSGBASICINTERNALIMAGENODE_P_H
+#define QSGBASICINTERNALIMAGENODE_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 QSGBasicInternalImageNode : public QSGInternalImageNode
+{
+public:
+ QSGBasicInternalImageNode();
+
+ 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/qsgdefaultrectanglenode.cpp b/src/quick/scenegraph/qsgbasicinternalrectanglenode.cpp
index 5ef52e8722..df124cce35 100644
--- a/src/quick/scenegraph/qsgdefaultrectanglenode.cpp
+++ b/src/quick/scenegraph/qsgbasicinternalrectanglenode.cpp
@@ -1,4 +1,3 @@
-
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
@@ -38,17 +37,9 @@
**
****************************************************************************/
-
-
-#include "qsgdefaultrectanglenode_p.h"
-
-#include <QtQuick/qsgvertexcolormaterial.h>
-#include <QtQuick/qsgtexturematerial.h>
-
-#include <QtQuick/private/qsgcontext_p.h>
+#include "qsgbasicinternalrectanglenode_p.h"
#include <QtCore/qmath.h>
-#include <QtCore/qvarlengtharray.h>
QT_BEGIN_NAMESPACE
@@ -96,95 +87,16 @@ namespace
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)
+ QSGGeometry::Attribute::createWithAttributeType(0, 2, QSGGeometry::FloatType, QSGGeometry::PositionAttribute),
+ QSGGeometry::Attribute::createWithAttributeType(1, 4, QSGGeometry::UnsignedByteType, QSGGeometry::ColorAttribute),
+ QSGGeometry::Attribute::createWithAttributeType(2, 2, QSGGeometry::FloatType, QSGGeometry::TexCoordAttribute)
};
static QSGGeometry::AttributeSet attrs = { 3, sizeof(SmoothVertex), data };
return attrs;
}
}
-class SmoothColorMaterialShader : public QSGMaterialShader
-{
-public:
- SmoothColorMaterialShader();
-
- virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect);
- virtual char const *const *attributeNames() const;
-
-private:
- virtual void initialize();
-
- int m_matrixLoc;
- int m_opacityLoc;
- int m_pixelSizeLoc;
-};
-
-SmoothColorMaterialShader::SmoothColorMaterialShader()
- : QSGMaterialShader()
-{
- setShaderSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/qt-project.org/scenegraph/shaders/smoothcolor.vert"));
- setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/scenegraph/shaders/smoothcolor.frag"));
-}
-
-void SmoothColorMaterialShader::updateState(const RenderState &state, QSGMaterial *, QSGMaterial *oldEffect)
-{
- if (state.isOpacityDirty())
- program()->setUniformValue(m_opacityLoc, state.opacity());
-
- if (state.isMatrixDirty())
- program()->setUniformValue(m_matrixLoc, state.combinedMatrix());
-
- if (oldEffect == 0) {
- // The viewport is constant, so set the pixel size uniform only once.
- QRect r = state.viewportRect();
- program()->setUniformValue(m_pixelSizeLoc, 2.0f / r.width(), 2.0f / r.height());
- }
-}
-
-char const *const *SmoothColorMaterialShader::attributeNames() const
-{
- static char const *const attributes[] = {
- "vertex",
- "vertexColor",
- "vertexOffset",
- 0
- };
- return attributes;
-}
-
-void SmoothColorMaterialShader::initialize()
-{
- m_matrixLoc = program()->uniformLocation("matrix");
- m_opacityLoc = program()->uniformLocation("opacity");
- m_pixelSizeLoc = program()->uniformLocation("pixelSize");
-}
-
-QSGSmoothColorMaterial::QSGSmoothColorMaterial()
-{
- setFlag(RequiresFullMatrixExceptTranslate, true);
- setFlag(Blending, true);
-}
-
-int QSGSmoothColorMaterial::compare(const QSGMaterial *) const
-{
- return 0;
-}
-
-QSGMaterialType *QSGSmoothColorMaterial::type() const
-{
- static QSGMaterialType type;
- return &type;
-}
-
-QSGMaterialShader *QSGSmoothColorMaterial::createShader() const
-{
- return new SmoothColorMaterialShader;
-}
-
-
-QSGDefaultRectangleNode::QSGDefaultRectangleNode()
+QSGBasicInternalRectangleNode::QSGBasicInternalRectangleNode()
: m_radius(0)
, m_pen_width(0)
, m_aligned(true)
@@ -194,14 +106,13 @@ QSGDefaultRectangleNode::QSGDefaultRectangleNode()
, m_geometry(QSGGeometry::defaultAttributes_ColoredPoint2D(), 0)
{
setGeometry(&m_geometry);
- setMaterial(&m_material);
#ifdef QSG_RUNTIME_DESCRIPTION
- qsgnode_set_description(this, QLatin1String("rectangle"));
+ qsgnode_set_description(this, QLatin1String("internalrectangle"));
#endif
}
-void QSGDefaultRectangleNode::setRect(const QRectF &rect)
+void QSGBasicInternalRectangleNode::setRect(const QRectF &rect)
{
if (rect == m_rect)
return;
@@ -209,7 +120,7 @@ void QSGDefaultRectangleNode::setRect(const QRectF &rect)
m_dirty_geometry = true;
}
-void QSGDefaultRectangleNode::setColor(const QColor &color)
+void QSGBasicInternalRectangleNode::setColor(const QColor &color)
{
if (color == m_color)
return;
@@ -218,7 +129,7 @@ void QSGDefaultRectangleNode::setColor(const QColor &color)
m_dirty_geometry = true;
}
-void QSGDefaultRectangleNode::setPenColor(const QColor &color)
+void QSGBasicInternalRectangleNode::setPenColor(const QColor &color)
{
if (color == m_border_color)
return;
@@ -227,7 +138,7 @@ void QSGDefaultRectangleNode::setPenColor(const QColor &color)
m_dirty_geometry = true;
}
-void QSGDefaultRectangleNode::setPenWidth(qreal width)
+void QSGBasicInternalRectangleNode::setPenWidth(qreal width)
{
if (width == m_pen_width)
return;
@@ -236,7 +147,7 @@ void QSGDefaultRectangleNode::setPenWidth(qreal width)
}
-void QSGDefaultRectangleNode::setGradientStops(const QGradientStops &stops)
+void QSGBasicInternalRectangleNode::setGradientStops(const QGradientStops &stops)
{
if (stops.constData() == m_gradient_stops.constData())
return;
@@ -249,7 +160,7 @@ void QSGDefaultRectangleNode::setGradientStops(const QGradientStops &stops)
m_dirty_geometry = true;
}
-void QSGDefaultRectangleNode::setRadius(qreal radius)
+void QSGBasicInternalRectangleNode::setRadius(qreal radius)
{
if (radius == m_radius)
return;
@@ -257,24 +168,26 @@ void QSGDefaultRectangleNode::setRadius(qreal radius)
m_dirty_geometry = true;
}
-void QSGDefaultRectangleNode::setAntialiasing(bool antialiasing)
+void QSGBasicInternalRectangleNode::setAntialiasing(bool antialiasing)
{
+ if (!supportsAntialiasing())
+ return;
+
if (antialiasing == m_antialiasing)
return;
m_antialiasing = antialiasing;
if (m_antialiasing) {
- setMaterial(&m_smoothMaterial);
setGeometry(new QSGGeometry(smoothAttributeSet(), 0));
setFlag(OwnsGeometry, true);
} else {
- setMaterial(&m_material);
setGeometry(&m_geometry);
setFlag(OwnsGeometry, false);
}
+ updateMaterialAntialiasing();
m_dirty_geometry = true;
}
-void QSGDefaultRectangleNode::setAligned(bool aligned)
+void QSGBasicInternalRectangleNode::setAligned(bool aligned)
{
if (aligned == m_aligned)
return;
@@ -282,30 +195,19 @@ void QSGDefaultRectangleNode::setAligned(bool aligned)
m_dirty_geometry = true;
}
-void QSGDefaultRectangleNode::update()
+void QSGBasicInternalRectangleNode::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;
- }
- }
-
+ updateMaterialBlending(&state);
markDirty(state);
}
}
-void QSGDefaultRectangleNode::updateGeometry()
+void QSGBasicInternalRectangleNode::updateGeometry()
{
float width = float(m_rect.width());
float height = float(m_rect.height());
@@ -315,7 +217,7 @@ void QSGDefaultRectangleNode::updateGeometry()
penWidth = qRound(penWidth);
QSGGeometry *g = geometry();
- g->setDrawingMode(GL_TRIANGLE_STRIP);
+ g->setDrawingMode(QSGGeometry::DrawTriangleStrip);
int vertexStride = g->sizeOfVertex();
union {
@@ -782,5 +684,4 @@ void QSGDefaultRectangleNode::updateGeometry()
}
}
-
QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/qsgdefaultrectanglenode_p.h b/src/quick/scenegraph/qsgbasicinternalrectanglenode_p.h
index 4cfe921127..98e53669ce 100644
--- a/src/quick/scenegraph/qsgdefaultrectanglenode_p.h
+++ b/src/quick/scenegraph/qsgbasicinternalrectanglenode_p.h
@@ -38,8 +38,8 @@
****************************************************************************/
-#ifndef QSGDEFAULTRECTANGLENODE_P_H
-#define QSGDEFAULTRECTANGLENODE_P_H
+#ifndef QSGBASICINTERNALRECTANGLENODE_P_H
+#define QSGBASICINTERNALRECTANGLENODE_P_H
//
// W A R N I N G
@@ -54,46 +54,31 @@
#include <private/qsgadaptationlayer_p.h>
-#include <QtQuick/qsgvertexcolormaterial.h>
-
QT_BEGIN_NAMESPACE
-class QSGContext;
-
-class Q_QUICK_PRIVATE_EXPORT QSGSmoothColorMaterial : public QSGMaterial
+class Q_QUICK_PRIVATE_EXPORT QSGBasicInternalRectangleNode : public QSGInternalRectangleNode
{
public:
- QSGSmoothColorMaterial();
-
- int compare(const QSGMaterial *other) const;
+ QSGBasicInternalRectangleNode();
+
+ 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 QSGMaterialType *type() const;
- virtual QSGMaterialShader *createShader() const;
-};
+ virtual bool supportsAntialiasing() const { return true; }
+ virtual void updateMaterialAntialiasing() = 0;
+ virtual void updateMaterialBlending(QSGNode::DirtyState *state) = 0;
-class Q_QUICK_PRIVATE_EXPORT QSGDefaultRectangleNode : public QSGRectangleNode
-{
-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();
- QSGVertexColorMaterial m_material;
- QSGSmoothColorMaterial m_smoothMaterial;
-
QRectF m_rect;
QGradientStops m_gradient_stops;
QColor m_color;
diff --git a/src/quick/scenegraph/qsgcontext.cpp b/src/quick/scenegraph/qsgcontext.cpp
index 842afe9dff..d52f69c7a3 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,31 +98,9 @@ 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
+bool qsg_useConsistentTiming()
{
-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;
-};
-
-static bool qsg_useConsistentTiming()
-{
- static int use = -1;
+ int use = -1;
if (use < 0) {
use = !qEnvironmentVariableIsEmpty("QSG_FIXED_ANIMATION_STEP") && qgetenv("QSG_FIXED_ANIMATION_STEP") != "no"
? 1 : 0;
@@ -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 *)
@@ -396,9 +268,9 @@ void QSGContext::renderContextInvalidated(QSGRenderContext *)
/*!
Convenience factory function for creating a colored rectangle with the given geometry.
*/
-QSGRectangleNode *QSGContext::createRectangleNode(const QRectF &rect, const QColor &c)
+QSGInternalRectangleNode *QSGContext::createInternalRectangleNode(const QRectF &rect, const QColor &c)
{
- QSGRectangleNode *node = createRectangleNode();
+ QSGInternalRectangleNode *node = createInternalRectangleNode();
node->setRect(rect);
node->setColor(c);
node->update();
@@ -406,139 +278,76 @@ 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
+ 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.
*/
-QSGPainterNode *QSGContext::createPainterNode(QQuickPaintedItem *item)
+QSGGuiThreadShaderEffectManager *QSGContext::createGuiThreadShaderEffectManager()
{
- return new QSGDefaultPainterNode(item);
+ return nullptr;
}
/*!
- Factory function for scene graph backends of the Text elements;
+ 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.
*/
-QSGGlyphNode *QSGContext::createGlyphNode(QSGRenderContext *rc, bool preferNativeGlyphNode)
+QSGShaderEffectNode *QSGContext::createShaderEffectNode(QSGRenderContext *, QSGGuiThreadShaderEffectManager *)
{
- 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.
- */
-QSGNinePatchNode *QSGContext::createNinePatchNode()
-{
- return 0;
-}
-
-/*!
- 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
{
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)
+ Returns a pointer to the (presumably) global renderer interface.
+
+ \note This function may be called on the gui thread in order to get access
+ to QSGRendererInterface::graphicsApi() and other getters.
+
+ \note it is expected that the simple queries (graphicsApi, shaderType,
+ etc.) are available regardless of the render context validity (i.e.
+ scenegraph status). This does not apply to engine-specific getters like
+ getResource(). In the end this means that this function must always return
+ a valid object in subclasses, even when renderContext->isValid() is false.
+ The typical pattern is to implement the QSGRendererInterface in the
+ QSGContext or QSGRenderContext subclass itself, whichever is more suitable.
+ */
+QSGRendererInterface *QSGContext::rendererInterface(QSGRenderContext *renderContext)
{
- d_func()->distanceFieldDisabled = !enabled;
+ Q_UNUSED(renderContext);
+ qWarning("QSGRendererInterface not implemented");
+ return nullptr;
}
-
-/*!
- Returns true if the scene graph uses the distance field technique to render text
- */
-bool QSGContext::isDistanceFieldEnabled() const
+QSGRenderContext::QSGRenderContext(QSGContext *context)
+ : m_sg(context)
+ , m_distanceFieldCacheManager(0)
{
- return !d_func()->distanceFieldDisabled;
}
-
-
-/*!
- Creates a new animation driver.
- */
-
-QAnimationDriver *QSGContext::createAnimationDriver(QObject *parent)
+QSGRenderContext::~QSGRenderContext()
{
- return new QSGAnimationDriver(parent);
}
-QSGRenderContext::QSGRenderContext(QSGContext *context)
- : m_gl(0)
- , m_sg(context)
- , m_atlasManager(0)
- , m_depthStencilManager(0)
- , m_distanceFieldCacheManager(0)
- , m_maxTextureSize(0)
- , m_brokenIBOs(false)
- , m_serializedRender(false)
- , m_attachToGLContext(true)
+void QSGRenderContext::initialize(void *context)
{
+ Q_UNUSED(context);
}
-QSGRenderContext::~QSGRenderContext()
+void QSGRenderContext::invalidate()
{
- invalidate();
}
void QSGRenderContext::endSync()
@@ -547,49 +356,14 @@ void QSGRenderContext::endSync()
m_texturesToDelete.clear();
}
-static QBasicMutex qsg_framerender_mutex;
-
-void QSGRenderContext::renderNextFrame(QSGRenderer *renderer, GLuint fboId)
-{
- if (m_serializedRender)
- qsg_framerender_mutex.lock();
-
- renderer->renderScene(fboId);
-
- if (m_serializedRender)
- qsg_framerender_mutex.unlock();
-
-}
-
/*!
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)
{
@@ -598,178 +372,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 (vendor && strstr(vendor, "nouveau"))
- m_brokenIBOs = true;
- const char *renderer = (const char *) funcs->glGetString(GL_RENDERER);
- if (renderer && strstr(renderer, "llvmpipe"))
- m_serializedRender = true;
- if (vendor && renderer && 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)
{
@@ -799,41 +413,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..6b9db105e7 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,36 +62,32 @@
#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;
+class QSGInternalRectangleNode;
+class QSGInternalImageNode;
class QSGPainterNode;
class QSGGlyphNode;
-class QSGNinePatchNode;
class QSGRenderer;
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;
+class QSGRectangleNode;
+class QSGImageNode;
+class QSGNinePatchNode;
+class QSGSpriteNode;
Q_DECLARE_LOGGING_CATEGORY(QSG_LOG_TIME_RENDERLOOP)
Q_DECLARE_LOGGING_CATEGORY(QSG_LOG_TIME_COMPILATION)
@@ -112,39 +109,28 @@ 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 setAttachToGraphicsContext(bool attach) { Q_UNUSED(attach); }
- virtual void compile(QSGMaterialShader *shader, QSGMaterial *material, const char *vertexCode = 0, const char *fragmentCode = 0);
- virtual void initialize(QSGMaterialShader *shader);
+ virtual int maxTextureSize() const = 0;
- 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 +139,21 @@ public Q_SLOTS:
void textureFactoryDestroyed(QObject *o);
protected:
- QOpenGLContext *m_gl;
- QSGContext *m_sg;
+ // Hold m_sg with QPointer in the rare case it gets deleted before us.
+ QPointer<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,30 +163,40 @@ public:
};
explicit QSGContext(QObject *parent = 0);
- ~QSGContext();
+ virtual ~QSGContext();
virtual void renderContextInitialized(QSGRenderContext *renderContext);
virtual void renderContextInvalidated(QSGRenderContext *renderContext);
- virtual QSGRenderContext *createRenderContext();
-
- 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 QSGRenderContext *createRenderContext() = 0;
+
+ QSGInternalRectangleNode *createInternalRectangleNode(const QRectF &rect, const QColor &c);
+ virtual QSGInternalRectangleNode *createInternalRectangleNode() = 0;
+ virtual QSGInternalImageNode *createInternalImageNode() = 0;
+ virtual QSGPainterNode *createPainterNode(QQuickPaintedItem *item) = 0;
+ virtual QSGGlyphNode *createGlyphNode(QSGRenderContext *rc, bool preferNativeGlyphNode) = 0;
+ virtual QSGLayer *createLayer(QSGRenderContext *renderContext) = 0;
+ virtual QSGGuiThreadShaderEffectManager *createGuiThreadShaderEffectManager();
+ virtual QSGShaderEffectNode *createShaderEffectNode(QSGRenderContext *renderContext,
+ QSGGuiThreadShaderEffectManager *mgr);
+#if QT_CONFIG(quick_sprite)
+ virtual QSGSpriteNode *createSpriteNode() = 0;
+#endif
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);
+
+ virtual QSGRectangleNode *createRectangleNode() = 0;
+ virtual QSGImageNode *createImageNode() = 0;
+ virtual QSGNinePatchNode *createNinePatchNode() = 0;
static QSGContext *createDefaultContext();
static QQuickTextureFactory *createTextureFactoryFromImage(const QImage &image);
static QSGRenderLoop *createWindowManager();
+
+ static void setBackend(const QString &backend);
};
QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/qsgcontextplugin.cpp b/src/quick/scenegraph/qsgcontextplugin.cpp
index f5c2b6880b..3751891455 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,109 @@ Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader,
(QSGContextFactoryInterface_iid, QLatin1String("/scenegraph")))
#endif
-struct QSGAdaptionPluginData
+struct QSGAdaptationBackendData
{
- QSGAdaptionPluginData()
- : tried(false)
- , factory(0)
- {
- }
-
- ~QSGAdaptionPluginData()
- {
- }
+ QSGAdaptationBackendData();
bool tried;
QSGContextFactoryInterface *factory;
- QString deviceName;
+ QString name;
+ QSGContextFactoryInterface::Flags flags;
+
+ QVector<QSGContextFactoryInterface *> builtIns;
+
+ QString quickWindowBackendRequest;
};
-Q_GLOBAL_STATIC(QSGAdaptionPluginData, qsg_adaptation_data)
+QSGAdaptationBackendData::QSGAdaptationBackendData()
+ : tried(false)
+ , factory(nullptr)
+ , flags(0)
+{
+ // Fill in the table with the built-in adaptations.
+ builtIns.append(new QSGSoftwareAdaptation);
+}
+
+Q_GLOBAL_STATIC(QSGAdaptationBackendData, qsg_adaptation_data)
+
+// 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()
+{
+ return qsg_adaptation_data()->flags;
+}
-QSGAdaptionPluginData *contextFactory()
+QSGAdaptationBackendData *contextFactory()
{
- QSGAdaptionPluginData *plugin = qsg_adaptation_data();
- if (!plugin->tried) {
+ QSGAdaptationBackendData *backendData = qsg_adaptation_data();
+
+ if (!backendData->tried) {
+ backendData->tried = true;
- plugin->tried = true;
const QStringList args = QGuiApplication::arguments();
- QString device;
+ QString requestedBackend = backendData->quickWindowBackendRequest; // empty or set via QQuickWindow::setBackend()
+
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;
-#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)));
- }
+ 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()) {
+ qCDebug(QSG_LOG_INFO) << "Loading backend" << requestedBackend;
+
+ // 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);
+ }
+ 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 // QT_NO_LIBRARY
+ }
}
- return plugin;
+
+ return backendData;
}
@@ -126,10 +182,14 @@ QSGAdaptionPluginData *contextFactory()
*/
QSGContext *QSGContext::createDefaultContext()
{
- QSGAdaptionPluginData *plugin = contextFactory();
- if (plugin->factory)
- return plugin->factory->create(plugin->deviceName);
- return new QSGContext();
+ QSGAdaptationBackendData *backendData = contextFactory();
+ if (backendData->factory)
+ return backendData->factory->create(backendData->name);
+#ifndef QT_NO_OPENGL
+ return new QSGDefaultContext();
+#else
+ return nullptr;
+#endif
}
@@ -143,9 +203,9 @@ QSGContext *QSGContext::createDefaultContext()
QQuickTextureFactory *QSGContext::createTextureFactoryFromImage(const QImage &image)
{
- QSGAdaptionPluginData *plugin = contextFactory();
- if (plugin->factory)
- return plugin->factory->createTextureFactoryFromImage(image);
+ QSGAdaptationBackendData *backendData = contextFactory();
+ if (backendData->factory)
+ return backendData->factory->createTextureFactoryFromImage(image);
return 0;
}
@@ -157,14 +217,19 @@ QQuickTextureFactory *QSGContext::createTextureFactoryFromImage(const QImage &im
QSGRenderLoop *QSGContext::createWindowManager()
{
- QSGAdaptionPluginData *plugin = contextFactory();
- if (plugin->factory)
- return plugin->factory->createWindowManager();
+ QSGAdaptationBackendData *backendData = contextFactory();
+ if (backendData->factory)
+ return backendData->factory->createWindowManager();
return 0;
}
+void QSGContext::setBackend(const QString &backend)
+{
+ QSGAdaptationBackendData *backendData = qsg_adaptation_data();
+ if (backendData->tried)
+ qWarning("Scenegraph already initialized, setBackend() request ignored");
-
-
+ backendData->quickWindowBackendRequest = backend;
+}
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..405f1d86a4
--- /dev/null
+++ b/src/quick/scenegraph/qsgdefaultcontext.cpp
@@ -0,0 +1,290 @@
+/****************************************************************************
+**
+** 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/qsgdefaultinternalrectanglenode_p.h>
+#include <QtQuick/private/qsgdefaultinternalimagenode_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 <QtQuick/private/qsgdefaultrectanglenode_p.h>
+#include <QtQuick/private/qsgdefaultimagenode_p.h>
+#include <QtQuick/private/qsgdefaultninepatchnode_p.h>
+#if QT_CONFIG(quick_sprite)
+#include <QtQuick/private/qsgdefaultspritenode_p.h>
+#endif
+
+#include <QtGui/QOpenGLContext>
+#include <QtGui/QOpenGLFramebufferObject>
+
+#include <QtQuick/QQuickWindow>
+
+#include <private/qqmlglobal_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QSGMultisampleAntialiasing {
+ class ImageNode : public QSGDefaultInternalImageNode {
+ public:
+ void setAntialiasing(bool) { }
+ };
+
+
+ class RectangleNode : public QSGDefaultInternalRectangleNode {
+ 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;
+ for (const QByteArray &e : qAsConst(exts))
+ all += ' ' + e;
+ qCDebug(QSG_LOG_INFO) << "GL_EXTENSIONS: " << all.constData();
+ qCDebug(QSG_LOG_INFO) << "Max Texture Size: " << openglRenderContext->maxTextureSize();
+ qCDebug(QSG_LOG_INFO) << "Debug context: " << format.testOption(QSurfaceFormat::DebugContext);
+ }
+
+ m_mutex.unlock();
+}
+
+void QSGDefaultContext::renderContextInvalidated(QSGRenderContext *)
+{
+}
+
+QSGRenderContext *QSGDefaultContext::createRenderContext()
+{
+ return new QSGDefaultRenderContext(this);
+}
+
+QSGInternalRectangleNode *QSGDefaultContext::createInternalRectangleNode()
+{
+ return m_antialiasingMethod == MsaaAntialiasing
+ ? new QSGMultisampleAntialiasing::RectangleNode
+ : new QSGDefaultInternalRectangleNode;
+}
+
+QSGInternalImageNode *QSGDefaultContext::createInternalImageNode()
+{
+ return m_antialiasingMethod == MsaaAntialiasing
+ ? new QSGMultisampleAntialiasing::ImageNode
+ : new QSGDefaultInternalImageNode;
+}
+
+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;
+ }
+}
+
+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;
+}
+
+QSGRectangleNode *QSGDefaultContext::createRectangleNode()
+{
+ return new QSGDefaultRectangleNode;
+}
+
+QSGImageNode *QSGDefaultContext::createImageNode()
+{
+ return new QSGDefaultImageNode;
+}
+
+QSGNinePatchNode *QSGDefaultContext::createNinePatchNode()
+{
+ return new QSGDefaultNinePatchNode;
+}
+
+#if QT_CONFIG(quick_sprite)
+QSGSpriteNode *QSGDefaultContext::createSpriteNode()
+{
+ return new QSGDefaultSpriteNode;
+}
+#endif
+
+QSGRendererInterface::GraphicsApi QSGDefaultContext::graphicsApi() const
+{
+ return OpenGL;
+}
+
+QSGRendererInterface::ShaderType QSGDefaultContext::shaderType() const
+{
+ return GLSL;
+}
+
+QSGRendererInterface::ShaderCompilationTypes QSGDefaultContext::shaderCompilationType() const
+{
+ return RuntimeCompilation;
+}
+
+QSGRendererInterface::ShaderSourceTypes QSGDefaultContext::shaderSourceType() const
+{
+ return ShaderSourceString | ShaderSourceFile;
+}
+
+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..ab319502ef
--- /dev/null
+++ b/src/quick/scenegraph/qsgdefaultcontext_p.h
@@ -0,0 +1,101 @@
+/****************************************************************************
+**
+** 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 Q_QUICK_PRIVATE_EXPORT QSGDefaultContext : public QSGContext, public QSGRendererInterface
+{
+public:
+ QSGDefaultContext(QObject *parent = 0);
+ ~QSGDefaultContext();
+
+ void renderContextInitialized(QSGRenderContext *renderContext) override;
+ void renderContextInvalidated(QSGRenderContext *) override;
+ QSGRenderContext *createRenderContext() override;
+ QSGInternalRectangleNode *createInternalRectangleNode() override;
+ QSGInternalImageNode *createInternalImageNode() override;
+ QSGPainterNode *createPainterNode(QQuickPaintedItem *item) override;
+ QSGGlyphNode *createGlyphNode(QSGRenderContext *rc, bool preferNativeGlyphNode) override;
+ QSGLayer *createLayer(QSGRenderContext *renderContext) override;
+ QSurfaceFormat defaultSurfaceFormat() const override;
+ QSGRendererInterface *rendererInterface(QSGRenderContext *renderContext) override;
+ QSGRectangleNode *createRectangleNode() override;
+ QSGImageNode *createImageNode() override;
+ QSGNinePatchNode *createNinePatchNode() override;
+#if QT_CONFIG(quick_sprite)
+ QSGSpriteNode *createSpriteNode() override;
+#endif
+
+ void setDistanceFieldEnabled(bool enabled);
+ bool isDistanceFieldEnabled() const;
+
+ GraphicsApi graphicsApi() const override;
+ ShaderType shaderType() const override;
+ ShaderCompilationTypes shaderCompilationType() const override;
+ ShaderSourceTypes shaderSourceType() 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/qsgdefaultdistancefieldglyphcache.cpp b/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp
index bb4a3b04d5..f0a336e229 100644
--- a/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp
+++ b/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp
@@ -508,7 +508,7 @@ bool QSGDefaultDistanceFieldGlyphCache::useTextureUploadWorkaround() const
bool QSGDefaultDistanceFieldGlyphCache::createFullSizeTextures() const
{
- return qsgPreferFullSizeGlyphCacheTextures() && glyphCount() > QT_DISTANCEFIELD_HIGHGLYPHCOUNT;
+ return qsgPreferFullSizeGlyphCacheTextures() && glyphCount() > QT_DISTANCEFIELD_HIGHGLYPHCOUNT();
}
int QSGDefaultDistanceFieldGlyphCache::maxTextureSize() const
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 161373306a..b001899915 100644
--- a/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp
+++ b/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp
@@ -50,6 +50,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>
@@ -445,7 +446,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
deleted file mode 100644
index bb4db150c0..0000000000
--- a/src/quick/scenegraph/qsgdefaultimagenode.cpp
+++ /dev/null
@@ -1,671 +0,0 @@
-/****************************************************************************
-**
-** 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 "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>
-
-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:
- SmoothTextureMaterialShader();
-
- virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect);
- virtual char const *const *attributeNames() const;
-
-protected:
- virtual void initialize();
-
- int m_pixelSizeLoc;
-};
-
-
-QSGSmoothTextureMaterial::QSGSmoothTextureMaterial()
-{
- setFlag(RequiresFullMatrixExceptTranslate, true);
- setFlag(Blending, true);
-}
-
-void QSGSmoothTextureMaterial::setTexture(QSGTexture *texture)
-{
- m_texture = texture;
-}
-
-QSGMaterialType *QSGSmoothTextureMaterial::type() const
-{
- static QSGMaterialType type;
- return &type;
-}
-
-QSGMaterialShader *QSGSmoothTextureMaterial::createShader() const
-{
- return new SmoothTextureMaterialShader;
-}
-
-SmoothTextureMaterialShader::SmoothTextureMaterialShader()
- : QSGTextureMaterialShader()
-{
- setShaderSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/qt-project.org/scenegraph/shaders/smoothtexture.vert"));
- setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/scenegraph/shaders/smoothtexture.frag"));
-}
-
-void SmoothTextureMaterialShader::updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect)
-{
- if (oldEffect == 0) {
- // The viewport is constant, so set the pixel size uniform only once.
- QRect r = state.viewportRect();
- program()->setUniformValue(m_pixelSizeLoc, 2.0f / r.width(), 2.0f / r.height());
- }
- QSGTextureMaterialShader::updateState(state, newEffect, oldEffect);
-}
-
-char const *const *SmoothTextureMaterialShader::attributeNames() const
-{
- static char const *const attributes[] = {
- "vertex",
- "multiTexCoord",
- "vertexOffset",
- "texCoordOffset",
- 0
- };
- return attributes;
-}
-
-void SmoothTextureMaterialShader::initialize()
-{
- m_pixelSizeLoc = program()->uniformLocation("pixelSize");
- QSGTextureMaterialShader::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)
-{
- if (m_material.filtering() == filtering)
- return;
-
- m_material.setFiltering(filtering);
- m_materialO.setFiltering(filtering);
- m_smoothMaterial.setFiltering(filtering);
- markDirty(DirtyMaterial);
-}
-
-
-void QSGDefaultImageNode::setMipmapFiltering(QSGTexture::Filtering filtering)
-{
- if (m_material.mipmapFiltering() == filtering)
- return;
-
- m_material.setMipmapFiltering(filtering);
- m_materialO.setMipmapFiltering(filtering);
- m_smoothMaterial.setMipmapFiltering(filtering);
- markDirty(DirtyMaterial);
-}
-
-void QSGDefaultImageNode::setVerticalWrapMode(QSGTexture::WrapMode wrapMode)
-{
- if (m_material.verticalWrapMode() == wrapMode)
- return;
-
- m_material.setVerticalWrapMode(wrapMode);
- m_materialO.setVerticalWrapMode(wrapMode);
- m_smoothMaterial.setVerticalWrapMode(wrapMode);
- markDirty(DirtyMaterial);
-}
-
-void QSGDefaultImageNode::setHorizontalWrapMode(QSGTexture::WrapMode wrapMode)
-{
- if (m_material.horizontalWrapMode() == wrapMode)
- return;
-
- m_material.setHorizontalWrapMode(wrapMode);
- m_materialO.setHorizontalWrapMode(wrapMode);
- m_smoothMaterial.setHorizontalWrapMode(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)
-{
- 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)
-{
- if (mirror == m_mirror)
- return;
- m_mirror = mirror;
- m_dirtyGeometry = true;
-}
-
-
-void QSGDefaultImageNode::update()
-{
- if (m_dirtyGeometry)
- updateGeometry();
-}
-
-void QSGDefaultImageNode::preprocess()
-{
- 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()) {
- m_material.setFlag(QSGMaterial::Blending, !alpha);
- doDirty = true;
- }
-
- if (doDirty)
- markDirty(DirtyMaterial);
-}
-
-inline static bool isPowerOfTwo(int x)
-{
- // Assumption: x >= 1
- 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)
-{
- *(*indices)++ = topLeft;
- *(*indices)++ = bottomLeft;
- *(*indices)++ = bottomRight;
- *(*indices)++ = bottomRight;
- *(*indices)++ = topRight;
- *(*indices)++ = topLeft;
-}
-
-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();
-#ifndef QT_OPENGL_ES_2
- 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 {
- int hCells = hTiles;
- int vCells = vTiles;
- if (m_innerTargetRect.width() == 0)
- hCells = 0;
- if (m_innerTargetRect.left() != m_targetRect.left())
- ++hCells;
- if (m_innerTargetRect.right() != m_targetRect.right())
- ++hCells;
- if (m_innerTargetRect.height() == 0)
- vCells = 0;
- if (m_innerTargetRect.top() != m_targetRect.top())
- ++vCells;
- if (m_innerTargetRect.bottom() != m_targetRect.bottom())
- ++vCells;
- QVarLengthArray<X, 32> xData(2 * hCells);
- QVarLengthArray<Y, 32> yData(2 * vCells);
- X *xs = xData.data();
- Y *ys = yData.data();
-
- if (m_innerTargetRect.left() != m_targetRect.left()) {
- xs[0].x = m_targetRect.left();
- xs[0].tx = sourceRect.left();
- xs[1].x = m_innerTargetRect.left();
- xs[1].tx = innerSourceRect.left();
- xs += 2;
- }
- if (m_innerTargetRect.width() != 0) {
- xs[0].x = m_innerTargetRect.left();
- xs[0].tx = innerSourceRect.x() + (m_subSourceRect.left() - floorLeft) * innerSourceRect.width();
- ++xs;
- float b = m_innerTargetRect.width() / m_subSourceRect.width();
- float a = m_innerTargetRect.x() - m_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 = m_innerTargetRect.right();
- xs[0].tx = innerSourceRect.x() + (m_subSourceRect.right() - ceilRight + 1) * innerSourceRect.width();
- ++xs;
- }
- if (m_innerTargetRect.right() != m_targetRect.right()) {
- xs[0].x = m_innerTargetRect.right();
- xs[0].tx = innerSourceRect.right();
- xs[1].x = m_targetRect.right();
- xs[1].tx = sourceRect.right();
- xs += 2;
- }
- Q_ASSERT(xs == xData.data() + xData.size());
- if (m_mirror) {
- float leftPlusRight = m_targetRect.left() + m_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 (m_innerTargetRect.top() != m_targetRect.top()) {
- ys[0].y = m_targetRect.top();
- ys[0].ty = sourceRect.top();
- ys[1].y = m_innerTargetRect.top();
- ys[1].ty = innerSourceRect.top();
- ys += 2;
- }
- if (m_innerTargetRect.height() != 0) {
- ys[0].y = m_innerTargetRect.top();
- ys[0].ty = innerSourceRect.y() + (m_subSourceRect.top() - floorTop) * innerSourceRect.height();
- ++ys;
- float b = m_innerTargetRect.height() / m_subSourceRect.height();
- float a = m_innerTargetRect.y() - m_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 = m_innerTargetRect.bottom();
- ys[0].ty = innerSourceRect.y() + (m_subSourceRect.bottom() - ceilBottom + 1) * innerSourceRect.height();
- ++ys;
- }
- if (m_innerTargetRect.bottom() != m_targetRect.bottom()) {
- ys[0].y = m_innerTargetRect.bottom();
- ys[0].ty = innerSourceRect.bottom();
- ys[1].y = m_targetRect.bottom();
- ys[1].ty = sourceRect.bottom();
- ys += 2;
- }
- Q_ASSERT(ys == yData.data() + yData.size());
-
- if (m_antialiasing) {
- QSGGeometry *g = geometry();
- Q_ASSERT(g != &m_geometry);
-
- 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(m_targetRect.width()) < qAbs(m_targetRect.height())
- ? m_targetRect.width() : m_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 {
- m_geometry.allocate(hCells * vCells * 4, hCells * vCells * 6);
- m_geometry.setDrawingMode(GL_TRIANGLES);
- QSGGeometry::TexturedPoint2D *vertices = m_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 = m_geometry.indexDataAsUShort();
- for (int i = 0; i < 4 * vCells * hCells; i += 4)
- appendQuad(&indices, i, i + 1, i + 2, i + 3);
- }
- }
- }
- markDirty(DirtyGeometry);
- m_dirtyGeometry = false;
-}
-
-QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/qsgdefaultinternalimagenode.cpp b/src/quick/scenegraph/qsgdefaultinternalimagenode.cpp
new file mode 100644
index 0000000000..1d54628acd
--- /dev/null
+++ b/src/quick/scenegraph/qsgdefaultinternalimagenode.cpp
@@ -0,0 +1,226 @@
+/****************************************************************************
+**
+** 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 "qsgdefaultinternalimagenode_p.h"
+#include <private/qsgmaterialshader_p.h>
+#include <private/qsgtexturematerial_p.h>
+#include <QtGui/qopenglfunctions.h>
+#include <QtCore/qmath.h>
+
+QT_BEGIN_NAMESPACE
+
+class SmoothTextureMaterialShader : public QSGTextureMaterialShader
+{
+public:
+ SmoothTextureMaterialShader();
+
+ virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect);
+ virtual char const *const *attributeNames() const;
+
+protected:
+ virtual void initialize();
+
+ int m_pixelSizeLoc;
+};
+
+
+QSGSmoothTextureMaterial::QSGSmoothTextureMaterial()
+{
+ setFlag(RequiresFullMatrixExceptTranslate, true);
+ setFlag(Blending, true);
+}
+
+void QSGSmoothTextureMaterial::setTexture(QSGTexture *texture)
+{
+ m_texture = texture;
+}
+
+QSGMaterialType *QSGSmoothTextureMaterial::type() const
+{
+ static QSGMaterialType type;
+ return &type;
+}
+
+QSGMaterialShader *QSGSmoothTextureMaterial::createShader() const
+{
+ return new SmoothTextureMaterialShader;
+}
+
+SmoothTextureMaterialShader::SmoothTextureMaterialShader()
+ : QSGTextureMaterialShader()
+{
+ setShaderSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/qt-project.org/scenegraph/shaders/smoothtexture.vert"));
+ setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/scenegraph/shaders/smoothtexture.frag"));
+}
+
+void SmoothTextureMaterialShader::updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect)
+{
+ if (oldEffect == 0) {
+ // The viewport is constant, so set the pixel size uniform only once.
+ QRect r = state.viewportRect();
+ program()->setUniformValue(m_pixelSizeLoc, 2.0f / r.width(), 2.0f / r.height());
+ }
+ QSGTextureMaterialShader::updateState(state, newEffect, oldEffect);
+}
+
+char const *const *SmoothTextureMaterialShader::attributeNames() const
+{
+ static char const *const attributes[] = {
+ "vertex",
+ "multiTexCoord",
+ "vertexOffset",
+ "texCoordOffset",
+ 0
+ };
+ return attributes;
+}
+
+void SmoothTextureMaterialShader::initialize()
+{
+ m_pixelSizeLoc = program()->uniformLocation("pixelSize");
+ QSGTextureMaterialShader::initialize();
+}
+
+QSGDefaultInternalImageNode::QSGDefaultInternalImageNode()
+{
+ setMaterial(&m_materialO);
+ setOpaqueMaterial(&m_material);
+}
+
+void QSGDefaultInternalImageNode::setFiltering(QSGTexture::Filtering filtering)
+{
+ if (m_material.filtering() == filtering)
+ return;
+
+ m_material.setFiltering(filtering);
+ m_materialO.setFiltering(filtering);
+ m_smoothMaterial.setFiltering(filtering);
+ markDirty(DirtyMaterial);
+}
+
+void QSGDefaultInternalImageNode::setMipmapFiltering(QSGTexture::Filtering filtering)
+{
+ if (m_material.mipmapFiltering() == filtering)
+ return;
+
+ m_material.setMipmapFiltering(filtering);
+ m_materialO.setMipmapFiltering(filtering);
+ m_smoothMaterial.setMipmapFiltering(filtering);
+ markDirty(DirtyMaterial);
+}
+
+void QSGDefaultInternalImageNode::setVerticalWrapMode(QSGTexture::WrapMode wrapMode)
+{
+ if (m_material.verticalWrapMode() == wrapMode)
+ return;
+
+ m_material.setVerticalWrapMode(wrapMode);
+ m_materialO.setVerticalWrapMode(wrapMode);
+ m_smoothMaterial.setVerticalWrapMode(wrapMode);
+ markDirty(DirtyMaterial);
+}
+
+void QSGDefaultInternalImageNode::setHorizontalWrapMode(QSGTexture::WrapMode wrapMode)
+{
+ if (m_material.horizontalWrapMode() == wrapMode)
+ return;
+
+ m_material.setHorizontalWrapMode(wrapMode);
+ m_materialO.setHorizontalWrapMode(wrapMode);
+ m_smoothMaterial.setHorizontalWrapMode(wrapMode);
+ markDirty(DirtyMaterial);
+}
+
+void QSGDefaultInternalImageNode::updateMaterialAntialiasing()
+{
+ if (m_antialiasing) {
+ setMaterial(&m_smoothMaterial);
+ setOpaqueMaterial(0);
+ } else {
+ setMaterial(&m_materialO);
+ setOpaqueMaterial(&m_material);
+ }
+}
+
+void QSGDefaultInternalImageNode::setMaterialTexture(QSGTexture *texture)
+{
+ m_material.setTexture(texture);
+ m_materialO.setTexture(texture);
+ m_smoothMaterial.setTexture(texture);
+}
+
+QSGTexture *QSGDefaultInternalImageNode::materialTexture() const
+{
+ return m_material.texture();
+}
+
+bool QSGDefaultInternalImageNode::updateMaterialBlending()
+{
+ const bool alpha = m_material.flags() & QSGMaterial::Blending;
+ if (materialTexture() && alpha != materialTexture()->hasAlphaChannel()) {
+ m_material.setFlag(QSGMaterial::Blending, !alpha);
+ return true;
+ }
+ return false;
+}
+
+inline static bool isPowerOfTwo(int x)
+{
+ // Assumption: x >= 1
+ return x == (x & -x);
+}
+
+bool QSGDefaultInternalImageNode::supportsWrap(const QSize &size) const
+{
+ bool wrapSupported = true;
+
+ QOpenGLContext *ctx = QOpenGLContext::currentContext();
+#ifndef QT_OPENGL_ES_2
+ if (ctx->isOpenGLES())
+#endif
+ {
+ bool npotSupported = ctx->functions()->hasOpenGLFeature(QOpenGLFunctions::NPOTTextureRepeat);
+ const bool isNpot = !isPowerOfTwo(size.width()) || !isPowerOfTwo(size.height());
+ wrapSupported = npotSupported || !isNpot;
+ }
+
+ return wrapSupported;
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/qsgdefaultimagenode_p.h b/src/quick/scenegraph/qsgdefaultinternalimagenode_p.h
index 2d8abc1d35..1fc7834bd1 100644
--- a/src/quick/scenegraph/qsgdefaultimagenode_p.h
+++ b/src/quick/scenegraph/qsgdefaultinternalimagenode_p.h
@@ -38,8 +38,8 @@
****************************************************************************/
-#ifndef QSGDEFAULTIMAGENODE_P_H
-#define QSGDEFAULTIMAGENODE_P_H
+#ifndef QSGDEFAULTINTERNALIMAGENODE_P_H
+#define QSGDEFAULTINTERNALIMAGENODE_P_H
//
// W A R N I N G
@@ -53,6 +53,7 @@
//
#include <private/qsgadaptationlayer_p.h>
+#include <private/qsgbasicinternalimagenode_p.h>
#include <QtQuick/qsgtexturematerial.h>
QT_BEGIN_NAMESPACE
@@ -65,47 +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 QSGDefaultInternalImageNode : public QSGBasicInternalImageNode
{
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();
+ QSGDefaultInternalImageNode();
- 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();
+ 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/qsgdefaultinternalrectanglenode.cpp b/src/quick/scenegraph/qsgdefaultinternalrectanglenode.cpp
new file mode 100644
index 0000000000..94414444ba
--- /dev/null
+++ b/src/quick/scenegraph/qsgdefaultinternalrectanglenode.cpp
@@ -0,0 +1,159 @@
+
+/****************************************************************************
+**
+** 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 "qsgdefaultinternalrectanglenode_p.h"
+
+#include <QtQuick/qsgvertexcolormaterial.h>
+#include <QtQuick/qsgtexturematerial.h>
+
+#include <QtQuick/private/qsgcontext_p.h>
+
+#include <QtCore/qmath.h>
+#include <QtCore/qvarlengtharray.h>
+
+QT_BEGIN_NAMESPACE
+
+class SmoothColorMaterialShader : public QSGMaterialShader
+{
+public:
+ SmoothColorMaterialShader();
+
+ virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect);
+ virtual char const *const *attributeNames() const;
+
+private:
+ virtual void initialize();
+
+ int m_matrixLoc;
+ int m_opacityLoc;
+ int m_pixelSizeLoc;
+};
+
+SmoothColorMaterialShader::SmoothColorMaterialShader()
+ : QSGMaterialShader()
+{
+ setShaderSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/qt-project.org/scenegraph/shaders/smoothcolor.vert"));
+ setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/scenegraph/shaders/smoothcolor.frag"));
+}
+
+void SmoothColorMaterialShader::updateState(const RenderState &state, QSGMaterial *, QSGMaterial *oldEffect)
+{
+ if (state.isOpacityDirty())
+ program()->setUniformValue(m_opacityLoc, state.opacity());
+
+ if (state.isMatrixDirty())
+ program()->setUniformValue(m_matrixLoc, state.combinedMatrix());
+
+ if (oldEffect == 0) {
+ // The viewport is constant, so set the pixel size uniform only once.
+ QRect r = state.viewportRect();
+ program()->setUniformValue(m_pixelSizeLoc, 2.0f / r.width(), 2.0f / r.height());
+ }
+}
+
+char const *const *SmoothColorMaterialShader::attributeNames() const
+{
+ static char const *const attributes[] = {
+ "vertex",
+ "vertexColor",
+ "vertexOffset",
+ 0
+ };
+ return attributes;
+}
+
+void SmoothColorMaterialShader::initialize()
+{
+ m_matrixLoc = program()->uniformLocation("matrix");
+ m_opacityLoc = program()->uniformLocation("opacity");
+ m_pixelSizeLoc = program()->uniformLocation("pixelSize");
+}
+
+QSGSmoothColorMaterial::QSGSmoothColorMaterial()
+{
+ setFlag(RequiresFullMatrixExceptTranslate, true);
+ setFlag(Blending, true);
+}
+
+int QSGSmoothColorMaterial::compare(const QSGMaterial *) const
+{
+ return 0;
+}
+
+QSGMaterialType *QSGSmoothColorMaterial::type() const
+{
+ static QSGMaterialType type;
+ return &type;
+}
+
+QSGMaterialShader *QSGSmoothColorMaterial::createShader() const
+{
+ return new SmoothColorMaterialShader;
+}
+
+QSGDefaultInternalRectangleNode::QSGDefaultInternalRectangleNode()
+{
+ setMaterial(&m_material);
+}
+
+void QSGDefaultInternalRectangleNode::updateMaterialAntialiasing()
+{
+ if (m_antialiasing)
+ setMaterial(&m_smoothMaterial);
+ else
+ setMaterial(&m_material);
+}
+
+void QSGDefaultInternalRectangleNode::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/quick/scenegraph/qsgdefaultinternalrectanglenode_p.h b/src/quick/scenegraph/qsgdefaultinternalrectanglenode_p.h
new file mode 100644
index 0000000000..a3f734a7b3
--- /dev/null
+++ b/src/quick/scenegraph/qsgdefaultinternalrectanglenode_p.h
@@ -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$
+**
+****************************************************************************/
+
+
+#ifndef QSGDEFAULTINTERNALRECTANGLENODE_P_H
+#define QSGDEFAULTINTERNALRECTANGLENODE_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 <private/qsgbasicinternalrectanglenode_p.h>
+#include <QtQuick/qsgvertexcolormaterial.h>
+
+QT_BEGIN_NAMESPACE
+
+class QSGContext;
+
+class Q_QUICK_PRIVATE_EXPORT QSGSmoothColorMaterial : public QSGMaterial
+{
+public:
+ QSGSmoothColorMaterial();
+
+ int compare(const QSGMaterial *other) const override;
+
+protected:
+ QSGMaterialType *type() const override;
+ QSGMaterialShader *createShader() const override;
+};
+
+class Q_QUICK_PRIVATE_EXPORT QSGDefaultInternalRectangleNode : public QSGBasicInternalRectangleNode
+{
+public:
+ QSGDefaultInternalRectangleNode();
+
+private:
+ void updateMaterialAntialiasing() override;
+ void updateMaterialBlending(QSGNode::DirtyState *state) override;
+
+ QSGVertexColorMaterial m_material;
+ QSGSmoothColorMaterial m_smoothMaterial;
+};
+
+QT_END_NAMESPACE
+
+#endif
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/qsgdefaultrendercontext.cpp b/src/quick/scenegraph/qsgdefaultrendercontext.cpp
new file mode 100644
index 0000000000..1a17453baf
--- /dev/null
+++ b/src/quick/scenegraph/qsgdefaultrendercontext.cpp
@@ -0,0 +1,307 @@
+/****************************************************************************
+**
+** 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)
+{
+ if (!m_sg)
+ return;
+
+ 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 (vendor && strstr(vendor, "nouveau"))
+ m_brokenIBOs = true;
+ const char *renderer = (const char *) funcs->glGetString(GL_RENDERER);
+ if (renderer && strstr(renderer, "llvmpipe"))
+ m_serializedRender = true;
+ if (vendor && renderer && 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;
+
+ if (m_sg)
+ m_sg->renderContextInvalidated(this);
+ emit invalidated();
+}
+
+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::setAttachToGraphicsContext(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..0aed46b658
--- /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 Q_QUICK_PRIVATE_EXPORT 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 setAttachToGraphicsContext(bool attach) override;
+
+ static QSGDefaultRenderContext *from(QOpenGLContext *context);
+
+ bool hasBrokenIndexBufferObjects() const { return m_brokenIBOs; }
+ int maxTextureSize() const override { 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/qsgdefaultspritenode.cpp b/src/quick/scenegraph/qsgdefaultspritenode.cpp
new file mode 100644
index 0000000000..5eb8fb6e08
--- /dev/null
+++ b/src/quick/scenegraph/qsgdefaultspritenode.cpp
@@ -0,0 +1,303 @@
+/****************************************************************************
+**
+** 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 "qsgdefaultspritenode_p.h"
+
+#include <QtQuick/QSGMaterial>
+#include <QtGui/QOpenGLShaderProgram>
+
+QT_BEGIN_NAMESPACE
+
+struct SpriteVertex {
+ float x;
+ float y;
+ float tx;
+ float ty;
+};
+
+struct SpriteVertices {
+ SpriteVertex v1;
+ SpriteVertex v2;
+ SpriteVertex v3;
+ SpriteVertex v4;
+};
+
+class QQuickSpriteMaterial : public QSGMaterial
+{
+public:
+ QQuickSpriteMaterial();
+ ~QQuickSpriteMaterial();
+ QSGMaterialType *type() const override { static QSGMaterialType type; return &type; }
+ QSGMaterialShader *createShader() const override;
+ int compare(const QSGMaterial *other) const override
+ {
+ return this - static_cast<const QQuickSpriteMaterial *>(other);
+ }
+
+ QSGTexture *texture;
+
+ float animT;
+ float animX1;
+ float animY1;
+ float animX2;
+ float animY2;
+ float animW;
+ float animH;
+};
+
+QQuickSpriteMaterial::QQuickSpriteMaterial()
+ : texture(0)
+ , animT(0.0f)
+ , animX1(0.0f)
+ , animY1(0.0f)
+ , animX2(0.0f)
+ , animY2(0.0f)
+ , animW(1.0f)
+ , animH(1.0f)
+{
+ setFlag(Blending, true);
+}
+
+QQuickSpriteMaterial::~QQuickSpriteMaterial()
+{
+ delete texture;
+}
+
+class SpriteMaterialData : public QSGMaterialShader
+{
+public:
+ SpriteMaterialData()
+ : QSGMaterialShader()
+ {
+ setShaderSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/qt-project.org/scenegraph/shaders/sprite.vert"));
+ setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/scenegraph/shaders/sprite.frag"));
+ }
+
+ void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *) Q_DECL_OVERRIDE
+ {
+ QQuickSpriteMaterial *m = static_cast<QQuickSpriteMaterial *>(newEffect);
+ m->texture->bind();
+
+ program()->setUniformValue(m_opacity_id, state.opacity());
+ program()->setUniformValue(m_animData_id, m->animW, m->animH, m->animT);
+ program()->setUniformValue(m_animPos_id, m->animX1, m->animY1, m->animX2, m->animY2);
+
+ if (state.isMatrixDirty())
+ program()->setUniformValue(m_matrix_id, state.combinedMatrix());
+ }
+
+ void initialize() Q_DECL_OVERRIDE {
+ m_matrix_id = program()->uniformLocation("qt_Matrix");
+ m_opacity_id = program()->uniformLocation("qt_Opacity");
+ m_animData_id = program()->uniformLocation("animData");
+ m_animPos_id = program()->uniformLocation("animPos");
+ }
+
+ char const *const *attributeNames() const Q_DECL_OVERRIDE {
+ static const char *attr[] = {
+ "vPos",
+ "vTex",
+ 0
+ };
+ return attr;
+ }
+
+ int m_matrix_id;
+ int m_opacity_id;
+ int m_animData_id;
+ int m_animPos_id;
+};
+
+QSGMaterialShader *QQuickSpriteMaterial::createShader() const
+{
+ return new SpriteMaterialData;
+}
+
+static QSGGeometry::Attribute Sprite_Attributes[] = {
+ QSGGeometry::Attribute::create(0, 2, QSGGeometry::FloatType, true), // pos
+ QSGGeometry::Attribute::create(1, 2, QSGGeometry::FloatType), // tex
+};
+
+static QSGGeometry::AttributeSet Sprite_AttributeSet =
+{
+ 2, // Attribute Count
+ (2+2) * sizeof(float),
+ Sprite_Attributes
+};
+
+QSGDefaultSpriteNode::QSGDefaultSpriteNode()
+ : m_material(new QQuickSpriteMaterial)
+ , m_geometryDirty(true)
+ , m_sheetSize(QSize(64, 64))
+{
+ // Setup geometry data
+ m_geometry = new QSGGeometry(Sprite_AttributeSet, 4, 6);
+ m_geometry->setDrawingMode(QSGGeometry::DrawTriangles);
+ quint16 *indices = m_geometry->indexDataAsUShort();
+ indices[0] = 0;
+ indices[1] = 1;
+ indices[2] = 2;
+ indices[3] = 1;
+ indices[4] = 3;
+ indices[5] = 2;
+
+ setGeometry(m_geometry);
+ setMaterial(m_material);
+ setFlag(OwnsGeometry, true);
+ setFlag(OwnsMaterial, true);
+}
+
+void QSGDefaultSpriteNode::setTexture(QSGTexture *texture)
+{
+ m_material->texture = texture;
+ m_geometryDirty = true;
+ markDirty(DirtyMaterial);
+}
+
+void QSGDefaultSpriteNode::setTime(float time)
+{
+ m_material->animT = time;
+ markDirty(DirtyMaterial);
+}
+
+void QSGDefaultSpriteNode::setSourceA(const QPoint &source)
+{
+ if (m_sourceA != source) {
+ m_sourceA = source;
+ m_material->animX1 = static_cast<float>(source.x()) / m_sheetSize.width();
+ m_material->animY1 = static_cast<float>(source.y()) / m_sheetSize.height();
+ markDirty(DirtyMaterial);
+ }
+}
+
+void QSGDefaultSpriteNode::setSourceB(const QPoint &source)
+{
+ if (m_sourceB != source) {
+ m_sourceB = source;
+ m_material->animX2 = static_cast<float>(source.x()) / m_sheetSize.width();
+ m_material->animY2 = static_cast<float>(source.y()) / m_sheetSize.height();
+ markDirty(DirtyMaterial);
+ }
+}
+
+void QSGDefaultSpriteNode::setSpriteSize(const QSize &size)
+{
+ if (m_spriteSize != size) {
+ m_spriteSize = size;
+ m_material->animW = static_cast<float>(size.width()) / m_sheetSize.width();
+ m_material->animH = static_cast<float>(size.height()) / m_sheetSize.height();
+ markDirty(DirtyMaterial);
+ }
+
+}
+
+void QSGDefaultSpriteNode::setSheetSize(const QSize &size)
+{
+ if (m_sheetSize != size) {
+ m_sheetSize = size;
+
+ // Update all dependent properties
+ m_material->animX1 = static_cast<float>(m_sourceA.x()) / m_sheetSize.width();
+ m_material->animY1 = static_cast<float>(m_sourceA.y()) / m_sheetSize.height();
+ m_material->animX2 = static_cast<float>(m_sourceB.x()) / m_sheetSize.width();
+ m_material->animY2 = static_cast<float>(m_sourceB.y()) / m_sheetSize.height();
+ m_material->animW = static_cast<float>(m_spriteSize.width()) / m_sheetSize.width();
+ m_material->animH = static_cast<float>(m_spriteSize.height()) / m_sheetSize.height();
+ markDirty(DirtyMaterial);
+ }
+}
+
+void QSGDefaultSpriteNode::setSize(const QSizeF &size)
+{
+ if (m_size != size) {
+ m_size = size;
+ m_geometryDirty = true;
+ }
+}
+
+void QSGDefaultSpriteNode::setFiltering(QSGTexture::Filtering filtering)
+{
+ m_material->texture->setFiltering(filtering);
+ markDirty(DirtyMaterial);
+}
+
+void QSGDefaultSpriteNode::update()
+{
+ if (m_geometryDirty) {
+ updateGeometry();
+ m_geometryDirty = false;
+ }
+}
+
+void QSGDefaultSpriteNode::updateGeometry()
+{
+ if (!m_material->texture)
+ return;
+
+ SpriteVertices *p = (SpriteVertices *) m_geometry->vertexData();
+
+ QRectF texRect = m_material->texture->normalizedTextureSubRect();
+
+ p->v1.tx = texRect.topLeft().x();
+ p->v1.ty = texRect.topLeft().y();
+
+ p->v2.tx = texRect.topRight().x();
+ p->v2.ty = texRect.topRight().y();
+
+ p->v3.tx = texRect.bottomLeft().x();
+ p->v3.ty = texRect.bottomLeft().y();
+
+ p->v4.tx = texRect.bottomRight().x();
+ p->v4.ty = texRect.bottomRight().y();
+
+ p->v1.x = 0;
+ p->v1.y = 0;
+
+ p->v2.x = m_size.width();
+ p->v2.y = 0;
+
+ p->v3.x = 0;
+ p->v3.y = m_size.height();
+
+ p->v4.x = m_size.width();
+ p->v4.y = m_size.height();
+ markDirty(DirtyGeometry);
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/qsgdefaultspritenode_p.h b/src/quick/scenegraph/qsgdefaultspritenode_p.h
new file mode 100644
index 0000000000..78aa8cc0cf
--- /dev/null
+++ b/src/quick/scenegraph/qsgdefaultspritenode_p.h
@@ -0,0 +1,91 @@
+/****************************************************************************
+**
+** 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 QSGDEFAULTSPRITENODE_H
+#define QSGDEFAULTSPRITENODE_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/qtquickglobal_p.h>
+
+QT_REQUIRE_CONFIG(quick_sprite);
+
+#include <private/qsgadaptationlayer_p.h>
+
+QT_BEGIN_NAMESPACE
+class QQuickSpriteMaterial;
+class QSGDefaultSpriteNode : public QSGSpriteNode
+{
+public:
+ QSGDefaultSpriteNode();
+
+ void setTexture(QSGTexture *texture) override;
+ void setTime(float time) override;
+ void setSourceA(const QPoint &source) override;
+ void setSourceB(const QPoint &source) override;
+ void setSpriteSize(const QSize &size) override;
+ void setSheetSize(const QSize &size) override;
+ void setSize(const QSizeF &size) override;
+ void setFiltering(QSGTexture::Filtering filtering) override;
+ void update() override;
+private:
+ void updateGeometry();
+
+ QQuickSpriteMaterial *m_material;
+ QSGGeometry *m_geometry;
+ bool m_geometryDirty;
+ QPoint m_sourceA;
+ QPoint m_sourceB;
+ QSize m_spriteSize;
+ QSize m_sheetSize;
+ QSizeF m_size;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGDEFAULTSPRITENODE_H
diff --git a/src/quick/scenegraph/qsgrenderloop.cpp b/src/quick/scenegraph/qsgrenderloop.cpp
index b5f149eff7..df7bbe6ceb 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,13 @@
#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>
+#if QT_CONFIG(quick_shadereffect)
+# include <private/qquickopenglshadereffectnode_p.h>
+#endif
+#endif
#ifdef Q_OS_WIN
# include <QtCore/qt_windows.h>
@@ -67,9 +72,9 @@
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 +86,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()
@@ -97,7 +102,7 @@ void QSGRenderLoop::cleanup()
{
if (!s_instance)
return;
- foreach (QQuickWindow *w, s_instance->windows()) {
+ for (QQuickWindow *w : s_instance->windows()) {
QQuickWindowPrivate *wd = QQuickWindowPrivate::get(w);
if (wd->windowManager == s_instance) {
s_instance->windowDestroyed(w);
@@ -113,17 +118,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 +172,7 @@ public:
QImage grabContent;
};
-
+#endif
QSGRenderLoop *QSGRenderLoop::instance()
{
if (!s_instance) {
@@ -174,7 +182,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 +234,10 @@ QSGRenderLoop *QSGRenderLoop::instance()
break;
}
}
-
+#endif
qAddPostRoutine(QSGRenderLoop::cleanup);
}
+
return s_instance;
}
@@ -253,20 +262,24 @@ void QSGRenderLoop::handleContextCreationFailure(QQuickWindow *window,
const bool signalEmitted =
QQuickWindowPrivate::get(window)->emitError(QQuickWindow::ContextNotAvailable,
translatedMessage);
-#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
if (!signalEmitted && !QLibraryInfo::isDebugBuild() && !GetConsoleWindow()) {
MessageBox(0, (LPCTSTR) translatedMessage.utf16(),
(LPCTSTR)(QCoreApplication::applicationName().utf16()),
MB_OK | MB_ICONERROR);
}
-#endif // Q_OS_WIN && !Q_OS_WINCE && !Q_OS_WINRT
+#endif // Q_OS_WIN && !Q_OS_WINRT
if (!signalEmitted)
qFatal("%s", qPrintable(untranslatedMessage));
}
-
+#ifndef QT_NO_OPENGL
QSGGuiThreadRenderLoop::QSGGuiThreadRenderLoop()
: gl(0)
{
+ if (qsg_useConsistentTiming()) {
+ QUnifiedTimer::instance(true)->setConsistentTiming(true);
+ qCDebug(QSG_LOG_INFO, "using fixed animation steps");
+ }
sg = QSGContext::createDefaultContext();
rc = sg->createRenderContext();
}
@@ -315,7 +328,9 @@ void QSGGuiThreadRenderLoop::windowDestroyed(QQuickWindow *window)
if (Q_UNLIKELY(!current))
qCDebug(QSG_LOG_RENDERLOOP) << "cleanup without an OpenGL context";
- QQuickShaderEffectMaterial::cleanupMaterialCache();
+#if QT_CONFIG(quick_shadereffect) && QT_CONFIG(opengl)
+ QQuickOpenGLShaderEffectMaterial::cleanupMaterialCache();
+#endif
d->cleanupNodesOnShutdown();
if (m_windows.size() == 0) {
@@ -354,8 +369,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);
}
@@ -367,7 +384,7 @@ void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window)
return;
if (!data.grabOnly) {
- cd->flushDelayedTouchEvent();
+ cd->flushFrameSynchronousEvents();
// Event delivery/processing triggered the window to be deleted or stop rendering.
if (!m_windows.contains(window))
return;
@@ -476,6 +493,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 96abd4267b..693012154e 100644
--- a/src/quick/scenegraph/qsgthreadedrenderloop.cpp
+++ b/src/quick/scenegraph/qsgthreadedrenderloop.cpp
@@ -63,7 +63,10 @@
#include <private/qqmldebugserviceinterfaces_p.h>
#include <private/qqmldebugconnector_p.h>
-#include <private/qquickshadereffectnode_p.h>
+#if QT_CONFIG(quick_shadereffect)
+#include <private/qquickopenglshadereffectnode_p.h>
+#endif
+#include <private/qsgdefaultrendercontext_p.h>
/*
Overall design:
@@ -268,7 +271,6 @@ public:
QSGRenderThread(QSGThreadedRenderLoop *w, QSGRenderContext *renderContext)
: wm(w)
, gl(0)
- , sgrc(renderContext)
, animatorDriver(0)
, pendingUpdate(0)
, sleeping(false)
@@ -277,6 +279,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 +328,7 @@ public:
QSGThreadedRenderLoop *wm;
QOpenGLContext *gl;
- QSGRenderContext *sgrc;
+ QSGDefaultRenderContext *sgrc;
QAnimationDriver *animatorDriver;
@@ -486,7 +489,9 @@ void QSGRenderThread::invalidateOpenGL(QQuickWindow *window, bool inDestructor,
QQuickWindowPrivate *dd = QQuickWindowPrivate::get(window);
- QQuickShaderEffectMaterial::cleanupMaterialCache();
+#if QT_CONFIG(quick_shadereffect)
+ QQuickOpenGLShaderEffectMaterial::cleanupMaterialCache();
+#endif
// The canvas nodes must be cleaned up regardless if we are in the destructor..
if (wipeSG) {
@@ -1141,7 +1146,7 @@ void QSGThreadedRenderLoop::polishAndSync(Window *w, bool inExpose)
}
// Flush pending touch events.
- QQuickWindowPrivate::get(window)->flushDelayedTouchEvent();
+ QQuickWindowPrivate::get(window)->flushFrameSynchronousEvents();
// The delivery of the event might have caused the window to stop rendering
w = windowFor(m_windows, window);
if (!w || !w->thread || !w->thread->window) {
diff --git a/src/quick/scenegraph/qsgwindowsrenderloop.cpp b/src/quick/scenegraph/qsgwindowsrenderloop.cpp
index 901fbbec43..941a7b3f3c 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>
+#if QT_CONFIG(quick_shadereffect) && QT_CONFIG(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();
+#if QT_CONFIG(quick_shadereffect) && QT_CONFIG(opengl)
+ QQuickOpenGLShaderEffectMaterial::cleanupMaterialCache();
+#endif
d->cleanupNodesOnShutdown();
if (m_windows.size() == 0) {
@@ -257,7 +263,7 @@ void QSGWindowsRenderLoop::windowDestroyed(QQuickWindow *window)
bool QSGWindowsRenderLoop::anyoneShowing() const
{
- foreach (const WindowData &wd, m_windows)
+ for (const WindowData &wd : qAsConst(m_windows))
if (wd.window->isVisible() && wd.window->isExposed() && wd.window->size().isValid())
return true;
return false;
@@ -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()) {
@@ -371,7 +382,7 @@ void QSGWindowsRenderLoop::render()
{
RLDEBUG("render");
bool rendered = false;
- foreach (const WindowData &wd, m_windows) {
+ for (const WindowData &wd : qAsConst(m_windows)) {
if (wd.pendingUpdate) {
const_cast<WindowData &>(wd).pendingUpdate = false;
renderWindow(wd.window);
@@ -434,7 +445,7 @@ void QSGWindowsRenderLoop::renderWindow(QQuickWindow *window)
}
}
- d->flushDelayedTouchEvent();
+ d->flushFrameSynchronousEvents();
// Event delivery or processing has caused the window to stop rendering.
if (!windowData(window))
return;
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..edf4aa08c5 100644
--- a/src/quick/scenegraph/scenegraph.pri
+++ b/src/quick/scenegraph/scenegraph.pri
@@ -1,173 +1,224 @@
-!contains(QT_CONFIG, egl):DEFINES += QT_NO_EGL
-
# DEFINES += QSG_SEPARATE_INDEX_BUFFER
# DEFINES += QSG_DISTANCEFIELD_CACHE_DEBUG
# 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
+
+qtConfig(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 \
+ $$PWD/util/qsgrectanglenode.h \
+ $$PWD/util/qsgimagenode.h \
+ $$PWD/util/qsgninepatchnode.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 \
+ $$PWD/util/qsgrectanglenode.cpp \
+ $$PWD/util/qsgimagenode.cpp \
+ $$PWD/util/qsgninepatchnode.cpp
+
+qtConfig(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/qsgbasicinternalrectanglenode_p.h \
+ $$PWD/qsgbasicinternalimagenode_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/qsgbasicinternalrectanglenode.cpp \
+ $$PWD/qsgbasicinternalimagenode.cpp \
+ $$PWD/qsgbasicglyphnode.cpp \
+ $$PWD/qsgrenderloop.cpp
+
+qtConfig(opengl(es1|es2)?) {
+ SOURCES += \
+ $$PWD/qsgdefaultglyphnode.cpp \
+ $$PWD/qsgdefaultglyphnode_p.cpp \
+ $$PWD/qsgdefaultdistancefieldglyphcache.cpp \
+ $$PWD/qsgdistancefieldglyphnode.cpp \
+ $$PWD/qsgdistancefieldglyphnode_p.cpp \
+ $$PWD/qsgdefaultinternalimagenode.cpp \
+ $$PWD/qsgdefaultinternalrectanglenode.cpp \
+ $$PWD/qsgdefaultrendercontext.cpp \
+ $$PWD/qsgdefaultcontext.cpp \
+ $$PWD/util/qsgdefaultpainternode.cpp \
+ $$PWD/util/qsgdefaultrectanglenode.cpp \
+ $$PWD/util/qsgdefaultimagenode.cpp \
+ $$PWD/util/qsgdefaultninepatchnode.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/qsgdefaultinternalimagenode_p.h \
+ $$PWD/qsgdefaultinternalrectanglenode_p.h \
+ $$PWD/qsgdefaultrendercontext_p.h \
+ $$PWD/qsgdefaultcontext_p.h \
+ $$PWD/util/qsgdefaultpainternode_p.h \
+ $$PWD/util/qsgdefaultrectanglenode_p.h \
+ $$PWD/util/qsgdefaultimagenode_p.h \
+ $$PWD/util/qsgdefaultninepatchnode_p.h \
+ $$PWD/qsgdefaultlayer_p.h \
+ $$PWD/qsgthreadedrenderloop_p.h \
+ $$PWD/qsgwindowsrenderloop_p.h
+
+ qtConfig(quick-sprite) {
+ SOURCES += \
+ $$PWD/qsgdefaultspritenode.cpp
+ HEADERS += \
+ $$PWD/qsgdefaultspritenode_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
+qtConfig(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/scenegraph.qrc b/src/quick/scenegraph/scenegraph.qrc
index ef6da71334..0687530be1 100644
--- a/src/quick/scenegraph/scenegraph.qrc
+++ b/src/quick/scenegraph/scenegraph.qrc
@@ -68,5 +68,9 @@
<file>shaders/vertexcolor_core.vert</file>
<file>shaders/visualization.vert</file>
<file>shaders/visualization.frag</file>
+ <file>shaders/sprite.frag</file>
+ <file>shaders/sprite.vert</file>
+ <file>shaders/sprite_core.frag</file>
+ <file>shaders/sprite_core.vert</file>
</qresource>
</RCC>
diff --git a/src/quick/items/shaders/sprite.frag b/src/quick/scenegraph/shaders/sprite.frag
index e1fcb0f006..e1fcb0f006 100644
--- a/src/quick/items/shaders/sprite.frag
+++ b/src/quick/scenegraph/shaders/sprite.frag
diff --git a/src/quick/items/shaders/sprite.vert b/src/quick/scenegraph/shaders/sprite.vert
index fc826f60b4..fc826f60b4 100644
--- a/src/quick/items/shaders/sprite.vert
+++ b/src/quick/scenegraph/shaders/sprite.vert
diff --git a/src/quick/items/shaders/sprite_core.frag b/src/quick/scenegraph/shaders/sprite_core.frag
index c1087a8754..c1087a8754 100644
--- a/src/quick/items/shaders/sprite_core.frag
+++ b/src/quick/scenegraph/shaders/sprite_core.frag
diff --git a/src/quick/items/shaders/sprite_core.vert b/src/quick/scenegraph/shaders/sprite_core.vert
index 5027bf03fc..5027bf03fc 100644
--- a/src/quick/items/shaders/sprite_core.vert
+++ b/src/quick/scenegraph/shaders/sprite_core.vert
diff --git a/src/quick/scenegraph/util/qsgatlastexture.cpp b/src/quick/scenegraph/util/qsgatlastexture.cpp
index 27806c48ae..40c3293c7b 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/qsgdefaultimagenode.cpp b/src/quick/scenegraph/util/qsgdefaultimagenode.cpp
new file mode 100644
index 0000000000..6afe591dca
--- /dev/null
+++ b/src/quick/scenegraph/util/qsgdefaultimagenode.cpp
@@ -0,0 +1,205 @@
+/****************************************************************************
+**
+** 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 "qsgdefaultimagenode_p.h"
+#include <private/qsgnode_p.h>
+
+QT_BEGIN_NAMESPACE
+
+QSGDefaultImageNode::QSGDefaultImageNode()
+ : m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4)
+ , m_texCoordMode(QSGDefaultImageNode::NoTransform)
+ , m_isAtlasTexture(false)
+ , m_ownsTexture(false)
+{
+ setGeometry(&m_geometry);
+ setMaterial(&m_material);
+ setOpaqueMaterial(&m_opaque_material);
+ m_material.setMipmapFiltering(QSGTexture::None);
+ m_opaque_material.setMipmapFiltering(QSGTexture::None);
+#ifdef QSG_RUNTIME_DESCRIPTION
+ qsgnode_set_description(this, QLatin1String("image"));
+#endif
+}
+
+QSGDefaultImageNode::~QSGDefaultImageNode()
+{
+ if (m_ownsTexture)
+ delete m_material.texture();
+}
+
+void QSGDefaultImageNode::setFiltering(QSGTexture::Filtering filtering)
+{
+ if (m_material.filtering() == filtering)
+ return;
+
+ m_material.setFiltering(filtering);
+ m_opaque_material.setFiltering(filtering);
+ markDirty(DirtyMaterial);
+}
+
+QSGTexture::Filtering QSGDefaultImageNode::filtering() const
+{
+ return m_material.filtering();
+}
+
+void QSGDefaultImageNode::setMipmapFiltering(QSGTexture::Filtering filtering)
+{
+ if (m_material.mipmapFiltering() == filtering)
+ return;
+
+ m_material.setMipmapFiltering(filtering);
+ m_opaque_material.setMipmapFiltering(filtering);
+ markDirty(DirtyMaterial);
+}
+
+QSGTexture::Filtering QSGDefaultImageNode::mipmapFiltering() const
+{
+ return m_material.mipmapFiltering();
+}
+
+void QSGDefaultImageNode::setRect(const QRectF &r)
+{
+ if (m_rect == r)
+ return;
+
+ m_rect = r;
+ rebuildGeometry(&m_geometry, texture(), m_rect, m_sourceRect, m_texCoordMode);
+ markDirty(DirtyGeometry);
+}
+
+QRectF QSGDefaultImageNode::rect() const
+{
+ return m_rect;
+}
+
+void QSGDefaultImageNode::setSourceRect(const QRectF &r)
+{
+ if (m_sourceRect == r)
+ return;
+
+ m_sourceRect = r;
+ rebuildGeometry(&m_geometry, texture(), m_rect, m_sourceRect, m_texCoordMode);
+ markDirty(DirtyGeometry);
+}
+
+QRectF QSGDefaultImageNode::sourceRect() const
+{
+ return m_sourceRect;
+}
+
+void QSGDefaultImageNode::setTexture(QSGTexture *texture)
+{
+ Q_ASSERT(texture);
+ if (m_ownsTexture)
+ delete m_material.texture();
+ m_material.setTexture(texture);
+ m_opaque_material.setTexture(texture);
+ rebuildGeometry(&m_geometry, texture, m_rect, m_sourceRect, m_texCoordMode);
+
+ DirtyState dirty = DirtyMaterial;
+ // It would be tempting to skip the extra bit here and instead use
+ // m_material.texture to get the old state, but that texture could
+ // have been deleted in the mean time.
+ bool wasAtlas = m_isAtlasTexture;
+ m_isAtlasTexture = texture->isAtlasTexture();
+ if (wasAtlas || m_isAtlasTexture)
+ dirty |= DirtyGeometry;
+ markDirty(dirty);
+}
+
+QSGTexture *QSGDefaultImageNode::texture() const
+{
+ return m_material.texture();
+}
+
+void QSGDefaultImageNode::setTextureCoordinatesTransform(TextureCoordinatesTransformMode mode)
+{
+ if (m_texCoordMode == mode)
+ return;
+ m_texCoordMode = mode;
+ rebuildGeometry(&m_geometry, texture(), m_rect, m_sourceRect, m_texCoordMode);
+ markDirty(DirtyMaterial);
+}
+
+QSGDefaultImageNode::TextureCoordinatesTransformMode QSGDefaultImageNode::textureCoordinatesTransform() const
+{
+ return m_texCoordMode;
+}
+
+void QSGDefaultImageNode::setOwnsTexture(bool owns)
+{
+ m_ownsTexture = owns;
+}
+
+bool QSGDefaultImageNode::ownsTexture() const
+{
+ return m_ownsTexture;
+}
+
+void QSGDefaultImageNode::rebuildGeometry(QSGGeometry *g,
+ QSGTexture *texture,
+ const QRectF &rect,
+ QRectF sourceRect,
+ TextureCoordinatesTransformMode texCoordMode)
+{
+ if (!texture)
+ return;
+
+ if (!sourceRect.width() || !sourceRect.height()) {
+ QSize ts = texture->textureSize();
+ sourceRect = QRectF(0, 0, ts.width(), ts.height());
+ }
+
+ // Maybe transform the texture coordinates
+ if (texCoordMode.testFlag(QSGImageNode::MirrorHorizontally)) {
+ float tmp = sourceRect.left();
+ sourceRect.setLeft(sourceRect.right());
+ sourceRect.setRight(tmp);
+ }
+ if (texCoordMode.testFlag(QSGImageNode::MirrorVertically)) {
+ float tmp = sourceRect.top();
+ sourceRect.setTop(sourceRect.bottom());
+ sourceRect.setBottom(tmp);
+ }
+
+ QSGGeometry::updateTexturedRectGeometry(g, rect, texture->convertToNormalizedSourceRect(sourceRect));
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/util/qsgdefaultimagenode_p.h b/src/quick/scenegraph/util/qsgdefaultimagenode_p.h
new file mode 100644
index 0000000000..eb6c487c18
--- /dev/null
+++ b/src/quick/scenegraph/util/qsgdefaultimagenode_p.h
@@ -0,0 +1,107 @@
+/****************************************************************************
+**
+** 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 QSGDEFAULTIMAGENODE_P_H
+#define QSGDEFAULTIMAGENODE_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/private/qtquickglobal_p.h>
+#include <QtQuick/qsgimagenode.h>
+#include <QtQuick/qsggeometry.h>
+#include <QtQuick/qsgtexturematerial.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_QUICK_PRIVATE_EXPORT QSGDefaultImageNode : public QSGImageNode
+{
+public:
+ QSGDefaultImageNode();
+ ~QSGDefaultImageNode();
+
+ void setRect(const QRectF &rect) override;
+ QRectF rect() const override;
+
+ void setSourceRect(const QRectF &r) override;
+ QRectF sourceRect() const override;
+
+ void setTexture(QSGTexture *texture) override;
+ QSGTexture *texture() const override;
+
+ void setFiltering(QSGTexture::Filtering filtering) override;
+ QSGTexture::Filtering filtering() const override;
+
+ void setMipmapFiltering(QSGTexture::Filtering filtering) override;
+ QSGTexture::Filtering mipmapFiltering() const override;
+
+ void setTextureCoordinatesTransform(TextureCoordinatesTransformMode mode) override;
+ TextureCoordinatesTransformMode textureCoordinatesTransform() const override;
+
+ void setOwnsTexture(bool owns) override;
+ bool ownsTexture() const override;
+
+ static void rebuildGeometry(QSGGeometry *g,
+ QSGTexture *texture,
+ const QRectF &rect,
+ QRectF sourceRect,
+ TextureCoordinatesTransformMode texCoordMode);
+
+private:
+ QSGGeometry m_geometry;
+ QSGOpaqueTextureMaterial m_opaque_material;
+ QSGTextureMaterial m_material;
+ QRectF m_rect;
+ QRectF m_sourceRect;
+ TextureCoordinatesTransformMode m_texCoordMode;
+ uint m_isAtlasTexture : 1;
+ uint m_ownsTexture : 1;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGDEFAULTIMAGENODE_P_H
diff --git a/src/quick/scenegraph/util/qsgdefaultninepatchnode.cpp b/src/quick/scenegraph/util/qsgdefaultninepatchnode.cpp
new file mode 100644
index 0000000000..e5a53a3617
--- /dev/null
+++ b/src/quick/scenegraph/util/qsgdefaultninepatchnode.cpp
@@ -0,0 +1,136 @@
+/****************************************************************************
+**
+** 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 "qsgdefaultninepatchnode_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QSGDefaultNinePatchNode::QSGDefaultNinePatchNode()
+ : m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4)
+{
+ m_geometry.setDrawingMode(QSGGeometry::DrawTriangleStrip);
+ setGeometry(&m_geometry);
+ setMaterial(&m_material);
+}
+
+QSGDefaultNinePatchNode::~QSGDefaultNinePatchNode()
+{
+ delete m_material.texture();
+}
+
+void QSGDefaultNinePatchNode::setTexture(QSGTexture *texture)
+{
+ delete m_material.texture();
+ m_material.setTexture(texture);
+}
+
+void QSGDefaultNinePatchNode::setBounds(const QRectF &bounds)
+{
+ m_bounds = bounds;
+}
+
+void QSGDefaultNinePatchNode::setDevicePixelRatio(qreal ratio)
+{
+ m_devicePixelRatio = ratio;
+}
+
+void QSGDefaultNinePatchNode::setPadding(qreal left, qreal top, qreal right, qreal bottom)
+{
+ m_padding = QVector4D(left, top, right, bottom);
+}
+
+void QSGDefaultNinePatchNode::update()
+{
+ rebuildGeometry(m_material.texture(), &m_geometry, m_padding, m_bounds, m_devicePixelRatio);
+ markDirty(QSGNode::DirtyGeometry | QSGNode::DirtyMaterial);
+}
+
+void QSGDefaultNinePatchNode::rebuildGeometry(QSGTexture *texture, QSGGeometry *geometry, const QVector4D &padding,
+ const QRectF &bounds, qreal dpr)
+{
+ if (padding.x() <= 0 && padding.y() <= 0 && padding.z() <= 0 && padding.w() <= 0) {
+ geometry->allocate(4, 0);
+ QSGGeometry::updateTexturedRectGeometry(geometry, bounds, texture->normalizedTextureSubRect());
+ return;
+ }
+
+ QRectF tc = texture->normalizedTextureSubRect();
+ QSize ts = texture->textureSize();
+ ts.setHeight(ts.height() / dpr);
+ ts.setWidth(ts.width() / dpr);
+
+ qreal invtw = tc.width() / ts.width();
+ qreal invth = tc.height() / ts.height();
+
+ struct Coord { qreal p; qreal t; };
+ Coord cx[4] = { { bounds.left(), tc.left() },
+ { bounds.left() + padding.x(), tc.left() + padding.x() * invtw },
+ { bounds.right() - padding.z(), tc.right() - padding.z() * invtw },
+ { bounds.right(), tc.right() }
+ };
+ Coord cy[4] = { { bounds.top(), tc.top() },
+ { bounds.top() + padding.y(), tc.top() + padding.y() * invth },
+ { bounds.bottom() - padding.w(), tc.bottom() - padding.w() * invth },
+ { bounds.bottom(), tc.bottom() }
+ };
+
+ geometry->allocate(16, 28);
+ QSGGeometry::TexturedPoint2D *v = geometry->vertexDataAsTexturedPoint2D();
+ for (int y = 0; y < 4; ++y) {
+ for (int x = 0; x < 4; ++x) {
+ v->set(cx[x].p, cy[y].p, cx[x].t, cy[y].t);
+ ++v;
+ }
+ }
+
+ quint16 *i = geometry->indexDataAsUShort();
+ for (int r = 0; r < 3; ++r) {
+ if (r > 0)
+ *i++ = 4 * r;
+ for (int c = 0; c < 4; ++c) {
+ i[0] = 4 * r + c;
+ i[1] = 4 * r + c + 4;
+ i += 2;
+ }
+ if (r < 2)
+ *i++ = 4 * r + 3 + 4;
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/util/qsgdefaultninepatchnode_p.h b/src/quick/scenegraph/util/qsgdefaultninepatchnode_p.h
new file mode 100644
index 0000000000..675cf48f47
--- /dev/null
+++ b/src/quick/scenegraph/util/qsgdefaultninepatchnode_p.h
@@ -0,0 +1,86 @@
+/****************************************************************************
+**
+** 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 QSGDEFAULTNINEPATCHNODE_P_H
+#define QSGDEFAULTNINEPATCHNODE_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/qtquickglobal_p.h>
+#include <QtQuick/qsgninepatchnode.h>
+#include <QtQuick/qsggeometry.h>
+#include <QtQuick/qsgtexturematerial.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_QUICK_PRIVATE_EXPORT QSGDefaultNinePatchNode : public QSGNinePatchNode
+{
+public:
+ QSGDefaultNinePatchNode();
+ ~QSGDefaultNinePatchNode();
+
+ 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;
+
+ static void rebuildGeometry(QSGTexture *texture, QSGGeometry *geometry, const QVector4D &padding,
+ const QRectF &bounds, qreal dpr);
+
+private:
+ QRectF m_bounds;
+ qreal m_devicePixelRatio;
+ QVector4D m_padding;
+ QSGGeometry m_geometry;
+ QSGTextureMaterial m_material;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGDEFAULTNINEPATCHNODE_P_H
diff --git a/src/quick/scenegraph/util/qsgdefaultpainternode.cpp b/src/quick/scenegraph/util/qsgdefaultpainternode.cpp
index 16625b889b..389b9e0b4e 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 7e23264100..7488f7878d 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/qsgdefaultrectanglenode.cpp b/src/quick/scenegraph/util/qsgdefaultrectanglenode.cpp
new file mode 100644
index 0000000000..e1c8672add
--- /dev/null
+++ b/src/quick/scenegraph/util/qsgdefaultrectanglenode.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 "qsgdefaultrectanglenode_p.h"
+#include "qsgflatcolormaterial.h"
+
+QT_BEGIN_NAMESPACE
+
+// Unlike our predecessor, QSGSimpleRectNode, use QSGVertexColorMaterial
+// instead of Flat in order to allow better batching in the renderer.
+
+QSGDefaultRectangleNode::QSGDefaultRectangleNode()
+ : m_geometry(QSGGeometry::defaultAttributes_ColoredPoint2D(), 4)
+{
+ QSGGeometry::updateColoredRectGeometry(&m_geometry, QRectF());
+ setMaterial(&m_material);
+ setGeometry(&m_geometry);
+ setColor(QColor(255, 255, 255));
+#ifdef QSG_RUNTIME_DESCRIPTION
+ qsgnode_set_description(this, QLatin1String("rectangle"));
+#endif
+}
+
+void QSGDefaultRectangleNode::setRect(const QRectF &rect)
+{
+ QSGGeometry::updateColoredRectGeometry(&m_geometry, rect);
+ markDirty(QSGNode::DirtyGeometry);
+}
+
+QRectF QSGDefaultRectangleNode::rect() const
+{
+ const QSGGeometry::ColoredPoint2D *pts = m_geometry.vertexDataAsColoredPoint2D();
+ return QRectF(pts[0].x,
+ pts[0].y,
+ pts[3].x - pts[0].x,
+ pts[3].y - pts[0].y);
+}
+
+void QSGDefaultRectangleNode::setColor(const QColor &color)
+{
+ if (color != m_color) {
+ m_color = color;
+ QSGGeometry::ColoredPoint2D *pts = m_geometry.vertexDataAsColoredPoint2D();
+ for (int i = 0; i < 4; ++i) {
+ pts[i].r = uchar(qRound(m_color.redF() * m_color.alphaF() * 255));
+ pts[i].g = uchar(qRound(m_color.greenF() * m_color.alphaF() * 255));
+ pts[i].b = uchar(qRound(m_color.blueF() * m_color.alphaF() * 255));
+ pts[i].a = uchar(qRound(m_color.alphaF() * 255));
+ }
+ markDirty(QSGNode::DirtyGeometry);
+ }
+}
+
+QColor QSGDefaultRectangleNode::color() const
+{
+ return m_color;
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/util/qsgdefaultrectanglenode_p.h b/src/quick/scenegraph/util/qsgdefaultrectanglenode_p.h
new file mode 100644
index 0000000000..965aa8dabb
--- /dev/null
+++ b/src/quick/scenegraph/util/qsgdefaultrectanglenode_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 QSGDEFAULTRECTANGLENODE_P_H
+#define QSGDEFAULTRECTANGLENODE_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/qcolor.h>
+#include <QtQuick/qsgrectanglenode.h>
+#include <QtQuick/qsgvertexcolormaterial.h>
+
+QT_BEGIN_NAMESPACE
+
+class QSGDefaultRectangleNode : public QSGRectangleNode
+{
+public:
+ QSGDefaultRectangleNode();
+
+ void setRect(const QRectF &rect) override;
+ QRectF rect() const override;
+
+ void setColor(const QColor &color) override;
+ QColor color() const override;
+
+private:
+ QSGVertexColorMaterial m_material;
+ QSGGeometry m_geometry;
+ QColor m_color;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGDEFAULTRECTANGLENODE_P_H
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..ad1fcfa470 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())
{
}
@@ -111,15 +116,19 @@ QSGEngine::~QSGEngine()
void QSGEngine::initialize(QOpenGLContext *context)
{
Q_D(QSGEngine);
- if (QOpenGLContext::currentContext() != context) {
+#ifndef QT_NO_OPENGL
+ if (context && QOpenGLContext::currentContext() != context) {
qWarning("WARNING: The context must be current before calling QSGEngine::initialize.");
return;
}
-
- if (!d->sgRenderContext->isValid()) {
- d->sgRenderContext->setAttachToGLContext(false);
+#endif
+ if (d->sgRenderContext && !d->sgRenderContext->isValid()) {
+ d->sgRenderContext->setAttachToGraphicsContext(false);
d->sgRenderContext->initialize(context);
- connect(context, &QOpenGLContext::aboutToBeDestroyed, this, &QSGEngine::invalidate);
+#ifndef QT_NO_OPENGL
+ if (context)
+ connect(context, &QOpenGLContext::aboutToBeDestroyed, this, &QSGEngine::invalidate);
+#endif
}
}
@@ -198,4 +207,59 @@ 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;
+}
+
+/*!
+ Creates a simple rectangle node. When the scenegraph is not initialized, the return value is null.
+
+ This is cross-backend alternative to constructing a QSGSimpleRectNode directly.
+
+ \since 5.8
+ \sa QSGRectangleNode
+ */
+QSGRectangleNode *QSGEngine::createRectangleNode() const
+{
+ Q_D(const QSGEngine);
+ return d->sgRenderContext->isValid() ? d->sgRenderContext->sceneGraphContext()->createRectangleNode() : nullptr;
+}
+
+/*!
+ Creates a simple image node. When the scenegraph is not initialized, the return value is null.
+
+ This is cross-backend alternative to constructing a QSGSimpleTextureNode directly.
+
+ \since 5.8
+ \sa QSGImageNode
+ */
+
+QSGImageNode *QSGEngine::createImageNode() const
+{
+ Q_D(const QSGEngine);
+ return d->sgRenderContext->isValid() ? d->sgRenderContext->sceneGraphContext()->createImageNode() : nullptr;
+}
+
+/*!
+ Creates a nine patch node. When the scenegraph is not initialized, the return value is null.
+
+ \since 5.8
+ */
+
+QSGNinePatchNode *QSGEngine::createNinePatchNode() const
+{
+ Q_D(const QSGEngine);
+ return d->sgRenderContext->isValid() ? d->sgRenderContext->sceneGraphContext()->createNinePatchNode() : nullptr;
+}
+
QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/util/qsgengine.h b/src/quick/scenegraph/util/qsgengine.h
index 89af71fb47..3c8b61852e 100644
--- a/src/quick/scenegraph/util/qsgengine.h
+++ b/src/quick/scenegraph/util/qsgengine.h
@@ -49,6 +49,10 @@ class QOpenGLContext;
class QSGAbstractRenderer;
class QSGEnginePrivate;
class QSGTexture;
+class QSGRendererInterface;
+class QSGRectangleNode;
+class QSGImageNode;
+class QSGNinePatchNode;
class Q_QUICK_EXPORT QSGEngine : public QObject
{
@@ -72,6 +76,10 @@ 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;
+ QSGRectangleNode *createRectangleNode() const;
+ QSGImageNode *createImageNode() const;
+ QSGNinePatchNode *createNinePatchNode() const;
};
QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/util/qsgflatcolormaterial.cpp b/src/quick/scenegraph/util/qsgflatcolormaterial.cpp
index 836b5759a2..2ce27275cd 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
@@ -56,9 +57,10 @@ public:
private:
virtual void initialize();
-
+#ifndef QT_NO_OPENGL
int m_matrix_id;
int m_color_id;
+#endif
};
QSGMaterialType FlatColorMaterialShader::type;
@@ -66,14 +68,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 +94,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 +109,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
}
@@ -115,6 +126,9 @@ void FlatColorMaterialShader::initialize()
\inmodule QtQuick
\ingroup qtquick-scenegraph-materials
+ \warning This utility class is only functional when running with the OpenGL
+ backend of the Qt Quick scenegraph.
+
The flat color material will fill every pixel in a geometry using
a solid color. The color can contain transparency.
diff --git a/src/quick/scenegraph/util/qsgimagenode.cpp b/src/quick/scenegraph/util/qsgimagenode.cpp
new file mode 100644
index 0000000000..a78bfc1c66
--- /dev/null
+++ b/src/quick/scenegraph/util/qsgimagenode.cpp
@@ -0,0 +1,190 @@
+/****************************************************************************
+**
+** 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 "qsgimagenode.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QSGImageNode
+ \brief The QSGImageNode class is provided for convenience to easily draw
+ textured content using the QML scene graph.
+
+ \inmodule QtQuick
+ \since 5.8
+
+ \warning The image node class must have a texture before being
+ added to the scene graph to be rendered.
+ */
+
+/*!
+ \fn void QSGImageNode::setRect(const QRectF &rect)
+
+ Sets the target rect of this image node to \a rect.
+ */
+
+/*!
+ \fn void QSGImageNode::setRect(qreal x, qreal y, qreal w, qreal h)
+ \overload
+
+ Sets the rectangle of this image node to begin at (\a x, \a y) and have
+ width \a w and height \a h.
+ */
+
+/*!
+ \fn QRectF QSGImageNode::rect() const
+
+ Returns the target rect of this image node.
+ */
+
+/*!
+ \fn void QSGImageNode::setSourceRect(const QRectF &rect)
+
+ Sets the source rect of this image node to \a rect.
+ */
+
+/*!
+ \fn void QSGImageNode::setSourceRect(qreal x, qreal y, qreal w, qreal h)
+ \overload
+
+ Sets the rectangle of this image node to show its texture from (\a x, \a y) and
+ have width \a w and height \a h relatively to the QSGTexture::textureSize.
+ */
+
+/*!
+ \fn QRectF QSGImageNode::sourceRect() const
+
+ Returns the source rect of this image node.
+ */
+
+/*!
+ \fn void QSGImageNode::setTexture(QSGTexture *texture)
+
+ Sets the texture of this image node to \a texture.
+
+ Use setOwnsTexture() to set whether the node should take
+ ownership of the texture. By default, the node does not
+ take ownership.
+
+ \warning An image node must have a texture before being added to the
+ scenegraph to be rendered.
+ */
+
+/*!
+ \fn QSGTexture *QSGImageNode::texture() const
+
+ Returns the texture for this image node.
+ */
+
+/*!
+ \fn void QSGImageNode::setFiltering(QSGTexture::Filtering filtering)
+
+ Sets the filtering to be used for this image node to \a filtering.
+
+ For smooth scaling, use QSGTexture::Linear. For normal scaling, use
+ QSGTexture::Nearest.
+ */
+
+/*!
+ \fn QSGTexture::Filtering QSGImageNode::filtering() const
+
+ Returns the filtering for this image node.
+ */
+
+/*!
+ \fn void QSGImageNode::setMipmapFiltering(QSGTexture::Filtering filtering)
+
+ Sets the mipmap filtering to be used for this image node to \a filtering.
+
+ For smooth scaling between mip maps, use QSGTexture::Linear.
+ For normal scaling, use QSGTexture::Nearest.
+ */
+
+/*!
+ \fn QSGTexture::Filtering QSGImageNode::mipmapFiltering() const
+
+ Returns the mipmap filtering for this image node.
+ */
+
+/*!
+ \enum QSGImageNode::TextureCoordinatesTransformFlag
+
+ The TextureCoordinatesTransformFlag enum is used to specify the mode used
+ to generate texture coordinates for a textured quad.
+
+ \value NoTransform Texture coordinates are oriented with window coordinates
+ i.e. with origin at top-left.
+
+ \value MirrorHorizontally Texture coordinates are inverted in the horizontal axis with
+ respect to window coordinates
+
+ \value MirrorVertically Texture coordinates are inverted in the vertical axis with
+ respect to window coordinates
+ */
+
+/*!
+ \fn void QSGImageNode::setTextureCoordinatesTransform(TextureCoordinatesTransformMode mode)
+
+ Sets the method used to generate texture coordinates to \a mode. This can
+ be used to obtain correct orientation of the texture. This is commonly
+ needed when using a third-party OpenGL library to render to texture as
+ OpenGL has an inverted y-axis relative to Qt Quick.
+ */
+
+/*!
+ \fn QSGImageNode::TextureCoordinatesTransformMode textureCoordinatesTransform() const
+
+ Returns the mode used to generate texture coordinates for this node.
+ */
+
+/*!
+ \fn void QSGImageNode::setOwnsTexture(bool owns)
+
+ Sets whether the node takes ownership of the texture to \a owns.
+
+ By default, the node does not take ownership of the texture.
+ */
+
+/*!
+ \fn bool QSGImageNode::ownsTexture() const
+
+ \return \c true if the node takes ownership of the texture; otherwise \c false.
+ */
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/util/qsgimagenode.h b/src/quick/scenegraph/util/qsgimagenode.h
new file mode 100644
index 0000000000..7eab42c4e6
--- /dev/null
+++ b/src/quick/scenegraph/util/qsgimagenode.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 QSGIMAGENODE_H
+#define QSGIMAGENODE_H
+
+#include <QtQuick/qsgnode.h>
+#include <QtQuick/qsgtexture.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_QUICK_EXPORT QSGImageNode : public QSGGeometryNode
+{
+public:
+ virtual ~QSGImageNode() { }
+
+ virtual void setRect(const QRectF &rect) = 0;
+ inline void setRect(qreal x, qreal y, qreal w, qreal h) { setRect(QRectF(x, y, w, h)); }
+ virtual QRectF rect() const = 0;
+
+ virtual void setSourceRect(const QRectF &r) = 0;
+ inline void setSourceRect(qreal x, qreal y, qreal w, qreal h) { setSourceRect(QRectF(x, y, w, h)); }
+ virtual QRectF sourceRect() const = 0;
+
+ virtual void setTexture(QSGTexture *texture) = 0;
+ virtual QSGTexture *texture() const = 0;
+
+ virtual void setFiltering(QSGTexture::Filtering filtering) = 0;
+ virtual QSGTexture::Filtering filtering() const = 0;
+
+ virtual void setMipmapFiltering(QSGTexture::Filtering filtering) = 0;
+ virtual QSGTexture::Filtering mipmapFiltering() const = 0;
+
+ enum TextureCoordinatesTransformFlag {
+ NoTransform = 0x00,
+ MirrorHorizontally = 0x01,
+ MirrorVertically = 0x02
+ };
+ Q_DECLARE_FLAGS(TextureCoordinatesTransformMode, TextureCoordinatesTransformFlag)
+
+ virtual void setTextureCoordinatesTransform(TextureCoordinatesTransformMode mode) = 0;
+ virtual TextureCoordinatesTransformMode textureCoordinatesTransform() const = 0;
+
+ virtual void setOwnsTexture(bool owns) = 0;
+ virtual bool ownsTexture() const = 0;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QSGImageNode::TextureCoordinatesTransformMode)
+
+QT_END_NAMESPACE
+
+#endif // QSGIMAGENODE_H
diff --git a/src/quick/scenegraph/util/qsgninepatchnode.cpp b/src/quick/scenegraph/util/qsgninepatchnode.cpp
new file mode 100644
index 0000000000..9c167ca76f
--- /dev/null
+++ b/src/quick/scenegraph/util/qsgninepatchnode.cpp
@@ -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$
+**
+****************************************************************************/
+
+#include "qsgninepatchnode.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QSGNinePatchNode
+ \inmodule QtQuick
+ \since 5.8
+ \internal
+ */
+
+/*!
+ \fn void QSGNinePatchNode::setTexture(QSGTexture *texture)
+ \internal
+ */
+
+/*!
+ \fn void QSGNinePatchNode::setBounds(const QRectF &bounds)
+ \internal
+ */
+
+/*!
+ \fn void QSGNinePatchNode::setDevicePixelRatio(qreal ratio)
+ \internal
+ */
+
+/*!
+ \fn void QSGNinePatchNode::setPadding(qreal left, qreal top, qreal right, qreal bottom)
+ \internal
+ */
+
+
+/*!
+ \fn void QSGNinePatchNode::update()
+ \internal
+ */
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/util/qsgninepatchnode.h b/src/quick/scenegraph/util/qsgninepatchnode.h
new file mode 100644
index 0000000000..8677a432ba
--- /dev/null
+++ b/src/quick/scenegraph/util/qsgninepatchnode.h
@@ -0,0 +1,62 @@
+/****************************************************************************
+**
+** 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 QSGNINEPATCHNODE_H
+#define QSGNINEPATCHNODE_H
+
+#include <QtQuick/qsgnode.h>
+#include <QtQuick/qsgtexture.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_QUICK_EXPORT QSGNinePatchNode : public QSGGeometryNode
+{
+public:
+ virtual ~QSGNinePatchNode() { }
+
+ virtual void setTexture(QSGTexture *texture) = 0;
+ virtual void setBounds(const QRectF &bounds) = 0;
+ virtual void setDevicePixelRatio(qreal ratio) = 0;
+ virtual void setPadding(qreal left, qreal top, qreal right, qreal bottom) = 0;
+ virtual void update() = 0;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGNINEPATCHNODE_H
diff --git a/src/quick/scenegraph/util/qsgrectanglenode.cpp b/src/quick/scenegraph/util/qsgrectanglenode.cpp
new file mode 100644
index 0000000000..38c1f16a63
--- /dev/null
+++ b/src/quick/scenegraph/util/qsgrectanglenode.cpp
@@ -0,0 +1,86 @@
+/****************************************************************************
+**
+** 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 "qsgrectanglenode.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QSGRectangleNode
+
+ \brief The QSGRectangleNode class is a convenience class for drawing
+ solid filled rectangles using scenegraph.
+ \inmodule QtQuick
+ \since 5.8
+ */
+
+/*!
+ \fn void QSGRectangleNode::setRect(const QRectF &rect)
+
+ Sets the rectangle of this rect node to \a rect.
+ */
+
+/*!
+ \fn void QSGRectangleNode::setRect(qreal x, qreal y, qreal w, qreal h)
+ \overload
+
+ Sets the rectangle of this rect node to begin at (\a x, \a y) and have
+ width \a w and height \a h.
+ */
+
+/*!
+ \fn QRectF QSGRectangleNode::rect() const
+
+ Returns the rectangle that this rect node covers.
+ */
+
+/*!
+ \fn void QSGRectangleNode::setColor(const QColor &color)
+
+ Sets the color of this rectangle to \a color. The default color will be
+ white.
+ */
+
+/*!
+ \fn QColor QSGRectangleNode::color() const
+
+ Returns the color of this rectangle.
+ */
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/util/qsgrectanglenode.h b/src/quick/scenegraph/util/qsgrectanglenode.h
new file mode 100644
index 0000000000..8e0da1d9c7
--- /dev/null
+++ b/src/quick/scenegraph/util/qsgrectanglenode.h
@@ -0,0 +1,62 @@
+/****************************************************************************
+**
+** 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 QSGRECTANGLENODE_H
+#define QSGRECTANGLENODE_H
+
+#include <QtQuick/qsgnode.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_QUICK_EXPORT QSGRectangleNode : public QSGGeometryNode
+{
+public:
+ virtual ~QSGRectangleNode() { }
+
+ virtual void setRect(const QRectF &rect) = 0;
+ inline void setRect(qreal x, qreal y, qreal w, qreal h) { setRect(QRectF(x, y, w, h)); }
+ virtual QRectF rect() const = 0;
+
+ virtual void setColor(const QColor &color) = 0;
+ virtual QColor color() const = 0;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGRECTANGLENODE_H
diff --git a/src/quick/scenegraph/util/qsgsimplematerial.cpp b/src/quick/scenegraph/util/qsgsimplematerial.cpp
index 923179071b..7214a255df 100644
--- a/src/quick/scenegraph/util/qsgsimplematerial.cpp
+++ b/src/quick/scenegraph/util/qsgsimplematerial.cpp
@@ -41,11 +41,14 @@
\class QSGSimpleMaterialShader
\brief The QSGSimpleMaterialShader class provides a convenient way of
- building custom materials for the scene graph.
+ building custom OpenGL-based materials for the scene graph.
\inmodule QtQuick
\ingroup qtquick-scenegraph-materials
+ \warning This utility class is only functional when running with the OpenGL
+ backend of the Qt Quick scenegraph.
+
Where the QSGMaterial and QSGMaterialShader API requires a bit of
boilerplate code to create a functioning material, the
QSGSimpleMaterialShader tries to hide some of this through the use
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/qsgsimplerectnode.cpp b/src/quick/scenegraph/util/qsgsimplerectnode.cpp
index 3f6b8b0eec..28b177be84 100644
--- a/src/quick/scenegraph/util/qsgsimplerectnode.cpp
+++ b/src/quick/scenegraph/util/qsgsimplerectnode.cpp
@@ -49,6 +49,12 @@ QT_BEGIN_NAMESPACE
solid filled rectangles using scenegraph.
\inmodule QtQuick
+ \warning This utility class is only functional when running with the OpenGL
+ or software backends of the Qt Quick scenegraph. For a proper cross-platform
+ alternative prefer using QSGRectangleNode via
+ QQuickWindow::createRectangleNode() or QSGEngine::createRectangleNode().
+
+ \deprecated
*/
diff --git a/src/quick/scenegraph/util/qsgsimpletexturenode.cpp b/src/quick/scenegraph/util/qsgsimpletexturenode.cpp
index 1208a6bc72..6ce37de7cb 100644
--- a/src/quick/scenegraph/util/qsgsimpletexturenode.cpp
+++ b/src/quick/scenegraph/util/qsgsimpletexturenode.cpp
@@ -97,6 +97,13 @@ static void qsgsimpletexturenode_update(QSGGeometry *g,
\warning The simple texture node class must have a texture before being
added to the scene graph to be rendered.
+
+ \warning This utility class is only functional when running with the OpenGL
+ or software backends of the Qt Quick scenegraph. For a proper cross-platform
+ alternative prefer using QSGImageNode via
+ QQuickWindow::createImageNode() or QSGEngine::createImageNode().
+
+ \deprecated
*/
/*!
diff --git a/src/quick/scenegraph/util/qsgtexture.cpp b/src/quick/scenegraph/util/qsgtexture.cpp
index 9b9c77dce4..4cf339aeb8 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__)
@@ -68,7 +70,9 @@
#include <QHash>
#endif
+#ifndef QT_NO_OPENGL
static QElapsedTimer qsg_renderer_timer;
+#endif
#ifndef QT_NO_DEBUG
static const bool qsg_leak_check = !qEnvironmentVariableIsEmpty("QML_LEAK_CHECK");
@@ -82,11 +86,13 @@ static const bool qsg_leak_check = !qEnvironmentVariableIsEmpty("QML_LEAK_CHECK"
QT_BEGIN_NAMESPACE
+#ifndef QT_NO_OPENGL
inline static bool isPowerOfTwo(int x)
{
// Assumption: x >= 1
return x == (x & -x);
}
+#endif
QSGTexturePrivate::QSGTexturePrivate()
: wrapChanged(false)
@@ -278,6 +284,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 +292,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 +527,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 +562,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 +582,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 +617,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 +629,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 +643,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 +705,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 +810,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..119828bc81 100644
--- a/src/quick/scenegraph/util/qsgtexturematerial.cpp
+++ b/src/quick/scenegraph/util/qsgtexturematerial.cpp
@@ -39,25 +39,30 @@
#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
+#ifndef QT_NO_OPENGL
inline static bool isPowerOfTwo(int x)
{
// Assumption: x >= 1
return x == (x & -x);
}
+#endif
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 +73,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 +95,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 +106,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
}
@@ -118,6 +129,9 @@ void QSGOpaqueTextureMaterialShader::updateState(const RenderState &state, QSGMa
\inmodule QtQuick
\ingroup qtquick-scenegraph-materials
+ \warning This utility class is only functional when running with the OpenGL
+ backend of the Qt Quick scenegraph.
+
The opaque textured material will fill every pixel in a geometry with
the supplied texture. The material does not respect the opacity of the
QSGMaterialShader::RenderState, so opacity nodes in the parent chain
@@ -312,6 +326,9 @@ int QSGOpaqueTextureMaterial::compare(const QSGMaterial *o) const
\inmodule QtQuick
\ingroup qtquick-scenegraph-materials
+ \warning This utility class is only functional when running with the OpenGL
+ backend of the Qt Quick scenegraph.
+
The textured material will fill every pixel in a geometry with
the supplied texture.
@@ -362,22 +379,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..847ec289d8 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
@@ -55,9 +55,10 @@ public:
private:
virtual void initialize();
-
+#ifndef QT_NO_OPENGL
int m_matrix_id;
int m_opacity_id;
+#endif
};
QSGMaterialType QSGVertexColorMaterialShader::type;
@@ -65,17 +66,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 +93,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
}
@@ -99,6 +108,9 @@ void QSGVertexColorMaterialShader::initialize()
\inmodule QtQuick
\ingroup qtquick-scenegraph-materials
+ \warning This utility class is only functional when running with the OpenGL
+ backend of the Qt Quick scenegraph.
+
The vertex color material will give each vertex in a geometry a color. Pixels between
vertices will be linearly interpolated. The colors can contain transparency.
diff --git a/src/quick/util/qquickanimation.cpp b/src/quick/util/qquickanimation.cpp
index 741a583803..206b92eb81 100644
--- a/src/quick/util/qquickanimation.cpp
+++ b/src/quick/util/qquickanimation.cpp
@@ -1202,7 +1202,7 @@ QAbstractAnimationJob* QQuickPropertyAction::transition(QQuickStateActions &acti
{
for (int ii = 0; ii < actions.count(); ++ii) {
const QQuickStateAction &action = actions.at(ii);
- QQmlPropertyPrivate::write(action.property, action.toValue, QQmlPropertyPrivate::BypassInterceptor | QQmlPropertyPrivate::DontRemoveBinding);
+ QQmlPropertyPrivate::write(action.property, action.toValue, QQmlPropertyData::BypassInterceptor | QQmlPropertyData::DontRemoveBinding);
}
}
virtual void debugAction(QDebug d, int indentLevel) const {
@@ -2535,7 +2535,7 @@ void QQuickAnimationPropertyUpdater::setValue(qreal v)
QQuickStateAction &action = actions[ii];
if (v == 1.) {
- QQmlPropertyPrivate::write(action.property, action.toValue, QQmlPropertyPrivate::BypassInterceptor | QQmlPropertyPrivate::DontRemoveBinding);
+ QQmlPropertyPrivate::write(action.property, action.toValue, QQmlPropertyData::BypassInterceptor | QQmlPropertyData::DontRemoveBinding);
} else {
if (!fromSourced && !fromDefined) {
action.fromValue = action.property.read();
@@ -2551,7 +2551,7 @@ void QQuickAnimationPropertyUpdater::setValue(qreal v)
}
}
if (interpolator)
- QQmlPropertyPrivate::write(action.property, interpolator(action.fromValue.constData(), action.toValue.constData(), v), QQmlPropertyPrivate::BypassInterceptor | QQmlPropertyPrivate::DontRemoveBinding);
+ QQmlPropertyPrivate::write(action.property, interpolator(action.fromValue.constData(), action.toValue.constData(), v), QQmlPropertyData::BypassInterceptor | QQmlPropertyData::DontRemoveBinding);
}
if (deleted)
return;
@@ -2643,7 +2643,7 @@ QQuickStateActions QQuickPropertyAnimation::createTransitionActions(QQuickStateA
}
if (!successfullyCreatedDefaultProperty) {
- foreach (const QString &errorMessage, errorMessages)
+ for (const QString &errorMessage : qAsConst(errorMessages))
qmlInfo(this) << errorMessage;
}
}
diff --git a/src/quick/util/qquickanimator.cpp b/src/quick/util/qquickanimator.cpp
index abae6321b0..5d2af0f940 100644
--- a/src/quick/util/qquickanimator.cpp
+++ b/src/quick/util/qquickanimator.cpp
@@ -503,6 +503,7 @@ QQuickRotationAnimator::RotationDirection QQuickRotationAnimator::direction() co
return d->direction;
}
+#if QT_CONFIG(quick_shadereffect) && QT_CONFIG(opengl)
/*!
\qmltype UniformAnimator
\instantiates QQuickUniformAnimator
@@ -580,5 +581,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..0fc900c5ac 100644
--- a/src/quick/util/qquickanimator_p.h
+++ b/src/quick/util/qquickanimator_p.h
@@ -170,6 +170,7 @@ protected:
QString propertyName() const { return QStringLiteral("rotation"); }
};
+#if QT_CONFIG(quick_shadereffect) && QT_CONFIG(opengl)
class QQuickUniformAnimatorPrivate;
class Q_QUICK_PRIVATE_EXPORT QQuickUniformAnimator : public QQuickAnimator
{
@@ -190,6 +191,7 @@ protected:
QQuickAnimatorJob *createJob() const;
QString propertyName() const;
};
+#endif
QT_END_NAMESPACE
@@ -199,6 +201,7 @@ QML_DECLARE_TYPE(QQuickYAnimator)
QML_DECLARE_TYPE(QQuickScaleAnimator)
QML_DECLARE_TYPE(QQuickRotationAnimator)
QML_DECLARE_TYPE(QQuickOpacityAnimator)
+#if QT_CONFIG(quick_shadereffect) && QT_CONFIG(opengl)
QML_DECLARE_TYPE(QQuickUniformAnimator)
-
+#endif
#endif // QQUICKANIMATOR_P_H
diff --git a/src/quick/util/qquickanimatorcontroller.cpp b/src/quick/util/qquickanimatorcontroller.cpp
index 3fc7d87840..6d8167413e 100644
--- a/src/quick/util/qquickanimatorcontroller.cpp
+++ b/src/quick/util/qquickanimatorcontroller.cpp
@@ -69,14 +69,14 @@ QQuickAnimatorController::~QQuickAnimatorController()
{
// The proxy job might already have been deleted, in which case we
// need to avoid calling functions on them. Then delete the job.
- foreach (QAbstractAnimationJob *job, m_deleting) {
+ for (QAbstractAnimationJob *job : qAsConst(m_deleting)) {
m_starting.take(job);
m_stopping.take(job);
m_animatorRoots.take(job);
delete job;
}
- foreach (QQuickAnimatorProxyJob *proxy, m_animatorRoots)
+ for (QQuickAnimatorProxyJob *proxy : qAsConst(m_animatorRoots))
proxy->controllerWasDeleted();
for (auto it = m_animatorRoots.keyBegin(), end = m_animatorRoots.keyEnd(); it != end; ++it)
delete *it;
@@ -171,7 +171,7 @@ static void qquick_initialize_helper(QAbstractAnimationJob *job, QQuickAnimatorC
void QQuickAnimatorController::beforeNodeSync()
{
- foreach (QAbstractAnimationJob *job, m_deleting) {
+ for (QAbstractAnimationJob *job : qAsConst(m_deleting)) {
m_starting.take(job);
m_stopping.take(job);
m_animatorRoots.take(job);
@@ -182,7 +182,7 @@ void QQuickAnimatorController::beforeNodeSync()
if (m_starting.size())
m_window->update();
- foreach (QQuickAnimatorProxyJob *proxy, m_starting) {
+ for (QQuickAnimatorProxyJob *proxy : qAsConst(m_starting)) {
QAbstractAnimationJob *job = proxy->job();
job->addAnimationChangeListener(this, QAbstractAnimationJob::Completion);
qquick_initialize_helper(job, this, true);
@@ -192,7 +192,7 @@ void QQuickAnimatorController::beforeNodeSync()
}
m_starting.clear();
- foreach (QQuickAnimatorProxyJob *proxy, m_stopping) {
+ for (QQuickAnimatorProxyJob *proxy : qAsConst(m_stopping)) {
QAbstractAnimationJob *job = proxy->job();
job->stop();
}
@@ -208,7 +208,7 @@ void QQuickAnimatorController::beforeNodeSync()
m_nodesAreInvalid = false;
}
- foreach (QQuickAnimatorJob *job, m_activeLeafAnimations) {
+ for (QQuickAnimatorJob *job : qAsConst(m_activeLeafAnimations)) {
if (!job->target())
continue;
else if (m_deletedSinceLastFrame.contains(job->target()))
@@ -218,7 +218,7 @@ void QQuickAnimatorController::beforeNodeSync()
xform->transformHelper()->sync();
}
}
- foreach (QQuickItem *wiped, m_deletedSinceLastFrame) {
+ for (QQuickItem *wiped : qAsConst(m_deletedSinceLastFrame)) {
QQuickTransformAnimatorJob::Helper *helper = m_transforms.take(wiped);
// Helper will now already have been reset in all animators referencing it.
delete helper;
@@ -229,7 +229,7 @@ void QQuickAnimatorController::beforeNodeSync()
void QQuickAnimatorController::afterNodeSync()
{
- foreach (QQuickAnimatorJob *job, m_activeLeafAnimations) {
+ for (QQuickAnimatorJob *job : qAsConst(m_activeLeafAnimations)) {
if (job->target())
job->afterNodeSync();
}
@@ -249,10 +249,10 @@ void QQuickAnimatorController::stopProxyJobs()
// to be outside the lock. It is also safe because deletion of
// proxies happens on the GUI thread, where this code is also executing.
lock();
- QSet<QQuickAnimatorProxyJob *> jobs = m_proxiesToStop;
+ const QSet<QQuickAnimatorProxyJob *> jobs = m_proxiesToStop;
m_proxiesToStop.clear();
unlock();
- foreach (QQuickAnimatorProxyJob *p, jobs)
+ for (QQuickAnimatorProxyJob *p : jobs)
p->stop();
}
@@ -265,7 +265,7 @@ void QQuickAnimatorController::animationFinished(QAbstractAnimationJob *job)
* needed in any case.
*/
if (!m_deleting.contains(job)) {
- QQuickAnimatorProxyJob *proxy = m_animatorRoots[job];
+ QQuickAnimatorProxyJob *proxy = m_animatorRoots.value(job);
if (proxy) {
m_window->update();
m_proxiesToStop << proxy;
diff --git a/src/quick/util/qquickanimatorjob.cpp b/src/quick/util/qquickanimatorjob.cpp
index 8d5ecadab5..1176cf1ff7 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>
-
+#if QT_CONFIG(quick_shadereffect) && QT_CONFIG(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()));
@@ -327,11 +329,13 @@ void QQuickTransformAnimatorJob::Helper::sync()
| QQuickItemPrivate::Size;
QQuickItemPrivate *d = QQuickItemPrivate::get(item);
+#if QT_CONFIG(quick_shadereffect)
if (d->extra.isAllocated()
&& d->extra->layer
&& d->extra->layer->enabled()) {
d = QQuickItemPrivate::get(d->extra->layer->m_effectSource);
}
+#endif
quint32 dirty = mask & d->dirtyAttributes;
@@ -390,7 +394,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 +410,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;
@@ -423,11 +425,13 @@ void QQuickOpacityAnimatorJob::initialize(QQuickAnimatorController *controller)
{
QQuickAnimatorJob::initialize(controller);
QQuickItemPrivate *d = QQuickItemPrivate::get(m_target);
+#if QT_CONFIG(quick_shadereffect)
if (d->extra.isAllocated()
&& d->extra->layer
&& d->extra->layer->enabled()) {
d = QQuickItemPrivate::get(d->extra->layer->m_effectSource);
}
+#endif
m_opacityNode = d->opacityNode();
if (!m_opacityNode) {
@@ -477,7 +481,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 +496,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 +515,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 +547,7 @@ void QQuickRotationAnimatorJob::writeBack()
m_target->setRotation(value());
}
+#if QT_CONFIG(quick_shadereffect) && QT_CONFIG(opengl)
QQuickUniformAnimatorJob::QQuickUniformAnimatorJob()
: m_node(0)
, m_uniformIndex(-1)
@@ -556,7 +558,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 +571,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 +596,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 +616,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/qquickapplication.cpp b/src/quick/util/qquickapplication.cpp
index a4e2f0eb0e..5c26b23ff7 100644
--- a/src/quick/util/qquickapplication.cpp
+++ b/src/quick/util/qquickapplication.cpp
@@ -90,4 +90,9 @@ Qt::ApplicationState QQuickApplication::state() const
return QGuiApplication::applicationState();
}
+QFont QQuickApplication::font() const
+{
+ return QGuiApplication::font();
+}
+
QT_END_NAMESPACE
diff --git a/src/quick/util/qquickapplication_p.h b/src/quick/util/qquickapplication_p.h
index 971c9a203a..091cb094ed 100644
--- a/src/quick/util/qquickapplication_p.h
+++ b/src/quick/util/qquickapplication_p.h
@@ -52,6 +52,7 @@
//
#include <QtCore/QObject>
+#include <QtGui/QFont>
#include <qqml.h>
#include <QtQml/private/qqmlglobal_p.h>
#include <private/qtquickglobal_p.h>
@@ -66,6 +67,7 @@ class Q_AUTOTEST_EXPORT QQuickApplication : public QQmlApplication
Q_PROPERTY(Qt::LayoutDirection layoutDirection READ layoutDirection NOTIFY layoutDirectionChanged)
Q_PROPERTY(bool supportsMultipleWindows READ supportsMultipleWindows CONSTANT)
Q_PROPERTY(Qt::ApplicationState state READ state NOTIFY stateChanged)
+ Q_PROPERTY(QFont font READ font CONSTANT)
public:
explicit QQuickApplication(QObject *parent = 0);
@@ -74,6 +76,7 @@ public:
Qt::LayoutDirection layoutDirection() const;
bool supportsMultipleWindows() const;
Qt::ApplicationState state() const;
+ QFont font() const;
Q_SIGNALS:
void activeChanged();
diff --git a/src/quick/util/qquickbehavior.cpp b/src/quick/util/qquickbehavior.cpp
index 147380037d..1d3ee2c4be 100644
--- a/src/quick/util/qquickbehavior.cpp
+++ b/src/quick/util/qquickbehavior.cpp
@@ -180,7 +180,7 @@ void QQuickBehavior::write(const QVariant &value)
if (!d->animation || bypass) {
if (d->animationInstance)
d->animationInstance->stop();
- QQmlPropertyPrivate::write(d->property, value, QQmlPropertyPrivate::BypassInterceptor | QQmlPropertyPrivate::DontRemoveBinding);
+ QQmlPropertyPrivate::write(d->property, value, QQmlPropertyData::BypassInterceptor | QQmlPropertyData::DontRemoveBinding);
d->targetValue = value;
return;
}
@@ -206,7 +206,7 @@ void QQuickBehavior::write(const QVariant &value)
// is needed (value has not changed). If the Behavior was already
// running, let it continue as normal to ensure correct behavior and state.
if (!behaviorActive && d->targetValue == currentValue) {
- QQmlPropertyPrivate::write(d->property, value, QQmlPropertyPrivate::BypassInterceptor | QQmlPropertyPrivate::DontRemoveBinding);
+ QQmlPropertyPrivate::write(d->property, value, QQmlPropertyData::BypassInterceptor | QQmlPropertyData::DontRemoveBinding);
return;
}
@@ -234,7 +234,7 @@ void QQuickBehavior::write(const QVariant &value)
d->blockRunningChanged = false;
}
if (!after.contains(d->property))
- QQmlPropertyPrivate::write(d->property, value, QQmlPropertyPrivate::BypassInterceptor | QQmlPropertyPrivate::DontRemoveBinding);
+ QQmlPropertyPrivate::write(d->property, value, QQmlPropertyData::BypassInterceptor | QQmlPropertyData::DontRemoveBinding);
}
void QQuickBehavior::setTarget(const QQmlProperty &property)
diff --git a/src/quick/util/qquickfontloader.cpp b/src/quick/util/qquickfontloader.cpp
index 64fd458ef0..d13291c30a 100644
--- a/src/quick/util/qquickfontloader.cpp
+++ b/src/quick/util/qquickfontloader.cpp
@@ -45,14 +45,18 @@
#include <QStringList>
#include <QUrl>
#include <QDebug>
-#include <QNetworkRequest>
-#include <QNetworkReply>
+
#include <QFontDatabase>
#include <private/qobject_p.h>
#include <qqmlinfo.h>
#include <qqmlfile.h>
+#if QT_CONFIG(qml_network)
+#include <QNetworkRequest>
+#include <QNetworkReply>
+#endif
+
#include <QtCore/QCoreApplication>
QT_BEGIN_NAMESPACE
@@ -66,28 +70,37 @@ Q_OBJECT
public:
explicit QQuickFontObject(int _id = -1);
+#if QT_CONFIG(qml_network)
void download(const QUrl &url, QNetworkAccessManager *manager);
Q_SIGNALS:
void fontDownloaded(const QString&, QQuickFontLoader::Status);
+private:
+ int redirectCount;
+ QNetworkReply *reply;
+
private Q_SLOTS:
void replyFinished();
+#endif // qml_network
public:
int id;
-private:
- QNetworkReply *reply;
- int redirectCount;
-
Q_DISABLE_COPY(QQuickFontObject)
};
QQuickFontObject::QQuickFontObject(int _id)
- : QObject(0), id(_id), reply(0), redirectCount(0) {}
+ : QObject(0)
+#if QT_CONFIG(qml_network)
+ ,redirectCount(0), reply(0)
+#endif
+ ,id(_id)
+{
+}
+#if QT_CONFIG(qml_network)
void QQuickFontObject::download(const QUrl &url, QNetworkAccessManager *manager)
{
QNetworkRequest req(url);
@@ -128,7 +141,7 @@ void QQuickFontObject::replyFinished()
reply = 0;
}
}
-
+#endif // qml_network
class QQuickFontLoaderPrivate : public QObjectPrivate
{
@@ -251,10 +264,11 @@ void QQuickFontLoader::setSource(const QUrl &url)
updateFontInfo(QString(), Error);
}
} else {
- updateFontInfo(QFontDatabase::applicationFontFamilies(fontLoaderFonts()->map[d->url]->id).at(0), Ready);
+ updateFontInfo(QFontDatabase::applicationFontFamilies(fontLoaderFonts()->map.value(d->url)->id).at(0), Ready);
}
} else {
if (!fontLoaderFonts()->map.contains(d->url)) {
+#if QT_CONFIG(qml_network)
QQuickFontObject *fo = new QQuickFontObject;
fontLoaderFonts()->map[d->url] = fo;
fo->download(d->url, qmlEngine(this)->networkAccessManager());
@@ -262,13 +276,20 @@ void QQuickFontLoader::setSource(const QUrl &url)
emit statusChanged();
QObject::connect(fo, SIGNAL(fontDownloaded(QString,QQuickFontLoader::Status)),
this, SLOT(updateFontInfo(QString,QQuickFontLoader::Status)));
+#else
+// Silently fail if compiled with no_network
+#endif
} else {
- QQuickFontObject *fo = fontLoaderFonts()->map[d->url];
+ QQuickFontObject *fo = fontLoaderFonts()->map.value(d->url);
if (fo->id == -1) {
+#if QT_CONFIG(qml_network)
d->status = Loading;
emit statusChanged();
QObject::connect(fo, SIGNAL(fontDownloaded(QString,QQuickFontLoader::Status)),
this, SLOT(updateFontInfo(QString,QQuickFontLoader::Status)));
+#else
+// Silently fail if compiled with no_network
+#endif
}
else
updateFontInfo(QFontDatabase::applicationFontFamilies(fo->id).at(0), Ready);
diff --git a/src/quick/util/qquickglobal.cpp b/src/quick/util/qquickglobal.cpp
index be4c968ab2..7692cc79f9 100644
--- a/src/quick/util/qquickglobal.cpp
+++ b/src/quick/util/qquickglobal.cpp
@@ -301,6 +301,7 @@ public:
QV4::ScopedValue vundl(scope, obj->get((s = v4->newString(QStringLiteral("underline")))));
QV4::ScopedValue vweight(scope, obj->get((s = v4->newString(QStringLiteral("weight")))));
QV4::ScopedValue vwspac(scope, obj->get((s = v4->newString(QStringLiteral("wordSpacing")))));
+ QV4::ScopedValue vhint(scope, obj->get((s = v4->newString(QStringLiteral("hintingPreference")))));
// pull out the values, set ok to true if at least one valid field is given.
if (vbold->isBoolean()) {
@@ -351,6 +352,10 @@ public:
retn.setWordSpacing(vwspac->asDouble());
if (ok) *ok = true;
}
+ if (vhint->isInt32()) {
+ retn.setHintingPreference(static_cast<QFont::HintingPreference>(vhint->integerValue()));
+ if (ok) *ok = true;
+ }
return retn;
}
diff --git a/src/quick/util/qquickpath.cpp b/src/quick/util/qquickpath.cpp
index 6b491a433c..25a4433a9b 100644
--- a/src/quick/util/qquickpath.cpp
+++ b/src/quick/util/qquickpath.cpp
@@ -358,7 +358,7 @@ QPainterPath QQuickPath::createPath(const QPointF &startPoint, const QPointF &en
bool usesPercent = false;
int index = 0;
- foreach (QQuickPathElement *pathElement, d->_pathElements) {
+ for (QQuickPathElement *pathElement : qAsConst(d->_pathElements)) {
if (QQuickCurve *curve = qobject_cast<QQuickCurve *>(pathElement)) {
QQuickPathData data;
data.index = index;
@@ -382,7 +382,7 @@ QPainterPath QQuickPath::createPath(const QPointF &startPoint, const QPointF &en
}
// Fixup end points
- const AttributePoint &last = attributePoints.last();
+ const AttributePoint &last = attributePoints.constLast();
for (int ii = 0; ii < attributes.count(); ++ii) {
if (!last.values.contains(attributes.at(ii)))
endpoint(attributePoints, attributes.at(ii));
@@ -407,11 +407,11 @@ QPainterPath QQuickPath::createPath(const QPointF &startPoint, const QPointF &en
}
attributePoints[ii].origpercent /= length;
attributePoints[ii].percent = point.values.value(percentString);
- prevorigpercent = attributePoints[ii].origpercent;
- prevpercent = attributePoints[ii].percent;
+ prevorigpercent = attributePoints.at(ii).origpercent;
+ prevpercent = attributePoints.at(ii).percent;
} else {
attributePoints[ii].origpercent /= length;
- attributePoints[ii].percent = attributePoints[ii].origpercent;
+ attributePoints[ii].percent = attributePoints.at(ii).origpercent;
}
}
@@ -432,17 +432,17 @@ void QQuickPath::classBegin()
void QQuickPath::disconnectPathElements()
{
- Q_D(QQuickPath);
+ Q_D(const QQuickPath);
- foreach (QQuickPathElement *pathElement, d->_pathElements)
+ for (QQuickPathElement *pathElement : d->_pathElements)
disconnect(pathElement, SIGNAL(changed()), this, SLOT(processPath()));
}
void QQuickPath::connectPathElements()
{
- Q_D(QQuickPath);
+ Q_D(const QQuickPath);
- foreach (QQuickPathElement *pathElement, d->_pathElements)
+ for (QQuickPathElement *pathElement : d->_pathElements)
connect(pathElement, SIGNAL(changed()), this, SLOT(processPath()));
}
@@ -453,7 +453,7 @@ void QQuickPath::gatherAttributes()
QSet<QString> attributes;
// First gather up all the attributes
- foreach (QQuickPathElement *pathElement, d->_pathElements) {
+ for (QQuickPathElement *pathElement : qAsConst(d->_pathElements)) {
if (QQuickCurve *curve = qobject_cast<QQuickCurve *>(pathElement))
d->_pathCurves.append(curve);
else if (QQuickPathAttribute *attribute = qobject_cast<QQuickPathAttribute *>(pathElement))
@@ -488,7 +488,7 @@ QStringList QQuickPath::attributes() const
QSet<QString> attrs;
// First gather up all the attributes
- foreach (QQuickPathElement *pathElement, d->_pathElements) {
+ for (QQuickPathElement *pathElement : d->_pathElements) {
if (QQuickPathAttribute *attribute =
qobject_cast<QQuickPathAttribute *>(pathElement))
attrs.insert(attribute->name());
diff --git a/src/quick/util/qquickpath_p.h b/src/quick/util/qquickpath_p.h
index e2c99de44e..457f69d20f 100644
--- a/src/quick/util/qquickpath_p.h
+++ b/src/quick/util/qquickpath_p.h
@@ -51,10 +51,15 @@
// We mean it.
//
+#include <private/qtquickglobal_p.h>
+
+QT_REQUIRE_CONFIG(quick_path);
+
#include <qqml.h>
#include <private/qqmlnullablevalue_p.h>
#include <private/qbezier_p.h>
+#include <private/qtquickglobal_p.h>
#include <QtCore/QObject>
#include <QtGui/QPainterPath>
@@ -69,7 +74,7 @@ struct QQuickPathData
QList<QQuickCurve*> curves;
};
-class Q_AUTOTEST_EXPORT QQuickPathElement : public QObject
+class Q_QUICK_PRIVATE_EXPORT QQuickPathElement : public QObject
{
Q_OBJECT
public:
@@ -78,7 +83,7 @@ Q_SIGNALS:
void changed();
};
-class Q_AUTOTEST_EXPORT QQuickPathAttribute : public QQuickPathElement
+class Q_QUICK_PRIVATE_EXPORT QQuickPathAttribute : public QQuickPathElement
{
Q_OBJECT
@@ -103,7 +108,7 @@ private:
qreal _value;
};
-class Q_AUTOTEST_EXPORT QQuickCurve : public QQuickPathElement
+class Q_QUICK_PRIVATE_EXPORT QQuickCurve : public QQuickPathElement
{
Q_OBJECT
@@ -145,7 +150,7 @@ private:
QQmlNullableValue<qreal> _relativeY;
};
-class Q_AUTOTEST_EXPORT QQuickPathLine : public QQuickCurve
+class Q_QUICK_PRIVATE_EXPORT QQuickPathLine : public QQuickCurve
{
Q_OBJECT
public:
@@ -154,7 +159,7 @@ public:
void addToPath(QPainterPath &path, const QQuickPathData &);
};
-class Q_AUTOTEST_EXPORT QQuickPathQuad : public QQuickCurve
+class Q_QUICK_PRIVATE_EXPORT QQuickPathQuad : public QQuickCurve
{
Q_OBJECT
@@ -194,7 +199,7 @@ private:
QQmlNullableValue<qreal> _relativeControlY;
};
-class Q_AUTOTEST_EXPORT QQuickPathCubic : public QQuickCurve
+class Q_QUICK_PRIVATE_EXPORT QQuickPathCubic : public QQuickCurve
{
Q_OBJECT
@@ -260,7 +265,7 @@ private:
QQmlNullableValue<qreal> _relativeControl2Y;
};
-class Q_AUTOTEST_EXPORT QQuickPathCatmullRomCurve : public QQuickCurve
+class Q_QUICK_PRIVATE_EXPORT QQuickPathCatmullRomCurve : public QQuickCurve
{
Q_OBJECT
public:
@@ -269,7 +274,7 @@ public:
void addToPath(QPainterPath &path, const QQuickPathData &);
};
-class Q_AUTOTEST_EXPORT QQuickPathArc : public QQuickCurve
+class Q_QUICK_PRIVATE_EXPORT QQuickPathArc : public QQuickCurve
{
Q_OBJECT
Q_PROPERTY(qreal radiusX READ radiusX WRITE setRadiusX NOTIFY radiusXChanged)
@@ -311,7 +316,7 @@ private:
ArcDirection _direction;
};
-class Q_AUTOTEST_EXPORT QQuickPathSvg : public QQuickCurve
+class Q_QUICK_PRIVATE_EXPORT QQuickPathSvg : public QQuickCurve
{
Q_OBJECT
Q_PROPERTY(QString path READ path WRITE setPath NOTIFY pathChanged)
@@ -330,7 +335,7 @@ private:
QString _path;
};
-class Q_AUTOTEST_EXPORT QQuickPathPercent : public QQuickPathElement
+class Q_QUICK_PRIVATE_EXPORT QQuickPathPercent : public QQuickPathElement
{
Q_OBJECT
Q_PROPERTY(qreal value READ value WRITE setValue NOTIFY valueChanged)
@@ -359,7 +364,7 @@ struct QQuickCachedBezier
};
class QQuickPathPrivate;
-class Q_AUTOTEST_EXPORT QQuickPath : public QObject, public QQmlParserStatus
+class Q_QUICK_PRIVATE_EXPORT QQuickPath : public QObject, public QQmlParserStatus
{
Q_OBJECT
diff --git a/src/quick/util/qquickpath_p_p.h b/src/quick/util/qquickpath_p_p.h
index 3e4ccc7eb6..1dc3c1c47a 100644
--- a/src/quick/util/qquickpath_p_p.h
+++ b/src/quick/util/qquickpath_p_p.h
@@ -51,6 +51,10 @@
// We mean it.
//
+#include <private/qtquickglobal_p.h>
+
+QT_REQUIRE_CONFIG(quick_path);
+
#include "qquickpath_p.h"
#include <qqml.h>
diff --git a/src/quick/util/qquickpathinterpolator_p.h b/src/quick/util/qquickpathinterpolator_p.h
index ce18190977..0fdb1a444f 100644
--- a/src/quick/util/qquickpathinterpolator_p.h
+++ b/src/quick/util/qquickpathinterpolator_p.h
@@ -51,6 +51,10 @@
// We mean it.
//
+#include <private/qtquickglobal_p.h>
+
+QT_REQUIRE_CONFIG(quick_path);
+
#include <qqml.h>
#include <QObject>
diff --git a/src/quick/util/qquickpixmapcache.cpp b/src/quick/util/qquickpixmapcache.cpp
index 50b867125e..96b88636fe 100644
--- a/src/quick/util/qquickpixmapcache.cpp
+++ b/src/quick/util/qquickpixmapcache.cpp
@@ -38,7 +38,6 @@
****************************************************************************/
#include "qquickpixmapcache_p.h"
-#include <qqmlnetworkaccessmanagerfactory.h>
#include <qquickimageprovider.h>
#include <qqmlengine.h>
@@ -55,7 +54,6 @@
#include <QCoreApplication>
#include <QImageReader>
#include <QHash>
-#include <QNetworkReply>
#include <QPixmapCache>
#include <QFile>
#include <QThread>
@@ -66,10 +64,15 @@
#include <QWaitCondition>
#include <QtCore/qdebug.h>
#include <private/qobject_p.h>
-#include <QSslError>
#include <QQmlFile>
#include <QMetaMethod>
+#if QT_CONFIG(qml_network)
+#include <qqmlnetworkaccessmanagerfactory.h>
+#include <QNetworkReply>
+#include <QSslError>
+#endif
+
#include <private/qquickprofiler_p.h>
#define IMAGEREQUEST_MAX_NETWORK_REQUEST_COUNT 8
@@ -203,7 +206,9 @@ private:
friend class QQuickPixmapReaderThreadObject;
void processJobs();
void processJob(QQuickPixmapReply *, const QUrl &, const QString &, AutoTransform, QQuickImageProvider::ImageType, QQuickImageProvider *);
+#if QT_CONFIG(qml_network)
void networkRequestDone(QNetworkReply *);
+#endif
void asyncResponseFinished(QQuickImageResponse *);
QList<QQuickPixmapReply*> jobs;
@@ -215,10 +220,11 @@ private:
QQuickPixmapReaderThreadObject *threadObject;
QWaitCondition waitCondition;
+#if QT_CONFIG(qml_network)
QNetworkAccessManager *networkAccessManager();
QNetworkAccessManager *accessManager;
-
QHash<QNetworkReply*,QQuickPixmapReply*> networkJobs;
+#endif
QHash<QQuickImageResponse*,QQuickPixmapReply*> asyncResponses;
static int replyDownloadProgress;
@@ -343,6 +349,7 @@ QQuickPixmapReply::Event::~Event()
delete textureFactory;
}
+#if QT_CONFIG(qml_network)
QNetworkAccessManager *QQuickPixmapReader::networkAccessManager()
{
if (!accessManager) {
@@ -351,6 +358,7 @@ QNetworkAccessManager *QQuickPixmapReader::networkAccessManager()
}
return accessManager;
}
+#endif
static void maybeRemoveAlpha(QImage *image)
{
@@ -421,7 +429,10 @@ static bool readImage(const QUrl& url, QIODevice *dev, QImage *image, QString *e
}
QQuickPixmapReader::QQuickPixmapReader(QQmlEngine *eng)
-: QThread(eng), engine(eng), threadObject(0), accessManager(0)
+: QThread(eng), engine(eng), threadObject(0)
+#if QT_CONFIG(qml_network)
+, accessManager(0)
+#endif
{
eventLoopQuitHack = new QObject;
eventLoopQuitHack->moveToThread(this);
@@ -437,19 +448,27 @@ QQuickPixmapReader::~QQuickPixmapReader()
mutex.lock();
// manually cancel all outstanding jobs.
- foreach (QQuickPixmapReply *reply, jobs) {
+ for (QQuickPixmapReply *reply : qAsConst(jobs)) {
if (reply->data && reply->data->reply == reply)
reply->data->reply = 0;
delete reply;
}
jobs.clear();
- QList<QQuickPixmapReply*> activeJobs = networkJobs.values() + asyncResponses.values();
- foreach (QQuickPixmapReply *reply, activeJobs ) {
+#if QT_CONFIG(qml_network)
+
+ const auto cancelJob = [this](QQuickPixmapReply *reply) {
if (reply->loading) {
cancelled.append(reply);
reply->data = 0;
}
- }
+ };
+
+ for (auto *reply : qAsConst(networkJobs))
+ cancelJob(reply);
+
+ for (auto *reply : qAsConst(asyncResponses))
+ cancelJob(reply);
+#endif
if (threadObject) threadObject->processJobs();
mutex.unlock();
@@ -457,6 +476,7 @@ QQuickPixmapReader::~QQuickPixmapReader()
wait();
}
+#if QT_CONFIG(qml_network)
void QQuickPixmapReader::networkRequestDone(QNetworkReply *reply)
{
QQuickPixmapReply *job = networkJobs.take(reply);
@@ -506,6 +526,7 @@ void QQuickPixmapReader::networkRequestDone(QNetworkReply *reply)
// kick off event loop again incase we have dropped below max request count
threadObject->processJobs();
}
+#endif // qml_network
void QQuickPixmapReader::asyncResponseFinished(QQuickImageResponse *response)
{
@@ -556,8 +577,10 @@ bool QQuickPixmapReaderThreadObject::event(QEvent *e)
void QQuickPixmapReaderThreadObject::networkRequestDone()
{
+#if QT_CONFIG(qml_network)
QNetworkReply *reply = static_cast<QNetworkReply *>(sender());
reader->networkRequestDone(reply);
+#endif
}
void QQuickPixmapReaderThreadObject::asyncResponseFinished()
@@ -576,6 +599,7 @@ void QQuickPixmapReader::processJobs()
// Clean cancelled jobs
if (!cancelled.isEmpty()) {
+#if QT_CONFIG(qml_network)
for (int i = 0; i < cancelled.count(); ++i) {
QQuickPixmapReply *job = cancelled.at(i);
QNetworkReply *reply = networkJobs.key(job, 0);
@@ -597,13 +621,14 @@ void QQuickPixmapReader::processJobs()
job->deleteLater();
}
cancelled.clear();
+#endif
}
if (!jobs.isEmpty()) {
// Find a job we can use
bool usableJob = false;
for (int i = jobs.count() - 1; !usableJob && i >= 0; i--) {
- QQuickPixmapReply *job = jobs[i];
+ QQuickPixmapReply *job = jobs.at(i);
const QUrl url = job->url;
QString localFile;
QQuickImageProvider::ImageType imageType = QQuickImageProvider::Invalid;
@@ -617,7 +642,11 @@ void QQuickPixmapReader::processJobs()
usableJob = true;
} else {
localFile = QQmlFile::urlToLocalFileOrQrc(url);
- usableJob = !localFile.isEmpty() || networkJobs.count() < IMAGEREQUEST_MAX_NETWORK_REQUEST_COUNT;
+ usableJob = !localFile.isEmpty()
+#if QT_CONFIG(qml_network)
+ || networkJobs.count() < IMAGEREQUEST_MAX_NETWORK_REQUEST_COUNT
+#endif
+ ;
}
@@ -742,6 +771,7 @@ void QQuickPixmapReader::processJob(QQuickPixmapReply *runningJob, const QUrl &u
runningJob->postReply(errorCode, errorStr, readSize, QQuickTextureFactory::textureFactoryForImage(image));
mutex.unlock();
} else {
+#if QT_CONFIG(qml_network)
// Network resource
QNetworkRequest req(url);
req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true);
@@ -751,6 +781,9 @@ void QQuickPixmapReader::processJob(QQuickPixmapReply *runningJob, const QUrl &u
QMetaObject::connect(reply, replyFinished, threadObject, threadNetworkRequestDone);
networkJobs.insert(reply, runningJob);
+#else
+// Silently fail if compiled with no_network
+#endif
}
}
}
@@ -807,11 +840,13 @@ void QQuickPixmapReader::cancel(QQuickPixmapReply *reply)
void QQuickPixmapReader::run()
{
if (replyDownloadProgress == -1) {
+#if QT_CONFIG(qml_network)
replyDownloadProgress = QMetaMethod::fromSignal(&QNetworkReply::downloadProgress).methodIndex();
replyFinished = QMetaMethod::fromSignal(&QNetworkReply::finished).methodIndex();
- downloadProgress = QMetaMethod::fromSignal(&QQuickPixmapReply::downloadProgress).methodIndex();
const QMetaObject *ir = &QQuickPixmapReaderThreadObject::staticMetaObject;
threadNetworkRequestDone = ir->indexOfSlot("networkRequestDone()");
+#endif
+ downloadProgress = QMetaMethod::fromSignal(&QQuickPixmapReply::downloadProgress).methodIndex();
}
mutex.lock();
@@ -886,15 +921,13 @@ QQuickPixmapStore::~QQuickPixmapStore()
#ifndef QT_NO_DEBUG
int leakedPixmaps = 0;
#endif
- QList<QQuickPixmapData*> cachedData = m_cache.values();
-
// Prevent unreferencePixmap() from assuming it needs to kick
// off the cache expiry timer, as we're shrinking the cache
// manually below after releasing all the pixmaps.
m_timerId = -2;
// unreference all (leaked) pixmaps
- foreach (QQuickPixmapData* pixmap, cachedData) {
+ for (auto *pixmap : qAsConst(m_cache)) {
int currRefCount = pixmap->refCount;
if (currRefCount) {
#ifndef QT_NO_DEBUG
diff --git a/src/quick/util/qquickprofiler.cpp b/src/quick/util/qquickprofiler.cpp
index f8d090cc2c..841a1c9bcf 100644
--- a/src/quick/util/qquickprofiler.cpp
+++ b/src/quick/util/qquickprofiler.cpp
@@ -70,7 +70,7 @@ void QQuickProfiler::registerAnimationCallback()
class CallbackRegistrationHelper : public QObject {
Q_OBJECT
-public slots:
+public:
void registerAnimationTimerCallback()
{
QQuickProfiler::registerAnimationCallback();
@@ -86,7 +86,12 @@ QQuickProfiler::QQuickProfiler(QObject *parent) : QObject(parent)
m_timer.start();
CallbackRegistrationHelper *helper = new CallbackRegistrationHelper; // will delete itself
helper->moveToThread(QCoreApplication::instance()->thread());
- QMetaObject::invokeMethod(helper, "registerAnimationTimerCallback", Qt::QueuedConnection);
+
+ // Queue the signal to have the animation timer registration run in the right thread;
+ QObject signalSource;
+ connect(&signalSource, &QObject::destroyed,
+ helper, &CallbackRegistrationHelper::registerAnimationTimerCallback,
+ Qt::QueuedConnection);
}
QQuickProfiler::~QQuickProfiler()
@@ -110,8 +115,9 @@ void QQuickProfiler::stopProfilingImpl()
m_data.clear();
}
-void QQuickProfiler::reportDataImpl()
+void QQuickProfiler::reportDataImpl(bool trackLocations)
{
+ Q_UNUSED(trackLocations);
QMutexLocker lock(&m_dataMutex);
emit dataReady(m_data);
m_data.clear();
diff --git a/src/quick/util/qquickprofiler_p.h b/src/quick/util/qquickprofiler_p.h
index b58d4f47c1..66ed212722 100644
--- a/src/quick/util/qquickprofiler_p.h
+++ b/src/quick/util/qquickprofiler_p.h
@@ -62,52 +62,22 @@
QT_BEGIN_NAMESPACE
+#ifdef QT_NO_QML_DEBUGGER
+
+#define Q_QUICK_PROFILE_IF_ENABLED(feature, Code)
+
+struct QQuickProfiler {
+ static void registerAnimationCallback() {}
+};
+
+#else
+
#define Q_QUICK_PROFILE_IF_ENABLED(feature, Code)\
if (QQuickProfiler::featuresEnabled & (1 << feature)) {\
Code;\
} else\
(void)0
-#define Q_QUICK_PROFILE(feature, Method)\
- Q_QUICK_PROFILE_IF_ENABLED(feature, QQuickProfiler::Method)
-
-#define Q_QUICK_SG_PROFILE_START(Type)\
- Q_QUICK_PROFILE_IF_ENABLED(QQuickProfiler::ProfileSceneGraph,\
- (QQuickProfiler::startSceneGraphFrame<Type>()))
-
-#define Q_QUICK_SG_PROFILE_RECORD(Type)\
- Q_QUICK_PROFILE_IF_ENABLED(QQuickProfiler::ProfileSceneGraph,\
- (QQuickProfiler::recordSceneGraphTimestamp<Type>()))
-
-#define Q_QUICK_SG_PROFILE_SKIP(Type, Skip)\
- Q_QUICK_PROFILE_IF_ENABLED(QQuickProfiler::ProfileSceneGraph,\
- (QQuickProfiler::skipSceneGraphTimestamps<Type, Skip>()))
-
-#define Q_QUICK_SG_PROFILE_START_SYNCHRONIZED(Type1, Type2)\
- Q_QUICK_PROFILE_IF_ENABLED(QQuickProfiler::ProfileSceneGraph,\
- (QQuickProfiler::startSceneGraphFrame<Type1, Type2>()))
-
-#define Q_QUICK_SG_PROFILE_SWITCH(Type1, Type2) \
- Q_QUICK_PROFILE_IF_ENABLED(QQuickProfiler::ProfileSceneGraph,\
- (QQuickProfiler::reportSceneGraphFrame<Type1, true, Type2>()))
-
-#define Q_QUICK_SG_PROFILE_REPORT(Type)\
- Q_QUICK_PROFILE_IF_ENABLED(QQuickProfiler::ProfileSceneGraph,\
- (QQuickProfiler::reportSceneGraphFrame<Type, false>()))
-
-#define Q_QUICK_SG_PROFILE_END(Type)\
- Q_QUICK_PROFILE_IF_ENABLED(QQuickProfiler::ProfileSceneGraph,\
- (QQuickProfiler::reportSceneGraphFrame<Type, true>()))
-
-#define Q_QUICK_SG_PROFILE_END_WITH_PAYLOAD(Type, Payload)\
- Q_QUICK_PROFILE_IF_ENABLED(QQuickProfiler::ProfileSceneGraph,\
- (QQuickProfiler::reportSceneGraphFrame<Type, true>(Payload)))
-
-
-#define Q_QUICK_INPUT_PROFILE(Type, DetailType, A, B)\
- Q_QUICK_PROFILE_IF_ENABLED(QQuickProfiler::ProfileInputEvents,\
- (QQuickProfiler::inputEvent<Type, DetailType>(A, B)))
-
// This struct is somewhat dangerous to use:
// You can save values either with 32 or 64 bit precision. toByteArrays will
// guess the precision from messageType. If you state the wrong messageType
@@ -319,17 +289,15 @@ public:
qint64 timestamp() { return m_timer.nsecsElapsed(); }
-
static quint64 featuresEnabled;
- static bool profilingSceneGraph()
- {
- return featuresEnabled & (1 << QQuickProfiler::ProfileSceneGraph);
- }
static void initialize(QObject *parent);
virtual ~QQuickProfiler();
+signals:
+ void dataReady(const QVector<QQuickProfilerData> &data);
+
protected:
friend class QQuickProfilerAdapter;
@@ -347,16 +315,54 @@ protected:
m_data.append(message);
}
-signals:
- void dataReady(const QVector<QQuickProfilerData> &data);
-
-protected slots:
void startProfilingImpl(quint64 features);
void stopProfilingImpl();
- void reportDataImpl();
+ void reportDataImpl(bool trackLocations);
void setTimer(const QElapsedTimer &t);
};
+#endif // QT_NO_QML_DEBUGGER
+
+#define Q_QUICK_PROFILE(feature, Method)\
+ Q_QUICK_PROFILE_IF_ENABLED(feature, QQuickProfiler::Method)
+
+#define Q_QUICK_SG_PROFILE_START(Type)\
+ Q_QUICK_PROFILE_IF_ENABLED(QQuickProfiler::ProfileSceneGraph,\
+ (QQuickProfiler::startSceneGraphFrame<Type>()))
+
+#define Q_QUICK_SG_PROFILE_RECORD(Type)\
+ Q_QUICK_PROFILE_IF_ENABLED(QQuickProfiler::ProfileSceneGraph,\
+ (QQuickProfiler::recordSceneGraphTimestamp<Type>()))
+
+#define Q_QUICK_SG_PROFILE_SKIP(Type, Skip)\
+ Q_QUICK_PROFILE_IF_ENABLED(QQuickProfiler::ProfileSceneGraph,\
+ (QQuickProfiler::skipSceneGraphTimestamps<Type, Skip>()))
+
+#define Q_QUICK_SG_PROFILE_START_SYNCHRONIZED(Type1, Type2)\
+ Q_QUICK_PROFILE_IF_ENABLED(QQuickProfiler::ProfileSceneGraph,\
+ (QQuickProfiler::startSceneGraphFrame<Type1, Type2>()))
+
+#define Q_QUICK_SG_PROFILE_SWITCH(Type1, Type2) \
+ Q_QUICK_PROFILE_IF_ENABLED(QQuickProfiler::ProfileSceneGraph,\
+ (QQuickProfiler::reportSceneGraphFrame<Type1, true, Type2>()))
+
+#define Q_QUICK_SG_PROFILE_REPORT(Type)\
+ Q_QUICK_PROFILE_IF_ENABLED(QQuickProfiler::ProfileSceneGraph,\
+ (QQuickProfiler::reportSceneGraphFrame<Type, false>()))
+
+#define Q_QUICK_SG_PROFILE_END(Type)\
+ Q_QUICK_PROFILE_IF_ENABLED(QQuickProfiler::ProfileSceneGraph,\
+ (QQuickProfiler::reportSceneGraphFrame<Type, true>()))
+
+#define Q_QUICK_SG_PROFILE_END_WITH_PAYLOAD(Type, Payload)\
+ Q_QUICK_PROFILE_IF_ENABLED(QQuickProfiler::ProfileSceneGraph,\
+ (QQuickProfiler::reportSceneGraphFrame<Type, true>(Payload)))
+
+
+#define Q_QUICK_INPUT_PROFILE(Type, DetailType, A, B)\
+ Q_QUICK_PROFILE_IF_ENABLED(QQuickProfiler::ProfileInputEvents,\
+ (QQuickProfiler::inputEvent<Type, DetailType>(A, B)))
+
QT_END_NAMESPACE
#endif
diff --git a/src/quick/util/qquickpropertychanges.cpp b/src/quick/util/qquickpropertychanges.cpp
index 0119aecb7e..37a910876e 100644
--- a/src/quick/util/qquickpropertychanges.cpp
+++ b/src/quick/util/qquickpropertychanges.cpp
@@ -46,7 +46,6 @@
#include <private/qqmlcustomparser_p.h>
#include <qqmlexpression.h>
#include <private/qqmlbinding_p.h>
-#include <private/qqmlcompiler_p.h>
#include <qqmlcontext.h>
#include <private/qqmlproperty_p.h>
#include <private/qqmlcontext_p.h>
@@ -202,7 +201,7 @@ public:
QPointer<QObject> object;
QList<const QV4::CompiledData::Binding *> bindings;
- QQmlRefPointer<QQmlCompiledData> cdata;
+ QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit;
bool decoded : 1;
bool restore : 1;
@@ -257,8 +256,8 @@ void QQuickPropertyChangesPrivate::decode()
if (decoded)
return;
- foreach (const QV4::CompiledData::Binding *binding, bindings)
- decodeBinding(QString(), cdata->compilationUnit->data, binding);
+ for (const QV4::CompiledData::Binding *binding : qAsConst(bindings))
+ decodeBinding(QString(), compilationUnit->data, binding);
bindings.clear();
@@ -288,7 +287,7 @@ void QQuickPropertyChangesPrivate::decodeBinding(const QString &propertyPrefix,
QQuickReplaceSignalHandler *handler = new QQuickReplaceSignalHandler;
handler->property = prop;
handler->expression.take(new QQmlBoundSignalExpression(object, QQmlPropertyPrivate::get(prop)->signalIndex(),
- QQmlContextData::get(qmlContext(q)), object, cdata->compilationUnit->runtimeFunctions[binding->value.compiledScriptIndex]));
+ QQmlContextData::get(qmlContext(q)), object, compilationUnit->runtimeFunctions.at(binding->value.compiledScriptIndex)));
signalReplacements << handler;
return;
}
@@ -338,12 +337,12 @@ void QQuickPropertyChangesParser::verifyBindings(const QV4::CompiledData::Unit *
verifyList(qmlUnit, props.at(ii));
}
-void QQuickPropertyChangesParser::applyBindings(QObject *obj, QQmlCompiledData *cdata, const QList<const QV4::CompiledData::Binding *> &bindings)
+void QQuickPropertyChangesParser::applyBindings(QObject *obj, QV4::CompiledData::CompilationUnit *compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings)
{
QQuickPropertyChangesPrivate *p =
static_cast<QQuickPropertyChangesPrivate *>(QObjectPrivate::get(obj));
p->bindings = bindings;
- p->cdata = cdata;
+ p->compilationUnit = compilationUnit;
p->decoded = false;
}
@@ -456,12 +455,14 @@ QQuickPropertyChanges::ActionList QQuickPropertyChanges::actions()
QQmlBinding *newBinding = 0;
if (e.id != QQmlBinding::Invalid) {
QV4::Scope scope(QQmlEnginePrivate::getV4Engine(qmlEngine(this)));
- QV4::ScopedValue function(scope, QV4::FunctionObject::createQmlFunction(context, object(), d->cdata->compilationUnit->runtimeFunctions[e.id]));
- newBinding = new QQmlBinding(function, object(), context);
+ QV4::ScopedValue function(scope, QV4::FunctionObject::createQmlFunction(context, object(), d->compilationUnit->runtimeFunctions.at(e.id)));
+ newBinding = QQmlBinding::create(&QQmlPropertyPrivate::get(prop)->core,
+ function, object(), context);
}
// QQmlBinding *newBinding = e.id != QQmlBinding::Invalid ? QQmlBinding::createBinding(e.id, object(), qmlContext(this)) : 0;
if (!newBinding)
- newBinding = new QQmlBinding(e.expression, object(), context, e.url.toString(), e.line, e.column);
+ newBinding = QQmlBinding::create(&QQmlPropertyPrivate::get(prop)->core,
+ e.expression, object(), context, e.url.toString(), e.line, e.column);
if (d->isExplicit) {
// in this case, we don't want to assign a binding, per se,
@@ -596,7 +597,7 @@ void QQuickPropertyChanges::changeValue(const QString &name, const QVariant &val
state()->addEntryToRevertList(action);
QQmlAbstractBinding *oldBinding = QQmlPropertyPrivate::binding(action.property);
if (oldBinding)
- oldBinding->setEnabled(false, QQmlPropertyPrivate::DontRemoveBinding | QQmlPropertyPrivate::BypassInterceptor);
+ oldBinding->setEnabled(false, QQmlPropertyData::DontRemoveBinding | QQmlPropertyData::BypassInterceptor);
d->property(name).write(value);
}
}
@@ -625,9 +626,12 @@ void QQuickPropertyChanges::changeExpression(const QString &name, const QString
if (entry.name == name) {
entry.expression = expression;
if (state() && state()->isStateActive()) {
- QQmlBinding *newBinding = new QQmlBinding(expression, object(), qmlContext(this));
- newBinding->setTarget(d->property(name));
- QQmlPropertyPrivate::setBinding(newBinding, QQmlPropertyPrivate::None, QQmlPropertyPrivate::DontRemoveBinding | QQmlPropertyPrivate::BypassInterceptor);
+ auto prop = d->property(name);
+ QQmlBinding *newBinding = QQmlBinding::create(
+ &QQmlPropertyPrivate::get(prop)->core, expression, object(),
+ qmlContext(this));
+ newBinding->setTarget(prop);
+ QQmlPropertyPrivate::setBinding(newBinding, QQmlPropertyPrivate::None, QQmlPropertyData::DontRemoveBinding | QQmlPropertyData::BypassInterceptor);
}
return;
}
@@ -640,13 +644,16 @@ void QQuickPropertyChanges::changeExpression(const QString &name, const QString
if (hadValue) {
QQmlAbstractBinding *oldBinding = QQmlPropertyPrivate::binding(d->property(name));
if (oldBinding) {
- oldBinding->setEnabled(false, QQmlPropertyPrivate::DontRemoveBinding | QQmlPropertyPrivate::BypassInterceptor);
+ oldBinding->setEnabled(false, QQmlPropertyData::DontRemoveBinding | QQmlPropertyData::BypassInterceptor);
state()->changeBindingInRevertList(object(), name, oldBinding);
}
- QQmlBinding *newBinding = new QQmlBinding(expression, object(), qmlContext(this));
- newBinding->setTarget(d->property(name));
- QQmlPropertyPrivate::setBinding(newBinding, QQmlPropertyPrivate::None, QQmlPropertyPrivate::DontRemoveBinding | QQmlPropertyPrivate::BypassInterceptor);
+ auto prop = d->property(name);
+ QQmlBinding *newBinding = QQmlBinding::create(
+ &QQmlPropertyPrivate::get(prop)->core, expression, object(),
+ qmlContext(this));
+ newBinding->setTarget(prop);
+ QQmlPropertyPrivate::setBinding(newBinding, QQmlPropertyPrivate::None, QQmlPropertyData::DontRemoveBinding | QQmlPropertyData::BypassInterceptor);
} else {
QQuickStateAction action;
action.restore = restoreEntryValues();
@@ -655,7 +662,9 @@ void QQuickPropertyChanges::changeExpression(const QString &name, const QString
action.specifiedObject = object();
action.specifiedProperty = name;
- QQmlBinding *newBinding = new QQmlBinding(expression, object(), qmlContext(this));
+ QQmlBinding *newBinding = QQmlBinding::create(
+ &QQmlPropertyPrivate::get(action.property)->core, expression,
+ object(), qmlContext(this));
if (d->isExplicit) {
// don't assign the binding, merely evaluate the expression.
// XXX TODO: add a static QQmlJavaScriptExpression::evaluate(QString)
@@ -670,9 +679,9 @@ void QQuickPropertyChanges::changeExpression(const QString &name, const QString
state()->addEntryToRevertList(action);
QQmlAbstractBinding *oldBinding = QQmlPropertyPrivate::binding(action.property);
if (oldBinding)
- oldBinding->setEnabled(false, QQmlPropertyPrivate::DontRemoveBinding | QQmlPropertyPrivate::BypassInterceptor);
+ oldBinding->setEnabled(false, QQmlPropertyData::DontRemoveBinding | QQmlPropertyData::BypassInterceptor);
- QQmlPropertyPrivate::setBinding(newBinding, QQmlPropertyPrivate::None, QQmlPropertyPrivate::DontRemoveBinding | QQmlPropertyPrivate::BypassInterceptor);
+ QQmlPropertyPrivate::setBinding(newBinding, QQmlPropertyPrivate::None, QQmlPropertyData::DontRemoveBinding | QQmlPropertyData::BypassInterceptor);
}
}
}
diff --git a/src/quick/util/qquickpropertychanges_p.h b/src/quick/util/qquickpropertychanges_p.h
index 9d086b7688..0537750338 100644
--- a/src/quick/util/qquickpropertychanges_p.h
+++ b/src/quick/util/qquickpropertychanges_p.h
@@ -104,7 +104,7 @@ public:
void verifyList(const QV4::CompiledData::Unit *qmlUnit, const QV4::CompiledData::Binding *binding);
virtual void verifyBindings(const QV4::CompiledData::Unit *qmlUnit, const QList<const QV4::CompiledData::Binding *> &props);
- virtual void applyBindings(QObject *obj, QQmlCompiledData *cdata, const QList<const QV4::CompiledData::Binding *> &bindings);
+ virtual void applyBindings(QObject *obj, QV4::CompiledData::CompilationUnit *compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings);
};
diff --git a/src/quick/util/qquicksmoothedanimation.cpp b/src/quick/util/qquicksmoothedanimation.cpp
index b7eadf5e30..e7beee8ed3 100644
--- a/src/quick/util/qquicksmoothedanimation.cpp
+++ b/src/quick/util/qquicksmoothedanimation.cpp
@@ -254,8 +254,8 @@ void QSmoothedAnimation::updateCurrentTime(int t)
qreal value = easeFollow(time_seconds);
value *= (invert? -1.0: 1.0);
QQmlPropertyPrivate::write(target, initialValue + value,
- QQmlPropertyPrivate::BypassInterceptor
- | QQmlPropertyPrivate::DontRemoveBinding);
+ QQmlPropertyData::BypassInterceptor
+ | QQmlPropertyData::DontRemoveBinding);
}
void QSmoothedAnimation::init()
@@ -287,8 +287,8 @@ void QSmoothedAnimation::init()
break;
case QQuickSmoothedAnimation::Sync:
QQmlPropertyPrivate::write(target, to,
- QQmlPropertyPrivate::BypassInterceptor
- | QQmlPropertyPrivate::DontRemoveBinding);
+ QQmlPropertyData::BypassInterceptor
+ | QQmlPropertyData::DontRemoveBinding);
trackVelocity = 0;
stop();
return;
@@ -304,8 +304,8 @@ void QSmoothedAnimation::init()
if (!recalc()) {
QQmlPropertyPrivate::write(target, to,
- QQmlPropertyPrivate::BypassInterceptor
- | QQmlPropertyPrivate::DontRemoveBinding);
+ QQmlPropertyData::BypassInterceptor
+ | QQmlPropertyData::DontRemoveBinding);
stop();
return;
}
@@ -377,9 +377,8 @@ QQuickSmoothedAnimation::~QQuickSmoothedAnimation()
}
QQuickSmoothedAnimationPrivate::QQuickSmoothedAnimationPrivate()
- : anim(0)
+ : anim(new QSmoothedAnimation)
{
- anim = new QSmoothedAnimation;
}
QQuickSmoothedAnimationPrivate::~QQuickSmoothedAnimationPrivate()
@@ -393,7 +392,7 @@ QQuickSmoothedAnimationPrivate::~QQuickSmoothedAnimationPrivate()
void QQuickSmoothedAnimationPrivate::updateRunningAnimations()
{
- foreach(QSmoothedAnimation* ease, activeAnimations.values()){
+ for (QSmoothedAnimation *ease : qAsConst(activeAnimations)) {
ease->maximumEasingTime = anim->maximumEasingTime;
ease->reversingMode = anim->reversingMode;
ease->velocity = anim->velocity;
@@ -410,7 +409,7 @@ QAbstractAnimationJob* QQuickSmoothedAnimation::transition(QQuickStateActions &a
Q_UNUSED(direction);
Q_D(QQuickSmoothedAnimation);
- QQuickStateActions dataActions = QQuickPropertyAnimation::createTransitionActions(actions, modified, defaultTarget);
+ const QQuickStateActions dataActions = QQuickPropertyAnimation::createTransitionActions(actions, modified, defaultTarget);
QContinuingAnimationGroupJob *wrapperGroup = new QContinuingAnimationGroupJob();
@@ -445,7 +444,8 @@ QAbstractAnimationJob* QQuickSmoothedAnimation::transition(QQuickStateActions &a
anims.insert(ease);
}
- foreach (QSmoothedAnimation *ease, d->activeAnimations.values()){
+ const auto copy = d->activeAnimations;
+ for (QSmoothedAnimation *ease : copy) {
if (!anims.contains(ease)) {
ease->clearTemplate();
d->activeAnimations.remove(ease->target);
diff --git a/src/quick/util/qquickspringanimation.cpp b/src/quick/util/qquickspringanimation.cpp
index df077350e9..294122150a 100644
--- a/src/quick/util/qquickspringanimation.cpp
+++ b/src/quick/util/qquickspringanimation.cpp
@@ -301,8 +301,8 @@ void QSpringAnimation::updateCurrentTime(int time)
qreal old_to = to;
QQmlPropertyPrivate::write(target, currentValue,
- QQmlPropertyPrivate::BypassInterceptor |
- QQmlPropertyPrivate::DontRemoveBinding);
+ QQmlPropertyData::BypassInterceptor |
+ QQmlPropertyData::DontRemoveBinding);
if (stopped && old_to == to) { // do not stop if we got restarted
if (animationTemplate)
@@ -585,7 +585,8 @@ QAbstractAnimationJob* QQuickSpringAnimation::transition(QQuickStateActions &act
animation->restart();
anims.insert(animation);
}
- foreach (QSpringAnimation *anim, d->activeAnimations.values()){
+ const auto copy = d->activeAnimations;
+ for (QSpringAnimation *anim : copy) {
if (!anims.contains(anim)) {
anim->clearTemplate();
d->activeAnimations.remove(anim->target);
diff --git a/src/quick/util/qquickstate.cpp b/src/quick/util/qquickstate.cpp
index 947a5b6e4e..2d3934cce8 100644
--- a/src/quick/util/qquickstate.cpp
+++ b/src/quick/util/qquickstate.cpp
@@ -334,7 +334,7 @@ QQuickStatePrivate::generateActionList() const
}
}
- foreach(QQuickStateOperation *op, operations)
+ for (QQuickStateOperation *op : operations)
applyList << op->actions();
inState = false;
@@ -676,7 +676,7 @@ void QQuickState::apply(QQuickTransition *trans, QQuickState *revert)
#ifndef QT_NO_DEBUG_STREAM
// Output for debugging
if (stateChangeDebug()) {
- foreach(const QQuickStateAction &action, applyList) {
+ for (const QQuickStateAction &action : qAsConst(applyList)) {
if (action.event)
qWarning() << " QQuickStateAction event:" << action.event->type();
else
diff --git a/src/quick/util/qquickstategroup.cpp b/src/quick/util/qquickstategroup.cpp
index c163b401fb..200f243a1b 100644
--- a/src/quick/util/qquickstategroup.cpp
+++ b/src/quick/util/qquickstategroup.cpp
@@ -44,7 +44,6 @@
#include <private/qqmlbinding_p.h>
#include <private/qqmlglobal_p.h>
-#include <QtCore/qstringbuilder.h>
#include <QtCore/qstringlist.h>
#include <QtCore/qdebug.h>
#include <QtCore/qvector.h>
@@ -306,7 +305,7 @@ void QQuickStateGroup::componentComplete()
for (int ii = 0; ii < d->states.count(); ++ii) {
QQuickState *state = d->states.at(ii);
if (!state->isNamed())
- state->setName(QLatin1String("anonymousState") % QString::number(++d->unnamedCount));
+ state->setName(QLatin1String("anonymousState") + QString::number(++d->unnamedCount));
}
if (d->updateAutoState()) {
diff --git a/src/quick/util/qquicktimeline.cpp b/src/quick/util/qquicktimeline.cpp
index 74baa3bfda..b3f0caa2b3 100644
--- a/src/quick/util/qquicktimeline.cpp
+++ b/src/quick/util/qquicktimeline.cpp
@@ -154,7 +154,7 @@ void QQuickTimeLinePrivate::add(QQuickTimeLineObject &g, const Op &o)
}
if (!iter->ops.isEmpty() &&
o.type == Op::Pause &&
- iter->ops.last().type == Op::Pause) {
+ iter->ops.constLast().type == Op::Pause) {
iter->ops.last().length += o.length;
iter->length += o.length;
} else {
diff --git a/src/quick/util/qquicktransitionmanager.cpp b/src/quick/util/qquicktransitionmanager.cpp
index 55abb0a207..714e6d62b6 100644
--- a/src/quick/util/qquicktransitionmanager.cpp
+++ b/src/quick/util/qquicktransitionmanager.cpp
@@ -106,7 +106,7 @@ void QQuickTransitionManager::complete()
void QQuickTransitionManagerPrivate::applyBindings()
{
- foreach(const QQuickStateAction &action, bindingsList) {
+ for (const QQuickStateAction &action : qAsConst(bindingsList)) {
if (action.toBinding) {
QQmlPropertyPrivate::setBinding(action.toBinding.data());
} else if (action.event) {
@@ -133,7 +133,7 @@ void QQuickTransitionManager::transition(const QList<QQuickStateAction> &list,
QQuickStateOperation::ActionList applyList = list;
// Determine which actions are binding changes and disable any current bindings
- foreach(const QQuickStateAction &action, applyList) {
+ for (const QQuickStateAction &action : qAsConst(applyList)) {
if (action.toBinding)
d->bindingsList << action;
if (action.fromBinding)
@@ -159,9 +159,9 @@ void QQuickTransitionManager::transition(const QList<QQuickStateAction> &list,
for (int ii = 0; ii < applyList.size(); ++ii) {
const QQuickStateAction &action = applyList.at(ii);
if (action.toBinding) {
- QQmlPropertyPrivate::setBinding(action.toBinding.data(), QQmlPropertyPrivate::None, QQmlPropertyPrivate::BypassInterceptor | QQmlPropertyPrivate::DontRemoveBinding);
+ QQmlPropertyPrivate::setBinding(action.toBinding.data(), QQmlPropertyPrivate::None, QQmlPropertyData::BypassInterceptor | QQmlPropertyData::DontRemoveBinding);
} else if (!action.event) {
- QQmlPropertyPrivate::write(action.property, action.toValue, QQmlPropertyPrivate::BypassInterceptor | QQmlPropertyPrivate::DontRemoveBinding);
+ QQmlPropertyPrivate::write(action.property, action.toValue, QQmlPropertyData::BypassInterceptor | QQmlPropertyData::DontRemoveBinding);
} else if (action.event->isReversable()) {
if (action.reverseEvent)
action.event->reverse();
@@ -184,7 +184,7 @@ void QQuickTransitionManager::transition(const QList<QQuickStateAction> &list,
}
// Revert back to the original values
- foreach(const QQuickStateAction &action, applyList) {
+ for (const QQuickStateAction &action : qAsConst(applyList)) {
if (action.event) {
if (action.event->isReversable()) {
action.event->clearBindings();
@@ -197,7 +197,7 @@ void QQuickTransitionManager::transition(const QList<QQuickStateAction> &list,
if (action.toBinding)
QQmlPropertyPrivate::removeBinding(action.property); // Make sure this is disabled during the transition
- QQmlPropertyPrivate::write(action.property, action.fromValue, QQmlPropertyPrivate::BypassInterceptor | QQmlPropertyPrivate::DontRemoveBinding);
+ QQmlPropertyPrivate::write(action.property, action.fromValue, QQmlPropertyData::BypassInterceptor | QQmlPropertyData::DontRemoveBinding);
}
}
@@ -239,7 +239,7 @@ void QQuickTransitionManager::transition(const QList<QQuickStateAction> &list,
// be applied immediately. We skip applying bindings, as they are all
// applied at the end in applyBindings() to avoid any nastiness mid
// transition
- foreach(const QQuickStateAction &action, applyList) {
+ for (const QQuickStateAction &action : qAsConst(applyList)) {
if (action.event && !action.event->changesBindings()) {
if (action.event->isReversable() && action.reverseEvent)
action.event->reverse();
@@ -251,7 +251,7 @@ void QQuickTransitionManager::transition(const QList<QQuickStateAction> &list,
}
#ifndef QT_NO_DEBUG_STREAM
if (stateChangeDebug()) {
- foreach(const QQuickStateAction &action, applyList) {
+ for (const QQuickStateAction &action : qAsConst(applyList)) {
if (action.event)
qWarning() << " No transition for event:" << action.event->type();
else
diff --git a/src/quick/util/qquickutilmodule.cpp b/src/quick/util/qquickutilmodule.cpp
index f5d11c6230..7e2973a78b 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");
+#if QT_CONFIG(quick_shadereffect) && QT_CONFIG(opengl)
qmlRegisterType<QQuickUniformAnimator>("QtQuick", 2, 2, "UniformAnimator");
-
+#endif
qmlRegisterType<QQuickStateOperation>();
qmlRegisterCustomType<QQuickPropertyChanges>("QtQuick",2,0,"PropertyChanges", new QQuickPropertyChangesParser);
diff --git a/src/quick/util/qquickvaluetypes.cpp b/src/quick/util/qquickvaluetypes.cpp
index 416a325238..e673df0451 100644
--- a/src/quick/util/qquickvaluetypes.cpp
+++ b/src/quick/util/qquickvaluetypes.cpp
@@ -80,6 +80,36 @@ qreal QQuickColorValueType::a() const
return v.alphaF();
}
+qreal QQuickColorValueType::hsvHue() const
+{
+ return v.hsvHueF();
+}
+
+qreal QQuickColorValueType::hsvSaturation() const
+{
+ return v.hsvSaturationF();
+}
+
+qreal QQuickColorValueType::hsvValue() const
+{
+ return v.valueF();
+}
+
+qreal QQuickColorValueType::hslHue() const
+{
+ return v.hslHueF();
+}
+
+qreal QQuickColorValueType::hslSaturation() const
+{
+ return v.hslSaturationF();
+}
+
+qreal QQuickColorValueType::hslLightness() const
+{
+ return v.lightnessF();
+}
+
void QQuickColorValueType::setR(qreal r)
{
v.setRedF(r);
@@ -100,6 +130,48 @@ void QQuickColorValueType::setA(qreal a)
v.setAlphaF(a);
}
+void QQuickColorValueType::setHsvHue(qreal hsvHue)
+{
+ qreal hue, saturation, value, alpha;
+ v.getHsvF(&hue, &saturation, &value, &alpha);
+ v.setHsvF(hsvHue, saturation, value, alpha);
+}
+
+void QQuickColorValueType::setHsvSaturation(qreal hsvSaturation)
+{
+ qreal hue, saturation, value, alpha;
+ v.getHsvF(&hue, &saturation, &value, &alpha);
+ v.setHsvF(hue, hsvSaturation, value, alpha);
+}
+
+void QQuickColorValueType::setHsvValue(qreal hsvValue)
+{
+ qreal hue, saturation, value, alpha;
+ v.getHsvF(&hue, &saturation, &value, &alpha);
+ v.setHsvF(hue, saturation, hsvValue, alpha);
+}
+
+void QQuickColorValueType::setHslHue(qreal hslHue)
+{
+ qreal hue, saturation, lightness, alpha;
+ v.getHslF(&hue, &saturation, &lightness, &alpha);
+ v.setHslF(hslHue, saturation, lightness, alpha);
+}
+
+void QQuickColorValueType::setHslSaturation(qreal hslSaturation)
+{
+ qreal hue, saturation, lightness, alpha;
+ v.getHslF(&hue, &saturation, &lightness, &alpha);
+ v.setHslF(hue, hslSaturation, lightness, alpha);
+}
+
+void QQuickColorValueType::setHslLightness(qreal hslLightness)
+{
+ qreal hue, saturation, lightness, alpha;
+ v.getHslF(&hue, &saturation, &lightness, &alpha);
+ v.setHslF(hue, saturation, hslLightness, alpha);
+}
+
QString QQuickVector2DValueType::toString() const
{
return QString(QLatin1String("QVector2D(%1, %2)")).arg(v.x()).arg(v.y());
@@ -676,4 +748,14 @@ void QQuickFontValueType::setWordSpacing(qreal size)
v.setWordSpacing(size);
}
+QQuickFontValueType::HintingPreference QQuickFontValueType::hintingPreference() const
+{
+ return QQuickFontValueType::HintingPreference(v.hintingPreference());
+}
+
+void QQuickFontValueType::setHintingPreference(QQuickFontValueType::HintingPreference hintingPreference)
+{
+ v.setHintingPreference(QFont::HintingPreference(hintingPreference));
+}
+
QT_END_NAMESPACE
diff --git a/src/quick/util/qquickvaluetypes_p.h b/src/quick/util/qquickvaluetypes_p.h
index 80b9ce3109..4a1598ec5c 100644
--- a/src/quick/util/qquickvaluetypes_p.h
+++ b/src/quick/util/qquickvaluetypes_p.h
@@ -78,6 +78,12 @@ class QQuickColorValueType
Q_PROPERTY(qreal g READ g WRITE setG FINAL)
Q_PROPERTY(qreal b READ b WRITE setB FINAL)
Q_PROPERTY(qreal a READ a WRITE setA FINAL)
+ Q_PROPERTY(qreal hsvHue READ hsvHue WRITE setHsvHue FINAL)
+ Q_PROPERTY(qreal hsvSaturation READ hsvSaturation WRITE setHsvSaturation FINAL)
+ Q_PROPERTY(qreal hsvValue READ hsvValue WRITE setHsvValue FINAL)
+ Q_PROPERTY(qreal hslHue READ hslHue WRITE setHslHue FINAL)
+ Q_PROPERTY(qreal hslSaturation READ hslSaturation WRITE setHslSaturation FINAL)
+ Q_PROPERTY(qreal hslLightness READ hslLightness WRITE setHslLightness FINAL)
Q_GADGET
public:
Q_INVOKABLE QString toString() const;
@@ -86,10 +92,22 @@ public:
qreal g() const;
qreal b() const;
qreal a() const;
+ qreal hsvHue() const;
+ qreal hsvSaturation() const;
+ qreal hsvValue() const;
+ qreal hslHue() const;
+ qreal hslSaturation() const;
+ qreal hslLightness() const;
void setR(qreal);
void setG(qreal);
void setB(qreal);
void setA(qreal);
+ void setHsvHue(qreal);
+ void setHsvSaturation(qreal);
+ void setHsvValue(qreal);
+ void setHslHue(qreal);
+ void setHslSaturation(qreal);
+ void setHslLightness(qreal);
};
class QQuickVector2DValueType
@@ -304,6 +322,7 @@ class QQuickFontValueType
Q_PROPERTY(Capitalization capitalization READ capitalization WRITE setCapitalization FINAL)
Q_PROPERTY(qreal letterSpacing READ letterSpacing WRITE setLetterSpacing FINAL)
Q_PROPERTY(qreal wordSpacing READ wordSpacing WRITE setWordSpacing FINAL)
+ Q_PROPERTY(HintingPreference hintingPreference READ hintingPreference WRITE setHintingPreference FINAL)
public:
enum FontWeight { Thin = QFont::Thin,
@@ -323,6 +342,14 @@ public:
Capitalize = QFont::Capitalize };
Q_ENUM(Capitalization)
+ enum HintingPreference {
+ PreferDefaultHinting = QFont::PreferDefaultHinting,
+ PreferNoHinting = QFont::PreferNoHinting,
+ PreferVerticalHinting = QFont::PreferVerticalHinting,
+ PreferFullHinting = QFont::PreferFullHinting
+ };
+ Q_ENUM(HintingPreference)
+
Q_INVOKABLE QString toString() const;
QString family() const;
@@ -363,6 +390,9 @@ public:
qreal wordSpacing() const;
void setWordSpacing(qreal spacing);
+
+ HintingPreference hintingPreference() const;
+ void setHintingPreference(HintingPreference);
};
QT_END_NAMESPACE
diff --git a/src/quick/util/util.pri b/src/quick/util/util.pri
index ffb31ae75e..1ef1018a31 100644
--- a/src/quick/util/util.pri
+++ b/src/quick/util/util.pri
@@ -17,8 +17,6 @@ SOURCES += \
$$PWD/qquickbehavior.cpp \
$$PWD/qquickfontloader.cpp \
$$PWD/qquickstyledtext.cpp \
- $$PWD/qquickpath.cpp \
- $$PWD/qquickpathinterpolator.cpp \
$$PWD/qquickimageprovider.cpp \
$$PWD/qquicksvgparser.cpp \
$$PWD/qquickvaluetypes.cpp \
@@ -26,12 +24,13 @@ SOURCES += \
$$PWD/qquickanimator.cpp \
$$PWD/qquickanimatorjob.cpp \
$$PWD/qquickanimatorcontroller.cpp \
- $$PWD/qquickprofiler.cpp \
$$PWD/qquickfontmetrics.cpp \
$$PWD/qquicktextmetrics.cpp \
$$PWD/qquickshortcut.cpp \
$$PWD/qquickvalidator.cpp
+!contains(QT_CONFIG, no-qml-debug): SOURCES += $$PWD/qquickprofiler.cpp
+
HEADERS += \
$$PWD/qquickapplication_p.h\
$$PWD/qquickutilmodule_p.h\
@@ -54,9 +53,6 @@ HEADERS += \
$$PWD/qquickbehavior_p.h \
$$PWD/qquickfontloader_p.h \
$$PWD/qquickstyledtext_p.h \
- $$PWD/qquickpath_p.h \
- $$PWD/qquickpath_p_p.h \
- $$PWD/qquickpathinterpolator_p.h \
$$PWD/qquickimageprovider.h \
$$PWD/qquicksvgparser_p.h \
$$PWD/qquickvaluetypes_p.h \
@@ -69,3 +65,13 @@ HEADERS += \
$$PWD/qquicktextmetrics_p.h \
$$PWD/qquickshortcut_p.h \
$$PWD/qquickvalidator_p.h
+
+qtConfig(quick-path) {
+ SOURCES += \
+ $$PWD/qquickpath.cpp \
+ $$PWD/qquickpathinterpolator.cpp
+ HEADERS += \
+ $$PWD/qquickpath_p.h \
+ $$PWD/qquickpath_p_p.h \
+ $$PWD/qquickpathinterpolator_p.h
+}
diff --git a/src/quickwidgets/qquickwidget.cpp b/src/quickwidgets/qquickwidget.cpp
index b0838e5dc4..de3692afb0 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,15 @@
#include <QtGui/private/qguiapplication_p.h>
#include <QtGui/qpa/qplatformintegration.h>
+#ifndef QT_NO_OPENGL
+#include <QtGui/QOpenGLContext>
+#include <QtGui/QOpenGLFunctions>
+#include <QtGui/private/qopenglextensions_p.h>
+#endif
+#include <QtGui/QPainter>
+
+#include <QtQuick/QSGRendererInterface>
+
#ifdef Q_OS_WIN
# include <QtWidgets/QMessageBox>
# include <QtCore/QLibraryInfo>
@@ -65,7 +76,9 @@
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 QQuickWidgetRenderControl : public QQuickRenderControl
{
@@ -89,10 +102,19 @@ 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) {
+#ifndef QT_NO_OPENGL
+ if (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::RasterGLSurface))
+ setRenderToTexture();
+ else
+#endif
+ qWarning("QQuickWidget is not supported on this platform.");
+ }
engine = e;
@@ -121,22 +143,38 @@ void QQuickWidgetPrivate::ensureEngine() const
void QQuickWidgetPrivate::invalidateRenderControl()
{
- if (!context) // this is not an error, could be called before creating the context, or multiple times
- return;
+#ifndef QT_NO_OPENGL
+ 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;
+ }
}
+#endif
renderControl->invalidate();
}
void QQuickWidgetPrivate::handleWindowChange()
{
+ if (offscreenWindow->isPersistentSceneGraph() && qGuiApp->testAttribute(Qt::AA_ShareOpenGLContexts))
+ return;
+
+ // In case of !isPersistentSceneGraph or when we need a new context due to
+ // the need to share resources with the new window's context, we must both
+ // invalidate the scenegraph and destroy the context. With
+ // QQuickRenderControl destroying the context must be preceded by an
+ // invalidate to prevent being left with dangling context references in the
+ // rendercontrol.
+
invalidateRenderControl();
- destroyContext();
+
+ if (!useSoftwareRenderer)
+ destroyContext();
}
QQuickWidgetPrivate::QQuickWidgetPrivate()
@@ -145,15 +183,18 @@ QQuickWidgetPrivate::QQuickWidgetPrivate()
, offscreenWindow(0)
, offscreenSurface(0)
, renderControl(0)
+#ifndef QT_NO_OPENGL
, fbo(0)
, resolvedFbo(0)
, context(0)
+#endif
, resizeMode(QQuickWidget::SizeViewToRootObject)
, initialSize(0,0)
, eventPending(false)
, updatePending(false)
, fakeHidden(false)
, requestedSamples(0)
+ , useSoftwareRenderer(false)
{
}
@@ -161,14 +202,21 @@ 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 {
+#ifndef QT_NO_OPENGL
+ // 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();
+#endif
+ }
}
void QQuickWidgetPrivate::execute()
@@ -196,47 +244,67 @@ void QQuickWidgetPrivate::execute()
}
}
-void QQuickWidgetPrivate::itemGeometryChanged(QQuickItem *resizeItem, const QRectF &newGeometry, const QRectF &oldGeometry)
+void QQuickWidgetPrivate::itemGeometryChanged(QQuickItem *resizeItem, QQuickGeometryChange change,
+ const QRectF &diff)
{
Q_Q(QQuickWidget);
if (resizeItem == root && resizeMode == QQuickWidget::SizeViewToRootObject) {
// wait for both width and height to be changed
resizetimer.start(0,q);
}
- QQuickItemChangeListener::itemGeometryChanged(resizeItem, newGeometry, oldGeometry);
+ QQuickItemChangeListener::itemGeometryChanged(resizeItem, change, diff);
}
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) {
+#ifndef QT_NO_OPENGL
+ // 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();
- static_cast<QOpenGLExtensions *>(context->functions())->flushShared();
+ QOpenGLContextPrivate::get(context)->defaultFboRedirect = 0;
+#endif
+ } else {
+ //Software Renderer
+ if (needsSync) {
+ renderControl->polishItems();
+ renderControl->sync();
+ }
+
+ QQuickWindowPrivate *cd = QQuickWindowPrivate::get(offscreenWindow);
+ auto softwareRenderer = static_cast<QSGSoftwareRenderer*>(cd->renderer);
+ if (softwareRenderer && !softwareImage.isNull()) {
+ softwareRenderer->setCurrentPaintDevice(&softwareImage);
+ renderControl->render();
- QOpenGLContextPrivate::get(context)->defaultFboRedirect = 0;
+ updateRegion += softwareRenderer->flushRegion();
+ }
+ }
}
void QQuickWidgetPrivate::renderSceneGraph()
@@ -247,13 +315,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);
@@ -262,15 +332,24 @@ 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) {
+#ifndef QT_NO_OPENGL
+ if (!context)
+ return QImage();
- context->makeCurrent(offscreenSurface);
+ context->makeCurrent(offscreenSurface);
+#endif
+ }
return renderControl->grab();
}
@@ -339,6 +418,36 @@ QObject *QQuickWidgetPrivate::focusObject()
entire purpose of QQuickWidget is to render Quick scenes without a separate native
window, hence making it a native widget should always be avoided.
+ \section1 Scene graph and context persistency
+
+ QQuickWidget honors QQuickWindow::isPersistentSceneGraph(), meaning that
+ applications can decide - by calling
+ QQuickWindow::setPersistentSceneGraph() on the window returned from the
+ quickWindow() function - to let scenegraph nodes and other Qt Quick scene
+ related resources be released whenever the widget becomes hidden. By default
+ persistency is enabled, just like with QQuickWindow.
+
+ When running with the OpenGL backend of the scene graph, QQuickWindow
+ offers the possibility to disable persistent OpenGL contexts as well. This
+ setting is currently ignored by QQuickWidget and the context is always
+ persistent. The OpenGL context is thus not destroyed when hiding the
+ widget. The context is destroyed only when the widget is destroyed or when
+ the widget gets reparented into another top-level widget's child hierarchy.
+ However, some applications, in particular those that have their own
+ graphics resources due to performing custom OpenGL rendering in the Qt
+ Quick scene, may wish to disable the latter since they may not be prepared
+ to handle the loss of the context when moving a QQuickWidget into another
+ window. Such applications can set the
+ QCoreApplication::AA_ShareOpenGLContexts attribute. For a discussion on the
+ details of resource initialization and cleanup, refer to the QOpenGLWidget
+ documentation.
+
+ \note QQuickWidget offers less fine-grained control over its internal
+ OpenGL context than QOpenGLWidget, and there are subtle differences, most
+ notably that disabling the persistent scene graph will lead to destroying
+ the context on a window change regardless of the presence of
+ QCoreApplication::AA_ShareOpenGLContexts.
+
\section1 Limitations
Putting other widgets underneath and making the QQuickWidget transparent will not lead
@@ -359,6 +468,13 @@ QObject *QQuickWidgetPrivate::focusObject()
Qt::WA_TranslucentBackground on the top-level window, request an alpha channel, and
change the Qt Quick Scenegraph's clear color to Qt::transparent via setClearColor().
+ \section1 Support when not using OpenGL
+
+ In addition to OpenGL, the \c software backend of Qt Quick also supports
+ QQuickWidget. Other backends, for example the Direct 3D 12 one, are not
+ compatible however and attempting to construct a QQuickWidget will lead to
+ problems.
+
\sa {Exposing Attributes of C++ Types to QML}, {Qt Quick Widgets Example}, QQuickView
*/
@@ -386,11 +502,8 @@ QQuickWidget::QQuickWidget(QWidget *parent)
*/
QQuickWidget::QQuickWidget(const QUrl &source, QWidget *parent)
-: QWidget(*(new QQuickWidgetPrivate), parent, 0)
+ : QQuickWidget(parent)
{
- setMouseTracking(true);
- setFocusPolicy(Qt::StrongFocus);
- d_func()->init();
setSource(source);
}
@@ -462,8 +575,8 @@ void QQuickWidget::setContent(const QUrl& url, QQmlComponent *component, QObject
d->component = component;
if (d->component && d->component->isError()) {
- QList<QQmlError> errorList = d->component->errors();
- foreach (const QQmlError &error, errorList) {
+ const QList<QQmlError> errorList = d->component->errors();
+ for (const QQmlError &error : errorList) {
QMessageLogger(error.url().toString().toLatin1().constData(), error.line(), 0).warning()
<< error;
}
@@ -652,9 +765,14 @@ void QQuickWidgetPrivate::updateSize()
q->resize(newSize);
}
} else if (resizeMode == QQuickWidget::SizeRootObjectToView) {
- if (!qFuzzyCompare(q->width(), root->width()))
+ bool needToUpdateWidth = !qFuzzyCompare(q->width(), root->width());
+ bool needToUpdateHeight = !qFuzzyCompare(q->height(), root->height());
+
+ if (needToUpdateWidth && needToUpdateHeight)
+ root->setSize(QSizeF(q->width(), q->height()));
+ else if (needToUpdateWidth)
root->setWidth(q->width());
- if (!qFuzzyCompare(q->height(), root->height()))
+ else if (needToUpdateHeight)
root->setHeight(q->height());
}
}
@@ -706,19 +824,22 @@ void QQuickWidgetPrivate::handleContextCreationFailure(const QSurfaceFormat &for
if (signalConnected)
emit q->sceneGraphError(QQuickWindow::ContextNotAvailable, translatedMessage);
-#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
if (!signalConnected && !QLibraryInfo::isDebugBuild() && !GetConsoleWindow())
QMessageBox::critical(q, QCoreApplication::applicationName(), translatedMessage);
-#endif // Q_OS_WIN && !Q_OS_WINCE && !Q_OS_WINRT
+#endif // Q_OS_WIN && !Q_OS_WINRT
if (!signalConnected)
qFatal("%s", qPrintable(untranslatedMessage));
}
+// Never called by Software Rendering backend
void QQuickWidgetPrivate::createContext()
{
+#ifndef QT_NO_OPENGL
Q_Q(QQuickWidget);
- // On hide-show we invalidate() but our context is kept.
- // We nonetheless need to initialize() again.
+
+ // On hide-show we may invalidate() (when !isPersistentSceneGraph) but our
+ // context is kept. We may need to initialize() again, though.
const bool reinit = context && !offscreenWindow->openglContext();
if (!reinit) {
@@ -752,18 +873,23 @@ void QQuickWidgetPrivate::createContext()
offscreenSurface->create();
}
- if (context->makeCurrent(offscreenSurface))
- renderControl->initialize(context);
- else
+ if (context->makeCurrent(offscreenSurface)) {
+ if (!offscreenWindow->openglContext())
+ renderControl->initialize(context);
+ } else
+#endif
qWarning("QQuickWidget: Failed to make context current");
}
+// Never called by Software Rendering backend
void QQuickWidgetPrivate::destroyContext()
{
delete offscreenSurface;
offscreenSurface = 0;
+#ifndef QT_NO_OPENGL
delete context;
context = 0;
+#endif
}
void QQuickWidget::createFramebufferObject()
@@ -775,6 +901,20 @@ void QQuickWidget::createFramebufferObject()
if (size().isEmpty())
return;
+ // Even though this is just an offscreen window we should set the position on it, as it might be
+ // useful for an item to know the actual position of the scene.
+ // Note: The position will be update when we get a move event (see: updatePosition()).
+ const QPoint &globalPos = mapToGlobal(QPoint(0, 0));
+ d->offscreenWindow->setGeometry(globalPos.x(), globalPos.y(), width(), height());
+
+ if (d->useSoftwareRenderer) {
+ const QSize imageSize = size() * devicePixelRatio();
+ d->softwareImage = QImage(imageSize, QImage::Format_ARGB32_Premultiplied);
+ d->softwareImage.setDevicePixelRatio(devicePixelRatio());
+ return;
+ }
+
+#ifndef QT_NO_OPENGL
QOpenGLContext *context = d->offscreenWindow->openglContext();
if (!context) {
@@ -834,11 +974,6 @@ void QQuickWidget::createFramebufferObject()
}
#endif
- // Even though this is just an offscreen window we should set the position on it, as it might be
- // useful for an item to know the actual position of the scene.
- // Note: The position will be update when we get a move event (see: updatePosition()).
- const QPoint &globalPos = mapToGlobal(QPoint(0, 0));
- d->offscreenWindow->setGeometry(globalPos.x(), globalPos.y(), width(), height());
d->offscreenWindow->setRenderTarget(d->fbo);
if (samples > 0)
@@ -848,15 +983,24 @@ void QQuickWidget::createFramebufferObject()
// Having one would mean create() was called and platforms that only support
// a single native window were in trouble.
Q_ASSERT(!d->offscreenWindow->handle());
+#endif
}
void QQuickWidget::destroyFramebufferObject()
{
Q_D(QQuickWidget);
+
+ if (d->useSoftwareRenderer) {
+ d->softwareImage = QImage();
+ return;
+ }
+
+#ifndef QT_NO_OPENGL
delete d->fbo;
d->fbo = 0;
delete d->resolvedFbo;
d->resolvedFbo = 0;
+#endif
}
QQuickWidget::ResizeMode QQuickWidget::resizeMode() const
@@ -874,8 +1018,8 @@ void QQuickWidget::continueExecute()
disconnect(d->component, SIGNAL(statusChanged(QQmlComponent::Status)), this, SLOT(continueExecute()));
if (d->component->isError()) {
- QList<QQmlError> errorList = d->component->errors();
- foreach (const QQmlError &error, errorList) {
+ const QList<QQmlError> errorList = d->component->errors();
+ for (const QQmlError &error : errorList) {
QMessageLogger(error.url().toString().toLatin1().constData(), error.line(), 0).warning()
<< error;
}
@@ -886,8 +1030,8 @@ void QQuickWidget::continueExecute()
QObject *obj = d->component->create();
if (d->component->isError()) {
- QList<QQmlError> errorList = d->component->errors();
- foreach (const QQmlError &error, errorList) {
+ const QList<QQmlError> errorList = d->component->errors();
+ for (const QQmlError &error : errorList) {
QMessageLogger(error.url().toString().toLatin1().constData(), error.line(), 0).warning()
<< error;
}
@@ -934,6 +1078,7 @@ void QQuickWidgetPrivate::setRootObject(QObject *obj)
}
}
+#ifndef QT_NO_OPENGL
GLuint QQuickWidgetPrivate::textureId() const
{
Q_Q(const QQuickWidget);
@@ -945,6 +1090,7 @@ GLuint QQuickWidgetPrivate::textureId() const
return resolvedFbo ? resolvedFbo->texture()
: (fbo ? fbo->texture() : 0);
}
+#endif
/*!
\internal
@@ -1027,26 +1173,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();
- }
+#ifndef QT_NO_OPENGL
+ 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;
+ QOpenGLContext *context = d->offscreenWindow->openglContext();
+ if (!context) {
+ qWarning("QQuickWidget::resizeEvent() no OpenGL context");
+ return;
+ }
+#endif
}
d->render(needsSync);
@@ -1110,22 +1266,24 @@ void QQuickWidget::mouseDoubleClickEvent(QMouseEvent *e)
void QQuickWidget::showEvent(QShowEvent *)
{
Q_D(QQuickWidget);
- d->createContext();
- if (d->offscreenWindow->openglContext()) {
- d->render(true);
- // render() may have led to a QQuickWindow::update() call (for
- // example, having a scene with a QQuickFramebufferObject::Renderer
- // calling update() in its render()) which in turn results in
- // renderRequested in the rendercontrol, ending up in
- // triggerUpdate. In this case just calling update() is not
- // acceptable, we need the full renderSceneGraph issued from
- // timerEvent().
- if (!d->eventPending && d->updatePending) {
- d->updatePending = false;
- update();
+ if (!d->useSoftwareRenderer) {
+ d->createContext();
+ if (d->offscreenWindow->openglContext()) {
+ d->render(true);
+ // render() may have led to a QQuickWindow::update() call (for
+ // example, having a scene with a QQuickFramebufferObject::Renderer
+ // calling update() in its render()) which in turn results in
+ // renderRequested in the rendercontrol, ending up in
+ // triggerUpdate. In this case just calling update() is not
+ // acceptable, we need the full renderSceneGraph issued from
+ // timerEvent().
+ if (!d->eventPending && d->updatePending) {
+ d->updatePending = false;
+ update();
+ }
+ } else {
+ triggerUpdate();
}
- } else {
- triggerUpdate();
}
QWindowPrivate *offscreenPrivate = QWindowPrivate::get(d->offscreenWindow);
if (!offscreenPrivate->visible) {
@@ -1141,7 +1299,8 @@ void QQuickWidget::showEvent(QShowEvent *)
void QQuickWidget::hideEvent(QHideEvent *)
{
Q_D(QQuickWidget);
- d->invalidateRenderControl();
+ if (!d->offscreenWindow->isPersistentSceneGraph())
+ d->invalidateRenderControl();
QWindowPrivate *offscreenPrivate = QWindowPrivate::get(d->offscreenWindow);
if (offscreenPrivate->visible) {
offscreenPrivate->visible = false;
@@ -1249,11 +1408,17 @@ bool QQuickWidget::event(QEvent *e)
d->offscreenWindow->setScreen(newScreen);
if (d->offscreenSurface)
d->offscreenSurface->setScreen(newScreen);
+#ifndef QT_NO_OPENGL
if (d->context)
d->context->setScreen(newScreen);
+#endif
}
- if (d->fbo) {
+ if (d->useSoftwareRenderer
+#ifndef QT_NO_OPENGL
+ || d->fbo
+#endif
+ ) {
// This will check the size taking the devicePixelRatio into account
// and recreate if needed.
createFramebufferObject();
@@ -1425,3 +1590,25 @@ 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
+ const auto rects = d->updateRegion.rects();
+ for (auto targetRect : 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 b01d634fcd..3d64981797 100644
--- a/src/quickwidgets/qquickwidget_p.h
+++ b/src/quickwidgets/qquickwidget_p.h
@@ -87,7 +87,7 @@ public:
~QQuickWidgetPrivate();
void execute();
- void itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry) Q_DECL_OVERRIDE;
+ void itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change, const QRectF &diff) Q_DECL_OVERRIDE;
void initResize();
void updateSize();
void updatePosition();
@@ -101,8 +101,12 @@ public:
QObject *focusObject() Q_DECL_OVERRIDE;
+#ifndef QT_NO_OPENGL
GLuint textureId() const Q_DECL_OVERRIDE;
QImage grabFramebuffer() Q_DECL_OVERRIDE;
+#else
+ QImage grabFramebuffer();
+#endif
void init(QQmlEngine* e = 0);
void ensureEngine() const;
@@ -121,9 +125,12 @@ public:
QQuickWindow *offscreenWindow;
QOffscreenSurface *offscreenSurface;
QQuickRenderControl *renderControl;
+
+#ifndef QT_NO_OPENGL
QOpenGLFramebufferObject *fbo;
QOpenGLFramebufferObject *resolvedFbo;
QOpenGLContext *context;
+#endif
QQuickWidget::ResizeMode resizeMode;
QSize initialSize;
@@ -135,6 +142,10 @@ public:
bool fakeHidden;
int requestedSamples;
+
+ bool useSoftwareRenderer;
+ QImage softwareImage;
+ QRegion updateRegion;
};
QT_END_NAMESPACE
diff --git a/src/quickwidgets/quickwidgets.pro b/src/quickwidgets/quickwidgets.pro
index 87409e31c5..2438e577ae 100644
--- a/src/quickwidgets/quickwidgets.pro
+++ b/src/quickwidgets/quickwidgets.pro
@@ -2,7 +2,7 @@ TARGET = QtQuickWidgets
QT = core-private gui-private qml-private quick-private widgets-private
-DEFINES += QT_NO_URL_CAST_FROM_STRING QT_NO_INTEGER_EVENT_COORDINATES
+DEFINES += QT_NO_URL_CAST_FROM_STRING QT_NO_INTEGER_EVENT_COORDINATES QT_NO_FOREACH
HEADERS += \
qquickwidget.h \
diff --git a/src/src.pro b/src/src.pro
index 385e4eb601..f585ef15ca 100644
--- a/src/src.pro
+++ b/src/src.pro
@@ -1,14 +1,17 @@
TEMPLATE = subdirs
CONFIG += ordered
+include($$OUT_PWD/quick/qtquick-config.pri)
+QT_FOR_CONFIG += quick-private
SUBDIRS += \
qml
-qtHaveModule(gui):contains(QT_CONFIG, opengl(es1|es2)?) {
+qtHaveModule(gui) {
SUBDIRS += \
quick \
- qmltest \
- particles
+ qmltest
+ qtConfig(quick-sprite):qtConfig(opengl(es1|es2)?): \
+ SUBDIRS += particles
qtHaveModule(widgets): SUBDIRS += quickwidgets
}
diff --git a/tests/auto/auto.pro b/tests/auto/auto.pro
index 425e88b983..556f5ddc7a 100644
--- a/tests/auto/auto.pro
+++ b/tests/auto/auto.pro
@@ -2,16 +2,21 @@ TEMPLATE=subdirs
SUBDIRS=\
qml \
quick \
- particles \
qmltest \
qmldevtools \
cmake \
installed_cmake \
toolsupport
-qtHaveModule(widgets): SUBDIRS += quickwidgets
+qtHaveModule(gui):qtConfig(opengl(es1|es2)?) {
+ SUBDIRS += particles
+ qtHaveModule(widgets): SUBDIRS += quickwidgets
+
+}
+
+# console applications not supported
+uikit: SUBDIRS -= qmltest
qmldevtools.CONFIG = host_build
installed_cmake.depends = cmake
-
diff --git a/tests/auto/particles/particles.pro b/tests/auto/particles/particles.pro
index 265a1eabc7..6ee1290dbb 100644
--- a/tests/auto/particles/particles.pro
+++ b/tests/auto/particles/particles.pro
@@ -25,6 +25,5 @@ PRIVATETESTS += \
qquickturbulence \
qquickwander
-contains(QT_CONFIG, private_tests) {
+qtConfig(private_tests): \
SUBDIRS += $$PRIVATETESTS
-}
diff --git a/tests/auto/particles/qquickage/tst_qquickage.cpp b/tests/auto/particles/qquickage/tst_qquickage.cpp
index 04c6fbd9e4..a233b30043 100644
--- a/tests/auto/particles/qquickage/tst_qquickage.cpp
+++ b/tests/auto/particles/qquickage/tst_qquickage.cpp
@@ -61,7 +61,7 @@ void tst_qquickage::test_kill()
ensureAnimTime(600, system->m_animation);
QVERIFY(extremelyFuzzyCompare(system->groupData[0]->size(), 500, 10));
- foreach (QQuickParticleData *d, system->groupData[0]->data) {
+ for (QQuickParticleData *d : qAsConst(system->groupData[0]->data)) {
if (d->t == -1)
continue; //Particle data unused
@@ -86,7 +86,7 @@ void tst_qquickage::test_jump()
ensureAnimTime(600, system->m_animation);
QVERIFY(extremelyFuzzyCompare(system->groupData[0]->size(), 500, 10));
- foreach (QQuickParticleData *d, system->groupData[0]->data) {
+ for (QQuickParticleData *d : qAsConst(system->groupData[0]->data)) {
if (d->t == -1)
continue; //Particle data unused
@@ -112,7 +112,7 @@ void tst_qquickage::test_onceOff()
ensureAnimTime(600, system->m_animation);
QVERIFY(extremelyFuzzyCompare(system->groupData[0]->size(), 500, 10));
- foreach (QQuickParticleData *d, system->groupData[0]->data) {
+ for (QQuickParticleData *d : qAsConst(system->groupData[0]->data)) {
if (d->t == -1)
continue; //Particle data unused
@@ -138,7 +138,7 @@ void tst_qquickage::test_sustained()
//TODO: Ensure some particles have lived to 0.4s point despite unified timer
//Can't verify size, because particles never die. It will constantly grow.
- foreach (QQuickParticleData *d, system->groupData[0]->data) {
+ for (QQuickParticleData *d : qAsConst(system->groupData[0]->data)) {
if (d->t == -1)
continue; //Particle data unused
diff --git a/tests/auto/particles/qquickangleddirection/tst_qquickangleddirection.cpp b/tests/auto/particles/qquickangleddirection/tst_qquickangleddirection.cpp
index 4ff72f159d..b3fc01caa6 100644
--- a/tests/auto/particles/qquickangleddirection/tst_qquickangleddirection.cpp
+++ b/tests/auto/particles/qquickangleddirection/tst_qquickangleddirection.cpp
@@ -58,7 +58,7 @@ void tst_qquickangleddirection::test_basic()
ensureAnimTime(600, system->m_animation);
QVERIFY(extremelyFuzzyCompare(system->groupData[0]->size(), 500, 10));
- foreach (QQuickParticleData *d, system->groupData[0]->data) {
+ for (QQuickParticleData *d : qAsConst(system->groupData[0]->data)) {
if (d->t == -1)
continue; //Particle data unused
diff --git a/tests/auto/particles/qquickcumulativedirection/tst_qquickcumulativedirection.cpp b/tests/auto/particles/qquickcumulativedirection/tst_qquickcumulativedirection.cpp
index 795fcaf42e..360b222367 100644
--- a/tests/auto/particles/qquickcumulativedirection/tst_qquickcumulativedirection.cpp
+++ b/tests/auto/particles/qquickcumulativedirection/tst_qquickcumulativedirection.cpp
@@ -57,7 +57,7 @@ void tst_qquickcumulativedirection::test_basic()
ensureAnimTime(600, system->m_animation);
QVERIFY(extremelyFuzzyCompare(system->groupData[0]->size(), 500, 10));
- foreach (QQuickParticleData *d, system->groupData[0]->data) {
+ for (QQuickParticleData *d : qAsConst(system->groupData[0]->data)) {
if (d->t == -1)
continue; //Particle data unused
diff --git a/tests/auto/particles/qquickcustomaffector/tst_qquickcustomaffector.cpp b/tests/auto/particles/qquickcustomaffector/tst_qquickcustomaffector.cpp
index fb15af4dd1..ee05fb29c9 100644
--- a/tests/auto/particles/qquickcustomaffector/tst_qquickcustomaffector.cpp
+++ b/tests/auto/particles/qquickcustomaffector/tst_qquickcustomaffector.cpp
@@ -59,7 +59,7 @@ void tst_qquickcustomaffector::test_basic()
ensureAnimTime(600, system->m_animation);
QVERIFY(extremelyFuzzyCompare(system->groupData[0]->size(), 500, 10));
- foreach (QQuickParticleData *d, system->groupData[0]->data) {
+ for (QQuickParticleData *d : qAsConst(system->groupData[0]->data)) {
if (d->t == -1)
continue; //Particle data unused
//in CI the whole simulation often happens at once, so dead particles end up missing out
@@ -92,7 +92,7 @@ void tst_qquickcustomaffector::test_move()
ensureAnimTime(600, system->m_animation);
QVERIFY(extremelyFuzzyCompare(system->groupData[0]->size(), 500, 10));
- foreach (QQuickParticleData *d, system->groupData[0]->data) {
+ for (QQuickParticleData *d : qAsConst(system->groupData[0]->data)) {
if (d->t == -1)
continue; //Particle data unused
if (!d->stillAlive(system))
diff --git a/tests/auto/particles/qquickcustomparticle/tst_qquickcustomparticle.cpp b/tests/auto/particles/qquickcustomparticle/tst_qquickcustomparticle.cpp
index a722508a3e..60c6a37899 100644
--- a/tests/auto/particles/qquickcustomparticle/tst_qquickcustomparticle.cpp
+++ b/tests/auto/particles/qquickcustomparticle/tst_qquickcustomparticle.cpp
@@ -60,7 +60,7 @@ void tst_qquickcustomparticle::test_basic()
bool oneNonZero = false;
QVERIFY(extremelyFuzzyCompare(system->groupData[0]->size(), 500, 10));
- foreach (QQuickParticleData *d, system->groupData[0]->data) {
+ for (QQuickParticleData *d : qAsConst(system->groupData[0]->data)) {
if (d->t == -1)
continue; //Particle data unused
diff --git a/tests/auto/particles/qquickellipseextruder/tst_qquickellipseextruder.cpp b/tests/auto/particles/qquickellipseextruder/tst_qquickellipseextruder.cpp
index ac8e2bac49..a1fe5b225d 100644
--- a/tests/auto/particles/qquickellipseextruder/tst_qquickellipseextruder.cpp
+++ b/tests/auto/particles/qquickellipseextruder/tst_qquickellipseextruder.cpp
@@ -74,7 +74,7 @@ void tst_qquickellipseextruder::test_basic()
//Filled
QVERIFY(extremelyFuzzyCompare(system->groupData[0]->size(), 500, 10));
- foreach (QQuickParticleData *d, system->groupData[0]->data) {
+ for (QQuickParticleData *d : qAsConst(system->groupData[0]->data)) {
if (d->t == -1)
continue; //Particle data unused
@@ -91,7 +91,7 @@ void tst_qquickellipseextruder::test_basic()
//Just border
QCOMPARE(system->groupData[1]->size(), 500);
- foreach (QQuickParticleData *d, system->groupData[1]->data) {
+ for (QQuickParticleData *d : qAsConst(system->groupData[1]->data)) {
if (d->t == -1)
continue; //Particle data unused
diff --git a/tests/auto/particles/qquickfriction/tst_qquickfriction.cpp b/tests/auto/particles/qquickfriction/tst_qquickfriction.cpp
index 70e0291330..ab682c591e 100644
--- a/tests/auto/particles/qquickfriction/tst_qquickfriction.cpp
+++ b/tests/auto/particles/qquickfriction/tst_qquickfriction.cpp
@@ -59,7 +59,7 @@ void tst_qquickfriction::test_basic()
//Default is just slowed a little
QVERIFY(extremelyFuzzyCompare(system->groupData[0]->size(), 500, 10));
- foreach (QQuickParticleData *d, system->groupData[0]->data) {
+ for (QQuickParticleData *d : qAsConst(system->groupData[0]->data)) {
if (d->t == -1)
continue; //Particle data unused
@@ -76,7 +76,7 @@ void tst_qquickfriction::test_basic()
//Nondefault comes to a complete stop within the first half of its life
QCOMPARE(system->groupData[1]->size(), 500);
- foreach (QQuickParticleData *d, system->groupData[1]->data) {
+ for (QQuickParticleData *d : qAsConst(system->groupData[1]->data)) {
if (d->t == -1)
continue; //Particle data unused
@@ -103,7 +103,7 @@ void tst_qquickfriction::test_threshold()
//Velocity capped at 50, but it might take a frame or two to get there
QVERIFY(extremelyFuzzyCompare(system->groupData[0]->size(), 500, 10));
- foreach (QQuickParticleData *d, system->groupData[0]->data) {
+ for (QQuickParticleData *d : qAsConst(system->groupData[0]->data)) {
if (d->t == -1.0f)
continue; //Particle data unused
if (myFuzzyGEQ(d->t, ((qreal)system->timeInt/1000.0) - 0.1))
diff --git a/tests/auto/particles/qquickgravity/tst_qquickgravity.cpp b/tests/auto/particles/qquickgravity/tst_qquickgravity.cpp
index 8ef075dc9b..34566b90eb 100644
--- a/tests/auto/particles/qquickgravity/tst_qquickgravity.cpp
+++ b/tests/auto/particles/qquickgravity/tst_qquickgravity.cpp
@@ -58,7 +58,7 @@ void tst_qquickgravity::test_basic()
QVERIFY(extremelyFuzzyCompare(system->groupData[0]->size(), 500, 10));
float mag = 707.10678f;
- foreach (QQuickParticleData *d, system->groupData[0]->data) {
+ for (QQuickParticleData *d : qAsConst(system->groupData[0]->data)) {
if (d->t == -1 || !d->stillAlive(system))
continue; //Particle data unused or dead
diff --git a/tests/auto/particles/qquickgroupgoal/tst_qquickgroupgoal.cpp b/tests/auto/particles/qquickgroupgoal/tst_qquickgroupgoal.cpp
index fc270b991f..1228b4cc68 100644
--- a/tests/auto/particles/qquickgroupgoal/tst_qquickgroupgoal.cpp
+++ b/tests/auto/particles/qquickgroupgoal/tst_qquickgroupgoal.cpp
@@ -58,7 +58,7 @@ void tst_qquickgroupgoal::test_instantTransition()
ensureAnimTime(600, system->m_animation);
QVERIFY(system->groupData[0]->size() <= 500 && system->groupData[0]->size() >= 450);
- foreach (QQuickParticleData *d, system->groupData[0]->data) {
+ for (QQuickParticleData *d : qAsConst(system->groupData[0]->data)) {
if (d->t == -1)
continue; //Particle data unused
diff --git a/tests/auto/particles/qquickimageparticle/tst_qquickimageparticle.cpp b/tests/auto/particles/qquickimageparticle/tst_qquickimageparticle.cpp
index cd684ec59b..7e07878d39 100644
--- a/tests/auto/particles/qquickimageparticle/tst_qquickimageparticle.cpp
+++ b/tests/auto/particles/qquickimageparticle/tst_qquickimageparticle.cpp
@@ -72,7 +72,7 @@ void tst_qquickimageparticle::test_basic()
ensureAnimTime(600, system->m_animation);
QVERIFY(extremelyFuzzyCompare(system->groupData[0]->size(), 500, 10));
- foreach (QQuickParticleData *d, system->groupData[0]->data) {
+ for (QQuickParticleData *d : qAsConst(system->groupData[0]->data)) {
if (d->t == -1)
continue; //Particle data unused
@@ -116,7 +116,7 @@ void tst_qquickimageparticle::test_colored()
ensureAnimTime(600, system->m_animation);
QVERIFY(extremelyFuzzyCompare(system->groupData[0]->size(), 500, 10));
- foreach (QQuickParticleData *d, system->groupData[0]->data) {
+ for (QQuickParticleData *d : qAsConst(system->groupData[0]->data)) {
if (d->t == -1)
continue; //Particle data unused
@@ -160,7 +160,7 @@ void tst_qquickimageparticle::test_colorVariance()
ensureAnimTime(600, system->m_animation);
QVERIFY(extremelyFuzzyCompare(system->groupData[0]->size(), 500, 10));
- foreach (QQuickParticleData *d, system->groupData[0]->data) {
+ for (QQuickParticleData *d : qAsConst(system->groupData[0]->data)) {
if (d->t == -1)
continue; //Particle data unused
@@ -205,7 +205,7 @@ void tst_qquickimageparticle::test_deformed()
ensureAnimTime(600, system->m_animation);
QVERIFY(extremelyFuzzyCompare(system->groupData[0]->size(), 500, 10));
- foreach (QQuickParticleData *d, system->groupData[0]->data) {
+ for (QQuickParticleData *d : qAsConst(system->groupData[0]->data)) {
if (d->t == -1)
continue; //Particle data unused
@@ -249,7 +249,7 @@ void tst_qquickimageparticle::test_tabled()
ensureAnimTime(600, system->m_animation);
QVERIFY(extremelyFuzzyCompare(system->groupData[0]->size(), 500, 10));
- foreach (QQuickParticleData *d, system->groupData[0]->data) {
+ for (QQuickParticleData *d : qAsConst(system->groupData[0]->data)) {
if (d->t == -1)
continue; //Particle data unused
@@ -294,7 +294,7 @@ void tst_qquickimageparticle::test_sprite()
ensureAnimTime(600, system->m_animation);
QVERIFY(extremelyFuzzyCompare(system->groupData[0]->size(), 500, 10));
- foreach (QQuickParticleData *d, system->groupData[0]->data) {
+ for (QQuickParticleData *d : qAsConst(system->groupData[0]->data)) {
if (d->t == -1)
continue; //Particle data unused
diff --git a/tests/auto/particles/qquickitemparticle/tst_qquickitemparticle.cpp b/tests/auto/particles/qquickitemparticle/tst_qquickitemparticle.cpp
index 98aac71e93..d9791cdb33 100644
--- a/tests/auto/particles/qquickitemparticle/tst_qquickitemparticle.cpp
+++ b/tests/auto/particles/qquickitemparticle/tst_qquickitemparticle.cpp
@@ -60,7 +60,7 @@ void tst_qquickitemparticle::test_basic()
ensureAnimTime(600, system->m_animation);
QVERIFY(extremelyFuzzyCompare(system->groupData[0]->size(), 500, 10));
- foreach (QQuickParticleData *d, system->groupData[0]->data) {
+ for (QQuickParticleData *d : qAsConst(system->groupData[0]->data)) {
if (d->t == -1)
continue; //Particle data unused
diff --git a/tests/auto/particles/qquicklineextruder/tst_qquicklineextruder.cpp b/tests/auto/particles/qquicklineextruder/tst_qquicklineextruder.cpp
index 1f36874f0f..6baf8539d3 100644
--- a/tests/auto/particles/qquicklineextruder/tst_qquicklineextruder.cpp
+++ b/tests/auto/particles/qquicklineextruder/tst_qquicklineextruder.cpp
@@ -57,7 +57,7 @@ void tst_qquicklineextruder::test_basic()
ensureAnimTime(600, system->m_animation);
QVERIFY(extremelyFuzzyCompare(system->groupData[0]->size(), 500, 10));
- foreach (QQuickParticleData *d, system->groupData[0]->data) {
+ for (QQuickParticleData *d : qAsConst(system->groupData[0]->data)) {
if (d->t == -1)
continue; //Particle data unused
@@ -73,7 +73,7 @@ void tst_qquicklineextruder::test_basic()
}
QCOMPARE(system->groupData[1]->size(), 500);
- foreach (QQuickParticleData *d, system->groupData[1]->data) {
+ for (QQuickParticleData *d : qAsConst(system->groupData[1]->data)) {
if (d->t == -1)
continue; //Particle data unused
diff --git a/tests/auto/particles/qquickmaskextruder/tst_qquickmaskextruder.cpp b/tests/auto/particles/qquickmaskextruder/tst_qquickmaskextruder.cpp
index a3d0988019..d8481efc47 100644
--- a/tests/auto/particles/qquickmaskextruder/tst_qquickmaskextruder.cpp
+++ b/tests/auto/particles/qquickmaskextruder/tst_qquickmaskextruder.cpp
@@ -57,7 +57,7 @@ void tst_qquickmaskextruder::test_basic()
ensureAnimTime(600, system->m_animation);
QVERIFY(extremelyFuzzyCompare(system->groupData[0]->size(), 500, 10));
- foreach (QQuickParticleData *d, system->groupData[0]->data) {
+ for (QQuickParticleData *d : qAsConst(system->groupData[0]->data)) {
if (d->t == -1)
continue; //Particle data unused
diff --git a/tests/auto/particles/qquickparticlegroup/tst_qquickparticlegroup.cpp b/tests/auto/particles/qquickparticlegroup/tst_qquickparticlegroup.cpp
index 8b7224623a..8791d19db6 100644
--- a/tests/auto/particles/qquickparticlegroup/tst_qquickparticlegroup.cpp
+++ b/tests/auto/particles/qquickparticlegroup/tst_qquickparticlegroup.cpp
@@ -58,7 +58,7 @@ void tst_qquickparticlegroup::test_instantTransition()
//A frame or two worth of particles will be missed, the transition doesn't take effect on the frame it's spawned (QTBUG-21781)
QVERIFY(system->groupData[0]->size() <= 500 && system->groupData[0]->size() >= 450);
- foreach (QQuickParticleData *d, system->groupData[0]->data) {
+ for (QQuickParticleData *d : qAsConst(system->groupData[0]->data)) {
if (d->t == -1)
continue; //Particle data unused
diff --git a/tests/auto/particles/qquickparticlesystem/tst_qquickparticlesystem.cpp b/tests/auto/particles/qquickparticlesystem/tst_qquickparticlesystem.cpp
index 4a3c5cdc74..5c82b946e5 100644
--- a/tests/auto/particles/qquickparticlesystem/tst_qquickparticlesystem.cpp
+++ b/tests/auto/particles/qquickparticlesystem/tst_qquickparticlesystem.cpp
@@ -58,7 +58,7 @@ void tst_qquickparticlesystem::test_basic()
QVERIFY(extremelyFuzzyCompare(system->groupData[0]->size(), 500, 10));
int stillAlive = 0;
- foreach (QQuickParticleData *d, system->groupData[0]->data) {
+ for (QQuickParticleData *d : qAsConst(system->groupData[0]->data)) {
if (d->t == -1)
continue; //Particle data unused
diff --git a/tests/auto/particles/qquickpointattractor/tst_qquickpointattractor.cpp b/tests/auto/particles/qquickpointattractor/tst_qquickpointattractor.cpp
index 99a8530d3a..c0d7d38512 100644
--- a/tests/auto/particles/qquickpointattractor/tst_qquickpointattractor.cpp
+++ b/tests/auto/particles/qquickpointattractor/tst_qquickpointattractor.cpp
@@ -57,7 +57,7 @@ void tst_qquickpointattractor::test_basic()
ensureAnimTime(600, system->m_animation);
QVERIFY(extremelyFuzzyCompare(system->groupData[0]->size(), 500, 10));
- foreach (QQuickParticleData *d, system->groupData[0]->data) {
+ for (QQuickParticleData *d : qAsConst(system->groupData[0]->data)) {
if (d->t == -1)
continue; //Particle data unused
diff --git a/tests/auto/particles/qquickpointdirection/tst_qquickpointdirection.cpp b/tests/auto/particles/qquickpointdirection/tst_qquickpointdirection.cpp
index 0d150fb86d..5cc23e0306 100644
--- a/tests/auto/particles/qquickpointdirection/tst_qquickpointdirection.cpp
+++ b/tests/auto/particles/qquickpointdirection/tst_qquickpointdirection.cpp
@@ -57,7 +57,7 @@ void tst_qquickpointdirection::test_basic()
ensureAnimTime(600, system->m_animation);
QVERIFY(extremelyFuzzyCompare(system->groupData[0]->size(), 500, 10));
- foreach (QQuickParticleData *d, system->groupData[0]->data) {
+ for (QQuickParticleData *d : qAsConst(system->groupData[0]->data)) {
if (d->t == -1)
continue; //Particle data unused
diff --git a/tests/auto/particles/qquickrectangleextruder/tst_qquickrectangleextruder.cpp b/tests/auto/particles/qquickrectangleextruder/tst_qquickrectangleextruder.cpp
index 24b30bf9ab..414e2d36f7 100644
--- a/tests/auto/particles/qquickrectangleextruder/tst_qquickrectangleextruder.cpp
+++ b/tests/auto/particles/qquickrectangleextruder/tst_qquickrectangleextruder.cpp
@@ -57,7 +57,7 @@ void tst_qquickrectangleextruder::test_basic()
ensureAnimTime(600, system->m_animation);
QVERIFY(extremelyFuzzyCompare(system->groupData[0]->size(), 500, 10));
- foreach (QQuickParticleData *d, system->groupData[0]->data) {
+ for (QQuickParticleData *d : qAsConst(system->groupData[0]->data)) {
if (d->t == -1)
continue; //Particle data unused
@@ -76,7 +76,7 @@ void tst_qquickrectangleextruder::test_basic()
}
QCOMPARE(system->groupData[1]->size(), 500);
- foreach (QQuickParticleData *d, system->groupData[1]->data) {
+ for (QQuickParticleData *d : qAsConst(system->groupData[1]->data)) {
if (d->t == -1)
continue; //Particle data unused
diff --git a/tests/auto/particles/qquickspritegoal/tst_qquickspritegoal.cpp b/tests/auto/particles/qquickspritegoal/tst_qquickspritegoal.cpp
index 8249159e43..ca5d9171f9 100644
--- a/tests/auto/particles/qquickspritegoal/tst_qquickspritegoal.cpp
+++ b/tests/auto/particles/qquickspritegoal/tst_qquickspritegoal.cpp
@@ -57,7 +57,7 @@ void tst_qquickspritegoal::test_instantTransition()
ensureAnimTime(600, system->m_animation);
QVERIFY(system->groupData[0]->size() <= 500 && system->groupData[0]->size() >= 450);
- foreach (QQuickParticleData *d, system->groupData[0]->data) {
+ for (QQuickParticleData *d : qAsConst(system->groupData[0]->data)) {
if (d->t == -1)
continue; //Particle data unused
diff --git a/tests/auto/particles/qquicktargetdirection/tst_qquicktargetdirection.cpp b/tests/auto/particles/qquicktargetdirection/tst_qquicktargetdirection.cpp
index 8d9556fdac..2f45263c37 100644
--- a/tests/auto/particles/qquicktargetdirection/tst_qquicktargetdirection.cpp
+++ b/tests/auto/particles/qquicktargetdirection/tst_qquicktargetdirection.cpp
@@ -57,7 +57,7 @@ void tst_qquicktargetdirection::test_basic()
ensureAnimTime(600, system->m_animation);
QVERIFY(extremelyFuzzyCompare(system->groupData[0]->size(), 500, 10));
- foreach (QQuickParticleData *d, system->groupData[0]->data) {
+ for (QQuickParticleData *d : qAsConst(system->groupData[0]->data)) {
if (d->t == -1)
continue; //Particle data unused
diff --git a/tests/auto/particles/qquicktrailemitter/tst_qquicktrailemitter.cpp b/tests/auto/particles/qquicktrailemitter/tst_qquicktrailemitter.cpp
index 99ecd7a06a..27d9bf01c9 100644
--- a/tests/auto/particles/qquicktrailemitter/tst_qquicktrailemitter.cpp
+++ b/tests/auto/particles/qquicktrailemitter/tst_qquicktrailemitter.cpp
@@ -57,7 +57,7 @@ void tst_qquicktrailemitter::test_basic()
ensureAnimTime(600, system->m_animation);
QVERIFY(extremelyFuzzyCompare(system->groupData[0]->size(), 500, 10));
- foreach (QQuickParticleData *d, system->groupData[0]->data) {
+ for (QQuickParticleData *d : qAsConst(system->groupData[0]->data)) {
if (d->t == -1)
continue; //Particle data unused
@@ -74,7 +74,7 @@ void tst_qquicktrailemitter::test_basic()
}
QVERIFY(extremelyFuzzyCompare(system->groupData[1]->size(), 500, 10));
- foreach (QQuickParticleData *d, system->groupData[1]->data) {
+ for (QQuickParticleData *d : qAsConst(system->groupData[1]->data)) {
if (d->t == -1)
continue; //Particle data unused
diff --git a/tests/auto/particles/qquickturbulence/tst_qquickturbulence.cpp b/tests/auto/particles/qquickturbulence/tst_qquickturbulence.cpp
index c8024470e4..74fafdc1d2 100644
--- a/tests/auto/particles/qquickturbulence/tst_qquickturbulence.cpp
+++ b/tests/auto/particles/qquickturbulence/tst_qquickturbulence.cpp
@@ -59,7 +59,7 @@ void tst_qquickturbulence::test_basic()
//Note that the noise image built-in provides the 'randomness', so this test should be stable so long as it and the size
//of the Turbulence item remain the same
QVERIFY(extremelyFuzzyCompare(system->groupData[0]->size(), 500, 10));
- foreach (QQuickParticleData *d, system->groupData[0]->data) {
+ for (QQuickParticleData *d : qAsConst(system->groupData[0]->data)) {
if (d->t == -1)
continue; //Particle data unused
diff --git a/tests/auto/particles/qquickwander/tst_qquickwander.cpp b/tests/auto/particles/qquickwander/tst_qquickwander.cpp
index 6e37fc648c..51ef7a272e 100644
--- a/tests/auto/particles/qquickwander/tst_qquickwander.cpp
+++ b/tests/auto/particles/qquickwander/tst_qquickwander.cpp
@@ -61,7 +61,7 @@ void tst_qquickwander::test_basic()
//the 500 was randomly changed from 0.0 in velocity
bool vxChanged = false;
bool vyChanged = false;
- foreach (QQuickParticleData *d, system->groupData[0]->data) {
+ for (QQuickParticleData *d : qAsConst(system->groupData[0]->data)) {
if (d->t == -1)
continue; //Particle data unused
diff --git a/tests/auto/qml/animation/qparallelanimationgroupjob/tst_qparallelanimationgroupjob.cpp b/tests/auto/qml/animation/qparallelanimationgroupjob/tst_qparallelanimationgroupjob.cpp
index ab8fa8a9ef..476ad2e955 100644
--- a/tests/auto/qml/animation/qparallelanimationgroupjob/tst_qparallelanimationgroupjob.cpp
+++ b/tests/auto/qml/animation/qparallelanimationgroupjob/tst_qparallelanimationgroupjob.cpp
@@ -63,8 +63,8 @@ private slots:
void tst_QParallelAnimationGroupJob::initTestCase()
{
qRegisterMetaType<QAbstractAnimationJob::State>("QAbstractAnimationJob::State");
-#if defined(Q_OS_MAC) || defined(Q_OS_WINCE)
- // give the mac/wince app start event queue time to clear
+#if defined(Q_OS_DARWIN)
+ // give the Apple application's start event queue time to clear
QTest::qWait(1000);
#endif
}
diff --git a/tests/auto/qml/debugger/debugger.pro b/tests/auto/qml/debugger/debugger.pro
index ccb2d71c53..a50411e18b 100644
--- a/tests/auto/qml/debugger/debugger.pro
+++ b/tests/auto/qml/debugger/debugger.pro
@@ -20,6 +20,6 @@ PRIVATETESTS += \
SUBDIRS += $$PUBLICTESTS
-contains(QT_CONFIG, private_tests) {
+qtConfig(private_tests): \
SUBDIRS += $$PRIVATETESTS
-}
+
diff --git a/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs/tst_qqmldebugjs.cpp b/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs/tst_qqmldebugjs.cpp
index b800cc3715..d1150be831 100644
--- a/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs/tst_qqmldebugjs.cpp
+++ b/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs/tst_qqmldebugjs.cpp
@@ -42,11 +42,6 @@
#include <QtCore/qlibraryinfo.h>
#include <QtQml/qjsengine.h>
-#if defined (Q_OS_WINCE)
-#undef IN
-#undef OUT
-#endif
-
const char *V8REQUEST = "v8request";
const char *V8MESSAGE = "v8message";
const char *SEQ = "seq";
diff --git a/tests/auto/qml/debugger/qqmlenginedebugservice/tst_qqmlenginedebugservice.cpp b/tests/auto/qml/debugger/qqmlenginedebugservice/tst_qqmlenginedebugservice.cpp
index 4568d25dc1..40e19d375d 100644
--- a/tests/auto/qml/debugger/qqmlenginedebugservice/tst_qqmlenginedebugservice.cpp
+++ b/tests/auto/qml/debugger/qqmlenginedebugservice/tst_qqmlenginedebugservice.cpp
@@ -1223,16 +1223,15 @@ void tst_QQmlEngineDebugService::queryObjectTree()
int main(int argc, char *argv[])
{
int _argc = argc + 1;
- char **_argv = new char*[_argc];
+ QScopedArrayPointer<char *>_argv(new char*[_argc]);
for (int i = 0; i < argc; ++i)
_argv[i] = argv[i];
char arg[] = "-qmljsdebugger=port:3768,services:QmlDebugger";
_argv[_argc - 1] = arg;
- QGuiApplication app(_argc, _argv);
+ QGuiApplication app(_argc, _argv.data());
tst_QQmlEngineDebugService tc;
- return QTest::qExec(&tc, _argc, _argv);
- delete _argv;
+ return QTest::qExec(&tc, _argc, _argv.data());
}
#include "tst_qqmlenginedebugservice.moc"
diff --git a/tests/auto/qml/debugger/qqmlprofilerservice/data/controlFromJS.qml b/tests/auto/qml/debugger/qqmlprofilerservice/data/controlFromJS.qml
index d1db2af367..dd7cb2055d 100644
--- a/tests/auto/qml/debugger/qqmlprofilerservice/data/controlFromJS.qml
+++ b/tests/auto/qml/debugger/qqmlprofilerservice/data/controlFromJS.qml
@@ -26,10 +26,10 @@
**
****************************************************************************/
-import QtQuick 2.0
+import QtQml 2.0
-Rectangle {
- Timer {
+QtObject {
+ property var timer: Timer {
running: true
interval: 1
onTriggered: {
@@ -38,7 +38,7 @@ Rectangle {
}
}
- Timer {
+ property var stopTimer: Timer {
id: stopTimer
interval: 1000
onTriggered: {
@@ -47,7 +47,7 @@ Rectangle {
}
}
- Timer {
+ property var endTimer: Timer {
id: endTimer
interval: 1000
onTriggered: Qt.quit();
diff --git a/tests/auto/qml/debugger/qqmlprofilerservice/data/exit.qml b/tests/auto/qml/debugger/qqmlprofilerservice/data/exit.qml
index b250524caa..4236d70ea3 100644
--- a/tests/auto/qml/debugger/qqmlprofilerservice/data/exit.qml
+++ b/tests/auto/qml/debugger/qqmlprofilerservice/data/exit.qml
@@ -1,6 +1,6 @@
-import QtQuick 2.0
+import QtQml 2.0
-Item {
+QtObject {
Timer {
running: true
interval: 1
diff --git a/tests/auto/qml/debugger/qqmlprofilerservice/data/javascript.qml b/tests/auto/qml/debugger/qqmlprofilerservice/data/javascript.qml
index 0555d49652..e25c7524f4 100644
--- a/tests/auto/qml/debugger/qqmlprofilerservice/data/javascript.qml
+++ b/tests/auto/qml/debugger/qqmlprofilerservice/data/javascript.qml
@@ -1,6 +1,6 @@
-import QtQuick 2.0
+import QtQml 2.0
-Rectangle {
+QtObject {
function something(i) {
if (i > 10) {
something(i / 4);
@@ -9,8 +9,8 @@ Rectangle {
}
}
- width: 400
- height: 400
+ property int width: 400
+ property int height: 400
onWidthChanged: something(width);
Component.onCompleted: width = 500;
diff --git a/tests/auto/qml/debugger/qqmlprofilerservice/data/signalSourceLocation.qml b/tests/auto/qml/debugger/qqmlprofilerservice/data/signalSourceLocation.qml
index 25e63669c4..0eff9e9030 100644
--- a/tests/auto/qml/debugger/qqmlprofilerservice/data/signalSourceLocation.qml
+++ b/tests/auto/qml/debugger/qqmlprofilerservice/data/signalSourceLocation.qml
@@ -1,8 +1,8 @@
-import QtQuick 2.0
+import QtQml 2.0
-Rectangle {
- width: 400
- height: 400
+QtObject {
+ property int width: 400
+ property int height: 400
onWidthChanged: console.log(width);
Component.onCompleted: width = 500;
diff --git a/tests/auto/qml/debugger/qqmlprofilerservice/data/test.qml b/tests/auto/qml/debugger/qqmlprofilerservice/data/test.qml
index 9c36e13c5b..5dd5caf12f 100644
--- a/tests/auto/qml/debugger/qqmlprofilerservice/data/test.qml
+++ b/tests/auto/qml/debugger/qqmlprofilerservice/data/test.qml
@@ -1,5 +1,5 @@
-import QtQuick 2.0
+import QtQml 2.0
-Item {
+QtObject {
}
diff --git a/tests/auto/qml/debugger/qqmlprofilerservice/data/timer.qml b/tests/auto/qml/debugger/qqmlprofilerservice/data/timer.qml
index 18b8947172..4af555a27b 100644
--- a/tests/auto/qml/debugger/qqmlprofilerservice/data/timer.qml
+++ b/tests/auto/qml/debugger/qqmlprofilerservice/data/timer.qml
@@ -1,10 +1,10 @@
-import QtQuick 2.0
+import QtQml 2.0
-Rectangle {
- width: 100
- height: 62
+QtObject {
+ property int width: 100
+ property int height: 62
- Timer {
+ property var timer: Timer {
running: true
repeat: true
interval: 50
diff --git a/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp b/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp
index 4f9088d67d..c4b17aa60a 100644
--- a/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp
+++ b/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp
@@ -64,7 +64,8 @@ class QQmlProfilerTestClient : public QQmlProfilerClient
Q_OBJECT
public:
- QQmlProfilerTestClient(QQmlDebugConnection *connection) : QQmlProfilerClient(connection) {}
+ QQmlProfilerTestClient(QQmlDebugConnection *connection) : QQmlProfilerClient(connection),
+ lastTimestamp(-1) {}
QVector<QQmlProfilerData> qmlMessages;
QVector<QQmlProfilerData> javascriptMessages;
@@ -72,6 +73,8 @@ public:
QVector<QQmlProfilerData> asynchronousMessages;
QVector<QQmlProfilerData> pixmapMessages;
+ qint64 lastTimestamp;
+
signals:
void recordingFinished();
@@ -114,6 +117,8 @@ void QQmlProfilerTestClient::traceFinished(qint64 time, int engineId)
void QQmlProfilerTestClient::rangeStart(QQmlProfilerDefinitions::RangeType type, qint64 startTime)
{
QVERIFY(type >= 0 && type < QQmlProfilerDefinitions::MaximumRangeType);
+ QVERIFY(lastTimestamp <= startTime);
+ lastTimestamp = startTime;
QQmlProfilerData data(startTime, QQmlProfilerDefinitions::RangeStart, type);
if (type == QQmlProfilerDefinitions::Javascript)
javascriptMessages.append(data);
@@ -125,6 +130,8 @@ void QQmlProfilerTestClient::rangeData(QQmlProfilerDefinitions::RangeType type,
const QString &string)
{
QVERIFY(type >= 0 && type < QQmlProfilerDefinitions::MaximumRangeType);
+ QVERIFY(lastTimestamp <= time);
+ lastTimestamp = time;
QQmlProfilerData data(time, QQmlProfilerDefinitions::RangeData, type, string);
if (type == QQmlProfilerDefinitions::Javascript)
javascriptMessages.append(data);
@@ -137,6 +144,8 @@ void QQmlProfilerTestClient::rangeLocation(QQmlProfilerDefinitions::RangeType ty
{
QVERIFY(type >= 0 && type < QQmlProfilerDefinitions::MaximumRangeType);
QVERIFY(location.line >= -2);
+ QVERIFY(lastTimestamp <= time);
+ lastTimestamp = time;
QQmlProfilerData data(time, QQmlProfilerDefinitions::RangeLocation, type, location.filename);
data.line = location.line;
data.column = location.column;
@@ -149,6 +158,8 @@ void QQmlProfilerTestClient::rangeLocation(QQmlProfilerDefinitions::RangeType ty
void QQmlProfilerTestClient::rangeEnd(QQmlProfilerDefinitions::RangeType type, qint64 endTime)
{
QVERIFY(type >= 0 && type < QQmlProfilerDefinitions::MaximumRangeType);
+ QVERIFY(lastTimestamp <= endTime);
+ lastTimestamp = endTime;
QQmlProfilerData data(endTime, QQmlProfilerDefinitions::RangeEnd, type);
if (type == QQmlProfilerDefinitions::Javascript)
javascriptMessages.append(data);
@@ -161,6 +172,8 @@ void QQmlProfilerTestClient::animationFrame(qint64 time, int frameRate, int anim
QVERIFY(threadId >= 0);
QVERIFY(frameRate != -1);
QVERIFY(animationCount != -1);
+ QVERIFY(lastTimestamp <= time);
+ lastTimestamp = time;
QQmlProfilerData data(time, QQmlProfilerDefinitions::Event,
QQmlProfilerDefinitions::AnimationFrame);
data.framerate = frameRate;
@@ -178,6 +191,8 @@ void QQmlProfilerTestClient::sceneGraphEvent(QQmlProfilerDefinitions::SceneGraph
Q_UNUSED(numericData3);
Q_UNUSED(numericData4);
Q_UNUSED(numericData5);
+ QVERIFY(lastTimestamp <= time);
+ lastTimestamp = time;
asynchronousMessages.append(QQmlProfilerData(time, QQmlProfilerDefinitions::SceneGraphFrame,
type));
}
@@ -186,6 +201,8 @@ void QQmlProfilerTestClient::pixmapCacheEvent(QQmlProfilerDefinitions::PixmapEve
qint64 time, const QString &url, int numericData1,
int numericData2)
{
+ QVERIFY(lastTimestamp <= time);
+ lastTimestamp = time;
QQmlProfilerData data(time, QQmlProfilerDefinitions::PixmapCacheEvent, type, url);
switch (type) {
case QQmlProfilerDefinitions::PixmapSizeKnown:
@@ -205,6 +222,8 @@ void QQmlProfilerTestClient::pixmapCacheEvent(QQmlProfilerDefinitions::PixmapEve
void QQmlProfilerTestClient::memoryAllocation(QQmlProfilerDefinitions::MemoryType type, qint64 time,
qint64 amount)
{
+ QVERIFY(lastTimestamp <= time);
+ lastTimestamp = time;
QQmlProfilerData data(time, QQmlProfilerDefinitions::MemoryAllocation, type);
data.amount = amount;
jsHeapMessages.append(data);
@@ -213,6 +232,8 @@ void QQmlProfilerTestClient::memoryAllocation(QQmlProfilerDefinitions::MemoryTyp
void QQmlProfilerTestClient::inputEvent(QQmlProfilerDefinitions::InputEventType type, qint64 time,
int a, int b)
{
+ QVERIFY(lastTimestamp <= time);
+ lastTimestamp = time;
qmlMessages.append(QQmlProfilerData(time, QQmlProfilerDefinitions::Event, type,
QString::number(a) + QLatin1Char('x') +
QString::number(b)));
@@ -597,7 +618,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 +629,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);
@@ -667,11 +688,11 @@ void tst_QQmlProfilerService::signalSourceLocation()
QLatin1String("signalSourceLocation.qml"));
expected.line = 8;
expected.column = 28;
- VERIFY(MessageListQML, 13, expected, CheckAll);
+ VERIFY(MessageListQML, 9, expected, CheckAll);
expected.line = 7;
expected.column = 21;
- VERIFY(MessageListQML, 15, expected, CheckAll);
+ VERIFY(MessageListQML, 11, expected, CheckAll);
}
void tst_QQmlProfilerService::javascript()
diff --git a/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp b/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp
index 3f89913f3b..a23b7e37eb 100644
--- a/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp
+++ b/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp
@@ -326,7 +326,7 @@ private slots:
private:
QV4Debugger *debugger() const
{
- return static_cast<QV4Debugger *>(m_v4->debugger);
+ return static_cast<QV4Debugger *>(m_v4->debugger());
}
void evaluateJavaScript(const QString &script, const QString &fileName, int lineNumber = 1)
{
@@ -444,7 +444,7 @@ void tst_qv4debugger::addBreakPointWhilePaused()
static QV4::ReturnedValue someCall(QV4::CallContext *ctx)
{
- static_cast<QV4Debugger *>(ctx->d()->engine->debugger)
+ static_cast<QV4Debugger *>(ctx->d()->engine->debugger())
->removeBreakPoint("removeBreakPointForNextInstruction", 2);
return QV4::Encode::undefined();
}
diff --git a/tests/auto/qml/debugger/shared/debugutil_p.h b/tests/auto/qml/debugger/shared/debugutil_p.h
index 6fe5d897e0..1ec0a6513d 100644
--- a/tests/auto/qml/debugger/shared/debugutil_p.h
+++ b/tests/auto/qml/debugger/shared/debugutil_p.h
@@ -135,8 +135,6 @@ public:
int lastResponseId;
bool lastResult;
-public slots:
-
void recordResponse(int requestId, bool result)
{
lastResponseId = requestId;
diff --git a/tests/auto/qml/qjsengine/tst_qjsengine.cpp b/tests/auto/qml/qjsengine/tst_qjsengine.cpp
index 63fefbe9f1..c20f0888d8 100644
--- a/tests/auto/qml/qjsengine/tst_qjsengine.cpp
+++ b/tests/auto/qml/qjsengine/tst_qjsengine.cpp
@@ -73,6 +73,7 @@ private slots:
void newQObject();
void newQObject_ownership();
void newQObject_deletedEngine();
+ void newQMetaObject();
void exceptionInSlot();
void globalObjectProperties();
void globalObjectEquals();
@@ -124,6 +125,7 @@ private slots:
void jsIncDecNonObjectProperty();
void JSONparse();
void arraySort();
+ void lookupOnDisappearingProperty();
void qRegExpInport_data();
void qRegExpInport();
@@ -192,6 +194,8 @@ private slots:
void withNoContext();
void holeInPropertyData();
+ void basicBlockMergeAfterLoopPeeling();
+
signals:
void testSignal();
};
@@ -716,6 +720,104 @@ void tst_QJSEngine::newQObject_deletedEngine()
QTRY_VERIFY(spy.count());
}
+class TestQMetaObject : public QObject {
+ Q_OBJECT
+ Q_PROPERTY(int called READ called)
+public:
+ enum Enum1 {
+ Zero = 0,
+ One,
+ Two
+ };
+ enum Enum2 {
+ A = 0,
+ B,
+ C
+ };
+ Q_ENUMS(Enum1 Enum2)
+
+ Q_INVOKABLE TestQMetaObject()
+ : m_called(1) {
+ }
+ Q_INVOKABLE TestQMetaObject(int)
+ : m_called(2) {
+ }
+ Q_INVOKABLE TestQMetaObject(QString)
+ : m_called(3) {
+ }
+ Q_INVOKABLE TestQMetaObject(QString, int)
+ : m_called(4) {
+ }
+ int called() const {
+ return m_called;
+ }
+private:
+ int m_called;
+};
+
+void tst_QJSEngine::newQMetaObject() {
+ {
+ QJSEngine engine;
+ QJSValue metaObject = engine.newQMetaObject(&TestQMetaObject::staticMetaObject);
+ QCOMPARE(metaObject.isNull(), false);
+ QCOMPARE(metaObject.isObject(), true);
+ QCOMPARE(metaObject.isQObject(), false);
+ QCOMPARE(metaObject.isCallable(), true);
+ QCOMPARE(metaObject.isQMetaObject(), true);
+
+ QCOMPARE(metaObject.toQMetaObject(), &TestQMetaObject::staticMetaObject);
+
+ QVERIFY(metaObject.strictlyEquals(engine.newQMetaObject<TestQMetaObject>()));
+
+
+ {
+ auto result = metaObject.callAsConstructor();
+ if (result.isError())
+ qDebug() << result.toString();
+ QCOMPARE(result.isError(), false);
+ QCOMPARE(result.isNull(), false);
+ QCOMPARE(result.isObject(), true);
+ QCOMPARE(result.isQObject(), true);
+ QVERIFY(result.property("constructor").strictlyEquals(metaObject));
+ QVERIFY(result.prototype().strictlyEquals(metaObject));
+
+
+ QCOMPARE(result.property("called").toInt(), 1);
+
+ }
+
+ QJSValue integer(42);
+ QJSValue string("foo");
+
+ {
+ auto result = metaObject.callAsConstructor({integer});
+ QCOMPARE(result.property("called").toInt(), 2);
+ }
+
+ {
+ auto result = metaObject.callAsConstructor({string});
+ QCOMPARE(result.property("called").toInt(), 3);
+ }
+
+ {
+ auto result = metaObject.callAsConstructor({string, integer});
+ QCOMPARE(result.property("called").toInt(), 4);
+ }
+ }
+
+ {
+ QJSEngine engine;
+ QJSValue metaObject = engine.newQMetaObject(&TestQMetaObject::staticMetaObject);
+ QCOMPARE(metaObject.property("Zero").toInt(), 0);
+ QCOMPARE(metaObject.property("One").toInt(), 1);
+ QCOMPARE(metaObject.property("Two").toInt(), 2);
+ QCOMPARE(metaObject.property("A").toInt(), 0);
+ QCOMPARE(metaObject.property("B").toInt(), 1);
+ QCOMPARE(metaObject.property("C").toInt(), 2);
+ }
+
+}
+
void tst_QJSEngine::exceptionInSlot()
{
QJSEngine engine;
@@ -1029,11 +1131,14 @@ void tst_QJSEngine::builtinFunctionNames_data()
QTest::newRow("Math.pow") << QString("Math.pow") << QString("pow");
QTest::newRow("Math.random") << QString("Math.random") << QString("random");
QTest::newRow("Math.round") << QString("Math.round") << QString("round");
+ QTest::newRow("Math.sign") << QString("Math.sign") << QString("sign");
QTest::newRow("Math.sin") << QString("Math.sin") << QString("sin");
QTest::newRow("Math.sqrt") << QString("Math.sqrt") << QString("sqrt");
QTest::newRow("Math.tan") << QString("Math.tan") << QString("tan");
QTest::newRow("Number") << QString("Number") << QString("Number");
+ QTest::newRow("Number.isFinite") << QString("Number.isFinite") << QString("isFinite");
+ QTest::newRow("Number.isNaN") << QString("Number.isNaN") << QString("isNaN");
QTest::newRow("Number.prototype.toString") << QString("Number.prototype.toString") << QString("toString");
QTest::newRow("Number.prototype.toLocaleString") << QString("Number.prototype.toLocaleString") << QString("toLocaleString");
QTest::newRow("Number.prototype.valueOf") << QString("Number.prototype.valueOf") << QString("valueOf");
@@ -1062,6 +1167,8 @@ void tst_QJSEngine::builtinFunctionNames_data()
QTest::newRow("String.prototype.charAt") << QString("String.prototype.charAt") << QString("charAt");
QTest::newRow("String.prototype.charCodeAt") << QString("String.prototype.charCodeAt") << QString("charCodeAt");
QTest::newRow("String.prototype.concat") << QString("String.prototype.concat") << QString("concat");
+ QTest::newRow("String.prototype.endsWith") << QString("String.prototype.endsWith") << QString("endsWith");
+ QTest::newRow("String.prototype.includes") << QString("String.prototype.includes") << QString("includes");
QTest::newRow("String.prototype.indexOf") << QString("String.prototype.indexOf") << QString("indexOf");
QTest::newRow("String.prototype.lastIndexOf") << QString("String.prototype.lastIndexOf") << QString("lastIndexOf");
QTest::newRow("String.prototype.localeCompare") << QString("String.prototype.localeCompare") << QString("localeCompare");
@@ -1070,6 +1177,7 @@ void tst_QJSEngine::builtinFunctionNames_data()
QTest::newRow("String.prototype.search") << QString("String.prototype.search") << QString("search");
QTest::newRow("String.prototype.slice") << QString("String.prototype.slice") << QString("slice");
QTest::newRow("String.prototype.split") << QString("String.prototype.split") << QString("split");
+ QTest::newRow("String.prototype.startsWith") << QString("String.prototype.startsWith") << QString("startsWith");
QTest::newRow("String.prototype.substring") << QString("String.prototype.substring") << QString("substring");
QTest::newRow("String.prototype.toLowerCase") << QString("String.prototype.toLowerCase") << QString("toLowerCase");
QTest::newRow("String.prototype.toLocaleLowerCase") << QString("String.prototype.toLocaleLowerCase") << QString("toLocaleLowerCase");
@@ -1341,6 +1449,7 @@ void tst_QJSEngine::valueConversion_QVariant()
QCOMPARE(qjsvalue_cast<QVariant>(QJSValue(123)), QVariant(123));
QVERIFY(eng.toScriptValue(QVariant(QMetaType::VoidStar, 0)).isNull());
+ QVERIFY(eng.toScriptValue(QVariant::fromValue(nullptr)).isNull());
{
QVariantMap map;
@@ -1922,6 +2031,7 @@ void tst_QJSEngine::jsNumberClass()
QVERIFY(ctor.property("NaN").isNumber());
QVERIFY(ctor.property("NEGATIVE_INFINITY").isNumber());
QVERIFY(ctor.property("POSITIVE_INFINITY").isNumber());
+ QVERIFY(ctor.property("EPSILON").isNumber());
}
QCOMPARE(proto.toNumber(), qreal(0));
QVERIFY(proto.property("constructor").strictlyEquals(ctor));
@@ -1960,6 +2070,50 @@ void tst_QJSEngine::jsNumberClass()
QCOMPARE(ret.toNumber(), qreal(456));
}
+ QVERIFY(ctor.property("isFinite").isCallable());
+ {
+ QJSValue ret = eng.evaluate("Number.isFinite()");
+ QVERIFY(ret.isBool());
+ QCOMPARE(ret.toBool(), false);
+ }
+ {
+ QJSValue ret = eng.evaluate("Number.isFinite(NaN)");
+ QVERIFY(ret.isBool());
+ QCOMPARE(ret.toBool(), false);
+ }
+ {
+ QJSValue ret = eng.evaluate("Number.isFinite(Infinity)");
+ QVERIFY(ret.isBool());
+ QCOMPARE(ret.toBool(), false);
+ }
+ {
+ QJSValue ret = eng.evaluate("Number.isFinite(-Infinity)");
+ QVERIFY(ret.isBool());
+ QCOMPARE(ret.toBool(), false);
+ }
+ {
+ QJSValue ret = eng.evaluate("Number.isFinite(123)");
+ QVERIFY(ret.isBool());
+ QCOMPARE(ret.toBool(), true);
+ }
+
+ QVERIFY(ctor.property("isNaN").isCallable());
+ {
+ QJSValue ret = eng.evaluate("Number.isNaN()");
+ QVERIFY(ret.isBool());
+ QCOMPARE(ret.toBool(), false);
+ }
+ {
+ QJSValue ret = eng.evaluate("Number.isNaN(NaN)");
+ QVERIFY(ret.isBool());
+ QCOMPARE(ret.toBool(), true);
+ }
+ {
+ QJSValue ret = eng.evaluate("Number.isNaN(123)");
+ QVERIFY(ret.isBool());
+ QCOMPARE(ret.toBool(), false);
+ }
+
QVERIFY(proto.property("toString").isCallable());
{
QJSValue ret = eng.evaluate("new Number(123).toString()");
@@ -2813,6 +2967,22 @@ void tst_QJSEngine::arraySort()
"crashMe();");
}
+void tst_QJSEngine::lookupOnDisappearingProperty()
+{
+ QJSEngine eng;
+ QJSValue func = eng.evaluate("(function(){\"use strict\"; return eval(\"function(obj) { return obj.someProperty; }\")})()");
+ QVERIFY(func.isCallable());
+
+ QJSValue o = eng.newObject();
+ o.setProperty(QStringLiteral("someProperty"), 42);
+
+ QCOMPARE(func.call(QJSValueList()<< o).toInt(), 42);
+
+ o = eng.newObject();
+ QVERIFY(func.call(QJSValueList()<< o).isUndefined());
+ QVERIFY(func.call(QJSValueList()<< o).isUndefined());
+}
+
static QRegExp minimal(QRegExp r) { r.setMinimal(true); return r; }
void tst_QJSEngine::qRegExpInport_data()
@@ -3876,6 +4046,22 @@ void tst_QJSEngine::holeInPropertyData()
QVERIFY(ok.toBool());
}
+void tst_QJSEngine::basicBlockMergeAfterLoopPeeling()
+{
+ QJSEngine engine;
+ QJSValue ok = engine.evaluate(
+ "function crashMe() {\n"
+ " var seen = false;\n"
+ " while (globalVar) {\n"
+ " if (seen)\n"
+ " return;\n"
+ " seen = true;\n"
+ " }\n"
+ "}\n");
+ QVERIFY(!ok.isCallable());
+
+}
+
QTEST_MAIN(tst_QJSEngine)
#include "tst_qjsengine.moc"
diff --git a/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp b/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp
index 859caf72c7..d28bbc1ffa 100644
--- a/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp
+++ b/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp
@@ -431,6 +431,12 @@ void tst_QJSValue::toString()
QVERIFY(!o.engine());
QCOMPARE(o.toString(), QStringLiteral("1,2,3"));
}
+
+ {
+ QByteArray hello = QByteArrayLiteral("Hello World");
+ QJSValue jsValue = eng.toScriptValue(hello);
+ QCOMPARE(jsValue.toString(), QString::fromUtf8(hello));
+ }
}
void tst_QJSValue::toNumber()
@@ -978,8 +984,8 @@ void tst_QJSValue::toVariant()
QCOMPARE(qjsvalue_cast<QVariant>(undefined), QVariant());
QJSValue null = eng.evaluate("null");
- QCOMPARE(null.toVariant(), QVariant(QMetaType::VoidStar, 0));
- QCOMPARE(qjsvalue_cast<QVariant>(null), QVariant(QMetaType::VoidStar, 0));
+ QCOMPARE(null.toVariant(), QVariant::fromValue(nullptr));
+ QCOMPARE(qjsvalue_cast<QVariant>(null), QVariant::fromValue(nullptr));
{
QJSValue number = eng.toScriptValue(123.0);
@@ -1055,8 +1061,8 @@ void tst_QJSValue::toVariant()
QCOMPARE(qjsvalue_cast<QVariant>(undef), QVariant());
QJSValue nil = QJSValue(QJSValue::NullValue);
- QCOMPARE(nil.toVariant(), QVariant(QMetaType::VoidStar, 0));
- QCOMPARE(qjsvalue_cast<QVariant>(nil), QVariant(QMetaType::VoidStar, 0));
+ QCOMPARE(nil.toVariant(), QVariant::fromValue(nullptr));
+ QCOMPARE(qjsvalue_cast<QVariant>(nil), QVariant::fromValue(nullptr));
}
// array
diff --git a/tests/auto/qml/qml.pro b/tests/auto/qml/qml.pro
index 28f04be5d7..68a2eace19 100644
--- a/tests/auto/qml/qml.pro
+++ b/tests/auto/qml/qml.pro
@@ -8,7 +8,6 @@ PUBLICTESTS += \
qjsvalueiterator \
qjsonbinding \
qmlmin \
- qmlplugindump \
qqmlcomponent \
qqmlconsole \
qqmlengine \
@@ -61,7 +60,9 @@ PRIVATETESTS += \
v4misc \
qqmltranslation \
qqmlimport \
- qqmlobjectmodel
+ qqmlobjectmodel \
+ qmldiskcache \
+ qv4mm
qtHaveModule(widgets) {
PUBLICTESTS += \
@@ -72,14 +73,13 @@ qtHaveModule(widgets) {
SUBDIRS += $$PUBLICTESTS \
qqmlextensionplugin
SUBDIRS += $$METATYPETESTS
-!winrt { # no QProcess on winrt
+!uikit:!winrt { # no QProcess on uikit/winrt
!contains(QT_CONFIG, no-qml-debug): SUBDIRS += debugger
- SUBDIRS += qmllint
+ SUBDIRS += qmllint qmlplugindump
}
-contains(QT_CONFIG, private_tests) {
+qtConfig(private_tests): \
SUBDIRS += $$PRIVATETESTS
-}
qtNomakeTools( \
qmlplugindump \
diff --git a/tests/auto/qml/qmldiskcache/qmldiskcache.pro b/tests/auto/qml/qmldiskcache/qmldiskcache.pro
new file mode 100644
index 0000000000..f98a157b6a
--- /dev/null
+++ b/tests/auto/qml/qmldiskcache/qmldiskcache.pro
@@ -0,0 +1,9 @@
+CONFIG += testcase
+TARGET = tst_qmldiskcache
+osx:CONFIG -= app_bundle
+
+SOURCES += tst_qmldiskcache.cpp
+
+RESOURCES += test.qml
+
+QT += core-private qml-private testlib
diff --git a/tests/auto/qml/qmldiskcache/test.qml b/tests/auto/qml/qmldiskcache/test.qml
new file mode 100644
index 0000000000..d632422616
--- /dev/null
+++ b/tests/auto/qml/qmldiskcache/test.qml
@@ -0,0 +1,4 @@
+import QtQml 2.0
+QtObject {
+ property int value: 20
+}
diff --git a/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp
new file mode 100644
index 0000000000..e2c0055ea1
--- /dev/null
+++ b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp
@@ -0,0 +1,671 @@
+/****************************************************************************
+**
+** 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 <qtest.h>
+
+#include <private/qv4compileddata_p.h>
+#include <private/qv4compiler_p.h>
+#include <private/qv4jsir_p.h>
+#include <private/qv4isel_p.h>
+#include <private/qv8engine_p.h>
+#include <private/qv4engine_p.h>
+#include <QQmlComponent>
+#include <QQmlEngine>
+#include <QQmlFileSelector>
+#include <QThread>
+#include <QCryptographicHash>
+#include <QStandardPaths>
+#include <QDirIterator>
+
+class tst_qmldiskcache: public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void initTestCase();
+
+ void regenerateAfterChange();
+ void registerImportForImplicitComponent();
+ void basicVersionChecks();
+ void recompileAfterChange();
+ void fileSelectors();
+ void localAliases();
+ void cacheResources();
+ void stableOrderOfDependentCompositeTypes();
+};
+
+// A wrapper around QQmlComponent to ensure the temporary reference counts
+// on the type data as a result of the main thread <> loader thread communication
+// are dropped. Regular Synchronous loading will leave us with an event posted
+// to the gui thread and an extra refcount that will only be dropped after the
+// event delivery. A plain sendPostedEvents() however is insufficient because
+// we can't be sure that the event is posted after the constructor finished.
+class CleanlyLoadingComponent : public QQmlComponent
+{
+public:
+ CleanlyLoadingComponent(QQmlEngine *engine, const QUrl &url)
+ : QQmlComponent(engine, url, QQmlComponent::Asynchronous)
+ { waitForLoad(); }
+ CleanlyLoadingComponent(QQmlEngine *engine, const QString &fileName)
+ : QQmlComponent(engine, fileName, QQmlComponent::Asynchronous)
+ { waitForLoad(); }
+
+ void waitForLoad()
+ {
+ QTRY_VERIFY(status() == QQmlComponent::Ready || status() == QQmlComponent::Error);
+ }
+};
+
+static void waitForFileSystem()
+{
+ // On macOS with HFS+ the precision of file times is measured in seconds, so to ensure that
+ // the newly written file has a modification date newer than an existing cache file, we must
+ // wait.
+ // Similar effects of lacking precision have been observed on some Linux systems.
+ QThread::sleep(1);
+}
+
+struct TestCompiler
+{
+ TestCompiler(QQmlEngine *engine)
+ : engine(engine)
+ , tempDir()
+ , testFilePath(tempDir.path() + QStringLiteral("/test.qml"))
+ , cacheFilePath(tempDir.path() + QStringLiteral("/test.qmlc"))
+ , mappedFile(cacheFilePath)
+ , currentMapping(nullptr)
+ {
+ }
+
+ bool compile(const QByteArray &contents)
+ {
+ closeMapping();
+ engine->clearComponentCache();
+
+ waitForFileSystem();
+
+ {
+ QFile f(testFilePath);
+ if (!f.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
+ lastErrorString = f.errorString();
+ return false;
+ }
+ if (f.write(contents) != contents.size()) {
+ lastErrorString = f.errorString();
+ return false;
+ }
+ }
+
+ CleanlyLoadingComponent component(engine, testFilePath);
+ if (!component.isReady()) {
+ lastErrorString = component.errorString();
+ return false;
+ }
+
+ return true;
+ }
+
+ const QV4::CompiledData::Unit *mapUnit()
+ {
+ if (!mappedFile.open(QIODevice::ReadOnly)) {
+ lastErrorString = mappedFile.errorString();
+ return nullptr;
+ }
+
+ currentMapping = mappedFile.map(/*offset*/0, mappedFile.size());
+ if (!currentMapping) {
+ lastErrorString = mappedFile.errorString();
+ return nullptr;
+ }
+ QV4::CompiledData::Unit *unitPtr;
+ memcpy(&unitPtr, &currentMapping, sizeof(unitPtr));
+ return unitPtr;
+ }
+
+ typedef void (*HeaderTweakFunction)(QV4::CompiledData::Unit *header);
+ bool tweakHeader(HeaderTweakFunction function)
+ {
+ closeMapping();
+
+ QFile f(cacheFilePath);
+ if (!f.open(QIODevice::ReadWrite))
+ return false;
+ QV4::CompiledData::Unit header;
+ if (f.read(reinterpret_cast<char *>(&header), sizeof(header)) != sizeof(header))
+ return false;
+ function(&header);
+ f.seek(0);
+ return f.write(reinterpret_cast<const char *>(&header), sizeof(header)) == sizeof(header);
+ }
+
+ bool verify()
+ {
+ QV4::ExecutionEngine *v4 = QV8Engine::getV4(engine);
+ QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit = v4->iselFactory->createUnitForLoading();
+ return unit->loadFromDisk(QUrl::fromLocalFile(testFilePath), v4->iselFactory.data(), &lastErrorString);
+ }
+
+ void closeMapping()
+ {
+ if (currentMapping) {
+ mappedFile.unmap(currentMapping);
+ currentMapping = nullptr;
+ }
+ mappedFile.close();
+ }
+
+ void clearCache()
+ {
+ closeMapping();
+ QFile::remove(cacheFilePath);
+ }
+
+ QQmlEngine *engine;
+ const QTemporaryDir tempDir;
+ const QString testFilePath;
+ const QString cacheFilePath;
+ QString lastErrorString;
+ QFile mappedFile;
+ uchar *currentMapping;
+};
+
+void tst_qmldiskcache::initTestCase()
+{
+ qputenv("QML_FORCE_DISK_CACHE", "1");
+}
+
+void tst_qmldiskcache::regenerateAfterChange()
+{
+ QQmlEngine engine;
+ TestCompiler testCompiler(&engine);
+
+ QVERIFY(testCompiler.tempDir.isValid());
+
+ const QByteArray contents = QByteArrayLiteral("import QtQml 2.0\n"
+ "QtObject {\n"
+ " property string blah: Qt.platform;\n"
+ "}");
+
+ QVERIFY2(testCompiler.compile(contents), qPrintable(testCompiler.lastErrorString));
+
+ {
+ const QV4::CompiledData::Unit *testUnit = testCompiler.mapUnit();
+ QVERIFY2(testUnit, qPrintable(testCompiler.lastErrorString));
+
+ QCOMPARE(quint32(testUnit->nObjects), quint32(1));
+
+ const QV4::CompiledData::Object *obj = testUnit->objectAt(0);
+ QCOMPARE(quint32(obj->nBindings), quint32(1));
+ QCOMPARE(quint32(obj->bindingTable()->type), quint32(QV4::CompiledData::Binding::Type_Script));
+ QCOMPARE(quint32(obj->bindingTable()->value.compiledScriptIndex), quint32(1));
+
+ QCOMPARE(quint32(testUnit->functionTableSize), quint32(2));
+
+ const QV4::CompiledData::Function *bindingFunction = testUnit->functionAt(1);
+ QVERIFY(bindingFunction->codeOffset > testUnit->unitSize);
+ }
+
+ {
+ const QByteArray newContents = QByteArrayLiteral("import QtQml 2.0\n"
+ "QtObject {\n"
+ " property string blah: Qt.platform;\n"
+ " property int secondProperty: 42;\n"
+ "}");
+
+ QVERIFY2(testCompiler.compile(newContents), qPrintable(testCompiler.lastErrorString));
+ const QV4::CompiledData::Unit *testUnit = testCompiler.mapUnit();
+ QVERIFY2(testUnit, qPrintable(testCompiler.lastErrorString));
+
+ QCOMPARE(quint32(testUnit->nObjects), quint32(1));
+
+ const QV4::CompiledData::Object *obj = testUnit->objectAt(0);
+ QCOMPARE(quint32(obj->nBindings), quint32(2));
+ QCOMPARE(quint32(obj->bindingTable()->type), quint32(QV4::CompiledData::Binding::Type_Number));
+ QCOMPARE(obj->bindingTable()->valueAsNumber(), double(42));
+
+ QCOMPARE(quint32(testUnit->functionTableSize), quint32(2));
+
+ const QV4::CompiledData::Function *bindingFunction = testUnit->functionAt(1);
+ QVERIFY(bindingFunction->codeOffset > testUnit->unitSize);
+ }
+}
+
+void tst_qmldiskcache::registerImportForImplicitComponent()
+{
+ QQmlEngine engine;
+
+ TestCompiler testCompiler(&engine);
+ QVERIFY(testCompiler.tempDir.isValid());
+
+ const QByteArray contents = QByteArrayLiteral("import QtQuick 2.0\n"
+ "Loader {\n"
+ " sourceComponent: Item {}\n"
+ "}");
+
+ QVERIFY2(testCompiler.compile(contents), qPrintable(testCompiler.lastErrorString));
+ {
+ const QV4::CompiledData::Unit *testUnit = testCompiler.mapUnit();
+ QVERIFY2(testUnit, qPrintable(testCompiler.lastErrorString));
+
+ QCOMPARE(quint32(testUnit->nImports), quint32(2));
+ QCOMPARE(testUnit->stringAt(testUnit->importAt(0)->uriIndex), QStringLiteral("QtQuick"));
+
+ QQmlType *componentType = QQmlMetaType::qmlType(&QQmlComponent::staticMetaObject);
+
+ QCOMPARE(testUnit->stringAt(testUnit->importAt(1)->uriIndex), QString(componentType->module()));
+ QCOMPARE(testUnit->stringAt(testUnit->importAt(1)->qualifierIndex), QStringLiteral("QmlInternals"));
+
+ QCOMPARE(quint32(testUnit->nObjects), quint32(3));
+
+ const QV4::CompiledData::Object *obj = testUnit->objectAt(0);
+ QCOMPARE(quint32(obj->nBindings), quint32(1));
+ QCOMPARE(quint32(obj->bindingTable()->type), quint32(QV4::CompiledData::Binding::Type_Object));
+
+ const QV4::CompiledData::Object *implicitComponent = testUnit->objectAt(obj->bindingTable()->value.objectIndex);
+ QCOMPARE(testUnit->stringAt(implicitComponent->inheritedTypeNameIndex), QStringLiteral("QmlInternals.") + componentType->elementName());
+ }
+}
+
+void tst_qmldiskcache::basicVersionChecks()
+{
+ QQmlEngine engine;
+
+ TestCompiler testCompiler(&engine);
+ QVERIFY(testCompiler.tempDir.isValid());
+
+ const QByteArray contents = QByteArrayLiteral("import QtQml 2.0\n"
+ "QtObject {\n"
+ " property string blah: Qt.platform;\n"
+ "}");
+
+ {
+ testCompiler.clearCache();
+ QVERIFY2(testCompiler.compile(contents), qPrintable(testCompiler.lastErrorString));
+ QVERIFY2(testCompiler.verify(), qPrintable(testCompiler.lastErrorString));
+ }
+
+ {
+ testCompiler.clearCache();
+ QVERIFY2(testCompiler.compile(contents), qPrintable(testCompiler.lastErrorString));
+
+ testCompiler.tweakHeader([](QV4::CompiledData::Unit *header) {
+ header->qtVersion = 0;
+ });
+
+ QVERIFY(!testCompiler.verify());
+ QCOMPARE(testCompiler.lastErrorString, QString::fromUtf8("Qt version mismatch. Found 0 expected %1").arg(QT_VERSION, 0, 16));
+ testCompiler.clearCache();
+ }
+
+ {
+ testCompiler.clearCache();
+ QVERIFY2(testCompiler.compile(contents), qPrintable(testCompiler.lastErrorString));
+
+ testCompiler.tweakHeader([](QV4::CompiledData::Unit *header) {
+ header->version = 0;
+ });
+
+ QVERIFY(!testCompiler.verify());
+ QCOMPARE(testCompiler.lastErrorString, QString::fromUtf8("V4 data structure version mismatch. Found 0 expected %1").arg(QV4_DATA_STRUCTURE_VERSION, 0, 16));
+ }
+
+ {
+ testCompiler.clearCache();
+ QVERIFY2(testCompiler.compile(contents), qPrintable(testCompiler.lastErrorString));
+
+ testCompiler.tweakHeader([](QV4::CompiledData::Unit *header) {
+ header->architectureIndex = 0;
+ });
+
+ QVERIFY(!testCompiler.verify());
+ QCOMPARE(testCompiler.lastErrorString, QString::fromUtf8("Architecture mismatch. Found expected %1").arg(QSysInfo::buildAbi()));
+ }
+
+ {
+ testCompiler.clearCache();
+ QVERIFY2(testCompiler.compile(contents), qPrintable(testCompiler.lastErrorString));
+
+ testCompiler.tweakHeader([](QV4::CompiledData::Unit *header) {
+ header->codeGeneratorIndex = 0;
+ });
+
+ QVERIFY(!testCompiler.verify());
+ QCOMPARE(testCompiler.lastErrorString, QString::fromUtf8("Code generator mismatch. Found code generated by but expected %1").arg(QV8Engine::getV4(&engine)->iselFactory->codeGeneratorName));
+ }
+}
+
+class TypeVersion1 : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(int value READ value WRITE setValue NOTIFY valueChanged);
+public:
+
+
+ int m_value = 0;
+ int value() const { return m_value; }
+ void setValue(int v) { m_value = v; emit valueChanged(); }
+
+signals:
+ void valueChanged();
+};
+
+// Same as TypeVersion1 except the property type changed!
+class TypeVersion2 : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QString value READ value WRITE setValue NOTIFY valueChanged);
+public:
+
+
+ QString m_value;
+ QString value() const { return m_value; }
+ void setValue(QString v) { m_value = v; emit valueChanged(); }
+
+signals:
+ void valueChanged();
+};
+
+void tst_qmldiskcache::recompileAfterChange()
+{
+ QQmlEngine engine;
+
+ TestCompiler testCompiler(&engine);
+ QVERIFY(testCompiler.tempDir.isValid());
+
+ const QByteArray contents = QByteArrayLiteral("import TypeTest 1.0\n"
+ "TypeThatWillChange {\n"
+ "}");
+
+ qmlRegisterType<TypeVersion1>("TypeTest", 1, 0, "TypeThatWillChange");
+
+ {
+ testCompiler.clearCache();
+ QVERIFY2(testCompiler.compile(contents), qPrintable(testCompiler.lastErrorString));
+ QVERIFY2(testCompiler.verify(), qPrintable(testCompiler.lastErrorString));
+ }
+
+ QDateTime initialCacheTimeStamp = QFileInfo(testCompiler.cacheFilePath).lastModified();
+
+ {
+ CleanlyLoadingComponent component(&engine, testCompiler.testFilePath);
+ QScopedPointer<TypeVersion1> obj(qobject_cast<TypeVersion1*>(component.create()));
+ QVERIFY(!obj.isNull());
+ QCOMPARE(QFileInfo(testCompiler.cacheFilePath).lastModified(), initialCacheTimeStamp);
+ }
+
+ engine.clearComponentCache();
+
+ {
+ CleanlyLoadingComponent component(&engine, testCompiler.testFilePath);
+ QScopedPointer<TypeVersion1> obj(qobject_cast<TypeVersion1*>(component.create()));
+ QVERIFY(!obj.isNull());
+ QCOMPARE(QFileInfo(testCompiler.cacheFilePath).lastModified(), initialCacheTimeStamp);
+ }
+
+ engine.clearComponentCache();
+ qmlClearTypeRegistrations();
+ qmlRegisterType<TypeVersion2>("TypeTest", 1, 0, "TypeThatWillChange");
+
+ waitForFileSystem();
+
+ {
+ CleanlyLoadingComponent component(&engine, testCompiler.testFilePath);
+ QScopedPointer<TypeVersion2> obj(qobject_cast<TypeVersion2*>(component.create()));
+ QVERIFY(!obj.isNull());
+ QVERIFY(QFileInfo(testCompiler.cacheFilePath).lastModified() > initialCacheTimeStamp);
+ }
+}
+
+void tst_qmldiskcache::fileSelectors()
+{
+ QQmlEngine engine;
+
+ QTemporaryDir tempDir;
+ QVERIFY(tempDir.isValid());
+
+ const QString testFilePath = tempDir.path() + "/test.qml";
+ {
+ QFile f(testFilePath);
+ QVERIFY2(f.open(QIODevice::WriteOnly), qPrintable(f.errorString()));
+ f.write(QByteArrayLiteral("import QtQml 2.0\nQtObject { property int value: 42 }"));
+ }
+
+ const QString selector = QStringLiteral("testSelector");
+ const QString selectorPath = tempDir.path() + "/+" + selector;
+ const QString selectedTestFilePath = selectorPath + "/test.qml";
+ {
+ QVERIFY(QDir::root().mkpath(selectorPath));
+ QFile f(selectorPath + "/test.qml");
+ QVERIFY2(f.open(QIODevice::WriteOnly), qPrintable(f.errorString()));
+ f.write(QByteArrayLiteral("import QtQml 2.0\nQtObject { property int value: 100 }"));
+ }
+
+ {
+ QQmlComponent component(&engine, testFilePath);
+ QScopedPointer<QObject> obj(component.create());
+ QVERIFY(!obj.isNull());
+ QCOMPARE(obj->property("value").toInt(), 42);
+
+ QFile cacheFile(testFilePath + "c");
+ QVERIFY2(cacheFile.exists(), qPrintable(cacheFile.fileName()));
+ }
+
+ QQmlFileSelector qmlSelector(&engine);
+ qmlSelector.setExtraSelectors(QStringList() << selector);
+
+ engine.clearComponentCache();
+
+ {
+ QQmlComponent component(&engine, testFilePath);
+ QScopedPointer<QObject> obj(component.create());
+ QVERIFY(!obj.isNull());
+ QCOMPARE(obj->property("value").toInt(), 100);
+
+ QFile cacheFile(selectedTestFilePath + "c");
+ QVERIFY2(cacheFile.exists(), qPrintable(cacheFile.fileName()));
+ }
+}
+
+void tst_qmldiskcache::localAliases()
+{
+ QQmlEngine engine;
+
+ TestCompiler testCompiler(&engine);
+ QVERIFY(testCompiler.tempDir.isValid());
+
+ const QByteArray contents = QByteArrayLiteral("import QtQml 2.0\n"
+ "QtObject {\n"
+ " id: root\n"
+ " property int prop: 100\n"
+ " property alias dummy1: root.prop\n"
+ " property alias dummy2: root.prop\n"
+ " property alias dummy3: root.prop\n"
+ " property alias dummy4: root.prop\n"
+ " property alias dummy5: root.prop\n"
+ " property alias foo: root.prop\n"
+ " property alias bar: root.foo\n"
+ "}");
+
+ {
+ testCompiler.clearCache();
+ QVERIFY2(testCompiler.compile(contents), qPrintable(testCompiler.lastErrorString));
+ QVERIFY2(testCompiler.verify(), qPrintable(testCompiler.lastErrorString));
+ }
+
+ {
+ CleanlyLoadingComponent component(&engine, testCompiler.testFilePath);
+ QScopedPointer<QObject> obj(component.create());
+ QVERIFY(!obj.isNull());
+ QCOMPARE(obj->property("bar").toInt(), 100);
+ }
+
+ engine.clearComponentCache();
+
+ {
+ CleanlyLoadingComponent component(&engine, testCompiler.testFilePath);
+ QScopedPointer<QObject> obj(component.create());
+ QVERIFY(!obj.isNull());
+ QCOMPARE(obj->property("bar").toInt(), 100);
+ }
+}
+
+void tst_qmldiskcache::cacheResources()
+{
+ const QString cacheDirectory = QStandardPaths::writableLocation(QStandardPaths::CacheLocation);
+ QVERIFY(QDir::root().mkpath(cacheDirectory));
+
+ const QString qmlCacheDirectory = cacheDirectory + QLatin1String("/qmlcache/");
+ QVERIFY(QDir(qmlCacheDirectory).removeRecursively());
+ QVERIFY(QDir::root().mkpath(qmlCacheDirectory));
+ QVERIFY(QDir(qmlCacheDirectory).entryList(QDir::NoDotAndDotDot).isEmpty());
+
+
+ QQmlEngine engine;
+
+ {
+ CleanlyLoadingComponent component(&engine, QUrl("qrc:/test.qml"));
+ qDebug() << component.errorString();
+ QScopedPointer<QObject> obj(component.create());
+ QVERIFY(!obj.isNull());
+ QCOMPARE(obj->property("value").toInt(), 20);
+ }
+
+ const QStringList entries = QDir(qmlCacheDirectory).entryList(QDir::NoDotAndDotDot | QDir::Files);
+ QCOMPARE(entries.count(), 1);
+
+ {
+ QFile cacheFile(qmlCacheDirectory + QLatin1Char('/') + entries.constFirst());
+ QVERIFY2(cacheFile.open(QIODevice::ReadOnly), qPrintable(cacheFile.errorString()));
+ QV4::CompiledData::Unit unit;
+ QVERIFY(cacheFile.read(reinterpret_cast<char *>(&unit), sizeof(unit)) == sizeof(unit));
+ QCOMPARE(qint64(unit.sourceTimeStamp), QFileInfo(QCoreApplication::applicationFilePath()).lastModified().toMSecsSinceEpoch());
+ }
+}
+
+void tst_qmldiskcache::stableOrderOfDependentCompositeTypes()
+{
+ QQmlEngine engine;
+
+ QTemporaryDir tempDir;
+ QVERIFY(tempDir.isValid());
+
+ {
+ const QString depFilePath = tempDir.path() + "/FirstDependentType.qml";
+ QFile f(depFilePath);
+ QVERIFY2(f.open(QIODevice::WriteOnly), qPrintable(f.errorString()));
+ f.write(QByteArrayLiteral("import QtQml 2.0\nQtObject { property int value: 42 }"));
+ }
+
+ {
+ const QString depFilePath = tempDir.path() + "/SecondDependentType.qml";
+ QFile f(depFilePath);
+ QVERIFY2(f.open(QIODevice::WriteOnly), qPrintable(f.errorString()));
+ f.write(QByteArrayLiteral("import QtQml 2.0\nQtObject { property int value: 100 }"));
+ }
+
+ const QString testFilePath = tempDir.path() + "/main.qml";
+ {
+ QFile f(testFilePath);
+ QVERIFY2(f.open(QIODevice::WriteOnly), qPrintable(f.errorString()));
+ f.write(QByteArrayLiteral("import QtQml 2.0\nQtObject {\n"
+ " property QtObject dep1: FirstDependentType{}\n"
+ " property QtObject dep2: SecondDependentType{}\n"
+ " property int value: dep1.value + dep2.value\n"
+ "}"));
+ }
+
+ QByteArray firstDependentTypeClassName;
+ QByteArray secondDependentTypeClassName;
+
+ {
+ CleanlyLoadingComponent component(&engine, QUrl::fromLocalFile(testFilePath));
+ QScopedPointer<QObject> obj(component.create());
+ QVERIFY(!obj.isNull());
+ QCOMPARE(obj->property("value").toInt(), 142);
+
+ firstDependentTypeClassName = qvariant_cast<QObject *>(obj->property("dep1"))->metaObject()->className();
+ secondDependentTypeClassName = qvariant_cast<QObject *>(obj->property("dep2"))->metaObject()->className();
+ }
+
+ QVERIFY(firstDependentTypeClassName != secondDependentTypeClassName);
+ QVERIFY2(firstDependentTypeClassName.contains("QMLTYPE"), firstDependentTypeClassName.constData());
+ QVERIFY2(secondDependentTypeClassName.contains("QMLTYPE"), secondDependentTypeClassName.constData());
+
+ const QString testFileCachePath = testFilePath + QLatin1Char('c');
+ QVERIFY(QFile::exists(testFileCachePath));
+ QDateTime initialCacheTimeStamp = QFileInfo(testFileCachePath).lastModified();
+
+ engine.clearComponentCache();
+ waitForFileSystem();
+
+ // Creating the test component a second time should load it from the cache (same time stamp),
+ // despite the class names of the dependent composite types differing.
+ {
+ CleanlyLoadingComponent component(&engine, QUrl::fromLocalFile(testFilePath));
+ QScopedPointer<QObject> obj(component.create());
+ QVERIFY(!obj.isNull());
+ QCOMPARE(obj->property("value").toInt(), 142);
+
+ QVERIFY(qvariant_cast<QObject *>(obj->property("dep1"))->metaObject()->className() != firstDependentTypeClassName);
+ QVERIFY(qvariant_cast<QObject *>(obj->property("dep2"))->metaObject()->className() != secondDependentTypeClassName);
+ }
+
+ {
+ QVERIFY(QFile::exists(testFileCachePath));
+ QDateTime newCacheTimeStamp = QFileInfo(testFileCachePath).lastModified();
+ QCOMPARE(newCacheTimeStamp, initialCacheTimeStamp);
+ }
+
+ // Now change the first dependent QML type and see if we correctly re-generate the
+ // caches.
+ engine.clearComponentCache();
+ waitForFileSystem();
+ {
+ const QString depFilePath = tempDir.path() + "/FirstDependentType.qml";
+ QFile f(depFilePath);
+ QVERIFY2(f.open(QIODevice::WriteOnly), qPrintable(f.errorString()));
+ f.write(QByteArrayLiteral("import QtQml 2.0\nQtObject { property int value: 40 }"));
+ }
+
+ {
+ CleanlyLoadingComponent component(&engine, QUrl::fromLocalFile(testFilePath));
+ QScopedPointer<QObject> obj(component.create());
+ QVERIFY(!obj.isNull());
+ QCOMPARE(obj->property("value").toInt(), 140);
+ }
+
+ {
+ QVERIFY(QFile::exists(testFileCachePath));
+ QDateTime newCacheTimeStamp = QFileInfo(testFileCachePath).lastModified();
+ QVERIFY2(newCacheTimeStamp > initialCacheTimeStamp, qPrintable(newCacheTimeStamp.toString()));
+ }
+}
+
+QTEST_MAIN(tst_qmldiskcache)
+
+#include "tst_qmldiskcache.moc"
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/qqmlbinding/data/delayed.qml b/tests/auto/qml/qqmlbinding/data/delayed.qml
new file mode 100644
index 0000000000..6f8281cc33
--- /dev/null
+++ b/tests/auto/qml/qqmlbinding/data/delayed.qml
@@ -0,0 +1,26 @@
+import QtQuick 2.8
+
+Item {
+ width: 400
+ height: 400
+
+ property int changeCount: 0
+
+ property string text1
+ property string text2
+
+ function updateText() {
+ text1 = "Hello"
+ text2 = "World"
+ }
+
+ Text {
+ anchors.centerIn: parent
+ Binding on text {
+ value: text1 + " " + text2
+ delayed: true
+ }
+ onTextChanged: ++changeCount
+ }
+}
+
diff --git a/tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp b/tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp
index 3e8dfbdb12..6f1d82eca5 100644
--- a/tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp
+++ b/tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp
@@ -49,6 +49,7 @@ private slots:
void warningOnReadOnlyProperty();
void disabledOnUnknownProperty();
void disabledOnReadonlyProperty();
+ void delayed();
private:
QQmlEngine engine;
@@ -281,6 +282,27 @@ void tst_qqmlbinding::disabledOnReadonlyProperty()
QCOMPARE(messageHandler.messages().count(), 0);
}
+void tst_qqmlbinding::delayed()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, testFileUrl("delayed.qml"));
+ QQuickItem *item = qobject_cast<QQuickItem*>(c.create());
+
+ QVERIFY(item != 0);
+ // update on creation
+ QCOMPARE(item->property("changeCount").toInt(), 1);
+
+ QMetaObject::invokeMethod(item, "updateText");
+ // doesn't update immediately
+ QCOMPARE(item->property("changeCount").toInt(), 1);
+
+ QCoreApplication::processEvents();
+ // only updates once (non-delayed would update twice)
+ QCOMPARE(item->property("changeCount").toInt(), 2);
+
+ delete item;
+}
+
QTEST_MAIN(tst_qqmlbinding)
#include "tst_qqmlbinding.moc"
diff --git a/tests/auto/qml/qqmlconsole/data/categorized_logging.qml b/tests/auto/qml/qqmlconsole/data/categorized_logging.qml
new file mode 100644
index 0000000000..d19b6ecc41
--- /dev/null
+++ b/tests/auto/qml/qqmlconsole/data/categorized_logging.qml
@@ -0,0 +1,65 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Pelagicore AG
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml 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$
+**
+****************************************************************************/
+
+import QtQuick 2.8
+
+Item {
+ id:root
+
+ LoggingCategory {
+ id: testCategory
+ name: "qt.test"
+ }
+
+ LoggingCategory {
+ id: emptyCategory
+ }
+
+ Component.onCompleted: {
+ console.debug(testCategory, "console.debug");
+ console.log(testCategory, "console.log");
+ console.info(testCategory, "console.info");
+ console.warn(testCategory, "console.warn");
+ console.error(testCategory, "console.error");
+
+ testCategory.name = "qt.test2";
+
+ console.error(emptyCategory, "console.error");
+ }
+}
diff --git a/tests/auto/qml/qqmlconsole/tst_qqmlconsole.cpp b/tests/auto/qml/qqmlconsole/tst_qqmlconsole.cpp
index f12656c5fe..f832143935 100644
--- a/tests/auto/qml/qqmlconsole/tst_qqmlconsole.cpp
+++ b/tests/auto/qml/qqmlconsole/tst_qqmlconsole.cpp
@@ -40,6 +40,7 @@ public:
private slots:
void logging();
+ void categorized_logging();
void tracing();
void profiling();
void testAssert();
@@ -87,6 +88,41 @@ void tst_qqmlconsole::logging()
delete object;
}
+void tst_qqmlconsole::categorized_logging()
+{
+ QUrl testUrl = testFileUrl("categorized_logging.qml");
+ QQmlTestMessageHandler messageHandler;
+ messageHandler.setIncludeCategoriesEnabled(true);
+
+ QLoggingCategory testCategory("qt.test");
+ testCategory.setEnabled(QtDebugMsg, true);
+ QVERIFY(testCategory.isDebugEnabled());
+ QVERIFY(testCategory.isWarningEnabled());
+ QVERIFY(testCategory.isCriticalEnabled());
+
+ QQmlComponent component(&engine, testUrl);
+ QObject *object = component.create();
+ QVERIFY2(object != 0, component.errorString().toUtf8());
+
+ QVERIFY(messageHandler.messages().contains("qt.test: console.info"));
+ QVERIFY(messageHandler.messages().contains("qt.test: console.warn"));
+ QVERIFY(messageHandler.messages().contains("qt.test: console.error"));
+
+ QString emptyCategory = "default: " + QString::fromLatin1("%1:%2:%3: ").arg(testUrl.toString()).arg(50).arg(5) +
+ "QML LoggingCategory: Declaring the name of the LoggingCategory is mandatory and cannot be changed later !";
+ QVERIFY(messageHandler.messages().contains(emptyCategory));
+
+ QString changedCategory = "default: " + QString::fromLatin1("%1:%2:%3: ").arg(testUrl.toString()).arg(45).arg(5) +
+ "QML LoggingCategory: The name of a LoggingCategory cannot be changed after the Item is created";
+ QVERIFY(messageHandler.messages().contains(changedCategory));
+
+ QString useEmptyCategory = "default: " + QString::fromLatin1("%1:%2: ").arg(testUrl.toString()).arg(63) +
+ "Error: A QmlLoggingCatgory was provided without a valid name";
+ QVERIFY(messageHandler.messages().contains(useEmptyCategory));
+
+ delete object;
+}
+
void tst_qqmlconsole::tracing()
{
QUrl testUrl = testFileUrl("tracing.qml");
diff --git a/tests/auto/qml/qqmlecmascript/data/NullObjectInitializerBase.qml b/tests/auto/qml/qqmlecmascript/data/NullObjectInitializerBase.qml
new file mode 100644
index 0000000000..4006a2a782
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/NullObjectInitializerBase.qml
@@ -0,0 +1,5 @@
+import QtQml 2.0
+QtObject {
+ property Component factory: Component { QtObject {} }
+ property QtObject testProperty: factory.createObject()
+}
diff --git a/tests/auto/qml/qqmlecmascript/data/dependenciesWithFunctions.qml b/tests/auto/qml/qqmlecmascript/data/dependenciesWithFunctions.qml
new file mode 100644
index 0000000000..57d3ae2f14
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/dependenciesWithFunctions.qml
@@ -0,0 +1,12 @@
+import QtQml 2.0
+QtObject {
+ property int value: 100
+ property bool success: {
+ unrelatedFunction();
+ return value == 42;
+ }
+ property int unrelatedValue: 100
+ function unrelatedFunction() {
+ return unrelatedValue;
+ }
+}
diff --git a/tests/auto/qml/qqmlecmascript/data/nullObjectInitializer.2.qml b/tests/auto/qml/qqmlecmascript/data/nullObjectInitializer.2.qml
new file mode 100644
index 0000000000..d59f8f99f9
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/nullObjectInitializer.2.qml
@@ -0,0 +1,8 @@
+import QtQml 2.0
+NullObjectInitializerBase {
+ testProperty: null
+ property bool success: false
+ Component.onCompleted: {
+ success = testProperty === null;
+ }
+}
diff --git a/tests/auto/qml/qqmlecmascript/data/nullObjectInitializer.qml b/tests/auto/qml/qqmlecmascript/data/nullObjectInitializer.qml
new file mode 100644
index 0000000000..32bc62c9f2
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/nullObjectInitializer.qml
@@ -0,0 +1,4 @@
+import QtQml 2.0
+QtObject {
+ property QtObject testProperty: null
+}
diff --git a/tests/auto/qml/qqmlecmascript/testtypes.h b/tests/auto/qml/qqmlecmascript/testtypes.h
index d4d051443f..47fb2a56e7 100644
--- a/tests/auto/qml/qqmlecmascript/testtypes.h
+++ b/tests/auto/qml/qqmlecmascript/testtypes.h
@@ -790,7 +790,8 @@ public:
Q_INVOKABLE void method_QObject(QObject *a) { invoke(13); m_actuals << qVariantFromValue(a); }
Q_INVOKABLE void method_QScriptValue(QJSValue a) { invoke(14); m_actuals << qVariantFromValue(a); }
Q_INVOKABLE void method_intQScriptValue(int a, QJSValue b) { invoke(15); m_actuals << a << qVariantFromValue(b); }
- Q_INVOKABLE QJSValue method_intQJSValue(int a, QJSValue b) { invoke(29); m_actuals << a << qVariantFromValue(b); return b.call(); }
+ Q_INVOKABLE void method_QByteArray(QByteArray value) { invoke(29); m_actuals << value; }
+ Q_INVOKABLE QJSValue method_intQJSValue(int a, QJSValue b) { invoke(30); m_actuals << a << qVariantFromValue(b); return b.call(); }
Q_INVOKABLE QJSValue method_intQJSValue(int a, int b) { m_actuals << a << b; return QJSValue();} // Should never be called.
Q_INVOKABLE void method_overload(int a) { invoke(16); m_actuals << a; }
diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
index 26a15d730c..be04ec2bf3 100644
--- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
+++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
@@ -84,6 +84,7 @@ private slots:
void arrayExpressions();
void contextPropertiesTriggerReeval();
void objectPropertiesTriggerReeval();
+ void dependenciesWithFunctions();
void deferredProperties();
void deferredPropertiesErrors();
void deferredPropertiesInComponents();
@@ -210,6 +211,7 @@ private slots:
void dynamicCreationOwnership();
void regExpBug();
void nullObjectBinding();
+ void nullObjectInitializer();
void deletedEngine();
void libraryScriptAssert();
void variantsAssignedUndefined();
@@ -881,6 +883,18 @@ void tst_qqmlecmascript::objectPropertiesTriggerReeval()
}
}
+void tst_qqmlecmascript::dependenciesWithFunctions()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("dependenciesWithFunctions.qml"));
+
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY2(object, qPrintable(component.errorString()));
+ QVERIFY(!object->property("success").toBool());
+ object->setProperty("value", 42);
+ QVERIFY(object->property("success").toBool());
+}
+
void tst_qqmlecmascript::deferredProperties()
{
QQmlComponent component(&engine, testFileUrl("deferredProperties.qml"));
@@ -2318,7 +2332,7 @@ static inline bool evaluate_error(QV8Engine *engine, const QV4::Value &o, const
QV4::ScopedCallData d(scope, 1);
d->args[0] = o;
d->thisObject = engine->global();
- function->call(d);
+ function->call(scope, d);
if (scope.engine->hasException) {
scope.engine->catchException();
return true;
@@ -2344,16 +2358,15 @@ static inline bool evaluate_value(QV8Engine *engine, const QV4::Value &o,
if (!function)
return false;
- QV4::ScopedValue value(scope);
QV4::ScopedCallData d(scope, 1);
d->args[0] = o;
d->thisObject = engine->global();
- value = function->call(d);
+ function->call(scope, d);
if (scope.engine->hasException) {
scope.engine->catchException();
return false;
}
- return QV4::Runtime::strictEqual(value, result);
+ return QV4::Runtime::method_strictEqual(scope.result, result);
}
static inline QV4::ReturnedValue evaluate(QV8Engine *engine, const QV4::Value &o,
@@ -2377,12 +2390,12 @@ static inline QV4::ReturnedValue evaluate(QV8Engine *engine, const QV4::Value &o
QV4::ScopedCallData d(scope, 1);
d->args[0] = o;
d->thisObject = engine->global();
- QV4::ScopedValue result(scope, function->call(d));
+ function->call(scope, d);
if (scope.engine->hasException) {
scope.engine->catchException();
return QV4::Encode::undefined();
}
- return result->asReturnedValue();
+ return scope.result.asReturnedValue();
}
#define EVALUATE_ERROR(source) evaluate_error(engine, object, source)
@@ -2946,9 +2959,16 @@ void tst_qqmlecmascript::callQtInvokables()
QCOMPARE(o->actuals().count(), 0);
o->reset();
- QV4::ScopedValue ret(scope, EVALUATE("object.method_intQJSValue(123, function() { return \"Hello world!\";})"));
+ QVERIFY(EVALUATE_VALUE("object.method_QByteArray(\"Hello\")", QV4::Primitive::undefinedValue()));
QCOMPARE(o->error(), false);
QCOMPARE(o->invoked(), 29);
+ QCOMPARE(o->actuals().count(), 1);
+ QCOMPARE(qvariant_cast<QByteArray>(o->actuals().at(0)), QByteArray("Hello"));
+
+ o->reset();
+ QV4::ScopedValue ret(scope, EVALUATE("object.method_intQJSValue(123, function() { return \"Hello world!\";})"));
+ QCOMPARE(o->error(), false);
+ QCOMPARE(o->invoked(), 30);
QVERIFY(ret->isString());
QCOMPARE(ret->toQStringNoThrow(), QString("Hello world!"));
QCOMPARE(o->actuals().count(), 2);
@@ -5707,6 +5727,49 @@ void tst_qqmlecmascript::nullObjectBinding()
delete object;
}
+void tst_qqmlecmascript::nullObjectInitializer()
+{
+ {
+ QQmlComponent component(&engine, testFileUrl("nullObjectInitializer.qml"));
+ QScopedPointer<QObject> obj(component.create());
+ QVERIFY(!obj.isNull());
+
+ QQmlData *ddata = QQmlData::get(obj.data(), /*create*/false);
+ QVERIFY(ddata);
+
+ {
+ const int propertyIndex = obj->metaObject()->indexOfProperty("testProperty");
+ QVERIFY(propertyIndex > 0);
+ QVERIFY(!ddata->hasBindingBit(propertyIndex));
+ }
+
+ QVariant value = obj->property("testProperty");
+ QVERIFY(value.userType() == qMetaTypeId<QObject*>());
+ QVERIFY(!value.value<QObject*>());
+ }
+
+ {
+ QQmlComponent component(&engine, testFileUrl("nullObjectInitializer.2.qml"));
+ QScopedPointer<QObject> obj(component.create());
+ QVERIFY(!obj.isNull());
+
+ QQmlData *ddata = QQmlData::get(obj.data(), /*create*/false);
+ QVERIFY(ddata);
+
+ {
+ const int propertyIndex = obj->metaObject()->indexOfProperty("testProperty");
+ QVERIFY(propertyIndex > 0);
+ QVERIFY(ddata->hasBindingBit(propertyIndex));
+ }
+
+ QVERIFY(obj->property("success").toBool());
+
+ QVariant value = obj->property("testProperty");
+ QVERIFY(value.userType() == qMetaTypeId<QObject*>());
+ QVERIFY(!value.value<QObject*>());
+ }
+}
+
// Test that bindings don't evaluate once the engine has been destroyed
void tst_qqmlecmascript::deletedEngine()
{
@@ -5768,7 +5831,7 @@ void tst_qqmlecmascript::variants()
QVERIFY(object != 0);
QCOMPARE(object->property("undefinedVariant").type(), QVariant::Invalid);
- QCOMPARE(int(object->property("nullVariant").type()), int(QMetaType::VoidStar));
+ QCOMPARE(int(object->property("nullVariant").type()), int(QMetaType::Nullptr));
QCOMPARE(object->property("intVariant").type(), QVariant::Int);
QCOMPARE(object->property("doubleVariant").type(), QVariant::Double);
@@ -7196,14 +7259,16 @@ namespace QV4 {
namespace Heap {
struct WeakReferenceSentinel : public Object {
- WeakReferenceSentinel(WeakValue *weakRef, bool *resultPtr)
- : weakRef(weakRef)
- , resultPtr(resultPtr) {
-
+ void init(WeakValue *weakRef, bool *resultPtr)
+ {
+ Object::init();
+ this->weakRef = weakRef;
+ this->resultPtr = resultPtr;
}
- ~WeakReferenceSentinel() {
+ void destroy() {
*resultPtr = weakRef->isNullOrUndefined();
+ Object::destroy();
}
WeakValue *weakRef;
diff --git a/tests/auto/qml/qqmlengine/data/qtqmlModule.4.qml b/tests/auto/qml/qqmlengine/data/qtqmlModule.4.qml
index 9b9b7922da..94ee46ddf0 100644
--- a/tests/auto/qml/qqmlengine/data/qtqmlModule.4.qml
+++ b/tests/auto/qml/qqmlengine/data/qtqmlModule.4.qml
@@ -1,4 +1,4 @@
-import QtQml 2.5
+import QtQml 2.50
QtObject {
}
diff --git a/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp b/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp
index 3208745c5c..9c155eda5b 100644
--- a/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp
+++ b/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp
@@ -275,6 +275,12 @@ void tst_qqmlengine::clearComponentCache()
// Modify qml file
{
+ // On macOS with HFS+ the precision of file times is measured in seconds, so to ensure that
+ // the newly written file has a modification date newer than an existing cache file, we must
+ // wait.
+ // Similar effects of lacking precision have been observed on some Linux systems.
+ QThread::sleep(1);
+
QFile file("temp.qml");
QVERIFY(file.open(QIODevice::WriteOnly));
file.write("import QtQuick 2.0\nQtObject {\nproperty int test: 11\n}\n");
@@ -317,6 +323,11 @@ public:
QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
QCoreApplication::processEvents();
+ // There might be JS function objects around that hold a last ref to the compilation unit that's
+ // keeping the type compilation data (CompilationUnit) around. Let's collect them as well so that
+ // trim works well.
+ engine->collectGarbage();
+
engine->trimComponentCache();
}
@@ -615,9 +626,9 @@ void tst_qqmlengine::qtqmlModule_data()
<< QString(testFileUrl("qtqmlModule.3.qml").toString() + QLatin1String(":1 module \"QtQml\" version 1.0 is not installed\n"))
<< QStringList();
- QTest::newRow("import QtQml of incorrect version (2.5)")
+ QTest::newRow("import QtQml of incorrect version (2.50)")
<< testFileUrl("qtqmlModule.4.qml")
- << QString(testFileUrl("qtqmlModule.4.qml").toString() + QLatin1String(":1 module \"QtQml\" version 2.5 is not installed\n"))
+ << QString(testFileUrl("qtqmlModule.4.qml").toString() + QLatin1String(":1 module \"QtQml\" version 2.50 is not installed\n"))
<< QStringList();
QTest::newRow("QtQml 2.0 module provides Component, QtObject, Connections, Binding and Timer")
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/qml/qqmlextensionplugin/tst_qqmlextensionplugin.cpp b/tests/auto/qml/qqmlextensionplugin/tst_qqmlextensionplugin.cpp
index bd44bd7c8b..124a107a37 100644
--- a/tests/auto/qml/qqmlextensionplugin/tst_qqmlextensionplugin.cpp
+++ b/tests/auto/qml/qqmlextensionplugin/tst_qqmlextensionplugin.cpp
@@ -109,7 +109,7 @@ void tst_qqmlextensionplugin::iidCheck()
QFETCH(QString, filePath);
QPluginLoader loader(filePath);
- QVERIFY(loader.load());
+ QVERIFY2(loader.load(), qPrintable(loader.errorString()));
QVERIFY(loader.instance() != Q_NULLPTR);
if (qobject_cast<QQmlExtensionPlugin *>(loader.instance())) {
diff --git a/tests/auto/qml/qqmllanguage/data/alias.12.qml b/tests/auto/qml/qqmllanguage/data/alias.12.qml
new file mode 100644
index 0000000000..cc17318092
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/alias.12.qml
@@ -0,0 +1,15 @@
+import QtQml 2.0
+
+QtObject {
+ id: root
+ property alias topLevelAlias: subObject.targetProperty
+
+ property QtObject referencingSubObject: QtObject {
+ property alias success: root.topLevelAlias
+ }
+
+ property QtObject foo: QtObject {
+ id: subObject
+ property bool targetProperty: true
+ }
+}
diff --git a/tests/auto/qml/qqmllanguage/data/alias.13.qml b/tests/auto/qml/qqmllanguage/data/alias.13.qml
new file mode 100644
index 0000000000..cff8a72d9a
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/alias.13.qml
@@ -0,0 +1,16 @@
+import QtQml 2.0
+
+QtObject {
+ id: root
+ property bool targetProperty: true
+
+ property QtObject foo: QtObject {
+ id: otherSubObject
+ property alias theAlias: root.targetProperty
+ }
+
+ property QtObject referencingSubObject: QtObject {
+ property alias success: otherSubObject.theAlias
+ }
+
+}
diff --git a/tests/auto/qml/qqmllanguage/data/alias.14.errors.txt b/tests/auto/qml/qqmllanguage/data/alias.14.errors.txt
new file mode 100644
index 0000000000..90a3ea4317
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/alias.14.errors.txt
@@ -0,0 +1 @@
+10:34:References to other aliases within the same object are not supported at the moment
diff --git a/tests/auto/qml/qqmllanguage/data/alias.14.qml b/tests/auto/qml/qqmllanguage/data/alias.14.qml
new file mode 100644
index 0000000000..ff3c36d990
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/alias.14.qml
@@ -0,0 +1,17 @@
+import QtQml 2.0
+
+QtObject {
+ id: root
+ property bool targetProperty: true
+
+ property QtObject foo: QtObject {
+ id: otherSubObject
+ property alias theAliasOrigin: root.targetProperty
+ property alias theAlias: otherSubObject.theAliasOrigin
+ }
+
+ property QtObject referencingSubObject: QtObject {
+ property alias success: otherSubObject.theAlias
+ }
+
+}
diff --git a/tests/auto/qml/qqmllanguage/data/arraybuffer_method_arg.qml b/tests/auto/qml/qqmllanguage/data/arraybuffer_method_arg.qml
new file mode 100644
index 0000000000..b64a2e23c0
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/arraybuffer_method_arg.qml
@@ -0,0 +1,11 @@
+import QtQuick 2.7
+import Test 1.0
+
+MyArrayBufferTestClass {
+ property bool ok: false
+ Component.onCompleted: {
+ var data = new Uint8Array([1, 2, 3]);
+ var sum = byteArrayMethod_Sum(data.buffer);
+ ok = sum == 6;
+ }
+}
diff --git a/tests/auto/qml/qqmllanguage/data/arraybuffer_method_overload.qml b/tests/auto/qml/qqmllanguage/data/arraybuffer_method_overload.qml
new file mode 100644
index 0000000000..9e1c91810a
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/arraybuffer_method_overload.qml
@@ -0,0 +1,7 @@
+import QtQuick 2.7
+import Test 1.0
+
+MyArrayBufferTestClass {
+ property bool ok: false
+ Component.onCompleted: ok = byteArrayMethod_Overloaded(new ArrayBuffer());
+}
diff --git a/tests/auto/qml/qqmllanguage/data/arraybuffer_method_return.qml b/tests/auto/qml/qqmllanguage/data/arraybuffer_method_return.qml
new file mode 100644
index 0000000000..5a4f9fec0b
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/arraybuffer_method_return.qml
@@ -0,0 +1,15 @@
+import QtQuick 2.7
+import Test 1.0
+
+MyArrayBufferTestClass {
+ property bool ok: false
+ Component.onCompleted: {
+ var buf = byteArrayMethod_CountUp(1, 3);
+ var view = new DataView(buf);
+ ok = buf instanceof ArrayBuffer
+ && buf.byteLength == 3
+ && view.getUint8(0) == 1
+ && view.getUint8(1) == 2
+ && view.getUint8(2) == 3;
+ }
+}
diff --git a/tests/auto/qml/qqmllanguage/data/arraybuffer_property_get.qml b/tests/auto/qml/qqmllanguage/data/arraybuffer_property_get.qml
new file mode 100644
index 0000000000..78ebb1abe1
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/arraybuffer_property_get.qml
@@ -0,0 +1,5 @@
+import Test 1.0
+
+MyArrayBufferTestClass {
+ property bool ok: byteArrayProperty instanceof ArrayBuffer
+}
diff --git a/tests/auto/qml/qqmllanguage/data/arraybuffer_property_set.qml b/tests/auto/qml/qqmllanguage/data/arraybuffer_property_set.qml
new file mode 100644
index 0000000000..e8a51273ca
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/arraybuffer_property_set.qml
@@ -0,0 +1,11 @@
+import QtQuick 2.7
+import Test 1.0
+
+MyArrayBufferTestClass {
+ property bool ok: false
+ onByteArraySignal: ok = byteArrayProperty instanceof ArrayBuffer
+ Component.onCompleted: {
+ byteArrayProperty = new ArrayBuffer(42);
+ ok = byteArrayProperty instanceof ArrayBuffer && byteArrayProperty.byteLength == 42;
+ }
+}
diff --git a/tests/auto/qml/qqmllanguage/data/arraybuffer_signal_arg.qml b/tests/auto/qml/qqmllanguage/data/arraybuffer_signal_arg.qml
new file mode 100644
index 0000000000..d9f436e788
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/arraybuffer_signal_arg.qml
@@ -0,0 +1,14 @@
+import QtQuick 2.7
+import Test 1.0
+
+MyArrayBufferTestClass {
+ property bool ok: false
+ onByteArraySignal: {
+ var view = new DataView(arg);
+ ok = arg instanceof ArrayBuffer
+ && arg.byteLength == 2
+ && view.getUint8(0) == 42
+ && view.getUint8(1) == 43;
+ }
+ Component.onCompleted: emitByteArraySignal(42, 2)
+}
diff --git a/tests/auto/qml/qqmllanguage/data/badCompositeRegistration.1.errors.txt b/tests/auto/qml/qqmllanguage/data/badCompositeRegistration.1.errors.txt
index 7a75447a62..acf0d1da84 100644
--- a/tests/auto/qml/qqmllanguage/data/badCompositeRegistration.1.errors.txt
+++ b/tests/auto/qml/qqmllanguage/data/badCompositeRegistration.1.errors.txt
@@ -1,2 +1,2 @@
3:1:Type RegisteredCompositeType2 unavailable
--1:-1:File not found
+-1:-1:No such file or directory
diff --git a/tests/auto/qml/qqmllanguage/data/cppnamespace.qml b/tests/auto/qml/qqmllanguage/data/cppnamespace.qml
index e1daf3b78f..efedf2b14a 100644
--- a/tests/auto/qml/qqmllanguage/data/cppnamespace.qml
+++ b/tests/auto/qml/qqmllanguage/data/cppnamespace.qml
@@ -1,4 +1,5 @@
import Test 1.0
MyNamespacedType {
+ myEnum: MyNamespace.Key5
}
diff --git a/tests/auto/qml/qqmllanguage/data/invalidAlias.11.errors.txt b/tests/auto/qml/qqmllanguage/data/invalidAlias.11.errors.txt
new file mode 100644
index 0000000000..b79b660c46
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/invalidAlias.11.errors.txt
@@ -0,0 +1 @@
+5:5:Circular alias reference detected
diff --git a/tests/auto/qml/qqmllanguage/data/invalidAlias.11.qml b/tests/auto/qml/qqmllanguage/data/invalidAlias.11.qml
new file mode 100644
index 0000000000..9b50b48df8
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/invalidAlias.11.qml
@@ -0,0 +1,10 @@
+import QtQml 2.0
+
+QtObject {
+ id: root
+ property alias a: subObject.b
+ property QtObject foo: QtObject {
+ id: subObject
+ property alias b: root.a
+ }
+}
diff --git a/tests/auto/qml/qqmllanguage/data/rootItemIsComponent.qml b/tests/auto/qml/qqmllanguage/data/rootItemIsComponent.qml
new file mode 100644
index 0000000000..dd653352d9
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/rootItemIsComponent.qml
@@ -0,0 +1,6 @@
+import QtQml 2.0
+Component {
+ QtObject {
+ id: blah
+ }
+}
diff --git a/tests/auto/qml/qqmllanguage/data/singletonTest10.error.txt b/tests/auto/qml/qqmllanguage/data/singletonTest10.error.txt
deleted file mode 100644
index 32d2ed857e..0000000000
--- a/tests/auto/qml/qqmllanguage/data/singletonTest10.error.txt
+++ /dev/null
@@ -1 +0,0 @@
-4:1:Composite Singleton Type SingletonType is not creatable.
diff --git a/tests/auto/qml/qqmllanguage/data/singletonTest12.error.txt b/tests/auto/qml/qqmllanguage/data/singletonTest12.error.txt
index 716cf5709a..b3082d80e6 100644
--- a/tests/auto/qml/qqmllanguage/data/singletonTest12.error.txt
+++ b/tests/auto/qml/qqmllanguage/data/singletonTest12.error.txt
@@ -1,2 +1,2 @@
5:5:Type RegisteredCompositeType unavailable
-2:1:pragma Singleton used with a non composite singleton type CompositeSingletonTest/RegisteredCompositeType
+-1:-1:pragma Singleton used with a non composite singleton type CompositeSingletonTest/RegisteredCompositeType
diff --git a/tests/auto/qml/qqmllanguage/data/singletonTest4.error.txt b/tests/auto/qml/qqmllanguage/data/singletonTest4.error.txt
index 77c26df310..ebeab6987b 100644
--- a/tests/auto/qml/qqmllanguage/data/singletonTest4.error.txt
+++ b/tests/auto/qml/qqmllanguage/data/singletonTest4.error.txt
@@ -1 +1 @@
-2:1:No matching type found, pragma Singleton files cannot be used by QQmlComponent.
+-1:-1:No matching type found, pragma Singleton files cannot be used by QQmlComponent.
diff --git a/tests/auto/qml/qqmllanguage/data/uncreatableTypeAsProperty.qml b/tests/auto/qml/qqmllanguage/data/uncreatableTypeAsProperty.qml
new file mode 100644
index 0000000000..8369ab1eea
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/uncreatableTypeAsProperty.qml
@@ -0,0 +1,6 @@
+import QtQml 2.0
+import Test 1.0
+
+QtObject {
+ property MyUncreateableBaseClass someProperty;
+}
diff --git a/tests/auto/qml/qqmllanguage/testtypes.cpp b/tests/auto/qml/qqmllanguage/testtypes.cpp
index 9593bfc940..3af7645ff7 100644
--- a/tests/auto/qml/qqmllanguage/testtypes.cpp
+++ b/tests/auto/qml/qqmllanguage/testtypes.cpp
@@ -27,8 +27,6 @@
****************************************************************************/
#include "testtypes.h"
-#include <private/qqmlcompiler_p.h>
-
static QObject *myTypeObjectSingleton(QQmlEngine *engine, QJSEngine *scriptEngine)
{
Q_UNUSED(engine)
@@ -47,6 +45,7 @@ void registerTypes()
qmlRegisterType<MyDotPropertyObject>("Test",1,0,"MyDotPropertyObject");
qmlRegisterType<MyNamespace::MyNamespacedType>("Test",1,0,"MyNamespacedType");
qmlRegisterType<MyNamespace::MySecondNamespacedType>("Test",1,0,"MySecondNamespacedType");
+ qmlRegisterUncreatableMetaObject(MyNamespace::staticMetaObject, "Test", 1, 0, "MyNamespace", "Access to enums & flags only");
qmlRegisterType<MyParserStatus>("Test",1,0,"MyParserStatus");
qmlRegisterType<MyGroupedObject>();
qmlRegisterType<MyRevisionedClass>("Test",1,0,"MyRevisionedClass");
@@ -98,6 +97,8 @@ void registerTypes()
qmlRegisterType<MyCompositeBaseType>("Test", 1, 0, "MyCompositeBaseType");
qmlRegisterSingletonType<MyTypeObjectSingleton>("Test", 1, 0, "MyTypeObjectSingleton", myTypeObjectSingleton);
+
+ qmlRegisterType<MyArrayBufferTestClass>("Test", 1, 0, "MyArrayBufferTestClass");
}
QVariant myCustomVariantTypeConverter(const QString &data)
@@ -108,11 +109,11 @@ QVariant myCustomVariantTypeConverter(const QString &data)
}
-void CustomBindingParser::applyBindings(QObject *object, QQmlCompiledData *cdata, const QList<const QV4::CompiledData::Binding *> &bindings)
+void CustomBindingParser::applyBindings(QObject *object, QV4::CompiledData::CompilationUnit *compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings)
{
CustomBinding *customBinding = qobject_cast<CustomBinding*>(object);
Q_ASSERT(customBinding);
- customBinding->cdata = cdata;
+ customBinding->compilationUnit = compilationUnit;
customBinding->bindings = bindings;
}
@@ -121,17 +122,18 @@ void CustomBinding::componentComplete()
Q_ASSERT(m_target);
foreach (const QV4::CompiledData::Binding *binding, bindings) {
- QString name = cdata->compilationUnit->data->stringAt(binding->propertyNameIndex);
+ QString name = compilationUnit->data->stringAt(binding->propertyNameIndex);
int bindingId = binding->value.compiledScriptIndex;
QQmlContextData *context = QQmlContextData::get(qmlContext(this));
QV4::Scope scope(QQmlEnginePrivate::getV4Engine(qmlEngine(this)));
- QV4::ScopedValue function(scope, QV4::FunctionObject::createQmlFunction(context, m_target, cdata->compilationUnit->runtimeFunctions[bindingId]));
- QQmlBinding *qmlBinding = new QQmlBinding(function, m_target, context);
+ QV4::ScopedValue function(scope, QV4::FunctionObject::createQmlFunction(context, m_target, compilationUnit->runtimeFunctions[bindingId]));
QQmlProperty property(m_target, name, qmlContext(this));
+ QQmlBinding *qmlBinding = QQmlBinding::create(&QQmlPropertyPrivate::get(property)->core,
+ function, m_target, context);
qmlBinding->setTarget(property);
QQmlPropertyPrivate::setBinding(property, qmlBinding);
}
@@ -167,7 +169,7 @@ void EnumSupportingCustomParser::verifyBindings(const QV4::CompiledData::Unit *q
}
}
-void SimpleObjectCustomParser::applyBindings(QObject *object, QQmlCompiledData *, const QList<const QV4::CompiledData::Binding *> &bindings)
+void SimpleObjectCustomParser::applyBindings(QObject *object, QV4::CompiledData::CompilationUnit *, const QList<const QV4::CompiledData::Binding *> &bindings)
{
SimpleObjectWithCustomParser *o = qobject_cast<SimpleObjectWithCustomParser*>(object);
Q_ASSERT(o);
diff --git a/tests/auto/qml/qqmllanguage/testtypes.h b/tests/auto/qml/qqmllanguage/testtypes.h
index 337ba91532..6c62bcf7b9 100644
--- a/tests/auto/qml/qqmllanguage/testtypes.h
+++ b/tests/auto/qml/qqmllanguage/testtypes.h
@@ -43,7 +43,6 @@
#include <QtQml/qqmlpropertyvaluesource.h>
#include <QtQml/qqmlscriptstring.h>
#include <QtQml/qqmlproperty.h>
-#include <private/qqmlcompiler_p.h>
#include <private/qqmlcustomparser_p.h>
QVariant myCustomVariantTypeConverter(const QString &data);
@@ -731,9 +730,19 @@ private:
namespace MyNamespace {
+ Q_NAMESPACE
+ enum MyNSEnum {
+ Key1 = 1,
+ Key2,
+ Key5 = 5
+ };
+ Q_ENUM_NS(MyNSEnum);
+
class MyNamespacedType : public QObject
{
Q_OBJECT
+ Q_PROPERTY(MyNamespace::MyNSEnum myEnum MEMBER m_myEnum)
+ MyNamespace::MyNSEnum m_myEnum = MyNSEnum::Key1;
};
class MySecondNamespacedType : public QObject
@@ -757,14 +766,14 @@ class MyCustomParserTypeParser : public QQmlCustomParser
{
public:
virtual void verifyBindings(const QV4::CompiledData::Unit *, const QList<const QV4::CompiledData::Binding *> &) {}
- virtual void applyBindings(QObject *, QQmlCompiledData *, const QList<const QV4::CompiledData::Binding *> &) {}
+ virtual void applyBindings(QObject *, QV4::CompiledData::CompilationUnit *, const QList<const QV4::CompiledData::Binding *> &) {}
};
class EnumSupportingCustomParser : public QQmlCustomParser
{
public:
virtual void verifyBindings(const QV4::CompiledData::Unit *, const QList<const QV4::CompiledData::Binding *> &);
- virtual void applyBindings(QObject *, QQmlCompiledData *, const QList<const QV4::CompiledData::Binding *> &) {}
+ virtual void applyBindings(QObject *, QV4::CompiledData::CompilationUnit *, const QList<const QV4::CompiledData::Binding *> &) {}
};
class MyParserStatus : public QObject, public QQmlParserStatus
@@ -1114,6 +1123,58 @@ public:
static QObject *qmlAttachedProperties(QObject *parent) { return new QObject(parent); }
};
+class MyArrayBufferTestClass : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QByteArray byteArrayProperty READ byteArrayProperty WRITE setByteArrayProperty NOTIFY byteArrayPropertyChanged)
+
+signals:
+ void byteArrayPropertyChanged();
+ void byteArraySignal(QByteArray arg);
+
+public:
+ QByteArray byteArrayPropertyValue;
+ QByteArray byteArrayProperty() const {
+ return byteArrayPropertyValue;
+ }
+ void setByteArrayProperty(const QByteArray &v) {
+ byteArrayPropertyValue = v;
+ emit byteArrayPropertyChanged();
+ }
+ Q_INVOKABLE void emitByteArraySignal(char begin, char num) {
+ byteArraySignal(byteArrayMethod_CountUp(begin, num));
+ }
+ Q_INVOKABLE int byteArrayMethod_Sum(QByteArray arg) {
+ int sum = 0;
+ for (int i = 0; i < arg.size(); ++i) {
+ sum += arg[i];
+ }
+ return sum;
+ }
+ Q_INVOKABLE QByteArray byteArrayMethod_CountUp(char begin, int num) {
+ QByteArray ret;
+ for (int i = 0; i < num; ++i) {
+ ret.push_back(begin++);
+ }
+ return ret;
+ }
+ Q_INVOKABLE bool byteArrayMethod_Overloaded(QByteArray) {
+ return true;
+ }
+ Q_INVOKABLE bool byteArrayMethod_Overloaded(int) {
+ return false;
+ }
+ Q_INVOKABLE bool byteArrayMethod_Overloaded(QString) {
+ return false;
+ }
+ Q_INVOKABLE bool byteArrayMethod_Overloaded(QJSValue) {
+ return false;
+ }
+ Q_INVOKABLE bool byteArrayMethod_Overloaded(QVariant) {
+ return false;
+ }
+};
+
Q_DECLARE_METATYPE(MyEnum2Class::EnumB)
Q_DECLARE_METATYPE(MyEnum1Class::EnumA)
Q_DECLARE_METATYPE(Qt::TextFormat)
@@ -1142,7 +1203,7 @@ public:
void setTarget(QObject *newTarget) { m_target = newTarget; }
QPointer<QObject> m_target;
- QQmlRefPointer<QQmlCompiledData> cdata;
+ QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit;
QList<const QV4::CompiledData::Binding*> bindings;
QByteArray m_bindingData;
};
@@ -1150,7 +1211,7 @@ public:
class CustomBindingParser : public QQmlCustomParser
{
virtual void verifyBindings(const QV4::CompiledData::Unit *, const QList<const QV4::CompiledData::Binding *> &) {}
- virtual void applyBindings(QObject *, QQmlCompiledData *, const QList<const QV4::CompiledData::Binding *> &);
+ virtual void applyBindings(QObject *, QV4::CompiledData::CompilationUnit *, const QList<const QV4::CompiledData::Binding *> &);
};
class SimpleObjectWithCustomParser : public QObject
@@ -1196,7 +1257,7 @@ private:
class SimpleObjectCustomParser : public QQmlCustomParser
{
virtual void verifyBindings(const QV4::CompiledData::Unit *, const QList<const QV4::CompiledData::Binding *> &) {}
- virtual void applyBindings(QObject *, QQmlCompiledData *, const QList<const QV4::CompiledData::Binding *> &);
+ virtual void applyBindings(QObject *, QV4::CompiledData::CompilationUnit *, const QList<const QV4::CompiledData::Binding *> &);
};
class RootObjectInCreationTester : public QObject
diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
index 4e4b939e8b..ad06946b0b 100644
--- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
+++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
@@ -108,6 +108,7 @@ private slots:
void bindTypeToJSValue();
void customParserTypes();
void rootAsQmlComponent();
+ void rootItemIsComponent();
void inlineQmlComponents();
void idProperty();
void autoNotifyConnection();
@@ -188,6 +189,8 @@ private slots:
void subclassedUncreateableRevision_data();
void subclassedUncreateableRevision();
+ void uncreatableTypesAsProperties();
+
void propertyInit();
void remoteLoadCrash();
void signalWithDefaultArg();
@@ -246,10 +249,11 @@ private slots:
void earlyIdObjectAccess();
- void dataAlignment();
-
void deleteSingletons();
+ void arrayBuffer_data();
+ void arrayBuffer();
+
private:
QQmlEngine engine;
QStringList defaultImportPathList;
@@ -490,6 +494,7 @@ void tst_qqmllanguage::errors_data()
QTest::newRow("invalidAlias.8") << "invalidAlias.8.qml" << "invalidAlias.8.errors.txt" << false;
QTest::newRow("invalidAlias.9") << "invalidAlias.9.qml" << "invalidAlias.9.errors.txt" << false;
QTest::newRow("invalidAlias.10") << "invalidAlias.10.qml" << "invalidAlias.10.errors.txt" << false;
+ QTest::newRow("invalidAlias.11") << "invalidAlias.11.qml" << "invalidAlias.11.errors.txt" << false;
QTest::newRow("invalidAttachedProperty.1") << "invalidAttachedProperty.1.qml" << "invalidAttachedProperty.1.errors.txt" << false;
QTest::newRow("invalidAttachedProperty.2") << "invalidAttachedProperty.2.qml" << "invalidAttachedProperty.2.errors.txt" << false;
@@ -1193,6 +1198,19 @@ void tst_qqmllanguage::rootAsQmlComponent()
QCOMPARE(object->getChildren()->count(), 2);
}
+void tst_qqmllanguage::rootItemIsComponent()
+{
+ QQmlComponent component(&engine, testFileUrl("rootItemIsComponent.qml"));
+ VERIFY_ERRORS(0);
+ QScopedPointer<QObject> root(component.create());
+ QVERIFY(qobject_cast<QQmlComponent*>(root.data()));
+ QScopedPointer<QObject> other(qobject_cast<QQmlComponent*>(root.data())->create());
+ QVERIFY(!other.isNull());
+ QQmlContext *context = qmlContext(other.data());
+ QVERIFY(context);
+ QCOMPARE(context->nameForObject(other.data()), QStringLiteral("blah"));
+}
+
// Tests that components can be specified inline
void tst_qqmllanguage::inlineQmlComponents()
{
@@ -1787,6 +1805,48 @@ void tst_qqmllanguage::aliasProperties()
delete object;
}
+
+ // Nested aliases with a qml file
+ {
+ QQmlComponent component(&engine, testFileUrl("alias.12.qml"));
+ VERIFY_ERRORS(0);
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+
+ QPointer<QObject> subObject = qvariant_cast<QObject*>(object->property("referencingSubObject"));
+ QVERIFY(!subObject.isNull());
+
+ QVERIFY(subObject->property("success").toBool());
+ }
+
+ // Nested aliases with a qml file with reverse ordering
+ {
+ // This is known to fail at the moment.
+ QQmlComponent component(&engine, testFileUrl("alias.13.qml"));
+ VERIFY_ERRORS(0);
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+
+ QPointer<QObject> subObject = qvariant_cast<QObject*>(object->property("referencingSubObject"));
+ QVERIFY(!subObject.isNull());
+
+ QVERIFY(subObject->property("success").toBool());
+ }
+
+ // "Nested" aliases within an object that require iterative resolution
+ {
+ // This is known to fail at the moment.
+ QQmlComponent component(&engine, testFileUrl("alias.14.qml"));
+ VERIFY_ERRORS(0);
+
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+
+ QPointer<QObject> subObject = qvariant_cast<QObject*>(object->property("referencingSubObject"));
+ QVERIFY(!subObject.isNull());
+
+ QVERIFY(subObject->property("success").toBool());
+ }
}
// QTBUG-13374 Test that alias properties and signals can coexist
@@ -2058,8 +2118,13 @@ void tst_qqmllanguage::scriptStringWithoutSourceCode()
QQmlTypeData *td = eng->typeLoader.getType(url);
Q_ASSERT(td);
- QV4::CompiledData::Unit *qmlUnit = td->compiledData()->compilationUnit->data;
- Q_ASSERT(qmlUnit);
+ const QV4::CompiledData::Unit *readOnlyQmlUnit = td->compilationUnit()->data;
+ Q_ASSERT(readOnlyQmlUnit);
+ QV4::CompiledData::Unit *qmlUnit = reinterpret_cast<QV4::CompiledData::Unit *>(malloc(readOnlyQmlUnit->unitSize));
+ memcpy(qmlUnit, readOnlyQmlUnit, readOnlyQmlUnit->unitSize);
+ qmlUnit->flags &= ~QV4::CompiledData::Unit::StaticData;
+ td->compilationUnit()->data = qmlUnit;
+
const QV4::CompiledData::Object *rootObject = qmlUnit->objectAt(qmlUnit->indexOfRootObject);
QCOMPARE(qmlUnit->stringAt(rootObject->inheritedTypeNameIndex), QString("MyTypeObject"));
quint32 i;
@@ -2536,7 +2601,7 @@ void tst_qqmllanguage::basicRemote_data()
QTest::newRow("no need for qmldir") << QUrl(serverdir+"Test.qml") << "" << "";
QTest::newRow("absent qmldir") << QUrl(serverdir+"/noqmldir/Test.qml") << "" << "";
- QTest::newRow("need qmldir") << QUrl(serverdir+"TestLocal.qml") << "" << "";
+ QTest::newRow("need qmldir") << QUrl(serverdir+"TestNamed.qml") << "" << "";
}
void tst_qqmllanguage::basicRemote()
@@ -2576,6 +2641,8 @@ void tst_qqmllanguage::importsRemote_data()
<< "";
QTest::newRow("remote import with local") << "import \""+serverdir+"\"\nTestLocal {}" << "QQuickImage"
<< "";
+ QTest::newRow("remote import with qualifier") << "import \""+serverdir+"\" as NS\nNS.NamedLocal {}" << "QQuickImage"
+ << "";
QTest::newRow("wrong remote import with undeclared local") << "import \""+serverdir+"\"\nWrongTestLocal {}" << ""
<< "WrongTestLocal is not a type";
QTest::newRow("wrong remote import of internal local") << "import \""+serverdir+"\"\nLocalInternal {}" << ""
@@ -2882,7 +2949,7 @@ void tst_qqmllanguage::importIncorrectCase()
QCOMPARE(errors.count(), 1);
const QString expectedError = isCaseSensitiveFileSystem(dataDirectory()) ?
- QStringLiteral("File not found") :
+ QStringLiteral("No such file or directory") :
QStringLiteral("File name case mismatch");
QCOMPARE(errors.at(0).description(), expectedError);
@@ -3156,6 +3223,14 @@ void tst_qqmllanguage::subclassedUncreateableRevision()
delete obj;
}
+void tst_qqmllanguage::uncreatableTypesAsProperties()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("uncreatableTypeAsProperty.qml"));
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+}
+
void tst_qqmllanguage::initTestCase()
{
QQmlDataTest::initTestCase();
@@ -3861,12 +3936,11 @@ void tst_qqmllanguage::compositeSingletonInstantiateError()
VERIFY_ERRORS("singletonTest9.error.txt");
}
-// Having a composite singleton type as dynamic property type fails
-// (like C++ singleton)
+// Having a composite singleton type as dynamic property type is allowed
void tst_qqmllanguage::compositeSingletonDynamicPropertyError()
{
QQmlComponent component(&engine, testFile("singletonTest10.qml"));
- VERIFY_ERRORS("singletonTest10.error.txt");
+ VERIFY_ERRORS(0);
}
// Having a composite singleton type as dynamic signal parameter succeeds
@@ -4054,7 +4128,7 @@ void tst_qqmllanguage::preservePropertyCacheOnGroupObjects()
QVERIFY(subCache);
QQmlPropertyData *pd = subCache->property(QStringLiteral("newProperty"), /*object*/0, /*context*/0);
QVERIFY(pd);
- QCOMPARE(pd->propType, qMetaTypeId<int>());
+ QCOMPARE(pd->propType(), qMetaTypeId<int>());
}
void tst_qqmllanguage::propertyCacheInSync()
@@ -4120,14 +4194,6 @@ void tst_qqmllanguage::earlyIdObjectAccess()
QVERIFY(o->property("success").toBool());
}
-void tst_qqmllanguage::dataAlignment()
-{
- QVERIFY(sizeof(QQmlVMEMetaData) % sizeof(int) == 0);
- QVERIFY(sizeof(QQmlVMEMetaData::AliasData) % sizeof(int) == 0);
- QVERIFY(sizeof(QQmlVMEMetaData::PropertyData) % sizeof(int) == 0);
- QVERIFY(sizeof(QQmlVMEMetaData::MethodData) % sizeof(int) == 0);
-}
-
void tst_qqmllanguage::deleteSingletons()
{
QPointer<QObject> singleton;
@@ -4146,6 +4212,27 @@ void tst_qqmllanguage::deleteSingletons()
QVERIFY(singleton.data() == 0);
}
+void tst_qqmllanguage::arrayBuffer_data()
+{
+ QTest::addColumn<QString>("file");
+ QTest::newRow("arraybuffer_property_get") << "arraybuffer_property_get.qml";
+ QTest::newRow("arraybuffer_property_set") << "arraybuffer_property_set.qml";
+ QTest::newRow("arraybuffer_signal_arg") << "arraybuffer_signal_arg.qml";
+ QTest::newRow("arraybuffer_method_arg") << "arraybuffer_method_arg.qml";
+ QTest::newRow("arraybuffer_method_return") << "arraybuffer_method_return.qml";
+ QTest::newRow("arraybuffer_method_overload") << "arraybuffer_method_overload.qml";
+}
+
+void tst_qqmllanguage::arrayBuffer()
+{
+ QFETCH(QString, file);
+ QQmlComponent component(&engine, testFile(file));
+ VERIFY_ERRORS(0);
+ QObject *object = component.create();
+ QVERIFY(object != 0);
+ QCOMPARE(object->property("ok").toBool(), true);
+}
+
QTEST_MAIN(tst_qqmllanguage)
#include "tst_qqmllanguage.moc"
diff --git a/tests/auto/qml/qqmlmoduleplugin/plugin.2.1/childplugin/childplugin.cpp b/tests/auto/qml/qqmlmoduleplugin/plugin.2.1/childplugin/childplugin.cpp
index 53247e7912..515d56a3c4 100644
--- a/tests/auto/qml/qqmlmoduleplugin/plugin.2.1/childplugin/childplugin.cpp
+++ b/tests/auto/qml/qqmlmoduleplugin/plugin.2.1/childplugin/childplugin.cpp
@@ -53,7 +53,7 @@ private:
class MyChildPlugin : public QQmlExtensionPlugin
{
Q_OBJECT
- Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
+ Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid)
public:
MyChildPlugin()
diff --git a/tests/auto/qml/qqmlmoduleplugin/plugin.2/childplugin/childplugin.cpp b/tests/auto/qml/qqmlmoduleplugin/plugin.2/childplugin/childplugin.cpp
index a59347d3a9..56545cfa3c 100644
--- a/tests/auto/qml/qqmlmoduleplugin/plugin.2/childplugin/childplugin.cpp
+++ b/tests/auto/qml/qqmlmoduleplugin/plugin.2/childplugin/childplugin.cpp
@@ -53,7 +53,7 @@ private:
class MyChildPlugin : public QQmlExtensionPlugin
{
Q_OBJECT
- Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
+ Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid)
public:
MyChildPlugin()
diff --git a/tests/auto/qml/qqmlmoduleplugin/plugin/childplugin/childplugin.cpp b/tests/auto/qml/qqmlmoduleplugin/plugin/childplugin/childplugin.cpp
index 5f4f96f7e4..28490d3d98 100644
--- a/tests/auto/qml/qqmlmoduleplugin/plugin/childplugin/childplugin.cpp
+++ b/tests/auto/qml/qqmlmoduleplugin/plugin/childplugin/childplugin.cpp
@@ -52,7 +52,7 @@ private:
class MyChildPlugin : public QQmlExtensionPlugin
{
Q_OBJECT
- Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
+ Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid)
public:
MyChildPlugin()
diff --git a/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp b/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp
index 1d01f69d9b..fe73610bcc 100644
--- a/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp
+++ b/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp
@@ -157,7 +157,7 @@ void tst_qqmlproperty::qmlmetaproperty()
QObject *obj = new QObject;
- QQmlAbstractBinding::Ptr binding(new QQmlBinding(QLatin1String("null"), 0, engine.rootContext()));
+ QQmlAbstractBinding::Ptr binding(QQmlBinding::create(nullptr, QLatin1String("null"), 0, engine.rootContext()));
QVERIFY(binding);
QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(obj, QObjectPrivate::get(obj)->signalIndex("destroyed()"), QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), QString(), -1, -1);
QQmlJavaScriptExpression::DeleteWatcher sigExprWatcher(sigExpr);
@@ -194,7 +194,8 @@ void tst_qqmlproperty::qmlmetaproperty()
QQmlPropertyPrivate::takeSignalExpression(prop, sigExpr);
QVERIFY(sigExprWatcher.wasDeleted());
QCOMPARE(prop.index(), -1);
- QCOMPARE(QQmlPropertyPrivate::valueTypeCoreIndex(prop), -1);
+ QCOMPARE(QQmlPropertyPrivate::propertyIndex(prop).valueTypeIndex(), -1);
+ QVERIFY(!QQmlPropertyPrivate::propertyIndex(prop).hasValueTypeIndex());
delete obj;
}
@@ -398,7 +399,7 @@ void tst_qqmlproperty::qmlmetaproperty_object()
{
QQmlProperty prop(&object);
- QQmlAbstractBinding::Ptr binding(new QQmlBinding(QLatin1String("null"), 0, engine.rootContext()));
+ QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(prop)->core, QLatin1String("null"), 0, engine.rootContext()));
QVERIFY(binding);
QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&object, QObjectPrivate::get(&object)->signalIndex("destroyed()"), QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), QString(), -1, -1);
QQmlJavaScriptExpression::DeleteWatcher sigExprWatcher(sigExpr);
@@ -437,7 +438,8 @@ void tst_qqmlproperty::qmlmetaproperty_object()
QQmlPropertyPrivate::takeSignalExpression(prop, sigExpr);
QVERIFY(sigExprWatcher.wasDeleted());
QCOMPARE(prop.index(), -1);
- QCOMPARE(QQmlPropertyPrivate::valueTypeCoreIndex(prop), -1);
+ QCOMPARE(QQmlPropertyPrivate::propertyIndex(prop).valueTypeIndex(), -1);
+ QVERIFY(!QQmlPropertyPrivate::propertyIndex(prop).hasValueTypeIndex());
delete obj;
}
@@ -445,7 +447,7 @@ void tst_qqmlproperty::qmlmetaproperty_object()
{
QQmlProperty prop(&dobject);
- QQmlAbstractBinding::Ptr binding(new QQmlBinding(QLatin1String("null"), 0, engine.rootContext()));
+ QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(prop)->core, QLatin1String("null"), 0, engine.rootContext()));
static_cast<QQmlBinding *>(binding.data())->setTarget(prop);
QVERIFY(binding);
QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&dobject, QObjectPrivate::get(&dobject)->signalIndex("clicked()"), QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), QString(), -1, -1);
@@ -487,7 +489,8 @@ void tst_qqmlproperty::qmlmetaproperty_object()
QQmlPropertyPrivate::takeSignalExpression(prop, sigExpr);
QVERIFY(sigExprWatcher.wasDeleted());
QCOMPARE(prop.index(), dobject.metaObject()->indexOfProperty("defaultProperty"));
- QCOMPARE(QQmlPropertyPrivate::valueTypeCoreIndex(prop), -1);
+ QCOMPARE(QQmlPropertyPrivate::propertyIndex(prop).valueTypeIndex(), -1);
+ QVERIFY(!QQmlPropertyPrivate::propertyIndex(prop).hasValueTypeIndex());
delete obj;
}
@@ -501,7 +504,7 @@ void tst_qqmlproperty::qmlmetaproperty_object_string()
{
QQmlProperty prop(&object, QString("defaultProperty"));
- QQmlAbstractBinding::Ptr binding(new QQmlBinding(QLatin1String("null"), 0, engine.rootContext()));
+ QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(prop)->core, QLatin1String("null"), 0, engine.rootContext()));
QVERIFY(binding);
QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&object, QObjectPrivate::get(&object)->signalIndex("destroyed()"), QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), QString(), -1, -1);
QQmlJavaScriptExpression::DeleteWatcher sigExprWatcher(sigExpr);
@@ -540,7 +543,8 @@ void tst_qqmlproperty::qmlmetaproperty_object_string()
QQmlPropertyPrivate::takeSignalExpression(prop, sigExpr);
QVERIFY(sigExprWatcher.wasDeleted());
QCOMPARE(prop.index(), -1);
- QCOMPARE(QQmlPropertyPrivate::valueTypeCoreIndex(prop), -1);
+ QCOMPARE(QQmlPropertyPrivate::propertyIndex(prop).valueTypeIndex(), -1);
+ QVERIFY(!QQmlPropertyPrivate::propertyIndex(prop).hasValueTypeIndex());
delete obj;
}
@@ -548,7 +552,7 @@ void tst_qqmlproperty::qmlmetaproperty_object_string()
{
QQmlProperty prop(&dobject, QString("defaultProperty"));
- QQmlAbstractBinding::Ptr binding(new QQmlBinding(QLatin1String("null"), 0, engine.rootContext()));
+ QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(prop)->core, QLatin1String("null"), 0, engine.rootContext()));
static_cast<QQmlBinding *>(binding.data())->setTarget(prop);
QVERIFY(binding);
QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&dobject, QObjectPrivate::get(&dobject)->signalIndex("clicked()"), QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), QString(), -1, -1);
@@ -590,7 +594,8 @@ void tst_qqmlproperty::qmlmetaproperty_object_string()
QQmlPropertyPrivate::takeSignalExpression(prop, sigExpr);
QVERIFY(sigExprWatcher.wasDeleted());
QCOMPARE(prop.index(), dobject.metaObject()->indexOfProperty("defaultProperty"));
- QCOMPARE(QQmlPropertyPrivate::valueTypeCoreIndex(prop), -1);
+ QCOMPARE(QQmlPropertyPrivate::propertyIndex(prop).valueTypeIndex(), -1);
+ QVERIFY(!QQmlPropertyPrivate::propertyIndex(prop).hasValueTypeIndex());
delete obj;
}
@@ -598,7 +603,7 @@ void tst_qqmlproperty::qmlmetaproperty_object_string()
{
QQmlProperty prop(&dobject, QString("onClicked"));
- QQmlAbstractBinding::Ptr binding(new QQmlBinding(QLatin1String("null"), 0, engine.rootContext()));
+ QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(prop)->core, QLatin1String("null"), 0, engine.rootContext()));
static_cast<QQmlBinding *>(binding.data())->setTarget(prop);
QVERIFY(binding);
QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&dobject, QQmlPropertyPrivate::get(prop)->signalIndex(), QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), QString(), -1, -1);
@@ -639,7 +644,8 @@ void tst_qqmlproperty::qmlmetaproperty_object_string()
QVERIFY(!sigExprWatcher.wasDeleted());
QCOMPARE(QQmlPropertyPrivate::signalExpression(prop), sigExpr);
QCOMPARE(prop.index(), dobject.metaObject()->indexOfMethod("clicked()"));
- QCOMPARE(QQmlPropertyPrivate::valueTypeCoreIndex(prop), -1);
+ QCOMPARE(QQmlPropertyPrivate::propertyIndex(prop).valueTypeIndex(), -1);
+ QVERIFY(!QQmlPropertyPrivate::propertyIndex(prop).hasValueTypeIndex());
delete obj;
}
@@ -647,7 +653,7 @@ void tst_qqmlproperty::qmlmetaproperty_object_string()
{
QQmlProperty prop(&dobject, QString("onPropertyWithNotifyChanged"));
- QQmlAbstractBinding::Ptr binding(new QQmlBinding(QLatin1String("null"), 0, engine.rootContext()));
+ QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(prop)->core, QLatin1String("null"), 0, engine.rootContext()));
static_cast<QQmlBinding *>(binding.data())->setTarget(prop);
QVERIFY(binding);
QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&dobject, QQmlPropertyPrivate::get(prop)->signalIndex(), QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), QString(), -1, -1);
@@ -688,7 +694,8 @@ void tst_qqmlproperty::qmlmetaproperty_object_string()
QVERIFY(!sigExprWatcher.wasDeleted());
QCOMPARE(QQmlPropertyPrivate::signalExpression(prop), sigExpr);
QCOMPARE(prop.index(), dobject.metaObject()->indexOfMethod("oddlyNamedNotifySignal()"));
- QCOMPARE(QQmlPropertyPrivate::valueTypeCoreIndex(prop), -1);
+ QCOMPARE(QQmlPropertyPrivate::propertyIndex(prop).valueTypeIndex(), -1);
+ QVERIFY(!QQmlPropertyPrivate::propertyIndex(prop).hasValueTypeIndex());
delete obj;
}
@@ -702,7 +709,7 @@ void tst_qqmlproperty::qmlmetaproperty_object_context()
{
QQmlProperty prop(&object, engine.rootContext());
- QQmlAbstractBinding::Ptr binding(new QQmlBinding(QLatin1String("null"), 0, engine.rootContext()));
+ QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(prop)->core, QLatin1String("null"), 0, engine.rootContext()));
QVERIFY(binding);
QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&object, QObjectPrivate::get(&object)->signalIndex("destroyed()"), QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), QString(), -1, -1);
QQmlJavaScriptExpression::DeleteWatcher sigExprWatcher(sigExpr);
@@ -741,7 +748,8 @@ void tst_qqmlproperty::qmlmetaproperty_object_context()
QQmlPropertyPrivate::takeSignalExpression(prop, sigExpr);
QVERIFY(sigExprWatcher.wasDeleted());
QCOMPARE(prop.index(), -1);
- QCOMPARE(QQmlPropertyPrivate::valueTypeCoreIndex(prop), -1);
+ QCOMPARE(QQmlPropertyPrivate::propertyIndex(prop).valueTypeIndex(), -1);
+ QVERIFY(!QQmlPropertyPrivate::propertyIndex(prop).hasValueTypeIndex());
delete obj;
}
@@ -749,7 +757,7 @@ void tst_qqmlproperty::qmlmetaproperty_object_context()
{
QQmlProperty prop(&dobject, engine.rootContext());
- QQmlAbstractBinding::Ptr binding(new QQmlBinding(QLatin1String("null"), 0, engine.rootContext()));
+ QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(prop)->core, QLatin1String("null"), 0, engine.rootContext()));
static_cast<QQmlBinding *>(binding.data())->setTarget(prop);
QVERIFY(binding);
QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&dobject, QObjectPrivate::get(&dobject)->signalIndex("clicked()"), QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), QString(), -1, -1);
@@ -791,7 +799,8 @@ void tst_qqmlproperty::qmlmetaproperty_object_context()
QQmlPropertyPrivate::takeSignalExpression(prop, sigExpr);
QVERIFY(sigExprWatcher.wasDeleted());
QCOMPARE(prop.index(), dobject.metaObject()->indexOfProperty("defaultProperty"));
- QCOMPARE(QQmlPropertyPrivate::valueTypeCoreIndex(prop), -1);
+ QCOMPARE(QQmlPropertyPrivate::propertyIndex(prop).valueTypeIndex(), -1);
+ QVERIFY(!QQmlPropertyPrivate::propertyIndex(prop).hasValueTypeIndex());
delete obj;
}
@@ -805,7 +814,7 @@ void tst_qqmlproperty::qmlmetaproperty_object_string_context()
{
QQmlProperty prop(&object, QString("defaultProperty"), engine.rootContext());
- QQmlAbstractBinding::Ptr binding(new QQmlBinding(QLatin1String("null"), 0, engine.rootContext()));
+ QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(prop)->core, QLatin1String("null"), 0, engine.rootContext()));
QVERIFY(binding);
QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&object, QObjectPrivate::get(&object)->signalIndex("destroyed()"), QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), QString(), -1, -1);
QQmlJavaScriptExpression::DeleteWatcher sigExprWatcher(sigExpr);
@@ -844,7 +853,8 @@ void tst_qqmlproperty::qmlmetaproperty_object_string_context()
QQmlPropertyPrivate::takeSignalExpression(prop, sigExpr);
QVERIFY(sigExprWatcher.wasDeleted());
QCOMPARE(prop.index(), -1);
- QCOMPARE(QQmlPropertyPrivate::valueTypeCoreIndex(prop), -1);
+ QCOMPARE(QQmlPropertyPrivate::propertyIndex(prop).valueTypeIndex(), -1);
+ QVERIFY(!QQmlPropertyPrivate::propertyIndex(prop).hasValueTypeIndex());
delete obj;
}
@@ -852,7 +862,7 @@ void tst_qqmlproperty::qmlmetaproperty_object_string_context()
{
QQmlProperty prop(&dobject, QString("defaultProperty"), engine.rootContext());
- QQmlAbstractBinding::Ptr binding(new QQmlBinding(QLatin1String("null"), 0, engine.rootContext()));
+ QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(prop)->core, QLatin1String("null"), 0, engine.rootContext()));
static_cast<QQmlBinding *>(binding.data())->setTarget(prop);
QVERIFY(binding);
QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&dobject, QObjectPrivate::get(&dobject)->signalIndex("clicked()"), QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), QString(), -1, -1);
@@ -894,7 +904,8 @@ void tst_qqmlproperty::qmlmetaproperty_object_string_context()
QQmlPropertyPrivate::takeSignalExpression(prop, sigExpr);
QVERIFY(sigExprWatcher.wasDeleted());
QCOMPARE(prop.index(), dobject.metaObject()->indexOfProperty("defaultProperty"));
- QCOMPARE(QQmlPropertyPrivate::valueTypeCoreIndex(prop), -1);
+ QCOMPARE(QQmlPropertyPrivate::propertyIndex(prop).valueTypeIndex(), -1);
+ QVERIFY(!QQmlPropertyPrivate::propertyIndex(prop).hasValueTypeIndex());
delete obj;
}
@@ -902,7 +913,7 @@ void tst_qqmlproperty::qmlmetaproperty_object_string_context()
{
QQmlProperty prop(&dobject, QString("onClicked"), engine.rootContext());
- QQmlAbstractBinding::Ptr binding(new QQmlBinding(QLatin1String("null"), 0, engine.rootContext()));
+ QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(prop)->core, QLatin1String("null"), 0, engine.rootContext()));
static_cast<QQmlBinding *>(binding.data())->setTarget(prop);
QVERIFY(binding);
QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&dobject, QQmlPropertyPrivate::get(prop)->signalIndex(), QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), QString(), -1, -1);
@@ -943,7 +954,8 @@ void tst_qqmlproperty::qmlmetaproperty_object_string_context()
QVERIFY(!sigExprWatcher.wasDeleted());
QCOMPARE(QQmlPropertyPrivate::signalExpression(prop), sigExpr);
QCOMPARE(prop.index(), dobject.metaObject()->indexOfMethod("clicked()"));
- QCOMPARE(QQmlPropertyPrivate::valueTypeCoreIndex(prop), -1);
+ QCOMPARE(QQmlPropertyPrivate::propertyIndex(prop).valueTypeIndex(), -1);
+ QVERIFY(!QQmlPropertyPrivate::propertyIndex(prop).hasValueTypeIndex());
delete obj;
}
@@ -951,7 +963,7 @@ void tst_qqmlproperty::qmlmetaproperty_object_string_context()
{
QQmlProperty prop(&dobject, QString("onPropertyWithNotifyChanged"), engine.rootContext());
- QQmlAbstractBinding::Ptr binding(new QQmlBinding(QLatin1String("null"), 0, engine.rootContext()));
+ QQmlAbstractBinding::Ptr binding(QQmlBinding::create(&QQmlPropertyPrivate::get(prop)->core, QLatin1String("null"), 0, engine.rootContext()));
static_cast<QQmlBinding *>(binding.data())->setTarget(prop);
QVERIFY(binding);
QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(&dobject, QQmlPropertyPrivate::get(prop)->signalIndex(), QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), QString(), -1, -1);
@@ -992,7 +1004,8 @@ void tst_qqmlproperty::qmlmetaproperty_object_string_context()
QVERIFY(!sigExprWatcher.wasDeleted());
QCOMPARE(QQmlPropertyPrivate::signalExpression(prop), sigExpr);
QCOMPARE(prop.index(), dobject.metaObject()->indexOfMethod("oddlyNamedNotifySignal()"));
- QCOMPARE(QQmlPropertyPrivate::valueTypeCoreIndex(prop), -1);
+ QCOMPARE(QQmlPropertyPrivate::propertyIndex(prop).valueTypeIndex(), -1);
+ QVERIFY(!QQmlPropertyPrivate::propertyIndex(prop).hasValueTypeIndex());
delete obj;
}
diff --git a/tests/auto/qml/qqmlpropertycache/tst_qqmlpropertycache.cpp b/tests/auto/qml/qqmlpropertycache/tst_qqmlpropertycache.cpp
index 5f15afff85..824fe445c0 100644
--- a/tests/auto/qml/qqmlpropertycache/tst_qqmlpropertycache.cpp
+++ b/tests/auto/qml/qqmlpropertycache/tst_qqmlpropertycache.cpp
@@ -30,6 +30,8 @@
#include <private/qqmlpropertycache_p.h>
#include <QtQml/qqmlengine.h>
#include <private/qv8engine_p.h>
+#include <private/qmetaobjectbuilder_p.h>
+#include <QCryptographicHash>
#include "../../shared/util.h"
class tst_qqmlpropertycache : public QObject
@@ -45,6 +47,9 @@ private slots:
void methodsDerived();
void signalHandlers();
void signalHandlersDerived();
+ void metaObjectSize_data();
+ void metaObjectSize();
+ void metaObjectChecksum();
private:
QQmlEngine engine;
@@ -105,17 +110,17 @@ void tst_qqmlpropertycache::properties()
QQmlRefPointer<QQmlPropertyCache> cache(new QQmlPropertyCache(v4, metaObject));
QQmlPropertyData *data;
- QVERIFY(data = cacheProperty(cache, "propertyA"));
- QCOMPARE(data->coreIndex, metaObject->indexOfProperty("propertyA"));
+ QVERIFY((data = cacheProperty(cache, "propertyA")));
+ QCOMPARE(data->coreIndex(), metaObject->indexOfProperty("propertyA"));
- QVERIFY(data = cacheProperty(cache, "propertyB"));
- QCOMPARE(data->coreIndex, metaObject->indexOfProperty("propertyB"));
+ QVERIFY((data = cacheProperty(cache, "propertyB")));
+ QCOMPARE(data->coreIndex(), metaObject->indexOfProperty("propertyB"));
- QVERIFY(data = cacheProperty(cache, "propertyC"));
- QCOMPARE(data->coreIndex, metaObject->indexOfProperty("propertyC"));
+ QVERIFY((data = cacheProperty(cache, "propertyC")));
+ QCOMPARE(data->coreIndex(), metaObject->indexOfProperty("propertyC"));
- QVERIFY(data = cacheProperty(cache, "propertyD"));
- QCOMPARE(data->coreIndex, metaObject->indexOfProperty("propertyD"));
+ QVERIFY((data = cacheProperty(cache, "propertyD")));
+ QCOMPARE(data->coreIndex(), metaObject->indexOfProperty("propertyD"));
}
void tst_qqmlpropertycache::propertiesDerived()
@@ -129,17 +134,17 @@ void tst_qqmlpropertycache::propertiesDerived()
QQmlRefPointer<QQmlPropertyCache> cache(parentCache->copyAndAppend(object.metaObject()));
QQmlPropertyData *data;
- QVERIFY(data = cacheProperty(cache, "propertyA"));
- QCOMPARE(data->coreIndex, metaObject->indexOfProperty("propertyA"));
+ QVERIFY((data = cacheProperty(cache, "propertyA")));
+ QCOMPARE(data->coreIndex(), metaObject->indexOfProperty("propertyA"));
- QVERIFY(data = cacheProperty(cache, "propertyB"));
- QCOMPARE(data->coreIndex, metaObject->indexOfProperty("propertyB"));
+ QVERIFY((data = cacheProperty(cache, "propertyB")));
+ QCOMPARE(data->coreIndex(), metaObject->indexOfProperty("propertyB"));
- QVERIFY(data = cacheProperty(cache, "propertyC"));
- QCOMPARE(data->coreIndex, metaObject->indexOfProperty("propertyC"));
+ QVERIFY((data = cacheProperty(cache, "propertyC")));
+ QCOMPARE(data->coreIndex(), metaObject->indexOfProperty("propertyC"));
- QVERIFY(data = cacheProperty(cache, "propertyD"));
- QCOMPARE(data->coreIndex, metaObject->indexOfProperty("propertyD"));
+ QVERIFY((data = cacheProperty(cache, "propertyD")));
+ QCOMPARE(data->coreIndex(), metaObject->indexOfProperty("propertyD"));
}
void tst_qqmlpropertycache::methods()
@@ -152,29 +157,29 @@ void tst_qqmlpropertycache::methods()
QQmlRefPointer<QQmlPropertyCache> cache(new QQmlPropertyCache(v4, metaObject));
QQmlPropertyData *data;
- QVERIFY(data = cacheProperty(cache, "slotA"));
- QCOMPARE(data->coreIndex, metaObject->indexOfMethod("slotA()"));
+ QVERIFY((data = cacheProperty(cache, "slotA")));
+ QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("slotA()"));
- QVERIFY(data = cacheProperty(cache, "slotB"));
- QCOMPARE(data->coreIndex, metaObject->indexOfMethod("slotB()"));
+ QVERIFY((data = cacheProperty(cache, "slotB")));
+ QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("slotB()"));
- QVERIFY(data = cacheProperty(cache, "signalA"));
- QCOMPARE(data->coreIndex, metaObject->indexOfMethod("signalA()"));
+ QVERIFY((data = cacheProperty(cache, "signalA")));
+ QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("signalA()"));
- QVERIFY(data = cacheProperty(cache, "signalB"));
- QCOMPARE(data->coreIndex, metaObject->indexOfMethod("signalB()"));
+ QVERIFY((data = cacheProperty(cache, "signalB")));
+ QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("signalB()"));
- QVERIFY(data = cacheProperty(cache, "propertyAChanged"));
- QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyAChanged()"));
+ QVERIFY((data = cacheProperty(cache, "propertyAChanged")));
+ QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("propertyAChanged()"));
- QVERIFY(data = cacheProperty(cache, "propertyBChanged"));
- QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyBChanged()"));
+ QVERIFY((data = cacheProperty(cache, "propertyBChanged")));
+ QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("propertyBChanged()"));
- QVERIFY(data = cacheProperty(cache, "propertyCChanged"));
- QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyCChanged()"));
+ QVERIFY((data = cacheProperty(cache, "propertyCChanged")));
+ QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("propertyCChanged()"));
- QVERIFY(data = cacheProperty(cache, "propertyDChanged"));
- QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyDChanged()"));
+ QVERIFY((data = cacheProperty(cache, "propertyDChanged")));
+ QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("propertyDChanged()"));
}
void tst_qqmlpropertycache::methodsDerived()
@@ -188,29 +193,29 @@ void tst_qqmlpropertycache::methodsDerived()
QQmlRefPointer<QQmlPropertyCache> cache(parentCache->copyAndAppend(object.metaObject()));
QQmlPropertyData *data;
- QVERIFY(data = cacheProperty(cache, "slotA"));
- QCOMPARE(data->coreIndex, metaObject->indexOfMethod("slotA()"));
+ QVERIFY((data = cacheProperty(cache, "slotA")));
+ QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("slotA()"));
- QVERIFY(data = cacheProperty(cache, "slotB"));
- QCOMPARE(data->coreIndex, metaObject->indexOfMethod("slotB()"));
+ QVERIFY((data = cacheProperty(cache, "slotB")));
+ QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("slotB()"));
- QVERIFY(data = cacheProperty(cache, "signalA"));
- QCOMPARE(data->coreIndex, metaObject->indexOfMethod("signalA()"));
+ QVERIFY((data = cacheProperty(cache, "signalA")));
+ QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("signalA()"));
- QVERIFY(data = cacheProperty(cache, "signalB"));
- QCOMPARE(data->coreIndex, metaObject->indexOfMethod("signalB()"));
+ QVERIFY((data = cacheProperty(cache, "signalB")));
+ QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("signalB()"));
- QVERIFY(data = cacheProperty(cache, "propertyAChanged"));
- QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyAChanged()"));
+ QVERIFY((data = cacheProperty(cache, "propertyAChanged")));
+ QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("propertyAChanged()"));
- QVERIFY(data = cacheProperty(cache, "propertyBChanged"));
- QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyBChanged()"));
+ QVERIFY((data = cacheProperty(cache, "propertyBChanged")));
+ QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("propertyBChanged()"));
- QVERIFY(data = cacheProperty(cache, "propertyCChanged"));
- QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyCChanged()"));
+ QVERIFY((data = cacheProperty(cache, "propertyCChanged")));
+ QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("propertyCChanged()"));
- QVERIFY(data = cacheProperty(cache, "propertyDChanged"));
- QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyDChanged()"));
+ QVERIFY((data = cacheProperty(cache, "propertyDChanged")));
+ QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("propertyDChanged()"));
}
void tst_qqmlpropertycache::signalHandlers()
@@ -223,23 +228,23 @@ void tst_qqmlpropertycache::signalHandlers()
QQmlRefPointer<QQmlPropertyCache> cache(new QQmlPropertyCache(v4, metaObject));
QQmlPropertyData *data;
- QVERIFY(data = cacheProperty(cache, "onSignalA"));
- QCOMPARE(data->coreIndex, metaObject->indexOfMethod("signalA()"));
+ QVERIFY((data = cacheProperty(cache, "onSignalA")));
+ QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("signalA()"));
- QVERIFY(data = cacheProperty(cache, "onSignalB"));
- QCOMPARE(data->coreIndex, metaObject->indexOfMethod("signalB()"));
+ QVERIFY((data = cacheProperty(cache, "onSignalB")));
+ QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("signalB()"));
- QVERIFY(data = cacheProperty(cache, "onPropertyAChanged"));
- QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyAChanged()"));
+ QVERIFY((data = cacheProperty(cache, "onPropertyAChanged")));
+ QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("propertyAChanged()"));
- QVERIFY(data = cacheProperty(cache, "onPropertyBChanged"));
- QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyBChanged()"));
+ QVERIFY((data = cacheProperty(cache, "onPropertyBChanged")));
+ QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("propertyBChanged()"));
- QVERIFY(data = cacheProperty(cache, "onPropertyCChanged"));
- QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyCChanged()"));
+ QVERIFY((data = cacheProperty(cache, "onPropertyCChanged")));
+ QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("propertyCChanged()"));
- QVERIFY(data = cacheProperty(cache, "onPropertyDChanged"));
- QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyDChanged()"));
+ QVERIFY((data = cacheProperty(cache, "onPropertyDChanged")));
+ QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("propertyDChanged()"));
}
void tst_qqmlpropertycache::signalHandlersDerived()
@@ -253,25 +258,145 @@ void tst_qqmlpropertycache::signalHandlersDerived()
QQmlRefPointer<QQmlPropertyCache> cache(parentCache->copyAndAppend(object.metaObject()));
QQmlPropertyData *data;
- QVERIFY(data = cacheProperty(cache, "onSignalA"));
- QCOMPARE(data->coreIndex, metaObject->indexOfMethod("signalA()"));
+ QVERIFY((data = cacheProperty(cache, "onSignalA")));
+ QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("signalA()"));
- QVERIFY(data = cacheProperty(cache, "onSignalB"));
- QCOMPARE(data->coreIndex, metaObject->indexOfMethod("signalB()"));
+ QVERIFY((data = cacheProperty(cache, "onSignalB")));
+ QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("signalB()"));
- QVERIFY(data = cacheProperty(cache, "onPropertyAChanged"));
- QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyAChanged()"));
+ QVERIFY((data = cacheProperty(cache, "onPropertyAChanged")));
+ QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("propertyAChanged()"));
- QVERIFY(data = cacheProperty(cache, "onPropertyBChanged"));
- QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyBChanged()"));
+ QVERIFY((data = cacheProperty(cache, "onPropertyBChanged")));
+ QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("propertyBChanged()"));
- QVERIFY(data = cacheProperty(cache, "onPropertyCChanged"));
- QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyCChanged()"));
+ QVERIFY((data = cacheProperty(cache, "onPropertyCChanged")));
+ QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("propertyCChanged()"));
- QVERIFY(data = cacheProperty(cache, "onPropertyDChanged"));
- QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyDChanged()"));
+ QVERIFY((data = cacheProperty(cache, "onPropertyDChanged")));
+ QCOMPARE(data->coreIndex(), metaObject->indexOfMethod("propertyDChanged()"));
}
-QTEST_MAIN(tst_qqmlpropertycache)
+class TestClass : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(int prop READ prop WRITE setProp NOTIFY propChanged)
+ int m_prop;
+
+public:
+ enum MyEnum {
+ First, Second
+ };
+ Q_ENUM(MyEnum)
+
+ Q_CLASSINFO("Foo", "Bar")
+
+ TestClass() {}
+
+ int prop() const
+ {
+ return m_prop;
+ }
+
+public slots:
+ void setProp(int prop)
+ {
+ if (m_prop == prop)
+ return;
+
+ m_prop = prop;
+ emit propChanged(prop);
+ }
+signals:
+ void propChanged(int prop);
+};
+
+class TestClassWithParameters : public QObject
+{
+ Q_OBJECT
+
+public:
+ Q_INVOKABLE void slotWithArguments(int firstArg) {
+ Q_UNUSED(firstArg);
+ }
+};
+
+class TestClassWithClassInfo : public QObject
+{
+ Q_OBJECT
+ Q_CLASSINFO("Key", "Value")
+};
#include "tst_qqmlpropertycache.moc"
+
+#define ARRAY_SIZE(arr) \
+ int(sizeof(arr) / sizeof(arr[0]))
+
+#define TEST_CLASS(Class) \
+ QTest::newRow(#Class) << &Class::staticMetaObject << ARRAY_SIZE(qt_meta_data_##Class) << ARRAY_SIZE(qt_meta_stringdata_##Class.data)
+
+Q_DECLARE_METATYPE(const QMetaObject*);
+
+void tst_qqmlpropertycache::metaObjectSize_data()
+{
+ QTest::addColumn<const QMetaObject*>("metaObject");
+ QTest::addColumn<int>("expectedFieldCount");
+ QTest::addColumn<int>("expectedStringCount");
+
+ TEST_CLASS(TestClass);
+ TEST_CLASS(TestClassWithParameters);
+ TEST_CLASS(TestClassWithClassInfo);
+}
+
+void tst_qqmlpropertycache::metaObjectSize()
+{
+ QFETCH(const QMetaObject *, metaObject);
+ QFETCH(int, expectedFieldCount);
+ QFETCH(int, expectedStringCount);
+
+ int size = 0;
+ int stringDataSize = 0;
+ bool valid = QQmlPropertyCache::determineMetaObjectSizes(*metaObject, &size, &stringDataSize);
+ QVERIFY(valid);
+
+ QCOMPARE(size, expectedFieldCount - 1); // Remove trailing zero field until fixed in moc.
+ QCOMPARE(stringDataSize, expectedStringCount);
+}
+
+void tst_qqmlpropertycache::metaObjectChecksum()
+{
+ QMetaObjectBuilder builder;
+ builder.setClassName("Test");
+ builder.addClassInfo("foo", "bar");
+
+ QCryptographicHash hash(QCryptographicHash::Md5);
+
+ QScopedPointer<QMetaObject, QScopedPointerPodDeleter> mo(builder.toMetaObject());
+ QVERIFY(!mo.isNull());
+
+ QVERIFY(QQmlPropertyCache::addToHash(hash, *mo.data()));
+ QByteArray initialHash = hash.result();
+ QVERIFY(!initialHash.isEmpty());
+ hash.reset();
+
+ {
+ QVERIFY(QQmlPropertyCache::addToHash(hash, *mo.data()));
+ QByteArray nextHash = hash.result();
+ QVERIFY(!nextHash.isEmpty());
+ hash.reset();
+ QCOMPARE(initialHash, nextHash);
+ }
+
+ builder.addProperty("testProperty", "int", -1);
+
+ mo.reset(builder.toMetaObject());
+ {
+ QVERIFY(QQmlPropertyCache::addToHash(hash, *mo.data()));
+ QByteArray nextHash = hash.result();
+ QVERIFY(!nextHash.isEmpty());
+ hash.reset();
+ QVERIFY(initialHash != nextHash);
+ }
+}
+
+QTEST_MAIN(tst_qqmlpropertycache)
diff --git a/tests/auto/qml/qqmlqt/data/LaterComponent.qml b/tests/auto/qml/qqmlqt/data/LaterComponent.qml
new file mode 100644
index 0000000000..7dbd81d93d
--- /dev/null
+++ b/tests/auto/qml/qqmlqt/data/LaterComponent.qml
@@ -0,0 +1,14 @@
+import QtQuick 2.0
+import LaterImports 1.0
+
+TestElement {
+ id: deleteme
+ function testFn() {
+ gc(); // at this point, obj is deleted.
+ dangerousFunction(); // calling this function will throw an exeption
+ // because this object has been deleted and its context is not available
+
+ // which means that we shouldn't get to this line.
+ row.test10_1 = 1;
+ }
+}
diff --git a/tests/auto/qml/qqmlqt/data/LaterComponent2.qml b/tests/auto/qml/qqmlqt/data/LaterComponent2.qml
new file mode 100644
index 0000000000..56bcc0235b
--- /dev/null
+++ b/tests/auto/qml/qqmlqt/data/LaterComponent2.qml
@@ -0,0 +1,13 @@
+import QtQuick 2.0
+
+Item {
+ id: deleteme2
+ function testFn() {
+ // this function shouldn't be called,
+ // since the object will have been deleted.
+ var crashy = Qt.createQmlObject("import QtQuick 2.0; Item { }", deleteme2) // invalid calling context if invoked after gc
+ row.test11_1 = 2;
+ }
+
+ Component.onDestruction: row.test11_1 = 1; // success == the object was deleted, but testFn wasn't called.
+}
diff --git a/tests/auto/qml/qqmlqt/data/LaterComponent3.qml b/tests/auto/qml/qqmlqt/data/LaterComponent3.qml
new file mode 100644
index 0000000000..c6f445253a
--- /dev/null
+++ b/tests/auto/qml/qqmlqt/data/LaterComponent3.qml
@@ -0,0 +1,14 @@
+import QtQuick 2.0
+import LaterImports 1.0
+
+TestElement {
+ id: deleteme3
+ function testFn() {
+ gc(); // at this point, obj is deleted.
+ dangerousFunction(); // calling this function will throw an exeption
+ // because this object has been deleted and its context is not available
+
+ // which means that we shouldn't get to this line.
+ row.test12_1 = 1;
+ }
+}
diff --git a/tests/auto/qml/qqmlqt/data/LaterComponent4.qml b/tests/auto/qml/qqmlqt/data/LaterComponent4.qml
new file mode 100644
index 0000000000..0c53bd368b
--- /dev/null
+++ b/tests/auto/qml/qqmlqt/data/LaterComponent4.qml
@@ -0,0 +1,12 @@
+import QtQuick 2.0
+
+Item {
+ id: deleteme4
+ function testFn() {
+ // this function shouldn't be called,
+ // since the object will have been deleted.
+ row.test13_1 = 2;
+ }
+
+ Component.onDestruction: row.test13_1 = 1; // success == the object was deleted, but testFn wasn't called.
+}
diff --git a/tests/auto/qml/qqmlqt/data/exit.qml b/tests/auto/qml/qqmlqt/data/exit.qml
new file mode 100644
index 0000000000..12727d9f04
--- /dev/null
+++ b/tests/auto/qml/qqmlqt/data/exit.qml
@@ -0,0 +1,7 @@
+import QtQuick 2.0
+
+QtObject {
+ property int returnCode: -1
+ Component.onCompleted: Qt.exit(returnCode)
+}
+
diff --git a/tests/auto/qml/qqmlqt/data/later.qml b/tests/auto/qml/qqmlqt/data/later.qml
new file mode 100644
index 0000000000..a90f3aba9f
--- /dev/null
+++ b/tests/auto/qml/qqmlqt/data/later.qml
@@ -0,0 +1,124 @@
+import QtQuick 2.0
+import LaterImports 1.0
+
+Row {
+ id: row
+ Repeater {
+ id: repeater
+ model: 5
+ delegate: Item { }
+ }
+
+ property bool test1_1: false
+ property bool test1_2: row.focus
+ property bool test2_1: false
+ property bool test2_2: (firstFunctionCallCounter == 1 && secondFunctionCallCounter == 1 && signalCallCounter == 1)
+
+ property int firstFunctionCallCounter: 0
+ property int secondFunctionCallCounter: 0
+ property int signalCallCounter: 0
+
+ signal testSignal
+ onTestSignal: {
+ signalCallCounter++;
+ }
+
+ onChildrenChanged: {
+ Qt.callLater(row.forceActiveFocus); // built-in function
+ Qt.callLater(row.firstFunction); // JS function
+ Qt.callLater(row.testSignal); // signal
+ }
+
+ function firstFunction() {
+ firstFunctionCallCounter++;
+ }
+
+ function secondFunction() {
+ secondFunctionCallCounter++;
+ }
+
+ Component.onCompleted: {
+ test1_1 = !row.focus;
+ test2_1 = (firstFunctionCallCounter == 0);
+
+ Qt.callLater(secondFunction);
+ Qt.callLater(firstFunction);
+ Qt.callLater(secondFunction);
+ }
+
+ function test2() {
+ repeater.model = 2;
+ }
+
+ function test3() {
+ Qt.callLater(test3_recursive);
+ }
+
+ property int recursion: 0
+ property bool test3_1: (recursion == 1)
+ property bool test3_2: (recursion == 2)
+ property bool test3_3: (recursion == 3)
+ function test3_recursive() {
+ if (recursion < 3) {
+ Qt.callLater(test3_recursive);
+ Qt.callLater(test3_recursive);
+ }
+ recursion++;
+ }
+
+ function test4() {
+ Qt.callLater(functionThatDoesNotExist);
+ }
+
+ property bool test5_1: false
+ function test5() {
+ Qt.callLater(functionWithArguments, "THESE", "ARGS", "WILL", "BE", "OVERWRITTEN")
+ Qt.callLater(functionWithArguments, "firstArg", 2, "thirdArg")
+ }
+
+ function functionWithArguments(firstStr, secondInt, thirdString) {
+ test5_1 = (firstStr == "firstArg" && secondInt == 2 && thirdString == "thirdArg");
+ }
+
+
+ property bool test6_1: (callOrder_Later == "TWO THREE ") // not "THREE TWO "
+ function test6() {
+ Qt.callLater(test6Function1, "ONE");
+ Qt.callLater(test6Function2, "TWO");
+ Qt.callLater(test6Function1, "THREE");
+ }
+
+ property string callOrder_Later
+ function test6Function1(arg) { callOrder_Later += arg + " "; }
+ function test6Function2(arg) { callOrder_Later += arg + " "; }
+
+ property int test9_1: SingletonType.intProp;
+ function test9() {
+ SingletonType.resetIntProp();
+ Qt.callLater(SingletonType.testFunc)
+ Qt.callLater(SingletonType.testFunc)
+ Qt.callLater(SingletonType.testFunc)
+ Qt.callLater(SingletonType.testFunc)
+ // should only get called once.
+ }
+
+ property int test10_1: 0
+ function test10() {
+ var c = Qt.createComponent("LaterComponent.qml");
+ var obj = c.createObject(); // QML ownership.
+ Qt.callLater(obj.testFn);
+ // note: obj will be cleaned up during next gc().
+ }
+
+ property int test11_1: 0
+ function test11() {
+ var c = Qt.createComponent("LaterComponent2.qml");
+ var obj = c.createObject(); // QML ownership.
+ Qt.callLater(obj.testFn);
+ gc(); // this won't actually collect the obj, we need to trigger gc manually in the test.
+ }
+
+ function test14() {
+ Qt.callLater(console.log, "success")
+ }
+}
diff --git a/tests/auto/qml/qqmlqt/data/timeRoundtrip.qml b/tests/auto/qml/qqmlqt/data/timeRoundtrip.qml
new file mode 100644
index 0000000000..9d73640c87
--- /dev/null
+++ b/tests/auto/qml/qqmlqt/data/timeRoundtrip.qml
@@ -0,0 +1,8 @@
+import QtQuick 2.0
+
+QtObject {
+ Component.onCompleted: {
+ var t = tp.time;
+ tp.time = t;
+ }
+}
diff --git a/tests/auto/qml/qqmlqt/tst_qqmlqt.cpp b/tests/auto/qml/qqmlqt/tst_qqmlqt.cpp
index 2f44c34bc2..0576650d01 100644
--- a/tests/auto/qml/qqmlqt/tst_qqmlqt.cpp
+++ b/tests/auto/qml/qqmlqt/tst_qqmlqt.cpp
@@ -46,6 +46,15 @@
#include <QFont>
#include "../../shared/util.h"
+// Copied from tst_qdatetime.cpp
+#ifdef Q_OS_WIN
+# include <qt_windows.h>
+# include <time.h>
+# if defined(Q_OS_WINRT)
+# define tzset()
+# endif
+#endif
+
class tst_qqmlqt : public QQmlDataTest
{
Q_OBJECT
@@ -53,6 +62,7 @@ public:
tst_qqmlqt() {}
private slots:
+ void initTestCase();
void enums();
void rgba();
void hsla();
@@ -86,13 +96,74 @@ private slots:
void atob();
void fontFamilies();
void quit();
+ void exit();
void resolvedUrl();
+ void later_data();
+ void later();
void qtObjectContents();
+ void timeRoundtrip_data();
+ void timeRoundtrip();
+
private:
QQmlEngine engine;
};
+// for callLater()
+class TestElement : public QQuickItem
+{
+ Q_OBJECT
+public:
+ TestElement() : m_intptr(new int) {}
+ ~TestElement() { delete m_intptr; }
+
+ Q_INVOKABLE void dangerousFunction() {
+ delete m_intptr;
+ m_intptr = new int;
+ *m_intptr = 5;
+ }
+private:
+ int *m_intptr;
+};
+
+// for callLater()
+class TestModuleApi : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(int intProp READ intProp WRITE setIntProp NOTIFY intPropChanged)
+
+public:
+ TestModuleApi() : m_int(0) {}
+ ~TestModuleApi() {}
+
+ int intProp() const { return m_int; }
+ void setIntProp(int v) { m_int = v; emit intPropChanged(); }
+
+ Q_INVOKABLE void testFunc() { ++m_int; emit intPropChanged(); }
+ Q_INVOKABLE void resetIntProp() { m_int = 0; emit intPropChanged(); }
+
+signals:
+ void intPropChanged();
+
+private:
+ int m_int;
+};
+
+static QObject *test_module_api_factory(QQmlEngine *engine, QJSEngine *scriptEngine)
+{
+ Q_UNUSED(engine)
+ Q_UNUSED(scriptEngine)
+ TestModuleApi *api = new TestModuleApi;
+ return api;
+}
+
+void tst_qqmlqt::initTestCase()
+{
+ QQmlDataTest::initTestCase();
+ qmlRegisterSingletonType<TestModuleApi>("LaterImports", 1, 0, "SingletonType", test_module_api_factory);
+ qmlRegisterType<TestElement>("LaterImports", 1, 0, "TestElement");
+}
+
void tst_qqmlqt::enums()
{
QQmlComponent component(&engine, testFileUrl("enums.qml"));
@@ -814,8 +885,6 @@ void tst_qqmlqt::dateTimeFormattingVariants_data()
QTime time(11, 16, 39, 755);
temporary = QDateTime(QDate(1970,1,1), time);
- QTest::newRow("formatDate, qtime") << "formatDate" << QVariant::fromValue(time) << (QStringList() << temporary.date().toString(Qt::DefaultLocaleShortDate) << temporary.date().toString(Qt::DefaultLocaleLongDate) << temporary.date().toString("ddd MMMM d yy"));
- QTest::newRow("formatDateTime, qtime") << "formatDateTime" << QVariant::fromValue(time) << (QStringList() << temporary.toString(Qt::DefaultLocaleShortDate) << temporary.toString(Qt::DefaultLocaleLongDate) << temporary.toString("M/d/yy H:m:s a"));
QTest::newRow("formatTime, qtime") << "formatTime" << QVariant::fromValue(time) << (QStringList() << temporary.time().toString(Qt::DefaultLocaleShortDate) << temporary.time().toString(Qt::DefaultLocaleLongDate) << temporary.time().toString("H:m:s a") << temporary.time().toString("hh:mm:ss.zzz"));
QDate date(2011,5,31);
@@ -922,6 +991,20 @@ void tst_qqmlqt::quit()
delete object;
}
+void tst_qqmlqt::exit()
+{
+ QQmlComponent component(&engine, testFileUrl("exit.qml"));
+
+ QSignalSpy spy(&engine, &QQmlEngine::exit);
+ QObject *object = component.create();
+ QVERIFY(object != Q_NULLPTR);
+ QCOMPARE(spy.count(), 1);
+ QList<QVariant> arguments = spy.takeFirst();
+ QVERIFY(arguments.at(0).toInt() == object->property("returnCode").toInt());
+
+ delete object;
+}
+
void tst_qqmlqt::resolvedUrl()
{
QQmlComponent component(&engine, testFileUrl("resolvedUrl.qml"));
@@ -935,6 +1018,107 @@ void tst_qqmlqt::resolvedUrl()
delete object;
}
+void tst_qqmlqt::later_data()
+{
+ QTest::addColumn<QString>("function");
+ QTest::addColumn<QStringList>("expectedWarnings");
+ QTest::addColumn<QStringList>("propNames");
+ QTest::addColumn<QVariantList>("values");
+
+ QVariant vtrue = QVariant(true);
+
+ QTest::newRow("callLater from onCompleted")
+ << QString()
+ << QStringList()
+ << (QStringList() << "test1_1" << "test2_1" << "processEvents" << "test1_2" << "test2_2")
+ << (QVariantList() << vtrue << vtrue << QVariant() << vtrue << vtrue);
+
+ QTest::newRow("trigger Qt.callLater() via repeater")
+ << QString(QLatin1String("test2"))
+ << QStringList()
+ << (QStringList() << "processEvents" << "test2_2")
+ << (QVariantList() << QVariant() << vtrue);
+
+ QTest::newRow("recursive Qt.callLater()")
+ << QString(QLatin1String("test3"))
+ << QStringList()
+ << (QStringList() << "processEvents" << "test3_1" << "processEvents" << "test3_2" << "processEvents" << "test3_3")
+ << (QVariantList() << QVariant() << vtrue << QVariant() << vtrue << QVariant() << vtrue);
+
+ QTest::newRow("nonexistent function")
+ << QString(QLatin1String("test4"))
+ << (QStringList() << QString(testFileUrl("later.qml").toString() + QLatin1String(":70: ReferenceError: functionThatDoesNotExist is not defined")))
+ << QStringList()
+ << QVariantList();
+
+ QTest::newRow("callLater with different args")
+ << QString(QLatin1String("test5"))
+ << QStringList()
+ << (QStringList() << "processEvents" << "test5_1")
+ << (QVariantList() << QVariant() << vtrue);
+
+ QTest::newRow("delayed call ordering")
+ << QString(QLatin1String("test6"))
+ << QStringList()
+ << (QStringList() << "processEvents" << "test6_1")
+ << (QVariantList() << QVariant() << vtrue);
+
+ QTest::newRow("invoke module api invokable")
+ << QString(QLatin1String("test9"))
+ << QStringList()
+ << (QStringList() << "processEvents" << "test9_1" << "processEvents")
+ << (QVariantList() << QVariant() << QVariant(1) << QVariant());
+
+ QTest::newRow("invoke function of deleted QObject via callLater() causing deletion")
+ << QString(QLatin1String("test10"))
+ << (QStringList() << QString(testFileUrl("LaterComponent.qml").toString() + QLatin1String(":8: ReferenceError: dangerousFunction is not defined (exception occurred during delayed function evaluation)")))
+ << (QStringList() << "processEvents" << "test10_1" << "processEvents")
+ << (QVariantList() << QVariant() << QVariant(0) << QVariant());
+
+ QTest::newRow("invoke function of deleted QObject via callLater() after deletion")
+ << QString(QLatin1String("test11"))
+ << QStringList()
+ << (QStringList() << "collectGarbage" << "processEvents" << "test11_1" << "processEvents")
+ << (QVariantList() << QVariant() << QVariant() << QVariant(1) << QVariant());
+
+ QTest::newRow("invoke function which has no script origin")
+ << QString(QLatin1String("test14"))
+ << QStringList()
+ << (QStringList() << "collectGarbage")
+ << (QVariantList() << QVariant());
+}
+
+void tst_qqmlqt::later()
+{
+ QFETCH(QString, function);
+ QFETCH(QStringList, expectedWarnings);
+ QFETCH(QStringList, propNames);
+ QFETCH(QVariantList, values);
+
+ foreach (const QString &w, expectedWarnings)
+ QTest::ignoreMessage(QtWarningMsg, qPrintable(w));
+
+ QQmlComponent component(&engine, testFileUrl("later.qml"));
+ QObject *root = component.create();
+ QVERIFY(root != 0);
+
+ if (!function.isEmpty())
+ QMetaObject::invokeMethod(root, qPrintable(function));
+
+ for (int i = 0; i < propNames.size(); ++i) {
+ if (propNames.at(i) == QLatin1String("processEvents")) {
+ QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
+ QCoreApplication::processEvents();
+ } else if (propNames.at(i) == QLatin1String("collectGarbage")) {
+ engine.collectGarbage();
+ } else {
+ QCOMPARE(root->property(qPrintable(propNames.at(i))), values.at(i));
+ }
+ }
+
+ delete root;
+}
+
void tst_qqmlqt::qtObjectContents()
{
struct StaticQtMetaObject : public QObject
@@ -980,6 +1164,104 @@ void tst_qqmlqt::qtObjectContents()
delete object;
}
+class TimeProvider: public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QTime time READ time WRITE setTime NOTIFY timeChanged)
+
+public:
+ TimeProvider(const QTime &t)
+ : m_getTime(t)
+ {}
+
+ QTime time() const { return m_getTime; }
+ void setTime(const QTime &t) { m_putTime = t; emit timeChanged(); }
+
+signals:
+ void timeChanged();
+
+public:
+ QTime m_getTime, m_putTime;
+};
+
+class TimeZoneSwitch
+{
+public:
+ TimeZoneSwitch(const char *newZone)
+ : doChangeZone(qstrcmp(newZone, "localtime") == 0)
+ {
+ if (!doChangeZone)
+ return;
+
+ hadOldZone = qEnvironmentVariableIsSet("TZ");
+ if (hadOldZone) {
+ oldZone = qgetenv("TZ");
+ }
+ qputenv("TZ", newZone);
+ tzset();
+ }
+
+ ~TimeZoneSwitch()
+ {
+ if (!doChangeZone)
+ return;
+
+ if (hadOldZone)
+ qputenv("TZ", oldZone);
+ else
+ qunsetenv("TZ");
+ tzset();
+ }
+
+private:
+ bool doChangeZone;
+ bool hadOldZone;
+ QByteArray oldZone;
+};
+
+void tst_qqmlqt::timeRoundtrip_data()
+{
+ QTest::addColumn<QTime>("time");
+
+ // Local timezone:
+ QTest::newRow("localtime") << QTime(0, 0, 0);
+
+ // No DST:
+ QTest::newRow("UTC") << QTime(0, 0, 0);
+ QTest::newRow("Europe/Amsterdam") << QTime(1, 0, 0);
+ QTest::newRow("Asia/Jakarta") << QTime(7, 0, 0);
+
+ // DST:
+ QTest::newRow("Namibia/Windhoek") << QTime(1, 0, 0);
+ QTest::newRow("Australia/Adelaide") << QTime(10, 0, 0);
+ QTest::newRow("Australia/Hobart") << QTime(10, 0, 0);
+ QTest::newRow("Pacific/Auckland") << QTime(12, 0, 0);
+ QTest::newRow("Pacific/Samoa") << QTime(13, 0, 0);
+}
+
+void tst_qqmlqt::timeRoundtrip()
+{
+#ifdef Q_OS_WIN
+ QSKIP("On Windows, the DateObject doesn't handle DST transitions correctly when the timezone is not localtime."); // I.e.: for this test.
+#endif
+
+ TimeZoneSwitch tzs(QTest::currentDataTag());
+ QFETCH(QTime, time);
+
+ TimeProvider tp(time);
+
+ QQmlEngine eng;
+ eng.rootContext()->setContextProperty(QLatin1String("tp"), &tp);
+ QQmlComponent component(&eng, testFileUrl("timeRoundtrip.qml"));
+ QObject *obj = component.create();
+ QVERIFY(obj != 0);
+
+ // QML reads m_getTime and saves the result as m_putTime; this should come out the same, without
+ // any perturbation (e.g. by DST effects) from converting from QTime to V4's Date and back
+ // again.
+ QCOMPARE(tp.m_getTime, tp.m_putTime);
+}
+
QTEST_MAIN(tst_qqmlqt)
#include "tst_qqmlqt.moc"
diff --git a/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp b/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp
index bf255ba6a0..1fc803a395 100644
--- a/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp
+++ b/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp
@@ -31,7 +31,6 @@
#include <QQmlComponent>
#include <QTranslator>
#include <QQmlContext>
-#include <private/qqmlcompiler_p.h>
#include <private/qqmlengine_p.h>
#include "../../shared/util.h"
@@ -77,15 +76,15 @@ void tst_qqmltranslation::translation()
QQmlContext *context = qmlContext(object);
QQmlEnginePrivate *engine = QQmlEnginePrivate::get(context->engine());
QQmlTypeData *typeData = engine->typeLoader.getType(context->baseUrl());
- QQmlCompiledData *cdata = typeData->compiledData();
- QVERIFY(cdata);
+ QV4::CompiledData::CompilationUnit *compilationUnit = typeData->compilationUnit();
+ QVERIFY(compilationUnit);
QSet<QString> compiledTranslations;
compiledTranslations << QStringLiteral("basic")
<< QStringLiteral("disambiguation")
<< QStringLiteral("singular") << QStringLiteral("plural");
- const QV4::CompiledData::Unit *unit = cdata->compilationUnit->data;
+ const QV4::CompiledData::Unit *unit = compilationUnit->data;
const QV4::CompiledData::Object *rootObject = unit->objectAt(unit->indexOfRootObject);
const QV4::CompiledData::Binding *binding = rootObject->bindingTable();
for (quint32 i = 0; i < rootObject->nBindings; ++i, ++binding) {
@@ -96,7 +95,7 @@ void tst_qqmltranslation::translation()
if (expectCompiledTranslation) {
if (binding->type != QV4::CompiledData::Binding::Type_Translation)
qDebug() << "binding for property" << propertyName << "is not a compiled translation";
- QCOMPARE(binding->type, quint32(QV4::CompiledData::Binding::Type_Translation));
+ QCOMPARE(quint32(binding->type), quint32(QV4::CompiledData::Binding::Type_Translation));
} else {
if (binding->type == QV4::CompiledData::Binding::Type_Translation)
qDebug() << "binding for property" << propertyName << "is not supposed to be a compiled translation";
@@ -137,10 +136,10 @@ void tst_qqmltranslation::idTranslation()
QQmlContext *context = qmlContext(object);
QQmlEnginePrivate *engine = QQmlEnginePrivate::get(context->engine());
QQmlTypeData *typeData = engine->typeLoader.getType(context->baseUrl());
- QQmlCompiledData *cdata = typeData->compiledData();
- QVERIFY(cdata);
+ QV4::CompiledData::CompilationUnit *compilationUnit = typeData->compilationUnit();
+ QVERIFY(compilationUnit);
- const QV4::CompiledData::Unit *unit = cdata->compilationUnit->data;
+ const QV4::CompiledData::Unit *unit = compilationUnit->data;
const QV4::CompiledData::Object *rootObject = unit->objectAt(unit->indexOfRootObject);
const QV4::CompiledData::Binding *binding = rootObject->bindingTable();
for (quint32 i = 0; i < rootObject->nBindings; ++i, ++binding) {
@@ -148,7 +147,7 @@ void tst_qqmltranslation::idTranslation()
if (propertyName == "idTranslation") {
if (binding->type != QV4::CompiledData::Binding::Type_TranslationById)
qDebug() << "binding for property" << propertyName << "is not a compiled translation";
- QCOMPARE(binding->type, quint32(QV4::CompiledData::Binding::Type_TranslationById));
+ QCOMPARE(quint32(binding->type), quint32(QV4::CompiledData::Binding::Type_TranslationById));
} else {
QVERIFY(binding->type != QV4::CompiledData::Binding::Type_Translation);
}
diff --git a/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp b/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp
index 36350e2d08..3d3a7ff725 100644
--- a/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp
+++ b/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp
@@ -32,7 +32,6 @@
#include <QtQuick/qquickitem.h>
#include <QtQml/private/qqmlengine_p.h>
#include <QtQml/private/qqmltypeloader_p.h>
-#include <QtQml/private/qqmlcompiler_p.h>
#include "../../shared/util.h"
class tst_QQMLTypeLoader : public QQmlDataTest
@@ -90,7 +89,7 @@ void tst_QQMLTypeLoader::trimCache()
if (i % 10 == 0) {
// keep ref on data, don't add ref on data->compiledData()
} else if (i % 5 == 0) {
- data->compiledData()->addref();
+ data->compilationUnit()->addref();
data->release();
} else {
data->release();
diff --git a/tests/auto/qml/qqmlvaluetypes/data/color_read.qml b/tests/auto/qml/qqmlvaluetypes/data/color_read.qml
index bc92b1e5f9..73d2b921a7 100644
--- a/tests/auto/qml/qqmlvaluetypes/data/color_read.qml
+++ b/tests/auto/qml/qqmlvaluetypes/data/color_read.qml
@@ -5,5 +5,11 @@ MyTypeObject {
property real v_g: color.g
property real v_b: color.b
property real v_a: color.a
+ property real hsv_h: color.hsvHue
+ property real hsv_s: color.hsvSaturation
+ property real hsv_v: color.hsvValue
+ property real hsl_h: color.hslHue
+ property real hsl_s: color.hslSaturation
+ property real hsl_l: color.hslLightness
property variant copy: color
}
diff --git a/tests/auto/qml/qqmlvaluetypes/data/color_write_HSL.qml b/tests/auto/qml/qqmlvaluetypes/data/color_write_HSL.qml
new file mode 100644
index 0000000000..0034163bbe
--- /dev/null
+++ b/tests/auto/qml/qqmlvaluetypes/data/color_write_HSL.qml
@@ -0,0 +1,8 @@
+import Test 1.0
+
+MyTypeObject {
+ color.hslHue: if (true) 0.43
+ color.hslSaturation: if (true) 0.74
+ color.hslLightness: if (true) 0.54
+ color.a: if (true) 0.7
+}
diff --git a/tests/auto/qml/qqmlvaluetypes/data/color_write_HSV.qml b/tests/auto/qml/qqmlvaluetypes/data/color_write_HSV.qml
new file mode 100644
index 0000000000..1fc47d460e
--- /dev/null
+++ b/tests/auto/qml/qqmlvaluetypes/data/color_write_HSV.qml
@@ -0,0 +1,8 @@
+import Test 1.0
+
+MyTypeObject {
+ color.hsvHue: if (true) 0.43
+ color.hsvSaturation: if (true) 0.77
+ color.hsvValue: if (true) 0.88
+ color.a: if (true) 0.7
+}
diff --git a/tests/auto/qml/qqmlvaluetypes/testtypes.h b/tests/auto/qml/qqmlvaluetypes/testtypes.h
index 77d723fbd4..bcfe4028c6 100644
--- a/tests/auto/qml/qqmlvaluetypes/testtypes.h
+++ b/tests/auto/qml/qqmlvaluetypes/testtypes.h
@@ -194,7 +194,7 @@ class MyOffsetValueInterceptor : public QObject, public QQmlPropertyValueInterce
Q_INTERFACES(QQmlPropertyValueInterceptor)
public:
virtual void setTarget(const QQmlProperty &p) { prop = p; }
- virtual void write(const QVariant &value) { QQmlPropertyPrivate::write(prop, value.toInt() + 13, QQmlPropertyPrivate::BypassInterceptor); }
+ virtual void write(const QVariant &value) { QQmlPropertyPrivate::write(prop, value.toInt() + 13, QQmlPropertyData::BypassInterceptor); }
private:
QQmlProperty prop;
@@ -215,7 +215,7 @@ public:
c.getRgb(&r, &g, &b, &a);
c.setRgb(a, b, g, r);
- QQmlPropertyPrivate::write(prop, c, QQmlPropertyPrivate::BypassInterceptor);
+ QQmlPropertyPrivate::write(prop, c, QQmlPropertyData::BypassInterceptor);
}
private:
@@ -230,7 +230,7 @@ public:
virtual void setTarget(const QQmlProperty &p) { prop = p; }
virtual void write(const QVariant &)
{
- QQmlPropertyPrivate::write(prop, 0.0f, QQmlPropertyPrivate::BypassInterceptor);
+ QQmlPropertyPrivate::write(prop, 0.0f, QQmlPropertyData::BypassInterceptor);
}
private:
diff --git a/tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp b/tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp
index 300f5b90e5..803bad197a 100644
--- a/tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp
+++ b/tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp
@@ -30,6 +30,7 @@
#include <QQmlEngine>
#include <QQmlComponent>
#include <QDebug>
+#include <QJSValueIterator>
#include <private/qquickvaluetypes_p.h>
#include <private/qqmlglobal_p.h>
#include "../../shared/util.h"
@@ -89,6 +90,7 @@ private slots:
void customValueTypeInQml();
void gadgetInheritance();
void toStringConversion();
+ void enumerableProperties();
private:
QQmlEngine engine;
@@ -905,6 +907,15 @@ void tst_qqmlvaluetypes::color()
QCOMPARE((float)object->property("v_g").toDouble(), (float)0.88);
QCOMPARE((float)object->property("v_b").toDouble(), (float)0.6);
QCOMPARE((float)object->property("v_a").toDouble(), (float)0.34);
+
+ QCOMPARE(qRound(object->property("hsv_h").toDouble() * 100), 43);
+ QCOMPARE(qRound(object->property("hsv_s").toDouble() * 100), 77);
+ QCOMPARE(qRound(object->property("hsv_v").toDouble() * 100), 88);
+
+ QCOMPARE(qRound(object->property("hsl_h").toDouble() * 100), 43);
+ QCOMPARE(qRound(object->property("hsl_s").toDouble() * 100), 74);
+ QCOMPARE(qRound(object->property("hsl_l").toDouble() * 100), 54);
+
QColor comparison;
comparison.setRedF(0.2);
comparison.setGreenF(0.88);
@@ -931,6 +942,30 @@ void tst_qqmlvaluetypes::color()
}
{
+ QQmlComponent component(&engine, testFileUrl("color_write_HSV.qml"));
+ MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
+ QVERIFY(object != 0);
+
+ QColor newColor;
+ newColor.setHsvF(0.43, 0.77, 0.88, 0.7);
+ QCOMPARE(object->color(), newColor);
+
+ delete object;
+ }
+
+ {
+ QQmlComponent component(&engine, testFileUrl("color_write_HSL.qml"));
+ MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
+ QVERIFY(object != 0);
+
+ QColor newColor;
+ newColor.setHslF(0.43, 0.74, 0.54, 0.7);
+ QCOMPARE(object->color(), newColor);
+
+ delete object;
+ }
+
+ {
QQmlComponent component(&engine, testFileUrl("color_compare.qml"));
MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
QVERIFY(object != 0);
@@ -1649,6 +1684,25 @@ void tst_qqmlvaluetypes::toStringConversion()
QCOMPARE(stringConversion.toString(), StringLessGadget_to_QString(g));
}
+void tst_qqmlvaluetypes::enumerableProperties()
+{
+ QJSEngine engine;
+ DerivedGadget g;
+ QJSValue value = engine.toScriptValue(g);
+ QSet<QString> names;
+ QJSValueIterator it(value);
+ while (it.hasNext()) {
+ it.next();
+ const QString name = it.name();
+ QVERIFY(!names.contains(name));
+ names.insert(name);
+ }
+
+ QCOMPARE(names.count(), 2);
+ QVERIFY(names.contains(QStringLiteral("baseProperty")));
+ QVERIFY(names.contains(QStringLiteral("derivedProperty")));
+}
+
QTEST_MAIN(tst_qqmlvaluetypes)
diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/send_patch.expect b/tests/auto/qml/qqmlxmlhttprequest/data/send_patch.expect
new file mode 100644
index 0000000000..8c13977462
--- /dev/null
+++ b/tests/auto/qml/qqmlxmlhttprequest/data/send_patch.expect
@@ -0,0 +1,17 @@
+PATCH /qqmlxmlhttprequest.cpp HTTP/1.1
+Accept-Language: en-US
+If-Match: "ETagNumber"
+Content-Type: application/example
+Content-Length: 247
+Connection: Keep-Alive
+Accept-Encoding: gzip, deflate
+User-Agent: Mozilla/5.0
+Host: {{ServerHostUrl}}
+
+--- a/qqmlxmlhttprequest.cpp
++++ b/qqmlxmlhttprequest.cpp
+@@ -1238,11 +1238,13 @@
+- } else if (m_method == QLatin1String("OPTIONS")) {
++ } else if (m_method == QLatin1String("OPTIONS") ||
++ (m_method == QLatin1String("PATCH"))) {
+
diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/send_patch.qml b/tests/auto/qml/qqmlxmlhttprequest/data/send_patch.qml
new file mode 100644
index 0000000000..2abf1c60a8
--- /dev/null
+++ b/tests/auto/qml/qqmlxmlhttprequest/data/send_patch.qml
@@ -0,0 +1,68 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 Canonical Limited and/or its subsidiary(-ies).
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** 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 Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.0
+
+QtObject {
+ property string url
+
+ property bool dataOK: false
+ property bool headerOK: false
+
+ Component.onCompleted: {
+ var x = new XMLHttpRequest;
+ x.open("PATCH", url);
+ x.setRequestHeader("Accept-Language","en-US");
+ x.setRequestHeader("If-Match","\"ETagNumber\"");
+
+ // Test to the end
+ x.onreadystatechange = function() {
+ if (x.readyState == XMLHttpRequest.HEADERS_RECEIVED) {
+ headerOK = (x.getResponseHeader("Content-Location") == "/qqmlxmlhttprequest.cpp") &&
+ (x.getResponseHeader("ETag") == "\"ETagNumber\"") &&
+ (x.status == "204");
+ } else if (x.readyState == XMLHttpRequest.DONE) {
+ dataOK = (x.responseText === "");
+ }
+ }
+
+ var body = "--- a/qqmlxmlhttprequest.cpp\n" +
+ "+++ b/qqmlxmlhttprequest.cpp\n" +
+ "@@ -1238,11 +1238,13 @@\n" +
+ "- } else if (m_method == QLatin1String(\"OPTIONS\")) {\n" +
+ "+ } else if (m_method == QLatin1String(\"OPTIONS\") ||\n" +
+ "+ (m_method == QLatin1String(\"PATCH\"))) {\n"
+
+ x.send(body);
+ }
+}
diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/send_patch.reply b/tests/auto/qml/qqmlxmlhttprequest/data/send_patch.reply
new file mode 100644
index 0000000000..cece41ced1
--- /dev/null
+++ b/tests/auto/qml/qqmlxmlhttprequest/data/send_patch.reply
@@ -0,0 +1,3 @@
+HTTP/1.1 204 No Content
+Content-Location: /qqmlxmlhttprequest.cpp
+ETag: "ETagNumber"
diff --git a/tests/auto/qml/qqmlxmlhttprequest/tst_qqmlxmlhttprequest.cpp b/tests/auto/qml/qqmlxmlhttprequest/tst_qqmlxmlhttprequest.cpp
index 425e12677f..1ce07ecdab 100644
--- a/tests/auto/qml/qqmlxmlhttprequest/tst_qqmlxmlhttprequest.cpp
+++ b/tests/auto/qml/qqmlxmlhttprequest/tst_qqmlxmlhttprequest.cpp
@@ -71,6 +71,7 @@ private slots:
void send_withdata_data();
void send_options();
void send_options_data();
+ void send_patch();
void abort();
void abort_unsent();
void abort_opened();
@@ -639,6 +640,26 @@ void tst_qqmlxmlhttprequest::send_options_data()
QTest::newRow("OPTIONS (with data)") << "testdocument.html" << "send_data.10.expect" << "send_data.9.qml" << "send_data.2.reply";
}
+void tst_qqmlxmlhttprequest::send_patch()
+{
+ TestHTTPServer server;
+ QVERIFY2(server.listen(), qPrintable(server.errorString()));
+ QVERIFY(server.wait(testFileUrl("send_patch.expect"),
+ testFileUrl("send_patch.reply"),
+ // the content of response file will be ignored due to 204 status code
+ testFileUrl("testdocument.html")));
+
+ QQmlComponent component(&engine, testFileUrl("send_patch.qml"));
+ QScopedPointer<QObject> object(component.beginCreate(engine.rootContext()));
+ QVERIFY(!object.isNull());
+ object->setProperty("url", server.urlString("/qqmlxmlhttprequest.cpp"));
+ component.completeCreate();
+
+ QTRY_VERIFY(object->property("dataOK").toBool());
+ QTRY_VERIFY(object->property("headerOK").toBool());
+}
+
+
// Test abort() has no effect in unsent state
void tst_qqmlxmlhttprequest::abort_unsent()
{
diff --git a/tests/auto/qml/qquickfolderlistmodel/tst_qquickfolderlistmodel.cpp b/tests/auto/qml/qquickfolderlistmodel/tst_qquickfolderlistmodel.cpp
index f3ba3e8971..f19e82032a 100644
--- a/tests/auto/qml/qquickfolderlistmodel/tst_qquickfolderlistmodel.cpp
+++ b/tests/auto/qml/qquickfolderlistmodel/tst_qquickfolderlistmodel.cpp
@@ -56,6 +56,7 @@ public slots:
}
private slots:
+ void initTestCase();
void basicProperties();
void showFiles();
void resetFiltering();
@@ -97,6 +98,15 @@ void tst_qquickfolderlistmodel::checkNoErrors(const QQmlComponent& component)
QVERIFY(!component.isError());
}
+void tst_qquickfolderlistmodel::initTestCase()
+{
+ // The tests rely on a fixed number of files in the directory with the qml files
+ // (the data dir), so disable the disk cache to avoid creating .qmlc files and
+ // confusing the test.
+ qputenv("QML_DISABLE_DISK_CACHE", "1");
+ QQmlDataTest::initTestCase();
+}
+
void tst_qquickfolderlistmodel::basicProperties()
{
QQmlComponent component(&engine, testFileUrl("basic.qml"));
diff --git a/tests/auto/qml/qv4mm/qv4mm.pro b/tests/auto/qml/qv4mm/qv4mm.pro
new file mode 100644
index 0000000000..d9b749af4a
--- /dev/null
+++ b/tests/auto/qml/qv4mm/qv4mm.pro
@@ -0,0 +1,8 @@
+CONFIG += testcase
+TARGET = tst_qv4mm
+osx:CONFIG -= app_bundle
+
+SOURCES += tst_qv4mm.cpp
+
+QT += qml qml-private testlib
+
diff --git a/tests/auto/qml/qv4mm/tst_qv4mm.cpp b/tests/auto/qml/qv4mm/tst_qv4mm.cpp
new file mode 100644
index 0000000000..d4ba363d00
--- /dev/null
+++ b/tests/auto/qml/qv4mm/tst_qv4mm.cpp
@@ -0,0 +1,58 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 basysKom GmbH.
+** 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 <qtest.h>
+#include <QQmlEngine>
+#include <private/qv4mm_p.h>
+
+class tst_qv4mm : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void gcStats();
+ void tweaks();
+};
+
+void tst_qv4mm::gcStats()
+{
+ qputenv(QV4_MM_STATS, "1");
+ QQmlEngine engine;
+ engine.collectGarbage();
+}
+
+void tst_qv4mm::tweaks()
+{
+ qputenv(QV4_MM_MAXBLOCK_SHIFT, "5");
+ qputenv(QV4_MM_MAX_CHUNK_SIZE, "65536");
+ QQmlEngine engine;
+}
+
+QTEST_MAIN(tst_qv4mm)
+
+#include "tst_qv4mm.moc"
diff --git a/tests/auto/qmldevtools/compile/compile.pro b/tests/auto/qmldevtools/compile/compile.pro
index 54430eb668..832700698f 100644
--- a/tests/auto/qmldevtools/compile/compile.pro
+++ b/tests/auto/qmldevtools/compile/compile.pro
@@ -5,7 +5,7 @@ force_bootstrap {
!build_pass: CONFIG += release
} else {
QT = core
- !build_pass:contains(QT_CONFIG, debug_and_release): CONFIG += release
+ !build_pass:qtConfig(debug_and_release): CONFIG += release
}
QT += qmldevtools-private
macx:CONFIG -= app_bundle
diff --git a/tests/auto/qmldevtools/qmldevtools.pro b/tests/auto/qmldevtools/qmldevtools.pro
index a0ca1bff87..a9352d4df3 100644
--- a/tests/auto/qmldevtools/qmldevtools.pro
+++ b/tests/auto/qmldevtools/qmldevtools.pro
@@ -1,6 +1,4 @@
TEMPLATE = subdirs
-contains(QT_CONFIG, private_tests) {
- SUBDIRS += \
- compile
-}
+qtConfig(private_tests): \
+ SUBDIRS += compile
diff --git a/tests/auto/qmltest/selftests/tst_tryVerify.qml b/tests/auto/qmltest/selftests/tst_tryVerify.qml
new file mode 100644
index 0000000000..6f29d8643d
--- /dev/null
+++ b/tests/auto/qmltest/selftests/tst_tryVerify.qml
@@ -0,0 +1,72 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+import QtQuick 2.8
+import QtTest 1.1
+
+TestCase {
+ name: "tst_tryVerify"
+
+ Item {
+ id: item
+ }
+
+ QtObject {
+ id: itemContainer
+ property Item i
+ }
+
+ Timer {
+ id: timer
+ interval: 100
+ onTriggered: itemContainer.i = item
+ }
+
+ function resetTimer() {
+ itemContainer.i = null;
+ timer.restart();
+ }
+
+ function test_tryVerify() {
+ timer.start();
+ tryVerify(function(){ return itemContainer.i; }, 200, "string");
+ compare(itemContainer.i, item);
+
+ resetTimer();
+ tryVerify(function(){ return itemContainer.i; }, 200);
+ compare(itemContainer.i, item);
+
+ resetTimer();
+ tryVerify(function(){ return itemContainer.i; });
+ compare(itemContainer.i, item);
+
+ resetTimer();
+ tryVerify(function(){ return !itemContainer.i; }, 0, "string");
+ verify(!itemContainer.i);
+ }
+}
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..904f85c4c6 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::FloatType, false),
+ QSGGeometry::Attribute::create(1, 4, QSGGeometry::UnsignedByteType, false),
+ QSGGeometry::Attribute::create(2, 4, QSGGeometry::FloatType, 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..6c94b484ae 100644
--- a/tests/auto/quick/nokeywords/tst_nokeywords.cpp
+++ b/tests/auto/quick/nokeywords/tst_nokeywords.cpp
@@ -48,13 +48,15 @@
#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>
-#include <QtQuick/private/qsgdefaultrectanglenode_p.h>
+#include <QtQuick/private/qsgdefaultinternalimagenode_p.h>
+#include <QtQuick/private/qsgdefaultinternalrectanglenode_p.h>
#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/qquickaccessible/qquickaccessible.pro b/tests/auto/quick/qquickaccessible/qquickaccessible.pro
index 02915e8e22..537aad882f 100644
--- a/tests/auto/quick/qquickaccessible/qquickaccessible.pro
+++ b/tests/auto/quick/qquickaccessible/qquickaccessible.pro
@@ -15,10 +15,3 @@ OTHER_FILES += data/checkbuttons.qml \
data/pushbutton.qml \
data/statictext.qml \
data/ignored.qml \
-
-wince*: {
- accessneeded.files = $$QT.widgets.plugins/accessible/*.dll
- accessneeded.path = accessible
- DEPLOYMENT += accessneeded
-}
-
diff --git a/tests/auto/quick/qquickanimatedimage/tst_qquickanimatedimage.cpp b/tests/auto/quick/qquickanimatedimage/tst_qquickanimatedimage.cpp
index b34f58f7c4..34b9fb6b07 100644
--- a/tests/auto/quick/qquickanimatedimage/tst_qquickanimatedimage.cpp
+++ b/tests/auto/quick/qquickanimatedimage/tst_qquickanimatedimage.cpp
@@ -179,7 +179,11 @@ void tst_qquickanimatedimage::mirror_running()
QImage frame0_expected = frame0.transformed(transform);
QImage frame1_expected = frame1.transformed(transform);
+ if (window.devicePixelRatio() != 1.0 && window.rendererInterface()->graphicsApi() == QSGRendererInterface::Software)
+ QSKIP("QTBUG-53823");
QCOMPARE(frame0_flipped, frame0_expected);
+ if (window.devicePixelRatio() != 1.0 && window.rendererInterface()->graphicsApi() == QSGRendererInterface::Software)
+ QSKIP("QTBUG-53823");
QCOMPARE(frame1_flipped, frame1_expected);
delete anim;
@@ -212,6 +216,8 @@ void tst_qquickanimatedimage::mirror_notRunning()
screenshot = window.grabWindow();
screenshot.save("screen.png");
+ if (window.devicePixelRatio() != 1.0 && window.rendererInterface()->graphicsApi() == QSGRendererInterface::Software)
+ QSKIP("QTBUG-53823");
QCOMPARE(screenshot, expected);
// mirroring should not change the current frame or playing status
diff --git a/tests/auto/quick/qquickapplication/tst_qquickapplication.cpp b/tests/auto/quick/qquickapplication/tst_qquickapplication.cpp
index 1bd163fc4a..114f906736 100644
--- a/tests/auto/quick/qquickapplication/tst_qquickapplication.cpp
+++ b/tests/auto/quick/qquickapplication/tst_qquickapplication.cpp
@@ -47,6 +47,7 @@ private slots:
void active();
void state();
void layoutDirection();
+ void font();
void inputMethod();
void styleHints();
void cleanup();
@@ -196,6 +197,21 @@ void tst_qquickapplication::layoutDirection()
QCOMPARE(Qt::LayoutDirection(item->property("layoutDirection").toInt()), Qt::LeftToRight);
}
+void tst_qquickapplication::font()
+{
+ QQmlComponent component(&engine);
+ component.setData("import QtQuick 2.0; Item { property font defaultFont: Qt.application.font }", QUrl::fromLocalFile(""));
+ QQuickItem *item = qobject_cast<QQuickItem *>(component.create());
+ QVERIFY(item);
+ QQuickView view;
+ item->setParentItem(view.rootObject());
+
+ QVariant defaultFontProperty = item->property("defaultFont");
+ QVERIFY(defaultFontProperty.isValid());
+ QCOMPARE(defaultFontProperty.type(), QVariant::Font);
+ QCOMPARE(defaultFontProperty.value<QFont>(), qApp->font());
+}
+
void tst_qquickapplication::inputMethod()
{
// technically not in QQuickApplication, but testing anyway here
diff --git a/tests/auto/quick/qquickborderimage/data/mesh.qml b/tests/auto/quick/qquickborderimage/data/mesh.qml
new file mode 100644
index 0000000000..203bf25867
--- /dev/null
+++ b/tests/auto/quick/qquickborderimage/data/mesh.qml
@@ -0,0 +1,22 @@
+import QtQuick 2.8
+
+Item {
+ width: 300
+ height: 300
+ Image {
+ id: image
+ source: "colors.png"
+ visible: false
+ }
+ ShaderEffect {
+ anchors.fill: parent
+ property variant source: image
+ mesh: BorderImageMesh {
+ border.left: 30
+ border.right: 30
+ border.top: 30
+ border.bottom: 30
+ size: image.sourceSize
+ }
+ }
+}
diff --git a/tests/auto/quick/qquickborderimage/data/nonmesh.qml b/tests/auto/quick/qquickborderimage/data/nonmesh.qml
new file mode 100644
index 0000000000..7a1830a942
--- /dev/null
+++ b/tests/auto/quick/qquickborderimage/data/nonmesh.qml
@@ -0,0 +1,11 @@
+import QtQuick 2.8
+
+BorderImage {
+ width: 300
+ height: 300
+ source: "colors.png"
+ border.left: 30
+ border.right: 30
+ border.top: 30
+ border.bottom: 30
+}
diff --git a/tests/auto/quick/qquickborderimage/qquickborderimage.pro b/tests/auto/quick/qquickborderimage/qquickborderimage.pro
index 3e16063559..ba6c01737a 100644
--- a/tests/auto/quick/qquickborderimage/qquickborderimage.pro
+++ b/tests/auto/quick/qquickborderimage/qquickborderimage.pro
@@ -7,6 +7,7 @@ SOURCES += tst_qquickborderimage.cpp \
../../shared/testhttpserver.cpp
include (../../shared/util.pri)
+include (../shared/util.pri)
TESTDATA = data/*
diff --git a/tests/auto/quick/qquickborderimage/tst_qquickborderimage.cpp b/tests/auto/quick/qquickborderimage/tst_qquickborderimage.cpp
index c11ae1e8c9..5d242fab9e 100644
--- a/tests/auto/quick/qquickborderimage/tst_qquickborderimage.cpp
+++ b/tests/auto/quick/qquickborderimage/tst_qquickborderimage.cpp
@@ -44,6 +44,7 @@
#include "../../shared/testhttpserver.h"
#include "../../shared/util.h"
+#include "../shared/visualtestutil.h"
Q_DECLARE_METATYPE(QQuickImageBase::Status)
@@ -75,6 +76,9 @@ private slots:
void statusChanges_data();
void sourceSizeChanges();
void progressAndStatusChanges();
+#ifndef QT_NO_OPENGL
+ void borderImageMesh();
+#endif
private:
QQmlEngine engine;
@@ -242,6 +246,11 @@ void tst_qquickborderimage::mirror()
image->setProperty("mirror", true);
screenshot = window->grabWindow();
+
+ window->show();
+ QTest::qWaitForWindowExposed(window);
+ if (window->rendererInterface()->graphicsApi() == QSGRendererInterface::Software)
+ QSKIP("QTBUG-53823");
QCOMPARE(screenshot, srcPixmap);
delete window;
@@ -574,7 +583,22 @@ void tst_qquickborderimage::progressAndStatusChanges()
delete obj;
}
+#ifndef QT_NO_OPENGL
+void tst_qquickborderimage::borderImageMesh()
+{
+ QQuickView *window = new QQuickView;
+ window->setSource(testFileUrl("nonmesh.qml"));
+ window->show();
+ QTest::qWaitForWindowExposed(window);
+ QImage nonmesh = window->grabWindow();
+
+ window->setSource(testFileUrl("mesh.qml"));
+ QImage mesh = window->grabWindow();
+
+ QVERIFY(QQuickVisualTestUtil::compareImages(mesh, nonmesh));
+}
+#endif
QTEST_MAIN(tst_qquickborderimage)
#include "tst_qquickborderimage.moc"
diff --git a/tests/auto/quick/qquickcanvasitem/data/CanvasTestCase.qml b/tests/auto/quick/qquickcanvasitem/data/CanvasTestCase.qml
index e49f0ac462..b0fb7fcf8c 100644
--- a/tests/auto/quick/qquickcanvasitem/data/CanvasTestCase.qml
+++ b/tests/auto/quick/qquickcanvasitem/data/CanvasTestCase.qml
@@ -31,7 +31,9 @@ TestCase {
}
function createCanvasObject(data) {
- return component.createObject(testCase, data.properties);
+ var canvas = component.createObject(testCase, data.properties);
+ waitForRendering(canvas);
+ return canvas;
}
function comparePixel(ctx,x,y,r,g,b,a, d)
diff --git a/tests/auto/quick/qquickcanvasitem/data/tst_canvas.qml b/tests/auto/quick/qquickcanvasitem/data/tst_canvas.qml
index 93f85107a7..565f906fb1 100644
--- a/tests/auto/quick/qquickcanvasitem/data/tst_canvas.qml
+++ b/tests/auto/quick/qquickcanvasitem/data/tst_canvas.qml
@@ -590,6 +590,7 @@ CanvasTestCase {
verify(canvas);
canvas.width = 100;
canvas.height = 100;
+ waitForRendering(canvas);
var ctx = canvas.getContext("2d");
diff --git a/tests/auto/quick/qquickcanvasitem/data/tst_image.qml b/tests/auto/quick/qquickcanvasitem/data/tst_image.qml
index 46a038a13c..1f695d7080 100644
--- a/tests/auto/quick/qquickcanvasitem/data/tst_image.qml
+++ b/tests/auto/quick/qquickcanvasitem/data/tst_image.qml
@@ -667,6 +667,7 @@ CanvasTestCase {
var canvas2 = Qt.createQmlObject("import QtQuick 2.0; Canvas{renderTarget:Canvas.Image; renderStrategy:Canvas.Immediate}", canvas);
canvas2.width = 100;
canvas2.height = 50;
+ waitForRendering(canvas2);
var ctx2 = canvas2.getContext('2d');
ctx2.fillStyle = '#0f0';
ctx2.fillRect(0, 0, 100, 50);
diff --git a/tests/auto/quick/qquickflickable/data/contentXY.qml b/tests/auto/quick/qquickflickable/data/contentXY.qml
new file mode 100644
index 0000000000..8215976949
--- /dev/null
+++ b/tests/auto/quick/qquickflickable/data/contentXY.qml
@@ -0,0 +1,6 @@
+import QtQuick 2.0
+
+Flickable {
+ width: 400; height: 400
+ contentWidth: 1e11; contentHeight: 1e11
+}
diff --git a/tests/auto/quick/qquickflickable/data/keepGrab.qml b/tests/auto/quick/qquickflickable/data/keepGrab.qml
new file mode 100644
index 0000000000..32546658d0
--- /dev/null
+++ b/tests/auto/quick/qquickflickable/data/keepGrab.qml
@@ -0,0 +1,22 @@
+import QtQuick 2.3
+
+Flickable {
+ id: flickable
+ width: 400
+ height: 400
+ contentWidth: 800
+ contentHeight: 800
+
+ Rectangle {
+ x: 200
+ y: 200
+ width: 400
+ height: 400
+ color: "green"
+ MouseArea {
+ id: ma
+ objectName: "ma"
+ anchors.fill: parent
+ }
+ }
+}
diff --git a/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp b/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp
index d0f015324c..b774481592 100644
--- a/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp
+++ b/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp
@@ -93,6 +93,8 @@ private slots:
void cleanup();
void contentSize();
void ratios_smallContent();
+ void contentXYNotTruncatedToInt();
+ void keepGrab();
private:
void flickWithTouch(QQuickWindow *window, QTouchDevice *touchDevice, const QPoint &from, const QPoint &to);
@@ -1223,7 +1225,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()));
@@ -1966,6 +1970,72 @@ void tst_qquickflickable::ratios_smallContent()
QCOMPARE(obj->property("widthRatioIs").toDouble(), 1.);
}
+// QTBUG-48018
+void tst_qquickflickable::contentXYNotTruncatedToInt()
+{
+ QScopedPointer<QQuickView> window(new QQuickView);
+ window->setSource(testFileUrl("contentXY.qml"));
+ QTRY_COMPARE(window->status(), QQuickView::Ready);
+ QQuickViewTestUtil::centerOnScreen(window.data());
+ QQuickViewTestUtil::moveMouseAway(window.data());
+ window->show();
+ QVERIFY(QTest::qWaitForWindowActive(window.data()));
+
+ QQuickFlickable *flickable = qobject_cast<QQuickFlickable*>(window->rootObject());
+ QVERIFY(flickable);
+
+ flickable->setContentX(1e10);
+ flick(window.data(), QPoint(200, 100), QPoint(100, 100), 50);
+
+ // make sure we are not clipped at 2^31
+ QVERIFY(flickable->contentX() > qreal(1e10));
+}
+
+void tst_qquickflickable::keepGrab()
+{
+ QScopedPointer<QQuickView> window(new QQuickView);
+ window->setSource(testFileUrl("keepGrab.qml"));
+ QTRY_COMPARE(window->status(), QQuickView::Ready);
+ QQuickViewTestUtil::centerOnScreen(window.data());
+ QQuickViewTestUtil::moveMouseAway(window.data());
+ window->show();
+ QVERIFY(QTest::qWaitForWindowActive(window.data()));
+
+ QQuickFlickable *flickable = qobject_cast<QQuickFlickable*>(window->rootObject());
+ QVERIFY(flickable);
+
+ QQuickMouseArea *ma = flickable->findChild<QQuickMouseArea*>("ma");
+ QVERIFY(ma);
+ ma->setPreventStealing(true);
+
+ QPoint pos(250, 250);
+ moveAndPress(window.data(), pos);
+ for (int i = 0; i < 6; ++i) {
+ pos += QPoint(10, 10);
+ QTest::mouseMove(window.data(), pos);
+ QTest::qWait(10);
+ }
+ QTest::mouseRelease(window.data(), Qt::LeftButton, 0, QPoint(310, 310));
+ QTest::qWait(10);
+
+ QCOMPARE(flickable->contentX(), 0.0);
+ QCOMPARE(flickable->contentY(), 0.0);
+
+ ma->setPreventStealing(false);
+
+ pos = QPoint(250, 250);
+ moveAndPress(window.data(), pos);
+ for (int i = 0; i < 6; ++i) {
+ pos += QPoint(10, 10);
+ QTest::mouseMove(window.data(), pos);
+ QTest::qWait(10);
+ }
+ QTest::mouseRelease(window.data(), Qt::LeftButton, 0, QPoint(310, 310));
+ QTest::qWait(10);
+
+ QVERIFY(flickable->contentX() != 0.0);
+ QVERIFY(flickable->contentY() != 0.0);
+}
QTEST_MAIN(tst_qquickflickable)
diff --git a/tests/auto/quick/qquickgraphicsinfo/data/basic.qml b/tests/auto/quick/qquickgraphicsinfo/data/basic.qml
new file mode 100644
index 0000000000..6ff3b82cec
--- /dev/null
+++ b/tests/auto/quick/qquickgraphicsinfo/data/basic.qml
@@ -0,0 +1,14 @@
+import QtQuick 2.8
+
+Item {
+ property int api: GraphicsInfo.api
+
+ property int shaderType: GraphicsInfo.shaderType
+ property int shaderCompilationType: GraphicsInfo.shaderCompilationType
+ property int shaderSourceType: GraphicsInfo.shaderSourceType
+
+ property int majorVersion: GraphicsInfo.majorVersion
+ property int minorVersion: GraphicsInfo.minorVersion
+ property int profile: GraphicsInfo.profile
+ property int renderableType: GraphicsInfo.renderableType
+}
diff --git a/tests/auto/quick/qquickgraphicsinfo/qquickgraphicsinfo.pro b/tests/auto/quick/qquickgraphicsinfo/qquickgraphicsinfo.pro
new file mode 100644
index 0000000000..a4296ad9a2
--- /dev/null
+++ b/tests/auto/quick/qquickgraphicsinfo/qquickgraphicsinfo.pro
@@ -0,0 +1,14 @@
+CONFIG += testcase
+TARGET = tst_qquickgraphicsinfo
+SOURCES += tst_qquickgraphicsinfo.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/qquickgraphicsinfo/tst_qquickgraphicsinfo.cpp b/tests/auto/quick/qquickgraphicsinfo/tst_qquickgraphicsinfo.cpp
new file mode 100644
index 0000000000..256fa43d2e
--- /dev/null
+++ b/tests/auto/quick/qquickgraphicsinfo/tst_qquickgraphicsinfo.cpp
@@ -0,0 +1,85 @@
+/****************************************************************************
+**
+** 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"
+
+#ifndef QT_NO_OPENGL
+#include <QtGui/qopenglcontext.h>
+#include <QtGui/qsurfaceformat.h>
+#endif
+
+class tst_QQuickGraphicsInfo : public QQmlDataTest
+{
+ Q_OBJECT
+
+private slots:
+ void testProperties();
+};
+
+void tst_QQuickGraphicsInfo::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);
+
+#ifndef QT_NO_OPENGL
+ if (expectedAPI == QSGRendererInterface::OpenGL) {
+ QCOMPARE(obj->property("shaderType").toInt(), int(QSGRendererInterface::GLSL));
+ QVERIFY(view.openglContext());
+ QSurfaceFormat format = view.openglContext()->format();
+ QCOMPARE(obj->property("majorVersion").toInt(), format.majorVersion());
+ QCOMPARE(obj->property("minorVersion").toInt(), format.minorVersion());
+ QCOMPARE(obj->property("profile").toInt(), static_cast<int>(format.profile()));
+ QCOMPARE(obj->property("renderableType").toInt(), static_cast<int>(format.renderableType()));
+ }
+#endif
+}
+
+QTEST_MAIN(tst_QQuickGraphicsInfo)
+
+#include "tst_qquickgraphicsinfo.moc"
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/data/layoutmirroring_window.qml b/tests/auto/quick/qquickitem2/data/layoutmirroring_window.qml
new file mode 100644
index 0000000000..3bac0716e8
--- /dev/null
+++ b/tests/auto/quick/qquickitem2/data/layoutmirroring_window.qml
@@ -0,0 +1,7 @@
+import QtQuick 2.0
+import QtQuick.Window 2.0
+
+Window {
+ LayoutMirroring.enabled: true
+ LayoutMirroring.childrenInherit: true
+}
diff --git a/tests/auto/quick/qquickitem2/tst_qquickitem.cpp b/tests/auto/quick/qquickitem2/tst_qquickitem.cpp
index 438c7bacf1..6554d749dd 100644
--- a/tests/auto/quick/qquickitem2/tst_qquickitem.cpp
+++ b/tests/auto/quick/qquickitem2/tst_qquickitem.cpp
@@ -88,6 +88,7 @@ private slots:
void keyNavigation_focusReason();
void keyNavigation_loop();
void layoutMirroring();
+ void layoutMirroringWindow();
void layoutMirroringIllegalParent();
void smooth();
void antialiasing();
@@ -1776,11 +1777,28 @@ void tst_QQuickItem::layoutMirroring()
delete parentItem2;
}
+void tst_QQuickItem::layoutMirroringWindow()
+{
+ QQmlComponent component(&engine);
+ component.loadUrl(testFileUrl("layoutmirroring_window.qml"));
+ QScopedPointer<QObject> object(component.create());
+ QQuickWindow *window = qobject_cast<QQuickWindow *>(object.data());
+ QVERIFY(window);
+ window->show();
+
+ QQuickItemPrivate *content = QQuickItemPrivate::get(window->contentItem());
+ QCOMPARE(content->effectiveLayoutMirror, true);
+ QCOMPARE(content->inheritedLayoutMirror, true);
+ QCOMPARE(content->isMirrorImplicit, false);
+ QCOMPARE(content->inheritMirrorFromParent, true);
+ QCOMPARE(content->inheritMirrorFromItem, true);
+}
+
void tst_QQuickItem::layoutMirroringIllegalParent()
{
QQmlComponent component(&engine);
component.setData("import QtQuick 2.0; QtObject { LayoutMirroring.enabled: true; LayoutMirroring.childrenInherit: true }", QUrl::fromLocalFile(""));
- QTest::ignoreMessage(QtWarningMsg, "<Unknown File>:1:21: QML QtObject: LayoutDirection attached property only works with Items");
+ QTest::ignoreMessage(QtWarningMsg, "<Unknown File>:1:21: QML QtObject: LayoutDirection attached property only works with Items and Windows");
QObject *object = component.create();
QVERIFY(object != 0);
}
@@ -2706,113 +2724,122 @@ void tst_QQuickItem::childrenRectBottomRightCorner()
struct TestListener : public QQuickItemChangeListener
{
- TestListener(bool remove = false) : remove(remove) { reset(); }
-
- void itemGeometryChanged(QQuickItem *, const QRectF &newGeometry, const QRectF &) override { ++itemGeometryChanges; value = newGeometry; }
- void itemSiblingOrderChanged(QQuickItem *) override { ++itemSiblingOrderChanges; }
- void itemVisibilityChanged(QQuickItem *) override { ++itemVisibilityChanges; }
- void itemOpacityChanged(QQuickItem *) override { ++itemOpacityChanges; }
- void itemRotationChanged(QQuickItem *) override { ++itemRotationChanges; }
- void itemImplicitWidthChanged(QQuickItem *) override { ++itemImplicitWidthChanges; }
- void itemImplicitHeightChanged(QQuickItem *) override { ++itemImplicitHeightChanges; }
+ TestListener(bool remove = false) : remove(remove) { }
+ void itemGeometryChanged(QQuickItem *item, QQuickGeometryChange, const QRectF &diff) override
+ {
+ record(item, QQuickItemPrivate::Geometry, diff);
+ }
+ void itemSiblingOrderChanged(QQuickItem *item) override
+ {
+ record(item, QQuickItemPrivate::SiblingOrder);
+ }
+ void itemVisibilityChanged(QQuickItem *item) override
+ {
+ record(item, QQuickItemPrivate::Visibility);
+ }
+ void itemOpacityChanged(QQuickItem *item) override
+ {
+ record(item, QQuickItemPrivate::Opacity);
+ }
+ void itemRotationChanged(QQuickItem *item) override
+ {
+ record(item, QQuickItemPrivate::Rotation);
+ }
+ void itemImplicitWidthChanged(QQuickItem *item) override
+ {
+ record(item, QQuickItemPrivate::ImplicitWidth);
+ }
+ void itemImplicitHeightChanged(QQuickItem *item) override
+ {
+ record(item, QQuickItemPrivate::ImplicitHeight);
+ }
void itemDestroyed(QQuickItem *item) override
{
- ++itemDestructions;
- // QTBUG-53453
- if (remove)
- QQuickItemPrivate::get(item)->removeItemChangeListener(this, QQuickItemPrivate::Destroyed);
+ record(item, QQuickItemPrivate::Destroyed);
}
void itemChildAdded(QQuickItem *item, QQuickItem *child) override
{
- ++itemChildAdditions;
- value = QVariant::fromValue(child);
- // QTBUG-53453
- if (remove)
- QQuickItemPrivate::get(item)->removeItemChangeListener(this, QQuickItemPrivate::Children);
+ record(item, QQuickItemPrivate::Children, QVariant::fromValue(child));
}
void itemChildRemoved(QQuickItem *item, QQuickItem *child) override
{
- ++itemChildRemovals;
- value = QVariant::fromValue(child);
- // QTBUG-53453
- if (remove)
- QQuickItemPrivate::get(item)->removeItemChangeListener(this, QQuickItemPrivate::Children);
+ record(item, QQuickItemPrivate::Children, QVariant::fromValue(child));
}
void itemParentChanged(QQuickItem *item, QQuickItem *parent) override
{
- ++itemParentChanges;
- value = QVariant::fromValue(parent);
- // QTBUG-53453
- if (remove)
- QQuickItemPrivate::get(item)->removeItemChangeListener(this, QQuickItemPrivate::Parent);
+ record(item, QQuickItemPrivate::Parent, QVariant::fromValue(parent));
}
QQuickAnchorsPrivate *anchorPrivate() override { return nullptr; }
- void reset()
+ void record(QQuickItem *item, QQuickItemPrivate::ChangeType change, const QVariant &value = QVariant())
+ {
+ changes += change;
+ values[change] = value;
+ // QTBUG-54732
+ if (remove)
+ QQuickItemPrivate::get(item)->removeItemChangeListener(this, change);
+ }
+
+ int count(QQuickItemPrivate::ChangeType change) const
+ {
+ return changes.count(change);
+ }
+
+ QVariant value(QQuickItemPrivate::ChangeType change) const
{
- value = QVariant();
- itemGeometryChanges = 0;
- itemSiblingOrderChanges = 0;
- itemVisibilityChanges = 0;
- itemOpacityChanges = 0;
- itemDestructions = 0;
- itemChildAdditions = 0;
- itemChildRemovals = 0;
- itemParentChanges = 0;
- itemRotationChanges = 0;
- itemImplicitWidthChanges = 0;
- itemImplicitHeightChanges = 0;
+ return values.value(change);
}
bool remove;
- QVariant value;
- int itemGeometryChanges;
- int itemSiblingOrderChanges;
- int itemVisibilityChanges;
- int itemOpacityChanges;
- int itemDestructions;
- int itemChildAdditions;
- int itemChildRemovals;
- int itemParentChanges;
- int itemRotationChanges;
- int itemImplicitWidthChanges;
- int itemImplicitHeightChanges;
+ QList<QQuickItemPrivate::ChangeType> changes;
+ QHash<QQuickItemPrivate::ChangeType, QVariant> values;
};
void tst_QQuickItem::changeListener()
{
- QQuickItem item;
+ QQuickWindow window;
+ window.show();
+ QTest::qWaitForWindowExposed(&window);
+
+ QQuickItem *item = new QQuickItem;
TestListener itemListener;
- QQuickItemPrivate::get(&item)->addItemChangeListener(&itemListener, QQuickItemPrivate::Geometry | QQuickItemPrivate::ImplicitWidth | QQuickItemPrivate::ImplicitHeight |
- QQuickItemPrivate::Opacity | QQuickItemPrivate::Rotation);
+ QQuickItemPrivate::get(item)->addItemChangeListener(&itemListener, QQuickItemPrivate::ChangeTypes(0xffff));
- item.setImplicitWidth(50);
- QCOMPARE(itemListener.itemImplicitWidthChanges, 1);
- QCOMPARE(itemListener.itemGeometryChanges, 1);
- QCOMPARE(itemListener.value, QVariant(QRectF(0,0,50,0)));
+ item->setImplicitWidth(10);
+ QCOMPARE(itemListener.count(QQuickItemPrivate::ImplicitWidth), 1);
+ QCOMPARE(itemListener.count(QQuickItemPrivate::Geometry), 1);
+ QCOMPARE(itemListener.value(QQuickItemPrivate::Geometry), QVariant(QRectF(0,0,10,0)));
- item.setImplicitHeight(50);
- QCOMPARE(itemListener.itemImplicitHeightChanges, 1);
- QCOMPARE(itemListener.itemGeometryChanges, 2);
- QCOMPARE(itemListener.value, QVariant(QRectF(0,0,50,50)));
+ item->setImplicitHeight(20);
+ QCOMPARE(itemListener.count(QQuickItemPrivate::ImplicitHeight), 1);
+ QCOMPARE(itemListener.count(QQuickItemPrivate::Geometry), 2);
+ QCOMPARE(itemListener.value(QQuickItemPrivate::Geometry), QVariant(QRectF(0,0,0,20)));
- item.setWidth(100);
- QCOMPARE(itemListener.itemGeometryChanges, 3);
- QCOMPARE(itemListener.value, QVariant(QRectF(0,0,100,50)));
+ item->setWidth(item->width() + 30);
+ QCOMPARE(itemListener.count(QQuickItemPrivate::Geometry), 3);
+ QCOMPARE(itemListener.value(QQuickItemPrivate::Geometry), QVariant(QRectF(0,0,30,0)));
- item.setHeight(100);
- QCOMPARE(itemListener.itemGeometryChanges, 4);
- QCOMPARE(itemListener.value, QVariant(QRectF(0,0,100,100)));
+ item->setHeight(item->height() + 40);
+ QCOMPARE(itemListener.count(QQuickItemPrivate::Geometry), 4);
+ QCOMPARE(itemListener.value(QQuickItemPrivate::Geometry), QVariant(QRectF(0,0,0,40)));
- item.setOpacity(0.5);
- QCOMPARE(itemListener.itemOpacityChanges, 1);
+ item->setOpacity(0.5);
+ QCOMPARE(itemListener.count(QQuickItemPrivate::Opacity), 1);
- item.setRotation(90);
- QCOMPARE(itemListener.itemRotationChanges, 1);
+ item->setRotation(90);
+ QCOMPARE(itemListener.count(QQuickItemPrivate::Rotation), 1);
- QQuickItem *parent = new QQuickItem;
+ item->setParentItem(window.contentItem());
+ QCOMPARE(itemListener.count(QQuickItemPrivate::Parent), 1);
+
+ item->setVisible(false);
+ QCOMPARE(itemListener.count(QQuickItemPrivate::Visibility), 1);
+
+ QQuickItemPrivate::get(item)->removeItemChangeListener(&itemListener, QQuickItemPrivate::ChangeTypes(0xffff));
+
+ QQuickItem *parent = new QQuickItem(window.contentItem());
TestListener parentListener;
QQuickItemPrivate::get(parent)->addItemChangeListener(&parentListener, QQuickItemPrivate::Children);
@@ -2824,52 +2851,79 @@ void tst_QQuickItem::changeListener()
QQuickItemPrivate::get(child2)->addItemChangeListener(&child2Listener, QQuickItemPrivate::Parent | QQuickItemPrivate::SiblingOrder | QQuickItemPrivate::Destroyed);
child1->setParentItem(parent);
- QCOMPARE(parentListener.itemChildAdditions, 1);
- QCOMPARE(parentListener.value, QVariant::fromValue(child1));
- QCOMPARE(child1Listener.itemParentChanges, 1);
- QCOMPARE(child1Listener.value, QVariant::fromValue(parent));
+ QCOMPARE(parentListener.count(QQuickItemPrivate::Children), 1);
+ QCOMPARE(parentListener.value(QQuickItemPrivate::Children), QVariant::fromValue(child1));
+ QCOMPARE(child1Listener.count(QQuickItemPrivate::Parent), 1);
+ QCOMPARE(child1Listener.value(QQuickItemPrivate::Parent), QVariant::fromValue(parent));
child2->setParentItem(parent);
- QCOMPARE(parentListener.itemChildAdditions, 2);
- QCOMPARE(parentListener.value, QVariant::fromValue(child2));
- QCOMPARE(child2Listener.itemParentChanges, 1);
- QCOMPARE(child2Listener.value, QVariant::fromValue(parent));
+ QCOMPARE(parentListener.count(QQuickItemPrivate::Children), 2);
+ QCOMPARE(parentListener.value(QQuickItemPrivate::Children), QVariant::fromValue(child2));
+ QCOMPARE(child2Listener.count(QQuickItemPrivate::Parent), 1);
+ QCOMPARE(child2Listener.value(QQuickItemPrivate::Parent), QVariant::fromValue(parent));
child2->stackBefore(child1);
- QCOMPARE(child1Listener.itemSiblingOrderChanges, 1);
- QCOMPARE(child2Listener.itemSiblingOrderChanges, 1);
+ QCOMPARE(child1Listener.count(QQuickItemPrivate::SiblingOrder), 1);
+ QCOMPARE(child2Listener.count(QQuickItemPrivate::SiblingOrder), 1);
child1->setParentItem(nullptr);
- QCOMPARE(parentListener.itemChildRemovals, 1);
- QCOMPARE(parentListener.value, QVariant::fromValue(child1));
- QCOMPARE(child1Listener.itemParentChanges, 2);
- QCOMPARE(child1Listener.value, QVariant::fromValue<QQuickItem *>(nullptr));
+ QCOMPARE(parentListener.count(QQuickItemPrivate::Children), 3);
+ QCOMPARE(parentListener.value(QQuickItemPrivate::Children), QVariant::fromValue(child1));
+ QCOMPARE(child1Listener.count(QQuickItemPrivate::Parent), 2);
+ QCOMPARE(child1Listener.value(QQuickItemPrivate::Parent), QVariant::fromValue<QQuickItem *>(nullptr));
delete child1;
- QCOMPARE(child1Listener.itemDestructions, 1);
+ QCOMPARE(child1Listener.count(QQuickItemPrivate::Destroyed), 1);
delete child2;
- QCOMPARE(parentListener.itemChildRemovals, 2);
- QCOMPARE(parentListener.value, QVariant::fromValue(child2));
- QCOMPARE(child2Listener.itemParentChanges, 2);
- QCOMPARE(child2Listener.value, QVariant::fromValue<QQuickItem *>(nullptr));
- QCOMPARE(child2Listener.itemDestructions, 1);
+ QCOMPARE(parentListener.count(QQuickItemPrivate::Children), 4);
+ QCOMPARE(parentListener.value(QQuickItemPrivate::Children), QVariant::fromValue(child2));
+ QCOMPARE(child2Listener.count(QQuickItemPrivate::Parent), 2);
+ QCOMPARE(child2Listener.value(QQuickItemPrivate::Parent), QVariant::fromValue<QQuickItem *>(nullptr));
+ QCOMPARE(child2Listener.count(QQuickItemPrivate::Destroyed), 1);
QQuickItemPrivate::get(parent)->removeItemChangeListener(&parentListener, QQuickItemPrivate::Children);
QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.count(), 0);
- // QTBUG-53453: all listeners should get invoked even if they remove themselves while iterating the listeners
+ // QTBUG-54732: all listeners should get invoked even if they remove themselves while iterating the listeners
QList<TestListener *> listeners;
for (int i = 0; i < 5; ++i)
listeners << new TestListener(true);
+ // itemVisibilityChanged x 5
+ foreach (TestListener *listener, listeners)
+ QQuickItemPrivate::get(parent)->addItemChangeListener(listener, QQuickItemPrivate::Visibility);
+ QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.count(), listeners.count());
+ parent->setVisible(false);
+ foreach (TestListener *listener, listeners)
+ QCOMPARE(listener->count(QQuickItemPrivate::Visibility), 1);
+ QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.count(), 0);
+
+ // itemRotationChanged x 5
+ foreach (TestListener *listener, listeners)
+ QQuickItemPrivate::get(parent)->addItemChangeListener(listener, QQuickItemPrivate::Rotation);
+ QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.count(), listeners.count());
+ parent->setRotation(90);
+ foreach (TestListener *listener, listeners)
+ QCOMPARE(listener->count(QQuickItemPrivate::Rotation), 1);
+ QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.count(), 0);
+
+ // itemOpacityChanged x 5
+ foreach (TestListener *listener, listeners)
+ QQuickItemPrivate::get(parent)->addItemChangeListener(listener, QQuickItemPrivate::Opacity);
+ QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.count(), listeners.count());
+ parent->setOpacity(0.5);
+ foreach (TestListener *listener, listeners)
+ QCOMPARE(listener->count(QQuickItemPrivate::Opacity), 1);
+ QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.count(), 0);
+
// itemChildAdded() x 5
foreach (TestListener *listener, listeners)
QQuickItemPrivate::get(parent)->addItemChangeListener(listener, QQuickItemPrivate::Children);
QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.count(), listeners.count());
child1 = new QQuickItem(parent);
foreach (TestListener *listener, listeners)
- QCOMPARE(listener->itemChildAdditions, 1);
+ QCOMPARE(listener->count(QQuickItemPrivate::Children), 1);
QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.count(), 0);
// itemParentChanged() x 5
@@ -2878,9 +2932,36 @@ void tst_QQuickItem::changeListener()
QCOMPARE(QQuickItemPrivate::get(child1)->changeListeners.count(), listeners.count());
child1->setParentItem(nullptr);
foreach (TestListener *listener, listeners)
- QCOMPARE(listener->itemParentChanges, 1);
+ QCOMPARE(listener->count(QQuickItemPrivate::Parent), 1);
QCOMPARE(QQuickItemPrivate::get(child1)->changeListeners.count(), 0);
+ // itemImplicitWidthChanged() x 5
+ foreach (TestListener *listener, listeners)
+ QQuickItemPrivate::get(parent)->addItemChangeListener(listener, QQuickItemPrivate::ImplicitWidth);
+ QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.count(), listeners.count());
+ parent->setImplicitWidth(parent->implicitWidth() + 1);
+ foreach (TestListener *listener, listeners)
+ QCOMPARE(listener->count(QQuickItemPrivate::ImplicitWidth), 1);
+ QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.count(), 0);
+
+ // itemImplicitHeightChanged() x 5
+ foreach (TestListener *listener, listeners)
+ QQuickItemPrivate::get(parent)->addItemChangeListener(listener, QQuickItemPrivate::ImplicitHeight);
+ QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.count(), listeners.count());
+ parent->setImplicitHeight(parent->implicitHeight() + 1);
+ foreach (TestListener *listener, listeners)
+ QCOMPARE(listener->count(QQuickItemPrivate::ImplicitHeight), 1);
+ QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.count(), 0);
+
+ // itemGeometryChanged() x 5
+ foreach (TestListener *listener, listeners)
+ QQuickItemPrivate::get(parent)->addItemChangeListener(listener, QQuickItemPrivate::Geometry);
+ QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.count(), listeners.count());
+ parent->setWidth(parent->width() + 1);
+ foreach (TestListener *listener, listeners)
+ QCOMPARE(listener->count(QQuickItemPrivate::Geometry), 1);
+ QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.count(), 0);
+
// itemChildRemoved() x 5
child1->setParentItem(parent);
foreach (TestListener *listener, listeners)
@@ -2888,7 +2969,7 @@ void tst_QQuickItem::changeListener()
QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.count(), listeners.count());
delete child1;
foreach (TestListener *listener, listeners)
- QCOMPARE(listener->itemChildRemovals, 1);
+ QCOMPARE(listener->count(QQuickItemPrivate::Children), 2);
QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.count(), 0);
// itemDestroyed() x 5
@@ -2897,7 +2978,7 @@ void tst_QQuickItem::changeListener()
QCOMPARE(QQuickItemPrivate::get(parent)->changeListeners.count(), listeners.count());
delete parent;
foreach (TestListener *listener, listeners)
- QCOMPARE(listener->itemDestructions, 1);
+ QCOMPARE(listener->count(QQuickItemPrivate::Destroyed), 1);
}
// QTBUG-13893
@@ -3223,7 +3304,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()));
@@ -3244,7 +3325,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..2576a1b0fc 100644
--- a/tests/auto/quick/qquickitemlayer/tst_qquickitemlayer.cpp
+++ b/tests/auto/quick/qquickitemlayer/tst_qquickitemlayer.cpp
@@ -30,6 +30,7 @@
#include <QtQuick/qquickitem.h>
#include <QtQuick/qquickview.h>
+#include <QtQuick/qsgrendererinterface.h>
#include <QtGui/qopenglcontext.h>
#include <QtGui/qopenglfunctions.h>
@@ -60,9 +61,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();
@@ -90,17 +92,20 @@ private:
bool m_isMesaSoftwareRasterizer;
int m_mesaVersion;
+ bool m_isOpenGLRenderer;
};
tst_QQuickItemLayer::tst_QQuickItemLayer()
: m_isMesaSoftwareRasterizer(false)
, m_mesaVersion(0)
+ , m_isOpenGLRenderer(true)
{
}
void tst_QQuickItemLayer::initTestCase()
{
QQmlDataTest::initTestCase();
+#ifndef QT_NO_OPENGL
QWindow window;
QOpenGLContext context;
window.setSurfaceType(QWindow::OpenGLSurface);
@@ -129,6 +134,13 @@ void tst_QQuickItemLayer::initTestCase()
m_mesaVersion = QT_VERSION_CHECK(major, minor, patch);
}
}
+ window.create();
+#endif
+ QQuickView view;
+ view.showNormal();
+ QTest::qWaitForWindowExposed(&view);
+ if (view.rendererInterface()->graphicsApi() != QSGRendererInterface::OpenGL)
+ m_isOpenGLRenderer = false;
}
// The test draws a red and a blue box next to each other and tests that the
@@ -165,8 +177,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 +203,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
@@ -203,6 +213,9 @@ void tst_QQuickItemLayer::layerSourceRect()
if (m_isMesaSoftwareRasterizer && m_mesaVersion < QT_VERSION_CHECK(7, 11, 0))
QSKIP("Mesa Software Rasterizer below version 7.11 does not render this test correctly.");
+ if (!m_isOpenGLRenderer)
+ QSKIP("Only OpenGL Renderer supports GLSL ShaderEffects");
+
QImage fb = runTest("SourceRect.qml");
// Check that the edges are converted to blue
@@ -223,6 +236,10 @@ void tst_QQuickItemLayer::layerIsTextureProvider()
{
if (m_isMesaSoftwareRasterizer && m_mesaVersion < QT_VERSION_CHECK(7, 11, 0))
QSKIP("Mesa Software Rasterizer below version 7.11 does not render this test correctly.");
+
+ if (!m_isOpenGLRenderer)
+ QSKIP("Only OpenGL Renderer supports GLSL ShaderEffects");
+
QImage fb = runTest("TextureProvider.qml");
QCOMPARE(fb.pixel(0, 0), qRgb(0xff, 0, 0));
QCOMPARE(fb.pixel(fb.width() - 1, 0), qRgb(0, 0xff, 0));
@@ -256,6 +273,9 @@ void tst_QQuickItemLayer::layerVisibility()
if (m_isMesaSoftwareRasterizer && m_mesaVersion < QT_VERSION_CHECK(7, 11, 0))
QSKIP("Mesa Software Rasterizer below version 7.11 does not render this test correctly.");
+ if (!m_isOpenGLRenderer)
+ QSKIP("Only OpenGL Renderer supports GLSL ShaderEffects");
+
QFETCH(bool, visible);
QFETCH(bool, effect);
QFETCH(qreal, opacity);
@@ -304,6 +324,9 @@ void tst_QQuickItemLayer::layerZOrder()
if (m_isMesaSoftwareRasterizer && m_mesaVersion < QT_VERSION_CHECK(7, 11, 0))
QSKIP("Mesa Software Rasterizer below version 7.11 does not render this test correctly.");
+ if (!m_isOpenGLRenderer)
+ QSKIP("Only OpenGL Renderer supports GLSL ShaderEffects");
+
QFETCH(bool, effect);
QQuickView view;
@@ -338,6 +361,9 @@ void tst_QQuickItemLayer::changeZOrder()
if (m_isMesaSoftwareRasterizer && m_mesaVersion < QT_VERSION_CHECK(7, 11, 0))
QSKIP("Mesa Software Rasterizer below version 7.11 does not render this test correctly.");
+ if (!m_isOpenGLRenderer)
+ QSKIP("Only OpenGL Renderer supports GLSL ShaderEffects");
+
QFETCH(bool, layered);
QFETCH(bool, effect);
@@ -405,6 +431,10 @@ void tst_QQuickItemLayer::changeSamplerName()
{
if (m_isMesaSoftwareRasterizer && m_mesaVersion < QT_VERSION_CHECK(7, 11, 0))
QSKIP("Mesa Software Rasterizer below version 7.11 does not render this test correctly.");
+
+ if (!m_isOpenGLRenderer)
+ QSKIP("Only OpenGL Renderer supports GLSL ShaderEffects");
+
QImage fb = runTest("SamplerNameChange.qml");
QCOMPARE(fb.pixel(0, 0), qRgb(0, 0, 0xff));
}
@@ -413,6 +443,9 @@ void tst_QQuickItemLayer::itemEffect()
{
if (m_isMesaSoftwareRasterizer && m_mesaVersion < QT_VERSION_CHECK(7, 11, 0))
QSKIP("Mesa Software Rasterizer below version 7.11 does not render this test correctly.");
+ if (!m_isOpenGLRenderer)
+ QSKIP("Only OpenGL Renderer supports GLSL ShaderEffects");
+
QImage fb = runTest("ItemEffect.qml");
QCOMPARE(fb.pixel(0, 0), qRgb(0xff, 0, 0));
QCOMPARE(fb.pixel(199, 0), qRgb(0xff, 0, 0));
@@ -448,6 +481,9 @@ void tst_QQuickItemLayer::textureMirroring()
{
QFETCH(int, mirroring);
+ if (!m_isOpenGLRenderer)
+ QSKIP("Only OpenGL Renderer supports GLSL ShaderEffects");
+
QQuickView view;
view.setSource(testFileUrl("TextureMirroring.qml"));
diff --git a/tests/auto/quick/qquicklistview/BLACKLIST b/tests/auto/quick/qquicklistview/BLACKLIST
index 269696ce8c..d259c11219 100644
--- a/tests/auto/quick/qquicklistview/BLACKLIST
+++ b/tests/auto/quick/qquicklistview/BLACKLIST
@@ -2,3 +2,6 @@
*
[enforceRange_withoutHighlight]
osx
+#QTBUG-53863
+[populateTransitions]
+opensuse-42.1
diff --git a/tests/auto/quick/qquickloader/data/qmldir b/tests/auto/quick/qquickloader/data/qmldir
deleted file mode 100644
index bf42b507c0..0000000000
--- a/tests/auto/quick/qquickloader/data/qmldir
+++ /dev/null
@@ -1 +0,0 @@
-# For tst_QDeclarativeLoader::networkRequestUrl; no types needed though.
diff --git a/tests/auto/quick/qquickloader/tst_qquickloader.cpp b/tests/auto/quick/qquickloader/tst_qquickloader.cpp
index fe22a238b2..77af4796b6 100644
--- a/tests/auto/quick/qquickloader/tst_qquickloader.cpp
+++ b/tests/auto/quick/qquickloader/tst_qquickloader.cpp
@@ -213,7 +213,7 @@ void tst_QQuickLoader::sourceOrComponent_data()
QTest::newRow("source with encoded subdir binding") << "source" << "source: encodeURIComponent('subdir/Test.qml')\n" << testFileUrl("subdir/Test.qml") << "";
QTest::newRow("sourceComponent") << "component" << "Component { id: comp; Rectangle { width: 100; height: 50 } }\n sourceComponent: comp\n" << QUrl() << "";
QTest::newRow("invalid source") << "source" << "source: 'IDontExist.qml'\n" << testFileUrl("IDontExist.qml")
- << QString(testFileUrl("IDontExist.qml").toString() + ": File not found");
+ << QString(testFileUrl("IDontExist.qml").toString() + ": No such file or directory");
}
void tst_QQuickLoader::clear()
@@ -748,7 +748,7 @@ void tst_QQuickLoader::initialPropertyValuesError_data()
<< (QStringList() << QString(testFileUrl("initialPropertyValues.error.1.qml").toString() + ":6:5: QML Loader: setSource: value is not an object"));
QTest::newRow("nonexistent source url") << testFileUrl("initialPropertyValues.error.2.qml")
- << (QStringList() << QString(testFileUrl("NonexistentSourceComponent.qml").toString() + ": File not found"));
+ << (QStringList() << QString(testFileUrl("NonexistentSourceComponent.qml").toString() + ": No such file or directory"));
QTest::newRow("invalid source url") << testFileUrl("initialPropertyValues.error.3.qml")
<< (QStringList() << QString(testFileUrl("InvalidSourceComponent.qml").toString() + ":5:1: Syntax error"));
@@ -901,7 +901,7 @@ void tst_QQuickLoader::asynchronous_data()
<< QStringList();
QTest::newRow("Non-existent component") << testFileUrl("IDoNotExist.qml")
- << (QStringList() << QString(testFileUrl("IDoNotExist.qml").toString() + ": File not found"));
+ << (QStringList() << QString(testFileUrl("IDoNotExist.qml").toString() + ": No such file or directory"));
QTest::newRow("Invalid component") << testFileUrl("InvalidSourceComponent.qml")
<< (QStringList() << QString(testFileUrl("InvalidSourceComponent.qml").toString() + ":5:1: Syntax error"));
diff --git a/tests/auto/quick/qquickmousearea/data/qtbug54019.qml b/tests/auto/quick/qquickmousearea/data/qtbug54019.qml
new file mode 100644
index 0000000000..75cca2691a
--- /dev/null
+++ b/tests/auto/quick/qquickmousearea/data/qtbug54019.qml
@@ -0,0 +1,21 @@
+import QtQuick 2.7
+
+Item {
+ width: 200
+ height: 200
+ MouseArea {
+ id: ma
+ property string str: "foo!"
+ width: 150; height: 150
+ hoverEnabled: true
+
+ Rectangle {
+ anchors.fill: parent
+ color: ma.containsMouse ? "lightsteelblue" : "gray"
+ }
+ Text {
+ text: ma.str
+ textFormat: Text.PlainText // consequently Text does not care about hover events
+ }
+ }
+}
diff --git a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp
index 5891e67df6..f22528a8a0 100644
--- a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp
+++ b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp
@@ -29,6 +29,7 @@
#include <QtTest/QtTest>
#include <QtTest/QSignalSpy>
#include <QtQuick/private/qquickdrag_p.h>
+#include <QtQuick/private/qquickitem_p.h>
#include <QtQuick/private/qquickmousearea_p.h>
#include <QtQuick/private/qquickrectangle_p.h>
#include <private/qquickflickable_p.h>
@@ -107,6 +108,7 @@ private slots:
void hoverPropagation();
void hoverVisible();
void hoverAfterPress();
+ void subtreeHoverEnabled();
void disableAfterPress();
void onWheel();
void transformedMouseArea_data();
@@ -124,8 +126,12 @@ private slots:
void containsPress_data();
void containsPress();
void ignoreBySource();
+ void notPressedAfterStolenGrab();
private:
+ int startDragDistance() const {
+ return QGuiApplication::styleHints()->startDragDistance();
+ }
void acceptedButton_data();
void rejectedButton_data();
QTouchDevice *device;
@@ -321,7 +327,8 @@ void tst_QQuickMouseArea::dragging()
QVERIFY(!drag->active());
- QTest::mousePress(&window, button, 0, QPoint(100,100));
+ QPoint p = QPoint(100,100);
+ QTest::mousePress(&window, button, 0, p);
QVERIFY(!drag->active());
QCOMPARE(blackRect->x(), 50.0);
@@ -332,18 +339,32 @@ void tst_QQuickMouseArea::dragging()
// The item is moved relative to the position of the mouse when the drag
// was triggered, this prevents a sudden change in position when the drag
// threshold is exceeded.
- QTest::mouseMove(&window, QPoint(111,111), 50);
- QTest::mouseMove(&window, QPoint(116,116), 50);
- QTest::mouseMove(&window, QPoint(122,122), 50);
+ int dragThreshold = QGuiApplication::styleHints()->startDragDistance();
+
+ // move the minimum distance to activate drag
+ p += QPoint(dragThreshold + 1, dragThreshold + 1);
+ QTest::mouseMove(&window, p);
+ QVERIFY(!drag->active());
+
+ // from here on move the item
+ p += QPoint(1, 1);
+ QTest::mouseMove(&window, p);
+ QTRY_VERIFY(drag->active());
+ // on macOS the cursor movement is going through a native event which
+ // means that it can actually take some time to show
+ QTRY_COMPARE(blackRect->x(), 50.0 + 1);
+ QCOMPARE(blackRect->y(), 50.0 + 1);
+
+ p += QPoint(10, 10);
+ QTest::mouseMove(&window, p);
QTRY_VERIFY(drag->active());
QTRY_COMPARE(blackRect->x(), 61.0);
QCOMPARE(blackRect->y(), 61.0);
- QTest::mouseRelease(&window, button, 0, QPoint(122,122));
-
+ QTest::mouseRelease(&window, button, 0, p);
QTRY_VERIFY(!drag->active());
- QCOMPARE(blackRect->x(), 61.0);
+ QTRY_COMPARE(blackRect->x(), 61.0);
QCOMPARE(blackRect->y(), 61.0);
}
@@ -532,15 +553,18 @@ void tst_QQuickMouseArea::cancelDragging()
QVERIFY(!drag->active());
- QTest::mousePress(&window, Qt::LeftButton, 0, QPoint(100,100));
+ QPoint p = QPoint(100,100);
+ QTest::mousePress(&window, Qt::LeftButton, 0, p);
QVERIFY(!drag->active());
QCOMPARE(blackRect->x(), 50.0);
QCOMPARE(blackRect->y(), 50.0);
- QTest::mouseMove(&window, QPoint(111,111), 50);
- QTest::mouseMove(&window, QPoint(116,116), 50);
- QTest::mouseMove(&window, QPoint(122,122), 50);
+ p += QPoint(startDragDistance() + 1, 0);
+ QTest::mouseMove(&window, p);
+
+ p += QPoint(11, 11);
+ QTest::mouseMove(&window, p);
QTRY_VERIFY(drag->active());
QTRY_COMPARE(blackRect->x(), 61.0);
@@ -575,7 +599,8 @@ void tst_QQuickMouseArea::setDragOnPressed()
QQuickItem *target = mouseArea->findChild<QQuickItem*>("target");
QVERIFY(target);
- QTest::mousePress(&window, Qt::LeftButton, 0, QPoint(100,100));
+ QPoint p = QPoint(100, 100);
+ QTest::mousePress(&window, Qt::LeftButton, 0, p);
QQuickDrag *drag = mouseArea->drag();
QVERIFY(drag);
@@ -587,19 +612,17 @@ void tst_QQuickMouseArea::setDragOnPressed()
// First move event triggers drag, second is acted upon.
// This is due to possibility of higher stacked area taking precedence.
- QTest::mouseMove(&window, QPoint(111,102));
- QTest::qWait(50);
- QTest::mouseMove(&window, QPoint(122,122));
- QTest::qWait(50);
+ p += QPoint(startDragDistance() + 1, 0);
+ QTest::mouseMove(&window, p);
- QVERIFY(drag->active());
- QCOMPARE(target->x(), 61.0);
+ p += QPoint(11, 0);
+ QTest::mouseMove(&window, p);
+ QTRY_VERIFY(drag->active());
+ QTRY_COMPARE(target->x(), 61.0);
QCOMPARE(target->y(), 50.0);
- QTest::mouseRelease(&window, Qt::LeftButton, 0, QPoint(122,122));
- QTest::qWait(50);
-
- QVERIFY(!drag->active());
+ QTest::mouseRelease(&window, Qt::LeftButton, 0, p);
+ QTRY_VERIFY(!drag->active());
QCOMPARE(target->x(), 61.0);
QCOMPARE(target->y(), 50.0);
}
@@ -748,7 +771,7 @@ void tst_QQuickMouseArea::onMousePressRejected()
QVERIFY(!window.rootObject()->property("mr1_canceled").toBool());
QVERIFY(window.rootObject()->property("mr2_pressed").toBool());
QVERIFY(!window.rootObject()->property("mr2_released").toBool());
- QVERIFY(window.rootObject()->property("mr2_canceled").toBool());
+ QVERIFY(!window.rootObject()->property("mr2_canceled").toBool());
QTest::qWait(200);
@@ -792,14 +815,14 @@ void tst_QQuickMouseArea::pressedCanceledOnWindowDeactivate()
QGuiApplication::sendEvent(&window, &pressEvent);
- QVERIFY(window.rootObject()->property("pressed").toBool());
+ QTRY_VERIFY(window.rootObject()->property("pressed").toBool());
QVERIFY(!window.rootObject()->property("canceled").toBool());
QCOMPARE(window.rootObject()->property("released").toInt(), expectedRelease);
QCOMPARE(window.rootObject()->property("clicked").toInt(), expectedClicks);
if (doubleClick) {
QGuiApplication::sendEvent(&window, &releaseEvent);
- QVERIFY(!window.rootObject()->property("pressed").toBool());
+ QTRY_VERIFY(!window.rootObject()->property("pressed").toBool());
QVERIFY(!window.rootObject()->property("canceled").toBool());
QCOMPARE(window.rootObject()->property("released").toInt(), ++expectedRelease);
QCOMPARE(window.rootObject()->property("clicked").toInt(), ++expectedClicks);
@@ -808,7 +831,7 @@ void tst_QQuickMouseArea::pressedCanceledOnWindowDeactivate()
QMouseEvent pressEvent2(QEvent::MouseButtonDblClick, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, 0);
QGuiApplication::sendEvent(&window, &pressEvent2);
- QVERIFY(window.rootObject()->property("pressed").toBool());
+ QTRY_VERIFY(window.rootObject()->property("pressed").toBool());
QVERIFY(!window.rootObject()->property("canceled").toBool());
QCOMPARE(window.rootObject()->property("released").toInt(), expectedRelease);
QCOMPARE(window.rootObject()->property("clicked").toInt(), expectedClicks);
@@ -820,23 +843,21 @@ void tst_QQuickMouseArea::pressedCanceledOnWindowDeactivate()
secondWindow->setProperty("visible", true);
QTest::qWaitForWindowExposed(secondWindow);
- QVERIFY(!window.rootObject()->property("pressed").toBool());
+ QTRY_VERIFY(!window.rootObject()->property("pressed").toBool());
QVERIFY(window.rootObject()->property("canceled").toBool());
QCOMPARE(window.rootObject()->property("released").toInt(), expectedRelease);
QCOMPARE(window.rootObject()->property("clicked").toInt(), expectedClicks);
//press again
QGuiApplication::sendEvent(&window, &pressEvent);
- QVERIFY(window.rootObject()->property("pressed").toBool());
+ QTRY_VERIFY(window.rootObject()->property("pressed").toBool());
QVERIFY(!window.rootObject()->property("canceled").toBool());
QCOMPARE(window.rootObject()->property("released").toInt(), expectedRelease);
QCOMPARE(window.rootObject()->property("clicked").toInt(), expectedClicks);
- QTest::qWait(200);
-
//release
QGuiApplication::sendEvent(&window, &releaseEvent);
- QVERIFY(!window.rootObject()->property("pressed").toBool());
+ QTRY_VERIFY(!window.rootObject()->property("pressed").toBool());
QVERIFY(!window.rootObject()->property("canceled").toBool());
QCOMPARE(window.rootObject()->property("released").toInt(), ++expectedRelease);
QCOMPARE(window.rootObject()->property("clicked").toInt(), ++expectedClicks);
@@ -994,48 +1015,60 @@ void tst_QQuickMouseArea::preventStealing()
QSignalSpy mousePositionSpy(mouseArea, SIGNAL(positionChanged(QQuickMouseEvent*)));
- QTest::mousePress(&window, Qt::LeftButton, 0, QPoint(80, 80));
+ QPoint p = QPoint(80, 80);
+ QTest::mousePress(&window, Qt::LeftButton, 0, p);
// Without preventStealing, mouse movement over MouseArea would
// cause the Flickable to steal mouse and trigger content movement.
- QTest::mouseMove(&window,QPoint(69,69));
- QTest::mouseMove(&window,QPoint(58,58));
- QTest::mouseMove(&window,QPoint(47,47));
+ p += QPoint(-startDragDistance() * 2, -startDragDistance() * 2);
+ QTest::mouseMove(&window, p);
+ p += QPoint(-10, -10);
+ QTest::mouseMove(&window, p);
+ p += QPoint(-10, -10);
+ QTest::mouseMove(&window, p);
+ p += QPoint(-10, -10);
+ QTest::mouseMove(&window, p);
- // We should have received all three move events
- QCOMPARE(mousePositionSpy.count(), 3);
+ // We should have received all four move events
+ QTRY_COMPARE(mousePositionSpy.count(), 4);
+ mousePositionSpy.clear();
QVERIFY(mouseArea->pressed());
// Flickable content should not have moved.
QCOMPARE(flickable->contentX(), 0.);
QCOMPARE(flickable->contentY(), 0.);
- QTest::mouseRelease(&window, Qt::LeftButton, 0, QPoint(47, 47));
+ QTest::mouseRelease(&window, Qt::LeftButton, 0, p);
// Now allow stealing and confirm Flickable does its thing.
window.rootObject()->setProperty("stealing", false);
- QTest::mousePress(&window, Qt::LeftButton, 0, QPoint(80, 80));
+ p = QPoint(80, 80);
+ QTest::mousePress(&window, Qt::LeftButton, 0, p);
// Without preventStealing, mouse movement over MouseArea would
// cause the Flickable to steal mouse and trigger content movement.
- QTest::mouseMove(&window,QPoint(69,69));
- QTest::mouseMove(&window,QPoint(58,58));
- QTest::mouseMove(&window,QPoint(47,47));
+ p += QPoint(-startDragDistance() * 2, -startDragDistance() * 2);
+ QTest::mouseMove(&window, p);
+ p += QPoint(-10, -10);
+ QTest::mouseMove(&window, p);
+ p += QPoint(-10, -10);
+ QTest::mouseMove(&window, p);
+ p += QPoint(-10, -10);
+ QTest::mouseMove(&window, p);
// We should only have received the first move event
- QCOMPARE(mousePositionSpy.count(), 4);
+ QTRY_COMPARE(mousePositionSpy.count(), 1);
// Our press should be taken away
QVERIFY(!mouseArea->pressed());
- // Flickable content should have moved.
-
- QCOMPARE(flickable->contentX(), 11.);
- QCOMPARE(flickable->contentY(), 11.);
+ // Flickable swallows the first move, then moves 2*10 px
+ QTRY_COMPARE(flickable->contentX(), 20.);
+ QCOMPARE(flickable->contentY(), 20.);
- QTest::mouseRelease(&window, Qt::LeftButton, 0, QPoint(50, 50));
+ QTest::mouseRelease(&window, Qt::LeftButton, 0, p);
}
void tst_QQuickMouseArea::clickThrough()
@@ -1280,6 +1313,26 @@ void tst_QQuickMouseArea::hoverAfterPress()
QCOMPARE(mouseArea->hovered(), false);
}
+void tst_QQuickMouseArea::subtreeHoverEnabled()
+{
+ QQuickView window;
+ QByteArray errorMessage;
+ QVERIFY2(initView(window, testFileUrl("qtbug54019.qml"), true, &errorMessage), errorMessage.constData());
+ QQuickItem *root = window.rootObject();
+ QVERIFY(root != 0);
+
+ QQuickMouseArea *mouseArea = root->findChild<QQuickMouseArea*>();
+ QQuickItemPrivate *rootPrivate = QQuickItemPrivate::get(root);
+ QVERIFY(mouseArea != 0);
+ QTest::mouseMove(&window, QPoint(10, 160));
+ QCOMPARE(mouseArea->hovered(), false);
+ QVERIFY(rootPrivate->subtreeHoverEnabled);
+ QTest::mouseMove(&window, QPoint(10, 10));
+ QCOMPARE(mouseArea->hovered(), true);
+ QTest::mouseMove(&window, QPoint(160, 10));
+ QCOMPARE(mouseArea->hovered(), false);
+}
+
void tst_QQuickMouseArea::disableAfterPress()
{
QQuickView window;
@@ -1304,9 +1357,8 @@ void tst_QQuickMouseArea::disableAfterPress()
QCOMPARE(blackRect, drag->target());
QVERIFY(!drag->active());
-
- QTest::mousePress(&window, Qt::LeftButton, 0, QPoint(100,100));
-
+ QPoint p = QPoint(100,100);
+ QTest::mousePress(&window, Qt::LeftButton, 0, p);
QTRY_COMPARE(mousePressSpy.count(), 1);
QVERIFY(!drag->active());
@@ -1316,22 +1368,24 @@ void tst_QQuickMouseArea::disableAfterPress()
// First move event triggers drag, second is acted upon.
// This is due to possibility of higher stacked area taking precedence.
- QTest::mouseMove(&window, QPoint(111,111));
- QTest::qWait(50);
- QTest::mouseMove(&window, QPoint(122,122));
+ p += QPoint(startDragDistance() + 1, 0);
+ QTest::mouseMove(&window, p);
+ p += QPoint(11, 11);
+ QTest::mouseMove(&window, p);
QTRY_COMPARE(mousePositionSpy.count(), 2);
- QVERIFY(drag->active());
- QCOMPARE(blackRect->x(), 61.0);
+ QTRY_VERIFY(drag->active());
+ QTRY_COMPARE(blackRect->x(), 61.0);
QCOMPARE(blackRect->y(), 61.0);
mouseArea->setEnabled(false);
// move should still be acted upon
- QTest::mouseMove(&window, QPoint(133,133));
- QTest::qWait(50);
- QTest::mouseMove(&window, QPoint(144,144));
+ p += QPoint(11, 11);
+ QTest::mouseMove(&window, p);
+ p += QPoint(11, 11);
+ QTest::mouseMove(&window, p);
QTRY_COMPARE(mousePositionSpy.count(), 4);
@@ -1342,7 +1396,7 @@ void tst_QQuickMouseArea::disableAfterPress()
QVERIFY(mouseArea->pressed());
QVERIFY(mouseArea->hovered());
- QTest::mouseRelease(&window, Qt::LeftButton, 0, QPoint(144,144));
+ QTest::mouseRelease(&window, Qt::LeftButton, 0, p);
QTRY_COMPARE(mouseReleaseSpy.count(), 1);
@@ -1599,36 +1653,39 @@ void tst_QQuickMouseArea::changeAxis()
QVERIFY(!drag->active());
// Start a diagonal drag
- QTest::mousePress(&view, Qt::LeftButton, 0, QPoint(100, 100));
+ QPoint p = QPoint(100, 100);
+ QTest::mousePress(&view, Qt::LeftButton, 0, p);
QVERIFY(!drag->active());
QCOMPARE(blackRect->x(), 50.0);
QCOMPARE(blackRect->y(), 50.0);
- QTest::mouseMove(&view, QPoint(111, 111));
- QTest::qWait(50);
- QTest::mouseMove(&view, QPoint(122, 122));
-
+ p += QPoint(startDragDistance() + 1, startDragDistance() + 1);
+ QTest::mouseMove(&view, p);
+ p += QPoint(11, 11);
+ QTest::mouseMove(&view, p);
QTRY_VERIFY(drag->active());
- QCOMPARE(blackRect->x(), 61.0);
+ QTRY_COMPARE(blackRect->x(), 61.0);
QCOMPARE(blackRect->y(), 61.0);
QCOMPARE(drag->axis(), QQuickDrag::XAndYAxis);
/* When blackRect.x becomes bigger than 75, the drag axis is changed to
* Drag.YAxis by the QML code. Verify that this happens, and that the drag
* movement is effectively constrained to the Y axis. */
- QTest::mouseMove(&view, QPoint(144, 144));
+ p += QPoint(22, 22);
+ QTest::mouseMove(&view, p);
QTRY_COMPARE(blackRect->x(), 83.0);
QTRY_COMPARE(blackRect->y(), 83.0);
QTRY_COMPARE(drag->axis(), QQuickDrag::YAxis);
- QTest::mouseMove(&view, QPoint(155, 155));
+ p += QPoint(11, 11);
+ QTest::mouseMove(&view, p);
QTRY_COMPARE(blackRect->y(), 94.0);
QCOMPARE(blackRect->x(), 83.0);
- QTest::mouseRelease(&view, Qt::LeftButton, 0, QPoint(155, 155));
+ QTest::mouseRelease(&view, Qt::LeftButton, 0, p);
QTRY_VERIFY(!drag->active());
QCOMPARE(blackRect->x(), 83.0);
@@ -1678,11 +1735,17 @@ void tst_QQuickMouseArea::moveAndReleaseWithoutPress()
QTest::mousePress(&window, Qt::LeftButton, 0, QPoint(100,100));
+ // the press was not accepted, make sure there is no move or release event
QTest::mouseMove(&window, QPoint(110,110), 50);
- QTRY_COMPARE(root->property("hadMove").toBool(), false);
+
+ // use qwait here because we want to make sure an event does NOT happen
+ // the test fails if the default state changes, while it shouldn't
+ QTest::qWait(100);
+ QCOMPARE(root->property("hadMove").toBool(), false);
QTest::mouseRelease(&window, Qt::LeftButton, 0, QPoint(110,110));
- QTRY_COMPARE(root->property("hadRelease").toBool(), false);
+ QTest::qWait(100);
+ QCOMPARE(root->property("hadRelease").toBool(), false);
}
void tst_QQuickMouseArea::nestedStopAtBounds_data()
@@ -1926,33 +1989,43 @@ void tst_QQuickMouseArea::ignoreBySource()
QVERIFY(flickable);
// MouseArea should grab the press because it's interested in non-synthesized mouse events
- QTest::mousePress(&window, Qt::LeftButton, 0, QPoint(80, 80));
- QVERIFY(window.mouseGrabberItem() == mouseArea);
+ QPoint p = QPoint(80, 80);
+ QTest::mousePress(&window, Qt::LeftButton, 0, p);
+ QCOMPARE(window.mouseGrabberItem(), mouseArea);
// That was a real mouse event
- QVERIFY(root->property("lastEventSource").toInt() == Qt::MouseEventNotSynthesized);
+ QCOMPARE(root->property("lastEventSource").toInt(), int(Qt::MouseEventNotSynthesized));
// Flickable content should not move
- QTest::mouseMove(&window,QPoint(69,69));
- QTest::mouseMove(&window,QPoint(58,58));
- QTest::mouseMove(&window,QPoint(47,47));
+ p -= QPoint(startDragDistance() + 1, startDragDistance() + 1);
+ QTest::mouseMove(&window, p);
+ p -= QPoint(11, 11);
+ QTest::mouseMove(&window, p);
+ p -= QPoint(11, 11);
+ QTest::mouseMove(&window, p);
QCOMPARE(flickable->contentX(), 0.);
QCOMPARE(flickable->contentY(), 0.);
- QTest::mouseRelease(&window, Qt::LeftButton, 0, QPoint(47, 47));
+ QTest::mouseRelease(&window, Qt::LeftButton, 0, p);
+ QCOMPARE(window.mouseGrabberItem(), nullptr);
// Now try touch events and confirm that MouseArea ignores them, while Flickable does its thing
-
- QTest::touchEvent(&window, device).press(0, QPoint(80, 80), &window);
+ p = QPoint(80, 80);
+ QTest::touchEvent(&window, device).press(0, p, &window);
QQuickTouchUtils::flush(&window);
- QVERIFY(window.mouseGrabberItem() != mouseArea);
+ QCOMPARE(window.mouseGrabberItem(), flickable);
+
// That was a fake mouse event
QCOMPARE(root->property("lastEventSource").toInt(), int(Qt::MouseEventSynthesizedByQt));
- QTest::touchEvent(&window, device).move(0, QPoint(69,69), &window);
- QTest::touchEvent(&window, device).move(0, QPoint(69,69), &window);
- QTest::touchEvent(&window, device).move(0, QPoint(47,47), &window);
+ p -= QPoint(startDragDistance() + 1, startDragDistance() + 1);
+ QTest::touchEvent(&window, device).move(0, p, &window);
+ p -= QPoint(11, 11);
+ QTest::touchEvent(&window, device).move(0, p, &window);
+ p -= QPoint(11, 11);
+ QTest::touchEvent(&window, device).move(0, p, &window);
+
QQuickTouchUtils::flush(&window);
QCOMPARE(window.mouseGrabberItem(), flickable);
- QTest::touchEvent(&window, device).release(0, QPoint(47,47), &window);
+ QTest::touchEvent(&window, device).release(0, p, &window);
QQuickTouchUtils::flush(&window);
// Flickable content should have moved
@@ -1967,15 +2040,19 @@ void tst_QQuickMouseArea::ignoreBySource()
// MouseArea should ignore the press because it's interested in synthesized mouse events
- QTest::mousePress(&window, Qt::LeftButton, 0, QPoint(80, 80));
+ p = QPoint(80, 80);
+ QTest::mousePress(&window, Qt::LeftButton, 0, p);
QVERIFY(window.mouseGrabberItem() != mouseArea);
// That was a real mouse event
QVERIFY(root->property("lastEventSource").toInt() == Qt::MouseEventNotSynthesized);
// Flickable content should move
- QTest::mouseMove(&window,QPoint(69,69));
- QTest::mouseMove(&window,QPoint(58,58));
- QTest::mouseMove(&window,QPoint(47,47));
+ p -= QPoint(startDragDistance() + 1, startDragDistance() + 1);
+ QTest::mouseMove(&window, p);
+ p -= QPoint(11, 11);
+ QTest::mouseMove(&window, p);
+ p -= QPoint(11, 11);
+ QTest::mouseMove(&window, p);
QTRY_VERIFY(flickable->contentX() > 1);
QVERIFY(flickable->contentY() > 1);
@@ -1984,13 +2061,16 @@ void tst_QQuickMouseArea::ignoreBySource()
flickable->setContentY(0);
// Now try touch events and confirm that MouseArea gets them, while Flickable doesn't
-
- QTest::touchEvent(&window, device).press(0, QPoint(80, 80), &window);
+ p = QPoint(80, 80);
+ QTest::touchEvent(&window, device).press(0, p, &window);
QQuickTouchUtils::flush(&window);
QCOMPARE(window.mouseGrabberItem(), mouseArea);
- QTest::touchEvent(&window, device).move(0, QPoint(69,69), &window);
- QTest::touchEvent(&window, device).move(0, QPoint(69,69), &window);
- QTest::touchEvent(&window, device).move(0, QPoint(47,47), &window);
+ p -= QPoint(startDragDistance() + 1, startDragDistance() + 1);
+ QTest::touchEvent(&window, device).move(0, p, &window);
+ p -= QPoint(11, 11);
+ QTest::touchEvent(&window, device).move(0, p, &window);
+ p -= QPoint(11, 11);
+ QTest::touchEvent(&window, device).move(0, p, &window);
QQuickTouchUtils::flush(&window);
QCOMPARE(window.mouseGrabberItem(), mouseArea);
QTest::touchEvent(&window, device).release(0, QPoint(47,47), &window);
@@ -2001,6 +2081,23 @@ void tst_QQuickMouseArea::ignoreBySource()
QCOMPARE(flickable->contentY(), 0.);
}
+void tst_QQuickMouseArea::notPressedAfterStolenGrab()
+{
+ QQuickWindow window;
+ window.resize(200, 200);
+ window.show();
+ QTest::qWaitForWindowExposed(&window);
+
+ QQuickMouseArea *ma = new QQuickMouseArea(window.contentItem());
+ ma->setSize(window.size());
+ QObject::connect(ma,
+ static_cast<void (QQuickMouseArea::*)(QQuickMouseEvent*)>(&QQuickMouseArea::pressed),
+ [&]() { window.contentItem()->grabMouse(); });
+
+ QTest::mouseClick(&window, Qt::LeftButton);
+ QVERIFY(!ma->pressed());
+}
+
QTEST_MAIN(tst_QQuickMouseArea)
#include "tst_qquickmousearea.moc"
diff --git a/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp b/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp
index c3981c466f..2872556a94 100644
--- a/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp
+++ b/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp
@@ -915,13 +915,13 @@ void tst_QQuickMultiPointTouchArea::mouseAsTouchpoint()
// Touch both, release one, manipulate other touchpoint with mouse
QTest::touchEvent(window.data(), device).press(1, touch1);
QQuickTouchUtils::flush(window.data());
- QTest::touchEvent(window.data(), device).press(2, touch2);
+ QTest::touchEvent(window.data(), device).move(1, touch1).press(2, touch2);
QQuickTouchUtils::flush(window.data());
QCOMPARE(touch1rect->property("x").toInt(), touch1.x());
QCOMPARE(touch1rect->property("y").toInt(), touch1.y());
QCOMPARE(touch2rect->property("x").toInt(), touch2.x());
QCOMPARE(touch2rect->property("y").toInt(), touch2.y());
- QTest::touchEvent(window.data(), device).release(1, touch1);
+ QTest::touchEvent(window.data(), device).release(1, touch1).move(2, touch2);
touch1.setY(20);
QTest::mousePress(window.data(), Qt::LeftButton, 0, touch1);
QQuickTouchUtils::flush(window.data());
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/qquickpincharea/data/pinchproperties.qml b/tests/auto/quick/qquickpincharea/data/pinchproperties.qml
index 37d706fc8e..c5daf6cbfe 100644
--- a/tests/auto/quick/qquickpincharea/data/pinchproperties.qml
+++ b/tests/auto/quick/qquickpincharea/data/pinchproperties.qml
@@ -2,7 +2,7 @@ import QtQuick 2.0
Rectangle {
id: whiteRect
property variant center
- property real scale
+ property real scale: -1.0
property int pointCount: 0
property bool pinchActive: false
width: 240; height: 320
diff --git a/tests/auto/quick/qquickpincharea/tst_qquickpincharea.cpp b/tests/auto/quick/qquickpincharea/tst_qquickpincharea.cpp
index aee35b4b90..c1a51fd659 100644
--- a/tests/auto/quick/qquickpincharea/tst_qquickpincharea.cpp
+++ b/tests/auto/quick/qquickpincharea/tst_qquickpincharea.cpp
@@ -286,6 +286,7 @@ void tst_QQuickPinchArea::pan()
QPoint p1(80, 80);
QPoint p2(100, 100);
{
+ const int dragThreshold = QGuiApplication::styleHints()->startDragDistance();
QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(window, device);
pinchSequence.press(0, p1, window).commit();
QQuickTouchUtils::flush(window);
@@ -293,23 +294,63 @@ void tst_QQuickPinchArea::pan()
// we have to reuse the same pinchSequence object.
pinchSequence.stationary(0).press(1, p2, window).commit();
QQuickTouchUtils::flush(window);
- p1 += QPoint(10,10);
- p2 += QPoint(10,10);
- pinchSequence.move(0, p1,window).move(1, p2,window).commit();
+ QVERIFY(!root->property("pinchActive").toBool());
+ QCOMPARE(root->property("scale").toReal(), -1.0);
+
+ p1 += QPoint(dragThreshold - 1, 0);
+ p2 += QPoint(dragThreshold - 1, 0);
+ pinchSequence.move(0, p1, window).move(1, p2, window).commit();
QQuickTouchUtils::flush(window);
+ // movement < dragThreshold: pinch not yet active
+ QVERIFY(!root->property("pinchActive").toBool());
+ QCOMPARE(root->property("scale").toReal(), -1.0);
- QCOMPARE(root->property("scale").toReal(), 1.0);
+ // exactly the dragThreshold: pinch starts
+ p1 += QPoint(1, 0);
+ p2 += QPoint(1, 0);
+ pinchSequence.move(0, p1, window).move(1, p2, window).commit();
+ QQuickTouchUtils::flush(window);
QVERIFY(root->property("pinchActive").toBool());
+ QCOMPARE(root->property("scale").toReal(), 1.0);
- p1 += QPoint(10,10);
- p2 += QPoint(10,10);
- pinchSequence.move(0, p1,window).move(1, p2,window).commit();
+ // Calculation of the center point is tricky at first:
+ // center point of the two touch points in item coordinates:
+ // scene coordinates: (80, 80) + (dragThreshold, 0), (100, 100) + (dragThreshold, 0)
+ // = ((180+dT)/2, 180/2) = (90+dT, 90)
+ // item coordinates: (scene) - (50, 50) = (40+dT, 40)
+ QCOMPARE(root->property("center").toPointF(), QPointF(40 + dragThreshold, 40));
+ // pan started, but no actual movement registered yet:
+ // blackrect starts at 50,50
+ QCOMPARE(blackRect->x(), 50.0);
+ QCOMPARE(blackRect->y(), 50.0);
+
+ p1 += QPoint(10, 0);
+ p2 += QPoint(10, 0);
+ pinchSequence.move(0, p1, window).move(1, p2, window).commit();
QQuickTouchUtils::flush(window);
- }
+ QCOMPARE(root->property("center").toPointF(), QPointF(40 + 10 + dragThreshold, 40));
+ QCOMPARE(blackRect->x(), 60.0);
+ QCOMPARE(blackRect->y(), 50.0);
- QCOMPARE(root->property("center").toPointF(), QPointF(60, 60)); // blackrect is at 50,50
- QCOMPARE(blackRect->x(), 60.0);
- QCOMPARE(blackRect->y(), 60.0);
+ p1 += QPoint(0, 10);
+ p2 += QPoint(0, 10);
+ pinchSequence.move(0, p1, window).move(1, p2, window).commit();
+ QQuickTouchUtils::flush(window);
+ // next big surprise: the center is in item local coordinates and the item was just
+ // moved 10 to the right... which offsets the center point 10 to the left
+ QCOMPARE(root->property("center").toPointF(), QPointF(40 + 10 - 10 + dragThreshold, 40 + 10));
+ QCOMPARE(blackRect->x(), 60.0);
+ QCOMPARE(blackRect->y(), 60.0);
+
+ p1 += QPoint(10, 10);
+ p2 += QPoint(10, 10);
+ pinchSequence.move(0, p1, window).move(1, p2, window).commit();
+ QQuickTouchUtils::flush(window);
+ // now the item moved again, thus the center point of the touch is moved in total by (10, 10)
+ QCOMPARE(root->property("center").toPointF(), QPointF(50 + dragThreshold, 50));
+ QCOMPARE(blackRect->x(), 70.0);
+ QCOMPARE(blackRect->y(), 70.0);
+ }
// pan x beyond bound
p1 += QPoint(100,100);
@@ -318,7 +359,7 @@ void tst_QQuickPinchArea::pan()
QQuickTouchUtils::flush(window);
QCOMPARE(blackRect->x(), 140.0);
- QCOMPARE(blackRect->y(), 160.0);
+ QCOMPARE(blackRect->y(), 170.0);
QTest::touchEvent(window, device).release(0, p1, window).release(1, p2, window);
QQuickTouchUtils::flush(window);
@@ -470,6 +511,7 @@ void tst_QQuickPinchArea::cancel()
QCOMPARE(blackRect->scale(), 1.5);
QTouchEvent cancelEvent(QEvent::TouchCancel);
+ cancelEvent.setDevice(device);
QCoreApplication::sendEvent(window, &cancelEvent);
QQuickTouchUtils::flush(window);
diff --git a/tests/auto/quick/qquickshadereffect/tst_qquickshadereffect.cpp b/tests/auto/quick/qquickshadereffect/tst_qquickshadereffect.cpp
index 483cdf7a41..fe33dbd4d8 100644
--- a/tests/auto/quick/qquickshadereffect/tst_qquickshadereffect.cpp
+++ b/tests/auto/quick/qquickshadereffect/tst_qquickshadereffect.cpp
@@ -30,16 +30,16 @@
#include <QList>
#include <QByteArray>
-#include <private/qquickshadereffect_p.h>
-
+#include <private/qquickopenglshadereffect_p.h>
+#include <QMatrix4x4>
#include <QtQuick/QQuickView>
+#include <QtQml/QQmlEngine>
#include "../../shared/util.h"
-
class TestShaderEffect : public QQuickShaderEffect
{
Q_OBJECT
- Q_PROPERTY(QVariant source READ dummyRead NOTIFY dummyChanged)
+ Q_PROPERTY(QVariant source READ dummyRead NOTIFY sourceChanged)
Q_PROPERTY(QVariant _0aA9zZ READ dummyRead NOTIFY dummyChanged)
Q_PROPERTY(QVariant x86 READ dummyRead NOTIFY dummyChanged)
Q_PROPERTY(QVariant X READ dummyRead NOTIFY dummyChanged)
@@ -48,17 +48,18 @@ class TestShaderEffect : public QQuickShaderEffect
public:
QMatrix4x4 mat4x4Read() const { return QMatrix4x4(1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1); }
QVariant dummyRead() const { return QVariant(); }
- bool isConnected(const QMetaMethod &signal) const { return m_signals.contains(signal); }
+
+ int signalsConnected = 0;
protected:
- void connectNotify(const QMetaMethod &signal) { m_signals.append(signal); }
- void disconnectNotify(const QMetaMethod &signal) { m_signals.removeOne(signal); }
+ void connectNotify(const QMetaMethod &) { ++signalsConnected; }
+ void disconnectNotify(const QMetaMethod &) { --signalsConnected; }
signals:
void dummyChanged();
+ void sourceChanged();
private:
- QList<QMetaMethod> m_signals;
};
class tst_qquickshadereffect : public QQmlDataTest
@@ -84,12 +85,13 @@ private:
TexCoordPresent = 0x02,
MatrixPresent = 0x04,
OpacityPresent = 0x08,
- PropertyPresent = 0x10
+ SourcePresent = 0x10
};
};
tst_qquickshadereffect::tst_qquickshadereffect()
{
+ qmlRegisterType<TestShaderEffect>("ShaderEffectTest", 1, 0, "TestShaderEffect");
}
void tst_qquickshadereffect::initTestCase()
@@ -122,7 +124,7 @@ void tst_qquickshadereffect::lookThroughShaderCode_data()
"void main() { \n"
" gl_FragColor = texture2D(source, qt_TexCoord0) * qt_Opacity; \n"
"}")
- << (VertexPresent | TexCoordPresent | MatrixPresent | OpacityPresent | PropertyPresent);
+ << (VertexPresent | TexCoordPresent | MatrixPresent | OpacityPresent | SourcePresent);
QTest::newRow("empty")
<< QByteArray(" ") // one space -- if completely empty, default will be used instead.
@@ -135,14 +137,14 @@ void tst_qquickshadereffect::lookThroughShaderCode_data()
"attribute highp vec4 qt_Vertex;\n"
"// attribute highp vec2 qt_MultiTexCoord0;")
<< QByteArray("uniform int source; // uniform lowp float qt_Opacity;")
- << (VertexPresent | PropertyPresent);
+ << (VertexPresent | SourcePresent);
QTest::newRow("inside block comments")
<< QByteArray("/*uniform highp mat4 qt_Matrix;\n"
"*/attribute highp vec4 qt_Vertex;\n"
"/*/attribute highp vec2 qt_MultiTexCoord0;//**/")
<< QByteArray("/**/uniform int source; /* uniform lowp float qt_Opacity; */")
- << (VertexPresent | PropertyPresent);
+ << (VertexPresent | SourcePresent);
QTest::newRow("inside preprocessor directive")
<< QByteArray("#define uniform\nhighp mat4 qt_Matrix;\n"
@@ -150,7 +152,7 @@ void tst_qquickshadereffect::lookThroughShaderCode_data()
"#if\\\nattribute highp vec2 qt_MultiTexCoord0;")
<< QByteArray("uniform int source;\n"
" # undef uniform lowp float qt_Opacity;")
- << (VertexPresent | PropertyPresent);
+ << (VertexPresent | SourcePresent);
QTest::newRow("line comments between")
@@ -158,21 +160,21 @@ void tst_qquickshadereffect::lookThroughShaderCode_data()
"attribute//\nhighp//\nvec4//\nqt_Vertex;\n"
" //*/ uniform \n attribute //\\ \n highp //// \n vec2 //* \n qt_MultiTexCoord0;")
<< QByteArray("uniform// lowp float qt_Opacity;\nsampler2D source;")
- << (VertexPresent | TexCoordPresent | MatrixPresent | PropertyPresent);
+ << (VertexPresent | TexCoordPresent | MatrixPresent | SourcePresent);
QTest::newRow("block comments between")
<< QByteArray("uniform/*foo*/highp/*/bar/*/mat4/**//**/qt_Matrix;\n"
"attribute/**/highp/**/vec4/**/qt_Vertex;\n"
" /* * */ attribute /*///*/ highp /****/ vec2 /**/ qt_MultiTexCoord0;")
<< QByteArray("uniform/*/ uniform//lowp/*float qt_Opacity;*/sampler2D source;")
- << (VertexPresent | TexCoordPresent | MatrixPresent | PropertyPresent);
+ << (VertexPresent | TexCoordPresent | MatrixPresent | SourcePresent);
QTest::newRow("preprocessor directive between")
<< QByteArray("uniform\n#foo\nhighp\n#bar\nmat4\n#baz\\\nblimey\nqt_Matrix;\n"
"attribute\n#\nhighp\n#\nvec4\n#\nqt_Vertex;\n"
" #uniform \n attribute \n # foo \n highp \n # bar \n vec2 \n#baz \n qt_MultiTexCoord0;")
<< QByteArray("uniform\n#if lowp float qt_Opacity;\nsampler2D source;")
- << (VertexPresent | TexCoordPresent | MatrixPresent | PropertyPresent);
+ << (VertexPresent | TexCoordPresent | MatrixPresent | SourcePresent);
QTest::newRow("newline between")
<< QByteArray("uniform\nhighp\nmat4\nqt_Matrix\n;\n"
@@ -180,7 +182,7 @@ void tst_qquickshadereffect::lookThroughShaderCode_data()
" \n attribute \n highp \n vec2 \n qt_Multi\nTexCoord0 \n ;")
<< QByteArray("uniform\nsampler2D\nsource;"
"uniform lowp float qt_Opacity;")
- << (VertexPresent | MatrixPresent | OpacityPresent | PropertyPresent);
+ << (VertexPresent | MatrixPresent | OpacityPresent | SourcePresent);
QTest::newRow("extra characters #1")
@@ -221,28 +223,28 @@ void tst_qquickshadereffect::lookThroughShaderCode_data()
"attribute highp qt_MultiTexCoord0;\n")
<< QByteArray("uniform lowp float qt_Opacity;\n"
"uniform mediump float source;\n")
- << (MatrixPresent | OpacityPresent | PropertyPresent);
+ << (MatrixPresent | OpacityPresent | SourcePresent);
QTest::newRow("property name #1")
<< QByteArray("uniform highp vec3 _0aA9zZ;")
<< QByteArray(" ")
- << int(PropertyPresent);
+ << int(SourcePresent);
QTest::newRow("property name #2")
<< QByteArray("uniform mediump vec2 x86;")
<< QByteArray(" ")
- << int(PropertyPresent);
+ << int(SourcePresent);
QTest::newRow("property name #3")
<< QByteArray("uniform lowp float X;")
<< QByteArray(" ")
- << int(PropertyPresent);
+ << int(SourcePresent);
QTest::newRow("property name #4")
<< QByteArray("uniform highp mat4 mat4x4;")
<< QByteArray(" ")
- << int(PropertyPresent);
+ << int(SourcePresent);
}
void tst_qquickshadereffect::lookThroughShaderCode()
@@ -251,9 +253,11 @@ void tst_qquickshadereffect::lookThroughShaderCode()
QFETCH(QByteArray, fragmentShader);
QFETCH(int, presenceFlags);
- TestShaderEffect item;
- QMetaMethod dummyChangedSignal = QMetaMethod::fromSignal(&TestShaderEffect::dummyChanged);
- QVERIFY(!item.isConnected(dummyChangedSignal)); // Nothing connected yet.
+ QQmlEngine engine;
+ QQmlComponent component(&engine);
+ component.setData("import QtQuick 2.0\nimport ShaderEffectTest 1.0\nTestShaderEffect {}", QUrl());
+ QScopedPointer<TestShaderEffect> item(qobject_cast<TestShaderEffect*>(component.create()));
+ QCOMPARE(item->signalsConnected, 1);
QString expected;
if ((presenceFlags & VertexPresent) == 0)
@@ -265,12 +269,12 @@ void tst_qquickshadereffect::lookThroughShaderCode()
if ((presenceFlags & OpacityPresent) == 0)
expected += "Warning: Shaders are missing reference to \'qt_Opacity\'.\n";
- item.setVertexShader(vertexShader);
- item.setFragmentShader(fragmentShader);
- QCOMPARE(item.parseLog(), expected);
+ item->setVertexShader(vertexShader);
+ item->setFragmentShader(fragmentShader);
+ QCOMPARE(item->parseLog(), expected);
// If the uniform was successfully parsed, the notify signal has been connected to an update slot.
- QCOMPARE(item.isConnected(dummyChangedSignal), (presenceFlags & PropertyPresent) != 0);
+ QCOMPARE(item->signalsConnected, (presenceFlags & SourcePresent) ? 2 : 1);
}
void tst_qquickshadereffect::deleteSourceItem()
diff --git a/tests/auto/quick/qquicktext/BLACKLIST b/tests/auto/quick/qquicktext/BLACKLIST
index c2e7ef03c0..531d981159 100644
--- a/tests/auto/quick/qquicktext/BLACKLIST
+++ b/tests/auto/quick/qquicktext/BLACKLIST
@@ -2,3 +2,5 @@
*
[lineLaidOutRelayout]
msvc-2015
+[fontSizeMode]
+opensuse-42.1
diff --git a/tests/auto/quick/qquicktext/tst_qquicktext.cpp b/tests/auto/quick/qquicktext/tst_qquicktext.cpp
index f31859ee49..2032f72e26 100644
--- a/tests/auto/quick/qquicktext/tst_qquicktext.cpp
+++ b/tests/auto/quick/qquicktext/tst_qquicktext.cpp
@@ -147,8 +147,9 @@ private slots:
void padding();
- void zeroWidthAndElidedDoesntRender();
+ void hintingPreference();
+ void zeroWidthAndElidedDoesntRender();
void hAlignWidthDependsOnImplicitWidth_data();
void hAlignWidthDependsOnImplicitWidth();
@@ -4165,6 +4166,33 @@ void tst_qquicktext::padding()
delete root;
}
+void tst_qquicktext::hintingPreference()
+{
+ {
+ QString componentStr = "import QtQuick 2.0\nText { text: \"Hello world!\" }";
+ QQmlComponent textComponent(&engine);
+ textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
+
+ QVERIFY(textObject != 0);
+ QCOMPARE((int)textObject->font().hintingPreference(), (int)QFont::PreferDefaultHinting);
+
+ delete textObject;
+ }
+ {
+ QString componentStr = "import QtQuick 2.0\nText { text: \"Hello world!\"; font.hintingPreference: Font.PreferNoHinting }";
+ QQmlComponent textComponent(&engine);
+ textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
+ QQuickText *textObject = qobject_cast<QQuickText*>(textComponent.create());
+
+ QVERIFY(textObject != 0);
+ QCOMPARE((int)textObject->font().hintingPreference(), (int)QFont::PreferNoHinting);
+
+ delete textObject;
+ }
+}
+
+
void tst_qquicktext::zeroWidthAndElidedDoesntRender()
{
// Tests QTBUG-34990
diff --git a/tests/auto/quick/qquicktextedit/BLACKLIST b/tests/auto/quick/qquicktextedit/BLACKLIST
deleted file mode 100644
index 5f3208f848..0000000000
--- a/tests/auto/quick/qquicktextedit/BLACKLIST
+++ /dev/null
@@ -1,4 +0,0 @@
-[undo]
-*
-[undo_keypressevents]
-*
diff --git a/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp b/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp
index cb2a7bfd83..5ed0e9eea7 100644
--- a/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp
+++ b/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp
@@ -108,6 +108,7 @@ private slots:
void selectionOnFocusOut();
void focusOnPress();
void selection();
+ void overwriteMode();
void isRightToLeft_data();
void isRightToLeft();
void keySelection();
@@ -1467,6 +1468,74 @@ void tst_qquicktextedit::selection()
QVERIFY(textEditObject->selectedText().isNull());
}
+void tst_qquicktextedit::overwriteMode()
+{
+ QString componentStr = "import QtQuick 2.0\nTextEdit { focus: true; }";
+ QQmlComponent textEditComponent(&engine);
+ textEditComponent.setData(componentStr.toLatin1(), QUrl());
+ QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit*>(textEditComponent.create());
+ QVERIFY(textEdit != 0);
+
+ QSignalSpy spy(textEdit, SIGNAL(overwriteModeChanged(bool)));
+
+ QQuickWindow window;
+ textEdit->setParentItem(window.contentItem());
+ window.show();
+ window.requestActivate();
+ QTest::qWaitForWindowActive(&window);
+
+ QVERIFY(textEdit->hasActiveFocus());
+
+ textEdit->setOverwriteMode(true);
+ QCOMPARE(spy.count(), 1);
+ QCOMPARE(true, textEdit->overwriteMode());
+ textEdit->setOverwriteMode(false);
+ QCOMPARE(spy.count(), 2);
+ QCOMPARE(false, textEdit->overwriteMode());
+
+ QVERIFY(!textEdit->overwriteMode());
+ QString insertString = "Some first text";
+ for (int j = 0; j < insertString.length(); j++)
+ QTest::keyClick(&window, insertString.at(j).toLatin1());
+
+ QCOMPARE(textEdit->text(), QString("Some first text"));
+
+ textEdit->setOverwriteMode(true);
+ QCOMPARE(spy.count(), 3);
+ textEdit->setCursorPosition(5);
+
+ insertString = "shiny";
+ for (int j = 0; j < insertString.length(); j++)
+ QTest::keyClick(&window, insertString.at(j).toLatin1());
+ QCOMPARE(textEdit->text(), QString("Some shiny text"));
+
+ textEdit->setCursorPosition(textEdit->text().length());
+ QTest::keyClick(&window, Qt::Key_Enter);
+
+ textEdit->setOverwriteMode(false);
+ QCOMPARE(spy.count(), 4);
+
+ insertString = "Second paragraph";
+
+ for (int j = 0; j < insertString.length(); j++)
+ QTest::keyClick(&window, insertString.at(j).toLatin1());
+ QCOMPARE(textEdit->lineCount(), 2);
+
+ textEdit->setCursorPosition(15);
+
+ QCOMPARE(textEdit->cursorPosition(), 15);
+
+ textEdit->setOverwriteMode(true);
+ QCOMPARE(spy.count(), 5);
+
+ insertString = " blah";
+ for (int j = 0; j < insertString.length(); j++)
+ QTest::keyClick(&window, insertString.at(j).toLatin1());
+ QCOMPARE(textEdit->lineCount(), 2);
+
+ QCOMPARE(textEdit->text(), QString("Some shiny text blah\nSecond paragraph"));
+}
+
void tst_qquicktextedit::isRightToLeft_data()
{
QTest::addColumn<QString>("text");
diff --git a/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp b/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp
index ea88f9dadb..8dc3053d89 100644
--- a/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp
+++ b/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp
@@ -105,6 +105,7 @@ private slots:
void wrap();
void selection();
void persistentSelection();
+ void overwriteMode();
void isRightToLeft_data();
void isRightToLeft();
void moveCursorSelection_data();
@@ -782,6 +783,48 @@ void tst_qquicktextinput::persistentSelection()
QCOMPARE(input->property("selected").toString(), QLatin1String("ell"));
}
+void tst_qquicktextinput::overwriteMode()
+{
+ QString componentStr = "import QtQuick 2.0\nTextInput { focus: true; }";
+ QQmlComponent textInputComponent(&engine);
+ textInputComponent.setData(componentStr.toLatin1(), QUrl());
+ QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
+ QVERIFY(textInput != 0);
+
+ QSignalSpy spy(textInput, SIGNAL(overwriteModeChanged(bool)));
+
+ QQuickWindow window;
+ textInput->setParentItem(window.contentItem());
+ window.show();
+ window.requestActivate();
+ QTest::qWaitForWindowActive(&window);
+
+ QVERIFY(textInput->hasActiveFocus());
+
+ textInput->setOverwriteMode(true);
+ QCOMPARE(spy.count(), 1);
+ QCOMPARE(true, textInput->overwriteMode());
+ textInput->setOverwriteMode(false);
+ QCOMPARE(spy.count(), 2);
+ QCOMPARE(false, textInput->overwriteMode());
+
+ QVERIFY(!textInput->overwriteMode());
+ QString insertString = "Some first text";
+ for (int j = 0; j < insertString.length(); j++)
+ QTest::keyClick(&window, insertString.at(j).toLatin1());
+
+ QCOMPARE(textInput->text(), QString("Some first text"));
+
+ textInput->setOverwriteMode(true);
+ QCOMPARE(spy.count(), 3);
+ textInput->setCursorPosition(5);
+
+ insertString = "shiny";
+ for (int j = 0; j < insertString.length(); j++)
+ QTest::keyClick(&window, insertString.at(j).toLatin1());
+ QCOMPARE(textInput->text(), QString("Some shiny text"));
+}
+
void tst_qquicktextinput::isRightToLeft_data()
{
QTest::addColumn<QString>("text");
diff --git a/tests/auto/quick/qquickview/tst_qquickview.cpp b/tests/auto/quick/qquickview/tst_qquickview.cpp
index 05922ae20f..fa9192edb4 100644
--- a/tests/auto/quick/qquickview/tst_qquickview.cpp
+++ b/tests/auto/quick/qquickview/tst_qquickview.cpp
@@ -36,6 +36,30 @@
#include <QtCore/QDebug>
#include <QtQml/qqmlengine.h>
+class SizeChangesListener : public QObject, public QVector<QSize>
+{
+ Q_OBJECT
+public:
+ explicit SizeChangesListener(QQuickItem *item);
+private slots:
+ void onSizeChanged();
+private:
+ QQuickItem *item;
+
+};
+
+SizeChangesListener::SizeChangesListener(QQuickItem *item) :
+ item(item)
+{
+ connect(item, &QQuickItem::widthChanged, this, &SizeChangesListener::onSizeChanged);
+ connect(item, &QQuickItem::heightChanged, this, &SizeChangesListener::onSizeChanged);
+}
+
+void SizeChangesListener::onSizeChanged()
+{
+ append(QSize(item->width(), item->height()));
+}
+
class tst_QQuickView : public QQmlDataTest
{
Q_OBJECT
@@ -127,7 +151,6 @@ void tst_QQuickView::resizemodeitem()
QCOMPARE(item->width(), 80.0);
QCOMPARE(item->height(), 100.0);
QTRY_COMPARE(view->size(), QSize(80, 100));
- QCOMPARE(view->size(), QSize(80, 100));
QCOMPARE(view->size(), view->sizeHint());
// size update from root object disabled
@@ -139,8 +162,16 @@ void tst_QQuickView::resizemodeitem()
QCOMPARE(QSize(item->width(), item->height()), view->sizeHint());
// size update from view
+ QCoreApplication::processEvents(); // make sure the last resize events are gone
+ SizeChangesListener sizeListener(item);
view->resize(QSize(200,300));
QTRY_COMPARE(item->width(), 200.0);
+
+ for (int i = 0; i < sizeListener.count(); ++i) {
+ // Check that we have the correct geometry on all signals
+ QCOMPARE(sizeListener.at(i), view->size());
+ }
+
QCOMPARE(item->height(), 300.0);
QCOMPARE(view->size(), QSize(200, 300));
QCOMPARE(view->size(), view->sizeHint());
diff --git a/tests/auto/quick/qquickwindow/qquickwindow.pro b/tests/auto/quick/qquickwindow/qquickwindow.pro
index f0d287f30f..05093ba8e0 100644
--- a/tests/auto/quick/qquickwindow/qquickwindow.pro
+++ b/tests/auto/quick/qquickwindow/qquickwindow.pro
@@ -7,7 +7,7 @@ include(../shared/util.pri)
macx:CONFIG -= app_bundle
-QT += core-private gui-private qml-private quick-private testlib
+QT += core-private qml-private quick-private testlib
TESTDATA = data/*
diff --git a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp
index 1365e8b751..acccac8eca 100644
--- a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp
+++ b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp
@@ -40,10 +40,11 @@
#include "../shared/visualtestutil.h"
#include "../shared/viewtestutil.h"
#include <QSignalSpy>
-#include <qpa/qwindowsysteminterface.h>
#include <private/qquickwindow_p.h>
#include <private/qguiapplication_p.h>
#include <QRunnable>
+#include <QOpenGLFunctions>
+#include <QSGRendererInterface>
struct TouchEventData {
QEvent::Type type;
@@ -156,6 +157,7 @@ public:
lastVelocity = lastVelocityFromMouseMove = QVector2D();
lastMousePos = QPointF();
lastMouseCapabilityFlags = 0;
+ touchEventCount = 0;
}
static void clearMousePressCounter()
@@ -273,25 +275,18 @@ class tst_qquickwindow : public QQmlDataTest
Q_OBJECT
public:
tst_qquickwindow()
+ : touchDevice(QTest::createTouchDevice())
+ , touchDeviceWithVelocity(QTest::createTouchDevice())
{
QQuickWindow::setDefaultAlphaBuffer(true);
+ touchDeviceWithVelocity->setCapabilities(QTouchDevice::Position | QTouchDevice::Velocity);
}
private slots:
- void initTestCase()
- {
- QQmlDataTest::initTestCase();
- touchDevice = new QTouchDevice;
- touchDevice->setType(QTouchDevice::TouchScreen);
- QWindowSystemInterface::registerTouchDevice(touchDevice);
- touchDeviceWithVelocity = new QTouchDevice;
- touchDeviceWithVelocity->setType(QTouchDevice::TouchScreen);
- touchDeviceWithVelocity->setCapabilities(QTouchDevice::Position | QTouchDevice::Velocity);
- QWindowSystemInterface::registerTouchDevice(touchDeviceWithVelocity);
- }
void cleanup();
-
+#ifndef QT_NO_OPENGL
void openglContextCreatedSignal();
+#endif
void aboutToStopSignal();
void constantUpdates();
@@ -308,6 +303,9 @@ private slots:
void touchEvent_reentrant();
void touchEvent_velocity();
+ void mergeTouchPointLists_data();
+ void mergeTouchPointLists();
+
void mouseFromTouch_basic();
void clearWindow();
@@ -368,18 +366,21 @@ private slots:
void testHoverChildMouseEventFilter();
void testHoverTimestamp();
+
+ void pointerEventTypeAndPointCount();
+
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 *>();
@@ -391,12 +392,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;
@@ -438,8 +442,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()));
@@ -525,9 +528,8 @@ void tst_qquickwindow::touchEvent_basic()
// press single point
QTest::touchEvent(window, touchDevice).press(0, topItem->mapToScene(pos).toPoint(),window);
- QTest::qWait(50);
-
- QCOMPARE(topItem->lastEvent.touchPoints.count(), 1);
+ QQuickTouchUtils::flush(window);
+ QTRY_COMPARE(topItem->lastEvent.touchPoints.count(), 1);
QVERIFY(middleItem->lastEvent.touchPoints.isEmpty());
QVERIFY(bottomItem->lastEvent.touchPoints.isEmpty());
@@ -535,9 +537,10 @@ void tst_qquickwindow::touchEvent_basic()
// would put the decorated window at that position rather than the window itself.
COMPARE_TOUCH_DATA(topItem->lastEvent, makeTouchData(QEvent::TouchBegin, window, Qt::TouchPointPressed, makeTouchPoint(topItem, pos)));
topItem->reset();
+ QTest::touchEvent(window, touchDevice).release(0, topItem->mapToScene(pos).toPoint(), window);
// press multiple points
- QTest::touchEvent(window, touchDevice).press(0, topItem->mapToScene(pos).toPoint(),window)
+ QTest::touchEvent(window, touchDevice).press(0, topItem->mapToScene(pos).toPoint(), window)
.press(1, bottomItem->mapToScene(pos).toPoint(), window);
QQuickTouchUtils::flush(window);
QCOMPARE(topItem->lastEvent.touchPoints.count(), 1);
@@ -547,6 +550,7 @@ void tst_qquickwindow::touchEvent_basic()
COMPARE_TOUCH_DATA(bottomItem->lastEvent, makeTouchData(QEvent::TouchBegin, window, Qt::TouchPointPressed, makeTouchPoint(bottomItem, pos)));
topItem->reset();
bottomItem->reset();
+ QTest::touchEvent(window, touchDevice).release(0, topItem->mapToScene(pos).toPoint(), window).release(1, bottomItem->mapToScene(pos).toPoint(), window);
// touch point on top item moves to bottom item, but top item should still receive the event
QTest::touchEvent(window, touchDevice).press(0, topItem->mapToScene(pos).toPoint(), window);
@@ -557,6 +561,7 @@ void tst_qquickwindow::touchEvent_basic()
COMPARE_TOUCH_DATA(topItem->lastEvent, makeTouchData(QEvent::TouchUpdate, window, Qt::TouchPointMoved,
makeTouchPoint(topItem, topItem->mapFromItem(bottomItem, pos), pos)));
topItem->reset();
+ QTest::touchEvent(window, touchDevice).release(0, bottomItem->mapToScene(pos).toPoint(), window);
// touch point on bottom item moves to top item, but bottom item should still receive the event
QTest::touchEvent(window, touchDevice).press(0, bottomItem->mapToScene(pos).toPoint(), window);
@@ -567,6 +572,7 @@ void tst_qquickwindow::touchEvent_basic()
COMPARE_TOUCH_DATA(bottomItem->lastEvent, makeTouchData(QEvent::TouchUpdate, window, Qt::TouchPointMoved,
makeTouchPoint(bottomItem, bottomItem->mapFromItem(topItem, pos), pos)));
bottomItem->reset();
+ QTest::touchEvent(window, touchDevice).release(0, bottomItem->mapToScene(pos).toPoint(), window);
// a single stationary press on an item shouldn't cause an event
QTest::touchEvent(window, touchDevice).press(0, topItem->mapToScene(pos).toPoint(), window);
@@ -671,6 +677,7 @@ void tst_qquickwindow::touchEvent_propagation()
QVERIFY(bottomItem->lastEvent.touchPoints.isEmpty());
COMPARE_TOUCH_DATA(middleItem->lastEvent, makeTouchData(QEvent::TouchBegin, window, Qt::TouchPointPressed,
makeTouchPoint(middleItem, middleItem->mapFromItem(topItem, pos))));
+ QTest::touchEvent(window, touchDevice).release(0, pointInTopItem, window);
// touch top and middle items, middle item should get both events
QTest::touchEvent(window, touchDevice).press(0, pointInTopItem, window)
@@ -682,6 +689,8 @@ void tst_qquickwindow::touchEvent_propagation()
COMPARE_TOUCH_DATA(middleItem->lastEvent, makeTouchData(QEvent::TouchBegin, window, Qt::TouchPointPressed,
(QList<QTouchEvent::TouchPoint>() << makeTouchPoint(middleItem, middleItem->mapFromItem(topItem, pos))
<< makeTouchPoint(middleItem, pos) )));
+ QTest::touchEvent(window, touchDevice).release(0, pointInTopItem, window)
+ .release(1, pointInMiddleItem, window);
middleItem->reset();
// disable middleItem as well
@@ -706,6 +715,8 @@ void tst_qquickwindow::touchEvent_propagation()
bottomItem->acceptTouchEvents = acceptTouchEvents;
bottomItem->setEnabled(enableItem);
bottomItem->setVisible(showItem);
+ QTest::touchEvent(window, touchDevice).release(0, pointInTopItem, window)
+ .release(1, pointInMiddleItem, window);
// no events should be received
QTest::touchEvent(window, touchDevice).press(0, pointInTopItem, window)
@@ -715,7 +726,9 @@ void tst_qquickwindow::touchEvent_propagation()
QVERIFY(topItem->lastEvent.touchPoints.isEmpty());
QVERIFY(middleItem->lastEvent.touchPoints.isEmpty());
QVERIFY(bottomItem->lastEvent.touchPoints.isEmpty());
-
+ QTest::touchEvent(window, touchDevice).release(0, pointInTopItem, window)
+ .release(1, pointInMiddleItem, window)
+ .release(2, pointInBottomItem, window);
topItem->reset();
middleItem->reset();
bottomItem->reset();
@@ -741,6 +754,7 @@ void tst_qquickwindow::touchEvent_propagation()
COMPARE_TOUCH_DATA(topItem->lastEvent, makeTouchData(QEvent::TouchBegin, window, Qt::TouchPointPressed,
makeTouchPoint(topItem, pos)));
}
+ QTest::touchEvent(window, touchDevice).release(0, pointInTopItem, window);
delete topItem;
delete middleItem;
@@ -859,6 +873,8 @@ void tst_qquickwindow::touchEvent_velocity()
QWindowSystemInterface::handleTouchEvent(window, touchDeviceWithVelocity, points);
QGuiApplication::processEvents();
QQuickTouchUtils::flush(window);
+ QCOMPARE(item->touchEventCount, 1);
+
points[0].state = Qt::TouchPointMoved;
points[0].area.adjust(5, 5, 5, 5);
QVector2D velocity(1.5, 2.5);
@@ -891,6 +907,67 @@ void tst_qquickwindow::touchEvent_velocity()
delete item;
}
+void tst_qquickwindow::mergeTouchPointLists_data()
+{
+ QTest::addColumn<QVector<QQuickItem*>>("list1");
+ QTest::addColumn<QVector<QQuickItem*>>("list2");
+ QTest::addColumn<QVector<QQuickItem*>>("expected");
+ QTest::addColumn<bool>("showItem");
+
+ // FIXME: do not leak all these items
+ auto item1 = new QQuickItem();
+ auto item2 = new QQuickItem();
+ auto item3 = new QQuickItem();
+ auto item4 = new QQuickItem();
+ auto item5 = new QQuickItem();
+
+ QTest::newRow("empty") << QVector<QQuickItem*>() << QVector<QQuickItem*>() << QVector<QQuickItem*>();
+ QTest::newRow("single list left")
+ << (QVector<QQuickItem*>() << item1 << item2 << item3)
+ << QVector<QQuickItem*>()
+ << (QVector<QQuickItem*>() << item1 << item2 << item3);
+ QTest::newRow("single list right")
+ << QVector<QQuickItem*>()
+ << (QVector<QQuickItem*>() << item1 << item2 << item3)
+ << (QVector<QQuickItem*>() << item1 << item2 << item3);
+ QTest::newRow("two lists identical")
+ << (QVector<QQuickItem*>() << item1 << item2 << item3)
+ << (QVector<QQuickItem*>() << item1 << item2 << item3)
+ << (QVector<QQuickItem*>() << item1 << item2 << item3);
+ QTest::newRow("two lists 1")
+ << (QVector<QQuickItem*>() << item1 << item2 << item5)
+ << (QVector<QQuickItem*>() << item3 << item4 << item5)
+ << (QVector<QQuickItem*>() << item1 << item2 << item3 << item4 << item5);
+ QTest::newRow("two lists 2")
+ << (QVector<QQuickItem*>() << item1 << item2 << item5)
+ << (QVector<QQuickItem*>() << item3 << item4 << item5)
+ << (QVector<QQuickItem*>() << item1 << item2 << item3 << item4 << item5);
+ QTest::newRow("two lists 3")
+ << (QVector<QQuickItem*>() << item1 << item2 << item3)
+ << (QVector<QQuickItem*>() << item1 << item4 << item5)
+ << (QVector<QQuickItem*>() << item1 << item2 << item3 << item4 << item5);
+ QTest::newRow("two lists 4")
+ << (QVector<QQuickItem*>() << item1 << item3 << item4)
+ << (QVector<QQuickItem*>() << item2 << item3 << item5)
+ << (QVector<QQuickItem*>() << item1 << item2 << item3 << item4 << item5);
+ QTest::newRow("two lists 5")
+ << (QVector<QQuickItem*>() << item1 << item2 << item4)
+ << (QVector<QQuickItem*>() << item1 << item3 << item4)
+ << (QVector<QQuickItem*>() << item1 << item2 << item3 << item4);
+}
+
+void tst_qquickwindow::mergeTouchPointLists()
+{
+ QFETCH(QVector<QQuickItem*>, list1);
+ QFETCH(QVector<QQuickItem*>, list2);
+ QFETCH(QVector<QQuickItem*>, expected);
+
+ QQuickWindow win;
+ auto windowPrivate = QQuickWindowPrivate::get(&win);
+ auto targetList = windowPrivate->mergePointerTargets(list1, list2);
+ QCOMPARE(targetList, expected);
+}
+
void tst_qquickwindow::mouseFromTouch_basic()
{
// Turn off accepting touch events with acceptTouchEvents. This
@@ -1229,13 +1306,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();
@@ -1245,15 +1323,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());
@@ -1264,7 +1343,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();
@@ -1285,8 +1365,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;
}
@@ -1593,7 +1672,6 @@ void tst_qquickwindow::hideThenDelete()
QSignalSpy *openglDestroyed = 0;
QSignalSpy *sgInvalidated = 0;
- bool threaded = false;
{
QQuickWindow window;
@@ -1608,9 +1686,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();
@@ -1618,6 +1700,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)
@@ -1632,7 +1717,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()
@@ -2029,6 +2117,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());
@@ -2037,12 +2128,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);
}
@@ -2091,7 +2183,7 @@ public:
}
static int deleted;
};
-
+#ifndef QT_NO_OPENGL
class GlRenderJob : public QRunnable
{
public:
@@ -2113,7 +2205,7 @@ public:
QMutex *mutex;
QWaitCondition *condition;
};
-
+#endif
int RenderJob::deleted = 0;
void tst_qquickwindow::testRenderJob()
@@ -2162,25 +2254,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
@@ -2198,7 +2294,6 @@ void tst_qquickwindow::testRenderJob()
class EventCounter : public QQuickRectangle
{
- Q_OBJECT
public:
EventCounter(QQuickItem *parent = 0)
: QQuickRectangle(parent)
@@ -2388,6 +2483,58 @@ void tst_qquickwindow::testHoverTimestamp()
QCOMPARE(hoverConsumer->hoverTimestamps.last(), 5UL);
}
+void tst_qquickwindow::pointerEventTypeAndPointCount()
+{
+ QPointF localPosition(33, 66);
+ QPointF scenePosition(133, 166);
+ QPointF screenPosition(333, 366);
+ QMouseEvent me(QEvent::MouseButtonPress, localPosition, scenePosition, screenPosition,
+ Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
+ QTouchEvent te(QEvent::TouchBegin, touchDevice, Qt::NoModifier, Qt::TouchPointPressed,
+ QList<QTouchEvent::TouchPoint>() << QTouchEvent::TouchPoint(1));
+
+
+ QQuickPointerMouseEvent pme;
+ pme.reset(&me);
+ QVERIFY(pme.isValid());
+ QCOMPARE(pme.asMouseEvent(localPosition), &me);
+ QVERIFY(pme.asPointerMouseEvent());
+ QVERIFY(!pme.asPointerTouchEvent());
+ QVERIFY(!pme.asPointerTabletEvent());
+// QVERIFY(!pe->asTabletEvent()); // TODO
+ QCOMPARE(pme.pointCount(), 1);
+ QCOMPARE(pme.point(0)->scenePos(), scenePosition);
+ QCOMPARE(pme.asMouseEvent(localPosition)->localPos(), localPosition);
+ QCOMPARE(pme.asMouseEvent(localPosition)->screenPos(), screenPosition);
+
+ QQuickPointerTouchEvent pte;
+ pte.reset(&te);
+ QVERIFY(pte.isValid());
+ QCOMPARE(pte.asTouchEvent(), &te);
+ QVERIFY(!pte.asPointerMouseEvent());
+ QVERIFY(pte.asPointerTouchEvent());
+ QVERIFY(!pte.asPointerTabletEvent());
+ QVERIFY(pte.asTouchEvent());
+// QVERIFY(!pte.asTabletEvent()); // TODO
+ QCOMPARE(pte.pointCount(), 1);
+ QCOMPARE(pte.touchPointById(1)->id(), 1);
+ QVERIFY(!pte.touchPointById(0));
+
+ te.setTouchPoints(QList<QTouchEvent::TouchPoint>() << QTouchEvent::TouchPoint(1) << QTouchEvent::TouchPoint(2));
+ pte.reset(&te);
+ QCOMPARE(pte.pointCount(), 2);
+ QCOMPARE(pte.touchPointById(1)->id(), 1);
+ QCOMPARE(pte.touchPointById(2)->id(), 2);
+ QVERIFY(!pte.touchPointById(0));
+
+ te.setTouchPoints(QList<QTouchEvent::TouchPoint>() << QTouchEvent::TouchPoint(2));
+ pte.reset(&te);
+ QCOMPARE(pte.pointCount(), 1);
+ QCOMPARE(pte.touchPointById(2)->id(), 2);
+ QVERIFY(!pte.touchPointById(1));
+ QVERIFY(!pte.touchPointById(0));
+}
+
QTEST_MAIN(tst_qquickwindow)
#include "tst_qquickwindow.moc"
diff --git a/tests/auto/quick/quick.pro b/tests/auto/quick/quick.pro
index 2e43702e7c..c7ba4de86c 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
+qtConfig(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,7 @@ QUICKTESTS = \
qquickflickable \
qquickflipable \
qquickfocusscope \
- qquickframebufferobject \
+ qquickgraphicsinfo \
qquickgridview \
qquickimage \
qquickitem \
@@ -58,16 +67,13 @@ QUICKTESTS = \
qquickloader \
qquickmousearea \
qquickmultipointtoucharea \
- qquickopenglinfo \
qquickpainteditem \
qquickpathview \
qquickpincharea \
qquickpositioners \
qquickrectangle \
qquickrepeater \
- qquickshadereffect \
qquickshortcut \
- qquickspritesequence \
qquicktext \
qquicktextdocument \
qquicktextedit \
@@ -82,9 +88,9 @@ QUICKTESTS = \
SUBDIRS += $$PUBLICTESTS
-!contains(QT_CONFIG, accessibility):QUICKTESTS -= qquickaccessible
+!qtConfig(accessibility):QUICKTESTS -= qquickaccessible
-contains(QT_CONFIG, private_tests) {
+qtConfig(private_tests) {
SUBDIRS += $$PRIVATETESTS
SUBDIRS += $$QUICKTESTS
}
diff --git a/tests/auto/quick/rendernode/tst_rendernode.cpp b/tests/auto/quick/rendernode/tst_rendernode.cpp
index 249612797b..e0fe2c42fc 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;
@@ -259,8 +262,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/scenegraph.pro b/tests/auto/quick/scenegraph/scenegraph.pro
index 320228bd08..40ff7750cc 100644
--- a/tests/auto/quick/scenegraph/scenegraph.pro
+++ b/tests/auto/quick/scenegraph/scenegraph.pro
@@ -3,6 +3,7 @@ TARGET = tst_scenegraph
SOURCES += tst_scenegraph.cpp
include (../../shared/util.pri)
+include (../shared/util.pri)
macx:CONFIG -= app_bundle
diff --git a/tests/auto/quick/scenegraph/tst_scenegraph.cpp b/tests/auto/quick/scenegraph/tst_scenegraph.cpp
index 1e46727526..f6d624d871 100644
--- a/tests/auto/quick/scenegraph/tst_scenegraph.cpp
+++ b/tests/auto/quick/scenegraph/tst_scenegraph.cpp
@@ -28,18 +28,26 @@
#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>
#include "../../shared/util.h"
+#include "../shared/visualtestutil.h"
+
+using namespace QQuickVisualTestUtil;
class PerPixelRect : public QQuickItem
{
@@ -97,9 +105,9 @@ private slots:
void render_data();
void render();
-
+#ifndef QT_NO_OPENGL
void hideWithOtherContext();
-
+#endif
void createTextureFromImage_data();
void createTextureFromImage();
@@ -122,6 +130,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();
@@ -154,6 +163,7 @@ void tst_SceneGraph::initTestCase()
qDebug() << "Broken Mipmap: " << m_brokenMipmapSupport;
context.doneCurrent();
+#endif
}
QQuickView *tst_SceneGraph::createView(const QString &file, QWindow *parent, int x, int y, int w, int h)
@@ -169,12 +179,17 @@ QQuickView *tst_SceneGraph::createView(const QString &file, QWindow *parent, int
// 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;
@@ -182,44 +197,6 @@ bool containsSomethingOtherThanWhite(const QImage &image)
return false;
}
-// When running on native Nvidia graphics cards on linux, the
-// distance field glyph pixels have a measurable, but not visible
-// pixel error. Use a custom compare function to avoid
-//
-// This was GT-216 with the ubuntu "nvidia-319" driver package.
-// llvmpipe does not show the same issue.
-//
-bool compareImages(const QImage &ia, const QImage &ib)
-{
- if (ia.size() != ib.size())
- qDebug() << "images are of different size" << ia.size() << ib.size();
- Q_ASSERT(ia.size() == ib.size());
- Q_ASSERT(ia.format() == ib.format());
-
- int w = ia.width();
- int h = ia.height();
- const int tolerance = 5;
- for (int y=0; y<h; ++y) {
- const uint *as= (const uint *) ia.constScanLine(y);
- const uint *bs= (const uint *) ib.constScanLine(y);
- for (int x=0; x<w; ++x) {
- uint a = as[x];
- uint b = bs[x];
-
- // No tolerance for error in the alpha.
- if ((a & 0xff000000) != (b & 0xff000000))
- return false;
- if (qAbs(qRed(a) - qRed(b)) > tolerance)
- return false;
- if (qAbs(qRed(a) - qRed(b)) > tolerance)
- return false;
- if (qAbs(qRed(a) - qRed(b)) > tolerance)
- return false;
- }
- }
- return true;
-}
-
void tst_SceneGraph::manyWindows_data()
{
QTest::addColumn<QString>("file");
@@ -245,24 +222,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());
@@ -451,6 +430,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);
@@ -493,6 +479,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.
@@ -513,6 +500,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...
@@ -525,6 +515,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()
{
@@ -550,6 +541,10 @@ void tst_SceneGraph::createTextureFromImage()
QFETCH(bool, expectedAlpha);
QQuickView view;
+ view.show();
+ QTest::qWaitForWindowExposed(&view);
+ QTRY_VERIFY(view.isSceneGraphInitialized());
+
QScopedPointer<QSGTexture> texture(view.createTextureFromImage(image, (QQuickWindow::CreateTextureOptions) flags));
QCOMPARE(texture->hasAlphaChannel(), expectedAlpha);
}
diff --git a/tests/auto/quick/shared/visualtestutil.cpp b/tests/auto/quick/shared/visualtestutil.cpp
index 3e18c83ee2..eabfe5368b 100644
--- a/tests/auto/quick/shared/visualtestutil.cpp
+++ b/tests/auto/quick/shared/visualtestutil.cpp
@@ -61,3 +61,38 @@ void QQuickVisualTestUtil::dumpTree(QQuickItem *parent, int depth)
}
}
+// A custom compare function to avoid issues such as:
+// When running on native Nvidia graphics cards on linux, the
+// distance field glyph pixels have a measurable, but not visible
+// pixel error. This was GT-216 with the ubuntu "nvidia-319" driver package.
+// llvmpipe does not show the same issue.
+bool QQuickVisualTestUtil::compareImages(const QImage &ia, const QImage &ib)
+{
+ if (ia.size() != ib.size())
+ qDebug() << "images are of different size" << ia.size() << ib.size();
+ Q_ASSERT(ia.size() == ib.size());
+ Q_ASSERT(ia.format() == ib.format());
+
+ int w = ia.width();
+ int h = ia.height();
+ const int tolerance = 5;
+ for (int y=0; y<h; ++y) {
+ const uint *as= (const uint *) ia.constScanLine(y);
+ const uint *bs= (const uint *) ib.constScanLine(y);
+ for (int x=0; x<w; ++x) {
+ uint a = as[x];
+ uint b = bs[x];
+
+ // No tolerance for error in the alpha.
+ if ((a & 0xff000000) != (b & 0xff000000))
+ return false;
+ if (qAbs(qRed(a) - qRed(b)) > tolerance)
+ return false;
+ if (qAbs(qRed(a) - qRed(b)) > tolerance)
+ return false;
+ if (qAbs(qRed(a) - qRed(b)) > tolerance)
+ return false;
+ }
+ }
+ return true;
+}
diff --git a/tests/auto/quick/shared/visualtestutil.h b/tests/auto/quick/shared/visualtestutil.h
index 2b377a4bab..2daf86cd83 100644
--- a/tests/auto/quick/shared/visualtestutil.h
+++ b/tests/auto/quick/shared/visualtestutil.h
@@ -96,6 +96,8 @@ namespace QQuickVisualTestUtil
items << qobject_cast<QQuickItem*>(findItem<T>(parent, objectName, indexes[i]));
return items;
}
+
+ bool compareImages(const QImage &ia, const QImage &ib);
}
#define QQUICK_VERIFY_POLISH(item) \
diff --git a/tests/auto/quick/touchmouse/data/touchpointdeliveryorder.qml b/tests/auto/quick/touchmouse/data/touchpointdeliveryorder.qml
new file mode 100644
index 0000000000..9f67c226a0
--- /dev/null
+++ b/tests/auto/quick/touchmouse/data/touchpointdeliveryorder.qml
@@ -0,0 +1,39 @@
+import QtQuick 2.0
+import Qt.test 1.0
+
+Rectangle {
+ id: root
+
+ width: 600
+ height: 400
+
+ EventItem {
+ objectName: "background"
+ width: 600
+ height: 400
+ Rectangle { anchors.fill: parent; color: "lightsteelblue" }
+
+ EventItem {
+ objectName: "left"
+ width: 300
+ height: 300
+ Rectangle { anchors.fill: parent; color: "yellow" }
+ }
+
+ EventItem {
+ objectName: "right"
+ x: 300
+ width: 300
+ height: 300
+ Rectangle { anchors.fill: parent; color: "green" }
+ }
+
+ EventItem {
+ objectName: "middle"
+ x: 150
+ width: 300
+ height: 300
+ Rectangle { anchors.fill: parent; color: "blue" }
+ }
+ }
+}
diff --git a/tests/auto/quick/touchmouse/touchmouse.pro b/tests/auto/quick/touchmouse/touchmouse.pro
index 0df9bc53d3..49818bb399 100644
--- a/tests/auto/quick/touchmouse/touchmouse.pro
+++ b/tests/auto/quick/touchmouse/touchmouse.pro
@@ -1,7 +1,7 @@
CONFIG += testcase
TARGET = tst_touchmouse
-QT += core-private gui-private qml-private quick-private testlib
+QT += core-private qml-private quick-private testlib
macx:CONFIG -= app_bundle
diff --git a/tests/auto/quick/touchmouse/tst_touchmouse.cpp b/tests/auto/quick/touchmouse/tst_touchmouse.cpp
index 72cc76a855..309c01dcdc 100644
--- a/tests/auto/quick/touchmouse/tst_touchmouse.cpp
+++ b/tests/auto/quick/touchmouse/tst_touchmouse.cpp
@@ -33,13 +33,12 @@
#include <QtQuick/qquickview.h>
#include <QtQuick/qquickitem.h>
+#include <QtQuick/private/qquickevents_p_p.h>
#include <QtQuick/private/qquickmousearea_p.h>
#include <QtQuick/private/qquickmultipointtoucharea_p.h>
#include <QtQuick/private/qquickpincharea_p.h>
#include <QtQuick/private/qquickflickable_p.h>
-#include <qpa/qwindowsysteminterface.h>
-
-#include <private/qquickwindow_p.h>
+#include <QtQuick/private/qquickwindow_p.h>
#include <QtQml/qqmlengine.h>
#include <QtQml/qqmlproperty.h>
@@ -66,15 +65,22 @@ struct Event
class EventItem : public QQuickItem
{
Q_OBJECT
+
+Q_SIGNALS:
+ void onTouchEvent(QQuickItem *receiver);
+
public:
EventItem(QQuickItem *parent = 0)
: QQuickItem(parent), acceptMouse(false), acceptTouch(false), filterTouch(false)
- {}
+ {
+ setAcceptedMouseButtons(Qt::LeftButton);
+ }
void touchEvent(QTouchEvent *event)
{
eventList.append(Event(event->type(), event->touchPoints()));
event->setAccepted(acceptTouch);
+ emit onTouchEvent(this);
}
void mousePressEvent(QMouseEvent *event)
{
@@ -132,7 +138,7 @@ class tst_TouchMouse : public QQmlDataTest
Q_OBJECT
public:
tst_TouchMouse()
- :device(0)
+ :device(QTest::createTouchDevice())
{}
private slots:
@@ -145,6 +151,7 @@ private slots:
void mouseOverTouch();
void buttonOnFlickable();
+ void buttonOnDelayedPressFlickable_data();
void buttonOnDelayedPressFlickable();
void buttonOnTouch();
@@ -155,6 +162,7 @@ private slots:
void tapOnDismissiveTopMouseAreaClicksBottomOne();
void touchGrabCausesMouseUngrab();
+ void touchPointDeliveryOrder();
void hoverEnabled();
@@ -191,11 +199,6 @@ void tst_TouchMouse::initTestCase()
QQmlDataTest::initTestCase();
qmlRegisterType<EventItem>("Qt.test", 1, 0, "EventItem");
- if (!device) {
- device = new QTouchDevice;
- device->setType(QTouchDevice::TouchScreen);
- QWindowSystemInterface::registerTouchDevice(device);
- }
}
void tst_TouchMouse::simpleTouchEvent()
@@ -217,15 +220,17 @@ void tst_TouchMouse::simpleTouchEvent()
p1 = QPoint(20, 20);
QTest::touchEvent(window, device).press(0, p1, window);
QQuickTouchUtils::flush(window);
- QCOMPARE(eventItem1->eventList.size(), 1);
+ // Get a touch and then mouse event offered
+ QCOMPARE(eventItem1->eventList.size(), 2);
QCOMPARE(eventItem1->eventList.at(0).type, QEvent::TouchBegin);
p1 += QPoint(10, 0);
QTest::touchEvent(window, device).move(0, p1, window);
QQuickTouchUtils::flush(window);
- QCOMPARE(eventItem1->eventList.size(), 1);
+ // Not accepted, no updates
+ QCOMPARE(eventItem1->eventList.size(), 2);
QTest::touchEvent(window, device).release(0, p1, window);
QQuickTouchUtils::flush(window);
- QCOMPARE(eventItem1->eventList.size(), 1);
+ QCOMPARE(eventItem1->eventList.size(), 2);
eventItem1->eventList.clear();
// Accept touch
@@ -256,8 +261,7 @@ void tst_TouchMouse::simpleTouchEvent()
QCOMPARE(eventItem1->eventList.size(), 2);
QCOMPARE(eventItem1->eventList.at(0).type, QEvent::TouchBegin);
QCOMPARE(eventItem1->eventList.at(1).type, QEvent::MouseButtonPress);
- QQuickWindowPrivate *windowPriv = QQuickWindowPrivate::get(window);
- QCOMPARE(windowPriv->mouseGrabberItem, eventItem1);
+ QCOMPARE(window->mouseGrabberItem(), eventItem1);
QPoint localPos = eventItem1->mapFromScene(p1).toPoint();
QPoint globalPos = window->mapToGlobal(p1);
@@ -292,17 +296,16 @@ void tst_TouchMouse::simpleTouchEvent()
p1 = QPoint(20, 20);
QTest::touchEvent(window, device).press(0, p1, window);
QQuickTouchUtils::flush(window);
- QCOMPARE(eventItem1->eventList.size(), 3);
+ QCOMPARE(eventItem1->eventList.size(), 2);
QCOMPARE(eventItem1->eventList.at(0).type, QEvent::TouchBegin);
QCOMPARE(eventItem1->eventList.at(1).type, QEvent::MouseButtonPress);
- QCOMPARE(eventItem1->eventList.at(2).type, QEvent::UngrabMouse);
p1 += QPoint(10, 0);
QTest::touchEvent(window, device).move(0, p1, window);
QQuickTouchUtils::flush(window);
- QCOMPARE(eventItem1->eventList.size(), 3);
+ QCOMPARE(eventItem1->eventList.size(), 2);
QTest::touchEvent(window, device).release(0, p1, window);
QQuickTouchUtils::flush(window);
- QCOMPARE(eventItem1->eventList.size(), 3);
+ QCOMPARE(eventItem1->eventList.size(), 2);
eventItem1->eventList.clear();
// wait to avoid getting a double click event
@@ -579,8 +582,9 @@ void tst_TouchMouse::buttonOnFlickable()
QQuickWindowPrivate *windowPriv = QQuickWindowPrivate::get(window);
QCOMPARE(windowPriv->touchMouseId, 0);
- QCOMPARE(windowPriv->itemForTouchPointId[0], eventItem1);
- QCOMPARE(windowPriv->mouseGrabberItem, eventItem1);
+ auto pointerEvent = QQuickPointerDevice::touchDevices().at(0)->pointerEvent();
+ QCOMPARE(pointerEvent->point(0)->grabber(), eventItem1);
+ QCOMPARE(window->mouseGrabberItem(), eventItem1);
p1 += QPoint(0, -10);
QPoint p2 = p1 + QPoint(0, -10);
@@ -598,9 +602,9 @@ void tst_TouchMouse::buttonOnFlickable()
QCOMPARE(eventItem1->eventList.at(2).type, QEvent::TouchUpdate);
QCOMPARE(eventItem1->eventList.at(3).type, QEvent::MouseMove);
- QCOMPARE(windowPriv->mouseGrabberItem, flickable);
+ QCOMPARE(window->mouseGrabberItem(), flickable);
QCOMPARE(windowPriv->touchMouseId, 0);
- QCOMPARE(windowPriv->itemForTouchPointId[0], flickable);
+ QCOMPARE(pointerEvent->point(0)->grabber(), flickable);
QVERIFY(flickable->isMovingVertically());
QTest::touchEvent(window, device).release(0, p3, window);
@@ -608,11 +612,25 @@ void tst_TouchMouse::buttonOnFlickable()
delete window;
}
+void tst_TouchMouse::buttonOnDelayedPressFlickable_data()
+{
+ QTest::addColumn<bool>("scrollBeforeDelayIsOver");
+
+ // the item should never see the event,
+ // due to the pressDelay which never delivers if we start moving
+ QTest::newRow("scroll before press delay is over") << true;
+
+ // wait until the "button" sees the press but then
+ // start moving: the button gets a press and cancel event
+ QTest::newRow("scroll after press delay is over") << false;
+}
+
void tst_TouchMouse::buttonOnDelayedPressFlickable()
{
// flickable - height 500 / 1000
// - eventItem1 y: 100, height 100
// - eventItem2 y: 300, height 100
+ QFETCH(bool, scrollBeforeDelayIsOver);
qApp->setAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents, true);
filteredEventList.clear();
@@ -631,7 +649,8 @@ void tst_TouchMouse::buttonOnDelayedPressFlickable()
window->installEventFilter(this);
- flickable->setPressDelay(60);
+ // wait 600 ms before letting the child see the press event
+ flickable->setPressDelay(600);
// should a mouse area button be clickable on top of flickable? yes :)
EventItem *eventItem1 = window->rootObject()->findChild<EventItem*>("eventItem1");
@@ -647,25 +666,23 @@ void tst_TouchMouse::buttonOnDelayedPressFlickable()
// wait to avoid getting a double click event
QTest::qWait(qApp->styleHints()->mouseDoubleClickInterval() + 10);
+ QQuickWindowPrivate *windowPriv = QQuickWindowPrivate::get(window);
+ QCOMPARE(windowPriv->touchMouseId, -1); // no grabber
- // check that flickable moves - mouse button
- QCOMPARE(eventItem1->eventList.size(), 0);
+ // touch press
QPoint p1 = QPoint(10, 110);
QTest::touchEvent(window, device).press(0, p1, window);
QQuickTouchUtils::flush(window);
- // Flickable initially steals events
- QCOMPARE(eventItem1->eventList.size(), 0);
- // but we'll get the delayed mouse press after a delay
- QTRY_COMPARE(eventItem1->eventList.size(), 1);
- QCOMPARE(eventItem1->eventList.at(0).type, QEvent::MouseButtonPress);
- QCOMPARE(filteredEventList.count(), 1);
- // eventItem1 should have the mouse grab, and have moved the itemForTouchPointId
- // for the touchMouseId to the new grabber.
- QQuickWindowPrivate *windowPriv = QQuickWindowPrivate::get(window);
- QCOMPARE(windowPriv->touchMouseId, 0);
- QCOMPARE(windowPriv->itemForTouchPointId[0], eventItem1);
- QCOMPARE(windowPriv->mouseGrabberItem, eventItem1);
+ if (scrollBeforeDelayIsOver) {
+ // no events, the flickable got scrolled, the button sees nothing
+ QCOMPARE(eventItem1->eventList.size(), 0);
+ } else {
+ // wait until the button sees the press
+ QTRY_COMPARE(eventItem1->eventList.size(), 1);
+ QCOMPARE(eventItem1->eventList.at(0).type, QEvent::MouseButtonPress);
+ QCOMPARE(filteredEventList.count(), 1);
+ }
p1 += QPoint(0, -10);
QPoint p2 = p1 + QPoint(0, -10);
@@ -677,20 +694,35 @@ void tst_TouchMouse::buttonOnDelayedPressFlickable()
QQuickTouchUtils::flush(window);
QTest::touchEvent(window, device).move(0, p3, window);
QQuickTouchUtils::flush(window);
- QVERIFY(flickable->isMovingVertically());
+ QTRY_VERIFY(flickable->isMovingVertically());
+
+ if (scrollBeforeDelayIsOver) {
+ QCOMPARE(eventItem1->eventList.size(), 0);
+ QCOMPARE(filteredEventList.count(), 0);
+ } else {
+ // see at least press, move and ungrab
+ QTRY_VERIFY(eventItem1->eventList.size() > 2);
+ QCOMPARE(eventItem1->eventList.at(0).type, QEvent::MouseButtonPress);
+ QCOMPARE(eventItem1->eventList.last().type, QEvent::UngrabMouse);
+ QCOMPARE(filteredEventList.count(), 1);
+ }
// flickable should have the mouse grab, and have moved the itemForTouchPointId
// for the touchMouseId to the new grabber.
- QCOMPARE(windowPriv->mouseGrabberItem, flickable);
+ QCOMPARE(window->mouseGrabberItem(), flickable);
QCOMPARE(windowPriv->touchMouseId, 0);
- QCOMPARE(windowPriv->itemForTouchPointId[0], flickable);
+ auto pointerEvent = QQuickPointerDevice::touchDevices().at(0)->pointerEvent();
+ QCOMPARE(pointerEvent->point(0)->grabber(), flickable);
QTest::touchEvent(window, device).release(0, p3, window);
QQuickTouchUtils::flush(window);
// We should not have received any synthesised mouse events from Qt gui,
// just the delayed press.
- QCOMPARE(filteredEventList.count(), 1);
+ if (scrollBeforeDelayIsOver)
+ QCOMPARE(filteredEventList.count(), 0);
+ else
+ QCOMPARE(filteredEventList.count(), 1);
delete window;
}
@@ -1097,9 +1129,7 @@ void tst_TouchMouse::mouseOnFlickableOnPinch()
pinchSequence.move(0, p, window).commit();
QQuickTouchUtils::flush(window);
- QQuickWindowPrivate *windowPriv = QQuickWindowPrivate::get(window);
- qDebug() << "Mouse Grabber: " << windowPriv->mouseGrabberItem << " itemForTouchPointId: " << windowPriv->itemForTouchPointId;
- QCOMPARE(windowPriv->mouseGrabberItem, flickable);
+ QCOMPARE(window->mouseGrabberItem(), flickable);
// Add a second finger, this should lead to stealing
p1 = QPoint(40, 100);
@@ -1229,6 +1259,105 @@ void tst_TouchMouse::touchGrabCausesMouseUngrab()
delete window;
}
+void tst_TouchMouse::touchPointDeliveryOrder()
+{
+ // Touch points should be first delivered to the item under the primary finger
+ QScopedPointer<QQuickView> window(createView());
+
+ window->setSource(testFileUrl("touchpointdeliveryorder.qml"));
+ /*
+ The items are positioned from left to right:
+ | background |
+ | left |
+ | | right |
+ | middle |
+ 0 150 300 450 600
+ */
+ QPoint pLeft = QPoint(100, 100);
+ QPoint pRight = QPoint(500, 100);
+ QPoint pLeftMiddle = QPoint(200, 100);
+ QPoint pRightMiddle = QPoint(350, 100);
+
+ window->show();
+ QVERIFY(QTest::qWaitForWindowExposed(window.data()));
+
+ QVector<QQuickItem*> events;
+ EventItem *background = window->rootObject()->findChild<EventItem*>("background");
+ EventItem *left = window->rootObject()->findChild<EventItem*>("left");
+ EventItem *middle = window->rootObject()->findChild<EventItem*>("middle");
+ EventItem *right = window->rootObject()->findChild<EventItem*>("right");
+ QVERIFY(background);
+ QVERIFY(left);
+ QVERIFY(middle);
+ QVERIFY(right);
+ connect(background, &EventItem::onTouchEvent, [&events](QQuickItem* receiver){ events.append(receiver); });
+ connect(left, &EventItem::onTouchEvent, [&events](QQuickItem* receiver){ events.append(receiver); });
+ connect(middle, &EventItem::onTouchEvent, [&events](QQuickItem* receiver){ events.append(receiver); });
+ connect(right, &EventItem::onTouchEvent, [&events](QQuickItem* receiver){ events.append(receiver); });
+
+ QTest::touchEvent(window.data(), device).press(0, pLeft, window.data());
+ QQuickTouchUtils::flush(window.data());
+
+ // Touch on left, then background
+ QCOMPARE(events.size(), 2);
+ QCOMPARE(events.at(0), left);
+ QCOMPARE(events.at(1), background);
+ events.clear();
+
+ // New press events are deliverd first, the stationary point was not accepted, thus it doesn't get delivered
+ QTest::touchEvent(window.data(), device).stationary(0).press(1, pRightMiddle, window.data());
+ QQuickTouchUtils::flush(window.data());
+ QCOMPARE(events.size(), 3);
+ QCOMPARE(events.at(0), middle);
+ QCOMPARE(events.at(1), right);
+ QCOMPARE(events.at(2), background);
+ events.clear();
+
+ QTest::touchEvent(window.data(), device).release(0, pLeft, window.data()).release(1, pRightMiddle, window.data());
+ QQuickTouchUtils::flush(window.data());
+ QCOMPARE(events.size(), 0); // no accepted events
+
+ // Two presses, the first point should come first
+ QTest::touchEvent(window.data(), device).press(0, pLeft, window.data()).press(1, pRight, window.data());
+ QQuickTouchUtils::flush(window.data());
+ QCOMPARE(events.size(), 3);
+ QCOMPARE(events.at(0), left);
+ QCOMPARE(events.at(1), right);
+ QCOMPARE(events.at(2), background);
+ QTest::touchEvent(window.data(), device).release(0, pLeft, window.data()).release(1, pRight, window.data());
+ events.clear();
+
+ // Again, pressing right first
+ QTest::touchEvent(window.data(), device).press(0, pRight, window.data()).press(1, pLeft, window.data());
+ QQuickTouchUtils::flush(window.data());
+ QCOMPARE(events.size(), 3);
+ QCOMPARE(events.at(0), right);
+ QCOMPARE(events.at(1), left);
+ QCOMPARE(events.at(2), background);
+ QTest::touchEvent(window.data(), device).release(0, pRight, window.data()).release(1, pLeft, window.data());
+ events.clear();
+
+ // Two presses, both hitting the middle item on top, then branching left and right, then bottom
+ // Each target should be offered the events exactly once, middle first, left must come before right (id 0)
+ QTest::touchEvent(window.data(), device).press(0, pLeftMiddle, window.data()).press(1, pRightMiddle, window.data());
+ QCOMPARE(events.size(), 4);
+ QCOMPARE(events.at(0), middle);
+ QCOMPARE(events.at(1), left);
+ QCOMPARE(events.at(2), right);
+ QCOMPARE(events.at(3), background);
+ QTest::touchEvent(window.data(), device).release(0, pLeftMiddle, window.data()).release(1, pRightMiddle, window.data());
+ events.clear();
+
+ QTest::touchEvent(window.data(), device).press(0, pRightMiddle, window.data()).press(1, pLeftMiddle, window.data());
+ qDebug() << events;
+ QCOMPARE(events.size(), 4);
+ QCOMPARE(events.at(0), middle);
+ QCOMPARE(events.at(1), right);
+ QCOMPARE(events.at(2), left);
+ QCOMPARE(events.at(3), background);
+ QTest::touchEvent(window.data(), device).release(0, pRightMiddle, window.data()).release(1, pLeftMiddle, window.data());
+}
+
void tst_TouchMouse::hoverEnabled()
{
// QTouchDevice *device = new QTouchDevice;
@@ -1276,8 +1405,8 @@ void tst_TouchMouse::hoverEnabled()
// ------------------------- Touch click on mouseArea1
QTest::touchEvent(window, device).press(0, p1, window);
- QVERIFY(enterSpy1.count() == 1);
- QVERIFY(enterSpy2.count() == 0);
+ QCOMPARE(enterSpy1.count(), 1);
+ QCOMPARE(enterSpy2.count(), 0);
QVERIFY(mouseArea1->pressed());
QVERIFY(mouseArea1->hovered());
QVERIFY(!mouseArea2->hovered());
@@ -1288,33 +1417,36 @@ void tst_TouchMouse::hoverEnabled()
QVERIFY(!mouseArea2->hovered());
// ------------------------- Touch click on mouseArea2
+ if (QGuiApplication::platformName().compare(QLatin1String("xcb"), Qt::CaseInsensitive) == 0)
+ QSKIP("hover can be momentarily inconsistent on X11, depending on timing of flushFrameSynchronousEvents with touch and mouse movements (QTBUG-55350)");
+
QTest::touchEvent(window, device).press(0, p2, window);
QVERIFY(mouseArea1->hovered());
QVERIFY(mouseArea2->hovered());
QVERIFY(mouseArea2->pressed());
- QVERIFY(enterSpy1.count() == 1);
- QVERIFY(enterSpy2.count() == 1);
+ QCOMPARE(enterSpy1.count(), 1);
+ QCOMPARE(enterSpy2.count(), 1);
QTest::touchEvent(window, device).release(0, p2, window);
QVERIFY(clickSpy2.count() == 1);
QVERIFY(mouseArea1->hovered());
QVERIFY(!mouseArea2->hovered());
- QVERIFY(exitSpy1.count() == 0);
- QVERIFY(exitSpy2.count() == 1);
+ QCOMPARE(exitSpy1.count(), 0);
+ QCOMPARE(exitSpy2.count(), 1);
// ------------------------- Another touch click on mouseArea1
QTest::touchEvent(window, device).press(0, p1, window);
- QVERIFY(enterSpy1.count() == 1);
- QVERIFY(enterSpy2.count() == 1);
+ QCOMPARE(enterSpy1.count(), 1);
+ QCOMPARE(enterSpy2.count(), 1);
QVERIFY(mouseArea1->pressed());
QVERIFY(mouseArea1->hovered());
QVERIFY(!mouseArea2->hovered());
QTest::touchEvent(window, device).release(0, p1, window);
- QVERIFY(clickSpy1.count() == 2);
+ QCOMPARE(clickSpy1.count(), 2);
QVERIFY(mouseArea1->hovered());
QVERIFY(!mouseArea1->pressed());
QVERIFY(!mouseArea2->hovered());
diff --git a/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp b/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp
index ab2c41b6bf..5e8f762e23 100644
--- a/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp
+++ b/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp
@@ -34,6 +34,7 @@
#include <QtQuick/qquickitem.h>
#include "../../shared/util.h"
#include <QtGui/QWindow>
+#include <QtGui/QImage>
#include <QtCore/QDebug>
#include <QtQml/qqmlengine.h>
@@ -55,6 +56,7 @@ private slots:
void readback();
void renderingSignals();
void grabBeforeShow();
+ void reparentToNewWindow();
void nullEngine();
};
@@ -302,6 +304,27 @@ void tst_qquickwidget::grabBeforeShow()
QVERIFY(!widget.grab().isNull());
}
+void tst_qquickwidget::reparentToNewWindow()
+{
+ QWidget window1;
+ QWidget window2;
+
+ QQuickWidget *qqw = new QQuickWidget(&window1);
+ qqw->setSource(testFileUrl("rectangle.qml"));
+ window1.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&window1, 5000));
+ window2.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&window2, 5000));
+
+ QSignalSpy afterRenderingSpy(qqw->quickWindow(), &QQuickWindow::afterRendering);
+ qqw->setParent(&window2);
+ qqw->show();
+ QTRY_VERIFY(afterRenderingSpy.size() > 0);
+
+ QImage img = qqw->grabFramebuffer();
+ QCOMPARE(img.pixel(5, 5), qRgb(255, 0, 0));
+}
+
void tst_qquickwidget::nullEngine()
{
QQuickWidget widget;
diff --git a/tests/auto/shared/util.cpp b/tests/auto/shared/util.cpp
index 55041eeb4d..96beb51612 100644
--- a/tests/auto/shared/util.cpp
+++ b/tests/auto/shared/util.cpp
@@ -101,11 +101,15 @@ Q_GLOBAL_STATIC(QMutex, qQmlTestMessageHandlerMutex)
QQmlTestMessageHandler *QQmlTestMessageHandler::m_instance = 0;
-void QQmlTestMessageHandler::messageHandler(QtMsgType, const QMessageLogContext &, const QString &message)
+void QQmlTestMessageHandler::messageHandler(QtMsgType, const QMessageLogContext &context, const QString &message)
{
QMutexLocker locker(qQmlTestMessageHandlerMutex());
- if (QQmlTestMessageHandler::m_instance)
- QQmlTestMessageHandler::m_instance->m_messages.push_back(message);
+ if (QQmlTestMessageHandler::m_instance) {
+ if (QQmlTestMessageHandler::m_instance->m_includeCategories)
+ QQmlTestMessageHandler::m_instance->m_messages.push_back(QString("%1: %2").arg(context.category, message));
+ else
+ QQmlTestMessageHandler::m_instance->m_messages.push_back(message);
+ }
}
QQmlTestMessageHandler::QQmlTestMessageHandler()
@@ -114,6 +118,7 @@ QQmlTestMessageHandler::QQmlTestMessageHandler()
Q_ASSERT(!QQmlTestMessageHandler::m_instance);
QQmlTestMessageHandler::m_instance = this;
m_oldHandler = qInstallMessageHandler(messageHandler);
+ m_includeCategories = false;
}
QQmlTestMessageHandler::~QQmlTestMessageHandler()
diff --git a/tests/auto/shared/util.h b/tests/auto/shared/util.h
index 47a4aae231..33d7cbd1d0 100644
--- a/tests/auto/shared/util.h
+++ b/tests/auto/shared/util.h
@@ -87,12 +87,15 @@ public:
void clear() { m_messages.clear(); }
+ void setIncludeCategoriesEnabled(bool enabled) { m_includeCategories = enabled; }
+
private:
- static void messageHandler(QtMsgType, const QMessageLogContext &, const QString &message);
+ static void messageHandler(QtMsgType, const QMessageLogContext &context, const QString &message);
static QQmlTestMessageHandler *m_instance;
QStringList m_messages;
QtMessageHandler m_oldHandler;
+ bool m_includeCategories;
};
#endif // QQMLTESTUTILS_H
diff --git a/tests/benchmarks/benchmarks.pro b/tests/benchmarks/benchmarks.pro
index c7e7c6829a..5e6bc65815 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
+qtConfig(private_tests) {
+ qtConfig(opengl(es1|es2)?):SUBDIRS += particles
}
diff --git a/tests/benchmarks/particles/affectors/tst_affectors.cpp b/tests/benchmarks/particles/affectors/tst_affectors.cpp
index 475b8d28ec..99d175564b 100644
--- a/tests/benchmarks/particles/affectors/tst_affectors.cpp
+++ b/tests/benchmarks/particles/affectors/tst_affectors.cpp
@@ -86,7 +86,7 @@ void tst_affectors::test_basic()
int stillAlive = 0;
QVERIFY(extremelyFuzzyCompare(system->groupData[0]->size(), 1000, 10));//Small simulation variance is permissible.
- foreach (QQuickParticleData *d, system->groupData[0]->data) {
+ for (QQuickParticleData *d : qAsConst(system->groupData[0]->data)) {
if (d->t == -1)
continue; //Particle data unused
@@ -126,7 +126,7 @@ void tst_affectors::test_filtered()
int stillAlive = 0;
QVERIFY(extremelyFuzzyCompare(system->groupData[1]->size(), 1000, 10));//Small simulation variance is permissible.
- foreach (QQuickParticleData *d, system->groupData[1]->data) {
+ for (QQuickParticleData *d : qAsConst(system->groupData[1]->data)) {
if (d->t == -1)
continue; //Particle data unused
diff --git a/tests/benchmarks/particles/emission/tst_emission.cpp b/tests/benchmarks/particles/emission/tst_emission.cpp
index b107120a28..fe08305f68 100644
--- a/tests/benchmarks/particles/emission/tst_emission.cpp
+++ b/tests/benchmarks/particles/emission/tst_emission.cpp
@@ -79,7 +79,7 @@ void tst_emission::test_basic()
int stillAlive = 0;
QVERIFY(extremelyFuzzyCompare(system->groupData[0]->size(), 1000, 10));//Small simulation variance is permissible.
- foreach (QQuickParticleData *d, system->groupData[0]->data) {
+ for (QQuickParticleData *d : qAsConst(system->groupData[0]->data)) {
if (d->t == -1)
continue; //Particle data unused
diff --git a/tests/benchmarks/qml/animation/tst_animation.cpp b/tests/benchmarks/qml/animation/tst_animation.cpp
index 4f693f9fa8..59f5a57f5c 100644
--- a/tests/benchmarks/qml/animation/tst_animation.cpp
+++ b/tests/benchmarks/qml/animation/tst_animation.cpp
@@ -107,8 +107,8 @@ void tst_animation::animationelements_data()
{
QTest::addColumn<QString>("type");
- QSet<QString> types = QQmlMetaType::qmlTypeNames().toSet();
- foreach (const QString &type, types) {
+ const QSet<QString> types = QQmlMetaType::qmlTypeNames().toSet();
+ for (const QString &type : types) {
if (type.contains(QLatin1String("Animation")))
QTest::newRow(type.toLatin1()) << type;
}
diff --git a/tests/benchmarks/qml/compilation/tst_compilation.cpp b/tests/benchmarks/qml/compilation/tst_compilation.cpp
index 3f2cb5b94f..690e193b53 100644
--- a/tests/benchmarks/qml/compilation/tst_compilation.cpp
+++ b/tests/benchmarks/qml/compilation/tst_compilation.cpp
@@ -75,7 +75,9 @@ void tst_compilation::boomblock()
QQmlComponent c(&engine);
c.setData(data, QUrl());
}
-
+#ifdef QT_NO_OPENGL
+ QSKIP("boomblock imports Particles which requires OpenGL Support");
+#endif
QBENCHMARK {
QQmlComponent c(&engine);
c.setData(data, QUrl());
diff --git a/tests/benchmarks/qml/creation/tst_creation.cpp b/tests/benchmarks/qml/creation/tst_creation.cpp
index e5ad04b92f..a1c09db158 100644
--- a/tests/benchmarks/qml/creation/tst_creation.cpp
+++ b/tests/benchmarks/qml/creation/tst_creation.cpp
@@ -62,6 +62,12 @@ private slots:
void itemtests_qml_data();
void itemtests_qml();
+ void bindings_cpp();
+ void bindings_cpp2();
+ void bindings_qml();
+
+ void bindings_parent_qml();
+
void anchors_creation();
void anchors_heightChange();
@@ -325,6 +331,89 @@ void tst_creation::itemtests_qml()
QBENCHMARK { delete component.create(); }
}
+void tst_creation::bindings_cpp()
+{
+ QQuickItem item;
+ QMetaProperty widthProp = item.metaObject()->property(item.metaObject()->indexOfProperty("width"));
+ QMetaProperty heightProp = item.metaObject()->property(item.metaObject()->indexOfProperty("height"));
+ connect(&item, &QQuickItem::heightChanged, [&item, &widthProp, &heightProp](){
+ QVariant height = heightProp.read(&item);
+ widthProp.write(&item, height);
+ });
+
+ int height = 0;
+ QBENCHMARK {
+ item.setHeight(++height);
+ }
+}
+
+void tst_creation::bindings_cpp2()
+{
+ QQuickItem item;
+ int widthProp = item.metaObject()->indexOfProperty("width");
+ int heightProp = item.metaObject()->indexOfProperty("height");
+ connect(&item, &QQuickItem::heightChanged, [&item, widthProp, heightProp](){
+
+ qreal height = -1;
+ void *args[] = { &height, 0 };
+ QMetaObject::metacall(&item, QMetaObject::ReadProperty, heightProp, args);
+
+ int flags = 0;
+ int status = -1;
+ void *argv[] = { &height, 0, &status, &flags };
+ QMetaObject::metacall(&item, QMetaObject::WriteProperty, widthProp, argv);
+ });
+
+ int height = 0;
+ QBENCHMARK {
+ item.setHeight(++height);
+ }
+}
+
+void tst_creation::bindings_qml()
+{
+ QByteArray data = "import QtQuick 2.0\nItem { width: height }";
+
+ QQmlComponent component(&engine);
+ component.setData(data, QUrl());
+ if (!component.isReady()) {
+ qWarning() << "Unable to create component: " << component.errorString();
+ return;
+ }
+
+ QQuickItem *obj = dynamic_cast<QQuickItem *>(component.create());
+ QVERIFY(obj != nullptr);
+
+ int height = 0;
+ QBENCHMARK {
+ obj->setHeight(++height);
+ }
+
+ delete obj;
+}
+
+void tst_creation::bindings_parent_qml()
+{
+ QByteArray data = "import QtQuick 2.0\nItem { Item { width: parent.height }}";
+
+ QQmlComponent component(&engine);
+ component.setData(data, QUrl());
+ if (!component.isReady()) {
+ qWarning() << "Unable to create component: " << component.errorString();
+ return;
+ }
+
+ QQuickItem *obj = dynamic_cast<QQuickItem *>(component.create());
+ QVERIFY(obj != nullptr);
+
+ int height = 0;
+ QBENCHMARK {
+ obj->setHeight(++height);
+ }
+
+ delete obj;
+}
+
void tst_creation::anchors_creation()
{
QQmlComponent component(&engine);
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..85e7ab7a15
--- /dev/null
+++ b/tests/manual/nodetypes/Effects.qml
@@ -0,0 +1,221 @@
+/****************************************************************************
+**
+** 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 GraphicsInfo 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 bool useHLSLSourceString: false // toggle to provide HLSL shaders as strings instead of bytecode in files
+
+ 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 hlslVertexShader: "cbuffer ConstantBuffer : register(b0) {" +
+ " float4x4 qt_Matrix;" +
+ " float qt_Opacity; }" +
+ "struct PSInput {" +
+ " float4 position : SV_POSITION;" +
+ " float2 coord : TEXCOORD0; };" +
+ "PSInput main(float4 position : POSITION, float2 coord : TEXCOORD0) {" +
+ " PSInput result;" +
+ " result.position = mul(qt_Matrix, position);" +
+ " result.coord = coord;" +
+ " return result;" +
+ "}";
+
+ property string hlslPixelShader:"cbuffer ConstantBuffer : register(b0) {" +
+ " float4x4 qt_Matrix;" +
+ " float qt_Opacity;" +
+ " float amplitude;" +
+ " float frequency;" +
+ " float time; }" +
+ "Texture2D source : register(t0);" +
+ "SamplerState sourceSampler : register(s0);" +
+ "float4 main(float4 position : SV_POSITION, float2 coord : TEXCOORD0) : SV_TARGET" +
+ "{" +
+ " float2 p = sin(time + frequency * coord);" +
+ " return source.Sample(sourceSampler, coord + amplitude * float2(p.y, -p.x)) * qt_Opacity;" +
+ "}";
+
+ property string hlslVertexShaderByteCode: "qrc:/vs_wobble.cso"
+ property string hlslPixelShaderByteCode: "qrc:/ps_wobble.cso"
+
+ vertexShader: customVertexShader ? (GraphicsInfo.shaderType === GraphicsInfo.HLSL
+ ? (useHLSLSourceString ? hlslVertexShader : hlslVertexShaderByteCode)
+ : (GraphicsInfo.shaderType === GraphicsInfo.GLSL ? glslVertexShader : "")) : ""
+
+ fragmentShader: GraphicsInfo.shaderType === GraphicsInfo.HLSL
+ ? (useHLSLSourceString ? hlslPixelShader : hlslPixelShaderByteCode)
+ : (GraphicsInfo.shaderType === GraphicsInfo.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: GraphicsInfo.shaderType === GraphicsInfo.HLSL ? "qrc:/ps_shadow1.cso" : (GraphicsInfo.shaderType === GraphicsInfo.GLSL ? eff2.glslShaderPass1 : "")
+ }
+ }
+ fragmentShader: GraphicsInfo.shaderType === GraphicsInfo.HLSL ? "qrc:/ps_shadow1.cso" : (GraphicsInfo.shaderType === GraphicsInfo.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: GraphicsInfo.shaderType === GraphicsInfo.HLSL ? "qrc:/ps_shadow2.cso" : (GraphicsInfo.shaderType === GraphicsInfo.GLSL ? glslShaderPass2 : "")
+ }
+
+ Column {
+ anchors.bottom: parent.bottom
+ Text {
+ color: "yellow"
+ font.pointSize: 24
+ text: "Shader effect is " + (GraphicsInfo.shaderType === GraphicsInfo.HLSL ? "HLSL" : (GraphicsInfo.shaderType === GraphicsInfo.GLSL ? "GLSL" : "UNKNOWN")) + " based";
+ }
+ Text {
+ text: GraphicsInfo.shaderType + " " + GraphicsInfo.shaderCompilationType + " " + GraphicsInfo.shaderSourceType
+ }
+ Text {
+ text: eff.status + " " + eff.log
+ }
+ }
+ }
+}
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/LotsOfImages.qml b/tests/manual/nodetypes/LotsOfImages.qml
new file mode 100644
index 0000000000..38356a3390
--- /dev/null
+++ b/tests/manual/nodetypes/LotsOfImages.qml
@@ -0,0 +1,73 @@
+/****************************************************************************
+**
+** 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 {
+ Grid {
+ columns: 20
+ spacing: 4
+ anchors.centerIn: parent
+
+ Repeater {
+ model: 500
+
+ Image {
+ source: "qrc:/qt.png"
+
+ // async true, cache false -> there is a separate, new texture for each and every image
+ // and the pixel data reading is done over and over again on a separate thread.
+ asynchronous: true
+ cache: false
+
+ width: 20
+ height: 20
+ }
+ }
+ }
+
+ Rectangle {
+ width: 100
+ height: 100
+ anchors.centerIn: parent
+ color: "red"
+ 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..a9fe09972c
--- /dev/null
+++ b/tests/manual/nodetypes/main.qml
@@ -0,0 +1,83 @@
+/****************************************************************************
+**
+** 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_5)
+ loader.source = "qrc:/LotsOfImages.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..c5382bab4a
--- /dev/null
+++ b/tests/manual/nodetypes/nodetypes.cpp
@@ -0,0 +1,206 @@
+/****************************************************************************
+**
+** 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");
+ qDebug(" [I] - Images");
+ qDebug(" [5] - A lot of async 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..959b43cf18
--- /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 LotsOfImages.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..78c0d085a1
--- /dev/null
+++ b/tests/manual/nodetypes/nodetypes.qrc
@@ -0,0 +1,21 @@
+<RCC>
+ <qresource prefix="/">
+ <file>main.qml</file>
+ <file>Rects.qml</file>
+ <file>LotsOfRects.qml</file>
+ <file>Images.qml</file>
+ <file>LotsOfImages.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/tests/manual/qmlplugindump/tst_qmlplugindump.cpp b/tests/manual/qmlplugindump/tst_qmlplugindump.cpp
index ed00682a83..1bedd4427b 100644
--- a/tests/manual/qmlplugindump/tst_qmlplugindump.cpp
+++ b/tests/manual/qmlplugindump/tst_qmlplugindump.cpp
@@ -92,7 +92,7 @@ public:
{
QRegularExpression re;
QRegularExpressionMatch m;
- Q_FOREACH (const QString &e, m_expected) {
+ for (const QString &e : m_expected) {
re.setPattern(e);
m = re.match(QString::fromLatin1(buffer));
if (!m.hasMatch()) {
@@ -187,7 +187,8 @@ Test createTest(const QString &id, const QJsonObject &def)
return Test(id);
}
QStringList patterns;
- Q_FOREACH (const QJsonValue &x, expected.toArray()) {
+ const auto expectedArray = expected.toArray();
+ for (const QJsonValue &x : expectedArray) {
if (!x.isString()) {
qWarning() << "Wrong definition for test: " << id << ".";
return Test(id);
@@ -331,7 +332,7 @@ void tst_qmlplugindump::plugin_data()
QTest::addColumn<QString>("version");
QTest::addColumn<QStringList>("expected");
- Q_FOREACH (const Test &t, tests) {
+ for (const Test &t : qAsConst(tests)) {
if (t.isNull())
QSKIP("Errors in test definition.");
QTest::newRow(t.id.toLatin1().data()) << t.project << t.version << t.expected;
@@ -357,9 +358,9 @@ void tst_qmlplugindump::plugin()
void tst_qmlplugindump::cleanupTestCase()
{
QSet<const QString> projects;
- Q_FOREACH (const Test &t, tests)
+ for (const Test &t : qAsConst(tests))
projects.insert(t.project);
- Q_FOREACH (const QString &p, projects) {
+ for (const QString &p : qAsConst(projects)) {
if (!cleanUpSample(p))
qWarning() << "Error in cleaning up project" << p << ".";
}
diff --git a/tests/manual/scenegraph_lancelot/scenegrabber/main.cpp b/tests/manual/scenegraph_lancelot/scenegrabber/main.cpp
index 5098d51134..886cfc6599 100644
--- a/tests/manual/scenegraph_lancelot/scenegrabber/main.cpp
+++ b/tests/manual/scenegraph_lancelot/scenegrabber/main.cpp
@@ -183,8 +183,8 @@ int main(int argc, char *argv[])
v.setSource(QUrl::fromLocalFile(ifile));
if (noText) {
- QList<QQuickItem*> items = v.rootObject()->findChildren<QQuickItem*>();
- foreach (QQuickItem *item, items) {
+ const QList<QQuickItem*> items = v.rootObject()->findChildren<QQuickItem*>();
+ for (QQuickItem *item : items) {
if (QByteArray(item->metaObject()->className()).contains("Text"))
item->setVisible(false);
}
diff --git a/tests/manual/scenegraph_lancelot/scenegraph/tst_scenegraph.cpp b/tests/manual/scenegraph_lancelot/scenegraph/tst_scenegraph.cpp
index 426e06ccc2..3f28d90e7b 100644
--- a/tests/manual/scenegraph_lancelot/scenegraph/tst_scenegraph.cpp
+++ b/tests/manual/scenegraph_lancelot/scenegraph/tst_scenegraph.cpp
@@ -156,7 +156,7 @@ void tst_Scenegraph::setupTestSuite(const QByteArray& filter)
}
std::sort(itemFiles.begin(), itemFiles.end());
- foreach (const QString &filePath, itemFiles) {
+ for (const QString &filePath : qAsConst(itemFiles)) {
QByteArray itemName = filePath.mid(testSuitePath.length() + 1).toLatin1();
QBaselineTest::newRow(itemName, checksumFileOrDir(filePath)) << filePath;
numItems++;
@@ -238,7 +238,9 @@ quint16 tst_Scenegraph::checksumFileOrDir(const QString &path)
if (fi.isDir()) {
static const QStringList nameFilters = QStringList() << "*.qml" << "*.cpp" << "*.png" << "*.jpg";
quint16 cs = 0;
- foreach (QString item, QDir(fi.filePath()).entryList(nameFilters, QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot))
+ const auto entryList = QDir(fi.filePath()).entryList(nameFilters,
+ QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot);
+ for (const QString &item : entryList)
cs ^= checksumFileOrDir(path + QLatin1Char('/') + item);
return cs;
}
diff --git a/tools/fdegen/fdegen.pro b/tools/fdegen/fdegen.pro
deleted file mode 100644
index a52533280e..0000000000
--- a/tools/fdegen/fdegen.pro
+++ /dev/null
@@ -1,8 +0,0 @@
-TEMPLATE = app
-TARGET = fdegen
-INCLUDEPATH += .
-
-# Input
-SOURCES += main.cpp
-
-LIBS += -ldwarf -lelf
diff --git a/tools/fdegen/main.cpp b/tools/fdegen/main.cpp
deleted file mode 100644
index 53ee9dec2a..0000000000
--- a/tools/fdegen/main.cpp
+++ /dev/null
@@ -1,362 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the V4VM module 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 <libdwarf.h>
-#include <dwarf.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
-
-#define DEBUG
-
-#ifdef DEBUG
-#include <libelf.h>
-#endif
-
-#include <qglobal.h>
-
-enum DwarfRegs {
-#if defined(Q_PROCESSOR_X86_64)
- // X86-64
- RAX = 0,
- RDX = 1,
- RCX = 2,
- RBX = 3,
- RSI = 4,
- RDI = 5,
- RBP = 6,
- RSP = 7,
- R8 = 8,
- R9 = 9,
- R10 = 10,
- R11 = 11,
- R12 = 12,
- R13 = 13,
- R14 = 14,
- R15 = 15,
- RIP = 16,
-
- InstructionPointerRegister = RIP,
- StackPointerRegister = RSP,
- StackFrameRegister = RBP
-#elif defined(Q_PROCESSOR_X86)
- // x86
- EAX = 0,
- EDX = 1,
- ECX = 2,
- EBX = 3,
- ESP = 4,
- EBP = 5,
- ESI = 6,
- EDI = 7,
- EIP = 8,
-
- InstructionPointerRegister = EIP,
- StackPointerRegister = ESP,
- StackFrameRegister = EBP
-#else
-#error Not ported yet
-#endif
-};
-
-static const DwarfRegs calleeSavedRegisters[] = {
-#if defined(Q_PROCESSOR_X86_64)
- R12,
- R14
-#elif defined(Q_PROCESSOR_X86)
- ESI,
- EDI
-#endif
-};
-static const int calleeSavedRegisterCount = sizeof(calleeSavedRegisters) / sizeof(calleeSavedRegisters[0]);
-
-#if QT_POINTER_SIZE == 8
-#define Elf_Ehdr Elf64_Ehdr
-#define elf_newehdr elf64_newehdr
-#define Elf_Shdr Elf64_Shdr
-#define elf_getshdr elf64_getshdr
-#else
-#define Elf_Ehdr Elf32_Ehdr
-#define elf_newehdr elf32_newehdr
-#define Elf_Shdr Elf32_Shdr
-#define elf_getshdr elf32_getshdr
-#endif
-
-static void die(const char *msg)
-{
- fprintf(stderr, "error: %s\n", msg);
- exit(1);
-}
-
-static int createSectionCallback(
- char *name,
- int size,
- Dwarf_Unsigned /*type*/,
- Dwarf_Unsigned /*flags*/,
- Dwarf_Unsigned /*link*/,
- Dwarf_Unsigned /*info*/,
- Dwarf_Unsigned* /*sect_name_index*/,
- void * /*user_data*/,
- int* /*error*/)
-{
- if (strcmp(name, ".debug_frame"))
- return 0;
- fprintf(stderr, "createsection called with %s and size %d\n", name, size);
- return 1;
-}
-
-static unsigned char cie_init_instructions[] = {
- DW_CFA_def_cfa, StackPointerRegister, /*offset in bytes */sizeof(void*),
- DW_CFA_offset | InstructionPointerRegister, 1,
-};
-
-int main()
-{
- Dwarf_Error error = 0;
- Dwarf_P_Debug dw = dwarf_producer_init_c(DW_DLC_WRITE | DW_DLC_SIZE_64,
- createSectionCallback,
- /* error handler */0,
- /* error arg */0,
- /* user data */0,
- &error);
- if (error != 0)
- die("dwarf_producer_init_c failed");
-
- Dwarf_Unsigned cie = dwarf_add_frame_cie(dw,
- "",
- /* code alignment factor */sizeof(void*),
- /* data alignment factor */-sizeof(void*),
- /* return address reg*/InstructionPointerRegister,
- cie_init_instructions,
- sizeof(cie_init_instructions),
- &error);
- if (error != 0)
- die("dwarf_add_frame_cie failed");
-
- Dwarf_P_Fde fde = dwarf_new_fde(dw, &error);
- if (error != 0)
- die("dwarf_new_fde failed");
-
- /* New entry in state machine for code offset 1 after push rbp instruction */
- dwarf_add_fde_inst(fde,
- DW_CFA_advance_loc,
- /*offset in code alignment units*/ 1,
- /* unused*/ 0,
- &error);
-
- /* After "push rbp" the offset to the CFA is now 2 instead of 1 */
- dwarf_add_fde_inst(fde,
- DW_CFA_def_cfa_offset_sf,
- /*offset in code alignment units*/ -2,
- /* unused*/ 0,
- &error);
-
- /* After "push rbp" the value of rbp is now stored at offset 1 from CFA */
- dwarf_add_fde_inst(fde,
- DW_CFA_offset,
- StackFrameRegister,
- 2,
- &error);
-
- /* New entry in state machine for code offset 3 for mov rbp, rsp instruction */
- dwarf_add_fde_inst(fde,
- DW_CFA_advance_loc,
- /*offset in code alignment units*/ 3,
- /* unused */ 0,
- &error);
-
- /* After "mov rbp, rsp" the cfa is reachable via rbp */
- dwarf_add_fde_inst(fde,
- DW_CFA_def_cfa_register,
- StackFrameRegister,
- /* unused */0,
- &error);
-
- /* Callee saved registers */
- for (int i = 0; i < calleeSavedRegisterCount; ++i) {
- dwarf_add_fde_inst(fde,
- DW_CFA_offset,
- calleeSavedRegisters[i],
- i + 3,
- &error);
- }
-
- dwarf_add_frame_fde(dw, fde,
- /* die */0,
- cie,
- /*virt addr */0,
- /* length of code */0,
- /* symbol index */0,
- &error);
- if (error != 0)
- die("dwarf_add_frame_fde failed");
-
- dwarf_transform_to_disk_form(dw, &error);
- if (error != 0)
- die("dwarf_transform_to_disk_form failed");
-
- Dwarf_Unsigned len = 0;
- Dwarf_Signed elfIdx = 0;
- unsigned char *bytes = (unsigned char *)dwarf_get_section_bytes(dw, /* section */1,
- &elfIdx, &len, &error);
- if (error != 0)
- die("dwarf_get_section_bytes failed");
-
- // libdwarf doesn't add a terminating FDE with zero length, so let's add one
- // ourselves.
- unsigned char *newBytes = (unsigned char *)alloca(len + 4);
- memcpy(newBytes, bytes, len);
- newBytes[len] = 0;
- newBytes[len + 1] = 0;
- newBytes[len + 2] = 0;
- newBytes[len + 3] = 0;
- newBytes[len + 4] = 0;
- bytes = newBytes;
- len += 4;
-
- // Reset CIE-ID back to 0 as expected for .eh_frames
- bytes[4] = 0;
- bytes[5] = 0;
- bytes[6] = 0;
- bytes[7] = 0;
-
- unsigned fde_offset = bytes[0] + 4;
-
- bytes[fde_offset + 4] = fde_offset + 4;
-
- printf("static const unsigned char cie_fde_data[] = {\n");
- int i = 0;
- while (i < len) {
- printf(" ");
- for (int j = 0; i < len && j < 8; ++j, ++i)
- printf("0x%x, ", bytes[i]);
- printf("\n");
- }
- printf("};\n");
-
- printf("static const int fde_offset = %d;\n", fde_offset);
- printf("static const int initial_location_offset = %d;\n", fde_offset + 8);
- printf("static const int address_range_offset = %d;\n", fde_offset + 8 + sizeof(void*));
-
-#ifdef DEBUG
- {
- if (elf_version(EV_CURRENT) == EV_NONE)
- die("wrong elf version");
- int fd = open("debug.o", O_WRONLY | O_CREAT, 0777);
- if (fd < 0)
- die("cannot create debug.o");
-
- Elf *e = elf_begin(fd, ELF_C_WRITE, 0);
- if (!e)
- die("elf_begin failed");
-
- Elf_Ehdr *ehdr = elf_newehdr(e);
- if (!ehdr)
- die(elf_errmsg(-1));
-
- ehdr->e_ident[EI_DATA] = ELFDATA2LSB;
-#if defined(Q_PROCESSOR_X86_64)
- ehdr->e_machine = EM_X86_64;
-#elif defined(Q_PROCESSOR_X86)
- ehdr->e_machine = EM_386;
-#else
-#error port me :)
-#endif
- ehdr->e_type = ET_EXEC;
- ehdr->e_version = EV_CURRENT;
-
- Elf_Scn *section = elf_newscn(e);
- if (!section)
- die("elf_newscn failed");
-
- Elf_Data *data = elf_newdata(section);
- if (!data)
- die(elf_errmsg(-1));
- data->d_align = 4;
- data->d_off = 0;
- data->d_buf = bytes;
- data->d_size = len;
- data->d_type = ELF_T_BYTE;
- data->d_version = EV_CURRENT;
-
- Elf_Shdr *shdr = elf_getshdr(section);
- if (!shdr)
- die(elf_errmsg(-1));
-
- shdr->sh_name = 1;
- shdr->sh_type = SHT_PROGBITS;
- shdr->sh_entsize = 0;
-
- char stringTable[] = {
- 0,
- '.', 'e', 'h', '_', 'f', 'r', 'a', 'm', 'e', 0,
- '.', 's', 'h', 's', 't', 'r', 't', 'a', 'b', 0
- };
-
- section = elf_newscn(e);
- if (!section)
- die("elf_newscn failed");
-
- data = elf_newdata(section);
- if (!data)
- die(elf_errmsg(-1));
- data->d_align = 1;
- data->d_off = 0;
- data->d_buf = stringTable;
- data->d_size = sizeof(stringTable);
- data->d_type = ELF_T_BYTE;
- data->d_version = EV_CURRENT;
-
- shdr = elf_getshdr(section);
- if (!shdr)
- die(elf_errmsg(-1));
-
- shdr->sh_name = 11;
- shdr->sh_type = SHT_STRTAB;
- shdr->sh_flags = SHF_STRINGS | SHF_ALLOC;
- shdr->sh_entsize = 0;
-
- ehdr->e_shstrndx = elf_ndxscn(section);
-
- if (elf_update(e, ELF_C_WRITE) < 0)
- die(elf_errmsg(-1));
-
- elf_end(e);
- close(fd);
- }
-#endif
-
- dwarf_producer_finish(dw, &error);
- if (error != 0)
- die("dwarf_producer_finish failed");
- return 0;
-}
diff --git a/tools/qml/main.cpp b/tools/qml/main.cpp
index a795144984..be62500858 100644
--- a/tools/qml/main.cpp
+++ b/tools/qml/main.cpp
@@ -160,18 +160,23 @@ public:
LoadWatcher(QQmlApplicationEngine *e, int expected)
: QObject(e)
, earlyExit(false)
+ , returnCode(0)
, expect(expected)
, haveOne(false)
{
connect(e, SIGNAL(objectCreated(QObject*,QUrl)),
this, SLOT(checkFinished(QObject*)));
// QQmlApplicationEngine also connects quit() to QCoreApplication::quit
- // but if called before exec() then QCoreApplication::quit does nothing
+ // and exit() to QCoreApplication::exit but if called before exec()
+ // then QCoreApplication::quit or QCoreApplication::exit does nothing
connect(e, SIGNAL(quit()),
this, SLOT(quit()));
+ connect(e, &QQmlEngine::exit,
+ this, &LoadWatcher::exit);
}
bool earlyExit;
+ int returnCode;
private:
void contain(QObject *o, const QUrl &containPath);
@@ -187,7 +192,7 @@ public Q_SLOTS:
checkForWindow(o);
haveOne = true;
if (conf && qae)
- foreach (PartialScene *ps, conf->completers)
+ for (PartialScene *ps : qAsConst(conf->completers))
if (o->inherits(ps->itemType().toUtf8().constData()))
contain(o, ps->container());
}
@@ -196,14 +201,20 @@ public Q_SLOTS:
if (! --expect) {
printf("qml: Did not load any objects, exiting.\n");
- exit(2);//Different return code from qFatal
+ std::exit(2);//Different return code from qFatal
}
}
void quit() {
//Will be checked before calling exec()
earlyExit = true;
+ returnCode = 0;
}
+ void exit(int retCode) {
+ earlyExit = true;
+ returnCode = retCode;
+ }
+
#if defined(QT_GUI_LIB) && !defined(QT_NO_OPENGL)
void onOpenGlContextCreated(QOpenGLContext *context);
#endif
@@ -402,8 +413,8 @@ static void loadDummyDataFiles(QQmlEngine &engine, const QString& directory)
QObject *dummyData = comp.create();
if (comp.isError()) {
- QList<QQmlError> errors = comp.errors();
- foreach (const QQmlError &error, errors)
+ const QList<QQmlError> errors = comp.errors();
+ for (const QQmlError &error : errors)
qWarning() << error;
}
@@ -555,7 +566,7 @@ int main(int argc, char *argv[])
if (!dummyDir.isEmpty() && QFileInfo (dummyDir).isDir())
loadDummyDataFiles(e, dummyDir);
- foreach (const QString &path, files) {
+ for (const QString &path : qAsConst(files)) {
//QUrl::fromUserInput doesn't treat no scheme as relative file paths
#ifndef QT_NO_REGULAREXPRESSION
QRegularExpression urlRe("[[:word:]]+://.*");
@@ -582,7 +593,7 @@ int main(int argc, char *argv[])
}
if (lw->earlyExit)
- return 0;
+ return lw->returnCode;
return app->exec();
}
diff --git a/tools/qml/qml.pro b/tools/qml/qml.pro
index fe90916980..5f05054d04 100644
--- a/tools/qml/qml.pro
+++ b/tools/qml/qml.pro
@@ -12,6 +12,6 @@ mac {
ICON = qml.icns
}
-DEFINES += QT_QML_DEBUG_NO_WARNING
+!contains(QT_CONFIG, no-qml-debug): DEFINES += QT_QML_DEBUG_NO_WARNING
load(qt_tool)
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/qmleasing/splineeditor.cpp b/tools/qmleasing/splineeditor.cpp
index 78ed9606db..6fee013c62 100644
--- a/tools/qmleasing/splineeditor.cpp
+++ b/tools/qmleasing/splineeditor.cpp
@@ -34,6 +34,7 @@
#include <QContextMenuEvent>
#include <QDebug>
#include <QApplication>
+#include <QVector>
const int canvasWidth = 640;
const int canvasHeight = 320;
@@ -287,7 +288,7 @@ QHash<QString, QEasingCurve> SplineEditor::presets() const
QString SplineEditor::generateCode()
{
QString s = QLatin1String("[");
- foreach (const QPointF &point, m_controlPoints) {
+ for (const QPointF &point : qAsConst(m_controlPoints)) {
s += QString::number(point.x(), 'g', 2) + QLatin1Char(',')
+ QString::number(point.y(), 'g', 3) + QLatin1Char(',');
}
@@ -672,25 +673,23 @@ void SplineEditor::setEasingCurve(const QString &code)
if (m_block)
return;
if (code.startsWith(QLatin1Char('[')) && code.endsWith(QLatin1Char(']'))) {
- QString cleanCode = code;
- cleanCode.remove(0, 1);
- cleanCode.chop(1);
- const QStringList stringList = cleanCode.split(QLatin1Char(','), QString::SkipEmptyParts);
+ const QStringRef cleanCode(&code, 1, code.size() - 2);
+ const auto stringList = cleanCode.split(QLatin1Char(','), QString::SkipEmptyParts);
if (stringList.count() >= 6 && (stringList.count() % 6 == 0)) {
- QList<qreal> realList;
+ QVector<qreal> realList;
realList.reserve(stringList.count());
- foreach (const QString &string, stringList) {
+ for (const QStringRef &string : stringList) {
bool ok;
realList.append(string.toDouble(&ok));
if (!ok)
return;
}
- QList<QPointF> points;
+ QVector<QPointF> points;
const int count = realList.count() / 2;
points.reserve(count);
for (int i = 0; i < count; ++i)
points.append(QPointF(realList.at(i * 2), realList.at(i * 2 + 1)));
- if (points.last() == QPointF(1.0, 1.0)) {
+ if (points.constLast() == QPointF(1.0, 1.0)) {
QEasingCurve easingCurve(QEasingCurve::BezierSpline);
for (int i = 0; i < points.count() / 3; ++i) {
diff --git a/tools/qmlimportscanner/main.cpp b/tools/qmlimportscanner/main.cpp
index 2569d78c63..f7f5a5e4e4 100644
--- a/tools/qmlimportscanner/main.cpp
+++ b/tools/qmlimportscanner/main.cpp
@@ -55,6 +55,14 @@ QT_USE_NAMESPACE
QStringList g_qmlImportPaths;
+static inline QString typeLiteral() { return QStringLiteral("type"); }
+static inline QString versionLiteral() { return QStringLiteral("version"); }
+static inline QString nameLiteral() { return QStringLiteral("name"); }
+static inline QString pluginsLiteral() { return QStringLiteral("plugins"); }
+static inline QString pathLiteral() { return QStringLiteral("path"); }
+static inline QString classnamesLiteral() { return QStringLiteral("classnames"); }
+static inline QString dependenciesLiteral() { return QStringLiteral("dependencies"); }
+
static void printUsage(const QString &appNameIn)
{
const std::wstring appName = appNameIn.toStdWString();
@@ -84,14 +92,14 @@ QVariantList findImportsInAst(QQmlJS::AST::UiHeaderItemList *headerItemList, con
// handle directory imports
if (!importNode->fileName.isEmpty()) {
QString name = importNode->fileName.toString();
- import[QStringLiteral("name")] = name;
+ import[nameLiteral()] = name;
if (name.endsWith(QLatin1String(".js"))) {
- import[QStringLiteral("type")] = QStringLiteral("javascript");
+ import[typeLiteral()] = QStringLiteral("javascript");
} else {
- import[QStringLiteral("type")] = QStringLiteral("directory");
+ import[typeLiteral()] = QStringLiteral("directory");
}
- import[QStringLiteral("path")] = QDir::cleanPath(path + QLatin1Char('/') + name);
+ import[pathLiteral()] = QDir::cleanPath(path + QLatin1Char('/') + name);
} else {
// Walk the id chain ("Foo" -> "Bar" -> etc)
QString name;
@@ -103,9 +111,9 @@ QVariantList findImportsInAst(QQmlJS::AST::UiHeaderItemList *headerItemList, con
}
name.chop(1); // remove trailing "."
if (!name.isEmpty())
- import[QStringLiteral("name")] = name;
- import[QStringLiteral("type")] = QStringLiteral("module");
- import[QStringLiteral("version")] = code.mid(importNode->versionToken.offset, importNode->versionToken.length);
+ import[nameLiteral()] = name;
+ import[typeLiteral()] = QStringLiteral("module");
+ import[versionLiteral()] = code.mid(importNode->versionToken.offset, importNode->versionToken.length);
}
imports.append(import);
@@ -117,7 +125,7 @@ QVariantList findImportsInAst(QQmlJS::AST::UiHeaderItemList *headerItemList, con
// Read the qmldir file, extract a list of plugins by
// parsing the "plugin" and "classname" lines.
QVariantMap pluginsForModulePath(const QString &modulePath) {
- QFile qmldirFile(modulePath + QStringLiteral("/qmldir"));
+ QFile qmldirFile(modulePath + QLatin1String("/qmldir"));
if (!qmldirFile.exists())
return QVariantMap();
@@ -141,16 +149,16 @@ QVariantMap pluginsForModulePath(const QString &modulePath) {
if (dep.length() != 3)
std::cerr << "depends: expected 2 arguments: module identifier and version" << std::endl;
else
- dependencies << QString::fromUtf8(dep[1]) + QStringLiteral(" ") + QString::fromUtf8(dep[2]).simplified();
+ dependencies << QString::fromUtf8(dep[1]) + QLatin1Char(' ') + QString::fromUtf8(dep[2]).simplified();
}
} while (line.length() > 0);
QVariantMap pluginInfo;
- pluginInfo[QStringLiteral("plugins")] = plugins.simplified();
- pluginInfo[QStringLiteral("classnames")] = classnames.simplified();
+ pluginInfo[pluginsLiteral()] = plugins.simplified();
+ pluginInfo[classnamesLiteral()] = classnames.simplified();
if (dependencies.length())
- pluginInfo[QStringLiteral("dependencies")] = dependencies;
+ pluginInfo[dependenciesLiteral()] = dependencies;
return pluginInfo;
}
@@ -163,7 +171,7 @@ QString resolveImportPath(const QString &uri, const QString &version)
QString ver = version;
while (true) {
- foreach (const QString &qmlImportPath, g_qmlImportPaths) {
+ for (const QString &qmlImportPath : qAsConst(g_qmlImportPaths)) {
// Search for the most specific version first, and search
// also for the version in parent modules. For example:
// - qml/QtQml/Models.2.0
@@ -210,25 +218,25 @@ QVariantList findPathsForModuleImports(const QVariantList &imports)
for (int i = 0; i < importsCopy.length(); ++i) {
QVariantMap import = qvariant_cast<QVariantMap>(importsCopy[i]);
- if (import[QStringLiteral("type")] == QLatin1String("module")) {
- QString path = resolveImportPath(import.value(QStringLiteral("name")).toString(), import.value(QStringLiteral("version")).toString());
+ if (import[typeLiteral()] == QLatin1String("module")) {
+ QString path = resolveImportPath(import.value(nameLiteral()).toString(), import.value(versionLiteral()).toString());
if (!path.isEmpty())
- import[QStringLiteral("path")] = path;
- QVariantMap plugininfo = pluginsForModulePath(import.value(QStringLiteral("path")).toString());
- QString plugins = plugininfo.value(QStringLiteral("plugins")).toString();
- QString classnames = plugininfo.value(QStringLiteral("classnames")).toString();
+ import[pathLiteral()] = path;
+ QVariantMap plugininfo = pluginsForModulePath(import.value(pathLiteral()).toString());
+ QString plugins = plugininfo.value(pluginsLiteral()).toString();
+ QString classnames = plugininfo.value(classnamesLiteral()).toString();
if (!plugins.isEmpty())
- import[QStringLiteral("plugin")] = plugins;
+ import.insert(QStringLiteral("plugin"), plugins);
if (!classnames.isEmpty())
- import[QStringLiteral("classname")] = classnames;
- if (plugininfo.contains(QStringLiteral("dependencies"))) {
- QStringList dependencies = plugininfo.value(QStringLiteral("dependencies")).toStringList();
- foreach (const QString &line, dependencies) {
- QList<QString> dep = line.split(QLatin1Char(' '));
+ import.insert(QStringLiteral("classname"), classnames);
+ if (plugininfo.contains(dependenciesLiteral())) {
+ const QStringList dependencies = plugininfo.value(dependenciesLiteral()).toStringList();
+ for (const QString &line : dependencies) {
+ const auto dep = line.splitRef(QLatin1Char(' '));
QVariantMap depImport;
- depImport[QStringLiteral("type")] = QStringLiteral("module");
- depImport[QStringLiteral("name")] = dep[0];
- depImport[QStringLiteral("version")] = dep[1];
+ depImport[typeLiteral()] = QStringLiteral("module");
+ depImport[nameLiteral()] = dep[0].toString();
+ depImport[versionLiteral()] = dep[1].toString();
importsCopy.append(depImport);
}
}
@@ -277,8 +285,8 @@ struct ImportCollector : public QQmlJS::Directives
virtual void importFile(const QString &jsfile, const QString &module, int line, int column)
{
QVariantMap entry;
- entry[QLatin1String("type")] = QStringLiteral("javascript");
- entry[QLatin1String("path")] = jsfile;
+ entry[typeLiteral()] = QStringLiteral("javascript");
+ entry[pathLiteral()] = jsfile;
imports << entry;
Q_UNUSED(module);
@@ -290,12 +298,12 @@ struct ImportCollector : public QQmlJS::Directives
{
QVariantMap entry;
if (uri.contains(QLatin1Char('/'))) {
- entry[QLatin1String("type")] = QStringLiteral("directory");
- entry[QLatin1String("name")] = uri;
+ entry[typeLiteral()] = QStringLiteral("directory");
+ entry[nameLiteral()] = uri;
} else {
- entry[QLatin1String("type")] = QStringLiteral("module");
- entry[QLatin1String("name")] = uri;
- entry[QLatin1String("version")] = version;
+ entry[typeLiteral()] = QStringLiteral("module");
+ entry[nameLiteral()] = uri;
+ entry[versionLiteral()] = version;
}
imports << entry;
@@ -354,7 +362,7 @@ QVariantList findQmlImportsInFile(const QString &filePath)
QVariantList mergeImports(const QVariantList &a, const QVariantList &b)
{
QVariantList merged = a;
- foreach (const QVariant &variant, b) {
+ for (const QVariant &variant : b) {
if (!merged.contains(variant))
merged.append(variant);
}
@@ -413,19 +421,19 @@ QVariantList findQmlImportsInDirectory(const QString &qmlDir)
continue;
}
- foreach (const QFileInfo &x, entries)
+ for (const QFileInfo &x : entries)
if (x.isFile())
ret = mergeImports(ret, findQmlImportsInFile(x.absoluteFilePath()));
}
return ret;
}
-QSet<QString> importModulePaths(QVariantList imports) {
+QSet<QString> importModulePaths(const QVariantList &imports) {
QSet<QString> ret;
- foreach (const QVariant &importVariant, imports) {
+ for (const QVariant &importVariant : imports) {
QVariantMap import = qvariant_cast<QVariantMap>(importVariant);
- QString path = import.value(QStringLiteral("path")).toString();
- QString type = import.value(QStringLiteral("type")).toString();
+ QString path = import.value(pathLiteral()).toString();
+ QString type = import.value(typeLiteral()).toString();
if (type == QLatin1String("module") && !path.isEmpty())
ret.insert(QDir(path).canonicalPath());
}
@@ -440,13 +448,13 @@ QVariantList findQmlImportsRecursively(const QStringList &qmlDirs, const QString
QVariantList ret;
// scan all app root qml directories for imports
- foreach (const QString &qmlDir, qmlDirs) {
+ for (const QString &qmlDir : qmlDirs) {
QVariantList imports = findQmlImportsInDirectory(qmlDir);
ret = mergeImports(ret, imports);
}
// scan app qml files for imports
- foreach (const QString &file, scanFiles) {
+ for (const QString &file : scanFiles) {
QVariantList imports = findQmlImportsInFile(file);
ret = mergeImports(ret, imports);
}
diff --git a/tools/qmljs/qmljs.cpp b/tools/qmljs/qmljs.cpp
index 68aa52ce91..4b63357363 100644
--- a/tools/qmljs/qmljs.cpp
+++ b/tools/qmljs/qmljs.cpp
@@ -32,8 +32,10 @@
#include "private/qv4errorobject_p.h"
#include "private/qv4globalobject_p.h"
#include "private/qv4codegen_p.h"
+#if QT_CONFIG(qml_interpreter)
#include "private/qv4isel_moth_p.h"
#include "private/qv4vme_moth_p.h"
+#endif
#include "private/qv4objectproto_p.h"
#include "private/qv4isel_p.h"
#include "private/qv4mm_p.h"
@@ -43,10 +45,14 @@
#ifdef V4_ENABLE_JIT
# include "private/qv4isel_masm_p.h"
+#else
+QT_REQUIRE_CONFIG(qml_interpreter);
#endif // V4_ENABLE_JIT
#include <QtCore/QCoreApplication>
#include <QtCore/QFile>
+#include <QtCore/QFileInfo>
+#include <QtCore/QDateTime>
#include <private/qqmljsengine_p.h>
#include <private/qqmljslexer_p.h>
#include <private/qqmljsparser_p.h>
@@ -61,14 +67,14 @@ using namespace QV4;
struct Print: FunctionObject
{
struct Data : Heap::FunctionObject {
- Data(ExecutionContext *scope)
- : Heap::FunctionObject(scope, QStringLiteral("print"))
+ void init(ExecutionContext *scope)
{
+ Heap::FunctionObject::init(scope, QStringLiteral("print"));
}
};
V4_OBJECT(FunctionObject)
- static ReturnedValue call(const Managed *, CallData *callData)
+ static void call(const Managed *, Scope &scope, CallData *callData)
{
for (int i = 0; i < callData->argc; ++i) {
QString s = callData->args[i].toQStringNoThrow();
@@ -77,7 +83,7 @@ struct Print: FunctionObject
std::cout << qPrintable(s);
}
std::cout << std::endl;
- return Encode::undefined();
+ scope.result = Encode::undefined();
}
};
@@ -86,18 +92,18 @@ DEFINE_OBJECT_VTABLE(Print);
struct GC: public FunctionObject
{
struct Data : Heap::FunctionObject {
- Data(ExecutionContext *scope)
- : Heap::FunctionObject(scope, QStringLiteral("gc"))
+ void init(ExecutionContext *scope)
{
+ Heap::FunctionObject::init(scope, QStringLiteral("gc"));
}
};
V4_OBJECT(FunctionObject)
- static ReturnedValue call(const Managed *m, CallData *)
+ static void call(const Managed *m, Scope &scope, CallData *)
{
static_cast<const GC *>(m)->engine()->memoryManager->runGC();
- return Encode::undefined();
+ scope.result = Encode::undefined();
}
};
@@ -118,7 +124,7 @@ static void showException(QV4::ExecutionContext *ctx, const QV4::Value &exceptio
std::cerr << "Uncaught exception: " << qPrintable(message->toQStringNoThrow()) << std::endl;
}
- foreach (const QV4::StackFrame &frame, trace) {
+ for (const QV4::StackFrame &frame : trace) {
std::cerr << " at " << qPrintable(frame.function) << " (" << qPrintable(frame.source);
if (frame.line >= 0)
std::cerr << ':' << frame.line;
@@ -143,6 +149,7 @@ int main(int argc, char *argv[])
#endif
bool runAsQml = false;
+ bool cache = false;
if (!args.isEmpty()) {
if (args.first() == QLatin1String("--jit")) {
@@ -150,16 +157,23 @@ int main(int argc, char *argv[])
args.removeFirst();
}
+#if QT_CONFIG(qml_interpreter)
if (args.first() == QLatin1String("--interpret")) {
mode = use_moth;
args.removeFirst();
}
+#endif
if (args.first() == QLatin1String("--qml")) {
runAsQml = true;
args.removeFirst();
}
+ if (args.first() == QLatin1String("--cache")) {
+ cache = true;
+ args.removeFirst();
+ }
+
if (args.first() == QLatin1String("--help")) {
std::cerr << "Usage: qmljs [|--jit|--interpret|--qml] file..." << std::endl;
return EXIT_SUCCESS;
@@ -171,7 +185,9 @@ int main(int argc, char *argv[])
case use_moth: {
QV4::EvalISelFactory* iSelFactory = 0;
if (mode == use_moth) {
+#if QT_CONFIG(qml_interpreter)
iSelFactory = new QV4::Moth::ISelFactory;
+#endif
#ifdef V4_ENABLE_JIT
} else {
iSelFactory = new QV4::JIT::ISelFactory;
@@ -188,18 +204,41 @@ int main(int argc, char *argv[])
QV4::ScopedObject gc(scope, vm.memoryManager->allocObject<builtins::GC>(ctx));
vm.globalObject->put(QV4::ScopedString(scope, vm.newIdentifier(QStringLiteral("gc"))).getPointer(), gc);
- foreach (const QString &fn, args) {
+ for (const QString &fn : qAsConst(args)) {
QFile file(fn);
if (file.open(QFile::ReadOnly)) {
- const QString code = QString::fromUtf8(file.readAll());
- file.close();
+ QScopedPointer<QV4::Script> script;
+ if (cache && QFile::exists(fn + QLatin1Char('c'))) {
+ QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit = iSelFactory->createUnitForLoading();
+ QString error;
+ if (unit->loadFromDisk(QUrl::fromLocalFile(fn), iSelFactory, &error)) {
+ script.reset(new QV4::Script(&vm, nullptr, unit));
+ } else {
+ std::cout << "Error loading" << qPrintable(fn) << "from disk cache:" << qPrintable(error) << std::endl;
+ }
+ }
+ if (!script) {
+ const QString code = QString::fromUtf8(file.readAll());
+ file.close();
+ script.reset(new QV4::Script(ctx, code, fn));
+ script->parseAsBinding = runAsQml;
+ script->parse();
+ }
QV4::ScopedValue result(scope);
- QV4::Script script(ctx, code, fn);
- script.parseAsBinding = runAsQml;
- script.parse();
- if (!scope.engine->hasException)
- result = script.run();
+ if (!scope.engine->hasException) {
+ const auto unit = script->compilationUnit;
+ if (cache && unit && !(unit->data->flags & QV4::CompiledData::Unit::StaticData)) {
+ if (unit->data->sourceTimeStamp == 0) {
+ const_cast<QV4::CompiledData::Unit*>(unit->data)->sourceTimeStamp = QFileInfo(fn).lastModified().toMSecsSinceEpoch();
+ }
+ QString saveError;
+ if (!unit->saveToDisk(QUrl::fromLocalFile(fn), &saveError)) {
+ std::cout << "Error saving JS cache file: " << qPrintable(saveError) << std::endl;
+ }
+ }
+ result = script->run();
+ }
if (scope.engine->hasException) {
QV4::StackTrace trace;
QV4::ScopedValue ex(scope, scope.engine->catchException(&trace));
diff --git a/tools/qmlmin/main.cpp b/tools/qmlmin/main.cpp
index 2b18a8442b..d2bad9d0d5 100644
--- a/tools/qmlmin/main.cpp
+++ b/tools/qmlmin/main.cpp
@@ -120,7 +120,7 @@ protected:
static QString quote(const QString &string)
{
QString quotedString;
- foreach (const QChar &ch, string) {
+ for (const QChar &ch : string) {
if (ch == QLatin1Char('"'))
quotedString += QLatin1String("\\\"");
else {
diff --git a/tools/qmlplugindump/main.cpp b/tools/qmlplugindump/main.cpp
index 5a39e497d2..ea54fee885 100644
--- a/tools/qmlplugindump/main.cpp
+++ b/tools/qmlplugindump/main.cpp
@@ -76,7 +76,7 @@
static const uint qtQmlMajorVersion = 2;
static const uint qtQmlMinorVersion = 2;
static const uint qtQuickMajorVersion = 2;
-static const uint qtQuickMinorVersion = 7;
+static const uint qtQuickMinorVersion = 8;
const QString qtQuickQualifiedName = QString::fromLatin1("QtQuick %1.%2")
.arg(qtQuickMajorVersion)
@@ -248,15 +248,15 @@ QSet<const QMetaObject *> collectReachableMetaObjects(QQmlEngine *engine,
QSet<const QQmlType *> baseExports = qmlTypesByCppName.value(it.key());
const QSet<QByteArray> extensionCppNames = it.value();
- foreach (const QByteArray &extensionCppName, extensionCppNames) {
+ for (const QByteArray &extensionCppName : extensionCppNames) {
const QSet<const QQmlType *> extensionExports = qmlTypesByCppName.value(extensionCppName);
// remove extension exports from base imports
// unfortunately the QQmlType pointers don't match, so can't use QSet::subtract
QSet<const QQmlType *> newBaseExports;
- foreach (const QQmlType *baseExport, baseExports) {
+ for (const QQmlType *baseExport : qAsConst(baseExports)) {
bool match = false;
- foreach (const QQmlType *extensionExport, extensionExports) {
+ for (const QQmlType *extensionExport : extensionExports) {
if (baseExport->qmlTypeName() == extensionExport->qmlTypeName()
&& baseExport->majorVersion() == extensionExport->majorVersion()
&& baseExport->minorVersion() == extensionExport->minorVersion()) {
@@ -469,7 +469,7 @@ public:
void dumpComposite(QQmlEngine *engine, const QSet<const QQmlType *> &compositeType, QSet<QByteArray> &defaultReachableNames)
{
- foreach (const QQmlType *type, compositeType)
+ for (const QQmlType *type : compositeType)
dumpCompositeItem(engine, type, defaultReachableNames);
}
@@ -518,7 +518,7 @@ public:
}
}
- foreach (const QMetaObject *meta, objectsToMerge)
+ for (const QMetaObject *meta : qAsConst(objectsToMerge))
writeMetaContent(meta, &knownAttributes);
qml->writeEndObject();
@@ -542,11 +542,11 @@ public:
if (meta->superClass())
qml->writeScriptBinding(QLatin1String("prototype"), enquote(convertToId(meta->superClass())));
- QSet<const QQmlType *> qmlTypes = qmlTypesByCppName.value(meta->className());
+ const QSet<const QQmlType *> qmlTypes = qmlTypesByCppName.value(meta->className());
if (!qmlTypes.isEmpty()) {
QHash<QString, const QQmlType *> exports;
- foreach (const QQmlType *qmlTy, qmlTypes) {
+ for (const QQmlType *qmlTy : qmlTypes) {
const QString exportString = getExportString(qmlTy->qmlTypeName(), qmlTy->majorVersion(), qmlTy->minorVersion());
exports.insert(exportString, qmlTy);
}
@@ -564,7 +564,7 @@ public:
// write meta object revisions
QStringList metaObjectRevisions;
- foreach (const QString &exportString, exportStrings) {
+ for (const QString &exportString : qAsConst(exportStrings)) {
int metaObjectRevision = exports[exportString]->metaObjectRevision();
metaObjectRevisions += QString::number(metaObjectRevision);
}
@@ -749,7 +749,7 @@ static bool readDependenciesData(QString dependenciesFile, const QByteArray &fil
if (verbose) {
std::cerr << "parsing "
<< qPrintable( dependenciesFile ) << " skipping";
- foreach (const QString &uriToSkip, urisToSkip)
+ for (const QString &uriToSkip : urisToSkip)
std::cerr << ' ' << qPrintable(uriToSkip);
std::cerr << std::endl;
}
@@ -763,13 +763,13 @@ static bool readDependenciesData(QString dependenciesFile, const QByteArray &fil
return false;
}
if (doc.isArray()) {
- QStringList requiredKeys = QStringList() << QStringLiteral("name")
- << QStringLiteral("type")
- << QStringLiteral("version");
+ const QStringList requiredKeys = QStringList() << QStringLiteral("name")
+ << QStringLiteral("type")
+ << QStringLiteral("version");
foreach (const QJsonValue &dep, doc.array()) {
if (dep.isObject()) {
QJsonObject obj = dep.toObject();
- foreach (const QString &requiredKey, requiredKeys)
+ for (const QString &requiredKey : requiredKeys)
if (!obj.contains(requiredKey) || obj.value(requiredKey).isString())
continue;
if (obj.value(QStringLiteral("type")).toString() != QLatin1String("module"))
@@ -850,7 +850,7 @@ static bool getDependencies(const QQmlEngine &engine, const QString &pluginImpor
if (!importScanner.waitForFinished()) {
std::cerr << "failure to start " << qPrintable(command);
- foreach (const QString &arg, commandArgs)
+ for (const QString &arg : qAsConst(commandArgs))
std::cerr << ' ' << qPrintable(arg);
std::cerr << std::endl;
return false;
@@ -863,7 +863,7 @@ static bool getDependencies(const QQmlEngine &engine, const QString &pluginImpor
}
QStringList aux;
- foreach (const QString &str, *dependencies) {
+ for (const QString &str : qAsConst(*dependencies)) {
if (!str.startsWith("Qt.test.qtestroot"))
aux += str;
}
@@ -977,7 +977,7 @@ int main(int argc, char *argv[])
}
}
- if (!requireWindowManager)
+ if (!requireWindowManager && qEnvironmentVariableIsEmpty("QT_QPA_PLATFORM"))
qputenv("QT_QPA_PLATFORM", QByteArrayLiteral("minimal"));
else
QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts, true);
@@ -1124,7 +1124,7 @@ int main(int argc, char *argv[])
// load the QtQml builtins and the dependencies
{
QByteArray code(qtQmlImportString.toUtf8());
- foreach (const QString &moduleToImport, dependencies) {
+ for (const QString &moduleToImport : qAsConst(dependencies)) {
code.append("\nimport ");
code.append(moduleToImport.toUtf8());
}
@@ -1154,7 +1154,7 @@ int main(int argc, char *argv[])
QSet<const QMetaObject *> metas;
if (action == Builtins) {
- foreach (const QMetaObject *m, defaultReachable) {
+ for (const QMetaObject *m : qAsConst(defaultReachable)) {
if (m->className() == QLatin1String("Qt")) {
metas.insert(m);
break;
@@ -1175,7 +1175,7 @@ int main(int argc, char *argv[])
return EXIT_INVALIDARGUMENTS;
}
metas = defaultReachable;
- foreach (const QMetaObject *m, defaultReachable) {
+ for (const QMetaObject *m : qAsConst(defaultReachable)) {
if (m->className() == QLatin1String("Qt")) {
metas.remove(m);
break;
@@ -1196,7 +1196,7 @@ int main(int argc, char *argv[])
QString::number(qtObjectType->minorVersion())).toUtf8();
}
// avoid importing dependencies?
- foreach (const QString &moduleToImport, dependencies) {
+ for (const QString &moduleToImport : qAsConst(dependencies)) {
importCode.append("\nimport ");
importCode.append(moduleToImport.toUtf8());
}
@@ -1231,9 +1231,9 @@ int main(int argc, char *argv[])
// Also eliminate meta objects with the same classname.
// This is required because extended objects seem not to share
// a single meta object instance.
- foreach (const QMetaObject *mo, defaultReachable)
+ for (const QMetaObject *mo : qAsConst(defaultReachable))
defaultReachableNames.insert(QByteArray(mo->className()));
- foreach (const QMetaObject *mo, candidates) {
+ for (const QMetaObject *mo : qAsConst(candidates)) {
if (!defaultReachableNames.contains(mo->className()))
metas.insert(mo);
}
@@ -1265,19 +1265,19 @@ int main(int argc, char *argv[])
compactDependencies(&dependencies);
QStringList quotedDependencies;
- foreach (const QString &dep, dependencies)
+ for (const QString &dep : qAsConst(dependencies))
quotedDependencies << enquote(dep);
qml.writeArrayBinding("dependencies", quotedDependencies);
// put the metaobjects into a map so they are always dumped in the same order
QMap<QString, const QMetaObject *> nameToMeta;
- foreach (const QMetaObject *meta, metas)
+ for (const QMetaObject *meta : qAsConst(metas))
nameToMeta.insert(convertToId(meta), meta);
Dumper dumper(&qml);
if (relocatable)
dumper.setRelocatableModuleUri(pluginImportUri);
- foreach (const QMetaObject *meta, nameToMeta) {
+ for (const QMetaObject *meta : qAsConst(nameToMeta)) {
dumper.dump(QQmlEnginePrivate::get(&engine), meta, uncreatableMetas.contains(meta), singletonMetas.contains(meta));
}
diff --git a/tools/qmlplugindump/qmlstreamwriter.cpp b/tools/qmlplugindump/qmlstreamwriter.cpp
index dd3c188fe4..3632cee60d 100644
--- a/tools/qmlplugindump/qmlstreamwriter.cpp
+++ b/tools/qmlplugindump/qmlstreamwriter.cpp
@@ -179,7 +179,7 @@ void QmlStreamWriter::flushPotentialLinesWithNewlines()
{
if (m_maybeOneline)
m_stream->write("\n");
- foreach (const QByteArray &line, m_pendingLines) {
+ for (const QByteArray &line : qAsConst(m_pendingLines)) {
writeIndent();
m_stream->write(line);
m_stream->write("\n");
diff --git a/tools/qmlprofiler/commandlistener.h b/tools/qmlprofiler/commandlistener.h
index c10b199daa..2a994bf449 100644
--- a/tools/qmlprofiler/commandlistener.h
+++ b/tools/qmlprofiler/commandlistener.h
@@ -33,7 +33,7 @@
class CommandListener : public QObject {
Q_OBJECT
-public slots:
+public:
void readCommand();
signals:
diff --git a/tools/qmlprofiler/main.cpp b/tools/qmlprofiler/main.cpp
index d3e2beb83f..c7cb979ff8 100644
--- a/tools/qmlprofiler/main.cpp
+++ b/tools/qmlprofiler/main.cpp
@@ -39,8 +39,10 @@ int main(int argc, char *argv[])
QThread listenerThread;
CommandListener listener;
listener.moveToThread(&listenerThread);
- QObject::connect(&listener, SIGNAL(command(QString)), &app, SLOT(userCommand(QString)));
- QObject::connect(&app, SIGNAL(readyForCommand()), &listener, SLOT(readCommand()));
+ QObject::connect(&listener, &CommandListener::command,
+ &app, &QmlProfilerApplication::userCommand);
+ QObject::connect(&app, &QmlProfilerApplication::readyForCommand,
+ &listener, &CommandListener::readCommand);
listenerThread.start();
int exitValue = app.exec();
listenerThread.quit();
diff --git a/tools/qmlprofiler/qmlprofilerapplication.cpp b/tools/qmlprofiler/qmlprofilerapplication.cpp
index b04ff7e558..033492b516 100644
--- a/tools/qmlprofiler/qmlprofilerapplication.cpp
+++ b/tools/qmlprofiler/qmlprofilerapplication.cpp
@@ -87,17 +87,21 @@ QmlProfilerApplication::QmlProfilerApplication(int &argc, char **argv) :
m_connectionAttempts(0)
{
m_connectTimer.setInterval(1000);
- connect(&m_connectTimer, SIGNAL(timeout()), this, SLOT(tryToConnect()));
+ connect(&m_connectTimer, &QTimer::timeout, this, &QmlProfilerApplication::tryToConnect);
- connect(&m_connection, SIGNAL(connected()), this, SLOT(connected()));
+ connect(&m_connection, &QQmlDebugConnection::connected,
+ this, &QmlProfilerApplication::connected);
- connect(&m_qmlProfilerClient, SIGNAL(enabledChanged(bool)),
- this, SLOT(traceClientEnabledChanged(bool)));
- connect(&m_qmlProfilerClient, SIGNAL(recordingStarted()), this, SLOT(notifyTraceStarted()));
- connect(&m_qmlProfilerClient, SIGNAL(error(QString)), this, SLOT(logError(QString)));
+ connect(&m_qmlProfilerClient, &QmlProfilerClient::enabledChanged,
+ this, &QmlProfilerApplication::traceClientEnabledChanged);
+ connect(&m_qmlProfilerClient, &QmlProfilerClient::recordingStarted,
+ this, &QmlProfilerApplication::notifyTraceStarted);
+ connect(&m_qmlProfilerClient, &QmlProfilerClient::error,
+ this, &QmlProfilerApplication::logError);
- connect(&m_profilerData, SIGNAL(error(QString)), this, SLOT(logError(QString)));
- connect(&m_profilerData, SIGNAL(dataReady()), this, SLOT(traceFinished()));
+ connect(&m_profilerData, &QmlProfilerData::error, this, &QmlProfilerApplication::logError);
+ connect(&m_profilerData, &QmlProfilerData::dataReady,
+ this, &QmlProfilerApplication::traceFinished);
}
@@ -257,7 +261,7 @@ void QmlProfilerApplication::parseArguments()
int QmlProfilerApplication::exec()
{
- QTimer::singleShot(0, this, SLOT(run()));
+ QTimer::singleShot(0, this, &QmlProfilerApplication::run);
return QCoreApplication::exec();
}
@@ -270,8 +274,8 @@ quint64 QmlProfilerApplication::parseFeatures(const QStringList &featureList, co
bool exclude)
{
quint64 features = exclude ? std::numeric_limits<quint64>::max() : 0;
- QStringList givenFeatures = values.split(QLatin1Char(','));
- foreach (const QString &f, givenFeatures) {
+ const QStringList givenFeatures = values.split(QLatin1Char(','));
+ for (const QString &f : givenFeatures) {
int index = featureList.indexOf(f);
if (index < 0) {
logError(tr("Unknown feature '%1'").arg(f));
@@ -343,7 +347,7 @@ bool QmlProfilerApplication::checkOutputFile(PendingRequest pending)
void QmlProfilerApplication::userCommand(const QString &command)
{
- QStringList args = command.split(QChar::Space, QString::SkipEmptyParts);
+ auto args = command.splitRef(QChar::Space, QString::SkipEmptyParts);
if (args.isEmpty()) {
prompt();
return;
@@ -397,7 +401,7 @@ void QmlProfilerApplication::userCommand(const QString &command)
} else if (m_profilerData.isEmpty()) {
prompt(tr("No data was recorded so far."));
} else {
- m_interactiveOutputFile = args.length() > 0 ? args[0] : m_outputFile;
+ m_interactiveOutputFile = args.length() > 0 ? args.at(0).toString() : m_outputFile;
if (checkOutputFile(REQUEST_OUTPUT_FILE))
output();
}
@@ -414,7 +418,7 @@ void QmlProfilerApplication::userCommand(const QString &command)
if (!m_recording && m_profilerData.isEmpty()) {
prompt(tr("No data was recorded so far."));
} else {
- m_interactiveOutputFile = args.length() > 0 ? args[0] : m_outputFile;
+ m_interactiveOutputFile = args.length() > 0 ? args.at(0).toString() : m_outputFile;
if (checkOutputFile(REQUEST_FLUSH_FILE))
flush();
}
@@ -460,9 +464,9 @@ void QmlProfilerApplication::run()
arguments << m_programArguments;
m_process->setProcessChannelMode(QProcess::MergedChannels);
- connect(m_process, SIGNAL(readyRead()), this, SLOT(processHasOutput()));
- connect(m_process, SIGNAL(finished(int,QProcess::ExitStatus)), this,
- SLOT(processFinished()));
+ connect(m_process, &QIODevice::readyRead, this, &QmlProfilerApplication::processHasOutput);
+ connect(m_process, static_cast<void(QProcess::*)(int)>(&QProcess::finished),
+ this, [this](int){ processFinished(); });
logStatus(QString("Starting '%1 %2' ...").arg(m_programPath,
arguments.join(QLatin1Char(' '))));
m_process->start(m_programPath, arguments);
diff --git a/tools/qmlprofiler/qmlprofilerapplication.h b/tools/qmlprofiler/qmlprofilerapplication.h
index 04f9d43c87..13f0f041f0 100644
--- a/tools/qmlprofiler/qmlprofilerapplication.h
+++ b/tools/qmlprofiler/qmlprofilerapplication.h
@@ -58,16 +58,14 @@ public:
void parseArguments();
int exec();
bool isInteractive() const;
-
-signals:
- void readyForCommand();
-
-public slots:
void userCommand(const QString &command);
void notifyTraceStarted();
void outputData();
-private slots:
+signals:
+ void readyForCommand();
+
+private:
void run();
void tryToConnect();
void connected();
@@ -81,7 +79,6 @@ private slots:
void logError(const QString &error);
void logStatus(const QString &status);
-private:
quint64 parseFeatures(const QStringList &featureList, const QString &values, bool exclude);
bool checkOutputFile(PendingRequest pending);
void flush();
diff --git a/tools/qmlprofiler/qmlprofilerdata.cpp b/tools/qmlprofiler/qmlprofilerdata.cpp
index 74fa44c1d6..048c92bb93 100644
--- a/tools/qmlprofiler/qmlprofilerdata.cpp
+++ b/tools/qmlprofiler/qmlprofilerdata.cpp
@@ -248,7 +248,7 @@ void QmlProfilerData::addQmlEvent(QQmlProfilerDefinitions::RangeType type,
eventHashStr = getHashStringForQmlEvent(eventLocation, type);
} else {
const QString filePath = QUrl(eventLocation.filename).path();
- displayName = filePath.mid(
+ displayName = filePath.midRef(
filePath.lastIndexOf(QLatin1Char('/')) + 1) +
QLatin1Char(':') + QString::number(eventLocation.line);
eventHashStr = getHashStringForQmlEvent(eventLocation, type);
@@ -327,8 +327,8 @@ void QmlProfilerData::addPixmapCacheEvent(QQmlProfilerDefinitions::PixmapEventTy
QString filePath = QUrl(location).path();
- QString eventHashStr = filePath.mid(filePath.lastIndexOf(QLatin1Char('/')) + 1) +
- QStringLiteral(":") + QString::number(type);
+ const QString eventHashStr = filePath.midRef(filePath.lastIndexOf(QLatin1Char('/')) + 1)
+ + QLatin1Char(':') + QString::number(type);
QmlRangeEventData *newEvent;
if (d->eventDescriptions.contains(eventHashStr)) {
newEvent = d->eventDescriptions[eventHashStr];
@@ -572,7 +572,7 @@ bool QmlProfilerData::save(const QString &filename)
stream.writeEndElement(); // eventData
stream.writeStartElement(QStringLiteral("profilerDataModel"));
- foreach (const QmlRangeEventStartInstance &event, d->startInstanceList) {
+ for (const QmlRangeEventStartInstance &event : qAsConst(d->startInstanceList)) {
stream.writeStartElement(QStringLiteral("range"));
stream.writeAttribute(QStringLiteral("startTime"), QString::number(event.startTime));
if (event.duration >= 0)
diff --git a/tools/qmlprofiler/qmlprofilerdata.h b/tools/qmlprofiler/qmlprofilerdata.h
index 2570513d93..00ef037071 100644
--- a/tools/qmlprofiler/qmlprofilerdata.h
+++ b/tools/qmlprofiler/qmlprofilerdata.h
@@ -58,12 +58,6 @@ public:
bool isEmpty() const;
-signals:
- void error(QString);
- void stateChanged();
- void dataReady();
-
-public slots:
void clear();
void setTraceEndTime(qint64 time);
void setTraceStartTime(qint64 time);
@@ -83,6 +77,11 @@ public slots:
void complete();
bool save(const QString &filename);
+signals:
+ void error(QString);
+ void stateChanged();
+ void dataReady();
+
private:
void sortStartTimes();
void computeQmlTime();
diff --git a/tools/qmlscene/main.cpp b/tools/qmlscene/main.cpp
index c892f60680..9e1002166d 100644
--- a/tools/qmlscene/main.cpp
+++ b/tools/qmlscene/main.cpp
@@ -175,10 +175,10 @@ QFileInfoList findQmlFiles(const QString &dirName)
QFileInfoList ret;
if (dir.exists()) {
- QFileInfoList fileInfos = dir.entryInfoList(QStringList() << "*.qml",
- QDir::Files | QDir::AllDirs | QDir::NoDotAndDotDot);
+ const QFileInfoList fileInfos = dir.entryInfoList(QStringList() << "*.qml",
+ QDir::Files | QDir::AllDirs | QDir::NoDotAndDotDot);
- foreach (QFileInfo fileInfo, fileInfos) {
+ for (const QFileInfo &fileInfo : fileInfos) {
if (fileInfo.isDir())
ret += findQmlFiles(fileInfo.filePath());
else if (fileInfo.fileName().length() > 0 && fileInfo.fileName().at(0).isLower())
@@ -196,9 +196,9 @@ static int displayOptionsDialog(Options *options)
QFormLayout *layout = new QFormLayout(&dialog);
QComboBox *qmlFileComboBox = new QComboBox(&dialog);
- QFileInfoList fileInfos = findQmlFiles(":/bundle") + findQmlFiles("./qmlscene-resources");
+ const QFileInfoList fileInfos = findQmlFiles(":/bundle") + findQmlFiles("./qmlscene-resources");
- foreach (QFileInfo fileInfo, fileInfos)
+ for (const QFileInfo &fileInfo : fileInfos)
qmlFileComboBox->addItem(fileInfo.dir().dirName() + QLatin1Char('/') + fileInfo.fileName(), QVariant::fromValue(fileInfo));
QCheckBox *originalCheckBox = new QCheckBox(&dialog);
@@ -319,8 +319,8 @@ static void loadDummyDataFiles(QQmlEngine &engine, const QString& directory)
QObject *dummyData = comp.create();
if(comp.isError()) {
- QList<QQmlError> errors = comp.errors();
- foreach (const QQmlError &error, errors)
+ const QList<QQmlError> errors = comp.errors();
+ for (const QQmlError &error : errors)
fprintf(stderr, "%s\n", qPrintable(error.toString()));
}
@@ -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)
@@ -453,7 +457,7 @@ int main(int argc, char ** argv)
options.applicationAttributes.append(Qt::AA_DisableHighDpiScaling);
}
- foreach (Qt::ApplicationAttribute a, options.applicationAttributes)
+ for (Qt::ApplicationAttribute a : qAsConst(options.applicationAttributes))
QCoreApplication::setAttribute(a);
#ifdef QT_WIDGETS_LIB
QApplication app(argc, argv);
@@ -560,6 +564,7 @@ int main(int argc, char ** argv)
loadDummyDataFiles(engine, fi.path());
}
QObject::connect(&engine, SIGNAL(quit()), QCoreApplication::instance(), SLOT(quit()));
+ QObject::connect(&engine, &QQmlEngine::exit, QCoreApplication::instance(), &QCoreApplication::exit);
component->loadUrl(options.url);
while (component->isLoading())
QCoreApplication::processEvents();
@@ -592,8 +597,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);
diff --git a/tools/qmlscene/qmlscene.pro b/tools/qmlscene/qmlscene.pro
index 0411fd8e31..b1267612c5 100644
--- a/tools/qmlscene/qmlscene.pro
+++ b/tools/qmlscene/qmlscene.pro
@@ -4,6 +4,7 @@ CONFIG += no_import_scan
SOURCES += main.cpp
-DEFINES += QML_RUNTIME_TESTING QT_QML_DEBUG_NO_WARNING
+DEFINES += QML_RUNTIME_TESTING
+!contains(QT_CONFIG, no-qml-debug): DEFINES += QT_QML_DEBUG_NO_WARNING
load(qt_tool)
diff --git a/tools/tools.pro b/tools/tools.pro
index 18bfe28a8a..3952ec4b01 100644
--- a/tools/tools.pro
+++ b/tools/tools.pro
@@ -1,4 +1,5 @@
TEMPLATE = subdirs
+QT_FOR_CONFIG += qml-private
SUBDIRS += \
qmlmin \
qmlimportscanner
@@ -11,7 +12,7 @@ qmlimportscanner.CONFIG = host_build
qml \
qmllint
- !contains(QT_CONFIG, no-qml-debug): SUBDIRS += qmlprofiler
+ qtConfig(qml-network):!contains(QT_CONFIG, no-qml-debug): SUBDIRS += qmlprofiler
qtHaveModule(quick) {
!static: {
@@ -23,7 +24,7 @@ qmlimportscanner.CONFIG = host_build
qtHaveModule(widgets): SUBDIRS += qmleasing
}
qtHaveModule(qmltest): SUBDIRS += qmltestrunner
- contains(QT_CONFIG, private_tests): SUBDIRS += qmljs
+ qtConfig(private_tests): SUBDIRS += qmljs
}
qml.depends = qmlimportscanner