aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-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--examples/qml/qml.pro12
-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.cpp38
-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.cpp273
-rw-r--r--examples/quick/scenegraph/rendernode/d3d12renderer.h80
-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.cpp154
-rw-r--r--examples/quick/scenegraph/rendernode/openglrenderer.h74
-rw-r--r--examples/quick/scenegraph/rendernode/rendernode.pro39
-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.cpp91
-rw-r--r--examples/quick/scenegraph/rendernode/softwarerenderer.h62
-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--qtdeclarative.pro3
-rw-r--r--src/3rdparty/masm/masm-defs.pri2
-rw-r--r--src/imports/folderlistmodel/plugin.cpp2
-rw-r--r--src/imports/imports.pro6
-rw-r--r--src/imports/layouts/plugin.cpp2
-rw-r--r--src/imports/layouts/qquicklayout.cpp17
-rw-r--r--src/imports/layouts/qquicklinearlayout.cpp16
-rw-r--r--src/imports/localstorage/plugin.cpp8
-rw-r--r--src/imports/models/plugin.cpp2
-rw-r--r--src/imports/particles/plugin.cpp2
-rw-r--r--src/imports/qtquick2/plugin.cpp2
-rw-r--r--src/imports/qtquick2/plugins.qmltypes11
-rw-r--r--src/imports/settings/plugin.cpp2
-rw-r--r--src/imports/statemachine/plugin.cpp2
-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/window/plugin.cpp2
-rw-r--r--src/imports/xmllistmodel/plugin.cpp2
-rw-r--r--src/particles/qquickcustomparticle.cpp42
-rw-r--r--src/particles/qquickcustomparticle_p.h22
-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/qqmlenginedebugservice.cpp25
-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.cpp71
-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.pro44
-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.cpp3233
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12engine_p.h387
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12engine_p_p.h451
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12glyphcache.cpp184
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12glyphcache_p.h88
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12glyphnode.cpp90
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12glyphnode_p.h74
-rw-r--r--src/plugins/scenegraph/d3d12/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.cpp784
-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.pro2
-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.pri8
-rw-r--r--src/qml/compiler/qqmlirbuilder.cpp407
-rw-r--r--src/qml/compiler/qqmlirbuilder_p.h138
-rw-r--r--src/qml/compiler/qqmlpropertycachecreator.cpp71
-rw-r--r--src/qml/compiler/qqmlpropertycachecreator_p.h746
-rw-r--r--src/qml/compiler/qqmlpropertyvalidator.cpp703
-rw-r--r--src/qml/compiler/qqmlpropertyvalidator_p.h87
-rw-r--r--src/qml/compiler/qqmltypecompiler.cpp1938
-rw-r--r--src/qml/compiler/qqmltypecompiler_p.h269
-rw-r--r--src/qml/compiler/qv4codegen.cpp7
-rw-r--r--src/qml/compiler/qv4compileddata.cpp316
-rw-r--r--src/qml/compiler/qv4compileddata_p.h544
-rw-r--r--src/qml/compiler/qv4compiler.cpp214
-rw-r--r--src/qml/compiler/qv4compiler_p.h11
-rw-r--r--src/qml/compiler/qv4instr_moth_p.h23
-rw-r--r--src/qml/compiler/qv4isel_moth.cpp194
-rw-r--r--src/qml/compiler/qv4isel_moth_p.h18
-rw-r--r--src/qml/compiler/qv4isel_p.cpp3
-rw-r--r--src/qml/compiler/qv4isel_p.h41
-rw-r--r--src/qml/compiler/qv4isel_util_p.h50
-rw-r--r--src/qml/compiler/qv4jsir.cpp331
-rw-r--r--src/qml/compiler/qv4jsir_p.h445
-rw-r--r--src/qml/compiler/qv4ssa.cpp1019
-rw-r--r--src/qml/debugger/debugger.pri26
-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)7
-rw-r--r--src/qml/debugger/qqmlmemoryprofiler_p.h (renamed from src/qml/qml/qqmlmemoryprofiler_p.h)9
-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/javascript/functionlist.qdoc24
-rw-r--r--src/qml/jit/qv4assembler.cpp70
-rw-r--r--src/qml/jit/qv4assembler_p.h145
-rw-r--r--src/qml/jit/qv4binop.cpp62
-rw-r--r--src/qml/jit/qv4binop_p.h4
-rw-r--r--src/qml/jit/qv4isel_masm.cpp170
-rw-r--r--src/qml/jit/qv4isel_masm_p.h14
-rw-r--r--src/qml/jit/qv4regalloc.cpp83
-rw-r--r--src/qml/jit/qv4unop.cpp43
-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.cpp56
-rw-r--r--src/qml/jsapi/qjsvalue.h2
-rw-r--r--src/qml/jsruntime/jsruntime.pri13
-rw-r--r--src/qml/jsruntime/qv4argumentsobject.cpp24
-rw-r--r--src/qml/jsruntime/qv4argumentsobject_p.h4
-rw-r--r--src/qml/jsruntime/qv4arraybuffer.cpp30
-rw-r--r--src/qml/jsruntime/qv4arraybuffer_p.h4
-rw-r--r--src/qml/jsruntime/qv4arraydata.cpp2
-rw-r--r--src/qml/jsruntime/qv4arrayobject.cpp64
-rw-r--r--src/qml/jsruntime/qv4arrayobject_p.h4
-rw-r--r--src/qml/jsruntime/qv4booleanobject.cpp9
-rw-r--r--src/qml/jsruntime/qv4booleanobject_p.h4
-rw-r--r--src/qml/jsruntime/qv4dataview.cpp22
-rw-r--r--src/qml/jsruntime/qv4dataview_p.h4
-rw-r--r--src/qml/jsruntime/qv4dateobject.cpp16
-rw-r--r--src/qml/jsruntime/qv4dateobject_p.h4
-rw-r--r--src/qml/jsruntime/qv4debugging_p.h15
-rw-r--r--src/qml/jsruntime/qv4engine.cpp55
-rw-r--r--src/qml/jsruntime/qv4engine_p.h50
-rw-r--r--src/qml/jsruntime/qv4errorobject.cpp49
-rw-r--r--src/qml/jsruntime/qv4errorobject_p.h16
-rw-r--r--src/qml/jsruntime/qv4function.cpp6
-rw-r--r--src/qml/jsruntime/qv4functionobject.cpp190
-rw-r--r--src/qml/jsruntime/qv4functionobject_p.h28
-rw-r--r--src/qml/jsruntime/qv4globalobject.cpp38
-rw-r--r--src/qml/jsruntime/qv4globalobject_p.h4
-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.cpp84
-rw-r--r--src/qml/jsruntime/qv4lookup.cpp24
-rw-r--r--src/qml/jsruntime/qv4mathobject.cpp14
-rw-r--r--src/qml/jsruntime/qv4mathobject_p.h1
-rw-r--r--src/qml/jsruntime/qv4numberobject.cpp32
-rw-r--r--src/qml/jsruntime/qv4numberobject_p.h6
-rw-r--r--src/qml/jsruntime/qv4object.cpp22
-rw-r--r--src/qml/jsruntime/qv4object_p.h17
-rw-r--r--src/qml/jsruntime/qv4objectproto.cpp21
-rw-r--r--src/qml/jsruntime/qv4objectproto_p.h4
-rw-r--r--src/qml/jsruntime/qv4persistent_p.h4
-rw-r--r--src/qml/jsruntime/qv4profiling.cpp28
-rw-r--r--src/qml/jsruntime/qv4profiling_p.h123
-rw-r--r--src/qml/jsruntime/qv4qobjectwrapper.cpp290
-rw-r--r--src/qml/jsruntime/qv4qobjectwrapper_p.h34
-rw-r--r--src/qml/jsruntime/qv4regexpobject.cpp52
-rw-r--r--src/qml/jsruntime/qv4regexpobject_p.h4
-rw-r--r--src/qml/jsruntime/qv4runtime.cpp565
-rw-r--r--src/qml/jsruntime/qv4runtime_p.h434
-rw-r--r--src/qml/jsruntime/qv4runtimeapi_p.h315
-rw-r--r--src/qml/jsruntime/qv4scopedvalue_p.h53
-rw-r--r--src/qml/jsruntime/qv4script.cpp58
-rw-r--r--src/qml/jsruntime/qv4script_p.h6
-rw-r--r--src/qml/jsruntime/qv4sequenceobject.cpp6
-rw-r--r--src/qml/jsruntime/qv4string.cpp121
-rw-r--r--src/qml/jsruntime/qv4string_p.h66
-rw-r--r--src/qml/jsruntime/qv4stringobject.cpp129
-rw-r--r--src/qml/jsruntime/qv4stringobject_p.h7
-rw-r--r--src/qml/jsruntime/qv4typedarray.cpp81
-rw-r--r--src/qml/jsruntime/qv4typedarray_p.h4
-rw-r--r--src/qml/jsruntime/qv4value_p.h13
-rw-r--r--src/qml/jsruntime/qv4vme_moth.cpp158
-rw-r--r--src/qml/memory/qv4mm.cpp132
-rw-r--r--src/qml/memory/qv4mm_p.h4
-rw-r--r--src/qml/parser/qqmljsengine_p.cpp2
-rw-r--r--src/qml/qml.pro12
-rw-r--r--src/qml/qml/ftw/ftw.pri1
-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.pri10
-rw-r--r--src/qml/qml/qqmlabstractbinding_p.h12
-rw-r--r--src/qml/qml/qqmlaccessors_p.h7
-rw-r--r--src/qml/qml/qqmlapplicationengine.cpp11
-rw-r--r--src/qml/qml/qqmlbinding.cpp305
-rw-r--r--src/qml/qml/qqmlbinding_p.h45
-rw-r--r--src/qml/qml/qqmlboundsignal.cpp19
-rw-r--r--src/qml/qml/qqmlcompileddata.cpp165
-rw-r--r--src/qml/qml/qqmlcompiler_p.h160
-rw-r--r--src/qml/qml/qqmlcomponent.cpp73
-rw-r--r--src/qml/qml/qqmlcomponent.h9
-rw-r--r--src/qml/qml/qqmlcomponent_p.h6
-rw-r--r--src/qml/qml/qqmlcontext.cpp57
-rw-r--r--src/qml/qml/qqmlcontext_p.h13
-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.h14
-rw-r--r--src/qml/qml/qqmldelayedcallqueue.cpp215
-rw-r--r--src/qml/qml/qqmldelayedcallqueue_p.h104
-rw-r--r--src/qml/qml/qqmlengine.cpp147
-rw-r--r--src/qml/qml/qqmlengine.h4
-rw-r--r--src/qml/qml/qqmlengine_p.h16
-rw-r--r--src/qml/qml/qqmlexpression.cpp11
-rw-r--r--src/qml/qml/qqmlexpression_p.h3
-rw-r--r--src/qml/qml/qqmlfile.cpp30
-rw-r--r--src/qml/qml/qqmlfile.h2
-rw-r--r--src/qml/qml/qqmlimport.cpp13
-rw-r--r--src/qml/qml/qqmlincubator.cpp32
-rw-r--r--src/qml/qml/qqmlincubator_p.h4
-rw-r--r--src/qml/qml/qqmljavascriptexpression.cpp73
-rw-r--r--src/qml/qml/qqmljavascriptexpression_p.h19
-rw-r--r--src/qml/qml/qqmlmetatype.cpp23
-rw-r--r--src/qml/qml/qqmlnetworkaccessmanagerfactory.cpp4
-rw-r--r--src/qml/qml/qqmlnetworkaccessmanagerfactory.h3
-rw-r--r--src/qml/qml/qqmlobjectcreator.cpp339
-rw-r--r--src/qml/qml/qqmlobjectcreator_p.h21
-rw-r--r--src/qml/qml/qqmlplatform.cpp2
-rw-r--r--src/qml/qml/qqmlproperty.cpp190
-rw-r--r--src/qml/qml/qqmlproperty_p.h18
-rw-r--r--src/qml/qml/qqmlpropertycache.cpp405
-rw-r--r--src/qml/qml/qqmlpropertycache_p.h187
-rw-r--r--src/qml/qml/qqmltypeloader.cpp574
-rw-r--r--src/qml/qml/qqmltypeloader_p.h80
-rw-r--r--src/qml/qml/qqmlvaluetype.cpp2
-rw-r--r--src/qml/qml/qqmlvaluetype_p.h2
-rw-r--r--src/qml/qml/qqmlvaluetypeproxybinding.cpp2
-rw-r--r--src/qml/qml/qqmlvaluetypeproxybinding_p.h2
-rw-r--r--src/qml/qml/qqmlvaluetypewrapper.cpp34
-rw-r--r--src/qml/qml/qqmlvaluetypewrapper_p.h1
-rw-r--r--src/qml/qml/qqmlvme.cpp1
-rw-r--r--src/qml/qml/qqmlvme_p.h4
-rw-r--r--src/qml/qml/qqmlvmemetaobject.cpp509
-rw-r--r--src/qml/qml/qqmlvmemetaobject_p.h116
-rw-r--r--src/qml/qml/qqmlxmlhttprequest.cpp31
-rw-r--r--src/qml/qml/qqmlxmlhttprequest_p.h4
-rw-r--r--src/qml/qml/v8/qqmlbuiltinfunctions.cpp33
-rw-r--r--src/qml/qml/v8/qqmlbuiltinfunctions_p.h4
-rw-r--r--src/qml/qml/v8/qv8engine.cpp7
-rw-r--r--src/qml/qml/v8/qv8engine_p.h11
-rw-r--r--src/qml/qml/v8/v8.pri1
-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.cpp34
-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.h2
-rw-r--r--src/qml/types/qquickworkerscript.cpp30
-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.pro2
-rw-r--r--src/qmltest/quicktestresult.cpp2
-rw-r--r--src/quick/designer/qqmldesignermetaobject.cpp64
-rw-r--r--src/quick/designer/qqmldesignermetaobject_p.h3
-rw-r--r--src/quick/designer/qquickdesignercustomobjectdata.cpp7
-rw-r--r--src/quick/designer/qquickdesignersupport.cpp2
-rw-r--r--src/quick/designer/qquickdesignerwindowmanager.cpp11
-rw-r--r--src/quick/designer/qquickdesignerwindowmanager_p.h7
-rw-r--r--src/quick/doc/snippets/qml/externaldrag.qml76
-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/qquickcanvasitem.cpp77
-rw-r--r--src/quick/items/context2d/qquickcontext2d.cpp55
-rw-r--r--src/quick/items/context2d/qquickcontext2dcommandbuffer.cpp6
-rw-r--r--src/quick/items/context2d/qquickcontext2dtexture.cpp17
-rw-r--r--src/quick/items/context2d/qquickcontext2dtexture_p.h18
-rw-r--r--src/quick/items/context2d/qquickcontext2dtile.cpp13
-rw-r--r--src/quick/items/context2d/qquickcontext2dtile_p.h9
-rw-r--r--src/quick/items/items.pri81
-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.cpp11
-rw-r--r--src/quick/items/qquickanimatedimage_p_p.h12
-rw-r--r--src/quick/items/qquickanimatedsprite.cpp686
-rw-r--r--src/quick/items/qquickanimatedsprite_p.h286
-rw-r--r--src/quick/items/qquickanimatedsprite_p_p.h96
-rw-r--r--src/quick/items/qquickborderimage.cpp102
-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/qquickdrag.cpp53
-rw-r--r--src/quick/items/qquickdrag_p.h6
-rw-r--r--src/quick/items/qquickevents.cpp22
-rw-r--r--src/quick/items/qquickevents_p_p.h90
-rw-r--r--src/quick/items/qquickflickable.cpp6
-rw-r--r--src/quick/items/qquickflickable_p_p.h3
-rw-r--r--src/quick/items/qquickframebufferobject.cpp2
-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/qquickimage.cpp4
-rw-r--r--src/quick/items/qquickimplicitsizeitem.cpp13
-rw-r--r--src/quick/items/qquickitem.cpp373
-rw-r--r--src/quick/items/qquickitem.h2
-rw-r--r--src/quick/items/qquickitem_p.h35
-rw-r--r--src/quick/items/qquickitemchangelistener_p.h59
-rw-r--r--src/quick/items/qquickitemgrabresult.cpp3
-rw-r--r--src/quick/items/qquickitemsmodule.cpp20
-rw-r--r--src/quick/items/qquickitemview.cpp5
-rw-r--r--src/quick/items/qquickitemview_p_p.h2
-rw-r--r--src/quick/items/qquicklistview.cpp26
-rw-r--r--src/quick/items/qquicklistview_p.h6
-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.cpp36
-rw-r--r--src/quick/items/qquickmousearea_p_p.h3
-rw-r--r--src/quick/items/qquickmultipointtoucharea.cpp4
-rw-r--r--src/quick/items/qquickopenglinfo.cpp4
-rw-r--r--src/quick/items/qquickopenglshadereffect.cpp973
-rw-r--r--src/quick/items/qquickopenglshadereffect_p.h194
-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)52
-rw-r--r--src/quick/items/qquickpainteditem.cpp29
-rw-r--r--src/quick/items/qquickpathview_p.h3
-rw-r--r--src/quick/items/qquickpathview_p_p.h6
-rw-r--r--src/quick/items/qquickpositioners_p_p.h4
-rw-r--r--src/quick/items/qquickrectangle.cpp5
-rw-r--r--src/quick/items/qquickrendercontrol.cpp65
-rw-r--r--src/quick/items/qquickshadereffect.cpp1315
-rw-r--r--src/quick/items/qquickshadereffect_p.h123
-rw-r--r--src/quick/items/qquickshadereffectmesh.cpp287
-rw-r--r--src/quick/items/qquickshadereffectmesh_p.h59
-rw-r--r--src/quick/items/qquickshadereffectsource.cpp11
-rw-r--r--src/quick/items/qquickshadereffectsource_p.h12
-rw-r--r--src/quick/items/qquicksprite_p.h2
-rw-r--r--src/quick/items/qquickspriteengine.cpp13
-rw-r--r--src/quick/items/qquickspriteengine_p.h2
-rw-r--r--src/quick/items/qquickspritesequence.cpp402
-rw-r--r--src/quick/items/qquickspritesequence_p.h68
-rw-r--r--src/quick/items/qquickspritesequence_p_p.h90
-rw-r--r--src/quick/items/qquickstateoperations.cpp26
-rw-r--r--src/quick/items/qquicktext.cpp35
-rw-r--r--src/quick/items/qquicktextcontrol.cpp33
-rw-r--r--src/quick/items/qquicktextcontrol_p.h3
-rw-r--r--src/quick/items/qquicktextedit.cpp68
-rw-r--r--src/quick/items/qquicktextedit_p.h5
-rw-r--r--src/quick/items/qquicktextinput.cpp94
-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.cpp20
-rw-r--r--src/quick/items/qquickview_p.h2
-rw-r--r--src/quick/items/qquickwindow.cpp809
-rw-r--r--src/quick/items/qquickwindow.h21
-rw-r--r--src/quick/items/qquickwindow_p.h31
-rw-r--r--src/quick/qtquick2.cpp15
-rw-r--r--src/quick/quick.pro11
-rw-r--r--src/quick/scenegraph/adaptations/adaptations.pri1
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer.cpp330
-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.cpp220
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarecontext_p.h112
-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.cpp414
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode_p.h153
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater.cpp307
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater_p.h141
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer.cpp156
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer_p.h84
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarerenderlistbuilder.cpp174
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarerenderlistbuilder_p.h96
-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.h92
-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.pri43
-rw-r--r--src/quick/scenegraph/coreapi/qsgabstractrenderer.h4
-rw-r--r--src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp83
-rw-r--r--src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h13
-rw-r--r--src/quick/scenegraph/coreapi/qsggeometry.cpp141
-rw-r--r--src/quick/scenegraph/coreapi/qsggeometry.h78
-rw-r--r--src/quick/scenegraph/coreapi/qsgmaterial.cpp28
-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.cpp22
-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.cpp250
-rw-r--r--src/quick/scenegraph/coreapi/qsgrendernode.h95
-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.cpp58
-rw-r--r--src/quick/scenegraph/qsgadaptationlayer_p.h191
-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.h93
-rw-r--r--src/quick/scenegraph/qsgcontextplugin.cpp159
-rw-r--r--src/quick/scenegraph/qsgcontextplugin_p.h9
-rw-r--r--src/quick/scenegraph/qsgdefaultcontext.cpp286
-rw-r--r--src/quick/scenegraph/qsgdefaultcontext_p.h99
-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.cpp303
-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.h87
-rw-r--r--src/quick/scenegraph/qsgrenderloop.cpp57
-rw-r--r--src/quick/scenegraph/qsgrenderloop_p.h6
-rw-r--r--src/quick/scenegraph/qsgthreadedrenderloop.cpp11
-rw-r--r--src/quick/scenegraph/qsgwindowsrenderloop.cpp23
-rw-r--r--src/quick/scenegraph/qsgwindowsrenderloop_p.h5
-rw-r--r--src/quick/scenegraph/scenegraph.pri270
-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.cpp6
-rw-r--r--src/quick/util/qquickanimator.cpp3
-rw-r--r--src/quick/util/qquickanimator_p.h6
-rw-r--r--src/quick/util/qquickanimatorjob.cpp32
-rw-r--r--src/quick/util/qquickanimatorjob_p.h7
-rw-r--r--src/quick/util/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.cpp37
-rw-r--r--src/quick/util/qquickglobal.cpp5
-rw-r--r--src/quick/util/qquickpath_p.h23
-rw-r--r--src/quick/util/qquickpixmapcache.cpp59
-rw-r--r--src/quick/util/qquickprofiler.cpp12
-rw-r--r--src/quick/util/qquickprofiler_p.h106
-rw-r--r--src/quick/util/qquickpropertychanges.cpp49
-rw-r--r--src/quick/util/qquickpropertychanges_p.h2
-rw-r--r--src/quick/util/qquicksmoothedanimation.cpp20
-rw-r--r--src/quick/util/qquickspringanimation.cpp7
-rw-r--r--src/quick/util/qquickstategroup.cpp3
-rw-r--r--src/quick/util/qquicktransitionmanager.cpp6
-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.pri7
-rw-r--r--src/quickwidgets/qquickwidget.cpp392
-rw-r--r--src/quickwidgets/qquickwidget.h39
-rw-r--r--src/quickwidgets/qquickwidget_p.h13
-rw-r--r--src/quickwidgets/quickwidgets.pro1
-rw-r--r--src/src.pro13
-rw-r--r--tests/auto/auto.pro8
-rw-r--r--tests/auto/qml/animation/qparallelanimationgroupjob/tst_qparallelanimationgroupjob.cpp4
-rw-r--r--tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs/tst_qqmldebugjs.cpp5
-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.cpp150
-rw-r--r--tests/auto/qml/qml.pro4
-rw-r--r--tests/auto/qml/qmldiskcache/qmldiskcache.pro7
-rw-r--r--tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp326
-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/qqmlecmascript/data/NullObjectInitializerBase.qml5
-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.cpp64
-rw-r--r--tests/auto/qml/qqmlengine/data/qtqmlModule.4.qml2
-rw-r--r--tests/auto/qml/qqmlengine/tst_qqmlengine.cpp9
-rw-r--r--tests/auto/qml/qqmlenginecleanup/data/types.qml3
-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/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.cpp17
-rw-r--r--tests/auto/qml/qqmllanguage/testtypes.h63
-rw-r--r--tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp110
-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.cpp26
-rw-r--r--tests/auto/qml/qqmlpropertycache/tst_qqmlpropertycache.cpp199
-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/later.qml124
-rw-r--r--tests/auto/qml/qqmlqt/tst_qqmlqt.cpp159
-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/qv4mm/qv4mm.pro8
-rw-r--r--tests/auto/qml/qv4mm/tst_qv4mm.cpp58
-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/tst_qquickflickable.cpp2
-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/tst_qquickitem.cpp267
-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/tst_qquickloader.cpp6
-rw-r--r--tests/auto/quick/qquickmousearea/data/qtbug54019.qml21
-rw-r--r--tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp257
-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.cpp65
-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.cpp124
-rw-r--r--tests/auto/quick/quick.pro20
-rw-r--r--tests/auto/quick/rendernode/tst_rendernode.cpp33
-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/touchmouse.pro2
-rw-r--r--tests/auto/quick/touchmouse/tst_touchmouse.cpp23
-rw-r--r--tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp23
-rw-r--r--tests/benchmarks/benchmarks.pro2
-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--tools/qml/qml.pro2
-rw-r--r--tools/qmleasing/mainwindow.cpp1
-rw-r--r--tools/qmljs/qmljs.cpp8
-rw-r--r--tools/qmlprofiler/commandlistener.h2
-rw-r--r--tools/qmlprofiler/main.cpp6
-rw-r--r--tools/qmlprofiler/qmlprofilerapplication.cpp28
-rw-r--r--tools/qmlprofiler/qmlprofilerapplication.h11
-rw-r--r--tools/qmlprofiler/qmlprofilerdata.h11
-rw-r--r--tools/qmlscene/main.cpp8
-rw-r--r--tools/qmlscene/qmlscene.pro3
767 files changed, 50444 insertions, 13834 deletions
diff --git a/.gitignore b/.gitignore
index e949cddb22..f33da3c8b3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -282,3 +282,5 @@ src/qml/RegExpJitTables.h
src/qml/udis86_itab.c
src/qml/udis86_itab.h
+# Generated HLSL bytecode headers
+*.hlslh
diff --git a/.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/examples/qml/qml.pro b/examples/qml/qml.pro
index eb4c98e5c4..4ed935f92f 100644
--- a/examples/qml/qml.pro
+++ b/examples/qml/qml.pro
@@ -1,9 +1,13 @@
TEMPLATE = subdirs
-qtHaveModule(quick): SUBDIRS += \
- networkaccessmanagerfactory \
- qmlextensionplugins \
- xmlhttprequest
+qtHaveModule(quick) {
+ SUBDIRS += \
+ qmlextensionplugins \
+ xmlhttprequest
+
+ !no_network: SUBDIRS += \
+ networkaccessmanagerfactory
+}
SUBDIRS += \
referenceexamples \
diff --git a/examples/quick/quick.pro b/examples/quick/quick.pro
index 63730e53da..b164bf4f5b 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
+contains(QT_CONFIG, 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 65258d958e..2f73447a5d 100644
--- a/examples/quick/quickwidgets/quickwidget/main.cpp
+++ b/examples/quick/quickwidgets/quickwidget/main.cpp
@@ -41,6 +41,7 @@
#include <QQuickWidget>
#include <QQmlError>
#include <QtWidgets>
+#include "fbitem.h"
class MainWindow : public QMainWindow {
Q_OBJECT
@@ -52,6 +53,7 @@ private slots:
void sceneGraphError(QQuickWindow::SceneGraphError error, const QString &message);
void grabToFile();
void renderToFile();
+ void createQuickWidgetsInTabs(QMdiArea *mdiArea);
private:
QQuickWidget *m_quickWidget;
@@ -74,7 +76,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");
@@ -86,14 +88,42 @@ MainWindow::MainWindow()
m_quickWidget->setResizeMode(QQuickWidget::SizeRootObjectToView );
m_quickWidget->setSource(source);
- centralWidget ->addSubWindow(m_quickWidget);
+ centralWidget->addSubWindow(m_quickWidget);
setCentralWidget(centralWidget);
QMenu *fileMenu = menuBar()->addMenu(tr("&File"));
- fileMenu->addAction(tr("Grab to imFage"), this, &MainWindow::grabToFile);
+ fileMenu->addAction(tr("Grab to image"), this, &MainWindow::grabToFile);
fileMenu->addAction(tr("Render to pixmap"), this, &MainWindow::renderToFile);
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)
@@ -139,6 +169,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..2465f4cbc7
--- /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:
+#ifdef HAS_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..f475ec838b
--- /dev/null
+++ b/examples/quick/scenegraph/rendernode/d3d12renderer.cpp
@@ -0,0 +1,273 @@
+/****************************************************************************
+**
+** 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>
+
+#ifdef HAS_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().
+
+#endif // HAS_D3D12
diff --git a/examples/quick/scenegraph/rendernode/d3d12renderer.h b/examples/quick/scenegraph/rendernode/d3d12renderer.h
new file mode 100644
index 0000000000..a81db0f398
--- /dev/null
+++ b/examples/quick/scenegraph/rendernode/d3d12renderer.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 D3D12RENDERER_H
+#define D3D12RENDERER_H
+
+#include <qsgrendernode.h>
+
+#ifdef HAS_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;
+
+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 // HAS_D3D12
+
+#endif
diff --git a/examples/quick/scenegraph/rendernode/main.cpp b/examples/quick/scenegraph/rendernode/main.cpp
new file mode 100644
index 0000000000..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..33c59198b8
--- /dev/null
+++ b/examples/quick/scenegraph/rendernode/openglrenderer.cpp
@@ -0,0 +1,154 @@
+/****************************************************************************
+**
+** 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;
+}
+
+#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..28d528e617
--- /dev/null
+++ b/examples/quick/scenegraph/rendernode/openglrenderer.h
@@ -0,0 +1,74 @@
+/****************************************************************************
+**
+** 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
+
+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;
+
+private:
+ void init();
+
+ QQuickItem *m_item;
+ QOpenGLShaderProgram *m_program = nullptr;
+ int m_matrixUniform;
+ int m_opacityUniform;
+ QOpenGLBuffer *m_vbo = nullptr;
+};
+
+#endif // QT_NO_OPENGL
+
+#endif
diff --git a/examples/quick/scenegraph/rendernode/rendernode.pro b/examples/quick/scenegraph/rendernode/rendernode.pro
new file mode 100644
index 0000000000..851d5927bd
--- /dev/null
+++ b/examples/quick/scenegraph/rendernode/rendernode.pro
@@ -0,0 +1,39 @@
+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
+
+config_d3d12 {
+ DEFINES += HAS_D3D12
+ HEADERS += d3d12renderer.h
+ SOURCES += d3d12renderer.cpp
+ LIBS += -ld3d12
+
+ VSPS = shader.hlsl
+ vshader.input = VSPS
+ vshader.header = vs_shader.hlslh
+ vshader.entry = VS_Simple
+ vshader.type = vs_5_0
+ pshader.input = VSPS
+ pshader.header = ps_shader.hlslh
+ pshader.entry = PS_Simple
+ pshader.type = ps_5_0
+
+ HLSL_SHADERS = vshader pshader
+ load(hlsl_bytecode_header)
+}
diff --git a/examples/quick/scenegraph/rendernode/rendernode.qrc b/examples/quick/scenegraph/rendernode/rendernode.qrc
new file mode 100644
index 0000000000..3674baccd8
--- /dev/null
+++ b/examples/quick/scenegraph/rendernode/rendernode.qrc
@@ -0,0 +1,5 @@
+<RCC>
+ <qresource prefix="/scenegraph/rendernode">
+ <file>main.qml</file>
+ </qresource>
+</RCC>
diff --git a/examples/quick/scenegraph/rendernode/shader.hlsl b/examples/quick/scenegraph/rendernode/shader.hlsl
new file mode 100644
index 0000000000..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..2f11c56f29
--- /dev/null
+++ b/examples/quick/scenegraph/rendernode/softwarerenderer.cpp
@@ -0,0 +1,91 @@
+/****************************************************************************
+**
+** 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);
+
+ 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;
+}
diff --git a/examples/quick/scenegraph/rendernode/softwarerenderer.h b/examples/quick/scenegraph/rendernode/softwarerenderer.h
new file mode 100644
index 0000000000..60036f96a1
--- /dev/null
+++ b/examples/quick/scenegraph/rendernode/softwarerenderer.h
@@ -0,0 +1,62 @@
+/****************************************************************************
+**
+** 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>
+
+class QQuickItem;
+
+class SoftwareRenderNode : public QSGRenderNode
+{
+public:
+ SoftwareRenderNode(QQuickItem *item);
+ ~SoftwareRenderNode();
+
+ void render(const RenderState *state) override;
+ void releaseResources() override;
+ StateFlags changedStates() const override;
+
+private:
+ QQuickItem *m_item;
+};
+
+#endif
diff --git a/examples/quick/scenegraph/scenegraph.pro b/examples/quick/scenegraph/scenegraph.pro
index cf50cdb903..1015d7be3d 100644
--- a/examples/quick/scenegraph/scenegraph.pro
+++ b/examples/quick/scenegraph/scenegraph.pro
@@ -1,14 +1,21 @@
TEMPLATE = subdirs
+
+contains(QT_CONFIG, opengl(es1|es2)?) {
+ SUBDIRS += \
+ graph \
+ simplematerial \
+ sgengine \
+ textureinsgnode \
+ openglunderqml \
+ textureinsgnode \
+ textureinthread \
+ twotextureproviders
+}
+
SUBDIRS += \
customgeometry \
- graph \
- openglunderqml \
- sgengine \
- simplematerial \
- textureinsgnode \
- textureinthread \
- threadedanimation \
- twotextureproviders
+ rendernode \
+ threadedanimation
EXAMPLE_FILES += \
shared
diff --git a/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/qtdeclarative.pro b/qtdeclarative.pro
index 5b94da9b69..c9df6e053b 100644
--- a/qtdeclarative.pro
+++ b/qtdeclarative.pro
@@ -1,3 +1,6 @@
+load(configure)
+qtCompileTest(d3d12)
+
CONFIG += tests_need_tools examples_need_tools
load(qt_parts)
diff --git a/src/3rdparty/masm/masm-defs.pri b/src/3rdparty/masm/masm-defs.pri
index c4c7d3ce9a..b5480babc6 100644
--- a/src/3rdparty/masm/masm-defs.pri
+++ b/src/3rdparty/masm/masm-defs.pri
@@ -38,6 +38,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/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/imports.pro b/src/imports/imports.pro
index 5332fb0ef2..9e3cdf3f42 100644
--- a/src/imports/imports.pro
+++ b/src/imports/imports.pro
@@ -13,9 +13,13 @@ qtHaveModule(quick) {
SUBDIRS += \
layouts \
qtquick2 \
- particles \
window \
testlib
+
+ contains(QT_CONFIG, opengl(es1|es2)?) {
+ SUBDIRS += \
+ particles
+ }
}
qtHaveModule(xmlpatterns) : SUBDIRS += xmllistmodel
diff --git a/src/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/qquicklayout.cpp b/src/imports/layouts/qquicklayout.cpp
index abc8f97cec..98e2aea44f 100644
--- a/src/imports/layouts/qquicklayout.cpp
+++ b/src/imports/layouts/qquicklayout.cpp
@@ -754,27 +754,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 0b4a1968d7..fdf8479133 100644
--- a/src/imports/layouts/qquicklinearlayout.cpp
+++ b/src/imports/layouts/qquicklinearlayout.cpp
@@ -333,10 +333,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;
}
@@ -465,13 +465,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 fbcb6ddea5..f59ccc551e 100644
--- a/src/imports/localstorage/plugin.cpp
+++ b/src/imports/localstorage/plugin.cpp
@@ -431,7 +431,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();
@@ -479,7 +479,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())
@@ -750,7 +750,7 @@ void QQuickLocalStorage::openDatabaseSync(QQmlV4Function *args)
ScopedCallData callData(scope, 1);
callData->thisObject = scope.engine->globalObject;
callData->args[0] = db;
- dbcreationCallback->call(callData);
+ dbcreationCallback->call(scope, callData);
}
args->setReturnValue(db.asReturnedValue());
@@ -776,7 +776,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/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/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/qtquick2/plugin.cpp b/src/imports/qtquick2/plugin.cpp
index 4fb6907077..e56027c1bb 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..c3a14254f3 100644
--- a/src/imports/qtquick2/plugins.qmltypes
+++ b/src/imports/qtquick2/plugins.qmltypes
@@ -1730,6 +1730,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 +1752,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 {
@@ -4618,6 +4628,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" }
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/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/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 046ce507d4..bc6786f9f8 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/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/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/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/plugins/plugins.pro b/src/plugins/plugins.pro
index 273407c19d..11c8641e21 100644
--- a/src/plugins/plugins.pro
+++ b/src/plugins/plugins.pro
@@ -1,2 +1,4 @@
TEMPLATE = subdirs
+
!contains(QT_CONFIG, no-qml-debug):SUBDIRS += qmltooling
+SUBDIRS += scenegraph
diff --git a/src/plugins/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/qqmlenginedebugservice.cpp b/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp
index ff4e30835d..12ed987ca0 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();
@@ -758,17 +762,20 @@ 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);
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..a4320098c0 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 << 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..eee1dd7eae 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,
@@ -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() &&
@@ -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());
+ auto location = m_functionLocations.find(props.id);
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;
+ if (trackLocations)
+ d << static_cast<qint64>(props.id);
+ if (location != m_functionLocations.end()) {
+ messages.push_back(d.squeezedData());
+ d.clear();
+ d << props.start << RangeLocation << 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 << RangeData << 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..7682e7b075 100644
--- a/src/plugins/qmltooling/qmltooling.pro
+++ b/src/plugins/qmltooling/qmltooling.pro
@@ -1,30 +1,32 @@
TEMPLATE = subdirs
-# Utilities
-SUBDIRS += \
- packetprotocol
+!no_network {
+ # Utilities
+ SUBDIRS += \
+ packetprotocol
-# Connectors
-SUBDIRS += \
- qmldbg_native \
- qmldbg_server \
+ # Connectors
+ SUBDIRS += \
+ qmldbg_native \
+ qmldbg_server \
qmldbg_local \
qmldbg_tcp
-# Services
-SUBDIRS += \
- qmldbg_debugger \
- qmldbg_profiler
+ # Services
+ SUBDIRS += \
+ qmldbg_debugger \
+ qmldbg_profiler
-qmldbg_server.depends = packetprotocol
-qmldbg_native.depends = packetprotocol
-qmldbg_debugger.depends = packetprotocol
-qmldbg_profiler.depends = packetprotocol
+ qmldbg_server.depends = packetprotocol
+ qmldbg_native.depends = packetprotocol
+ qmldbg_debugger.depends = packetprotocol
+ qmldbg_profiler.depends = packetprotocol
-qtHaveModule(quick) {
- SUBDIRS += \
- qmldbg_inspector \
- qmldbg_quickprofiler
- qmldbg_inspector.depends = packetprotocol
- qmldbg_quickprofiler.depends = packetprotocol
+ qtHaveModule(quick) {
+ SUBDIRS += \
+ qmldbg_inspector \
+ qmldbg_quickprofiler
+ qmldbg_inspector.depends = packetprotocol
+ qmldbg_quickprofiler.depends = packetprotocol
+ }
}
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..fc3ea4e22e
--- /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::TypeUnsignedShort);
+ geometry->allocate(glyphIndexes.size() * 4, glyphIndexes.size() * 6);
+ QVector4D *vp = reinterpret_cast<QVector4D *>(geometry->vertexDataAsTexturedPoint2D());
+ Q_ASSERT(geometry->sizeOfVertex() == sizeof(QVector4D));
+ ushort *ip = geometry->indexDataAsUShort();
+
+ QPointF position(p.x(), p.y() - m_font.ascent());
+ bool supportsSubPixelPositions = fontD->fontEngine->supportsSubPixelPositions();
+ for (int i = 0; i < glyphIndexes.size(); ++i) {
+ QFixed subPixelPosition;
+ if (supportsSubPixelPositions)
+ subPixelPosition = fontD->fontEngine->subPixelPositionForX(QFixed::fromReal(glyphPositions.at(i).x()));
+
+ QTextureGlyphCache::GlyphAndSubPixelPosition glyph(glyphIndexes.at(i), subPixelPosition);
+ const QTextureGlyphCache::Coord &c = cache->coords.value(glyph);
+
+ QPointF glyphPosition = glyphPositions.at(i) + position;
+ float x = (qFloor(glyphPosition.x() * glyphCacheScaleX) * glyphCacheInverseScaleX)
+ + (c.baseLineX * glyphCacheInverseScaleX) - margin;
+ float y = (qRound(glyphPosition.y() * glyphCacheScaleY) * glyphCacheInverseScaleY)
+ - (c.baseLineY * glyphCacheInverseScaleY) - margin;
+
+ float w = c.w * glyphCacheInverseScaleX;
+ float h = c.h * glyphCacheInverseScaleY;
+
+ *boundingRect |= QRectF(x + margin, y + margin, w, h);
+
+ float cx1 = x - margins.left();
+ float cx2 = x + w + margins.right();
+ float cy1 = y - margins.top();
+ float cy2 = y + h + margins.bottom();
+
+ float tx1 = c.x - margins.left();
+ float tx2 = c.x + c.w + margins.right();
+ float ty1 = c.y - margins.top();
+ float ty2 = c.y + c.h + margins.bottom();
+
+ if (baseLine->isNull())
+ *baseLine = glyphPosition;
+
+ vp[4 * i + 0] = QVector4D(cx1, cy1, tx1, ty1);
+ vp[4 * i + 1] = QVector4D(cx2, cy1, tx2, ty1);
+ vp[4 * i + 2] = QVector4D(cx1, cy2, tx1, ty2);
+ vp[4 * i + 3] = QVector4D(cx2, cy2, tx2, ty2);
+
+ int o = i * 4;
+ ip[6 * i + 0] = o + 0;
+ ip[6 * i + 1] = o + 2;
+ ip[6 * i + 2] = o + 3;
+ ip[6 * i + 3] = o + 3;
+ ip[6 * i + 4] = o + 1;
+ ip[6 * i + 5] = o + 0;
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12builtinmaterials_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12builtinmaterials_p.h
new file mode 100644
index 0000000000..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..34ab4d11b0
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12engine.cpp
@@ -0,0 +1,3233 @@
+/****************************************************************************
+**
+** 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, "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, "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, "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, "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, "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, "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, "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)
+{
+ d->queueTextureUpload(id, QVector<QImage>() << image, QVector<QPoint>() << dstPos);
+}
+
+void QSGD3D12Engine::queueTextureUpload(uint id, const QVector<QImage> &images, const QVector<QPoint> &dstPos)
+{
+ d->queueTextureUpload(id, images, dstPos);
+}
+
+void QSGD3D12Engine::releaseTexture(uint id)
+{
+ d->releaseTexture(id);
+}
+
+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::TypeUnsignedByte:
+ format = formatMap_ub[tupleSize];
+ if (size)
+ *size = tupleSize;
+ break;
+ case QSGGeometry::TypeFloat:
+ format = formatMap_f[tupleSize];
+ if (size)
+ *size = sizeof(float) * tupleSize;
+ break;
+
+ case QSGGeometry::TypeUnsignedShort:
+ format = FmtUnsignedShort;
+ if (size)
+ *size = sizeof(ushort) * tupleSize;
+ break;
+ case QSGGeometry::TypeUnsignedInt:
+ format = FmtUnsignedInt;
+ if (size)
+ *size = sizeof(uint) * tupleSize;
+ break;
+
+ case QSGGeometry::TypeByte:
+ case QSGGeometry::TypeInt:
+ case QSGGeometry::TypeShort:
+ qWarning("no mapping for GL type 0x%x", sgtype);
+ break;
+
+ default:
+ qWarning("unknown GL type 0x%x", sgtype);
+ break;
+ }
+
+ return format;
+}
+
+int QSGD3D12Engine::mipMapLevels(const QSize &size)
+{
+ return ceil(log2(qMax(size.width(), size.height()))) + 1;
+}
+
+inline static bool isPowerOfTwo(int x)
+{
+ // Assumption: x >= 1
+ return x == (x & -x);
+}
+
+QSize QSGD3D12Engine::mipMapAdjustedSourceSize(const QSize &size)
+{
+ if (size.isEmpty())
+ return size;
+
+ QSize adjustedSize = size;
+
+ // ### for now only power-of-two sizes are mipmap-capable
+ if (!isPowerOfTwo(size.width()))
+ adjustedSize.setWidth(qNextPowerOfTwo(size.width()));
+ if (!isPowerOfTwo(size.height()))
+ adjustedSize.setHeight(qNextPowerOfTwo(size.height()));
+
+ return adjustedSize;
+}
+
+void QSGD3D12EnginePrivate::releaseResources()
+{
+ if (!initialized)
+ return;
+
+ mipmapper.releaseResources();
+ devLossTest.releaseResources();
+
+ frameCommandList = nullptr;
+ copyCommandList = nullptr;
+
+ copyCommandAllocator = nullptr;
+ for (int i = 0; i < frameInFlightCount; ++i) {
+ frameCommandAllocator[i] = nullptr;
+ pframeData[i].gpuCbvSrvUavHeap = nullptr;
+ delete frameFence[i];
+ }
+
+ defaultDS = nullptr;
+ for (int i = 0; i < swapChainBufferCount; ++i) {
+ backBufferRT[i] = nullptr;
+ defaultRT[i] = nullptr;
+ }
+
+ psoCache.clear();
+ rootSigCache.clear();
+ buffers.clear();
+ textures.clear();
+ renderTargets.clear();
+
+ cpuDescHeapManager.releaseResources();
+
+ commandQueue = nullptr;
+ copyCommandQueue = nullptr;
+
+ dcompTarget = nullptr;
+ dcompVisual = nullptr;
+ dcompDevice = nullptr;
+
+ swapChain = nullptr;
+
+ delete presentFence;
+ textureUploadFence = nullptr;
+
+ deviceManager()->unref();
+
+ initialized = false;
+
+ // 'window' must be kept, may just be a device loss
+}
+
+void QSGD3D12EnginePrivate::initialize(WId w, const QSize &size, float dpr, int surfaceFormatSamples, 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()).setEnabled(QtDebugMsg, true);
+
+ qCDebug(QSG_LOG_INFO, "d3d12 engine init. swap chain buffer count %d, max frames prepared without blocking %d",
+ swapChainBufferCount, frameInFlightCount);
+ if (waitableSwapChainMaxLatency)
+ qCDebug(QSG_LOG_INFO, "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, "Enabling debug layer");
+ ComPtr<ID3D12Debug> debugController;
+ if (SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(&debugController))))
+ debugController->EnableDebugLayer();
+ }
+
+ 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,
+ QImage::Format *imageFormat, int *bytesPerPixel)
+{
+ DXGI_FORMAT f = DXGI_FORMAT_R8G8B8A8_UNORM;
+ QImage::Format convFormat = format;
+ int bpp = 4;
+
+ if (!mipmap) {
+ switch (format) {
+ case QImage::Format_Grayscale8:
+ case QImage::Format_Indexed8:
+ case QImage::Format_Alpha8:
+ f = DXGI_FORMAT_R8_UNORM;
+ bpp = 1;
+ break;
+ case QImage::Format_RGB32:
+ f = DXGI_FORMAT_B8G8R8A8_UNORM;
+ break;
+ case QImage::Format_ARGB32:
+ f = DXGI_FORMAT_B8G8R8A8_UNORM;
+ convFormat = wantsAlpha ? QImage::Format_ARGB32_Premultiplied : QImage::Format_RGB32;
+ break;
+ case QImage::Format_ARGB32_Premultiplied:
+ f = DXGI_FORMAT_B8G8R8A8_UNORM;
+ convFormat = wantsAlpha ? format : QImage::Format_RGB32;
+ break;
+ default:
+ convFormat = wantsAlpha ? QImage::Format_RGBA8888_Premultiplied : QImage::Format_RGBX8888;
+ break;
+ }
+ } else {
+ // Mipmap generation needs unordered access and BGRA is not an option for that. Stick to RGBA.
+ convFormat = wantsAlpha ? QImage::Format_RGBA8888_Premultiplied : QImage::Format_RGBX8888;
+ }
+
+ if (imageFormat)
+ *imageFormat = convFormat;
+
+ if (bytesPerPixel)
+ *bytesPerPixel = bpp;
+
+ return f;
+}
+
+static inline QImage::Format imageFormatForTexture(DXGI_FORMAT format)
+{
+ QImage::Format f = QImage::Format_Invalid;
+
+ switch (format) {
+ case DXGI_FORMAT_R8G8B8A8_UNORM:
+ f = QImage::Format_RGBA8888_Premultiplied;
+ break;
+ case DXGI_FORMAT_B8G8R8A8_UNORM:
+ f = QImage::Format_ARGB32_Premultiplied;
+ break;
+ case DXGI_FORMAT_R8_UNORM:
+ f = QImage::Format_Grayscale8;
+ break;
+ default:
+ break;
+ }
+
+ return f;
+}
+
+void QSGD3D12EnginePrivate::createTexture(uint id, const QSize &size, QImage::Format format,
+ QSGD3D12Engine::TextureCreateFlags createFlags)
+{
+ ensureDevice();
+
+ Q_ASSERT(id);
+ const int idx = id - 1;
+ Q_ASSERT(idx < textures.count() && textures[idx].entryInUse());
+ Texture &t(textures[idx]);
+
+ syncEntryFlags(&t, Texture::Alpha, createFlags & QSGD3D12Engine::TextureWithAlpha);
+ syncEntryFlags(&t, Texture::MipMap, createFlags & QSGD3D12Engine::TextureWithMipMaps);
+
+ const QSize adjustedSize = !t.mipmap() ? size : QSGD3D12Engine::mipMapAdjustedSourceSize(size);
+
+ D3D12_HEAP_PROPERTIES defaultHeapProp = {};
+ defaultHeapProp.Type = D3D12_HEAP_TYPE_DEFAULT;
+
+ D3D12_RESOURCE_DESC textureDesc = {};
+ textureDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
+ textureDesc.Width = adjustedSize.width();
+ textureDesc.Height = adjustedSize.height();
+ textureDesc.DepthOrArraySize = 1;
+ textureDesc.MipLevels = !t.mipmap() ? 1 : QSGD3D12Engine::mipMapLevels(adjustedSize);
+ textureDesc.Format = textureFormat(format, t.alpha(), t.mipmap(), nullptr, nullptr);
+ textureDesc.SampleDesc.Count = 1;
+ textureDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
+ if (t.mipmap())
+ textureDesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
+
+ HRESULT hr = device->CreateCommittedResource(&defaultHeapProp, D3D12_HEAP_FLAG_NONE, &textureDesc,
+ D3D12_RESOURCE_STATE_COMMON, nullptr, IID_PPV_ARGS(&t.texture));
+ if (FAILED(hr)) {
+ qWarning("Failed to create texture resource: 0x%x", hr);
+ return;
+ }
+
+ t.srv = cpuDescHeapManager.allocate(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
+
+ D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
+ srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
+ srvDesc.Format = textureDesc.Format;
+ srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
+ srvDesc.Texture2D.MipLevels = textureDesc.MipLevels;
+
+ device->CreateShaderResourceView(t.texture.Get(), &srvDesc, t.srv);
+
+ if (t.mipmap()) {
+ // Mipmap generation will need an UAV for each level that needs to be generated.
+ t.mipUAVs.clear();
+ for (int level = 1; level < textureDesc.MipLevels; ++level) {
+ D3D12_UNORDERED_ACCESS_VIEW_DESC uavDesc = {};
+ uavDesc.Format = textureDesc.Format;
+ uavDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2D;
+ uavDesc.Texture2D.MipSlice = level;
+ D3D12_CPU_DESCRIPTOR_HANDLE h = cpuDescHeapManager.allocate(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
+ device->CreateUnorderedAccessView(t.texture.Get(), nullptr, &uavDesc, h);
+ t.mipUAVs.append(h);
+ }
+ }
+
+ if (Q_UNLIKELY(debug_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)
+{
+ 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(), 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(), &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..46cd73e63a
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12engine_p.h
@@ -0,0 +1,387 @@
+/****************************************************************************
+**
+** 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 = 0x1,
+ TextureWithMipMaps = 0x2
+ };
+ Q_DECLARE_FLAGS(TextureCreateFlags, TextureCreateFlag)
+
+ uint genTexture();
+ void createTexture(uint id, const QSize &size, QImage::Format format, TextureCreateFlags flags);
+ void queueTextureResize(uint id, const QSize &size);
+ void queueTextureUpload(uint id, const QImage &image, const QPoint &dstPos = QPoint());
+ void queueTextureUpload(uint id, const QVector<QImage> &images, const QVector<QPoint> &dstPos);
+ void releaseTexture(uint id);
+ 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)
+
+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..2b1e3dc7b7
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12engine_p_p.h
@@ -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$
+**
+****************************************************************************/
+
+#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);
+ 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;
+
+ ComPtr<IDCompositionDevice> dcompDevice;
+ ComPtr<IDCompositionTarget> dcompTarget;
+ ComPtr<IDCompositionVisual> dcompVisual;
+};
+
+inline uint qHash(const QSGD3D12EnginePrivate::PersistentFrameData::PendingRelease &pr, uint seed = 0)
+{
+ Q_UNUSED(seed);
+ return pr.id + pr.type;
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12glyphcache.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12glyphcache.cpp
new file mode 100644
index 0000000000..45ef202e83
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12glyphcache.cpp
@@ -0,0 +1,184 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgd3d12glyphcache_p.h"
+#include "qsgd3d12engine_p.h"
+
+QT_BEGIN_NAMESPACE
+
+// NOTE: Avoid categorized logging. It is slow.
+
+#define DECLARE_DEBUG_VAR(variable) \
+ static bool debug_ ## variable() \
+ { static bool value = qgetenv("QSG_RENDERER_DEBUG").contains(QT_STRINGIFY(variable)); return value; }
+
+DECLARE_DEBUG_VAR(render)
+
+QSGD3D12GlyphCache::QSGD3D12GlyphCache(QSGD3D12Engine *engine, QFontEngine::GlyphFormat format, const QTransform &matrix)
+ : QTextureGlyphCache(format, matrix),
+ m_engine(engine)
+{
+}
+
+QSGD3D12GlyphCache::~QSGD3D12GlyphCache()
+{
+ if (m_id)
+ m_engine->releaseTexture(m_id);
+}
+
+void QSGD3D12GlyphCache::createTextureData(int width, int height)
+{
+ width = qMax(128, width);
+ height = qMax(32, height);
+
+ m_id = m_engine->genTexture();
+ Q_ASSERT(m_id);
+
+ if (Q_UNLIKELY(debug_render()))
+ qDebug("new glyph cache texture %u of size %dx%d, fontengine format %d", m_id, width, height, m_format);
+
+ m_size = QSize(width, height);
+
+ const QImage::Format imageFormat =
+ m_format == QFontEngine::Format_A8 ? QImage::Format_Alpha8 : QImage::Format_ARGB32_Premultiplied;
+ m_engine->createTexture(m_id, m_size, imageFormat, QSGD3D12Engine::TextureWithAlpha);
+}
+
+void QSGD3D12GlyphCache::resizeTextureData(int width, int height)
+{
+ width = qMax(128, width);
+ height = qMax(32, height);
+
+ if (m_size.width() >= width && m_size.height() >= height)
+ return;
+
+ if (Q_UNLIKELY(debug_render()))
+ qDebug("glyph cache texture %u resize to %dx%d", m_id, width, height);
+
+ m_size = QSize(width, height);
+
+ m_engine->queueTextureResize(m_id, m_size);
+}
+
+void QSGD3D12GlyphCache::beginFillTexture()
+{
+ Q_ASSERT(m_glyphImages.isEmpty() && m_glyphPos.isEmpty());
+}
+
+void QSGD3D12GlyphCache::fillTexture(const Coord &c, glyph_t glyph, QFixed subPixelPosition)
+{
+ QImage mask = textureMapForGlyph(glyph, subPixelPosition);
+ const int maskWidth = mask.width();
+ const int maskHeight = mask.height();
+
+ if (mask.format() == QImage::Format_Mono) {
+ mask = mask.convertToFormat(QImage::Format_Indexed8);
+ for (int y = 0; y < maskHeight; ++y) {
+ uchar *src = mask.scanLine(y);
+ for (int x = 0; x < maskWidth; ++x)
+ src[x] = -src[x]; // convert 0 and 1 into 0 and 255
+ }
+ } else if (mask.depth() == 32) {
+ if (mask.format() == QImage::Format_RGB32) {
+ // We need to make the alpha component equal to the average of the RGB values.
+ // This is needed when drawing sub-pixel antialiased text on translucent targets.
+ for (int y = 0; y < maskHeight; ++y) {
+ QRgb *src = reinterpret_cast<QRgb *>(mask.scanLine(y));
+ for (int x = 0; x < maskWidth; ++x) {
+ const int r = qRed(src[x]);
+ const int g = qGreen(src[x]);
+ const int b = qBlue(src[x]);
+ int avg;
+ if (mask.format() == QImage::Format_RGB32)
+ avg = (r + g + b + 1) / 3; // "+1" for rounding.
+ else // Format_ARGB32_Premultiplied
+ avg = qAlpha(src[x]);
+ src[x] = qRgba(r, g, b, avg);
+ }
+ }
+ }
+ }
+
+ m_glyphImages.append(mask);
+ m_glyphPos.append(QPoint(c.x, c.y));
+}
+
+void QSGD3D12GlyphCache::endFillTexture()
+{
+ if (m_glyphImages.isEmpty())
+ return;
+
+ Q_ASSERT(m_id);
+
+ m_engine->queueTextureUpload(m_id, m_glyphImages, m_glyphPos);
+
+ // Nothing else left to do, it is up to the text material to call
+ // useTexture() which will then add the texture dependency to the frame.
+
+ m_glyphImages.clear();
+ m_glyphPos.clear();
+}
+
+int QSGD3D12GlyphCache::glyphPadding() const
+{
+ return 1;
+}
+
+int QSGD3D12GlyphCache::maxTextureWidth() const
+{
+ return 16384;
+}
+
+int QSGD3D12GlyphCache::maxTextureHeight() const
+{
+ return 16384;
+}
+
+void QSGD3D12GlyphCache::useTexture()
+{
+ if (m_id)
+ m_engine->useTexture(m_id);
+}
+
+QSize QSGD3D12GlyphCache::currentSize() const
+{
+ return m_size;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12glyphcache_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12glyphcache_p.h
new file mode 100644
index 0000000000..88d3d36f33
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12glyphcache_p.h
@@ -0,0 +1,88 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGD3D12GLYPHCACHE_P_H
+#define QSGD3D12GLYPHCACHE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtGui/private/qtextureglyphcache_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QSGD3D12Engine;
+
+class QSGD3D12GlyphCache : public QTextureGlyphCache
+{
+public:
+ QSGD3D12GlyphCache(QSGD3D12Engine *engine, QFontEngine::GlyphFormat format, const QTransform &matrix);
+ ~QSGD3D12GlyphCache();
+
+ void createTextureData(int width, int height) override;
+ void resizeTextureData(int width, int height) override;
+ void beginFillTexture() override;
+ void fillTexture(const Coord &c, glyph_t glyph, QFixed subPixelPosition) override;
+ void endFillTexture() override;
+ int glyphPadding() const override;
+ int maxTextureWidth() const override;
+ int maxTextureHeight() const override;
+
+ void useTexture();
+ QSize currentSize() const;
+
+private:
+ QSGD3D12Engine *m_engine;
+ uint m_id = 0;
+ QVector<QImage> m_glyphImages;
+ QVector<QPoint> m_glyphPos;
+ QSize m_size;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGD3D12GLYPHCACHE_P_H
diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12glyphnode.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12glyphnode.cpp
new file mode 100644
index 0000000000..e559739018
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12glyphnode.cpp
@@ -0,0 +1,90 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgd3d12glyphnode_p.h"
+#include "qsgd3d12builtinmaterials_p.h"
+
+QT_BEGIN_NAMESPACE
+
+void QSGD3D12GlyphNode::setMaterialColor(const QColor &color)
+{
+ static_cast<QSGD3D12TextMaterial *>(m_material)->setColor(color);
+}
+
+void QSGD3D12GlyphNode::update()
+{
+ QRawFont font = m_glyphs.rawFont();
+ QMargins margins(0, 0, 0, 0);
+
+ if (m_style == QQuickText::Normal) {
+ // QSGBasicGlyphNode dtor will delete
+ m_material = new QSGD3D12TextMaterial(QSGD3D12TextMaterial::Normal, m_rc, font);
+ } else if (m_style == QQuickText::Outline) {
+ QSGD3D12TextMaterial *material = new QSGD3D12TextMaterial(QSGD3D12TextMaterial::Outlined,
+ m_rc, font, QFontEngine::Format_A8);
+ material->setStyleColor(m_styleColor);
+ m_material = material;
+ margins = QMargins(1, 1, 1, 1);
+ } else {
+ QSGD3D12TextMaterial *material = new QSGD3D12TextMaterial(QSGD3D12TextMaterial::Styled,
+ m_rc, font, QFontEngine::Format_A8);
+ if (m_style == QQuickText::Sunken) {
+ material->setStyleShift(QVector2D(0, -1));
+ margins.setTop(1);
+ } else if (m_style == QQuickText::Raised) {
+ material->setStyleShift(QVector2D(0, 1));
+ margins.setBottom(1);
+ }
+ material->setStyleColor(m_styleColor);
+ m_material = material;
+ }
+
+ QSGD3D12TextMaterial *textMaterial = static_cast<QSGD3D12TextMaterial *>(m_material);
+ textMaterial->setColor(m_color);
+
+ QRectF boundingRect;
+ textMaterial->populate(m_position, m_glyphs.glyphIndexes(), m_glyphs.positions(), geometry(),
+ &boundingRect, &m_baseLine, margins);
+ setBoundingRect(boundingRect);
+
+ setMaterial(m_material);
+ markDirty(DirtyGeometry);
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12glyphnode_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12glyphnode_p.h
new file mode 100644
index 0000000000..d04a8e8777
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12glyphnode_p.h
@@ -0,0 +1,74 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGD3D12GLYPHNODE_P_H
+#define QSGD3D12GLYPHNODE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qsgbasicglyphnode_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QSGD3D12RenderContext;
+
+class QSGD3D12GlyphNode : public QSGBasicGlyphNode
+{
+public:
+ QSGD3D12GlyphNode(QSGD3D12RenderContext *rc) : m_rc(rc) { }
+
+ void setMaterialColor(const QColor &color) override;
+ void update() override;
+
+private:
+ QSGD3D12RenderContext *m_rc;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGD3D12GLYPHNODE_P_H
diff --git a/src/plugins/scenegraph/d3d12/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..ce633ae996
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12renderer.cpp
@@ -0,0 +1,784 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgd3d12renderer_p.h"
+#include "qsgd3d12rendercontext_p.h"
+#include <private/qsgnodeupdater_p.h>
+#include <private/qsgrendernode_p.h>
+
+#include "vs_stencilclip.hlslh"
+#include "ps_stencilclip.hlslh"
+
+//#define I_LIKE_STENCIL
+
+QT_BEGIN_NAMESPACE
+
+#define QSGNODE_TRAVERSE(NODE) for (QSGNode *child = NODE->firstChild(); child; child = child->nextSibling())
+
+// NOTE: Avoid categorized logging. It is slow.
+
+#define DECLARE_DEBUG_VAR(variable) \
+ static bool debug_ ## variable() \
+ { static bool value = qgetenv("QSG_RENDERER_DEBUG").contains(QT_STRINGIFY(variable)); return value; }
+
+DECLARE_DEBUG_VAR(build)
+DECLARE_DEBUG_VAR(change)
+DECLARE_DEBUG_VAR(render)
+
+class DummyUpdater : public QSGNodeUpdater
+{
+public:
+ void updateState(QSGNode *) { };
+};
+
+QSGD3D12Renderer::QSGD3D12Renderer(QSGRenderContext *context)
+ : QSGRenderer(context),
+ m_renderList(16),
+ m_vboData(1024),
+ m_iboData(256),
+ m_cboData(4096)
+{
+ setNodeUpdater(new DummyUpdater);
+}
+
+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 };
+ Q_ASSERT(attrs[i].semantic >= 1 && attrs[i].semantic < _countof(semanticNames));
+ const int tupleSize = attrs[i].tupleSize;
+ ie.semanticName = semanticNames[attrs[i].semantic];
+ ie.semanticIndex = semanticIndices[attrs[i].semantic];
+ ie.offset = offset;
+ int bytesPerTuple = 0;
+ ie.format = QSGD3D12Engine::toDXGIFormat(QSGGeometry::Type(attrs[i].type), tupleSize, &bytesPerTuple);
+ if (ie.format == FmtUnknown)
+ qFatal("QSGD3D12Renderer: unsupported tuple size for attribute type 0x%x", attrs[i].type);
+ offset += bytesPerTuple;
+ // There is one buffer with interleaved data so the slot is always 0.
+ ie.slot = 0;
+ }
+}
+
+void QSGD3D12Renderer::queueDrawCall(const QSGGeometry *g, const QSGD3D12Renderer::Element &e)
+{
+ QSGD3D12Engine::DrawParams dp;
+ dp.mode = QSGGeometry::DrawingMode(g->drawingMode());
+ dp.vertexBuf = m_vertexBuf;
+ dp.constantBuf = m_constantBuf;
+ dp.vboOffset = e.vboOffset;
+ dp.vboSize = g->vertexCount() * g->sizeOfVertex();
+ dp.vboStride = g->sizeOfVertex();
+ dp.cboOffset = e.cboOffset;
+
+ if (e.iboOffset >= 0) {
+ const QSGGeometry::Type indexType = QSGGeometry::Type(g->indexType());
+ const QSGD3D12Format indexFormat = QSGD3D12Engine::toDXGIFormat(indexType);
+ if (indexFormat == FmtUnknown)
+ qFatal("QSGD3D12Renderer: unsupported index type 0x%x", indexType);
+ dp.count = g->indexCount();
+ dp.indexBuf = m_indexBuf;
+ dp.startIndexIndex = e.iboOffset / e.iboStride;
+ dp.indexFormat = indexFormat;
+ } else {
+ dp.count = g->vertexCount();
+ }
+
+ m_engine->queueDraw(dp);
+}
+
+void QSGD3D12Renderer::setupClipping(const QSGClipNode *clip, int elementIndex)
+{
+ const QRect devRect = deviceRect();
+ QRect scissorRect;
+ int clipTypes = 0;
+ quint32 stencilValue = 0;
+
+ while (clip) {
+ QMatrix4x4 m = projectionMatrix();
+ if (clip->matrix())
+ m *= *clip->matrix();
+
+#ifndef I_LIKE_STENCIL
+ const bool isRectangleWithNoPerspective = clip->isRectangular()
+ && qFuzzyIsNull(m(3, 0)) && qFuzzyIsNull(m(3, 1));
+ const bool noRotate = qFuzzyIsNull(m(0, 1)) && qFuzzyIsNull(m(1, 0));
+ const bool isRotate90 = qFuzzyIsNull(m(0, 0)) && qFuzzyIsNull(m(1, 1));
+
+ if (isRectangleWithNoPerspective && (noRotate || isRotate90)) {
+ QRectF bbox = clip->clipRect();
+ float invW = 1.0f / m(3, 3);
+ float fx1, fy1, fx2, fy2;
+ if (noRotate) {
+ fx1 = (bbox.left() * m(0, 0) + m(0, 3)) * invW;
+ fy1 = (bbox.bottom() * m(1, 1) + m(1, 3)) * invW;
+ fx2 = (bbox.right() * m(0, 0) + m(0, 3)) * invW;
+ fy2 = (bbox.top() * m(1, 1) + m(1, 3)) * invW;
+ } else {
+ Q_ASSERT(isRotate90);
+ fx1 = (bbox.bottom() * m(0, 1) + m(0, 3)) * invW;
+ fy1 = (bbox.left() * m(1, 0) + m(1, 3)) * invW;
+ fx2 = (bbox.top() * m(0, 1) + m(0, 3)) * invW;
+ fy2 = (bbox.right() * m(1, 0) + m(1, 3)) * invW;
+ }
+
+ if (fx1 > fx2)
+ qSwap(fx1, fx2);
+ if (fy1 > fy2)
+ qSwap(fy1, fy2);
+
+ int ix1 = qRound((fx1 + 1) * devRect.width() * 0.5f);
+ int iy1 = qRound((fy1 + 1) * devRect.height() * 0.5f);
+ int ix2 = qRound((fx2 + 1) * devRect.width() * 0.5f);
+ int iy2 = qRound((fy2 + 1) * devRect.height() * 0.5f);
+
+ if (!(clipTypes & ClipScissor)) {
+ scissorRect = QRect(ix1, devRect.height() - iy2, ix2 - ix1, iy2 - iy1);
+ clipTypes |= ClipScissor;
+ } else {
+ scissorRect &= QRect(ix1, devRect.height() - iy2, ix2 - ix1, iy2 - iy1);
+ }
+ } else
+#endif
+ {
+ clipTypes |= ClipStencil;
+ renderStencilClip(clip, elementIndex, m, stencilValue);
+ }
+
+ clip = clip->clipList();
+ }
+
+ setScissor((clipTypes & ClipScissor) ? scissorRect : viewportRect());
+
+ if (clipTypes & ClipStencil) {
+ m_pipelineState.stencilEnable = true;
+ m_engine->queueSetStencilRef(stencilValue);
+ m_currentStencilValue = stencilValue;
+ } else {
+ m_pipelineState.stencilEnable = false;
+ m_currentStencilValue = 0;
+ }
+
+ m_currentClipTypes = clipTypes;
+}
+
+void QSGD3D12Renderer::setScissor(const QRect &r)
+{
+ if (m_activeScissorRect == r)
+ return;
+
+ m_activeScissorRect = r;
+ m_engine->queueScissor(r);
+}
+
+void QSGD3D12Renderer::renderStencilClip(const QSGClipNode *clip, int elementIndex,
+ const QMatrix4x4 &m, quint32 &stencilValue)
+{
+ QSGD3D12PipelineState sps;
+ sps.shaders.vs = g_VS_StencilClip;
+ sps.shaders.vsSize = sizeof(g_VS_StencilClip);
+ sps.shaders.ps = g_PS_StencilClip;
+ sps.shaders.psSize = sizeof(g_PS_StencilClip);
+
+ m_engine->queueClearDepthStencil(1, 0, QSGD3D12Engine::ClearStencil);
+ sps.stencilEnable = true;
+ sps.colorWrite = false;
+ sps.depthWrite = false;
+
+ sps.stencilFunc = QSGD3D12PipelineState::CompareEqual;
+ sps.stencilFailOp = QSGD3D12PipelineState::StencilKeep;
+ sps.stencilDepthFailOp = QSGD3D12PipelineState::StencilKeep;
+ sps.stencilPassOp = QSGD3D12PipelineState::StencilIncr;
+
+ m_engine->queueSetStencilRef(stencilValue);
+
+ int clipIndex = elementIndex;
+ while (m_renderList.at(--clipIndex).node != clip) {
+ Q_ASSERT(clipIndex >= 0);
+ }
+ const Element &ce = m_renderList.at(clipIndex);
+ Q_ASSERT(ce.node == clip);
+
+ const QSGGeometry *g = clip->geometry();
+ Q_ASSERT(g->attributeCount() == 1);
+ Q_ASSERT(g->attributes()[0].tupleSize == 2);
+ Q_ASSERT(g->attributes()[0].type == QSGGeometry::TypeFloat);
+
+ setInputLayout(g, &sps);
+ m_engine->finalizePipeline(sps);
+
+ Q_ASSERT(ce.cboSize > 0);
+ quint8 *p = m_cboData.data() + ce.cboOffset;
+ memcpy(p, m.constData(), 16 * sizeof(float));
+ m_engine->markBufferDirty(m_constantBuf, ce.cboOffset, ce.cboSize);
+
+ queueDrawCall(g, ce);
+
+ ++stencilValue;
+}
+
+void QSGD3D12Renderer::renderRenderNode(QSGRenderNode *node, int elementIndex)
+{
+ QSGRenderNodePrivate *rd = QSGRenderNodePrivate::get(node);
+ RenderNodeState state;
+
+ setupClipping(rd->m_clip_list, elementIndex);
+
+ QMatrix4x4 pm = projectionMatrix();
+ state.m_projectionMatrix = &pm;
+ state.m_scissorEnabled = m_currentClipTypes & ClipScissor;
+ state.m_stencilEnabled = m_currentClipTypes & ClipStencil;
+ state.m_scissorRect = m_activeScissorRect;
+ state.m_stencilValue = m_currentStencilValue;
+
+ // ### rendernodes do not have the QSGBasicGeometryNode infrastructure
+ // for storing combined matrices, opacity and such, but perhaps they should.
+ QSGNode *xform = node->parent();
+ QSGNode *root = rootNode();
+ QMatrix4x4 modelview;
+ while (xform != root) {
+ if (xform->type() == QSGNode::TransformNodeType) {
+ modelview *= static_cast<QSGTransformNode *>(xform)->combinedMatrix();
+ break;
+ }
+ xform = xform->parent();
+ }
+ rd->m_matrix = &modelview;
+
+ QSGNode *opacity = node->parent();
+ rd->m_opacity = 1.0;
+ while (opacity != rootNode()) {
+ if (opacity->type() == QSGNode::OpacityNodeType) {
+ rd->m_opacity = static_cast<QSGOpacityNode *>(opacity)->combinedOpacity();
+ break;
+ }
+ opacity = opacity->parent();
+ }
+
+ node->render(&state);
+
+ m_engine->invalidateCachedFrameState();
+ // For simplicity, reset viewport, scissor, blend factor, stencil ref when
+ // any of them got changed. This will likely be rare so skip these otherwise.
+ // Render target, pipeline state, draw call related stuff will be reset always.
+ const bool restoreMinimal = node->changedStates() == 0;
+ m_engine->restoreFrameState(restoreMinimal);
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12renderer_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12renderer_p.h
new file mode 100644
index 0000000000..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..bad222dbaa
--- /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::createWithSemantic(0, 2, QSGGeometry::TypeFloat, QSGGeometry::Attribute::POSITION),
+ QSGGeometry::Attribute::createWithSemantic(1, 2, QSGGeometry::TypeFloat, QSGGeometry::Attribute::TEXCOORD),
+};
+
+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..f9d92e8ee9
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/shaders/textmask.hlsl
@@ -0,0 +1,104 @@
+struct VSInput
+{
+ float4 position : POSITION;
+ float2 coord : TEXCOORD0;
+};
+
+cbuffer ConstantBuffer : register(b0)
+{
+ float4x4 mvp;
+ float2 textureScale;
+ float dpr;
+ float color; // for TextMask24 and 32
+ float4 colorVec; // for TextMask8 and Styled and Outlined
+ float2 shift; // for Styled
+ float4 styleColor; // for Styled and Outlined
+};
+
+struct PSInput
+{
+ float4 position : SV_POSITION;
+ float2 coord : TEXCOORD0;
+};
+
+Texture2D tex : register(t0);
+SamplerState samp : register(s0);
+
+PSInput VS_TextMask(VSInput input)
+{
+ PSInput result;
+ result.position = mul(mvp, floor(input.position * dpr + 0.5) / dpr);
+ result.coord = input.coord * textureScale;
+ return result;
+}
+
+float4 PS_TextMask24(PSInput input) : SV_TARGET
+{
+ float4 glyph = tex.Sample(samp, input.coord);
+ return float4(glyph.rgb * color, glyph.a);
+}
+
+float4 PS_TextMask32(PSInput input) : SV_TARGET
+{
+ return tex.Sample(samp, input.coord) * color;
+}
+
+float4 PS_TextMask8(PSInput input) : SV_TARGET
+{
+ return colorVec * tex.Sample(samp, input.coord).r;
+}
+
+struct StyledPSInput
+{
+ float4 position : SV_POSITION;
+ float2 coord : TEXCOORD0;
+ float2 shiftedCoord : TEXCOORD1;
+};
+
+StyledPSInput VS_StyledText(VSInput input)
+{
+ StyledPSInput result;
+ result.position = mul(mvp, floor(input.position * dpr + 0.5) / dpr);
+ result.coord = input.coord * textureScale;
+ result.shiftedCoord = (input.coord - shift) * textureScale;
+ return result;
+}
+
+float4 PS_StyledText(StyledPSInput input) : SV_TARGET
+{
+ float glyph = tex.Sample(samp, input.coord).r;
+ float style = clamp(tex.Sample(samp, input.shiftedCoord).r - glyph, 0.0, 1.0);
+ return style * styleColor + glyph * colorVec;
+}
+
+struct OutlinedPSInput
+{
+ float4 position : SV_POSITION;
+ float2 coord : TEXCOORD0;
+ float2 coordUp : TEXCOORD1;
+ float2 coordDown : TEXCOORD2;
+ float2 coordLeft : TEXCOORD3;
+ float2 coordRight : TEXCOORD4;
+};
+
+OutlinedPSInput VS_OutlinedText(VSInput input)
+{
+ OutlinedPSInput result;
+ result.position = mul(mvp, floor(input.position * dpr + 0.5) / dpr);
+ result.coord = input.coord * textureScale;
+ result.coordUp = (input.coord - float2(0.0, -1.0)) * textureScale;
+ result.coordDown = (input.coord - float2(0.0, 1.0)) * textureScale;
+ result.coordLeft = (input.coord - float2(-1.0, 0.0)) * textureScale;
+ result.coordRight = (input.coord - float2(1.0, 0.0)) * textureScale;
+ return result;
+}
+
+float4 PS_OutlinedText(OutlinedPSInput input) : SV_TARGET
+{
+ float glyph = tex.Sample(samp, input.coord).r;
+ float outline = clamp(clamp(tex.Sample(samp, input.coordUp).r
+ + tex.Sample(samp, input.coordDown).r
+ + tex.Sample(samp, input.coordLeft).r
+ + tex.Sample(samp, input.coordRight).r, 0.0, 1.0) - glyph, 0.0, 1.0);
+ return outline * styleColor + glyph * colorVec;
+}
diff --git a/src/plugins/scenegraph/d3d12/shaders/texture.hlsl b/src/plugins/scenegraph/d3d12/shaders/texture.hlsl
new file mode 100644
index 0000000000..1ae6579e8d
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/shaders/texture.hlsl
@@ -0,0 +1,33 @@
+struct VSInput
+{
+ float4 position : POSITION;
+ float2 coord : TEXCOORD0;
+};
+
+cbuffer ConstantBuffer : register(b0)
+{
+ float4x4 mvp;
+ float opacity;
+};
+
+struct PSInput
+{
+ float4 position : SV_POSITION;
+ float2 coord : TEXCOORD0;
+};
+
+Texture2D tex : register(t0);
+SamplerState samp : register(s0);
+
+PSInput VS_Texture(VSInput input)
+{
+ PSInput result;
+ result.position = mul(mvp, input.position);
+ result.coord = input.coord;
+ return result;
+}
+
+float4 PS_Texture(PSInput input) : SV_TARGET
+{
+ return tex.Sample(samp, input.coord) * opacity;
+}
diff --git a/src/plugins/scenegraph/d3d12/shaders/vertexcolor.hlsl b/src/plugins/scenegraph/d3d12/shaders/vertexcolor.hlsl
new file mode 100644
index 0000000000..a0569bb5c1
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/shaders/vertexcolor.hlsl
@@ -0,0 +1,32 @@
+struct VSInput
+{
+ float4 position : POSITION;
+ float4 color : COLOR;
+};
+
+cbuffer ConstantBuffer : register(b0)
+{
+ float4x4 mvp;
+ float opacity;
+};
+
+struct PSInput
+{
+ float4 position : SV_POSITION;
+ float4 color : COLOR;
+};
+
+PSInput VS_VertexColor(VSInput input)
+{
+ PSInput result;
+
+ result.position = mul(mvp, input.position);
+ result.color = input.color * opacity;
+
+ return result;
+}
+
+float4 PS_VertexColor(PSInput input) : SV_TARGET
+{
+ return input.color;
+}
diff --git a/src/plugins/scenegraph/scenegraph.pro b/src/plugins/scenegraph/scenegraph.pro
new file mode 100644
index 0000000000..2cf05165c4
--- /dev/null
+++ b/src/plugins/scenegraph/scenegraph.pro
@@ -0,0 +1,2 @@
+TEMPLATE = subdirs
+config_d3d12: SUBDIRS += d3d12
diff --git a/src/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..ad73c26b15 100644
--- a/src/qml/compiler/compiler.pri
+++ b/src/qml/compiler/compiler.pri
@@ -26,12 +26,16 @@ SOURCES += \
HEADERS += \
$$PWD/qqmltypecompiler_p.h \
$$PWD/qv4isel_moth_p.h \
- $$PWD/qv4instr_moth_p.h
+ $$PWD/qv4instr_moth_p.h \
+ $$PWD/qqmlpropertycachecreator_p.h \
+ $$PWD/qqmlpropertyvalidator_p.h
SOURCES += \
$$PWD/qqmltypecompiler.cpp \
$$PWD/qv4instr_moth.cpp \
- $$PWD/qv4isel_moth.cpp
+ $$PWD/qv4isel_moth.cpp \
+ $$PWD/qqmlpropertycachecreator.cpp \
+ $$PWD/qqmlpropertyvalidator.cpp
}
diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp
index eaf0e72296..f736e04b88 100644
--- a/src/qml/compiler/qqmlirbuilder.cpp
+++ b/src/qml/compiler/qqmlirbuilder.cpp
@@ -49,7 +49,6 @@
#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 +71,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 +147,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 +193,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 +219,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 +251,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");
@@ -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;
+
+ 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,6 +1348,17 @@ bool IRBuilder::isStatementNodeScript(QQmlJS::AST::Statement *statement)
return true;
}
+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)
{
QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit = output.javaScriptCompilationUnit;
@@ -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)
@@ -1354,13 +1413,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 +1437,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 +1449,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 +1464,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 +1499,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;
}
@@ -1438,7 +1521,7 @@ QV4::CompiledData::Unit *QmlUnitGenerator::generate(Document &output)
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 +1529,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 +1697,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);
}
@@ -1822,7 +1905,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 +1992,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 +2011,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;
diff --git a/src/qml/compiler/qqmlirbuilder_p.h b/src/qml/compiler/qqmlirbuilder_p.h
index 057ed1be9f..821a6bba6d 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,11 @@ 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 +509,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 +527,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 +535,7 @@ public:
QList<const QV4::CompiledData::Import *> _imports;
QList<Pragma*> _pragmas;
- QList<Object*> _objects;
+ QVector<Object*> _objects;
QV4::CompiledData::TypeReferenceMap _typeReferences;
@@ -451,17 +553,17 @@ struct Q_QML_PRIVATE_EXPORT QmlUnitGenerator
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 +573,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..09a093d911
--- /dev/null
+++ b/src/qml/compiler/qqmlpropertycachecreator_p.h
@@ -0,0 +1,746 @@
+/****************************************************************************
+**
+** 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) {
+ quint32 flags = QQmlPropertyData::IsSignal | QQmlPropertyData::IsFunction |
+ QQmlPropertyData::IsVMESignal;
+
+ 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) {
+ quint32 flags = QQmlPropertyData::IsSignal | QQmlPropertyData::IsFunction |
+ QQmlPropertyData::IsVMESignal;
+
+ 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();
+ }
+ }
+ }
+ }
+
+ quint32 flags = QQmlPropertyData::IsSignal | QQmlPropertyData::IsFunction |
+ QQmlPropertyData::IsVMESignal;
+ if (paramCount)
+ flags |= QQmlPropertyData::HasArguments;
+
+ 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) {
+ quint32 flags = QQmlPropertyData::IsFunction | QQmlPropertyData::IsVMEFunction;
+
+ 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 |= QQmlPropertyData::HasArguments;
+ 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;
+ quint32 propertyFlags = 0;
+
+ if (p->type == QV4::CompiledData::Property::Var) {
+ propertyType = QMetaType::QVariant;
+ propertyFlags = QQmlPropertyData::IsVarProperty;
+ } else if (p->type < builtinTypeCount) {
+ propertyType = builtinTypes[p->type].metaType;
+
+ 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)) {
+ 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 |= 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 (!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, quint32 *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->encodedMetaPropertyIndex == -1)
+ continue;
+
+ const QQmlPropertyCache *targetCache = propertyCaches->at(targetObjectIndex);
+ Q_ASSERT(targetCache);
+
+ int coreIndex;
+ QQmlPropertyData::decodeValueTypePropertyIndex(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, quint32 *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 = QQmlPropertyData::IsAlias;
+
+ 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 |= QQmlPropertyData::IsQObjectDerived;
+ } else {
+ int coreIndex;
+ int valueTypeIndex = QQmlPropertyData::decodeValueTypePropertyIndex(alias.encodedMetaPropertyIndex, &coreIndex);
+
+ 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 |= targetProperty->getFlags() & QQmlPropertyData::PropTypeFlagMask;
+
+ if (targetProperty->isVarProperty())
+ *propertyFlags |= QQmlPropertyData::IsQVariant;
+ }
+ }
+ }
+
+ if (!(alias.flags & QV4::CompiledData::Property::IsReadOnly) && writable)
+ *propertyFlags |= QQmlPropertyData::IsWritable;
+ else
+ *propertyFlags &= ~QQmlPropertyData::IsWritable;
+
+ if (resettable)
+ *propertyFlags |= QQmlPropertyData::IsResettable;
+ else
+ *propertyFlags &= ~QQmlPropertyData::IsResettable;
+}
+
+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;
+ quint32 propertyFlags = 0;
+ 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..c644330e97
--- /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 f49996a3b8..a0b219e28e 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();
@@ -236,64 +162,39 @@ bool QQmlTypeCompiler::compile()
// 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();
}
@@ -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);
@@ -1169,7 +570,7 @@ 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;
}
@@ -1218,7 +619,7 @@ 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);
@@ -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,20 +744,20 @@ 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;
+ QQmlPropertyData *pd = binding->propertyNameIndex != quint32(0) ? resolver.property(stringAt(binding->propertyNameIndex), &notInRevision) : defaultProperty;
if (!pd || pd->propType != scriptStringMetaType)
continue;
@@ -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 {
@@ -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());
+
+ int propIdx = -1;
+
+ 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 = 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 = QQmlPropertyData::encodeValueTypePropertyIndex(propIdx, valueTypeIndex);
+ } else {
+ if (targetProperty->isQObject())
+ alias->flags |= QV4::CompiledData::Alias::AliasPointsToPointerObject;
+ }
}
+
+ alias->encodedMetaPropertyIndex = propIdx;
+ 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..6ad6ad8557 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,48 @@ 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;
+ QVector<quint32> componentRoots;
- 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<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 +318,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 +338,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 +352,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 +384,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 +399,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 +407,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 ad28e4d6d1..4130ad08d7 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);
@@ -2932,7 +2933,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/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp
index a63f35152a..2aab1743cc 100644
--- a/src/qml/compiler/qv4compileddata.cpp
+++ b/src/qml/compiler/qv4compileddata.cpp
@@ -47,6 +47,14 @@
#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 <QQmlPropertyMap>
+#include <QDateTime>
+#include <QSaveFile>
+#include <QFile>
+#include <QFileInfo>
+#include <QScopedValueRollback>
#endif
#include <private/qqmlirbuilder_p.h>
#include <QCoreApplication>
@@ -67,11 +75,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 +126,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)
@@ -146,12 +163,6 @@ 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);
-#endif
-
if (data->indexOfRootFunction != -1)
return runtimeFunctions[data->indexOfRootFunction];
else
@@ -162,10 +173,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;
@@ -189,6 +216,223 @@ 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::saveToDisk(QString *errorString)
+{
+ errorString->clear();
+
+ const QUrl unitUrl = url();
+ if (!unitUrl.isLocalFile()) {
+ *errorString = QStringLiteral("File has to be a local file.");
+ return false;
+ }
+
+ // Foo.qml -> Foo.qmlc
+ QSaveFile cacheFile(unitUrl.toLocalFile() + QLatin1Char('c'));
+ 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 (!url.isLocalFile()) {
+ *errorString = QStringLiteral("File has to be a local file.");
+ return false;
+ }
+
+ const QString sourcePath = url.toLocalFile();
+ QScopedPointer<QFile> cacheFile(new QFile(sourcePath + QLatin1Char('c')));
+
+ if (!cacheFile->open(QIODevice::ReadOnly)) {
+ *errorString = cacheFile->errorString();
+ return false;
+ }
+
+ {
+ CompiledData::Unit header;
+ qint64 bytesRead = cacheFile->read(reinterpret_cast<char *>(&header), sizeof(header));
+
+ if (bytesRead != sizeof(header)) {
+ *errorString = QStringLiteral("File too small for the header fields");
+ return false;
+ }
+
+ if (strncmp(header.magic, CompiledData::magic_str, sizeof(header.magic))) {
+ *errorString = QStringLiteral("Magic bytes in the header do not match");
+ return false;
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+
+ {
+ QFileInfo sourceCode(sourcePath);
+ if (sourceCode.exists() && sourceCode.lastModified().toMSecsSinceEpoch() != header.sourceTimeStamp) {
+ *errorString = QStringLiteral("QML source file has a different time stamp than cached file.");
+ return false;
+ }
+ }
+
+ }
+ // Data structure and qt version matched, so now we can access the rest of the file safely.
+
+ uchar *cacheData = cacheFile->map(/*offset*/0, cacheFile->size());
+ if (!cacheData) {
+ *errorString = cacheFile->errorString();
+ return false;
+ }
+
+ QScopedValueRollback<const Unit *> dataPtrChange(data, reinterpret_cast<const Unit *>(cacheData));
+
+ {
+ 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();
+ 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 +449,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 +531,56 @@ 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();
+ }
+}
+
+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);
+}
+#endif
+
}
}
diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h
index 8c617875e0..82e303f27d 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 0x01
+
+class QIODevice;
class QQmlPropertyCache;
class QQmlPropertyData;
+class QQmlTypeNameCache;
+class QQmlScriptData;
+class QQmlType;
+class QQmlEngine;
namespace QmlIR {
struct Document;
@@ -76,25 +91,48 @@ struct Function;
}
struct Function;
+class EvalISelFactory;
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 +142,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 +165,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 +193,7 @@ struct JSClass
struct String
{
- quint32 flags; // isArrayIndex
- qint32 size;
+ LEInt32 size;
// uint16 strdata[]
static int calculateSize(const QString &str) {
@@ -158,7 +203,7 @@ struct String
struct Function
{
- enum Flags {
+ enum Flags : unsigned int {
HasDirectEval = 0x1,
UsesArgumentsObject = 0x2,
IsStrict = 0x4,
@@ -166,36 +211,46 @@ 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;
+ 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
+ // Absolute offset into file where the code for this function is located. Only used when the function
+ // is serialized.
+ LEUInt64 codeOffset;
+ LEUInt64 codeSize;
+
// quint32 formalsIndex[nFormals]
// quint32 localsIndex[nLocals]
// 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 +262,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 +283,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 +366,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 +391,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 +413,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 {
+ QJsonPrivate::qle_bitfield<0, 31> type;
+ QJsonPrivate::qle_bitfield<31, 1> flags; // readonly
+ };
+ LEUInt32 customTypeNameIndex; // If type >= Custom
+ Location location;
+};
+
+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 {
- quint32 customTypeNameIndex; // If type >= Custom
- quint32 aliasIdValueIndex; // If type == Alias
+ 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)
};
- quint32 aliasPropertyValueIndex;
- quint32 flags; // readonly
Location location;
- Location aliasLocation; // If type == Alias
+ 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 +509,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 +532,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 +544,111 @@ 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;
+ quint8 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
+
+ LEUInt32 architectureIndex; // string index to QSysInfo::buildAbi()
+ LEUInt32 codeGeneratorIndex;
- enum {
+ enum : unsigned int {
IsJavascript = 0x1,
IsQml = 0x2,
StaticData = 0x4, // Unit data persistent in memory?
IsSingleton = 0x8,
IsSharedLibrary = 0x10 // .pragma shared?
};
- 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 +658,31 @@ 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);
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);
}
@@ -531,22 +695,13 @@ struct Unit
}
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)
@@ -574,7 +729,67 @@ 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 *);
+
+ void doDynamicTypeCheck();
};
+// map from name index
+typedef QHash<int, ResolvedTypeReference*> ResolvedTypeReferenceMap;
+#endif
// index is per-object binding index
typedef QVector<QQmlPropertyData*> BindingPropertyData;
@@ -597,7 +812,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 +829,75 @@ 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);
+
+ 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;
+
+ int metaTypeId;
+ int listMetaTypeId;
+ bool isRegisteredWithEngine;
+
+ QScopedPointer<QIODevice> 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(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..768a4ffcd3 100644
--- a/src/qml/compiler/qv4compiler.cpp
+++ b/src/qml/compiler/qv4compiler.cpp
@@ -42,6 +42,7 @@
#include <qv4isel_p.h>
#include <private/qv4string_p.h>
#include <private/qv4value_p.h>
+#include <private/qv4alloca_p.h>
QV4::Compiler::StringTableGenerator::StringTableGenerator()
{
@@ -75,16 +76,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 +98,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 +179,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,94 +218,45 @@ 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);
+ CompiledData::LEUInt32 *functionOffsets = reinterpret_cast<CompiledData::LEUInt32*>(alloca(irModule->functions.size() * sizeof(CompiledData::LEUInt32)));
+ uint jsClassDataOffset = 0;
- 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);
+ char *dataPtr;
+ CompiledData::Unit *unit;
+ {
+ QV4::CompiledData::Unit tempHeader = generateHeader(option, functionOffsets, &jsClassDataOffset);
+ dataPtr = reinterpret_cast<char *>(malloc(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);
+ 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();
+ {
+ memcpy(dataPtr + jsClassDataOffset, jsClassData.constData(), jsClassData.size());
- 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());
+ // 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
@@ -308,13 +266,12 @@ QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(GeneratorO
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);
- function->index = index;
function->nameIndex = getStringId(*irFunction->name);
function->flags = 0;
if (irFunction->hasDirectEval)
@@ -336,8 +293,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 +319,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 +332,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 +350,70 @@ 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;
+ memcpy(unit.magic, CompiledData::magic_str, sizeof(unit.magic));
+ unit.flags = QV4::CompiledData::Unit::IsJavascript;
+ unit.version = QV4_DATA_STRUCTURE_VERSION;
+ unit.qtVersion = QT_VERSION;
+ unit.architectureIndex = registerString(QSysInfo::buildAbi());
+ unit.codeGeneratorIndex = registerString(codeGeneratorName);
+
+ 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();
+ 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);
+ }
+
+ 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;
- return CompiledData::Function::calculateSize(function->nFormals, function->nLocals, function->nInnerFunctions,
- function->nDependingIdObjects, function->nDependingContextProperties + function->nDependingScopeProperties);
+ 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..20b871c4e8 100644
--- a/src/qml/compiler/qv4instr_moth_p.h
+++ b/src/qml/compiler/qv4instr_moth_p.h
@@ -58,10 +58,17 @@
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 +169,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 +181,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
@@ -252,6 +261,8 @@ union Instr
MOTH_INSTR_HEADER
Param result;
};
+
+#ifndef QT_NO_QML_DEBUGGING
struct instr_line {
MOTH_INSTR_HEADER
qint32 lineNumber;
@@ -260,6 +271,8 @@ union Instr
MOTH_INSTR_HEADER
qint32 lineNumber;
};
+#endif // QT_NO_QML_DEBUGGING
+
struct instr_loadRuntimeString {
MOTH_INSTR_HEADER
int stringId;
@@ -672,7 +685,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 +770,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 be10d50e9b..41790c04a9 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:
@@ -250,7 +251,7 @@ protected:
_unhandled.removeLast();
}
- s->accept(this);
+ visit(s);
}
if (IR::Jump *jump = s->asJump()) {
@@ -273,7 +274,7 @@ protected:
moves.order();
QList<IR::Move *> newMoves = moves.insertMoves(_currentBasicBlock, _function, true);
foreach (IR::Move *move, newMoves)
- move->accept(this);
+ visit(move);
}
}
@@ -312,8 +313,8 @@ protected:
};
} // 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)
@@ -411,6 +412,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;
@@ -420,10 +422,11 @@ void InstructionSelection::run(int functionIndex)
line.lineNumber = currentLine;
addInstruction(line);
}
+#endif
}
}
- s->accept(this);
+ visit(s);
}
}
@@ -1040,11 +1043,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);
@@ -1091,6 +1094,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)
@@ -1098,11 +1112,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;
@@ -1113,11 +1123,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()) {
@@ -1152,12 +1158,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);
@@ -1493,12 +1495,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) {
@@ -1584,6 +1581,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) {
@@ -1594,3 +1614,81 @@ 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;
+
+ 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 *codePtr = codeRefs.at(i).constData();
+ written = device->write(reinterpret_cast<const char *>(codePtr), compiledFunction->codeSize);
+ if (written != qint64(compiledFunction->codeSize)) {
+ *errorString = device->errorString();
+ return false;
+ }
+ }
+ return true;
+}
+
+bool CompilationUnit::memoryMapCode(QString *errorString)
+{
+ Q_UNUSED(errorString);
+ Q_ASSERT(codeRefs.isEmpty());
+ codeRefs.reserve(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.append(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..2d2bb91228 100644
--- a/src/qml/compiler/qv4isel_moth_p.h
+++ b/src/qml/compiler/qv4isel_moth_p.h
@@ -66,7 +66,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 +80,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);
@@ -174,6 +177,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 +207,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..d97eec5e1d 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()
diff --git a/src/qml/compiler/qv4isel_p.h b/src/qml/compiler/qv4isel_p.h
index 88d2071c52..a3fa80b6f0 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;
@@ -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..b6c5226894 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,7 +907,7 @@ 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)
@@ -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..51b8797862 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,52 @@ 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
+#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)
+#ifndef QT_NO_QML_DEBUGGER
, debugMode(debugMode)
{}
+#else
+ { Q_UNUSED(debugMode); }
+#endif
~Module();
void setFileName(const QString &name);
@@ -1060,6 +1203,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 +1282,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 +1350,7 @@ private:
int _statementCount;
};
-class CloneExpr: protected IR::ExprVisitor
+class CloneExpr
{
public:
explicit CloneExpr(IR::BasicBlock *block = 0);
@@ -1210,7 +1368,7 @@ public:
{
Expr *c = expr;
qSwap(cloned, c);
- expr->accept(this);
+ visit(expr);
qSwap(cloned, c);
return static_cast<ExprSubclass *>(c);
}
@@ -1253,23 +1411,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 +1423,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 +1436,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 +1444,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..283fb24897 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);
- }
+private:
+ void visit(Stmt *s)
+ {
+ if (s->asPhi()) {
+ // nothing to do
+ } else if (auto move = s->asMove()) {
+ visit(move->source);
- virtual void visitMove(Move *s) {
- s->source->accept(this);
+ if (Temp *t = move->target->asTemp()) {
+ addTemp(t);
- if (Temp *t = s->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);
+ }
+ }
+
+ virtual void visitTemp(Temp *) {}
- void visitName(Name *e) Q_DECL_OVERRIDE {
+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);
+ visit(use);
}
-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);
- }
-
- 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);
+ }
}
};
@@ -4381,7 +4500,7 @@ void removeUnreachleBlocks(IR::Function *function)
function->renumberBasicBlocks();
}
-class ConvertArgLocals: protected StmtVisitor, protected ExprVisitor
+class ConvertArgLocals
{
public:
ConvertArgLocals(IR::Function *function)
@@ -4419,10 +4538,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 +4552,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 +4603,7 @@ private:
e = t;
}
} else {
- e->accept(this);
+ visit(e);
}
}
@@ -4498,7 +4626,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 +4634,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 +4833,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 +4855,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 +4903,7 @@ static void verifyNoPointerSharing(IR::Function *function)
if (!DoVerification)
return;
- class : public StmtVisitor, public ExprVisitor {
+ class {
public:
void operator()(IR::Function *f)
{
@@ -4777,44 +4911,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 +4949,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,21 +4967,88 @@ 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
+
+ // 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) {
@@ -5103,6 +5283,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 +5360,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/debugger/debugger.pri b/src/qml/debugger/debugger.pri
index 30a44eedd1..74dcb250a8 100644
--- a/src/qml/debugger/debugger.pri
+++ b/src/qml/debugger/debugger.pri
@@ -1,21 +1,27 @@
-contains(QT_CONFIG, no-qml-debug):DEFINES += QT_NO_QML_DEBUGGER
+contains(QT_CONFIG, no-qml-debug) {
+ 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 9276bd0544..386fb60b3a 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;
}
QT_END_NAMESPACE
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..60f6d96eaf 100644
--- a/src/qml/qml/qqmlmemoryprofiler.cpp
+++ b/src/qml/debugger/qqmlmemoryprofiler.cpp
@@ -97,12 +97,9 @@ static bool openLibrary()
return state == Loaded;
}
-QQmlMemoryScope::QQmlMemoryScope(const QUrl &url) : pushed(false)
+QQmlMemoryScope::QQmlMemoryScope(const QUrl &url)
+ : QQmlMemoryScope(url.path().toUtf8().constData())
{
- if (openLibrary() && memprofile_is_enabled()) {
- memprofile_push_location(url.path().toUtf8().constData(), 0);
- pushed = true;
- }
}
QQmlMemoryScope::QQmlMemoryScope(const char *string) : pushed(false)
diff --git a/src/qml/qml/qqmlmemoryprofiler_p.h b/src/qml/debugger/qqmlmemoryprofiler_p.h
index 4b0ba823ba..59f08704ca 100644
--- a/src/qml/qml/qqmlmemoryprofiler_p.h
+++ b/src/qml/debugger/qqmlmemoryprofiler_p.h
@@ -55,6 +55,13 @@
QT_BEGIN_NAMESPACE
+#ifdef QT_NO_QML_DEBUGGER
+
+#define QML_MEMORY_SCOPE_URL(url)
+#define QML_MEMORY_SCOPE_STRING(s)
+
+#else
+
class QUrl;
class Q_QML_PRIVATE_EXPORT QQmlMemoryScope
@@ -83,5 +90,7 @@ public:
#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/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/jit/qv4assembler.cpp b/src/qml/jit/qv4assembler.cpp
index 3da1aaa010..08e4f0a8c0 100644
--- a/src/qml/jit/qv4assembler.cpp
+++ b/src/qml/jit/qv4assembler.cpp
@@ -79,14 +79,70 @@ 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);
+ Q_ASSERT(codeRefs.isEmpty());
+ codeRefs.reserve(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.append(codeRef);
+ }
+
+ return true;
}
const Assembler::VoidType Assembler::Void;
diff --git a/src/qml/jit/qv4assembler_p.h b/src/qml/jit/qv4assembler_p.h
index a5028bc683..669b0b2ff4 100644
--- a/src/qml/jit/qv4assembler_p.h
+++ b/src/qml/jit/qv4assembler_p.h
@@ -63,6 +63,8 @@
#include <config.h>
#include <wtf/Vector.h>
+#include <climits>
+
#if ENABLE(ASSEMBLER)
#include <assembler/MacroAssembler.h>
@@ -73,20 +75,16 @@ 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);
// Coderef + execution engine
@@ -94,14 +92,6 @@ struct CompilationUnit : public QV4::CompiledData::CompilationUnit
QList<QVector<QV4::Primitive> > constantValues;
};
-struct RelativeCall {
- JSC::MacroAssembler::Address addr;
-
- explicit RelativeCall(const JSC::MacroAssembler::Address &addr)
- : addr(addr)
- {}
-};
-
struct LookupCall {
JSC::MacroAssembler::Address addr;
uint getterSetterOffset;
@@ -112,6 +102,13 @@ struct LookupCall {
{}
};
+struct RuntimeCall {
+ JSC::MacroAssembler::Address addr;
+
+ inline RuntimeCall(uint offset = uint(INT_MIN));
+ bool isValid() const { return addr.offset >= 0; }
+};
+
template <typename T>
struct ExceptionCheck {
enum { NeedsCheck = 1 };
@@ -321,12 +318,12 @@ public:
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 +341,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);
@@ -441,13 +429,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);
@@ -457,7 +438,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);
@@ -476,15 +457,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)
{
@@ -544,11 +516,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);
@@ -705,34 +672,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();
@@ -1154,9 +1093,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 = constantTable().loadValueAddress(QV4::Primitive::fromDouble(double(INT_MAX) + 1), scratchReg);
+ subDouble(inversionAddress, FPGpr0);
Jump canNeverHappen = branchTruncateDoubleToUint32(FPGpr0, scratchReg);
canNeverHappen.link(this);
or32(TrustedImm32(1 << 31), scratchReg);
@@ -1184,7 +1122,9 @@ private:
IR::Function *_function;
QHash<IR::BasicBlock *, Label> _addrs;
QHash<IR::BasicBlock *, QVector<Jump> > _patches;
- QList<CallToLink> _callsToLink;
+#ifndef QT_NO_DEBUG
+ QVector<CallInfo> _callInfos;
+#endif
struct DataLabelPatch {
DataLabelPtr dataLabel;
@@ -1245,24 +1185,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..c09fc6fdca 100644
--- a/src/qml/jit/qv4binop.cpp
+++ b/src/qml/jit/qv4binop.cpp
@@ -45,14 +45,14 @@ 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 }
#define OPCONTEXT(op) \
- { isel_stringIfy(op), 0, op, 0, 0 }
+ { "Runtime::" isel_stringIfy(op), INT_MIN, offsetof(QV4::Runtime, op), 0, 0 }
#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 }
#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 }
#define NULL_OP \
{ 0, 0, 0, 0, 0 }
@@ -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(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(target, info.name, context,
Assembler::EngineRegister,
Assembler::PointerToValue(lhs),
Assembler::PointerToValue(rhs));
diff --git a/src/qml/jit/qv4binop_p.h b/src/qml/jit/qv4binop_p.h
index 791e335970..c246ee43b0 100644
--- a/src/qml/jit/qv4binop_p.h
+++ b/src/qml/jit/qv4binop_p.h
@@ -77,8 +77,8 @@ 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;
};
diff --git a/src/qml/jit/qv4isel_masm.cpp b/src/qml/jit/qv4isel_masm.cpp
index bf658fe689..4066ab213c 100644
--- a/src/qml/jit/qv4isel_masm.cpp
+++ b/src/qml/jit/qv4isel_masm.cpp
@@ -159,12 +159,6 @@ 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)
linkBuffer.patch(p.dataLabel, linkBuffer.locationOf(p.target));
@@ -193,6 +187,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
+ foreach (CallInfo call, _callInfos)
+ functions[linkBuffer.locationOf(call.label).dataLocation()] = call.functionName;
+#endif
+
QBuffer buf;
buf.open(QIODevice::WriteOnly);
WTF::setDataFile(new QIODevicePrintStream(&buf));
@@ -258,8 +258,8 @@ 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)
@@ -353,7 +353,7 @@ void InstructionSelection::run(int functionIndex)
lastLine = s->location.startLine;
}
}
- s->accept(this);
+ visit(s);
}
}
@@ -393,12 +393,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 +410,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 +425,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 +475,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 +486,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 +500,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 +508,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 +534,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 +614,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 +635,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 +659,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 +716,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 +726,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,7 +748,7 @@ 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));
}
}
@@ -756,11 +756,11 @@ void InstructionSelection::getProperty(IR::Expr *base, const QString &name, IR::
void InstructionSelection::getQmlContextProperty(IR::Expr *base, IR::Member::MemberKind kind, int index, 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));
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));
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 +768,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 +787,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 +796,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 +807,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 +821,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 +834,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 +981,9 @@ 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); } 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); } while (0)
void InstructionSelection::unop(IR::AluOp oper, IR::Expr *source, IR::Expr *target)
{
@@ -1003,12 +1003,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 +1025,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 +1042,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 +1118,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 +1183,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;
@@ -1215,7 +1215,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));
isInt.link(_as);
@@ -1253,7 +1253,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);
@@ -1266,7 +1266,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);
@@ -1284,7 +1284,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;
@@ -1309,7 +1309,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);
@@ -1320,7 +1320,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);
@@ -1331,7 +1331,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;
@@ -1351,13 +1351,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());
@@ -1369,14 +1369,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());
}
@@ -1386,7 +1386,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());
@@ -1422,7 +1422,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);
}
@@ -1432,7 +1432,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;
@@ -1454,8 +1454,8 @@ void InstructionSelection::visitCJump(IR::CJump *s)
return;
}
- Runtime::CompareOperation op = 0;
- Runtime::CompareOperationContext opContext = 0;
+ RuntimeCall op;
+ RuntimeCall opContext;
const char *opName = 0;
switch (b->op) {
default: Q_UNREACHABLE(); Q_ASSERT(!"todo"); break;
@@ -1476,7 +1476,7 @@ 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)
+ if (opContext.isValid())
_as->generateFunctionCallImp(Assembler::ReturnValueRegister, opName, opContext,
Assembler::EngineRegister,
Assembler::PointerToValue(b->left),
@@ -1800,7 +1800,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),
@@ -1954,12 +1954,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 366d510072..4b35a72e01 100644
--- a/src/qml/jit/qv4isel_masm_p.h
+++ b/src/qml/jit/qv4isel_masm_p.h
@@ -76,7 +76,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);
@@ -244,8 +244,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(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);
@@ -285,11 +285,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..deef719b67 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);
}
}
}
@@ -809,7 +809,8 @@ 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;
@@ -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())
@@ -1179,8 +1180,20 @@ 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;
@@ -1210,47 +1223,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);
+ switch (s->stmtKind) {
+ case Stmt::MoveStmt: {
+ auto m = s->asMove();
+ if (Temp *t = m->target->asTemp())
+ maybeGenerateSpill(t);
- s->source->accept(this);
- s->target->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 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
diff --git a/src/qml/jit/qv4unop.cpp b/src/qml/jit/qv4unop.cpp
index cb9131d731..6a32069ac4 100644
--- a/src/qml/jit/qv4unop.cpp
+++ b/src/qml/jit/qv4unop.cpp
@@ -47,11 +47,11 @@ 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); } while (0)
void Unop::generate(IR::Expr *source, IR::Expr *target)
{
- Runtime::UnaryOperation call = 0;
+ RuntimeCall call;
const char *name = 0;
switch (op) {
case IR::OpNot:
@@ -60,19 +60,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(target, name, call, Assembler::PointerToValue(source));
}
void Unop::generateUMinus(IR::Expr *source, IR::Expr *target)
@@ -82,15 +81,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 +99,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 +128,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..e5c1dcdb80 100644
--- a/src/qml/jsapi/qjsvalue.cpp
+++ b/src/qml/jsapi/qjsvalue.cpp
@@ -663,11 +663,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 +719,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 +767,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 +938,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 +1234,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 +1308,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/jsruntime/jsruntime.pri b/src/qml/jsruntime/jsruntime.pri
index 038b23e8d3..cf434ee2ed 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 \
@@ -40,11 +39,16 @@ SOURCES += \
$$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
+} else {
+ DEFINES += QT_NO_QML_DEBUGGER
+}
+
HEADERS += \
$$PWD/qv4global_p.h \
$$PWD/qv4engine_p.h \
@@ -99,6 +103,7 @@ HEADERS += \
HEADERS += \
$$PWD/qv4runtime_p.h \
+ $$PWD/qv4runtimeapi_p.h \
$$PWD/qv4value_p.h \
$$PWD/qv4string_p.h \
$$PWD/qv4value_p.h
@@ -111,3 +116,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..b55494823c 100644
--- a/src/qml/jsruntime/qv4argumentsobject.cpp
+++ b/src/qml/jsruntime/qv4argumentsobject.cpp
@@ -134,7 +134,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 +203,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..6ccfd89fd4 100644
--- a/src/qml/jsruntime/qv4argumentsobject_p.h
+++ b/src/qml/jsruntime/qv4argumentsobject_p.h
@@ -88,7 +88,7 @@ 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
@@ -103,7 +103,7 @@ 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
diff --git a/src/qml/jsruntime/qv4arraybuffer.cpp b/src/qml/jsruntime/qv4arraybuffer.cpp
index d170bde0e8..e006773c9e 100644
--- a/src/qml/jsruntime/qv4arraybuffer.cpp
+++ b/src/qml/jsruntime/qv4arraybuffer.cpp
@@ -51,29 +51,34 @@ Heap::ArrayBufferCtor::ArrayBufferCtor(QV4::ExecutionContext *scope)
{
}
-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)
@@ -184,7 +189,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();
diff --git a/src/qml/jsruntime/qv4arraybuffer_p.h b/src/qml/jsruntime/qv4arraybuffer_p.h
index 0413d2f28d..d079aeb9f7 100644
--- a/src/qml/jsruntime/qv4arraybuffer_p.h
+++ b/src/qml/jsruntime/qv4arraybuffer_p.h
@@ -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);
diff --git a/src/qml/jsruntime/qv4arraydata.cpp b/src/qml/jsruntime/qv4arraydata.cpp
index 62ece57c41..f4afe46fcb 100644
--- a/src/qml/jsruntime/qv4arraydata.cpp
+++ b/src/qml/jsruntime/qv4arraydata.cpp
@@ -682,7 +682,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/qv4arrayobject.cpp b/src/qml/jsruntime/qv4arrayobject.cpp
index 3521673461..4d15c6c137 100644
--- a/src/qml/jsruntime/qv4arrayobject.cpp
+++ b/src/qml/jsruntime/qv4arrayobject.cpp
@@ -54,18 +54,19 @@ Heap::ArrayCtor::ArrayCtor(QV4::ExecutionContext *scope)
{
}
-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);
}
@@ -704,7 +706,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;
@@ -716,8 +717,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);
}
@@ -740,7 +741,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);
@@ -749,8 +749,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);
@@ -782,7 +782,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();
}
@@ -804,7 +804,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;
@@ -818,8 +817,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();
}
@@ -840,7 +839,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;
@@ -856,8 +854,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;
}
@@ -879,17 +877,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)
@@ -898,21 +895,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)
@@ -935,16 +932,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)
@@ -959,13 +955,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..f49ed76b02 100644
--- a/src/qml/jsruntime/qv4arrayobject_p.h
+++ b/src/qml/jsruntime/qv4arrayobject_p.h
@@ -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..57c54e15c4 100644
--- a/src/qml/jsruntime/qv4booleanobject.cpp
+++ b/src/qml/jsruntime/qv4booleanobject.cpp
@@ -50,17 +50,16 @@ Heap::BooleanCtor::BooleanCtor(QV4::ExecutionContext *scope)
{
}
-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..17543e33e0 100644
--- a/src/qml/jsruntime/qv4booleanobject_p.h
+++ b/src/qml/jsruntime/qv4booleanobject_p.h
@@ -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/qv4dataview.cpp b/src/qml/jsruntime/qv4dataview.cpp
index f296ffd71e..fac3d5316c 100644
--- a/src/qml/jsruntime/qv4dataview.cpp
+++ b/src/qml/jsruntime/qv4dataview.cpp
@@ -54,32 +54,34 @@ Heap::DataViewCtor::DataViewCtor(QV4::ExecutionContext *scope)
{
}
-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..f996a4c2f1 100644
--- a/src/qml/jsruntime/qv4dataview_p.h
+++ b/src/qml/jsruntime/qv4dataview_p.h
@@ -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..df648ba9ee 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);
@@ -651,9 +651,8 @@ Heap::DateCtor::DateCtor(QV4::ExecutionContext *scope)
{
}
-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 +686,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 +1310,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..13e9e04040 100644
--- a/src/qml/jsruntime/qv4dateobject_p.h
+++ b/src/qml/jsruntime/qv4dateobject_p.h
@@ -104,8 +104,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 15a4ed2e56..f5bae4b258 100644
--- a/src/qml/jsruntime/qv4engine.cpp
+++ b/src/qml/jsruntime/qv4engine.cpp
@@ -136,8 +136,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 +143,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;
@@ -442,10 +444,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,17 +471,19 @@ 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()
{
@@ -910,8 +916,8 @@ ReturnedValue ExecutionEngine::throwError(const Value &value)
else
exceptionStackTrace = stackTrace();
- if (debugger)
- debugger->aboutToThrow();
+ if (QV4::Debugging::Debugger *debug = debugger())
+ debug->aboutToThrow();
return Encode::undefined();
}
@@ -969,7 +975,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 +999,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);
}
@@ -1137,7 +1143,7 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, int
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)!
@@ -1265,6 +1271,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:
@@ -1441,6 +1449,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:
@@ -1502,6 +1512,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.
@@ -1533,6 +1548,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;
diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h
index aeb2533d35..f42f727295 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);
@@ -475,9 +488,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 +589,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 +600,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..3763bf2613 100644
--- a/src/qml/jsruntime/qv4errorobject.cpp
+++ b/src/qml/jsruntime/qv4errorobject.cpp
@@ -160,13 +160,9 @@ ReturnedValue ErrorObject::method_get_stack(CallContext *ctx)
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);
- }
+ trace += frame.function + QLatin1Char('@') + frame.source;
+ if (frame.line >= 0)
+ trace += QLatin1Char(':') + QString::number(frame.line);
}
This->d()->stack = ctx->d()->engine->newString(trace);
}
@@ -243,16 +239,15 @@ Heap::ErrorCtor::ErrorCtor(QV4::ExecutionContext *scope, const QString &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)
@@ -260,11 +255,10 @@ Heap::EvalErrorCtor::EvalErrorCtor(QV4::ExecutionContext *scope)
{
}
-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)
@@ -272,11 +266,10 @@ Heap::RangeErrorCtor::RangeErrorCtor(QV4::ExecutionContext *scope)
{
}
-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)
@@ -284,11 +277,10 @@ Heap::ReferenceErrorCtor::ReferenceErrorCtor(QV4::ExecutionContext *scope)
{
}
-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)
@@ -296,11 +288,10 @@ Heap::SyntaxErrorCtor::SyntaxErrorCtor(QV4::ExecutionContext *scope)
{
}
-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)
@@ -308,11 +299,10 @@ Heap::TypeErrorCtor::TypeErrorCtor(QV4::ExecutionContext *scope)
{
}
-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)
@@ -320,11 +310,10 @@ Heap::URIErrorCtor::URIErrorCtor(QV4::ExecutionContext *scope)
{
}
-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..42a3d05d9f 100644
--- a/src/qml/jsruntime/qv4errorobject_p.h
+++ b/src/qml/jsruntime/qv4errorobject_p.h
@@ -221,50 +221,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..6b9c552350 100644
--- a/src/qml/jsruntime/qv4functionobject.cpp
+++ b/src/qml/jsruntime/qv4functionobject.cpp
@@ -166,14 +166,6 @@ ReturnedValue FunctionObject::name() const
return get(scope()->engine->id_name());
}
-
-ReturnedValue FunctionObject::newInstance()
-{
- Scope scope(internalClass()->engine);
- ScopedCallData callData(scope);
- return construct(callData);
-}
-
ReturnedValue FunctionObject::construct(const Managed *that, CallData *)
{
return static_cast<const FunctionObject *>(that)->engine()->throwTypeError();
@@ -251,9 +243,8 @@ Heap::FunctionCtor::FunctionCtor(QV4::ExecutionContext *scope)
}
// 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 +257,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 +271,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,13 +294,13 @@ 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);
@@ -376,7 +373,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 +391,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)
@@ -422,19 +422,20 @@ Heap::ScriptFunction::ScriptFunction(QV4::ExecutionContext *scope, Function *fun
{
}
-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,39 +443,37 @@ 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);
@@ -511,14 +510,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));
@@ -542,24 +542,24 @@ 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 (!scope.result.isManaged() || !scope.result.managed())
+ 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));
@@ -579,12 +579,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()
@@ -606,20 +604,21 @@ Heap::BuiltinFunction::BuiltinFunction(QV4::ExecutionContext *scope, QV4::String
{
}
-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);
@@ -630,18 +629,19 @@ ReturnedValue BuiltinFunction::call(const Managed *that, 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);
@@ -652,7 +652,7 @@ ReturnedValue IndexedBuiltinFunction::call(const Managed *that, CallData *callDa
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);
@@ -685,12 +685,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 +703,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 +723,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..be80b87873 100644
--- a/src/qml/jsruntime/qv4functionobject_p.h
+++ b/src/qml/jsruntime/qv4functionobject_p.h
@@ -145,8 +145,6 @@ 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 *);
@@ -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,20 +200,20 @@ 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,
@@ -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..b88e271a26 100644
--- a/src/qml/jsruntime/qv4globalobject.cpp
+++ b/src/qml/jsruntime/qv4globalobject.cpp
@@ -338,13 +338,14 @@ Heap::EvalFunction::EvalFunction(QV4::ExecutionContext *scope)
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..403639f8c1 100644
--- a/src/qml/jsruntime/qv4globalobject_p.h
+++ b/src/qml/jsruntime/qv4globalobject_p.h
@@ -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..c33d2cad11 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>
+#ifndef QT_NO_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)
+#ifndef QT_NO_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));
+#ifndef QT_NO_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;
+#ifndef QT_NO_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()
{
+#ifndef QT_NO_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 //QT_NO_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];
+#ifndef QT_NO_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..1750e6a7e1 100644
--- a/src/qml/jsruntime/qv4include_p.h
+++ b/src/qml/jsruntime/qv4include_p.h
@@ -62,7 +62,9 @@
QT_BEGIN_NAMESPACE
class QQmlEngine;
+#ifndef QT_NO_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;
+
+#ifndef QT_NO_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 af359bc0d3..bac45e18c8 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)
@@ -360,18 +346,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);
@@ -429,11 +403,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;
@@ -441,13 +417,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 a211d46153..5b665d8836 100644
--- a/src/qml/jsruntime/qv4jsonobject.cpp
+++ b/src/qml/jsruntime/qv4jsonobject.cpp
@@ -590,8 +590,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);
}
@@ -644,36 +643,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;
}
@@ -686,53 +688,53 @@ 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");
}
- o = value->asReturnedValue();
+ o = scope.result.asReturnedValue();
if (o) {
if (!o->as<FunctionObject>()) {
if (o->as<ArrayObject>()) {
@@ -807,10 +809,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('}');
}
@@ -853,10 +855,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;
diff --git a/src/qml/jsruntime/qv4lookup.cpp b/src/qml/jsruntime/qv4lookup.cpp
index 46e47307ef..42e561bc7c 100644
--- a/src/qml/jsruntime/qv4lookup.cpp
+++ b/src/qml/jsruntime/qv4lookup.cpp
@@ -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/qv4mathobject.cpp b/src/qml/jsruntime/qv4mathobject.cpp
index cb17583b98..9fe8224736 100644
--- a/src/qml/jsruntime/qv4mathobject.cpp
+++ b/src/qml/jsruntime/qv4mathobject.cpp
@@ -80,6 +80,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 +293,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..01e778d9ec 100644
--- a/src/qml/jsruntime/qv4mathobject_p.h
+++ b/src/qml/jsruntime/qv4mathobject_p.h
@@ -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/qv4numberobject.cpp b/src/qml/jsruntime/qv4numberobject.cpp
index ab3e03b183..d27eef1d79 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;
@@ -75,17 +76,16 @@ Heap::NumberCtor::NumberCtor(QV4::ExecutionContext *scope)
{
}
-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..2416165c78 100644
--- a/src/qml/jsruntime/qv4numberobject_p.h
+++ b/src/qml/jsruntime/qv4numberobject_p.h
@@ -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..aeb185049f 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;
}
diff --git a/src/qml/jsruntime/qv4object_p.h b/src/qml/jsruntime/qv4object_p.h
index e4431a9fc9..b68dbc27b7 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"
QT_BEGIN_NAMESPACE
@@ -112,8 +113,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);
@@ -324,14 +325,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);
diff --git a/src/qml/jsruntime/qv4objectproto.cpp b/src/qml/jsruntime/qv4objectproto.cpp
index 015294e48a..cfd76166f2 100644
--- a/src/qml/jsruntime/qv4objectproto.cpp
+++ b/src/qml/jsruntime/qv4objectproto.cpp
@@ -59,28 +59,30 @@ Heap::ObjectCtor::ObjectCtor(QV4::ExecutionContext *scope)
{
}
-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..47a37b196f 100644
--- a/src/qml/jsruntime/qv4objectproto_p.h
+++ b/src/qml/jsruntime/qv4objectproto_p.h
@@ -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_p.h b/src/qml/jsruntime/qv4persistent_p.h
index 5b1926468a..a0ade2068f 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 {
@@ -167,7 +167,7 @@ public:
Managed *asManaged() const {
if (!val)
return 0;
- return val->as<Managed>();
+ return val->managed();
}
template <typename T>
T *as() const {
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 462c4f3171..20179643fd 100644
--- a/src/qml/jsruntime/qv4qobjectwrapper.cpp
+++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp
@@ -54,6 +54,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 +75,7 @@
#include <QtCore/qvarlengtharray.h>
#include <QtCore/qtimer.h>
#include <QtCore/qatomic.h>
+#include <QtCore/qmetaobject.h>
QT_BEGIN_NAMESPACE
@@ -83,7 +85,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 +105,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)
@@ -360,7 +362,7 @@ ReturnedValue QObjectWrapper::getProperty(ExecutionEngine *engine, QObject *obje
Scope scope(engine);
QV4::ScopedValue rv(scope, LoadProperty<ReadAccessor::Accessor>(engine, object, *property, nptr));
- if (captureRequired) {
+ if (captureRequired && !property->isConstant()) {
if (property->accessors->notifier) {
if (n && ep->propertyCapture)
ep->propertyCapture->captureProperty(n);
@@ -463,7 +465,7 @@ 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 = QQmlBinding::create(property, value, object, callingQmlContext);
newBinding->setTarget(object, *property);
}
}
@@ -795,8 +797,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;
@@ -814,7 +816,7 @@ struct QObjectSlotDispatcher : public QtPrivate::QSlotObjectBase
}
}
- f->call(callData);
+ f->call(scope, callData);
if (scope.hasException()) {
QQmlError error = v4->catchExceptionAsQmlError();
if (error.description().isEmpty()) {
@@ -859,7 +861,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;
@@ -974,7 +976,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,
@@ -1098,6 +1100,7 @@ private:
// Pointers to allocData
union {
QString *qstringPtr;
+ QByteArray *qbyteArrayPtr;
QVariant *qvariantPtr;
QList<QObject *> *qlistPtr;
QJSValue *qjsValuePtr;
@@ -1112,7 +1115,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.
@@ -1124,7 +1128,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);
@@ -1135,14 +1139,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();
}
@@ -1219,6 +1223,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:
@@ -1345,7 +1356,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;
@@ -1360,9 +1372,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);
@@ -1375,11 +1391,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);
}
}
@@ -1398,7 +1414,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;
@@ -1413,11 +1430,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;
@@ -1448,7 +1465,7 @@ 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;
@@ -1477,6 +1494,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>()) {
@@ -1676,6 +1695,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)
@@ -1730,7 +1751,7 @@ 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));
+ Scoped<QObjectMethod> method(valueScope, valueScope.engine->memoryManager->allocObject<QObjectMethod>(scope));
method->d()->propertyCache = valueType->d()->propertyCache;
method->d()->index = index;
method->d()->valueTypeWrapper = valueType->d();
@@ -1754,17 +1775,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.data(),16);
if (d()->object) {
QString objectName = d()->object->objectName();
- if (!objectName.isEmpty()) {
- result += QLatin1String(", \"");
- result += objectName;
- result += QLatin1Char('\"');
- }
+ if (!objectName.isEmpty())
+ result += QLatin1String(", \"") + objectName + QLatin1Char('\"');
}
result += QLatin1Char(')');
@@ -1794,25 +1811,32 @@ QV4::ReturnedValue QObjectMethod::method_destroy(QV4::ExecutionContext *ctx, con
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();
+ if (!d()->valueTypeWrapper) {
+ scope.result = Encode::undefined();
+ return;
+ }
object = QQmlObjectOrGadget(d()->propertyCache.data(), d()->valueTypeWrapper->gadgetPtr);
}
@@ -1821,16 +1845,20 @@ ReturnedValue QObjectMethod::callInternal(CallData *callData) const
if (d()->propertyCache) {
QQmlPropertyData *data = d()->propertyCache->method(d()->index);
- if (!data)
- return QV4::Encode::undefined();
+ if (!data) {
+ scope.result = QV4::Encode::undefined();
+ return;
+ }
method = *data;
} else {
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();
@@ -1846,21 +1874,20 @@ ReturnedValue QObjectMethod::callInternal(CallData *callData) const
}
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);
- 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);
}
}
@@ -1875,6 +1902,169 @@ void QObjectMethod::markObjects(Heap::Base *that, ExecutionEngine *e)
DEFINE_OBJECT_VTABLE(QObjectMethod);
+
+Heap::QMetaObjectWrapper::QMetaObjectWrapper(const QMetaObject *metaObject)
+ : metaObject(metaObject) {
+
+}
+
+void Heap::QMetaObjectWrapper::ensureConstructorsCache() {
+
+ const int count = metaObject->constructorCount();
+ if (constructors.size() != count) {
+ constructors.clear();
+ constructors.reserve(count);
+
+ for (int i = 0; i < count; ++i) {
+ QMetaMethod method = metaObject->constructor(i);
+ QQmlPropertyData d;
+ d.load(method);
+ d.coreIndex = i;
+ constructors << d;
+ }
+ }
+}
+
+
+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()->constructors.isEmpty()) {
+ return v4->throwTypeError(QStringLiteral("%1 has no invokable constructor")
+ .arg(QLatin1String(mo->className())));
+ }
+
+ Scope scope(v4);
+ Scoped<QObjectWrapper> object(scope);
+
+ if (d()->constructors.size() == 1) {
+ object = callConstructor(d()->constructors.first(), 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::InvokeMetaMethod);
+}
+
+
+ReturnedValue QMetaObjectWrapper::callOverloadedConstructor(QV4::ExecutionEngine *engine, QV4::CallData *callArgs) const {
+ const int numberOfConstructors = d()->constructors.size();
+ 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.at(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.at(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);
+
+
+
+
Heap::QmlSignalHandler::QmlSignalHandler(QObject *object, int signalIndex)
: object(object)
, signalIndex(signalIndex)
diff --git a/src/qml/jsruntime/qv4qobjectwrapper_p.h b/src/qml/jsruntime/qv4qobjectwrapper_p.h
index f101f352f1..8ab5c96c3a 100644
--- a/src/qml/jsruntime/qv4qobjectwrapper_p.h
+++ b/src/qml/jsruntime/qv4qobjectwrapper_p.h
@@ -93,6 +93,14 @@ struct QObjectMethod : FunctionObject {
const QMetaObject *metaObject();
};
+struct QMetaObjectWrapper : FunctionObject {
+ QMetaObjectWrapper(const QMetaObject* metaObject);
+ const QMetaObject* metaObject;
+ QVector<QQmlPropertyData> constructors;
+
+ void ensureConstructorsCache();
+};
+
struct QmlSignalHandler : Object {
QmlSignalHandler(QObject *object, int signalIndex);
QPointer<QObject> object;
@@ -184,11 +192,33 @@ struct Q_QML_EXPORT QObjectMethod : public QV4::FunctionObject
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
diff --git a/src/qml/jsruntime/qv4regexpobject.cpp b/src/qml/jsruntime/qv4regexpobject.cpp
index 71e2bd1a06..ab00859b3b 100644
--- a/src/qml/jsruntime/qv4regexpobject.cpp
+++ b/src/qml/jsruntime/qv4regexpobject.cpp
@@ -185,8 +185,7 @@ QRegExp RegExpObject::toQRegExp() const
QString RegExpObject::toString() const
{
- QString result = QLatin1Char('/') + source();
- result += QLatin1Char('/');
+ QString result = QLatin1Char('/') + source() + QLatin1Char('/');
if (global())
result += QLatin1Char('g');
if (value()->ignoreCase)
@@ -232,34 +231,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 +273,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 +429,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..655e120c16 100644
--- a/src/qml/jsruntime/qv4regexpobject_p.h
+++ b/src/qml/jsruntime/qv4regexpobject_p.h
@@ -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 0e10f7699e..a69874cacb 100644
--- a/src/qml/jsruntime/qv4runtime.cpp
+++ b/src/qml/jsruntime/qv4runtime.cpp
@@ -252,11 +252,11 @@ 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)
@@ -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));
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));
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));
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));
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)
{
const QmlContext &c = static_cast<const QmlContext &>(context);
return QV4::QObjectWrapper::getProperty(engine, c.d()->qml->scopeObject, propertyIndex, false);
}
-ReturnedValue Runtime::getQmlContextObjectProperty(ExecutionEngine *engine, const Value &context, int propertyIndex)
+ReturnedValue Runtime::method_getQmlContextObjectProperty(ExecutionEngine *engine, const Value &context, int propertyIndex)
{
const QmlContext &c = static_cast<const QmlContext &>(context);
return QV4::QObjectWrapper::getProperty(engine, c.d()->qml->context->contextObject, propertyIndex, false);
}
-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,7 +1476,7 @@ 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);
@@ -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);
}
-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,6 +1541,289 @@ 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);
+}
+
+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();
+}
+
#endif // V4_BOOTSTRAP
} // namespace QV4
diff --git a/src/qml/jsruntime/qv4runtime_p.h b/src/qml/jsruntime/qv4runtime_p.h
index b63777e164..88c09ec5f9 100644
--- a/src/qml/jsruntime/qv4runtime_p.h
+++ b/src/qml/jsruntime/qv4runtime_p.h
@@ -55,10 +55,9 @@
#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;
@@ -100,156 +99,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 +136,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..cbc7a2ddc1
--- /dev/null
+++ b/src/qml/jsruntime/qv4runtimeapi_p.h
@@ -0,0 +1,315 @@
+/****************************************************************************
+**
+** 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;
+
+#define RUNTIME_METHOD(returnvalue, name, args) \
+ typedef returnvalue (*Method_##name)args; \
+ 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));
+ RUNTIME_METHOD(ReturnedValue, getQmlContextObjectProperty, (ExecutionEngine *engine, const Value &context, int propertyIndex));
+ 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 a6f7f8e0e7..44d171e087 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..a2e379ec1a 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);
};
}
-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,7 +83,6 @@ 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)
@@ -113,32 +94,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 +128,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 +148,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 +194,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) {
@@ -255,7 +230,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..e81bc3049c 100644
--- a/src/qml/jsruntime/qv4script_p.h
+++ b/src/qml/jsruntime/qv4script_p.h
@@ -74,7 +74,7 @@ struct ContextStateSaver {
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)
@@ -83,7 +83,7 @@ struct ContextStateSaver {
{
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)
@@ -126,7 +126,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 fa2409a85c..b15ac7eb5e 100644
--- a/src/qml/jsruntime/qv4sequenceobject.cpp
+++ b/src/qml/jsruntime/qv4sequenceobject.cpp
@@ -411,8 +411,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:
@@ -531,7 +531,7 @@ public:
Q_ASSERT(d()->object);
Q_ASSERT(d()->isReference);
int status = -1;
- QQmlPropertyPrivate::WriteFlags flags = QQmlPropertyPrivate::DontRemoveBinding;
+ QQmlPropertyData::WriteFlags flags = QQmlPropertyData::DontRemoveBinding;
void *a[] = { &d()->container, 0, &status, &flags };
QMetaObject::metacall(d()->object, QMetaObject::WriteProperty, d()->propertyIndex, a);
}
diff --git a/src/qml/jsruntime/qv4string.cpp b/src/qml/jsruntime/qv4string.cpp
index abef885249..3901514326 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)
@@ -198,31 +149,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 +169,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 +188,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..ff42ab6471 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,6 +70,7 @@ struct Q_QML_PRIVATE_EXPORT String : Base {
StringType_ArrayIndex
};
+#ifndef V4_BOOTSTRAP
String(MemoryManager *mm, const QString &text);
String(MemoryManager *mm, String *l, String *n);
~String() {
@@ -130,8 +131,8 @@ struct Q_QML_PRIVATE_EXPORT String : Base {
MemoryManager *mm;
private:
static void append(const String *data, QChar *ch);
-};
#endif
+};
}
@@ -183,8 +184,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 +213,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..1989f747e9 100644
--- a/src/qml/jsruntime/qv4stringobject.cpp
+++ b/src/qml/jsruntime/qv4stringobject.cpp
@@ -157,28 +157,24 @@ Heap::StringCtor::StringCtor(QV4::ExecutionContext *scope)
{
}
-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 +192,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 +202,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 +292,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 +337,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 +419,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 +436,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 +447,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 +462,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 +578,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 +602,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 +637,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());
@@ -716,6 +769,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..54425b0909 100644
--- a/src/qml/jsruntime/qv4stringobject_p.h
+++ b/src/qml/jsruntime/qv4stringobject_p.h
@@ -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..ae5ebcad1b 100644
--- a/src/qml/jsruntime/qv4typedarray.cpp
+++ b/src/qml/jsruntime/qv4typedarray.cpp
@@ -208,30 +208,34 @@ Heap::TypedArrayCtor::TypedArrayCtor(QV4::ExecutionContext *scope, TypedArray::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,19 +346,21 @@ 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)
@@ -582,5 +604,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..d96027b96a 100644
--- a/src/qml/jsruntime/qv4typedarray_p.h
+++ b/src/qml/jsruntime/qv4typedarray_p.h
@@ -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);
};
diff --git a/src/qml/jsruntime/qv4value_p.h b/src/qml/jsruntime/qv4value_p.h
index b2d64213f5..dae4d11767 100644
--- a/src/qml/jsruntime/qv4value_p.h
+++ b/src/qml/jsruntime/qv4value_p.h
@@ -322,16 +322,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)
@@ -381,7 +387,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() {
diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp
index ecbc243baa..b6dc7716ba 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;
@@ -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));
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));
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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/memory/qv4mm.cpp b/src/qml/memory/qv4mm.cpp
index fe94a11082..a6d7c3b1ed 100644
--- a/src/qml/memory/qv4mm.cpp
+++ b/src/qml/memory/qv4mm.cpp
@@ -60,6 +60,10 @@
#include <valgrind/memcheck.h>
#endif
+#ifdef V4_USE_HEAPTRACK
+#include <heaptrack_api.h>
+#endif
+
#if OS(QNX)
#include <sys/storage.h> // __tls()
#endif
@@ -68,7 +72,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;
@@ -79,9 +83,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;
}
@@ -94,9 +98,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;
}
@@ -108,29 +112,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;
@@ -147,24 +142,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));
@@ -174,8 +184,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();
}
}
@@ -198,7 +208,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());
@@ -226,7 +236,10 @@ bool sweepChunk(MemoryManager::Data::ChunkHeader *header, uint *itemsInUse, Exec
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.
@@ -289,10 +302,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;
@@ -324,14 +338,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;
@@ -347,19 +361,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];
@@ -374,6 +395,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);
}
@@ -430,7 +452,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 detroyObject on qobjectwrappers now, so that they can emit the destroyed
@@ -465,29 +487,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);
@@ -516,8 +542,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;
}
@@ -561,7 +587,7 @@ void MemoryManager::runGC()
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();
@@ -589,11 +615,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;
}
@@ -604,7 +630,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;
}
@@ -667,7 +693,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..026cbd8c6b 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 {
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 f4862a17a6..cc5023f12a 100644
--- a/src/qml/qml.pro
+++ b/src/qml/qml.pro
@@ -1,11 +1,17 @@
TARGET = QtQml
-QT = core-private network
+QT = core-private
+
+no_network {
+ DEFINES += QT_NO_NETWORK
+} else {
+ 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,7 +22,7 @@ exists("qqml_enable_gcov") {
LIBS_PRIVATE += -lgcov
}
-greaterThan(QT_GCC_MAJOR_VERSION, 5) {
+gcc:!intel_icc:greaterThan(QT_GCC_MAJOR_VERSION, 5) {
# Our code is bad. Temporary workaround.
QMAKE_CXXFLAGS += -fno-delete-null-pointer-checks
}
diff --git a/src/qml/qml/ftw/ftw.pri b/src/qml/qml/ftw/ftw.pri
index a671cfa12d..addf1d9ff8 100644
--- a/src/qml/qml/ftw/ftw.pri
+++ b/src/qml/qml/ftw/ftw.pri
@@ -11,7 +11,6 @@ HEADERS += \
$$PWD/qdeletewatcher_p.h \
$$PWD/qrecyclepool_p.h \
$$PWD/qflagpointer_p.h \
- $$PWD/qpointervaluepair_p.h \
$$PWD/qlazilyallocated_p.h \
$$PWD/qqmlnullablevalue_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..cc394b78cb 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 \
@@ -39,7 +38,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 +48,8 @@ SOURCES += \
$$PWD/qqmltypewrapper.cpp \
$$PWD/qqmlfileselector.cpp \
$$PWD/qqmlobjectcreator.cpp \
- $$PWD/qqmldirparser.cpp
+ $$PWD/qqmldirparser.cpp \
+ $$PWD/qqmldelayedcallqueue.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 \
@@ -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,8 @@ HEADERS += \
$$PWD/qqmlfileselector_p.h \
$$PWD/qqmlfileselector.h \
$$PWD/qqmlobjectcreator_p.h \
- $$PWD/qqmldirparser_p.h
+ $$PWD/qqmldirparser_p.h \
+ $$PWD/qqmldelayedcallqueue_p.h
include(ftw/ftw.pri)
include(v8/v8.pri)
diff --git a/src/qml/qml/qqmlabstractbinding_p.h b/src/qml/qml/qqmlabstractbinding_p.h
index 674178153a..0ccfae4610 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
@@ -83,7 +82,7 @@ public:
// 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) {}
@@ -113,8 +114,15 @@ protected:
inline void setNextBinding(QQmlAbstractBinding *);
int 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
index 55562a5307..e98663adfe 100644
--- a/src/qml/qml/qqmlaccessors_p.h
+++ b/src/qml/qml/qqmlaccessors_p.h
@@ -102,11 +102,15 @@ class QQmlNotifier;
} \
} while (false);
-#define QML_PRIVATE_ACCESSOR(clazz, cpptype, name, variable) \
+#define QML_PRIVATE_ACCESSOR(clazz, cpptype, name, variable, setter) \
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; \
+ } \
+ static void clazz ## _ ## name ## Write(QObject *o, void *rv) \
+ { \
+ static_cast<clazz *>(o)->setter(*static_cast<cpptype *>(rv)); \
}
#define QML_PROPERTY_NAME(name) #name, sizeof #name - 1
@@ -115,6 +119,7 @@ class QQmlAccessors
{
public:
void (*read)(QObject *object, void *output);
+ void (*write)(QObject *object, void *output);
void (*notifier)(QObject *object, QQmlNotifier **notifier);
};
diff --git a/src/qml/qml/qqmlapplicationengine.cpp b/src/qml/qml/qqmlapplicationengine.cpp
index 57cdd3f47f..8c342e0592 100644
--- a/src/qml/qml/qqmlapplicationengine.cpp
+++ b/src/qml/qml/qqmlapplicationengine.cpp
@@ -211,11 +211,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 +225,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..10d16a8a12 100644
--- a/src/qml/qml/qqmlbinding.cpp
+++ b/src/qml/qml/qqmlbinding.cpp
@@ -42,8 +42,8 @@
#include "qqml.h"
#include "qqmlcontext.h"
#include "qqmlinfo.h"
-#include "qqmlcompiler_p.h"
#include "qqmldata_p.h"
+#include "qqmlaccessors_p.h"
#include <private/qqmlprofiler_p.h>
#include <private/qqmlexpression_p.h>
#include <private/qqmlscriptstring_p.h>
@@ -57,27 +57,28 @@
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(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(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 +91,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(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(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(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 +157,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 +166,73 @@ void QQmlBinding::update(QQmlPropertyPrivate::WriteFlags flags)
if (QQmlData::wasDeleted(targetObject()))
return;
+ // Check for a binding update loop
+ if (Q_UNLIKELY(updatingFlag())) {
+ QQmlProperty p = QQmlPropertyPrivate::restore(targetObject(), getPropertyData(), 0);
+ QQmlAbstractBinding::printBindingLoopError(p);
+ return;
+ }
+ setUpdatingFlag(true);
+
+ DeleteWatcher watcher(this);
+
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);
- QQmlAbstractBinding::printBindingLoopError(p);
- return;
- }
+ if (canUseAccessor())
+ flags.setFlag(QQmlPropertyData::BypassInterceptor);
QQmlBindingProfiler prof(ep->profiler, this, f);
- setUpdatingFlag(true);
-
- QQmlJavaScriptExpression::DeleteWatcher watcher(this);
+ doUpdate(this, watcher, flags, scope, f);
- QQmlPropertyData pd = getPropertyData();
+ if (!watcher.wasDeleted())
+ setUpdatingFlag(false);
+}
- if (pd.propType == qMetaTypeId<QQmlBinding *>()) {
+// 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(QQmlBinding *binding, const DeleteWatcher &,
+ QQmlPropertyData::WriteFlags flags, QV4::Scope &,
+ const QV4::ScopedFunctionObject &) Q_DECL_OVERRIDE Q_DECL_FINAL
+ {
+ QQmlPropertyData pd = getPropertyData();
int idx = pd.coreIndex;
Q_ASSERT(idx != -1);
- QQmlBinding *t = this;
int status = -1;
- void *a[] = { &t, 0, &status, &flags };
+ void *a[] = { &binding, 0, &status, &flags };
QMetaObject::metacall(*m_target, QMetaObject::WriteProperty, idx, a);
+ }
+};
- } else {
+template<int StaticPropType>
+class GenericBinding: public QQmlBinding
+{
+protected:
+ void doUpdate(QQmlBinding *binding, 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);
+ binding->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()) {
@@ -214,59 +252,65 @@ void QQmlBinding::update(QQmlPropertyPrivate::WriteFlags flags)
ep->dereferenceScarceResources();
}
- if (!watcher.wasDeleted())
- setUpdatingFlag(false);
-}
-
-// 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)
-{
- 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);
+ // 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)
+ {
+ QQmlPropertyData pd = getPropertyData();
+ 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;
+ Q_ASSERT(targetObject());
+
+ if (Q_LIKELY(!isUndefined && !pd.isValueTypeVirtual())) {
+ 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, 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 QV4::Value &result,
+ bool isUndefined, QQmlPropertyData::WriteFlags flags)
+{
QQmlEngine *engine = context()->engine;
QV8Engine *v8engine = QQmlEnginePrivate::getV8Engine(engine);
@@ -378,11 +422,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()
@@ -409,11 +454,18 @@ 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())) {
+ int coreIndex = getPropertyCoreIndex();
+ if (coreIndex == -1 || interceptorMetaObject->intercepts(coreIndex))
+ m_nextBinding.clearFlag2();
+ }
+
if (e)
update(flags);
}
@@ -509,4 +561,39 @@ QQmlPropertyData QQmlBinding::getPropertyData() const
return d;
}
+Q_ALWAYS_INLINE int QQmlBinding::getPropertyCoreIndex() const
+{
+ int coreIndex;
+ int valueTypeIndex = QQmlPropertyData::decodeValueTypePropertyIndex(m_targetIndex, &coreIndex);
+ if (valueTypeIndex != -1) {
+ return -1;
+ } else {
+ return coreIndex;
+ }
+}
+
+QQmlBinding *QQmlBinding::newBinding(const QQmlPropertyData *property)
+{
+ 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>;
+ }
+}
+
QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmlbinding_p.h b/src/qml/qml/qqmlbinding_p.h
index 7814b9dee8..0c797d7df2 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,12 +72,12 @@ 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 &);
@@ -86,13 +85,11 @@ public:
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,28 @@ public:
QVariant evaluate();
- virtual QString expressionIdentifier();
- virtual void expressionChanged();
+ QString expressionIdentifier() Q_DECL_OVERRIDE;
+ void expressionChanged() Q_DECL_OVERRIDE;
+
+protected:
+ virtual void doUpdate(QQmlBinding *binding, const DeleteWatcher &watcher,
+ QQmlPropertyData::WriteFlags flags, QV4::Scope &scope,
+ const QV4::ScopedFunctionObject &f) = 0;
+
+ QQmlPropertyData getPropertyData() const;
+ int getPropertyCoreIndex() const;
+ int getPropertyType() const;
+
+ bool slowWrite(const QQmlPropertyData &core, 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(const QQmlPropertyData *property);
};
bool QQmlBinding::updatingFlag() const
diff --git a/src/qml/qml/qqmlboundsignal.cpp b/src/qml/qml/qqmlboundsignal.cpp
index c6a7cde240..ef837183db 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>
@@ -83,9 +81,7 @@ 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 += 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 c64851cac5..9c7b4fe1c0 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"
@@ -335,14 +334,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();
@@ -356,10 +352,7 @@ void QQmlComponentPrivate::clear()
typeData = 0;
}
- if (cc) {
- cc->release();
- cc = 0;
- }
+ compilationUnit = nullptr;
}
/*!
@@ -393,8 +386,6 @@ QQmlComponent::~QQmlComponent()
d->typeData->unregisterCallback(d);
d->typeData->release();
}
- if (d->cc)
- d->cc->release();
}
/*!
@@ -422,7 +413,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;
@@ -512,11 +503,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);
}
/*!
@@ -531,10 +519,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);
}
@@ -546,11 +533,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;
- d->loadUrl(d->engine->baseUrl().resolved(QUrl::fromLocalFile(fileName)));
}
/*!
@@ -562,25 +546,22 @@ 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;
d->loadUrl(d->engine->baseUrl().resolved(QUrl::fromLocalFile(fileName)), mode);
}
/*!
\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;
}
@@ -873,7 +854,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;
@@ -893,11 +874,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;
@@ -914,7 +897,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;
}
@@ -1058,9 +1041,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);
@@ -1193,7 +1176,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);
@@ -1282,7 +1265,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();
@@ -1515,7 +1498,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);
}
}
@@ -1546,7 +1529,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..6621f48e20 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) {
@@ -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/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 ad2456a68d..a9f2acdb06 100644
--- a/src/qml/qml/qqmldata_p.h
+++ b/src/qml/qml/qqmldata_p.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.
@@ -179,10 +185,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;
@@ -223,7 +229,7 @@ public:
static inline void flushPendingBinding(QObject *, int coreIndex);
- static void ensurePropertyCache(QJSEngine *engine, QObject *object);
+ static QQmlPropertyCache *ensurePropertyCache(QJSEngine *engine, QObject *object);
private:
// For attachedProperties
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 28f8bf4855..e4e7530af7 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>
+
+#ifndef QT_NO_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>
@@ -94,17 +91,15 @@
#include <private/qqmlinstantiator_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)
@@ -182,6 +177,7 @@ 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");
@@ -493,6 +489,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
@@ -531,6 +531,7 @@ The following functions are also on the Qt object.
\li application.active
\li application.state
\li application.layoutDirection
+ \li application.font
\endlist
*/
@@ -601,12 +602,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),
+#ifndef QT_NO_NETWORK
+ networkAccessManager(0), networkAccessManagerFactory(0),
+#endif
+ urlInterceptor(0), scarceResourcesRefCount(0), importDatabase(e), typeLoader(e),
uniqueId(1), incubatorCount(0), incubationController(0)
{
}
@@ -614,7 +620,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);
@@ -635,7 +640,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
@@ -643,12 +648,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)
@@ -677,7 +679,7 @@ QQmlData::QQmlData()
hasTaintedV4Object(false), isQueuedForDeletion(false), rootObjectInCreation(false),
hasInterceptorMetaObject(false), hasVMEMetaObject(false), parentFrozen(false), bindingBitsSize(0), bindingBits(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();
@@ -845,8 +847,8 @@ void QQmlData::flushPendingBindingImpl(int coreIndex)
b = b->nextBinding();
if (b && b->targetPropertyIndex() == coreIndex)
- b->setEnabled(true, QQmlPropertyPrivate::BypassInterceptor |
- QQmlPropertyPrivate::DontRemoveBinding);
+ b->setEnabled(true, QQmlPropertyData::BypassInterceptor |
+ QQmlPropertyData::DontRemoveBinding);
}
bool QQmlEnginePrivate::baseModulesUninitialized = true;
@@ -1066,7 +1068,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);
+ }
+}
+#ifndef QT_NO_NETWORK
/*!
Sets the \a factory to use for creating QNetworkAccessManager(s).
@@ -1095,16 +1107,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);
@@ -1143,6 +1145,7 @@ QNetworkAccessManager *QQmlEngine::networkAccessManager() const
Q_D(const QQmlEngine);
return d->getNetworkAccessManager();
}
+#endif // QT_NO_NETWORK
/*!
@@ -1405,7 +1408,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;
@@ -1636,13 +1639,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;
}
@@ -1665,7 +1668,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 {
@@ -1801,14 +1804,16 @@ void QQmlData::setPendingBindingBit(QObject *obj, int coreIndex)
QQmlData_setBit(this, obj, coreIndex * 2 + 1);
}
-void QQmlData::ensurePropertyCache(QJSEngine *engine, QObject *object)
+QQmlPropertyCache *QQmlData::ensurePropertyCache(QJSEngine *engine, QObject *object)
{
Q_ASSERT(engine);
QQmlData *ddata = QQmlData::get(object, /*create*/true);
- if (ddata->propertyCache)
- return;
- ddata->propertyCache = QJSEnginePrivate::get(engine)->cache(object);
- if (ddata->propertyCache) ddata->propertyCache->addref();
+ if (!ddata->propertyCache) {
+ ddata->propertyCache = QJSEnginePrivate::get(engine)->cache(object);
+ if (ddata->propertyCache)
+ ddata->propertyCache->addref();
+ }
+ return ddata->propertyCache;
}
void QQmlEnginePrivate::sendQuit()
@@ -2219,9 +2224,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 +2236,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 +2248,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 +2261,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 +2271,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 +2291,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 +2325,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 +2357,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 +2365,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..132af78f80 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;
+#ifndef QT_NO_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);
+#ifndef QT_NO_NETWORK
void setNetworkAccessManagerFactory(QQmlNetworkAccessManagerFactory *);
QQmlNetworkAccessManagerFactory *networkAccessManagerFactory() const;
QNetworkAccessManager *networkAccessManager() const;
+#endif
void setUrlInterceptor(QQmlAbstractUrlInterceptor* urlInterceptor);
QQmlAbstractUrlInterceptor* urlInterceptor() const;
diff --git a/src/qml/qml/qqmlengine_p.h b/src/qml/qml/qqmlengine_p.h
index 003cfa0112..949060f395 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;
-
+#ifndef QT_NO_NETWORK
QNetworkAccessManager *createNetworkAccessManager(QObject *parent) const;
QNetworkAccessManager *getNetworkAccessManager() const;
mutable QNetworkAccessManager *networkAccessManager;
mutable QQmlNetworkAccessManagerFactory *networkAccessManagerFactory;
-
+#endif
QHash<QString,QSharedPointer<QQmlImageProviderBase> > imageProviders;
QQmlAbstractUrlInterceptor* urlInterceptor;
@@ -216,8 +220,8 @@ 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;
@@ -260,7 +264,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/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..741c25e206 100644
--- a/src/qml/qml/qqmlexpression_p.h
+++ b/src/qml/qml/qqmlexpression_p.h
@@ -57,7 +57,6 @@
#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 +76,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..4769402855 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;
+
+#ifndef QT_NO_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;
-
+#ifndef QT_NO_NETWORK
QQmlFileNetworkReply *reply;
+#endif
};
+#ifndef QT_NO_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 // QT_NO_NETWORK
QQmlFilePrivate::QQmlFilePrivate()
-: error(None), reply(0)
+: error(None)
+#ifndef QT_NO_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()
{
+#ifndef QT_NO_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;
+#ifndef QT_NO_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 {
+#ifndef QT_NO_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 {
+#ifndef QT_NO_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();
}
+#ifndef QT_NO_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..3dd683a2cd 100644
--- a/src/qml/qml/qqmlfile.h
+++ b/src/qml/qml/qqmlfile.h
@@ -80,10 +80,12 @@ public:
void clear();
void clear(QObject *);
+#ifndef QT_NO_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 b51c78b8d4..9393f74a8d 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()));
@@ -913,7 +913,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 +921,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;
@@ -1702,10 +1702,7 @@ QString QQmlImportDatabase::resolvePlugin(QQmlTypeLoader *typeLoader,
resolvedPath += Slash;
foreach (const QString &suffix, suffixes) {
- QString pluginFileName = prefix;
-
- pluginFileName += baseName;
- pluginFileName += suffix;
+ QString pluginFileName = prefix + baseName + suffix;
QString absolutePath = typeLoader->absoluteFilePath(resolvedPath + pluginFileName);
if (!absolutePath.isEmpty())
@@ -1820,7 +1817,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
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 8ba4b5eba1..5d96240e5b 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,12 @@ QQmlJavaScriptExpression::~QQmlJavaScriptExpression()
void QQmlJavaScriptExpression::setNotifyOnValueChanged(bool v)
{
activeGuards.setFlagValue(v);
- if (!v) clearGuards();
+ if (!v) clearActiveGuards();
}
void QQmlJavaScriptExpression::resetNotifyOnValueChanged()
{
- clearGuards();
+ clearActiveGuards();
}
void QQmlJavaScriptExpression::setContext(QQmlContextData *context)
@@ -147,16 +148,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 +158,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 +178,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 +186,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 +196,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 +213,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 +235,17 @@ void QQmlPropertyCapture::captureProperty(QQmlNotifier *n)
g->connect(n);
}
- expression->activeGuards.prepend(g);
+ if (duration == OnlyOnce)
+ 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 +284,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 +304,40 @@ void QQmlPropertyCapture::registerQmlDependencies(QV4::ExecutionEngine *engine,
if (!capture)
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 +421,18 @@ void QQmlJavaScriptExpression::createQmlBinding(QQmlContextData *ctxt, QObject *
}
-void QQmlJavaScriptExpression::clearGuards()
+void QQmlJavaScriptExpression::clearActiveGuards()
{
while (QQmlJavaScriptExpressionGuard *g = activeGuards.takeFirst())
g->Delete();
}
+void QQmlJavaScriptExpression::clearPermanentGuards()
+{
+ 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..a5c7a80153 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,
@@ -159,10 +158,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 +180,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/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp
index 7b129e2b57..271b4f1b31 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);
}
@@ -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..c94db8e168 100644
--- a/src/qml/qml/qqmlnetworkaccessmanagerfactory.cpp
+++ b/src/qml/qml/qqmlnetworkaccessmanagerfactory.cpp
@@ -41,6 +41,8 @@
QT_BEGIN_NAMESPACE
+#ifndef QT_NO_NETWORK
+
/*!
\class QQmlNetworkAccessManagerFactory
\since 5.0
@@ -101,4 +103,6 @@ QQmlNetworkAccessManagerFactory::~QQmlNetworkAccessManagerFactory()
implementation of this method is reentrant.
*/
+#endif //QT_NO_NETWORK
+
QT_END_NAMESPACE
diff --git a/src/qml/qml/qqmlnetworkaccessmanagerfactory.h b/src/qml/qml/qqmlnetworkaccessmanagerfactory.h
index 8e3b94fad3..ba3561b9f4 100644
--- a/src/qml/qml/qqmlnetworkaccessmanagerfactory.h
+++ b/src/qml/qml/qqmlnetworkaccessmanagerfactory.h
@@ -45,6 +45,7 @@
QT_BEGIN_NAMESPACE
+#ifndef QT_NO_NETWORK
class QNetworkAccessManager;
class Q_QML_EXPORT QQmlNetworkAccessManagerFactory
@@ -55,6 +56,8 @@ public:
};
+#endif //QT_NO_NETWORK
+
QT_END_NAMESPACE
#endif // QQMLNETWORKACCESSMANAGERFACTORY_H
diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp
index 831965f36a..3000f3e695 100644
--- a/src/qml/qml/qqmlobjectcreator.cpp
+++ b/src/qml/qml/qqmlobjectcreator.cpp
@@ -54,6 +54,7 @@
#include <private/qqmlscriptstring_p.h>
#include <private/qqmlpropertyvalueinterceptor_p.h>
#include <private/qqmlvaluetypeproxybinding_p.h>
+#include <private/qqmlaccessors_p.h>
QT_USE_NAMESPACE
@@ -69,12 +70,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)
, activeVMEDataForRootContext(activeVMEDataForRootContext)
{
init(parentContext);
@@ -82,24 +82,26 @@ QQmlObjectCreator::QQmlObjectCreator(QQmlContextData *parentContext, QQmlCompile
sharedState = new QQmlObjectCreatorSharedState;
topLevelCreator = true;
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)
, activeVMEDataForRootContext(0)
{
init(parentContext);
@@ -114,10 +116,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 +162,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 +184,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 +202,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 +239,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 +258,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,11 +278,7 @@ 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;
@@ -317,16 +306,14 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
} 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));
} 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) {
@@ -334,8 +321,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
_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);
@@ -344,8 +330,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
_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 +338,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 +358,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 +377,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: {
@@ -422,8 +399,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
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);
+ property->writeProperty(_qobject, &buffer, propertyWriteFlags);
}
}
break;
@@ -432,16 +408,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 +428,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 +436,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 +488,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 +500,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 +513,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 +526,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:
@@ -576,39 +538,34 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
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> >()) {
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> >()) {
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> >()) {
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> >()) {
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>()) {
QJSValue value;
@@ -623,8 +580,7 @@ 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;
}
@@ -640,8 +596,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
break;
}
- argv[0] = value.data();
- QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex, argv);
+ property->writeProperty(_qobject, value.data(), propertyWriteFlags);
}
break;
}
@@ -658,14 +613,14 @@ 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) {
@@ -673,7 +628,7 @@ void QQmlObjectCreator::setupBindings(const QBitArray &bindingsToSkip)
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);
}
@@ -691,7 +646,7 @@ void QQmlObjectCreator::setupBindings(const QBitArray &bindingsToSkip)
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) {
@@ -709,9 +664,17 @@ 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()) {
@@ -736,7 +699,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) {
@@ -763,8 +726,8 @@ 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);
@@ -816,7 +779,7 @@ 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;
}
@@ -828,7 +791,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con
QQmlPropertyPrivate::removeBinding(_bindingTarget, 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());
@@ -842,8 +805,6 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con
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,
@@ -853,6 +814,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con
if (_valueTypeProperty)
targetCorePropertyData = QQmlPropertyPrivate::saveValueType(*_valueTypeProperty, _qobject->metaObject(), property->coreIndex, engine);
+ QQmlBinding *qmlBinding = QQmlBinding::create(&targetCorePropertyData, function, _scopeObject, context);
sharedState->allCreatedBindings.push(QQmlAbstractBinding::Ptr(qmlBinding));
qmlBinding->setTarget(_bindingTarget, targetCorePropertyData);
@@ -943,8 +905,8 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con
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 };
@@ -1009,9 +971,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);
@@ -1026,18 +988,17 @@ void QQmlObjectCreator::setupFunctions()
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 +1011,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,23 +1023,22 @@ 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()));
+ compilationUnit, obj, type->qmlTypeName(), context->url()));
instance = type->create();
if (!instance) {
recordError(obj->location, tr("Unable to create object of type %1").arg(stringAt(obj->inheritedTypeNameIndex)));
@@ -1097,17 +1059,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 +1115,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 +1159,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);
@@ -1224,8 +1184,8 @@ QQmlContextData *QQmlObjectCreator::finalize(QQmlInstantiationInterrupt &interru
QQmlData *data = QQmlData::get(b->targetObject());
Q_ASSERT(data);
data->clearPendingBindingBit(b->targetPropertyIndex());
- b->setEnabled(true, QQmlPropertyPrivate::BypassInterceptor |
- QQmlPropertyPrivate::DontRemoveBinding);
+ b->setEnabled(true, QQmlPropertyData::BypassInterceptor |
+ QQmlPropertyData::DontRemoveBinding);
if (watcher.hasRecursed() || interrupt.shouldInterrupt())
return 0;
@@ -1294,7 +1254,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 +1269,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 +1285,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/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 df2ff05de1..b3eb0a5619 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"
@@ -317,8 +317,7 @@ void QQmlPropertyPrivate::initProperty(QObject *obj, const QString &name)
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
}
@@ -859,7 +858,7 @@ 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);
@@ -1035,15 +1034,13 @@ QVariant QQmlPropertyPrivate::readValueProperty()
} else if (core.isQList()) {
QQmlListProperty<QObject> prop;
- void *args[] = { &prop, 0 };
- QMetaObject::metacall(object, QMetaObject::ReadProperty, core.coreIndex, args);
+ 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 {
@@ -1060,7 +1057,7 @@ QVariant QQmlPropertyPrivate::readValueProperty()
value = QVariant(core.propType, (void*)0);
args[0] = value.data();
}
- QMetaObject::metacall(object, QMetaObject::ReadProperty, core.coreIndex, args);
+ core.readPropertyWithArgs(object, args);
if (core.propType != QMetaType::QVariant && args[0] != value.data())
return QVariant((QVariant::Type)core.propType, args[0]);
@@ -1148,7 +1145,7 @@ 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);
}
@@ -1157,10 +1154,10 @@ bool
QQmlPropertyPrivate::writeValueProperty(QObject *object,
const QQmlPropertyData &core,
const QVariant &value,
- QQmlContextData *context, WriteFlags flags)
+ QQmlContextData *context,QQmlPropertyData::WriteFlags flags)
{
// Remove any existing bindings on this property
- if (!(flags & DontRemoveBinding) && object)
+ if (!(flags & QQmlPropertyData::DontRemoveBinding) && object)
removeBinding(object, core.encodedIndex());
bool rv = false;
@@ -1190,145 +1187,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);
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));
} else {
QQmlType *type = QQmlMetaType::qmlType(QQmlMetaType::listType(property.propType));
- if (!type) return false;
+ 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);
@@ -1368,7 +1371,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());
@@ -1411,8 +1415,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;
}
@@ -1428,10 +1431,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();
}
/*!
@@ -1513,7 +1515,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;
diff --git a/src/qml/qml/qqmlproperty_p.h b/src/qml/qml/qqmlproperty_p.h
index 58fea9c239..9398c74621 100644
--- a/src/qml/qml/qqmlproperty_p.h
+++ b/src/qml/qml/qqmlproperty_p.h
@@ -68,13 +68,6 @@ 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;
@@ -97,7 +90,7 @@ 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,
@@ -105,9 +98,9 @@ public:
static bool writeValueProperty(QObject *,
const QQmlPropertyData &,
const QVariant &, QQmlContextData *,
- WriteFlags flags = 0);
+ QQmlPropertyData::WriteFlags flags = 0);
static bool write(QObject *, const QQmlPropertyData &, const QVariant &,
- QQmlContextData *, WriteFlags flags = 0);
+ QQmlContextData *, QQmlPropertyData::WriteFlags flags = 0);
static void findAliasTarget(QObject *, int, QObject **, int *);
enum BindingFlag {
@@ -116,7 +109,7 @@ 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);
@@ -144,7 +137,7 @@ public:
QQmlBoundSignalExpression *);
static void takeSignalExpression(const QQmlProperty &that,
QQmlBoundSignalExpression *);
- static bool write(const QQmlProperty &that, const QVariant &, WriteFlags);
+ static bool write(const QQmlProperty &that, const QVariant &, QQmlPropertyData::WriteFlags);
static int valueTypeCoreIndex(const QQmlProperty &that);
static int bindingIndex(const QQmlProperty &that);
static int bindingIndex(const QQmlPropertyData &that);
@@ -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 0522aa93ee..322e519706 100644
--- a/src/qml/qml/qqmlpropertycache.cpp
+++ b/src/qml/qml/qqmlpropertycache.cpp
@@ -184,10 +184,16 @@ void QQmlPropertyData::load(const QMetaMethod &m)
{
coreIndex = m.methodIndex();
arguments = 0;
+
+ propType = m.returnType();
+
flags |= IsFunction;
if (m.methodType() == QMetaMethod::Signal)
flags |= IsSignal;
- propType = m.returnType();
+ else if (m.methodType() == QMetaMethod::Constructor) {
+ flags |= IsConstructor;
+ propType = QMetaType::QObjectStar;
+ }
if (m.parameterCount()) {
flags |= HasArguments;
@@ -206,11 +212,15 @@ void QQmlPropertyData::load(const QMetaMethod &m)
void QQmlPropertyData::lazyLoad(const QMetaMethod &m)
{
coreIndex = m.methodIndex();
+ propType = QMetaType::Void;
arguments = 0;
flags |= IsFunction;
if (m.methodType() == QMetaMethod::Signal)
flags |= IsSignal;
- propType = QMetaType::Void;
+ else if (m.methodType() == QMetaMethod::Constructor) {
+ flags |= IsConstructor;
+ propType = QMetaType::QObjectStar;
+ }
const char *returnType = m.typeName();
if (!returnType)
@@ -220,9 +230,10 @@ void QQmlPropertyData::lazyLoad(const QMetaMethod &m)
flags |= NotFullyResolved;
}
- if (m.parameterCount()) {
+ const int paramCount = m.parameterCount();
+ if (paramCount) {
flags |= HasArguments;
- if ((m.parameterCount() == 1) && (m.parameterTypes().first() == "QQmlV4Function*")) {
+ if ((paramCount == 1) && (m.parameterTypes().first() == "QQmlV4Function*")) {
flags |= IsV4Function;
}
}
@@ -249,11 +260,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)
+ : QQmlPropertyCache(e)
{
- Q_ASSERT(engine);
Q_ASSERT(metaObject);
update(metaObject);
@@ -416,12 +424,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,34 +439,19 @@ 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 *
@@ -523,11 +510,12 @@ void QQmlPropertyCache::append(const QMetaObject *metaObject,
bool hasFastProperty = false;
for (int ii = 0; ii < classInfoCount; ++ii) {
int idx = ii + classInfoOffset;
-
- if (0 == qstrcmp(metaObject->classInfo(idx).name(), "qt_HasQmlAccessors")) {
+ QMetaClassInfo mci = metaObject->classInfo(idx);
+ const char *name = mci.name();
+ if (0 == qstrcmp(name, "qt_HasQmlAccessors")) {
hasFastProperty = true;
- } else if (0 == qstrcmp(metaObject->classInfo(idx).name(), "DefaultProperty")) {
- _defaultPropertyName = QString::fromUtf8(metaObject->classInfo(idx).value());
+ } else if (0 == qstrcmp(name, "DefaultProperty")) {
+ _defaultPropertyName = QString::fromUtf8(mci.value());
}
}
@@ -610,7 +598,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;
}
@@ -802,48 +790,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);
@@ -860,11 +806,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;
}
}
@@ -891,11 +837,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
@@ -1296,6 +1246,215 @@ void QQmlPropertyCache::toMetaObjectBuilder(QMetaObjectBuilder &builder)
}
}
+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) {
+ const QByteArrayDataPtr data = { const_cast<QByteArrayData*>(&mo.d.stringdata[i]) };
+ hash.addData(QByteArray(data));
+ }
+
+ return true;
+}
+
+QByteArray QQmlPropertyCache::checksum(bool *ok)
+{
+ if (!_checksum.isEmpty()) {
+ *ok = true;
+ 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().
@@ -1451,7 +1610,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);
@@ -1506,39 +1666,53 @@ int *QQmlMetaObject::methodParameterTypes(int index, QVarLengthArray<int, 9> &du
} 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);
@@ -1546,4 +1720,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 96dbc72f32..750537e707 100644
--- a/src/qml/qml/qqmlpropertycache_p.h
+++ b/src/qml/qml/qqmlpropertycache_p.h
@@ -61,9 +61,11 @@
#include <QtCore/qvector.h>
#include <private/qv4value_p.h>
+#include <private/qqmlaccessors_p.h>
QT_BEGIN_NAMESPACE
+class QCryptographicHash;
class QMetaProperty;
class QQmlEngine;
class QJSEngine;
@@ -72,7 +74,8 @@ 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
@@ -115,9 +118,10 @@ public:
IsSignalHandler = 0x00800000, // Function is a signal handler
IsOverload = 0x01000000, // Function is an overload of another function
IsCloned = 0x02000000, // The function was marked as cloned
+ IsConstructor = 0x04000000, // The function was marked is a constructor
// Internal QQmlPropertyCache flags
- NotFullyResolved = 0x04000000, // True if the type data is to be lazily resolved
+ NotFullyResolved = 0x08000000, // True if the type data is to be lazily resolved
// Flags that are set based on the propType field
PropTypeFlagMask = IsQObjectDerived | IsEnumType | IsQList | IsQmlBinding | IsQJSValue |
@@ -156,12 +160,15 @@ public:
bool isSignalHandler() const { return flags & IsSignalHandler; }
bool isOverload() const { return flags & IsOverload; }
bool isCloned() const { return flags & IsCloned; }
+ bool isConstructor() const { return flags & IsConstructor; }
bool hasOverride() const { return !(flags & IsValueTypeVirtual) &&
!(flags & HasAccessors) &&
overrideIndex >= 0; }
bool hasRevision() const { return !(flags & HasAccessors) && revision != 0; }
+ bool isFullyResolved() const { return !(flags & NotFullyResolved); }
+
// Returns -1 if not a value type virtual property
inline int getValueTypeCoreIndex() const;
@@ -223,6 +230,13 @@ Q_DECLARE_OPERATORS_FOR_FLAGS(QQmlPropertyRawData::Flags)
class QQmlPropertyData : public QQmlPropertyRawData
{
public:
+ enum WriteFlag {
+ BypassInterceptor = 0x01,
+ DontRemoveBinding = 0x02,
+ RemoveBindingOnAliasWrite = 0x04
+ };
+ Q_DECLARE_FLAGS(WriteFlags, WriteFlag)
+
inline QQmlPropertyData();
inline QQmlPropertyData(const QQmlPropertyRawData &);
@@ -236,6 +250,33 @@ 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 (hasAccessors()) {
+ accessors->read(target, args[0]);
+ } else {
+ QMetaObject::metacall(target, QMetaObject::ReadProperty, coreIndex, args);
+ }
+ }
+
+ bool writeProperty(QObject *target, void *value, WriteFlags flags) const
+ {
+ if (flags.testFlag(BypassInterceptor) && hasAccessors() && accessors->write) {
+ accessors->write(target, value);
+ } else {
+ int status = -1;
+ void *argv[] = { value, 0, &status, &flags };
+ QMetaObject::metacall(target, QMetaObject::WriteProperty, coreIndex, argv);
+ }
+ return true;
+ }
+
private:
friend class QQmlPropertyCache;
void lazyLoad(const QMetaProperty &);
@@ -326,6 +367,11 @@ public:
void toMetaObjectBuilder(QMetaObjectBuilder &);
+ 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();
@@ -333,7 +379,8 @@ 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;
@@ -394,6 +441,7 @@ private:
QByteArray _dynamicStringData;
QString _defaultPropertyName;
QQmlPropertyCacheMethodArguments *argumentsCache;
+ QByteArray _checksum;
};
// QQmlMetaObject serves as a wrapper around either QMetaObject or QQmlPropertyCache.
@@ -406,6 +454,8 @@ class QQmlEnginePrivate;
class Q_QML_EXPORT QQmlMetaObject
{
public:
+ typedef QVarLengthArray<int, 9> ArgTypeStorage;
+
inline QQmlMetaObject();
inline QQmlMetaObject(QObject *);
inline QQmlMetaObject(const QMetaObject *);
@@ -425,7 +475,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);
@@ -435,6 +486,9 @@ public:
protected:
QBiPointer<QQmlPropertyCache, const QMetaObject> _m;
+ int *methodParameterTypes(const QMetaMethod &method, ArgTypeStorage *argStorage,
+ QByteArray *unknownTypeError) const;
+
};
class QQmlObjectOrGadget: public QQmlMetaObject
@@ -453,6 +507,20 @@ 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;
};
QQmlPropertyData::QQmlPropertyData()
@@ -502,6 +570,21 @@ inline QQmlPropertyData *QQmlPropertyCache::ensureResolved(QQmlPropertyData *p)
return p;
}
+// Returns this property cache's metaObject. May be null if it hasn't been created yet.
+inline const QMetaObject *QQmlPropertyCache::metaObject() const
+{
+ return _metaObject;
+}
+
+// Returns the first C++ type's QMetaObject - that is, the first QMetaObject not created by
+// QML
+inline const QMetaObject *QQmlPropertyCache::firstCppMetaObject() const
+{
+ while (_parent && (_metaObject == 0 || _ownMetaObject))
+ return _parent->firstCppMetaObject();
+ return _metaObject;
+}
+
inline QQmlPropertyData *QQmlPropertyCache::property(int index) const
{
if (index < 0 || index >= (propertyIndexCacheStart + propertyIndexCache.count()))
@@ -514,6 +597,57 @@ 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
{
@@ -635,6 +769,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/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp
index 739a833a30..c54af43a6f 100644
--- a/src/qml/qml/qqmltypeloader.cpp
+++ b/src/qml/qml/qqmltypeloader.cpp
@@ -45,14 +45,16 @@
#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 <QtCore/qdir.h>
#include <QtCore/qfile.h>
+#include <QtCore/qdatetime.h>
#include <QtCore/qdebug.h>
#include <QtCore/qmutex.h>
#include <QtCore/qthread.h>
@@ -62,6 +64,8 @@
#include <QtCore/qwaitcondition.h>
#include <QtQml/qqmlextensioninterface.h>
+#include <functional>
+
#if defined (Q_OS_UNIX)
#include <sys/types.h>
#include <sys/stat.h>
@@ -98,6 +102,8 @@
#endif
DEFINE_BOOL_CONFIG_OPTION(dumpErrors, QML_DUMP_ERRORS);
+DEFINE_BOOL_CONFIG_OPTION(diskCache, QML_DISK_CACHE);
+DEFINE_BOOL_CONFIG_OPTION(forceDiskCacheRefresh, QML_FORCE_DISK_CACHE_REFRESH);
QT_BEGIN_NAMESPACE
@@ -110,8 +116,23 @@ namespace {
LockHolder(LockType *l) : lock(*l) { lock.lock(); }
~LockHolder() { lock.unlock(); }
};
+
+ struct DeferredCall
+ {
+ std::function<void()> callback;
+ ~DeferredCall() { callback(); }
+ };
+
+ template <typename Callback>
+ DeferredCall defer(Callback &&cb)
+ {
+ DeferredCall c;
+ c.callback = std::move(cb);
+ return c;
+ }
}
+#ifndef QT_NO_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 +152,7 @@ public slots:
private:
QQmlTypeLoader *l;
};
+#endif // QT_NO_NETWORK
class QQmlTypeLoaderThread : public QQmlThread
{
@@ -138,9 +160,10 @@ class QQmlTypeLoaderThread : public QQmlThread
public:
QQmlTypeLoaderThread(QQmlTypeLoader *loader);
+#ifndef QT_NO_NETWORK
QNetworkAccessManager *networkAccessManager() const;
QQmlTypeLoaderNetworkReplyProxy *networkReplyProxy() const;
-
+#endif // QT_NO_NETWORK
void load(QQmlDataBlob *b);
void loadAsync(QQmlDataBlob *b);
void loadWithStaticData(QQmlDataBlob *b, const QByteArray &);
@@ -163,11 +186,13 @@ private:
void initializeEngineMain(QQmlExtensionInterface *iface, const char *uri);
QQmlTypeLoader *m_loader;
+#ifndef QT_NO_NETWORK
mutable QNetworkAccessManager *m_networkAccessManager;
mutable QQmlTypeLoaderNetworkReplyProxy *m_networkReplyProxy;
+#endif // QT_NO_NETWORK
};
-
+#ifndef QT_NO_NETWORK
QQmlTypeLoaderNetworkReplyProxy::QQmlTypeLoaderNetworkReplyProxy(QQmlTypeLoader *l)
: l(l)
{
@@ -196,7 +221,7 @@ void QQmlTypeLoaderNetworkReplyProxy::manualFinished(QNetworkReply *reply)
l->networkReplyProgress(reply, replySize, replySize);
l->networkReplyFinished(reply);
}
-
+#endif // QT_NO_NETWORK
/*!
\class QQmlDataBlob
@@ -430,6 +455,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 +538,7 @@ void QQmlDataBlob::done()
{
}
+#ifndef QT_NO_NETWORK
/*!
Invoked if there is a network error while fetching this blob.
@@ -532,6 +591,7 @@ void QQmlDataBlob::networkError(QNetworkReply::NetworkError networkError)
setError(error);
}
+#endif // QT_NO_NETWORK
/*!
Called if \a blob, which was previously waited for, has an error.
@@ -730,12 +790,16 @@ void QQmlDataBlob::ThreadData::setProgress(quint8 v)
}
QQmlTypeLoaderThread::QQmlTypeLoaderThread(QQmlTypeLoader *loader)
-: m_loader(loader), m_networkAccessManager(0), m_networkReplyProxy(0)
+: m_loader(loader)
+#ifndef QT_NO_NETWORK
+, m_networkAccessManager(0), m_networkReplyProxy(0)
+#endif // QT_NO_NETWORK
{
// Do that after initializing all the members.
startup();
}
+#ifndef QT_NO_NETWORK
QNetworkAccessManager *QQmlTypeLoaderThread::networkAccessManager() const
{
Q_ASSERT(isThisThread());
@@ -753,6 +817,7 @@ QQmlTypeLoaderNetworkReplyProxy *QQmlTypeLoaderThread::networkReplyProxy() const
Q_ASSERT(m_networkReplyProxy); // Must call networkAccessManager() first
return m_networkReplyProxy;
}
+#endif // QT_NO_NETWORK
void QQmlTypeLoaderThread::load(QQmlDataBlob *b)
{
@@ -810,10 +875,12 @@ void QQmlTypeLoaderThread::initializeEngine(QQmlExtensionInterface *iface,
void QQmlTypeLoaderThread::shutdownThread()
{
+#ifndef QT_NO_NETWORK
delete m_networkAccessManager;
m_networkAccessManager = 0;
delete m_networkReplyProxy;
m_networkReplyProxy = 0;
+#endif // QT_NO_NETWORK
}
void QQmlTypeLoaderThread::loadThread(QQmlDataBlob *b)
@@ -899,12 +966,14 @@ void QQmlTypeLoader::invalidate()
m_thread = 0;
}
+#ifndef QT_NO_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 // QT_NO_NETWORK
}
void QQmlTypeLoader::lock()
@@ -1065,13 +1134,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 +1144,10 @@ void QQmlTypeLoader::loadThread(QQmlDataBlob *blob)
if (blob->m_data.isAsync())
m_thread->callDownloadProgressChanged(blob, 1.);
- setData(blob, &file);
+ setData(blob, fileName);
} else {
-
+#ifndef QT_NO_NETWORK
QNetworkReply *reply = m_thread->networkAccessManager()->get(QNetworkRequest(blob->m_url));
QQmlTypeLoaderNetworkReplyProxy *nrp = m_thread->networkReplyProxy();
blob->addref();
@@ -1099,14 +1164,15 @@ void QQmlTypeLoader::loadThread(QQmlDataBlob *blob)
#ifdef DATABLOB_DEBUG
qWarning("QQmlDataBlob: requested %s", qPrintable(blob->url().toString()));
-#endif
-
+#endif // DATABLOB_DEBUG
+#endif // QT_NO_NETWORK
}
}
#define DATALOADER_MAXIMUM_REDIRECT_RECURSION 16
#define TYPELOADER_MINIMUM_TRIM_THRESHOLD 64
+#ifndef QT_NO_NETWORK
void QQmlTypeLoader::networkReplyFinished(QNetworkReply *reply)
{
Q_ASSERT(m_thread->isThisThread());
@@ -1162,6 +1228,7 @@ void QQmlTypeLoader::networkReplyProgress(QNetworkReply *reply,
m_thread->callDownloadProgressChanged(blob, blob->m_data.progress());
}
}
+#endif // QT_NO_NETWORK
/*!
Return the QQmlEngine associated with this loader
@@ -1197,11 +1264,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 +1319,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)
{
}
@@ -1416,51 +1483,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) {
@@ -1950,7 +1972,9 @@ void QQmlTypeLoader::trimCache()
QList<TypeCache::Iterator> unneededTypes;
for (TypeCache::Iterator iter = m_typeCache.begin(), end = m_typeCache.end(); iter != end; ++iter) {
QQmlTypeData *typeData = iter.value();
- if (typeData->m_compiledData && typeData->count() == 1
+ // 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->isComplete() && typeData->m_compiledData && typeData->count() == 1
&& typeData->m_compiledData->count() == 1) {
// There are no live objects of this type
unneededTypes.append(iter);
@@ -1992,7 +2016,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)
{
}
@@ -2005,14 +2029,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
@@ -2020,19 +2041,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
+QV4::CompiledData::CompilationUnit *QQmlTypeData::compilationUnit() const
{
- return m_compositeSingletons;
-}
-
-QQmlCompiledData *QQmlTypeData::compiledData() const
-{
- return m_compiledData;
+ return m_compiledData.data();
}
void QQmlTypeData::registerCallback(TypeDataCallback *callback)
@@ -2048,10 +2059,120 @@ void QQmlTypeData::unregisterCallback(TypeDataCallback *callback)
Q_ASSERT(!m_callbacks.contains(callback));
}
+bool QQmlTypeData::tryLoadFromDiskCache()
+{
+ if (!diskCache())
+ return false;
+
+ if (forceDiskCacheRefresh())
+ 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)) {
+ qDebug() << "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::rebuildTypeAndPropertyCaches()
+{
+ Q_ASSERT(m_compiledData);
+
+ QQmlEnginePrivate * const engine = QQmlEnginePrivate::get(typeLoader()->engine());
+
+ {
+ QQmlCompileError error = buildTypeResolutionCaches(&m_compiledData->importCache, &m_compiledData->resolvedTypes);
+ if (error.isSet()) {
+ setError(error);
+ return;
+ }
+ }
+
+ {
+ 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()
{
+ auto cleanup = defer([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()) {
@@ -2063,16 +2184,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;
@@ -2082,11 +2204,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()) {
@@ -2100,27 +2223,81 @@ void QQmlTypeData::done()
error.setDescription(QQmlTypeLoader::tr("Type %1 unavailable").arg(typeName));
errors.prepend(error);
setError(errors);
+ 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();
+ if (!m_document.isNull()) {
+ // Compile component
+ compile();
+ } else {
+ rebuildTypeAndPropertyCaches();
+ }
- 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);
+ if (isError())
+ return;
+
+ {
+ QQmlEnginePrivate * const engine = QQmlEnginePrivate::get(typeLoader()->engine());
+ {
+ // Sanity check property bindings
+ QQmlPropertyValidator validator(engine, m_importCache, m_compiledData);
+ QVector<QQmlCompileError> errors = validator.validate();
+ if (!errors.isEmpty()) {
+ setError(errors);
+ return;
+ }
+ }
+
+ m_compiledData->finalize(engine);
}
- // Compile component
- if (!isError())
- compile();
+ {
+ 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_document.reset();
- m_implicitImport = 0;
+ m_compiledData->importCache->add(qualifier.toString(), scriptIndex, enclosingNamespace);
+ QQmlScriptData *scriptData = script.script->scriptData();
+ scriptData->addref();
+ m_compiledData->dependentScripts << scriptData;
+ }
+ }
}
void QQmlTypeData::completed()
@@ -2155,9 +2332,19 @@ bool QQmlTypeData::loadImplicitImport()
void QQmlTypeData::dataReceived(const Data &data)
{
- QString code = QString::fromUtf8(data.data(), data.size());
+ if (tryLoadFromDiskCache())
+ return;
+
+ qint64 sourceTimeStamp;
+ QString error;
+ QString code = QString::fromUtf8(data.readAll(&error, &sourceTimeStamp));
+ if (!error.isEmpty()) {
+ setError(error);
+ return;
+ }
QQmlEngine *qmlEngine = typeLoader()->engine();
- m_document.reset(new QmlIR::Document(QV8Engine::getV4(qmlEngine)->debugger != 0));
+ m_document.reset(new QmlIR::Document(QV8Engine::getV4(qmlEngine)->debugger() != 0));
+ m_document->jsModule.sourceTimeStamp = sourceTimeStamp;
QmlIR::IRBuilder compiler(QV8Engine::get(qmlEngine)->illegalNames());
if (!compiler.generateFromQml(code, finalUrlString(), m_document.data())) {
QList<QQmlError> errors;
@@ -2180,14 +2367,14 @@ void QQmlTypeData::dataReceived(const Data &data)
void QQmlTypeData::initializeFromCachedUnit(const QQmlPrivate::CachedQmlUnit *unit)
{
QQmlEngine *qmlEngine = typeLoader()->engine();
- m_document.reset(new QmlIR::Document(QV8Engine::getV4(qmlEngine)->debugger != 0));
+ m_document.reset(new QmlIR::Document(QV8Engine::getV4(qmlEngine)->debugger() != 0));
unit->loadIR(m_document.data(), unit);
continueLoadFromIR();
}
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
@@ -2200,14 +2387,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;
}
@@ -2228,14 +2415,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()
@@ -2280,20 +2459,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()
{
- Q_ASSERT(m_compiledData == 0);
+ Q_ASSERT(m_compiledData.isNull());
- m_compiledData = new QQmlCompiledData(typeLoader()->engine());
+ QQmlRefPointer<QQmlTypeNameCache> importCache;
+ QV4::CompiledData::ResolvedTypeReferenceMap resolvedTypeCache;
+ QQmlCompileError error = buildTypeResolutionCaches(&importCache, &resolvedTypeCache);
+ if (error.isSet()) {
+ setError(error);
+ return;
+ }
- QQmlTypeCompiler compiler(QQmlEnginePrivate::get(typeLoader()->engine()), m_compiledData, this, m_document.data());
- if (!compiler.compile()) {
+ QQmlTypeCompiler compiler(QQmlEnginePrivate::get(typeLoader()->engine()), this, m_document.data(), importCache, resolvedTypeCache);
+ m_compiledData = compiler.compile();
+ if (!m_compiledData) {
setError(compiler.compilationErrors());
- m_compiledData->release();
- m_compiledData = 0;
+ return;
+ }
+ if (diskCache() || forceDiskCacheRefresh()) {
+ QString errorString;
+ if (!m_compiledData->saveToDisk(&errorString)) {
+ qDebug() << "Error saving cached version of" << m_compiledData->url().toString() << "to disk:" << errorString;
+ }
}
}
@@ -2346,7 +2539,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
@@ -2416,6 +2609,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;
@@ -2537,10 +2781,6 @@ QV4::ReturnedValue QQmlScriptData::scriptValueForContext(QQmlContextData *parent
ctxt->importedScripts = effectiveCtxt->importedScripts;
}
- if (ctxt->imports) {
- ctxt->imports->addref();
- }
-
if (effectiveCtxt) {
ctxt->setParent(effectiveCtxt, true);
} else {
@@ -2628,10 +2868,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 (diskCache() && !forceDiskCacheRefresh()) {
+ QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit = v4->iselFactory->createUnitForLoading();
+ QString error;
+ if (unit->loadFromDisk(url(), v4->iselFactory.data(), &error)) {
+ initializeFromCompilationUnit(unit);
+ return;
+ } else {
+ qDebug() << "Error loading" << url().toString() << "from disk cache:" << error;
+ }
+ }
+
+
+ QmlIR::Document irUnit(v4->debugger() != 0);
+
+ 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;
@@ -2656,6 +2915,13 @@ void QQmlScriptBlob::dataReceived(const Data &data)
// The js unit owns the data and will free the qml unit.
unit->data = unitData;
+ if (diskCache() || forceDiskCacheRefresh()) {
+ QString errorString;
+ if (!unit->saveToDisk(&errorString)) {
+ qDebug() << "Error saving cached version of" << unit->url().toString() << "to disk:" << errorString;
+ }
+ }
+
initializeFromCompilationUnit(unit);
}
@@ -2666,8 +2932,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()) {
@@ -2679,17 +2948,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);
@@ -2783,7 +3050,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 *)
@@ -2791,6 +3063,30 @@ 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)
+ *sourceTimeStamp = QFileInfo(f).lastModified().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..ab6b046fcf 100644
--- a/src/qml/qml/qqmltypeloader_p.h
+++ b/src/qml/qml/qqmltypeloader_p.h
@@ -53,7 +53,9 @@
#include <QtCore/qobject.h>
#include <QtCore/qatomic.h>
+#ifndef QT_NO_NETWORK
#include <QtNetwork/qnetworkreply.h>
+#endif
#include <QtQml/qqmlerror.h>
#include <QtQml/qqmlengine.h>
#include <QtQml/qqmlfile.h>
@@ -75,11 +77,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 +131,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();
+#ifndef QT_NO_NETWORK
virtual void networkError(QNetworkReply::NetworkError);
+#endif
virtual void dependencyError(QQmlDataBlob *);
virtual void dependencyComplete(QQmlDataBlob *);
virtual void allDependenciesDone();
@@ -233,7 +233,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);
@@ -250,7 +249,6 @@ public:
virtual QString stringAt(int) const { return QString(); }
QQmlImports m_importCache;
- bool m_isSingleton;
QHash<const QV4::CompiledData::Import*, int> m_unresolvedImports;
QList<QQmlQmldirData *> m_qmldirs;
};
@@ -320,20 +318,24 @@ public:
private:
friend class QQmlDataBlob;
friend class QQmlTypeLoaderThread;
+#ifndef QT_NO_NETWORK
friend class QQmlTypeLoaderNetworkReplyProxy;
+#endif // QT_NO_NETWORK
void shutdownThread();
void loadThread(QQmlDataBlob *);
void loadWithStaticDataThread(QQmlDataBlob *, const QByteArray &);
void loadWithCachedUnitThread(QQmlDataBlob *blob, const QQmlPrivate::CachedQmlUnit *unit);
+#ifndef QT_NO_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 +364,9 @@ private:
QQmlEngine *m_engine;
QQmlTypeLoaderThread *m_thread;
+#ifndef QT_NO_NETWORK
NetworkReplies m_networkReplies;
+#endif
TypeCache m_typeCache;
int m_typeCacheTrimThreshold;
ScriptCache m_scriptCache;
@@ -381,6 +385,7 @@ private:
class Q_AUTOTEST_EXPORT QQmlTypeData : public QQmlTypeLoader::Blob
{
+ Q_DECLARE_TR_FUNCTIONS(QQmlTypeData)
public:
struct TypeReference
{
@@ -412,13 +417,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 +441,21 @@ protected:
virtual QString stringAt(int index) const;
private:
+ bool tryLoadFromDiskCache();
void continueLoadFromIR();
void resolveTypes();
+ QQmlCompileError buildTypeResolutionCaches(
+ QQmlRefPointer<QQmlTypeNameCache> *importCache,
+ QV4::CompiledData::ResolvedTypeReferenceMap *resolvedTypeCache
+ ) const;
void compile();
+ void rebuildTypeAndPropertyCaches();
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);
QScopedPointer<QmlIR::Document> m_document;
+ QV4::CompiledData::TypeReferenceMap m_typeReferences;
QList<ScriptReference> m_scripts;
@@ -458,11 +466,10 @@ private:
QHash<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 +579,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/qqmlvaluetype.cpp b/src/qml/qml/qqmlvaluetype.cpp
index 44fd47244d..8e87ec7f63 100644
--- a/src/qml/qml/qqmlvaluetype.cpp
+++ b/src/qml/qml/qqmlvaluetype.cpp
@@ -219,7 +219,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;
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..595cd01d05 100644
--- a/src/qml/qml/qqmlvaluetypeproxybinding.cpp
+++ b/src/qml/qml/qqmlvaluetypeproxybinding.cpp
@@ -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) {
diff --git a/src/qml/qml/qqmlvaluetypeproxybinding_p.h b/src/qml/qml/qqmlvaluetypeproxybinding_p.h
index de5acc2984..6e297bb3ea 100644
--- a/src/qml/qml/qqmlvaluetypeproxybinding_p.h
+++ b/src/qml/qml/qqmlvaluetypeproxybinding_p.h
@@ -63,7 +63,7 @@ public:
QQmlAbstractBinding *binding(int 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..b69eea61c5 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
@@ -245,6 +246,34 @@ PropertyAttributes QQmlValueTypeWrapper::query(const Managed *m, String *name)
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,8 +332,7 @@ 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('(');
+ 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) {
@@ -431,7 +459,7 @@ void QQmlValueTypeWrapper::put(Managed *m, String *name, const Value &value)
QV4::Scoped<QQmlBindingFunction> bindingFunction(scope, (const Value &)f);
bindingFunction->initBindingLocation();
- QQmlBinding *newBinding = new QQmlBinding(value, reference->d()->object, context);
+ QQmlBinding *newBinding = QQmlBinding::create(&cacheData, value, reference->d()->object, context);
newBinding->setTarget(reference->d()->object, cacheData);
QQmlPropertyPrivate::setBinding(newBinding);
return;
diff --git a/src/qml/qml/qqmlvaluetypewrapper_p.h b/src/qml/qml/qqmlvaluetypewrapper_p.h
index c2861f5bfa..94eeba366a 100644
--- a/src/qml/qml/qqmlvaluetypewrapper_p.h
+++ b/src/qml/qml/qqmlvaluetypewrapper_p.h
@@ -99,6 +99,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..d5001674ad 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,32 @@ 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;
+ QQmlPropertyData::decodeValueTypePropertyIndex(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();
@@ -184,7 +220,7 @@ 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)
@@ -242,7 +278,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 +314,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 +446,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 +459,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 +472,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 +485,7 @@ double QQmlVMEMetaObject::readPropertyAsDouble(int id)
QString QQmlVMEMetaObject::readPropertyAsString(int id)
{
- QV4::MemberData *md = propertiesAsMemberData();
+ QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
if (!md)
return QString();
@@ -474,7 +498,7 @@ QString QQmlVMEMetaObject::readPropertyAsString(int id)
QUrl QQmlVMEMetaObject::readPropertyAsUrl(int id)
{
- QV4::MemberData *md = propertiesAsMemberData();
+ QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
if (!md)
return QUrl();
@@ -488,7 +512,7 @@ QUrl QQmlVMEMetaObject::readPropertyAsUrl(int id)
QDate QQmlVMEMetaObject::readPropertyAsDate(int id)
{
- QV4::MemberData *md = propertiesAsMemberData();
+ QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
if (!md)
return QDate();
@@ -502,7 +526,7 @@ QDate QQmlVMEMetaObject::readPropertyAsDate(int id)
QDateTime QQmlVMEMetaObject::readPropertyAsDateTime(int id)
{
- QV4::MemberData *md = propertiesAsMemberData();
+ QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
if (!md)
return QDateTime();
@@ -516,7 +540,7 @@ QDateTime QQmlVMEMetaObject::readPropertyAsDateTime(int id)
QSizeF QQmlVMEMetaObject::readPropertyAsSizeF(int id)
{
- QV4::MemberData *md = propertiesAsMemberData();
+ QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
if (!md)
return QSizeF();
@@ -530,7 +554,7 @@ QSizeF QQmlVMEMetaObject::readPropertyAsSizeF(int id)
QPointF QQmlVMEMetaObject::readPropertyAsPointF(int id)
{
- QV4::MemberData *md = propertiesAsMemberData();
+ QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
if (!md)
return QPointF();
@@ -544,7 +568,7 @@ QPointF QQmlVMEMetaObject::readPropertyAsPointF(int id)
QObject* QQmlVMEMetaObject::readPropertyAsQObject(int id)
{
- QV4::MemberData *md = propertiesAsMemberData();
+ QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
if (!md)
return 0;
@@ -558,7 +582,7 @@ QObject* QQmlVMEMetaObject::readPropertyAsQObject(int id)
QList<QObject *> *QQmlVMEMetaObject::readPropertyAsList(int id)
{
- QV4::MemberData *md = propertiesAsMemberData();
+ QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
if (!md)
return 0;
@@ -574,7 +598,7 @@ QList<QObject *> *QQmlVMEMetaObject::readPropertyAsList(int id)
QRectF QQmlVMEMetaObject::readPropertyAsRectF(int id)
{
- QV4::MemberData *md = propertiesAsMemberData();
+ QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
if (!md)
return QRectF();
@@ -596,15 +620,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 +649,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 +835,68 @@ 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;
+
+ int coreIndex;
+ const int valueTypePropertyIndex = QQmlPropertyData::decodeValueTypePropertyIndex(aliasData->encodedMetaPropertyIndex, &coreIndex);
+
// 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, aliasData->encodedMetaPropertyIndex);
}
}
- 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 +909,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 +917,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
@@ -853,23 +941,21 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void *
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.
@@ -887,22 +973,23 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void *
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,7 +997,7 @@ 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)
@@ -925,9 +1012,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 +1052,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,7 +1083,7 @@ 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 ||
@@ -1015,61 +1102,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 +1122,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 +1166,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 +1183,24 @@ 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())
+ *valueTypeIndex = QQmlPropertyData::decodeValueTypePropertyIndex(aliasData->encodedMetaPropertyIndex, coreIndex);
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 +1210,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..4a81fc50d2 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>
{
@@ -165,18 +101,27 @@ public:
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(int coreIndex) const
+ {
+ for (auto it = interceptors; it; it = it->m_next) {
+ if (it->m_coreIndex == coreIndex)
+ 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..201d634411 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) && !defined(QT_NO_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;
@@ -715,7 +715,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)
@@ -1235,7 +1235,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 +1560,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();
@@ -1620,22 +1621,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();
@@ -1735,7 +1737,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 +2041,6 @@ void *qt_add_qmlxmlhttprequest(ExecutionEngine *v4)
QT_END_NAMESPACE
-#endif // QT_NO_XMLSTREAMREADER
+#endif // QT_NO_XMLSTREAMREADER && QT_NO_NETWORK
#include <qqmlxmlhttprequest.moc>
diff --git a/src/qml/qml/qqmlxmlhttprequest_p.h b/src/qml/qml/qqmlxmlhttprequest_p.h
index 7bbfb5243c..df30873915 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) && !defined(QT_NO_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 && QT_NO_NETWORK
#endif // QQMLXMLHTTPREQUEST_P_H
diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp
index b9fb1f4ffe..76a900c846 100644
--- a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp
+++ b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp
@@ -45,6 +45,7 @@
#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>
@@ -146,6 +147,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()
@@ -1282,11 +1285,10 @@ void QQmlBindingFunction::initBindingLocation()
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)
@@ -1995,6 +1997,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..d29983c476 100644
--- a/src/qml/qml/v8/qqmlbuiltinfunctions_p.h
+++ b/src/qml/qml/v8/qqmlbuiltinfunctions_p.h
@@ -135,6 +135,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;
@@ -186,7 +188,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..e08ff3b979 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,22 @@ QV8Engine::~QV8Engine()
qDeleteAll(m_extensionData);
m_extensionData.clear();
+#if !defined(QT_NO_XMLSTREAMREADER) && defined(QT_NO_NETWORK)
qt_rem_qmlxmlhttprequest(m_v4Engine, m_xmlHttpRequestData);
m_xmlHttpRequestData = 0;
+#endif
delete m_listModelData;
m_listModelData = 0;
delete m_v4Engine;
}
+#ifndef QT_NO_NETWORK
QNetworkAccessManager *QV8Engine::networkAccessManager()
{
return QQmlEnginePrivate::get(m_engine)->getNetworkAccessManager();
}
+#endif
const QSet<QString> &QV8Engine::illegalNames() const
{
@@ -189,8 +194,10 @@ void QV8Engine::initializeGlobal()
QQmlDateExtension::registerExtension(m_v4Engine);
QQmlNumberExtension::registerExtension(m_v4Engine);
+#if !defined(QT_NO_XMLSTREAMREADER) && !defined(QT_NO_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..390831609b 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);
+#ifndef QT_NO_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/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 0e7b0c1b14..d6d4553946 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>
@@ -92,17 +91,17 @@ 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);
}
};
@@ -235,10 +234,8 @@ void QQmlDelegateModelPrivate::init()
}
QQmlDelegateModel::QQmlDelegateModel()
-: QQmlInstanceModel(*(new QQmlDelegateModelPrivate(0)))
+ : QQmlDelegateModel(nullptr, nullptr)
{
- Q_D(QQmlDelegateModel);
- d->init();
}
QQmlDelegateModel::QQmlDelegateModel(QQmlContext *ctxt, QObject *parent)
@@ -1657,7 +1654,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(
@@ -1665,7 +1662,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);
@@ -1713,7 +1710,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)));
@@ -1721,7 +1718,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);
@@ -1921,9 +1918,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);
@@ -2322,7 +2320,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;
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..d3ff032ae9 100644
--- a/src/qml/types/qqmllistmodel_p_p.h
+++ b/src/qml/types/qqmllistmodel_p_p.h
@@ -199,7 +199,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..10666476fe 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>
+#ifndef QT_NO_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();
+
+#ifndef QT_NO_NETWORK
virtual QNetworkAccessManager *networkAccessManager();
+#endif
QQuickWorkerScriptEnginePrivate *p;
@@ -150,7 +155,9 @@ public:
QV4::PersistentValue onmessage;
private:
QV4::PersistentValue createsend;
+#ifndef QT_NO_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)
+#ifndef QT_NO_NETWORK
+, accessManager(0)
+#endif
{
m_v4Engine->v8Engine = this;
}
QQuickWorkerScriptEnginePrivate::WorkerEngine::~WorkerEngine()
{
+#ifndef QT_NO_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();
}
+#ifndef QT_NO_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/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..ebbcbcd1eb 100644
--- a/src/qmltest/qmltest.pro
+++ b/src/qmltest/qmltest.pro
@@ -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/quicktestresult.cpp b/src/qmltest/quicktestresult.cpp
index 0a820b79db..e05d29bc05 100644
--- a/src/qmltest/quicktestresult.cpp
+++ b/src/qmltest/quicktestresult.cpp
@@ -248,7 +248,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/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..3c8f4b281c 100644
--- a/src/quick/designer/qquickdesignercustomobjectdata.cpp
+++ b/src/quick/designer/qquickdesignercustomobjectdata.cpp
@@ -194,7 +194,7 @@ void QQuickDesignerCustomObjectData::doResetProperty(QQmlContext *context, const
#endif
if (qmlBinding)
qmlBinding->setTarget(property);
- QQmlPropertyPrivate::setBinding(binding, QQmlPropertyPrivate::None, QQmlPropertyPrivate::DontRemoveBinding);
+ QQmlPropertyPrivate::setBinding(binding, QQmlPropertyPrivate::None, QQmlPropertyData::DontRemoveBinding);
if (qmlBinding)
qmlBinding->update();
@@ -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/qquickdesignersupport.cpp b/src/quick/designer/qquickdesignersupport.cpp
index 78ed89a107..f063cd3a81 100644
--- a/src/quick/designer/qquickdesignersupport.cpp
+++ b/src/quick/designer/qquickdesignersupport.cpp
@@ -92,6 +92,7 @@ void QQuickDesignerSupport::refFromEffectItem(QQuickItem *referencedItem, bool h
texture->setRect(referencedItem->boundingRect());
texture->setSize(referencedItem->boundingRect().size().toSize());
texture->setRecursive(true);
+#ifndef QT_NO_OPENGL
#ifndef QT_OPENGL_ES
if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL)
texture->setFormat(GL_RGBA8);
@@ -100,6 +101,7 @@ void QQuickDesignerSupport::refFromEffectItem(QQuickItem *referencedItem, bool h
#else
texture->setFormat(GL_RGBA);
#endif
+#endif
texture->setHasMipmaps(false);
m_itemTextureHash.insert(referencedItem, texture);
diff --git a/src/quick/designer/qquickdesignerwindowmanager.cpp b/src/quick/designer/qquickdesignerwindowmanager.cpp
index efa3bcb51a..2d37db08e7 100644
--- a/src/quick/designer/qquickdesignerwindowmanager.cpp
+++ b/src/quick/designer/qquickdesignerwindowmanager.cpp
@@ -39,8 +39,9 @@
#include "qquickdesignerwindowmanager_p.h"
#include "private/qquickwindow_p.h"
-#include <QtGui/QOpenGLContext>
-
+#ifndef QT_NO_OPENGL
+# include <QtQuick/private/qsgdefaultrendercontext_p.h>
+#endif
#include <QtQuick/QQuickWindow>
QT_BEGIN_NAMESPACE
@@ -48,7 +49,7 @@ QT_BEGIN_NAMESPACE
QQuickDesignerWindowManager::QQuickDesignerWindowManager()
: m_sgContext(QSGContext::createDefaultContext())
{
- m_renderContext.reset(new QSGRenderContext(m_sgContext.data()));
+ m_renderContext.reset(m_sgContext.data()->createRenderContext());
}
void QQuickDesignerWindowManager::show(QQuickWindow *window)
@@ -66,6 +67,7 @@ void QQuickDesignerWindowManager::windowDestroyed(QQuickWindow *)
void QQuickDesignerWindowManager::makeOpenGLContext(QQuickWindow *window)
{
+#ifndef QT_NO_OPENGL
if (!m_openGlContext) {
m_openGlContext.reset(new QOpenGLContext());
m_openGlContext->setFormat(window->requestedFormat());
@@ -76,6 +78,9 @@ void QQuickDesignerWindowManager::makeOpenGLContext(QQuickWindow *window)
} else {
m_openGlContext->makeCurrent(window);
}
+#else
+ Q_UNUSED(window)
+#endif
}
void QQuickDesignerWindowManager::exposureChanged(QQuickWindow *)
diff --git a/src/quick/designer/qquickdesignerwindowmanager_p.h b/src/quick/designer/qquickdesignerwindowmanager_p.h
index 8af54c117f..a50f8aa49f 100644
--- a/src/quick/designer/qquickdesignerwindowmanager_p.h
+++ b/src/quick/designer/qquickdesignerwindowmanager_p.h
@@ -57,6 +57,10 @@
#include <private/qtquickglobal_p.h>
#include <QtQuick/private/qsgcontext_p.h>
+#ifndef QT_NO_OPENGL
+# include <QtGui/QOpenGLContext>
+#endif
+
QT_BEGIN_NAMESPACE
@@ -64,7 +68,6 @@ class QQuickWindow;
class QSGContext;
class QSGRenderContext;
class QAnimationDriver;
-class QOpenGLContext;
class QQuickDesignerWindowManager : public QSGRenderLoop
{
@@ -94,7 +97,9 @@ public:
static void createOpenGLContext(QQuickWindow *window);
private:
+#ifndef QT_NO_OPENGL
QScopedPointer<QOpenGLContext> m_openGlContext;
+#endif
QScopedPointer<QSGContext> m_sgContext;
QScopedPointer<QSGRenderContext> m_renderContext;
};
diff --git a/src/quick/doc/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/src/concepts/visualcanvas/adaptations.qdoc b/src/quick/doc/src/concepts/visualcanvas/adaptations.qdoc
new file mode 100644
index 0000000000..bbc64f1484
--- /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 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.
+
+Peformance-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 caedd72dc5..c820e08e1b 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/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/qquickcontext2d.cpp b/src/quick/items/context2d/qquickcontext2d.cpp
index b2117d3eb9..b924701f2b 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>
@@ -2669,15 +2674,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();
@@ -3980,10 +3985,12 @@ public:
~QQuickContext2DThreadCleanup()
{
+#ifndef QT_NO_OPENGL
context->makeCurrent(surface);
delete texture;
context->doneCurrent();
delete context;
+#endif
surface->deleteLater();
}
@@ -4019,6 +4026,7 @@ QQuickContext2D::~QQuickContext2D()
delete m_buffer;
if (m_renderTarget == QQuickCanvasItem::FramebufferObject) {
+#ifndef QT_NO_OPENGL
if (m_renderStrategy == QQuickCanvasItem::Immediate && m_glContext) {
Q_ASSERT(QThread::currentThread() == m_glContext->thread());
m_glContext->makeCurrent(m_surface.data());
@@ -4039,6 +4047,7 @@ QQuickContext2D::~QQuickContext2D()
m_texture->deleteLater();
}
}
+#endif
} else {
// Image based does not have GL resources, but must still be deleted
// on its designated thread after it has completed whatever it might
@@ -4064,8 +4073,6 @@ void QQuickContext2D::init(QQuickCanvasItem *canvasItem, const QVariantMap &args
m_canvas = canvasItem;
m_renderTarget = canvasItem->renderTarget();
-
- QQuickWindow *window = canvasItem->window();
m_renderStrategy = canvasItem->renderStrategy();
#ifdef Q_OS_WIN
@@ -4084,12 +4091,24 @@ void QQuickContext2D::init(QQuickCanvasItem *canvasItem, const QVariantMap &args
m_renderTarget = QQuickCanvasItem::Image;
}
+ // Disable Framebuffer Object based rendering when not running with OpenGL
+ if (m_renderTarget == QQuickCanvasItem::FramebufferObject) {
+ QSGRendererInterface *rif = canvasItem->window()->rendererInterface();
+ if (rif && rif->graphicsApi() != QSGRendererInterface::OpenGL)
+ m_renderTarget = QQuickCanvasItem::Image;
+ }
+
switch (m_renderTarget) {
case QQuickCanvasItem::Image:
m_texture = new QQuickContext2DImageTexture;
break;
case QQuickCanvasItem::FramebufferObject:
+#ifndef QT_NO_OPENGL
m_texture = new QQuickContext2DFBOTexture;
+#else
+ // It shouldn't be possible to use a FramebufferObject without OpenGL
+ m_texture = nullptr;
+#endif
break;
}
@@ -4103,18 +4122,27 @@ void QQuickContext2D::init(QQuickCanvasItem *canvasItem, const QVariantMap &args
m_thread = QThread::currentThread();
QThread *renderThread = m_thread;
- QThread *sceneGraphThread = window->openglContext() ? window->openglContext()->thread() : 0;
+#ifndef QT_NO_OPENGL
+ QQuickWindow *window = canvasItem->window();
+ QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window);
+ QThread *sceneGraphThread = wd->context->thread();
if (m_renderStrategy == QQuickCanvasItem::Threaded)
renderThread = QQuickContext2DRenderThread::instance(qmlEngine(canvasItem));
else if (m_renderStrategy == QQuickCanvasItem::Cooperative)
renderThread = sceneGraphThread;
+#else
+ if (m_renderStrategy == QQuickCanvasItem::Threaded)
+ renderThread = QQuickContext2DRenderThread::instance(qmlEngine(canvasItem));
+#endif
+
if (renderThread && renderThread != QThread::currentThread())
m_texture->moveToThread(renderThread);
-
+#ifndef QT_NO_OPENGL
if (m_renderTarget == QQuickCanvasItem::FramebufferObject && renderThread != sceneGraphThread) {
- QOpenGLContext *cc = QQuickWindowPrivate::get(window)->context->openglContext();
+ auto openglRenderContext = static_cast<const QSGDefaultRenderContext *>(QQuickWindowPrivate::get(window)->context);
+ QOpenGLContext *cc = openglRenderContext->openglContext();
m_surface.reset(new QOffscreenSurface);
m_surface->setFormat(window->format());
m_surface->create();
@@ -4125,7 +4153,7 @@ void QQuickContext2D::init(QQuickCanvasItem *canvasItem, const QVariantMap &args
m_glContext->moveToThread(renderThread);
m_texture->initializeOpenGL(m_glContext, m_surface.data());
}
-
+#endif
connect(m_texture, SIGNAL(textureChanged()), SIGNAL(textureChanged()));
reset();
@@ -4172,6 +4200,7 @@ QImage QQuickContext2D::toImage(const QRectF& bounds)
flush();
m_texture->grabImage(bounds);
} else {
+#ifndef QT_NO_OPENGL
QQuickWindow *window = m_canvas->window();
QOpenGLContext *ctx = window ? window->openglContext() : 0;
if (ctx && ctx->isValid()) {
@@ -4187,6 +4216,10 @@ QImage QQuickContext2D::toImage(const QRectF& bounds)
qWarning() << "Cannot read pixels from canvas before opengl context is valid";
return QImage();
}
+#else
+ flush();
+ m_texture->grabImage(bounds);
+#endif
}
} else if (m_renderStrategy == QQuickCanvasItem::Cooperative) {
qWarning() << "Pixel readback is not supported in Cooperative mode, please try Threaded or Immediate mode";
diff --git a/src/quick/items/context2d/qquickcontext2dcommandbuffer.cpp b/src/quick/items/context2d/qquickcontext2dcommandbuffer.cpp
index 58b374e696..8d659040b3 100644
--- a/src/quick/items/context2d/qquickcontext2dcommandbuffer.cpp
+++ b/src/quick/items/context2d/qquickcontext2dcommandbuffer.cpp
@@ -42,9 +42,11 @@
#include <qqml.h>
#include <QtCore/QMutex>
#include <QtQuick/qsgtexture.h>
-#include <QtGui/QOpenGLContext>
#include <QtGui/QPaintEngine>
-#include <QtGui/private/qopenglpaintengine_p.h>
+#ifndef QT_NO_OPENGL
+# include <QtGui/QOpenGLContext>
+# include <QtGui/private/qopenglpaintengine_p.h>
+#endif
#define HAS_SHADOW(offsetX, offsetY, blur, color) (color.isValid() && color.alpha() && (blur || offsetX || offsetY))
diff --git a/src/quick/items/context2d/qquickcontext2dtexture.cpp b/src/quick/items/context2d/qquickcontext2dtexture.cpp
index f3513f447a..ddecf7c3d4 100644
--- a/src/quick/items/context2d/qquickcontext2dtexture.cpp
+++ b/src/quick/items/context2d/qquickcontext2dtexture.cpp
@@ -44,14 +44,16 @@
#include <QtQuick/private/qsgtexture_p.h>
#include "qquickcontext2dcommandbuffer_p.h"
#include <QOpenGLPaintDevice>
-
+#ifndef QT_NO_OPENGL
#include <QOpenGLFramebufferObject>
#include <QOpenGLFramebufferObjectFormat>
+#include <QOpenGLFunctions>
+#endif
#include <QtCore/QThread>
#include <QtGui/QGuiApplication>
QT_BEGIN_NAMESPACE
-
+#ifndef QT_NO_OPENGL
#define QT_MINIMUM_FBO_SIZE 64
static inline int qt_next_power_of_two(int v)
@@ -85,10 +87,12 @@ struct GLAcquireContext {
}
QOpenGLContext *ctx;
};
-
+#endif
QQuickContext2DTexture::QQuickContext2DTexture()
: m_context(0)
+#ifndef QT_NO_OPENGL
, m_gl(0)
+#endif
, m_surface(0)
, m_item(0)
, m_canvasWindowChanged(false)
@@ -250,9 +254,9 @@ void QQuickContext2DTexture::paint(QQuickContext2DCommandBuffer *ccb)
return;
}
QQuickContext2D::mutex.unlock();
-
+#ifndef QT_NO_OPENGL
GLAcquireContext currentContext(m_gl, m_surface);
-
+#endif
if (!m_tiledCanvas) {
paintWithoutTiles(ccb);
delete ccb;
@@ -379,7 +383,7 @@ bool QQuickContext2DTexture::event(QEvent *e)
}
return QObject::event(e);
}
-
+#ifndef QT_NO_OPENGL
static inline QSize npotAdjustedSize(const QSize &size)
{
static bool checked = false;
@@ -646,6 +650,7 @@ void QQuickContext2DFBOTexture::endPainting()
m_fbo->bindDefault();
}
+#endif
QQuickContext2DImageTexture::QQuickContext2DImageTexture()
: QQuickContext2DTexture()
diff --git a/src/quick/items/context2d/qquickcontext2dtexture_p.h b/src/quick/items/context2d/qquickcontext2dtexture_p.h
index edc7283283..ed38382892 100644
--- a/src/quick/items/context2d/qquickcontext2dtexture_p.h
+++ b/src/quick/items/context2d/qquickcontext2dtexture_p.h
@@ -54,10 +54,10 @@
#include <QtQuick/qsgtexture.h>
#include "qquickcanvasitem_p.h"
#include "qquickcontext2d_p.h"
-
-#include <QOpenGLContext>
-#include <QOpenGLFramebufferObject>
-
+#ifndef QT_NO_OPENGL
+# include <QOpenGLContext>
+# include <QOpenGLFramebufferObject>
+#endif
#include <QtCore/QMutex>
#include <QtCore/QWaitCondition>
#include <QtCore/QThread>
@@ -121,11 +121,12 @@ public:
// Called during sync() on the scene graph thread while GUI is blocked.
virtual QSGTexture *textureForNextFrame(QSGTexture *lastFrame, QQuickWindow *window) = 0;
bool event(QEvent *e);
-
+#ifndef QT_NO_OPENGL
void initializeOpenGL(QOpenGLContext *gl, QOffscreenSurface *s) {
m_gl = gl;
m_surface = s;
}
+#endif
Q_SIGNALS:
void textureChanged();
@@ -152,8 +153,9 @@ protected:
QList<QQuickContext2DTile*> m_tiles;
QQuickContext2D *m_context;
-
+#ifndef QT_NO_OPENGL
QOpenGLContext *m_gl;
+#endif
QSurface *m_surface;
QQuickContext2D::State m_state;
@@ -174,7 +176,7 @@ protected:
uint m_painting : 1;
uint m_onCustomThread : 1; // Not GUI and not SGRender
};
-
+#ifndef QT_NO_OPENGL
class QQuickContext2DFBOTexture : public QQuickContext2DTexture
{
Q_OBJECT
@@ -209,7 +211,7 @@ private:
GLuint m_displayTextures[2];
int m_displayTexture;
};
-
+#endif
class QSGPlainTexture;
class QQuickContext2DImageTexture : public QQuickContext2DTexture
{
diff --git a/src/quick/items/context2d/qquickcontext2dtile.cpp b/src/quick/items/context2d/qquickcontext2dtile.cpp
index a1503762de..95b6c9d961 100644
--- a/src/quick/items/context2d/qquickcontext2dtile.cpp
+++ b/src/quick/items/context2d/qquickcontext2dtile.cpp
@@ -38,10 +38,11 @@
****************************************************************************/
#include "qquickcontext2dtile_p.h"
-
-#include <QOpenGLFramebufferObject>
-#include <QOpenGLFramebufferObjectFormat>
-#include <QOpenGLPaintDevice>
+#ifndef QT_NO_OPENGL
+# include <QOpenGLFramebufferObject>
+# include <QOpenGLFramebufferObjectFormat>
+# include <QOpenGLPaintDevice>
+#endif
QT_BEGIN_NAMESPACE
@@ -96,7 +97,7 @@ QPainter* QQuickContext2DTile::createPainter(bool smooth, bool antialiasing)
return 0;
}
-
+#ifndef QT_NO_OPENGL
QQuickContext2DFBOTile::QQuickContext2DFBOTile()
: QQuickContext2DTile()
, m_fbo(0)
@@ -146,7 +147,7 @@ void QQuickContext2DFBOTile::setRect(const QRect& r)
m_fbo = new QOpenGLFramebufferObject(r.size(), format);
}
}
-
+#endif
QQuickContext2DImageTile::QQuickContext2DImageTile()
: QQuickContext2DTile()
diff --git a/src/quick/items/context2d/qquickcontext2dtile_p.h b/src/quick/items/context2d/qquickcontext2dtile_p.h
index d9be2023c4..a87202daae 100644
--- a/src/quick/items/context2d/qquickcontext2dtile_p.h
+++ b/src/quick/items/context2d/qquickcontext2dtile_p.h
@@ -52,8 +52,9 @@
//
#include "qquickcontext2d_p.h"
-#include <QOpenGLFramebufferObject>
-
+#ifndef QT_NO_OPENGL
+# include <QOpenGLFramebufferObject>
+#endif
QT_BEGIN_NAMESPACE
class QQuickContext2DTexture;
@@ -82,7 +83,7 @@ protected:
QPainter m_painter;
};
-
+#ifndef QT_NO_OPENGL
class QQuickContext2DFBOTile : public QQuickContext2DTile
{
public:
@@ -99,7 +100,7 @@ private:
QOpenGLFramebufferObject *m_fbo;
};
-
+#endif
class QQuickContext2DImageTile : public QQuickContext2DTile
{
public:
diff --git a/src/quick/items/items.pri b/src/quick/items/items.pri
index ab966b6ccc..beaf3540bc 100644
--- a/src/quick/items/items.pri
+++ b/src/quick/items/items.pri
@@ -63,10 +63,6 @@ HEADERS += \
$$PWD/qquickstateoperations_p.h \
$$PWD/qquickimplicitsizeitem_p.h \
$$PWD/qquickimplicitsizeitem_p_p.h \
- $$PWD/qquickspriteengine_p.h \
- $$PWD/qquicksprite_p.h \
- $$PWD/qquickspritesequence_p.h \
- $$PWD/qquickanimatedsprite_p.h \
$$PWD/qquickdrag_p.h \
$$PWD/qquickdroparea_p.h \
$$PWD/qquickmultipointtoucharea_p.h \
@@ -76,11 +72,20 @@ HEADERS += \
$$PWD/qquickscreen_p.h \
$$PWD/qquickwindowattached_p.h \
$$PWD/qquickwindowmodule_p.h \
- $$PWD/qquickframebufferobject.h \
- $$PWD/qquickitemgrabresult.h \
+ $$PWD/qquickshadereffectsource_p.h \
+ $$PWD/qquickshadereffectmesh_p.h \
+ $$PWD/qquickshadereffect_p.h \
+ $$PWD/qquickgenericshadereffect_p.h \
$$PWD/qquickrendercontrol.h \
$$PWD/qquickrendercontrol_p.h \
- $$PWD/qquickopenglinfo_p.h
+ $$PWD/qquickgraphicsinfo_p.h \
+ $$PWD/qquickitemgrabresult.h \
+ $$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/qquickevents.cpp \
@@ -120,10 +125,6 @@ SOURCES += \
$$PWD/qquickitemanimation.cpp \
$$PWD/qquickstateoperations.cpp \
$$PWD/qquickimplicitsizeitem.cpp \
- $$PWD/qquickspriteengine.cpp \
- $$PWD/qquicksprite.cpp \
- $$PWD/qquickspritesequence.cpp \
- $$PWD/qquickanimatedsprite.cpp \
$$PWD/qquickaccessibleattached.cpp \
$$PWD/qquickdrag.cpp \
$$PWD/qquickdroparea.cpp \
@@ -133,36 +134,42 @@ SOURCES += \
$$PWD/qquickwindowmodule.cpp \
$$PWD/qquickscreen.cpp \
$$PWD/qquickwindowattached.cpp \
- $$PWD/qquickframebufferobject.cpp \
- $$PWD/qquickitemgrabresult.cpp \
+ $$PWD/qquickshadereffectsource.cpp \
+ $$PWD/qquickshadereffectmesh.cpp \
+ $$PWD/qquickshadereffect.cpp \
+ $$PWD/qquickgenericshadereffect.cpp \
$$PWD/qquickrendercontrol.cpp \
- $$PWD/qquickopenglinfo.cpp
+ $$PWD/qquickgraphicsinfo.cpp \
+ $$PWD/qquickitemgrabresult.cpp \
+ $$PWD/qquickspriteengine.cpp \
+ $$PWD/qquicksprite.cpp \
+ $$PWD/qquickspritesequence.cpp \
+ $$PWD/qquickanimatedsprite.cpp
-SOURCES += \
- $$PWD/qquickshadereffect.cpp \
- $$PWD/qquickshadereffectmesh.cpp \
- $$PWD/qquickshadereffectnode.cpp \
- $$PWD/qquickshadereffectsource.cpp \
+# Items that depend on OpenGL Renderer
+contains(QT_CONFIG, opengl(es1|es2)?) {
+ SOURCES += \
+ $$PWD/qquickopenglinfo.cpp \
+ $$PWD/qquickopenglshadereffect.cpp \
+ $$PWD/qquickopenglshadereffectnode.cpp \
+ $$PWD/qquickframebufferobject.cpp
-HEADERS += \
- $$PWD/qquickshadereffect_p.h \
- $$PWD/qquickshadereffectmesh_p.h \
- $$PWD/qquickshadereffectnode_p.h \
- $$PWD/qquickshadereffectsource_p.h \
+ HEADERS += \
+ $$PWD/qquickopenglinfo_p.h \
+ $$PWD/qquickopenglshadereffect_p.h \
+ $$PWD/qquickopenglshadereffectnode_p.h \
+ $$PWD/qquickframebufferobject.h
-OTHER_FILES += \
- $$PWD/shaders/sprite.vert \
- $$PWD/shaders/sprite.frag \
- $$PWD/shaders/shadereffect.vert \
- $$PWD/shaders/shadereffect.frag \
- $$PWD/shaders/shadereffectfallback.vert \
- $$PWD/shaders/shadereffectfallback.frag \
- $$PWD/shaders/sprite_core.vert \
- $$PWD/shaders/sprite_core.frag \
- $$PWD/shaders/shadereffect_core.vert \
- $$PWD/shaders/shadereffect_core.frag \
- $$PWD/shaders/shadereffectfallback_core.vert \
- $$PWD/shaders/shadereffectfallback_core.frag
+ OTHER_FILES += \
+ $$PWD/shaders/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
+}
RESOURCES += \
$$PWD/items.qrc
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..6f14bf15fe 100644
--- a/src/quick/items/qquickanimatedimage.cpp
+++ b/src/quick/items/qquickanimatedimage.cpp
@@ -47,8 +47,10 @@
#include <QtQml/qqmlfile.h>
#include <QtQml/qqmlengine.h>
#include <QtGui/qmovie.h>
+#ifndef QT_NO_NETWORK
#include <QtNetwork/qnetworkrequest.h>
#include <QtNetwork/qnetworkreply.h>
+#endif
QT_BEGIN_NAMESPACE
@@ -144,8 +146,10 @@ QQuickAnimatedImage::QQuickAnimatedImage(QQuickItem *parent)
QQuickAnimatedImage::~QQuickAnimatedImage()
{
Q_D(QQuickAnimatedImage);
+#ifndef QT_NO_NETWORK
if (d->reply)
d->reply->deleteLater();
+#endif
delete d->_movie;
qDeleteAll(d->frameMap);
d->frameMap.clear();
@@ -264,10 +268,12 @@ void QQuickAnimatedImage::setSource(const QUrl &url)
if (url == d->url)
return;
+#ifndef QT_NO_NETWORK
if (d->reply) {
d->reply->deleteLater();
d->reply = 0;
}
+#endif
d->setImage(QImage());
qDeleteAll(d->frameMap);
@@ -319,6 +325,7 @@ void QQuickAnimatedImage::load()
d->_movie = new QMovie(lf);
movieRequestFinished();
} else {
+#ifndef QT_NO_NETWORK
if (d->status != Loading) {
d->status = Loading;
emit statusChanged(d->status);
@@ -335,6 +342,7 @@ void QQuickAnimatedImage::load()
this, SLOT(movieRequestFinished()));
QObject::connect(d->reply, SIGNAL(downloadProgress(qint64,qint64)),
this, SLOT(requestProgress(qint64,qint64)));
+#endif
}
}
}
@@ -343,8 +351,10 @@ void QQuickAnimatedImage::load()
void QQuickAnimatedImage::movieRequestFinished()
{
+
Q_D(QQuickAnimatedImage);
+#ifndef QT_NO_NETWORK
if (d->reply) {
d->redirectCount++;
if (d->redirectCount < ANIMATEDIMAGE_MAXIMUM_REDIRECT_RECURSION) {
@@ -360,6 +370,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();
diff --git a/src/quick/items/qquickanimatedimage_p_p.h b/src/quick/items/qquickanimatedimage_p_p.h
index 6d32f1071f..ed735d1c9c 100644
--- a/src/quick/items/qquickanimatedimage_p_p.h
+++ b/src/quick/items/qquickanimatedimage_p_p.h
@@ -58,7 +58,9 @@
QT_BEGIN_NAMESPACE
class QMovie;
+#ifndef QT_NO_NETWORK
class QNetworkReply;
+#endif
class QQuickAnimatedImagePrivate : public QQuickImagePrivate
{
@@ -66,7 +68,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)
+#ifndef QT_NO_NETWORK
+ , reply(0), redirectCount(0)
+#endif
+ , currentSourceSize(0, 0)
{
}
@@ -76,9 +82,11 @@ public:
bool paused;
int preset_currentframe;
QMovie *_movie;
+ bool oldPlaying;
+#ifndef QT_NO_NETWORK
QNetworkReply *reply;
int redirectCount;
- bool oldPlaying;
+#endif
QMap<int, QQuickPixmap *> frameMap;
QSize currentSourceSize;
};
diff --git a/src/quick/items/qquickanimatedsprite.cpp b/src/quick/items/qquickanimatedsprite.cpp
index 77c7ae106b..991ca8519f 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>
@@ -55,111 +56,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
@@ -315,18 +211,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()));
@@ -334,6 +223,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));
@@ -348,23 +327,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);
@@ -373,10 +354,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);
update();
}
@@ -388,14 +370,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);
update();
}
@@ -409,10 +392,12 @@ void QQuickAnimatedSprite::advance(int frames)
*/
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);
update();
}
@@ -427,246 +412,363 @@ 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);
update();
}
-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();
- update();
+ 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)
{
- 2, // Attribute Count
- (2+2) * sizeof(float),
- AnimatedSprite_Attributes
-};
+ Q_D(QQuickAnimatedSprite);
+
+ if (d->m_sprite->m_frames != arg) {
+ d->m_sprite->setFrameCount(arg);
+ Q_EMIT frameCountChanged(arg);
+ reloadImage();
+ }
+}
-void QQuickAnimatedSprite::sizeVertices(QSGGeometryNode *node)
+void QQuickAnimatedSprite::setFrameHeight(int 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_frameHeight != arg) {
+ d->m_sprite->setFrameHeight(arg);
+ Q_EMIT frameHeightChanged(arg);
+ reloadImage();
+ }
+}
- p->v3.x = 0;
- p->v3.y = height();
+void QQuickAnimatedSprite::setFrameWidth(int arg)
+{
+ Q_D(QQuickAnimatedSprite);
- p->v4.x = width();
- p->v4.y = height();
+ if (d->m_sprite->m_frameWidth != arg) {
+ d->m_sprite->setFrameWidth(arg);
+ Q_EMIT frameWidthChanged(arg);
+ reloadImage();
+ }
}
-QSGGeometryNode* QQuickAnimatedSprite::buildNode()
+void QQuickAnimatedSprite::setFrameX(int arg)
{
- if (!m_spriteEngine) {
+ 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)
+{
+ Q_D(QQuickAnimatedSprite);
+
+ if (d->m_sprite->m_frameY != arg) {
+ d->m_sprite->setFrameY(arg);
+ Q_EMIT frameYChanged(arg);
+ reloadImage();
+ }
+}
+
+void QQuickAnimatedSprite::setFrameRate(qreal arg)
+{
+ Q_D(QQuickAnimatedSprite);
+
+ if (d->m_sprite->m_frameRate != arg) {
+ d->m_sprite->setFrameRate(arg);
+ Q_EMIT frameRateChanged(arg);
+ if (d->m_running)
+ restart();
+ }
+}
+
+void QQuickAnimatedSprite::setFrameDuration(int arg)
+{
+ Q_D(QQuickAnimatedSprite);
+
+ 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);
+}
+
+void QQuickAnimatedSprite::resetFrameDuration()
+{
+ 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();
+ update();
+}
+
+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();
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;
}
- 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;
update();
}
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)
- update();
-
- if (node) {
- node->markDirty(QSGNode::DirtyMaterial);
- }
- }
+ if (d->m_running && !d->m_paused)
+ update();
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);
update();
}
} 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);
update();
}
- 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;
@@ -676,9 +778,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;
}
@@ -695,15 +797,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 1e5981c1a4..cbd6524c40 100644
--- a/src/quick/items/qquickanimatedsprite_p.h
+++ b/src/quick/items/qquickanimatedsprite_p.h
@@ -62,6 +62,8 @@ class QQuickSprite;
class QQuickSpriteEngine;
class QSGGeometryNode;
class QQuickAnimatedSpriteMaterial;
+class QQuickAnimatedSpritePrivate;
+class QSGSpriteNode;
class Q_AUTOTEST_EXPORT QQuickAnimatedSprite : public QQuickItem
{
Q_OBJECT
@@ -94,80 +96,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 +119,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 +139,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();
@@ -367,21 +169,13 @@ protected:
QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *) Q_DECL_OVERRIDE;
private:
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..0e4a1e9066
--- /dev/null
+++ b/src/quick/items/qquickanimatedsprite_p_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 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 "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..ca5ad8dbbd 100644
--- a/src/quick/items/qquickborderimage.cpp
+++ b/src/quick/items/qquickborderimage.cpp
@@ -43,7 +43,9 @@
#include <QtQml/qqmlinfo.h>
#include <QtQml/qqmlfile.h>
#include <QtQml/qqmlengine.h>
+#ifndef QT_NO_NETWORK
#include <QtNetwork/qnetworkreply.h>
+#endif
#include <QtCore/qfile.h>
#include <QtCore/qmath.h>
#include <QtGui/qguiapplication.h>
@@ -169,9 +171,11 @@ QQuickBorderImage::QQuickBorderImage(QQuickItem *parent)
QQuickBorderImage::~QQuickBorderImage()
{
+#ifndef QT_NO_NETWORK
Q_D(QQuickBorderImage);
if (d->sciReply)
d->sciReply->deleteLater();
+#endif
}
/*!
@@ -270,10 +274,12 @@ void QQuickBorderImage::setSource(const QUrl &url)
if (url == d->url)
return;
+#ifndef QT_NO_NETWORK
if (d->sciReply) {
d->sciReply->deleteLater();
d->sciReply = 0;
}
+#endif
d->url = url;
d->sciurl = QUrl();
@@ -311,6 +317,7 @@ void QQuickBorderImage::load()
setGridScaledImage(QQuickGridScaledImage(&file));
return;
} else {
+#ifndef QT_NO_NETWORK
if (d->progress != 0.0) {
d->progress = 0.0;
emit progressChanged(d->progress);
@@ -320,6 +327,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 +537,7 @@ void QQuickBorderImage::requestFinished()
pixmapChange();
}
+#ifndef QT_NO_NETWORK
#define BORDERIMAGE_MAX_REDIRECT 16
void QQuickBorderImage::sciRequestFinished()
@@ -558,12 +567,59 @@ void QQuickBorderImage::sciRequestFinished()
setGridScaledImage(sci);
}
}
+#endif // QT_NO_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 +631,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 +644,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..f2764660f6 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;
+#ifndef QT_NO_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..56fbbab049 100644
--- a/src/quick/items/qquickborderimage_p_p.h
+++ b/src/quick/items/qquickborderimage_p_p.h
@@ -58,17 +58,20 @@
QT_BEGIN_NAMESPACE
+#ifndef QT_NO_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)
+#ifndef QT_NO_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;
+
+#ifndef QT_NO_NETWORK
+ QNetworkReply *sciReply;
+ int redirectCount;
+#endif
};
QT_END_NAMESPACE
diff --git a/src/quick/items/qquickdrag.cpp b/src/quick/items/qquickdrag.cpp
index 9a24d7a8a0..8a0af6c263 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/qquickevents.cpp b/src/quick/items/qquickevents.cpp
index 5061c19f1e..14c0adf393 100644
--- a/src/quick/items/qquickevents.cpp
+++ b/src/quick/items/qquickevents.cpp
@@ -415,4 +415,26 @@ 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.
+*/
+
QT_END_NAMESPACE
diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h
index b28ab555b0..065e025152 100644
--- a/src/quick/items/qquickevents_p_p.h
+++ b/src/quick/items/qquickevents_p_p.h
@@ -73,10 +73,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 +121,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 +165,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 +179,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 +207,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,6 +218,23 @@ 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;
};
@@ -186,5 +243,6 @@ QT_END_NAMESPACE
QML_DECLARE_TYPE(QQuickKeyEvent)
QML_DECLARE_TYPE(QQuickMouseEvent)
QML_DECLARE_TYPE(QQuickWheelEvent)
+QML_DECLARE_TYPE(QQuickCloseEvent)
#endif // QQUICKEVENTS_P_P_H
diff --git a/src/quick/items/qquickflickable.cpp b/src/quick/items/qquickflickable.cpp
index f72923a0d5..dcba5c2d71 100644
--- a/src/quick/items/qquickflickable.cpp
+++ b/src/quick/items/qquickflickable.cpp
@@ -292,14 +292,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);
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/qquickframebufferobject.cpp b/src/quick/items/qquickframebufferobject.cpp
index 0356b72e1d..d8147a48a5 100644
--- a/src/quick/items/qquickframebufferobject.cpp
+++ b/src/quick/items/qquickframebufferobject.cpp
@@ -40,7 +40,7 @@
#include "qquickframebufferobject.h"
#include <QtGui/QOpenGLFramebufferObject>
-
+#include <QtGui/QOpenGLFunctions>
#include <private/qquickitem_p.h>
#include <QSGSimpleTextureNode>
diff --git a/src/quick/items/qquickgenericshadereffect.cpp b/src/quick/items/qquickgenericshadereffect.cpp
new file mode 100644
index 0000000000..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/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/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 aaa7ce04b9..8905faf973 100644
--- a/src/quick/items/qquickitem.cpp
+++ b/src/quick/items/qquickitem.cpp
@@ -85,9 +85,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 +98,7 @@ void debugFocusTree(QQuickItem *item, QQuickItem *scope = 0, int depth = 1)
<< item->hasActiveFocus()
<< item->isFocusScope()
<< item;
- foreach (QQuickItem *child, item->childItems()) {
+ for (QQuickItem *child : item->childItems()) {
debugFocusTree(
child,
item->isFocusScope() || !scope ? item : scope,
@@ -114,17 +113,17 @@ static void QQuickItem_parentNotifier(QObject *o, QQmlNotifier **n)
*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)
+QML_PRIVATE_ACCESSOR(QQuickItem, QQuickItem *, parent, parentItem, setParentItem)
+QML_PRIVATE_ACCESSOR(QQuickItem, qreal, x, x, setX)
+QML_PRIVATE_ACCESSOR(QQuickItem, qreal, y, y, setY)
+QML_PRIVATE_ACCESSOR(QQuickItem, qreal, width, width, setWidth)
+QML_PRIVATE_ACCESSOR(QQuickItem, qreal, height, height, setHeight)
-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 };
+static QQmlAccessors QQuickItem_parent = { QQuickItem_parentRead, QQuickItem_parentWrite, QQuickItem_parentNotifier };
+static QQmlAccessors QQuickItem_x = { QQuickItem_xRead, QQuickItem_xWrite, 0 };
+static QQmlAccessors QQuickItem_y = { QQuickItem_yRead, QQuickItem_yWrite, 0 };
+static QQmlAccessors QQuickItem_width = { QQuickItem_widthRead, QQuickItem_widthWrite, 0 };
+static QQmlAccessors QQuickItem_height = { QQuickItem_heightRead, QQuickItem_heightWrite, 0 };
QML_DECLARE_PROPERTIES(QQuickItem) {
{ QML_PROPERTY_NAME(parent), 0, &QQuickItem_parent },
@@ -178,8 +177,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 +187,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 +200,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 +223,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 +257,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 +277,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 +289,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 +1332,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 +1354,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 +1367,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 +1397,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 +1410,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 +1424,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)) {
+ d->item->window()->sendEvent(targetItem, event);
if (event->isAccepted()) {
- d->imeItem = i;
+ d->imeItem = targetItem;
d->inIM = false;
return;
}
@@ -1452,13 +1443,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;
}
}
@@ -1615,11 +1605,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 +2314,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 +2332,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,8 +2361,7 @@ 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);
}
@@ -2883,8 +2852,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 +2882,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 +2909,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 +2952,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 +2979,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 +3004,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 +3127,8 @@ QQuickItemPrivate::QQuickItemPrivate()
, isAccessible(false)
, culled(false)
, hasCursor(false)
- , hasCursorInChild(false)
+ , subtreeCursorEnabled(false)
+ , subtreeHoverEnabled(false)
, activeFocusOnTab(false)
, implicitAntialiasing(false)
, antialiasingValid(false)
@@ -3196,17 +3162,6 @@ 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();
@@ -3355,7 +3310,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 +3451,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 +3575,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 +3679,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 +3768,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 +3822,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 +3834,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);
@@ -4290,7 +4249,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();
@@ -4862,14 +4821,16 @@ void QQuickItem::componentComplete()
QQuickAnchorsPrivate::get(d->_anchors)->updateOnComplete();
}
- if (d->extra.isAllocated() && d->extra->layer)
- d->extra->layer->componentComplete();
+ if (d->extra.isAllocated()) {
+ if (d->extra->layer)
+ d->extra->layer->componentComplete();
- 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();
@@ -5763,15 +5724,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
@@ -5813,18 +5772,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);
}
@@ -5968,7 +5924,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);
@@ -5978,7 +5934,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);
@@ -5991,7 +5947,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);
@@ -6001,7 +5957,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);
@@ -6011,7 +5967,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);
@@ -6024,7 +5980,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);
@@ -6386,7 +6342,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);
@@ -6550,7 +6506,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);
@@ -6977,7 +6933,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()
@@ -6989,7 +6945,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
@@ -7040,6 +6996,7 @@ void QQuickItem::setAcceptHoverEvents(bool enabled)
{
Q_D(QQuickItem);
d->hoverEnabled = enabled;
+ d->setHasHoverInChild(enabled);
}
void QQuickItemPrivate::setHasCursorInChild(bool hasCursor)
@@ -7048,17 +7005,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);
@@ -7067,12 +7025,37 @@ 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);
+ }
+}
+
void QQuickItemPrivate::markObjects(QV4::ExecutionEngine *e)
{
Q_Q(QQuickItem);
QV4::QObjectWrapper::markWrapper(q, e);
- foreach (QQuickItem *child, childItems)
+ for (QQuickItem *child : qAsConst(childItems))
QQuickItemPrivate::get(child)->markObjects(e);
}
@@ -7187,15 +7170,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);
}
@@ -7248,26 +7223,7 @@ 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);
}
/*!
@@ -7281,14 +7237,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);
}
/*!
@@ -7347,7 +7296,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;
}
/*!
@@ -8150,7 +8101,7 @@ void QQuickItemLayer::itemOpacityChanged(QQuickItem *item)
updateOpacity();
}
-void QQuickItemLayer::itemGeometryChanged(QQuickItem *, const QRectF &, const QRectF &)
+void QQuickItemLayer::itemGeometryChanged(QQuickItem *, QQuickGeometryChange, const QRectF &)
{
updateGeometry();
}
diff --git a/src/quick/items/qquickitem.h b/src/quick/items/qquickitem.h
index c5c17615ee..0352f5ace8 100644
--- a/src/quick/items/qquickitem.h
+++ b/src/quick/items/qquickitem.h
@@ -369,7 +369,6 @@ Q_SIGNALS:
void clipChanged(bool);
Q_REVISION(1) void windowChanged(QQuickWindow* window);
- // XXX todo
void childrenChanged();
void opacityChanged();
void enabledChanged();
@@ -454,7 +453,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 fed3e88b68..9ea2712e18 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"
@@ -76,7 +77,6 @@
#include <QtCore/qelapsedtimer.h>
#include <QtQuick/private/qquickshadereffectsource_p.h>
-#include <QtQuick/private/qquickshadereffect_p.h>
QT_BEGIN_NAMESPACE
@@ -98,7 +98,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;
@@ -188,7 +188,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;
@@ -318,24 +318,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; }
};
@@ -389,8 +377,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;
@@ -426,8 +414,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;
@@ -605,9 +594,12 @@ public:
virtual void mirrorChange() {}
void setHasCursorInChild(bool hasCursor);
+ void setHasHoverInChild(bool hasHover);
// recursive helper to let a visual parent mark its visual children
void markObjects(QV4::ExecutionEngine *e);
+
+ virtual void updatePolish() { }
};
/*
@@ -775,6 +767,7 @@ public:
QQuickItem *imeItem;
QList<QQuickItem *> targets;
QQuickItem *item;
+ QQuickKeyEvent theKeyEvent;
};
class QQuickKeysAttached : public QObject, public QQuickItemKeyFilter
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..0db5323863 100644
--- a/src/quick/items/qquickitemgrabresult.cpp
+++ b/src/quick/items/qquickitemgrabresult.cpp
@@ -240,8 +240,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 08d95119d7..fb60331ce2 100644
--- a/src/quick/items/qquickitemsmodule.cpp
+++ b/src/quick/items/qquickitemsmodule.cpp
@@ -41,7 +41,6 @@
#include "qquickitem.h"
#include "qquickitem_p.h"
-#include "qquickitemgrabresult.h"
#include "qquickevents_p_p.h"
#include "qquickrectangle_p.h"
#include "qquickfocusscope_p.h"
@@ -70,18 +69,23 @@
#include "qquicktranslate_p.h"
#include "qquickstateoperations_p.h"
#include "qquickitemanimation_p.h"
-#include <private/qquickshadereffect_p.h>
#include <QtQuick/private/qquickshadereffectsource_p.h>
//#include <private/qquickpincharea_p.h>
#include <QtQuick/private/qquickcanvasitem_p.h>
#include <QtQuick/private/qquickcontext2d_p.h>
+#include "qquickitemgrabresult.h"
#include "qquicksprite_p.h"
#include "qquickspritesequence_p.h"
#include "qquickanimatedsprite_p.h"
+#ifndef QT_NO_OPENGL
+# include "qquickopenglinfo_p.h"
+#endif
+#include "qquickgraphicsinfo_p.h"
+#include "qquickshadereffect_p.h"
+#include "qquickshadereffectmesh_p.h"
#include "qquickdrag_p.h"
#include "qquickdroparea_p.h"
#include "qquickmultipointtoucharea_p.h"
-#include "qquickopenglinfo_p.h"
#include <private/qqmlmetatype_p.h>
#include <QtQuick/private/qquickaccessibleattached_p.h>
@@ -178,7 +182,6 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor)
qmlRegisterType<QQuickTextInput,2>(uri,2,2,"TextInput");
qmlRegisterType<QQuickTextInput,3>(uri,2,4,"TextInput");
qmlRegisterType<QQuickViewSection>(uri,major,minor,"ViewSection");
-
qmlRegisterType<QQuickItemGrabResult>();
qmlRegisterType<QQuickItemLayer>();
qmlRegisterType<QQuickAnchors>();
@@ -207,10 +210,10 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor)
qmlRegisterType<QQuickPinch>(uri,major,minor,"Pinch");
qmlRegisterType<QQuickPinchEvent>();
- qmlRegisterType<QQuickShaderEffect>("QtQuick", 2, 0, "ShaderEffect");
qmlRegisterType<QQuickShaderEffectSource>("QtQuick", 2, 0, "ShaderEffectSource");
qmlRegisterUncreatableType<QQuickShaderEffectMesh>("QtQuick", 2, 0, "ShaderEffectMesh", QQuickShaderEffectMesh::tr("Cannot create instance of abstract class ShaderEffectMesh."));
qmlRegisterType<QQuickGridMesh>("QtQuick", 2, 0, "GridMesh");
+ qmlRegisterType<QQuickShaderEffect>("QtQuick", 2, 0, "ShaderEffect");
qmlRegisterUncreatableType<QQuickPaintedItem>("QtQuick", 2, 0, "PaintedItem", QQuickPaintedItem::tr("Cannot create instance of abstract class PaintedItem"));
@@ -262,8 +265,10 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor)
qmlRegisterType<QQuickListView, 2>(uri, 2, 4, "ListView");
qmlRegisterType<QQuickMouseArea, 1>(uri, 2, 4, "MouseArea");
qmlRegisterType<QQuickShaderEffect, 1>(uri, 2, 4, "ShaderEffect");
- qmlRegisterUncreatableType<QQuickOpenGLInfo>(uri, 2, 4,"OpenGLInfo", QQuickOpenGLInfo::tr("OpenGLInfo is only available via attached properties"));
+#ifndef QT_NO_OPENGL
+ qmlRegisterUncreatableType<QQuickOpenGLInfo>(uri, 2, 4,"OpenGLInfo", QQuickOpenGLInfo::tr("OpenGLInfo is only available via attached properties"));
+#endif
qmlRegisterType<QQuickPinchArea, 1>(uri, 2, 5,"PinchArea");
qmlRegisterType<QQuickImage, 2>(uri, 2, 5,"Image");
qmlRegisterType<QQuickMouseArea, 2>(uri, 2, 5, "MouseArea");
@@ -288,6 +293,9 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor)
qmlRegisterType<QQuickPathView, 7>(uri, 2, 7, "PathView");
qmlRegisterUncreatableType<QQuickMouseEvent, 7>(uri, 2, 7, nullptr, QQuickMouseEvent::tr("MouseEvent is only available within handlers in MouseArea"));
+
+ qmlRegisterUncreatableType<QQuickGraphicsInfo>(uri, 2, 8,"GraphicsInfo", QQuickGraphicsInfo::tr("GraphicsInfo is only available via attached properties"));
+ qmlRegisterType<QQuickBorderImageMesh>("QtQuick", 2, 8, "BorderImageMesh");
}
static void initResources()
diff --git a/src/quick/items/qquickitemview.cpp b/src/quick/items/qquickitemview.cpp
index aff03b7539..3c1a5a6ebe 100644
--- a/src/quick/items/qquickitemview.cpp
+++ b/src/quick/items/qquickitemview.cpp
@@ -1205,10 +1205,11 @@ void QQuickItemViewPrivate::showVisibleItems() const
}
}
-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;
diff --git a/src/quick/items/qquickitemview_p_p.h b/src/quick/items/qquickitemview_p_p.h
index 5e104cf208..0893d6e749 100644
--- a/src/quick/items/qquickitemview_p_p.h
+++ b/src/quick/items/qquickitemview_p_p.h
@@ -378,7 +378,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/qquicklistview.cpp b/src/quick/items/qquicklistview.cpp
index 4b9b7df98a..7bbc6dc9ba 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,
@@ -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());
+ 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();
diff --git a/src/quick/items/qquicklistview_p.h b/src/quick/items/qquicklistview_p.h
index b8ab38bbba..5a5e8a55fe 100644
--- a/src/quick/items/qquicklistview_p.h
+++ b/src/quick/items/qquicklistview_p.h
@@ -53,11 +53,13 @@
#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 +105,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 234105986a..33cc6c9a63 100644
--- a/src/quick/items/qquickmousearea.cpp
+++ b/src/quick/items/qquickmousearea.cpp
@@ -40,7 +40,6 @@
#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>
@@ -55,6 +54,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),
@@ -764,7 +765,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);
@@ -806,7 +808,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);
@@ -826,7 +829,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);
@@ -839,10 +843,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);
@@ -869,8 +874,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())
@@ -1002,7 +1008,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);
@@ -1083,8 +1090,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
@@ -1125,6 +1131,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();
@@ -1180,7 +1187,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;
@@ -1346,8 +1354,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..ac5598767a 100644
--- a/src/quick/items/qquickmultipointtoucharea.cpp
+++ b/src/quick/items/qquickmultipointtoucharea.cpp
@@ -940,8 +940,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/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..9d24a6c511
--- /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..ed56a76409
--- /dev/null
+++ b/src/quick/items/qquickopenglshadereffect_p.h
@@ -0,0 +1,194 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKOPENGLSHADEREFFECT_P_H
+#define QQUICKOPENGLSHADEREFFECT_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtQuick/qquickitem.h>
+
+#include <QtQuick/qsgmaterial.h>
+#include <private/qtquickglobal_p.h>
+#include <private/qsgadaptationlayer_p.h>
+#include <private/qquickopenglshadereffectnode_p.h>
+#include "qquickshadereffect_p.h"
+#include "qquickshadereffectmesh_p.h"
+
+#include <QtCore/qpointer.h>
+#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..5abe3ae3d0 100644
--- a/src/quick/items/qquickshadereffectnode_p.h
+++ b/src/quick/items/qquickopenglshadereffectnode_p.h
@@ -37,8 +37,8 @@
**
****************************************************************************/
-#ifndef QQUICKSHADEREFFECTNODE_P_H
-#define QQUICKSHADEREFFECTNODE_P_H
+#ifndef QQUICKOPENGLSHADEREFFECTNODE_P_H
+#define QQUICKOPENGLSHADEREFFECTNODE_P_H
//
// W A R N I N G
@@ -56,13 +56,14 @@
#include <QtQuick/qsgtextureprovider.h>
#include <QtQuick/qquickitem.h>
#include <private/qtquickglobal_p.h>
+#include <private/qquickshadereffect_p.h>
#include <QtCore/qsharedpointer.h>
#include <QtCore/qpointer.h>
QT_BEGIN_NAMESPACE
-struct QQuickShaderEffectMaterialKey {
+struct QQuickOpenGLShaderEffectMaterialKey {
enum ShaderType
{
VertexShader,
@@ -72,15 +73,15 @@ struct QQuickShaderEffectMaterialKey {
QByteArray sourceCode[ShaderTypeCount];
- bool operator == (const QQuickShaderEffectMaterialKey &other) const;
- bool operator != (const QQuickShaderEffectMaterialKey &other) const;
+ bool operator == (const QQuickOpenGLShaderEffectMaterialKey &other) const;
+ bool operator != (const QQuickOpenGLShaderEffectMaterialKey &other) const;
};
-uint qHash(const QQuickShaderEffectMaterialKey &key);
+uint qHash(const QQuickOpenGLShaderEffectMaterialKey &key);
class QQuickCustomMaterialShader;
-class QQuickShaderEffectNode;
-class Q_QUICK_PRIVATE_EXPORT QQuickShaderEffectMaterial : public QSGMaterial
+class QQuickOpenGLShaderEffectNode;
+class Q_QUICK_PRIVATE_EXPORT QQuickOpenGLShaderEffectMaterial : public QSGMaterial
{
public:
struct UniformData
@@ -89,30 +90,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 +131,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 +160,4 @@ private Q_SLOTS:
QT_END_NAMESPACE
-#endif // QQUICKSHADEREFFECTNODE_P_H
+#endif // QQUICKOPENGLSHADEREFFECTNODE_P_H
diff --git a/src/quick/items/qquickpainteditem.cpp b/src/quick/items/qquickpainteditem.cpp
index 75919d0791..d21eb93dbf 100644
--- a/src/quick/items/qquickpainteditem.cpp
+++ b/src/quick/items/qquickpainteditem.cpp
@@ -63,13 +63,14 @@ public:
\inmodule QtQuick
- The QQuickPaintedItem makes it possible to use the QPainter API with the QML Scene Graph.
- It sets up a textured rectangle in the Scene Graph and uses a QPainter to paint
- onto the texture. The render target can be either a QImage or a QOpenGLFramebufferObject.
- When the render target is a QImage, QPainter first renders into the image then
- the content is uploaded to the texture.
- When a QOpenGLFramebufferObject is used, QPainter paints directly onto the texture.
- Call update() to trigger a repaint.
+ The QQuickPaintedItem makes it possible to use the QPainter API with the
+ QML Scene Graph. It sets up a textured rectangle in the Scene Graph and
+ uses a QPainter to paint onto the texture. The render target can be either
+ a QImage or, when OpenGL is in use, a QOpenGLFramebufferObject. When the
+ render target is a QImage, QPainter first renders into the image then the
+ content is uploaded to the texture. When a QOpenGLFramebufferObject is
+ used, QPainter paints directly onto the texture. Call update() to trigger a
+ repaint.
To enable QPainter to do anti-aliased rendering, use setAntialiasing().
@@ -78,6 +79,10 @@ public:
public function: paint(), which implements the actual painting. The
painting will be inside the rectangle spanning from 0,0 to
width(),height().
+
+ \note It important to understand the performance implications such items
+ can incur. See QQuickPaintedItem::RenderTarget and
+ QQuickPaintedItem::renderTarget.
*/
/*!
@@ -172,8 +177,6 @@ QQuickPaintedItem::~QQuickPaintedItem()
is processed by the QML Scene Graph when the next frame is rendered. The item will only be
redrawn if it is visible.
- Note that calling this function will trigger a repaint of the whole scene.
-
\sa paint()
*/
void QQuickPaintedItem::update(const QRect &rect)
@@ -499,6 +502,12 @@ void QQuickPaintedItem::setFillColor(const QColor &c)
the QQuickPaintedItem::FramebufferObject render target if the item gets resized often.
By default, the render target is QQuickPaintedItem::Image.
+
+ \note Some Qt Quick backends may not support all render target options. For
+ example, it is likely that non-OpenGL backends will lack support for
+ QQuickPaintedItem::FramebufferObject and
+ QQuickPaintedItem::InvertedYFramebufferObject. Requesting these will then
+ be ignored.
*/
QQuickPaintedItem::RenderTarget QQuickPaintedItem::renderTarget() const
{
@@ -652,11 +661,13 @@ QSGTextureProvider *QQuickPaintedItem::textureProvider() const
return QQuickItem::textureProvider();
Q_D(const QQuickPaintedItem);
+#ifndef QT_NO_OPENGL
QQuickWindow *w = window();
if (!w || !w->openglContext() || QThread::currentThread() != w->openglContext()->thread()) {
qWarning("QQuickPaintedItem::textureProvider: can only be queried on the rendering thread of an exposed window");
return 0;
}
+#endif
if (!d->textureProvider)
d->textureProvider = new QQuickPaintedItemTextureProvider();
d->textureProvider->node = d->node;
diff --git a/src/quick/items/qquickpathview_p.h b/src/quick/items/qquickpathview_p.h
index daec965f02..bdd56b6311 100644
--- a/src/quick/items/qquickpathview_p.h
+++ b/src/quick/items/qquickpathview_p.h
@@ -53,6 +53,7 @@
#include "qquickitem.h"
+#include <private/qtquickglobal_p.h>
#include <private/qquickpath_p.h>
QT_BEGIN_NAMESPACE
@@ -61,7 +62,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..0441603a4b 100644
--- a/src/quick/items/qquickpathview_p_p.h
+++ b/src/quick/items/qquickpathview_p_p.h
@@ -61,6 +61,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 +76,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_p.h b/src/quick/items/qquickpositioners_p_p.h
index f0fbac2df7..d18f9b3724 100644
--- a/src/quick/items/qquickpositioners_p_p.h
+++ b/src/quick/items/qquickpositioners_p_p.h
@@ -137,9 +137,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 74ca0f482a..223976c32f 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>
@@ -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..8b69447002 100644
--- a/src/quick/items/qquickrendercontrol.cpp
+++ b/src/quick/items/qquickrendercontrol.cpp
@@ -44,7 +44,11 @@
#include <QtCore/QTime>
#include <QtQuick/private/qquickanimatorcontroller_p.h>
-#include <QtGui/QOpenGLContext>
+#ifndef QT_NO_OPENGL
+# include <QtGui/QOpenGLContext>
+# include <QtQuick/private/qsgdefaultrendercontext_p.h>
+# include <QtQuick/private/qquickopenglshadereffectnode_p.h>
+#endif
#include <QtGui/private/qguiapplication_p.h>
#include <qpa/qplatformintegration.h>
@@ -52,14 +56,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 +124,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 +141,7 @@ QQuickRenderControlPrivate::QQuickRenderControlPrivate()
qAddPostRoutine(cleanup);
sg = QSGContext::createDefaultContext();
}
- rc = new QSGRenderContext(sg);
+ rc = sg->createRenderContext();
}
void QQuickRenderControlPrivate::cleanup()
@@ -183,7 +190,9 @@ void QQuickRenderControlPrivate::windowDestroyed()
delete QQuickWindowPrivate::get(window)->animationController;
QQuickWindowPrivate::get(window)->animationController = 0;
- QQuickShaderEffectMaterial::cleanupMaterialCache();
+#ifndef QT_NO_OPENGL
+ QQuickOpenGLShaderEffectMaterial::cleanupMaterialCache();
+#endif
window = 0;
}
@@ -204,8 +213,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 +223,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 +240,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 +259,7 @@ void QQuickRenderControl::polishItems()
return;
QQuickWindowPrivate *cd = QQuickWindowPrivate::get(d->window);
- cd->flushDelayedTouchEvent();
+ cd->flushFrameSynchronousEvents();
if (!d->window)
return;
cd->polishItems();
@@ -362,8 +374,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/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..17b009039a 100644
--- a/src/quick/items/qquickshadereffect_p.h
+++ b/src/quick/items/qquickshadereffect_p.h
@@ -52,51 +52,13 @@
//
#include <QtQuick/qquickitem.h>
-
-#include <QtQuick/qsgmaterial.h>
#include <private/qtquickglobal_p.h>
-#include <private/qsgadaptationlayer_p.h>
-#include <private/qquickshadereffectnode_p.h>
-#include "qquickshadereffectmesh_p.h"
-
-#include <QtCore/qpointer.h>
QT_BEGIN_NAMESPACE
-const char *qtPositionAttributeName();
-const char *qtTexCoordAttributeName();
-
-class QSGContext;
-class QSignalMapper;
-class QQuickCustomMaterialShader;
-
-// Common class for QQuickShaderEffect and QQuickCustomParticle.
-struct Q_QUICK_PRIVATE_EXPORT QQuickShaderEffectCommon
-{
- typedef QQuickShaderEffectMaterialKey Key;
- typedef QQuickShaderEffectMaterial::UniformData UniformData;
-
- ~QQuickShaderEffectCommon();
- void disconnectPropertySignals(QQuickItem *item, Key::ShaderType shaderType);
- void connectPropertySignals(QQuickItem *item, Key::ShaderType shaderType);
- void updateParseLog(bool ignoreAttributes);
- void lookThroughShaderCode(QQuickItem *item, Key::ShaderType shaderType, const QByteArray &code);
- void updateShader(QQuickItem *item, Key::ShaderType shaderType);
- void updateMaterial(QQuickShaderEffectNode *node, QQuickShaderEffectMaterial *material,
- bool updateUniforms, bool updateUniformValues, bool updateTextureProviders);
- void updateWindow(QQuickWindow *window);
-
- // Called by slots in QQuickShaderEffect:
- void sourceDestroyed(QObject *object);
- void propertyChanged(QQuickItem *item, int mappedId, bool *textureProviderChanged);
-
- Key source;
- QVector<QByteArray> attributes;
- QVector<UniformData> uniformData[Key::ShaderTypeCount];
- QVector<QSignalMapper *> signalMappers[Key::ShaderTypeCount];
- QString parseLog;
-};
-
+class QQuickOpenGLShaderEffect;
+class QQuickGenericShaderEffect;
+class QQuickShaderEffectPrivate;
class Q_QUICK_PRIVATE_EXPORT QQuickShaderEffect : public QQuickItem
{
@@ -111,16 +73,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 +88,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 +124,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..8616bf8022 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::TypeUnsignedShort);
} 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..c5f1d19866 100644
--- a/src/quick/items/qquickshadereffectmesh_p.h
+++ b/src/quick/items/qquickshadereffectmesh_p.h
@@ -43,8 +43,8 @@
#include <QtGui/qcolor.h>
#include <QtCore/qobject.h>
#include <QtCore/qsize.h>
-#include <QtCore/qvariant.h>
-#include <QtGui/qopenglfunctions.h>
+#include <QtCore/qvector.h>
+#include <QtCore/qbytearray.h>
#ifndef QQUICKSHADEREFFECTMESH_P_H
#define QQUICKSHADEREFFECTMESH_P_H
@@ -62,6 +62,9 @@
QT_BEGIN_NAMESPACE
+const char *qtPositionAttributeName();
+const char *qtTexCoordAttributeName();
+
class QSGGeometry;
class QRectF;
@@ -70,8 +73,10 @@ class QQuickShaderEffectMesh : public QObject
Q_OBJECT
public:
QQuickShaderEffectMesh(QObject *parent = 0);
- // If 'geometry' != 0, 'attributes' is the same as last time the function was called.
- virtual QSGGeometry *updateGeometry(QSGGeometry *geometry, const QVector<QByteArray> &attributes, const QRectF &srcRect, const QRectF &rect) = 0;
+ virtual bool validateAttributes(const QVector<QByteArray> &attributes, int *posIndex) = 0;
+ // If 'geometry' != 0, 'attrCount' is the same as last time the function was called.
+ virtual QSGGeometry *updateGeometry(QSGGeometry *geometry, int attrCount, int posIndex,
+ const QRectF &srcRect, const QRectF &rect) = 0;
// If updateGeometry() fails, the reason should appear in the log.
virtual QString log() const { return QString(); }
@@ -86,7 +91,9 @@ class QQuickGridMesh : public QQuickShaderEffectMesh
Q_PROPERTY(QSize resolution READ resolution WRITE setResolution NOTIFY resolutionChanged)
public:
QQuickGridMesh(QObject *parent = 0);
- QSGGeometry *updateGeometry(QSGGeometry *geometry, const QVector<QByteArray> &attributes, const QRectF &srcRect, const QRectF &rect) Q_DECL_OVERRIDE;
+ bool validateAttributes(const QVector<QByteArray> &attributes, int *posIndex) Q_DECL_OVERRIDE;
+ QSGGeometry *updateGeometry(QSGGeometry *geometry, int attrCount, int posIndex,
+ const QRectF &srcRect, const QRectF &rect) Q_DECL_OVERRIDE;
QString log() const Q_DECL_OVERRIDE { return m_log; }
void setResolution(const QSize &res);
@@ -100,6 +107,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..ee18bf195a 100644
--- a/src/quick/items/qquickshadereffectsource_p.h
+++ b/src/quick/items/qquickshadereffectsource_p.h
@@ -55,7 +55,7 @@
#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 +93,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 +168,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..684d432f39 100644
--- a/src/quick/items/qquicksprite_p.h
+++ b/src/quick/items/qquicksprite_p.h
@@ -300,7 +300,7 @@ private Q_SLOTS:
private:
friend class QQuickImageParticle;
- friend class QQuickSpriteSequence;
+ //friend class QQuickSpriteSequence;
friend class QQuickAnimatedSprite;
friend class QQuickSpriteEngine;
friend class QQuickStochasticEngine;
diff --git a/src/quick/items/qquickspriteengine.cpp b/src/quick/items/qquickspriteengine.cpp
index df044988e0..083e02fe3f 100644
--- a/src/quick/items/qquickspriteengine.cpp
+++ b/src/quick/items/qquickspriteengine.cpp
@@ -111,7 +111,7 @@ QQuickSpriteEngine::QQuickSpriteEngine(QObject *parent)
}
QQuickSpriteEngine::QQuickSpriteEngine(QList<QQuickSprite*> sprites, QObject *parent)
- : QQuickStochasticEngine(parent), m_startedImageAssembly(false), m_loaded(false)
+ : QQuickSpriteEngine(parent)
{
foreach (QQuickSprite* sprite, sprites)
m_states << (QQuickStochasticState*)sprite;
@@ -372,7 +372,7 @@ void QQuickSpriteEngine::startAssemblingImage()
m_startedImageAssembly = true;
}
-QImage QQuickSpriteEngine::assembledImage()
+QImage QQuickSpriteEngine::assembledImage(int maxSize)
{
QQuickPixmap::Status stat = status();
if (!m_errorsPrinted && stat == QQuickPixmap::Error) {
@@ -389,16 +389,7 @@ 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){
if (state->frames() > m_maxFrames)
m_maxFrames = state->frames();
diff --git a/src/quick/items/qquickspriteengine_p.h b/src/quick/items/qquickspriteengine_p.h
index 65f58fafcb..1bdcc9a741 100644
--- a/src/quick/items/qquickspriteengine_p.h
+++ b/src/quick/items/qquickspriteengine_p.h
@@ -295,7 +295,7 @@ 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);
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..34af110a98 100644
--- a/src/quick/items/qquickspritesequence_p.h
+++ b/src/quick/items/qquickspritesequence_p.h
@@ -59,8 +59,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 +77,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 +93,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..4f352b5b69
--- /dev/null
+++ b/src/quick/items/qquickspritesequence_p_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 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 "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 59331647e5..4dc59b100a 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);
@@ -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);
@@ -2690,6 +2724,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/qquicktextedit.cpp b/src/quick/items/qquicktextedit.cpp
index 9b23abc877..d6d9d53a3b 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.
*/
@@ -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
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 03c6b892c4..0078c4ac21 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);
@@ -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)
@@ -4449,6 +4525,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 7ee01b5398..0e9d1e3951 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);
m_textures.append(texture);
node->setTargetRect(rect);
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 3ce96b673d..8b74d26576 100644
--- a/src/quick/items/qquickview.cpp
+++ b/src/quick/items/qquickview.cpp
@@ -44,9 +44,6 @@
#include "qquickitem_p.h"
#include "qquickitemchangelistener_p.h"
-#include <private/qqmldebugconnector_p.h>
-#include <private/qquickprofiler_p.h>
-#include <private/qqmldebugserviceinterfaces_p.h>
#include <private/qqmlmemoryprofiler_p.h>
#include <QtQml/qqmlengine.h>
@@ -131,14 +128,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);
}
/*!
@@ -194,9 +192,8 @@ QQuickView::QQuickView(QWindow *parent)
*/
QQuickView::QQuickView(const QUrl &source, QWindow *parent)
-: QQuickWindow(*(new QQuickViewPrivate), parent)
+ : QQuickView(parent)
{
- d_func()->init();
setSource(source);
}
@@ -433,9 +430,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());
}
}
diff --git a/src/quick/items/qquickview_p.h b/src/quick/items/qquickview_p.h
index 1bbff0de0e..c21468ef53 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 46ae993a18..a2c82c30a0 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();
}
@@ -746,6 +747,7 @@ void QQuickWindowPrivate::setMouseGrabber(QQuickItem *grabber)
if (mouseGrabberItem == grabber)
return;
+ qCDebug(DBG_MOUSE_TARGET) << "grabber" << mouseGrabberItem << "->" << grabber;
QQuickItem *oldGrabber = mouseGrabberItem;
mouseGrabberItem = grabber;
@@ -764,6 +766,51 @@ void QQuickWindowPrivate::setMouseGrabber(QQuickItem *grabber)
}
}
+void QQuickWindowPrivate::grabTouchPoints(QQuickItem *grabber, const QVector<int> &ids)
+{
+ Q_Q(QQuickWindow);
+ QSet<QQuickItem*> ungrab;
+ for (int i = 0; i < ids.count(); ++i) {
+ QQuickItem *oldGrabber = itemForTouchPointId.value(ids.at(i));
+ if (oldGrabber == grabber)
+ continue;
+
+ itemForTouchPointId[ids.at(i)] = grabber;
+ if (oldGrabber)
+ ungrab.insert(oldGrabber);
+
+ QQuickItem *originalMouseGrabberItem = mouseGrabberItem;
+ if (touchMouseId == ids.at(i) && mouseGrabberItem && mouseGrabberItem != grabber) {
+ qCDebug(DBG_MOUSE_TARGET) << "grabTouchPoints: grabber" << mouseGrabberItem << "-> null";
+ mouseGrabberItem = 0;
+ QEvent ev(QEvent::UngrabMouse);
+ q->sendEvent(originalMouseGrabberItem, &ev);
+ }
+ }
+ foreach (QQuickItem *oldGrabber, ungrab)
+ oldGrabber->touchUngrabEvent();
+}
+
+void QQuickWindowPrivate::removeGrabber(QQuickItem *grabber, bool mouse, bool touch)
+{
+ Q_Q(QQuickWindow);
+ if (Q_LIKELY(touch)) {
+ QMutableHashIterator<int, QQuickItem *> itemTouchMapIt(itemForTouchPointId);
+ while (itemTouchMapIt.hasNext()) {
+ if (itemTouchMapIt.next().value() == grabber) {
+ itemTouchMapIt.remove();
+ grabber->touchUngrabEvent();
+ }
+ }
+ }
+ if (Q_LIKELY(mouse) && mouseGrabberItem == grabber) {
+ qCDebug(DBG_MOUSE_TARGET) << "removeGrabber" << mouseGrabberItem << "-> null";
+ mouseGrabberItem = 0;
+ QEvent ev(QEvent::UngrabMouse);
+ q->sendEvent(grabber, &ev);
+ }
+}
+
void QQuickWindowPrivate::transformTouchPoints(QList<QTouchEvent::TouchPoint> &touchPoints, const QTransform &transform)
{
QMatrix4x4 transformMatrix(transform);
@@ -787,10 +834,6 @@ 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());
@@ -1120,8 +1163,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 +1178,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 +1193,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 +1212,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 +1237,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 +1348,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 +1368,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
@@ -1468,8 +1514,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
@@ -1651,71 +1696,6 @@ bool QQuickWindowPrivate::deliverMouseEvent(QMouseEvent *event)
return false;
}
-/*! \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();
-
- if (!d->mouseGrabberItem && (event->buttons() & event->button()) == event->buttons()) {
- if (d->deliverInitialMousePressEvent(d->contentItem, event))
- event->accept();
- else
- event->ignore();
- return;
- }
-
- d->deliverMouseEvent(event);
-}
-
bool QQuickWindowPrivate::sendHoverEvent(QEvent::Type type, QQuickItem *item,
const QPointF &scenePos, const QPointF &lastScenePos,
Qt::KeyboardModifiers modifiers, ulong timestamp,
@@ -1739,43 +1719,6 @@ bool QQuickWindowPrivate::sendHoverEvent(QEvent::Type type, QQuickItem *item,
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)
{
@@ -1788,13 +1731,16 @@ 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) {
@@ -1966,87 +1912,195 @@ 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());
+ deliverTouchEvent(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);
+ qCDebug(DBG_TOUCH) << event;
- // matching touch event? then merge the new event into the old one
- if (!mismatch) {
- delayedTouch->setTouchPoints(tpts);
- delayedTouch->setTimestamp(event->timestamp());
- return;
- }
- }
+ if (qquickwindow_no_touch_compression || touchRecursionGuard) {
+ deliverTouchEvent(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());
+ deliverTouchEvent(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)
+{
+ 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());
+ deliverMouseEvent(event);
+ break;
+ case QEvent::MouseButtonRelease:
+ Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseRelease, event->button(),
+ event->buttons());
+ if (!mouseGrabberItem) {
+ event->ignore();
return;
}
- } else {
- if (delayedTouch)
- deliverDelayedTouchEvent();
- reallyDeliverTouchEvent(event);
+
+ deliverMouseEvent(event);
+ if (mouseGrabberItem && !event->buttons())
+ mouseGrabberItem->ungrabMouse();
+ break;
+ case QEvent::MouseButtonDblClick:
+ Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseDoubleClick,
+ event->button(), event->buttons());
+
+ if (!mouseGrabberItem && (event->buttons() & event->button()) == event->buttons()) {
+ if (deliverInitialMousePressEvent(contentItem, event))
+ event->accept();
+ else
+ event->ignore();
+ return;
+ }
+
+ deliverMouseEvent(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 (!mouseGrabberItem) {
+ if (lastMousePosition.isNull())
+ lastMousePosition = event->windowPos();
+ QPointF last = 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;
+ }
+ deliverMouseEvent(event);
+ break;
+ default:
+ Q_ASSERT(false);
+ break;
}
}
-void QQuickWindowPrivate::flushDelayedTouchEvent()
+void QQuickWindowPrivate::flushFrameSynchronousEvents()
{
if (delayedTouch) {
deliverDelayedTouchEvent();
@@ -2057,9 +2111,20 @@ 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 (!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)
+void QQuickWindowPrivate::deliverTouchEvent(QTouchEvent *event)
{
qCDebug(DBG_TOUCH) << " - delivering" << event;
@@ -2209,7 +2274,7 @@ bool QQuickWindowPrivate::deliverTouchPoints(QQuickItem *item, QTouchEvent *even
return (acceptedNewPoints->count() == newPoints.count() && updatedPoints->isEmpty());
}
-// touchEventForItemBounds has no means to generate a touch event that contains
+// touchEventForItem 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.
@@ -2271,7 +2336,8 @@ bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QTouchEv
return touchEventAccepted;
}
-QTouchEvent *QQuickWindowPrivate::touchEventForItemBounds(QQuickItem *target, const QTouchEvent &originalEvent)
+// create touch event containing only points inside the target item
+QTouchEvent *QQuickWindowPrivate::touchEventForItem(QQuickItem *target, const QTouchEvent &originalEvent, bool alwaysCheckBounds)
{
const QList<QTouchEvent::TouchPoint> &touchPoints = originalEvent.touchPoints();
QList<QTouchEvent::TouchPoint> pointsInBounds;
@@ -2279,7 +2345,10 @@ QTouchEvent *QQuickWindowPrivate::touchEventForItemBounds(QQuickItem *target, co
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) {
+ // Touch presses are relevant to the target item only if they occur inside its bounds.
+ // Touch updates and releases are relevant if they occur inside, or if we want to
+ // finish the sequence because the press occurred inside.
+ if (tp.state() == Qt::TouchPointPressed || alwaysCheckBounds) {
QPointF p = target->mapFromScene(tp.scenePos());
if (target->contains(p))
pointsInBounds.append(tp);
@@ -2295,6 +2364,7 @@ QTouchEvent *QQuickWindowPrivate::touchEventForItemBounds(QQuickItem *target, co
return touchEvent;
}
+// copy a touch event's basic properties but give it new touch points
QTouchEvent *QQuickWindowPrivate::touchEventWithPoints(const QTouchEvent &event, const QList<QTouchEvent::TouchPoint> &newPoints)
{
Qt::TouchPointStates eventStates;
@@ -2478,7 +2548,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,7 +2578,7 @@ 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));
+ QScopedPointer<QTouchEvent> targetEvent(touchEventForItem(target, *event));
if (!targetEvent->touchPoints().isEmpty()) {
if (target->childMouseEventFilter(item, targetEvent.data())) {
qCDebug(DBG_TOUCH) << " - first chance intercepted on childMouseEventFilter by " << target;
@@ -2588,6 +2658,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;
@@ -2608,6 +2679,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
@@ -3174,10 +3254,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;
@@ -3200,17 +3280,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, or the scene graph is not using OpenGL,
+ this function will return null.
- If the scene graph is not ready, this function will return 0.
+ \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;
}
/*!
@@ -3225,7 +3318,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.
*/
@@ -3246,14 +3341,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.
*/
@@ -3264,7 +3359,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.
@@ -3326,13 +3421,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.
@@ -3355,7 +3454,7 @@ void QQuickWindow::setRenderTarget(QOpenGLFramebufferObject *fbo)
d->renderTargetSize = QSize();
}
}
-
+#endif
/*!
\overload
@@ -3365,6 +3464,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.
@@ -3406,19 +3509,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.
@@ -3435,33 +3542,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)
@@ -3503,7 +3619,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.
@@ -3518,7 +3634,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
@@ -3535,7 +3651,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
@@ -3556,15 +3672,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()
@@ -3576,16 +3693,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.
@@ -3597,16 +3714,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.
@@ -3641,6 +3758,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
*/
@@ -3653,15 +3774,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
@@ -3672,7 +3793,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.
@@ -3713,7 +3834,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
@@ -3730,9 +3852,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.
@@ -3751,7 +3873,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;
@@ -3763,7 +3885,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.
@@ -3774,15 +3896,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);
@@ -3790,6 +3915,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;
}
@@ -3859,7 +3989,7 @@ void QQuickWindow::setDefaultAlphaBuffer(bool useAlpha)
{
QQuickWindowPrivate::defaultAlphaBuffer = useAlpha;
}
-
+#ifndef QT_NO_OPENGL
/*!
\since 5.2
@@ -3877,6 +4007,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()
@@ -3931,7 +4064,7 @@ void QQuickWindow::resetOpenGLState()
QOpenGLFramebufferObject::bindDefault();
}
-
+#endif
/*!
\qmlproperty string Window::title
@@ -4340,7 +4473,7 @@ void QQuickWindowPrivate::runAndClearJobs(QList<QRunnable *> *jobs)
jobs->clear();
renderJobMutex.unlock();
- foreach (QRunnable *r, jobList) {
+ for (QRunnable *r : qAsConst(jobList)) {
r->run();
delete r;
}
@@ -4370,6 +4503,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..40a361a897 100644
--- a/src/quick/items/qquickwindow_p.h
+++ b/src/quick/items/qquickwindow_p.h
@@ -143,6 +143,8 @@ public:
bool translateTouchToMouse(QQuickItem *item, QTouchEvent *event);
void translateTouchEvent(QTouchEvent *touchEvent);
void setMouseGrabber(QQuickItem *grabber);
+ void grabTouchPoints(QQuickItem *grabber, const QVector<int> &ids);
+ void removeGrabber(QQuickItem *grabber, bool mouse = true, bool touch = true);
static void transformTouchPoints(QList<QTouchEvent::TouchPoint> &touchPoints, const QTransform &transform);
static QMouseEvent *cloneMouseEvent(QMouseEvent *event, QPointF *transformedLocalPos = 0);
bool deliverInitialMousePressEvent(QQuickItem *, QMouseEvent *);
@@ -156,15 +158,17 @@ public:
#endif
bool deliverTouchPoints(QQuickItem *, QTouchEvent *, const QList<QTouchEvent::TouchPoint> &, QSet<int> *,
QHash<QQuickItem *, QList<QTouchEvent::TouchPoint> > *, QSet<QQuickItem*> *filtered);
+ void handleTouchEvent(QTouchEvent *);
+ void handleMouseEvent(QMouseEvent *);
void deliverTouchEvent(QTouchEvent *);
- void reallyDeliverTouchEvent(QTouchEvent *);
+ bool compressTouchEvent(QTouchEvent *);
bool deliverTouchCancelEvent(QTouchEvent *);
void deliverDelayedTouchEvent();
- void flushDelayedTouchEvent();
+ void flushFrameSynchronousEvents();
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);
+ static QTouchEvent *touchEventForItem(QQuickItem *target, const QTouchEvent &originalEvent, bool alwaysCheckBounds = false);
+ static 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);
@@ -263,6 +267,7 @@ public:
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 +293,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 +310,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..c36adf56ec 100644
--- a/src/quick/qtquick2.cpp
+++ b/src/quick/qtquick2.cpp
@@ -49,7 +49,6 @@
#include <private/qqmldebugstatesdelegate_p.h>
#include <private/qqmlbinding_p.h>
#include <private/qqmlcontext_p.h>
-#include <private/qquickprofiler_p.h>
#include <private/qquickapplication_p.h>
#include <QtQuick/private/qquickpropertychanges_p.h>
#include <QtQuick/private/qquickstate_p.h>
@@ -63,6 +62,12 @@ static void initResources()
QT_BEGIN_NAMESPACE
+#ifdef QT_NO_QML_DEBUGGER
+
+class QQmlQtQuick2DebugStatesDelegate : public QQmlDebugStatesDelegate {};
+
+#else
+
class QQmlQtQuick2DebugStatesDelegate : public QQmlDebugStatesDelegate
{
public:
@@ -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/quick.pro b/src/quick/quick.pro
index 1c14ff8d57..f74a554aa9 100644
--- a/src/quick/quick.pro
+++ b/src/quick/quick.pro
@@ -1,12 +1,17 @@
TARGET = QtQuick
QT = core-private gui-private qml-private
-QT_PRIVATE = network
+!no_network {
+ QT_PRIVATE = network
+}
+no_network {
+ DEFINES += QT_NO_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,7 +32,7 @@ ANDROID_BUNDLED_FILES += \
include(util/util.pri)
include(scenegraph/scenegraph.pri)
include(items/items.pri)
-!wince:include(designer/designer.pri)
+include(designer/designer.pri)
contains(QT_CONFIG, 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..a6cb99ae05
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer.cpp
@@ -0,0 +1,330 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgabstractsoftwarerenderer_p.h"
+
+#include "qsgsoftwarerenderablenodeupdater_p.h"
+#include "qsgsoftwarerenderlistbuilder_p.h"
+#include "qsgsoftwarecontext_p.h"
+#include "qsgsoftwarerenderablenode_p.h"
+
+#include <QtCore/QLoggingCategory>
+#include <QtGui/QWindow>
+#include <QtQuick/QSGSimpleRectNode>
+
+Q_LOGGING_CATEGORY(lc2DRender, "qt.scenegraph.softwarecontext.abstractrenderer")
+
+QT_BEGIN_NAMESPACE
+
+QSGAbstractSoftwareRenderer::QSGAbstractSoftwareRenderer(QSGRenderContext *context)
+ : QSGRenderer(context)
+ , m_background(new QSGSimpleRectNode)
+ , m_nodeUpdater(new QSGSoftwareRenderableNodeUpdater(this))
+{
+ // Setup special background node
+ auto backgroundRenderable = new QSGSoftwareRenderableNode(QSGSoftwareRenderableNode::SimpleRect, m_background);
+ addNodeMapping(m_background, backgroundRenderable);
+}
+
+QSGAbstractSoftwareRenderer::~QSGAbstractSoftwareRenderer()
+{
+ // Cleanup RenderableNodes
+ delete m_background;
+
+ for (QSGSoftwareRenderableNode *node : m_nodes.values()) {
+ delete node;
+ }
+
+ delete m_nodeUpdater;
+}
+
+QSGSoftwareRenderableNode *QSGAbstractSoftwareRenderer::renderableNode(QSGNode *node) const
+{
+ return m_nodes.value(node, nullptr);
+}
+
+void QSGAbstractSoftwareRenderer::addNodeMapping(QSGNode *node, QSGSoftwareRenderableNode *renderableNode)
+{
+ m_nodes.insert(node, renderableNode);
+}
+
+void QSGAbstractSoftwareRenderer::appendRenderableNode(QSGSoftwareRenderableNode *node)
+{
+ m_renderableNodes.append(node);
+}
+
+void QSGAbstractSoftwareRenderer::nodeChanged(QSGNode *node, QSGNode::DirtyState state)
+{
+ if (state & QSGNode::DirtyGeometry) {
+ nodeGeometryUpdated(node);
+ }
+ if (state & QSGNode::DirtyMaterial) {
+ nodeMaterialUpdated(node);
+ }
+ if (state & QSGNode::DirtyMatrix) {
+ nodeMatrixUpdated(node);
+ }
+ if (state & QSGNode::DirtyNodeAdded) {
+ nodeAdded(node);
+ }
+ if (state & QSGNode::DirtyNodeRemoved) {
+ nodeRemoved(node);
+ }
+ if (state & QSGNode::DirtyOpacity) {
+ nodeOpacityUpdated(node);
+ }
+ if (state & QSGNode::DirtySubtreeBlocked) {
+ m_nodeUpdater->updateNodes(node);
+ }
+ if (state & QSGNode::DirtyForceUpdate) {
+ m_nodeUpdater->updateNodes(node);
+ }
+ QSGRenderer::nodeChanged(node, state);
+}
+
+QRegion QSGAbstractSoftwareRenderer::renderNodes(QPainter *painter)
+{
+ QRegion dirtyRegion;
+ // If there are no nodes, do nothing
+ if (m_renderableNodes.isEmpty())
+ return dirtyRegion;
+
+ auto iterator = m_renderableNodes.begin();
+ // First node is the background and needs to painted without blending
+ auto backgroundNode = *iterator;
+ dirtyRegion += backgroundNode->renderNode(painter, /*force opaque painting*/ true);
+ iterator++;
+
+ for (; iterator != m_renderableNodes.end(); ++iterator) {
+ auto node = *iterator;
+ dirtyRegion += node->renderNode(painter);
+ }
+
+ return dirtyRegion;
+}
+
+void QSGAbstractSoftwareRenderer::buildRenderList()
+{
+ // Clear the previous renderlist
+ m_renderableNodes.clear();
+ // Add the background renderable (always first)
+ m_renderableNodes.append(renderableNode(m_background));
+ // Build the renderlist
+ QSGSoftwareRenderListBuilder(this).visitChildren(rootNode());
+}
+
+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..144e75d3e6
--- /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");
+}
+
+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..80112c1121
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext.cpp
@@ -0,0 +1,220 @@
+/****************************************************************************
+**
+** 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"
+#include "qsgsoftwarespritenode_p.h"
+
+#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;
+}
+
+QSGSpriteNode *QSGSoftwareContext::createSpriteNode()
+{
+ return new QSGSoftwareSpriteNode;
+}
+
+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..dcc137b4b4
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarecontext_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 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;
+ QSGSpriteNode *createSpriteNode() override;
+
+ 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..032a06f946
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp
@@ -0,0 +1,414 @@
+/****************************************************************************
+**
+** 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"
+#include "qsgsoftwarespritenode_p.h"
+
+#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;
+ case QSGSoftwareRenderableNode::SpriteNode:
+ m_handle.spriteNode = static_cast<QSGSoftwareSpriteNode*>(node);
+ break;
+ 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;
+ case QSGSoftwareRenderableNode::SpriteNode:
+ m_isOpaque = m_handle.spriteNode->isOpaque();
+ boundingRect = m_handle.spriteNode->rect().toRect();
+ break;
+ case QSGSoftwareRenderableNode::RenderNode:
+ 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 { return QRect(); }
+ bool scissorEnabled() const { return false; }
+ int stencilValue() const { return 0; }
+ bool stencilEnabled() const { return false; }
+ const QRegion *clipRegion() const { 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;
+ painter->save();
+ m_handle.renderNode->render(&rs);
+ painter->restore();
+ const QRect fullRect = QRect(0, 0, painter->device()->width(), painter->device()->height());
+ m_previousDirtyRegion = fullRect;
+ m_isDirty = false;
+ m_dirtyRegion = QRegion();
+ return fullRect;
+ }
+ }
+
+ 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;
+ case QSGSoftwareRenderableNode::SpriteNode:
+ static_cast<QSGSoftwareSpriteNode *>(m_handle.spriteNode)->paint(painter);
+ break;
+ default:
+ break;
+ }
+
+ painter->restore();
+
+ QRegion areaToBeFlushed = m_dirtyRegion;
+ m_previousDirtyRegion = QRegion(m_boundingRect);
+ m_isDirty = false;
+ m_dirtyRegion = QRegion();
+
+ return areaToBeFlushed;
+}
+
+QRect QSGSoftwareRenderableNode::boundingRect() const
+{
+ // This returns the bounding area of a renderable node in world coordinates
+ return m_boundingRect;
+}
+
+bool QSGSoftwareRenderableNode::isDirtyRegionEmpty() const
+{
+ return m_dirtyRegion.isEmpty();
+}
+
+void QSGSoftwareRenderableNode::setTransform(const QTransform &transform)
+{
+ if (m_transform == transform)
+ return;
+ m_transform = transform;
+ update();
+}
+
+void QSGSoftwareRenderableNode::setClipRegion(const QRegion &clipRect, 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..0626b1e657
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode_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 QSGSOFTWARERENDERABLENODE_H
+#define QSGSOFTWARERENDERABLENODE_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtGui/QRegion>
+#include <QtCore/QRect>
+#include <QtGui/QTransform>
+#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,
+ SpriteNode,
+ 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..12dbf63353
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater.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 "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 *)
+{
+}
+
+bool QSGSoftwareRenderableNodeUpdater::visit(QSGSpriteNode *node)
+{
+ return updateRenderableNode(QSGSoftwareRenderableNode::SpriteNode, node);
+}
+
+void QSGSoftwareRenderableNodeUpdater::endVisit(QSGSpriteNode *)
+{
+
+}
+
+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..f204867236
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenodeupdater_p.h
@@ -0,0 +1,141 @@
+/****************************************************************************
+**
+** 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;
+ bool visit(QSGSpriteNode *) override;
+ void endVisit(QSGSpriteNode *) override;
+ 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..ae89ed7d8a
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer.cpp
@@ -0,0 +1,156 @@
+/****************************************************************************
+**
+** 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)
+ m_paintDevice = m_backingStore->paintDevice();
+
+ 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);
+
+ 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..4e34517dad
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderlistbuilder.cpp
@@ -0,0 +1,174 @@
+/****************************************************************************
+**
+** 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 *)
+{
+}
+
+bool QSGSoftwareRenderListBuilder::visit(QSGSpriteNode *node)
+{
+ return addRenderableNode(node);
+}
+
+void QSGSoftwareRenderListBuilder::endVisit(QSGSpriteNode *)
+{
+
+}
+
+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..807cb7fdbe
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderlistbuilder_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 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;
+ bool visit(QSGSpriteNode *) override;
+ void endVisit(QSGSpriteNode *) override;
+ 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..284ed3dff5
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarespritenode_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 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/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..97644fc36a
--- /dev/null
+++ b/src/quick/scenegraph/adaptations/software/software.pri
@@ -0,0 +1,43 @@
+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/qsgsoftwarespritenode.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/qsgsoftwarespritenode_p.h \
+ $$PWD/qsgsoftwarethreadedrenderloop_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..bee2015007 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...
@@ -1101,7 +1102,7 @@ void Renderer::nodeWasRemoved(Node *node)
if (m_renderNodeElements.isEmpty()) {
static bool useDepth = qEnvironmentVariableIsEmpty("QSG_NO_DEPTH_BUFFER");
- m_useDepthBuffer = useDepth && context()->openglContext()->format().depthBufferSize() > 0;
+ m_useDepthBuffer = useDepth && m_context->openglContext()->format().depthBufferSize() > 0;
}
}
}
@@ -2760,6 +2761,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 +2788,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 +2822,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 +2840,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 +2884,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..239557d527 100644
--- a/src/quick/scenegraph/coreapi/qsggeometry.cpp
+++ b/src/quick/scenegraph/coreapi/qsggeometry.cpp
@@ -39,10 +39,11 @@
#include "qsggeometry.h"
#include "qsggeometry_p.h"
-
-#include <qopenglcontext.h>
-#include <qopenglfunctions.h>
-#include <private/qopenglextensions_p.h>
+#ifndef QT_NO_OPENGL
+# include <qopenglcontext.h>
+# include <qopenglfunctions.h>
+# include <private/qopenglextensions_p.h>
+#endif
#ifdef Q_OS_QNX
#include <malloc.h>
@@ -53,10 +54,21 @@ QT_BEGIN_NAMESPACE
QSGGeometry::Attribute QSGGeometry::Attribute::create(int attributeIndex, int tupleSize, int primitiveType, bool isPrimitive)
{
- Attribute a = { attributeIndex, tupleSize, primitiveType, isPrimitive, 0 };
+ Attribute a = { attributeIndex, tupleSize, primitiveType, isPrimitive, UNKNOWN, 0 };
return a;
}
+QSGGeometry::Attribute QSGGeometry::Attribute::createWithSemantic(int pos, int tupleSize, int type, Semantic semantic)
+{
+ Attribute a;
+ a.position = pos;
+ a.tupleSize = tupleSize;
+ a.type = type;
+ a.isVertexCoordinate = semantic == POSITION;
+ a.semantic = semantic;
+ a.reserved = 0;
+ return a;
+}
/*!
Convenience function which returns attributes to be used for 2D solid
@@ -66,7 +78,7 @@ QSGGeometry::Attribute QSGGeometry::Attribute::create(int attributeIndex, int tu
const QSGGeometry::AttributeSet &QSGGeometry::defaultAttributes_Point2D()
{
static Attribute data[] = {
- QSGGeometry::Attribute::create(0, 2, GL_FLOAT, true)
+ Attribute::createWithSemantic(0, 2, TypeFloat, Attribute::POSITION)
};
static AttributeSet attrs = { 1, sizeof(float) * 2, data };
return attrs;
@@ -79,8 +91,8 @@ const QSGGeometry::AttributeSet &QSGGeometry::defaultAttributes_Point2D()
const QSGGeometry::AttributeSet &QSGGeometry::defaultAttributes_TexturedPoint2D()
{
static Attribute data[] = {
- QSGGeometry::Attribute::create(0, 2, GL_FLOAT, true),
- QSGGeometry::Attribute::create(1, 2, GL_FLOAT)
+ Attribute::createWithSemantic(0, 2, TypeFloat, Attribute::POSITION),
+ Attribute::createWithSemantic(1, 2, TypeFloat, Attribute::TEXCOORD)
};
static AttributeSet attrs = { 2, sizeof(float) * 4, data };
return attrs;
@@ -93,8 +105,8 @@ const QSGGeometry::AttributeSet &QSGGeometry::defaultAttributes_TexturedPoint2D(
const QSGGeometry::AttributeSet &QSGGeometry::defaultAttributes_ColoredPoint2D()
{
static Attribute data[] = {
- QSGGeometry::Attribute::create(0, 2, GL_FLOAT, true),
- QSGGeometry::Attribute::create(1, 4, GL_UNSIGNED_BYTE)
+ Attribute::createWithSemantic(0, 2, TypeFloat, Attribute::POSITION),
+ Attribute::createWithSemantic(1, 4, TypeUnsignedByte, Attribute::COLOR)
};
static AttributeSet attrs = { 2, 2 * sizeof(float) + 4 * sizeof(char), data };
return attrs;
@@ -206,9 +218,9 @@ const QSGGeometry::AttributeSet &QSGGeometry::defaultAttributes_ColoredPoint2D()
The QSGGeometry class stores the geometry of the primitives
rendered with the scene graph. It contains vertex data and
optionally index data. The mode used to draw the geometry is
- specified with setDrawingMode(), which maps directly to the OpenGL
+ specified with setDrawingMode(), which maps directly to the graphics API's
drawing mode, such as \c GL_TRIANGLE_STRIP, \c GL_TRIANGLES, or
- \c GL_POINTS.
+ \c GL_POINTS in case of OpenGL.
Vertices can be as simple as points defined by x and y values or
can be more complex where each vertex contains a normal, texture
@@ -394,7 +406,7 @@ QSGGeometry::QSGGeometry(const QSGGeometry::AttributeSet &attributes,
int vertexCount,
int indexCount,
int indexType)
- : m_drawing_mode(GL_TRIANGLE_STRIP)
+ : m_drawing_mode(DrawTriangleStrip)
, m_vertex_count(0)
, m_index_count(0)
, m_index_type(indexType)
@@ -410,21 +422,20 @@ QSGGeometry::QSGGeometry(const QSGGeometry::AttributeSet &attributes,
Q_UNUSED(m_reserved_bits);
Q_ASSERT(m_attributes.count > 0);
Q_ASSERT(m_attributes.stride > 0);
-
+#ifndef QT_NO_OPENGL
Q_ASSERT_X(indexType != GL_UNSIGNED_INT
|| static_cast<QOpenGLExtensions *>(QOpenGLContext::currentContext()->functions())
->hasOpenGLExtension(QOpenGLExtensions::ElementIndexUint),
"QSGGeometry::QSGGeometry",
"GL_UNSIGNED_INT is not supported, geometry will not render"
);
-
- if (indexType != GL_UNSIGNED_BYTE
- && indexType != GL_UNSIGNED_SHORT
- && indexType != GL_UNSIGNED_INT) {
+#endif
+ if (indexType != TypeUnsignedByte
+ && indexType != TypeUnsignedShort
+ && indexType != TypeUnsignedInt) {
qFatal("QSGGeometry: Unsupported index type, %x.\n", indexType);
}
-
// Because allocate reads m_vertex_count, m_index_count and m_owns_data, these
// need to be set before calling allocate...
allocate(vertexCount, indexCount);
@@ -516,23 +527,63 @@ const void *QSGGeometry::indexData() const
}
/*!
+ \enum QSGGeometry::DrawingMode
+
+ The values correspond to OpenGL enum values like \c GL_POINTS, \c GL_LINES,
+ etc. QSGGeometry provies its own type in order to be able to provide the
+ same API with non-OpenGL backends as well.
+
+ \value DrawPoints
+ \value DrawLines
+ \value DrawLineLoop
+ \value DrawLineStrip
+ \value DrawTriangles
+ \value DrawTriangleStrip
+ \value DrawTriangleFan
+ */
+
+/*!
+ \enum QSGGeometry::Type
+
+ The values correspond to OpenGL type constants like \c GL_BYTE, \c
+ GL_UNSIGNED_BYTE, etc. QSGGeometry provies its own type in order to be able
+ to provide the same API with non-OpenGL backends as well.
+
+ \value TypeByte
+ \value TypeUnsignedByte
+ \value TypeShort
+ \value TypeUnsignedShort
+ \value TypeInt
+ \value TypeUnsignedInt
+ \value TypeFloat
+ */
+
+/*!
Sets the \a mode to be used for drawing this geometry.
- The default value is \c GL_TRIANGLE_STRIP.
+ The default value is QSGGeometry::DrawTriangleStrip.
+
+ \sa DrawingMode
*/
-void QSGGeometry::setDrawingMode(GLenum mode)
+void QSGGeometry::setDrawingMode(unsigned int mode)
{
m_drawing_mode = mode;
}
/*!
- Gets the current line or point width or to be used for this geometry. This property
- only applies to line width when the drawingMode is \c GL_LINES, \c GL_LINE_STRIP, or
- \c GL_LINE_LOOP. For desktop OpenGL, it also applies to point size when the drawingMode
- is \c GL_POINTS.
+ Gets the current line or point width or to be used for this geometry. This
+ property only applies to line width when the drawingMode is DrawLines,
+ DarwLineStrip, or DrawLineLoop. For desktop OpenGL, it also applies to
+ point size when the drawingMode is DrawPoints.
The default value is \c 1.0
+ \note When not using OpenGL, support for point and line drawing may be
+ limited. For example, some APIs do not support point sprites and so setting
+ a size other than 1 is not possible. Some backends may be able implement
+ support via geometry shaders, but this is not guaranteed to be always
+ available.
+
\sa setLineWidth(), drawingMode()
*/
float QSGGeometry::lineWidth() const
@@ -541,14 +592,15 @@ float QSGGeometry::lineWidth() const
}
/*!
- Sets the line or point width to be used for this geometry to \a width. This property
- only applies to line width when the drawingMode is \c GL_LINES, \c GL_LINE_STRIP, or
- \c GL_LINE_LOOP. For Desktop OpenGL, it also applies to point size when the drawingMode
- is \c GL_POINTS.
+ Sets the line or point width to be used for this geometry to \a width. This
+ property only applies to line width when the drawingMode is DrawLines,
+ DrawLineStrip, or DrawLineLoop. For Desktop OpenGL, it also applies to
+ point size when the drawingMode is DrawPoints.
- \note How line width and point size are treated is implementation dependent: The application
- should not rely on these, but rather create triangles or similar to draw areas. On OpenGL ES,
- line width support is limited and point size is unsupported.
+ \note How line width and point size are treated is implementation
+ dependent: The application should not rely on these, but rather create
+ triangles or similar to draw areas. On OpenGL ES, line width support is
+ limited and point size is unsupported.
\sa lineWidth(), drawingMode()
*/
@@ -601,8 +653,8 @@ void QSGGeometry::allocate(int vertexCount, int indexCount)
m_index_data_offset = -1;
m_owns_data = false;
} else {
- Q_ASSERT(m_index_type == GL_UNSIGNED_INT || m_index_type == GL_UNSIGNED_SHORT);
- int indexByteSize = indexCount * (m_index_type == GL_UNSIGNED_SHORT ? sizeof(quint16) : sizeof(quint32));
+ Q_ASSERT(m_index_type == TypeUnsignedInt || m_index_type == TypeUnsignedShort);
+ int indexByteSize = indexCount * (m_index_type == TypeUnsignedShort ? sizeof(quint16) : sizeof(quint32));
m_data = (void *) malloc(vertexByteSize + indexByteSize);
m_index_data_offset = vertexByteSize;
m_owns_data = true;
@@ -674,6 +726,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..ae7b2f494c 100644
--- a/src/quick/scenegraph/coreapi/qsggeometry.h
+++ b/src/quick/scenegraph/coreapi/qsggeometry.h
@@ -54,15 +54,27 @@ public:
struct Q_QUICK_EXPORT Attribute
{
+ enum Semantic {
+ UNKNOWN,
+ POSITION,
+ COLOR,
+ TEXCOORD,
+ TEXCOORD1,
+ TEXCOORD2
+ };
+
int position;
int tupleSize;
int type;
uint isVertexCoordinate : 1;
- uint reserved : 31;
+ Semantic semantic : 4;
+
+ uint reserved : 27;
static Attribute create(int pos, int tupleSize, int primitiveType, bool isPosition = false);
+ static Attribute createWithSemantic(int pos, int tupleSize, int type, Semantic semantic);
};
struct AttributeSet {
@@ -104,14 +116,37 @@ public:
StaticPattern = 3
};
+ // Equivalents to GL_* drawing modes.
+ enum DrawingMode {
+ DrawPoints = 0x0000,
+ DrawLines = 0x0001,
+ DrawLineLoop = 0x0002,
+ DrawLineStrip = 0x0003,
+ DrawTriangles = 0x0004,
+ DrawTriangleStrip = 0x0005,
+ DrawTriangleFan = 0x0006
+ };
+
+ // Equivalents to GL_BYTE and similar type constants.
+ enum Type {
+ TypeByte = 0x1400,
+ TypeUnsignedByte = 0x1401,
+ TypeShort = 0x1402,
+ TypeUnsignedShort = 0x1403,
+ TypeInt = 0x1404,
+ TypeUnsignedInt = 0x1405,
+ TypeFloat = 0x1406
+ };
+
QSGGeometry(const QSGGeometry::AttributeSet &attribs,
int vertexCount,
int indexCount = 0,
- int indexType = GL_UNSIGNED_SHORT);
+ int indexType = TypeUnsignedShort);
virtual ~QSGGeometry();
- void setDrawingMode(GLenum mode);
- inline GLenum drawingMode() const { return m_drawing_mode; }
+ // must use unsigned int to be compatible with the old GLenum to keep BC
+ void setDrawingMode(unsigned int mode);
+ inline unsigned int drawingMode() const { return m_drawing_mode; }
void allocate(int vertexCount, int indexCount = 0);
@@ -147,6 +182,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 +223,25 @@ private:
inline uint *QSGGeometry::indexDataAsUInt()
{
- Q_ASSERT(m_index_type == GL_UNSIGNED_INT);
+ Q_ASSERT(m_index_type == TypeUnsignedInt);
return static_cast<uint *>(indexData());
}
inline quint16 *QSGGeometry::indexDataAsUShort()
{
- Q_ASSERT(m_index_type == GL_UNSIGNED_SHORT);
+ Q_ASSERT(m_index_type == TypeUnsignedShort);
return static_cast<quint16 *>(indexData());
}
inline const uint *QSGGeometry::indexDataAsUInt() const
{
- Q_ASSERT(m_index_type == GL_UNSIGNED_INT);
+ Q_ASSERT(m_index_type == TypeUnsignedInt);
return static_cast<const uint *>(indexData());
}
inline const quint16 *QSGGeometry::indexDataAsUShort() const
{
- Q_ASSERT(m_index_type == GL_UNSIGNED_SHORT);
+ Q_ASSERT(m_index_type == TypeUnsignedShort);
return static_cast<const quint16 *>(indexData());
}
@@ -214,7 +250,7 @@ inline QSGGeometry::Point2D *QSGGeometry::vertexDataAsPoint2D()
Q_ASSERT(m_attributes.count == 1);
Q_ASSERT(m_attributes.stride == 2 * sizeof(float));
Q_ASSERT(m_attributes.attributes[0].tupleSize == 2);
- Q_ASSERT(m_attributes.attributes[0].type == GL_FLOAT);
+ Q_ASSERT(m_attributes.attributes[0].type == TypeFloat);
Q_ASSERT(m_attributes.attributes[0].position == 0);
return static_cast<Point2D *>(m_data);
}
@@ -225,10 +261,10 @@ inline QSGGeometry::TexturedPoint2D *QSGGeometry::vertexDataAsTexturedPoint2D()
Q_ASSERT(m_attributes.stride == 4 * sizeof(float));
Q_ASSERT(m_attributes.attributes[0].position == 0);
Q_ASSERT(m_attributes.attributes[0].tupleSize == 2);
- Q_ASSERT(m_attributes.attributes[0].type == GL_FLOAT);
+ Q_ASSERT(m_attributes.attributes[0].type == TypeFloat);
Q_ASSERT(m_attributes.attributes[1].position == 1);
Q_ASSERT(m_attributes.attributes[1].tupleSize == 2);
- Q_ASSERT(m_attributes.attributes[1].type == GL_FLOAT);
+ Q_ASSERT(m_attributes.attributes[1].type == TypeFloat);
return static_cast<TexturedPoint2D *>(m_data);
}
@@ -238,10 +274,10 @@ inline QSGGeometry::ColoredPoint2D *QSGGeometry::vertexDataAsColoredPoint2D()
Q_ASSERT(m_attributes.stride == 2 * sizeof(float) + 4 * sizeof(char));
Q_ASSERT(m_attributes.attributes[0].position == 0);
Q_ASSERT(m_attributes.attributes[0].tupleSize == 2);
- Q_ASSERT(m_attributes.attributes[0].type == GL_FLOAT);
+ Q_ASSERT(m_attributes.attributes[0].type == TypeFloat);
Q_ASSERT(m_attributes.attributes[1].position == 1);
Q_ASSERT(m_attributes.attributes[1].tupleSize == 4);
- Q_ASSERT(m_attributes.attributes[1].type == GL_UNSIGNED_BYTE);
+ Q_ASSERT(m_attributes.attributes[1].type == TypeUnsignedByte);
return static_cast<ColoredPoint2D *>(m_data);
}
@@ -250,7 +286,7 @@ inline const QSGGeometry::Point2D *QSGGeometry::vertexDataAsPoint2D() const
Q_ASSERT(m_attributes.count == 1);
Q_ASSERT(m_attributes.stride == 2 * sizeof(float));
Q_ASSERT(m_attributes.attributes[0].tupleSize == 2);
- Q_ASSERT(m_attributes.attributes[0].type == GL_FLOAT);
+ Q_ASSERT(m_attributes.attributes[0].type == TypeFloat);
Q_ASSERT(m_attributes.attributes[0].position == 0);
return static_cast<const Point2D *>(m_data);
}
@@ -261,10 +297,10 @@ inline const QSGGeometry::TexturedPoint2D *QSGGeometry::vertexDataAsTexturedPoin
Q_ASSERT(m_attributes.stride == 4 * sizeof(float));
Q_ASSERT(m_attributes.attributes[0].position == 0);
Q_ASSERT(m_attributes.attributes[0].tupleSize == 2);
- Q_ASSERT(m_attributes.attributes[0].type == GL_FLOAT);
+ Q_ASSERT(m_attributes.attributes[0].type == TypeFloat);
Q_ASSERT(m_attributes.attributes[1].position == 1);
Q_ASSERT(m_attributes.attributes[1].tupleSize == 2);
- Q_ASSERT(m_attributes.attributes[1].type == GL_FLOAT);
+ Q_ASSERT(m_attributes.attributes[1].type == TypeFloat);
return static_cast<const TexturedPoint2D *>(m_data);
}
@@ -274,18 +310,18 @@ inline const QSGGeometry::ColoredPoint2D *QSGGeometry::vertexDataAsColoredPoint2
Q_ASSERT(m_attributes.stride == 2 * sizeof(float) + 4 * sizeof(char));
Q_ASSERT(m_attributes.attributes[0].position == 0);
Q_ASSERT(m_attributes.attributes[0].tupleSize == 2);
- Q_ASSERT(m_attributes.attributes[0].type == GL_FLOAT);
+ Q_ASSERT(m_attributes.attributes[0].type == TypeFloat);
Q_ASSERT(m_attributes.attributes[1].position == 1);
Q_ASSERT(m_attributes.attributes[1].tupleSize == 4);
- Q_ASSERT(m_attributes.attributes[1].type == GL_UNSIGNED_BYTE);
+ Q_ASSERT(m_attributes.attributes[1].type == TypeUnsignedByte);
return static_cast<const ColoredPoint2D *>(m_data);
}
int QSGGeometry::sizeOfIndex() const
{
- if (m_index_type == GL_UNSIGNED_SHORT) return 2;
- else if (m_index_type == GL_UNSIGNED_BYTE) return 1;
- else if (m_index_type == GL_UNSIGNED_INT) return 4;
+ if (m_index_type == TypeUnsignedShort) return 2;
+ else if (m_index_type == TypeUnsignedByte) return 1;
+ else if (m_index_type == TypeUnsignedInt) return 4;
return 0;
}
diff --git a/src/quick/scenegraph/coreapi/qsgmaterial.cpp b/src/quick/scenegraph/coreapi/qsgmaterial.cpp
index ceb53d0d14..42a4c4abd3 100644
--- a/src/quick/scenegraph/coreapi/qsgmaterial.cpp
+++ b/src/quick/scenegraph/coreapi/qsgmaterial.cpp
@@ -40,7 +40,13 @@
#include "qsgmaterial.h"
#include "qsgrenderer_p.h"
#include "qsgmaterialshader_p.h"
-#include <private/qsgshadersourcebuilder_p.h>
+#ifndef QT_NO_OPENGL
+# include <private/qsgshadersourcebuilder_p.h>
+# include <private/qsgdefaultcontext_p.h>
+# include <private/qsgdefaultrendercontext_p.h>
+# include <QtGui/QOpenGLFunctions>
+# include <QtGui/QOpenGLContext>
+#endif
QT_BEGIN_NAMESPACE
@@ -58,7 +64,7 @@ void qsg_set_material_failure()
qsg_material_failure = true;
}
#endif
-
+#ifndef QT_NO_OPENGL
const char *QSGMaterialShaderPrivate::loadShaderSource(QOpenGLShader::ShaderType type) const
{
QStringList files = m_sourceFiles[type];
@@ -68,6 +74,7 @@ const char *QSGMaterialShaderPrivate::loadShaderSource(QOpenGLShader::ShaderType
m_sources[type] = builder.source();
return m_sources[type].constData();
}
+#endif
#ifndef QT_NO_DEBUG
static const bool qsg_leak_check = !qEnvironmentVariableIsEmpty("QML_LEAK_CHECK");
@@ -220,7 +227,7 @@ QSGMaterialShader::~QSGMaterialShader()
defines the attribute register position in the vertex shader.
*/
-
+#ifndef QT_NO_OPENGL
/*!
\fn const char *QSGMaterialShader::vertexShader() const
@@ -256,7 +263,7 @@ const char *QSGMaterialShader::fragmentShader() const
Returns the shader program used by this QSGMaterialShader.
*/
-
+#endif
/*!
\fn void QSGMaterialShader::initialize()
@@ -313,6 +320,7 @@ void QSGMaterialShader::updateState(const RenderState & /* state */, QSGMaterial
{
}
+#ifndef QT_NO_OPENGL
/*!
Sets the GLSL source file for the shader stage \a type to \a sourceFile. The
default implementation of the vertexShader() and fragmentShader() functions
@@ -388,7 +396,7 @@ void QSGMaterialShader::compile()
}
}
-
+#endif
/*!
\class QSGMaterialShader::RenderState
@@ -542,7 +550,7 @@ QRect QSGMaterialShader::RenderState::deviceRect() const
return static_cast<const QSGRenderer *>(m_data)->deviceRect();
}
-
+#ifndef QT_NO_OPENGL
/*!
Returns the QOpenGLContext that is being used for rendering
@@ -550,9 +558,15 @@ QRect QSGMaterialShader::RenderState::deviceRect() const
QOpenGLContext *QSGMaterialShader::RenderState::context() const
{
- return static_cast<const QSGRenderer *>(m_data)->context()->openglContext();
+ // Only the QSGDefaultRenderContext will have an OpenGL Context to query
+ auto openGLRenderContext = static_cast<const QSGDefaultRenderContext *>(static_cast<const QSGRenderer *>(m_data)->context());
+ if (openGLRenderContext != nullptr)
+ return openGLRenderContext->openglContext();
+ else
+ return nullptr;
}
+#endif
#ifndef QT_NO_DEBUG
static int qt_material_count = 0;
diff --git a/src/quick/scenegraph/coreapi/qsgmaterial.h b/src/quick/scenegraph/coreapi/qsgmaterial.h
index 0a6a340092..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..c0967bcb09 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.
@@ -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::TypeFloat) {
float x1 = 1e10, x2 = -1e10, y1=1e10, y2=-1e10;
int stride = g->sizeOfVertex();
for (int i = 0; i < g->vertexCount(); ++i) {
diff --git a/src/quick/scenegraph/coreapi/qsgnode.h b/src/quick/scenegraph/coreapi/qsgnode.h
index 9d14ae04df..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..365abd09e2 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)
-
- This function is called by the renderer and should paint this node with OpenGL commands.
+ \fn void QSGRenderNode::render(const RenderState *state)
- The states necessary for clipping has already been set before the function is called.
- The clip is a combination of a stencil clip and scissor clip. Information about the clip is
- found in \a state.
+ 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.
- The following states are set before this function is called:
+ 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.
+
+ 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,142 @@ void QSGRenderNode::setInheritedOpacity(qreal opacity)
\li glDisable(GL_CULL_FACE)
\endlist
- States that are not listed above, but are included in \l StateFlags, can have arbitrary
- values.
+ States that are not listed above, but are included in \l StateFlags, can
+ have arbitrary values.
- \l changedStates() should return which states this function has changed. If a state is not
- covered by \l StateFlags, the state should be set to the default value according to the
- OpenGL specification.
+ \l changedStates() should return which states this function changes. If a
+ state is not covered by \l StateFlags, the state should be set to the
+ default value according to the OpenGL specification. For other APIs, see
+ the documentation for changedStates() for more information.
- \internal
+ For APIs other than OpenGL, it will likely be necessary to query certain
+ API-specific resources (for example, the graphics device or the command
+ list/buffer to add the commands to). This is done via QSGRendererInterface.
+
+ \sa QSGRendererInterface, QQuickWindow::rendererInterface()
*/
+/*!
+ This function is called when all custom graphics resources allocated by
+ this node have to be freed immediately. In case the node does not directly
+ allocate graphics resources (buffers, textures, render targets, fences,
+ etc.) through the graphics API that is in use, there is nothing to do here.
+
+ Failing to release all custom resources can lead to incorrect behavior in
+ graphics device loss scenarios on some systems since subsequent
+ reinitialization of the graphics system may fail.
+
+ \note Some scenegraph backends may choose not to call this function.
+ Therefore it is expected that QSGRenderNode implementations perform cleanup
+ both in their destructor and in releaseResources().
+
+ Unlike with the destructor, it is expected that render() can reinitialize
+ all resources it needs when called after a call to releaseResources().
+
+ With OpenGL, the scenegraph's OpenGL context will be current both when
+ calling the destructor and this function.
+ */
+void QSGRenderNode::releaseResources()
+{
+}
+
+/*!
+ \return pointer to the current model-view matrix.
+ */
+const QMatrix4x4 *QSGRenderNode::matrix() const
+{
+ return d->m_matrix;
+}
+
+/*!
+ \return the current clip list.
+ */
+const QSGClipNode *QSGRenderNode::clipList() const
+{
+ return d->m_clip_list;
+}
+
+/*!
+ \return the current effective opacity.
+ */
+qreal QSGRenderNode::inheritedOpacity() const
+{
+ return d->m_opacity;
+}
+
+QSGRenderNode::RenderState::~RenderState()
+{
+}
+
+/*!
+ \fn const QMatrix4x4 *QSGRenderNode::RenderState::projectionMatrix() const
+
+ \return pointer to the current projection matrix.
+
+ The model-view matrix can be retrieved with QSGRenderNode::matrix().
+ Typically \c{projection * modelview} is the matrix that is then used in the
+ vertex shader to transform the vertices.
+ */
+
+/*!
+ \fn const QMatrix4x4 *QSGRenderNode::RenderState::scissorRect() const
+
+ \return the current scissor rectangle when clipping is active.
+
+ \note Be aware of the differences between graphics APIs: for some the
+ scissor rect is only active when scissoring is enabled (for example,
+ OpenGL), while for others the scissor rect is equal to the viewport rect
+ when there is no need to scissor away anything (for example, Direct3D 12).
+ */
+
+/*!
+ \fn const QMatrix4x4 *QSGRenderNode::RenderState::scissorEnabled() const
+
+ \return the current state of scissoring.
+
+ \note Only relevant for graphics APIs that have a dedicated on/off state of
+ scissoring.
+ */
+
+/*!
+ \fn const QMatrix4x4 *QSGRenderNode::RenderState::stencilValue() const
+
+ \return the current stencil reference value when clipping is active.
+ */
+
+/*!
+ \fn const QMatrix4x4 *QSGRenderNode::RenderState::stencilEnabled() const
+
+ \return the current state of stencil testing.
+
+ \note With graphics APIs where stencil testing is enabled in pipeline state
+ objects, instead of individual state-setting commands, it is up to the
+ implementation of render() to enable stencil testing with operations
+ \c KEEP, comparison function \c EQUAL, and a read and write mask of \c 0xFF.
+ */
+
+/*!
+ \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..6eb425c03b
--- /dev/null
+++ b/src/quick/scenegraph/coreapi/qsgrendernode.h
@@ -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$
+**
+****************************************************************************/
+
+#ifndef QSGRENDERNODE_H
+#define QSGRENDERNODE_H
+
+#include <QtQuick/qsgnode.h>
+
+QT_BEGIN_NAMESPACE
+
+class QSGRenderNodePrivate;
+
+class Q_QUICK_EXPORT QSGRenderNode : public QSGNode
+{
+public:
+ enum StateFlag {
+ DepthState = 0x01,
+ StencilState = 0x02,
+ ScissorState = 0x04,
+ ColorState = 0x08,
+ BlendState = 0x10,
+ CullState = 0x20,
+ ViewportState = 0x40,
+ RenderTargetState = 0x80
+ };
+ Q_DECLARE_FLAGS(StateFlags, StateFlag)
+
+ struct Q_QUICK_EXPORT RenderState {
+ virtual ~RenderState();
+ virtual const QMatrix4x4 *projectionMatrix() const = 0;
+ virtual QRect scissorRect() const = 0;
+ virtual bool scissorEnabled() const = 0;
+ virtual int stencilValue() const = 0;
+ virtual bool stencilEnabled() const = 0;
+ virtual 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();
+
+ const QMatrix4x4 *matrix() const;
+ const QSGClipNode *clipList() const;
+ qreal inheritedOpacity() const;
+
+private:
+ QSGRenderNodePrivate *d;
+ friend class QSGRenderNodePrivate;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QSGRenderNode::StateFlags)
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/quick/scenegraph/coreapi/qsgrendernode_p.h b/src/quick/scenegraph/coreapi/qsgrendernode_p.h
index 8659b0e62c..5c42e55689 100644
--- a/src/quick/scenegraph/coreapi/qsgrendernode_p.h
+++ b/src/quick/scenegraph/coreapi/qsgrendernode_p.h
@@ -51,64 +51,23 @@
// We mean it.
//
-#include "qsgnode.h"
-#include <private/qtquickglobal_p.h>
+#include <QtQuick/qsgnode.h>
+#include <QtQuick/qsgrendernode.h>
QT_BEGIN_NAMESPACE
-namespace QSGBatchRenderer {
- class Renderer;
-}
-
-class Q_QUICK_PRIVATE_EXPORT QSGRenderNode : public QSGNode
+class QSGRenderNodePrivate
{
public:
- enum StateFlag
- {
- DepthState = 0x01, // depth mask, depth test enable, depth func, clear depth
- StencilState = 0x02, // stencil mask, stencil test enable, stencil op, stencil func, clear stencil
- ScissorState = 0x04, // scissor enable, scissor test enable
- ColorState = 0x08, // clear color, color mask
- BlendState = 0x10, // blend enable, blend func
- CullState = 0x20, // front face, cull face enable
- ViewportState = 0x40 // viewport
- };
- Q_DECLARE_FLAGS(StateFlags, StateFlag)
-
- struct RenderState
- {
- // The model-view matrix can be retrieved with matrix().
- // The opacity can be retrieved with inheritedOpacity().
- const QMatrix4x4 *projectionMatrix;
- QRect scissorRect;
- int stencilValue;
-
- bool stencilEnabled;
- bool scissorEnabled;
- };
-
- QSGRenderNode();
+ QSGRenderNodePrivate();
- virtual StateFlags changedStates() = 0;
- virtual void render(const RenderState &state) = 0;
-
- const QMatrix4x4 *matrix() const { return m_matrix; }
- const QSGClipNode *clipList() const { return m_clip_list; }
-
- void setInheritedOpacity(qreal opacity);
- qreal inheritedOpacity() const { return m_opacity; }
-
-private:
- friend class QSGNodeUpdater;
- friend class QSGBatchRenderer::Renderer;
+ static QSGRenderNodePrivate *get(QSGRenderNode *node) { return node->d; }
const QMatrix4x4 *m_matrix;
const QSGClipNode *m_clip_list;
qreal m_opacity;
};
-Q_DECLARE_OPERATORS_FOR_FLAGS(QSGRenderNode::StateFlags)
-
QT_END_NAMESPACE
#endif
diff --git a/src/quick/scenegraph/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..e219ddd82e 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()
@@ -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..a74b38dba8 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,20 @@ 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;
+ virtual bool visit(QSGSpriteNode *) = 0;
+ virtual void endVisit(QSGSpriteNode *) = 0;
+ virtual bool visit(QSGRenderNode *) = 0;
+ virtual void endVisit(QSGRenderNode *) = 0;
void visitChildren(QSGNode *node);
};
@@ -122,7 +125,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 +143,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 +189,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 +200,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 +213,148 @@ Q_SIGNALS:
void scheduledUpdateCompleted();
};
+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); }
+};
+
+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 +427,7 @@ public:
};
struct Texture {
- GLuint textureId;
+ uint textureId;
QSize size;
Texture() : textureId(0), size(QSize()) { }
@@ -359,10 +491,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 +544,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..685a51550d
--- /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::createWithSemantic(0, 2, QSGGeometry::TypeFloat, QSGGeometry::Attribute::POSITION),
+ QSGGeometry::Attribute::createWithSemantic(1, 2, QSGGeometry::TypeFloat, QSGGeometry::Attribute::TEXCOORD),
+ QSGGeometry::Attribute::createWithSemantic(2, 2, QSGGeometry::TypeFloat, QSGGeometry::Attribute::TEXCOORD1),
+ QSGGeometry::Attribute::createWithSemantic(3, 2, QSGGeometry::TypeFloat, QSGGeometry::Attribute::TEXCOORD2)
+ };
+ static QSGGeometry::AttributeSet attrs = { 4, sizeof(SmoothVertex), data };
+ return attrs;
+ }
+}
+
+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::TypeUnsignedShort);
+ } else {
+ geometry->allocate(hCells * vCells * 4, hCells * vCells * 6);
+ }
+ geometry->setDrawingMode(QSGGeometry::DrawTriangles);
+ QSGGeometry::TexturedPoint2D *vertices = geometry->vertexDataAsTexturedPoint2D();
+ ys = yData.data();
+ for (int j = 0; j < vCells; ++j, ys += 2) {
+ xs = xData.data();
+ for (int i = 0; i < hCells; ++i, xs += 2) {
+ vertices[0].x = vertices[2].x = xs[0].x;
+ vertices[0].tx = vertices[2].tx = xs[0].tx;
+ vertices[1].x = vertices[3].x = xs[1].x;
+ vertices[1].tx = vertices[3].tx = xs[1].tx;
+
+ vertices[0].y = vertices[1].y = ys[0].y;
+ vertices[0].ty = vertices[1].ty = ys[0].ty;
+ vertices[2].y = vertices[3].y = ys[1].y;
+ vertices[2].ty = vertices[3].ty = ys[1].ty;
+
+ vertices += 4;
+ }
+ }
+
+ quint16 *indices = geometry->indexDataAsUShort();
+ for (int i = 0; i < 4 * vCells * hCells; i += 4)
+ appendQuad(&indices, i, i + 1, i + 2, i + 3);
+ }
+ return geometry;
+}
+
+void 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..8fc850b60c 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::createWithSemantic(0, 2, QSGGeometry::TypeFloat, QSGGeometry::Attribute::POSITION),
+ QSGGeometry::Attribute::createWithSemantic(1, 4, QSGGeometry::TypeUnsignedByte, QSGGeometry::Attribute::COLOR),
+ QSGGeometry::Attribute::createWithSemantic(2, 2, QSGGeometry::TypeFloat, QSGGeometry::Attribute::TEXCOORD)
};
static QSGGeometry::AttributeSet attrs = { 3, sizeof(SmoothVertex), data };
return attrs;
}
}
-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 25945ad78a..688fc7db08 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,88 +278,33 @@ QSGRectangleNode *QSGContext::createRectangleNode(const QRectF &rect, const QCol
}
/*!
- Factory function for scene graph backends of the Rectangle element.
+ 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.
*/
-QSGRectangleNode *QSGContext::createRectangleNode()
+QSGGuiThreadShaderEffectManager *QSGContext::createGuiThreadShaderEffectManager()
{
- Q_D(QSGContext);
- return d->antialiasingMethod == MsaaAntialiasing
- ? new QSGMultisampleAntialiasing::RectangleNode
- : new QSGDefaultRectangleNode;
+ return nullptr;
}
/*!
- Factory function for scene graph backends of the Image element.
+ 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.
*/
-QSGImageNode *QSGContext::createImageNode()
+QSGShaderEffectNode *QSGContext::createShaderEffectNode(QSGRenderContext *, QSGGuiThreadShaderEffectManager *)
{
- Q_D(QSGContext);
- return d->antialiasingMethod == MsaaAntialiasing
- ? new QSGMultisampleAntialiasing::ImageNode
- : new QSGDefaultImageNode;
+ return nullptr;
}
/*!
- Factory function for scene graph backends of Painter elements
- */
-QSGPainterNode *QSGContext::createPainterNode(QQuickPaintedItem *item)
-{
- return new QSGDefaultPainterNode(item);
-}
-
-/*!
- Factory function for scene graph backends of the Text elements;
- */
-QSGGlyphNode *QSGContext::createGlyphNode(QSGRenderContext *rc, bool preferNativeGlyphNode)
-{
- Q_D(QSGContext);
-
- if (d->distanceFieldDisabled || preferNativeGlyphNode) {
- return new QSGDefaultGlyphNode;
- } else {
- QSGDistanceFieldGlyphNode *node = new QSGDistanceFieldGlyphNode(rc);
- node->setPreferredAntialiasingMode(d->distanceFieldAntialiasing);
- return node;
- }
-}
-
-/*!
- * Factory function for scene graph backends of the QStyle stylable elements. Returns a
- * null pointer if the backend doesn't provide its own node type.
- */
-QSGNinePatchNode *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
{
#ifdef Q_OS_MAC
@@ -497,52 +314,44 @@ 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()
@@ -551,49 +360,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)
{
@@ -602,178 +376,18 @@ void QSGRenderContext::registerFontengineForCleanup(QFontEngine *engine)
}
/*!
- Initializes the scene graph render context with the GL context \a context. This also
- emits the ready() signal so that the QML graph can start building scene graph nodes.
- */
-void QSGRenderContext::initialize(QOpenGLContext *context)
-{
- QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions();
- funcs->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &m_maxTextureSize);
-
- // Sanity check the surface format, in case it was overridden by the application
- QSurfaceFormat requested = m_sg->defaultSurfaceFormat();
- QSurfaceFormat actual = context->format();
- if (requested.depthBufferSize() > 0 && actual.depthBufferSize() <= 0)
- qWarning("QSGContext::initialize: depth buffer support missing, expect rendering errors");
- if (requested.stencilBufferSize() > 0 && actual.stencilBufferSize() <= 0)
- qWarning("QSGContext::initialize: stencil buffer support missing, expect rendering errors");
-
- if (!m_atlasManager)
- m_atlasManager = new QSGAtlasTexture::Manager();
-
- Q_ASSERT_X(!m_gl, "QSGRenderContext::initialize", "already initialized!");
- m_gl = context;
- if (m_attachToGLContext) {
- Q_ASSERT(!context->property(QSG_RENDERCONTEXT_PROPERTY).isValid());
- context->setProperty(QSG_RENDERCONTEXT_PROPERTY, QVariant::fromValue(this));
- }
- m_sg->renderContextInitialized(this);
-
-#ifdef Q_OS_LINUX
- const char *vendor = (const char *) funcs->glGetString(GL_VENDOR);
- if (strstr(vendor, "nouveau"))
- m_brokenIBOs = true;
- const char *renderer = (const char *) funcs->glGetString(GL_RENDERER);
- if (strstr(renderer, "llvmpipe"))
- m_serializedRender = true;
- if (strstr(vendor, "Hisilicon Technologies") && strstr(renderer, "Immersion.16"))
- m_brokenIBOs = true;
-#endif
-
- emit initialized();
-}
-
-void QSGRenderContext::invalidate()
-{
- if (!m_gl)
- return;
-
- qDeleteAll(m_texturesToDelete);
- m_texturesToDelete.clear();
-
- qDeleteAll(m_textures);
- m_textures.clear();
-
- /* The cleanup of the atlas textures is a bit intriguing.
- As part of the cleanup in the threaded render loop, we
- do:
- 1. call this function
- 2. call QCoreApp::sendPostedEvents() to immediately process
- any pending deferred deletes.
- 3. delete the GL context.
-
- As textures need the atlas manager while cleaning up, the
- manager needs to be cleaned up after the textures, so
- we post a deleteLater here at the very bottom so it gets
- deferred deleted last.
-
- Another alternative would be to use a QPointer in
- QSGAtlasTexture::Texture, but this seemed simpler.
- */
- m_atlasManager->invalidate();
- m_atlasManager->deleteLater();
- m_atlasManager = 0;
-
- // The following piece of code will read/write to the font engine's caches,
- // potentially from different threads. However, this is safe because this
- // code is only called from QQuickWindow's shutdown which is called
- // only when the GUI is blocked, and multiple threads will call it in
- // sequence. (see qsgdefaultglyphnode_p.cpp's init())
- for (QSet<QFontEngine *>::const_iterator it = m_fontEnginesToClean.constBegin(),
- end = m_fontEnginesToClean.constEnd(); it != end; ++it) {
- (*it)->clearGlyphCache(m_gl);
- if (!(*it)->ref.deref())
- delete *it;
- }
- m_fontEnginesToClean.clear();
-
- delete m_depthStencilManager;
- m_depthStencilManager = 0;
-
- delete m_distanceFieldCacheManager;
- m_distanceFieldCacheManager = 0;
-
- if (m_gl->property(QSG_RENDERCONTEXT_PROPERTY) == QVariant::fromValue(this))
- m_gl->setProperty(QSG_RENDERCONTEXT_PROPERTY, QVariant());
- m_gl = 0;
-
- m_sg->renderContextInvalidated(this);
- emit invalidated();
-}
-
-/*!
- Returns a shared pointer to a depth stencil buffer that can be used with \a fbo.
- */
-QSharedPointer<QSGDepthStencilBuffer> QSGRenderContext::depthStencilBufferForFbo(QOpenGLFramebufferObject *fbo)
-{
- if (!m_gl)
- return QSharedPointer<QSGDepthStencilBuffer>();
- QSGDepthStencilBufferManager *manager = depthStencilBufferManager();
- QSGDepthStencilBuffer::Format format;
- format.size = fbo->size();
- format.samples = fbo->format().samples();
- format.attachments = QSGDepthStencilBuffer::DepthAttachment | QSGDepthStencilBuffer::StencilAttachment;
- QSharedPointer<QSGDepthStencilBuffer> buffer = manager->bufferForFormat(format);
- if (buffer.isNull()) {
- buffer = QSharedPointer<QSGDepthStencilBuffer>(new QSGDefaultDepthStencilBuffer(m_gl, format));
- manager->insertBuffer(buffer);
- }
- return buffer;
-}
-
-/*!
- Returns a pointer to the context's depth/stencil buffer manager. This is useful for custom
- implementations of \l depthStencilBufferForFbo().
- */
-QSGDepthStencilBufferManager *QSGRenderContext::depthStencilBufferManager()
-{
- if (!m_gl)
- return 0;
- if (!m_depthStencilManager)
- m_depthStencilManager = new QSGDepthStencilBufferManager(m_gl);
- return m_depthStencilManager;
-}
-
-
-/*!
Factory function for texture objects.
If \a image is a valid image, the QSGTexture::setImage function
will be called with \a image as argument.
*/
-QSGTexture *QSGRenderContext::createTexture(const QImage &image, uint flags) const
-{
- bool atlas = flags & CreateTexture_Atlas;
- bool mipmap = flags & CreateTexture_Mipmap;
- bool alpha = flags & CreateTexture_Alpha;
-
- // The atlas implementation is only supported from the render thread and
- // does not support mipmaps.
- if (!mipmap && atlas && openglContext() && QThread::currentThread() == openglContext()->thread()) {
- QSGTexture *t = m_atlasManager->create(image, alpha);
- if (t)
- return t;
- }
-
- QSGPlainTexture *texture = new QSGPlainTexture();
- texture->setImage(image);
- if (texture->hasAlphaChannel() && !alpha)
- texture->setHasAlphaChannel(false);
-
- return texture;
-}
-
/*!
Factory function for the scene graph renderers.
The renderers are used for the toplevel renderer and once for every
QQuickShaderEffectSource used in the QML scene.
*/
-QSGRenderer *QSGRenderContext::createRenderer()
-{
- return new QSGBatchRenderer::Renderer(this);
-}
QSGTexture *QSGRenderContext::textureForFactory(QQuickTextureFactory *factory, QQuickWindow *window)
{
@@ -803,41 +417,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..43cf1c28ab 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,20 @@ public Q_SLOTS:
void textureFactoryDestroyed(QObject *o);
protected:
- QOpenGLContext *m_gl;
QSGContext *m_sg;
QMutex m_mutex;
QHash<QQuickTextureFactory *, QSGTexture *> m_textures;
QSet<QSGTexture *> m_texturesToDelete;
- QSGAtlasTexture::Manager *m_atlasManager;
-
- QSGDepthStencilBufferManager *m_depthStencilManager;
QSGDistanceFieldGlyphCacheManager *m_distanceFieldCacheManager;
QSet<QFontEngine *> m_fontEnginesToClean;
- int m_maxTextureSize;
- bool m_brokenIBOs;
- bool m_serializedRender;
- bool m_attachToGLContext;
};
class Q_QUICK_PRIVATE_EXPORT QSGContext : public QObject
{
Q_OBJECT
- Q_DECLARE_PRIVATE(QSGContext)
public:
enum AntialiasingMethod {
@@ -185,30 +162,38 @@ 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);
+ virtual QSGSpriteNode *createSpriteNode() = 0;
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..6964b74dc8
--- /dev/null
+++ b/src/quick/scenegraph/qsgdefaultcontext.cpp
@@ -0,0 +1,286 @@
+/****************************************************************************
+**
+** 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>
+#include <QtQuick/private/qsgdefaultspritenode_p.h>
+
+#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;
+}
+
+QSGSpriteNode *QSGDefaultContext::createSpriteNode()
+{
+ return new QSGDefaultSpriteNode;
+}
+
+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..88db5e1e9a
--- /dev/null
+++ b/src/quick/scenegraph/qsgdefaultcontext_p.h
@@ -0,0 +1,99 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef 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;
+ QSGSpriteNode *createSpriteNode() override;
+
+ 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 b28ca7725a..afb9b673d9 100644
--- a/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp
+++ b/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp
@@ -507,7 +507,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 3da7a10456..7c2663d5a3 100644
--- a/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp
+++ b/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp
@@ -49,6 +49,7 @@
#include <QtQuick/qquickwindow.h>
#include <QtQuick/private/qsgtexture_p.h>
+#include <QtQuick/private/qsgdefaultrendercontext_p.h>
#include <private/qrawfont_p.h>
#include <QtCore/qmath.h>
@@ -427,7 +428,7 @@ void QSGTextMaskMaterial::init(QFontEngine::GlyphFormat glyphFormat)
if (!m_glyphCache || int(m_glyphCache->glyphFormat()) != glyphFormat) {
m_glyphCache = new QOpenGLTextureGlyphCache(glyphFormat, glyphCacheTransform);
fontEngine->setGlyphCache(ctx, m_glyphCache.data());
- QSGRenderContext *sg = QSGRenderContext::from(ctx);
+ auto sg = QSGDefaultRenderContext::from(ctx);
Q_ASSERT(sg);
sg->registerFontengineForCleanup(fontEngine);
}
diff --git a/src/quick/scenegraph/qsgdefaultglyphnode_p.h b/src/quick/scenegraph/qsgdefaultglyphnode_p.h
index ea4c0ff787..0eb7a4e4bd 100644
--- a/src/quick/scenegraph/qsgdefaultglyphnode_p.h
+++ b/src/quick/scenegraph/qsgdefaultglyphnode_p.h
@@ -52,38 +52,15 @@
//
#include <private/qsgadaptationlayer_p.h>
-#include <QtQuick/qsgnode.h>
+#include <private/qsgbasicglyphnode_p.h>
QT_BEGIN_NAMESPACE
-class QSGTextMaskMaterial;
-class QSGDefaultGlyphNode: public QSGGlyphNode
+class QSGDefaultGlyphNode : public QSGBasicGlyphNode
{
public:
- QSGDefaultGlyphNode();
- virtual ~QSGDefaultGlyphNode();
-
- virtual QPointF baseLine() const { return m_baseLine; }
- virtual void setGlyphs(const QPointF &position, const QGlyphRun &glyphs);
- virtual void setColor(const QColor &color);
-
- virtual void setPreferredAntialiasingMode(AntialiasingMode) { }
- virtual void setStyle(QQuickText::TextStyle);
- virtual void setStyleColor(const QColor &);
-
- virtual void update();
-
-protected:
- QGlyphRun m_glyphs;
- QPointF m_position;
- QColor m_color;
- QQuickText::TextStyle m_style;
- QColor m_styleColor;
-
- QPointF m_baseLine;
- QSGTextMaskMaterial *m_material;
-
- QSGGeometry m_geometry;
+ void setMaterialColor(const QColor &color) override;
+ void update() override;
};
QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/qsgdefaultimagenode.cpp b/src/quick/scenegraph/qsgdefaultimagenode.cpp
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..4fcc81fb18
--- /dev/null
+++ b/src/quick/scenegraph/qsgdefaultrendercontext.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 "qsgdefaultrendercontext_p.h"
+
+#include <QtGui/QOpenGLFramebufferObject>
+
+#include <QtQuick/private/qsgbatchrenderer_p.h>
+#include <QtQuick/private/qsgrenderer_p.h>
+#include <QtQuick/private/qsgatlastexture_p.h>
+#include <QtQuick/private/qsgdefaultdistancefieldglyphcache_p.h>
+#include <QtQuick/private/qsgdistancefieldutil_p.h>
+
+QT_BEGIN_NAMESPACE
+
+#define QSG_RENDERCONTEXT_PROPERTY "_q_sgrendercontext"
+
+QSGDefaultRenderContext::QSGDefaultRenderContext(QSGContext *context)
+ : QSGRenderContext(context)
+ , m_gl(nullptr)
+ , m_depthStencilManager(nullptr)
+ , m_maxTextureSize(0)
+ , m_brokenIBOs(false)
+ , m_serializedRender(false)
+ , m_attachToGLContext(true)
+ , m_atlasManager(nullptr)
+{
+
+}
+
+/*!
+ Initializes the scene graph render context with the GL context \a context. This also
+ emits the ready() signal so that the QML graph can start building scene graph nodes.
+ */
+void QSGDefaultRenderContext::initialize(void *context)
+{
+ QOpenGLContext *openglContext = static_cast<QOpenGLContext *>(context);
+
+ QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions();
+ funcs->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &m_maxTextureSize);
+
+ // Sanity check the surface format, in case it was overridden by the application
+ QSurfaceFormat requested = m_sg->defaultSurfaceFormat();
+ QSurfaceFormat actual = openglContext->format();
+ if (requested.depthBufferSize() > 0 && actual.depthBufferSize() <= 0)
+ qWarning("QSGContext::initialize: depth buffer support missing, expect rendering errors");
+ if (requested.stencilBufferSize() > 0 && actual.stencilBufferSize() <= 0)
+ qWarning("QSGContext::initialize: stencil buffer support missing, expect rendering errors");
+
+ if (!m_atlasManager)
+ m_atlasManager = new QSGAtlasTexture::Manager();
+
+ Q_ASSERT_X(!m_gl, "QSGRenderContext::initialize", "already initialized!");
+ m_gl = openglContext;
+ if (m_attachToGLContext) {
+ Q_ASSERT(!openglContext->property(QSG_RENDERCONTEXT_PROPERTY).isValid());
+ openglContext->setProperty(QSG_RENDERCONTEXT_PROPERTY, QVariant::fromValue(this));
+ }
+ m_sg->renderContextInitialized(this);
+
+#ifdef Q_OS_LINUX
+ const char *vendor = (const char *) funcs->glGetString(GL_VENDOR);
+ if (strstr(vendor, "nouveau"))
+ m_brokenIBOs = true;
+ const char *renderer = (const char *) funcs->glGetString(GL_RENDERER);
+ if (strstr(renderer, "llvmpipe"))
+ m_serializedRender = true;
+ if (strstr(vendor, "Hisilicon Technologies") && strstr(renderer, "Immersion.16"))
+ m_brokenIBOs = true;
+#endif
+
+ emit initialized();
+}
+
+
+void QSGDefaultRenderContext::invalidate()
+{
+ if (!m_gl)
+ return;
+
+ qDeleteAll(m_texturesToDelete);
+ m_texturesToDelete.clear();
+
+ qDeleteAll(m_textures);
+ m_textures.clear();
+
+ /* The cleanup of the atlas textures is a bit intriguing.
+ As part of the cleanup in the threaded render loop, we
+ do:
+ 1. call this function
+ 2. call QCoreApp::sendPostedEvents() to immediately process
+ any pending deferred deletes.
+ 3. delete the GL context.
+
+ As textures need the atlas manager while cleaning up, the
+ manager needs to be cleaned up after the textures, so
+ we post a deleteLater here at the very bottom so it gets
+ deferred deleted last.
+
+ Another alternative would be to use a QPointer in
+ QSGAtlasTexture::Texture, but this seemed simpler.
+ */
+ m_atlasManager->invalidate();
+ m_atlasManager->deleteLater();
+ m_atlasManager = nullptr;
+
+ // The following piece of code will read/write to the font engine's caches,
+ // potentially from different threads. However, this is safe because this
+ // code is only called from QQuickWindow's shutdown which is called
+ // only when the GUI is blocked, and multiple threads will call it in
+ // sequence. (see qsgdefaultglyphnode_p.cpp's init())
+ for (QSet<QFontEngine *>::const_iterator it = m_fontEnginesToClean.constBegin(),
+ end = m_fontEnginesToClean.constEnd(); it != end; ++it) {
+ (*it)->clearGlyphCache(m_gl);
+ if (!(*it)->ref.deref())
+ delete *it;
+ }
+ m_fontEnginesToClean.clear();
+
+ delete m_depthStencilManager;
+ m_depthStencilManager = 0;
+
+ delete m_distanceFieldCacheManager;
+ m_distanceFieldCacheManager = 0;
+
+ if (m_gl->property(QSG_RENDERCONTEXT_PROPERTY) == QVariant::fromValue(this))
+ m_gl->setProperty(QSG_RENDERCONTEXT_PROPERTY, QVariant());
+ m_gl = 0;
+
+ 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..89b26f8660
--- /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::TypeFloat, true), // pos
+ QSGGeometry::Attribute::create(1, 2, QSGGeometry::TypeFloat), // 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..cb76bf8d83
--- /dev/null
+++ b/src/quick/scenegraph/qsgdefaultspritenode_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 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/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..e99e8dfe2e 100644
--- a/src/quick/scenegraph/qsgrenderloop.cpp
+++ b/src/quick/scenegraph/qsgrenderloop.cpp
@@ -47,7 +47,6 @@
#include <QtCore/QLibraryInfo>
#include <QtCore/private/qabstractanimation_p.h>
-#include <QtGui/QOpenGLContext>
#include <QtGui/QOffscreenSurface>
#include <QtGui/private/qguiapplication_p.h>
#include <qpa/qplatformintegration.h>
@@ -59,7 +58,11 @@
#include <QtQuick/private/qsgcontext_p.h>
#include <private/qquickprofiler_p.h>
-#include <private/qquickshadereffectnode_p.h>
+#ifndef QT_NO_OPENGL
+# include <QtGui/QOpenGLContext>
+# include <private/qsgdefaultrendercontext_p.h>
+# include <private/qquickopenglshadereffectnode_p.h>
+#endif
#ifdef Q_OS_WIN
# include <QtCore/qt_windows.h>
@@ -67,9 +70,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 +84,7 @@ extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_
DEFINE_BOOL_CONFIG_OPTION(qmlNoThreadedRenderer, QML_BAD_GUI_RENDER_LOOP);
DEFINE_BOOL_CONFIG_OPTION(qmlForceThreadedRenderer, QML_FORCE_THREADED_RENDERER); // Might trigger graphics driver threading bugs, use at own risk
-
+#endif
QSGRenderLoop *QSGRenderLoop::s_instance = 0;
QSGRenderLoop::~QSGRenderLoop()
@@ -97,7 +100,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 +116,20 @@ void QSGRenderLoop::cleanup()
*/
void QSGRenderLoop::postJob(QQuickWindow *window, QRunnable *job)
{
- Q_ASSERT(window);
Q_ASSERT(job);
-
+#ifndef QT_NO_OPENGL
+ Q_ASSERT(window);
if (window->openglContext()) {
window->openglContext()->makeCurrent(window);
job->run();
}
-
+#else
+ Q_UNUSED(window)
+ job->run();
+#endif
delete job;
}
-
+#ifndef QT_NO_OPENGL
class QSGGuiThreadRenderLoop : public QSGRenderLoop
{
Q_OBJECT
@@ -164,7 +170,7 @@ public:
QImage grabContent;
};
-
+#endif
QSGRenderLoop *QSGRenderLoop::instance()
{
if (!s_instance) {
@@ -174,7 +180,7 @@ QSGRenderLoop *QSGRenderLoop::instance()
const_cast<QLoggingCategory &>(QSG_LOG_INFO()).setEnabled(QtDebugMsg, true);
s_instance = QSGContext::createWindowManager();
-
+#ifndef QT_NO_OPENGL
if (!s_instance) {
enum RenderLoopType {
@@ -226,9 +232,10 @@ QSGRenderLoop *QSGRenderLoop::instance()
break;
}
}
-
+#endif
qAddPostRoutine(QSGRenderLoop::cleanup);
}
+
return s_instance;
}
@@ -253,20 +260,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 +326,9 @@ void QSGGuiThreadRenderLoop::windowDestroyed(QQuickWindow *window)
if (Q_UNLIKELY(!current))
qCDebug(QSG_LOG_RENDERLOOP) << "cleanup without an OpenGL context";
- QQuickShaderEffectMaterial::cleanupMaterialCache();
+#ifndef QT_NO_OPENGL
+ QQuickOpenGLShaderEffectMaterial::cleanupMaterialCache();
+#endif
d->cleanupNodesOnShutdown();
if (m_windows.size() == 0) {
@@ -354,8 +367,10 @@ void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window)
cd->fireOpenGLContextCreated(gl);
current = gl->makeCurrent(window);
}
- if (current)
- cd->context->initialize(gl);
+ if (current) {
+ auto openglRenderContext = static_cast<QSGDefaultRenderContext *>(cd->context);
+ openglRenderContext->initialize(gl);
+ }
} else {
current = gl->makeCurrent(window);
}
@@ -367,7 +382,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 +491,8 @@ void QSGGuiThreadRenderLoop::handleUpdateRequest(QQuickWindow *window)
renderWindow(window);
}
+#endif
+
#include "qsgrenderloop.moc"
QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/qsgrenderloop_p.h b/src/quick/scenegraph/qsgrenderloop_p.h
index 9159a2f01d..7bf40ecac4 100644
--- a/src/quick/scenegraph/qsgrenderloop_p.h
+++ b/src/quick/scenegraph/qsgrenderloop_p.h
@@ -69,6 +69,10 @@ class Q_QUICK_PRIVATE_EXPORT QSGRenderLoop : public QObject
Q_OBJECT
public:
+ enum RenderLoopFlags {
+ SupportsGrabWithoutExpose = 0x01
+ };
+
virtual ~QSGRenderLoop();
virtual void show(QQuickWindow *window) = 0;
@@ -104,6 +108,8 @@ public:
virtual bool interleaveIncubation() const { return false; }
+ virtual int flags() const { return 0; }
+
static void cleanup();
Q_SIGNALS:
diff --git a/src/quick/scenegraph/qsgthreadedrenderloop.cpp b/src/quick/scenegraph/qsgthreadedrenderloop.cpp
index 96abd4267b..6b9c67b2bd 100644
--- a/src/quick/scenegraph/qsgthreadedrenderloop.cpp
+++ b/src/quick/scenegraph/qsgthreadedrenderloop.cpp
@@ -63,7 +63,8 @@
#include <private/qqmldebugserviceinterfaces_p.h>
#include <private/qqmldebugconnector_p.h>
-#include <private/qquickshadereffectnode_p.h>
+#include <private/qquickopenglshadereffectnode_p.h>
+#include <private/qsgdefaultrendercontext_p.h>
/*
Overall design:
@@ -268,7 +269,6 @@ public:
QSGRenderThread(QSGThreadedRenderLoop *w, QSGRenderContext *renderContext)
: wm(w)
, gl(0)
- , sgrc(renderContext)
, animatorDriver(0)
, pendingUpdate(0)
, sleeping(false)
@@ -277,6 +277,7 @@ public:
, window(0)
, stopEventProcessing(false)
{
+ sgrc = static_cast<QSGDefaultRenderContext *>(renderContext);
#if defined(Q_OS_QNX) && !defined(Q_OS_BLACKBERRY) && defined(Q_PROCESSOR_X86)
// The SDP 6.6.0 x86 MESA driver requires a larger stack than the default.
setStackSize(1024 * 1024);
@@ -325,7 +326,7 @@ public:
QSGThreadedRenderLoop *wm;
QOpenGLContext *gl;
- QSGRenderContext *sgrc;
+ QSGDefaultRenderContext *sgrc;
QAnimationDriver *animatorDriver;
@@ -486,7 +487,7 @@ void QSGRenderThread::invalidateOpenGL(QQuickWindow *window, bool inDestructor,
QQuickWindowPrivate *dd = QQuickWindowPrivate::get(window);
- QQuickShaderEffectMaterial::cleanupMaterialCache();
+ QQuickOpenGLShaderEffectMaterial::cleanupMaterialCache();
// The canvas nodes must be cleaned up regardless if we are in the destructor..
if (wipeSG) {
@@ -1141,7 +1142,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..743e524a36 100644
--- a/src/quick/scenegraph/qsgwindowsrenderloop.cpp
+++ b/src/quick/scenegraph/qsgwindowsrenderloop.cpp
@@ -48,13 +48,17 @@
#include <QtQuick/private/qsgcontext_p.h>
#include <QtQuick/private/qquickwindow_p.h>
+#include <QtQuick/private/qsgdefaultrendercontext_p.h>
#include <QtQuick/QQuickWindow>
#include <private/qquickprofiler_p.h>
-#include <private/qquickshadereffectnode_p.h>
#include <private/qquickanimatorcontroller_p.h>
+#ifndef QT_NO_OPENGL
+#include <private/qquickopenglshadereffectnode_p.h>
+#endif
+
QT_BEGIN_NAMESPACE
extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha);
@@ -78,7 +82,7 @@ QSGWindowsRenderLoop::QSGWindowsRenderLoop()
, m_updateTimer(0)
, m_animationTimer(0)
{
- m_rc = m_sg->createRenderContext();
+ m_rc = static_cast<QSGDefaultRenderContext *>(m_sg->createRenderContext());
m_animationDriver = m_sg->createAnimationDriver(m_sg);
m_animationDriver->install();
@@ -241,7 +245,9 @@ void QSGWindowsRenderLoop::windowDestroyed(QQuickWindow *window)
if (Q_UNLIKELY(!current))
qCDebug(QSG_LOG_RENDERLOOP) << "cleanup without an OpenGL context";
- QQuickShaderEffectMaterial::cleanupMaterialCache();
+#ifndef QT_NO_OPENGL
+ QQuickOpenGLShaderEffectMaterial::cleanupMaterialCache();
+#endif
d->cleanupNodesOnShutdown();
if (m_windows.size() == 0) {
@@ -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..4ef8b0f638 100644
--- a/src/quick/scenegraph/scenegraph.pri
+++ b/src/quick/scenegraph/scenegraph.pri
@@ -5,169 +5,217 @@
# Core API
HEADERS += \
- $$PWD/coreapi/qsgbatchrenderer_p.h \
$$PWD/coreapi/qsggeometry.h \
$$PWD/coreapi/qsgmaterial.h \
+ $$PWD/coreapi/qsgmaterialshader_p.h \
$$PWD/coreapi/qsgnode.h \
$$PWD/coreapi/qsgnode_p.h \
$$PWD/coreapi/qsgnodeupdater_p.h \
$$PWD/coreapi/qsgabstractrenderer.h \
$$PWD/coreapi/qsgabstractrenderer_p.h \
$$PWD/coreapi/qsgrenderer_p.h \
+ $$PWD/coreapi/qsgrendernode.h \
$$PWD/coreapi/qsgrendernode_p.h \
- $$PWD/coreapi/qsggeometry_p.h \
- $$PWD/coreapi/qsgmaterialshader_p.h
+ $$PWD/coreapi/qsgrendererinterface.h \
+ $$PWD/coreapi/qsggeometry_p.h
SOURCES += \
$$PWD/coreapi/qsgabstractrenderer.cpp \
- $$PWD/coreapi/qsgbatchrenderer.cpp \
$$PWD/coreapi/qsggeometry.cpp \
$$PWD/coreapi/qsgmaterial.cpp \
$$PWD/coreapi/qsgnode.cpp \
$$PWD/coreapi/qsgnodeupdater.cpp \
$$PWD/coreapi/qsgrenderer.cpp \
$$PWD/coreapi/qsgrendernode.cpp \
- $$PWD/coreapi/qsgshaderrewriter.cpp
+ $$PWD/coreapi/qsgrendererinterface.cpp
+
+contains(QT_CONFIG, opengl(es1|es2)?) {
+ HEADERS += \
+ $$PWD/coreapi/qsgbatchrenderer_p.h
+ SOURCES += \
+ $$PWD/coreapi/qsgbatchrenderer.cpp \
+ $$PWD/coreapi/qsgshaderrewriter.cpp
+}
# Util API
HEADERS += \
$$PWD/util/qsgareaallocator_p.h \
- $$PWD/util/qsgatlastexture_p.h \
- $$PWD/util/qsgdepthstencilbuffer_p.h \
$$PWD/util/qsgengine.h \
$$PWD/util/qsgengine_p.h \
- $$PWD/util/qsgflatcolormaterial.h \
- $$PWD/util/qsgsimplematerial.h \
$$PWD/util/qsgsimplerectnode.h \
$$PWD/util/qsgsimpletexturenode.h \
- $$PWD/util/qsgtexturematerial.h \
- $$PWD/util/qsgtexturematerial_p.h \
- $$PWD/util/qsgvertexcolormaterial.h \
$$PWD/util/qsgtexture.h \
$$PWD/util/qsgtexture_p.h \
$$PWD/util/qsgtextureprovider.h \
- $$PWD/util/qsgdefaultpainternode_p.h \
$$PWD/util/qsgdistancefieldutil_p.h \
- $$PWD/util/qsgshadersourcebuilder_p.h
+ $$PWD/util/qsgflatcolormaterial.h \
+ $$PWD/util/qsgsimplematerial.h \
+ $$PWD/util/qsgtexturematerial.h \
+ $$PWD/util/qsgtexturematerial_p.h \
+ $$PWD/util/qsgvertexcolormaterial.h \
+ $$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
+
+contains(QT_CONFIG, opengl(es1|es2)?) {
+ HEADERS += \
+ $$PWD/util/qsgdepthstencilbuffer_p.h \
+ $$PWD/util/qsgshadersourcebuilder_p.h \
+ $$PWD/util/qsgatlastexture_p.h
+ SOURCES += \
+ $$PWD/util/qsgdepthstencilbuffer.cpp \
+ $$PWD/util/qsgatlastexture.cpp \
+ $$PWD/util/qsgshadersourcebuilder.cpp
+}
+
# QML / Adaptations API
HEADERS += \
$$PWD/qsgadaptationlayer_p.h \
$$PWD/qsgcontext_p.h \
$$PWD/qsgcontextplugin_p.h \
- $$PWD/qsgdefaultglyphnode_p.h \
- $$PWD/qsgdefaultdistancefieldglyphcache_p.h \
- $$PWD/qsgdistancefieldglyphnode_p.h \
- $$PWD/qsgdistancefieldglyphnode_p_p.h \
- $$PWD/qsgdefaultglyphnode_p_p.h \
- $$PWD/qsgdefaultimagenode_p.h \
- $$PWD/qsgdefaultrectanglenode_p.h \
- $$PWD/qsgrenderloop_p.h \
- $$PWD/qsgthreadedrenderloop_p.h \
- $$PWD/qsgwindowsrenderloop_p.h \
- $$PWD/qsgdefaultlayer_p.h
+ $$PWD/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
+
+contains(QT_CONFIG, opengl(es1|es2)?) {
+ SOURCES += \
+ $$PWD/qsgdefaultglyphnode.cpp \
+ $$PWD/qsgdefaultglyphnode_p.cpp \
+ $$PWD/qsgdefaultdistancefieldglyphcache.cpp \
+ $$PWD/qsgdistancefieldglyphnode.cpp \
+ $$PWD/qsgdistancefieldglyphnode_p.cpp \
+ $$PWD/qsgdefaultinternalimagenode.cpp \
+ $$PWD/qsgdefaultinternalrectanglenode.cpp \
+ $$PWD/qsgdefaultrendercontext.cpp \
+ $$PWD/qsgdefaultcontext.cpp \
+ $$PWD/qsgdefaultspritenode.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/qsgdefaultspritenode_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
+}
+
+# Built-in, non-plugin-based adaptations
+include(adaptations/adaptations.pri)
RESOURCES += \
$$PWD/scenegraph.qrc
-OTHER_FILES += \
- $$PWD/shaders/24bittextmask.frag \
- $$PWD/shaders/8bittextmask.frag \
- $$PWD/shaders/distancefieldoutlinetext.frag \
- $$PWD/shaders/distancefieldshiftedtext.frag \
- $$PWD/shaders/distancefieldshiftedtext.vert \
- $$PWD/shaders/distancefieldtext.frag \
- $$PWD/shaders/distancefieldtext.vert \
- $$PWD/shaders/flatcolor.frag \
- $$PWD/shaders/flatcolor.vert \
- $$PWD/shaders/hiqsubpixeldistancefieldtext.frag \
- $$PWD/shaders/hiqsubpixeldistancefieldtext.vert \
- $$PWD/shaders/loqsubpixeldistancefieldtext.frag \
- $$PWD/shaders/loqsubpixeldistancefieldtext.vert \
- $$PWD/shaders/opaquetexture.frag \
- $$PWD/shaders/opaquetexture.vert \
- $$PWD/shaders/outlinedtext.frag \
- $$PWD/shaders/outlinedtext.vert \
- $$PWD/shaders/rendernode.frag \
- $$PWD/shaders/rendernode.vert \
- $$PWD/shaders/smoothcolor.frag \
- $$PWD/shaders/smoothcolor.vert \
- $$PWD/shaders/smoothtexture.frag \
- $$PWD/shaders/smoothtexture.vert \
- $$PWD/shaders/stencilclip.frag \
- $$PWD/shaders/stencilclip.vert \
- $$PWD/shaders/styledtext.frag \
- $$PWD/shaders/styledtext.vert \
- $$PWD/shaders/textmask.frag \
- $$PWD/shaders/textmask.vert \
- $$PWD/shaders/texture.frag \
- $$PWD/shaders/vertexcolor.frag \
- $$PWD/shaders/vertexcolor.vert \
- $$PWD/shaders/24bittextmask_core.frag \
- $$PWD/shaders/8bittextmask_core.frag \
- $$PWD/shaders/distancefieldoutlinetext_core.frag \
- $$PWD/shaders/distancefieldshiftedtext_core.frag \
- $$PWD/shaders/distancefieldshiftedtext_core.vert \
- $$PWD/shaders/distancefieldtext_core.frag \
- $$PWD/shaders/distancefieldtext_core.vert \
- $$PWD/shaders/flatcolor_core.frag \
- $$PWD/shaders/flatcolor_core.vert \
- $$PWD/shaders/hiqsubpixeldistancefieldtext_core.frag \
- $$PWD/shaders/hiqsubpixeldistancefieldtext_core.vert \
- $$PWD/shaders/loqsubpixeldistancefieldtext_core.frag \
- $$PWD/shaders/loqsubpixeldistancefieldtext_core.vert \
- $$PWD/shaders/opaquetexture_core.frag \
- $$PWD/shaders/opaquetexture_core.vert \
- $$PWD/shaders/outlinedtext_core.frag \
- $$PWD/shaders/outlinedtext_core.vert \
- $$PWD/shaders/rendernode_core.frag \
- $$PWD/shaders/rendernode_core.vert \
- $$PWD/shaders/smoothcolor_core.frag \
- $$PWD/shaders/smoothcolor_core.vert \
- $$PWD/shaders/smoothtexture_core.frag \
- $$PWD/shaders/smoothtexture_core.vert \
- $$PWD/shaders/stencilclip_core.frag \
- $$PWD/shaders/stencilclip_core.vert \
- $$PWD/shaders/styledtext_core.frag \
- $$PWD/shaders/styledtext_core.vert \
- $$PWD/shaders/textmask_core.frag \
- $$PWD/shaders/textmask_core.vert \
- $$PWD/shaders/texture_core.frag \
- $$PWD/shaders/vertexcolor_core.frag \
- $$PWD/shaders/vertexcolor_core.vert \
- scenegraph/shaders/visualization.frag \
- scenegraph/shaders/visualization.vert
-
+# OpenGL Shaders
+contains(QT_CONFIG, opengl(es1|es2)?) {
+ OTHER_FILES += \
+ $$PWD/shaders/24bittextmask.frag \
+ $$PWD/shaders/8bittextmask.frag \
+ $$PWD/shaders/distancefieldoutlinetext.frag \
+ $$PWD/shaders/distancefieldshiftedtext.frag \
+ $$PWD/shaders/distancefieldshiftedtext.vert \
+ $$PWD/shaders/distancefieldtext.frag \
+ $$PWD/shaders/distancefieldtext.vert \
+ $$PWD/shaders/flatcolor.frag \
+ $$PWD/shaders/flatcolor.vert \
+ $$PWD/shaders/hiqsubpixeldistancefieldtext.frag \
+ $$PWD/shaders/hiqsubpixeldistancefieldtext.vert \
+ $$PWD/shaders/loqsubpixeldistancefieldtext.frag \
+ $$PWD/shaders/loqsubpixeldistancefieldtext.vert \
+ $$PWD/shaders/opaquetexture.frag \
+ $$PWD/shaders/opaquetexture.vert \
+ $$PWD/shaders/outlinedtext.frag \
+ $$PWD/shaders/outlinedtext.vert \
+ $$PWD/shaders/rendernode.frag \
+ $$PWD/shaders/rendernode.vert \
+ $$PWD/shaders/smoothcolor.frag \
+ $$PWD/shaders/smoothcolor.vert \
+ $$PWD/shaders/smoothtexture.frag \
+ $$PWD/shaders/smoothtexture.vert \
+ $$PWD/shaders/stencilclip.frag \
+ $$PWD/shaders/stencilclip.vert \
+ $$PWD/shaders/styledtext.frag \
+ $$PWD/shaders/styledtext.vert \
+ $$PWD/shaders/textmask.frag \
+ $$PWD/shaders/textmask.vert \
+ $$PWD/shaders/texture.frag \
+ $$PWD/shaders/vertexcolor.frag \
+ $$PWD/shaders/vertexcolor.vert \
+ $$PWD/shaders/24bittextmask_core.frag \
+ $$PWD/shaders/8bittextmask_core.frag \
+ $$PWD/shaders/distancefieldoutlinetext_core.frag \
+ $$PWD/shaders/distancefieldshiftedtext_core.frag \
+ $$PWD/shaders/distancefieldshiftedtext_core.vert \
+ $$PWD/shaders/distancefieldtext_core.frag \
+ $$PWD/shaders/distancefieldtext_core.vert \
+ $$PWD/shaders/flatcolor_core.frag \
+ $$PWD/shaders/flatcolor_core.vert \
+ $$PWD/shaders/hiqsubpixeldistancefieldtext_core.frag \
+ $$PWD/shaders/hiqsubpixeldistancefieldtext_core.vert \
+ $$PWD/shaders/loqsubpixeldistancefieldtext_core.frag \
+ $$PWD/shaders/loqsubpixeldistancefieldtext_core.vert \
+ $$PWD/shaders/opaquetexture_core.frag \
+ $$PWD/shaders/opaquetexture_core.vert \
+ $$PWD/shaders/outlinedtext_core.frag \
+ $$PWD/shaders/outlinedtext_core.vert \
+ $$PWD/shaders/rendernode_core.frag \
+ $$PWD/shaders/rendernode_core.vert \
+ $$PWD/shaders/smoothcolor_core.frag \
+ $$PWD/shaders/smoothcolor_core.vert \
+ $$PWD/shaders/smoothtexture_core.frag \
+ $$PWD/shaders/smoothtexture_core.vert \
+ $$PWD/shaders/stencilclip_core.frag \
+ $$PWD/shaders/stencilclip_core.vert \
+ $$PWD/shaders/styledtext_core.frag \
+ $$PWD/shaders/styledtext_core.vert \
+ $$PWD/shaders/textmask_core.frag \
+ $$PWD/shaders/textmask_core.vert \
+ $$PWD/shaders/texture_core.frag \
+ $$PWD/shaders/vertexcolor_core.frag \
+ $$PWD/shaders/vertexcolor_core.vert \
+ $$PWD/shaders/visualization.frag \
+ $$PWD/shaders/visualization.vert
+}
diff --git a/src/quick/scenegraph/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 e56f586c90..561eccdb0c 100644
--- a/src/quick/scenegraph/util/qsgdefaultpainternode.cpp
+++ b/src/quick/scenegraph/util/qsgdefaultpainternode.cpp
@@ -41,6 +41,7 @@
#include <QtQuick/private/qquickpainteditem_p.h>
+#include <QtQuick/private/qsgdefaultrendercontext_p.h>
#include <QtQuick/private/qsgcontext_p.h>
#include <private/qopenglextensions_p.h>
#include <qopenglframebufferobject.h>
@@ -96,7 +97,7 @@ QSGDefaultPainterNode::QSGDefaultPainterNode(QQuickPaintedItem *item)
, m_dirtyRenderTarget(false)
, m_dirtyTexture(false)
{
- m_context = static_cast<QQuickPaintedItemPrivate *>(QObjectPrivate::get(item))->sceneGraphRenderContext();
+ m_context = static_cast<QSGDefaultRenderContext *>(static_cast<QQuickPaintedItemPrivate *>(QObjectPrivate::get(item))->sceneGraphRenderContext());
setMaterial(&m_materialO);
setOpaqueMaterial(&m_material);
diff --git a/src/quick/scenegraph/util/qsgdefaultpainternode_p.h b/src/quick/scenegraph/util/qsgdefaultpainternode_p.h
index 3cabe01511..069ef155b1 100644
--- a/src/quick/scenegraph/util/qsgdefaultpainternode_p.h
+++ b/src/quick/scenegraph/util/qsgdefaultpainternode_p.h
@@ -63,6 +63,7 @@ QT_BEGIN_NAMESPACE
class QOpenGLFramebufferObject;
class QOpenGLPaintDevice;
+class QSGDefaultRenderContext;
class Q_QUICK_PRIVATE_EXPORT QSGPainterTexture : public QSGPlainTexture
{
@@ -127,7 +128,7 @@ private:
void updateRenderTarget();
void updateFBOSize();
- QSGRenderContext *m_context;
+ QSGDefaultRenderContext *m_context;
QQuickPaintedItem::RenderTarget m_preferredRenderTarget;
QQuickPaintedItem::RenderTarget m_actualRenderTarget;
diff --git a/src/quick/scenegraph/util/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..d782f9309f 100644
--- a/src/quick/util/qquickanimation.cpp
+++ b/src/quick/util/qquickanimation.cpp
@@ -1202,7 +1202,7 @@ QAbstractAnimationJob* QQuickPropertyAction::transition(QQuickStateActions &acti
{
for (int ii = 0; ii < actions.count(); ++ii) {
const QQuickStateAction &action = actions.at(ii);
- QQmlPropertyPrivate::write(action.property, action.toValue, QQmlPropertyPrivate::BypassInterceptor | QQmlPropertyPrivate::DontRemoveBinding);
+ QQmlPropertyPrivate::write(action.property, action.toValue, QQmlPropertyData::BypassInterceptor | QQmlPropertyData::DontRemoveBinding);
}
}
virtual void debugAction(QDebug d, int indentLevel) const {
@@ -2535,7 +2535,7 @@ void QQuickAnimationPropertyUpdater::setValue(qreal v)
QQuickStateAction &action = actions[ii];
if (v == 1.) {
- QQmlPropertyPrivate::write(action.property, action.toValue, QQmlPropertyPrivate::BypassInterceptor | QQmlPropertyPrivate::DontRemoveBinding);
+ QQmlPropertyPrivate::write(action.property, action.toValue, QQmlPropertyData::BypassInterceptor | QQmlPropertyData::DontRemoveBinding);
} else {
if (!fromSourced && !fromDefined) {
action.fromValue = action.property.read();
@@ -2551,7 +2551,7 @@ void QQuickAnimationPropertyUpdater::setValue(qreal v)
}
}
if (interpolator)
- QQmlPropertyPrivate::write(action.property, interpolator(action.fromValue.constData(), action.toValue.constData(), v), QQmlPropertyPrivate::BypassInterceptor | QQmlPropertyPrivate::DontRemoveBinding);
+ QQmlPropertyPrivate::write(action.property, interpolator(action.fromValue.constData(), action.toValue.constData(), v), QQmlPropertyData::BypassInterceptor | QQmlPropertyData::DontRemoveBinding);
}
if (deleted)
return;
diff --git a/src/quick/util/qquickanimator.cpp b/src/quick/util/qquickanimator.cpp
index abae6321b0..100b7bd402 100644
--- a/src/quick/util/qquickanimator.cpp
+++ b/src/quick/util/qquickanimator.cpp
@@ -502,7 +502,7 @@ QQuickRotationAnimator::RotationDirection QQuickRotationAnimator::direction() co
Q_D(const QQuickRotationAnimator);
return d->direction;
}
-
+#ifndef QT_NO_OPENGL
/*!
\qmltype UniformAnimator
\instantiates QQuickUniformAnimator
@@ -580,5 +580,6 @@ QQuickAnimatorJob *QQuickUniformAnimator::createJob() const
job->setUniform(u.toLatin1());
return job;
}
+#endif
QT_END_NAMESPACE
diff --git a/src/quick/util/qquickanimator_p.h b/src/quick/util/qquickanimator_p.h
index c23aa0a7e9..f1e2d4e1d9 100644
--- a/src/quick/util/qquickanimator_p.h
+++ b/src/quick/util/qquickanimator_p.h
@@ -169,7 +169,7 @@ protected:
QQuickAnimatorJob *createJob() const;
QString propertyName() const { return QStringLiteral("rotation"); }
};
-
+#ifndef QT_NO_OPENGL
class QQuickUniformAnimatorPrivate;
class Q_QUICK_PRIVATE_EXPORT QQuickUniformAnimator : public QQuickAnimator
{
@@ -190,6 +190,7 @@ protected:
QQuickAnimatorJob *createJob() const;
QString propertyName() const;
};
+#endif
QT_END_NAMESPACE
@@ -199,6 +200,7 @@ QML_DECLARE_TYPE(QQuickYAnimator)
QML_DECLARE_TYPE(QQuickScaleAnimator)
QML_DECLARE_TYPE(QQuickRotationAnimator)
QML_DECLARE_TYPE(QQuickOpacityAnimator)
+#ifndef QT_NO_OPENGL
QML_DECLARE_TYPE(QQuickUniformAnimator)
-
+#endif
#endif // QQUICKANIMATOR_P_H
diff --git a/src/quick/util/qquickanimatorjob.cpp b/src/quick/util/qquickanimatorjob.cpp
index 8d5ecadab5..a0c787dae5 100644
--- a/src/quick/util/qquickanimatorjob.cpp
+++ b/src/quick/util/qquickanimatorjob.cpp
@@ -43,8 +43,10 @@
#include "qquickanimator_p_p.h"
#include <private/qquickwindow_p.h>
#include <private/qquickitem_p.h>
-#include <private/qquickshadereffectnode_p.h>
-
+#ifndef QT_NO_OPENGL
+# include <private/qquickopenglshadereffectnode_p.h>
+# include <private/qquickopenglshadereffect_p.h>
+#endif
#include <private/qanimationgroupjob_p.h>
#include <qcoreapplication.h>
@@ -173,7 +175,7 @@ void QQuickAnimatorProxyJob::setWindow(QQuickWindow *window)
} else if (!m_controller && m_job) {
m_controller = QQuickWindowPrivate::get(window)->animationController;
- if (window->openglContext())
+ if (window->isSceneGraphInitialized())
readyToAnimate();
else
connect(window, SIGNAL(sceneGraphInitialized()), this, SLOT(sceneGraphInitialized()));
@@ -390,7 +392,6 @@ void QQuickXAnimatorJob::updateCurrentTime(int time)
{
if (!m_controller)
return;
- Q_ASSERT(!m_controller->m_window->openglContext() || m_controller->m_window->openglContext()->thread() == QThread::currentThread());
m_value = m_from + (m_to - m_from) * progress(time);
m_helper->dx = m_value;
@@ -407,7 +408,6 @@ void QQuickYAnimatorJob::updateCurrentTime(int time)
{
if (!m_controller)
return;
- Q_ASSERT(!m_controller->m_window->openglContext() || m_controller->m_window->openglContext()->thread() == QThread::currentThread());
m_value = m_from + (m_to - m_from) * progress(time);
m_helper->dy = m_value;
@@ -477,7 +477,6 @@ void QQuickOpacityAnimatorJob::updateCurrentTime(int time)
{
if (!m_controller || !m_opacityNode)
return;
- Q_ASSERT(!m_controller->m_window->openglContext() || m_controller->m_window->openglContext()->thread() == QThread::currentThread());
m_value = m_from + (m_to - m_from) * progress(time);
m_opacityNode->setOpacity(m_value);
@@ -493,7 +492,6 @@ void QQuickScaleAnimatorJob::updateCurrentTime(int time)
{
if (!m_controller)
return;
- Q_ASSERT(!m_controller->m_window->openglContext() || m_controller->m_window->openglContext()->thread() == QThread::currentThread());
m_value = m_from + (m_to - m_from) * progress(time);
m_helper->scale = m_value;
@@ -513,7 +511,6 @@ void QQuickRotationAnimatorJob::updateCurrentTime(int time)
{
if (!m_controller)
return;
- Q_ASSERT(!m_controller->m_window->openglContext() || m_controller->m_window->openglContext()->thread() == QThread::currentThread());
float t = progress(time);
@@ -546,6 +543,7 @@ void QQuickRotationAnimatorJob::writeBack()
m_target->setRotation(value());
}
+#ifndef QT_NO_OPENGL
QQuickUniformAnimatorJob::QQuickUniformAnimatorJob()
: m_node(0)
, m_uniformIndex(-1)
@@ -556,7 +554,7 @@ QQuickUniformAnimatorJob::QQuickUniformAnimatorJob()
void QQuickUniformAnimatorJob::setTarget(QQuickItem *target)
{
- if (qobject_cast<QQuickShaderEffect *>(target) != 0)
+ if (qobject_cast<QQuickOpenGLShaderEffect *>(target) != 0)
m_target = target;
}
@@ -569,14 +567,14 @@ void QQuickUniformAnimatorJob::nodeWasDestroyed()
void QQuickUniformAnimatorJob::afterNodeSync()
{
- m_node = static_cast<QQuickShaderEffectNode *>(QQuickItemPrivate::get(m_target)->paintNode);
+ m_node = static_cast<QQuickOpenGLShaderEffectNode *>(QQuickItemPrivate::get(m_target)->paintNode);
if (m_node && m_uniformIndex == -1 && m_uniformType == -1) {
- QQuickShaderEffectMaterial *material =
- static_cast<QQuickShaderEffectMaterial *>(m_node->material());
+ QQuickOpenGLShaderEffectMaterial *material =
+ static_cast<QQuickOpenGLShaderEffectMaterial *>(m_node->material());
bool found = false;
- for (int t=0; !found && t<QQuickShaderEffectMaterialKey::ShaderTypeCount; ++t) {
- const QVector<QQuickShaderEffectMaterial::UniformData> &uniforms = material->uniforms[t];
+ for (int t=0; !found && t<QQuickOpenGLShaderEffectMaterialKey::ShaderTypeCount; ++t) {
+ const QVector<QQuickOpenGLShaderEffectMaterial::UniformData> &uniforms = material->uniforms[t];
for (int i=0; i<uniforms.size(); ++i) {
if (uniforms.at(i).name == m_uniform) {
m_uniformIndex = i;
@@ -594,15 +592,14 @@ void QQuickUniformAnimatorJob::updateCurrentTime(int time)
{
if (!m_controller)
return;
- Q_ASSERT(!m_controller->m_window->openglContext() || m_controller->m_window->openglContext()->thread() == QThread::currentThread());
if (!m_node || m_uniformIndex == -1 || m_uniformType == -1)
return;
m_value = m_from + (m_to - m_from) * progress(time);
- QQuickShaderEffectMaterial *material =
- static_cast<QQuickShaderEffectMaterial *>(m_node->material());
+ QQuickOpenGLShaderEffectMaterial *material =
+ static_cast<QQuickOpenGLShaderEffectMaterial *>(m_node->material());
material->uniforms[m_uniformType][m_uniformIndex].value = m_value;
// As we're not touching the nodes, we need to explicitly mark it dirty.
// Otherwise, the renderer will abort repainting if this was the only
@@ -615,5 +612,6 @@ void QQuickUniformAnimatorJob::writeBack()
if (m_target)
m_target->setProperty(m_uniform, value());
}
+#endif
QT_END_NAMESPACE
diff --git a/src/quick/util/qquickanimatorjob_p.h b/src/quick/util/qquickanimatorjob_p.h
index 2b910d3737..64e849d322 100644
--- a/src/quick/util/qquickanimatorjob_p.h
+++ b/src/quick/util/qquickanimatorjob_p.h
@@ -68,7 +68,7 @@ class QQuickAbstractAnimation;
class QQuickAnimatorController;
class QQuickAnimatorProxyJobPrivate;
-class QQuickShaderEffectNode;
+class QQuickOpenGLShaderEffectNode;
class QSGOpacityNode;
@@ -277,7 +277,7 @@ public:
private:
QSGOpacityNode *m_opacityNode;
};
-
+#ifndef QT_NO_OPENGL
class Q_QUICK_PRIVATE_EXPORT QQuickUniformAnimatorJob : public QQuickAnimatorJob
{
public:
@@ -296,11 +296,12 @@ public:
private:
QByteArray m_uniform;
- QQuickShaderEffectNode *m_node;
+ QQuickOpenGLShaderEffectNode *m_node;
int m_uniformIndex : 8;
int m_uniformType : 8;
};
+#endif
QT_END_NAMESPACE
diff --git a/src/quick/util/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..b7367f3934 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>
+#ifndef QT_NO_NETWORK
+#include <QNetworkRequest>
+#include <QNetworkReply>
+#endif
+
#include <QtCore/QCoreApplication>
QT_BEGIN_NAMESPACE
@@ -66,28 +70,37 @@ Q_OBJECT
public:
explicit QQuickFontObject(int _id = -1);
+#ifndef QT_NO_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 // QT_NO_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)
+#ifndef QT_NO_NETWORK
+ ,redirectCount(0), reply(0)
+#endif
+ ,id(_id)
+{
+}
+#ifndef QT_NO_NETWORK
void QQuickFontObject::download(const QUrl &url, QNetworkAccessManager *manager)
{
QNetworkRequest req(url);
@@ -128,7 +141,7 @@ void QQuickFontObject::replyFinished()
reply = 0;
}
}
-
+#endif // QT_NO_NETWORK
class QQuickFontLoaderPrivate : public QObjectPrivate
{
@@ -255,6 +268,7 @@ void QQuickFontLoader::setSource(const QUrl &url)
}
} else {
if (!fontLoaderFonts()->map.contains(d->url)) {
+#ifndef QT_NO_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];
if (fo->id == -1) {
+#ifndef QT_NO_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_p.h b/src/quick/util/qquickpath_p.h
index e2c99de44e..fa6cbc668a 100644
--- a/src/quick/util/qquickpath_p.h
+++ b/src/quick/util/qquickpath_p.h
@@ -55,6 +55,7 @@
#include <private/qqmlnullablevalue_p.h>
#include <private/qbezier_p.h>
+#include <private/qtquickglobal_p.h>
#include <QtCore/QObject>
#include <QtGui/QPainterPath>
@@ -69,7 +70,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 +79,7 @@ Q_SIGNALS:
void changed();
};
-class Q_AUTOTEST_EXPORT QQuickPathAttribute : public QQuickPathElement
+class Q_QUICK_PRIVATE_EXPORT QQuickPathAttribute : public QQuickPathElement
{
Q_OBJECT
@@ -103,7 +104,7 @@ private:
qreal _value;
};
-class Q_AUTOTEST_EXPORT QQuickCurve : public QQuickPathElement
+class Q_QUICK_PRIVATE_EXPORT QQuickCurve : public QQuickPathElement
{
Q_OBJECT
@@ -145,7 +146,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 +155,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 +195,7 @@ private:
QQmlNullableValue<qreal> _relativeControlY;
};
-class Q_AUTOTEST_EXPORT QQuickPathCubic : public QQuickCurve
+class Q_QUICK_PRIVATE_EXPORT QQuickPathCubic : public QQuickCurve
{
Q_OBJECT
@@ -260,7 +261,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 +270,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 +312,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 +331,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 +360,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/qquickpixmapcache.cpp b/src/quick/util/qquickpixmapcache.cpp
index 6dee84b005..dc4b27d738 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>
+#ifndef QT_NO_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 *);
+#ifndef QT_NO_NETWORK
void networkRequestDone(QNetworkReply *);
+#endif
void asyncResponseFinished(QQuickImageResponse *);
QList<QQuickPixmapReply*> jobs;
@@ -215,10 +220,11 @@ private:
QQuickPixmapReaderThreadObject *threadObject;
QWaitCondition waitCondition;
+#ifndef QT_NO_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;
}
+#ifndef QT_NO_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)
+#ifndef QT_NO_NETWORK
+, accessManager(0)
+#endif
{
eventLoopQuitHack = new QObject;
eventLoopQuitHack->moveToThread(this);
@@ -443,13 +454,21 @@ QQuickPixmapReader::~QQuickPixmapReader()
delete reply;
}
jobs.clear();
- QList<QQuickPixmapReply*> activeJobs = networkJobs.values() + asyncResponses.values();
- foreach (QQuickPixmapReply *reply, activeJobs ) {
+#ifndef QT_NO_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();
}
+#ifndef QT_NO_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 // QT_NO_NETWORK
void QQuickPixmapReader::asyncResponseFinished(QQuickImageResponse *response)
{
@@ -556,8 +577,10 @@ bool QQuickPixmapReaderThreadObject::event(QEvent *e)
void QQuickPixmapReaderThreadObject::networkRequestDone()
{
+#ifndef QT_NO_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()) {
+#ifndef QT_NO_NETWORK
for (int i = 0; i < cancelled.count(); ++i) {
QQuickPixmapReply *job = cancelled.at(i);
QNetworkReply *reply = networkJobs.key(job, 0);
@@ -598,6 +622,7 @@ void QQuickPixmapReader::processJobs()
job->deleteLater();
}
cancelled.clear();
+#endif
}
if (!jobs.isEmpty()) {
@@ -618,7 +643,11 @@ void QQuickPixmapReader::processJobs()
usableJob = true;
} else {
localFile = QQmlFile::urlToLocalFileOrQrc(url);
- usableJob = !localFile.isEmpty() || networkJobs.count() < IMAGEREQUEST_MAX_NETWORK_REQUEST_COUNT;
+ usableJob = !localFile.isEmpty()
+#ifndef QT_NO_NETWORK
+ || networkJobs.count() < IMAGEREQUEST_MAX_NETWORK_REQUEST_COUNT
+#endif
+ ;
}
@@ -743,6 +772,7 @@ void QQuickPixmapReader::processJob(QQuickPixmapReply *runningJob, const QUrl &u
runningJob->postReply(errorCode, errorStr, readSize, QQuickTextureFactory::textureFactoryForImage(image));
mutex.unlock();
} else {
+#ifndef QT_NO_NETWORK
// Network resource
QNetworkRequest req(url);
req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true);
@@ -752,6 +782,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
}
}
}
@@ -808,11 +841,13 @@ void QQuickPixmapReader::cancel(QQuickPixmapReply *reply)
void QQuickPixmapReader::run()
{
if (replyDownloadProgress == -1) {
+#ifndef QT_NO_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();
@@ -887,15 +922,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..c4be68cd31 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;
@@ -258,7 +257,7 @@ void QQuickPropertyChangesPrivate::decode()
return;
foreach (const QV4::CompiledData::Binding *binding, bindings)
- decodeBinding(QString(), cdata->compilationUnit->data, binding);
+ 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[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[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..a992589040 100644
--- a/src/quick/util/qquicksmoothedanimation.cpp
+++ b/src/quick/util/qquicksmoothedanimation.cpp
@@ -254,8 +254,8 @@ void QSmoothedAnimation::updateCurrentTime(int t)
qreal value = easeFollow(time_seconds);
value *= (invert? -1.0: 1.0);
QQmlPropertyPrivate::write(target, initialValue + value,
- QQmlPropertyPrivate::BypassInterceptor
- | QQmlPropertyPrivate::DontRemoveBinding);
+ QQmlPropertyData::BypassInterceptor
+ | QQmlPropertyData::DontRemoveBinding);
}
void QSmoothedAnimation::init()
@@ -287,8 +287,8 @@ void QSmoothedAnimation::init()
break;
case QQuickSmoothedAnimation::Sync:
QQmlPropertyPrivate::write(target, to,
- QQmlPropertyPrivate::BypassInterceptor
- | QQmlPropertyPrivate::DontRemoveBinding);
+ QQmlPropertyData::BypassInterceptor
+ | QQmlPropertyData::DontRemoveBinding);
trackVelocity = 0;
stop();
return;
@@ -304,8 +304,8 @@ void QSmoothedAnimation::init()
if (!recalc()) {
QQmlPropertyPrivate::write(target, to,
- QQmlPropertyPrivate::BypassInterceptor
- | QQmlPropertyPrivate::DontRemoveBinding);
+ QQmlPropertyData::BypassInterceptor
+ | QQmlPropertyData::DontRemoveBinding);
stop();
return;
}
@@ -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;
@@ -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/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/qquicktransitionmanager.cpp b/src/quick/util/qquicktransitionmanager.cpp
index 55abb0a207..60f710549b 100644
--- a/src/quick/util/qquicktransitionmanager.cpp
+++ b/src/quick/util/qquicktransitionmanager.cpp
@@ -159,9 +159,9 @@ void QQuickTransitionManager::transition(const QList<QQuickStateAction> &list,
for (int ii = 0; ii < applyList.size(); ++ii) {
const QQuickStateAction &action = applyList.at(ii);
if (action.toBinding) {
- QQmlPropertyPrivate::setBinding(action.toBinding.data(), QQmlPropertyPrivate::None, QQmlPropertyPrivate::BypassInterceptor | QQmlPropertyPrivate::DontRemoveBinding);
+ QQmlPropertyPrivate::setBinding(action.toBinding.data(), QQmlPropertyPrivate::None, QQmlPropertyData::BypassInterceptor | QQmlPropertyData::DontRemoveBinding);
} else if (!action.event) {
- QQmlPropertyPrivate::write(action.property, action.toValue, QQmlPropertyPrivate::BypassInterceptor | QQmlPropertyPrivate::DontRemoveBinding);
+ QQmlPropertyPrivate::write(action.property, action.toValue, QQmlPropertyData::BypassInterceptor | QQmlPropertyData::DontRemoveBinding);
} else if (action.event->isReversable()) {
if (action.reverseEvent)
action.event->reverse();
@@ -197,7 +197,7 @@ void QQuickTransitionManager::transition(const QList<QQuickStateAction> &list,
if (action.toBinding)
QQmlPropertyPrivate::removeBinding(action.property); // Make sure this is disabled during the transition
- QQmlPropertyPrivate::write(action.property, action.fromValue, QQmlPropertyPrivate::BypassInterceptor | QQmlPropertyPrivate::DontRemoveBinding);
+ QQmlPropertyPrivate::write(action.property, action.fromValue, QQmlPropertyData::BypassInterceptor | QQmlPropertyData::DontRemoveBinding);
}
}
diff --git a/src/quick/util/qquickutilmodule.cpp b/src/quick/util/qquickutilmodule.cpp
index f5d11c6230..66994e22ba 100644
--- a/src/quick/util/qquickutilmodule.cpp
+++ b/src/quick/util/qquickutilmodule.cpp
@@ -107,8 +107,9 @@ void QQuickUtilModule::defineModule()
qmlRegisterType<QQuickScaleAnimator>("QtQuick", 2, 2, "ScaleAnimator");
qmlRegisterType<QQuickRotationAnimator>("QtQuick", 2, 2, "RotationAnimator");
qmlRegisterType<QQuickOpacityAnimator>("QtQuick", 2, 2, "OpacityAnimator");
+#ifndef QT_NO_OPENGL
qmlRegisterType<QQuickUniformAnimator>("QtQuick", 2, 2, "UniformAnimator");
-
+#endif
qmlRegisterType<QQuickStateOperation>();
qmlRegisterCustomType<QQuickPropertyChanges>("QtQuick",2,0,"PropertyChanges", new QQuickPropertyChangesParser);
diff --git a/src/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..2c31f6feed 100644
--- a/src/quick/util/util.pri
+++ b/src/quick/util/util.pri
@@ -26,12 +26,17 @@ 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) {
+ DEFINES += QT_NO_QML_DEBUGGER
+} else {
+ SOURCES += $$PWD/qquickprofiler.cpp
+}
+
HEADERS += \
$$PWD/qquickapplication_p.h\
$$PWD/qquickutilmodule_p.h\
diff --git a/src/quickwidgets/qquickwidget.cpp b/src/quickwidgets/qquickwidget.cpp
index b0838e5dc4..ad04c76783 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) {
+ 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);
}
@@ -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
@@ -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,24 @@ QQuickWindow *QQuickWidget::quickWindow() const
}
QT_END_NAMESPACE
+
+
+void QQuickWidget::paintEvent(QPaintEvent *event)
+{
+ Q_UNUSED(event)
+ Q_D(QQuickWidget);
+ if (d->useSoftwareRenderer) {
+ QPainter painter(this);
+ if (d->updateRegion.isNull()) {
+ //Paint everything
+ painter.drawImage(rect(), d->softwareImage);
+ } else {
+ //Paint only the updated areas
+ for (auto targetRect : d->updateRegion.rects()) {
+ auto sourceRect = QRect(targetRect.topLeft() * devicePixelRatio(), targetRect.size() * devicePixelRatio());
+ painter.drawImage(targetRect, d->softwareImage, sourceRect);
+ }
+ d->updateRegion = QRegion();
+ }
+ }
+}
diff --git a/src/quickwidgets/qquickwidget.h b/src/quickwidgets/qquickwidget.h
index 0732c506bb..56e6b01ac5 100644
--- a/src/quickwidgets/qquickwidget.h
+++ b/src/quickwidgets/qquickwidget.h
@@ -87,7 +87,7 @@ public:
QList<QQmlError> errors() const;
- QSize sizeHint() const;
+ QSize sizeHint() const override;
QSize initialSize() const;
void setFormat(const QSurfaceFormat &format);
@@ -115,34 +115,35 @@ private Q_SLOTS:
void triggerUpdate();
protected:
- virtual void resizeEvent(QResizeEvent *);
- virtual void timerEvent(QTimerEvent*);
+ void resizeEvent(QResizeEvent *) override;
+ void timerEvent(QTimerEvent*) override;
- virtual void keyPressEvent(QKeyEvent *);
- virtual void keyReleaseEvent(QKeyEvent *);
- virtual void mousePressEvent(QMouseEvent *);
- virtual void mouseReleaseEvent(QMouseEvent *);
- virtual void mouseMoveEvent(QMouseEvent *);
- virtual void mouseDoubleClickEvent(QMouseEvent *);
+ void keyPressEvent(QKeyEvent *) override;
+ void keyReleaseEvent(QKeyEvent *) override;
+ void mousePressEvent(QMouseEvent *) override;
+ void mouseReleaseEvent(QMouseEvent *) override;
+ void mouseMoveEvent(QMouseEvent *) override;
+ void mouseDoubleClickEvent(QMouseEvent *) override;
- virtual void showEvent(QShowEvent *);
- virtual void hideEvent(QHideEvent *);
+ void showEvent(QShowEvent *) override;
+ void hideEvent(QHideEvent *) override;
- virtual void focusInEvent(QFocusEvent * event);
- virtual void focusOutEvent(QFocusEvent * event);
+ void focusInEvent(QFocusEvent * event) override;
+ void focusOutEvent(QFocusEvent * event) override;
#ifndef QT_NO_WHEELEVENT
- virtual void wheelEvent(QWheelEvent *);
+ void wheelEvent(QWheelEvent *) override;
#endif
#ifndef QT_NO_DRAGANDDROP
- virtual void dragEnterEvent(QDragEnterEvent *);
- virtual void dragMoveEvent(QDragMoveEvent *);
- virtual void dragLeaveEvent(QDragLeaveEvent *);
- virtual void dropEvent(QDropEvent *);
+ void dragEnterEvent(QDragEnterEvent *) override;
+ void dragMoveEvent(QDragMoveEvent *) override;
+ void dragLeaveEvent(QDragLeaveEvent *) override;
+ void dropEvent(QDropEvent *) override;
#endif
- bool event(QEvent *);
+ bool event(QEvent *) override;
+ void paintEvent(QPaintEvent *event) override;
private:
Q_DISABLE_COPY(QQuickWidget)
diff --git a/src/quickwidgets/qquickwidget_p.h b/src/quickwidgets/qquickwidget_p.h
index 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..0e45e63307 100644
--- a/src/quickwidgets/quickwidgets.pro
+++ b/src/quickwidgets/quickwidgets.pro
@@ -3,6 +3,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
+contains(QT_CONFIG, no-qml-debug): DEFINES += QT_NO_QML_DEBUGGER
HEADERS += \
qquickwidget.h \
diff --git a/src/src.pro b/src/src.pro
index 385e4eb601..fbc4feaec4 100644
--- a/src/src.pro
+++ b/src/src.pro
@@ -1,17 +1,16 @@
TEMPLATE = subdirs
CONFIG += ordered
SUBDIRS += \
- qml
+ qml \
+ quick \
+ qmltest
qtHaveModule(gui):contains(QT_CONFIG, opengl(es1|es2)?) {
- SUBDIRS += \
- quick \
- qmltest \
- particles
-
- qtHaveModule(widgets): SUBDIRS += quickwidgets
+ SUBDIRS += particles
}
+qtHaveModule(gui): qtHaveModule(widgets): SUBDIRS += quickwidgets
+
SUBDIRS += \
plugins \
imports \
diff --git a/tests/auto/auto.pro b/tests/auto/auto.pro
index 425e88b983..b72a43d742 100644
--- a/tests/auto/auto.pro
+++ b/tests/auto/auto.pro
@@ -2,16 +2,18 @@ TEMPLATE=subdirs
SUBDIRS=\
qml \
quick \
- particles \
qmltest \
qmldevtools \
cmake \
installed_cmake \
toolsupport
-qtHaveModule(widgets): SUBDIRS += quickwidgets
+qtHaveModule(gui):contains(QT_CONFIG, opengl(es1|es2)?) {
+ SUBDIRS += particles
+ qtHaveModule(widgets): SUBDIRS += quickwidgets
+
+}
qmldevtools.CONFIG = host_build
installed_cmake.depends = cmake
-
diff --git a/tests/auto/qml/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/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/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 b1d19b5796..3ceeb97718 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();
@@ -714,6 +715,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;
@@ -1027,11 +1126,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");
@@ -1060,6 +1162,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");
@@ -1068,6 +1172,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");
@@ -1920,6 +2025,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));
@@ -1958,6 +2064,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()");
diff --git a/tests/auto/qml/qml.pro b/tests/auto/qml/qml.pro
index 28f04be5d7..a1daa7a0c4 100644
--- a/tests/auto/qml/qml.pro
+++ b/tests/auto/qml/qml.pro
@@ -61,7 +61,9 @@ PRIVATETESTS += \
v4misc \
qqmltranslation \
qqmlimport \
- qqmlobjectmodel
+ qqmlobjectmodel \
+ qmldiskcache \
+ qv4mm
qtHaveModule(widgets) {
PUBLICTESTS += \
diff --git a/tests/auto/qml/qmldiskcache/qmldiskcache.pro b/tests/auto/qml/qmldiskcache/qmldiskcache.pro
new file mode 100644
index 0000000000..f2d1a04780
--- /dev/null
+++ b/tests/auto/qml/qmldiskcache/qmldiskcache.pro
@@ -0,0 +1,7 @@
+CONFIG += testcase
+TARGET = tst_qmldiskcache
+osx:CONFIG -= app_bundle
+
+SOURCES += tst_qmldiskcache.cpp
+
+QT += core-private qml-private testlib
diff --git a/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp
new file mode 100644
index 0000000000..1fe63bb99a
--- /dev/null
+++ b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp
@@ -0,0 +1,326 @@
+/****************************************************************************
+**
+** 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 <QThread>
+
+class tst_qmldiskcache: public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void initTestCase();
+
+ void regenerateAfterChange();
+ void registerImportForImplicitComponent();
+ void basicVersionChecks();
+};
+
+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();
+
+ // Qt API limits the precision of QFileInfo::modificationTime() to seconds, so to ensure that
+ // the newly written file has a modification date newer than an existing cache file, we must
+ // wait.
+ QThread::sleep(1);
+ {
+ 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;
+ }
+ }
+
+ QQmlComponent 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_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));
+ }
+}
+
+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/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/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 aeb8b08cd4..de3ead917f 100644
--- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
+++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
@@ -208,6 +208,7 @@ private slots:
void dynamicCreationOwnership();
void regExpBug();
void nullObjectBinding();
+ void nullObjectInitializer();
void deletedEngine();
void libraryScriptAssert();
void variantsAssignedUndefined();
@@ -2314,7 +2315,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;
@@ -2340,16 +2341,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,
@@ -2373,12 +2373,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)
@@ -2942,9 +2942,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);
@@ -5703,6 +5710,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()
{
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..74e54b64e9 100644
--- a/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp
+++ b/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp
@@ -317,6 +317,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 +620,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/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/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..3921f89d23 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)
@@ -98,6 +96,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 +108,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 +121,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 +168,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..af7dc155d0 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);
@@ -757,14 +756,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 +1113,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 +1193,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 +1201,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 +1247,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..1a035be5e0 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,7 +2118,7 @@ void tst_qqmllanguage::scriptStringWithoutSourceCode()
QQmlTypeData *td = eng->typeLoader.getType(url);
Q_ASSERT(td);
- QV4::CompiledData::Unit *qmlUnit = td->compiledData()->compilationUnit->data;
+ const QV4::CompiledData::Unit *qmlUnit = td->compilationUnit()->data;
Q_ASSERT(qmlUnit);
const QV4::CompiledData::Object *rootObject = qmlUnit->objectAt(qmlUnit->indexOfRootObject);
QCOMPARE(qmlUnit->stringAt(rootObject->inheritedTypeNameIndex), QString("MyTypeObject"));
@@ -2882,7 +2942,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 +3216,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 +3929,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
@@ -4120,14 +4187,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 +4205,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 e140747881..63d7f1c12b 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);
@@ -406,7 +406,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);
@@ -453,7 +453,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);
@@ -509,7 +509,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);
@@ -556,7 +556,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);
@@ -606,7 +606,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);
@@ -655,7 +655,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);
@@ -710,7 +710,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);
@@ -757,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);
@@ -813,7 +813,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);
@@ -860,7 +860,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);
@@ -910,7 +910,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);
@@ -959,7 +959,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);
diff --git a/tests/auto/qml/qqmlpropertycache/tst_qqmlpropertycache.cpp b/tests/auto/qml/qqmlpropertycache/tst_qqmlpropertycache.cpp
index 5f15afff85..2916d8455c 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,16 +110,16 @@ void tst_qqmlpropertycache::properties()
QQmlRefPointer<QQmlPropertyCache> cache(new QQmlPropertyCache(v4, metaObject));
QQmlPropertyData *data;
- QVERIFY(data = cacheProperty(cache, "propertyA"));
+ QVERIFY((data = cacheProperty(cache, "propertyA")));
QCOMPARE(data->coreIndex, metaObject->indexOfProperty("propertyA"));
- QVERIFY(data = cacheProperty(cache, "propertyB"));
+ QVERIFY((data = cacheProperty(cache, "propertyB")));
QCOMPARE(data->coreIndex, metaObject->indexOfProperty("propertyB"));
- QVERIFY(data = cacheProperty(cache, "propertyC"));
+ QVERIFY((data = cacheProperty(cache, "propertyC")));
QCOMPARE(data->coreIndex, metaObject->indexOfProperty("propertyC"));
- QVERIFY(data = cacheProperty(cache, "propertyD"));
+ QVERIFY((data = cacheProperty(cache, "propertyD")));
QCOMPARE(data->coreIndex, metaObject->indexOfProperty("propertyD"));
}
@@ -129,16 +134,16 @@ void tst_qqmlpropertycache::propertiesDerived()
QQmlRefPointer<QQmlPropertyCache> cache(parentCache->copyAndAppend(object.metaObject()));
QQmlPropertyData *data;
- QVERIFY(data = cacheProperty(cache, "propertyA"));
+ QVERIFY((data = cacheProperty(cache, "propertyA")));
QCOMPARE(data->coreIndex, metaObject->indexOfProperty("propertyA"));
- QVERIFY(data = cacheProperty(cache, "propertyB"));
+ QVERIFY((data = cacheProperty(cache, "propertyB")));
QCOMPARE(data->coreIndex, metaObject->indexOfProperty("propertyB"));
- QVERIFY(data = cacheProperty(cache, "propertyC"));
+ QVERIFY((data = cacheProperty(cache, "propertyC")));
QCOMPARE(data->coreIndex, metaObject->indexOfProperty("propertyC"));
- QVERIFY(data = cacheProperty(cache, "propertyD"));
+ QVERIFY((data = cacheProperty(cache, "propertyD")));
QCOMPARE(data->coreIndex, metaObject->indexOfProperty("propertyD"));
}
@@ -152,28 +157,28 @@ void tst_qqmlpropertycache::methods()
QQmlRefPointer<QQmlPropertyCache> cache(new QQmlPropertyCache(v4, metaObject));
QQmlPropertyData *data;
- QVERIFY(data = cacheProperty(cache, "slotA"));
+ QVERIFY((data = cacheProperty(cache, "slotA")));
QCOMPARE(data->coreIndex, metaObject->indexOfMethod("slotA()"));
- QVERIFY(data = cacheProperty(cache, "slotB"));
+ QVERIFY((data = cacheProperty(cache, "slotB")));
QCOMPARE(data->coreIndex, metaObject->indexOfMethod("slotB()"));
- QVERIFY(data = cacheProperty(cache, "signalA"));
+ QVERIFY((data = cacheProperty(cache, "signalA")));
QCOMPARE(data->coreIndex, metaObject->indexOfMethod("signalA()"));
- QVERIFY(data = cacheProperty(cache, "signalB"));
+ QVERIFY((data = cacheProperty(cache, "signalB")));
QCOMPARE(data->coreIndex, metaObject->indexOfMethod("signalB()"));
- QVERIFY(data = cacheProperty(cache, "propertyAChanged"));
+ QVERIFY((data = cacheProperty(cache, "propertyAChanged")));
QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyAChanged()"));
- QVERIFY(data = cacheProperty(cache, "propertyBChanged"));
+ QVERIFY((data = cacheProperty(cache, "propertyBChanged")));
QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyBChanged()"));
- QVERIFY(data = cacheProperty(cache, "propertyCChanged"));
+ QVERIFY((data = cacheProperty(cache, "propertyCChanged")));
QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyCChanged()"));
- QVERIFY(data = cacheProperty(cache, "propertyDChanged"));
+ QVERIFY((data = cacheProperty(cache, "propertyDChanged")));
QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyDChanged()"));
}
@@ -188,28 +193,28 @@ void tst_qqmlpropertycache::methodsDerived()
QQmlRefPointer<QQmlPropertyCache> cache(parentCache->copyAndAppend(object.metaObject()));
QQmlPropertyData *data;
- QVERIFY(data = cacheProperty(cache, "slotA"));
+ QVERIFY((data = cacheProperty(cache, "slotA")));
QCOMPARE(data->coreIndex, metaObject->indexOfMethod("slotA()"));
- QVERIFY(data = cacheProperty(cache, "slotB"));
+ QVERIFY((data = cacheProperty(cache, "slotB")));
QCOMPARE(data->coreIndex, metaObject->indexOfMethod("slotB()"));
- QVERIFY(data = cacheProperty(cache, "signalA"));
+ QVERIFY((data = cacheProperty(cache, "signalA")));
QCOMPARE(data->coreIndex, metaObject->indexOfMethod("signalA()"));
- QVERIFY(data = cacheProperty(cache, "signalB"));
+ QVERIFY((data = cacheProperty(cache, "signalB")));
QCOMPARE(data->coreIndex, metaObject->indexOfMethod("signalB()"));
- QVERIFY(data = cacheProperty(cache, "propertyAChanged"));
+ QVERIFY((data = cacheProperty(cache, "propertyAChanged")));
QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyAChanged()"));
- QVERIFY(data = cacheProperty(cache, "propertyBChanged"));
+ QVERIFY((data = cacheProperty(cache, "propertyBChanged")));
QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyBChanged()"));
- QVERIFY(data = cacheProperty(cache, "propertyCChanged"));
+ QVERIFY((data = cacheProperty(cache, "propertyCChanged")));
QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyCChanged()"));
- QVERIFY(data = cacheProperty(cache, "propertyDChanged"));
+ QVERIFY((data = cacheProperty(cache, "propertyDChanged")));
QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyDChanged()"));
}
@@ -223,22 +228,22 @@ void tst_qqmlpropertycache::signalHandlers()
QQmlRefPointer<QQmlPropertyCache> cache(new QQmlPropertyCache(v4, metaObject));
QQmlPropertyData *data;
- QVERIFY(data = cacheProperty(cache, "onSignalA"));
+ QVERIFY((data = cacheProperty(cache, "onSignalA")));
QCOMPARE(data->coreIndex, metaObject->indexOfMethod("signalA()"));
- QVERIFY(data = cacheProperty(cache, "onSignalB"));
+ QVERIFY((data = cacheProperty(cache, "onSignalB")));
QCOMPARE(data->coreIndex, metaObject->indexOfMethod("signalB()"));
- QVERIFY(data = cacheProperty(cache, "onPropertyAChanged"));
+ QVERIFY((data = cacheProperty(cache, "onPropertyAChanged")));
QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyAChanged()"));
- QVERIFY(data = cacheProperty(cache, "onPropertyBChanged"));
+ QVERIFY((data = cacheProperty(cache, "onPropertyBChanged")));
QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyBChanged()"));
- QVERIFY(data = cacheProperty(cache, "onPropertyCChanged"));
+ QVERIFY((data = cacheProperty(cache, "onPropertyCChanged")));
QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyCChanged()"));
- QVERIFY(data = cacheProperty(cache, "onPropertyDChanged"));
+ QVERIFY((data = cacheProperty(cache, "onPropertyDChanged")));
QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyDChanged()"));
}
@@ -253,25 +258,145 @@ void tst_qqmlpropertycache::signalHandlersDerived()
QQmlRefPointer<QQmlPropertyCache> cache(parentCache->copyAndAppend(object.metaObject()));
QQmlPropertyData *data;
- QVERIFY(data = cacheProperty(cache, "onSignalA"));
+ QVERIFY((data = cacheProperty(cache, "onSignalA")));
QCOMPARE(data->coreIndex, metaObject->indexOfMethod("signalA()"));
- QVERIFY(data = cacheProperty(cache, "onSignalB"));
+ QVERIFY((data = cacheProperty(cache, "onSignalB")));
QCOMPARE(data->coreIndex, metaObject->indexOfMethod("signalB()"));
- QVERIFY(data = cacheProperty(cache, "onPropertyAChanged"));
+ QVERIFY((data = cacheProperty(cache, "onPropertyAChanged")));
QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyAChanged()"));
- QVERIFY(data = cacheProperty(cache, "onPropertyBChanged"));
+ QVERIFY((data = cacheProperty(cache, "onPropertyBChanged")));
QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyBChanged()"));
- QVERIFY(data = cacheProperty(cache, "onPropertyCChanged"));
+ QVERIFY((data = cacheProperty(cache, "onPropertyCChanged")));
QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyCChanged()"));
- QVERIFY(data = cacheProperty(cache, "onPropertyDChanged"));
+ 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/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/tst_qqmlqt.cpp b/tests/auto/qml/qqmlqt/tst_qqmlqt.cpp
index 2f44c34bc2..8150241e4a 100644
--- a/tests/auto/qml/qqmlqt/tst_qqmlqt.cpp
+++ b/tests/auto/qml/qqmlqt/tst_qqmlqt.cpp
@@ -53,6 +53,7 @@ public:
tst_qqmlqt() {}
private slots:
+ void initTestCase();
void enums();
void rgba();
void hsla();
@@ -87,12 +88,69 @@ private slots:
void fontFamilies();
void quit();
void resolvedUrl();
+ void later_data();
+ void later();
void qtObjectContents();
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"));
@@ -935,6 +993,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
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 3e8e1d23ea..74f3929783 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
@@ -89,7 +88,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/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/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..470ce3bd91 100644
--- a/tests/auto/quick/geometry/tst_geometry.cpp
+++ b/tests/auto/quick/geometry/tst_geometry.cpp
@@ -126,9 +126,9 @@ void GeometryTest::testCustomGeometry()
};
static QSGGeometry::Attribute attributes[] = {
- { 0, 2, GL_FLOAT, 0, 0},
- { 1, 4, GL_UNSIGNED_BYTE, 0, 0},
- { 2, 4, GL_FLOAT, 0, 0},
+ QSGGeometry::Attribute::create(0, 2, QSGGeometry::TypeFloat, false),
+ QSGGeometry::Attribute::create(1, 4, QSGGeometry::TypeUnsignedByte, false),
+ QSGGeometry::Attribute::create(2, 4, QSGGeometry::TypeFloat, false)
};
static QSGGeometry::AttributeSet set = { 4, 6 * sizeof(float) + 4 * sizeof(unsigned char), attributes };
diff --git a/tests/auto/quick/nodes/tst_nodestest.cpp b/tests/auto/quick/nodes/tst_nodestest.cpp
index 16c1174604..63e0aeb324 100644
--- a/tests/auto/quick/nodes/tst_nodestest.cpp
+++ b/tests/auto/quick/nodes/tst_nodestest.cpp
@@ -31,7 +31,6 @@
#include <QtGui/QOffscreenSurface>
#include <QtGui/QOpenGLContext>
-
#include <QtQuick/qsgnode.h>
#include <QtQuick/private/qsgbatchrenderer_p.h>
#include <QtQuick/private/qsgnodeupdater_p.h>
@@ -76,7 +75,7 @@ private Q_SLOTS:
private:
QOffscreenSurface *surface;
QOpenGLContext *context;
- QSGRenderContext *renderContext;
+ QSGDefaultRenderContext *renderContext;
};
void NodesTest::initTestCase()
@@ -91,7 +90,8 @@ void NodesTest::initTestCase()
QVERIFY(context->create());
QVERIFY(context->makeCurrent(surface));
- renderContext = renderLoop->createRenderContext(renderLoop->sceneGraphContext());
+ auto rc = renderLoop->createRenderContext(renderLoop->sceneGraphContext());
+ renderContext = static_cast<QSGDefaultRenderContext *>(rc);
QVERIFY(renderContext);
renderContext->initialize(context);
QVERIFY(renderContext->isValid());
@@ -110,7 +110,7 @@ void NodesTest::cleanupTestCase()
class DummyRenderer : public QSGBatchRenderer::Renderer
{
public:
- DummyRenderer(QSGRootNode *root, QSGRenderContext *renderContext)
+ DummyRenderer(QSGRootNode *root, QSGDefaultRenderContext *renderContext)
: QSGBatchRenderer::Renderer(renderContext)
, changedNode(0)
, changedState(0)
diff --git a/tests/auto/quick/nokeywords/tst_nokeywords.cpp b/tests/auto/quick/nokeywords/tst_nokeywords.cpp
index ffe76cc210..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/tst_qquickflickable.cpp b/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp
index 96fe97f372..e1678b9acd 100644
--- a/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp
+++ b/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp
@@ -1138,7 +1138,9 @@ void tst_qquickflickable::flickOnRelease()
QTRY_VERIFY(!flickable->isMoving());
#ifdef Q_OS_MAC
+# ifndef QT_NO_OPENGL
QEXPECT_FAIL("", "QTBUG-26094 stopping on a full pixel doesn't work on OS X", Continue);
+# endif
#endif
// Stop on a full pixel after user interaction
QCOMPARE(flickable->contentY(), (qreal)qRound(flickable->contentY()));
diff --git a/tests/auto/quick/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/tst_qquickitem.cpp b/tests/auto/quick/qquickitem2/tst_qquickitem.cpp
index a087efd6b8..169ef1cbab 100644
--- a/tests/auto/quick/qquickitem2/tst_qquickitem.cpp
+++ b/tests/auto/quick/qquickitem2/tst_qquickitem.cpp
@@ -2698,113 +2698,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(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.setImplicitWidth(50);
- QCOMPARE(itemListener.itemImplicitWidthChanges, 1);
- QCOMPARE(itemListener.itemGeometryChanges, 1);
- QCOMPARE(itemListener.value, QVariant(QRectF(0,0,50,0)));
+ 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.setImplicitHeight(50);
- QCOMPARE(itemListener.itemImplicitHeightChanges, 1);
- QCOMPARE(itemListener.itemGeometryChanges, 2);
- QCOMPARE(itemListener.value, QVariant(QRectF(0,0,50,50)));
+ item->setWidth(item->width() + 30);
+ QCOMPARE(itemListener.count(QQuickItemPrivate::Geometry), 3);
+ QCOMPARE(itemListener.value(QQuickItemPrivate::Geometry), QVariant(QRectF(0,0,30,0)));
- item.setWidth(100);
- QCOMPARE(itemListener.itemGeometryChanges, 3);
- QCOMPARE(itemListener.value, QVariant(QRectF(0,0,100,50)));
+ item->setHeight(item->height() + 40);
+ QCOMPARE(itemListener.count(QQuickItemPrivate::Geometry), 4);
+ QCOMPARE(itemListener.value(QQuickItemPrivate::Geometry), QVariant(QRectF(0,0,0,40)));
- item.setHeight(100);
- QCOMPARE(itemListener.itemGeometryChanges, 4);
- QCOMPARE(itemListener.value, QVariant(QRectF(0,0,100,100)));
+ item->setOpacity(0.5);
+ QCOMPARE(itemListener.count(QQuickItemPrivate::Opacity), 1);
- item.setOpacity(0.5);
- QCOMPARE(itemListener.itemOpacityChanges, 1);
+ item->setRotation(90);
+ QCOMPARE(itemListener.count(QQuickItemPrivate::Rotation), 1);
- item.setRotation(90);
- QCOMPARE(itemListener.itemRotationChanges, 1);
+ item->setParentItem(window.contentItem());
+ QCOMPARE(itemListener.count(QQuickItemPrivate::Parent), 1);
- QQuickItem *parent = new QQuickItem;
+ 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);
@@ -2816,52 +2825,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
@@ -2870,9 +2906,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)
@@ -2880,7 +2943,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
@@ -2889,7 +2952,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
@@ -3215,7 +3278,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()));
@@ -3236,7 +3299,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/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 aa1474df91..9c627ad69c 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();
@@ -125,6 +127,9 @@ private slots:
void ignoreBySource();
private:
+ int startDragDistance() const {
+ return QGuiApplication::styleHints()->startDragDistance();
+ }
void acceptedButton_data();
void rejectedButton_data();
QTouchDevice *device;
@@ -320,7 +325,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);
@@ -331,18 +337,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);
}
@@ -531,15 +551,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);
@@ -574,7 +597,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);
@@ -586,19 +610,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);
}
@@ -791,14 +813,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);
@@ -807,7 +829,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);
@@ -819,23 +841,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);
@@ -993,48 +1013,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.
+ // Flickable swallows the first move, then moves 2*10 px
+ QTRY_COMPARE(flickable->contentX(), 20.);
+ QCOMPARE(flickable->contentY(), 20.);
- QCOMPARE(flickable->contentX(), 11.);
- QCOMPARE(flickable->contentY(), 11.);
-
- QTest::mouseRelease(&window, Qt::LeftButton, 0, QPoint(50, 50));
+ QTest::mouseRelease(&window, Qt::LeftButton, 0, p);
}
void tst_QQuickMouseArea::clickThrough()
@@ -1286,6 +1318,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;
@@ -1310,9 +1362,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());
@@ -1322,22 +1373,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);
@@ -1348,7 +1401,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);
@@ -1605,36 +1658,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);
@@ -1684,11 +1740,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()
@@ -1840,33 +1902,41 @@ 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));
+ QPoint 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 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);
// 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);
// 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
@@ -1881,15 +1951,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);
@@ -1898,13 +1972,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);
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..1d7273f6df 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);
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 223d8feb67..f400af1d10 100644
--- a/tests/auto/quick/qquicktext/BLACKLIST
+++ b/tests/auto/quick/qquicktext/BLACKLIST
@@ -4,3 +4,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 5ee811fd37..6f7d494255 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();
private:
QStringList standard;
@@ -4150,6 +4151,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
index 297146195b..492d81531a 100644
--- a/tests/auto/quick/qquicktextedit/BLACKLIST
+++ b/tests/auto/quick/qquicktextedit/BLACKLIST
@@ -1,6 +1,2 @@
[mouseSelection]
*
-[undo]
-*
-[undo_keypressevents]
-*
diff --git a/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp b/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp
index b7bfc357f3..3c899c1e34 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 18ccd81633..942eb67563 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();
@@ -780,6 +781,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..d9bdf47041 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;
@@ -273,25 +274,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();
@@ -372,14 +366,14 @@ private:
QTouchDevice *touchDevice;
QTouchDevice *touchDeviceWithVelocity;
};
-
+#ifndef QT_NO_OPENGL
Q_DECLARE_METATYPE(QOpenGLContext *);
-
+#endif
void tst_qquickwindow::cleanup()
{
QVERIFY(QGuiApplication::topLevelWindows().isEmpty());
}
-
+#ifndef QT_NO_OPENGL
void tst_qquickwindow::openglContextCreatedSignal()
{
qRegisterMetaType<QOpenGLContext *>();
@@ -391,12 +385,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 +435,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()));
@@ -1229,13 +1225,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 +1242,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 +1262,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 +1284,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 +1591,6 @@ void tst_qquickwindow::hideThenDelete()
QSignalSpy *openglDestroyed = 0;
QSignalSpy *sgInvalidated = 0;
- bool threaded = false;
{
QQuickWindow window;
@@ -1608,9 +1605,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 +1619,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 +1636,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 +2036,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 +2047,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 +2102,7 @@ public:
}
static int deleted;
};
-
+#ifndef QT_NO_OPENGL
class GlRenderJob : public QRunnable
{
public:
@@ -2113,7 +2124,7 @@ public:
QMutex *mutex;
QWaitCondition *condition;
};
-
+#endif
int RenderJob::deleted = 0;
void tst_qquickwindow::testRenderJob()
@@ -2162,25 +2173,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 +2213,6 @@ void tst_qquickwindow::testRenderJob()
class EventCounter : public QQuickRectangle
{
- Q_OBJECT
public:
EventCounter(QQuickItem *parent = 0)
: QQuickRectangle(parent)
diff --git a/tests/auto/quick/quick.pro b/tests/auto/quick/quick.pro
index 2e43702e7c..70e5b8ef6a 100644
--- a/tests/auto/quick/quick.pro
+++ b/tests/auto/quick/quick.pro
@@ -2,10 +2,20 @@ TEMPLATE = subdirs
PUBLICTESTS += \
geometry \
- rendernode \
qquickpixmapcache
-qtHaveModule(widgets): PUBLICTESTS += nodes
+contains(QT_CONFIG, opengl(es1|es2)?) {
+ PUBLICTESTS += \
+ rendernode
+ qtHaveModule(widgets): PUBLICTESTS += nodes
+
+ QUICKTESTS += \
+ qquickanimatedsprite \
+ qquickframebufferobject \
+ qquickopenglinfo \
+ qquickspritesequence \
+ qquickshadereffect
+}
!cross_compile: PRIVATETESTS += examples
@@ -39,7 +49,6 @@ QUICKTESTS = \
qquickaccessible \
qquickanchors \
qquickanimatedimage \
- qquickanimatedsprite \
qquickdynamicpropertyanimation \
qquickborderimage \
qquickwindow \
@@ -48,7 +57,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 \
diff --git a/tests/auto/quick/rendernode/tst_rendernode.cpp b/tests/auto/quick/rendernode/tst_rendernode.cpp
index 782ebf5aee..e15649e62c 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;
@@ -212,16 +215,18 @@ void tst_rendernode::renderOrder()
QSKIP("This test does not work at display depths < 24");
QImage fb = runTest("RenderOrder.qml");
- QCOMPARE(fb.width(), 200);
- QCOMPARE(fb.height(), 200);
+ QQuickView v;
+ int devicePixelRatio = static_cast<int>(v.devicePixelRatio());
+ QCOMPARE(fb.width(), 200 * devicePixelRatio);
+ QCOMPARE(fb.height(), 200 * devicePixelRatio);
- QCOMPARE(fb.pixel(50, 50), qRgb(0xff, 0xff, 0xff));
- QCOMPARE(fb.pixel(50, 150), qRgb(0xff, 0xff, 0xff));
- QCOMPARE(fb.pixel(150, 50), qRgb(0x00, 0x00, 0xff));
+ QCOMPARE(fb.pixel(50 * devicePixelRatio, 50 * devicePixelRatio), qRgb(0xff, 0xff, 0xff));
+ QCOMPARE(fb.pixel(50 * devicePixelRatio, 150 * devicePixelRatio), qRgb(0xff, 0xff, 0xff));
+ QCOMPARE(fb.pixel(150 * devicePixelRatio, 50 * devicePixelRatio), qRgb(0x00, 0x00, 0xff));
QByteArray errorMessage;
- QVERIFY2(fuzzyCompareColor(fb.pixel(150, 150), qRgb(0x7f, 0x7f, 0xff), &errorMessage),
- msgColorMismatchAt(errorMessage, 150, 150).constData());
+ QVERIFY2(fuzzyCompareColor(fb.pixel(150 * devicePixelRatio, 150 * devicePixelRatio), qRgb(0x7f, 0x7f, 0xff), &errorMessage),
+ msgColorMismatchAt(errorMessage, 150 * devicePixelRatio, 150 * devicePixelRatio).constData());
}
/* The test uses a number of nested rectangles with clipping
@@ -257,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 1cca56a876..791bcb215a 100644
--- a/tests/auto/quick/scenegraph/tst_scenegraph.cpp
+++ b/tests/auto/quick/scenegraph/tst_scenegraph.cpp
@@ -28,17 +28,25 @@
#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/visualtestutil.h"
+
+using namespace QQuickVisualTestUtil;
class PerPixelRect : public QQuickItem
{
@@ -96,9 +104,9 @@ private slots:
void render_data();
void render();
-
+#ifndef QT_NO_OPENGL
void hideWithOtherContext();
-
+#endif
void createTextureFromImage_data();
void createTextureFromImage();
@@ -118,6 +126,7 @@ void tst_SceneGraph::initTestCase()
QSGRenderLoop *loop = QSGRenderLoop::instance();
qDebug() << "RenderLoop: " << loop;
+#ifndef QT_NO_OPENGL
QOpenGLContext context;
context.setFormat(loop->sceneGraphContext()->defaultSurfaceFormat());
context.create();
@@ -150,6 +159,7 @@ void tst_SceneGraph::initTestCase()
qDebug() << "Broken Mipmap: " << m_brokenMipmapSupport;
context.doneCurrent();
+#endif
}
QQuickView *createView(const QString &file, QWindow *parent = 0, int x = -1, int y = -1, int w = -1, int h = -1)
@@ -175,12 +185,17 @@ QImage showAndGrab(const QString &file, int w, int h)
// Assumes the images are opaque white...
bool containsSomethingOtherThanWhite(const QImage &image)
{
- Q_ASSERT(image.format() == QImage::Format_ARGB32_Premultiplied
- || image.format() == QImage::Format_RGB32);
- int w = image.width();
- int h = image.height();
+ QImage img;
+ if (image.format() != QImage::Format_ARGB32_Premultiplied
+ || image.format() != QImage::Format_RGB32)
+ img = image.convertToFormat(QImage::Format_ARGB32_Premultiplied);
+ else
+ img = image;
+
+ int w = img.width();
+ int h = img.height();
for (int y=0; y<h; ++y) {
- const uint *pixels = (const uint *) image.constScanLine(y);
+ const uint *pixels = (const uint *) img.constScanLine(y);
for (int x=0; x<w; ++x)
if (pixels[x] != 0xffffffff)
return true;
@@ -188,44 +203,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");
@@ -251,24 +228,26 @@ void tst_SceneGraph::manyWindows_data()
QTest::newRow("rects,subwindow,sharing") << QStringLiteral("manyWindows_rects.qml") << false << true;
}
+#ifndef QT_NO_OPENGL
struct ShareContextResetter {
public:
~ShareContextResetter() { qt_gl_set_global_share_context(0); }
};
+#endif
void tst_SceneGraph::manyWindows()
{
QFETCH(QString, file);
QFETCH(bool, toplevel);
QFETCH(bool, shared);
-
+#ifndef QT_NO_OPENGL
QOpenGLContext sharedGLContext;
ShareContextResetter cleanup; // To avoid dangling pointer in case of test-failure.
if (shared) {
QVERIFY(sharedGLContext.create());
qt_gl_set_global_share_context(&sharedGLContext);
}
-
+#endif
QScopedPointer<QWindow> parent;
if (!toplevel) {
parent.reset(new QWindow());
@@ -457,6 +436,13 @@ void tst_SceneGraph::render_data()
void tst_SceneGraph::render()
{
+ QQuickView dummy;
+ dummy.show();
+ QTest::qWaitForWindowExposed(&dummy);
+ if (dummy.rendererInterface()->graphicsApi() != QSGRendererInterface::OpenGL)
+ QSKIP("Skipping complex rendering tests due to not running with OpenGL");
+ dummy.hide();
+
QFETCH(QString, file);
QFETCH(QList<Sample>, baseStage);
QFETCH(QList<Sample>, finalStage);
@@ -499,6 +485,7 @@ void tst_SceneGraph::render()
}
}
+#ifndef QT_NO_OPENGL
// Testcase for QTBUG-34898. We make another context current on another surface
// in the GUI thread and hide the QQuickWindow while the other context is
// current on the other window.
@@ -519,6 +506,9 @@ void tst_SceneGraph::hideWithOtherContext()
view.show();
QVERIFY(QTest::qWaitForWindowExposed(&view));
+ if (view.rendererInterface()->graphicsApi() != QSGRendererInterface::OpenGL)
+ QSKIP("Skipping OpenGL context test due to not running with OpenGL");
+
renderingOnMainThread = view.openglContext()->thread() == QGuiApplication::instance()->thread();
// Make the local context current on the local window...
@@ -531,6 +521,7 @@ void tst_SceneGraph::hideWithOtherContext()
// GL calls to a new frame (see QOpenGLContext docs).
QVERIFY(!renderingOnMainThread || QOpenGLContext::currentContext() != &context);
}
+#endif
void tst_SceneGraph::createTextureFromImage_data()
{
@@ -556,6 +547,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/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 15d7ffd9d9..dc70081f09 100644
--- a/tests/auto/quick/touchmouse/tst_touchmouse.cpp
+++ b/tests/auto/quick/touchmouse/tst_touchmouse.cpp
@@ -37,7 +37,6 @@
#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>
@@ -132,7 +131,7 @@ class tst_TouchMouse : public QQmlDataTest
Q_OBJECT
public:
tst_TouchMouse()
- :device(0)
+ :device(QTest::createTouchDevice())
{}
private slots:
@@ -191,11 +190,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()
@@ -256,8 +250,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);
@@ -580,7 +573,7 @@ void tst_TouchMouse::buttonOnFlickable()
QQuickWindowPrivate *windowPriv = QQuickWindowPrivate::get(window);
QCOMPARE(windowPriv->touchMouseId, 0);
QCOMPARE(windowPriv->itemForTouchPointId[0], eventItem1);
- QCOMPARE(windowPriv->mouseGrabberItem, eventItem1);
+ QCOMPARE(window->mouseGrabberItem(), eventItem1);
p1 += QPoint(0, -10);
QPoint p2 = p1 + QPoint(0, -10);
@@ -598,7 +591,7 @@ 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);
QVERIFY(flickable->isMovingVertically());
@@ -665,7 +658,7 @@ void tst_TouchMouse::buttonOnDelayedPressFlickable()
QQuickWindowPrivate *windowPriv = QQuickWindowPrivate::get(window);
QCOMPARE(windowPriv->touchMouseId, 0);
QCOMPARE(windowPriv->itemForTouchPointId[0], eventItem1);
- QCOMPARE(windowPriv->mouseGrabberItem, eventItem1);
+ QCOMPARE(window->mouseGrabberItem(), eventItem1);
p1 += QPoint(0, -10);
QPoint p2 = p1 + QPoint(0, -10);
@@ -681,7 +674,7 @@ void tst_TouchMouse::buttonOnDelayedPressFlickable()
// 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);
@@ -1098,8 +1091,8 @@ void tst_TouchMouse::mouseOnFlickableOnPinch()
QQuickTouchUtils::flush(window);
QQuickWindowPrivate *windowPriv = QQuickWindowPrivate::get(window);
- qDebug() << "Mouse Grabber: " << windowPriv->mouseGrabberItem << " itemForTouchPointId: " << windowPriv->itemForTouchPointId;
- QCOMPARE(windowPriv->mouseGrabberItem, flickable);
+ qDebug() << "Mouse Grabber: " << window->mouseGrabberItem() << " itemForTouchPointId: " << windowPriv->itemForTouchPointId;
+ QCOMPARE(window->mouseGrabberItem(), flickable);
// Add a second finger, this should lead to stealing
p1 = QPoint(40, 100);
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/benchmarks/benchmarks.pro b/tests/benchmarks/benchmarks.pro
index c7e7c6829a..bd071ecf5c 100644
--- a/tests/benchmarks/benchmarks.pro
+++ b/tests/benchmarks/benchmarks.pro
@@ -1,5 +1,5 @@
TEMPLATE = subdirs
SUBDIRS = qml script
contains(QT_CONFIG, private_tests) {
- SUBDIRS += particles
+ contains(QT_CONFIG, opengl(es1|es2)?):SUBDIRS += particles
}
diff --git a/tests/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 b47bc14a3e..ca5802a8e7 100644
--- a/tests/benchmarks/qml/creation/tst_creation.cpp
+++ b/tests/benchmarks/qml/creation/tst_creation.cpp
@@ -68,6 +68,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();
@@ -382,6 +388,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/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/qmljs/qmljs.cpp b/tools/qmljs/qmljs.cpp
index 68aa52ce91..4f79546bcc 100644
--- a/tools/qmljs/qmljs.cpp
+++ b/tools/qmljs/qmljs.cpp
@@ -68,7 +68,7 @@ struct Print: FunctionObject
};
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 +77,7 @@ struct Print: FunctionObject
std::cout << qPrintable(s);
}
std::cout << std::endl;
- return Encode::undefined();
+ scope.result = Encode::undefined();
}
};
@@ -94,10 +94,10 @@ struct GC: public FunctionObject
};
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();
}
};
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..063e5e2961 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();
}
@@ -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.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..1185a8e7ae 100644
--- a/tools/qmlscene/main.cpp
+++ b/tools/qmlscene/main.cpp
@@ -361,7 +361,7 @@ static void usage()
puts(" ");
exit(1);
}
-
+#ifndef QT_NO_OPENGL
// Listen on GL context creation of the QQuickWindow in order to print diagnostic output.
class DiagnosticGlContextCreationListener : public QObject {
Q_OBJECT
@@ -389,7 +389,9 @@ private slots:
context->doneCurrent();
deleteLater();
}
+
};
+#endif
static void setWindowTitle(bool verbose, const QObject *topLevel, QWindow *window)
{
@@ -403,8 +405,10 @@ static void setWindowTitle(bool verbose, const QObject *topLevel, QWindow *windo
if (verbose) {
newTitle += QLatin1String(" [Qt ") + QLatin1String(QT_VERSION_STR) + QLatin1Char(' ')
+ QGuiApplication::platformName() + QLatin1Char(' ');
+#ifndef QT_NO_OPENGL
newTitle += QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL
? QLatin1String("GL") : QLatin1String("GLES");
+#endif
newTitle += QLatin1Char(']');
}
if (oldTitle != newTitle)
@@ -592,8 +596,10 @@ int main(int argc, char ** argv)
if (window) {
setWindowTitle(options.verbose, topLevel, window.data());
+#ifndef QT_NO_OPENGL
if (options.verbose)
new DiagnosticGlContextCreationListener(window.data());
+#endif
QSurfaceFormat surfaceFormat = window->requestedFormat();
if (options.multisample)
surfaceFormat.setSamples(16);
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)