summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore4
-rw-r--r--dist/changes-5.14.220
-rw-r--r--examples/qt3d/basicshapes-cpp/main.cpp5
-rw-r--r--examples/qt3d/qt3d.pro52
-rw-r--r--examples/qt3d/simple-cpp/main.cpp1
-rw-r--r--src/animation/backend/animationutils.cpp8
-rw-r--r--src/animation/backend/animationutils_p.h12
-rw-r--r--src/animation/backend/blendedclipanimator.cpp4
-rw-r--r--src/animation/backend/blendedclipanimator_p.h2
-rw-r--r--src/animation/backend/clipanimator.cpp4
-rw-r--r--src/animation/backend/clipanimator_p.h2
-rw-r--r--src/animation/backend/evaluateblendclipanimatorjob.cpp8
-rw-r--r--src/animation/backend/evaluateclipanimatorjob.cpp6
-rw-r--r--src/animation/backend/handler.cpp28
-rw-r--r--src/animation/backend/handler_p.h1
-rw-r--r--src/animation/frontend/qchannelmapper.cpp3
-rw-r--r--src/animation/frontend/qclock.cpp5
-rw-r--r--src/core/geometry/qgeometry.cpp3
-rw-r--r--src/core/jobs/qaspectjob.cpp2
-rw-r--r--src/core/jobs/qaspectjob_p.h4
-rw-r--r--src/core/jobs/task.cpp4
-rw-r--r--src/core/jobs/task_p.h6
-rw-r--r--src/core/nodes/qentity.cpp17
-rw-r--r--src/core/transforms/matrix4x4_avx2_p.h4
-rw-r--r--src/extras/3dtext/qextrudedtextgeometry.cpp1
-rw-r--r--src/extras/defaults/qdiffusemapmaterial.cpp20
-rw-r--r--src/extras/defaults/qdiffusemapmaterial_p.h4
-rw-r--r--src/extras/defaults/qdiffusespecularmapmaterial.cpp20
-rw-r--r--src/extras/defaults/qdiffusespecularmapmaterial_p.h4
-rw-r--r--src/extras/defaults/qdiffusespecularmaterial.cpp24
-rw-r--r--src/extras/defaults/qdiffusespecularmaterial_p.h4
-rw-r--r--src/extras/defaults/qgoochmaterial.cpp14
-rw-r--r--src/extras/defaults/qgoochmaterial_p.h3
-rw-r--r--src/extras/defaults/qmetalroughmaterial.cpp57
-rw-r--r--src/extras/defaults/qmetalroughmaterial_p.h4
-rw-r--r--src/extras/defaults/qmorphphongmaterial.cpp21
-rw-r--r--src/extras/defaults/qmorphphongmaterial_p.h4
-rw-r--r--src/extras/defaults/qnormaldiffusemapalphamaterial.cpp19
-rw-r--r--src/extras/defaults/qnormaldiffusemapmaterial.cpp20
-rw-r--r--src/extras/defaults/qnormaldiffusemapmaterial_p.h4
-rw-r--r--src/extras/defaults/qnormaldiffusespecularmapmaterial.cpp20
-rw-r--r--src/extras/defaults/qnormaldiffusespecularmapmaterial_p.h4
-rw-r--r--src/extras/defaults/qpervertexcolormaterial.cpp13
-rw-r--r--src/extras/defaults/qpervertexcolormaterial_p.h3
-rw-r--r--src/extras/defaults/qphongalphamaterial.cpp24
-rw-r--r--src/extras/defaults/qphongalphamaterial_p.h4
-rw-r--r--src/extras/defaults/qphongmaterial.cpp20
-rw-r--r--src/extras/defaults/qphongmaterial_p.h4
-rw-r--r--src/extras/defaults/qt3dwindow.cpp97
-rw-r--r--src/extras/defaults/qt3dwindow.h6
-rw-r--r--src/extras/extras.qrc18
-rw-r--r--src/extras/shaders/es2/phong.inc.frag1008
-rw-r--r--src/extras/shaders/es3/light.inc.frag1
-rw-r--r--src/extras/shaders/es3/metalrough.inc.frag11
-rw-r--r--src/extras/shaders/gl3/light.inc.frag1
-rw-r--r--src/extras/shaders/gl3/metalrough.inc.frag11
-rw-r--r--src/extras/shaders/graphs/phong.graph466
-rw-r--r--src/extras/shaders/rhi/coordinatesystems.inc70
-rw-r--r--src/extras/shaders/rhi/default.vert107
-rw-r--r--src/extras/shaders/rhi/defaultuniforms.inc30
-rw-r--r--src/extras/shaders/rhi/distancefieldtext.frag41
-rw-r--r--src/extras/shaders/rhi/distancefieldtext.vert26
-rw-r--r--src/extras/shaders/rhi/gooch.frag111
-rw-r--r--src/extras/shaders/rhi/gooch.vert25
-rw-r--r--src/extras/shaders/rhi/light.inc.frag28
-rw-r--r--src/extras/shaders/rhi/metalrough.inc.frag341
-rw-r--r--src/extras/shaders/rhi/morphphong.vert43
-rw-r--r--src/extras/shaders/rhi/pervertexcolor.frag150
-rw-r--r--src/extras/shaders/rhi/pervertexcolor.vert28
-rw-r--r--src/extras/shaders/rhi/phong.inc.frag138
-rw-r--r--src/extras/shaders/rhi/skybox.frag44
-rw-r--r--src/extras/shaders/rhi/skybox.vert39
-rw-r--r--src/extras/shaders/rhi/unlittexture.frag13
-rw-r--r--src/extras/shaders/rhi/unlittexture.vert47
-rw-r--r--src/extras/text/qdistancefieldglyphcache.cpp1
-rw-r--r--src/input/backend/mouseeventfilter.cpp1
-rw-r--r--src/input/frontend/qmousehandler.cpp7
-rw-r--r--src/plugins/renderers/opengl/debug/imguirenderer.cpp10
-rw-r--r--src/plugins/renderers/opengl/graphicshelpers/submissioncontext.cpp16
-rw-r--r--src/plugins/renderers/opengl/graphicshelpers/submissioncontext_p.h2
-rw-r--r--src/plugins/renderers/opengl/jobs/materialparametergathererjob.cpp26
-rw-r--r--src/plugins/renderers/opengl/jobs/materialparametergathererjob_p.h3
-rw-r--r--src/plugins/renderers/opengl/jobs/renderviewcommandbuilderjob.cpp42
-rw-r--r--src/plugins/renderers/opengl/jobs/renderviewcommandbuilderjob_p.h4
-rw-r--r--src/plugins/renderers/opengl/jobs/renderviewcommandupdaterjob.cpp7
-rw-r--r--src/plugins/renderers/opengl/jobs/renderviewjobutils.cpp9
-rw-r--r--src/plugins/renderers/opengl/jobs/renderviewjobutils_p.h4
-rw-r--r--src/plugins/renderers/opengl/opengl.pro3
-rw-r--r--src/plugins/renderers/opengl/renderer/gllights.cpp345
-rw-r--r--src/plugins/renderers/opengl/renderer/gllights_p.h88
-rw-r--r--src/plugins/renderers/opengl/renderer/glshader.cpp122
-rw-r--r--src/plugins/renderers/opengl/renderer/glshader_p.h53
-rw-r--r--src/plugins/renderers/opengl/renderer/renderer.cpp36
-rw-r--r--src/plugins/renderers/opengl/renderer/renderer.pri2
-rw-r--r--src/plugins/renderers/opengl/renderer/renderer_p.h4
-rw-r--r--src/plugins/renderers/opengl/renderer/renderview.cpp275
-rw-r--r--src/plugins/renderers/opengl/renderer/renderview_p.h16
-rw-r--r--src/plugins/renderers/opengl/renderer/renderviewbuilder.cpp137
-rw-r--r--src/plugins/renderers/opengl/renderer/renderviewbuilder_p.h8
-rw-r--r--src/plugins/renderers/opengl/renderer/shaderparameterpack.cpp10
-rw-r--r--src/plugins/renderers/opengl/renderer/shaderparameterpack_p.h15
-rw-r--r--src/plugins/renderers/renderers.pro4
-rw-r--r--src/plugins/renderers/rhi/graphicshelpers/graphicshelpers.pri9
-rw-r--r--src/plugins/renderers/rhi/graphicshelpers/submissioncontext.cpp1871
-rw-r--r--src/plugins/renderers/rhi/graphicshelpers/submissioncontext_p.h266
-rw-r--r--src/plugins/renderers/rhi/io/io.pri8
-rw-r--r--src/plugins/renderers/rhi/io/rhibuffer.cpp199
-rw-r--r--src/plugins/renderers/rhi/io/rhibuffer_p.h116
-rw-r--r--src/plugins/renderers/rhi/jobs/filtercompatibletechniquejob.cpp97
-rw-r--r--src/plugins/renderers/rhi/jobs/filtercompatibletechniquejob_p.h96
-rw-r--r--src/plugins/renderers/rhi/jobs/jobs.pri17
-rw-r--r--src/plugins/renderers/rhi/jobs/materialparametergathererjob.cpp140
-rw-r--r--src/plugins/renderers/rhi/jobs/materialparametergathererjob_p.h124
-rw-r--r--src/plugins/renderers/rhi/jobs/renderviewcommandbuilderjob.cpp82
-rw-r--r--src/plugins/renderers/rhi/jobs/renderviewcommandbuilderjob_p.h (renamed from src/plugins/renderers/opengl/jobs/renderviewbuilderjob_p.h)14
-rw-r--r--src/plugins/renderers/rhi/jobs/renderviewcommandupdaterjob.cpp (renamed from src/plugins/renderers/opengl/jobs/renderviewbuilderjob.cpp)21
-rw-r--r--src/plugins/renderers/rhi/jobs/renderviewcommandupdaterjob_p.h109
-rw-r--r--src/plugins/renderers/rhi/jobs/renderviewinitializerjob.cpp104
-rw-r--r--src/plugins/renderers/rhi/jobs/renderviewinitializerjob_p.h108
-rw-r--r--src/plugins/renderers/rhi/jobs/renderviewjobutils.cpp583
-rw-r--r--src/plugins/renderers/rhi/jobs/renderviewjobutils_p.h189
-rw-r--r--src/plugins/renderers/rhi/main.cpp60
-rw-r--r--src/plugins/renderers/rhi/managers/managers.pri8
-rw-r--r--src/plugins/renderers/rhi/managers/rhihandle_types_p.h85
-rw-r--r--src/plugins/renderers/rhi/managers/rhiresourcemanagers.cpp87
-rw-r--r--src/plugins/renderers/rhi/managers/rhiresourcemanagers_p.h153
-rw-r--r--src/plugins/renderers/rhi/renderer/commandexecuter.cpp400
-rw-r--r--src/plugins/renderers/rhi/renderer/commandexecuter_p.h98
-rw-r--r--src/plugins/renderers/rhi/renderer/logging.cpp69
-rw-r--r--src/plugins/renderers/rhi/renderer/logging_p.h85
-rw-r--r--src/plugins/renderers/rhi/renderer/rendercommand.cpp112
-rw-r--r--src/plugins/renderers/rhi/renderer/rendercommand_p.h211
-rw-r--r--src/plugins/renderers/rhi/renderer/renderer.cpp2537
-rw-r--r--src/plugins/renderers/rhi/renderer/renderer.pri27
-rw-r--r--src/plugins/renderers/rhi/renderer/renderer_p.h443
-rw-r--r--src/plugins/renderers/rhi/renderer/renderercache_p.h101
-rw-r--r--src/plugins/renderers/rhi/renderer/renderqueue.cpp138
-rw-r--r--src/plugins/renderers/rhi/renderer/renderqueue_p.h106
-rw-r--r--src/plugins/renderers/rhi/renderer/renderview.cpp1253
-rw-r--r--src/plugins/renderers/rhi/renderer/renderview_p.h455
-rw-r--r--src/plugins/renderers/rhi/renderer/renderviewbuilder.cpp841
-rw-r--r--src/plugins/renderers/rhi/renderer/renderviewbuilder_p.h153
-rw-r--r--src/plugins/renderers/rhi/renderer/rhigraphicspipeline.cpp (renamed from src/quick3d/imports/scene3d/scene3dcleaner.cpp)52
-rw-r--r--src/plugins/renderers/rhi/renderer/rhigraphicspipeline_p.h127
-rw-r--r--src/plugins/renderers/rhi/renderer/rhishader.cpp681
-rw-r--r--src/plugins/renderers/rhi/renderer/rhishader_p.h198
-rw-r--r--src/plugins/renderers/rhi/renderer/shaderparameterpack.cpp114
-rw-r--r--src/plugins/renderers/rhi/renderer/shaderparameterpack_p.h215
-rw-r--r--src/plugins/renderers/rhi/renderer/shadervariables_p.h126
-rw-r--r--src/plugins/renderers/rhi/rhi.pri17
-rw-r--r--src/plugins/renderers/rhi/rhi.pro32
-rw-r--r--src/plugins/renderers/rhi/rhirenderer.json3
-rw-r--r--src/plugins/renderers/rhi/textures/renderbuffer.cpp111
-rw-r--r--src/plugins/renderers/rhi/textures/renderbuffer_p.h92
-rw-r--r--src/plugins/renderers/rhi/textures/texture.cpp918
-rw-r--r--src/plugins/renderers/rhi/textures/texture_p.h249
-rw-r--r--src/plugins/renderers/rhi/textures/textures.pri9
-rw-r--r--src/quick3d/imports/animation/plugins.qmltypes2
-rw-r--r--src/quick3d/imports/core/plugins.qmltypes2
-rw-r--r--src/quick3d/imports/extras/plugins.qmltypes14
-rw-r--r--src/quick3d/imports/input/plugins.qmltypes15
-rw-r--r--src/quick3d/imports/logic/plugins.qmltypes2
-rw-r--r--src/quick3d/imports/render/plugins.qmltypes96
-rw-r--r--src/quick3d/imports/scene2d/plugins.qmltypes2
-rw-r--r--src/quick3d/imports/scene3d/importsscene3d.pro2
-rw-r--r--src/quick3d/imports/scene3d/plugins.qmltypes2
-rw-r--r--src/quick3d/imports/scene3d/scene3ditem.cpp123
-rw-r--r--src/quick3d/imports/scene3d/scene3ditem_p.h10
-rw-r--r--src/quick3d/imports/scene3d/scene3drenderer.cpp44
-rw-r--r--src/quick3d/imports/scene3d/scene3drenderer_p.h15
-rw-r--r--src/quick3d/quick3dextras/qt3dquickwindow.cpp17
-rw-r--r--src/quick3d/quick3dextras/quick3dextras.pro2
-rw-r--r--src/render/backend/abstractrenderer_p.h11
-rw-r--r--src/render/backend/cameralens.cpp2
-rw-r--r--src/render/backend/resourceaccessor.cpp2
-rw-r--r--src/render/backend/visitorutils_p.h22
-rw-r--r--src/render/configure.json13
-rw-r--r--src/render/framegraph/qframegraphnode.cpp68
-rw-r--r--src/render/framegraph/qframegraphnode_p.h1
-rw-r--r--src/render/framegraph/qlayerfilter.cpp3
-rw-r--r--src/render/framegraph/qrendercapture.cpp33
-rw-r--r--src/render/framegraph/qrenderpassfilter.cpp6
-rw-r--r--src/render/framegraph/qrenderstateset.cpp3
-rw-r--r--src/render/framegraph/qtechniquefilter.cpp6
-rw-r--r--src/render/framegraph/rendercapture.cpp3
-rw-r--r--src/render/frontend/qrenderapi.h61
-rw-r--r--src/render/frontend/qrenderaspect.cpp103
-rw-r--r--src/render/frontend/qrendercapabilities.h7
-rw-r--r--src/render/frontend/qrendersettings.h2
-rw-r--r--src/render/frontend/qrendertarget.cpp3
-rw-r--r--src/render/frontend/render-frontend.pri1
-rw-r--r--src/render/jobs/pickboundingvolumejob.cpp6
-rw-r--r--src/render/jobs/raycastingjob.cpp6
-rw-r--r--src/render/jobs/updatelevelofdetailjob.cpp6
-rw-r--r--src/render/lights/qenvironmentlight.cpp5
-rw-r--r--src/render/materialsystem/material.cpp16
-rw-r--r--src/render/materialsystem/prototypes/default.json160
-rw-r--r--src/render/materialsystem/qeffect.cpp6
-rw-r--r--src/render/materialsystem/qgraphicsapifilter.cpp22
-rw-r--r--src/render/materialsystem/qgraphicsapifilter.h3
-rw-r--r--src/render/materialsystem/qgraphicsapifilter_p.h2
-rw-r--r--src/render/materialsystem/qmaterial.cpp3
-rw-r--r--src/render/materialsystem/qrenderpass.cpp9
-rw-r--r--src/render/materialsystem/qtechnique.cpp9
-rw-r--r--src/render/materialsystem/shader.cpp2
-rw-r--r--src/render/materialsystem/shader_p.h1
-rw-r--r--src/render/materialsystem/shaderbuilder.cpp21
-rw-r--r--src/render/materialsystem/shaderdata.cpp2
-rw-r--r--src/render/materialsystem/shaderdata_p.h2
-rw-r--r--src/render/picking/qabstractraycaster.cpp3
-rw-r--r--src/render/render.pro2
-rw-r--r--src/render/renderstates/qblendequationarguments.h4
-rw-r--r--src/render/renderstates/renderstateset.cpp2
-rw-r--r--src/render/renderstates/renderstateset_p.h5
-rw-r--r--src/render/shadergraph/qshaderformat.cpp146
-rw-r--r--src/render/shadergraph/qshaderformat_p.h126
-rw-r--r--src/render/shadergraph/qshadergenerator.cpp861
-rw-r--r--src/render/shadergraph/qshadergenerator_p.h (renamed from src/quick3d/imports/scene3d/scene3dcleaner_p.h)43
-rw-r--r--src/render/shadergraph/qshadergraph.cpp317
-rw-r--r--src/render/shadergraph/qshadergraph_p.h126
-rw-r--r--src/render/shadergraph/qshadergraphloader.cpp271
-rw-r--r--src/render/shadergraph/qshadergraphloader_p.h102
-rw-r--r--src/render/shadergraph/qshaderlanguage.cpp57
-rw-r--r--src/render/shadergraph/qshaderlanguage_p.h164
-rw-r--r--src/render/shadergraph/qshadernode.cpp174
-rw-r--r--src/render/shadergraph/qshadernode_p.h131
-rw-r--r--src/render/shadergraph/qshadernodeport.cpp58
-rw-r--r--src/render/shadergraph/qshadernodeport_p.h91
-rw-r--r--src/render/shadergraph/qshadernodesloader.cpp293
-rw-r--r--src/render/shadergraph/qshadernodesloader_p.h99
-rw-r--r--src/render/shadergraph/shadergraph.pri21
-rw-r--r--src/render/surfaces/surfaces.pri9
-rw-r--r--src/render/surfaces/vulkaninstance.cpp75
-rw-r--r--src/render/surfaces/vulkaninstance_p.h55
-rw-r--r--src/render/texture/qabstracttexture.cpp3
-rw-r--r--src/src.pro2
-rw-r--r--tests/auto/animation/animationutils/tst_animationutils.cpp108
-rw-r--r--tests/auto/auto.pro6
-rw-r--r--tests/auto/extras/extras.pro6
-rw-r--r--tests/auto/render/commons/testrenderer.h3
-rw-r--r--tests/auto/render/opengl/opengl.pro5
-rw-r--r--tests/auto/render/opengl/renderer/tst_renderer.cpp13
-rw-r--r--tests/auto/render/opengl/renderviewbuilder/tst_renderviewbuilder.cpp14
-rw-r--r--tests/auto/render/render.pro28
-rw-r--r--tests/auto/render/shadergraph/qshadergenerator/qshadergenerator.pro5
-rw-r--r--tests/auto/render/shadergraph/qshadergenerator/tst_qshadergenerator.cpp1428
-rw-r--r--tests/auto/render/shadergraph/qshadergraph/qshadergraph.pro5
-rw-r--r--tests/auto/render/shadergraph/qshadergraph/tst_qshadergraph.cpp821
-rw-r--r--tests/auto/render/shadergraph/qshadergraphloader/qshadergraphloader.pro5
-rw-r--r--tests/auto/render/shadergraph/qshadergraphloader/tst_qshadergraphloader.cpp627
-rw-r--r--tests/auto/render/shadergraph/qshadernodes/qshadernodes.pro5
-rw-r--r--tests/auto/render/shadergraph/qshadernodes/tst_qshadernodes.cpp549
-rw-r--r--tests/auto/render/shadergraph/qshadernodesloader/qshadernodesloader.pro5
-rw-r--r--tests/auto/render/shadergraph/qshadernodesloader/tst_qshadernodesloader.cpp325
-rw-r--r--tests/auto/render/shadergraph/shadergraph.pro10
-rw-r--r--tests/benchmarks/render/opengl/opengl.pro4
-rw-r--r--tests/benchmarks/render/opengl/opengl_render_plugin.pri18
-rw-r--r--tests/benchmarks/render/opengl/shaderparameterpack/shaderparameterpack.pro17
-rw-r--r--tests/benchmarks/render/opengl/shaderparameterpack/tst_bench_shaderparameterpack.cpp95
-rw-r--r--tests/benchmarks/render/render.pro3
-rw-r--r--tests/manual/bigscene-cpp/main.cpp46
-rw-r--r--tests/manual/manual.pro137
-rw-r--r--tests/manual/quickwidget-switch/main.cpp125
-rw-r--r--tests/manual/quickwidget-switch/main.qml152
-rw-r--r--tests/manual/quickwidget-switch/quickwidget-switch.pro13
-rw-r--r--tests/manual/quickwidget-switch/quickwidget-switch.qrc5
-rw-r--r--tests/manual/quickwindow-switch/main.cpp68
-rw-r--r--tests/manual/quickwindow-switch/main.qml181
-rw-r--r--tests/manual/quickwindow-switch/quickwindow-switch.pro13
-rw-r--r--tests/manual/quickwindow-switch/quickwindow-switch.qrc5
-rw-r--r--tests/manual/rendercapture-cpp/mycapture.h2
-rw-r--r--tests/manual/rhi/main.cpp445
-rw-r--r--tests/manual/rhi/qtlogo.pngbin0 -> 11386 bytes
-rw-r--r--tests/manual/rhi/rhi.pro10
-rw-r--r--tools/qgltf/qgltf.cpp2
275 files changed, 26945 insertions, 772 deletions
diff --git a/.gitignore b/.gitignore
index 2f8201bc2..e0398853e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -82,3 +82,7 @@ AssimpLog.txt
*_wrapper.bat
wrapper.sh
wrapper.bat
+
+# macOS files
+.DS_Store
+
diff --git a/dist/changes-5.14.2 b/dist/changes-5.14.2
new file mode 100644
index 000000000..68a005176
--- /dev/null
+++ b/dist/changes-5.14.2
@@ -0,0 +1,20 @@
+Qt 5.14.2 is a bug-fix release. It maintains both forward and backward
+compatibility (source and binary) with Qt 5.14.0 through 5.14.1.
+
+For more details, refer to the online documentation included in this
+distribution. The documentation is also available online:
+
+https://doc.qt.io/qt-5/index.html
+
+The Qt version 5.14 series is binary compatible with the 5.13.x series.
+Applications compiled for 5.13 will continue to run with 5.14.
+
+Some of the changes listed in this file include issue tracking numbers
+corresponding to tasks in the Qt Bug Tracker:
+
+https://bugreports.qt.io/
+
+Each of these identifiers can be entered in the bug tracker to obtain more
+information about a particular change.
+
+ - This release contains only minor code improvements.
diff --git a/examples/qt3d/basicshapes-cpp/main.cpp b/examples/qt3d/basicshapes-cpp/main.cpp
index fffb83a20..a347e9fb2 100644
--- a/examples/qt3d/basicshapes-cpp/main.cpp
+++ b/examples/qt3d/basicshapes-cpp/main.cpp
@@ -63,8 +63,6 @@
#include <QtWidgets/QCommandLinkButton>
#include <QtGui/QScreen>
-#include <Qt3DInput/QInputAspect>
-
#include <Qt3DExtras/qtorusmesh.h>
#include <Qt3DRender/qmesh.h>
#include <Qt3DRender/qtechnique.h>
@@ -103,9 +101,6 @@ int main(int argc, char **argv)
widget->setWindowTitle(QStringLiteral("Basic shapes"));
- Qt3DInput::QInputAspect *input = new Qt3DInput::QInputAspect;
- view->registerAspect(input);
-
// Root entity
Qt3DCore::QEntity *rootEntity = new Qt3DCore::QEntity();
diff --git a/examples/qt3d/qt3d.pro b/examples/qt3d/qt3d.pro
index 91163a0d1..52ae5389c 100644
--- a/examples/qt3d/qt3d.pro
+++ b/examples/qt3d/qt3d.pro
@@ -1,30 +1,8 @@
TEMPLATE = subdirs
SUBDIRS += \
- simple-qml \
simple-cpp \
- multiviewport \
- wireframe \
- shadow-map-qml \
- wave \
- scene3d \
- controls \
- anaglyph-rendering \
- planets-qml \
- instanced-arrays-qml \
- lights \
- compute-particles \
- 3d-text \
- qardboard \
- advancedcustommaterial \
- simplecustommaterial \
- scene2d \
- phong-cubes \
- pbr-materials \
- controlsunderlay \
- scene3dview
-
-qtHaveModule(multimedia): SUBDIRS += audio-visualizer-qml
+ 3d-text
# qmake seems to break in some CI configurations, disable this for now
#SUBDIRS += qgltf
@@ -37,5 +15,33 @@ qtHaveModule(widgets) {
qtHaveModule(quickwidgets): SUBDIRS += widgets-scene3d
}
+qtHaveModule(quick) {
+ qtHaveModule(multimedia) {
+ SUBDIRS += audio-visualizer-qml
+ }
+
+ SUBDIRS += \
+ simple-qml \
+ shadow-map-qml \
+ instanced-arrays-qml \
+ planets-qml \
+ advancedcustommaterial \
+ anaglyph-rendering \
+ compute-particles \
+ phong-cubes \
+ lights \
+ scene3dview \
+ controlsunderlay \
+ simplecustommaterial \
+ qardboard \
+ pbr-materials \
+ scene2d \
+ multiviewport \
+ wireframe \
+ wave \
+ scene3d \
+ controls
+}
+
EXAMPLE_FILES += \
exampleresources
diff --git a/examples/qt3d/simple-cpp/main.cpp b/examples/qt3d/simple-cpp/main.cpp
index 6b45dd1d0..80da0552a 100644
--- a/examples/qt3d/simple-cpp/main.cpp
+++ b/examples/qt3d/simple-cpp/main.cpp
@@ -101,6 +101,7 @@ Qt3DCore::QEntity *createScene()
Qt3DCore::QEntity *sphereEntity = new Qt3DCore::QEntity(rootEntity);
Qt3DExtras::QSphereMesh *sphereMesh = new Qt3DExtras::QSphereMesh;
sphereMesh->setRadius(3);
+ sphereMesh->setGenerateTangents(true);
Qt3DCore::QTransform *sphereTransform = new Qt3DCore::QTransform;
OrbitTransformController *controller = new OrbitTransformController(sphereTransform);
diff --git a/src/animation/backend/animationutils.cpp b/src/animation/backend/animationutils.cpp
index 3f386d92a..a5656e230 100644
--- a/src/animation/backend/animationutils.cpp
+++ b/src/animation/backend/animationutils.cpp
@@ -80,7 +80,8 @@ ClipEvaluationData evaluationDataForClip(AnimationClip *clip,
animatorData.playbackRate, clip->duration(),
animatorData.loopCount, result.currentLoop);
result.isFinalFrame = isFinalFrame(result.localTime, clip->duration(),
- result.currentLoop, animatorData.loopCount);
+ result.currentLoop, animatorData.loopCount,
+ animatorData.playbackRate);
const bool hasNormalizedTime = isValidNormalizedTime(animatorData.normalizedLocalTime);
result.normalizedLocalTime = hasNormalizedTime ? animatorData.normalizedLocalTime
: result.localTime / clip->duration();
@@ -112,9 +113,10 @@ double localTimeFromElapsedTime(double t_current_local,
t_local = std::fmod(t_local, duration);
// Ensure we clamp to end of final loop
- if (int(loopNumber) == loopCount) {
+
+ if (int(loopNumber) == loopCount || int(loopNumber) < 0) {
loopNumber = loopCount - 1;
- t_local = duration;
+ t_local = playbackRate >= 0.0 ? duration : 0.0;
}
}
diff --git a/src/animation/backend/animationutils_p.h b/src/animation/backend/animationutils_p.h
index 81a964046..0e74728c2 100644
--- a/src/animation/backend/animationutils_p.h
+++ b/src/animation/backend/animationutils_p.h
@@ -317,11 +317,15 @@ AnimatorEvaluationData evaluationDataForAnimator(Animator animator,
inline bool isFinalFrame(double localTime,
double duration,
int currentLoop,
- int loopCount)
+ int loopCount,
+ double playbackRate)
{
- return (localTime >= duration &&
- loopCount != 0 &&
- currentLoop >= loopCount - 1);
+ // We must be on the final loop and
+ // - if playing forward, localTime must be equal or above the duration
+ // - if playing backward, localTime must be equal or below 0
+ if (playbackRate >= 0.0)
+ return (loopCount != 0 && currentLoop >= loopCount - 1 && localTime >= duration);
+ return (loopCount != 0 && currentLoop <= 0 && localTime <= 0);
}
inline bool isValidNormalizedTime(float t)
diff --git a/src/animation/backend/blendedclipanimator.cpp b/src/animation/backend/blendedclipanimator.cpp
index 7d771745b..14771247a 100644
--- a/src/animation/backend/blendedclipanimator.cpp
+++ b/src/animation/backend/blendedclipanimator.cpp
@@ -127,10 +127,10 @@ Qt3DCore::QNodeId BlendedClipAnimator::blendTreeRootId() const
return m_blendTreeRootId;
}
-void BlendedClipAnimator::setNormalizedLocalTime(float normalizedTime)
+void BlendedClipAnimator::setNormalizedLocalTime(float normalizedTime, bool allowMarkDirty)
{
m_normalizedLocalTime = normalizedTime;
- if (isValidNormalizedTime(m_normalizedLocalTime))
+ if (isValidNormalizedTime(m_normalizedLocalTime) && allowMarkDirty)
setDirty(Handler::BlendedClipAnimatorDirty);
}
diff --git a/src/animation/backend/blendedclipanimator_p.h b/src/animation/backend/blendedclipanimator_p.h
index 04f609438..2396cdd74 100644
--- a/src/animation/backend/blendedclipanimator_p.h
+++ b/src/animation/backend/blendedclipanimator_p.h
@@ -70,7 +70,7 @@ public:
Qt3DCore::QNodeId mapperId() const { return m_mapperId; }
Qt3DCore::QNodeId clockId() const { return m_clockId; }
bool isRunning() const { return m_running; }
- void setNormalizedLocalTime(float normalizedTime);
+ void setNormalizedLocalTime(float normalizedTime, bool allowMarkDirty = true);
float normalizedLocalTime() const { return m_normalizedLocalTime; }
// Called by BuildBlendTreeJob
diff --git a/src/animation/backend/clipanimator.cpp b/src/animation/backend/clipanimator.cpp
index 32b02d2bb..5d46e321a 100644
--- a/src/animation/backend/clipanimator.cpp
+++ b/src/animation/backend/clipanimator.cpp
@@ -95,10 +95,10 @@ void ClipAnimator::setRunning(bool running)
setDirty(Handler::ClipAnimatorDirty);
}
-void ClipAnimator::setNormalizedLocalTime(float normalizedTime)
+void ClipAnimator::setNormalizedLocalTime(float normalizedTime, bool allowMarkDirty)
{
m_normalizedLocalTime = normalizedTime;
- if (isValidNormalizedTime(normalizedTime))
+ if (isValidNormalizedTime(normalizedTime) && allowMarkDirty)
setDirty(Handler::ClipAnimatorDirty);
}
diff --git a/src/animation/backend/clipanimator_p.h b/src/animation/backend/clipanimator_p.h
index d154bdab9..65fc6bdc8 100644
--- a/src/animation/backend/clipanimator_p.h
+++ b/src/animation/backend/clipanimator_p.h
@@ -77,7 +77,7 @@ public:
bool isRunning() const { return m_running; }
void setLoops(int loops) { m_loops = loops; }
int loops() const { return m_loops; }
- void setNormalizedLocalTime(float normalizedLocalTime);
+ void setNormalizedLocalTime(float normalizedLocalTime, bool allowMarkDirty = true);
float normalizedLocalTime() const { return m_normalizedLocalTime; }
void syncFromFrontEnd(const Qt3DCore::QNode *frontEnd, bool firstTime) override;
diff --git a/src/animation/backend/evaluateblendclipanimatorjob.cpp b/src/animation/backend/evaluateblendclipanimatorjob.cpp
index 765531902..69df26992 100644
--- a/src/animation/backend/evaluateblendclipanimatorjob.cpp
+++ b/src/animation/backend/evaluateblendclipanimatorjob.cpp
@@ -125,11 +125,10 @@ void EvaluateBlendClipAnimatorJob::run()
blendedClipAnimator->setLastGlobalTimeNS(globalTimeNS);
blendedClipAnimator->setLastLocalTime(localTime);
blendedClipAnimator->setLastNormalizedLocalTime(float(phase));
- blendedClipAnimator->setNormalizedLocalTime(-1.0f); // Re-set to something invalid.
blendedClipAnimator->setCurrentLoop(animatorData.currentLoop);
// Prepare the change record
- const bool finalFrame = isFinalFrame(localTime, duration, animatorData.currentLoop, animatorData.loopCount);
+ const bool finalFrame = isFinalFrame(localTime, duration, animatorData.currentLoop, animatorData.loopCount, animatorData.playbackRate);
const QVector<MappingData> mappingData = blendedClipAnimator->mappingData();
auto record = prepareAnimationRecord(blendedClipAnimator->peerId(),
mappingData,
@@ -140,6 +139,11 @@ void EvaluateBlendClipAnimatorJob::run()
// Trigger callbacks either on this thread or by notifying the gui thread.
auto callbacks = prepareCallbacks(mappingData, blendedResults);
+ // Update the normalized time on the backend node so that
+ // frontend <-> backend sync will not mark things dirty
+ // unless the frontend normalized time really is different
+ blendedClipAnimator->setNormalizedLocalTime(record.normalizedTime, false);
+
setPostFrameData(record, callbacks);
}
diff --git a/src/animation/backend/evaluateclipanimatorjob.cpp b/src/animation/backend/evaluateclipanimatorjob.cpp
index 914a38139..84d08543e 100644
--- a/src/animation/backend/evaluateclipanimatorjob.cpp
+++ b/src/animation/backend/evaluateclipanimatorjob.cpp
@@ -100,7 +100,6 @@ void EvaluateClipAnimatorJob::run()
clipAnimator->setLastGlobalTimeNS(globalTimeNS);
clipAnimator->setLastLocalTime(preEvaluationDataForClip.localTime);
clipAnimator->setLastNormalizedLocalTime(preEvaluationDataForClip.normalizedLocalTime);
- clipAnimator->setNormalizedLocalTime(-1.0f); // Re-set to something invalid.
// Prepare property changes (if finalFrame it also prepares the change for the running property for the frontend)
auto record = prepareAnimationRecord(clipAnimator->peerId(),
@@ -112,6 +111,11 @@ void EvaluateClipAnimatorJob::run()
// Trigger callbacks either on this thread or by notifying the gui thread.
auto callbacks = prepareCallbacks(clipAnimator->mappingData(), formattedClipResults);
+ // Update the normalized time on the backend node so that
+ // frontend <-> backend sync will not mark things dirty
+ // unless the frontend normalized time really is different
+ clipAnimator->setNormalizedLocalTime(record.normalizedTime, false);
+
setPostFrameData(record, callbacks);
}
diff --git a/src/animation/backend/handler.cpp b/src/animation/backend/handler.cpp
index 112e2742b..95363d56f 100644
--- a/src/animation/backend/handler.cpp
+++ b/src/animation/backend/handler.cpp
@@ -44,6 +44,7 @@
#include <Qt3DAnimation/private/animationlogging_p.h>
#include <Qt3DAnimation/private/buildblendtreesjob_p.h>
#include <Qt3DAnimation/private/evaluateblendclipanimatorjob_p.h>
+#include <Qt3DCore/private/qaspectjob_p.h>
QT_BEGIN_NAMESPACE
@@ -84,9 +85,6 @@ void Handler::setDirty(DirtyFlag flag, Qt3DCore::QNodeId nodeId)
}
case ChannelMappingsDirty: {
- QMutexLocker lock(&m_mutex);
- const auto handle = m_channelMapperManager->lookupHandle(nodeId);
- m_dirtyChannelMappers.push_back(handle);
break;
}
@@ -118,11 +116,7 @@ void Handler::setClipAnimatorRunning(const HClipAnimator &handle, bool running)
// If being marked as not running, remove from set of running clips
if (!running) {
- const auto it = std::find_if(m_runningClipAnimators.begin(),
- m_runningClipAnimators.end(),
- [handle](const HClipAnimator &h) { return h == handle; });
- if (it != m_runningClipAnimators.end())
- m_runningClipAnimators.erase(it);
+ m_runningClipAnimators.removeAll(handle);
}
}
@@ -208,13 +202,14 @@ QVector<Qt3DCore::QAspectJobPtr> Handler::jobsToExecute(qint64 time)
const bool hasFindRunningClipAnimatorsJob = !m_dirtyClipAnimators.isEmpty();
if (hasFindRunningClipAnimatorsJob) {
qCDebug(HandlerLogic) << "Added FindRunningClipAnimatorsJob";
- m_findRunningClipAnimatorsJob->removeDependency(QWeakPointer<Qt3DCore::QAspectJob>());
cleanupHandleList(&m_dirtyClipAnimators);
m_findRunningClipAnimatorsJob->setDirtyClipAnimators(m_dirtyClipAnimators);
+ // Only set the dependency once
+ if (Q_UNLIKELY(m_findRunningClipAnimatorsJob->dependencies().empty()))
+ m_findRunningClipAnimatorsJob->addDependency(m_loadAnimationClipJob);
jobs.push_back(m_findRunningClipAnimatorsJob);
if (hasLoadAnimationClipJob)
- m_findRunningClipAnimatorsJob->addDependency(m_loadAnimationClipJob);
- m_dirtyClipAnimators.clear();
+ m_dirtyClipAnimators.clear();
}
// Rebuild blending trees if a blend tree is dirty
@@ -247,13 +242,10 @@ QVector<Qt3DCore::QAspectJobPtr> Handler::jobsToExecute(qint64 time)
// Set each job up with an animator to process and set dependencies
for (int i = 0; i < newSize; ++i) {
m_evaluateClipAnimatorJobs[i]->setClipAnimator(m_runningClipAnimators[i]);
- m_evaluateClipAnimatorJobs[i]->removeDependency(QWeakPointer<Qt3DCore::QAspectJob>());
- if (hasLoadAnimationClipJob &&
- !m_evaluateClipAnimatorJobs[i]->dependencies().contains(m_loadAnimationClipJob))
+ Qt3DCore::QAspectJobPrivate::get(m_evaluateClipAnimatorJobs[i].data())->clearDependencies();
+ if (hasLoadAnimationClipJob)
m_evaluateClipAnimatorJobs[i]->addDependency(m_loadAnimationClipJob);
-
- if (hasFindRunningClipAnimatorsJob &&
- !m_evaluateClipAnimatorJobs[i]->dependencies().contains(m_findRunningClipAnimatorsJob))
+ if (hasFindRunningClipAnimatorsJob)
m_evaluateClipAnimatorJobs[i]->addDependency(m_findRunningClipAnimatorsJob);
jobs.push_back(m_evaluateClipAnimatorJobs[i]);
}
@@ -276,7 +268,7 @@ QVector<Qt3DCore::QAspectJobPtr> Handler::jobsToExecute(qint64 time)
// Set each job up with an animator to process and set dependencies
for (int i = 0; i < newSize; ++i) {
m_evaluateBlendClipAnimatorJobs[i]->setBlendClipAnimator(m_runningBlendedClipAnimators[i]);
- m_evaluateBlendClipAnimatorJobs[i]->removeDependency(QWeakPointer<Qt3DCore::QAspectJob>());
+ Qt3DCore::QAspectJobPrivate::get(m_evaluateBlendClipAnimatorJobs[i].data())->clearDependencies();
if (hasLoadAnimationClipJob)
m_evaluateBlendClipAnimatorJobs[i]->addDependency(m_loadAnimationClipJob);
if (hasBuildBlendTreesJob)
diff --git a/src/animation/backend/handler_p.h b/src/animation/backend/handler_p.h
index 99e2ae1f6..e65bc0797 100644
--- a/src/animation/backend/handler_p.h
+++ b/src/animation/backend/handler_p.h
@@ -137,7 +137,6 @@ private:
QScopedPointer<SkeletonManager> m_skeletonManager;
QVector<HAnimationClip> m_dirtyAnimationClips;
- QVector<HChannelMapper> m_dirtyChannelMappers;
QVector<HClipAnimator> m_dirtyClipAnimators;
QVector<HBlendedClipAnimator> m_dirtyBlendedAnimators;
diff --git a/src/animation/frontend/qchannelmapper.cpp b/src/animation/frontend/qchannelmapper.cpp
index 2d0e0dd2d..711259e7f 100644
--- a/src/animation/frontend/qchannelmapper.cpp
+++ b/src/animation/frontend/qchannelmapper.cpp
@@ -93,7 +93,8 @@ void QChannelMapper::removeMapping(QAbstractChannelMapping *mapping)
{
Q_ASSERT(mapping);
Q_D(QChannelMapper);
- d->m_mappings.removeOne(mapping);
+ if (!d->m_mappings.removeOne(mapping))
+ return;
d->update();
// Remove bookkeeping connection
d->unregisterDestructionHelper(mapping);
diff --git a/src/animation/frontend/qclock.cpp b/src/animation/frontend/qclock.cpp
index 8d1cb5991..5fa727474 100644
--- a/src/animation/frontend/qclock.cpp
+++ b/src/animation/frontend/qclock.cpp
@@ -63,7 +63,10 @@ QClock::~QClock()
/*!
\property Qt3DAnimation::QClock::playbackRate
- The playback speed of the animation.
+ The playback speed of the animation. The playback speed can be negative.
+ When that is the case the animation will be played back from the current
+ normalized time value back to 0 and for the number of loops it had been
+ played for with a positive playback rate.
*/
double QClock::playbackRate() const
diff --git a/src/core/geometry/qgeometry.cpp b/src/core/geometry/qgeometry.cpp
index 16e204bbc..4e8dcacac 100644
--- a/src/core/geometry/qgeometry.cpp
+++ b/src/core/geometry/qgeometry.cpp
@@ -220,7 +220,8 @@ void QGeometry::removeAttribute(QAttribute *attribute)
{
Q_ASSERT(attribute);
Q_D(QGeometry);
- d->m_attributes.removeOne(attribute);
+ if (!d->m_attributes.removeOne(attribute))
+ return;
// Remove bookkeeping connection
d->unregisterDestructionHelper(attribute);
d->update();
diff --git a/src/core/jobs/qaspectjob.cpp b/src/core/jobs/qaspectjob.cpp
index 557756581..7c23f6894 100644
--- a/src/core/jobs/qaspectjob.cpp
+++ b/src/core/jobs/qaspectjob.cpp
@@ -70,7 +70,7 @@ QAspectJobPrivate *QAspectJobPrivate::get(QAspectJob *job)
return job->d_func();
}
-bool QAspectJobPrivate::isRequired()
+bool QAspectJobPrivate::isRequired() const
{
return true;
}
diff --git a/src/core/jobs/qaspectjob_p.h b/src/core/jobs/qaspectjob_p.h
index 63a2cc572..0c7802b02 100644
--- a/src/core/jobs/qaspectjob_p.h
+++ b/src/core/jobs/qaspectjob_p.h
@@ -72,9 +72,11 @@ public:
static QAspectJobPrivate *get(QAspectJob *job);
- virtual bool isRequired();
+ virtual bool isRequired() const;
virtual void postFrame(QAspectManager *aspectManager);
+ void clearDependencies() { m_dependencies.clear(); }
+
QVector<QWeakPointer<QAspectJob> > m_dependencies;
JobId m_jobId;
QString m_jobName;
diff --git a/src/core/jobs/task.cpp b/src/core/jobs/task.cpp
index f5bfae014..47de41989 100644
--- a/src/core/jobs/task.cpp
+++ b/src/core/jobs/task.cpp
@@ -68,7 +68,7 @@ AspectTaskRunnable::~AspectTaskRunnable()
{
}
-bool AspectTaskRunnable::isRequired()
+bool AspectTaskRunnable::isRequired() const
{
return m_job ? QAspectJobPrivate::get(m_job.data())->isRequired() : false;
}
@@ -105,7 +105,7 @@ SyncTaskRunnable::~SyncTaskRunnable()
{
}
-bool SyncTaskRunnable::isRequired()
+bool SyncTaskRunnable::isRequired() const
{
return true;
}
diff --git a/src/core/jobs/task_p.h b/src/core/jobs/task_p.h
index 90d0674b4..73c34534d 100644
--- a/src/core/jobs/task_p.h
+++ b/src/core/jobs/task_p.h
@@ -77,7 +77,7 @@ public:
virtual ~RunnableInterface();
- virtual bool isRequired() = 0;
+ virtual bool isRequired() const = 0;
virtual void run() = 0;
virtual int id() = 0;
@@ -97,7 +97,7 @@ public:
AspectTaskRunnable(QSystemInformationService *service);
~AspectTaskRunnable();
- bool isRequired() override;
+ bool isRequired() const override;
void run() override;
void setPooler(QThreadPooler *pooler) override { m_pooler = pooler; }
@@ -129,7 +129,7 @@ public:
QAtomicInt *atomicCount);
~SyncTaskRunnable();
- bool isRequired() override;
+ bool isRequired() const override;
void run() override;
void setPooler(QThreadPooler *pooler) override { m_pooler = pooler; }
diff --git a/src/core/nodes/qentity.cpp b/src/core/nodes/qentity.cpp
index 0a504b4eb..551dc685b 100644
--- a/src/core/nodes/qentity.cpp
+++ b/src/core/nodes/qentity.cpp
@@ -78,18 +78,19 @@ QString dumpNode(const Qt3DCore::QEntity *n) {
return res;
}
-QStringList dumpSG(const Qt3DCore::QEntity *n, int level = 0)
+QStringList dumpSG(const Qt3DCore::QNode *n, int level = 0)
{
QStringList reply;
- QString res = dumpNode(n);
- reply += res.rightJustified(res.length() + level * 2, ' ');
+ const auto *entity = qobject_cast<const Qt3DCore::QEntity *>(n);
+ if (entity != nullptr) {
+ QString res = dumpNode(entity);
+ reply += res.rightJustified(res.length() + level * 2, ' ');
+ level++;
+ }
const auto children = n->childNodes();
- for (auto *child: children) {
- auto *childFGNode = qobject_cast<Qt3DCore::QEntity *>(child);
- if (childFGNode != nullptr)
- reply += dumpSG(childFGNode, level + 1);
- }
+ for (auto *child: children)
+ reply += dumpSG(child, level);
return reply;
}
diff --git a/src/core/transforms/matrix4x4_avx2_p.h b/src/core/transforms/matrix4x4_avx2_p.h
index 0b35f0016..de40ee2a0 100644
--- a/src/core/transforms/matrix4x4_avx2_p.h
+++ b/src/core/transforms/matrix4x4_avx2_p.h
@@ -485,8 +485,8 @@ public:
friend Vector4D operator*(const Vector4D &vector, const Matrix4x4_AVX2 &matrix);
friend Vector4D operator*(const Matrix4x4_AVX2 &matrix, const Vector4D &vector);
- friend Q_3DCORE_PRIVATE_EXPORT Vector3D operator*(const Vector3D &vector, const Matrix4x4_AVX2 &matrix);
- friend Q_3DCORE_PRIVATE_EXPORT Vector3D operator*(const Matrix4x4_AVX2 &matrix, const Vector3D &vector);
+ friend Vector3D operator*(const Vector3D &vector, const Matrix4x4_AVX2 &matrix);
+ friend Vector3D operator*(const Matrix4x4_AVX2 &matrix, const Vector3D &vector);
friend Q_3DCORE_PRIVATE_EXPORT QDebug operator<<(QDebug dbg, const Matrix4x4_AVX2 &m);
diff --git a/src/extras/3dtext/qextrudedtextgeometry.cpp b/src/extras/3dtext/qextrudedtextgeometry.cpp
index 67f3b21ce..3c884f152 100644
--- a/src/extras/3dtext/qextrudedtextgeometry.cpp
+++ b/src/extras/3dtext/qextrudedtextgeometry.cpp
@@ -57,6 +57,7 @@
#include <QVector3D>
#include <QTextLayout>
#include <QTime>
+#include <QPainterPath>
QT_BEGIN_NAMESPACE
diff --git a/src/extras/defaults/qdiffusemapmaterial.cpp b/src/extras/defaults/qdiffusemapmaterial.cpp
index 83eec26a8..95ab7e9ac 100644
--- a/src/extras/defaults/qdiffusemapmaterial.cpp
+++ b/src/extras/defaults/qdiffusemapmaterial.cpp
@@ -72,13 +72,17 @@ QDiffuseMapMaterialPrivate::QDiffuseMapMaterialPrivate()
, m_diffuseMapGL3Technique(new QTechnique())
, m_diffuseMapGL2Technique(new QTechnique())
, m_diffuseMapES2Technique(new QTechnique())
+ , m_diffuseMapRHITechnique(new QTechnique())
, m_diffuseMapGL3RenderPass(new QRenderPass())
, m_diffuseMapGL2RenderPass(new QRenderPass())
, m_diffuseMapES2RenderPass(new QRenderPass())
+ , m_diffuseMapRHIRenderPass(new QRenderPass())
, m_diffuseMapGL3Shader(new QShaderProgram())
, m_diffuseMapGL3ShaderBuilder(new QShaderProgramBuilder())
, m_diffuseMapGL2ES2Shader(new QShaderProgram())
, m_diffuseMapGL2ES2ShaderBuilder(new QShaderProgramBuilder())
+ , m_diffuseMapRHIShader(new QShaderProgram())
+ , m_diffuseMapRHIShaderBuilder(new QShaderProgramBuilder())
, m_filterKey(new QFilterKey)
{
m_diffuseTexture->setMagnificationFilter(QAbstractTexture::Linear);
@@ -119,6 +123,14 @@ void QDiffuseMapMaterialPrivate::init()
QStringLiteral("specular"),
QStringLiteral("normal")});
+ m_diffuseMapRHIShader->setVertexShaderCode(QShaderProgram::loadSource(QUrl(QStringLiteral("qrc:/shaders/rhi/default.vert"))));
+ m_diffuseMapRHIShaderBuilder->setParent(q);
+ m_diffuseMapRHIShaderBuilder->setShaderProgram(m_diffuseMapRHIShader);
+ m_diffuseMapRHIShaderBuilder->setFragmentShaderGraph(QUrl(QStringLiteral("qrc:/shaders/graphs/phong.frag.json")));
+ m_diffuseMapRHIShaderBuilder->setEnabledLayers({QStringLiteral("diffuseTexture"),
+ QStringLiteral("specular"),
+ QStringLiteral("normal")});
+
m_diffuseMapGL3Technique->graphicsApiFilter()->setApi(QGraphicsApiFilter::OpenGL);
m_diffuseMapGL3Technique->graphicsApiFilter()->setMajorVersion(3);
m_diffuseMapGL3Technique->graphicsApiFilter()->setMinorVersion(1);
@@ -134,6 +146,10 @@ void QDiffuseMapMaterialPrivate::init()
m_diffuseMapES2Technique->graphicsApiFilter()->setMinorVersion(0);
m_diffuseMapES2Technique->graphicsApiFilter()->setProfile(QGraphicsApiFilter::NoProfile);
+ m_diffuseMapRHITechnique->graphicsApiFilter()->setApi(QGraphicsApiFilter::RHI);
+ m_diffuseMapRHITechnique->graphicsApiFilter()->setMajorVersion(1);
+ m_diffuseMapRHITechnique->graphicsApiFilter()->setMinorVersion(0);
+
m_filterKey->setParent(q);
m_filterKey->setName(QStringLiteral("renderingStyle"));
m_filterKey->setValue(QStringLiteral("forward"));
@@ -141,18 +157,22 @@ void QDiffuseMapMaterialPrivate::init()
m_diffuseMapGL3Technique->addFilterKey(m_filterKey);
m_diffuseMapGL2Technique->addFilterKey(m_filterKey);
m_diffuseMapES2Technique->addFilterKey(m_filterKey);
+ m_diffuseMapRHITechnique->addFilterKey(m_filterKey);
m_diffuseMapGL3RenderPass->setShaderProgram(m_diffuseMapGL3Shader);
m_diffuseMapGL2RenderPass->setShaderProgram(m_diffuseMapGL2ES2Shader);
m_diffuseMapES2RenderPass->setShaderProgram(m_diffuseMapGL2ES2Shader);
+ m_diffuseMapRHIRenderPass->setShaderProgram(m_diffuseMapRHIShader);
m_diffuseMapGL3Technique->addRenderPass(m_diffuseMapGL3RenderPass);
m_diffuseMapGL2Technique->addRenderPass(m_diffuseMapGL2RenderPass);
m_diffuseMapES2Technique->addRenderPass(m_diffuseMapES2RenderPass);
+ m_diffuseMapRHITechnique->addRenderPass(m_diffuseMapRHIRenderPass);
m_diffuseMapEffect->addTechnique(m_diffuseMapGL3Technique);
m_diffuseMapEffect->addTechnique(m_diffuseMapGL2Technique);
m_diffuseMapEffect->addTechnique(m_diffuseMapES2Technique);
+ m_diffuseMapEffect->addTechnique(m_diffuseMapRHITechnique);
m_diffuseMapEffect->addParameter(m_ambientParameter);
m_diffuseMapEffect->addParameter(m_diffuseParameter);
diff --git a/src/extras/defaults/qdiffusemapmaterial_p.h b/src/extras/defaults/qdiffusemapmaterial_p.h
index cef75a3f0..d83e8bff0 100644
--- a/src/extras/defaults/qdiffusemapmaterial_p.h
+++ b/src/extras/defaults/qdiffusemapmaterial_p.h
@@ -94,13 +94,17 @@ class QDiffuseMapMaterialPrivate : public Qt3DRender::QMaterialPrivate
Qt3DRender::QTechnique *m_diffuseMapGL3Technique;
Qt3DRender::QTechnique *m_diffuseMapGL2Technique;
Qt3DRender::QTechnique *m_diffuseMapES2Technique;
+ Qt3DRender::QTechnique *m_diffuseMapRHITechnique;
Qt3DRender::QRenderPass *m_diffuseMapGL3RenderPass;
Qt3DRender::QRenderPass *m_diffuseMapGL2RenderPass;
Qt3DRender::QRenderPass *m_diffuseMapES2RenderPass;
+ Qt3DRender::QRenderPass *m_diffuseMapRHIRenderPass;
Qt3DRender::QShaderProgram *m_diffuseMapGL3Shader;
Qt3DRender::QShaderProgramBuilder *m_diffuseMapGL3ShaderBuilder;
Qt3DRender::QShaderProgram *m_diffuseMapGL2ES2Shader;
Qt3DRender::QShaderProgramBuilder *m_diffuseMapGL2ES2ShaderBuilder;
+ Qt3DRender::QShaderProgram *m_diffuseMapRHIShader;
+ Qt3DRender::QShaderProgramBuilder *m_diffuseMapRHIShaderBuilder;
Qt3DRender::QFilterKey *m_filterKey;
Q_DECLARE_PUBLIC(QDiffuseMapMaterial)
diff --git a/src/extras/defaults/qdiffusespecularmapmaterial.cpp b/src/extras/defaults/qdiffusespecularmapmaterial.cpp
index 9a1092fbc..15dcb36bf 100644
--- a/src/extras/defaults/qdiffusespecularmapmaterial.cpp
+++ b/src/extras/defaults/qdiffusespecularmapmaterial.cpp
@@ -73,13 +73,17 @@ QDiffuseSpecularMapMaterialPrivate::QDiffuseSpecularMapMaterialPrivate()
, m_diffuseSpecularMapGL3Technique(new QTechnique())
, m_diffuseSpecularMapGL2Technique(new QTechnique())
, m_diffuseSpecularMapES2Technique(new QTechnique())
+ , m_diffuseSpecularMapRHITechnique(new QTechnique())
, m_diffuseSpecularMapGL3RenderPass(new QRenderPass())
, m_diffuseSpecularMapGL2RenderPass(new QRenderPass())
, m_diffuseSpecularMapES2RenderPass(new QRenderPass())
+ , m_diffuseSpecularMapRHIRenderPass(new QRenderPass())
, m_diffuseSpecularMapGL3Shader(new QShaderProgram())
, m_diffuseSpecularMapGL3ShaderBuilder(new QShaderProgramBuilder())
, m_diffuseSpecularMapGL2ES2Shader(new QShaderProgram())
, m_diffuseSpecularMapGL2ES2ShaderBuilder(new QShaderProgramBuilder())
+ , m_diffuseSpecularMapRHIShader(new QShaderProgram())
+ , m_diffuseSpecularMapRHIShaderBuilder(new QShaderProgramBuilder())
, m_filterKey(new QFilterKey)
{
m_diffuseTexture->setMagnificationFilter(QAbstractTexture::Linear);
@@ -126,6 +130,14 @@ void QDiffuseSpecularMapMaterialPrivate::init()
QStringLiteral("specularTexture"),
QStringLiteral("normal")});
+ m_diffuseSpecularMapRHIShader->setVertexShaderCode(QShaderProgram::loadSource(QUrl(QStringLiteral("qrc:/shaders/rhi/default.vert"))));
+ m_diffuseSpecularMapRHIShaderBuilder->setParent(q);
+ m_diffuseSpecularMapRHIShaderBuilder->setShaderProgram(m_diffuseSpecularMapRHIShader);
+ m_diffuseSpecularMapRHIShaderBuilder->setFragmentShaderGraph(QUrl(QStringLiteral("qrc:/shaders/graphs/phong.frag.json")));
+ m_diffuseSpecularMapRHIShaderBuilder->setEnabledLayers({QStringLiteral("diffuseTexture"),
+ QStringLiteral("specularTexture"),
+ QStringLiteral("normal")});
+
m_diffuseSpecularMapGL3Technique->graphicsApiFilter()->setApi(QGraphicsApiFilter::OpenGL);
m_diffuseSpecularMapGL3Technique->graphicsApiFilter()->setMajorVersion(3);
m_diffuseSpecularMapGL3Technique->graphicsApiFilter()->setMinorVersion(1);
@@ -141,6 +153,10 @@ void QDiffuseSpecularMapMaterialPrivate::init()
m_diffuseSpecularMapES2Technique->graphicsApiFilter()->setMinorVersion(0);
m_diffuseSpecularMapES2Technique->graphicsApiFilter()->setProfile(QGraphicsApiFilter::NoProfile);
+ m_diffuseSpecularMapRHITechnique->graphicsApiFilter()->setApi(QGraphicsApiFilter::RHI);
+ m_diffuseSpecularMapRHITechnique->graphicsApiFilter()->setMajorVersion(1);
+ m_diffuseSpecularMapRHITechnique->graphicsApiFilter()->setMinorVersion(0);
+
m_filterKey->setParent(q);
m_filterKey->setName(QStringLiteral("renderingStyle"));
m_filterKey->setValue(QStringLiteral("forward"));
@@ -148,18 +164,22 @@ void QDiffuseSpecularMapMaterialPrivate::init()
m_diffuseSpecularMapGL3Technique->addFilterKey(m_filterKey);
m_diffuseSpecularMapGL2Technique->addFilterKey(m_filterKey);
m_diffuseSpecularMapES2Technique->addFilterKey(m_filterKey);
+ m_diffuseSpecularMapRHITechnique->addFilterKey(m_filterKey);
m_diffuseSpecularMapGL3RenderPass->setShaderProgram(m_diffuseSpecularMapGL3Shader);
m_diffuseSpecularMapGL2RenderPass->setShaderProgram(m_diffuseSpecularMapGL2ES2Shader);
m_diffuseSpecularMapES2RenderPass->setShaderProgram(m_diffuseSpecularMapGL2ES2Shader);
+ m_diffuseSpecularMapRHIRenderPass->setShaderProgram(m_diffuseSpecularMapRHIShader);
m_diffuseSpecularMapGL3Technique->addRenderPass(m_diffuseSpecularMapGL3RenderPass);
m_diffuseSpecularMapGL2Technique->addRenderPass(m_diffuseSpecularMapGL2RenderPass);
m_diffuseSpecularMapES2Technique->addRenderPass(m_diffuseSpecularMapES2RenderPass);
+ m_diffuseSpecularMapRHITechnique->addRenderPass(m_diffuseSpecularMapRHIRenderPass);
m_diffuseSpecularMapEffect->addTechnique(m_diffuseSpecularMapGL3Technique);
m_diffuseSpecularMapEffect->addTechnique(m_diffuseSpecularMapGL2Technique);
m_diffuseSpecularMapEffect->addTechnique(m_diffuseSpecularMapES2Technique);
+ m_diffuseSpecularMapEffect->addTechnique(m_diffuseSpecularMapRHITechnique);
m_diffuseSpecularMapEffect->addParameter(m_ambientParameter);
m_diffuseSpecularMapEffect->addParameter(m_diffuseParameter);
diff --git a/src/extras/defaults/qdiffusespecularmapmaterial_p.h b/src/extras/defaults/qdiffusespecularmapmaterial_p.h
index f80922dff..021836c14 100644
--- a/src/extras/defaults/qdiffusespecularmapmaterial_p.h
+++ b/src/extras/defaults/qdiffusespecularmapmaterial_p.h
@@ -96,13 +96,17 @@ public:
Qt3DRender::QTechnique *m_diffuseSpecularMapGL3Technique;
Qt3DRender::QTechnique *m_diffuseSpecularMapGL2Technique;
Qt3DRender::QTechnique *m_diffuseSpecularMapES2Technique;
+ Qt3DRender::QTechnique *m_diffuseSpecularMapRHITechnique;
Qt3DRender::QRenderPass *m_diffuseSpecularMapGL3RenderPass;
Qt3DRender::QRenderPass *m_diffuseSpecularMapGL2RenderPass;
Qt3DRender::QRenderPass *m_diffuseSpecularMapES2RenderPass;
+ Qt3DRender::QRenderPass *m_diffuseSpecularMapRHIRenderPass;
Qt3DRender::QShaderProgram *m_diffuseSpecularMapGL3Shader;
Qt3DRender::QShaderProgramBuilder *m_diffuseSpecularMapGL3ShaderBuilder;
Qt3DRender::QShaderProgram *m_diffuseSpecularMapGL2ES2Shader;
Qt3DRender::QShaderProgramBuilder *m_diffuseSpecularMapGL2ES2ShaderBuilder;
+ Qt3DRender::QShaderProgram *m_diffuseSpecularMapRHIShader;
+ Qt3DRender::QShaderProgramBuilder *m_diffuseSpecularMapRHIShaderBuilder;
Qt3DRender::QFilterKey *m_filterKey;
Q_DECLARE_PUBLIC(QDiffuseSpecularMapMaterial)
diff --git a/src/extras/defaults/qdiffusespecularmaterial.cpp b/src/extras/defaults/qdiffusespecularmaterial.cpp
index 8938ce19a..2cb4c137c 100644
--- a/src/extras/defaults/qdiffusespecularmaterial.cpp
+++ b/src/extras/defaults/qdiffusespecularmaterial.cpp
@@ -78,13 +78,17 @@ QDiffuseSpecularMaterialPrivate::QDiffuseSpecularMaterialPrivate()
, m_gl3Technique(new QTechnique())
, m_gl2Technique(new QTechnique())
, m_es2Technique(new QTechnique())
+ , m_rhiTechnique(new QTechnique())
, m_gl3RenderPass(new QRenderPass())
, m_gl2RenderPass(new QRenderPass())
, m_es2RenderPass(new QRenderPass())
+ , m_rhiRenderPass(new QRenderPass())
, m_gl3Shader(new QShaderProgram())
, m_gl3ShaderBuilder(new QShaderProgramBuilder())
, m_gl2es2Shader(new QShaderProgram())
, m_gl2es2ShaderBuilder(new QShaderProgramBuilder())
+ , m_rhiShader(new QShaderProgram())
+ , m_rhiShaderBuilder(new QShaderProgramBuilder())
, m_noDepthMask(new QNoDepthMask())
, m_blendState(new QBlendEquationArguments())
, m_blendEquation(new QBlendEquation())
@@ -127,6 +131,14 @@ void QDiffuseSpecularMaterialPrivate::init()
QStringLiteral("specular"),
QStringLiteral("normal")});
+ m_rhiShader->setVertexShaderCode(QShaderProgram::loadSource(QUrl(QStringLiteral("qrc:/shaders/rhi/default.vert"))));
+ m_rhiShaderBuilder->setParent(q);
+ m_rhiShaderBuilder->setShaderProgram(m_rhiShader);
+ m_rhiShaderBuilder->setFragmentShaderGraph(QUrl(QStringLiteral("qrc:/shaders/graphs/phong.frag.json")));
+ m_rhiShaderBuilder->setEnabledLayers({QStringLiteral("diffuse"),
+ QStringLiteral("specular"),
+ QStringLiteral("normal")});
+
m_gl3Technique->graphicsApiFilter()->setApi(QGraphicsApiFilter::OpenGL);
m_gl3Technique->graphicsApiFilter()->setMajorVersion(3);
@@ -143,6 +155,10 @@ void QDiffuseSpecularMaterialPrivate::init()
m_es2Technique->graphicsApiFilter()->setMinorVersion(0);
m_es2Technique->graphicsApiFilter()->setProfile(QGraphicsApiFilter::NoProfile);
+ m_rhiTechnique->graphicsApiFilter()->setApi(QGraphicsApiFilter::RHI);
+ m_rhiTechnique->graphicsApiFilter()->setMajorVersion(1);
+ m_rhiTechnique->graphicsApiFilter()->setMinorVersion(0);
+
m_noDepthMask->setEnabled(false);
m_blendState->setEnabled(false);
m_blendState->setSourceRgb(QBlendEquationArguments::SourceAlpha);
@@ -153,6 +169,7 @@ void QDiffuseSpecularMaterialPrivate::init()
m_gl3RenderPass->setShaderProgram(m_gl3Shader);
m_gl2RenderPass->setShaderProgram(m_gl2es2Shader);
m_es2RenderPass->setShaderProgram(m_gl2es2Shader);
+ m_rhiRenderPass->setShaderProgram(m_rhiShader);
m_gl3RenderPass->addRenderState(m_noDepthMask);
m_gl3RenderPass->addRenderState(m_blendState);
@@ -166,9 +183,14 @@ void QDiffuseSpecularMaterialPrivate::init()
m_es2RenderPass->addRenderState(m_blendState);
m_es2RenderPass->addRenderState(m_blendEquation);
+ m_rhiRenderPass->addRenderState(m_noDepthMask);
+ m_rhiRenderPass->addRenderState(m_blendState);
+ m_rhiRenderPass->addRenderState(m_blendEquation);
+
m_gl3Technique->addRenderPass(m_gl3RenderPass);
m_gl2Technique->addRenderPass(m_gl2RenderPass);
m_es2Technique->addRenderPass(m_es2RenderPass);
+ m_rhiTechnique->addRenderPass(m_rhiRenderPass);
m_filterKey->setParent(q);
m_filterKey->setName(QStringLiteral("renderingStyle"));
@@ -177,10 +199,12 @@ void QDiffuseSpecularMaterialPrivate::init()
m_gl3Technique->addFilterKey(m_filterKey);
m_gl2Technique->addFilterKey(m_filterKey);
m_es2Technique->addFilterKey(m_filterKey);
+ m_rhiTechnique->addFilterKey(m_filterKey);
m_effect->addTechnique(m_gl3Technique);
m_effect->addTechnique(m_gl2Technique);
m_effect->addTechnique(m_es2Technique);
+ m_effect->addTechnique(m_rhiTechnique);
m_effect->addParameter(m_ambientParameter);
m_effect->addParameter(m_diffuseParameter);
diff --git a/src/extras/defaults/qdiffusespecularmaterial_p.h b/src/extras/defaults/qdiffusespecularmaterial_p.h
index 2b0b2184f..9e2b2bbbb 100644
--- a/src/extras/defaults/qdiffusespecularmaterial_p.h
+++ b/src/extras/defaults/qdiffusespecularmaterial_p.h
@@ -97,13 +97,17 @@ public:
Qt3DRender::QTechnique *m_gl3Technique;
Qt3DRender::QTechnique *m_gl2Technique;
Qt3DRender::QTechnique *m_es2Technique;
+ Qt3DRender::QTechnique *m_rhiTechnique;
Qt3DRender::QRenderPass *m_gl3RenderPass;
Qt3DRender::QRenderPass *m_gl2RenderPass;
Qt3DRender::QRenderPass *m_es2RenderPass;
+ Qt3DRender::QRenderPass *m_rhiRenderPass;
Qt3DRender::QShaderProgram *m_gl3Shader;
Qt3DRender::QShaderProgramBuilder *m_gl3ShaderBuilder;
Qt3DRender::QShaderProgram *m_gl2es2Shader;
Qt3DRender::QShaderProgramBuilder *m_gl2es2ShaderBuilder;
+ Qt3DRender::QShaderProgram *m_rhiShader;
+ Qt3DRender::QShaderProgramBuilder *m_rhiShaderBuilder;
Qt3DRender::QNoDepthMask *m_noDepthMask;
Qt3DRender::QBlendEquationArguments *m_blendState;
Qt3DRender::QBlendEquation *m_blendEquation;
diff --git a/src/extras/defaults/qgoochmaterial.cpp b/src/extras/defaults/qgoochmaterial.cpp
index 57ec359b3..b5665d776 100644
--- a/src/extras/defaults/qgoochmaterial.cpp
+++ b/src/extras/defaults/qgoochmaterial.cpp
@@ -65,11 +65,14 @@ QGoochMaterialPrivate::QGoochMaterialPrivate()
, m_gl3Technique(new Qt3DRender::QTechnique)
, m_gl2Technique(new Qt3DRender::QTechnique)
, m_es2Technique(new Qt3DRender::QTechnique)
+ , m_rhiTechnique(new Qt3DRender::QTechnique)
, m_gl3RenderPass(new Qt3DRender::QRenderPass)
, m_gl2RenderPass(new Qt3DRender::QRenderPass)
, m_es2RenderPass(new Qt3DRender::QRenderPass)
+ , m_rhiRenderPass(new Qt3DRender::QRenderPass)
, m_gl3Shader(new Qt3DRender::QShaderProgram)
, m_gl2ES2Shader(new Qt3DRender::QShaderProgram)
+ , m_rhiShader(new Qt3DRender::QShaderProgram)
, m_filterKey(new Qt3DRender::QFilterKey)
{
}
@@ -97,6 +100,8 @@ void QGoochMaterialPrivate::init()
m_gl3Shader->setFragmentShaderCode(Qt3DRender::QShaderProgram::loadSource(QUrl(QStringLiteral("qrc:/shaders/gl3/gooch.frag"))));
m_gl2ES2Shader->setVertexShaderCode(Qt3DRender::QShaderProgram::loadSource(QUrl(QStringLiteral("qrc:/shaders/es2/gooch.vert"))));
m_gl2ES2Shader->setFragmentShaderCode(Qt3DRender::QShaderProgram::loadSource(QUrl(QStringLiteral("qrc:/shaders/es2/gooch.frag"))));
+ m_rhiShader->setVertexShaderCode(Qt3DRender::QShaderProgram::loadSource(QUrl(QStringLiteral("qrc:/shaders/rhi/gooch.vert"))));
+ m_rhiShader->setFragmentShaderCode(Qt3DRender::QShaderProgram::loadSource(QUrl(QStringLiteral("qrc:/shaders/rhi/gooch.frag"))));
m_gl3Technique->graphicsApiFilter()->setApi(Qt3DRender::QGraphicsApiFilter::OpenGL);
m_gl3Technique->graphicsApiFilter()->setMajorVersion(3);
@@ -113,6 +118,11 @@ void QGoochMaterialPrivate::init()
m_es2Technique->graphicsApiFilter()->setMinorVersion(0);
m_es2Technique->graphicsApiFilter()->setProfile(Qt3DRender::QGraphicsApiFilter::NoProfile);
+ m_rhiTechnique->graphicsApiFilter()->setApi(Qt3DRender::QGraphicsApiFilter::RHI);
+ m_rhiTechnique->graphicsApiFilter()->setMajorVersion(1);
+ m_rhiTechnique->graphicsApiFilter()->setMinorVersion(0);
+ m_rhiTechnique->graphicsApiFilter()->setProfile(Qt3DRender::QGraphicsApiFilter::CoreProfile);
+
m_filterKey->setParent(q);
m_filterKey->setName(QStringLiteral("renderingStyle"));
m_filterKey->setValue(QStringLiteral("forward"));
@@ -120,18 +130,22 @@ void QGoochMaterialPrivate::init()
m_gl3Technique->addFilterKey(m_filterKey);
m_gl2Technique->addFilterKey(m_filterKey);
m_es2Technique->addFilterKey(m_filterKey);
+ m_rhiTechnique->addFilterKey(m_filterKey);
m_gl3RenderPass->setShaderProgram(m_gl3Shader);
m_gl2RenderPass->setShaderProgram(m_gl2ES2Shader);
m_es2RenderPass->setShaderProgram(m_gl2ES2Shader);
+ m_rhiRenderPass->setShaderProgram(m_rhiShader);
m_gl3Technique->addRenderPass(m_gl3RenderPass);
m_gl2Technique->addRenderPass(m_gl2RenderPass);
m_es2Technique->addRenderPass(m_es2RenderPass);
+ m_rhiTechnique->addRenderPass(m_rhiRenderPass);
m_effect->addTechnique(m_gl3Technique);
m_effect->addTechnique(m_gl2Technique);
m_effect->addTechnique(m_es2Technique);
+ m_effect->addTechnique(m_rhiTechnique);
m_effect->addParameter(m_diffuseParameter);
m_effect->addParameter(m_specularParameter);
diff --git a/src/extras/defaults/qgoochmaterial_p.h b/src/extras/defaults/qgoochmaterial_p.h
index d4ef256f6..aca9ce639 100644
--- a/src/extras/defaults/qgoochmaterial_p.h
+++ b/src/extras/defaults/qgoochmaterial_p.h
@@ -93,11 +93,14 @@ public:
Qt3DRender::QTechnique *m_gl3Technique;
Qt3DRender::QTechnique *m_gl2Technique;
Qt3DRender::QTechnique *m_es2Technique;
+ Qt3DRender::QTechnique *m_rhiTechnique;
Qt3DRender::QRenderPass *m_gl3RenderPass;
Qt3DRender::QRenderPass *m_gl2RenderPass;
Qt3DRender::QRenderPass *m_es2RenderPass;
+ Qt3DRender::QRenderPass *m_rhiRenderPass;
Qt3DRender::QShaderProgram *m_gl3Shader;
Qt3DRender::QShaderProgram *m_gl2ES2Shader;
+ Qt3DRender::QShaderProgram *m_rhiShader;
Qt3DRender::QFilterKey *m_filterKey;
Q_DECLARE_PUBLIC(QGoochMaterial)
diff --git a/src/extras/defaults/qmetalroughmaterial.cpp b/src/extras/defaults/qmetalroughmaterial.cpp
index a969593db..78e544ead 100644
--- a/src/extras/defaults/qmetalroughmaterial.cpp
+++ b/src/extras/defaults/qmetalroughmaterial.cpp
@@ -79,6 +79,10 @@ QMetalRoughMaterialPrivate::QMetalRoughMaterialPrivate()
, m_metalRoughES3RenderPass(new QRenderPass())
, m_metalRoughES3Shader(new QShaderProgram())
, m_metalRoughES3ShaderBuilder(new QShaderProgramBuilder())
+ , m_metalRoughRHITechnique(new QTechnique())
+ , m_metalRoughRHIRenderPass(new QRenderPass())
+ , m_metalRoughRHIShader(new QShaderProgram())
+ , m_metalRoughRHIShaderBuilder(new QShaderProgramBuilder())
, m_filterKey(new QFilterKey)
{
}
@@ -120,6 +124,16 @@ void QMetalRoughMaterialPrivate::init()
QStringLiteral("ambientOcclusion"),
QStringLiteral("normal")});
+ m_metalRoughRHIShader->setVertexShaderCode(QShaderProgram::loadSource(QUrl(QStringLiteral("qrc:/shaders/rhi/default.vert"))));
+ m_metalRoughRHIShaderBuilder->setParent(q);
+ m_metalRoughRHIShaderBuilder->setShaderProgram(m_metalRoughRHIShader);
+ m_metalRoughRHIShaderBuilder->setFragmentShaderGraph(QUrl(QStringLiteral("qrc:/shaders/graphs/metalrough.frag.json")));
+ m_metalRoughRHIShaderBuilder->setEnabledLayers({QStringLiteral("baseColor"),
+ QStringLiteral("metalness"),
+ QStringLiteral("roughness"),
+ QStringLiteral("ambientOcclusion"),
+ QStringLiteral("normal")});
+
m_metalRoughGL3Technique->graphicsApiFilter()->setApi(QGraphicsApiFilter::OpenGL);
m_metalRoughGL3Technique->graphicsApiFilter()->setMajorVersion(3);
m_metalRoughGL3Technique->graphicsApiFilter()->setMinorVersion(1);
@@ -129,6 +143,10 @@ void QMetalRoughMaterialPrivate::init()
m_metalRoughES3Technique->graphicsApiFilter()->setMajorVersion(3);
m_metalRoughES3Technique->graphicsApiFilter()->setMinorVersion(0);
+ m_metalRoughRHITechnique->graphicsApiFilter()->setApi(QGraphicsApiFilter::RHI);
+ m_metalRoughRHITechnique->graphicsApiFilter()->setMajorVersion(1);
+ m_metalRoughRHITechnique->graphicsApiFilter()->setMinorVersion(0);
+
m_filterKey->setParent(q);
m_filterKey->setName(QStringLiteral("renderingStyle"));
m_filterKey->setValue(QStringLiteral("forward"));
@@ -143,6 +161,16 @@ void QMetalRoughMaterialPrivate::init()
m_metalRoughES3Technique->addRenderPass(m_metalRoughES3RenderPass);
m_metalRoughEffect->addTechnique(m_metalRoughES3Technique);
+ m_metalRoughRHITechnique->addFilterKey(m_filterKey);
+ m_metalRoughRHIRenderPass->setShaderProgram(m_metalRoughRHIShader);
+ m_metalRoughRHITechnique->addRenderPass(m_metalRoughRHIRenderPass);
+ m_metalRoughEffect->addTechnique(m_metalRoughRHITechnique);
+
+ // Given parameters a parent
+ m_baseColorMapParameter->setParent(m_metalRoughEffect);
+ m_metalnessMapParameter->setParent(m_metalRoughEffect);
+ m_roughnessMapParameter->setParent(m_metalRoughEffect);
+
m_metalRoughEffect->addParameter(m_baseColorParameter);
m_metalRoughEffect->addParameter(m_metalnessParameter);
m_metalRoughEffect->addParameter(m_roughnessParameter);
@@ -338,15 +366,18 @@ void QMetalRoughMaterial::setBaseColor(const QVariant &baseColor)
layers.removeAll(QStringLiteral("baseColor"));
layers.append(QStringLiteral("baseColorMap"));
d->m_metalRoughEffect->addParameter(d->m_baseColorMapParameter);
- d->m_metalRoughEffect->removeParameter(d->m_baseColorParameter);
+ if (d->m_metalRoughEffect->parameters().contains(d->m_baseColorParameter))
+ d->m_metalRoughEffect->removeParameter(d->m_baseColorParameter);
} else {
layers.removeAll(QStringLiteral("baseColorMap"));
layers.append(QStringLiteral("baseColor"));
- d->m_metalRoughEffect->removeParameter(d->m_baseColorMapParameter);
+ if (d->m_metalRoughEffect->parameters().contains(d->m_baseColorMapParameter))
+ d->m_metalRoughEffect->removeParameter(d->m_baseColorMapParameter);
d->m_metalRoughEffect->addParameter(d->m_baseColorParameter);
}
d->m_metalRoughGL3ShaderBuilder->setEnabledLayers(layers);
d->m_metalRoughES3ShaderBuilder->setEnabledLayers(layers);
+ d->m_metalRoughRHIShaderBuilder->setEnabledLayers(layers);
}
void QMetalRoughMaterial::setMetalness(const QVariant &metalness)
@@ -360,15 +391,18 @@ void QMetalRoughMaterial::setMetalness(const QVariant &metalness)
layers.removeAll(QStringLiteral("metalness"));
layers.append(QStringLiteral("metalnessMap"));
d->m_metalRoughEffect->addParameter(d->m_metalnessMapParameter);
- d->m_metalRoughEffect->removeParameter(d->m_metalnessParameter);
+ if (d->m_metalRoughEffect->parameters().contains(d->m_metalnessParameter))
+ d->m_metalRoughEffect->removeParameter(d->m_metalnessParameter);
} else {
layers.removeAll(QStringLiteral("metalnessMap"));
layers.append(QStringLiteral("metalness"));
- d->m_metalRoughEffect->removeParameter(d->m_metalnessMapParameter);
+ if (d->m_metalRoughEffect->parameters().contains(d->m_metalnessMapParameter))
+ d->m_metalRoughEffect->removeParameter(d->m_metalnessMapParameter);
d->m_metalRoughEffect->addParameter(d->m_metalnessParameter);
}
d->m_metalRoughGL3ShaderBuilder->setEnabledLayers(layers);
d->m_metalRoughES3ShaderBuilder->setEnabledLayers(layers);
+ d->m_metalRoughRHIShaderBuilder->setEnabledLayers(layers);
}
void QMetalRoughMaterial::setRoughness(const QVariant &roughness)
@@ -382,15 +416,18 @@ void QMetalRoughMaterial::setRoughness(const QVariant &roughness)
layers.removeAll(QStringLiteral("roughness"));
layers.append(QStringLiteral("roughnessMap"));
d->m_metalRoughEffect->addParameter(d->m_roughnessMapParameter);
- d->m_metalRoughEffect->removeParameter(d->m_roughnessParameter);
+ if (d->m_metalRoughEffect->parameters().contains(d->m_roughnessParameter))
+ d->m_metalRoughEffect->removeParameter(d->m_roughnessParameter);
} else {
layers.removeAll(QStringLiteral("roughnessMap"));
layers.append(QStringLiteral("roughness"));
- d->m_metalRoughEffect->removeParameter(d->m_roughnessMapParameter);
+ if (d->m_metalRoughEffect->parameters().contains(d->m_roughnessMapParameter))
+ d->m_metalRoughEffect->removeParameter(d->m_roughnessMapParameter);
d->m_metalRoughEffect->addParameter(d->m_roughnessParameter);
}
d->m_metalRoughGL3ShaderBuilder->setEnabledLayers(layers);
d->m_metalRoughES3ShaderBuilder->setEnabledLayers(layers);
+ d->m_metalRoughRHIShaderBuilder->setEnabledLayers(layers);
}
void QMetalRoughMaterial::setAmbientOcclusion(const QVariant &ambientOcclusion)
@@ -406,10 +443,12 @@ void QMetalRoughMaterial::setAmbientOcclusion(const QVariant &ambientOcclusion)
} else {
layers.removeAll(QStringLiteral("ambientOcclusionMap"));
layers.append(QStringLiteral("ambientOcclusion"));
- d->m_metalRoughEffect->removeParameter(d->m_ambientOcclusionMapParameter);
+ if (d->m_metalRoughEffect->parameters().contains(d->m_ambientOcclusionMapParameter))
+ d->m_metalRoughEffect->removeParameter(d->m_ambientOcclusionMapParameter);
}
d->m_metalRoughGL3ShaderBuilder->setEnabledLayers(layers);
d->m_metalRoughES3ShaderBuilder->setEnabledLayers(layers);
+ d->m_metalRoughRHIShaderBuilder->setEnabledLayers(layers);
}
void QMetalRoughMaterial::setNormal(const QVariant &normal)
@@ -425,10 +464,12 @@ void QMetalRoughMaterial::setNormal(const QVariant &normal)
} else {
layers.removeAll(QStringLiteral("normalMap"));
layers.append(QStringLiteral("normal"));
- d->m_metalRoughEffect->removeParameter(d->m_normalMapParameter);
+ if (d->m_metalRoughEffect->parameters().contains(d->m_normalMapParameter))
+ d->m_metalRoughEffect->removeParameter(d->m_normalMapParameter);
}
d->m_metalRoughGL3ShaderBuilder->setEnabledLayers(layers);
d->m_metalRoughES3ShaderBuilder->setEnabledLayers(layers);
+ d->m_metalRoughRHIShaderBuilder->setEnabledLayers(layers);
}
void QMetalRoughMaterial::setTextureScale(float textureScale)
diff --git a/src/extras/defaults/qmetalroughmaterial_p.h b/src/extras/defaults/qmetalroughmaterial_p.h
index 4d8b68320..15a869ff1 100644
--- a/src/extras/defaults/qmetalroughmaterial_p.h
+++ b/src/extras/defaults/qmetalroughmaterial_p.h
@@ -99,6 +99,10 @@ public:
Qt3DRender::QRenderPass *m_metalRoughES3RenderPass;
Qt3DRender::QShaderProgram *m_metalRoughES3Shader;
Qt3DRender::QShaderProgramBuilder *m_metalRoughES3ShaderBuilder;
+ Qt3DRender::QTechnique *m_metalRoughRHITechnique;
+ Qt3DRender::QRenderPass *m_metalRoughRHIRenderPass;
+ Qt3DRender::QShaderProgram *m_metalRoughRHIShader;
+ Qt3DRender::QShaderProgramBuilder *m_metalRoughRHIShaderBuilder;
Qt3DRender::QFilterKey *m_filterKey;
Q_DECLARE_PUBLIC(QMetalRoughMaterial)
diff --git a/src/extras/defaults/qmorphphongmaterial.cpp b/src/extras/defaults/qmorphphongmaterial.cpp
index 1711a21dd..8469fc127 100644
--- a/src/extras/defaults/qmorphphongmaterial.cpp
+++ b/src/extras/defaults/qmorphphongmaterial.cpp
@@ -66,13 +66,17 @@ QMorphPhongMaterialPrivate::QMorphPhongMaterialPrivate()
, m_phongGL3Technique(new QTechnique())
, m_phongGL2Technique(new QTechnique())
, m_phongES2Technique(new QTechnique())
+ , m_phongRHITechnique(new QTechnique())
, m_phongGL3RenderPass(new QRenderPass())
, m_phongGL2RenderPass(new QRenderPass())
, m_phongES2RenderPass(new QRenderPass())
+ , m_phongRHIRenderPass(new QRenderPass())
, m_phongGL3Shader(new QShaderProgram())
, m_phongGL2ES2Shader(new QShaderProgram())
+ , m_phongRHIShader(new QShaderProgram())
, m_phongGL3ShaderBuilder(new QShaderProgramBuilder())
, m_phongGL2ES2ShaderBuilder(new QShaderProgramBuilder())
+ , m_phongRHIShaderBuilder(new QShaderProgramBuilder())
, m_filterKey(new QFilterKey)
{
}
@@ -99,6 +103,7 @@ void QMorphPhongMaterialPrivate::init()
m_phongGL3ShaderBuilder->setEnabledLayers({QStringLiteral("diffuse"),
QStringLiteral("specular"),
QStringLiteral("normal")});
+
m_phongGL2ES2Shader->setVertexShaderCode(QShaderProgram::loadSource(QUrl(QStringLiteral("qrc:/shaders/es2/morphphong.vert"))));
m_phongGL2ES2ShaderBuilder->setParent(q);
m_phongGL2ES2ShaderBuilder->setShaderProgram(m_phongGL2ES2Shader);
@@ -107,6 +112,14 @@ void QMorphPhongMaterialPrivate::init()
QStringLiteral("specular"),
QStringLiteral("normal")});
+ m_phongRHIShader->setVertexShaderCode(QShaderProgram::loadSource(QUrl(QStringLiteral("qrc:/shaders/rhi/morphphong.vert"))));
+ m_phongRHIShaderBuilder->setParent(q);
+ m_phongRHIShaderBuilder->setShaderProgram(m_phongRHIShader);
+ m_phongRHIShaderBuilder->setFragmentShaderGraph(QUrl(QStringLiteral("qrc:/shaders/graphs/phong.frag.json")));
+ m_phongRHIShaderBuilder->setEnabledLayers({QStringLiteral("diffuse"),
+ QStringLiteral("specular"),
+ QStringLiteral("normal")});
+
m_phongGL3Technique->graphicsApiFilter()->setApi(QGraphicsApiFilter::OpenGL);
m_phongGL3Technique->graphicsApiFilter()->setMajorVersion(3);
m_phongGL3Technique->graphicsApiFilter()->setMinorVersion(1);
@@ -122,13 +135,19 @@ void QMorphPhongMaterialPrivate::init()
m_phongES2Technique->graphicsApiFilter()->setMinorVersion(0);
m_phongES2Technique->graphicsApiFilter()->setProfile(QGraphicsApiFilter::NoProfile);
+ m_phongRHITechnique->graphicsApiFilter()->setApi(QGraphicsApiFilter::RHI);
+ m_phongRHITechnique->graphicsApiFilter()->setMajorVersion(1);
+ m_phongRHITechnique->graphicsApiFilter()->setMinorVersion(0);
+
m_phongGL3RenderPass->setShaderProgram(m_phongGL3Shader);
m_phongGL2RenderPass->setShaderProgram(m_phongGL2ES2Shader);
m_phongES2RenderPass->setShaderProgram(m_phongGL2ES2Shader);
+ m_phongRHIRenderPass->setShaderProgram(m_phongRHIShader);
m_phongGL3Technique->addRenderPass(m_phongGL3RenderPass);
m_phongGL2Technique->addRenderPass(m_phongGL2RenderPass);
m_phongES2Technique->addRenderPass(m_phongES2RenderPass);
+ m_phongRHITechnique->addRenderPass(m_phongRHIRenderPass);
m_filterKey->setParent(q);
m_filterKey->setName(QStringLiteral("renderingStyle"));
@@ -137,10 +156,12 @@ void QMorphPhongMaterialPrivate::init()
m_phongGL3Technique->addFilterKey(m_filterKey);
m_phongGL2Technique->addFilterKey(m_filterKey);
m_phongES2Technique->addFilterKey(m_filterKey);
+ m_phongRHITechnique->addFilterKey(m_filterKey);
m_phongEffect->addTechnique(m_phongGL3Technique);
m_phongEffect->addTechnique(m_phongGL2Technique);
m_phongEffect->addTechnique(m_phongES2Technique);
+ m_phongEffect->addTechnique(m_phongRHITechnique);
m_phongEffect->addParameter(m_ambientParameter);
m_phongEffect->addParameter(m_diffuseParameter);
diff --git a/src/extras/defaults/qmorphphongmaterial_p.h b/src/extras/defaults/qmorphphongmaterial_p.h
index 28b7750d9..4ee8954fe 100644
--- a/src/extras/defaults/qmorphphongmaterial_p.h
+++ b/src/extras/defaults/qmorphphongmaterial_p.h
@@ -90,13 +90,17 @@ public:
Qt3DRender::QTechnique *m_phongGL3Technique;
Qt3DRender::QTechnique *m_phongGL2Technique;
Qt3DRender::QTechnique *m_phongES2Technique;
+ Qt3DRender::QTechnique *m_phongRHITechnique;
Qt3DRender::QRenderPass *m_phongGL3RenderPass;
Qt3DRender::QRenderPass *m_phongGL2RenderPass;
Qt3DRender::QRenderPass *m_phongES2RenderPass;
+ Qt3DRender::QRenderPass *m_phongRHIRenderPass;
Qt3DRender::QShaderProgram *m_phongGL3Shader;
Qt3DRender::QShaderProgram *m_phongGL2ES2Shader;
+ Qt3DRender::QShaderProgram *m_phongRHIShader;
Qt3DRender::QShaderProgramBuilder *m_phongGL3ShaderBuilder;
Qt3DRender::QShaderProgramBuilder *m_phongGL2ES2ShaderBuilder;
+ Qt3DRender::QShaderProgramBuilder *m_phongRHIShaderBuilder;
Qt3DRender::QFilterKey *m_filterKey;
Q_DECLARE_PUBLIC(QMorphPhongMaterial)
diff --git a/src/extras/defaults/qnormaldiffusemapalphamaterial.cpp b/src/extras/defaults/qnormaldiffusemapalphamaterial.cpp
index 27fda6879..03b8d8385 100644
--- a/src/extras/defaults/qnormaldiffusemapalphamaterial.cpp
+++ b/src/extras/defaults/qnormaldiffusemapalphamaterial.cpp
@@ -101,6 +101,14 @@ void QNormalDiffuseMapAlphaMaterialPrivate::init()
QStringLiteral("specular"),
QStringLiteral("normalTexture")});
+ m_normalDiffuseRHIShader->setVertexShaderCode(QShaderProgram::loadSource(QUrl(QStringLiteral("qrc:/shaders/rhi/default.vert"))));
+ m_normalDiffuseRHIShaderBuilder->setParent(q);
+ m_normalDiffuseRHIShaderBuilder->setShaderProgram(m_normalDiffuseRHIShader);
+ m_normalDiffuseRHIShaderBuilder->setFragmentShaderGraph(QUrl(QStringLiteral("qrc:/shaders/graphs/phong.frag.json")));
+ m_normalDiffuseRHIShaderBuilder->setEnabledLayers({QStringLiteral("diffuseTexture"),
+ QStringLiteral("specular"),
+ QStringLiteral("normalTexture")});
+
m_normalDiffuseGL3Technique->graphicsApiFilter()->setApi(QGraphicsApiFilter::OpenGL);
m_normalDiffuseGL3Technique->graphicsApiFilter()->setMajorVersion(3);
m_normalDiffuseGL3Technique->graphicsApiFilter()->setMinorVersion(1);
@@ -116,6 +124,10 @@ void QNormalDiffuseMapAlphaMaterialPrivate::init()
m_normalDiffuseES2Technique->graphicsApiFilter()->setMinorVersion(0);
m_normalDiffuseES2Technique->graphicsApiFilter()->setProfile(QGraphicsApiFilter::NoProfile);
+ m_normalDiffuseRHITechnique->graphicsApiFilter()->setApi(QGraphicsApiFilter::RHI);
+ m_normalDiffuseRHITechnique->graphicsApiFilter()->setMajorVersion(1);
+ m_normalDiffuseRHITechnique->graphicsApiFilter()->setMinorVersion(0);
+
m_filterKey->setParent(q);
m_filterKey->setName(QStringLiteral("renderingStyle"));
m_filterKey->setValue(QStringLiteral("forward"));
@@ -123,6 +135,7 @@ void QNormalDiffuseMapAlphaMaterialPrivate::init()
m_normalDiffuseGL3Technique->addFilterKey(m_filterKey);
m_normalDiffuseGL2Technique->addFilterKey(m_filterKey);
m_normalDiffuseES2Technique->addFilterKey(m_filterKey);
+ m_normalDiffuseRHITechnique->addFilterKey(m_filterKey);
m_depthTest->setDepthFunction(QDepthTest::Less);
@@ -138,13 +151,19 @@ void QNormalDiffuseMapAlphaMaterialPrivate::init()
m_normalDiffuseES2RenderPass->addRenderState(m_alphaCoverage);
m_normalDiffuseES2RenderPass->addRenderState(m_depthTest);
+ m_normalDiffuseRHIRenderPass->setShaderProgram(m_normalDiffuseRHIShader);
+ m_normalDiffuseRHIRenderPass->addRenderState(m_alphaCoverage);
+ m_normalDiffuseRHIRenderPass->addRenderState(m_depthTest);
+
m_normalDiffuseGL3Technique->addRenderPass(m_normalDiffuseGL3RenderPass);
m_normalDiffuseGL2Technique->addRenderPass(m_normalDiffuseGL2RenderPass);
m_normalDiffuseES2Technique->addRenderPass(m_normalDiffuseES2RenderPass);
+ m_normalDiffuseRHITechnique->addRenderPass(m_normalDiffuseRHIRenderPass);
m_normalDiffuseEffect->addTechnique(m_normalDiffuseGL3Technique);
m_normalDiffuseEffect->addTechnique(m_normalDiffuseGL2Technique);
m_normalDiffuseEffect->addTechnique(m_normalDiffuseES2Technique);
+ m_normalDiffuseEffect->addTechnique(m_normalDiffuseRHITechnique);
m_normalDiffuseEffect->addParameter(m_ambientParameter);
m_normalDiffuseEffect->addParameter(m_diffuseParameter);
diff --git a/src/extras/defaults/qnormaldiffusemapmaterial.cpp b/src/extras/defaults/qnormaldiffusemapmaterial.cpp
index 9d41ddb32..c4f3e15b2 100644
--- a/src/extras/defaults/qnormaldiffusemapmaterial.cpp
+++ b/src/extras/defaults/qnormaldiffusemapmaterial.cpp
@@ -73,13 +73,17 @@ QNormalDiffuseMapMaterialPrivate::QNormalDiffuseMapMaterialPrivate()
, m_normalDiffuseGL3Technique(new QTechnique())
, m_normalDiffuseGL2Technique(new QTechnique())
, m_normalDiffuseES2Technique(new QTechnique())
+ , m_normalDiffuseRHITechnique(new QTechnique())
, m_normalDiffuseGL3RenderPass(new QRenderPass())
, m_normalDiffuseGL2RenderPass(new QRenderPass())
, m_normalDiffuseES2RenderPass(new QRenderPass())
+ , m_normalDiffuseRHIRenderPass(new QRenderPass())
, m_normalDiffuseGL3Shader(new QShaderProgram())
, m_normalDiffuseGL3ShaderBuilder(new QShaderProgramBuilder())
, m_normalDiffuseGL2ES2Shader(new QShaderProgram())
, m_normalDiffuseGL2ES2ShaderBuilder(new QShaderProgramBuilder())
+ , m_normalDiffuseRHIShader(new QShaderProgram())
+ , m_normalDiffuseRHIShaderBuilder(new QShaderProgramBuilder())
, m_filterKey(new QFilterKey)
{
m_diffuseTexture->setMagnificationFilter(QAbstractTexture::Linear);
@@ -128,6 +132,14 @@ void QNormalDiffuseMapMaterialPrivate::init()
QStringLiteral("specular"),
QStringLiteral("normalTexture")});
+ m_normalDiffuseRHIShader->setVertexShaderCode(QShaderProgram::loadSource(QUrl(QStringLiteral("qrc:/shaders/rhi/default.vert"))));
+ m_normalDiffuseRHIShaderBuilder->setParent(q);
+ m_normalDiffuseRHIShaderBuilder->setShaderProgram(m_normalDiffuseRHIShader);
+ m_normalDiffuseRHIShaderBuilder->setFragmentShaderGraph(QUrl(QStringLiteral("qrc:/shaders/graphs/phong.frag.json")));
+ m_normalDiffuseRHIShaderBuilder->setEnabledLayers({QStringLiteral("diffuseTexture"),
+ QStringLiteral("specular"),
+ QStringLiteral("normalTexture")});
+
m_normalDiffuseGL3Technique->graphicsApiFilter()->setApi(QGraphicsApiFilter::OpenGL);
m_normalDiffuseGL3Technique->graphicsApiFilter()->setMajorVersion(3);
m_normalDiffuseGL3Technique->graphicsApiFilter()->setMinorVersion(1);
@@ -143,6 +155,10 @@ void QNormalDiffuseMapMaterialPrivate::init()
m_normalDiffuseES2Technique->graphicsApiFilter()->setMinorVersion(0);
m_normalDiffuseES2Technique->graphicsApiFilter()->setProfile(QGraphicsApiFilter::NoProfile);
+ m_normalDiffuseRHITechnique->graphicsApiFilter()->setApi(QGraphicsApiFilter::RHI);
+ m_normalDiffuseRHITechnique->graphicsApiFilter()->setMajorVersion(1);
+ m_normalDiffuseRHITechnique->graphicsApiFilter()->setMinorVersion(0);
+
m_filterKey->setParent(q);
m_filterKey->setName(QStringLiteral("renderingStyle"));
m_filterKey->setValue(QStringLiteral("forward"));
@@ -150,18 +166,22 @@ void QNormalDiffuseMapMaterialPrivate::init()
m_normalDiffuseGL3Technique->addFilterKey(m_filterKey);
m_normalDiffuseGL2Technique->addFilterKey(m_filterKey);
m_normalDiffuseES2Technique->addFilterKey(m_filterKey);
+ m_normalDiffuseRHITechnique->addFilterKey(m_filterKey);
m_normalDiffuseGL3RenderPass->setShaderProgram(m_normalDiffuseGL3Shader);
m_normalDiffuseGL2RenderPass->setShaderProgram(m_normalDiffuseGL2ES2Shader);
m_normalDiffuseES2RenderPass->setShaderProgram(m_normalDiffuseGL2ES2Shader);
+ m_normalDiffuseRHIRenderPass->setShaderProgram(m_normalDiffuseRHIShader);
m_normalDiffuseGL3Technique->addRenderPass(m_normalDiffuseGL3RenderPass);
m_normalDiffuseGL2Technique->addRenderPass(m_normalDiffuseGL2RenderPass);
m_normalDiffuseES2Technique->addRenderPass(m_normalDiffuseES2RenderPass);
+ m_normalDiffuseRHITechnique->addRenderPass(m_normalDiffuseRHIRenderPass);
m_normalDiffuseEffect->addTechnique(m_normalDiffuseGL3Technique);
m_normalDiffuseEffect->addTechnique(m_normalDiffuseGL2Technique);
m_normalDiffuseEffect->addTechnique(m_normalDiffuseES2Technique);
+ m_normalDiffuseEffect->addTechnique(m_normalDiffuseRHITechnique);
m_normalDiffuseEffect->addParameter(m_ambientParameter);
m_normalDiffuseEffect->addParameter(m_diffuseParameter);
diff --git a/src/extras/defaults/qnormaldiffusemapmaterial_p.h b/src/extras/defaults/qnormaldiffusemapmaterial_p.h
index 1ac937b1e..5a5839682 100644
--- a/src/extras/defaults/qnormaldiffusemapmaterial_p.h
+++ b/src/extras/defaults/qnormaldiffusemapmaterial_p.h
@@ -98,13 +98,17 @@ public:
Qt3DRender::QTechnique *m_normalDiffuseGL3Technique;
Qt3DRender::QTechnique *m_normalDiffuseGL2Technique;
Qt3DRender::QTechnique *m_normalDiffuseES2Technique;
+ Qt3DRender::QTechnique *m_normalDiffuseRHITechnique;
Qt3DRender::QRenderPass *m_normalDiffuseGL3RenderPass;
Qt3DRender::QRenderPass *m_normalDiffuseGL2RenderPass;
Qt3DRender::QRenderPass *m_normalDiffuseES2RenderPass;
+ Qt3DRender::QRenderPass *m_normalDiffuseRHIRenderPass;
Qt3DRender::QShaderProgram *m_normalDiffuseGL3Shader;
Qt3DRender::QShaderProgramBuilder *m_normalDiffuseGL3ShaderBuilder;
Qt3DRender::QShaderProgram *m_normalDiffuseGL2ES2Shader;
Qt3DRender::QShaderProgramBuilder *m_normalDiffuseGL2ES2ShaderBuilder;
+ Qt3DRender::QShaderProgram *m_normalDiffuseRHIShader;
+ Qt3DRender::QShaderProgramBuilder *m_normalDiffuseRHIShaderBuilder;
Qt3DRender::QFilterKey *m_filterKey;
Q_DECLARE_PUBLIC(QNormalDiffuseMapMaterial)
diff --git a/src/extras/defaults/qnormaldiffusespecularmapmaterial.cpp b/src/extras/defaults/qnormaldiffusespecularmapmaterial.cpp
index a76d9856b..66586ef18 100644
--- a/src/extras/defaults/qnormaldiffusespecularmapmaterial.cpp
+++ b/src/extras/defaults/qnormaldiffusespecularmapmaterial.cpp
@@ -75,13 +75,17 @@ QNormalDiffuseSpecularMapMaterialPrivate::QNormalDiffuseSpecularMapMaterialPriva
, m_normalDiffuseSpecularGL3Technique(new QTechnique())
, m_normalDiffuseSpecularGL2Technique(new QTechnique())
, m_normalDiffuseSpecularES2Technique(new QTechnique())
+ , m_normalDiffuseSpecularRHITechnique(new QTechnique())
, m_normalDiffuseSpecularGL3RenderPass(new QRenderPass())
, m_normalDiffuseSpecularGL2RenderPass(new QRenderPass())
, m_normalDiffuseSpecularES2RenderPass(new QRenderPass())
+ , m_normalDiffuseSpecularRHIRenderPass(new QRenderPass())
, m_normalDiffuseSpecularGL3Shader(new QShaderProgram())
, m_normalDiffuseSpecularGL3ShaderBuilder(new QShaderProgramBuilder())
, m_normalDiffuseSpecularGL2ES2Shader(new QShaderProgram())
, m_normalDiffuseSpecularGL2ES2ShaderBuilder(new QShaderProgramBuilder())
+ , m_normalDiffuseSpecularRHIShader(new QShaderProgram())
+ , m_normalDiffuseSpecularRHIShaderBuilder(new QShaderProgramBuilder())
, m_filterKey(new QFilterKey)
{
m_diffuseTexture->setMagnificationFilter(QAbstractTexture::Linear);
@@ -136,6 +140,14 @@ void QNormalDiffuseSpecularMapMaterialPrivate::init()
QStringLiteral("specularTexture"),
QStringLiteral("normalTexture")});
+ m_normalDiffuseSpecularRHIShader->setVertexShaderCode(QShaderProgram::loadSource(QUrl(QStringLiteral("qrc:/shaders/rhi/default.vert"))));
+ m_normalDiffuseSpecularRHIShaderBuilder->setParent(q);
+ m_normalDiffuseSpecularRHIShaderBuilder->setShaderProgram(m_normalDiffuseSpecularRHIShader);
+ m_normalDiffuseSpecularRHIShaderBuilder->setFragmentShaderGraph(QUrl(QStringLiteral("qrc:/shaders/graphs/phong.frag.json")));
+ m_normalDiffuseSpecularRHIShaderBuilder->setEnabledLayers({QStringLiteral("diffuseTexture"),
+ QStringLiteral("specularTexture"),
+ QStringLiteral("normalTexture")});
+
m_normalDiffuseSpecularGL3Technique->graphicsApiFilter()->setApi(QGraphicsApiFilter::OpenGL);
m_normalDiffuseSpecularGL3Technique->graphicsApiFilter()->setMajorVersion(3);
m_normalDiffuseSpecularGL3Technique->graphicsApiFilter()->setMinorVersion(1);
@@ -151,6 +163,10 @@ void QNormalDiffuseSpecularMapMaterialPrivate::init()
m_normalDiffuseSpecularES2Technique->graphicsApiFilter()->setMinorVersion(0);
m_normalDiffuseSpecularES2Technique->graphicsApiFilter()->setProfile(QGraphicsApiFilter::NoProfile);
+ m_normalDiffuseSpecularRHITechnique->graphicsApiFilter()->setApi(QGraphicsApiFilter::RHI);
+ m_normalDiffuseSpecularRHITechnique->graphicsApiFilter()->setMajorVersion(1);
+ m_normalDiffuseSpecularRHITechnique->graphicsApiFilter()->setMinorVersion(0);
+
m_filterKey->setParent(q);
m_filterKey->setName(QStringLiteral("renderingStyle"));
m_filterKey->setValue(QStringLiteral("forward"));
@@ -158,18 +174,22 @@ void QNormalDiffuseSpecularMapMaterialPrivate::init()
m_normalDiffuseSpecularGL3Technique->addFilterKey(m_filterKey);
m_normalDiffuseSpecularGL2Technique->addFilterKey(m_filterKey);
m_normalDiffuseSpecularES2Technique->addFilterKey(m_filterKey);
+ m_normalDiffuseSpecularRHITechnique->addFilterKey(m_filterKey);
m_normalDiffuseSpecularGL3RenderPass->setShaderProgram(m_normalDiffuseSpecularGL3Shader);
m_normalDiffuseSpecularGL2RenderPass->setShaderProgram(m_normalDiffuseSpecularGL2ES2Shader);
m_normalDiffuseSpecularES2RenderPass->setShaderProgram(m_normalDiffuseSpecularGL2ES2Shader);
+ m_normalDiffuseSpecularRHIRenderPass->setShaderProgram(m_normalDiffuseSpecularRHIShader);
m_normalDiffuseSpecularGL3Technique->addRenderPass(m_normalDiffuseSpecularGL3RenderPass);
m_normalDiffuseSpecularGL2Technique->addRenderPass(m_normalDiffuseSpecularGL2RenderPass);
m_normalDiffuseSpecularES2Technique->addRenderPass(m_normalDiffuseSpecularES2RenderPass);
+ m_normalDiffuseSpecularRHITechnique->addRenderPass(m_normalDiffuseSpecularRHIRenderPass);
m_normalDiffuseSpecularEffect->addTechnique(m_normalDiffuseSpecularGL3Technique);
m_normalDiffuseSpecularEffect->addTechnique(m_normalDiffuseSpecularGL2Technique);
m_normalDiffuseSpecularEffect->addTechnique(m_normalDiffuseSpecularES2Technique);
+ m_normalDiffuseSpecularEffect->addTechnique(m_normalDiffuseSpecularRHITechnique);
m_normalDiffuseSpecularEffect->addParameter(m_ambientParameter);
m_normalDiffuseSpecularEffect->addParameter(m_diffuseParameter);
diff --git a/src/extras/defaults/qnormaldiffusespecularmapmaterial_p.h b/src/extras/defaults/qnormaldiffusespecularmapmaterial_p.h
index e02451cc0..0a0d519ad 100644
--- a/src/extras/defaults/qnormaldiffusespecularmapmaterial_p.h
+++ b/src/extras/defaults/qnormaldiffusespecularmapmaterial_p.h
@@ -99,13 +99,17 @@ public:
Qt3DRender::QTechnique *m_normalDiffuseSpecularGL3Technique;
Qt3DRender::QTechnique *m_normalDiffuseSpecularGL2Technique;
Qt3DRender::QTechnique *m_normalDiffuseSpecularES2Technique;
+ Qt3DRender::QTechnique *m_normalDiffuseSpecularRHITechnique;
Qt3DRender::QRenderPass *m_normalDiffuseSpecularGL3RenderPass;
Qt3DRender::QRenderPass *m_normalDiffuseSpecularGL2RenderPass;
Qt3DRender::QRenderPass *m_normalDiffuseSpecularES2RenderPass;
+ Qt3DRender::QRenderPass *m_normalDiffuseSpecularRHIRenderPass;
Qt3DRender::QShaderProgram *m_normalDiffuseSpecularGL3Shader;
Qt3DRender::QShaderProgramBuilder *m_normalDiffuseSpecularGL3ShaderBuilder;
Qt3DRender::QShaderProgram *m_normalDiffuseSpecularGL2ES2Shader;
Qt3DRender::QShaderProgramBuilder *m_normalDiffuseSpecularGL2ES2ShaderBuilder;
+ Qt3DRender::QShaderProgram *m_normalDiffuseSpecularRHIShader;
+ Qt3DRender::QShaderProgramBuilder *m_normalDiffuseSpecularRHIShaderBuilder;
Qt3DRender::QFilterKey *m_filterKey;
Q_DECLARE_PUBLIC(QNormalDiffuseSpecularMapMaterial)
diff --git a/src/extras/defaults/qpervertexcolormaterial.cpp b/src/extras/defaults/qpervertexcolormaterial.cpp
index f0462039e..ef41cbce7 100644
--- a/src/extras/defaults/qpervertexcolormaterial.cpp
+++ b/src/extras/defaults/qpervertexcolormaterial.cpp
@@ -64,11 +64,14 @@ QPerVertexColorMaterialPrivate::QPerVertexColorMaterialPrivate()
, m_vertexGL3Technique(new QTechnique())
, m_vertexGL2Technique(new QTechnique())
, m_vertexES2Technique(new QTechnique())
+ , m_vertexRHITechnique(new QTechnique())
, m_vertexGL3RenderPass(new QRenderPass())
, m_vertexGL2RenderPass(new QRenderPass())
, m_vertexES2RenderPass(new QRenderPass())
+ , m_vertexRHIRenderPass(new QRenderPass())
, m_vertexGL3Shader(new QShaderProgram())
, m_vertexGL2ES2Shader(new QShaderProgram())
+ , m_vertexRHIShader(new QShaderProgram())
, m_filterKey(new QFilterKey)
{
}
@@ -119,6 +122,8 @@ void QPerVertexColorMaterialPrivate::init()
m_vertexGL3Shader->setFragmentShaderCode(QShaderProgram::loadSource(QUrl(QStringLiteral("qrc:/shaders/gl3/pervertexcolor.frag"))));
m_vertexGL2ES2Shader->setVertexShaderCode(QShaderProgram::loadSource(QUrl(QStringLiteral("qrc:/shaders/es2/pervertexcolor.vert"))));
m_vertexGL2ES2Shader->setFragmentShaderCode(QShaderProgram::loadSource(QUrl(QStringLiteral("qrc:/shaders/es2/pervertexcolor.frag"))));
+ m_vertexRHIShader->setVertexShaderCode(QShaderProgram::loadSource(QUrl(QStringLiteral("qrc:/shaders/rhi/pervertexcolor.vert"))));
+ m_vertexRHIShader->setFragmentShaderCode(QShaderProgram::loadSource(QUrl(QStringLiteral("qrc:/shaders/rhi/pervertexcolor.frag"))));
m_vertexGL3Technique->graphicsApiFilter()->setApi(QGraphicsApiFilter::OpenGL);
m_vertexGL3Technique->graphicsApiFilter()->setMajorVersion(3);
@@ -135,6 +140,10 @@ void QPerVertexColorMaterialPrivate::init()
m_vertexES2Technique->graphicsApiFilter()->setMinorVersion(0);
m_vertexES2Technique->graphicsApiFilter()->setProfile(QGraphicsApiFilter::NoProfile);
+ m_vertexRHITechnique->graphicsApiFilter()->setApi(QGraphicsApiFilter::RHI);
+ m_vertexRHITechnique->graphicsApiFilter()->setMajorVersion(1);
+ m_vertexRHITechnique->graphicsApiFilter()->setMinorVersion(0);
+
Q_Q(QPerVertexColorMaterial);
m_filterKey->setParent(q);
m_filterKey->setName(QStringLiteral("renderingStyle"));
@@ -143,18 +152,22 @@ void QPerVertexColorMaterialPrivate::init()
m_vertexGL3Technique->addFilterKey(m_filterKey);
m_vertexGL2Technique->addFilterKey(m_filterKey);
m_vertexES2Technique->addFilterKey(m_filterKey);
+ m_vertexRHITechnique->addFilterKey(m_filterKey);
m_vertexGL3RenderPass->setShaderProgram(m_vertexGL3Shader);
m_vertexGL2RenderPass->setShaderProgram(m_vertexGL2ES2Shader);
m_vertexES2RenderPass->setShaderProgram(m_vertexGL2ES2Shader);
+ m_vertexRHIRenderPass->setShaderProgram(m_vertexRHIShader);
m_vertexGL3Technique->addRenderPass(m_vertexGL3RenderPass);
m_vertexGL2Technique->addRenderPass(m_vertexGL2RenderPass);
m_vertexES2Technique->addRenderPass(m_vertexES2RenderPass);
+ m_vertexRHITechnique->addRenderPass(m_vertexRHIRenderPass);
m_vertexEffect->addTechnique(m_vertexGL3Technique);
m_vertexEffect->addTechnique(m_vertexGL2Technique);
m_vertexEffect->addTechnique(m_vertexES2Technique);
+ m_vertexEffect->addTechnique(m_vertexRHITechnique);
q->setEffect(m_vertexEffect);
}
diff --git a/src/extras/defaults/qpervertexcolormaterial_p.h b/src/extras/defaults/qpervertexcolormaterial_p.h
index 678353525..8c5c3eefa 100644
--- a/src/extras/defaults/qpervertexcolormaterial_p.h
+++ b/src/extras/defaults/qpervertexcolormaterial_p.h
@@ -81,11 +81,14 @@ public:
Qt3DRender::QTechnique *m_vertexGL3Technique;
Qt3DRender::QTechnique *m_vertexGL2Technique;
Qt3DRender::QTechnique *m_vertexES2Technique;
+ Qt3DRender::QTechnique *m_vertexRHITechnique;
Qt3DRender::QRenderPass *m_vertexGL3RenderPass;
Qt3DRender::QRenderPass *m_vertexGL2RenderPass;
Qt3DRender::QRenderPass *m_vertexES2RenderPass;
+ Qt3DRender::QRenderPass *m_vertexRHIRenderPass;
Qt3DRender::QShaderProgram *m_vertexGL3Shader;
Qt3DRender::QShaderProgram *m_vertexGL2ES2Shader;
+ Qt3DRender::QShaderProgram *m_vertexRHIShader;
Qt3DRender::QFilterKey *m_filterKey;
Q_DECLARE_PUBLIC(QPerVertexColorMaterial)
diff --git a/src/extras/defaults/qphongalphamaterial.cpp b/src/extras/defaults/qphongalphamaterial.cpp
index 2f705072f..8d8464d85 100644
--- a/src/extras/defaults/qphongalphamaterial.cpp
+++ b/src/extras/defaults/qphongalphamaterial.cpp
@@ -72,13 +72,17 @@ QPhongAlphaMaterialPrivate::QPhongAlphaMaterialPrivate()
, m_phongAlphaGL3Technique(new QTechnique())
, m_phongAlphaGL2Technique(new QTechnique())
, m_phongAlphaES2Technique(new QTechnique())
+ , m_phongAlphaRHITechnique(new QTechnique())
, m_phongAlphaGL3RenderPass(new QRenderPass())
, m_phongAlphaGL2RenderPass(new QRenderPass())
, m_phongAlphaES2RenderPass(new QRenderPass())
+ , m_phongAlphaRHIRenderPass(new QRenderPass())
, m_phongAlphaGL3Shader(new QShaderProgram())
, m_phongAlphaGL3ShaderBuilder(new QShaderProgramBuilder())
, m_phongAlphaGL2ES2Shader(new QShaderProgram())
, m_phongAlphaGL2ES2ShaderBuilder(new QShaderProgramBuilder())
+ , m_phongAlphaRHIShader(new QShaderProgram())
+ , m_phongAlphaRHIShaderBuilder(new QShaderProgramBuilder())
, m_noDepthMask(new QNoDepthMask())
, m_blendState(new QBlendEquationArguments())
, m_blendEquation(new QBlendEquation())
@@ -116,6 +120,14 @@ void QPhongAlphaMaterialPrivate::init()
QStringLiteral("specular"),
QStringLiteral("normal")});
+ m_phongAlphaRHIShader->setVertexShaderCode(QShaderProgram::loadSource(QUrl(QStringLiteral("qrc:/shaders/rhi/default.vert"))));
+ m_phongAlphaRHIShaderBuilder->setParent(q);
+ m_phongAlphaRHIShaderBuilder->setShaderProgram(m_phongAlphaRHIShader);
+ m_phongAlphaRHIShaderBuilder->setFragmentShaderGraph(QUrl(QStringLiteral("qrc:/shaders/graphs/phong.frag.json")));
+ m_phongAlphaRHIShaderBuilder->setEnabledLayers({QStringLiteral("diffuse"),
+ QStringLiteral("specular"),
+ QStringLiteral("normal")});
+
m_phongAlphaGL3Technique->graphicsApiFilter()->setApi(QGraphicsApiFilter::OpenGL);
m_phongAlphaGL3Technique->graphicsApiFilter()->setMajorVersion(3);
m_phongAlphaGL3Technique->graphicsApiFilter()->setMinorVersion(1);
@@ -131,6 +143,10 @@ void QPhongAlphaMaterialPrivate::init()
m_phongAlphaES2Technique->graphicsApiFilter()->setMinorVersion(0);
m_phongAlphaES2Technique->graphicsApiFilter()->setProfile(QGraphicsApiFilter::NoProfile);
+ m_phongAlphaRHITechnique->graphicsApiFilter()->setApi(QGraphicsApiFilter::RHI);
+ m_phongAlphaRHITechnique->graphicsApiFilter()->setMajorVersion(1);
+ m_phongAlphaRHITechnique->graphicsApiFilter()->setMinorVersion(0);
+
m_filterKey->setParent(q);
m_filterKey->setName(QStringLiteral("renderingStyle"));
m_filterKey->setValue(QStringLiteral("forward"));
@@ -138,6 +154,7 @@ void QPhongAlphaMaterialPrivate::init()
m_phongAlphaGL3Technique->addFilterKey(m_filterKey);
m_phongAlphaGL2Technique->addFilterKey(m_filterKey);
m_phongAlphaES2Technique->addFilterKey(m_filterKey);
+ m_phongAlphaRHITechnique->addFilterKey(m_filterKey);
m_blendState->setSourceRgb(QBlendEquationArguments::SourceAlpha);
m_blendState->setDestinationRgb(QBlendEquationArguments::OneMinusSourceAlpha);
@@ -146,6 +163,7 @@ void QPhongAlphaMaterialPrivate::init()
m_phongAlphaGL3RenderPass->setShaderProgram(m_phongAlphaGL3Shader);
m_phongAlphaGL2RenderPass->setShaderProgram(m_phongAlphaGL2ES2Shader);
m_phongAlphaES2RenderPass->setShaderProgram(m_phongAlphaGL2ES2Shader);
+ m_phongAlphaRHIRenderPass->setShaderProgram(m_phongAlphaRHIShader);
m_phongAlphaGL3RenderPass->addRenderState(m_noDepthMask);
m_phongAlphaGL3RenderPass->addRenderState(m_blendState);
@@ -159,13 +177,19 @@ void QPhongAlphaMaterialPrivate::init()
m_phongAlphaES2RenderPass->addRenderState(m_blendState);
m_phongAlphaES2RenderPass->addRenderState(m_blendEquation);
+ m_phongAlphaRHIRenderPass->addRenderState(m_noDepthMask);
+ m_phongAlphaRHIRenderPass->addRenderState(m_blendState);
+ m_phongAlphaRHIRenderPass->addRenderState(m_blendEquation);
+
m_phongAlphaGL3Technique->addRenderPass(m_phongAlphaGL3RenderPass);
m_phongAlphaGL2Technique->addRenderPass(m_phongAlphaGL2RenderPass);
m_phongAlphaES2Technique->addRenderPass(m_phongAlphaES2RenderPass);
+ m_phongAlphaRHITechnique->addRenderPass(m_phongAlphaRHIRenderPass);
m_phongEffect->addTechnique(m_phongAlphaGL3Technique);
m_phongEffect->addTechnique(m_phongAlphaGL2Technique);
m_phongEffect->addTechnique(m_phongAlphaES2Technique);
+ m_phongEffect->addTechnique(m_phongAlphaRHITechnique);
m_phongEffect->addParameter(m_ambientParameter);
m_phongEffect->addParameter(m_diffuseParameter);
diff --git a/src/extras/defaults/qphongalphamaterial_p.h b/src/extras/defaults/qphongalphamaterial_p.h
index 97eaf7bc4..7909f4e34 100644
--- a/src/extras/defaults/qphongalphamaterial_p.h
+++ b/src/extras/defaults/qphongalphamaterial_p.h
@@ -94,13 +94,17 @@ public:
Qt3DRender::QTechnique *m_phongAlphaGL3Technique;
Qt3DRender::QTechnique *m_phongAlphaGL2Technique;
Qt3DRender::QTechnique *m_phongAlphaES2Technique;
+ Qt3DRender::QTechnique *m_phongAlphaRHITechnique;
Qt3DRender::QRenderPass *m_phongAlphaGL3RenderPass;
Qt3DRender::QRenderPass *m_phongAlphaGL2RenderPass;
Qt3DRender::QRenderPass *m_phongAlphaES2RenderPass;
+ Qt3DRender::QRenderPass *m_phongAlphaRHIRenderPass;
Qt3DRender::QShaderProgram *m_phongAlphaGL3Shader;
Qt3DRender::QShaderProgramBuilder *m_phongAlphaGL3ShaderBuilder;
Qt3DRender::QShaderProgram *m_phongAlphaGL2ES2Shader;
Qt3DRender::QShaderProgramBuilder *m_phongAlphaGL2ES2ShaderBuilder;
+ Qt3DRender::QShaderProgram *m_phongAlphaRHIShader;
+ Qt3DRender::QShaderProgramBuilder *m_phongAlphaRHIShaderBuilder;
Qt3DRender::QNoDepthMask *m_noDepthMask;
Qt3DRender::QBlendEquationArguments *m_blendState;
Qt3DRender::QBlendEquation *m_blendEquation;
diff --git a/src/extras/defaults/qphongmaterial.cpp b/src/extras/defaults/qphongmaterial.cpp
index 6318ea140..460bc9bb2 100644
--- a/src/extras/defaults/qphongmaterial.cpp
+++ b/src/extras/defaults/qphongmaterial.cpp
@@ -70,13 +70,17 @@ QPhongMaterialPrivate::QPhongMaterialPrivate()
, m_phongGL3Technique(new QTechnique())
, m_phongGL2Technique(new QTechnique())
, m_phongES2Technique(new QTechnique())
+ , m_phongRHITechnique(new QTechnique())
, m_phongGL3RenderPass(new QRenderPass())
, m_phongGL2RenderPass(new QRenderPass())
, m_phongES2RenderPass(new QRenderPass())
+ , m_phongRHIRenderPass(new QRenderPass())
, m_phongGL3Shader(new QShaderProgram())
, m_phongGL3ShaderBuilder(new QShaderProgramBuilder())
, m_phongGL2ES2Shader(new QShaderProgram())
, m_phongGL2ES2ShaderBuilder(new QShaderProgramBuilder())
+ , m_phongRHIShader(new QShaderProgram())
+ , m_phongRHIShaderBuilder(new QShaderProgramBuilder())
, m_filterKey(new QFilterKey)
{
}
@@ -110,6 +114,14 @@ void QPhongMaterialPrivate::init()
QStringLiteral("specular"),
QStringLiteral("normal")});
+ m_phongRHIShader->setVertexShaderCode(QShaderProgram::loadSource(QUrl(QStringLiteral("qrc:/shaders/rhi/default.vert"))));
+ m_phongRHIShaderBuilder->setParent(q);
+ m_phongRHIShaderBuilder->setShaderProgram(m_phongRHIShader);
+ m_phongRHIShaderBuilder->setFragmentShaderGraph(QUrl(QStringLiteral("qrc:/shaders/graphs/phong.frag.json")));
+ m_phongRHIShaderBuilder->setEnabledLayers({QStringLiteral("diffuse"),
+ QStringLiteral("specular"),
+ QStringLiteral("normal")});
+
m_phongGL3Technique->graphicsApiFilter()->setApi(QGraphicsApiFilter::OpenGL);
m_phongGL3Technique->graphicsApiFilter()->setMajorVersion(3);
m_phongGL3Technique->graphicsApiFilter()->setMinorVersion(1);
@@ -125,13 +137,19 @@ void QPhongMaterialPrivate::init()
m_phongES2Technique->graphicsApiFilter()->setMinorVersion(0);
m_phongES2Technique->graphicsApiFilter()->setProfile(QGraphicsApiFilter::NoProfile);
+ m_phongRHITechnique->graphicsApiFilter()->setApi(QGraphicsApiFilter::RHI);
+ m_phongRHITechnique->graphicsApiFilter()->setMajorVersion(1);
+ m_phongRHITechnique->graphicsApiFilter()->setMinorVersion(0);
+
m_phongGL3RenderPass->setShaderProgram(m_phongGL3Shader);
m_phongGL2RenderPass->setShaderProgram(m_phongGL2ES2Shader);
m_phongES2RenderPass->setShaderProgram(m_phongGL2ES2Shader);
+ m_phongRHIRenderPass->setShaderProgram(m_phongRHIShader);
m_phongGL3Technique->addRenderPass(m_phongGL3RenderPass);
m_phongGL2Technique->addRenderPass(m_phongGL2RenderPass);
m_phongES2Technique->addRenderPass(m_phongES2RenderPass);
+ m_phongRHITechnique->addRenderPass(m_phongRHIRenderPass);
m_filterKey->setParent(q);
m_filterKey->setName(QStringLiteral("renderingStyle"));
@@ -140,10 +158,12 @@ void QPhongMaterialPrivate::init()
m_phongGL3Technique->addFilterKey(m_filterKey);
m_phongGL2Technique->addFilterKey(m_filterKey);
m_phongES2Technique->addFilterKey(m_filterKey);
+ m_phongRHITechnique->addFilterKey(m_filterKey);
m_phongEffect->addTechnique(m_phongGL3Technique);
m_phongEffect->addTechnique(m_phongGL2Technique);
m_phongEffect->addTechnique(m_phongES2Technique);
+ m_phongEffect->addTechnique(m_phongRHITechnique);
m_phongEffect->addParameter(m_ambientParameter);
m_phongEffect->addParameter(m_diffuseParameter);
diff --git a/src/extras/defaults/qphongmaterial_p.h b/src/extras/defaults/qphongmaterial_p.h
index f1e55242b..2d8ae1be2 100644
--- a/src/extras/defaults/qphongmaterial_p.h
+++ b/src/extras/defaults/qphongmaterial_p.h
@@ -91,13 +91,17 @@ public:
Qt3DRender::QTechnique *m_phongGL3Technique;
Qt3DRender::QTechnique *m_phongGL2Technique;
Qt3DRender::QTechnique *m_phongES2Technique;
+ Qt3DRender::QTechnique *m_phongRHITechnique;
Qt3DRender::QRenderPass *m_phongGL3RenderPass;
Qt3DRender::QRenderPass *m_phongGL2RenderPass;
Qt3DRender::QRenderPass *m_phongES2RenderPass;
+ Qt3DRender::QRenderPass *m_phongRHIRenderPass;
Qt3DRender::QShaderProgram *m_phongGL3Shader;
Qt3DRender::QShaderProgramBuilder *m_phongGL3ShaderBuilder;
Qt3DRender::QShaderProgram *m_phongGL2ES2Shader;
Qt3DRender::QShaderProgramBuilder *m_phongGL2ES2ShaderBuilder;
+ Qt3DRender::QShaderProgram *m_phongRHIShader;
+ Qt3DRender::QShaderProgramBuilder *m_phongRHIShaderBuilder;
Qt3DRender::QFilterKey *m_filterKey;
Q_DECLARE_PUBLIC(QPhongMaterial)
diff --git a/src/extras/defaults/qt3dwindow.cpp b/src/extras/defaults/qt3dwindow.cpp
index 8af3ebbc9..8fcc77a29 100644
--- a/src/extras/defaults/qt3dwindow.cpp
+++ b/src/extras/defaults/qt3dwindow.cpp
@@ -61,11 +61,16 @@
#include <Qt3DInput/qinputsettings.h>
#include <Qt3DLogic/qlogicaspect.h>
#include <Qt3DRender/qcamera.h>
+#include <Qt3DRender/private/vulkaninstance_p.h>
#include <qopenglcontext.h>
#include <private/qrendersettings_p.h>
#include <QEvent>
+#if QT_CONFIG(vulkan)
+#include <QVulkanInstance>
+#endif
+
static void initResources()
{
#ifdef QT_STATIC
@@ -92,7 +97,7 @@ Qt3DWindowPrivate::Qt3DWindowPrivate()
{
}
-Qt3DWindow::Qt3DWindow(QScreen *screen)
+Qt3DWindow::Qt3DWindow(QScreen *screen, Qt3DRender::API api)
: QWindow(*new Qt3DWindowPrivate(), nullptr)
{
Q_D(Qt3DWindow);
@@ -102,25 +107,9 @@ Qt3DWindow::Qt3DWindow(QScreen *screen)
if (!d->parentWindow)
d->connectToScreen(screen ? screen : d->topLevelScreen.data());
- setSurfaceType(QSurface::OpenGLSurface);
+ setupWindowSurface(this, api);
resize(1024, 768);
-
- QSurfaceFormat format = QSurfaceFormat::defaultFormat();
-#ifdef QT_OPENGL_ES_2
- format.setRenderableType(QSurfaceFormat::OpenGLES);
-#else
- if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL) {
- format.setVersion(4, 3);
- format.setProfile(QSurfaceFormat::CoreProfile);
- }
-#endif
- format.setDepthBufferSize(24);
- format.setSamples(4);
- format.setStencilBufferSize(8);
- setFormat(format);
- QSurfaceFormat::setDefaultFormat(format);
-
d->m_aspectEngine->registerAspect(new Qt3DCore::QCoreAspect);
d->m_aspectEngine->registerAspect(d->m_renderAspect);
d->m_aspectEngine->registerAspect(d->m_inputAspect);
@@ -229,7 +218,6 @@ void Qt3DWindow::showEvent(QShowEvent *e)
d->m_initialized = true;
}
-
QWindow::showEvent(e);
}
@@ -259,6 +247,77 @@ bool Qt3DWindow::event(QEvent *e)
return QWindow::event(e);
}
+void setupWindowSurface(QWindow *window, Qt3DRender::API api) noexcept
+{
+ // If the user pass an API through the environment, we use that over the one passed as argument.
+ const auto userRequestedApi = qgetenv("QT3D_RHI_DEFAULT_API").toLower();
+ if (!userRequestedApi.isEmpty()) {
+ if (userRequestedApi == QByteArrayLiteral("opengl")) {
+ api = Qt3DRender::API::OpenGL;
+ } else if (userRequestedApi == QByteArrayLiteral("vulkan")) {
+ api = Qt3DRender::API::Vulkan;
+ } else if (userRequestedApi == QByteArrayLiteral("metal")) {
+ api = Qt3DRender::API::Metal;
+ } else if (userRequestedApi == QByteArrayLiteral("d3d11")) {
+ api = Qt3DRender::API::DirectX;
+ } else if (userRequestedApi == QByteArrayLiteral("null")) {
+ api = Qt3DRender::API::Null;
+ }
+ }
+
+ // We have to set the environment so that the backend is able to read it.
+ // Qt6: FIXME
+ switch (api)
+ {
+ case Qt3DRender::API::OpenGL:
+ qputenv("QT3D_RHI_DEFAULT_API", "opengl");
+ window->setSurfaceType(QSurface::OpenGLSurface);
+ break;
+ case Qt3DRender::API::DirectX:
+ qputenv("QT3D_RHI_DEFAULT_API", "d3d11");
+ window->setSurfaceType(QSurface::OpenGLSurface);
+ break;
+ case Qt3DRender::API::Null:
+ qputenv("QT3D_RHI_DEFAULT_API", "null");
+ window->setSurfaceType(QSurface::OpenGLSurface);
+ break;
+ case Qt3DRender::API::Metal:
+ qputenv("QT3D_RHI_DEFAULT_API", "metal");
+ window->setSurfaceType(QSurface::MetalSurface);
+ break;
+#if QT_CONFIG(vulkan)
+ case Qt3DRender::API::Vulkan:
+ {
+ qputenv("QT3D_RHI_DEFAULT_API", "vulkan");
+ window->setSurfaceType(QSurface::VulkanSurface);
+ window->setVulkanInstance(&Qt3DRender::staticVulkanInstance());
+ break;
+ }
+#endif
+ default:
+ break;
+ }
+ QSurfaceFormat format = QSurfaceFormat::defaultFormat();
+#ifdef QT_OPENGL_ES_2
+ format.setRenderableType(QSurfaceFormat::OpenGLES);
+#else
+ if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL) {
+ format.setVersion(4, 3);
+ format.setProfile(QSurfaceFormat::CoreProfile);
+ }
+#endif
+ if (!userRequestedApi.isEmpty()) {
+ // This is used for RHI
+ format.setVersion(1, 0);
+ }
+
+ format.setDepthBufferSize(24);
+ format.setSamples(4);
+ format.setStencilBufferSize(8);
+ window->setFormat(format);
+ QSurfaceFormat::setDefaultFormat(format);
+}
+
} // Qt3DExtras
QT_END_NAMESPACE
diff --git a/src/extras/defaults/qt3dwindow.h b/src/extras/defaults/qt3dwindow.h
index bf4f44ff0..c982746a8 100644
--- a/src/extras/defaults/qt3dwindow.h
+++ b/src/extras/defaults/qt3dwindow.h
@@ -52,6 +52,7 @@
#define QT3DWINDOW_H
#include <Qt3DExtras/qt3dextras_global.h>
+#include <Qt3DRender/qrenderapi.h>
#include <QtGui/QWindow>
QT_BEGIN_NAMESPACE
@@ -90,7 +91,7 @@ class Q_3DEXTRASSHARED_EXPORT Qt3DWindow : public QWindow
{
Q_OBJECT
public:
- Qt3DWindow(QScreen *screen = nullptr);
+ Qt3DWindow(QScreen *screen = nullptr, Qt3DRender::API = Qt3DRender::API::OpenGL);
~Qt3DWindow();
void registerAspect(Qt3DCore::QAbstractAspect *aspect);
@@ -118,6 +119,9 @@ private:
Q_DECLARE_PRIVATE(Qt3DWindow)
};
+Q_3DEXTRASSHARED_EXPORT
+void setupWindowSurface(QWindow* window, Qt3DRender::API) noexcept;
+
} // Qt3DExtras
QT_END_NAMESPACE
diff --git a/src/extras/extras.qrc b/src/extras/extras.qrc
index 2aedc6622..e31a6164b 100644
--- a/src/extras/extras.qrc
+++ b/src/extras/extras.qrc
@@ -39,5 +39,23 @@
<file>shaders/es2/distancefieldtext.vert</file>
<file>shaders/gl3/morphphong.vert</file>
<file>shaders/es2/morphphong.vert</file>
+ <file>shaders/graphs/phong.graph</file>
+ <file>shaders/rhi/unlittexture.vert</file>
+ <file>shaders/rhi/unlittexture.frag</file>
+ <file>shaders/rhi/skybox.vert</file>
+ <file>shaders/rhi/skybox.frag</file>
+ <file>shaders/rhi/phong.inc.frag</file>
+ <file>shaders/rhi/pervertexcolor.vert</file>
+ <file>shaders/rhi/pervertexcolor.frag</file>
+ <file>shaders/rhi/morphphong.vert</file>
+ <file>shaders/rhi/metalrough.inc.frag</file>
+ <file>shaders/rhi/light.inc.frag</file>
+ <file>shaders/rhi/gooch.vert</file>
+ <file>shaders/rhi/gooch.frag</file>
+ <file>shaders/rhi/distancefieldtext.vert</file>
+ <file>shaders/rhi/distancefieldtext.frag</file>
+ <file>shaders/rhi/default.vert</file>
+ <file>shaders/rhi/coordinatesystems.inc</file>
+ <file>shaders/rhi/defaultuniforms.inc</file>
</qresource>
</RCC>
diff --git a/src/extras/shaders/es2/phong.inc.frag100 b/src/extras/shaders/es2/phong.inc.frag100
index 0c326d0b6..c68c8e41a 100644
--- a/src/extras/shaders/es2/phong.inc.frag100
+++ b/src/extras/shaders/es2/phong.inc.frag100
@@ -66,8 +66,8 @@ void adsModel(const in FP vec3 vpos, const in FP vec3 vnormal, const in FP vec3
if ( lights[0].type != TYPE_DIRECTIONAL ) {
s = lights[0].position - vpos;
if (lights[0].constantAttenuation != 0.0
- || light[0].linearAttenuation != 0.0
- || light[0].quadraticAttenuation != 0.0) {
+ || lights[0].linearAttenuation != 0.0
+ || lights[0].quadraticAttenuation != 0.0) {
FP float dist = length(s);
att = 1.0 / (lights[0].constantAttenuation + lights[0].linearAttenuation * dist + lights[0].quadraticAttenuation * dist * dist);
}
@@ -99,8 +99,8 @@ void adsModel(const in FP vec3 vpos, const in FP vec3 vnormal, const in FP vec3
if ( lights[1].type != TYPE_DIRECTIONAL ) {
s = lights[1].position - vpos;
if (lights[1].constantAttenuation != 0.0
- || light[1].linearAttenuation != 0.0
- || light[1].quadraticAttenuation != 0.0) {
+ || lights[1].linearAttenuation != 0.0
+ || lights[1].quadraticAttenuation != 0.0) {
FP float dist = length(s);
att = 1.0 / (lights[1].constantAttenuation + lights[1].linearAttenuation * dist + lights[1].quadraticAttenuation * dist * dist);
}
diff --git a/src/extras/shaders/es3/light.inc.frag b/src/extras/shaders/es3/light.inc.frag
index 18012ccc1..7b6bf3bc5 100644
--- a/src/extras/shaders/es3/light.inc.frag
+++ b/src/extras/shaders/es3/light.inc.frag
@@ -20,6 +20,7 @@ uniform int lightCount;
struct EnvironmentLight {
highp samplerCube irradiance; // For diffuse contribution
highp samplerCube specular; // For specular contribution
+ int specularMipLevels;
};
uniform EnvironmentLight envLight;
uniform int envLightCount;
diff --git a/src/extras/shaders/es3/metalrough.inc.frag b/src/extras/shaders/es3/metalrough.inc.frag
index 188a367f5..fc014b81b 100644
--- a/src/extras/shaders/es3/metalrough.inc.frag
+++ b/src/extras/shaders/es3/metalrough.inc.frag
@@ -59,13 +59,6 @@ const FP float gamma = 2.2;
#pragma include light.inc.frag
-int mipLevelCount(const in FP samplerCube cube)
-{
- int baseSize = textureSize(cube, 0).x;
- int nMips = int(log2(float(baseSize > 0 ? baseSize : 1))) + 1;
- return nMips;
-}
-
FP float remapRoughness(const in FP float roughness)
{
// As per page 14 of
@@ -91,10 +84,8 @@ FP float alphaToMipLevel(FP float alpha)
const FP float k1 = 0.9921;
FP float glossiness = (pow(2.0, -10.0 / sqrt(specPower)) - k0) / k1;
- // TODO: Optimize by doing this on CPU and set as
- // uniform int envLight.specularMipLevels say (if present in shader).
// Lookup the number of mips in the specular envmap
- int mipLevels = mipLevelCount(envLight.specular);
+ int mipLevels = envLight.specularMipLevels;
// Offset of smallest miplevel we should use (corresponds to specular
// power of 1). I.e. in the 32x32 sized mip.
diff --git a/src/extras/shaders/gl3/light.inc.frag b/src/extras/shaders/gl3/light.inc.frag
index 0b642638f..a1b07a976 100644
--- a/src/extras/shaders/gl3/light.inc.frag
+++ b/src/extras/shaders/gl3/light.inc.frag
@@ -20,6 +20,7 @@ uniform int lightCount;
struct EnvironmentLight {
samplerCube irradiance; // For diffuse contribution
samplerCube specular; // For specular contribution
+ int specularMipLevels;
};
uniform EnvironmentLight envLight;
uniform int envLightCount = 0;
diff --git a/src/extras/shaders/gl3/metalrough.inc.frag b/src/extras/shaders/gl3/metalrough.inc.frag
index f7e3eecb7..f5fb81f51 100644
--- a/src/extras/shaders/gl3/metalrough.inc.frag
+++ b/src/extras/shaders/gl3/metalrough.inc.frag
@@ -55,13 +55,6 @@ uniform float gamma = 2.2;
#pragma include light.inc.frag
-int mipLevelCount(const in samplerCube cube)
-{
- int baseSize = textureSize(cube, 0).x;
- int nMips = int(log2(float(baseSize > 0 ? baseSize : 1))) + 1;
- return nMips;
-}
-
float remapRoughness(const in float roughness)
{
// As per page 14 of
@@ -87,10 +80,8 @@ float alphaToMipLevel(float alpha)
const float k1 = 0.9921;
float glossiness = (pow(2.0, -10.0 / sqrt(specPower)) - k0) / k1;
- // TODO: Optimize by doing this on CPU and set as
- // uniform int envLight.specularMipLevels say (if present in shader).
// Lookup the number of mips in the specular envmap
- int mipLevels = mipLevelCount(envLight.specular);
+ int mipLevels = envLight.specularMipLevels;
// Offset of smallest miplevel we should use (corresponds to specular
// power of 1). I.e. in the 32x32 sized mip.
diff --git a/src/extras/shaders/graphs/phong.graph b/src/extras/shaders/graphs/phong.graph
new file mode 100644
index 000000000..dedeb1067
--- /dev/null
+++ b/src/extras/shaders/graphs/phong.graph
@@ -0,0 +1,466 @@
+{
+ "nodes": [
+ {
+ "uuid": "{00000000-0000-0000-0000-000000000001}",
+ "type": "input",
+ "parameters": {
+ "name": "worldPosition",
+ "qualifier": {
+ "type": "QShaderLanguage::StorageQualifier",
+ "value": "QShaderLanguage::Input"
+ },
+ "type": {
+ "type": "QShaderLanguage::VariableType",
+ "value": "QShaderLanguage::Vec3"
+ }
+ }
+ },
+ {
+ "uuid": "{00000000-0000-0000-0000-000000000002}",
+ "type": "eyePosition"
+ },
+ {
+ "uuid": "{00000000-0000-0000-0000-000000000003}",
+ "type": "input",
+ "parameters": {
+ "name": "worldNormal",
+ "qualifier": {
+ "type": "QShaderLanguage::StorageQualifier",
+ "value": "QShaderLanguage::Input"
+ },
+ "type": {
+ "type": "QShaderLanguage::VariableType",
+ "value": "QShaderLanguage::Vec3"
+ }
+ }
+ },
+ {
+ "uuid": "{00000000-0000-0000-0000-000000000004}",
+ "type": "input",
+ "layers": ["normalTexture"],
+ "parameters": {
+ "name": "worldTangent",
+ "qualifier": {
+ "type": "QShaderLanguage::StorageQualifier",
+ "value": "QShaderLanguage::Input"
+ },
+ "type": {
+ "type": "QShaderLanguage::VariableType",
+ "value": "QShaderLanguage::Vec4"
+ }
+ }
+ },
+ {
+ "uuid": "{00000000-0000-0000-0000-000000000005}",
+ "type": "input",
+ "layers": ["diffuseTexture", "specularTexture", "normalTexture"],
+ "parameters": {
+ "name": "texCoord",
+ "qualifier": {
+ "type": "QShaderLanguage::StorageQualifier",
+ "value": "QShaderLanguage::Input"
+ },
+ "type": {
+ "type": "QShaderLanguage::VariableType",
+ "value": "QShaderLanguage::Vec2"
+ }
+ }
+ },
+ {
+ "uuid": "{00000000-0000-0000-0000-000000000006}",
+ "type": "input",
+ "parameters": {
+ "name": "ka",
+ "qualifier": {
+ "type": "QShaderLanguage::StorageQualifier",
+ "value": "QShaderLanguage::Uniform"
+ },
+ "type": {
+ "type": "QShaderLanguage::VariableType",
+ "value": "QShaderLanguage::Vec4"
+ }
+ }
+ },
+ {
+ "uuid": "{00000000-0000-0000-0000-000000000007}",
+ "type": "input",
+ "layers": ["diffuse"],
+ "parameters": {
+ "name": "kd",
+ "qualifier": {
+ "type": "QShaderLanguage::StorageQualifier",
+ "value": "QShaderLanguage::Uniform"
+ },
+ "type": {
+ "type": "QShaderLanguage::VariableType",
+ "value": "QShaderLanguage::Vec4"
+ }
+ }
+ },
+ {
+ "uuid": "{00000000-0000-0000-0000-000000000008}",
+ "type": "sampleTexture",
+ "layers": ["diffuseTexture"],
+ "parameters": {
+ "name": "diffuseTexture"
+ }
+ },
+ {
+ "uuid": "{00000000-0000-0000-0000-000000000009}",
+ "type": "input",
+ "layers": ["specular"],
+ "parameters": {
+ "name": "ks",
+ "qualifier": {
+ "type": "QShaderLanguage::StorageQualifier",
+ "value": "QShaderLanguage::Uniform"
+ },
+ "type": {
+ "type": "QShaderLanguage::VariableType",
+ "value": "QShaderLanguage::Vec4"
+ }
+ }
+ },
+ {
+ "uuid": "{00000000-0000-0000-0000-000000000010}",
+ "layers": ["specularTexture"],
+ "type": "sampleTexture",
+ "parameters": {
+ "name": "specularTexture"
+ }
+ },
+ {
+ "uuid": "{00000000-0000-0000-0000-000000000011}",
+ "type": "input",
+ "parameters": {
+ "name": "shininess",
+ "qualifier": {
+ "type": "QShaderLanguage::StorageQualifier",
+ "value": "QShaderLanguage::Uniform"
+ },
+ "type": {
+ "type": "QShaderLanguage::VariableType",
+ "value": "QShaderLanguage::Float"
+ }
+ }
+ },
+ {
+ "uuid": "{00000000-0000-0000-0000-000000000012}",
+ "type": "subtract",
+ "parameters": {
+ "type": {
+ "type": "QShaderLanguage::VariableType",
+ "value": "QShaderLanguage::Vec3"
+ }
+ }
+ },
+ {
+ "uuid": "{00000000-0000-0000-0000-000000000013}",
+ "type": "normalize",
+ "parameters": {
+ "type": {
+ "type": "QShaderLanguage::VariableType",
+ "value": "QShaderLanguage::Vec3"
+ }
+ }
+ },
+ {
+ "uuid": "{00000000-0000-0000-0000-000000000014}",
+ "type": "normalize",
+ "parameters": {
+ "type": {
+ "type": "QShaderLanguage::VariableType",
+ "value": "QShaderLanguage::Vec3"
+ }
+ }
+ },
+ {
+ "uuid": "{00000000-0000-0000-0000-000000000015}",
+ "type": "worldSpaceToTangentSpaceMatrix",
+ "layers": ["normalTexture"]
+ },
+ {
+ "uuid": "{00000000-0000-0000-0000-000000000016}",
+ "type": "transpose",
+ "layers": ["normalTexture"],
+ "parameters": {
+ "type": {
+ "type": "QShaderLanguage::VariableType",
+ "value": "QShaderLanguage::Mat3"
+ }
+ }
+ },
+ {
+ "uuid": "{00000000-0000-0000-0000-000000000017}",
+ "type": "sampleTexture",
+ "layers": ["normalTexture"],
+ "parameters": {
+ "name": "normalTexture"
+ }
+ },
+ {
+ "uuid": "{00000000-0000-0000-0000-000000000018}",
+ "type": "swizzle",
+ "layers": ["normalTexture"],
+ "parameters": {
+ "fields": "rgb",
+ "type": {
+ "type": "QShaderLanguage::VariableType",
+ "value": "QShaderLanguage::Vec3"
+ }
+ }
+ },
+ {
+ "uuid": "{00000000-0000-0000-0000-000000000019}",
+ "type": "constant",
+ "layers": ["normalTexture"],
+ "parameters": {
+ "constant": "2.0",
+ "type": {
+ "type": "QShaderLanguage::VariableType",
+ "value": "QShaderLanguage::Float"
+ }
+ }
+ },
+ {
+ "uuid": "{00000000-0000-0000-0000-000000000020}",
+ "type": "multiply",
+ "layers": ["normalTexture"],
+ "parameters": {
+ "type": {
+ "type": "QShaderLanguage::VariableType",
+ "value": "QShaderLanguage::Vec3"
+ }
+ }
+ },
+ {
+ "uuid": "{00000000-0000-0000-0000-000000000021}",
+ "type": "constant",
+ "layers": ["normalTexture"],
+ "parameters": {
+ "constant": "1.0",
+ "type": {
+ "type": "QShaderLanguage::VariableType",
+ "value": "QShaderLanguage::Vec3"
+ }
+ }
+ },
+ {
+ "uuid": "{00000000-0000-0000-0000-000000000022}",
+ "type": "subtract",
+ "parameters": {
+ "type": {
+ "type": "QShaderLanguage::VariableType",
+ "value": "QShaderLanguage::Vec3"
+ }
+ }
+ },
+ {
+ "uuid": "{00000000-0000-0000-0000-000000000023}",
+ "type": "multiply",
+ "parameters": {
+ "type": {
+ "type": "QShaderLanguage::VariableType",
+ "value": "QShaderLanguage::Vec3"
+ }
+ }
+ },
+ {
+ "uuid": "{00000000-0000-0000-0000-000000000024}",
+ "type": "phongFunction"
+ },
+ {
+ "uuid": "{00000000-0000-0000-0000-000000000025}",
+ "type": "fragColor"
+ }
+ ],
+ "edges": [
+ {
+ "sourceUuid": "{00000000-0000-0000-0000-000000000001}",
+ "sourcePort": "value",
+ "targetUuid": "{00000000-0000-0000-0000-000000000024}",
+ "targetPort": "worldPosition"
+ },
+ {
+ "sourceUuid": "{00000000-0000-0000-0000-000000000001}",
+ "sourcePort": "value",
+ "targetUuid": "{00000000-0000-0000-0000-000000000012}",
+ "targetPort": "subtrahend"
+ },
+ {
+ "sourceUuid": "{00000000-0000-0000-0000-000000000002}",
+ "sourcePort": "eyePosition",
+ "targetUuid": "{00000000-0000-0000-0000-000000000012}",
+ "targetPort": "minuend"
+ },
+ {
+ "sourceUuid": "{00000000-0000-0000-0000-000000000012}",
+ "sourcePort": "difference",
+ "targetUuid": "{00000000-0000-0000-0000-000000000013}",
+ "targetPort": "input"
+ },
+ {
+ "sourceUuid": "{00000000-0000-0000-0000-000000000013}",
+ "sourcePort": "output",
+ "targetUuid": "{00000000-0000-0000-0000-000000000024}",
+ "targetPort": "worldView"
+ },
+ {
+ "sourceUuid": "{00000000-0000-0000-0000-000000000003}",
+ "sourcePort": "value",
+ "targetUuid": "{00000000-0000-0000-0000-000000000014}",
+ "targetPort": "input",
+ "layers": ["normal"]
+ },
+ {
+ "sourceUuid": "{00000000-0000-0000-0000-000000000003}",
+ "sourcePort": "value",
+ "targetUuid": "{00000000-0000-0000-0000-000000000015}",
+ "targetPort": "worldNormal",
+ "layers": ["normalTexture"]
+ },
+ {
+ "sourceUuid": "{00000000-0000-0000-0000-000000000004}",
+ "sourcePort": "value",
+ "targetUuid": "{00000000-0000-0000-0000-000000000015}",
+ "targetPort": "worldTangent",
+ "layers": ["normalTexture"]
+ },
+ {
+ "sourceUuid": "{00000000-0000-0000-0000-000000000015}",
+ "sourcePort": "matrix",
+ "targetUuid": "{00000000-0000-0000-0000-000000000016}",
+ "targetPort": "input",
+ "layers": ["normalTexture"]
+ },
+ {
+ "sourceUuid": "{00000000-0000-0000-0000-000000000016}",
+ "sourcePort": "output",
+ "targetUuid": "{00000000-0000-0000-0000-000000000023}",
+ "targetPort": "first",
+ "layers": ["normalTexture"]
+ },
+ {
+ "sourceUuid": "{00000000-0000-0000-0000-000000000005}",
+ "sourcePort": "value",
+ "targetUuid": "{00000000-0000-0000-0000-000000000017}",
+ "targetPort": "coord",
+ "layers": ["normalTexture"]
+ },
+ {
+ "sourceUuid": "{00000000-0000-0000-0000-000000000017}",
+ "sourcePort": "color",
+ "targetUuid": "{00000000-0000-0000-0000-000000000018}",
+ "targetPort": "input",
+ "layers": ["normalTexture"]
+ },
+ {
+ "sourceUuid": "{00000000-0000-0000-0000-000000000018}",
+ "sourcePort": "output",
+ "targetUuid": "{00000000-0000-0000-0000-000000000020}",
+ "targetPort": "first",
+ "layers": ["normalTexture"]
+ },
+ {
+ "sourceUuid": "{00000000-0000-0000-0000-000000000019}",
+ "sourcePort": "value",
+ "targetUuid": "{00000000-0000-0000-0000-000000000020}",
+ "targetPort": "second",
+ "layers": ["normalTexture"]
+ },
+ {
+ "sourceUuid": "{00000000-0000-0000-0000-000000000020}",
+ "sourcePort": "product",
+ "targetUuid": "{00000000-0000-0000-0000-000000000022}",
+ "targetPort": "minuend",
+ "layers": ["normalTexture"]
+ },
+ {
+ "sourceUuid": "{00000000-0000-0000-0000-000000000021}",
+ "sourcePort": "value",
+ "targetUuid": "{00000000-0000-0000-0000-000000000022}",
+ "targetPort": "subtrahend",
+ "layers": ["normalTexture"]
+ },
+ {
+ "sourceUuid": "{00000000-0000-0000-0000-000000000022}",
+ "sourcePort": "difference",
+ "targetUuid": "{00000000-0000-0000-0000-000000000023}",
+ "targetPort": "second",
+ "layers": ["normalTexture"]
+ },
+ {
+ "sourceUuid": "{00000000-0000-0000-0000-000000000023}",
+ "sourcePort": "product",
+ "targetUuid": "{00000000-0000-0000-0000-000000000014}",
+ "targetPort": "input",
+ "layers": ["normalTexture"]
+ },
+ {
+ "sourceUuid": "{00000000-0000-0000-0000-000000000014}",
+ "sourcePort": "output",
+ "targetUuid": "{00000000-0000-0000-0000-000000000024}",
+ "targetPort": "worldNormal"
+ },
+ {
+ "sourceUuid": "{00000000-0000-0000-0000-000000000006}",
+ "sourcePort": "value",
+ "targetUuid": "{00000000-0000-0000-0000-000000000024}",
+ "targetPort": "ambient"
+ },
+ {
+ "sourceUuid": "{00000000-0000-0000-0000-000000000007}",
+ "sourcePort": "value",
+ "targetUuid": "{00000000-0000-0000-0000-000000000024}",
+ "targetPort": "diffuse",
+ "layers": ["diffuse"]
+ },
+ {
+ "sourceUuid": "{00000000-0000-0000-0000-000000000005}",
+ "sourcePort": "value",
+ "targetUuid": "{00000000-0000-0000-0000-000000000008}",
+ "targetPort": "coord",
+ "layers": ["diffuseTexture"]
+ },
+ {
+ "sourceUuid": "{00000000-0000-0000-0000-000000000008}",
+ "sourcePort": "color",
+ "targetUuid": "{00000000-0000-0000-0000-000000000024}",
+ "targetPort": "diffuse",
+ "layers": ["diffuseTexture"]
+ },
+ {
+ "sourceUuid": "{00000000-0000-0000-0000-000000000009}",
+ "sourcePort": "value",
+ "targetUuid": "{00000000-0000-0000-0000-000000000024}",
+ "targetPort": "specular",
+ "layers": ["specular"]
+ },
+ {
+ "sourceUuid": "{00000000-0000-0000-0000-000000000005}",
+ "sourcePort": "value",
+ "targetUuid": "{00000000-0000-0000-0000-000000000010}",
+ "targetPort": "coord",
+ "layers": ["specularTexture"]
+ },
+ {
+ "sourceUuid": "{00000000-0000-0000-0000-000000000010}",
+ "sourcePort": "color",
+ "targetUuid": "{00000000-0000-0000-0000-000000000024}",
+ "targetPort": "specular",
+ "layers": ["specularTexture"]
+ },
+ {
+ "sourceUuid": "{00000000-0000-0000-0000-000000000011}",
+ "sourcePort": "value",
+ "targetUuid": "{00000000-0000-0000-0000-000000000024}",
+ "targetPort": "shininess"
+ },
+ {
+ "sourceUuid": "{00000000-0000-0000-0000-000000000024}",
+ "sourcePort": "outputColor",
+ "targetUuid": "{00000000-0000-0000-0000-000000000025}",
+ "targetPort": "fragColor"
+ }
+ ]
+}
diff --git a/src/extras/shaders/rhi/coordinatesystems.inc b/src/extras/shaders/rhi/coordinatesystems.inc
new file mode 100644
index 000000000..ed3d2cb92
--- /dev/null
+++ b/src/extras/shaders/rhi/coordinatesystems.inc
@@ -0,0 +1,70 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+mat3 calcWorldSpaceToTangentSpaceMatrix(const in vec3 wNormal, const in vec4 wTangent)
+{
+ // Make the tangent truly orthogonal to the normal by using Gram-Schmidt.
+ // This allows to build the tangentMatrix below by simply transposing the
+ // tangent -> eyespace matrix (which would now be orthogonal)
+ vec3 wFixedTangent = normalize(wTangent.xyz - dot(wTangent.xyz, wNormal) * wNormal);
+
+ // Calculate binormal vector. No "real" need to renormalize it,
+ // as built by crossing two normal vectors.
+ // To orient the binormal correctly, use the fourth coordinate of the tangent,
+ // which is +1 for a right hand system, and -1 for a left hand system.
+ vec3 wBinormal = cross(wNormal, wFixedTangent.xyz) * wTangent.w;
+
+ // Construct matrix to transform from world space to tangent space
+ // This is the transpose of the tangentToWorld transformation matrix
+ mat3 tangentToWorldMatrix = mat3(wFixedTangent, wBinormal, wNormal);
+ mat3 worldToTangentMatrix = transpose(tangentToWorldMatrix);
+ return worldToTangentMatrix;
+}
+
diff --git a/src/extras/shaders/rhi/default.vert b/src/extras/shaders/rhi/default.vert
new file mode 100644
index 000000000..5679d8681
--- /dev/null
+++ b/src/extras/shaders/rhi/default.vert
@@ -0,0 +1,107 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#version 450
+
+layout(location = 0) in vec3 vertexPosition;
+layout(location = 1) in vec3 vertexNormal;
+layout(location = 2) in vec4 vertexTangent;
+layout(location = 3) in vec2 vertexTexCoord;
+
+layout(location = 0) out vec3 worldPosition;
+layout(location = 1) out vec3 worldNormal;
+layout(location = 2) out vec4 worldTangent;
+layout(location = 3) out vec2 texCoord;
+
+layout(std140, binding = 0) uniform qt3d_render_view_uniforms {
+ mat4 viewMatrix;
+ mat4 projectionMatrix;
+ mat4 viewProjectionMatrix;
+ mat4 inverseViewMatrix;
+ mat4 inverseProjectionMatrix;
+ mat4 inverseViewProjectionMatrix;
+ mat4 viewportMatrix;
+ mat4 inverseViewportMatrix;
+ vec4 textureTransformMatrix;
+ vec3 eyePosition;
+ float aspectRatio;
+ float gamma;
+ float exposure;
+ float time;
+};
+
+layout(std140, binding = 1) uniform qt3d_command_uniforms {
+ mat4 modelMatrix;
+ mat4 inverseModelMatrix;
+ mat4 modelViewMatrix;
+ mat3 modelNormalMatrix;
+ mat4 inverseModelViewMatrix;
+ mat4 modelViewProjection;
+ mat4 inverseModelViewProjectionMatrix;
+};
+
+layout(std140, binding = 2) uniform qt3d_extras_uniforms {
+ float texCoordScale;
+};
+
+void main()
+{
+ // Pass through scaled texture coordinates
+ texCoord = vertexTexCoord * texCoordScale;
+
+ // Transform position, normal, and tangent to world space
+ worldPosition = vec3(modelMatrix * vec4(vertexPosition, 1.0));
+ worldNormal = normalize(modelNormalMatrix * vertexNormal);
+ vec3 wt = normalize(vec3(modelMatrix * vec4(vertexTangent.xyz, 0.0)));
+ worldTangent = vec4(wt, vertexTangent.w);
+
+ // Calculate vertex position in clip coordinates
+ gl_Position = modelViewProjection * vec4(vertexPosition, 1.0);
+}
diff --git a/src/extras/shaders/rhi/defaultuniforms.inc b/src/extras/shaders/rhi/defaultuniforms.inc
new file mode 100644
index 000000000..ad2c51d50
--- /dev/null
+++ b/src/extras/shaders/rhi/defaultuniforms.inc
@@ -0,0 +1,30 @@
+layout(std140, binding = auto) uniform qt3d_render_view_uniforms {
+ mat4 viewMatrix;
+ mat4 projectionMatrix;
+ mat4 viewProjectionMatrix;
+ mat4 inverseViewMatrix;
+ mat4 inverseProjectionMatrix;
+ mat4 inverseViewProjectionMatrix;
+ mat4 viewportMatrix;
+ mat4 inverseViewportMatrix;
+ vec4 textureTransformMatrix;
+ vec3 eyePosition;
+ float aspectRatio;
+ float gamma;
+ float exposure;
+ float time;
+};
+
+layout(std140, binding = auto) uniform qt3d_command_uniforms {
+ mat4 modelMatrix;
+ mat4 inverseModelMatrix;
+ mat4 modelViewMatrix;
+ mat3 modelNormalMatrix;
+ mat4 inverseModelViewMatrix;
+ mat4 mvp;
+ mat4 inverseModelViewProjectionMatrix;
+};
+
+layout(std140, binding = auto) uniform qt3d_extras_uniforms {
+ float texCoordScale;
+};
diff --git a/src/extras/shaders/rhi/distancefieldtext.frag b/src/extras/shaders/rhi/distancefieldtext.frag
new file mode 100644
index 000000000..ec42f5056
--- /dev/null
+++ b/src/extras/shaders/rhi/distancefieldtext.frag
@@ -0,0 +1,41 @@
+#version 450
+
+layout(location = 0) in vec2 texCoord;
+layout(location = 1) in float zValue;
+
+layout(location = 0) out vec4 fragColor;
+
+layout(std140, binding = 2) uniform qt3d_custom_uniforms {
+ float minAlpha;
+ float maxAlpha;
+ float textureSize;
+ vec4 color;
+};
+layout(binding = 3) uniform sampler2D distanceFieldTexture;
+
+void main()
+{
+ // determine the scale of the glyph texture within pixel-space coordinates
+ // (that is, how many pixels are drawn for each texel)
+ vec2 texelDeltaX = abs(dFdx(texCoord));
+ vec2 texelDeltaY = abs(dFdy(texCoord));
+ float avgTexelDelta = textureSize * 0.5 * (texelDeltaX.x + texelDeltaX.y + texelDeltaY.x + texelDeltaY.y);
+ float texScale = 1.0 / avgTexelDelta;
+
+ // scaled to interval [0.0, 0.15]
+ float devScaleMin = 0.00;
+ float devScaleMax = 0.15;
+ float scaled = (clamp(texScale, devScaleMin, devScaleMax) - devScaleMin) / (devScaleMax - devScaleMin);
+
+ // thickness of glyphs should increase a lot for very small glyphs to make them readable
+ float base = 0.5;
+ float threshold = base * scaled;
+ float range = 0.06 / texScale;
+
+ float minAlpha = threshold - range;
+ float maxAlpha = threshold + range;
+
+ float distVal = texture(distanceFieldTexture, texCoord).r;
+ fragColor = vec4(color.rgb, color.a * smoothstep(minAlpha, maxAlpha, distVal));
+ gl_FragDepth = gl_FragCoord.z - zValue * 0.000001;
+}
diff --git a/src/extras/shaders/rhi/distancefieldtext.vert b/src/extras/shaders/rhi/distancefieldtext.vert
new file mode 100644
index 000000000..02efc898f
--- /dev/null
+++ b/src/extras/shaders/rhi/distancefieldtext.vert
@@ -0,0 +1,26 @@
+#version 450
+
+layout(location = 0) in vec3 vertexPosition;
+layout(location = 1) in vec2 vertexTexCoord;
+
+layout(location = 0) out vec2 texCoord;
+layout(location = 1) out float zValue;
+
+layout(std140, binding = 1) uniform qt3d_command_uniforms {
+ mat4 modelMatrix;
+ mat4 inverseModelMatrix;
+ mat4 modelViewMatrix;
+ mat3 modelNormalMatrix;
+ mat4 inverseModelViewMatrix;
+ mat4 mvp;
+ mat4 inverseModelViewProjectionMatrix;
+};
+
+void main()
+{
+ texCoord = vertexTexCoord;
+ zValue = vertexPosition.z;
+
+ gl_Position = mvp * vec4(vertexPosition.xy, 0.0, 1.0);
+}
+
diff --git a/src/extras/shaders/rhi/gooch.frag b/src/extras/shaders/rhi/gooch.frag
new file mode 100644
index 000000000..8a0a32f18
--- /dev/null
+++ b/src/extras/shaders/rhi/gooch.frag
@@ -0,0 +1,111 @@
+#version 450
+
+layout(location = 0) in vec3 worldPosition;
+layout(location = 1) in vec3 worldNormal;
+
+layout(location = 0) out vec4 fragColor;
+
+layout(std140, binding = 0) uniform qt3d_render_view_uniforms {
+ mat4 viewMatrix;
+ mat4 projectionMatrix;
+ mat4 viewProjectionMatrix;
+ mat4 inverseViewMatrix;
+ mat4 inverseProjectionMatrix;
+ mat4 inverseViewProjectionMatrix;
+ mat4 viewportMatrix;
+ mat4 inverseViewportMatrix;
+ vec4 textureTransformMatrix;
+ vec3 eyePosition;
+ float aspectRatio;
+ float gamma;
+ float exposure;
+ float time;
+};
+
+layout(std140, binding = 1) uniform qt3d_command_uniforms {
+ mat4 modelMatrix;
+ mat4 inverseModelMatrix;
+ mat4 modelViewMatrix;
+ mat3 modelNormalMatrix;
+ mat4 inverseModelViewMatrix;
+ mat4 mvp;
+ mat4 inverseModelViewProjectionMatrix;
+};
+
+layout(std140, binding = 2) uniform qt3d_custom_uniforms {
+ vec3 kd; // Diffuse reflectivity
+ vec3 ks; // Specular reflectivity
+ vec3 kblue; // Cool color
+ vec3 kyellow; // Warm color
+ float alpha; // Fraction of diffuse added to kblue
+ float beta; // Fraction of diffuse added to kyellow
+ float shininess; // Specular shininess factor
+};
+
+const int MAX_LIGHTS = 8;
+const int TYPE_POINT = 0;
+const int TYPE_DIRECTIONAL = 1;
+const int TYPE_SPOT = 2;
+
+struct Light {
+ int type;
+ vec3 position;
+ vec3 color;
+ float intensity;
+ vec3 direction;
+ float constantAttenuation;
+ float linearAttenuation;
+ float quadraticAttenuation;
+ float cutOffAngle;
+};
+
+layout(std140, binding = 3) uniform qt3d_light_uniforms {
+ uniform Light lights[MAX_LIGHTS];
+ uniform int lightCount;
+ uniform int envLightCount;
+};
+
+
+vec3 goochModel( const in vec3 pos, const in vec3 n )
+{
+ // Based upon the original Gooch lighting model paper at:
+ // http://www.cs.northwestern.edu/~ago820/SIG98/abstract.html
+
+ // Calculate kcool and kwarm from equation (3)
+ vec3 kcool = clamp(kblue + alpha * kd, 0.0, 1.0);
+ vec3 kwarm = clamp(kyellow + beta * kd, 0.0, 1.0);
+
+ vec3 result = vec3(0.0);
+ for (int i = 0; i < lightCount; ++i) {
+ // Calculate the vector from the light to the fragment
+ vec3 s = normalize( vec3( lights[i].position ) - pos );
+
+ // Calculate the cos theta factor mapped onto the range [0,1]
+ float sDotNFactor = ( 1.0 + dot( s, n ) ) / 2.0;
+
+ // Calculate the tone by blending the kcool and kwarm contributions
+ // as per equation (2)
+ vec3 intensity = mix( kcool, kwarm, sDotNFactor );
+
+ // Calculate the vector from the fragment to the eye position
+ vec3 v = normalize( eyePosition - pos );
+
+ // Reflect the light beam using the normal at this fragment
+ vec3 r = reflect( -s, n );
+
+ // Calculate the specular component
+ float specular = 0.0;
+ if ( dot( s, n ) > 0.0 )
+ specular = pow( max( dot( r, v ), 0.0 ), shininess );
+
+ // Sum the blended tone and specular highlight
+ result += intensity + ks * specular;
+ }
+
+ return result;
+}
+
+void main()
+{
+ fragColor = vec4( goochModel( worldPosition, normalize( worldNormal ) ), 1.0 );
+}
diff --git a/src/extras/shaders/rhi/gooch.vert b/src/extras/shaders/rhi/gooch.vert
new file mode 100644
index 000000000..0da69bce5
--- /dev/null
+++ b/src/extras/shaders/rhi/gooch.vert
@@ -0,0 +1,25 @@
+#version 450
+
+layout(location = 0) in vec3 vertexPosition;
+layout(location = 1) in vec3 vertexNormal;
+
+layout(location = 0) out vec3 worldPosition;
+layout(location = 1) out vec3 worldNormal;
+
+layout(std140, binding = 1) uniform qt3d_command_uniforms {
+ mat4 modelMatrix;
+ mat4 inverseModelMatrix;
+ mat4 modelViewMatrix;
+ mat3 modelNormalMatrix;
+ mat4 inverseModelViewMatrix;
+ mat4 mvp;
+ mat4 inverseModelViewProjectionMatrix;
+};
+
+void main()
+{
+ worldNormal = normalize( modelNormalMatrix * vertexNormal );
+ worldPosition = vec3( modelMatrix * vec4( vertexPosition, 1.0 ) );
+
+ gl_Position = mvp * vec4( vertexPosition, 1.0 );
+}
diff --git a/src/extras/shaders/rhi/light.inc.frag b/src/extras/shaders/rhi/light.inc.frag
new file mode 100644
index 000000000..8079ae8a3
--- /dev/null
+++ b/src/extras/shaders/rhi/light.inc.frag
@@ -0,0 +1,28 @@
+#pragma include defaultuniforms.inc
+
+const int MAX_LIGHTS = 8;
+const int TYPE_POINT = 0;
+const int TYPE_DIRECTIONAL = 1;
+const int TYPE_SPOT = 2;
+
+struct Light {
+ vec3 position;
+ float intensity;
+ vec3 color;
+ float constantAttenuation;
+ vec3 direction;
+ float linearAttenuation;
+ float quadraticAttenuation;
+ float cutOffAngle;
+ int type;
+};
+
+layout(std140, binding = auto) uniform qt3d_light_uniforms {
+ Light lights[MAX_LIGHTS];
+ int lightCount;
+ int envLightCount;
+};
+
+// Pre-convolved environment maps
+layout(binding = auto) uniform samplerCube envLight_irradiance; // For diffuse contribution
+layout(binding = auto) uniform samplerCube envLight_specular; // For specular contribution
diff --git a/src/extras/shaders/rhi/metalrough.inc.frag b/src/extras/shaders/rhi/metalrough.inc.frag
new file mode 100644
index 000000000..817e1192c
--- /dev/null
+++ b/src/extras/shaders/rhi/metalrough.inc.frag
@@ -0,0 +1,341 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#pragma include light.inc.frag
+
+int mipLevelCount(const in samplerCube cube)
+{
+ int baseSize = textureSize(cube, 0).x;
+ int nMips = int(log2(float(baseSize > 0 ? baseSize : 1))) + 1;
+ return nMips;
+}
+
+float remapRoughness(const in float roughness)
+{
+ // As per page 14 of
+ // http://www.frostbite.com/wp-content/uploads/2014/11/course_notes_moving_frostbite_to_pbr.pdf
+ // we remap the roughness to give a more perceptually linear response
+ // of "bluriness" as a function of the roughness specified by the user.
+ // r = roughness^2
+ const float maxSpecPower = 999999.0;
+ const float minRoughness = sqrt(2.0 / (maxSpecPower + 2));
+ return max(roughness * roughness, minRoughness);
+}
+
+float alphaToMipLevel(float alpha)
+{
+ float specPower = 2.0 / (alpha * alpha) - 2.0;
+
+ // We use the mip level calculation from Lys' default power drop, which in
+ // turn is a slight modification of that used in Marmoset Toolbag. See
+ // https://docs.knaldtech.com/doku.php?id=specular_lys for details.
+ // For now we assume a max specular power of 999999 which gives
+ // maxGlossiness = 1.
+ const float k0 = 0.00098;
+ const float k1 = 0.9921;
+ float glossiness = (pow(2.0, -10.0 / sqrt(specPower)) - k0) / k1;
+
+ // TODO: Optimize by doing this on CPU and set as
+ // uniform int envLight.specularMipLevels say (if present in shader).
+ // Lookup the number of mips in the specular envmap
+ int mipLevels = mipLevelCount(envLight_specular);
+
+ // Offset of smallest miplevel we should use (corresponds to specular
+ // power of 1). I.e. in the 32x32 sized mip.
+ const float mipOffset = 5.0;
+
+ // The final factor is really 1 - g / g_max but as mentioned above g_max
+ // is 1 by definition here so we can avoid the division. If we make the
+ // max specular power for the spec map configurable, this will need to
+ // be handled properly.
+ float mipLevel = (mipLevels - 1.0 - mipOffset) * (1.0 - glossiness);
+ return mipLevel;
+}
+
+float normalDistribution(const in vec3 n, const in vec3 h, const in float alpha)
+{
+ // Blinn-Phong approximation - see
+ // http://graphicrants.blogspot.co.uk/2013/08/specular-brdf-reference.html
+ float specPower = 2.0 / (alpha * alpha) - 2.0;
+ return (specPower + 2.0) / (2.0 * 3.14159) * pow(max(dot(n, h), 0.0), specPower);
+}
+
+vec3 fresnelFactor(const in vec3 color, const in float cosineFactor)
+{
+ // Calculate the Fresnel effect value
+ vec3 f = color;
+ vec3 F = f + (1.0 - f) * pow(1.0 - cosineFactor, 5.0);
+ return clamp(F, f, vec3(1.0));
+}
+
+float geometricModel(const in float lDotN,
+ const in float vDotN,
+ const in vec3 h)
+{
+ // Implicit geometric model (equal to denominator in specular model).
+ // This currently assumes that there is no attenuation by geometric shadowing or
+ // masking according to the microfacet theory.
+ return lDotN * vDotN;
+}
+
+vec3 specularModel(const in vec3 F0,
+ const in float sDotH,
+ const in float sDotN,
+ const in float vDotN,
+ const in vec3 n,
+ const in vec3 h)
+{
+ // Clamp sDotN and vDotN to small positive value to prevent the
+ // denominator in the reflection equation going to infinity. Balance this
+ // by using the clamped values in the geometric factor function to
+ // avoid ugly seams in the specular lighting.
+ float sDotNPrime = max(sDotN, 0.001);
+ float vDotNPrime = max(vDotN, 0.001);
+
+ vec3 F = fresnelFactor(F0, sDotH);
+ float G = geometricModel(sDotNPrime, vDotNPrime, h);
+
+ vec3 cSpec = F * G / (4.0 * sDotNPrime * vDotNPrime);
+ return clamp(cSpec, vec3(0.0), vec3(1.0));
+}
+
+vec3 pbrModel(const in int lightIndex,
+ const in vec3 wPosition,
+ const in vec3 wNormal,
+ const in vec3 wView,
+ const in vec3 baseColor,
+ const in float metalness,
+ const in float alpha,
+ const in float ambientOcclusion)
+{
+ // Calculate some useful quantities
+ vec3 n = wNormal;
+ vec3 s = vec3(0.0);
+ vec3 v = wView;
+ vec3 h = vec3(0.0);
+
+ float vDotN = dot(v, n);
+ float sDotN = 0.0;
+ float sDotH = 0.0;
+ float att = 1.0;
+
+ if (lights[lightIndex].type != TYPE_DIRECTIONAL) {
+ // Point and Spot lights
+ vec3 sUnnormalized = vec3(lights[lightIndex].position) - wPosition;
+ s = normalize(sUnnormalized);
+
+ // Calculate the attenuation factor
+ sDotN = dot(s, n);
+ if (sDotN > 0.0) {
+ if (lights[lightIndex].constantAttenuation != 0.0
+ || lights[lightIndex].linearAttenuation != 0.0
+ || lights[lightIndex].quadraticAttenuation != 0.0) {
+ float dist = length(sUnnormalized);
+ att = 1.0 / (lights[lightIndex].constantAttenuation +
+ lights[lightIndex].linearAttenuation * dist +
+ lights[lightIndex].quadraticAttenuation * dist * dist);
+ }
+
+ // The light direction is in world space already
+ if (lights[lightIndex].type == TYPE_SPOT) {
+ // Check if fragment is inside or outside of the spot light cone
+ if (degrees(acos(dot(-s, lights[lightIndex].direction))) > lights[lightIndex].cutOffAngle)
+ sDotN = 0.0;
+ }
+ }
+ } else {
+ // Directional lights
+ // The light direction is in world space already
+ s = normalize(-lights[lightIndex].direction);
+ sDotN = dot(s, n);
+ }
+
+ h = normalize(s + v);
+ sDotH = dot(s, h);
+
+ // Calculate diffuse component
+ vec3 diffuseColor = (1.0 - metalness) * baseColor * lights[lightIndex].color;
+ vec3 diffuse = diffuseColor * max(sDotN, 0.0) / 3.14159;
+
+ // Calculate specular component
+ vec3 dielectricColor = vec3(0.04);
+ vec3 F0 = mix(dielectricColor, baseColor, metalness);
+ vec3 specularFactor = vec3(0.0);
+ if (sDotN > 0.0) {
+ specularFactor = specularModel(F0, sDotH, sDotN, vDotN, n, h);
+ specularFactor *= normalDistribution(n, h, alpha);
+ }
+ vec3 specularColor = lights[lightIndex].color;
+ vec3 specular = specularColor * specularFactor;
+
+ // Blend between diffuse and specular to conserver energy
+ vec3 color = att * lights[lightIndex].intensity * (specular + diffuse * (vec3(1.0) - specular));
+
+ // Reduce by ambient occlusion amount
+ color *= ambientOcclusion;
+
+ return color;
+}
+
+vec3 pbrIblModel(const in vec3 wNormal,
+ const in vec3 wView,
+ const in vec3 baseColor,
+ const in float metalness,
+ const in float alpha,
+ const in float ambientOcclusion)
+{
+ // Calculate reflection direction of view vector about surface normal
+ // vector in world space. This is used in the fragment shader to sample
+ // from the environment textures for a light source. This is equivalent
+ // to the l vector for punctual light sources. Armed with this, calculate
+ // the usual factors needed
+ vec3 n = wNormal;
+ vec3 l = reflect(-wView, n);
+ vec3 v = wView;
+ vec3 h = normalize(l + v);
+ float vDotN = dot(v, n);
+ float lDotN = dot(l, n);
+ float lDotH = dot(l, h);
+
+ // Calculate diffuse component
+ vec3 diffuseColor = (1.0 - metalness) * baseColor;
+ vec3 diffuse = diffuseColor * texture(envLight_irradiance, l).rgb;
+
+ // Calculate specular component
+ vec3 dielectricColor = vec3(0.04);
+ vec3 F0 = mix(dielectricColor, baseColor, metalness);
+ vec3 specularFactor = specularModel(F0, lDotH, lDotN, vDotN, n, h);
+
+ float lod = alphaToMipLevel(alpha);
+//#define DEBUG_SPECULAR_LODS
+#ifdef DEBUG_SPECULAR_LODS
+ if (lod > 7.0)
+ return vec3(1.0, 0.0, 0.0);
+ else if (lod > 6.0)
+ return vec3(1.0, 0.333, 0.0);
+ else if (lod > 5.0)
+ return vec3(1.0, 1.0, 0.0);
+ else if (lod > 4.0)
+ return vec3(0.666, 1.0, 0.0);
+ else if (lod > 3.0)
+ return vec3(0.0, 1.0, 0.666);
+ else if (lod > 2.0)
+ return vec3(0.0, 0.666, 1.0);
+ else if (lod > 1.0)
+ return vec3(0.0, 0.0, 1.0);
+ else if (lod > 0.0)
+ return vec3(1.0, 0.0, 1.0);
+#endif
+ vec3 specularSkyColor = textureLod(envLight_specular, l, lod).rgb;
+ vec3 specular = specularSkyColor * specularFactor;
+
+ // Blend between diffuse and specular to conserve energy
+ vec3 color = specular + diffuse * (vec3(1.0) - specularFactor);
+
+ // Reduce by ambient occlusion amount
+ color *= ambientOcclusion;
+
+ return color;
+}
+
+vec3 toneMap(const in vec3 c)
+{
+ return c / (c + vec3(1.0));
+}
+
+vec3 gammaCorrect(const in vec3 color)
+{
+ return pow(color, vec3(1.0 / gamma));
+}
+
+vec4 metalRoughFunction(const in vec4 baseColor,
+ const in float metalness,
+ const in float roughness,
+ const in float ambientOcclusion,
+ const in vec3 worldPosition,
+ const in vec3 worldView,
+ const in vec3 worldNormal)
+{
+ vec3 cLinear = vec3(0.0);
+
+ // Remap roughness for a perceptually more linear correspondence
+ float alpha = remapRoughness(roughness);
+
+ for (int i = 0; i < envLightCount; ++i) {
+ cLinear += pbrIblModel(worldNormal,
+ worldView,
+ baseColor.rgb,
+ metalness,
+ alpha,
+ ambientOcclusion);
+ }
+
+ for (int i = 0; i < lightCount; ++i) {
+ cLinear += pbrModel(i,
+ worldPosition,
+ worldNormal,
+ worldView,
+ baseColor.rgb,
+ metalness,
+ alpha,
+ ambientOcclusion);
+ }
+
+ // Apply exposure correction
+ cLinear *= pow(2.0, exposure);
+
+ // Apply simple (Reinhard) tonemap transform to get into LDR range [0, 1]
+ vec3 cToneMapped = toneMap(cLinear);
+
+ // Apply gamma correction prior to display
+ vec3 cGamma = gammaCorrect(cToneMapped);
+
+ return vec4(cGamma, 1.0);
+}
diff --git a/src/extras/shaders/rhi/morphphong.vert b/src/extras/shaders/rhi/morphphong.vert
new file mode 100644
index 000000000..826145b20
--- /dev/null
+++ b/src/extras/shaders/rhi/morphphong.vert
@@ -0,0 +1,43 @@
+#version 450
+
+layout(location = 0) in vec3 vertexPosition;
+layout(location = 1) in vec3 vertexNormal;
+layout(location = 2) in vec3 vertexPositionTarget;
+layout(location = 3) in vec3 vertexNormalTarget;
+
+layout(location = 0) out vec3 worldPosition;
+layout(location = 1) out vec3 worldNormal;
+
+layout(std140, binding = 1) uniform qt3d_command_uniforms {
+ mat4 modelMatrix;
+ mat4 inverseModelMatrix;
+ mat4 modelViewMatrix;
+ mat3 modelNormalMatrix;
+ mat4 inverseModelViewMatrix;
+ mat4 modelViewProjection;
+ mat4 inverseModelViewProjectionMatrix;
+};
+
+layout(std140, binding = 3) uniform qt3d_morph_uniforms {
+ float interpolator;
+};
+
+void main()
+{
+ vec3 morphPos;
+ vec3 morphNormal;
+ if (interpolator > 0.0) {
+ // normalized
+ morphPos = mix(vertexPosition, vertexPositionTarget, interpolator);
+ morphNormal = normalize(mix(vertexNormal, vertexNormalTarget, interpolator));
+ } else {
+ // relative
+ morphPos = vertexPosition + vertexPositionTarget * abs(interpolator);
+ morphNormal = normalize(vertexNormal + vertexNormalTarget * abs(interpolator));
+ }
+
+ worldNormal = normalize( modelNormalMatrix * morphNormal );
+ worldPosition = vec3( modelMatrix * vec4( morphPos, 1.0 ) );
+
+ gl_Position = modelViewProjection * vec4( morphPos, 1.0 );
+}
diff --git a/src/extras/shaders/rhi/pervertexcolor.frag b/src/extras/shaders/rhi/pervertexcolor.frag
new file mode 100644
index 000000000..da3ea149e
--- /dev/null
+++ b/src/extras/shaders/rhi/pervertexcolor.frag
@@ -0,0 +1,150 @@
+#version 450
+
+layout(location = 0) in vec3 worldPosition;
+layout(location = 1) in vec3 worldNormal;
+layout(location = 2) in vec4 color;
+
+layout(location = 0) out vec4 fragColor;
+
+layout(std140, binding = 0) uniform qt3d_render_view_uniforms {
+ mat4 viewMatrix;
+ mat4 projectionMatrix;
+ mat4 viewProjectionMatrix;
+ mat4 inverseViewMatrix;
+ mat4 inverseProjectionMatrix;
+ mat4 inverseViewProjectionMatrix;
+ mat4 viewportMatrix;
+ mat4 inverseViewportMatrix;
+ vec4 textureTransformMatrix;
+ vec3 eyePosition;
+ float aspectRatio;
+ float gamma;
+ float exposure;
+ float time;
+};
+
+layout(std140, binding = 1) uniform qt3d_command_uniforms {
+ mat4 modelMatrix;
+ mat4 inverseModelMatrix;
+ mat4 modelViewMatrix;
+ mat3 modelNormalMatrix;
+ mat4 inverseModelViewMatrix;
+ mat4 mvp;
+ mat4 inverseModelViewProjectionMatrix;
+};
+
+const int MAX_LIGHTS = 8;
+const int TYPE_POINT = 0;
+const int TYPE_DIRECTIONAL = 1;
+const int TYPE_SPOT = 2;
+
+struct Light {
+ int type;
+ vec3 position;
+ vec3 color;
+ float intensity;
+ vec3 direction;
+ float constantAttenuation;
+ float linearAttenuation;
+ float quadraticAttenuation;
+ float cutOffAngle;
+};
+
+layout(std140, binding = 2) uniform qt3d_light_uniforms {
+ uniform Light lights[MAX_LIGHTS];
+ uniform int lightCount;
+ uniform int envLightCount;
+};
+
+void adsModel(const in vec3 worldPos,
+ const in vec3 worldNormal,
+ const in vec3 worldView,
+ const in float shininess,
+ out vec3 diffuseColor,
+ out vec3 specularColor)
+{
+ diffuseColor = vec3(0.0);
+ specularColor = vec3(0.0);
+
+ // We perform all work in world space
+ vec3 n = normalize(worldNormal);
+ vec3 s = vec3(0.0);
+
+ for (int i = 0; i < lightCount; ++i) {
+ float att = 1.0;
+ float sDotN = 0.0;
+
+ if (lights[i].type != TYPE_DIRECTIONAL) {
+ // Point and Spot lights
+
+ // Light position is already in world space
+ vec3 sUnnormalized = lights[i].position - worldPos;
+ s = normalize(sUnnormalized); // Light direction
+
+ // Calculate the attenuation factor
+ sDotN = dot(s, n);
+ if (sDotN > 0.0) {
+ if (lights[i].constantAttenuation != 0.0
+ || lights[i].linearAttenuation != 0.0
+ || lights[i].quadraticAttenuation != 0.0) {
+ float dist = length(sUnnormalized);
+ att = 1.0 / (lights[i].constantAttenuation +
+ lights[i].linearAttenuation * dist +
+ lights[i].quadraticAttenuation * dist * dist);
+ }
+
+ // The light direction is in world space already
+ if (lights[i].type == TYPE_SPOT) {
+ // Check if fragment is inside or outside of the spot light cone
+ if (degrees(acos(dot(-s, lights[i].direction))) > lights[i].cutOffAngle)
+ sDotN = 0.0;
+ }
+ }
+ } else {
+ // Directional lights
+ // The light direction is in world space already
+ s = normalize(-lights[i].direction);
+ sDotN = dot(s, n);
+ }
+
+ // Calculate the diffuse factor
+ float diffuse = max(sDotN, 0.0);
+
+ // Calculate the specular factor
+ float specular = 0.0;
+ if (diffuse > 0.0 && shininess > 0.0) {
+ float normFactor = (shininess + 2.0) / 2.0;
+ vec3 r = reflect(-s, n); // Reflection direction in world space
+ specular = normFactor * pow(max(dot(r, worldView), 0.0), shininess);
+ }
+
+ // Accumulate the diffuse and specular contributions
+ diffuseColor += att * lights[i].intensity * diffuse * lights[i].color;
+ specularColor += att * lights[i].intensity * specular * lights[i].color;
+ }
+}
+
+vec4 phongFunction(const in vec4 ambient,
+ const in vec4 diffuse,
+ const in vec4 specular,
+ const in float shininess,
+ const in vec3 worldPosition,
+ const in vec3 worldView,
+ const in vec3 worldNormal)
+{
+ // Calculate the lighting model, keeping the specular component separate
+ vec3 diffuseColor, specularColor;
+ adsModel(worldPosition, worldNormal, worldView, shininess, diffuseColor, specularColor);
+
+ // Combine spec with ambient+diffuse for final fragment color
+ vec3 color = (ambient.rgb + diffuseColor) * diffuse.rgb
+ + specularColor * specular.rgb;
+
+ return vec4(color, diffuse.a);
+}
+
+void main()
+{
+ vec3 worldView = normalize(eyePosition - worldPosition);
+ fragColor = phongFunction(color, color, vec4(0.0), 0.0, worldPosition, worldView, worldNormal);
+}
diff --git a/src/extras/shaders/rhi/pervertexcolor.vert b/src/extras/shaders/rhi/pervertexcolor.vert
new file mode 100644
index 000000000..3ae1c9fb0
--- /dev/null
+++ b/src/extras/shaders/rhi/pervertexcolor.vert
@@ -0,0 +1,28 @@
+#version 450
+
+layout(location = 0) in vec3 vertexPosition;
+layout(location = 1) in vec3 vertexNormal;
+layout(location = 2) in vec4 vertexColor;
+
+layout(location = 0) out vec3 worldPosition;
+layout(location = 1) out vec3 worldNormal;
+layout(location = 2) out vec4 color;
+
+layout(std140, binding = 1) uniform qt3d_command_uniforms {
+ mat4 modelMatrix;
+ mat4 inverseModelMatrix;
+ mat4 modelViewMatrix;
+ mat3 modelNormalMatrix;
+ mat4 inverseModelViewMatrix;
+ mat4 mvp;
+ mat4 inverseModelViewProjectionMatrix;
+};
+
+void main()
+{
+ worldNormal = normalize( modelNormalMatrix * vertexNormal );
+ worldPosition = vec3( modelMatrix * vec4( vertexPosition, 1.0 ) );
+ color = vertexColor;
+
+ gl_Position = mvp * vec4( vertexPosition, 1.0 );
+}
diff --git a/src/extras/shaders/rhi/phong.inc.frag b/src/extras/shaders/rhi/phong.inc.frag
new file mode 100644
index 000000000..47a6ecd4a
--- /dev/null
+++ b/src/extras/shaders/rhi/phong.inc.frag
@@ -0,0 +1,138 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#pragma include light.inc.frag
+
+void adsModel(const in vec3 worldPos,
+ const in vec3 worldNormal,
+ const in vec3 worldView,
+ const in float shininess,
+ out vec3 diffuseColor,
+ out vec3 specularColor)
+{
+ diffuseColor = vec3(0.0);
+ specularColor = vec3(0.0);
+
+ // We perform all work in world space
+ vec3 n = normalize(worldNormal);
+ vec3 s = vec3(0.0);
+
+ for (int i = 0; i < lightCount; ++i) {
+ float att = 1.0;
+ float sDotN = 0.0;
+
+ if (lights[i].type != TYPE_DIRECTIONAL) {
+ // Point and Spot lights
+
+ // Light position is already in world space
+ vec3 sUnnormalized = lights[i].position - worldPos;
+ s = normalize(sUnnormalized); // Light direction
+
+ // Calculate the attenuation factor
+ sDotN = dot(s, n);
+ if (sDotN > 0.0) {
+ if (lights[i].constantAttenuation != 0.0
+ || lights[i].linearAttenuation != 0.0
+ || lights[i].quadraticAttenuation != 0.0) {
+ float dist = length(sUnnormalized);
+ att = 1.0 / (lights[i].constantAttenuation +
+ lights[i].linearAttenuation * dist +
+ lights[i].quadraticAttenuation * dist * dist);
+ }
+
+ // The light direction is in world space already
+ if (lights[i].type == TYPE_SPOT) {
+ // Check if fragment is inside or outside of the spot light cone
+ if (degrees(acos(dot(-s, lights[i].direction))) > lights[i].cutOffAngle)
+ sDotN = 0.0;
+ }
+ }
+ } else {
+ // Directional lights
+ // The light direction is in world space already
+ s = normalize(-lights[i].direction);
+ sDotN = dot(s, n);
+ }
+
+ // Calculate the diffuse factor
+ float diffuse = max(sDotN, 0.0);
+
+ // Calculate the specular factor
+ float specular = 0.0;
+ if (diffuse > 0.0 && shininess > 0.0) {
+ float normFactor = (shininess + 2.0) / 2.0;
+ vec3 r = reflect(-s, n); // Reflection direction in world space
+ specular = normFactor * pow(max(dot(r, worldView), 0.0), shininess);
+ }
+
+ // Accumulate the diffuse and specular contributions
+ diffuseColor += att * lights[i].intensity * diffuse * lights[i].color;
+ specularColor += att * lights[i].intensity * specular * lights[i].color;
+ }
+}
+
+vec4 phongFunction(const in vec4 ambient,
+ const in vec4 diffuse,
+ const in vec4 specular,
+ const in float shininess,
+ const in vec3 worldPosition,
+ const in vec3 worldView,
+ const in vec3 worldNormal)
+{
+ // Calculate the lighting model, keeping the specular component separate
+ vec3 diffuseColor, specularColor;
+ adsModel(worldPosition, worldNormal, worldView, shininess, diffuseColor, specularColor);
+
+ // Combine spec with ambient+diffuse for final fragment color
+ vec3 color = (ambient.rgb + diffuseColor) * diffuse.rgb
+ + specularColor * specular.rgb;
+
+ return vec4(color, diffuse.a);
+}
diff --git a/src/extras/shaders/rhi/skybox.frag b/src/extras/shaders/rhi/skybox.frag
new file mode 100644
index 000000000..649894b15
--- /dev/null
+++ b/src/extras/shaders/rhi/skybox.frag
@@ -0,0 +1,44 @@
+#version 450
+
+layout(location = 0) in vec3 texCoord0;
+layout(location = 0) out vec4 fragColor;
+
+
+// Gamma correction
+
+layout(std140, binding = 0) uniform qt3d_render_view_uniforms {
+ mat4 viewMatrix;
+ mat4 projectionMatrix;
+ mat4 viewProjectionMatrix;
+ mat4 inverseViewMatrix;
+ mat4 inverseProjectionMatrix;
+ mat4 inverseViewProjectionMatrix;
+ mat4 viewportMatrix;
+ mat4 inverseViewportMatrix;
+ vec4 textureTransformMatrix;
+ vec3 eyePosition;
+ float aspectRatio;
+ float gamma;
+ float exposure;
+ float time;
+};
+
+layout(std140, binding = 2) uniform qt3d_morph_uniforms {
+ float gammaStrength;
+};
+
+layout(binding = 3) uniform samplerCube skyboxTexture;
+
+vec3 gammaCorrect(const in vec3 color)
+{
+ return pow(color, vec3(1.0 / gamma));
+}
+
+void main()
+{
+ vec4 baseColor = texture(skyboxTexture, texCoord0);
+ vec4 gammaColor = vec4(gammaCorrect(baseColor.rgb), 1.0);
+ // This is an odd way to enable or not gamma correction,
+ // but this is a way to avoid branching until we can generate shaders
+ fragColor = mix(baseColor, gammaColor, gammaStrength);
+}
diff --git a/src/extras/shaders/rhi/skybox.vert b/src/extras/shaders/rhi/skybox.vert
new file mode 100644
index 000000000..8f3de6f62
--- /dev/null
+++ b/src/extras/shaders/rhi/skybox.vert
@@ -0,0 +1,39 @@
+#version 450
+
+layout(location = 0) in vec3 vertexPosition;
+layout(location = 0) out vec3 texCoord0;
+
+layout(std140, binding = 0) uniform qt3d_render_view_uniforms {
+ mat4 viewMatrix;
+ mat4 projectionMatrix;
+ mat4 viewProjectionMatrix;
+ mat4 inverseViewMatrix;
+ mat4 inverseProjectionMatrix;
+ mat4 inverseViewProjectionMatrix;
+ mat4 viewportMatrix;
+ mat4 inverseViewportMatrix;
+ vec4 textureTransformMatrix;
+ vec3 eyePosition;
+ float aspectRatio;
+ float gamma;
+ float exposure;
+ float time;
+};
+
+layout(std140, binding = 1) uniform qt3d_command_uniforms {
+ mat4 modelMatrix;
+ mat4 inverseModelMatrix;
+ mat4 modelViewMatrix;
+ mat3 modelNormalMatrix;
+ mat4 inverseModelViewMatrix;
+ mat4 modelViewProjection;
+ mat4 inverseModelViewProjectionMatrix;
+};
+
+void main()
+{
+ texCoord0 = vertexPosition.xyz;
+ // Converting the viewMatrix to a mat3, then back to a mat4
+ // removes the translation component from it
+ gl_Position = vec4(projectionMatrix * mat4(mat3(viewMatrix)) * modelMatrix * vec4(vertexPosition, 1.0)).xyww;
+}
diff --git a/src/extras/shaders/rhi/unlittexture.frag b/src/extras/shaders/rhi/unlittexture.frag
new file mode 100644
index 000000000..e2b2c8fe2
--- /dev/null
+++ b/src/extras/shaders/rhi/unlittexture.frag
@@ -0,0 +1,13 @@
+#version 450
+
+layout(location = 0) in vec3 position;
+layout(location = 1) in vec2 texCoord;
+
+layout(location = 0) out vec4 fragColor;
+
+layout(binding = 3) uniform sampler2D diffuseTexture;
+
+void main()
+{
+ fragColor = texture( diffuseTexture, texCoord );
+}
diff --git a/src/extras/shaders/rhi/unlittexture.vert b/src/extras/shaders/rhi/unlittexture.vert
new file mode 100644
index 000000000..08f06e817
--- /dev/null
+++ b/src/extras/shaders/rhi/unlittexture.vert
@@ -0,0 +1,47 @@
+#version 450
+
+layout(location = 0) in vec3 vertexPosition;
+layout(location = 1) in vec2 vertexTexCoord;
+
+layout(location = 0) out vec3 position;
+layout(location = 1) out vec2 texCoord;
+
+layout(std140, binding = 0) uniform qt3d_render_view_uniforms {
+ mat4 viewMatrix;
+ mat4 projectionMatrix;
+ mat4 viewProjectionMatrix;
+ mat4 inverseViewMatrix;
+ mat4 inverseProjectionMatrix;
+ mat4 inverseViewProjectionMatrix;
+ mat4 viewportMatrix;
+ mat4 inverseViewportMatrix;
+ vec4 textureTransformMatrix;
+ vec3 eyePosition;
+ float aspectRatio;
+ float gamma;
+ float exposure;
+ float time;
+};
+
+layout(std140, binding = 1) uniform qt3d_command_uniforms {
+ mat4 modelMatrix;
+ mat4 inverseModelMatrix;
+ mat4 modelView;
+ mat3 modelNormalMatrix;
+ mat4 inverseModelViewMatrix;
+ mat4 mvp;
+ mat4 inverseModelViewProjectionMatrix;
+};
+
+layout(std140, binding = 2) uniform qt3d_custom_uniforms {
+ mat3 texCoordTransform;
+};
+
+void main()
+{
+ vec3 tt = texCoordTransform * vec3(vertexTexCoord, 1.0);
+ texCoord = (tt / tt.z).xy;
+ position = vec3( modelView * vec4( vertexPosition, 1.0 ) );
+
+ gl_Position = mvp * vec4( vertexPosition, 1.0 );
+}
diff --git a/src/extras/text/qdistancefieldglyphcache.cpp b/src/extras/text/qdistancefieldglyphcache.cpp
index a3819b8fe..66d2c0495 100644
--- a/src/extras/text/qdistancefieldglyphcache.cpp
+++ b/src/extras/text/qdistancefieldglyphcache.cpp
@@ -45,6 +45,7 @@
#include "qtextureatlas_p.h"
#include <QtGui/qfont.h>
+#include <QtGui/qpainterpath.h>
#include <QtGui/private/qdistancefield_p.h>
#include <Qt3DCore/private/qnode_p.h>
#include <Qt3DExtras/private/qtextureatlas_p.h>
diff --git a/src/input/backend/mouseeventfilter.cpp b/src/input/backend/mouseeventfilter.cpp
index 48b30725a..baa78ac25 100644
--- a/src/input/backend/mouseeventfilter.cpp
+++ b/src/input/backend/mouseeventfilter.cpp
@@ -77,6 +77,7 @@ bool MouseEventFilter::eventFilter(QObject *obj, QEvent *e)
}
case QEvent::MouseButtonPress:
case QEvent::MouseButtonRelease:
+ case QEvent::MouseButtonDblClick:
case QEvent::MouseMove:
// Creates copy and store event to be processed later on in an InputAspect job
m_inputHandler->appendMouseEvent(QMouseEvent(*static_cast<QMouseEvent *>(e)));
diff --git a/src/input/frontend/qmousehandler.cpp b/src/input/frontend/qmousehandler.cpp
index 49ccce0c5..6c354d55d 100644
--- a/src/input/frontend/qmousehandler.cpp
+++ b/src/input/frontend/qmousehandler.cpp
@@ -58,7 +58,7 @@ QMouseHandlerPrivate::QMouseHandlerPrivate()
{
m_shareable = false;
m_pressAndHoldTimer->setSingleShot(true);
- m_pressAndHoldTimer->setInterval(500);
+ m_pressAndHoldTimer->setInterval(800);
QObject::connect(m_pressAndHoldTimer, &QTimer::timeout, [this] {
emit q_func()->pressAndHold(m_lastPressedEvent.data());
});
@@ -77,15 +77,15 @@ void QMouseHandlerPrivate::mouseEvent(const QMouseEventPtr &event)
{
Q_Q(QMouseHandler);
switch (event->type()) {
- case QEvent::MouseButtonPress: {
+ case QEvent::MouseButtonPress:
m_lastPressedEvent = event;
m_pressAndHoldTimer->start();
emit q->pressed(event.data());
break;
- }
case QEvent::MouseButtonRelease:
m_pressAndHoldTimer->stop();
emit q->released(event.data());
+ emit q->clicked(event.data());
break;
#if QT_CONFIG(gestures)
case QEvent::Gesture:
@@ -96,6 +96,7 @@ void QMouseHandlerPrivate::mouseEvent(const QMouseEventPtr &event)
emit q->doubleClicked(event.data());
break;
case QEvent::MouseMove:
+ m_pressAndHoldTimer->stop();
emit q->positionChanged(event.data());
break;
default:
diff --git a/src/plugins/renderers/opengl/debug/imguirenderer.cpp b/src/plugins/renderers/opengl/debug/imguirenderer.cpp
index 8dd9b98fe..0acee945a 100644
--- a/src/plugins/renderers/opengl/debug/imguirenderer.cpp
+++ b/src/plugins/renderers/opengl/debug/imguirenderer.cpp
@@ -283,13 +283,21 @@ void ImGuiRenderer::renderDebugOverlay(const QVector<RenderView *> &renderViews,
QMetaObject::invokeMethod(m_renderer->services()->systemInformation(), "dumpCommand",
Qt::QueuedConnection, Q_ARG(QString, QLatin1String("render framegraph")));
ImGui::SameLine();
- if (ImGui::Button("FrameGraph Paths##1"))
+ if (ImGui::Button("Render Views##1"))
QMetaObject::invokeMethod(m_renderer->services()->systemInformation(), "dumpCommand",
Qt::QueuedConnection, Q_ARG(QString, QLatin1String("render framepaths")));
+
+ ImGui::AlignTextToFramePadding();
+ ImGui::Text(" ");
+ ImGui::SameLine();
+ if (ImGui::Button("Filter State##1"))
+ QMetaObject::invokeMethod(m_renderer->services()->systemInformation(), "dumpCommand",
+ Qt::QueuedConnection, Q_ARG(QString, QLatin1String("render filterstates")));
ImGui::SameLine();
if (ImGui::Button("JobsGraph##1"))
QMetaObject::invokeMethod(m_renderer->services()->systemInformation(), "dumpCommand",
Qt::QueuedConnection, Q_ARG(QString, QLatin1String("dump jobs")));
+
ImGui::End();
if (m_showGLInfoWindow)
diff --git a/src/plugins/renderers/opengl/graphicshelpers/submissioncontext.cpp b/src/plugins/renderers/opengl/graphicshelpers/submissioncontext.cpp
index 519f2ae98..d4d3457ab 100644
--- a/src/plugins/renderers/opengl/graphicshelpers/submissioncontext.cpp
+++ b/src/plugins/renderers/opengl/graphicshelpers/submissioncontext.cpp
@@ -1166,7 +1166,7 @@ void SubmissionContext::setUpdatedTexture(const Qt3DCore::QNodeIdVector &updated
// It will be easier if the QGraphicContext applies the QUniformPack
// than the other way around
-bool SubmissionContext::setParameters(ShaderParameterPack &parameterPack)
+bool SubmissionContext::setParameters(ShaderParameterPack &parameterPack, GLShader *shader)
{
static const int irradianceId = StringToInt::lookupId(QLatin1String("envLight.irradiance"));
static const int specularId = StringToInt::lookupId(QLatin1String("envLight.specular"));
@@ -1231,7 +1231,7 @@ bool SubmissionContext::setParameters(ShaderParameterPack &parameterPack)
}
}
- QOpenGLShaderProgram *shader = activeShader();
+ QOpenGLShaderProgram *glShader = activeShader();
// TO DO: We could cache the binding points somehow and only do the binding when necessary
// for SSBO and UBO
@@ -1245,7 +1245,7 @@ bool SubmissionContext::setParameters(ShaderParameterPack &parameterPack)
// This is currently not required as we are introspecting the bindingIndex
// value from the shaders and not replacing them, making such a call useless
// bindShaderStorageBlock(shader->programId(), b.m_blockIndex, b.m_bindingIndex);
- bindShaderStorageBlock(shader->programId(), b.m_blockIndex, b.m_bindingIndex);
+ bindShaderStorageBlock(glShader->programId(), b.m_blockIndex, b.m_bindingIndex);
// Needed to avoid conflict where the buffer would already
// be bound as a VertexArray
bindGLBuffer(ssbo, GLBuffer::ShaderStorageBuffer);
@@ -1260,7 +1260,7 @@ bool SubmissionContext::setParameters(ShaderParameterPack &parameterPack)
for (const BlockToUBO &b : blockToUBOs) {
Buffer *cpuBuffer = m_renderer->nodeManagers()->bufferManager()->lookupResource(b.m_bufferID);
GLBuffer *ubo = glBufferForRenderBuffer(cpuBuffer);
- bindUniformBlock(shader->programId(), b.m_blockIndex, uboIndex);
+ bindUniformBlock(glShader->programId(), b.m_blockIndex, uboIndex);
// Needed to avoid conflict where the buffer would already
// be bound as a VertexArray
bindGLBuffer(ubo, GLBuffer::UniformBuffer);
@@ -1270,11 +1270,11 @@ bool SubmissionContext::setParameters(ShaderParameterPack &parameterPack)
// Update uniforms in the Default Uniform Block
const PackUniformHash values = parameterPack.uniforms();
- const QVector<ShaderUniform> activeUniforms = parameterPack.submissionUniforms();
+ const QVector<int> &activeUniformsIndices = parameterPack.submissionUniformIndices();
+ const QVector<ShaderUniform> &shaderUniforms = shader->uniforms();
- for (const ShaderUniform &uniform : activeUniforms) {
- // We can use [] as we are sure the the uniform wouldn't
- // be un activeUniforms if there wasn't a matching value
+ for (const int shaderUniformIndex : activeUniformsIndices) {
+ const ShaderUniform &uniform = shaderUniforms[shaderUniformIndex];
const UniformValue &v = values.value(uniform.m_nameId);
// skip invalid textures/images
diff --git a/src/plugins/renderers/opengl/graphicshelpers/submissioncontext_p.h b/src/plugins/renderers/opengl/graphicshelpers/submissioncontext_p.h
index 59b2b78f3..4c895013c 100644
--- a/src/plugins/renderers/opengl/graphicshelpers/submissioncontext_p.h
+++ b/src/plugins/renderers/opengl/graphicshelpers/submissioncontext_p.h
@@ -137,7 +137,7 @@ public:
GLBuffer *glBufferForRenderBuffer(Buffer *buf);
// Parameters
- bool setParameters(ShaderParameterPack &parameterPack);
+ bool setParameters(ShaderParameterPack &parameterPack, GLShader *shader);
// RenderState
void setCurrentStateSet(RenderStateSet* ss);
diff --git a/src/plugins/renderers/opengl/jobs/materialparametergathererjob.cpp b/src/plugins/renderers/opengl/jobs/materialparametergathererjob.cpp
index 1f51ceba3..1dd26b847 100644
--- a/src/plugins/renderers/opengl/jobs/materialparametergathererjob.cpp
+++ b/src/plugins/renderers/opengl/jobs/materialparametergathererjob.cpp
@@ -59,8 +59,32 @@ const int likelyNumberOfParameters = 24;
} // anonymous
+class MaterialParameterGathererJobPrivate : public Qt3DCore::QAspectJobPrivate
+{
+public:
+ MaterialParameterGathererJobPrivate(MaterialParameterGathererJob *q) : q_ptr(q) { }
+ ~MaterialParameterGathererJobPrivate() override = default;
+
+ bool isRequired() const override;
+ void postFrame(Qt3DCore::QAspectManager *manager) override;
+
+ MaterialParameterGathererJob *q_ptr;
+ Q_DECLARE_PUBLIC(MaterialParameterGathererJob)
+};
+
+bool MaterialParameterGathererJobPrivate::isRequired() const
+{
+ return !q_ptr->m_handles.isEmpty();
+}
+
+void MaterialParameterGathererJobPrivate::postFrame(Qt3DCore::QAspectManager *manager)
+{
+ Q_UNUSED(manager)
+ materialParameterGathererCounter = 0;
+}
+
MaterialParameterGathererJob::MaterialParameterGathererJob()
- : Qt3DCore::QAspectJob()
+ : Qt3DCore::QAspectJob(*new MaterialParameterGathererJobPrivate(this))
, m_manager(nullptr)
, m_techniqueFilter(nullptr)
, m_renderPassFilter(nullptr)
diff --git a/src/plugins/renderers/opengl/jobs/materialparametergathererjob_p.h b/src/plugins/renderers/opengl/jobs/materialparametergathererjob_p.h
index cd5af8124..8c9997827 100644
--- a/src/plugins/renderers/opengl/jobs/materialparametergathererjob_p.h
+++ b/src/plugins/renderers/opengl/jobs/materialparametergathererjob_p.h
@@ -71,6 +71,7 @@ namespace OpenGL {
class Renderer;
// TO be executed for each FrameGraph branch with a given RenderPassFilter/TechniqueFilter
+class MaterialParameterGathererJobPrivate;
class Q_AUTOTEST_EXPORT MaterialParameterGathererJob : public Qt3DCore::QAspectJob
{
@@ -96,6 +97,8 @@ private:
// Material id to array of RenderPasse with parameters
MaterialParameterGathererData m_parameters;
QVector<HMaterial> m_handles;
+
+ Q_DECLARE_PRIVATE(MaterialParameterGathererJob)
};
typedef QSharedPointer<MaterialParameterGathererJob> MaterialParameterGathererJobPtr;
diff --git a/src/plugins/renderers/opengl/jobs/renderviewcommandbuilderjob.cpp b/src/plugins/renderers/opengl/jobs/renderviewcommandbuilderjob.cpp
index d8a33b693..d5e17e0bf 100644
--- a/src/plugins/renderers/opengl/jobs/renderviewcommandbuilderjob.cpp
+++ b/src/plugins/renderers/opengl/jobs/renderviewcommandbuilderjob.cpp
@@ -53,8 +53,32 @@ namespace {
int renderViewInstanceCounter = 0;
} // anonymous
+class RenderViewCommandBuilderJobPrivate : public Qt3DCore::QAspectJobPrivate
+{
+public:
+ RenderViewCommandBuilderJobPrivate(RenderViewCommandBuilderJob *q) : q_ptr(q) { }
+ ~RenderViewCommandBuilderJobPrivate() override = default;
+
+ bool isRequired() const override;
+ void postFrame(Qt3DCore::QAspectManager *manager) override;
+
+ RenderViewCommandBuilderJob *q_ptr;
+ Q_DECLARE_PUBLIC(RenderViewCommandBuilderJob)
+};
+
+bool RenderViewCommandBuilderJobPrivate::isRequired() const
+{
+ return q_ptr->m_renderView && !q_ptr->m_renderView->noDraw() && q_ptr->m_count > 0;
+}
+
+void RenderViewCommandBuilderJobPrivate::postFrame(Qt3DCore::QAspectManager *manager)
+{
+ Q_UNUSED(manager)
+ renderViewInstanceCounter = 0;
+}
+
RenderViewCommandBuilderJob::RenderViewCommandBuilderJob()
- : Qt3DCore::QAspectJob()
+ : Qt3DCore::QAspectJob(*new RenderViewCommandBuilderJobPrivate(this))
, m_offset(0)
, m_count(0)
, m_renderView(nullptr)
@@ -64,18 +88,14 @@ RenderViewCommandBuilderJob::RenderViewCommandBuilderJob()
void RenderViewCommandBuilderJob::run()
{
- if (!m_renderView->noDraw()) {
- if (m_count == 0)
- return;
-
- const bool isDraw = !m_renderView->isCompute();
- if (isDraw)
- m_commandData = m_renderView->buildDrawRenderCommands(m_entities, m_offset, m_count);
- else
- m_commandData = m_renderView->buildComputeRenderCommands(m_entities, m_offset, m_count);
- }
+ const bool isDraw = !m_renderView->isCompute();
+ if (isDraw)
+ m_commandData = m_renderView->buildDrawRenderCommands(m_entities, m_offset, m_count);
+ else
+ m_commandData = m_renderView->buildComputeRenderCommands(m_entities, m_offset, m_count);
}
+
} // OpenGL
} // Render
diff --git a/src/plugins/renderers/opengl/jobs/renderviewcommandbuilderjob_p.h b/src/plugins/renderers/opengl/jobs/renderviewcommandbuilderjob_p.h
index e9f8bb10a..52c055285 100644
--- a/src/plugins/renderers/opengl/jobs/renderviewcommandbuilderjob_p.h
+++ b/src/plugins/renderers/opengl/jobs/renderviewcommandbuilderjob_p.h
@@ -63,6 +63,8 @@ namespace Render {
namespace OpenGL {
+class RenderViewCommandBuilderJobPrivate;
+
class Q_AUTOTEST_EXPORT RenderViewCommandBuilderJob : public Qt3DCore::QAspectJob
{
public:
@@ -85,6 +87,8 @@ private:
RenderView *m_renderView;
QVector<Entity *> m_entities;
EntityRenderCommandData m_commandData;
+
+ Q_DECLARE_PRIVATE(RenderViewCommandBuilderJob)
};
typedef QSharedPointer<RenderViewCommandBuilderJob> RenderViewCommandBuilderJobPtr;
diff --git a/src/plugins/renderers/opengl/jobs/renderviewcommandupdaterjob.cpp b/src/plugins/renderers/opengl/jobs/renderviewcommandupdaterjob.cpp
index ed0854ecf..9bbdf50fd 100644
--- a/src/plugins/renderers/opengl/jobs/renderviewcommandupdaterjob.cpp
+++ b/src/plugins/renderers/opengl/jobs/renderviewcommandupdaterjob.cpp
@@ -60,17 +60,16 @@ public:
RenderViewCommandUpdaterJobPrivate(RenderViewCommandUpdaterJob *q) : q_ptr(q) { }
~RenderViewCommandUpdaterJobPrivate() override = default;
- bool isRequired() override;
+ bool isRequired() const override;
void postFrame(Qt3DCore::QAspectManager *manager) override;
RenderViewCommandUpdaterJob *q_ptr;
Q_DECLARE_PUBLIC(RenderViewCommandUpdaterJob)
};
-bool RenderViewCommandUpdaterJobPrivate::isRequired()
+bool RenderViewCommandUpdaterJobPrivate::isRequired() const
{
- Q_Q(RenderViewCommandUpdaterJob);
-
+ Q_Q(const RenderViewCommandUpdaterJob);
return q->m_renderView && !q->m_renderView->noDraw() && q->m_count > 0;
}
diff --git a/src/plugins/renderers/opengl/jobs/renderviewjobutils.cpp b/src/plugins/renderers/opengl/jobs/renderviewjobutils.cpp
index d4835054b..8774ac368 100644
--- a/src/plugins/renderers/opengl/jobs/renderviewjobutils.cpp
+++ b/src/plugins/renderers/opengl/jobs/renderviewjobutils.cpp
@@ -473,7 +473,10 @@ UniformBlockValueBuilder::~UniformBlockValueBuilder()
{
}
-void UniformBlockValueBuilder::buildActiveUniformNameValueMapHelper(ShaderData *currentShaderData, const QString &blockName, const QString &qmlPropertyName, const QVariant &value)
+void UniformBlockValueBuilder::buildActiveUniformNameValueMapHelper(const ShaderData *currentShaderData,
+ const QString &blockName,
+ const QString &qmlPropertyName,
+ const QVariant &value)
{
// In the end, values are either scalar or a scalar array
// Composed elements (structs, structs array) are simplified into simple scalars
@@ -534,7 +537,9 @@ void UniformBlockValueBuilder::buildActiveUniformNameValueMapHelper(ShaderData *
}
}
-void UniformBlockValueBuilder::buildActiveUniformNameValueMapStructHelper(ShaderData *rShaderData, const QString &blockName, const QString &qmlPropertyName)
+void UniformBlockValueBuilder::buildActiveUniformNameValueMapStructHelper(const ShaderData *rShaderData,
+ const QString &blockName,
+ const QString &qmlPropertyName)
{
const QHash<QString, ShaderData::PropertyValue> &properties = rShaderData->properties();
auto it = properties.begin();
diff --git a/src/plugins/renderers/opengl/jobs/renderviewjobutils_p.h b/src/plugins/renderers/opengl/jobs/renderviewjobutils_p.h
index 7b5ba2bfd..bc5bfd8aa 100644
--- a/src/plugins/renderers/opengl/jobs/renderviewjobutils_p.h
+++ b/src/plugins/renderers/opengl/jobs/renderviewjobutils_p.h
@@ -162,11 +162,11 @@ struct Q_AUTOTEST_EXPORT UniformBlockValueBuilder
QT3D_ALIGNED_MALLOC_AND_FREE()
- void buildActiveUniformNameValueMapHelper(ShaderData *currentShaderData,
+ void buildActiveUniformNameValueMapHelper(const ShaderData *currentShaderData,
const QString &blockName,
const QString &qmlPropertyName,
const QVariant &value);
- void buildActiveUniformNameValueMapStructHelper(ShaderData *rShaderData,
+ void buildActiveUniformNameValueMapStructHelper(const ShaderData *rShaderData,
const QString &blockName,
const QString &qmlPropertyName = QString());
diff --git a/src/plugins/renderers/opengl/opengl.pro b/src/plugins/renderers/opengl/opengl.pro
index ebd4ff5d1..f098513e8 100644
--- a/src/plugins/renderers/opengl/opengl.pro
+++ b/src/plugins/renderers/opengl/opengl.pro
@@ -1,5 +1,8 @@
TARGET = openglrenderer
+# We use QT_AUTOTEST_EXPORT to test the plug-ins, which needs QT_BUILDING_QT
+DEFINES += QT_BUILDING_QT
+
include(opengl.pri)
SOURCES += \
diff --git a/src/plugins/renderers/opengl/renderer/gllights.cpp b/src/plugins/renderers/opengl/renderer/gllights.cpp
new file mode 100644
index 000000000..c87fd766b
--- /dev/null
+++ b/src/plugins/renderers/opengl/renderer/gllights.cpp
@@ -0,0 +1,345 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 <Qt3DRender/private/stringtoint_p.h>
+#include "gllights_p.h"
+
+#define LIGHT_POSITION_NAME QLatin1String(".position")
+#define LIGHT_TYPE_NAME QLatin1String(".type")
+#define LIGHT_COLOR_NAME QLatin1String(".color")
+#define LIGHT_INTENSITY_NAME QLatin1String(".intensity")
+#define LIGHT_DIRECTION_NAME QLatin1String(".direction")
+#define LIGHT_LINEAR_ATTENUATION_NAME QLatin1String(".linearAttenuation")
+#define LIGHT_QUADRATIC_ATTENUATION_NAME QLatin1String(".quadraticAttenuation")
+#define LIGHT_CONSTANT_ATTENUATION_NAME QLatin1String(".constantAttenuation")
+#define LIGHT_CUT_OFF_ANGLE_NAME QLatin1String(".cutOffAngle")
+
+#define DECLARE_LIGHT_STRUCT_NAME(idx)\
+ QLatin1String("lights[") + QLatin1Char(char('0' + idx)) + QLatin1Char(']')
+
+#define DECLARE_LIGHT_POSITION_NAME(idx)\
+ StringToInt::lookupId(LIGHT_STRUCT_NAMES[idx] + LIGHT_POSITION_NAME)
+
+#define DECLARE_LIGHT_TYPE_NAME(idx)\
+ StringToInt::lookupId(LIGHT_STRUCT_NAMES[idx] + LIGHT_TYPE_NAME)
+
+#define DECLARE_LIGHT_COLOR_NAME(idx)\
+ StringToInt::lookupId(LIGHT_STRUCT_NAMES[idx] + LIGHT_COLOR_NAME)
+
+#define DECLARE_LIGHT_INTENSITY_NAME(idx)\
+ StringToInt::lookupId(LIGHT_STRUCT_NAMES[idx] + LIGHT_INTENSITY_NAME)
+
+#define DECLARE_LIGHT_DIRECTION_NAME(idx)\
+ StringToInt::lookupId(LIGHT_STRUCT_NAMES[idx] + LIGHT_DIRECTION_NAME)
+
+#define DECLARE_LIGHT_LINEAR_ATTENUATION_NAME(idx)\
+ StringToInt::lookupId(LIGHT_STRUCT_NAMES[idx] + LIGHT_LINEAR_ATTENUATION_NAME)
+
+#define DECLARE_LIGHT_QUADRATIC_ATTENUATION_NAME(idx)\
+ StringToInt::lookupId(LIGHT_STRUCT_NAMES[idx] + LIGHT_QUADRATIC_ATTENUATION_NAME)
+
+#define DECLARE_LIGHT_CONSTANT_ATTENUATION_NAME(idx)\
+ StringToInt::lookupId(LIGHT_STRUCT_NAMES[idx] + LIGHT_CONSTANT_ATTENUATION_NAME)
+
+#define DECLARE_LIGHT_CUT_OFF_ANGLE_NAME(idx)\
+ StringToInt::lookupId(LIGHT_STRUCT_NAMES[idx] + LIGHT_CUT_OFF_ANGLE_NAME)
+
+#define DECLARE_LIGHT_STRUCT_UNROLL_NAME(idx)\
+ QLatin1String("light_") + QLatin1Char(char('0' + idx))
+
+#define DECLARE_LIGHT_POSITION_UNROLL_NAME(idx)\
+ StringToInt::lookupId(LIGHT_STRUCT_UNROLL_NAMES[idx] + LIGHT_POSITION_NAME)
+
+#define DECLARE_LIGHT_TYPE_UNROLL_NAME(idx)\
+ StringToInt::lookupId(LIGHT_STRUCT_UNROLL_NAMES[idx] + LIGHT_TYPE_NAME)
+
+#define DECLARE_LIGHT_COLOR_UNROLL_NAME(idx)\
+ StringToInt::lookupId(LIGHT_STRUCT_UNROLL_NAMES[idx] + LIGHT_COLOR_NAME)
+
+#define DECLARE_LIGHT_INTENSITY_UNROLL_NAME(idx)\
+ StringToInt::lookupId(LIGHT_STRUCT_UNROLL_NAMES[idx] + LIGHT_INTENSITY_NAME)
+
+#define DECLARE_LIGHT_DIRECTION_UNROLL_NAME(idx)\
+ StringToInt::lookupId(LIGHT_STRUCT_UNROLL_NAMES[idx] + LIGHT_DIRECTION_NAME)
+
+#define DECLARE_LIGHT_LINEAR_ATTENUATION_UNROLL_NAME(idx)\
+ StringToInt::lookupId(LIGHT_STRUCT_UNROLL_NAMES[idx] + LIGHT_LINEAR_ATTENUATION_NAME)
+
+#define DECLARE_LIGHT_QUADRATIC_ATTENUATION_UNROLL_NAME(idx)\
+ StringToInt::lookupId(LIGHT_STRUCT_UNROLL_NAMES[idx] + LIGHT_QUADRATIC_ATTENUATION_NAME)
+
+#define DECLARE_LIGHT_CONSTANT_ATTENUATION_UNROLL_NAME(idx)\
+ StringToInt::lookupId(LIGHT_STRUCT_UNROLL_NAMES[idx] + LIGHT_CONSTANT_ATTENUATION_NAME)
+
+#define DECLARE_LIGHT_CUT_OFF_ANGLE_UNROLL_NAME(idx)\
+ StringToInt::lookupId(LIGHT_STRUCT_UNROLL_NAMES[idx] + LIGHT_CUT_OFF_ANGLE_NAME)
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+namespace Render {
+namespace OpenGL {
+
+int GLLights::LIGHT_COUNT_NAME_ID = StringToInt::lookupId(QLatin1String("lightCount"));
+
+QString GLLights::LIGHT_STRUCT_NAMES[MAX_LIGHTS] = {
+ DECLARE_LIGHT_STRUCT_NAME(0),
+ DECLARE_LIGHT_STRUCT_NAME(1),
+ DECLARE_LIGHT_STRUCT_NAME(2),
+ DECLARE_LIGHT_STRUCT_NAME(3),
+ DECLARE_LIGHT_STRUCT_NAME(4),
+ DECLARE_LIGHT_STRUCT_NAME(5),
+ DECLARE_LIGHT_STRUCT_NAME(6),
+ DECLARE_LIGHT_STRUCT_NAME(7)
+};
+
+int GLLights::LIGHT_POSITION_NAMES[MAX_LIGHTS] = {
+ DECLARE_LIGHT_POSITION_NAME(0),
+ DECLARE_LIGHT_POSITION_NAME(1),
+ DECLARE_LIGHT_POSITION_NAME(2),
+ DECLARE_LIGHT_POSITION_NAME(3),
+ DECLARE_LIGHT_POSITION_NAME(4),
+ DECLARE_LIGHT_POSITION_NAME(5),
+ DECLARE_LIGHT_POSITION_NAME(6),
+ DECLARE_LIGHT_POSITION_NAME(7)
+};
+
+int GLLights::LIGHT_TYPE_NAMES[MAX_LIGHTS] = {
+ DECLARE_LIGHT_TYPE_NAME(0),
+ DECLARE_LIGHT_TYPE_NAME(1),
+ DECLARE_LIGHT_TYPE_NAME(2),
+ DECLARE_LIGHT_TYPE_NAME(3),
+ DECLARE_LIGHT_TYPE_NAME(4),
+ DECLARE_LIGHT_TYPE_NAME(5),
+ DECLARE_LIGHT_TYPE_NAME(6),
+ DECLARE_LIGHT_TYPE_NAME(7)
+};
+
+int GLLights::LIGHT_COLOR_NAMES[MAX_LIGHTS] = {
+ DECLARE_LIGHT_COLOR_NAME(0),
+ DECLARE_LIGHT_COLOR_NAME(1),
+ DECLARE_LIGHT_COLOR_NAME(2),
+ DECLARE_LIGHT_COLOR_NAME(3),
+ DECLARE_LIGHT_COLOR_NAME(4),
+ DECLARE_LIGHT_COLOR_NAME(5),
+ DECLARE_LIGHT_COLOR_NAME(6),
+ DECLARE_LIGHT_COLOR_NAME(7)
+};
+
+int GLLights::LIGHT_INTENSITY_NAMES[MAX_LIGHTS] = {
+ DECLARE_LIGHT_INTENSITY_NAME(0),
+ DECLARE_LIGHT_INTENSITY_NAME(1),
+ DECLARE_LIGHT_INTENSITY_NAME(2),
+ DECLARE_LIGHT_INTENSITY_NAME(3),
+ DECLARE_LIGHT_INTENSITY_NAME(4),
+ DECLARE_LIGHT_INTENSITY_NAME(5),
+ DECLARE_LIGHT_INTENSITY_NAME(6),
+ DECLARE_LIGHT_INTENSITY_NAME(7)
+};
+
+int GLLights::LIGHT_DIRECTION_NAMES[MAX_LIGHTS] = {
+ DECLARE_LIGHT_DIRECTION_NAME(0),
+ DECLARE_LIGHT_DIRECTION_NAME(1),
+ DECLARE_LIGHT_DIRECTION_NAME(2),
+ DECLARE_LIGHT_DIRECTION_NAME(3),
+ DECLARE_LIGHT_DIRECTION_NAME(4),
+ DECLARE_LIGHT_DIRECTION_NAME(5),
+ DECLARE_LIGHT_DIRECTION_NAME(6),
+ DECLARE_LIGHT_DIRECTION_NAME(7)
+};
+
+int GLLights::LIGHT_LINEAR_ATTENUATION_NAMES[MAX_LIGHTS] = {
+ DECLARE_LIGHT_LINEAR_ATTENUATION_NAME(0),
+ DECLARE_LIGHT_LINEAR_ATTENUATION_NAME(1),
+ DECLARE_LIGHT_LINEAR_ATTENUATION_NAME(2),
+ DECLARE_LIGHT_LINEAR_ATTENUATION_NAME(3),
+ DECLARE_LIGHT_LINEAR_ATTENUATION_NAME(4),
+ DECLARE_LIGHT_LINEAR_ATTENUATION_NAME(5),
+ DECLARE_LIGHT_LINEAR_ATTENUATION_NAME(6),
+ DECLARE_LIGHT_LINEAR_ATTENUATION_NAME(7)
+};
+
+int GLLights::LIGHT_QUADRATIC_ATTENUATION_NAMES[MAX_LIGHTS] = {
+ DECLARE_LIGHT_QUADRATIC_ATTENUATION_NAME(0),
+ DECLARE_LIGHT_QUADRATIC_ATTENUATION_NAME(1),
+ DECLARE_LIGHT_QUADRATIC_ATTENUATION_NAME(2),
+ DECLARE_LIGHT_QUADRATIC_ATTENUATION_NAME(3),
+ DECLARE_LIGHT_QUADRATIC_ATTENUATION_NAME(4),
+ DECLARE_LIGHT_QUADRATIC_ATTENUATION_NAME(5),
+ DECLARE_LIGHT_QUADRATIC_ATTENUATION_NAME(6),
+ DECLARE_LIGHT_QUADRATIC_ATTENUATION_NAME(7)
+};
+
+int GLLights::LIGHT_CONSTANT_ATTENUATION_NAMES[MAX_LIGHTS] = {
+ DECLARE_LIGHT_CONSTANT_ATTENUATION_NAME(0),
+ DECLARE_LIGHT_CONSTANT_ATTENUATION_NAME(1),
+ DECLARE_LIGHT_CONSTANT_ATTENUATION_NAME(2),
+ DECLARE_LIGHT_CONSTANT_ATTENUATION_NAME(3),
+ DECLARE_LIGHT_CONSTANT_ATTENUATION_NAME(4),
+ DECLARE_LIGHT_CONSTANT_ATTENUATION_NAME(5),
+ DECLARE_LIGHT_CONSTANT_ATTENUATION_NAME(6),
+ DECLARE_LIGHT_CONSTANT_ATTENUATION_NAME(7)
+};
+
+int GLLights::LIGHT_CUT_OFF_ANGLE_NAMES[MAX_LIGHTS] = {
+ DECLARE_LIGHT_CUT_OFF_ANGLE_NAME(0),
+ DECLARE_LIGHT_CUT_OFF_ANGLE_NAME(1),
+ DECLARE_LIGHT_CUT_OFF_ANGLE_NAME(2),
+ DECLARE_LIGHT_CUT_OFF_ANGLE_NAME(3),
+ DECLARE_LIGHT_CUT_OFF_ANGLE_NAME(4),
+ DECLARE_LIGHT_CUT_OFF_ANGLE_NAME(5),
+ DECLARE_LIGHT_CUT_OFF_ANGLE_NAME(6),
+ DECLARE_LIGHT_CUT_OFF_ANGLE_NAME(7)
+};
+
+QString GLLights::LIGHT_STRUCT_UNROLL_NAMES[MAX_LIGHTS] = {
+ DECLARE_LIGHT_STRUCT_UNROLL_NAME(0),
+ DECLARE_LIGHT_STRUCT_UNROLL_NAME(1),
+ DECLARE_LIGHT_STRUCT_UNROLL_NAME(2),
+ DECLARE_LIGHT_STRUCT_UNROLL_NAME(3),
+ DECLARE_LIGHT_STRUCT_UNROLL_NAME(4),
+ DECLARE_LIGHT_STRUCT_UNROLL_NAME(5),
+ DECLARE_LIGHT_STRUCT_UNROLL_NAME(6),
+ DECLARE_LIGHT_STRUCT_UNROLL_NAME(7)
+};
+
+int GLLights::LIGHT_POSITION_UNROLL_NAMES[MAX_LIGHTS] = {
+ DECLARE_LIGHT_POSITION_UNROLL_NAME(0),
+ DECLARE_LIGHT_POSITION_UNROLL_NAME(1),
+ DECLARE_LIGHT_POSITION_UNROLL_NAME(2),
+ DECLARE_LIGHT_POSITION_UNROLL_NAME(3),
+ DECLARE_LIGHT_POSITION_UNROLL_NAME(4),
+ DECLARE_LIGHT_POSITION_UNROLL_NAME(5),
+ DECLARE_LIGHT_POSITION_UNROLL_NAME(6),
+ DECLARE_LIGHT_POSITION_UNROLL_NAME(7)
+};
+
+int GLLights::LIGHT_TYPE_UNROLL_NAMES[MAX_LIGHTS] = {
+ DECLARE_LIGHT_TYPE_UNROLL_NAME(0),
+ DECLARE_LIGHT_TYPE_UNROLL_NAME(1),
+ DECLARE_LIGHT_TYPE_UNROLL_NAME(2),
+ DECLARE_LIGHT_TYPE_UNROLL_NAME(3),
+ DECLARE_LIGHT_TYPE_UNROLL_NAME(4),
+ DECLARE_LIGHT_TYPE_UNROLL_NAME(5),
+ DECLARE_LIGHT_TYPE_UNROLL_NAME(6),
+ DECLARE_LIGHT_TYPE_UNROLL_NAME(7)
+};
+
+int GLLights::LIGHT_COLOR_UNROLL_NAMES[MAX_LIGHTS] = {
+ DECLARE_LIGHT_COLOR_UNROLL_NAME(0),
+ DECLARE_LIGHT_COLOR_UNROLL_NAME(1),
+ DECLARE_LIGHT_COLOR_UNROLL_NAME(2),
+ DECLARE_LIGHT_COLOR_UNROLL_NAME(3),
+ DECLARE_LIGHT_COLOR_UNROLL_NAME(4),
+ DECLARE_LIGHT_COLOR_UNROLL_NAME(5),
+ DECLARE_LIGHT_COLOR_UNROLL_NAME(6),
+ DECLARE_LIGHT_COLOR_UNROLL_NAME(7)
+};
+
+int GLLights::LIGHT_INTENSITY_UNROLL_NAMES[MAX_LIGHTS] = {
+ DECLARE_LIGHT_INTENSITY_UNROLL_NAME(0),
+ DECLARE_LIGHT_INTENSITY_UNROLL_NAME(1),
+ DECLARE_LIGHT_INTENSITY_UNROLL_NAME(2),
+ DECLARE_LIGHT_INTENSITY_UNROLL_NAME(3),
+ DECLARE_LIGHT_INTENSITY_UNROLL_NAME(4),
+ DECLARE_LIGHT_INTENSITY_UNROLL_NAME(5),
+ DECLARE_LIGHT_INTENSITY_UNROLL_NAME(6),
+ DECLARE_LIGHT_INTENSITY_UNROLL_NAME(7)
+};
+
+int GLLights::LIGHT_DIRECTION_UNROLL_NAMES[MAX_LIGHTS] = {
+ DECLARE_LIGHT_DIRECTION_UNROLL_NAME(0),
+ DECLARE_LIGHT_DIRECTION_UNROLL_NAME(1),
+ DECLARE_LIGHT_DIRECTION_UNROLL_NAME(2),
+ DECLARE_LIGHT_DIRECTION_UNROLL_NAME(3),
+ DECLARE_LIGHT_DIRECTION_UNROLL_NAME(4),
+ DECLARE_LIGHT_DIRECTION_UNROLL_NAME(5),
+ DECLARE_LIGHT_DIRECTION_UNROLL_NAME(6),
+ DECLARE_LIGHT_DIRECTION_UNROLL_NAME(7)
+};
+
+int GLLights::LIGHT_LINEAR_ATTENUATION_UNROLL_NAMES[MAX_LIGHTS] = {
+ DECLARE_LIGHT_LINEAR_ATTENUATION_UNROLL_NAME(0),
+ DECLARE_LIGHT_LINEAR_ATTENUATION_UNROLL_NAME(1),
+ DECLARE_LIGHT_LINEAR_ATTENUATION_UNROLL_NAME(2),
+ DECLARE_LIGHT_LINEAR_ATTENUATION_UNROLL_NAME(3),
+ DECLARE_LIGHT_LINEAR_ATTENUATION_UNROLL_NAME(4),
+ DECLARE_LIGHT_LINEAR_ATTENUATION_UNROLL_NAME(5),
+ DECLARE_LIGHT_LINEAR_ATTENUATION_UNROLL_NAME(6),
+ DECLARE_LIGHT_LINEAR_ATTENUATION_UNROLL_NAME(7)
+};
+
+int GLLights::LIGHT_QUADRATIC_ATTENUATION_UNROLL_NAMES[MAX_LIGHTS] = {
+ DECLARE_LIGHT_QUADRATIC_ATTENUATION_UNROLL_NAME(0),
+ DECLARE_LIGHT_QUADRATIC_ATTENUATION_UNROLL_NAME(1),
+ DECLARE_LIGHT_QUADRATIC_ATTENUATION_UNROLL_NAME(2),
+ DECLARE_LIGHT_QUADRATIC_ATTENUATION_UNROLL_NAME(3),
+ DECLARE_LIGHT_QUADRATIC_ATTENUATION_UNROLL_NAME(4),
+ DECLARE_LIGHT_QUADRATIC_ATTENUATION_UNROLL_NAME(5),
+ DECLARE_LIGHT_QUADRATIC_ATTENUATION_UNROLL_NAME(6),
+ DECLARE_LIGHT_QUADRATIC_ATTENUATION_UNROLL_NAME(7)
+};
+
+int GLLights::LIGHT_CONSTANT_ATTENUATION_UNROLL_NAMES[MAX_LIGHTS] = {
+ DECLARE_LIGHT_CONSTANT_ATTENUATION_UNROLL_NAME(0),
+ DECLARE_LIGHT_CONSTANT_ATTENUATION_UNROLL_NAME(1),
+ DECLARE_LIGHT_CONSTANT_ATTENUATION_UNROLL_NAME(2),
+ DECLARE_LIGHT_CONSTANT_ATTENUATION_UNROLL_NAME(3),
+ DECLARE_LIGHT_CONSTANT_ATTENUATION_UNROLL_NAME(4),
+ DECLARE_LIGHT_CONSTANT_ATTENUATION_UNROLL_NAME(5),
+ DECLARE_LIGHT_CONSTANT_ATTENUATION_UNROLL_NAME(6),
+ DECLARE_LIGHT_CONSTANT_ATTENUATION_UNROLL_NAME(7)
+};
+
+int GLLights::LIGHT_CUT_OFF_ANGLE_UNROLL_NAMES[MAX_LIGHTS] = {
+ DECLARE_LIGHT_CUT_OFF_ANGLE_UNROLL_NAME(0),
+ DECLARE_LIGHT_CUT_OFF_ANGLE_UNROLL_NAME(1),
+ DECLARE_LIGHT_CUT_OFF_ANGLE_UNROLL_NAME(2),
+ DECLARE_LIGHT_CUT_OFF_ANGLE_UNROLL_NAME(3),
+ DECLARE_LIGHT_CUT_OFF_ANGLE_UNROLL_NAME(4),
+ DECLARE_LIGHT_CUT_OFF_ANGLE_UNROLL_NAME(5),
+ DECLARE_LIGHT_CUT_OFF_ANGLE_UNROLL_NAME(6),
+ DECLARE_LIGHT_CUT_OFF_ANGLE_UNROLL_NAME(7)
+};
+
+} // namespace OpenGL
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
diff --git a/src/plugins/renderers/opengl/renderer/gllights_p.h b/src/plugins/renderers/opengl/renderer/gllights_p.h
new file mode 100644
index 000000000..90fa91588
--- /dev/null
+++ b/src/plugins/renderers/opengl/renderer/gllights_p.h
@@ -0,0 +1,88 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 QT3DRENDER_RENDER_OPENGL_GLLIGHTS_P_H
+#define QT3DRENDER_RENDER_OPENGL_GLLIGHTS_P_H
+
+#include <QString>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+namespace Render {
+namespace OpenGL {
+
+#define MAX_LIGHTS 8
+
+static_assert (MAX_LIGHTS < 10, "GL_Lights can't use the QChar trick anymore");
+
+struct GLLights
+{
+ static int LIGHT_COUNT_NAME_ID;
+
+ static QString LIGHT_STRUCT_NAMES[MAX_LIGHTS];
+ static int LIGHT_POSITION_NAMES[MAX_LIGHTS];
+ static int LIGHT_TYPE_NAMES[MAX_LIGHTS];
+ static int LIGHT_COLOR_NAMES[MAX_LIGHTS];
+ static int LIGHT_INTENSITY_NAMES[MAX_LIGHTS];
+ static int LIGHT_DIRECTION_NAMES[MAX_LIGHTS];
+ static int LIGHT_LINEAR_ATTENUATION_NAMES[MAX_LIGHTS];
+ static int LIGHT_QUADRATIC_ATTENUATION_NAMES[MAX_LIGHTS];
+ static int LIGHT_CONSTANT_ATTENUATION_NAMES[MAX_LIGHTS];
+ static int LIGHT_CUT_OFF_ANGLE_NAMES[MAX_LIGHTS];
+
+ static QString LIGHT_STRUCT_UNROLL_NAMES[MAX_LIGHTS];
+ static int LIGHT_POSITION_UNROLL_NAMES[MAX_LIGHTS];
+ static int LIGHT_TYPE_UNROLL_NAMES[MAX_LIGHTS];
+ static int LIGHT_COLOR_UNROLL_NAMES[MAX_LIGHTS];
+ static int LIGHT_INTENSITY_UNROLL_NAMES[MAX_LIGHTS];
+ static int LIGHT_DIRECTION_UNROLL_NAMES[MAX_LIGHTS];
+ static int LIGHT_LINEAR_ATTENUATION_UNROLL_NAMES[MAX_LIGHTS];
+ static int LIGHT_QUADRATIC_ATTENUATION_UNROLL_NAMES[MAX_LIGHTS];
+ static int LIGHT_CONSTANT_ATTENUATION_UNROLL_NAMES[MAX_LIGHTS];
+ static int LIGHT_CUT_OFF_ANGLE_UNROLL_NAMES[MAX_LIGHTS];
+};
+
+} // namespace OpenGL
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDER_OPENGL_GLLIGHTS_P_H
diff --git a/src/plugins/renderers/opengl/renderer/glshader.cpp b/src/plugins/renderers/opengl/renderer/glshader.cpp
index 5e92d84c2..564e78a8e 100644
--- a/src/plugins/renderers/opengl/renderer/glshader.cpp
+++ b/src/plugins/renderers/opengl/renderer/glshader.cpp
@@ -42,6 +42,7 @@
#include <Qt3DRender/private/stringtoint_p.h>
#include <graphicscontext_p.h>
#include <logging_p.h>
+#include <gllights_p.h>
QT_BEGIN_NAMESPACE
@@ -51,9 +52,51 @@ namespace Render {
namespace OpenGL {
+namespace {
+
+QVector<int> getLightUniformNameIds()
+{
+ QVector<int> names;
+ names.reserve(MAX_LIGHTS * 18 + 1);
+
+ names << GLLights::LIGHT_COUNT_NAME_ID;
+ for (int i = 0; i < MAX_LIGHTS; ++i) {
+ names << GLLights::LIGHT_TYPE_NAMES[i]
+ << GLLights::LIGHT_COLOR_NAMES[i]
+ << GLLights::LIGHT_POSITION_NAMES[i]
+ << GLLights::LIGHT_INTENSITY_NAMES[i]
+ << GLLights::LIGHT_DIRECTION_NAMES[i]
+ << GLLights::LIGHT_LINEAR_ATTENUATION_NAMES[i]
+ << GLLights::LIGHT_QUADRATIC_ATTENUATION_NAMES[i]
+ << GLLights::LIGHT_CONSTANT_ATTENUATION_NAMES[i]
+ << GLLights::LIGHT_CUT_OFF_ANGLE_NAMES[i]
+ << GLLights::LIGHT_TYPE_UNROLL_NAMES[i]
+ << GLLights::LIGHT_COLOR_UNROLL_NAMES[i]
+ << GLLights::LIGHT_POSITION_UNROLL_NAMES[i]
+ << GLLights::LIGHT_INTENSITY_UNROLL_NAMES[i]
+ << GLLights::LIGHT_DIRECTION_UNROLL_NAMES[i]
+ << GLLights::LIGHT_LINEAR_ATTENUATION_UNROLL_NAMES[i]
+ << GLLights::LIGHT_QUADRATIC_ATTENUATION_UNROLL_NAMES[i]
+ << GLLights::LIGHT_CONSTANT_ATTENUATION_UNROLL_NAMES[i]
+ << GLLights::LIGHT_CUT_OFF_ANGLE_UNROLL_NAMES[i];
+ }
+
+ return names;
+}
+
+template<typename Vector>
+bool fastContains(const Vector &v, int value)
+{
+ return std::binary_search(v.cbegin(), v.cend(), value);
+}
+
+}
+
GLShader::GLShader()
: m_isLoaded(false)
, m_graphicsContext(nullptr)
+ , m_parameterPackSize(0)
+ , m_hasActiveVariables(false)
{
m_shaderCode.resize(static_cast<int>(QShaderProgram::Compute) + 1);
}
@@ -106,7 +149,7 @@ QHash<QString, ShaderUniform> GLShader::activeUniformsForUniformBlock(int blockI
return m_uniformBlockIndexToShaderUniforms.value(blockIndex);
}
-ShaderUniformBlock GLShader::uniformBlockForBlockIndex(int blockIndex)
+ShaderUniformBlock GLShader::uniformBlockForBlockIndex(int blockIndex) const noexcept
{
for (int i = 0, m = m_uniformBlocks.size(); i < m; ++i) {
if (m_uniformBlocks[i].m_index == blockIndex) {
@@ -116,7 +159,7 @@ ShaderUniformBlock GLShader::uniformBlockForBlockIndex(int blockIndex)
return ShaderUniformBlock();
}
-ShaderUniformBlock GLShader::uniformBlockForBlockNameId(int blockNameId)
+ShaderUniformBlock GLShader::uniformBlockForBlockNameId(int blockNameId) const noexcept
{
for (int i = 0, m = m_uniformBlocks.size(); i < m; ++i) {
if (m_uniformBlocks[i].m_nameId == blockNameId) {
@@ -126,7 +169,7 @@ ShaderUniformBlock GLShader::uniformBlockForBlockNameId(int blockNameId)
return ShaderUniformBlock();
}
-ShaderUniformBlock GLShader::uniformBlockForBlockName(const QString &blockName)
+ShaderUniformBlock GLShader::uniformBlockForBlockName(const QString &blockName) const noexcept
{
for (int i = 0, m = m_uniformBlocks.size(); i < m; ++i) {
if (m_uniformBlocks[i].m_name == blockName) {
@@ -136,7 +179,7 @@ ShaderUniformBlock GLShader::uniformBlockForBlockName(const QString &blockName)
return ShaderUniformBlock();
}
-ShaderStorageBlock GLShader::storageBlockForBlockIndex(int blockIndex)
+ShaderStorageBlock GLShader::storageBlockForBlockIndex(int blockIndex) const noexcept
{
for (int i = 0, m = m_shaderStorageBlockNames.size(); i < m; ++i) {
if (m_shaderStorageBlocks[i].m_index == blockIndex)
@@ -145,7 +188,7 @@ ShaderStorageBlock GLShader::storageBlockForBlockIndex(int blockIndex)
return ShaderStorageBlock();
}
-ShaderStorageBlock GLShader::storageBlockForBlockNameId(int blockNameId)
+ShaderStorageBlock GLShader::storageBlockForBlockNameId(int blockNameId) const noexcept
{
for (int i = 0, m = m_shaderStorageBlockNames.size(); i < m; ++i) {
if (m_shaderStorageBlocks[i].m_nameId == blockNameId)
@@ -154,7 +197,7 @@ ShaderStorageBlock GLShader::storageBlockForBlockNameId(int blockNameId)
return ShaderStorageBlock();
}
-ShaderStorageBlock GLShader::storageBlockForBlockName(const QString &blockName)
+ShaderStorageBlock GLShader::storageBlockForBlockName(const QString &blockName) const noexcept
{
for (int i = 0, m = m_shaderStorageBlockNames.size(); i < m; ++i) {
if (m_shaderStorageBlocks[i].m_name == blockName)
@@ -163,6 +206,22 @@ ShaderStorageBlock GLShader::storageBlockForBlockName(const QString &blockName)
return ShaderStorageBlock();
}
+GLShader::ParameterKind GLShader::categorizeVariable(int nameId) const noexcept
+{
+ if (fastContains(m_uniformsNamesIds, nameId))
+ return ParameterKind::Uniform;
+ if (fastContains(m_uniformBlockNamesIds, nameId))
+ return ParameterKind::UBO;
+ if (fastContains(m_shaderStorageBlockNamesIds, nameId))
+ return ParameterKind::SSBO;
+ return ParameterKind::Struct;
+}
+
+bool GLShader::hasUniform(int nameId) const noexcept
+{
+ return m_uniformsNamesIds.contains(nameId);
+}
+
void GLShader::prepareUniforms(ShaderParameterPack &pack)
{
const PackUniformHash &values = pack.uniforms();
@@ -170,14 +229,20 @@ void GLShader::prepareUniforms(ShaderParameterPack &pack)
auto it = values.keys.cbegin();
const auto end = values.keys.cend();
+ const int shaderUniformsCount = m_uniforms.size();
+ const auto uIt = m_uniforms.cbegin();
+
while (it != end) {
// Find if there's a uniform with the same name id
- for (const ShaderUniform &uniform : qAsConst(m_uniforms)) {
- if (uniform.m_nameId == *it) {
- pack.setSubmissionUniform(uniform);
- break;
- }
- }
+
+ int i = 0;
+ const int targetNameId = *it;
+ while (i < shaderUniformsCount && (uIt + i)->m_nameId < targetNameId)
+ ++i;
+
+ if (i < shaderUniformsCount && (uIt + i)->m_nameId == targetNameId)
+ pack.setSubmissionUniformIndex(i);
+
++it;
}
}
@@ -188,7 +253,6 @@ void GLShader::setFragOutputs(const QHash<QString, int> &fragOutputs)
QMutexLocker lock(&m_mutex);
m_fragOutputs = fragOutputs;
}
-// updateDNA();
}
const QHash<QString, int> GLShader::fragOutputs() const
@@ -203,6 +267,7 @@ void GLShader::initializeUniforms(const QVector<ShaderUniform> &uniformsDescript
m_uniformsNames.resize(uniformsDescription.size());
m_uniformsNamesIds.reserve(uniformsDescription.size());
m_standardUniformNamesIds.reserve(5);
+ m_lightUniformsNamesIds.reserve(MAX_LIGHTS * 8 + 1);
QHash<QString, ShaderUniform> activeUniformsInDefaultBlock;
static const QVector<int> standardUniformNameIds = {
@@ -231,14 +296,18 @@ void GLShader::initializeUniforms(const QVector<ShaderUniform> &uniformsDescript
Shader::skinningPaletteNameId,
};
+ static const QVector<int> lightUniformNameIds = getLightUniformNameIds();
+
for (int i = 0, m = uniformsDescription.size(); i < m; i++) {
m_uniformsNames[i] = m_uniforms[i].m_name;
const int nameId = StringToInt::lookupId(m_uniformsNames[i]);
m_uniforms[i].m_nameId = nameId;
- // Is the uniform a Qt3D "Standard" uniform or a user defined one?
+ // Is the uniform a Qt3D "Standard" uniform, a light uniform or a user defined one?
if (standardUniformNameIds.contains(nameId))
m_standardUniformNamesIds.push_back(nameId);
+ else if (lightUniformNameIds.contains(nameId))
+ m_lightUniformsNamesIds.push_back(nameId);
else
m_uniformsNamesIds.push_back(nameId);
@@ -248,6 +317,18 @@ void GLShader::initializeUniforms(const QVector<ShaderUniform> &uniformsDescript
}
}
m_uniformBlockIndexToShaderUniforms.insert(-1, activeUniformsInDefaultBlock);
+
+ m_parameterPackSize += m_standardUniformNamesIds.size() + m_lightUniformsNamesIds.size() + m_uniformsNamesIds.size();
+ m_hasActiveVariables |= (m_parameterPackSize > 0);
+
+ // Sort by ascending order to make contains check faster
+ std::sort(m_uniformsNamesIds.begin(), m_uniformsNamesIds.end());
+ std::sort(m_lightUniformsNamesIds.begin(), m_lightUniformsNamesIds.end());
+ std::sort(m_standardUniformNamesIds.begin(), m_standardUniformNamesIds.end());
+ std::sort(m_uniforms.begin(), m_uniforms.end(),
+ [] (const ShaderUniform &a, const ShaderUniform &b) {
+ return a.m_nameId < b.m_nameId;
+ });
}
void GLShader::initializeAttributes(const QVector<ShaderAttribute> &attributesDescription)
@@ -261,6 +342,7 @@ void GLShader::initializeAttributes(const QVector<ShaderAttribute> &attributesDe
m_attributeNamesIds[i] = m_attributes[i].m_nameId;
qCDebug(Shaders) << "Active Attribute " << attributesDescription[i].m_name;
}
+ m_hasActiveVariables |= (m_attributeNamesIds.size() > 0);
}
void GLShader::initializeUniformBlocks(const QVector<ShaderUniformBlock> &uniformBlockDescription)
@@ -296,6 +378,12 @@ void GLShader::initializeUniformBlocks(const QVector<ShaderUniformBlock> &unifor
}
m_uniformBlockIndexToShaderUniforms.insert(uniformBlockDescription[i].m_index, activeUniformsInBlock);
}
+
+ m_parameterPackSize += m_uniformsNamesIds.size();
+ m_hasActiveVariables |= (m_parameterPackSize > 0);
+
+ // Sort by ascending order to make contains check faster
+ std::sort(m_uniformBlockNamesIds.begin(), m_uniformBlockNamesIds.end());
}
void GLShader::initializeShaderStorageBlocks(const QVector<ShaderStorageBlock> &shaderStorageBlockDescription)
@@ -310,6 +398,12 @@ void GLShader::initializeShaderStorageBlocks(const QVector<ShaderStorageBlock> &
m_shaderStorageBlocks[i].m_nameId =m_shaderStorageBlockNamesIds[i];
qCDebug(Shaders) << "Initializing Shader Storage Block {" << m_shaderStorageBlockNames[i] << "}";
}
+
+ m_parameterPackSize += m_shaderStorageBlockNamesIds.size();
+ m_hasActiveVariables |= (m_parameterPackSize > 0);
+
+ // Sort by ascending order to make contains check faster
+ std::sort(m_shaderStorageBlockNamesIds.begin(), m_shaderStorageBlockNamesIds.end());
}
} // OpenGL
diff --git a/src/plugins/renderers/opengl/renderer/glshader_p.h b/src/plugins/renderers/opengl/renderer/glshader_p.h
index 6bd5400af..ae447cd18 100644
--- a/src/plugins/renderers/opengl/renderer/glshader_p.h
+++ b/src/plugins/renderers/opengl/renderer/glshader_p.h
@@ -57,6 +57,9 @@
#include <Qt3DRender/qshaderprogram.h>
#include <QMutex>
+#ifdef QT_BUILD_INTERNAL
+ class tst_BenchShaderParameterPack;
+#endif
QT_BEGIN_NAMESPACE
@@ -84,31 +87,44 @@ public:
void setFragOutputs(const QHash<QString, int> &fragOutputs);
const QHash<QString, int> fragOutputs() const;
- inline QVector<int> uniformsNamesIds() const { return m_uniformsNamesIds; }
- inline QVector<int> standardUniformNameIds() const { return m_standardUniformNamesIds; }
- inline QVector<int> uniformBlockNamesIds() const { return m_uniformBlockNamesIds; }
- inline QVector<int> storageBlockNamesIds() const { return m_shaderStorageBlockNamesIds; }
- inline QVector<int> attributeNamesIds() const { return m_attributeNamesIds; }
+ inline const QVector<int> &uniformsNamesIds() const { return m_uniformsNamesIds; }
+ inline const QVector<int> &lightUniformsNamesIds() const { return m_lightUniformsNamesIds; }
+ inline const QVector<int> &standardUniformNameIds() const { return m_standardUniformNamesIds; }
+ inline const QVector<int> &uniformBlockNamesIds() const { return m_uniformBlockNamesIds; }
+ inline const QVector<int> &storageBlockNamesIds() const { return m_shaderStorageBlockNamesIds; }
+ inline const QVector<int> &attributeNamesIds() const { return m_attributeNamesIds; }
QVector<QString> uniformsNames() const;
QVector<QString> attributesNames() const;
QVector<QString> uniformBlockNames() const;
QVector<QString> storageBlockNames() const;
- inline QVector<ShaderUniform> uniforms() const { return m_uniforms; }
- inline QVector<ShaderAttribute> attributes() const { return m_attributes; }
- inline QVector<ShaderUniformBlock> uniformBlocks() const { return m_uniformBlocks; }
- inline QVector<ShaderStorageBlock> storageBlocks() const { return m_shaderStorageBlocks; }
+ inline const QVector<ShaderUniform> &uniforms() const { return m_uniforms; }
+ inline const QVector<ShaderAttribute> &attributes() const { return m_attributes; }
+ inline const QVector<ShaderUniformBlock> &uniformBlocks() const { return m_uniformBlocks; }
+ inline const QVector<ShaderStorageBlock> &storageBlocks() const { return m_shaderStorageBlocks; }
QHash<QString, ShaderUniform> activeUniformsForUniformBlock(int blockIndex) const;
- ShaderUniformBlock uniformBlockForBlockIndex(int blockNameId);
- ShaderUniformBlock uniformBlockForBlockNameId(int blockIndex);
- ShaderUniformBlock uniformBlockForBlockName(const QString &blockName);
+ ShaderUniformBlock uniformBlockForBlockIndex(int blockNameId) const noexcept;
+ ShaderUniformBlock uniformBlockForBlockNameId(int blockIndex) const noexcept;
+ ShaderUniformBlock uniformBlockForBlockName(const QString &blockName) const noexcept;
- ShaderStorageBlock storageBlockForBlockIndex(int blockIndex);
- ShaderStorageBlock storageBlockForBlockNameId(int blockNameId);
- ShaderStorageBlock storageBlockForBlockName(const QString &blockName);
+ ShaderStorageBlock storageBlockForBlockIndex(int blockIndex) const noexcept;
+ ShaderStorageBlock storageBlockForBlockNameId(int blockNameId) const noexcept;
+ ShaderStorageBlock storageBlockForBlockName(const QString &blockName) const noexcept;
+
+ enum ParameterKind {
+ Uniform,
+ UBO,
+ SSBO,
+ Struct
+ };
+ ParameterKind categorizeVariable(int nameId) const noexcept;
+
+ bool hasUniform(int nameId) const noexcept;
+ inline bool hasActiveVariables() const noexcept { return m_hasActiveVariables; }
+ inline int parameterPackSize() const noexcept { return m_parameterPackSize; }
QOpenGLShaderProgram *shaderProgram() { return &m_shader; }
@@ -122,6 +138,7 @@ private:
QVector<QString> m_uniformsNames;
QVector<int> m_uniformsNamesIds;
+ QVector<int> m_lightUniformsNamesIds;
QVector<int> m_standardUniformNamesIds;
QVector<ShaderUniform> m_uniforms;
@@ -141,6 +158,9 @@ private:
QHash<QString, int> m_fragOutputs;
QVector<QByteArray> m_shaderCode;
+ int m_parameterPackSize;
+ int m_hasActiveVariables;
+
// Private so that only GraphicContext can call it
void initializeUniforms(const QVector<ShaderUniform> &uniformsDescription);
void initializeAttributes(const QVector<ShaderAttribute> &attributesDescription);
@@ -148,6 +168,9 @@ private:
void initializeShaderStorageBlocks(const QVector<ShaderStorageBlock> &shaderStorageBlockDescription);
friend class GraphicsContext;
+#ifdef QT_BUILD_INTERNAL
+ friend class ::tst_BenchShaderParameterPack;
+#endif
mutable QMutex m_mutex;
QMetaObject::Connection m_contextConnection;
diff --git a/src/plugins/renderers/opengl/renderer/renderer.cpp b/src/plugins/renderers/opengl/renderer/renderer.cpp
index 3ad9c2818..57ee5ec88 100644
--- a/src/plugins/renderers/opengl/renderer/renderer.cpp
+++ b/src/plugins/renderers/opengl/renderer/renderer.cpp
@@ -995,16 +995,9 @@ void Renderer::prepareCommandsSubmission(const QVector<RenderView *> &renderView
// so we cannot unset its dirtiness at this point
if (rGeometryRenderer->isDirty())
rGeometryRenderer->unsetDirty();
-
- // Prepare the ShaderParameterPack based on the active uniforms of the shader
- shader->prepareUniforms(command.m_parameterPack);
-
} else if (command.m_type == RenderCommand::Compute) {
GLShader *shader = command.m_glShader;
Q_ASSERT(shader);
-
- // Prepare the ShaderParameterPack based on the active uniforms of the shader
- shader->prepareUniforms(command.m_parameterPack);
}
}
}
@@ -1172,6 +1165,10 @@ void Renderer::sendShaderChangesToFrontend(Qt3DCore::QAspectManager *manager)
Shader *s = m_nodesManager->shaderManager()->data(handle);
if (s->requiresFrontendSync()) {
QShaderProgram *frontend = static_cast<decltype(frontend)>(manager->lookupNode(s->peerId()));
+ // Could happen as a backend shader might live beyong the frontend
+ // the time needed to destroy the GLShader assoicated with it.
+ if (!frontend)
+ continue;
QShaderProgramPrivate *dFrontend = static_cast<decltype(dFrontend)>(QNodePrivate::get(frontend));
s->unsetRequiresFrontendSync();
dFrontend->setStatus(s->status());
@@ -1917,10 +1914,31 @@ QVector<Qt3DCore::QAspectJobPtr> Renderer::renderBinJobs()
m_updatedDisableSubtreeEnablers.push_back(node->peerId());
}
+ int idealThreadCount = QThread::idealThreadCount();
+ const QByteArray maxThreadCount = qgetenv("QT3D_MAX_THREAD_COUNT");
+ if (!maxThreadCount.isEmpty()) {
+ bool conversionOK = false;
+ const int maxThreadCountValue = maxThreadCount.toInt(&conversionOK);
+ if (conversionOK)
+ idealThreadCount = maxThreadCountValue;
+ }
+
const int fgBranchCount = m_frameGraphLeaves.size();
+ if (fgBranchCount > 1) {
+ int workBranches = fgBranchCount;
+ for (auto leaf: qAsConst(m_frameGraphLeaves))
+ if (leaf->nodeType() == FrameGraphNode::NoDraw)
+ --workBranches;
+
+ if (idealThreadCount > 4 && workBranches && maxThreadCount.isEmpty())
+ idealThreadCount = qMax(4, idealThreadCount / workBranches);
+ }
+
for (int i = 0; i < fgBranchCount; ++i) {
FrameGraphNode *leaf = m_frameGraphLeaves.at(i);
RenderViewBuilder builder(leaf, i, this);
+ builder.setOptimalJobCount(leaf->nodeType() == FrameGraphNode::NoDraw ? 1 : idealThreadCount);
+
// If we have a new RV (wasn't in the cache before, then it contains no cached data)
const bool isNewRV = !m_cache.leafNodeCache.contains(leaf);
builder.setLayerCacheNeedsToBeRebuilt(layersCacheNeedsToBeRebuilt || isNewRV);
@@ -2046,7 +2064,7 @@ void Renderer::performCompute(const RenderView *, RenderCommand *command)
}
{
Profiling::GLTimeRecorder recorder(Profiling::UniformUpdate, activeProfiler());
- m_submissionContext->setParameters(command->m_parameterPack);
+ m_submissionContext->setParameters(command->m_parameterPack, command->m_glShader);
}
{
Profiling::GLTimeRecorder recorder(Profiling::DispatchCompute, activeProfiler());
@@ -2140,7 +2158,7 @@ bool Renderer::executeCommandsSubmission(const RenderView *rv)
{
Profiling::GLTimeRecorder recorder(Profiling::UniformUpdate, activeProfiler());
//// Update program uniforms
- if (!m_submissionContext->setParameters(command.m_parameterPack)) {
+ if (!m_submissionContext->setParameters(command.m_parameterPack, command.m_glShader)) {
allCommandsIssued = false;
// If we have failed to set uniform (e.g unable to bind a texture)
// we won't perform the draw call which could show invalid content
diff --git a/src/plugins/renderers/opengl/renderer/renderer.pri b/src/plugins/renderers/opengl/renderer/renderer.pri
index 3e3f83c86..1a0240e77 100644
--- a/src/plugins/renderers/opengl/renderer/renderer.pri
+++ b/src/plugins/renderers/opengl/renderer/renderer.pri
@@ -1,6 +1,7 @@
INCLUDEPATH += $$PWD
SOURCES += \
+ $$PWD/gllights.cpp \
$$PWD/openglvertexarrayobject.cpp \
$$PWD/rendercommand.cpp \
$$PWD/renderer.cpp \
@@ -13,6 +14,7 @@ SOURCES += \
$$PWD/commandexecuter.cpp
HEADERS += \
+ $$PWD/gllights_p.h \
$$PWD/openglvertexarrayobject_p.h \
$$PWD/renderercache_p.h \
$$PWD/rendercommand_p.h \
diff --git a/src/plugins/renderers/opengl/renderer/renderer_p.h b/src/plugins/renderers/opengl/renderer/renderer_p.h
index 8cf610efb..d4980464a 100644
--- a/src/plugins/renderers/opengl/renderer/renderer_p.h
+++ b/src/plugins/renderers/opengl/renderer/renderer_p.h
@@ -173,7 +173,7 @@ public:
~Renderer();
void dumpInfo() const override;
- API api() const override { return AbstractRenderer::OpenGL; }
+ API api() const override { return Qt3DRender::API::OpenGL; }
qint64 time() const override;
void setTime(qint64 time) override;
@@ -270,7 +270,7 @@ public:
QSharedPointer<RenderBackendResourceAccessor> resourceAccessor() const override;
- const GraphicsApiFilterData *contextInfo() const;
+ const GraphicsApiFilterData *contextInfo() const override;
SubmissionContext *submissionContext() const;
inline RenderStateSet *defaultRenderState() const { return m_defaultRenderStateSet; }
diff --git a/src/plugins/renderers/opengl/renderer/renderview.cpp b/src/plugins/renderers/opengl/renderer/renderview.cpp
index 99ac88a94..a3a00782e 100644
--- a/src/plugins/renderers/opengl/renderer/renderview.cpp
+++ b/src/plugins/renderers/opengl/renderer/renderview.cpp
@@ -75,7 +75,8 @@
#include <Qt3DCore/qentity.h>
#include <QtGui/qsurface.h>
#include <algorithm>
-
+#include <atomic>
+#include <gllights_p.h>
#include <QDebug>
#if defined(QT3D_RENDER_VIEW_JOB_TIMINGS)
#include <QElapsedTimer>
@@ -87,33 +88,12 @@ namespace Qt3DRender {
namespace Render {
namespace OpenGL {
-
namespace {
// register our QNodeId's as a metatype during program loading
const int Q_DECL_UNUSED qNodeIdTypeId = qMetaTypeId<Qt3DCore::QNodeId>();
-const int MAX_LIGHTS = 8;
-
-#define LIGHT_POSITION_NAME QLatin1String(".position")
-#define LIGHT_TYPE_NAME QLatin1String(".type")
-#define LIGHT_COLOR_NAME QLatin1String(".color")
-#define LIGHT_INTENSITY_NAME QLatin1String(".intensity")
-
-int LIGHT_COUNT_NAME_ID = 0;
-int LIGHT_POSITION_NAMES[MAX_LIGHTS];
-int LIGHT_TYPE_NAMES[MAX_LIGHTS];
-int LIGHT_COLOR_NAMES[MAX_LIGHTS];
-int LIGHT_INTENSITY_NAMES[MAX_LIGHTS];
-QString LIGHT_STRUCT_NAMES[MAX_LIGHTS];
-
-int LIGHT_POSITION_UNROLL_NAMES[MAX_LIGHTS];
-int LIGHT_TYPE_UNROLL_NAMES[MAX_LIGHTS];
-int LIGHT_COLOR_UNROLL_NAMES[MAX_LIGHTS];
-int LIGHT_INTENSITY_UNROLL_NAMES[MAX_LIGHTS];
-QString LIGHT_STRUCT_UNROLL_NAMES[MAX_LIGHTS];
-
-bool wasInitialized = false;
+std::atomic_bool wasInitialized{};
} // anonymous namespace
@@ -168,9 +148,10 @@ static Matrix4x4 getProjectionMatrix(const CameraLens *lens)
}
UniformValue RenderView::standardUniformValue(RenderView::StandardUniform standardUniformType,
- Entity *entity,
- const Matrix4x4 &model) const
+ const Entity *entity) const
{
+ const Matrix4x4 &model = *(entity->worldTransform());
+
switch (standardUniformType) {
case ModelMatrix:
return UniformValue(model);
@@ -263,26 +244,11 @@ RenderView::RenderView()
m_workGroups[1] = 1;
m_workGroups[2] = 1;
- if (Q_UNLIKELY(!wasInitialized)) {
+ if (Q_UNLIKELY(!wasInitialized.exchange(true))) {
// Needed as we can control the init order of static/global variables across compile units
// and this hash relies on the static StringToInt class
- wasInitialized = true;
+
RenderView::ms_standardUniformSetters = RenderView::initializeStandardUniformSetters();
- LIGHT_COUNT_NAME_ID = StringToInt::lookupId(QLatin1String("lightCount"));
- for (int i = 0; i < MAX_LIGHTS; ++i) {
- Q_STATIC_ASSERT_X(MAX_LIGHTS < 10, "can't use the QChar trick anymore");
- LIGHT_STRUCT_NAMES[i] = QLatin1String("lights[") + QLatin1Char(char('0' + i)) + QLatin1Char(']');
- LIGHT_POSITION_NAMES[i] = StringToInt::lookupId(LIGHT_STRUCT_NAMES[i] + LIGHT_POSITION_NAME);
- LIGHT_TYPE_NAMES[i] = StringToInt::lookupId(LIGHT_STRUCT_NAMES[i] + LIGHT_TYPE_NAME);
- LIGHT_COLOR_NAMES[i] = StringToInt::lookupId(LIGHT_STRUCT_NAMES[i] + LIGHT_COLOR_NAME);
- LIGHT_INTENSITY_NAMES[i] = StringToInt::lookupId(LIGHT_STRUCT_NAMES[i] + LIGHT_INTENSITY_NAME);
-
- LIGHT_STRUCT_UNROLL_NAMES[i] = QLatin1String("light_") + QLatin1Char(char('0' + i));
- LIGHT_POSITION_UNROLL_NAMES[i] = StringToInt::lookupId(LIGHT_STRUCT_UNROLL_NAMES[i] + LIGHT_POSITION_NAME);
- LIGHT_TYPE_UNROLL_NAMES[i] = StringToInt::lookupId(LIGHT_STRUCT_UNROLL_NAMES[i] + LIGHT_TYPE_NAME);
- LIGHT_COLOR_UNROLL_NAMES[i] = StringToInt::lookupId(LIGHT_STRUCT_UNROLL_NAMES[i] + LIGHT_COLOR_NAME);
- LIGHT_INTENSITY_UNROLL_NAMES[i] = StringToInt::lookupId(LIGHT_STRUCT_UNROLL_NAMES[i] + LIGHT_INTENSITY_NAME);
- }
}
}
@@ -437,6 +403,7 @@ struct SubRangeSorter<QSortPolicy::Texture>
{
static void sortSubRange(CommandIt begin, const CommandIt end)
{
+#ifndef Q_OS_WIN
std::stable_sort(begin, end, [] (const RenderCommand &a, const RenderCommand &b) {
QVector<ShaderParameterPack::NamedResource> texturesA = a.m_parameterPack.textures();
QVector<ShaderParameterPack::NamedResource> texturesB = b.m_parameterPack.textures();
@@ -455,6 +422,7 @@ struct SubRangeSorter<QSortPolicy::Texture>
return identicalTextureCount < originalTextureASize;
});
+#endif
}
};
@@ -958,21 +926,16 @@ void RenderView::setUniformValue(ShaderParameterPack &uniformPack, int nameId, c
}
void RenderView::setStandardUniformValue(ShaderParameterPack &uniformPack,
- int glslNameId,
int nameId,
- Entity *entity,
- const Matrix4x4 &worldTransform) const
+ const Entity *entity) const
{
- uniformPack.setUniform(glslNameId, standardUniformValue(ms_standardUniformSetters[nameId], entity, worldTransform));
+ uniformPack.setUniform(nameId, standardUniformValue(ms_standardUniformSetters[nameId], entity));
}
void RenderView::setUniformBlockValue(ShaderParameterPack &uniformPack,
- GLShader *shader,
const ShaderUniformBlock &block,
const UniformValue &value) const
{
- Q_UNUSED(shader)
-
if (value.valueType() == UniformValue::NodeId) {
Buffer *buffer = nullptr;
@@ -988,11 +951,9 @@ void RenderView::setUniformBlockValue(ShaderParameterPack &uniformPack,
}
void RenderView::setShaderStorageValue(ShaderParameterPack &uniformPack,
- GLShader *shader,
const ShaderStorageBlock &block,
const UniformValue &value) const
{
- Q_UNUSED(shader)
if (value.valueType() == UniformValue::NodeId) {
Buffer *buffer = nullptr;
if ((buffer = m_manager->bufferManager()->lookupResource(*value.constData<Qt3DCore::QNodeId>())) != nullptr) {
@@ -1006,7 +967,10 @@ void RenderView::setShaderStorageValue(ShaderParameterPack &uniformPack,
}
}
-void RenderView::setDefaultUniformBlockShaderDataValue(ShaderParameterPack &uniformPack, GLShader *shader, ShaderData *shaderData, const QString &structName) const
+void RenderView::setDefaultUniformBlockShaderDataValue(ShaderParameterPack &uniformPack,
+ const GLShader *shader,
+ const ShaderData *shaderData,
+ const QString &structName) const
{
UniformBlockValueBuilder *builder = m_localData.localData();
builder->activeUniformNamesToValue.clear();
@@ -1030,6 +994,42 @@ void RenderView::setDefaultUniformBlockShaderDataValue(ShaderParameterPack &unif
}
}
+void RenderView::applyParameter(const Parameter *param,
+ RenderCommand *command,
+ const GLShader *shader) const noexcept
+{
+ const int nameId = param->nameId();
+ const UniformValue &uniformValue = param->uniformValue();
+ const GLShader::ParameterKind kind = shader->categorizeVariable(nameId);
+
+ switch (kind) {
+ case GLShader::Uniform: {
+ setUniformValue(command->m_parameterPack, nameId, uniformValue);
+ break;
+ }
+ case GLShader::UBO: {
+ setUniformBlockValue(command->m_parameterPack, shader->uniformBlockForBlockNameId(nameId), uniformValue);
+ break;
+ }
+ case GLShader::SSBO: {
+ setShaderStorageValue(command->m_parameterPack, shader->storageBlockForBlockNameId(nameId), uniformValue);
+ break;
+ }
+ case GLShader::Struct: {
+ ShaderData *shaderData = nullptr;
+ if (uniformValue.valueType() == UniformValue::NodeId &&
+ (shaderData = m_manager->shaderDataManager()->lookupResource(*uniformValue.constData<Qt3DCore::QNodeId>())) != nullptr) {
+ // Try to check if we have a struct or array matching a QShaderData parameter
+ setDefaultUniformBlockShaderDataValue(command->m_parameterPack, shader, shaderData, StringToInt::lookupString(nameId));
+ }
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+
void RenderView::setShaderAndUniforms(RenderCommand *command,
ParameterInfoList &parameters,
Entity *entity,
@@ -1052,11 +1052,6 @@ void RenderView::setShaderAndUniforms(RenderCommand *command,
// Builds the QUniformPack, sets shader standard uniforms and store attributes name / glname bindings
// If a parameter is defined and not found in the bindings it is assumed to be a binding of Uniform type with the glsl name
// equals to the parameter name
- const QVector<int> uniformNamesIds = shader->uniformsNamesIds();
- const QVector<int> standardUniformNamesIds = shader->standardUniformNameIds();
- const QVector<int> uniformBlockNamesIds = shader->uniformBlockNamesIds();
- const QVector<int> shaderStorageBlockNamesIds = shader->storageBlockNamesIds();
- const QVector<int> attributeNamesIds = shader->attributeNamesIds();
// Set fragData Name and index
// Later on we might want to relink the shader if attachments have changed
@@ -1073,113 +1068,95 @@ void RenderView::setShaderAndUniforms(RenderCommand *command,
shader->setFragOutputs(fragOutputs);
}
- if (!uniformNamesIds.isEmpty() || !standardUniformNamesIds.isEmpty() ||
- !attributeNamesIds.isEmpty() ||
- !shaderStorageBlockNamesIds.isEmpty() || !attributeNamesIds.isEmpty()) {
+ // Set default attributes
+ command->m_activeAttributes = shader->attributeNamesIds();
- // Set default standard uniforms without bindings
- const Matrix4x4 worldTransform = *(entity->worldTransform());
+ // At this point we know whether the command is a valid draw command or not
+ // We still need to process the uniforms as the command could be a compute command
+ command->m_isValid = !command->m_activeAttributes.empty();
- for (const int uniformNameId : standardUniformNamesIds)
- setStandardUniformValue(command->m_parameterPack, uniformNameId, uniformNameId, entity, worldTransform);
-
- // Set default attributes
- command->m_activeAttributes = attributeNamesIds;
+ if (shader->hasActiveVariables()) {
- // At this point we know whether the command is a valid draw command or not
- // We still need to process the uniforms as the command could be a compute command
- command->m_isValid = !command->m_activeAttributes.empty();
+ // Reserve amount of uniforms we are going to need
+ command->m_parameterPack.reserve(shader->parameterPackSize());
- // Parameters remaining could be
- // -> uniform scalar / vector
- // -> uniform struct / arrays
- // -> uniform block / array (4.3)
- // -> ssbo block / array (4.3)
+ const QVector<int> &standardUniformNamesIds = shader->standardUniformNameIds();
+ for (const int uniformNameId : standardUniformNamesIds)
+ setStandardUniformValue(command->m_parameterPack, uniformNameId, entity);
ParameterInfoList::const_iterator it = parameters.cbegin();
const ParameterInfoList::const_iterator parametersEnd = parameters.cend();
while (it != parametersEnd) {
- Parameter *param = m_manager->data<Parameter, ParameterManager>(it->handle);
- const UniformValue &uniformValue = param->uniformValue();
- if (uniformNamesIds.contains(it->nameId)) { // Parameter is a regular uniform
- setUniformValue(command->m_parameterPack, it->nameId, uniformValue);
- } else if (uniformBlockNamesIds.indexOf(it->nameId) != -1) { // Parameter is a uniform block
- setUniformBlockValue(command->m_parameterPack, shader, shader->uniformBlockForBlockNameId(it->nameId), uniformValue);
- } else if (shaderStorageBlockNamesIds.indexOf(it->nameId) != -1) { // Parameters is a SSBO
- setShaderStorageValue(command->m_parameterPack, shader, shader->storageBlockForBlockNameId(it->nameId), uniformValue);
- } else { // Parameter is a struct
- ShaderData *shaderData = nullptr;
- if (uniformValue.valueType() == UniformValue::NodeId &&
- (shaderData = m_manager->shaderDataManager()->lookupResource(*uniformValue.constData<Qt3DCore::QNodeId>())) != nullptr) {
- // Try to check if we have a struct or array matching a QShaderData parameter
- setDefaultUniformBlockShaderDataValue(command->m_parameterPack, shader, shaderData, StringToInt::lookupString(it->nameId));
- }
- // Otherwise: param unused by current shader
- }
+ const Parameter *param = m_manager->data<Parameter, ParameterManager>(it->handle);
+ applyParameter(param, command, shader);
++it;
}
// Lights
-
- int lightIdx = 0;
- for (const LightSource &lightSource : activeLightSources) {
- if (lightIdx == MAX_LIGHTS)
- break;
- Entity *lightEntity = lightSource.entity;
- const Matrix4x4 lightWorldTransform = *(lightEntity->worldTransform());
- const Vector3D worldPos = lightWorldTransform * Vector3D(0.0f, 0.0f, 0.0f);
- for (Light *light : lightSource.lights) {
- if (!light->isEnabled())
- continue;
-
- ShaderData *shaderData = m_manager->shaderDataManager()->lookupResource(light->shaderData());
- if (!shaderData)
- continue;
-
+ const QVector<int> &lightUniformNamesIds = shader->lightUniformsNamesIds();
+ if (!lightUniformNamesIds.empty()) {
+ int lightIdx = 0;
+ for (const LightSource &lightSource : activeLightSources) {
if (lightIdx == MAX_LIGHTS)
break;
+ Entity *lightEntity = lightSource.entity;
+ const Matrix4x4 lightWorldTransform = *(lightEntity->worldTransform());
+ const Vector3D worldPos = lightWorldTransform * Vector3D(0.0f, 0.0f, 0.0f);
+ for (Light *light : lightSource.lights) {
+ if (!light->isEnabled())
+ continue;
+
+ ShaderData *shaderData = m_manager->shaderDataManager()->lookupResource(light->shaderData());
+ if (!shaderData)
+ continue;
+
+ if (lightIdx == MAX_LIGHTS)
+ break;
- // Note: implicit conversion of values to UniformValue
- setUniformValue(command->m_parameterPack, LIGHT_POSITION_NAMES[lightIdx], worldPos);
- setUniformValue(command->m_parameterPack, LIGHT_TYPE_NAMES[lightIdx], int(QAbstractLight::PointLight));
- setUniformValue(command->m_parameterPack, LIGHT_COLOR_NAMES[lightIdx], Vector3D(1.0f, 1.0f, 1.0f));
- setUniformValue(command->m_parameterPack, LIGHT_INTENSITY_NAMES[lightIdx], 0.5f);
-
- setUniformValue(command->m_parameterPack, LIGHT_POSITION_UNROLL_NAMES[lightIdx], worldPos);
- setUniformValue(command->m_parameterPack, LIGHT_TYPE_UNROLL_NAMES[lightIdx], int(QAbstractLight::PointLight));
- setUniformValue(command->m_parameterPack, LIGHT_COLOR_UNROLL_NAMES[lightIdx], Vector3D(1.0f, 1.0f, 1.0f));
- setUniformValue(command->m_parameterPack, LIGHT_INTENSITY_UNROLL_NAMES[lightIdx], 0.5f);
-
-
- // There is no risk in doing that even if multithreaded
- // since we are sure that a shaderData is unique for a given light
- // and won't ever be referenced as a Component either
- Matrix4x4 *worldTransform = lightEntity->worldTransform();
- if (worldTransform)
- shaderData->updateWorldTransform(*worldTransform);
-
- setDefaultUniformBlockShaderDataValue(command->m_parameterPack, shader, shaderData, LIGHT_STRUCT_NAMES[lightIdx]);
- setDefaultUniformBlockShaderDataValue(command->m_parameterPack, shader, shaderData, LIGHT_STRUCT_UNROLL_NAMES[lightIdx]);
- ++lightIdx;
+ // Note: implicit conversion of values to UniformValue
+ if (lightUniformNamesIds.contains(GLLights::LIGHT_TYPE_NAMES[lightIdx])) {
+ setUniformValue(command->m_parameterPack, GLLights::LIGHT_POSITION_NAMES[lightIdx], worldPos);
+ setUniformValue(command->m_parameterPack, GLLights::LIGHT_TYPE_NAMES[lightIdx], int(QAbstractLight::PointLight));
+ setUniformValue(command->m_parameterPack, GLLights::LIGHT_COLOR_NAMES[lightIdx], Vector3D(1.0f, 1.0f, 1.0f));
+ setUniformValue(command->m_parameterPack, GLLights::LIGHT_INTENSITY_NAMES[lightIdx], 0.5f);
+ } else if (lightUniformNamesIds.contains(GLLights::LIGHT_TYPE_UNROLL_NAMES[lightIdx])) {
+ setUniformValue(command->m_parameterPack, GLLights::LIGHT_POSITION_UNROLL_NAMES[lightIdx], worldPos);
+ setUniformValue(command->m_parameterPack, GLLights::LIGHT_TYPE_UNROLL_NAMES[lightIdx], int(QAbstractLight::PointLight));
+ setUniformValue(command->m_parameterPack, GLLights::LIGHT_COLOR_UNROLL_NAMES[lightIdx], Vector3D(1.0f, 1.0f, 1.0f));
+ setUniformValue(command->m_parameterPack, GLLights::LIGHT_INTENSITY_UNROLL_NAMES[lightIdx], 0.5f);
+ }
+
+ // There is no risk in doing that even if multithreaded
+ // since we are sure that a shaderData is unique for a given light
+ // and won't ever be referenced as a Component either
+ Matrix4x4 *worldTransform = lightEntity->worldTransform();
+ if (worldTransform)
+ shaderData->updateWorldTransform(*worldTransform);
+
+ setDefaultUniformBlockShaderDataValue(command->m_parameterPack, shader, shaderData, GLLights::LIGHT_STRUCT_NAMES[lightIdx]);
+ setDefaultUniformBlockShaderDataValue(command->m_parameterPack, shader, shaderData, GLLights::LIGHT_STRUCT_UNROLL_NAMES[lightIdx]);
+ ++lightIdx;
+ }
}
- }
- if (uniformNamesIds.contains(LIGHT_COUNT_NAME_ID))
- setUniformValue(command->m_parameterPack, LIGHT_COUNT_NAME_ID, UniformValue(qMax((environmentLight ? 0 : 1), lightIdx)));
-
- // If no active light sources and no environment light, add a default light
- if (activeLightSources.isEmpty() && !environmentLight) {
- // Note: implicit conversion of values to UniformValue
- setUniformValue(command->m_parameterPack, LIGHT_POSITION_NAMES[0], Vector3D(10.0f, 10.0f, 0.0f));
- setUniformValue(command->m_parameterPack, LIGHT_TYPE_NAMES[0], int(QAbstractLight::PointLight));
- setUniformValue(command->m_parameterPack, LIGHT_COLOR_NAMES[0], Vector3D(1.0f, 1.0f, 1.0f));
- setUniformValue(command->m_parameterPack, LIGHT_INTENSITY_NAMES[0], 0.5f);
-
- setUniformValue(command->m_parameterPack, LIGHT_POSITION_UNROLL_NAMES[0], Vector3D(10.0f, 10.0f, 0.0f));
- setUniformValue(command->m_parameterPack, LIGHT_TYPE_UNROLL_NAMES[0], int(QAbstractLight::PointLight));
- setUniformValue(command->m_parameterPack, LIGHT_COLOR_UNROLL_NAMES[0], Vector3D(1.0f, 1.0f, 1.0f));
- setUniformValue(command->m_parameterPack, LIGHT_INTENSITY_UNROLL_NAMES[0], 0.5f);
+ setUniformValue(command->m_parameterPack, GLLights::LIGHT_COUNT_NAME_ID, UniformValue(qMax((environmentLight ? 0 : 1), lightIdx)));
+
+ // If no active light sources and no environment light, add a default light
+ if (activeLightSources.isEmpty() && !environmentLight) {
+ // Note: implicit conversion of values to UniformValue
+ if (lightUniformNamesIds.contains(GLLights::LIGHT_TYPE_NAMES[0])) {
+ setUniformValue(command->m_parameterPack, GLLights::LIGHT_POSITION_NAMES[0], Vector3D(10.0f, 10.0f, 0.0f));
+ setUniformValue(command->m_parameterPack, GLLights::LIGHT_TYPE_NAMES[0], int(QAbstractLight::PointLight));
+ setUniformValue(command->m_parameterPack, GLLights::LIGHT_COLOR_NAMES[0], Vector3D(1.0f, 1.0f, 1.0f));
+ setUniformValue(command->m_parameterPack, GLLights::LIGHT_INTENSITY_NAMES[0], 0.5f);
+ } else if (lightUniformNamesIds.contains(GLLights::LIGHT_TYPE_UNROLL_NAMES[lightIdx])) {
+ setUniformValue(command->m_parameterPack, GLLights::LIGHT_POSITION_UNROLL_NAMES[0], Vector3D(10.0f, 10.0f, 0.0f));
+ setUniformValue(command->m_parameterPack, GLLights::LIGHT_TYPE_UNROLL_NAMES[0], int(QAbstractLight::PointLight));
+ setUniformValue(command->m_parameterPack, GLLights::LIGHT_COLOR_UNROLL_NAMES[0], Vector3D(1.0f, 1.0f, 1.0f));
+ setUniformValue(command->m_parameterPack, GLLights::LIGHT_INTENSITY_UNROLL_NAMES[0], 0.5f);
+ }
+ }
}
// Environment Light
@@ -1200,6 +1177,9 @@ void RenderView::setShaderAndUniforms(RenderCommand *command,
}
setUniformValue(command->m_parameterPack, StringToInt::lookupId(QStringLiteral("envLightCount")), envLightCount);
}
+
+ // Prepare the ShaderParameterPack based on the active uniforms of the shader
+ shader->prepareUniforms(command->m_parameterPack);
}
}
@@ -1233,6 +1213,9 @@ bool RenderView::shouldSkipSubmission() const
if (m_clearBuffer != QClearBuffers::None)
return false;
+ if (!m_renderCaptureNodeId.isNull())
+ return false;
+
return true;
}
diff --git a/src/plugins/renderers/opengl/renderer/renderview_p.h b/src/plugins/renderers/opengl/renderer/renderview_p.h
index adab30f4e..6c41ce500 100644
--- a/src/plugins/renderers/opengl/renderer/renderview_p.h
+++ b/src/plugins/renderers/opengl/renderer/renderview_p.h
@@ -370,27 +370,25 @@ private:
static StandardUniformsNameToTypeHash initializeStandardUniformSetters();
UniformValue standardUniformValue(StandardUniform standardUniformType,
- Entity *entity,
- const Matrix4x4 &model) const;
+ const Entity *entity) const;
void setUniformValue(ShaderParameterPack &uniformPack, int nameId, const UniformValue &value) const;
void setStandardUniformValue(ShaderParameterPack &uniformPack,
- int glslNameId,
int nameId,
- Entity *entity,
- const Matrix4x4 &worldTransform) const;
+ const Entity *entity) const;
void setUniformBlockValue(ShaderParameterPack &uniformPack,
- GLShader *shader,
const ShaderUniformBlock &block,
const UniformValue &value) const;
void setShaderStorageValue(ShaderParameterPack &uniformPack,
- GLShader *shader,
const ShaderStorageBlock &block,
const UniformValue &value) const;
void setDefaultUniformBlockShaderDataValue(ShaderParameterPack &uniformPack,
- GLShader *shader,
- ShaderData *shaderData,
+ const GLShader *shader,
+ const ShaderData *shaderData,
const QString &structName) const;
+ void applyParameter(const Parameter *param,
+ RenderCommand *command,
+ const GLShader *shader) const noexcept;
};
} // namespace OpenGL
diff --git a/src/plugins/renderers/opengl/renderer/renderviewbuilder.cpp b/src/plugins/renderers/opengl/renderer/renderviewbuilder.cpp
index 8ed32ff10..b0ac76199 100644
--- a/src/plugins/renderers/opengl/renderer/renderviewbuilder.cpp
+++ b/src/plugins/renderers/opengl/renderer/renderviewbuilder.cpp
@@ -49,18 +49,13 @@ namespace Qt3DRender {
namespace Render {
namespace OpenGL {
-// In some cases having less jobs is better (especially on fast cpus where
-// splitting just adds more overhead). Ideally, we should try to set the value
-// depending on the platform/CPU/nbr of cores
-const int RenderViewBuilder::m_optimalParallelJobCount = QThread::idealThreadCount();
-
namespace {
-int findIdealNumberOfWorkers(int elementCount, int packetSize = 100)
+int findIdealNumberOfWorkers(int elementCount, int packetSize = 100, int maxJobCount = 1)
{
if (elementCount == 0 || packetSize == 0)
return 0;
- return std::min(std::max(elementCount / packetSize, 1), RenderViewBuilder::optimalJobCount());
+ return std::min(std::max(elementCount / packetSize, 1), maxJobCount);
}
@@ -93,9 +88,10 @@ public:
lock.unlock();
// Split among the ideal number of command builders
- const int idealPacketSize = std::min(std::max(100, entities.size() / RenderViewBuilder::optimalJobCount()), entities.size());
+ const int jobCount = m_renderViewCommandBuilderJobs.size();
+ const int idealPacketSize = std::min(std::max(10, entities.size() / jobCount), entities.size());
// Try to split work into an ideal number of workers
- const int m = findIdealNumberOfWorkers(entities.size(), idealPacketSize);
+ const int m = findIdealNumberOfWorkers(entities.size(), idealPacketSize, jobCount);
for (int i = 0; i < m; ++i) {
const RenderViewCommandBuilderJobPtr renderViewCommandBuilder = m_renderViewCommandBuilderJobs.at(i);
@@ -352,9 +348,9 @@ public:
}
// Split among the number of command builders
- // The idealPacketSize is at least 100 entities per worker
- const int idealPacketSize = std::min(std::max(100, filteredCommandData->size() / RenderViewBuilder::optimalJobCount()), filteredCommandData->size());
- const int m = findIdealNumberOfWorkers(filteredCommandData->size(), idealPacketSize);
+ const int jobCount = m_renderViewCommandUpdaterJobs.size();
+ const int idealPacketSize = std::min(std::max(10, filteredCommandData->size() / jobCount), filteredCommandData->size());
+ const int m = findIdealNumberOfWorkers(filteredCommandData->size(), idealPacketSize, jobCount);
for (int i = 0; i < m; ++i) {
const RenderViewCommandUpdaterJobPtr renderViewCommandBuilder = m_renderViewCommandUpdaterJobs.at(i);
@@ -474,6 +470,10 @@ RenderViewBuilder::RenderViewBuilder(Render::FrameGraphNode *leafNode, int rende
, m_syncFilterEntityByLayerJob()
, m_filterProximityJob(Render::FilterProximityDistanceJobPtr::create())
{
+ // In some cases having less jobs is better (especially on fast cpus where
+ // splitting just adds more overhead). Ideally, we should try to set the value
+ // depending on the platform/CPU/nbr of cores
+ m_optimalParallelJobCount = QThread::idealThreadCount();
}
RenderViewInitializerJobPtr RenderViewBuilder::renderViewJob() const
@@ -558,17 +558,16 @@ void RenderViewBuilder::prepareJobs()
m_frustumCullingJob->setRoot(m_renderer->sceneRoot());
if (m_renderCommandCacheNeedsToBeRebuilt) {
-
- m_renderViewCommandBuilderJobs.reserve(RenderViewBuilder::m_optimalParallelJobCount);
- for (auto i = 0; i < RenderViewBuilder::m_optimalParallelJobCount; ++i) {
+ m_renderViewCommandBuilderJobs.reserve(m_optimalParallelJobCount);
+ for (auto i = 0; i < m_optimalParallelJobCount; ++i) {
auto renderViewCommandBuilder = Render::OpenGL::RenderViewCommandBuilderJobPtr::create();
m_renderViewCommandBuilderJobs.push_back(renderViewCommandBuilder);
}
m_syncRenderViewPreCommandBuildingJob = CreateSynchronizerJobPtr(SyncPreCommandBuilding(m_renderViewJob,
- m_renderViewCommandBuilderJobs,
- m_renderer,
- m_leafNode),
- JobTypes::SyncRenderViewPreCommandBuilding);
+ m_renderViewCommandBuilderJobs,
+ m_renderer,
+ m_leafNode),
+ JobTypes::SyncRenderViewPreCommandBuilding);
}
m_renderViewJob->setRenderer(m_renderer);
@@ -577,8 +576,8 @@ void RenderViewBuilder::prepareJobs()
// RenderCommand building is the most consuming task -> split it
// Estimate the number of jobs to create based on the number of entities
- m_renderViewCommandUpdaterJobs.reserve(RenderViewBuilder::m_optimalParallelJobCount);
- for (auto i = 0; i < RenderViewBuilder::m_optimalParallelJobCount; ++i) {
+ m_renderViewCommandUpdaterJobs.reserve(m_optimalParallelJobCount);
+ for (auto i = 0; i < m_optimalParallelJobCount; ++i) {
auto renderViewCommandUpdater = Render::OpenGL::RenderViewCommandUpdaterJobPtr::create();
renderViewCommandUpdater->setRenderer(m_renderer);
m_renderViewCommandUpdaterJobs.push_back(renderViewCommandUpdater);
@@ -587,22 +586,23 @@ void RenderViewBuilder::prepareJobs()
if (m_materialGathererCacheNeedsToBeRebuilt) {
// Since Material gathering is an heavy task, we split it
const QVector<HMaterial> materialHandles = m_renderer->nodeManagers()->materialManager()->activeHandles();
- const int elementsPerJob = materialHandles.size() / RenderViewBuilder::m_optimalParallelJobCount;
- const int lastRemaingElements = materialHandles.size() % RenderViewBuilder::m_optimalParallelJobCount;
- m_materialGathererJobs.reserve(RenderViewBuilder::m_optimalParallelJobCount);
- for (auto i = 0; i < RenderViewBuilder::m_optimalParallelJobCount; ++i) {
- auto materialGatherer = MaterialParameterGathererJobPtr::create();
- materialGatherer->setNodeManagers(m_renderer->nodeManagers());
- if (i == RenderViewBuilder::m_optimalParallelJobCount - 1)
- materialGatherer->setHandles(materialHandles.mid(i * elementsPerJob, elementsPerJob + lastRemaingElements));
- else
- materialGatherer->setHandles(materialHandles.mid(i * elementsPerJob, elementsPerJob));
- m_materialGathererJobs.push_back(materialGatherer);
+ if (materialHandles.count()) {
+ const int elementsPerJob = qMax(materialHandles.size() / m_optimalParallelJobCount, 1);
+ m_materialGathererJobs.reserve(m_optimalParallelJobCount);
+ int elementCount = 0;
+ while (elementCount < materialHandles.size()) {
+ auto materialGatherer = MaterialParameterGathererJobPtr::create();
+ materialGatherer->setNodeManagers(m_renderer->nodeManagers());
+ materialGatherer->setHandles(materialHandles.mid(elementCount, elementsPerJob));
+ m_materialGathererJobs.push_back(materialGatherer);
+
+ elementCount += elementsPerJob;
+ }
}
m_syncMaterialGathererJob = CreateSynchronizerJobPtr(SyncMaterialParameterGatherer(m_materialGathererJobs,
- m_renderer,
- m_leafNode),
- JobTypes::SyncMaterialGatherer);
+ m_renderer,
+ m_leafNode),
+ JobTypes::SyncMaterialGatherer);
}
if (m_layerCacheNeedsToBeRebuilt) {
@@ -615,29 +615,29 @@ void RenderViewBuilder::prepareJobs()
}
m_syncRenderViewPreCommandUpdateJob = CreateSynchronizerJobPtr(SyncRenderViewPreCommandUpdate(m_renderViewJob,
- m_frustumCullingJob,
- m_filterProximityJob,
- m_materialGathererJobs,
- m_renderViewCommandUpdaterJobs,
- m_renderViewCommandBuilderJobs,
- m_renderer,
- m_leafNode,
- m_renderCommandCacheNeedsToBeRebuilt),
- JobTypes::SyncRenderViewPreCommandUpdate);
+ m_frustumCullingJob,
+ m_filterProximityJob,
+ m_materialGathererJobs,
+ m_renderViewCommandUpdaterJobs,
+ m_renderViewCommandBuilderJobs,
+ m_renderer,
+ m_leafNode,
+ m_renderCommandCacheNeedsToBeRebuilt),
+ JobTypes::SyncRenderViewPreCommandUpdate);
m_syncRenderViewPostCommandUpdateJob = CreateSynchronizerJobPtr(SyncRenderViewPostCommandUpdate(m_renderViewJob,
- m_renderViewCommandUpdaterJobs,
- m_renderer),
- JobTypes::SyncRenderViewPostCommandUpdate);
+ m_renderViewCommandUpdaterJobs,
+ m_renderer),
+ JobTypes::SyncRenderViewPostCommandUpdate);
m_syncRenderViewPostInitializationJob = CreateSynchronizerJobPtr(SyncRenderViewPostInitialization(m_renderViewJob,
- m_frustumCullingJob,
- m_filterEntityByLayerJob,
- m_filterProximityJob,
- m_materialGathererJobs,
- m_renderViewCommandUpdaterJobs,
- m_renderViewCommandBuilderJobs),
- JobTypes::SyncRenderViewInitialization);
+ m_frustumCullingJob,
+ m_filterEntityByLayerJob,
+ m_filterProximityJob,
+ m_materialGathererJobs,
+ m_renderViewCommandUpdaterJobs,
+ m_renderViewCommandBuilderJobs),
+ JobTypes::SyncRenderViewInitialization);
}
QVector<Qt3DCore::QAspectJobPtr> RenderViewBuilder::buildJobHierachy() const
@@ -793,9 +793,34 @@ bool RenderViewBuilder::renderCommandCacheNeedsToBeRebuilt() const
return m_renderCommandCacheNeedsToBeRebuilt;
}
-int RenderViewBuilder::optimalJobCount()
+int RenderViewBuilder::defaultJobCount()
+{
+ static int jobCount = 0;
+ if (jobCount)
+ return jobCount;
+
+ const QByteArray maxThreadCount = qgetenv("QT3D_MAX_THREAD_COUNT");
+ if (!maxThreadCount.isEmpty()) {
+ bool conversionOK = false;
+ const int maxThreadCountValue = maxThreadCount.toInt(&conversionOK);
+ if (conversionOK) {
+ jobCount = maxThreadCountValue;
+ return jobCount;
+ }
+ }
+
+ jobCount = QThread::idealThreadCount();
+ return jobCount;
+}
+
+int RenderViewBuilder::optimalJobCount() const
+{
+ return m_optimalParallelJobCount;
+}
+
+void RenderViewBuilder::setOptimalJobCount(int v)
{
- return RenderViewBuilder::m_optimalParallelJobCount;
+ m_optimalParallelJobCount = v;
}
QVector<Entity *> RenderViewBuilder::entitiesInSubset(const QVector<Entity *> &entities, const QVector<Entity *> &subset)
diff --git a/src/plugins/renderers/opengl/renderer/renderviewbuilder_p.h b/src/plugins/renderers/opengl/renderer/renderviewbuilder_p.h
index 54fc98352..17e5fa744 100644
--- a/src/plugins/renderers/opengl/renderer/renderviewbuilder_p.h
+++ b/src/plugins/renderers/opengl/renderer/renderviewbuilder_p.h
@@ -61,7 +61,6 @@
#include <renderviewcommandbuilderjob_p.h>
#include <renderviewcommandupdaterjob_p.h>
#include <materialparametergathererjob_p.h>
-#include <renderviewbuilderjob_p.h>
#include <renderview_p.h>
QT_BEGIN_NAMESPACE
@@ -112,7 +111,10 @@ public:
void setRenderCommandCacheNeedsToBeRebuilt(bool needsToBeRebuilt);
bool renderCommandCacheNeedsToBeRebuilt() const;
- static int optimalJobCount();
+ static int defaultJobCount();
+ int optimalJobCount() const;
+ void setOptimalJobCount(int v);
+
static QVector<Entity *> entitiesInSubset(const QVector<Entity *> &entities, const QVector<Entity *> &subset);
private:
@@ -140,7 +142,7 @@ private:
SynchronizerJobPtr m_syncMaterialGathererJob;
FilterProximityDistanceJobPtr m_filterProximityJob;
- static const int m_optimalParallelJobCount;
+ int m_optimalParallelJobCount;
};
} // OpenGL
diff --git a/src/plugins/renderers/opengl/renderer/shaderparameterpack.cpp b/src/plugins/renderers/opengl/renderer/shaderparameterpack.cpp
index c51595bb7..bc9e9434b 100644
--- a/src/plugins/renderers/opengl/renderer/shaderparameterpack.cpp
+++ b/src/plugins/renderers/opengl/renderer/shaderparameterpack.cpp
@@ -58,6 +58,12 @@ ShaderParameterPack::~ShaderParameterPack()
{
}
+void ShaderParameterPack::reserve(int uniformCount)
+{
+ m_uniforms.reserve(uniformCount);
+ m_submissionUniformIndices.reserve(uniformCount);
+}
+
void ShaderParameterPack::setUniform(const int glslNameId, const UniformValue &val)
{
m_uniforms.insert(glslNameId, val);
@@ -100,9 +106,9 @@ void ShaderParameterPack::setShaderStorageBuffer(BlockToSSBO blockToSSBO)
m_shaderStorageBuffers.push_back(std::move(blockToSSBO));
}
-void ShaderParameterPack::setSubmissionUniform(const ShaderUniform &uniform)
+void ShaderParameterPack::setSubmissionUniformIndex(const int uniformIdx)
{
- m_submissionUniforms.push_back(uniform);
+ m_submissionUniformIndices.push_back(uniformIdx);
}
} // namespace OpenGL
diff --git a/src/plugins/renderers/opengl/renderer/shaderparameterpack_p.h b/src/plugins/renderers/opengl/renderer/shaderparameterpack_p.h
index 31ef4f7ea..bb6bb0dc6 100644
--- a/src/plugins/renderers/opengl/renderer/shaderparameterpack_p.h
+++ b/src/plugins/renderers/opengl/renderer/shaderparameterpack_p.h
@@ -93,8 +93,12 @@ struct PackUniformHash
PackUniformHash()
{
- keys.reserve(10);
- values.reserve(10);
+ }
+
+ void reserve(int count)
+ {
+ keys.reserve(count);
+ values.reserve(count);
}
void insert(int key, const UniformValue &value)
@@ -142,13 +146,14 @@ class Q_AUTOTEST_EXPORT ShaderParameterPack
public:
~ShaderParameterPack();
+ void reserve(int uniformCount);
void setUniform(const int glslNameId, const UniformValue &val);
void setTexture(const int glslNameId, int uniformArrayIndex, Qt3DCore::QNodeId id);
void setImage(const int glslNameId, int uniformArrayIndex, Qt3DCore::QNodeId id);
void setUniformBuffer(BlockToUBO blockToUBO);
void setShaderStorageBuffer(BlockToSSBO blockToSSBO);
- void setSubmissionUniform(const ShaderUniform &uniform);
+ void setSubmissionUniformIndex(const int shaderUniformIndex);
inline PackUniformHash &uniforms() { return m_uniforms; }
inline const PackUniformHash &uniforms() const { return m_uniforms; }
@@ -194,7 +199,7 @@ public:
inline QVector<NamedResource> images() const { return m_images; }
inline QVector<BlockToUBO> uniformBuffers() const { return m_uniformBuffers; }
inline QVector<BlockToSSBO> shaderStorageBuffers() const { return m_shaderStorageBuffers; }
- inline QVector<ShaderUniform> submissionUniforms() const { return m_submissionUniforms; }
+ inline QVector<int> submissionUniformIndices() const { return m_submissionUniformIndices; }
private:
PackUniformHash m_uniforms;
@@ -202,7 +207,7 @@ private:
QVector<NamedResource> m_images;
QVector<BlockToUBO> m_uniformBuffers;
QVector<BlockToSSBO> m_shaderStorageBuffers;
- QVector<ShaderUniform> m_submissionUniforms;
+ QVector<int> m_submissionUniformIndices;
friend class RenderView;
};
diff --git a/src/plugins/renderers/renderers.pro b/src/plugins/renderers/renderers.pro
index dc58bf7fc..f5399ce84 100644
--- a/src/plugins/renderers/renderers.pro
+++ b/src/plugins/renderers/renderers.pro
@@ -7,3 +7,7 @@ QT_FOR_CONFIG += 3drender-private
#SUBDIRS += dummy
qtConfig(qt3d-opengl-renderer): SUBDIRS += opengl
+
+qtConfig(qt3d-rhi-renderer): {
+ qtHaveModule(shadertools): SUBDIRS += rhi
+}
diff --git a/src/plugins/renderers/rhi/graphicshelpers/graphicshelpers.pri b/src/plugins/renderers/rhi/graphicshelpers/graphicshelpers.pri
new file mode 100644
index 000000000..e156d3ce5
--- /dev/null
+++ b/src/plugins/renderers/rhi/graphicshelpers/graphicshelpers.pri
@@ -0,0 +1,9 @@
+#DEFINES += QT3D_RENDER_ASPECT_OPENGL_DEBUG
+
+INCLUDEPATH += $$PWD
+
+HEADERS += \
+ $$PWD/submissioncontext_p.h
+
+SOURCES += \
+ $$PWD/submissioncontext.cpp
diff --git a/src/plugins/renderers/rhi/graphicshelpers/submissioncontext.cpp b/src/plugins/renderers/rhi/graphicshelpers/submissioncontext.cpp
new file mode 100644
index 000000000..5b217929c
--- /dev/null
+++ b/src/plugins/renderers/rhi/graphicshelpers/submissioncontext.cpp
@@ -0,0 +1,1871 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 "submissioncontext_p.h"
+
+#include <Qt3DRender/qgraphicsapifilter.h>
+#include <Qt3DRender/qparameter.h>
+#include <Qt3DRender/qcullface.h>
+#include <Qt3DRender/qfrontface.h>
+#include <Qt3DRender/qdepthtest.h>
+#include <Qt3DRender/qblendequation.h>
+#include <Qt3DRender/qblendequationarguments.h>
+#include <Qt3DRender/qstenciloperationarguments.h>
+#include <Qt3DRender/qstenciltestarguments.h>
+#include <Qt3DRender/private/renderlogging_p.h>
+#include <Qt3DRender/private/shader_p.h>
+#include <Qt3DRender/private/material_p.h>
+#include <Qt3DRender/private/buffer_p.h>
+#include <Qt3DRender/private/attribute_p.h>
+#include <Qt3DRender/private/renderstates_p.h>
+#include <Qt3DRender/private/renderstateset_p.h>
+#include <Qt3DRender/private/rendertarget_p.h>
+#include <Qt3DRender/private/nodemanagers_p.h>
+#include <Qt3DRender/private/buffermanager_p.h>
+#include <Qt3DRender/private/managers_p.h>
+#include <Qt3DRender/private/attachmentpack_p.h>
+#include <Qt3DRender/private/qbuffer_p.h>
+#include <Qt3DRender/private/stringtoint_p.h>
+#include <Qt3DRender/private/vulkaninstance_p.h>
+#include <QGuiApplication>
+#include <texture_p.h>
+#include <rendercommand_p.h>
+#include <renderer_p.h>
+#include <rhiresourcemanagers_p.h>
+#include <renderbuffer_p.h>
+#include <rhishader_p.h>
+#include <QOpenGLShaderProgram>
+
+#include <private/qdebug_p.h>
+#include <QSurface>
+#include <QWindow>
+#include <QShaderBaker>
+
+#ifdef Q_OS_WIN
+#include <QtGui/private/qrhid3d11_p.h>
+#endif
+
+#if defined(Q_OS_MACOS) || defined(Q_OS_IOS)
+#include <QtGui/private/qrhimetal_p.h>
+#endif
+
+#ifndef QT_NO_OPENGL
+#include <QtGui/private/qrhigles2_p.h>
+#endif
+
+#if QT_CONFIG(vulkan)
+#include <QtGui/private/qrhivulkan_p.h>
+#endif
+#include <bitset>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+namespace Render {
+namespace Rhi {
+
+static QHash<unsigned int, SubmissionContext *> static_contexts;
+
+unsigned int nextFreeContextId() noexcept
+{
+ for (unsigned int i = 0; i < 0xffff; ++i) {
+ if (!static_contexts.contains(i))
+ return i;
+ }
+
+ qFatal("Couldn't find free context ID");
+ return 0;
+}
+
+namespace {
+
+RHIBuffer::Type attributeTypeToGLBufferType(QAttribute::AttributeType type) noexcept
+{
+ switch (type) {
+ case QAttribute::VertexAttribute:
+ return RHIBuffer::ArrayBuffer;
+ case QAttribute::IndexAttribute:
+ return RHIBuffer::IndexBuffer;
+ case QAttribute::DrawIndirectAttribute:
+ return RHIBuffer::DrawIndirectBuffer;
+ default:
+ Q_UNREACHABLE();
+ }
+}
+
+void copyGLFramebufferDataToImage(QImage &img, const uchar *srcData, uint stride, uint width,
+ uint height, QAbstractTexture::TextureFormat format) noexcept
+{
+ switch (format) {
+ case QAbstractTexture::RGBA32F: {
+ uchar *srcScanline = const_cast<uchar *>(srcData) + stride * (height - 1);
+ for (uint i = 0; i < height; ++i) {
+ uchar *dstScanline = img.scanLine(i);
+ float *pSrc = reinterpret_cast<float *>(srcScanline);
+ for (uint j = 0; j < width; j++) {
+ *dstScanline++ = (uchar)(255.0f * qBound(0.0f, pSrc[4 * j + 2], 1.0f));
+ *dstScanline++ = (uchar)(255.0f * qBound(0.0f, pSrc[4 * j + 1], 1.0f));
+ *dstScanline++ = (uchar)(255.0f * qBound(0.0f, pSrc[4 * j + 0], 1.0f));
+ *dstScanline++ = (uchar)(255.0f * qBound(0.0f, pSrc[4 * j + 3], 1.0f));
+ }
+ srcScanline -= stride;
+ }
+ } break;
+ default: {
+ uchar *srcScanline = (uchar *)srcData + stride * (height - 1);
+ for (uint i = 0; i < height; ++i) {
+ memcpy(img.scanLine(i), srcScanline, stride);
+ srcScanline -= stride;
+ }
+ } break;
+ }
+}
+
+// Render States Helpers
+
+template<typename GenericState>
+void applyStateHelper(const GenericState *state, QRhiGraphicsPipeline *gp) noexcept
+{
+ Q_UNUSED(state);
+ Q_UNUSED(gp);
+ qWarning() << "RHI Unhandled render state" << typeid(GenericState).name();
+}
+
+void applyStateHelper(const BlendEquationArguments *state, QRhiGraphicsPipeline *gp) noexcept
+{
+ const auto values = state->values();
+
+ // We assume a single color attachment
+ QRhiGraphicsPipeline::TargetBlend targetBlend {};
+
+ const bool hasTargetBlend = gp->cbeginTargetBlends() != gp->cendTargetBlends();
+ if (hasTargetBlend)
+ targetBlend = *(gp->cbeginTargetBlends());
+
+ auto getRHIBlendFactor = [](int arg) {
+ switch (arg) {
+ case QBlendEquationArguments::Zero:
+ return QRhiGraphicsPipeline::Zero;
+ case QBlendEquationArguments::One:
+ return QRhiGraphicsPipeline::One;
+ case QBlendEquationArguments::SourceColor:
+ return QRhiGraphicsPipeline::SrcColor;
+ case QBlendEquationArguments::SourceAlpha:
+ return QRhiGraphicsPipeline::SrcAlpha;
+ // ### Qt 6 Fix values
+ // case QBlendEquationArguments::Source1Alpha:
+ // return QRhiGraphicsPipeline::Src1Alpha;
+ // case QBlendEquationArguments::Source1Color:
+ // return QRhiGraphicsPipeline::Src1Color;
+ case QBlendEquationArguments::DestinationColor:
+ return QRhiGraphicsPipeline::DstColor;
+ case QBlendEquationArguments::DestinationAlpha:
+ return QRhiGraphicsPipeline::DstAlpha;
+ case QBlendEquationArguments::SourceAlphaSaturate:
+ return QRhiGraphicsPipeline::SrcAlphaSaturate;
+ case QBlendEquationArguments::ConstantColor:
+ return QRhiGraphicsPipeline::ConstantColor;
+ case QBlendEquationArguments::ConstantAlpha:
+ return QRhiGraphicsPipeline::ConstantAlpha;
+ case QBlendEquationArguments::OneMinusSourceColor:
+ return QRhiGraphicsPipeline::OneMinusSrcColor;
+ case QBlendEquationArguments::OneMinusSourceAlpha:
+ return QRhiGraphicsPipeline::OneMinusSrcAlpha;
+ case QBlendEquationArguments::OneMinusDestinationAlpha:
+ return QRhiGraphicsPipeline::OneMinusDstAlpha;
+ case QBlendEquationArguments::OneMinusDestinationColor:
+ return QRhiGraphicsPipeline::OneMinusDstColor;
+ case QBlendEquationArguments::OneMinusConstantColor:
+ return QRhiGraphicsPipeline::OneMinusConstantColor;
+ case QBlendEquationArguments::OneMinusConstantAlpha:
+ return QRhiGraphicsPipeline::OneMinusConstantAlpha;
+ case QBlendEquationArguments::OneMinusSource1Alpha:
+ return QRhiGraphicsPipeline::OneMinusSrc1Alpha;
+ case QBlendEquationArguments::OneMinusSource1Color:
+ return QRhiGraphicsPipeline::OneMinusSrc1Color;
+ default:
+ qDebug() << "Unhandled blend equation argument" << arg;
+ return QRhiGraphicsPipeline::Zero;
+ }
+ };
+
+ targetBlend.srcAlpha = getRHIBlendFactor(std::get<2>(values));
+ targetBlend.dstAlpha = getRHIBlendFactor(std::get<3>(values));
+ targetBlend.srcColor = getRHIBlendFactor(std::get<0>(values));
+ targetBlend.dstColor = getRHIBlendFactor(std::get<1>(values));
+ gp->setTargetBlends({ targetBlend });
+}
+
+void applyStateHelper(const BlendEquation *state, QRhiGraphicsPipeline *gp) noexcept
+{
+ const auto values = state->values();
+ const QBlendEquation::BlendFunction equation =
+ static_cast<QBlendEquation::BlendFunction>(std::get<0>(values));
+
+ // We assume a single color attachment
+ QRhiGraphicsPipeline::TargetBlend targetBlend;
+
+ const bool hasTargetBlend = gp->cbeginTargetBlends() != gp->cendTargetBlends();
+ if (hasTargetBlend)
+ targetBlend = *(gp->cbeginTargetBlends());
+
+ auto getRHIBlendOp = [](QBlendEquation::BlendFunction equation) {
+ switch (equation) {
+ case QBlendEquation::Add:
+ return QRhiGraphicsPipeline::Add;
+ case QBlendEquation::Subtract:
+ return QRhiGraphicsPipeline::Subtract;
+ case QBlendEquation::ReverseSubtract:
+ return QRhiGraphicsPipeline::ReverseSubtract;
+ case QBlendEquation::Min:
+ return QRhiGraphicsPipeline::Min;
+ case QBlendEquation::Max:
+ return QRhiGraphicsPipeline::Max;
+ }
+ };
+
+ targetBlend.enable = true;
+ targetBlend.opAlpha = getRHIBlendOp(equation);
+ gp->setTargetBlends({ targetBlend });
+}
+
+void applyStateHelper(const MSAAEnabled *state, QRhiGraphicsPipeline *gp,
+ const QSurfaceFormat &format) noexcept
+{
+ gp->setSampleCount(format.samples());
+}
+
+void applyStateHelper(const DepthTest *state, QRhiGraphicsPipeline *gp) noexcept
+{
+ gp->setDepthTest(true);
+ const QDepthTest::DepthFunction depthFunc =
+ static_cast<QDepthTest::DepthFunction>(std::get<0>(state->values()));
+ switch (depthFunc) {
+ case QDepthTest::Never:
+ gp->setDepthOp(QRhiGraphicsPipeline::Never);
+ break;
+ case QDepthTest::Always:
+ gp->setDepthOp(QRhiGraphicsPipeline::Always);
+ break;
+ case QDepthTest::Less:
+ gp->setDepthOp(QRhiGraphicsPipeline::Less);
+ break;
+ case QDepthTest::LessOrEqual:
+ gp->setDepthOp(QRhiGraphicsPipeline::LessOrEqual);
+ break;
+ case QDepthTest::Equal:
+ gp->setDepthOp(QRhiGraphicsPipeline::Equal);
+ break;
+ case QDepthTest::GreaterOrEqual:
+ gp->setDepthOp(QRhiGraphicsPipeline::GreaterOrEqual);
+ break;
+ case QDepthTest::Greater:
+ gp->setDepthOp(QRhiGraphicsPipeline::Greater);
+ break;
+ case QDepthTest::NotEqual:
+ gp->setDepthOp(QRhiGraphicsPipeline::NotEqual);
+ break;
+ }
+}
+
+void applyStateHelper(const NoDepthMask *state, QRhiGraphicsPipeline *gp) noexcept
+{
+ const auto values = state->values();
+ gp->setDepthWrite(std::get<0>(values));
+}
+
+void applyStateHelper(const CullFace *state, QRhiGraphicsPipeline *gp) noexcept
+{
+ const auto values = state->values();
+ const QCullFace::CullingMode cullingMode =
+ static_cast<QCullFace::CullingMode>(std::get<0>(values));
+ switch (cullingMode) {
+ case QCullFace::NoCulling:
+ gp->setCullMode(QRhiGraphicsPipeline::None);
+ break;
+ case QCullFace::Front:
+ gp->setCullMode(QRhiGraphicsPipeline::Front);
+ break;
+ case QCullFace::Back:
+ gp->setCullMode(QRhiGraphicsPipeline::Back);
+ break;
+ case QCullFace::FrontAndBack:
+ qWarning() << "RHI doesn't handle FrontAndBack CullFace";
+ break;
+ }
+}
+
+void applyStateHelper(const FrontFace *state, QRhiGraphicsPipeline *gp) noexcept
+{
+ const auto values = state->values();
+ const QFrontFace::WindingDirection cullingMode =
+ static_cast<QFrontFace::WindingDirection>(std::get<0>(values));
+
+ switch (cullingMode) {
+ case QFrontFace::ClockWise:
+ gp->setFrontFace(QRhiGraphicsPipeline::CW);
+ break;
+ case QFrontFace::CounterClockWise:
+ gp->setFrontFace(QRhiGraphicsPipeline::CCW);
+ break;
+ }
+}
+
+void applyStateHelper(const StencilTest *state, QRhiGraphicsPipeline *gp) noexcept
+{
+ const auto values = state->values();
+ gp->setStencilTest(true);
+
+ auto getCompareOp = [](int compareOp) {
+ switch (compareOp) {
+ case QStencilTestArguments::Never:
+ return QRhiGraphicsPipeline::Never;
+ case QStencilTestArguments::Always:
+ return QRhiGraphicsPipeline::Always;
+ case QStencilTestArguments::Less:
+ return QRhiGraphicsPipeline::Less;
+ case QStencilTestArguments::LessOrEqual:
+ return QRhiGraphicsPipeline::LessOrEqual;
+ case QStencilTestArguments::Equal:
+ return QRhiGraphicsPipeline::Equal;
+ case QStencilTestArguments::GreaterOrEqual:
+ return QRhiGraphicsPipeline::GreaterOrEqual;
+ case QStencilTestArguments::Greater:
+ return QRhiGraphicsPipeline::Greater;
+ case QStencilTestArguments::NotEqual:
+ return QRhiGraphicsPipeline::NotEqual;
+ default:
+ qDebug() << "Unhandled stencil test argument";
+ return QRhiGraphicsPipeline::Never;
+ }
+ };
+
+ QRhiGraphicsPipeline::StencilOpState frontCompare = gp->stencilFront();
+ frontCompare.compareOp = getCompareOp(std::get<0>(values));
+ gp->setStencilFront(frontCompare);
+
+ QRhiGraphicsPipeline::StencilOpState backCompare = gp->stencilBack();
+ backCompare.compareOp = getCompareOp(std::get<3>(values));
+ gp->setStencilBack(backCompare);
+}
+
+void applyStateHelper(const ColorMask *state, QRhiGraphicsPipeline *gp) noexcept
+{
+ const auto values = state->values();
+
+ // We assume a single color attachment
+ QRhiGraphicsPipeline::TargetBlend targetBlend;
+
+ const bool hasTargetBlend = gp->cbeginTargetBlends() != gp->cendTargetBlends();
+ if (hasTargetBlend)
+ targetBlend = *(gp->cbeginTargetBlends());
+
+ const bool redEnabled = std::get<0>(values);
+ const bool greenEnabled = std::get<1>(values);
+ const bool blueEnabled = std::get<2>(values);
+ const bool alphaEnabled = std::get<3>(values);
+
+ QRhiGraphicsPipeline::ColorMask colorMask;
+ if (redEnabled)
+ colorMask |= QRhiGraphicsPipeline::R;
+ if (greenEnabled)
+ colorMask |= QRhiGraphicsPipeline::G;
+ if (blueEnabled)
+ colorMask |= QRhiGraphicsPipeline::B;
+ if (alphaEnabled)
+ colorMask |= QRhiGraphicsPipeline::A;
+
+ targetBlend.colorWrite = colorMask;
+ gp->setTargetBlends({ targetBlend });
+}
+
+void applyStateHelper(const StencilOp *state, QRhiGraphicsPipeline *gp) noexcept
+{
+ const auto values = state->values();
+ auto getRHIStencilOp = [](int op) {
+ switch (op) {
+ case QStencilOperationArguments::Zero:
+ return QRhiGraphicsPipeline::StencilZero;
+ case QStencilOperationArguments::Keep:
+ return QRhiGraphicsPipeline::Keep;
+ case QStencilOperationArguments::Replace:
+ return QRhiGraphicsPipeline::Replace;
+ case QStencilOperationArguments::Increment:
+ return QRhiGraphicsPipeline::IncrementAndClamp;
+ case QStencilOperationArguments::Decrement:
+ return QRhiGraphicsPipeline::DecrementAndClamp;
+ case QStencilOperationArguments::IncrementWrap:
+ return QRhiGraphicsPipeline::IncrementAndWrap;
+ case QStencilOperationArguments::DecrementWrap:
+ return QRhiGraphicsPipeline::DecrementAndWrap;
+ case QStencilOperationArguments::Invert:
+ return QRhiGraphicsPipeline::Invert;
+ default:
+ qDebug() << "Unhandled stencil operation argument";
+ return QRhiGraphicsPipeline::StencilZero;
+ }
+ };
+
+ QRhiGraphicsPipeline::StencilOpState frontCompare = gp->stencilFront();
+ frontCompare.depthFailOp = getRHIStencilOp(std::get<1>(values));
+ frontCompare.failOp = getRHIStencilOp(std::get<0>(values));
+ frontCompare.passOp = getRHIStencilOp(std::get<2>(values));
+ gp->setStencilFront(frontCompare);
+
+ QRhiGraphicsPipeline::StencilOpState backCompare = gp->stencilBack();
+ backCompare.depthFailOp = getRHIStencilOp(std::get<4>(values));
+ backCompare.failOp = getRHIStencilOp(std::get<3>(values));
+ backCompare.passOp = getRHIStencilOp(std::get<5>(values));
+ gp->setStencilBack(backCompare);
+}
+
+void applyStateHelper(const StencilMask *state, QRhiGraphicsPipeline *gp) noexcept
+{
+ const auto values = state->values();
+ gp->setStencilWriteMask(std::get<0>(values));
+ gp->setStencilReadMask(std::get<1>(values));
+}
+
+static QShader::Stage rhiShaderStage(QShaderProgram::ShaderType type) noexcept
+{
+ switch (type) {
+ case QShaderProgram::Vertex:
+ return QShader::VertexStage;
+ case QShaderProgram::Fragment:
+ return QShader::FragmentStage;
+ case QShaderProgram::TessellationControl:
+ return QShader::TessellationControlStage;
+ case QShaderProgram::TessellationEvaluation:
+ return QShader::TessellationEvaluationStage;
+ case QShaderProgram::Geometry:
+ return QShader::GeometryStage;
+ case QShaderProgram::Compute:
+ return QShader::ComputeStage;
+ default:
+ std::abort();
+ }
+}
+
+} // anonymous
+
+SubmissionContext::SubmissionContext()
+ : m_ownCurrent(true),
+ m_id(nextFreeContextId()),
+ m_surface(nullptr),
+ m_activeShader(nullptr),
+ m_renderTargetFormat(QAbstractTexture::NoFormat),
+ m_material(nullptr),
+ m_activeFBO(0),
+ m_renderer(nullptr),
+ m_uboTempArray(QByteArray(1024, 0)),
+ m_initialized(false),
+ m_maxTextureUnits(0),
+ m_defaultFBO(0),
+ m_rhi(nullptr),
+ m_currentSwapChain(nullptr),
+ m_currentRenderPassDescriptor(nullptr)
+#ifndef QT_NO_OPENGL
+ ,
+ m_fallbackSurface(nullptr)
+#endif
+{
+ static_contexts[m_id] = this;
+ m_contextInfo.m_api = QGraphicsApiFilter::RHI;
+
+ // We set those version numbers because QShaderGenerator wants major > 0
+ m_contextInfo.m_major = 1;
+ m_contextInfo.m_minor = 0;
+}
+
+SubmissionContext::~SubmissionContext()
+{
+ releaseResources();
+
+ Q_ASSERT(static_contexts[m_id] == this);
+ static_contexts.remove(m_id);
+}
+
+void SubmissionContext::initialize()
+{
+ m_initialized = true;
+ // m_textureContext.initialize(this);
+
+ Qt3DRender::API requestedApi = Qt3DRender::API::OpenGL;
+ const auto userRequestedApi = qgetenv("QT3D_RHI_DEFAULT_API").toLower();
+ if (!userRequestedApi.isEmpty()) {
+ if (userRequestedApi == QByteArrayLiteral("opengl")) {
+ requestedApi = Qt3DRender::API::OpenGL;
+ } else if (userRequestedApi == QByteArrayLiteral("vulkan")) {
+ requestedApi = Qt3DRender::API::Vulkan;
+ } else if (userRequestedApi == QByteArrayLiteral("metal")) {
+ requestedApi = Qt3DRender::API::Metal;
+ } else if (userRequestedApi == QByteArrayLiteral("d3d11")) {
+ requestedApi = Qt3DRender::API::DirectX;
+ } else if (userRequestedApi == QByteArrayLiteral("null")) {
+ requestedApi = Qt3DRender::API::Null;
+ }
+ }
+
+ QRhi::Flags rhiFlags = QRhi::EnableDebugMarkers;
+
+#if QT_CONFIG(vulkan)
+ if (requestedApi == Qt3DRender::API::Vulkan) {
+ QRhiVulkanInitParams params;
+ params.inst = &Qt3DRender::staticVulkanInstance();
+ m_rhi = QRhi::create(QRhi::Vulkan, &params, rhiFlags);
+ }
+#endif
+
+#ifdef Q_OS_WIN
+ if (requestedApi == Qt3DRender::API::DirectX) {
+ QRhiD3D11InitParams params;
+ params.enableDebugLayer = true;
+ m_rhi = QRhi::create(QRhi::D3D11, &params, rhiFlags);
+ }
+#endif
+
+#if defined(Q_OS_MACOS) || defined(Q_OS_IOS)
+ if (requestedApi == Qt3DRender::API::Metal) {
+ QRhiMetalInitParams params;
+ m_rhi = QRhi::create(QRhi::Metal, &params, rhiFlags);
+ }
+#endif
+ if (requestedApi == Qt3DRender::API::Null) {
+ QRhiInitParams params;
+ m_rhi = QRhi::create(QRhi::Null, &params, rhiFlags);
+ }
+
+ if (requestedApi != Qt3DRender::API::OpenGL && m_rhi == nullptr) {
+ qWarning() << "RHI: Unable to use requested RHI Api, trying to fall back on OpenGL";
+ requestedApi = Qt3DRender::API::OpenGL;
+ }
+
+ if (requestedApi == Qt3DRender::API::OpenGL) {
+#ifndef QT_NO_OPENGL
+ m_fallbackSurface = QRhiGles2InitParams::newFallbackSurface();
+ QRhiGles2InitParams params;
+ params.format = QSurfaceFormat::defaultFormat();
+ params.fallbackSurface = m_fallbackSurface;
+ m_rhi = QRhi::create(QRhi::OpenGLES2, &params, rhiFlags);
+#else
+ qWarning() << "RHI: OpenGL not supported";
+#endif
+ }
+
+ Q_ASSERT(m_rhi != nullptr);
+}
+
+void SubmissionContext::resolveRenderTargetFormat()
+{
+ RHI_UNIMPLEMENTED;
+
+ //* const QSurfaceFormat format = m_gl->format();
+ //* const uint a = (format.alphaBufferSize() == -1) ? 0 : format.alphaBufferSize();
+ //* const uint r = format.redBufferSize();
+ //* const uint g = format.greenBufferSize();
+ //* const uint b = format.blueBufferSize();
+ //*
+ //* #define RGBA_BITS(r,g,b,a) (r | (g << 6) | (b << 12) | (a << 18))
+ //*
+ //* const uint bits = RGBA_BITS(r,g,b,a);
+ //* switch (bits) {
+ //* case RGBA_BITS(8,8,8,8):
+ //* m_renderTargetFormat = QAbstractTexture::RGBA8_UNorm;
+ //* break;
+ //* case RGBA_BITS(8,8,8,0):
+ //* m_renderTargetFormat = QAbstractTexture::RGB8_UNorm;
+ //* break;
+ //* case RGBA_BITS(5,6,5,0):
+ //* m_renderTargetFormat = QAbstractTexture::R5G6B5;
+ //* break;
+ //* }
+ //* #undef RGBA_BITS
+}
+
+bool SubmissionContext::beginDrawing(QSurface *surface)
+{
+ Q_ASSERT(surface);
+
+ m_surface = surface;
+
+ // TO DO: Find a way to make to pause work if the window is not exposed
+ // if (m_surface && m_surface->surfaceClass() == QSurface::Window) {
+ // qDebug() << Q_FUNC_INFO << 1;
+ // if (!static_cast<QWindow *>(m_surface)->isExposed())
+ // return false;
+ // qDebug() << Q_FUNC_INFO << 2;
+ // }
+
+ // Makes the surface current on the OpenGLContext
+ // and sets the right glHelper
+ // m_ownCurrent = !(m_gl->surface() == m_surface);
+ // if (m_ownCurrent && !makeCurrent(m_surface))
+ // return false;
+
+ // TODO: cache surface format somewhere rather than doing this every time render surface changes
+ resolveRenderTargetFormat();
+
+#if defined(QT3D_RENDER_ASPECT_RHI_DEBUG)
+ GLint err = m_gl->functions()->glGetError();
+ if (err != 0) {
+ qCWarning(Backend) << Q_FUNC_INFO << "glGetError:" << err;
+ }
+#endif
+
+ Q_ASSERT(isInitialized());
+
+ // need to reset these values every frame, may get overwritten elsewhere
+ RHI_UNIMPLEMENTED;
+
+ if (m_activeShader) {
+ m_activeShader = nullptr;
+ }
+
+ // Check if we have a swapchain for the Window, if not create one
+ SwapChainInfo *swapChainInfo = swapChainForSurface(surface);
+ QRhiSwapChain *swapChain = swapChainInfo->swapChain;
+
+ // TO DO: Check if that's required all the time
+ {
+ // Rebuild RenderPassDescriptor
+ // TODO -> this is not necessary, swapChain->buildOrResize already does it
+ // swapChainInfo->renderBuffer->setPixelSize(surface->size());
+ // swapChainInfo->renderBuffer->build();
+
+ // Resize swapchain if needed
+ if (m_surface->size() != swapChain->surfacePixelSize()) {
+ bool couldRebuild = swapChain->buildOrResize();
+ if (!couldRebuild)
+ return false;
+ }
+ }
+
+ m_currentSwapChain = swapChain;
+ m_currentRenderPassDescriptor = swapChainInfo->renderPassDescriptor;
+
+ // Begin Frame
+ const auto success = m_rhi->beginFrame(m_currentSwapChain);
+
+ return success == QRhi::FrameOpSuccess;
+}
+
+void SubmissionContext::endDrawing(bool swapBuffers)
+{
+ m_rhi->endFrame(m_currentSwapChain, {});
+
+ RHI_UNIMPLEMENTED;
+ //* if (swapBuffers)
+ //* m_gl->swapBuffers(m_surface);
+ //* if (m_ownCurrent)
+ //* m_gl->doneCurrent();
+ // m_textureContext.endDrawing();
+ //* static int i = 0;
+ //* if (i++ == 10)
+ //* std::exit(0);
+}
+
+void SubmissionContext::activateRenderTarget(Qt3DCore::QNodeId renderTargetNodeId,
+ const AttachmentPack &attachments, GLuint defaultFboId)
+{
+ RHI_UNIMPLEMENTED;
+ GLuint fboId = defaultFboId; // Default FBO
+ if (renderTargetNodeId) {
+ // New RenderTarget
+ if (!m_renderTargets.contains(renderTargetNodeId)) {
+ if (m_defaultFBO && fboId == m_defaultFBO) {
+ // this is the default fbo that some platforms create (iOS), we just register it
+ // Insert FBO into hash
+ m_renderTargets.insert(renderTargetNodeId, fboId);
+ } else {
+ RHI_UNIMPLEMENTED;
+ fboId = createRenderTarget(renderTargetNodeId, attachments);
+ }
+ } else {
+ RHI_UNIMPLEMENTED;
+ fboId = updateRenderTarget(renderTargetNodeId, attachments, true);
+ }
+ }
+ m_activeFBO = fboId;
+ //* m_glHelper->bindFrameBufferObject(m_activeFBO, GraphicsHelperInterface::FBODraw);
+ // Set active drawBuffers
+ activateDrawBuffers(attachments);
+}
+
+GLuint SubmissionContext::createRenderTarget(Qt3DCore::QNodeId renderTargetNodeId,
+ const AttachmentPack &attachments)
+{
+ RHI_UNIMPLEMENTED;
+ return 0;
+ //* const GLuint fboId = m_glHelper->createFrameBufferObject();
+ //* if (fboId) {
+ //* // The FBO is created and its attachments are set once
+ //* // Insert FBO into hash
+ //* m_renderTargets.insert(renderTargetNodeId, fboId);
+ //* // Bind FBO
+ //* m_glHelper->bindFrameBufferObject(fboId, GraphicsHelperInterface::FBODraw);
+ //* bindFrameBufferAttachmentHelper(fboId, attachments);
+ //* } else {
+ //* qCritical("Failed to create FBO");
+ //* }
+ //* return fboId;
+}
+
+GLuint SubmissionContext::updateRenderTarget(Qt3DCore::QNodeId renderTargetNodeId,
+ const AttachmentPack &attachments,
+ bool isActiveRenderTarget)
+{
+ RHI_UNIMPLEMENTED;
+ return 0;
+ //* const GLuint fboId = m_renderTargets.value(renderTargetNodeId);
+ //*
+ //* // We need to check if one of the attachment was resized
+ //* bool needsResize = !m_renderTargetsSize.contains(fboId); // not even initialized yet?
+ //* if (!needsResize) {
+ //* // render target exists, has attachment been resized?
+ //* RHITextureManager *rhiTextureManager =
+ //m_renderer->rhiResourceManagers()->rhiTextureManager();
+ //* const QSize s = m_renderTargetsSize[fboId];
+ //* const auto attachments_ = attachments.attachments();
+ //* for (const Attachment &attachment : attachments_) {
+ //* RHITexture *rTex = rhiTextureManager->lookupResource(attachment.m_textureUuid);
+ //* // ### TODO QTBUG-64757 this check is insufficient since the
+ //* // texture may have changed to another one with the same size. That
+ //* // case is not handled atm.
+ //* if (rTex) {
+ //* needsResize |= rTex->size() != s;
+ //* if (isActiveRenderTarget && attachment.m_point ==
+ //QRenderTargetOutput::Color0)
+ //* m_renderTargetFormat = rTex->properties().format;
+ //* }
+ //* }
+ //* }
+ //*
+ //* if (needsResize) {
+ //* m_glHelper->bindFrameBufferObject(fboId, GraphicsHelperInterface::FBODraw);
+ //* bindFrameBufferAttachmentHelper(fboId, attachments);
+ //* }
+ //*
+ //* return fboId;
+}
+
+QSize SubmissionContext::renderTargetSize(const QSize &surfaceSize) const
+{
+ RHI_UNIMPLEMENTED;
+ return surfaceSize;
+ //* QSize renderTargetSize{};
+ //* if (m_activeFBO != m_defaultFBO) {
+ //* // For external FBOs we may not have a m_renderTargets entry.
+ //* if (m_renderTargetsSize.contains(m_activeFBO)) {
+ //* renderTargetSize = m_renderTargetsSize[m_activeFBO];
+ //* } else if (surfaceSize.isValid()) {
+ //* renderTargetSize = surfaceSize;
+ //* } else {
+ //* // External FBO (when used with QtQuick2 Scene3D)
+ //*
+ //* // Query FBO color attachment 0 size
+ //* GLint attachmentObjectType = GL_NONE;
+ //* GLint attachment0Name = 0;
+ //* m_gl->functions()->glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER,
+ //* GL_COLOR_ATTACHMENT0,
+ //* GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE,
+ //* &attachmentObjectType);
+ //* m_gl->functions()->glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER,
+ //* GL_COLOR_ATTACHMENT0,
+ //* GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME,
+ //* &attachment0Name);
+ //*
+ //* if (attachmentObjectType == GL_RENDERBUFFER &&
+ //m_glHelper->supportsFeature(GraphicsHelperInterface::RenderBufferDimensionRetrieval))
+ //* renderTargetSize = m_glHelper->getRenderBufferDimensions(attachment0Name);
+ //* else if (attachmentObjectType == GL_TEXTURE &&
+ //m_glHelper->supportsFeature(GraphicsHelperInterface::TextureDimensionRetrieval))
+ //* // Assumes texture level 0 and GL_TEXTURE_2D target
+ //* renderTargetSize = m_glHelper->getTextureDimensions(attachment0Name,
+ //GL_TEXTURE_2D);
+ //* else
+ //* return renderTargetSize;
+ //* }
+ //* } else {
+ //* renderTargetSize = m_surface->size();
+ //* if (m_surface->surfaceClass() == QSurface::Window) {
+ //* const float dpr = static_cast<QWindow *>(m_surface)->devicePixelRatio();
+ //* renderTargetSize *= dpr;
+ //* }
+ //* }
+ //* return renderTargetSize;
+}
+
+QImage SubmissionContext::readFramebuffer(const QRect &rect)
+{
+ RHI_UNIMPLEMENTED;
+ return {};
+ //* QImage img;
+ //* const unsigned int area = rect.width() * rect.height();
+ //* unsigned int bytes;
+ //* GLenum format, type;
+ //* QImage::Format imageFormat;
+ //* uint stride;
+ //*
+ //* /* format value should match GL internalFormat */
+ //* GLenum internalFormat = m_renderTargetFormat;
+ //*
+ //* switch (m_renderTargetFormat) {
+ //* case QAbstractTexture::RGBAFormat:
+ //* case QAbstractTexture::RGBA8_SNorm:
+ //* case QAbstractTexture::RGBA8_UNorm:
+ //* case QAbstractTexture::RGBA8U:
+ //* case QAbstractTexture::SRGB8_Alpha8:
+ //*#ifdef QT_OPENGL_ES_2
+ //* format = GL_RGBA;
+ //* imageFormat = QImage::Format_RGBA8888_Premultiplied;
+ //*#else
+ //* format = GL_BGRA;
+ //* imageFormat = QImage::Format_ARGB32_Premultiplied;
+ //* internalFormat = GL_RGBA8;
+ //*#endif
+ //* type = GL_UNSIGNED_BYTE;
+ //* bytes = area * 4;
+ //* stride = rect.width() * 4;
+ //* break;
+ //* case QAbstractTexture::SRGB8:
+ //* case QAbstractTexture::RGBFormat:
+ //* case QAbstractTexture::RGB8U:
+ //* case QAbstractTexture::RGB8_UNorm:
+ //*#ifdef QT_OPENGL_ES_2
+ //* format = GL_RGBA;
+ //* imageFormat = QImage::Format_RGBX8888;
+ //*#else
+ //* format = GL_BGRA;
+ //* imageFormat = QImage::Format_RGB32;
+ //* internalFormat = GL_RGB8;
+ //*#endif
+ //* type = GL_UNSIGNED_BYTE;
+ //* bytes = area * 4;
+ //* stride = rect.width() * 4;
+ //* break;
+ //*#ifndef QT_OPENGL_ES_2
+ //* case QAbstractTexture::RG11B10F:
+ //* bytes = area * 4;
+ //* format = GL_RGB;
+ //* type = GL_UNSIGNED_INT_10F_11F_11F_REV;
+ //* imageFormat = QImage::Format_RGB30;
+ //* stride = rect.width() * 4;
+ //* break;
+ //* case QAbstractTexture::RGB10A2:
+ //* bytes = area * 4;
+ //* format = GL_RGBA;
+ //* type = GL_UNSIGNED_INT_2_10_10_10_REV;
+ //* imageFormat = QImage::Format_A2BGR30_Premultiplied;
+ //* stride = rect.width() * 4;
+ //* break;
+ //* case QAbstractTexture::R5G6B5:
+ //* bytes = area * 2;
+ //* format = GL_RGB;
+ //* type = GL_UNSIGNED_SHORT;
+ //* internalFormat = GL_UNSIGNED_SHORT_5_6_5_REV;
+ //* imageFormat = QImage::Format_RGB16;
+ //* stride = rect.width() * 2;
+ //* break;
+ //* case QAbstractTexture::RGBA16F:
+ //* case QAbstractTexture::RGBA16U:
+ //* case QAbstractTexture::RGBA32F:
+ //* case QAbstractTexture::RGBA32U:
+ //* bytes = area * 16;
+ //* format = GL_RGBA;
+ //* type = GL_FLOAT;
+ //* imageFormat = QImage::Format_ARGB32_Premultiplied;
+ //* stride = rect.width() * 16;
+ //* break;
+ //*#endif
+ //* default:
+ //* auto warning = qWarning();
+ //* warning << "Unable to convert";
+ //* QtDebugUtils::formatQEnum(warning, m_renderTargetFormat);
+ //* warning << "render target texture format to QImage.";
+ //* return img;
+ //* }
+ //*
+ //* GLint samples = 0;
+ //* m_gl->functions()->glGetIntegerv(GL_SAMPLES, &samples);
+ //* if (samples > 0 &&
+ //!m_glHelper->supportsFeature(GraphicsHelperInterface::BlitFramebuffer)) {
+ //* qCWarning(Backend) << Q_FUNC_INFO << "Unable to capture multisampled framebuffer; "
+ //* "Required feature BlitFramebuffer is missing.";
+ //* return img;
+ //* }
+ //*
+ //* img = QImage(rect.width(), rect.height(), imageFormat);
+ //*
+ //* QScopedArrayPointer<uchar> data(new uchar [bytes]);
+ //*
+ //* if (samples > 0) {
+ //* // resolve multisample-framebuffer to renderbuffer and read pixels from it
+ //* GLuint fbo, rb;
+ //* QOpenGLFunctions *gl = m_gl->functions();
+ //* gl->glGenFramebuffers(1, &fbo);
+ //* gl->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
+ //* gl->glGenRenderbuffers(1, &rb);
+ //* gl->glBindRenderbuffer(GL_RENDERBUFFER, rb);
+ //* gl->glRenderbufferStorage(GL_RENDERBUFFER, internalFormat, rect.width(),
+ //rect.height());
+ //* gl->glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ //GL_RENDERBUFFER, rb);
+ //*
+ //* const GLenum status = gl->glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER);
+ //* if (status != GL_FRAMEBUFFER_COMPLETE) {
+ //* gl->glDeleteRenderbuffers(1, &rb);
+ //* gl->glDeleteFramebuffers(1, &fbo);
+ //* qCWarning(Backend) << Q_FUNC_INFO << "Copy-framebuffer not complete: " << status;
+ //* return img;
+ //* }
+ //*
+ //* m_glHelper->blitFramebuffer(rect.x(), rect.y(), rect.x() + rect.width(), rect.y() +
+ //rect.height(),
+ //* 0, 0, rect.width(), rect.height(),
+ //* GL_COLOR_BUFFER_BIT, GL_NEAREST);
+ //* gl->glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
+ //* gl->glReadPixels(0,0,rect.width(), rect.height(), format, type, data.data());
+ //*
+ //* copyGLFramebufferDataToImage(img, data.data(), stride, rect.width(), rect.height(),
+ //m_renderTargetFormat);
+ //*
+ //* gl->glBindRenderbuffer(GL_RENDERBUFFER, rb);
+ //* gl->glDeleteRenderbuffers(1, &rb);
+ //* gl->glBindFramebuffer(GL_FRAMEBUFFER, m_activeFBO);
+ //* gl->glDeleteFramebuffers(1, &fbo);
+ //* } else {
+ //* // read pixels directly from framebuffer
+ //* m_gl->functions()->glReadPixels(rect.x(), rect.y(), rect.width(), rect.height(),
+ //format, type, data.data());
+ //* copyGLFramebufferDataToImage(img, data.data(), stride, rect.width(), rect.height(),
+ //m_renderTargetFormat);
+ //* }
+ //*
+ //* return img;
+}
+
+void SubmissionContext::releaseResources()
+{
+ m_renderBufferHash.clear();
+ RHI_UNIMPLEMENTED;
+
+ // Free RHI resources
+ {
+ qCDebug(Backend) << Q_FUNC_INFO;
+
+ // We must ensure no remaining resource before deleting m_rhi.
+ m_renderer->rhiResourceManagers()->releaseAllResources();
+
+ auto it = m_swapChains.begin();
+ while (it != m_swapChains.end()) {
+ SwapChainInfo &swapChainInfo = it.value();
+ delete swapChainInfo.renderPassDescriptor;
+ delete swapChainInfo.renderBuffer;
+ delete swapChainInfo.swapChain;
+ it = m_swapChains.erase(it);
+ }
+
+ delete m_rhi;
+ m_rhi = nullptr;
+
+#ifndef QT_NO_OPENGL
+ delete m_fallbackSurface;
+ m_fallbackSurface = nullptr;
+#endif
+ }
+
+ //* // Stop and destroy the OpenGL logger
+ //* if (m_debugLogger) {
+ //* m_debugLogger->stopLogging();
+ //* m_debugLogger.reset(nullptr);
+ //* }
+}
+
+// Called only from RenderThread
+bool SubmissionContext::activateShader(RHIShader *shader)
+{
+ RHI_UNIMPLEMENTED;
+ //* if (shader->shaderProgram() != m_activeShader) {
+ //* // Ensure material uniforms are re-applied
+ //* m_material = nullptr;
+ //*
+ //* m_activeShader = shader->shaderProgram();
+ //* if (Q_LIKELY(m_activeShader != nullptr)) {
+ //* m_activeShader->bind();
+ //* } else {
+ //* m_glHelper->useProgram(0);
+ //* qWarning() << "No shader program found";
+ //* return false;
+ //* }
+ //* }
+ return true;
+}
+
+void SubmissionContext::bindFrameBufferAttachmentHelper(GLuint fboId,
+ const AttachmentPack &attachments)
+{
+ RHI_UNIMPLEMENTED;
+ // Set FBO attachments. These are normally textures, except that on Open GL
+ // ES <= 3.1 we must use a renderbuffer if a combined depth+stencil is
+ // desired since this cannot be achieved neither with a single texture (not
+ // before GLES 3.2) nor with separate textures (no suitable format for
+ // stencil before 3.1 with the appropriate extension).
+
+ //* QSize fboSize;
+ //* RHITextureManager *rhiTextureManager =
+ //m_renderer->rhiResourceManagers()->rhiTextureManager();
+ //* const auto attachments_ = attachments.attachments();
+ //* for (const Attachment &attachment : attachments_) {
+ //* RHITexture *rTex = rhiTextureManager->lookupResource(attachment.m_textureUuid);
+ //* if (!m_glHelper->frameBufferNeedsRenderBuffer(attachment)) {
+ //* QOpenGLTexture *glTex = rTex ? rTex->getGLTexture() : nullptr;
+ //* if (glTex != nullptr) {
+ //* // The texture can not be rendered simultaniously by another renderer
+ //* Q_ASSERT(!rTex->isExternalRenderingEnabled());
+ //* if (fboSize.isEmpty())
+ //* fboSize = QSize(glTex->width(), glTex->height());
+ //* else
+ //* fboSize = QSize(qMin(fboSize.width(), glTex->width()),
+ //qMin(fboSize.height(), glTex->height()));
+ //* m_glHelper->bindFrameBufferAttachment(glTex, attachment);
+ //* }
+ //* } else {
+ //* RenderBuffer *renderBuffer = rTex ? rTex->getOrCreateRenderBuffer() : nullptr;
+ //* if (renderBuffer) {
+ //* if (fboSize.isEmpty())
+ //* fboSize = QSize(renderBuffer->width(), renderBuffer->height());
+ //* else
+ //* fboSize = QSize(qMin(fboSize.width(), renderBuffer->width()),
+ //qMin(fboSize.height(), renderBuffer->height()));
+ //* m_glHelper->bindFrameBufferAttachment(renderBuffer, attachment);
+ //* }
+ //* }
+ //* }
+ //* m_renderTargetsSize.insert(fboId, fboSize);
+}
+
+void SubmissionContext::activateDrawBuffers(const AttachmentPack &attachments)
+{
+ RHI_UNIMPLEMENTED;
+ //* const QVector<int> activeDrawBuffers = attachments.getGlDrawBuffers();
+ //*
+ //* if (m_glHelper->checkFrameBufferComplete()) {
+ //* if (activeDrawBuffers.size() > 1) {// We need MRT
+ //* if (m_glHelper->supportsFeature(GraphicsHelperInterface::MRT)) {
+ //* // Set up MRT, glDrawBuffers...
+ //* m_glHelper->drawBuffers(activeDrawBuffers.size(), activeDrawBuffers.data());
+ //* }
+ //* }
+ //* } else {
+ //* qCWarning(Backend) << "FBO incomplete";
+ //* }
+}
+
+void SubmissionContext::setActiveMaterial(Material *rmat)
+{
+ if (m_material == rmat)
+ return;
+
+ // m_textureContext.deactivateTexturesWithScope(TextureSubmissionContext::TextureScopeMaterial);
+ m_material = rmat;
+}
+
+void SubmissionContext::applyState(const StateVariant &stateVariant,
+ QRhiGraphicsPipeline *graphicsPipeline)
+{
+ switch (stateVariant.type) {
+
+ case AlphaCoverageStateMask: {
+ applyStateHelper(static_cast<const AlphaCoverage *>(stateVariant.constState()),
+ graphicsPipeline);
+ break;
+ }
+ case AlphaTestMask: {
+ applyStateHelper(static_cast<const AlphaFunc *>(stateVariant.constState()),
+ graphicsPipeline);
+ break;
+ }
+ case BlendStateMask: {
+ applyStateHelper(static_cast<const BlendEquation *>(stateVariant.constState()),
+ graphicsPipeline);
+ break;
+ }
+ case BlendEquationArgumentsMask: {
+ applyStateHelper(static_cast<const BlendEquationArguments *>(stateVariant.constState()),
+ graphicsPipeline);
+ break;
+ }
+ case MSAAEnabledStateMask: {
+ applyStateHelper(static_cast<const MSAAEnabled *>(stateVariant.constState()),
+ graphicsPipeline, m_renderer->format());
+ break;
+ }
+
+ case CullFaceStateMask: {
+ applyStateHelper(static_cast<const CullFace *>(stateVariant.constState()),
+ graphicsPipeline);
+ break;
+ }
+
+ case DepthWriteStateMask: {
+ applyStateHelper(static_cast<const NoDepthMask *>(stateVariant.constState()),
+ graphicsPipeline);
+ break;
+ }
+
+ case DepthTestStateMask: {
+ applyStateHelper(static_cast<const DepthTest *>(stateVariant.constState()),
+ graphicsPipeline);
+ break;
+ }
+
+ case DepthRangeMask: {
+ applyStateHelper(static_cast<const DepthRange *>(stateVariant.constState()),
+ graphicsPipeline);
+ break;
+ }
+
+ case RasterModeMask: {
+ applyStateHelper(static_cast<const RasterMode *>(stateVariant.constState()),
+ graphicsPipeline);
+ break;
+ }
+
+ case FrontFaceStateMask: {
+ applyStateHelper(static_cast<const FrontFace *>(stateVariant.constState()),
+ graphicsPipeline);
+ break;
+ }
+
+ case ScissorStateMask: {
+ applyStateHelper(static_cast<const ScissorTest *>(stateVariant.constState()),
+ graphicsPipeline);
+ break;
+ }
+
+ case StencilTestStateMask: {
+ applyStateHelper(static_cast<const StencilTest *>(stateVariant.constState()),
+ graphicsPipeline);
+ break;
+ }
+
+ case PointSizeMask: {
+ applyStateHelper(static_cast<const PointSize *>(stateVariant.constState()),
+ graphicsPipeline);
+ break;
+ }
+
+ case PolygonOffsetStateMask: {
+ applyStateHelper(static_cast<const PolygonOffset *>(stateVariant.constState()),
+ graphicsPipeline);
+ break;
+ }
+
+ case ColorStateMask: {
+ applyStateHelper(static_cast<const ColorMask *>(stateVariant.constState()),
+ graphicsPipeline);
+ break;
+ }
+
+ case ClipPlaneMask: {
+ applyStateHelper(static_cast<const ClipPlane *>(stateVariant.constState()),
+ graphicsPipeline);
+ break;
+ }
+
+ case SeamlessCubemapMask: {
+ applyStateHelper(static_cast<const SeamlessCubemap *>(stateVariant.constState()),
+ graphicsPipeline);
+ break;
+ }
+
+ case StencilOpMask: {
+ applyStateHelper(static_cast<const StencilOp *>(stateVariant.constState()),
+ graphicsPipeline);
+ break;
+ }
+
+ case StencilWriteStateMask: {
+ applyStateHelper(static_cast<const StencilMask *>(stateVariant.constState()),
+ graphicsPipeline);
+ break;
+ }
+
+ case DitheringStateMask: {
+ applyStateHelper(static_cast<const Dithering *>(stateVariant.constState()),
+ graphicsPipeline);
+ break;
+ }
+
+ case LineWidthMask: {
+ applyStateHelper(static_cast<const LineWidth *>(stateVariant.constState()),
+ graphicsPipeline);
+ break;
+ }
+ default:
+ Q_UNREACHABLE();
+ }
+}
+
+void SubmissionContext::applyStateSet(const RenderStateSet *ss,
+ QRhiGraphicsPipeline *graphicsPipeline)
+{
+ // Set default state values on graphicsPipeline
+ graphicsPipeline->setDepthWrite(true);
+ graphicsPipeline->setDepthTest(false);
+ graphicsPipeline->setSampleCount(format().samples());
+
+ const QVector<StateVariant> statesToSet = ss->states();
+ for (const StateVariant &ds : statesToSet)
+ applyState(ds, graphicsPipeline);
+}
+
+StateVariant *SubmissionContext::getState(RenderStateSet *ss, StateMask type) const
+{
+ const QVector<StateVariant> &statesToSet = ss->states();
+ for (int i = 0, m = statesToSet.size(); i < m; ++i) {
+ const StateVariant &ds = statesToSet.at(i);
+ if (ds.type == type)
+ return ss->states().begin() + i;
+ }
+ return nullptr;
+}
+
+SubmissionContext::SwapChainInfo *SubmissionContext::swapChainForSurface(QSurface *surface) noexcept
+{
+ SwapChainInfo &swapChainInfo = m_swapChains[surface];
+ auto &swapChain = swapChainInfo.swapChain;
+
+ if (swapChain == nullptr) {
+ swapChain = m_rhi->newSwapChain();
+ Q_ASSERT(surface->surfaceClass() == QSurface::Window);
+ QWindow *window = static_cast<QWindow *>(surface);
+ Q_ASSERT(window != nullptr);
+ const int samples = format().samples();
+
+ swapChain->setWindow(window);
+ swapChain->setFlags(QRhiSwapChain::Flags {});
+ swapChain->setSampleCount(samples);
+
+ QRhiRenderBuffer *renderBuffer =
+ m_rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil, QSize(), samples,
+ QRhiRenderBuffer::UsedWithSwapChainOnly);
+ swapChain->setDepthStencil(renderBuffer);
+
+ QRhiRenderPassDescriptor *renderPassDescriptor =
+ swapChain->newCompatibleRenderPassDescriptor();
+ swapChain->setRenderPassDescriptor(renderPassDescriptor);
+
+ // Build swapChain the first time
+ if (swapChain->buildOrResize()) {
+ swapChainInfo.swapChain = swapChain;
+ swapChainInfo.renderBuffer = renderBuffer;
+ swapChainInfo.renderPassDescriptor = renderPassDescriptor;
+ } else {
+ swapChain->releaseAndDestroyLater();
+ m_swapChains.remove(surface);
+ return nullptr;
+ }
+ }
+ return &swapChainInfo;
+}
+
+QRhiCommandBuffer *SubmissionContext::currentFrameCommandBuffer() const
+{
+ return m_currentSwapChain->currentFrameCommandBuffer();
+}
+
+QRhiRenderTarget *SubmissionContext::currentFrameRenderTarget() const
+{
+ return m_currentSwapChain->currentFrameRenderTarget();
+}
+
+QRhiSwapChain *SubmissionContext::currentSwapChain() const
+{
+ return m_currentSwapChain;
+}
+
+QRhiRenderPassDescriptor *SubmissionContext::currentRenderPassDescriptor() const
+{
+ return m_currentRenderPassDescriptor;
+}
+
+QSurfaceFormat SubmissionContext::format() const noexcept
+{
+ if (this->m_rhi && this->m_rhi->backend() == QRhi::OpenGLES2) {
+ auto rhi_gl = static_cast<const QRhiGles2NativeHandles *>(this->m_rhi->nativeHandles());
+ return rhi_gl->context->format();
+ }
+ return QSurfaceFormat::defaultFormat();
+}
+
+// It will be easier if the QGraphicContext applies the QUniformPack
+// than the other way around
+bool SubmissionContext::setParameters(ShaderParameterPack &parameterPack)
+{
+ static const int irradianceId = StringToInt::lookupId(QLatin1String("envLight_irradiance"));
+ static const int specularId = StringToInt::lookupId(QLatin1String("envLight_specular"));
+ // Activate textures and update TextureUniform in the pack
+ // with the correct textureUnit
+
+ // Set the pinned texture of the previous material texture
+ // to pinable so that we should easily find an available texture unit
+ // m_textureContext.deactivateTexturesWithScope(TextureSubmissionContext::TextureScopeMaterial);
+ // Update the uniforms with the correct texture unit id's
+ PackUniformHash &uniformValues = parameterPack.uniforms();
+
+ // Fill Texture Uniform Value with proper texture units
+ // so that they can be applied as regular uniforms in a second step
+ for (int i = 0; i < parameterPack.textures().size(); ++i) {
+ RHI_UNIMPLEMENTED;
+ //* const ShaderParameterPack::NamedResource &namedTex = parameterPack.textures().at(i);
+ //* // Given a Texture QNodeId, we retrieve the associated shared RHITexture
+ //* if (uniformValues.contains(namedTex.glslNameId)) {
+ //* RHITexture *t =
+ //m_renderer->rhiResourceManagers()->rhiTextureManager()->lookupResource(namedTex.nodeId);
+ //* if (t != nullptr) {
+ //* UniformValue &texUniform = uniformValues.value(namedTex.glslNameId);
+ //* if (texUniform.valueType() == UniformValue::TextureValue) {
+ //* const int texUnit =
+ //m_textureContext.activateTexture(TextureSubmissionContext::TextureScopeMaterial, m_gl, t);
+ //* texUniform.data<int>()[namedTex.uniformArrayIndex] = texUnit;
+ //* if (texUnit == -1) {
+ //* if (namedTex.glslNameId != irradianceId &&
+ //* namedTex.glslNameId != specularId) {
+ //* // Only return false if we are not dealing with env light textures
+ //* qCWarning(Backend) << "Unable to find suitable Texture Unit";
+ //* return false;
+ //* }
+ //* }
+ //* }
+ //* }
+ //* }
+ }
+
+ RHIShader *shader = activeShader();
+
+ // TO DO: We could cache the binding points somehow and only do the binding when necessary
+ // for SSBO and UBO
+
+ // Bind Shader Storage block to SSBO and update SSBO
+ const QVector<BlockToSSBO> &blockToSSBOs = parameterPack.shaderStorageBuffers();
+ for (const BlockToSSBO &b : blockToSSBOs) {
+ RHI_UNIMPLEMENTED;
+ Buffer *cpuBuffer =
+ m_renderer->nodeManagers()->bufferManager()->lookupResource(b.m_bufferID);
+ RHIBuffer *ssbo = rhiBufferForRenderBuffer(cpuBuffer);
+ // bindShaderStorageBlock
+ // This is currently not required as we are introspecting the bindingIndex
+ // value from the shaders and not replacing them, making such a call useless
+ // bindShaderStorageBlock(shader->programId(), b.m_blockIndex, b.m_bindingIndex);
+ // bindShaderStorageBlock(shader->programId(), b.m_blockIndex, b.m_bindingIndex);
+ // Needed to avoid conflict where the buffer would already
+ // be bound as a VertexArray
+ bindRHIBuffer(ssbo, RHIBuffer::ShaderStorageBuffer);
+ ssbo->bindBufferBase(this, b.m_bindingIndex, RHIBuffer::ShaderStorageBuffer);
+ // TO DO: Make sure that there's enough binding points
+ }
+
+ // Bind UniformBlocks to UBO and update UBO from Buffer
+ // TO DO: Convert ShaderData to Buffer so that we can use that generic process
+ const QVector<BlockToUBO> blockToUBOs = parameterPack.uniformBuffers();
+ int uboIndex = 0;
+ for (const BlockToUBO &b : blockToUBOs) {
+ RHI_UNIMPLEMENTED;
+ Buffer *cpuBuffer =
+ m_renderer->nodeManagers()->bufferManager()->lookupResource(b.m_bufferID);
+ RHIBuffer *ubo = rhiBufferForRenderBuffer(cpuBuffer);
+ // bindUniformBlock(shader->programId(), b.m_blockIndex, uboIndex);
+ // Needed to avoid conflict where the buffer would already
+ // be bound as a VertexArray
+ bindRHIBuffer(ubo, RHIBuffer::UniformBuffer);
+ ubo->bindBufferBase(this, uboIndex++, RHIBuffer::UniformBuffer);
+ // TO DO: Make sure that there's enough binding points
+ }
+
+ // Update uniforms in the Default Uniform Block
+ const PackUniformHash values = parameterPack.uniforms();
+ const QVector<ShaderUniform> activeUniforms = parameterPack.submissionUniforms();
+
+ for (const ShaderUniform &uniform : activeUniforms) {
+ RHI_UNIMPLEMENTED;
+ // We can use [] as we are sure the the uniform wouldn't
+ // be un activeUniforms if there wasn't a matching value
+ const UniformValue &v = values.value(uniform.m_nameId);
+
+ // skip invalid textures/images
+ if ((v.valueType() == UniformValue::TextureValue
+ || v.valueType() == UniformValue::ShaderImageValue)
+ && *v.constData<int>() == -1)
+ continue;
+
+ // applyUniform(uniform, v);
+ }
+ // if not all data is valid, the next frame will be rendered immediately
+ return true;
+}
+
+void SubmissionContext::updateBuffer(Buffer *buffer)
+{
+ const QHash<Qt3DCore::QNodeId, HRHIBuffer>::iterator it =
+ m_renderBufferHash.find(buffer->peerId());
+ if (it != m_renderBufferHash.end())
+ uploadDataToRHIBuffer(
+ buffer, m_renderer->rhiResourceManagers()->rhiBufferManager()->data(it.value()));
+}
+
+QByteArray SubmissionContext::downloadBufferContent(Buffer *buffer)
+{
+ const QHash<Qt3DCore::QNodeId, HRHIBuffer>::iterator it =
+ m_renderBufferHash.find(buffer->peerId());
+ if (it != m_renderBufferHash.end())
+ return downloadDataFromRHIBuffer(
+ buffer, m_renderer->rhiResourceManagers()->rhiBufferManager()->data(it.value()));
+ return QByteArray();
+}
+
+void SubmissionContext::releaseBuffer(Qt3DCore::QNodeId bufferId)
+{
+ auto it = m_renderBufferHash.find(bufferId);
+ if (it != m_renderBufferHash.end()) {
+ HRHIBuffer glBuffHandle = it.value();
+ RHIBuffer *glBuff =
+ m_renderer->rhiResourceManagers()->rhiBufferManager()->data(glBuffHandle);
+
+ Q_ASSERT(glBuff);
+ // Destroy the GPU resource
+ glBuff->destroy(this);
+ // Destroy the RHIBuffer instance
+ m_renderer->rhiResourceManagers()->rhiBufferManager()->releaseResource(bufferId);
+ // Remove Id - HRHIBuffer entry
+ m_renderBufferHash.erase(it);
+ }
+}
+
+bool SubmissionContext::hasRHIBufferForBuffer(Buffer *buffer)
+{
+ const QHash<Qt3DCore::QNodeId, HRHIBuffer>::iterator it =
+ m_renderBufferHash.find(buffer->peerId());
+ return (it != m_renderBufferHash.end());
+}
+
+RHIBuffer *SubmissionContext::rhiBufferForRenderBuffer(Buffer *buf)
+{
+ if (!m_renderBufferHash.contains(buf->peerId()))
+ m_renderBufferHash.insert(buf->peerId(), createRHIBufferFor(buf));
+ return m_renderer->rhiResourceManagers()->rhiBufferManager()->data(
+ m_renderBufferHash.value(buf->peerId()));
+}
+
+HRHIBuffer SubmissionContext::createRHIBufferFor(Buffer *buffer)
+{
+ m_renderer->rhiResourceManagers()->rhiBufferManager()->getOrCreateResource(buffer->peerId());
+ return m_renderer->rhiResourceManagers()->rhiBufferManager()->lookupHandle(buffer->peerId());
+}
+
+bool SubmissionContext::bindRHIBuffer(RHIBuffer *buffer, RHIBuffer::Type type)
+{
+ return buffer->bind(this, type);
+}
+
+void SubmissionContext::uploadDataToRHIBuffer(Buffer *buffer, RHIBuffer *b, bool releaseBuffer)
+{
+ // If the buffer is dirty (hence being called here)
+ // there are two possible cases
+ // * setData was called changing the whole data or functor (or the usage pattern)
+ // * partial buffer updates where received
+
+ // Note: we are only storing the updates data CPU side at this point
+ // actually upload will be performed when the buffer will be bound
+ // as we would otherwise need to know the usage type of the buffer
+ QVector<Qt3DRender::QBufferUpdate> updates = std::move(buffer->pendingBufferUpdates());
+ for (auto it = updates.begin(); it != updates.end(); ++it) {
+ auto update = it;
+ // We have a partial update
+ if (update->offset >= 0) {
+ // accumulate sequential updates as single one
+ int bufferSize = update->data.size();
+ auto it2 = it + 1;
+ while ((it2 != updates.end()) && (it2->offset - update->offset == bufferSize)) {
+ bufferSize += it2->data.size();
+ ++it2;
+ }
+ update->data.resize(bufferSize);
+ while (it + 1 != it2) {
+ ++it;
+ update->data.replace(it->offset - update->offset, it->data.size(), it->data);
+ it->data.clear();
+ }
+ // TO DO: based on the number of updates .., it might make sense to
+ // sometime use glMapBuffer rather than glBufferSubData
+ b->update(this, update->data, update->offset);
+ } else {
+ // We have an update that was done by calling QBuffer::setData
+ // which is used to resize or entirely clear the buffer
+ // Note: we use the buffer data directly in that case
+ b->orphan(this); // orphan the buffer
+ b->allocate(this, buffer->data(), false);
+ }
+ }
+
+ if (releaseBuffer) {
+ b->release(this);
+ }
+ qCDebug(Io) << "uploaded buffer size=" << buffer->data().size();
+}
+
+QByteArray SubmissionContext::downloadDataFromRHIBuffer(Buffer *buffer, RHIBuffer *b)
+{
+ if (!bindRHIBuffer(b,
+ RHIBuffer::ArrayBuffer)) // We're downloading, the type doesn't matter here
+ qCWarning(Io) << Q_FUNC_INFO << "buffer bind failed";
+
+ return b->download(this, buffer->data().size());
+}
+
+void SubmissionContext::blitFramebuffer(Qt3DCore::QNodeId inputRenderTargetId,
+ Qt3DCore::QNodeId outputRenderTargetId, QRect inputRect,
+ QRect outputRect, uint defaultFboId,
+ QRenderTargetOutput::AttachmentPoint inputAttachmentPoint,
+ QRenderTargetOutput::AttachmentPoint outputAttachmentPoint,
+ QBlitFramebuffer::InterpolationMethod interpolationMethod)
+{
+ RHI_UNIMPLEMENTED;
+ //* GLuint inputFboId = defaultFboId;
+ //* bool inputBufferIsDefault = true;
+ //* if (!inputRenderTargetId.isNull()) {
+ //* RenderTarget *renderTarget =
+ //m_renderer->nodeManagers()->renderTargetManager()->lookupResource(inputRenderTargetId);
+ //* if (renderTarget) {
+ //* AttachmentPack attachments(renderTarget,
+ //m_renderer->nodeManagers()->attachmentManager());
+ //* if (m_renderTargets.contains(inputRenderTargetId))
+ //* inputFboId = updateRenderTarget(inputRenderTargetId, attachments, false);
+ //* else
+ //* inputFboId = createRenderTarget(inputRenderTargetId, attachments);
+ //* }
+ //* inputBufferIsDefault = false;
+ //* }
+ //*
+ //* GLuint outputFboId = defaultFboId;
+ //* bool outputBufferIsDefault = true;
+ //* if (!outputRenderTargetId.isNull()) {
+ //* RenderTarget *renderTarget =
+ //m_renderer->nodeManagers()->renderTargetManager()->lookupResource(outputRenderTargetId);
+ //* if (renderTarget) {
+ //* AttachmentPack attachments(renderTarget,
+ //m_renderer->nodeManagers()->attachmentManager());
+ //* if (m_renderTargets.contains(outputRenderTargetId))
+ //* outputFboId = updateRenderTarget(outputRenderTargetId, attachments, false);
+ //* else
+ //* outputFboId = createRenderTarget(outputRenderTargetId, attachments);
+ //* }
+ //* outputBufferIsDefault = false;
+ //* }
+ //*
+ //* // Up until this point the input and output rects are normal Qt rectangles.
+ //* // Convert them to GL rectangles (Y at bottom).
+ //* const int inputFboHeight = inputFboId == defaultFboId ? m_surfaceSize.height() :
+ //m_renderTargetsSize[inputFboId].height();
+ //* const GLint srcX0 = inputRect.left();
+ //* const GLint srcY0 = inputFboHeight - (inputRect.top() + inputRect.height());
+ //* const GLint srcX1 = srcX0 + inputRect.width();
+ //* const GLint srcY1 = srcY0 + inputRect.height();
+ //*
+ //* const int outputFboHeight = outputFboId == defaultFboId ? m_surfaceSize.height() :
+ //m_renderTargetsSize[outputFboId].height();
+ //* const GLint dstX0 = outputRect.left();
+ //* const GLint dstY0 = outputFboHeight - (outputRect.top() + outputRect.height());
+ //* const GLint dstX1 = dstX0 + outputRect.width();
+ //* const GLint dstY1 = dstY0 + outputRect.height();
+ //*
+ //* //Get the last bounded framebuffers
+ //* const GLuint lastDrawFboId = boundFrameBufferObject();
+ //*
+ //* // Activate input framebuffer for reading
+ //* bindFramebuffer(inputFboId, GraphicsHelperInterface::FBORead);
+ //*
+ //* // Activate output framebuffer for writing
+ //* bindFramebuffer(outputFboId, GraphicsHelperInterface::FBODraw);
+ //*
+ //* //Bind texture
+ //* if (!inputBufferIsDefault)
+ //* readBuffer(GL_COLOR_ATTACHMENT0 + inputAttachmentPoint);
+ //*
+ //* if (!outputBufferIsDefault) {
+ //* // Note that we use glDrawBuffers, not glDrawBuffer. The
+ //* // latter is not available with GLES.
+ //* const int buf = outputAttachmentPoint;
+ //* drawBuffers(1, &buf);
+ //* }
+ //*
+ //* // Blit framebuffer
+ //* const GLenum mode = interpolationMethod ? GL_NEAREST : GL_LINEAR;
+ //* m_glHelper->blitFramebuffer(srcX0, srcY0, srcX1, srcY1,
+ //* dstX0, dstY0, dstX1, dstY1,
+ //* GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT,
+ //* mode);
+ //*
+ //* // Reset draw buffer
+ //* bindFramebuffer(lastDrawFboId, GraphicsHelperInterface::FBOReadAndDraw);
+ //* if (outputAttachmentPoint != QRenderTargetOutput::Color0) {
+ //* const int buf = QRenderTargetOutput::Color0;
+ //* drawBuffers(1, &buf);
+ //* }
+}
+
+namespace {
+template<std::size_t N>
+constexpr int getFirstAvailableBit(const std::bitset<N> &bits)
+{
+ for (std::size_t i = 0; i < N; i++) {
+ if (!bits.test(i))
+ return i;
+ }
+ return -1;
+}
+// This function ensures that the shader stages all have the same bindings
+void preprocessRHIShader(QVector<QByteArray> &shaderCodes)
+{
+ // Map the variable names to bindings
+ std::map<QByteArray, int> bindings;
+ bindings["qt3d_render_view_uniforms"] = 0;
+ bindings["qt3d_command_uniforms"] = 1;
+ std::bitset<512> assignedBindings;
+ assignedBindings.set(0);
+ assignedBindings.set(1);
+
+ thread_local const QRegularExpression samplerRegex(
+ QStringLiteral("binding\\s*=\\s*([0-9]+).*\\)\\s*uniform\\s*[ui]?sampler[a-zA-Z0-9]+"
+ "\\s*([a-zA-Z0-9_]+)\\s*;"));
+ thread_local const QRegularExpression uboRegex(
+ QStringLiteral("(?:std140\\s*,\\s*binding\\s*=\\s*([0-9]+).*|binding\\s*=\\s*([0-9]+)"
+ "\\s*,\\s*std140.*)\\)\\s*uniform\\s*([a-zA-Z0-9_]+)"));
+
+ auto replaceBinding = [&bindings, &assignedBindings](
+ int &offset, QRegularExpressionMatch &match, QByteArray &code,
+ int indexCapture, int variableCapture) noexcept {
+ int index = match.captured(indexCapture).toInt();
+ QByteArray variable = match.captured(variableCapture).toUtf8();
+
+ auto it = bindings.find(variable);
+ if (it == bindings.end()) {
+ // 1. Check if the index is already used
+ if (assignedBindings.test(index)) {
+ index = getFirstAvailableBit(assignedBindings);
+ if (index == -1) {
+ return;
+ }
+
+ const int indexStartOffset = match.capturedStart(indexCapture);
+ const int indexEndOffset = match.capturedEnd(indexCapture);
+ const int indexLength = indexEndOffset - indexStartOffset;
+ code.replace(indexStartOffset, indexLength, QByteArray::number(index));
+ }
+
+ assignedBindings.set(index);
+ bindings.emplace(std::move(variable), index);
+ } else {
+ int indexToUse = it->second;
+ const int indexStartOffset = match.capturedStart(indexCapture);
+ const int indexEndOffset = match.capturedEnd(indexCapture);
+ const int indexLength = indexEndOffset - indexStartOffset;
+ code.replace(indexStartOffset, indexLength, QByteArray::number(indexToUse));
+ }
+ // This may fail in the case where the replaced offset is an incredibly long number,
+ // which seems quite unlikely
+ offset = match.capturedEnd(0);
+ };
+
+ for (QByteArray &shaderCode : shaderCodes) {
+ // Regex for the sampler variables
+ int offset = 0;
+ auto match = samplerRegex.match(shaderCode, offset);
+ while (match.hasMatch()) {
+ const int indexCapture = 1;
+ const int variableCapture = 2;
+ replaceBinding(offset, match, shaderCode, indexCapture, variableCapture);
+
+ match = samplerRegex.match(shaderCode, offset);
+ }
+
+ // Regex for the UBOs
+ offset = 0;
+ match = uboRegex.match(shaderCode, offset);
+ while (match.hasMatch()) {
+ const int indexCapture = !match.capturedView(1).isEmpty() ? 1 : 2;
+ const int variableCapture = 3;
+ replaceBinding(offset, match, shaderCode, indexCapture, variableCapture);
+
+ match = uboRegex.match(shaderCode, offset);
+ }
+ }
+}
+
+int glslVersionForFormat(const QSurfaceFormat &format) noexcept
+{
+ const int major = format.majorVersion();
+ const int minor = format.minorVersion();
+
+ static const QHash<std::pair<int, int>, int> glVersionToGLSLVersion = {
+ { { 4, 6 }, 460 }, { { 4, 5 }, 450 }, { { 4, 4 }, 440 }, { { 4, 3 }, 430 },
+ { { 4, 2 }, 420 }, { { 4, 1 }, 410 }, { { 4, 0 }, 400 }, { { 3, 3 }, 330 },
+ { { 3, 2 }, 150 }, { { 3, 2 }, 120 }, { { 3, 1 }, 120 },
+ };
+
+ const auto it = glVersionToGLSLVersion.find({ major, minor });
+ if (it == glVersionToGLSLVersion.end()) {
+ if (major < 3) {
+ return 120;
+ } else {
+ return major * 100 + minor * 10;
+ }
+ } else {
+ return *it;
+ }
+}
+}
+
+// Called by GL Command Thread
+SubmissionContext::ShaderCreationInfo SubmissionContext::createShaderProgram(RHIShader *shader)
+{
+ // Compile shaders
+ const auto &shaderCode = shader->shaderCode();
+ QShaderBaker b;
+ b.setGeneratedShaders({
+ { QShader::SpirvShader, 100 },
+#ifndef QT_NO_OPENGL
+ { QShader::GlslShader, glslVersionForFormat(format()) },
+#endif
+ { QShader::HlslShader, QShaderVersion(50) },
+ { QShader::MslShader, QShaderVersion(12) },
+ });
+
+ b.setGeneratedShaderVariants({ QShader::Variant {},
+#ifndef QT_NO_OPENGL
+ QShader::Variant {},
+#endif
+ QShader::Variant {}, QShader::Variant {} });
+
+ // TODO handle caching as QShader does not have a built-in mechanism for that
+ QString logs;
+ bool success = true;
+ for (int i = QShaderProgram::Vertex; i <= QShaderProgram::Compute; ++i) {
+ const QShaderProgram::ShaderType type = static_cast<QShaderProgram::ShaderType>(i);
+ if (!shaderCode.at(i).isEmpty()) {
+ // Note: logs only return the error but not all the shader code
+ // we could append it
+
+ const auto rhiStage = rhiShaderStage(type);
+ b.setSourceString(shaderCode.at(i), rhiStage);
+ QShader bakedShader = b.bake();
+ if (b.errorMessage() != QString() || !bakedShader.isValid()) {
+ qDebug() << "Shader Error: " << b.errorMessage() << shaderCode.at(i).data()
+ << rhiStage;
+ logs += b.errorMessage();
+ success = false;
+ }
+ shader->m_stages[rhiStage] = std::move(bakedShader);
+ }
+ }
+
+ // Perform shader introspection
+ if (success)
+ shader->introspect();
+
+ return { success, logs };
+}
+
+// Called by Renderer::updateResources
+void SubmissionContext::loadShader(Shader *shaderNode, ShaderManager *shaderManager,
+ RHIShaderManager *rhiShaderManager)
+{
+ const Qt3DCore::QNodeId shaderId = shaderNode->peerId();
+ RHIShader *rhiShader = rhiShaderManager->lookupResource(shaderId);
+
+ // We already have a shader associated with the node
+ if (rhiShader != nullptr) {
+ // We need to abandon it
+ rhiShaderManager->abandon(rhiShader, shaderNode);
+ }
+
+ // We create or adopt an already created rhiShader
+ rhiShader = rhiShaderManager->createOrAdoptExisting(shaderNode);
+
+ const QVector<Qt3DCore::QNodeId> sharedShaderIds =
+ rhiShaderManager->shaderIdsForProgram(rhiShader);
+ if (sharedShaderIds.size() == 1) {
+ // Shader in the cache hasn't been loaded yet
+ QVector<QByteArray> shaderCodes = shaderNode->shaderCode();
+ preprocessRHIShader(shaderCodes);
+ rhiShader->setShaderCode(shaderCodes);
+
+ const ShaderCreationInfo loadResult = createShaderProgram(rhiShader);
+ shaderNode->setStatus(loadResult.linkSucceeded ? QShaderProgram::Ready
+ : QShaderProgram::Error);
+ shaderNode->setLog(loadResult.logs);
+ // Loaded in the sense we tried to load it (and maybe it failed)
+ rhiShader->setLoaded(true);
+ } else {
+ // Find an already loaded shader that shares the same QShaderProgram
+ for (const Qt3DCore::QNodeId &sharedShaderId : sharedShaderIds) {
+ if (sharedShaderId != shaderNode->peerId()) {
+ Shader *refShader = shaderManager->lookupResource(sharedShaderId);
+ // We only introspect once per actual OpenGL shader program
+ // rather than once per ShaderNode.
+ shaderNode->initializeFromReference(*refShader);
+ break;
+ }
+ }
+ }
+ shaderNode->unsetDirty();
+ // Ensure we will rebuilt material caches
+ shaderNode->requestCacheRebuild();
+}
+
+const GraphicsApiFilterData *SubmissionContext::contextInfo() const
+{
+ return &m_contextInfo;
+}
+
+} // namespace Rhi
+} // namespace Render
+} // namespace Qt3DRender of namespace
+
+QT_END_NAMESPACE
diff --git a/src/plugins/renderers/rhi/graphicshelpers/submissioncontext_p.h b/src/plugins/renderers/rhi/graphicshelpers/submissioncontext_p.h
new file mode 100644
index 000000000..7578639e6
--- /dev/null
+++ b/src/plugins/renderers/rhi/graphicshelpers/submissioncontext_p.h
@@ -0,0 +1,266 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
+** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 QT3DRENDER_RENDER_RHI_SUBMISSIONCONTEXT_H
+#define QT3DRENDER_RENDER_RHI_SUBMISSIONCONTEXT_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <rhibuffer_p.h>
+#include <Qt3DRender/qclearbuffers.h>
+#include <Qt3DRender/qattribute.h>
+#include <Qt3DRender/private/handle_types_p.h>
+#include <Qt3DRender/qblitframebuffer.h>
+#include <Qt3DRender/private/shader_p.h>
+#include <Qt3DRender/private/statemask_p.h>
+#include <Qt3DRender/private/qgraphicsapifilter_p.h>
+#include <shaderparameterpack_p.h>
+#include <shadervariables_p.h>
+#include <rhihandle_types_p.h>
+#include <QSurface>
+#include <QtGui/private/qrhi_p.h>
+#include <QOffscreenSurface>
+
+QT_BEGIN_NAMESPACE
+
+class QAbstractOpenGLFunctions;
+
+namespace Qt3DRender {
+
+namespace Render {
+
+class Material;
+class AttachmentPack;
+class Attribute;
+class Buffer;
+class ShaderManager;
+struct StateVariant;
+class RenderTarget;
+class RenderStateSet;
+
+namespace Rhi {
+
+class Renderer;
+class GraphicsHelperInterface;
+class RHITexture;
+class RHIShader;
+class RHIShaderManager;
+class RenderCommand;
+
+typedef QPair<QString, int> NamedUniformLocation;
+
+class Q_AUTOTEST_EXPORT SubmissionContext
+{
+public:
+ enum Feature {
+ MRT = 0,
+ Tessellation,
+ UniformBufferObject,
+ BindableFragmentOutputs,
+ PrimitiveRestart,
+ RenderBufferDimensionRetrieval,
+ TextureDimensionRetrieval,
+ ShaderStorageObject,
+ Compute,
+ DrawBuffersBlend,
+ BlitFramebuffer,
+ IndirectDrawing,
+ MapBuffer,
+ Fences,
+ ShaderImage
+ };
+
+ enum FBOBindMode { FBODraw, FBORead, FBOReadAndDraw };
+
+ SubmissionContext();
+ ~SubmissionContext();
+
+ void initialize();
+ int id() const; // unique, small integer ID of this context
+ void setRenderer(Renderer *renderer) { m_renderer = renderer; }
+
+ bool beginDrawing(QSurface *surface);
+ void endDrawing(bool swapBuffers);
+ void releaseResources();
+ void setOpenGLContext(QOpenGLContext *ctx);
+ bool isInitialized() const { return m_initialized; }
+ const GraphicsApiFilterData *contextInfo() const;
+
+ // Shaders
+ struct ShaderCreationInfo
+ {
+ bool linkSucceeded = false;
+ QString logs;
+ };
+
+ ShaderCreationInfo createShaderProgram(RHIShader *shaderNode);
+ void loadShader(Shader *shader, ShaderManager *shaderManager,
+ RHIShaderManager *rhiShaderManager);
+
+ GLuint defaultFBO() const { return m_defaultFBO; }
+
+ // Shaders
+ bool activateShader(RHIShader *shader);
+ RHIShader *activeShader() const { return m_activeShader; }
+
+ // FBO
+ GLuint activeFBO() const { return m_activeFBO; }
+ void activateRenderTarget(const Qt3DCore::QNodeId id, const AttachmentPack &attachments,
+ GLuint defaultFboId);
+ QSize renderTargetSize(const QSize &surfaceSize) const;
+ QImage readFramebuffer(const QRect &rect);
+ void blitFramebuffer(Qt3DCore::QNodeId outputRenderTargetId,
+ Qt3DCore::QNodeId inputRenderTargetId, QRect inputRect, QRect outputRect,
+ uint defaultFboId,
+ QRenderTargetOutput::AttachmentPoint inputAttachmentPoint,
+ QRenderTargetOutput::AttachmentPoint outputAttachmentPoint,
+ QBlitFramebuffer::InterpolationMethod interpolationMethod);
+
+ // Attributes
+ void specifyAttribute(const Attribute *attribute, Buffer *buffer,
+ const ShaderAttribute *attributeDescription);
+ void specifyIndices(Buffer *buffer);
+
+ // Buffer
+ void updateBuffer(Buffer *buffer);
+ QByteArray downloadBufferContent(Buffer *buffer);
+ void releaseBuffer(Qt3DCore::QNodeId bufferId);
+ bool hasRHIBufferForBuffer(Buffer *buffer);
+ RHIBuffer *rhiBufferForRenderBuffer(Buffer *buf);
+
+ // Parameters
+ bool setParameters(ShaderParameterPack &parameterPack);
+
+ // RenderState
+ void applyStateSet(const RenderStateSet *ss, QRhiGraphicsPipeline *graphicsPipeline);
+ StateVariant *getState(RenderStateSet *ss, StateMask type) const;
+
+ // Swap chain
+
+ struct SwapChainInfo
+ {
+ QRhiSwapChain *swapChain = nullptr;
+ QRhiRenderBuffer *renderBuffer = nullptr;
+ QRhiRenderPassDescriptor *renderPassDescriptor = nullptr;
+ };
+ SwapChainInfo *swapChainForSurface(QSurface *surface) noexcept;
+
+ QRhiResourceUpdateBatch *m_currentUpdates {};
+
+ QRhi *rhi() const { return m_rhi; }
+ QRhiCommandBuffer *currentFrameCommandBuffer() const;
+ QRhiRenderTarget *currentFrameRenderTarget() const;
+ QRhiRenderPassDescriptor *currentRenderPassDescriptor() const;
+ QRhiSwapChain *currentSwapChain() const;
+ QSurfaceFormat format() const noexcept;
+
+private:
+ // Material
+ Material *activeMaterial() const { return m_material; }
+ void setActiveMaterial(Material *rmat);
+
+ // FBO
+ void bindFrameBufferAttachmentHelper(GLuint fboId, const AttachmentPack &attachments);
+ void activateDrawBuffers(const AttachmentPack &attachments);
+ void resolveRenderTargetFormat();
+ GLuint createRenderTarget(Qt3DCore::QNodeId renderTargetNodeId,
+ const AttachmentPack &attachments);
+ GLuint updateRenderTarget(Qt3DCore::QNodeId renderTargetNodeId,
+ const AttachmentPack &attachments, bool isActiveRenderTarget);
+
+ // Buffers
+ HRHIBuffer createRHIBufferFor(Buffer *buffer);
+ void uploadDataToRHIBuffer(Buffer *buffer, RHIBuffer *b, bool releaseBuffer = false);
+ QByteArray downloadDataFromRHIBuffer(Buffer *buffer, RHIBuffer *b);
+ bool bindRHIBuffer(RHIBuffer *buffer, RHIBuffer::Type type);
+
+ // States
+ void applyState(const StateVariant &state, QRhiGraphicsPipeline *graphicsPipeline);
+
+ bool m_ownCurrent;
+ const unsigned int m_id;
+ QSurface *m_surface;
+ QSize m_surfaceSize;
+
+ RHIShader *m_activeShader;
+
+ QHash<Qt3DCore::QNodeId, HRHIBuffer> m_renderBufferHash;
+ QHash<Qt3DCore::QNodeId, GLuint> m_renderTargets;
+ QHash<GLuint, QSize> m_renderTargetsSize;
+ QAbstractTexture::TextureFormat m_renderTargetFormat;
+
+ Material *m_material;
+ GLuint m_activeFBO;
+
+ Renderer *m_renderer;
+ QByteArray m_uboTempArray;
+
+ bool m_initialized;
+
+ GLint m_maxTextureUnits;
+ GLuint m_defaultFBO;
+ GraphicsApiFilterData m_contextInfo;
+
+ QRhi *m_rhi;
+ QHash<QSurface *, SwapChainInfo> m_swapChains;
+ QRhiSwapChain *m_currentSwapChain;
+ QRhiRenderPassDescriptor *m_currentRenderPassDescriptor;
+
+#ifndef QT_NO_OPENGL
+ QOffscreenSurface *m_fallbackSurface;
+#endif
+};
+
+} // namespace Rhi
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDER_RHI_SUBMISSIONCONTEXT_H
diff --git a/src/plugins/renderers/rhi/io/io.pri b/src/plugins/renderers/rhi/io/io.pri
new file mode 100644
index 000000000..bac06ba95
--- /dev/null
+++ b/src/plugins/renderers/rhi/io/io.pri
@@ -0,0 +1,8 @@
+INCLUDEPATH += $$PWD
+
+SOURCES += \
+ $$PWD/rhibuffer.cpp
+
+HEADERS += \
+ $$PWD/rhibuffer_p.h
+
diff --git a/src/plugins/renderers/rhi/io/rhibuffer.cpp b/src/plugins/renderers/rhi/io/rhibuffer.cpp
new file mode 100644
index 000000000..7b63b01a2
--- /dev/null
+++ b/src/plugins/renderers/rhi/io/rhibuffer.cpp
@@ -0,0 +1,199 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 "rhibuffer_p.h"
+#include <submissioncontext_p.h>
+#include <QtGui/private/qrhi_p.h>
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+namespace Render {
+
+namespace Rhi {
+
+namespace {
+QRhiBuffer::UsageFlag bufferTypeToRhi(RHIBuffer::Type t)
+{
+ switch (t) {
+ case RHIBuffer::Type::ArrayBuffer:
+ return QRhiBuffer::VertexBuffer;
+ case RHIBuffer::Type::IndexBuffer:
+ return QRhiBuffer::IndexBuffer;
+ case RHIBuffer::Type::UniformBuffer:
+ return QRhiBuffer::UniformBuffer;
+ default:
+ RHI_UNIMPLEMENTED;
+ return QRhiBuffer::StorageBuffer;
+ }
+}
+}
+
+// A UBO is created for each ShaderData Shader Pair
+// That means a UBO is unique to a shader/shaderdata
+
+RHIBuffer::RHIBuffer() : m_bufferId(0), m_dynamic(true), m_lastTarget(GL_ARRAY_BUFFER) { }
+
+bool RHIBuffer::bind(SubmissionContext *ctx, Type t)
+{
+ assert(ctx->m_currentUpdates);
+ if (this->m_datasToUpload.empty())
+ return bool(m_rhiBuffer);
+
+ const auto uploadMethod = m_dynamic ? &QRhiResourceUpdateBatch::updateDynamicBuffer
+ : qOverload<QRhiBuffer *, int, int, const void *>(
+ &QRhiResourceUpdateBatch::uploadStaticBuffer);
+ if (!m_rhiBuffer) {
+ if (m_allocSize <= 0)
+ return false;
+
+ const auto kind = m_dynamic ? QRhiBuffer::Dynamic : QRhiBuffer::Static;
+ const auto usage = bufferTypeToRhi(t);
+
+ // RHI does not seem to support using the same buffer with different types
+ if (m_rhiBuffer)
+ assert(m_rhiBuffer->usage() == usage);
+
+ if (!m_rhiBuffer)
+ m_rhiBuffer = ctx->rhi()->newBuffer(kind, usage, m_allocSize);
+ assert(m_rhiBuffer);
+
+ m_rhiBuffer->build();
+#if defined(QT_DEBUG)
+ {
+ // for debug: we set the buffer to zero
+ auto ptr = new char[m_allocSize] {};
+ (ctx->m_currentUpdates->*uploadMethod)(m_rhiBuffer, 0, m_allocSize, ptr);
+ delete[] ptr;
+ }
+#endif
+ }
+
+ for (const std::pair<QByteArray, int> &pair : this->m_datasToUpload) {
+ const QByteArray &data = pair.first;
+ int offset = pair.second;
+ (ctx->m_currentUpdates->*uploadMethod)(m_rhiBuffer, offset, data.size(), data.constData());
+ }
+
+ m_datasToUpload.clear();
+ return true;
+}
+
+bool RHIBuffer::release(SubmissionContext *ctx)
+{
+ if (m_rhiBuffer)
+ m_rhiBuffer->release();
+ return true;
+}
+
+bool RHIBuffer::create(SubmissionContext *ctx)
+{
+ return true;
+}
+
+void RHIBuffer::destroy(SubmissionContext *ctx)
+{
+ if (m_rhiBuffer) {
+ m_rhiBuffer->releaseAndDestroyLater();
+ m_rhiBuffer = nullptr;
+ }
+ m_allocSize = 0;
+}
+
+void RHIBuffer::orphan(SubmissionContext *)
+{
+ m_datasToUpload.clear();
+ if (m_rhiBuffer) {
+ m_rhiBuffer->releaseAndDestroyLater();
+ m_rhiBuffer = nullptr;
+ }
+ m_allocSize = 0;
+}
+
+void RHIBuffer::allocate(SubmissionContext *ctx, const QByteArray &data, bool dynamic)
+{
+ m_datasToUpload.clear();
+ m_datasToUpload.push_back({ data, 0 });
+ m_allocSize = data.size();
+ m_dynamic = dynamic;
+}
+
+void RHIBuffer::update(SubmissionContext *ctx, const QByteArray &data, int offset)
+{
+ m_datasToUpload.push_back({ data, offset });
+}
+
+QByteArray RHIBuffer::download(SubmissionContext *ctx, uint size)
+{
+ RHI_UNIMPLEMENTED;
+ // char *gpu_ptr = ctx->mapBuffer(m_lastTarget, size);
+ // QByteArray data;
+ // if (gpu_ptr != nullptr) {
+ // data.resize(size);
+ // std::copy(gpu_ptr, gpu_ptr+size, data.data());
+ // }
+ // ctx->unmapBuffer(m_lastTarget);
+ // return data;
+ return {};
+}
+
+void RHIBuffer::bindBufferBase(SubmissionContext *ctx, int bindingPoint, RHIBuffer::Type t)
+{
+ RHI_UNIMPLEMENTED;
+ // ctx->bindBufferBase(glBufferTypes[t], bindingPoint, m_bufferId);
+}
+
+void RHIBuffer::bindBufferBase(SubmissionContext *ctx, int bindingPoint)
+{
+ RHI_UNIMPLEMENTED;
+ // ctx->bindBufferBase(m_lastTarget, bindingPoint, m_bufferId);
+}
+
+void RHIBuffer::cleanup()
+{
+ destroy(nullptr);
+}
+
+} // namespace Rhi
+
+} // namespace Render
+
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
diff --git a/src/plugins/renderers/rhi/io/rhibuffer_p.h b/src/plugins/renderers/rhi/io/rhibuffer_p.h
new file mode 100644
index 000000000..4616c4111
--- /dev/null
+++ b/src/plugins/renderers/rhi/io/rhibuffer_p.h
@@ -0,0 +1,116 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 QT3DRENDER_RENDER_RHI_RHIBUFFER_P_H
+#define QT3DRENDER_RENDER_RHI_RHIBUFFER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <Qt3DCore/qnodeid.h>
+#include <qbytearray.h>
+
+QT_BEGIN_NAMESPACE
+class QRhiBuffer;
+namespace Qt3DRender {
+
+namespace Render {
+
+namespace Rhi {
+
+class SubmissionContext;
+
+class RHIBuffer
+{
+public:
+ RHIBuffer();
+
+ enum Type {
+ ArrayBuffer = 0,
+ UniformBuffer,
+ IndexBuffer,
+ ShaderStorageBuffer,
+ PixelPackBuffer,
+ PixelUnpackBuffer,
+ DrawIndirectBuffer
+ };
+
+ bool bind(SubmissionContext *ctx, Type t);
+ bool release(SubmissionContext *ctx);
+ bool create(SubmissionContext *ctx);
+ void destroy(SubmissionContext *ctx);
+ void orphan(SubmissionContext *ctx);
+ void allocate(SubmissionContext *ctx, const QByteArray &data, bool dynamic = true);
+ void update(SubmissionContext *ctx, const QByteArray &data, int offset = 0);
+ QByteArray download(SubmissionContext *ctx, uint size);
+ void bindBufferBase(SubmissionContext *ctx, int bindingPoint, Type t);
+ void bindBufferBase(SubmissionContext *ctx, int bindingPoint);
+
+ void cleanup();
+
+ QRhiBuffer *rhiBuffer() const noexcept { return m_rhiBuffer; }
+
+private:
+ uint m_bufferId;
+ bool m_dynamic;
+ int m_allocSize {};
+ int m_lastTarget;
+
+ QRhiBuffer *m_rhiBuffer {};
+
+ std::vector<std::pair<QByteArray /*data*/, int /*offset*/>> m_datasToUpload;
+};
+
+} // namespace Rhi
+
+} // namespace Render
+
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDER_RHI_RHIBUFFER_P_H
diff --git a/src/plugins/renderers/rhi/jobs/filtercompatibletechniquejob.cpp b/src/plugins/renderers/rhi/jobs/filtercompatibletechniquejob.cpp
new file mode 100644
index 000000000..657e5eea7
--- /dev/null
+++ b/src/plugins/renderers/rhi/jobs/filtercompatibletechniquejob.cpp
@@ -0,0 +1,97 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 "filtercompatibletechniquejob_p.h"
+#include <Qt3DRender/private/techniquemanager_p.h>
+#include <Qt3DRender/private/nodemanagers_p.h>
+#include <Qt3DRender/private/job_common_p.h>
+#include <renderer_p.h>
+#include <submissioncontext_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+namespace Render {
+namespace Rhi {
+
+FilterCompatibleTechniqueJob::FilterCompatibleTechniqueJob()
+ : m_manager(nullptr), m_renderer(nullptr)
+{
+ SET_JOB_RUN_STAT_TYPE(this, JobTypes::FilterCompatibleTechniques, 0)
+}
+
+void FilterCompatibleTechniqueJob::setManager(TechniqueManager *manager)
+{
+ m_manager = manager;
+}
+
+TechniqueManager *FilterCompatibleTechniqueJob::manager() const
+{
+ return m_manager;
+}
+
+void FilterCompatibleTechniqueJob::setRenderer(Renderer *renderer)
+{
+ m_renderer = renderer;
+}
+
+Renderer *FilterCompatibleTechniqueJob::renderer() const
+{
+ return m_renderer;
+}
+
+void FilterCompatibleTechniqueJob::run()
+{
+ Q_ASSERT(m_manager != nullptr && m_renderer != nullptr);
+ Q_ASSERT(m_renderer->isRunning() && m_renderer->submissionContext()->isInitialized());
+
+ const QVector<Qt3DCore::QNodeId> dirtyTechniqueIds = m_manager->takeDirtyTechniques();
+ for (const Qt3DCore::QNodeId techniqueId : dirtyTechniqueIds) {
+ Technique *technique = m_manager->lookupResource(techniqueId);
+ if (Q_LIKELY(technique != nullptr))
+ technique->setCompatibleWithRenderer(
+ (*m_renderer->contextInfo() == *technique->graphicsApiFilter()));
+ }
+}
+
+} // namespace Rhi
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
diff --git a/src/plugins/renderers/rhi/jobs/filtercompatibletechniquejob_p.h b/src/plugins/renderers/rhi/jobs/filtercompatibletechniquejob_p.h
new file mode 100644
index 000000000..726ed9faa
--- /dev/null
+++ b/src/plugins/renderers/rhi/jobs/filtercompatibletechniquejob_p.h
@@ -0,0 +1,96 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 QT3DRENDER_RENDER_RHI_FILTERCOMPATIBLETECHNIQUEJOB_H
+#define QT3DRENDER_RENDER_RHI_FILTERCOMPATIBLETECHNIQUEJOB_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 <Qt3DCore/qaspectjob.h>
+#include <Qt3DRender/private/qt3drender_global_p.h>
+
+#include <QSharedPointer>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+namespace Render {
+
+class TechniqueManager;
+
+namespace Rhi {
+
+class Renderer;
+
+class Q_AUTOTEST_EXPORT FilterCompatibleTechniqueJob : public Qt3DCore::QAspectJob
+{
+public:
+ FilterCompatibleTechniqueJob();
+
+ void setManager(TechniqueManager *managers);
+ TechniqueManager *manager() const;
+
+ void setRenderer(Renderer *renderer);
+ Renderer *renderer() const;
+
+ void run() override;
+
+private:
+ TechniqueManager *m_manager;
+ Renderer *m_renderer;
+};
+
+typedef QSharedPointer<FilterCompatibleTechniqueJob> FilterCompatibleTechniqueJobPtr;
+
+} // namespace Rhi
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDER_RHI_FILTERCOMPATIBLETECHNIQUEJOB_H
diff --git a/src/plugins/renderers/rhi/jobs/jobs.pri b/src/plugins/renderers/rhi/jobs/jobs.pri
new file mode 100644
index 000000000..d80b8bfd9
--- /dev/null
+++ b/src/plugins/renderers/rhi/jobs/jobs.pri
@@ -0,0 +1,17 @@
+INCLUDEPATH += $$PWD
+
+SOURCES += \
+ $$PWD/filtercompatibletechniquejob.cpp \
+ $$PWD/materialparametergathererjob.cpp \
+ $$PWD/renderviewcommandbuilderjob.cpp \
+ $$PWD/renderviewcommandupdaterjob.cpp \
+ $$PWD/renderviewinitializerjob.cpp \
+ $$PWD/renderviewjobutils.cpp
+
+HEADERS += \
+ $$PWD/filtercompatibletechniquejob_p.h \
+ $$PWD/materialparametergathererjob_p.h \
+ $$PWD/renderviewcommandbuilderjob_p.h \
+ $$PWD/renderviewcommandupdaterjob_p.h \
+ $$PWD/renderviewinitializerjob_p.h \
+ $$PWD/renderviewjobutils_p.h
diff --git a/src/plugins/renderers/rhi/jobs/materialparametergathererjob.cpp b/src/plugins/renderers/rhi/jobs/materialparametergathererjob.cpp
new file mode 100644
index 000000000..c791d30f8
--- /dev/null
+++ b/src/plugins/renderers/rhi/jobs/materialparametergathererjob.cpp
@@ -0,0 +1,140 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Paul Lemire
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 "materialparametergathererjob_p.h"
+#include <Qt3DRender/private/nodemanagers_p.h>
+#include <Qt3DRender/private/managers_p.h>
+#include <Qt3DRender/private/renderpassfilternode_p.h>
+#include <Qt3DRender/private/techniquefilternode_p.h>
+#include <Qt3DRender/private/job_common_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+namespace Render {
+
+namespace Rhi {
+
+namespace {
+
+int materialParameterGathererCounter = 0;
+const int likelyNumberOfParameters = 24;
+
+} // anonymous
+
+MaterialParameterGathererJob::MaterialParameterGathererJob()
+ : Qt3DCore::QAspectJob(),
+ m_manager(nullptr),
+ m_techniqueFilter(nullptr),
+ m_renderPassFilter(nullptr)
+{
+ SET_JOB_RUN_STAT_TYPE(this, JobTypes::MaterialParameterGathering,
+ materialParameterGathererCounter++)
+}
+
+// TechniqueFilter / RenderPassFilter
+
+// Parameters from Material/Effect/Technique
+
+// Note: we could maybe improve that by having a smart update when we detect
+// that a parameter value has changed. That might require way more book keeping
+// which might make this solution a bit too complex
+
+// The fact that this can now be performed in parallel should already provide a big
+// improvement
+void MaterialParameterGathererJob::run()
+{
+ for (const HMaterial &materialHandle : qAsConst(m_handles)) {
+ Material *material = m_manager->materialManager()->data(materialHandle);
+
+ if (Q_UNLIKELY(!material->isEnabled()))
+ continue;
+
+ Effect *effect = m_manager->effectManager()->lookupResource(material->effect());
+ Technique *technique = findTechniqueForEffect(m_manager, m_techniqueFilter, effect);
+
+ if (Q_LIKELY(technique != nullptr)) {
+ RenderPassList passes =
+ findRenderPassesForTechnique(m_manager, m_renderPassFilter, technique);
+ if (Q_LIKELY(passes.size() > 0)) {
+ // Order set:
+ // 1 Pass Filter
+ // 2 Technique Filter
+ // 3 Material
+ // 4 Effect
+ // 5 Technique
+ // 6 RenderPass
+
+ // Add Parameters define in techniqueFilter and passFilter
+ // passFilter have priority over techniqueFilter
+
+ ParameterInfoList parameters;
+ // Doing the reserve allows a gain of 0.5ms on some of the demo examples
+ parameters.reserve(likelyNumberOfParameters);
+
+ if (m_renderPassFilter)
+ parametersFromParametersProvider(&parameters, m_manager->parameterManager(),
+ m_renderPassFilter);
+ if (m_techniqueFilter)
+ parametersFromParametersProvider(&parameters, m_manager->parameterManager(),
+ m_techniqueFilter);
+ // Get the parameters for our selected rendering setup (override what was defined in
+ // the technique/pass filter)
+ parametersFromMaterialEffectTechnique(&parameters, m_manager->parameterManager(),
+ material, effect, technique);
+
+ for (RenderPass *renderPass : passes) {
+ ParameterInfoList globalParameters = parameters;
+ parametersFromParametersProvider(&globalParameters,
+ m_manager->parameterManager(), renderPass);
+ m_parameters[material->peerId()].push_back({ renderPass, globalParameters });
+ }
+ }
+ }
+ }
+}
+
+} // Rhi
+
+} // Render
+
+} // Qt3DRender
+
+QT_END_NAMESPACE
diff --git a/src/plugins/renderers/rhi/jobs/materialparametergathererjob_p.h b/src/plugins/renderers/rhi/jobs/materialparametergathererjob_p.h
new file mode 100644
index 000000000..f635755d7
--- /dev/null
+++ b/src/plugins/renderers/rhi/jobs/materialparametergathererjob_p.h
@@ -0,0 +1,124 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Paul Lemire
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 QT3DRENDER_RENDER_RHI_MATERIALPARAMETERGATHERERJOB_P_H
+#define QT3DRENDER_RENDER_RHI_MATERIALPARAMETERGATHERERJOB_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <Qt3DCore/qaspectjob.h>
+#include <Qt3DCore/qnodeid.h>
+#include <Qt3DRender/private/handle_types_p.h>
+#include <Qt3DRender/private/qt3drender_global_p.h>
+#include <renderviewjobutils_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+namespace Render {
+
+class NodeManagers;
+class TechniqueFilter;
+class RenderPassFilter;
+
+namespace Rhi {
+class Renderer;
+
+// TO be executed for each FrameGraph branch with a given RenderPassFilter/TechniqueFilter
+
+class Q_AUTOTEST_EXPORT MaterialParameterGathererJob : public Qt3DCore::QAspectJob
+{
+public:
+ MaterialParameterGathererJob();
+
+ inline void setNodeManagers(NodeManagers *manager) Q_DECL_NOTHROW { m_manager = manager; }
+ inline void setTechniqueFilter(TechniqueFilter *techniqueFilter) Q_DECL_NOTHROW
+ {
+ m_techniqueFilter = techniqueFilter;
+ }
+ inline void setRenderPassFilter(RenderPassFilter *renderPassFilter) Q_DECL_NOTHROW
+ {
+ m_renderPassFilter = renderPassFilter;
+ }
+ inline const QHash<Qt3DCore::QNodeId, QVector<RenderPassParameterData>> &
+ materialToPassAndParameter() Q_DECL_NOTHROW
+ {
+ return m_parameters;
+ }
+ inline void setHandles(const QVector<HMaterial> &handles) Q_DECL_NOTHROW
+ {
+ m_handles = handles;
+ }
+
+ inline TechniqueFilter *techniqueFilter() const Q_DECL_NOTHROW { return m_techniqueFilter; }
+ inline RenderPassFilter *renderPassFilter() const Q_DECL_NOTHROW { return m_renderPassFilter; }
+
+ void run() final;
+
+private:
+ NodeManagers *m_manager;
+ TechniqueFilter *m_techniqueFilter;
+ RenderPassFilter *m_renderPassFilter;
+
+ // Material id to array of RenderPasse with parameters
+ MaterialParameterGathererData m_parameters;
+ QVector<HMaterial> m_handles;
+};
+
+typedef QSharedPointer<MaterialParameterGathererJob> MaterialParameterGathererJobPtr;
+
+} // Rhi
+
+} // Render
+
+} // Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDER_RHI_MATERIALPARAMETERGATHERERJOB_P_H
diff --git a/src/plugins/renderers/rhi/jobs/renderviewcommandbuilderjob.cpp b/src/plugins/renderers/rhi/jobs/renderviewcommandbuilderjob.cpp
new file mode 100644
index 000000000..7a7520b60
--- /dev/null
+++ b/src/plugins/renderers/rhi/jobs/renderviewcommandbuilderjob.cpp
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 "renderviewcommandbuilderjob_p.h"
+#include <Qt3DRender/private/job_common_p.h>
+#include <renderview_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+namespace Render {
+
+namespace Rhi {
+
+namespace {
+int renderViewInstanceCounter = 0;
+} // anonymous
+
+RenderViewCommandBuilderJob::RenderViewCommandBuilderJob()
+ : Qt3DCore::QAspectJob(), m_offset(0), m_count(0), m_renderView(nullptr)
+{
+ SET_JOB_RUN_STAT_TYPE(this, JobTypes::RenderViewCommandBuilder, renderViewInstanceCounter++)
+}
+
+void RenderViewCommandBuilderJob::run()
+{
+ if (!m_renderView->noDraw()) {
+ if (m_count == 0)
+ return;
+
+ const bool isDraw = !m_renderView->isCompute();
+ if (isDraw)
+ m_commandData = m_renderView->buildDrawRenderCommands(m_entities, m_offset, m_count);
+ else
+ m_commandData = m_renderView->buildComputeRenderCommands(m_entities, m_offset, m_count);
+ }
+}
+
+} // Rhi
+
+} // Render
+
+} // Qt3DRender
+
+QT_END_NAMESPACE
diff --git a/src/plugins/renderers/opengl/jobs/renderviewbuilderjob_p.h b/src/plugins/renderers/rhi/jobs/renderviewcommandbuilderjob_p.h
index 13e19daf7..0832772d3 100644
--- a/src/plugins/renderers/opengl/jobs/renderviewbuilderjob_p.h
+++ b/src/plugins/renderers/rhi/jobs/renderviewcommandbuilderjob_p.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2019 Klaralvdalens Datakonsult AB (KDAB).
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt3D module of the Qt Toolkit.
@@ -37,8 +37,8 @@
**
****************************************************************************/
-#ifndef QT3DRENDER_RENDER_OPENGL_RENDERVIEWCOMMANDBUILDERJOB_P_H
-#define QT3DRENDER_RENDER_OPENGL_RENDERVIEWCOMMANDBUILDERJOB_P_H
+#ifndef QT3DRENDER_RENDER_RHI_RENDERVIEWCOMMANDBUILDERJOB_P_H
+#define QT3DRENDER_RENDER_RHI_RENDERVIEWCOMMANDBUILDERJOB_P_H
//
// W A R N I N G
@@ -61,9 +61,7 @@ namespace Qt3DRender {
namespace Render {
-namespace OpenGL {
-
-class RenderView;
+namespace Rhi {
class Q_AUTOTEST_EXPORT RenderViewCommandBuilderJob : public Qt3DCore::QAspectJob
{
@@ -91,7 +89,7 @@ private:
typedef QSharedPointer<RenderViewCommandBuilderJob> RenderViewCommandBuilderJobPtr;
-} // OpenGL
+} // Rhi
} // Render
@@ -99,4 +97,4 @@ typedef QSharedPointer<RenderViewCommandBuilderJob> RenderViewCommandBuilderJobP
QT_END_NAMESPACE
-#endif // QT3DRENDER_RENDER_OPENGL_RENDERVIEWCOMMANDBUILDERJOB_P_H
+#endif // QT3DRENDER_RENDER_RHI_RENDERVIEWCOMMANDBUILDERJOB_P_H
diff --git a/src/plugins/renderers/opengl/jobs/renderviewbuilderjob.cpp b/src/plugins/renderers/rhi/jobs/renderviewcommandupdaterjob.cpp
index 468ab9342..442f9a7d3 100644
--- a/src/plugins/renderers/opengl/jobs/renderviewbuilderjob.cpp
+++ b/src/plugins/renderers/rhi/jobs/renderviewcommandupdaterjob.cpp
@@ -1,5 +1,6 @@
/****************************************************************************
**
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
** Copyright (C) 2016 Paul Lemire
** Contact: https://www.qt.io/licensing/
**
@@ -48,21 +49,21 @@ namespace Qt3DRender {
namespace Render {
-namespace OpenGL {
+namespace Rhi {
namespace {
int renderViewInstanceCounter = 0;
} // anonymous
RenderViewCommandUpdaterJob::RenderViewCommandUpdaterJob()
- : Qt3DCore::QAspectJob()
- , m_offset(0)
- , m_count(0)
- , m_renderView(nullptr)
- , m_renderer(nullptr)
- , m_renderables(nullptr)
+ : Qt3DCore::QAspectJob(),
+ m_offset(0),
+ m_count(0),
+ m_renderView(nullptr),
+ m_renderer(nullptr),
+ m_renderables()
{
- SET_JOB_RUN_STAT_TYPE(this, JobTypes::RenderCommandUpdater, renderViewInstanceCounter++);
+ SET_JOB_RUN_STAT_TYPE(this, JobTypes::RenderCommandUpdater, renderViewInstanceCounter++)
}
void RenderViewCommandUpdaterJob::run()
@@ -73,11 +74,11 @@ void RenderViewCommandUpdaterJob::run()
if (m_count == 0)
return;
// Update Render Commands (Uniform Change, Depth Change)
- m_renderView->updateRenderCommand(m_renderables, m_offset, m_count);
+ m_renderView->updateRenderCommand(m_renderables.data(), m_offset, m_count);
}
}
-} // OpenGL
+} // Rhi
} // Render
diff --git a/src/plugins/renderers/rhi/jobs/renderviewcommandupdaterjob_p.h b/src/plugins/renderers/rhi/jobs/renderviewcommandupdaterjob_p.h
new file mode 100644
index 000000000..3b40124ad
--- /dev/null
+++ b/src/plugins/renderers/rhi/jobs/renderviewcommandupdaterjob_p.h
@@ -0,0 +1,109 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
+** Copyright (C) 2016 Paul Lemire
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 QT3DRENDER_RENDER_RHI_RENDERVIEWCOMMANDUPDATEJOB_P_H
+#define QT3DRENDER_RENDER_RHI_RENDERVIEWCOMMANDUPDATEJOB_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <Qt3DCore/qaspectjob.h>
+#include <Qt3DRender/private/handle_types_p.h>
+#include <rendercommand_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+namespace Render {
+
+namespace Rhi {
+
+class RenderView;
+class Renderer;
+
+class Q_AUTOTEST_EXPORT RenderViewCommandUpdaterJob : public Qt3DCore::QAspectJob
+{
+public:
+ RenderViewCommandUpdaterJob();
+
+ inline void setRenderView(RenderView *rv) Q_DECL_NOTHROW { m_renderView = rv; }
+ inline void setRenderer(Renderer *renderer) Q_DECL_NOTHROW { m_renderer = renderer; }
+ inline void setRenderables(const EntityRenderCommandDataPtr &renderables, int offset,
+ int count) Q_DECL_NOTHROW
+ {
+ m_offset = offset;
+ m_count = count;
+ m_renderables = renderables;
+ }
+ EntityRenderCommandDataPtr renderables() const { return m_renderables; }
+
+ QVector<RenderCommand> &commands() Q_DECL_NOTHROW { return m_commands; }
+
+ void run() final;
+
+private:
+ int m_offset;
+ int m_count;
+ RenderView *m_renderView;
+ Renderer *m_renderer;
+ EntityRenderCommandDataPtr m_renderables;
+ QVector<RenderCommand> m_commands;
+};
+
+typedef QSharedPointer<RenderViewCommandUpdaterJob> RenderViewCommandUpdaterJobPtr;
+
+} // Rhi
+
+} // Render
+
+} // Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDER_RHI_RENDERVIEWCOMMANDUPDATEJOB_P_H
diff --git a/src/plugins/renderers/rhi/jobs/renderviewinitializerjob.cpp b/src/plugins/renderers/rhi/jobs/renderviewinitializerjob.cpp
new file mode 100644
index 000000000..2a95cecaa
--- /dev/null
+++ b/src/plugins/renderers/rhi/jobs/renderviewinitializerjob.cpp
@@ -0,0 +1,104 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
+** Copyright (C) 2016 Paul Lemire
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 "renderviewinitializerjob_p.h"
+
+#include <renderview_p.h>
+#include <renderer_p.h>
+#include <renderviewjobutils_p.h>
+#include <Qt3DRender/private/renderlogging_p.h>
+#include <Qt3DRender/private/job_common_p.h>
+
+#include <QElapsedTimer>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+namespace Render {
+namespace Rhi {
+
+namespace {
+// only accessed in ctor and dtor of RenderViewJob
+// which are always being called in a non concurrent manner
+int renderViewInstanceCounter = 0;
+} // anonymous
+
+RenderViewInitializerJob::RenderViewInitializerJob()
+ : m_renderer(nullptr),
+ m_fgLeaf(nullptr),
+ m_index(0),
+ m_renderView(nullptr) { SET_JOB_RUN_STAT_TYPE(this, JobTypes::RenderView,
+ renderViewInstanceCounter++) }
+
+ RenderViewInitializerJob::~RenderViewInitializerJob()
+{
+ renderViewInstanceCounter--;
+}
+
+void RenderViewInitializerJob::run()
+{
+ qCDebug(Jobs) << Q_FUNC_INFO << m_index;
+#if defined(QT3D_RENDER_VIEW_JOB_TIMINGS)
+ QElapsedTimer timer;
+ timer.start();
+ qint64 gatherLightsTime;
+ qint64 buildCommandsTime;
+#endif
+
+ // Create a RenderView object
+ // The RenderView are created from a QFrameAllocator stored in the current Thread local storage
+ m_renderView = new RenderView;
+
+ // RenderView should allocate heap resources using only the currentFrameAllocator
+ m_renderView->setRenderer(m_renderer);
+
+ // Populate the renderview's configuration from the framegraph
+ setRenderViewConfigFromFrameGraphLeafNode(m_renderView, m_fgLeaf);
+#if defined(QT3D_RENDER_VIEW_JOB_TIMINGS)
+ qint64 gatherStateTime = timer.nsecsElapsed();
+ timer.restart();
+#endif
+}
+
+} // namespace Rhi
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
diff --git a/src/plugins/renderers/rhi/jobs/renderviewinitializerjob_p.h b/src/plugins/renderers/rhi/jobs/renderviewinitializerjob_p.h
new file mode 100644
index 000000000..fb38c8716
--- /dev/null
+++ b/src/plugins/renderers/rhi/jobs/renderviewinitializerjob_p.h
@@ -0,0 +1,108 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Paul Lemire
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 QT3DRENDER_RENDER_RHI_RENDERVIEWINITIALIZERJOB_H
+#define QT3DRENDER_RENDER_RHI_RENDERVIEWINITIALIZERJOB_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <Qt3DCore/qaspectjob.h>
+#include <Qt3DCore/private/qframeallocator_p.h>
+#include <QSize>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+namespace Render {
+
+class FrameGraphNode;
+
+namespace Rhi {
+
+class Renderer;
+class RenderView;
+
+class Q_AUTOTEST_EXPORT RenderViewInitializerJob : public Qt3DCore::QAspectJob
+{
+public:
+ RenderViewInitializerJob();
+ ~RenderViewInitializerJob();
+
+ inline void setRenderer(Renderer *renderer) { m_renderer = renderer; }
+ inline RenderView *renderView() const Q_DECL_NOTHROW { return m_renderView; }
+
+ inline void setFrameGraphLeafNode(FrameGraphNode *fgLeaf) { m_fgLeaf = fgLeaf; }
+
+ // Sets the position in the queue of RenderViews that the
+ // RenderView generated by this job should be inserted. This is
+ // used to ensure that for example a RenderView for creating
+ // a shadow map texture is submitted before the RenderView that
+ // contains commands making use of the shadow map
+ inline void setSubmitOrderIndex(int index) { m_index = index; }
+ inline int submitOrderIndex() const { return m_index; }
+
+ void run() override;
+
+private:
+ Renderer *m_renderer;
+ FrameGraphNode *m_fgLeaf;
+ int m_index;
+ RenderView *m_renderView;
+};
+
+typedef QSharedPointer<RenderViewInitializerJob> RenderViewInitializerJobPtr;
+
+} // namespace Rhi
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDER_RHI_RENDERVIEWINITIALIZERJOB_H
diff --git a/src/plugins/renderers/rhi/jobs/renderviewjobutils.cpp b/src/plugins/renderers/rhi/jobs/renderviewjobutils.cpp
new file mode 100644
index 000000000..c9a37c5e1
--- /dev/null
+++ b/src/plugins/renderers/rhi/jobs/renderviewjobutils.cpp
@@ -0,0 +1,583 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 "renderviewjobutils_p.h"
+#include <Qt3DRender/private/renderlogging_p.h>
+
+#include <Qt3DRender/qgraphicsapifilter.h>
+#include <Qt3DRender/private/sphere_p.h>
+#include <Qt3DRender/qshaderdata.h>
+
+#include <Qt3DRender/private/cameraselectornode_p.h>
+#include <Qt3DRender/private/clearbuffers_p.h>
+#include <Qt3DRender/private/layerfilternode_p.h>
+#include <Qt3DRender/private/nodemanagers_p.h>
+#include <Qt3DRender/private/effect_p.h>
+#include <Qt3DRender/private/renderpassfilternode_p.h>
+#include <Qt3DRender/private/rendertargetselectornode_p.h>
+#include <Qt3DRender/private/sortpolicy_p.h>
+#include <Qt3DRender/private/techniquefilternode_p.h>
+#include <Qt3DRender/private/viewportnode_p.h>
+#include <Qt3DRender/private/managers_p.h>
+#include <Qt3DRender/private/shaderdata_p.h>
+#include <Qt3DRender/private/statesetnode_p.h>
+#include <Qt3DRender/private/dispatchcompute_p.h>
+#include <Qt3DRender/private/rendersurfaceselector_p.h>
+#include <Qt3DRender/private/rendercapture_p.h>
+#include <Qt3DRender/private/buffercapture_p.h>
+#include <Qt3DRender/private/stringtoint_p.h>
+#include <Qt3DRender/private/techniquemanager_p.h>
+#include <Qt3DRender/private/memorybarrier_p.h>
+#include <Qt3DRender/private/blitframebuffer_p.h>
+#include <Qt3DRender/private/waitfence_p.h>
+#include <Qt3DRender/private/renderstateset_p.h>
+#include <renderview_p.h>
+#include <shadervariables_p.h>
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt3DCore;
+
+namespace Qt3DRender {
+namespace Render {
+namespace Rhi {
+
+/*!
+ \internal
+ Walks up the framegraph tree from \a fgLeaf and builds up as much state
+ as possible and populates \a rv. For cases where we can't get the specific state
+ (e.g. because it depends upon more than just the framegraph) we store the data from
+ the framegraph that will be needed to later when the rest of the data becomes available
+*/
+void setRenderViewConfigFromFrameGraphLeafNode(RenderView *rv, const FrameGraphNode *fgLeaf)
+{
+ // The specific RenderPass to be used is also dependent upon the Effect and TechniqueFilter
+ // which is referenced by the Material which is referenced by the RenderMesh. So we can
+ // only store the filter info in the RenderView structure and use it to do the resolving
+ // when we build the RenderCommand list.
+ const NodeManagers *manager = rv->nodeManagers();
+ const FrameGraphNode *node = fgLeaf;
+
+ while (node) {
+ FrameGraphNode::FrameGraphNodeType type = node->nodeType();
+ if (node->isEnabled())
+ switch (type) {
+ case FrameGraphNode::InvalidNodeType:
+ // A base FrameGraphNode, can be used for grouping purposes
+ break;
+ case FrameGraphNode::CameraSelector:
+ // Can be set only once and we take camera nearest to the leaf node
+ if (!rv->renderCameraLens()) {
+ const CameraSelector *cameraSelector =
+ static_cast<const CameraSelector *>(node);
+ Entity *camNode = manager->renderNodesManager()->lookupResource(
+ cameraSelector->cameraUuid());
+ if (camNode) {
+ CameraLens *lens = camNode->renderComponent<CameraLens>();
+ rv->setRenderCameraEntity(camNode);
+ if (lens && lens->isEnabled()) {
+ rv->setRenderCameraLens(lens);
+ // ViewMatrix and ProjectionMatrix are computed
+ // later in updateMatrices()
+ // since at this point the transformation matrices
+ // may not yet have been updated
+ }
+ }
+ }
+ break;
+
+ case FrameGraphNode::LayerFilter: // Can be set multiple times in the tree
+ rv->appendLayerFilter(static_cast<const LayerFilterNode *>(node)->peerId());
+ break;
+
+ case FrameGraphNode::ProximityFilter: // Can be set multiple times in the tree
+ rv->appendProximityFilterId(node->peerId());
+ break;
+
+ case FrameGraphNode::RenderPassFilter:
+ // Can be set once
+ // TODO: Amalgamate all render pass filters from leaf to root
+ if (!rv->renderPassFilter())
+ rv->setRenderPassFilter(static_cast<const RenderPassFilter *>(node));
+ break;
+
+ case FrameGraphNode::RenderTarget: {
+ // Can be set once and we take render target nearest to the leaf node
+ const RenderTargetSelector *targetSelector =
+ static_cast<const RenderTargetSelector *>(node);
+ QNodeId renderTargetUid = targetSelector->renderTargetUuid();
+ HTarget renderTargetHandle =
+ manager->renderTargetManager()->lookupHandle(renderTargetUid);
+
+ // Add renderTarget Handle and build renderCommand AttachmentPack
+ if (!rv->renderTargetId()) {
+ rv->setRenderTargetId(renderTargetUid);
+
+ RenderTarget *renderTarget =
+ manager->renderTargetManager()->data(renderTargetHandle);
+ if (renderTarget)
+ rv->setAttachmentPack(AttachmentPack(renderTarget,
+ manager->attachmentManager(),
+ targetSelector->outputs()));
+ }
+ break;
+ }
+
+ case FrameGraphNode::ClearBuffers: {
+ const ClearBuffers *cbNode = static_cast<const ClearBuffers *>(node);
+ rv->addClearBuffers(cbNode);
+ break;
+ }
+
+ case FrameGraphNode::TechniqueFilter:
+ // Can be set once
+ // TODO Amalgamate all technique filters from leaf to root
+ if (!rv->techniqueFilter())
+ rv->setTechniqueFilter(static_cast<const TechniqueFilter *>(node));
+ break;
+
+ case FrameGraphNode::Viewport: {
+ // If the Viewport has already been set in a lower node
+ // Make it so that the new viewport is actually
+ // a subregion relative to that of the parent viewport
+ const ViewportNode *vpNode = static_cast<const ViewportNode *>(node);
+ rv->setViewport(ViewportNode::computeViewport(rv->viewport(), vpNode));
+ rv->setGamma(vpNode->gamma());
+ break;
+ }
+
+ case FrameGraphNode::SortMethod: {
+ const Render::SortPolicy *sortPolicy =
+ static_cast<const Render::SortPolicy *>(node);
+ rv->addSortType(sortPolicy->sortTypes());
+ break;
+ }
+
+ case FrameGraphNode::SubtreeEnabler:
+ // Has no meaning here. SubtreeEnabler was used
+ // in a prior step to filter the list of RenderViewJobs
+ break;
+
+ case FrameGraphNode::StateSet: {
+ const Render::StateSetNode *rStateSet =
+ static_cast<const Render::StateSetNode *>(node);
+ // Create global RenderStateSet for renderView if no stateSet was set before
+ RenderStateSet *stateSet = rv->stateSet();
+ if (stateSet == nullptr && rStateSet->hasRenderStates()) {
+ stateSet = new RenderStateSet();
+ rv->setStateSet(stateSet);
+ }
+
+ // Add states from new stateSet we might be missing
+ // but don' t override existing states (lower StateSetNode always has priority)
+ if (rStateSet->hasRenderStates())
+ addStatesToRenderStateSet(stateSet, rStateSet->renderStates(),
+ manager->renderStateManager());
+ break;
+ }
+
+ case FrameGraphNode::NoDraw: {
+ rv->setNoDraw(true);
+ break;
+ }
+
+ case FrameGraphNode::FrustumCulling: {
+ rv->setFrustumCulling(true);
+ break;
+ }
+
+ case FrameGraphNode::ComputeDispatch: {
+ const Render::DispatchCompute *dispatchCompute =
+ static_cast<const Render::DispatchCompute *>(node);
+ rv->setCompute(true);
+ rv->setComputeWorkgroups(dispatchCompute->x(), dispatchCompute->y(),
+ dispatchCompute->z());
+ break;
+ }
+
+ case FrameGraphNode::Lighting: {
+ // TODO
+ break;
+ }
+
+ case FrameGraphNode::Surface: {
+ // Use the surface closest to leaf node
+ if (rv->surface() == nullptr) {
+ const Render::RenderSurfaceSelector *surfaceSelector =
+ static_cast<const Render::RenderSurfaceSelector *>(node);
+ rv->setSurface(surfaceSelector->surface());
+ rv->setSurfaceSize(surfaceSelector->renderTargetSize()
+ * surfaceSelector->devicePixelRatio());
+ rv->setDevicePixelRatio(surfaceSelector->devicePixelRatio());
+ }
+ break;
+ }
+ case FrameGraphNode::RenderCapture: {
+ auto *renderCapture = const_cast<Render::RenderCapture *>(
+ static_cast<const Render::RenderCapture *>(node));
+ if (rv->renderCaptureNodeId().isNull() && renderCapture->wasCaptureRequested()) {
+ rv->setRenderCaptureNodeId(renderCapture->peerId());
+ rv->setRenderCaptureRequest(renderCapture->takeCaptureRequest());
+ }
+ break;
+ }
+
+ case FrameGraphNode::MemoryBarrier: {
+ // Not available in rhi
+ break;
+ }
+
+ case FrameGraphNode::BufferCapture: {
+ auto *bufferCapture = const_cast<Render::BufferCapture *>(
+ static_cast<const Render::BufferCapture *>(node));
+ if (bufferCapture != nullptr)
+ rv->setIsDownloadBuffersEnable(bufferCapture->isEnabled());
+ break;
+ }
+
+ case FrameGraphNode::BlitFramebuffer: {
+ const Render::BlitFramebuffer *blitFramebufferNode =
+ static_cast<const Render::BlitFramebuffer *>(node);
+ rv->setHasBlitFramebufferInfo(true);
+ BlitFramebufferInfo bfbInfo;
+ bfbInfo.sourceRenderTargetId = blitFramebufferNode->sourceRenderTargetId();
+ bfbInfo.destinationRenderTargetId =
+ blitFramebufferNode->destinationRenderTargetId();
+ bfbInfo.sourceRect = blitFramebufferNode->sourceRect();
+ bfbInfo.destinationRect = blitFramebufferNode->destinationRect();
+ bfbInfo.sourceAttachmentPoint = blitFramebufferNode->sourceAttachmentPoint();
+ bfbInfo.destinationAttachmentPoint =
+ blitFramebufferNode->destinationAttachmentPoint();
+ bfbInfo.interpolationMethod = blitFramebufferNode->interpolationMethod();
+ rv->setBlitFrameBufferInfo(bfbInfo);
+ break;
+ }
+
+ case FrameGraphNode::WaitFence: {
+ // Not available in rhi
+ break;
+ }
+
+ case FrameGraphNode::SetFence: {
+ // Not available in rhi
+ break;
+ }
+
+ case FrameGraphNode::NoPicking:
+ // Nothing to do RenderView wise for NoPicking
+ break;
+
+ default:
+ // Should never get here
+ qCWarning(Backend) << "Unhandled FrameGraphNode type";
+ }
+
+ node = node->parent();
+ }
+}
+
+/*!
+ \internal
+ Searches the best matching Technique from \a effect specified.
+*/
+Technique *findTechniqueForEffect(NodeManagers *manager, const TechniqueFilter *techniqueFilter,
+ Effect *effect)
+{
+ if (!effect)
+ return nullptr;
+
+ QVector<Technique *> matchingTechniques;
+ const bool hasInvalidTechniqueFilter =
+ (techniqueFilter == nullptr || techniqueFilter->filters().isEmpty());
+
+ // Iterate through the techniques in the effect
+ const auto techniqueIds = effect->techniques();
+ for (const QNodeId techniqueId : techniqueIds) {
+ Technique *technique = manager->techniqueManager()->lookupResource(techniqueId);
+
+ // Should be valid, if not there likely a problem with node addition/destruction changes
+ Q_ASSERT(technique);
+
+ // Check if the technique is compatible with the rendering API
+ // If no techniqueFilter is present, we return the technique as it satisfies OpenGL version
+ if (technique->isCompatibleWithRenderer()
+ && (hasInvalidTechniqueFilter
+ || technique->isCompatibleWithFilters(techniqueFilter->filters())))
+ matchingTechniques.append(technique);
+ }
+
+ if (matchingTechniques.size() == 0) // We failed to find a suitable technique to use :(
+ return nullptr;
+
+ if (matchingTechniques.size() == 1)
+ return matchingTechniques.first();
+
+ // Several compatible techniques, return technique with highest major and minor version
+ Technique *highest = matchingTechniques.first();
+ GraphicsApiFilterData filter = *highest->graphicsApiFilter();
+ for (auto it = matchingTechniques.cbegin() + 1; it < matchingTechniques.cend(); ++it) {
+ if (filter < *(*it)->graphicsApiFilter()) {
+ filter = *(*it)->graphicsApiFilter();
+ highest = *it;
+ }
+ }
+ return highest;
+}
+
+RenderPassList findRenderPassesForTechnique(NodeManagers *manager,
+ const RenderPassFilter *passFilter,
+ Technique *technique)
+{
+ Q_ASSERT(manager);
+ Q_ASSERT(technique);
+
+ RenderPassList passes;
+ const auto passIds = technique->renderPasses();
+ for (const QNodeId passId : passIds) {
+ RenderPass *renderPass = manager->renderPassManager()->lookupResource(passId);
+
+ if (renderPass && renderPass->isEnabled()) {
+ bool foundMatch = (!passFilter || passFilter->filters().size() == 0);
+
+ // A pass filter is present so we need to check for matching criteria
+ if (!foundMatch && renderPass->filterKeys().size() >= passFilter->filters().size()) {
+
+ // Iterate through the filter criteria and look for render passes with criteria that
+ // satisfy them
+ const auto filterKeyIds = passFilter->filters();
+ for (const QNodeId filterKeyId : filterKeyIds) {
+ foundMatch = false;
+ FilterKey *filterFilterKey =
+ manager->filterKeyManager()->lookupResource(filterKeyId);
+
+ const auto passFilterKeyIds = renderPass->filterKeys();
+ for (const QNodeId passFilterKeyId : passFilterKeyIds) {
+ FilterKey *passFilterKey =
+ manager->filterKeyManager()->lookupResource(passFilterKeyId);
+ if ((foundMatch = (*passFilterKey == *filterFilterKey)))
+ break;
+ }
+
+ if (!foundMatch) {
+ // No match for criterion in any of the render pass' criteria
+ break;
+ }
+ }
+ }
+
+ if (foundMatch) {
+ // Found a renderpass that satisfies our needs. Add it in order
+ passes << renderPass;
+ }
+ }
+ }
+
+ return passes;
+}
+
+ParameterInfoList::const_iterator findParamInfo(ParameterInfoList *params, const int nameId)
+{
+ const ParameterInfoList::const_iterator end = params->cend();
+ ParameterInfoList::const_iterator it = std::lower_bound(params->cbegin(), end, nameId);
+ if (it != end && it->nameId != nameId)
+ return end;
+ return it;
+}
+
+void addParametersForIds(ParameterInfoList *params, ParameterManager *manager,
+ const Qt3DCore::QNodeIdVector &parameterIds)
+{
+ for (const QNodeId paramId : parameterIds) {
+ const HParameter parameterHandle = manager->lookupHandle(paramId);
+ const Parameter *param = manager->data(parameterHandle);
+ ParameterInfoList::iterator it =
+ std::lower_bound(params->begin(), params->end(), param->nameId());
+ if (it == params->end() || it->nameId != param->nameId())
+ params->insert(it, ParameterInfo(param->nameId(), parameterHandle));
+ }
+}
+
+void parametersFromMaterialEffectTechnique(ParameterInfoList *infoList, ParameterManager *manager,
+ Material *material, Effect *effect, Technique *technique)
+{
+ // The parameters are taken in the following priority order:
+ //
+ // 1) Material
+ // 2) Effect
+ // 3) Technique
+ //
+ // That way a user can override defaults in Effect's and Techniques on a
+ // object manner and a Technique can override global defaults from the Effect.
+ parametersFromParametersProvider(infoList, manager, material);
+ parametersFromParametersProvider(infoList, manager, effect);
+ parametersFromParametersProvider(infoList, manager, technique);
+}
+
+// Only add states with types we don't already have
+void addStatesToRenderStateSet(RenderStateSet *stateSet, const QVector<Qt3DCore::QNodeId> &stateIds,
+ RenderStateManager *manager)
+{
+ for (const Qt3DCore::QNodeId &stateId : stateIds) {
+ RenderStateNode *node = manager->lookupResource(stateId);
+ if (node->isEnabled() && stateSet->canAddStateOfType(node->type())) {
+ stateSet->addState(node->impl());
+ }
+ }
+}
+
+namespace {
+
+const QString blockArray = QStringLiteral("[%1]");
+const int qNodeIdTypeId = qMetaTypeId<QNodeId>();
+
+}
+
+UniformBlockValueBuilder::UniformBlockValueBuilder()
+ : updatedPropertiesOnly(false), shaderDataManager(nullptr), textureManager(nullptr)
+{
+}
+
+UniformBlockValueBuilder::~UniformBlockValueBuilder() { }
+
+void UniformBlockValueBuilder::buildActiveUniformNameValueMapHelper(
+ const ShaderData *currentShaderData, const QString &blockName,
+ const QString &qmlPropertyName, const QVariant &value)
+{
+ // In the end, values are either scalar or a scalar array
+ // Composed elements (structs, structs array) are simplified into simple scalars
+ if (value.userType() == QMetaType::QVariantList) { // Array
+ QVariantList list = value.value<QVariantList>();
+ if (list.at(0).userType()
+ == qNodeIdTypeId) { // Array of struct qmlPropertyName[i].structMember
+ for (int i = 0; i < list.size(); ++i) {
+ const QVariant &variantElement = list.at(i);
+ if (list.at(i).userType() == qNodeIdTypeId) {
+ const auto nodeId = variantElement.value<QNodeId>();
+ ShaderData *subShaderData = shaderDataManager->lookupResource(nodeId);
+ if (subShaderData) {
+ buildActiveUniformNameValueMapStructHelper(
+ subShaderData,
+ blockName + QLatin1Char('.') + qmlPropertyName + blockArray.arg(i),
+ QLatin1String(""));
+ }
+ // Note: we only handle ShaderData as nested container nodes here
+ }
+ }
+ } else { // Array of scalar/vec qmlPropertyName[0]
+ QString varName;
+ varName.reserve(blockName.length() + 1 + qmlPropertyName.length() + 3);
+ varName.append(blockName);
+ varName.append(QLatin1String("."));
+ varName.append(qmlPropertyName);
+ varName.append(QLatin1String("[0]"));
+ if (uniforms.contains(varName)) {
+ qCDebug(Shaders) << "UBO array member " << varName << " set for update";
+ activeUniformNamesToValue.insert(StringToInt::lookupId(varName), value);
+ }
+ }
+ } else if (value.userType() == qNodeIdTypeId) { // Struct qmlPropertyName.structMember
+ const auto nodeId = value.value<QNodeId>();
+ ShaderData *rSubShaderData = shaderDataManager->lookupResource(nodeId);
+ if (rSubShaderData) {
+ buildActiveUniformNameValueMapStructHelper(rSubShaderData, blockName, qmlPropertyName);
+ } else if (textureManager->contains(nodeId)) {
+ const auto varId =
+ StringToInt::lookupId(blockName + QLatin1Char('.') + qmlPropertyName);
+ activeUniformNamesToValue.insert(varId, value);
+ }
+ } else { // Scalar / Vec
+ QString varName;
+ varName.reserve(blockName.length() + 1 + qmlPropertyName.length());
+ varName.append(blockName);
+ varName.append(QLatin1String("."));
+ varName.append(qmlPropertyName);
+ if (uniforms.contains(varName)) {
+ qCDebug(Shaders) << "UBO scalar member " << varName << " set for update";
+
+ // If the property needs to be transformed, we transform it here as
+ // the shaderdata cannot hold transformed properties for multiple
+ // thread contexts at once
+ activeUniformNamesToValue.insert(
+ StringToInt::lookupId(varName),
+ currentShaderData->getTransformedProperty(qmlPropertyName, viewMatrix));
+ }
+ }
+}
+
+void UniformBlockValueBuilder::buildActiveUniformNameValueMapStructHelper(
+ const ShaderData *rShaderData, const QString &blockName, const QString &qmlPropertyName)
+{
+ const QHash<QString, ShaderData::PropertyValue> &properties = rShaderData->properties();
+ auto it = properties.begin();
+ const auto end = properties.end();
+
+ while (it != end) {
+ QString fullBlockName;
+ fullBlockName.reserve(blockName.length() + 1 + qmlPropertyName.length());
+ fullBlockName.append(blockName);
+ if (!qmlPropertyName.isEmpty()) {
+ fullBlockName.append(QLatin1String("."));
+ fullBlockName.append(qmlPropertyName);
+ }
+ buildActiveUniformNameValueMapHelper(rShaderData, fullBlockName, it.key(),
+ it.value().value);
+ ++it;
+ }
+}
+
+ParameterInfo::ParameterInfo(const int nameId, const HParameter &handle)
+ : nameId(nameId), handle(handle)
+{
+}
+
+bool ParameterInfo::operator<(const ParameterInfo &other) const Q_DECL_NOEXCEPT
+{
+ return nameId < other.nameId;
+}
+
+bool ParameterInfo::operator<(const int otherNameId) const Q_DECL_NOEXCEPT
+{
+ return nameId < otherNameId;
+}
+
+} // namespace Rhi
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
diff --git a/src/plugins/renderers/rhi/jobs/renderviewjobutils_p.h b/src/plugins/renderers/rhi/jobs/renderviewjobutils_p.h
new file mode 100644
index 000000000..31bc29b8d
--- /dev/null
+++ b/src/plugins/renderers/rhi/jobs/renderviewjobutils_p.h
@@ -0,0 +1,189 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 QT3DRENDER_RENDER_RHI_RENDERVIEWJOBUTILS_P_H
+#define QT3DRENDER_RENDER_RHI_RENDERVIEWJOBUTILS_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <Qt3DRender/qt3drender_global.h>
+#include <Qt3DCore/qnodeid.h>
+#include <QtCore/qhash.h>
+#include <QtCore/qvariant.h>
+#include <QMatrix4x4>
+#include <Qt3DRender/private/uniform_p.h>
+#include <Qt3DRender/private/handle_types_p.h>
+#include <Qt3DRender/private/aligned_malloc_p.h>
+#include <shadervariables_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DCore {
+class QFrameAllocator;
+}
+
+namespace Qt3DRender {
+namespace Render {
+
+class FrameGraphNode;
+class ParameterManager;
+class Effect;
+class Entity;
+class Material;
+class RenderPass;
+class Technique;
+class TechniqueFilter;
+class RenderPassFilter;
+class NodeManagers;
+class ShaderDataManager;
+class ShaderData;
+class TextureManager;
+class RenderStateManager;
+class RenderStateCollection;
+class RenderStateSet;
+
+namespace Rhi {
+class Renderer;
+class RenderView;
+struct ShaderUniform;
+
+Q_AUTOTEST_EXPORT void setRenderViewConfigFromFrameGraphLeafNode(RenderView *rv,
+ const FrameGraphNode *fgLeaf);
+
+Q_AUTOTEST_EXPORT Technique *findTechniqueForEffect(NodeManagers *manager,
+ const TechniqueFilter *techniqueFilter,
+ Effect *effect);
+
+typedef QVarLengthArray<RenderPass *, 4> RenderPassList;
+Q_AUTOTEST_EXPORT RenderPassList findRenderPassesForTechnique(NodeManagers *manager,
+ const RenderPassFilter *passFilter,
+ Technique *technique);
+
+// Extracts the type T from a QVariant v without using QVariant::value which is slow
+// Note: Assumes you are 100% sure about the type you requested
+template<typename T>
+inline T variant_value(const QVariant &v)
+{
+ return *reinterpret_cast<const T *>(v.data());
+}
+
+struct ParameterInfo
+{
+ explicit ParameterInfo(const int nameId = -1, const HParameter &handle = HParameter());
+
+ int nameId;
+ HParameter handle;
+
+ bool operator<(const int otherNameId) const Q_DECL_NOEXCEPT;
+ bool operator<(const ParameterInfo &other) const Q_DECL_NOEXCEPT;
+};
+typedef QVector<ParameterInfo> ParameterInfoList;
+
+struct RenderPassParameterData
+{
+ RenderPass *pass;
+ ParameterInfoList parameterInfo;
+};
+QT3D_DECLARE_TYPEINFO_3(Qt3DRender, Render, Rhi, RenderPassParameterData, Q_MOVABLE_TYPE)
+
+using MaterialParameterGathererData = QHash<Qt3DCore::QNodeId, QVector<RenderPassParameterData>>;
+
+Q_AUTOTEST_EXPORT void parametersFromMaterialEffectTechnique(ParameterInfoList *infoList,
+ ParameterManager *manager,
+ Material *material, Effect *effect,
+ Technique *technique);
+
+Q_AUTOTEST_EXPORT void addParametersForIds(ParameterInfoList *params, ParameterManager *manager,
+ const QVector<Qt3DCore::QNodeId> &parameterIds);
+
+template<class T>
+void parametersFromParametersProvider(ParameterInfoList *infoList, ParameterManager *manager,
+ T *provider)
+{
+ addParametersForIds(infoList, manager, provider->parameters());
+}
+
+Q_AUTOTEST_EXPORT ParameterInfoList::const_iterator findParamInfo(ParameterInfoList *infoList,
+ const int nameId);
+
+Q_AUTOTEST_EXPORT void addStatesToRenderStateSet(RenderStateSet *stateSet,
+ const QVector<Qt3DCore::QNodeId> &stateIds,
+ RenderStateManager *manager);
+
+typedef QHash<int, QVariant> UniformBlockValueBuilderHash;
+
+struct Q_AUTOTEST_EXPORT UniformBlockValueBuilder
+{
+ UniformBlockValueBuilder();
+ ~UniformBlockValueBuilder();
+
+ QT3D_ALIGNED_MALLOC_AND_FREE()
+
+ void buildActiveUniformNameValueMapHelper(const ShaderData *currentShaderData,
+ const QString &blockName,
+ const QString &qmlPropertyName,
+ const QVariant &value);
+ void buildActiveUniformNameValueMapStructHelper(const ShaderData *rShaderData,
+ const QString &blockName,
+ const QString &qmlPropertyName = QString());
+
+ bool updatedPropertiesOnly;
+ QSet<QString> uniforms;
+ UniformBlockValueBuilderHash activeUniformNamesToValue;
+ ShaderDataManager *shaderDataManager;
+ TextureManager *textureManager;
+ Matrix4x4 viewMatrix;
+};
+
+} // namespace Rhi
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDER_RHI_RENDERVIEWJOBUTILS_P_H
diff --git a/src/plugins/renderers/rhi/main.cpp b/src/plugins/renderers/rhi/main.cpp
new file mode 100644
index 000000000..47efc7530
--- /dev/null
+++ b/src/plugins/renderers/rhi/main.cpp
@@ -0,0 +1,60 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 <Qt3DRender/private/qrendererplugin_p.h>
+#include <renderer_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class RhiRendererPlugin : public Qt3DRender::Render::QRendererPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID QRendererPluginFactoryInterface_iid FILE "rhirenderer.json")
+
+ Qt3DRender::Render::AbstractRenderer *
+ create(const QString &key, Qt3DRender::QRenderAspect::RenderType renderMode) override
+ {
+ Q_UNUSED(key)
+ return new Qt3DRender::Render::Rhi::Renderer(renderMode);
+ }
+};
+
+QT_END_NAMESPACE
+
+#include "main.moc"
diff --git a/src/plugins/renderers/rhi/managers/managers.pri b/src/plugins/renderers/rhi/managers/managers.pri
new file mode 100644
index 000000000..de3f29b30
--- /dev/null
+++ b/src/plugins/renderers/rhi/managers/managers.pri
@@ -0,0 +1,8 @@
+INCLUDEPATH += $$PWD
+
+HEADERS += \
+ $$PWD/rhihandle_types_p.h \
+ $$PWD/rhiresourcemanagers_p.h
+
+SOURCES += \
+ $$PWD/rhiresourcemanagers.cpp
diff --git a/src/plugins/renderers/rhi/managers/rhihandle_types_p.h b/src/plugins/renderers/rhi/managers/rhihandle_types_p.h
new file mode 100644
index 000000000..2e74131e1
--- /dev/null
+++ b/src/plugins/renderers/rhi/managers/rhihandle_types_p.h
@@ -0,0 +1,85 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 QT3DRENDER_RENDER_RHI_RHIHANDLE_TYPES_P_H
+#define QT3DRENDER_RENDER_RHI_RHIHANDLE_TYPES_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <Qt3DCore/private/qhandle_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+namespace Render {
+
+namespace Rhi {
+
+class RHIBuffer;
+class RHITexture;
+class RHIGraphicsPipeline;
+
+typedef Qt3DCore::QHandle<RHIBuffer> HRHIBuffer;
+typedef Qt3DCore::QHandle<RHITexture> HRHITexture;
+typedef Qt3DCore::QHandle<RHIGraphicsPipeline> HRHIGraphicsPipeline;
+
+} // namespace Rhi
+
+} // namespace Render
+
+} // namespace Qt3DRender
+
+#if defined(_MSC_VER)
+#define RHI_UNIMPLEMENTED // do { qDebug() << "Unimplemented: " << __FUNCSIG__; } while (0)
+#else
+#define RHI_UNIMPLEMENTED // do { qDebug() << "Unimplemented: " << __PRETTY_FUNCTION__; } while (0)
+#endif
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDER_RHI_RHIHANDLE_TYPES_P_H
diff --git a/src/plugins/renderers/rhi/managers/rhiresourcemanagers.cpp b/src/plugins/renderers/rhi/managers/rhiresourcemanagers.cpp
new file mode 100644
index 000000000..3e813250b
--- /dev/null
+++ b/src/plugins/renderers/rhi/managers/rhiresourcemanagers.cpp
@@ -0,0 +1,87 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 "rhiresourcemanagers_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+namespace Render {
+
+namespace Rhi {
+
+RHIResourceManagers::RHIResourceManagers()
+ : m_rhiBufferManager(new RHIBufferManager()),
+ m_rhiShaderManager(new RHIShaderManager()),
+ m_rhiTextureManager(new RHITextureManager()),
+ m_rhiGraphicsPipelineManager(new RHIGraphicsPipelineManager())
+{
+}
+
+RHIResourceManagers::~RHIResourceManagers()
+{
+ delete m_rhiTextureManager;
+ delete m_rhiShaderManager;
+ delete m_rhiBufferManager;
+ delete m_rhiGraphicsPipelineManager;
+}
+
+void RHIResourceManagers::releaseAllResources()
+{
+ auto releaseAll = [](auto *manager) noexcept {
+ const auto handles = manager->activeHandles();
+ for (const auto &handle : handles) {
+ manager->release(handle);
+ }
+ };
+
+ releaseAll(m_rhiTextureManager);
+ releaseAll(m_rhiBufferManager);
+ // releaseAll(m_rhiShaderManager);
+ releaseAll(m_rhiGraphicsPipelineManager);
+}
+
+} // Rhi
+
+} // Render
+
+} // Qt3DRender
+
+QT_END_NAMESPACE
diff --git a/src/plugins/renderers/rhi/managers/rhiresourcemanagers_p.h b/src/plugins/renderers/rhi/managers/rhiresourcemanagers_p.h
new file mode 100644
index 000000000..34758530d
--- /dev/null
+++ b/src/plugins/renderers/rhi/managers/rhiresourcemanagers_p.h
@@ -0,0 +1,153 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 QT3DRENDER_RENDER_RHI_RHIRESOURCEMANAGERS_P_H
+#define QT3DRENDER_RENDER_RHI_RHIRESOURCEMANAGERS_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <Qt3DRender/private/qt3drender_global_p.h>
+#include <Qt3DCore/private/qresourcemanager_p.h>
+#include <texture_p.h>
+#include <rhibuffer_p.h>
+#include <rhishader_p.h>
+#include <rhigraphicspipeline_p.h>
+#include <Qt3DRender/private/apishadermanager_p.h>
+#include <Qt3DRender/private/renderstateset_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+namespace Render {
+
+namespace Rhi {
+
+class Q_AUTOTEST_EXPORT RHIBufferManager
+ : public Qt3DCore::QResourceManager<RHIBuffer, Qt3DCore::QNodeId, Qt3DCore::NonLockingPolicy>
+{
+};
+
+class Q_AUTOTEST_EXPORT RHITextureManager
+ : public Qt3DCore::QResourceManager<RHITexture, Qt3DCore::QNodeId, Qt3DCore::NonLockingPolicy>
+{
+public:
+ QHash<RHITexture *, Qt3DCore::QNodeId> texNodeIdForRHITexture;
+};
+
+class Q_AUTOTEST_EXPORT RHIShaderManager : public APIShaderManager<RHIShader>
+{
+public:
+ explicit RHIShaderManager() : APIShaderManager<RHIShader>() { }
+};
+
+// Geometry | Shader | RenderStateMask
+struct GraphicsPipelineIdentifier
+{
+ HGeometry geometry;
+ Qt3DCore::QNodeId shader;
+ int renderViewIndex;
+};
+
+class Q_AUTOTEST_EXPORT RHIGraphicsPipelineManager
+ : public Qt3DCore::QResourceManager<RHIGraphicsPipeline, GraphicsPipelineIdentifier,
+ Qt3DCore::NonLockingPolicy>
+{
+public:
+ RHIGraphicsPipelineManager() { }
+};
+
+class Q_AUTOTEST_EXPORT RHIResourceManagers
+{
+public:
+ RHIResourceManagers();
+ ~RHIResourceManagers();
+
+ inline RHIShaderManager *rhiShaderManager() const noexcept { return m_rhiShaderManager; }
+ inline RHITextureManager *rhiTextureManager() const noexcept { return m_rhiTextureManager; }
+ inline RHIBufferManager *rhiBufferManager() const noexcept { return m_rhiBufferManager; }
+ inline RHIGraphicsPipelineManager *rhiGraphicsPipelineManager() const noexcept
+ {
+ return m_rhiGraphicsPipelineManager;
+ }
+
+ void releaseAllResources();
+
+private:
+ RHIBufferManager *m_rhiBufferManager;
+ RHIShaderManager *m_rhiShaderManager;
+ RHITextureManager *m_rhiTextureManager;
+ RHIGraphicsPipelineManager *m_rhiGraphicsPipelineManager;
+};
+
+inline uint qHash(const GraphicsPipelineIdentifier &key, uint seed)
+{
+ const QPair<HGeometry, Qt3DCore::QNodeId> p = { key.geometry, key.shader };
+ using QT_PREPEND_NAMESPACE(qHash);
+ return qHash(p, seed) + qHash(key.renderViewIndex, seed);
+}
+
+inline bool operator==(const GraphicsPipelineIdentifier &a, const GraphicsPipelineIdentifier &b)
+{
+ return a.geometry == b.geometry && a.shader == b.shader
+ && a.renderViewIndex == b.renderViewIndex;
+}
+
+} // Rhi
+
+} // Render
+
+} // Qt3DRender
+
+Q_DECLARE_RESOURCE_INFO(Qt3DRender::Render::Rhi::RHIGraphicsPipeline, Q_REQUIRES_CLEANUP)
+Q_DECLARE_RESOURCE_INFO(Qt3DRender::Render::Rhi::RHITexture, Q_REQUIRES_CLEANUP)
+Q_DECLARE_RESOURCE_INFO(Qt3DRender::Render::Rhi::RHIBuffer, Q_REQUIRES_CLEANUP)
+Q_DECLARE_RESOURCE_INFO(Qt3DRender::Render::Rhi::RHIShader, Q_REQUIRES_CLEANUP)
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDER_RHI_RHIRESOURCEMANAGERS_P_H
diff --git a/src/plugins/renderers/rhi/renderer/commandexecuter.cpp b/src/plugins/renderers/rhi/renderer/commandexecuter.cpp
new file mode 100644
index 000000000..cfa6531be
--- /dev/null
+++ b/src/plugins/renderers/rhi/renderer/commandexecuter.cpp
@@ -0,0 +1,400 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
+** Copyright (C) 2016 Paul Lemire <paul.lemire350@gmail.com>
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software 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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "commandexecuter_p.h"
+
+#include <Qt3DCore/private/qabstractaspect_p.h>
+#include <Qt3DCore/qbackendnode.h>
+#include <Qt3DRender/private/nodemanagers_p.h>
+#include <Qt3DRender/private/geometryrenderermanager_p.h>
+#include <Qt3DRender/private/stringtoint_p.h>
+#include <QJsonObject>
+#include <QJsonDocument>
+#include <QJsonArray>
+#include <submissioncontext_p.h>
+#include <renderview_p.h>
+#include <rendercommand_p.h>
+#include <renderer_p.h>
+#include <submissioncontext_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+namespace Debug {
+
+namespace {
+
+template<typename Type>
+QJsonObject typeToJsonObj(const Type &) noexcept
+{
+ return QJsonObject();
+}
+
+template<typename Type>
+QJsonValue typeToJsonValue(const Type &t) noexcept
+{
+ Q_UNUSED(t);
+ return QJsonValue();
+}
+
+template<>
+QJsonObject typeToJsonObj<QRectF>(const QRectF &rect) noexcept
+{
+ QJsonObject obj;
+
+ obj.insert(QLatin1String("x"), rect.x());
+ obj.insert(QLatin1String("y"), rect.y());
+ obj.insert(QLatin1String("width"), rect.width());
+ obj.insert(QLatin1String("height"), rect.height());
+
+ return obj;
+}
+
+template<>
+QJsonValue typeToJsonValue<QRectF>(const QRectF &rect) noexcept
+{
+ QJsonArray value;
+
+ value.push_back(rect.x());
+ value.push_back(rect.y());
+ value.push_back(rect.width());
+ value.push_back(rect.height());
+
+ return value;
+}
+
+template<>
+QJsonObject typeToJsonObj<QSize>(const QSize &s) noexcept
+{
+ QJsonObject obj;
+
+ obj.insert(QLatin1String("width"), s.width());
+ obj.insert(QLatin1String("height"), s.height());
+
+ return obj;
+}
+
+template<>
+QJsonValue typeToJsonValue<QSize>(const QSize &s) noexcept
+{
+ QJsonArray value;
+
+ value.push_back(s.width());
+ value.push_back(s.height());
+
+ return value;
+}
+
+template<>
+QJsonObject typeToJsonObj<QVector3D>(const QVector3D &v) noexcept
+{
+ QJsonObject obj;
+
+ obj.insert(QLatin1String("x"), v.x());
+ obj.insert(QLatin1String("y"), v.y());
+ obj.insert(QLatin1String("z"), v.z());
+
+ return obj;
+}
+
+template<>
+QJsonValue typeToJsonValue<QVector3D>(const QVector3D &v) noexcept
+{
+ QJsonArray value;
+
+ value.push_back(v.x());
+ value.push_back(v.y());
+ value.push_back(v.z());
+
+ return value;
+}
+
+template<>
+QJsonObject typeToJsonObj<Qt3DCore::QNodeId>(const Qt3DCore::QNodeId &v) noexcept
+{
+ QJsonObject obj;
+ obj.insert(QLatin1String("id"), qint64(v.id()));
+ return obj;
+}
+
+template<>
+QJsonValue typeToJsonValue<Qt3DCore::QNodeId>(const Qt3DCore::QNodeId &v) noexcept
+{
+ QJsonValue value(qint64(v.id()));
+ return value;
+}
+
+template<>
+QJsonObject typeToJsonObj<QVector4D>(const QVector4D &v) noexcept
+{
+ QJsonObject obj;
+
+ obj.insert(QLatin1String("x"), v.x());
+ obj.insert(QLatin1String("y"), v.y());
+ obj.insert(QLatin1String("z"), v.z());
+ obj.insert(QLatin1String("w"), v.w());
+
+ return obj;
+}
+
+template<>
+QJsonValue typeToJsonValue<QVector4D>(const QVector4D &v) noexcept
+{
+ QJsonArray value;
+
+ value.push_back(v.x());
+ value.push_back(v.y());
+ value.push_back(v.z());
+ value.push_back(v.w());
+
+ return value;
+}
+
+template<>
+QJsonObject typeToJsonObj<QMatrix4x4>(const QMatrix4x4 &v) noexcept
+{
+ QJsonObject obj;
+
+ obj.insert(QLatin1String("row 0"), typeToJsonObj(v.row(0)));
+ obj.insert(QLatin1String("row 1"), typeToJsonObj(v.row(0)));
+ obj.insert(QLatin1String("row 2"), typeToJsonObj(v.row(0)));
+ obj.insert(QLatin1String("row 3"), typeToJsonObj(v.row(0)));
+
+ return obj;
+}
+
+template<>
+QJsonValue typeToJsonValue<QMatrix4x4>(const QMatrix4x4 &v) noexcept
+{
+ QJsonArray value;
+
+ value.push_back(typeToJsonValue(v.row(0)));
+ value.push_back(typeToJsonValue(v.row(1)));
+ value.push_back(typeToJsonValue(v.row(2)));
+ value.push_back(typeToJsonValue(v.row(3)));
+
+ return value;
+}
+
+template<>
+QJsonValue typeToJsonValue<QVariant>(const QVariant &v) noexcept
+{
+ const int nodeTypeId = qMetaTypeId<Qt3DCore::QNodeId>();
+
+ if (v.userType() == nodeTypeId)
+ return typeToJsonValue(v.value<Qt3DCore::QNodeId>());
+
+ switch (v.userType()) {
+ case QMetaType::QVector3D:
+ return typeToJsonValue(v.value<QVector3D>());
+ case QMetaType::QVector4D:
+ return typeToJsonValue(v.value<QVector4D>());
+ case QMetaType::QMatrix4x4:
+ return typeToJsonValue(v.value<QMatrix4x4>());
+ default:
+ return QJsonValue::fromVariant(v);
+ }
+}
+
+template<typename Handle, typename Manager>
+QJsonObject backendNodeToJSon(Handle handle, Manager *manager) noexcept
+{
+ Qt3DCore::QBackendNode *node = manager->data(handle);
+ QJsonObject obj;
+ Qt3DCore::QNodeId id;
+ if (node != nullptr)
+ id = node->peerId();
+ obj.insert(QLatin1String("id"), qint64(id.id()));
+ return obj;
+}
+
+QJsonObject parameterPackToJson(const Render::Rhi::ShaderParameterPack &pack) noexcept
+{
+ QJsonObject obj;
+
+ const Render::Rhi::PackUniformHash &uniforms = pack.uniforms();
+ QJsonArray uniformsArray;
+ for (int i = 0, m = uniforms.keys.size(); i < m; ++i) {
+ QJsonObject uniformObj;
+ uniformObj.insert(QLatin1String("name"),
+ Render::StringToInt::lookupString(uniforms.keys.at(i)));
+ const Render::UniformValue::ValueType type = uniforms.values.at(i).valueType();
+ uniformObj.insert(QLatin1String("type"),
+ type == Render::UniformValue::ScalarValue ? QLatin1String("value")
+ : QLatin1String("texture"));
+ uniformsArray.push_back(uniformObj);
+ }
+ obj.insert(QLatin1String("uniforms"), uniformsArray);
+
+ QJsonArray texturesArray;
+ const QVector<Render::Rhi::ShaderParameterPack::NamedResource> &textures = pack.textures();
+ for (const auto &texture : textures) {
+ QJsonObject textureObj;
+ textureObj.insert(QLatin1String("name"),
+ Render::StringToInt::lookupString(texture.glslNameId));
+ textureObj.insert(QLatin1String("id"), qint64(texture.nodeId.id()));
+ texturesArray.push_back(textureObj);
+ }
+ obj.insert(QLatin1String("textures"), texturesArray);
+
+ const QVector<Render::Rhi::BlockToUBO> &ubos = pack.uniformBuffers();
+ QJsonArray ubosArray;
+ for (const auto &ubo : ubos) {
+ QJsonObject uboObj;
+ uboObj.insert(QLatin1String("index"), ubo.m_blockIndex);
+ uboObj.insert(QLatin1String("bufferId"), qint64(ubo.m_bufferID.id()));
+ ubosArray.push_back(uboObj);
+ }
+ obj.insert(QLatin1String("ubos"), ubosArray);
+
+ const QVector<Render::Rhi::BlockToSSBO> &ssbos = pack.shaderStorageBuffers();
+ QJsonArray ssbosArray;
+ for (const auto &ssbo : ssbos) {
+ QJsonObject ssboObj;
+ ssboObj.insert(QLatin1String("index"), ssbo.m_blockIndex);
+ ssboObj.insert(QLatin1String("bufferId"), qint64(ssbo.m_bufferID.id()));
+ ssbosArray.push_back(ssboObj);
+ }
+ obj.insert(QLatin1String("ssbos"), ssbosArray);
+
+ return obj;
+}
+
+} // anonymous
+
+CommandExecuter::CommandExecuter(Render::Rhi::Renderer *renderer) : m_renderer(renderer) { }
+
+// Render thread
+void CommandExecuter::performAsynchronousCommandExecution(
+ const QVector<Render::Rhi::RenderView *> &views)
+{
+ RHI_UNIMPLEMENTED;
+ //* QMutexLocker lock(&m_pendingCommandsMutex);
+ //* const QVector<Qt3DCore::Debug::AsynchronousCommandReply *> shellCommands =
+ //std::move(m_pendingCommands);
+ //* lock.unlock();
+ //*
+ //* for (auto *reply : shellCommands) {
+ //* if (reply->commandName() == QLatin1String("glinfo")) {
+ //* QJsonObject replyObj;
+ //* const GraphicsApiFilterData *contextInfo =
+ //m_renderer->submissionContext()->contextInfo();
+ //* if (contextInfo != nullptr) {
+ //* replyObj.insert(QLatin1String("api"),
+ //* contextInfo->m_api == QGraphicsApiFilter::OpenGL
+ //* ? QLatin1String("OpenGL")
+ //* : QLatin1String("OpenGLES"));
+ //* const QString versionString =
+ //* QString::number(contextInfo->m_major)
+ //* + QStringLiteral(".")
+ //* + QString::number(contextInfo->m_minor);
+ //* replyObj.insert(QLatin1String("version"), versionString);
+ //* replyObj.insert(QLatin1String("profile"),
+ //* contextInfo->m_profile == QGraphicsApiFilter::CoreProfile
+ //* ? QLatin1String("Core")
+ //* : contextInfo->m_profile ==
+ //QGraphicsApiFilter::CompatibilityProfile
+ //* ? QLatin1String("Compatibility")
+ //* : QLatin1String("None"));
+ //* }
+ //* reply->setData(QJsonDocument(replyObj).toJson());
+ //* } else if (reply->commandName() == QLatin1String("rendercommands")) {
+ //* QJsonObject replyObj;
+ //*
+ //* QJsonArray viewArray;
+ //* for (Render::Rhi::RenderView *v : views) {
+ //* QJsonObject viewObj;
+ //* viewObj.insert(QLatin1String("viewport"), typeToJsonValue(v->viewport()));
+ //* viewObj.insert(QLatin1String("surfaceSize"),
+ //typeToJsonValue(v->surfaceSize()));
+ //* viewObj.insert(QLatin1String("devicePixelRatio"), v->devicePixelRatio());
+ //* viewObj.insert(QLatin1String("noDraw"), v->noDraw());
+ //* viewObj.insert(QLatin1String("frustumCulling"), v->frustumCulling());
+ //* viewObj.insert(QLatin1String("compute"), v->isCompute());
+ //* viewObj.insert(QLatin1String("clearDepthValue"), v->clearDepthValue());
+ //* viewObj.insert(QLatin1String("clearStencilValue"), v->clearStencilValue());
+ //*
+ //* QJsonArray renderCommandsArray;
+ //* for (Render::Rhi::RenderCommand &c : v->commands()) {
+ //* QJsonObject commandObj;
+ //* Render::NodeManagers *nodeManagers = m_renderer->nodeManagers();
+ //* commandObj.insert(QLatin1String("shader"),
+ //typeToJsonValue(QVariant::fromValue(c.m_shaderId)));
+ //* commandObj.insert(QLatin1String("vao"), double(c.m_vao.handle()));
+ //* commandObj.insert(QLatin1String("instanceCount"), c.m_instanceCount);
+ //* commandObj.insert(QLatin1String("geometry"),
+ //backendNodeToJSon(c.m_geometry, nodeManagers->geometryManager()));
+ //* commandObj.insert(QLatin1String("geometryRenderer"),
+ //backendNodeToJSon(c.m_geometryRenderer, nodeManagers->geometryRendererManager()));
+ //* commandObj.insert(QLatin1String("shaderParameterPack"),
+ //parameterPackToJson(c.m_parameterPack));
+ //*
+ //* renderCommandsArray.push_back(commandObj);
+ //* }
+ //* viewObj.insert(QLatin1String("commands"), renderCommandsArray);
+ //* viewArray.push_back(viewObj);
+ //* }
+ //*
+ //* replyObj.insert(QLatin1String("renderViews"), viewArray);
+ //* reply->setData(QJsonDocument(replyObj).toJson());
+ //* }
+ //* reply->setFinished(true);
+ //* }
+}
+
+// Main thread
+QVariant CommandExecuter::executeCommand(const QStringList &args)
+{
+ RHI_UNIMPLEMENTED;
+ //* // Note: The replies will be deleted by the AspectCommandDebugger
+ //* if (args.length() > 0 &&
+ //* (args.first() == QLatin1String("glinfo") ||
+ //* args.first() == QLatin1String("rendercommands"))) {
+ //* auto reply = new Qt3DCore::Debug::AsynchronousCommandReply(args.first());
+ //* QMutexLocker lock(&m_pendingCommandsMutex);
+ //* m_pendingCommands.push_back(reply);
+ //* return QVariant::fromValue(reply);
+ //* }
+ return QVariant();
+}
+
+} // Debug
+
+} // Qt3DRenderer
+
+QT_END_NAMESPACE
diff --git a/src/plugins/renderers/rhi/renderer/commandexecuter_p.h b/src/plugins/renderers/rhi/renderer/commandexecuter_p.h
new file mode 100644
index 000000000..924930a49
--- /dev/null
+++ b/src/plugins/renderers/rhi/renderer/commandexecuter_p.h
@@ -0,0 +1,98 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
+** Copyright (C) 2016 Paul Lemire <paul.lemire350@gmail.com>
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software 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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DRENDER_DEBUG_COMMANDEXECUTER_H
+#define QT3DRENDER_DEBUG_COMMANDEXECUTER_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QVector>
+#include <QVariant>
+#include <QMutex>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DCore {
+
+namespace Debug {
+class AsynchronousCommandReply;
+} // Debug
+
+} // Qt3DCore
+
+namespace Qt3DRender {
+
+namespace Render {
+namespace Rhi {
+class Renderer;
+class RenderView;
+} // Rhi
+} // Render
+
+namespace Debug {
+
+class CommandExecuter
+{
+public:
+ explicit CommandExecuter(Render::Rhi::Renderer *renderer);
+
+ void performAsynchronousCommandExecution(const QVector<Render::Rhi::RenderView *> &views);
+
+ QVariant executeCommand(const QStringList &args);
+
+private:
+ Render::Rhi::Renderer *m_renderer;
+ QVector<Qt3DCore::Debug::AsynchronousCommandReply *> m_pendingCommands;
+ QMutex m_pendingCommandsMutex;
+};
+
+} // Debug
+
+} // Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_DEBUG_COMMANDEXECUTER_H
diff --git a/src/plugins/renderers/rhi/renderer/logging.cpp b/src/plugins/renderers/rhi/renderer/logging.cpp
new file mode 100644
index 000000000..29bdb2145
--- /dev/null
+++ b/src/plugins/renderers/rhi/renderer/logging.cpp
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 "logging_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+namespace Render {
+
+namespace Rhi {
+
+Q_LOGGING_CATEGORY(Backend, "Qt3D.Renderer.OpenGL.Backend", QtWarningMsg)
+Q_LOGGING_CATEGORY(Frontend, "Qt3D.Renderer.OpenGL.Frontend", QtWarningMsg)
+Q_LOGGING_CATEGORY(Io, "Qt3D.Renderer.OpenGL.IO", QtWarningMsg)
+Q_LOGGING_CATEGORY(Jobs, "Qt3D.Renderer.OpenGL.Jobs", QtWarningMsg)
+Q_LOGGING_CATEGORY(SceneLoaders, "Qt3D.Renderer.OpenGL.SceneLoaders", QtWarningMsg)
+Q_LOGGING_CATEGORY(Framegraph, "Qt3D.Renderer.OpenGL.Framegraph", QtWarningMsg)
+Q_LOGGING_CATEGORY(RenderNodes, "Qt3D.Renderer.OpenGL.RenderNodes", QtWarningMsg)
+Q_LOGGING_CATEGORY(Rendering, "Qt3D.Renderer.OpenGL.Rendering", QtWarningMsg)
+Q_LOGGING_CATEGORY(Memory, "Qt3D.Renderer.OpenGL.Memory", QtWarningMsg)
+Q_LOGGING_CATEGORY(Shaders, "Qt3D.Renderer.OpenGL.Shaders", QtWarningMsg)
+Q_LOGGING_CATEGORY(RenderStates, "Qt3D.Renderer.OpenGL.RenderStates", QtWarningMsg)
+Q_LOGGING_CATEGORY(VSyncAdvanceService, "Qt3D.Renderer.OpenGL.VsyncAdvanceService", QtWarningMsg)
+
+} // namespace Rhi
+
+} // namespace Render
+
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
diff --git a/src/plugins/renderers/rhi/renderer/logging_p.h b/src/plugins/renderers/rhi/renderer/logging_p.h
new file mode 100644
index 000000000..45d63978d
--- /dev/null
+++ b/src/plugins/renderers/rhi/renderer/logging_p.h
@@ -0,0 +1,85 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 QT3DRENDER_RENDER_RHI_RENDERLOGGING_P_H
+#define QT3DRENDER_RENDER_RHI_RENDERLOGGING_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QLoggingCategory>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+namespace Render {
+
+namespace Rhi {
+
+Q_DECLARE_LOGGING_CATEGORY(Backend)
+Q_DECLARE_LOGGING_CATEGORY(Frontend)
+Q_DECLARE_LOGGING_CATEGORY(Io)
+Q_DECLARE_LOGGING_CATEGORY(Jobs)
+Q_DECLARE_LOGGING_CATEGORY(SceneLoaders)
+Q_DECLARE_LOGGING_CATEGORY(Framegraph)
+Q_DECLARE_LOGGING_CATEGORY(RenderNodes)
+Q_DECLARE_LOGGING_CATEGORY(Rendering)
+Q_DECLARE_LOGGING_CATEGORY(Memory)
+Q_DECLARE_LOGGING_CATEGORY(Shaders)
+Q_DECLARE_LOGGING_CATEGORY(RenderStates)
+Q_DECLARE_LOGGING_CATEGORY(VSyncAdvanceService)
+
+} // namespace Rhi
+
+} // namespace Render
+
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDER_RHI_RENDERLOGGING_P_H
diff --git a/src/plugins/renderers/rhi/renderer/rendercommand.cpp b/src/plugins/renderers/rhi/renderer/rendercommand.cpp
new file mode 100644
index 000000000..8320abac1
--- /dev/null
+++ b/src/plugins/renderers/rhi/renderer/rendercommand.cpp
@@ -0,0 +1,112 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 "rendercommand_p.h"
+#include "renderer/rhigraphicspipeline_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+namespace Render {
+namespace Rhi {
+
+RenderCommand::RenderCommand()
+ : m_rhiShader(nullptr),
+ m_stateSet(nullptr),
+ m_depth(0.0f),
+ m_changeCost(0),
+ m_type(RenderCommand::Draw),
+ m_workGroups(),
+ m_primitiveCount(0),
+ m_primitiveType(QGeometryRenderer::Triangles),
+ m_restartIndexValue(-1),
+ m_firstInstance(0),
+ m_firstVertex(0),
+ m_verticesPerPatch(0),
+ m_instanceCount(0),
+ m_indexOffset(0),
+ m_indexAttributeByteOffset(0),
+ m_indexAttributeDataType(Qt3DRender::QAttribute::UnsignedShort),
+ m_indirectAttributeByteOffset(0),
+ m_drawIndexed(false),
+ m_drawIndirect(false),
+ m_primitiveRestartEnabled(false),
+ m_isValid(false),
+ indexAttribute(nullptr),
+ indexBuffer(nullptr),
+ m_commandUBO(),
+ pipeline(nullptr)
+
+{
+ m_workGroups[0] = 0;
+ m_workGroups[1] = 0;
+ m_workGroups[2] = 0;
+}
+
+bool RenderCommand::isValid() const noexcept
+{
+ return m_rhiShader && pipeline && pipeline->pipeline();
+}
+
+bool operator==(const RenderCommand &a, const RenderCommand &b) noexcept
+{
+ return (a.m_rhiShader == b.m_rhiShader && a.m_material == b.m_material
+ && a.m_stateSet == b.m_stateSet && a.m_geometry == b.m_geometry
+ && a.m_geometryRenderer == b.m_geometryRenderer
+ && a.m_indirectDrawBuffer == b.m_indirectDrawBuffer
+ && a.m_activeAttributes == b.m_activeAttributes && a.m_depth == b.m_depth
+ && a.m_changeCost == b.m_changeCost && a.m_shaderId == b.m_shaderId
+ && a.m_workGroups[0] == b.m_workGroups[0] && a.m_workGroups[1] == b.m_workGroups[1]
+ && a.m_workGroups[2] == b.m_workGroups[2] && a.m_primitiveCount == b.m_primitiveCount
+ && a.m_primitiveType == b.m_primitiveType
+ && a.m_restartIndexValue == b.m_restartIndexValue
+ && a.m_firstInstance == b.m_firstInstance && a.m_firstVertex == b.m_firstVertex
+ && a.m_verticesPerPatch == b.m_verticesPerPatch
+ && a.m_instanceCount == b.m_instanceCount && a.m_indexOffset == b.m_indexOffset
+ && a.m_indexAttributeByteOffset == b.m_indexAttributeByteOffset
+ && a.m_drawIndexed == b.m_drawIndexed && a.m_drawIndirect == b.m_drawIndirect
+ && a.m_primitiveRestartEnabled == b.m_primitiveRestartEnabled
+ && a.m_isValid == b.m_isValid && a.m_computeCommand == b.m_computeCommand);
+}
+
+} // namespace Rhi
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
diff --git a/src/plugins/renderers/rhi/renderer/rendercommand_p.h b/src/plugins/renderers/rhi/renderer/rendercommand_p.h
new file mode 100644
index 000000000..e6924a60c
--- /dev/null
+++ b/src/plugins/renderers/rhi/renderer/rendercommand_p.h
@@ -0,0 +1,211 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
+** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 QT3DRENDER_RENDER_RHI_RENDERCOMMAND_H
+#define QT3DRENDER_RENDER_RHI_RENDERCOMMAND_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qglobal.h>
+#include <shaderparameterpack_p.h>
+#include <rhihandle_types_p.h>
+#include <renderviewjobutils_p.h>
+#include <Qt3DRender/private/handle_types_p.h>
+#include <Qt3DRender/qgeometryrenderer.h>
+#include <QOpenGLShaderProgram>
+#include <QOpenGLTexture>
+#include <QMatrix4x4>
+#include <QtGui/private/qrhi_p.h>
+#include <Qt3DRender/qattribute.h>
+
+QT_BEGIN_NAMESPACE
+class QRhiGraphicsPipeline;
+class QRhiShaderResourceBindings;
+
+namespace Qt3DRender {
+
+namespace Render {
+
+class RenderStateSet;
+using RenderStateSetPtr = QSharedPointer<RenderStateSet>;
+
+namespace Rhi {
+
+class RHIShader;
+class RHIGraphicsPipeline;
+
+struct CommandUBO
+{
+ float modelMatrix[16];
+ float inverseModelMatrix[16];
+ float modelViewMatrix[16];
+ float modelNormalMatrix[12];
+ float inverseModelViewMatrix[16];
+ float mvp[16];
+ float inverseModelViewProjectionMatrix[16];
+};
+static_assert(sizeof(CommandUBO) == 6 * (16 * sizeof(float)) + 1 * (12 * sizeof(float)),
+ "UBO doesn't match std140");
+
+class Q_AUTOTEST_EXPORT RenderCommand
+{
+public:
+ RenderCommand();
+
+ bool isValid() const noexcept;
+
+ HMaterial m_material; // Purely used to ease sorting (minimize stage changes, binding changes
+ // ....)
+ RHIShader *m_rhiShader; // GL Shader to be used at render time
+ Qt3DCore::QNodeId m_shaderId; // Shader for given pass and mesh
+ ShaderParameterPack m_parameterPack; // Might need to be reworked so as to be able to destroy
+ // the Texture while submission is happening.
+ RenderStateSetPtr m_stateSet;
+
+ HGeometry m_geometry;
+ HGeometryRenderer m_geometryRenderer;
+
+ HBuffer m_indirectDrawBuffer; // Reference to indirect draw buffer (valid only m_drawIndirect ==
+ // true)
+ HComputeCommand m_computeCommand;
+
+ // A QAttribute pack might be interesting
+ // This is a temporary fix in the meantime, to remove the hacked methods in Technique
+ QVector<int> m_activeAttributes;
+
+ float m_depth;
+ int m_changeCost;
+
+ enum CommandType { Draw, Compute };
+
+ CommandType m_type;
+ int m_workGroups[3];
+
+ // Values filled for draw calls by Renderer (in prepare Submission)
+ GLsizei m_primitiveCount;
+ QGeometryRenderer::PrimitiveType m_primitiveType;
+ int m_restartIndexValue;
+ int m_firstInstance;
+ int m_firstVertex;
+ int m_verticesPerPatch;
+ int m_instanceCount;
+ int m_indexOffset;
+ uint m_indexAttributeByteOffset;
+ Qt3DRender::QAttribute::VertexBaseType m_indexAttributeDataType;
+ uint m_indirectAttributeByteOffset;
+ bool m_drawIndexed;
+ bool m_drawIndirect;
+ bool m_primitiveRestartEnabled;
+ bool m_isValid;
+
+ QVarLengthArray<QRhiCommandBuffer::VertexInput, 8> vertex_input;
+
+ const Attribute *indexAttribute {};
+ QRhiBuffer *indexBuffer {};
+
+ CommandUBO m_commandUBO;
+ RHIGraphicsPipeline *pipeline {};
+};
+
+Q_AUTOTEST_EXPORT bool operator==(const RenderCommand &a, const RenderCommand &b) noexcept;
+
+inline bool operator!=(const RenderCommand &lhs, const RenderCommand &rhs) noexcept
+{
+ return !operator==(lhs, rhs);
+}
+
+struct EntityRenderCommandData
+{
+ QVector<Entity *> entities;
+ QVector<RenderCommand> commands;
+ QVector<RenderPassParameterData> passesData;
+
+ void reserve(int size)
+ {
+ entities.reserve(size);
+ commands.reserve(size);
+ passesData.reserve(size);
+ }
+
+ inline int size() const { return entities.size(); }
+
+ inline void push_back(Entity *e, const RenderCommand &c, const RenderPassParameterData &p)
+ {
+ entities.push_back(e);
+ commands.push_back(c);
+ passesData.push_back(p);
+ }
+
+ inline void push_back(Entity *e, RenderCommand &&c, RenderPassParameterData &&p)
+ {
+ entities.push_back(e);
+ commands.push_back(std::move(c));
+ passesData.push_back(std::move(p));
+ }
+
+ EntityRenderCommandData &operator+=(EntityRenderCommandData &&t)
+ {
+ entities += std::move(t.entities);
+ commands += std::move(t.commands);
+ passesData += std::move(t.passesData);
+ return *this;
+ }
+};
+
+using EntityRenderCommandDataPtr = QSharedPointer<EntityRenderCommandData>;
+
+} // namespace Rhi
+
+} // namespace Render
+
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDER_RHI_RENDERCOMMAND_H
diff --git a/src/plugins/renderers/rhi/renderer/renderer.cpp b/src/plugins/renderers/rhi/renderer/renderer.cpp
new file mode 100644
index 000000000..ee9a26a98
--- /dev/null
+++ b/src/plugins/renderers/rhi/renderer/renderer.cpp
@@ -0,0 +1,2537 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
+** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 "renderer_p.h"
+
+#include <Qt3DCore/qentity.h>
+
+#include <Qt3DRender/qmaterial.h>
+#include <Qt3DRender/qmesh.h>
+#include <Qt3DRender/qrenderpass.h>
+#include <Qt3DRender/qshaderprogram.h>
+#include <Qt3DRender/qtechnique.h>
+#include <Qt3DRender/qrenderaspect.h>
+#include <Qt3DRender/qeffect.h>
+
+#include <Qt3DRender/private/qsceneimporter_p.h>
+#include <Qt3DRender/private/renderstates_p.h>
+#include <Qt3DRender/private/cameraselectornode_p.h>
+#include <Qt3DRender/private/framegraphvisitor_p.h>
+#include <Qt3DRender/private/cameralens_p.h>
+#include <Qt3DRender/private/entity_p.h>
+#include <Qt3DRender/private/renderlogging_p.h>
+#include <Qt3DRender/private/material_p.h>
+#include <Qt3DRender/private/renderpassfilternode_p.h>
+#include <Qt3DRender/private/shader_p.h>
+#include <Qt3DRender/private/buffer_p.h>
+#include <Qt3DRender/private/technique_p.h>
+#include <Qt3DRender/private/renderthread_p.h>
+#include <Qt3DRender/private/scenemanager_p.h>
+#include <Qt3DRender/private/techniquefilternode_p.h>
+#include <Qt3DRender/private/viewportnode_p.h>
+#include <Qt3DRender/private/vsyncframeadvanceservice_p.h>
+#include <Qt3DRender/private/managers_p.h>
+#include <Qt3DRender/private/buffermanager_p.h>
+#include <Qt3DRender/private/nodemanagers_p.h>
+#include <Qt3DRender/private/geometryrenderermanager_p.h>
+#include <Qt3DRender/private/techniquemanager_p.h>
+#include <Qt3DRender/private/platformsurfacefilter_p.h>
+#include <Qt3DRender/private/loadbufferjob_p.h>
+#include <Qt3DRender/private/rendercapture_p.h>
+#include <Qt3DRender/private/updatelevelofdetailjob_p.h>
+#include <Qt3DRender/private/buffercapture_p.h>
+#include <Qt3DRender/private/offscreensurfacehelper_p.h>
+#include <Qt3DRender/private/subtreeenabler_p.h>
+#include <Qt3DRender/private/qshaderprogrambuilder_p.h>
+#include <Qt3DRender/private/qshaderprogram_p.h>
+
+#include <Qt3DRender/qcameralens.h>
+#include <Qt3DCore/private/qabstractaspectjobmanager_p.h>
+#include <Qt3DCore/private/qaspectmanager_p.h>
+#include <Qt3DCore/private/qsysteminformationservice_p.h>
+#include <Qt3DCore/private/qsysteminformationservice_p_p.h>
+#include <Qt3DRender/private/resourceaccessor_p.h>
+#include <Qt3DRender/private/renderlogging_p.h>
+#include <Qt3DRender/private/renderstateset_p.h>
+#include <Qt3DRender/private/setfence_p.h>
+#include <Qt3DRender/private/stringtoint_p.h>
+#include <Qt3DRender/private/qrenderaspect_p.h>
+
+#include <rhibuffer_p.h>
+#include <rhigraphicspipeline_p.h>
+
+#include <rendercommand_p.h>
+#include <renderqueue_p.h>
+#include <renderview_p.h>
+#include <texture_p.h>
+#include <renderviewbuilder_p.h>
+#include <rhiresourcemanagers_p.h>
+#include <commandexecuter_p.h>
+#include <submissioncontext_p.h>
+
+#include <QStack>
+#include <QOffscreenSurface>
+#include <QSurface>
+#include <QElapsedTimer>
+#include <QLibraryInfo>
+#include <QMutexLocker>
+#include <QPluginLoader>
+#include <QDir>
+#include <QUrl>
+#include <QOffscreenSurface>
+#include <QWindow>
+#include <QThread>
+#include <QKeyEvent>
+#include <QMouseEvent>
+
+#include <QtGui/private/qopenglcontext_p.h>
+#include <QGuiApplication>
+
+QT_BEGIN_NAMESPACE
+
+// Crashes on AMD Radeon drivers on Windows. Disable for now.
+//#define SHADER_LOADING_IN_COMMAND_THREAD
+using namespace Qt3DCore;
+
+namespace Qt3DRender {
+namespace Render {
+namespace Rhi {
+
+namespace {
+
+class CachingLightGatherer : public LightGatherer
+{
+public:
+ CachingLightGatherer(RendererCache *cache) : LightGatherer(), m_cache(cache) { }
+
+ void run() override
+ {
+ LightGatherer::run();
+
+ QMutexLocker lock(m_cache->mutex());
+ m_cache->gatheredLights = lights();
+ m_cache->environmentLight = environmentLight();
+ }
+
+private:
+ RendererCache *m_cache;
+};
+
+class CachingRenderableEntityFilter : public RenderableEntityFilter
+{
+public:
+ CachingRenderableEntityFilter(RendererCache *cache) : RenderableEntityFilter(), m_cache(cache)
+ {
+ }
+
+ void run() override
+ {
+ RenderableEntityFilter::run();
+
+ QVector<Entity *> selectedEntities = filteredEntities();
+ std::sort(selectedEntities.begin(), selectedEntities.end());
+
+ QMutexLocker lock(m_cache->mutex());
+ m_cache->renderableEntities = selectedEntities;
+ }
+
+private:
+ RendererCache *m_cache;
+};
+
+class CachingComputableEntityFilter : public ComputableEntityFilter
+{
+public:
+ CachingComputableEntityFilter(RendererCache *cache) : ComputableEntityFilter(), m_cache(cache)
+ {
+ }
+
+ void run() override
+ {
+ ComputableEntityFilter::run();
+
+ QVector<Entity *> selectedEntities = filteredEntities();
+ std::sort(selectedEntities.begin(), selectedEntities.end());
+
+ QMutexLocker lock(m_cache->mutex());
+ m_cache->computeEntities = selectedEntities;
+ }
+
+private:
+ RendererCache *m_cache;
+};
+
+int locationForAttribute(Attribute *attr, RHIShader *shader) noexcept
+{
+ const QVector<ShaderAttribute> attribInfo = shader->attributes();
+ const auto it = std::find_if(
+ attribInfo.begin(), attribInfo.end(),
+ [attr](const ShaderAttribute &sAttr) { return attr->nameId() == sAttr.m_nameId; });
+ if (it != attribInfo.end())
+ return it->m_location;
+ return -1;
+}
+
+} // anonymous
+
+/*!
+ \internal
+
+ Renderer shutdown procedure:
+
+ Since the renderer relies on the surface and OpenGLContext to perform its cleanup,
+ it is shutdown when the surface is set to nullptr
+
+ When the surface is set to nullptr this will request the RenderThread to terminate
+ and will prevent createRenderBinJobs from returning a set of jobs as there is nothing
+ more to be rendered.
+
+ In turn, this will call shutdown which will make the OpenGL context current one last time
+ to allow cleanups requiring a call to QOpenGLContext::currentContext to execute properly.
+ At the end of that function, the GraphicsContext is set to null.
+
+ At this point though, the QAspectThread is still running its event loop and will only stop
+ a short while after.
+ */
+
+Renderer::Renderer(QRenderAspect::RenderType type)
+ : m_services(nullptr),
+ m_aspect(nullptr),
+ m_nodesManager(nullptr),
+ m_renderSceneRoot(nullptr),
+ m_defaultRenderStateSet(nullptr),
+ m_submissionContext(nullptr),
+ m_renderQueue(new RenderQueue()),
+ m_renderThread(type == QRenderAspect::Threaded ? new RenderThread(this) : nullptr),
+ m_vsyncFrameAdvanceService(new VSyncFrameAdvanceService(m_renderThread != nullptr)),
+ m_waitForInitializationToBeCompleted(0),
+ m_hasBeenInitializedMutex(),
+ m_exposed(0),
+ m_lastFrameCorrect(0),
+ m_glContext(nullptr),
+ m_time(0),
+ m_settings(nullptr),
+ m_updateShaderDataTransformJob(Render::UpdateShaderDataTransformJobPtr::create()),
+ m_cleanupJob(Render::FrameCleanupJobPtr::create()),
+ m_sendBufferCaptureJob(Render::SendBufferCaptureJobPtr::create()),
+ m_filterCompatibleTechniqueJob(FilterCompatibleTechniqueJobPtr::create()),
+ m_lightGathererJob(new CachingLightGatherer(&m_cache)),
+ m_renderableEntityFilterJob(new CachingRenderableEntityFilter(&m_cache)),
+ m_computableEntityFilterJob(new CachingComputableEntityFilter(&m_cache)),
+ m_bufferGathererJob(SynchronizerJobPtr::create([this] { lookForDirtyBuffers(); },
+ JobTypes::DirtyBufferGathering)),
+ m_textureGathererJob(SynchronizerJobPtr::create([this] { lookForDirtyTextures(); },
+ JobTypes::DirtyTextureGathering)),
+ m_introspectShaderJob(SynchronizerPostFramePtr::create(
+ [this] { reloadDirtyShaders(); },
+ [this](Qt3DCore::QAspectManager *m) { sendShaderChangesToFrontend(m); },
+ JobTypes::DirtyShaderGathering)),
+ m_ownedContext(false),
+ m_offscreenHelper(nullptr),
+ m_RHIResourceManagers(nullptr),
+ m_commandExecuter(new Qt3DRender::Debug::CommandExecuter(this)),
+ m_shouldSwapBuffers(true)
+{
+ std::fill_n(m_textureTransform, 4, 0.f);
+
+ // Set renderer as running - it will wait in the context of the
+ // RenderThread for RenderViews to be submitted
+ m_running.fetchAndStoreOrdered(1);
+ if (m_renderThread)
+ m_renderThread->waitForStart();
+
+ m_introspectShaderJob->addDependency(m_filterCompatibleTechniqueJob);
+
+ m_filterCompatibleTechniqueJob->setRenderer(this);
+
+ m_defaultRenderStateSet = new RenderStateSet;
+ m_defaultRenderStateSet->addState(StateVariant::createState<DepthTest>(GL_LESS));
+ m_defaultRenderStateSet->addState(StateVariant::createState<CullFace>(GL_BACK));
+ m_defaultRenderStateSet->addState(StateVariant::createState<ColorMask>(true, true, true, true));
+}
+
+Renderer::~Renderer()
+{
+ Q_ASSERT(m_running.fetchAndStoreOrdered(0) == 0);
+ if (m_renderThread)
+ Q_ASSERT(m_renderThread->isFinished());
+
+ delete m_renderQueue;
+ delete m_defaultRenderStateSet;
+ delete m_RHIResourceManagers;
+
+ if (!m_ownedContext)
+ QObject::disconnect(m_contextConnection);
+}
+
+void Renderer::dumpInfo() const
+{
+ qDebug() << Q_FUNC_INFO << "t =" << m_time;
+
+ const ShaderManager *shaderManager = m_nodesManager->shaderManager();
+ qDebug() << "=== Shader Manager ===";
+ qDebug() << *shaderManager;
+
+ const TextureManager *textureManager = m_nodesManager->textureManager();
+ qDebug() << "=== Texture Manager ===";
+ qDebug() << *textureManager;
+
+ const TextureImageManager *textureImageManager = m_nodesManager->textureImageManager();
+ qDebug() << "=== Texture Image Manager ===";
+ qDebug() << *textureImageManager;
+}
+
+API Renderer::api() const
+{
+ return API::OpenGL;
+}
+
+qint64 Renderer::time() const
+{
+ return m_time;
+}
+
+void Renderer::setTime(qint64 time)
+{
+ m_time = time;
+}
+
+void Renderer::setJobsInLastFrame(int jobsInLastFrame)
+{
+ m_jobsInLastFrame = jobsInLastFrame;
+}
+
+void Renderer::setAspect(QRenderAspect *aspect)
+{
+ m_aspect = aspect;
+ m_updateShaderDataTransformJob->addDependency(
+ QRenderAspectPrivate::get(aspect)->m_worldTransformJob);
+}
+
+void Renderer::setNodeManagers(NodeManagers *managers)
+{
+ m_nodesManager = managers;
+ m_RHIResourceManagers = new RHIResourceManagers();
+ m_scene2DResourceAccessor.reset(new ResourceAccessor(this, m_nodesManager));
+
+ m_updateShaderDataTransformJob->setManagers(m_nodesManager);
+ m_cleanupJob->setManagers(m_nodesManager);
+ m_filterCompatibleTechniqueJob->setManager(m_nodesManager->techniqueManager());
+ m_sendBufferCaptureJob->setManagers(m_nodesManager);
+ m_lightGathererJob->setManager(m_nodesManager->renderNodesManager());
+ m_renderableEntityFilterJob->setManager(m_nodesManager->renderNodesManager());
+ m_computableEntityFilterJob->setManager(m_nodesManager->renderNodesManager());
+}
+
+void Renderer::setServices(QServiceLocator *services)
+{
+ m_services = services;
+
+ m_nodesManager->sceneManager()->setDownloadService(m_services->downloadHelperService());
+}
+
+QRenderAspect *Renderer::aspect() const
+{
+ return m_aspect;
+}
+
+NodeManagers *Renderer::nodeManagers() const
+{
+ return m_nodesManager;
+}
+
+/*!
+ \internal
+
+ Return context which can be used to share resources safely
+ with qt3d main render context.
+*/
+QOpenGLContext *Renderer::shareContext() const
+{
+ return nullptr;
+}
+
+// Executed in the reloadDirtyShader job
+void Renderer::loadShader(Shader *shader, HShader shaderHandle)
+{
+ Q_UNUSED(shader);
+ if (!m_dirtyShaders.contains(shaderHandle))
+ m_dirtyShaders.push_back(shaderHandle);
+}
+
+void Renderer::setOpenGLContext(QOpenGLContext *context)
+{
+ m_glContext = context;
+}
+
+void Renderer::setScreen(QScreen *scr)
+{
+ m_screen = scr;
+}
+
+QScreen *Renderer::screen() const
+{
+ return m_screen;
+}
+
+bool Renderer::accessOpenGLTexture(Qt3DCore::QNodeId nodeId, QOpenGLTexture **texture,
+ QMutex **lock, bool readonly)
+{
+ RHI_UNIMPLEMENTED;
+
+ Texture *tex = m_nodesManager->textureManager()->lookupResource(nodeId);
+ if (!tex)
+ return false;
+
+ RHITexture *glTex = m_RHIResourceManagers->rhiTextureManager()->lookupResource(tex->peerId());
+ if (!glTex)
+ return false;
+
+ if (glTex->isDirty())
+ return false;
+
+ if (!readonly)
+ glTex->setExternalRenderingEnabled(true);
+
+ // RHITexture::TextureUpdateInfo texInfo =
+ // glTex->createOrUpdateRhiTexture(m_submissionContext.data()); *texture = texInfo.texture;
+
+ if (!readonly)
+ *lock = glTex->externalRenderingLock();
+
+ return true;
+}
+
+QSharedPointer<RenderBackendResourceAccessor> Renderer::resourceAccessor() const
+{
+ return m_scene2DResourceAccessor;
+}
+
+// Called in RenderThread context by the run method of RenderThread
+// RenderThread has locked the mutex already and unlocks it when this
+// method termintates
+void Renderer::initialize()
+{
+ QMutexLocker lock(&m_hasBeenInitializedMutex);
+ m_submissionContext.reset(new SubmissionContext);
+ m_submissionContext->setRenderer(this);
+
+ // RHI initialization
+ {
+ qCDebug(Backend) << Q_FUNC_INFO << "Requesting renderer initialize";
+ m_submissionContext->initialize();
+
+ // We need to adapt texture coordinates
+ // m_textureTransform is (a;b) in texCoord = a * texCoord + b
+ if (m_submissionContext->rhi()->isYUpInFramebuffer()) {
+ // OpenGL case - that is what we assume to be the default so we do not change
+ // anything
+ m_textureTransform[0] = 1.f;
+ m_textureTransform[1] = 1.f;
+ m_textureTransform[2] = 0.f;
+ m_textureTransform[3] = 0.f;
+ } else {
+ // Other cases : y = 1 - y
+ m_textureTransform[0] = 1.f;
+ m_textureTransform[1] = -1.f;
+ m_textureTransform[2] = 0.f;
+ m_textureTransform[3] = 1.f;
+ }
+
+ // Awake setScenegraphRoot in case it was waiting
+ m_waitForInitializationToBeCompleted.release(1);
+
+ // Allow the aspect manager to proceed
+ m_vsyncFrameAdvanceService->proceedToNextFrame();
+
+ // Force initial refresh
+ markDirty(AllDirty, nullptr);
+ return;
+ }
+}
+
+/*!
+ * \internal
+ *
+ * Signals for the renderer to stop rendering. If a threaded renderer is in use,
+ * the render thread will call releaseGraphicsResources() just before the thread exits.
+ * If rendering synchronously, this function will call releaseGraphicsResources().
+ */
+void Renderer::shutdown()
+{
+ // Ensure we have waited to be fully initialized before trying to shut down
+ // (in case initialization is taking place at the same time)
+ QMutexLocker lock(&m_hasBeenInitializedMutex);
+
+ qCDebug(Backend) << Q_FUNC_INFO << "Requesting renderer shutdown";
+ m_running.storeRelaxed(0);
+
+ // We delete any renderqueue that we may not have had time to render
+ // before the surface was destroyed
+ QMutexLocker lockRenderQueue(m_renderQueue->mutex());
+ qDeleteAll(m_renderQueue->nextFrameQueue());
+ m_renderQueue->reset();
+ lockRenderQueue.unlock();
+
+ if (!m_renderThread) {
+ releaseGraphicsResources();
+ } else {
+ // Wake up the render thread in case it is waiting for some renderviews
+ // to be ready. The isReadyToSubmit() function checks for a shutdown
+ // having been requested.
+ m_submitRenderViewsSemaphore.release(1);
+ m_renderThread->wait();
+ }
+
+ // Destroy internal managers
+ // This needs to be done before the nodeManager is destroy
+ // as the internal resources might somehow rely on nodeManager resources
+ delete m_RHIResourceManagers;
+ m_RHIResourceManagers = nullptr;
+}
+
+/*!
+ \internal
+
+ When using a threaded renderer this function is called in the context of the
+ RenderThread to do any shutdown and cleanup that needs to be performed in the
+ thread where the OpenGL context lives.
+
+ When using Scene3D or anything that provides a custom QOpenGLContext (not
+ owned by Qt3D) this function is called whenever the signal
+ QOpenGLContext::aboutToBeDestroyed is emitted. In that case this function
+ is called in the context of the emitter's thread.
+*/
+void Renderer::releaseGraphicsResources()
+{
+ // We may get called twice when running inside of a Scene3D. Once when Qt Quick
+ // wants to shutdown, and again when the render aspect gets unregistered. So
+ // check that we haven't already cleaned up before going any further.
+ if (!m_submissionContext)
+ return;
+
+ // Try to temporarily make the context current so we can free up any resources
+ QMutexLocker locker(&m_offscreenSurfaceMutex);
+ QOffscreenSurface *offscreenSurface = m_offscreenHelper->offscreenSurface();
+ if (!offscreenSurface) {
+ qWarning() << "Failed to make context current: OpenGL resources will not be destroyed";
+ // We still need to delete the submission context
+ m_submissionContext.reset(nullptr);
+ return;
+ }
+
+ //* QOpenGLContext *context = m_submissionContext->openGLContext();
+ //* Q_ASSERT(context);
+ //*
+ //* if (context->thread() == QThread::currentThread() && context->makeCurrent(offscreenSurface))
+ //{
+ //*
+ //* // Clean up the graphics context and any resources
+ //* const QVector<HRHITexture> activeTexturesHandles =
+ //m_RHIResourceManagers->rhiTextureManager()->activeHandles();
+ //* for (const HRHITexture &textureHandle : activeTexturesHandles) {
+ //* RHITexture *tex = m_RHIResourceManagers->rhiTextureManager()->data(textureHandle);
+ //* tex->destroy();
+ //* }
+ //*
+ //* // Do the same thing with buffers
+ //* const QVector<HRHIBuffer> activeBuffers =
+ //m_RHIResourceManagers->rhiBufferManager()->activeHandles();
+ //* for (const HRHIBuffer &bufferHandle : activeBuffers) {
+ //* RHIBuffer *buffer = m_RHIResourceManagers->rhiBufferManager()->data(bufferHandle);
+ //* buffer->destroy(m_submissionContext.data());
+ //* }
+ //*
+ //* // Do the same thing with shaders
+ //* const QVector<RHIShader *> shaders =
+ //m_RHIResourceManagers->rhiShaderManager()->takeActiveResources();
+ //* qDeleteAll(shaders);
+ //*
+ //*
+ //* context->doneCurrent();
+ //* } else {
+ //* qWarning() << "Failed to make context current: OpenGL resources will not be destroyed";
+ //* }
+ //*
+ //* if (m_ownedContext)
+ //* delete context;
+
+ m_submissionContext.reset(nullptr);
+
+ qCDebug(Backend) << Q_FUNC_INFO << "Renderer properly shutdown";
+}
+
+void Renderer::setSurfaceExposed(bool exposed)
+{
+ qCDebug(Backend) << "Window exposed: " << exposed;
+ m_exposed.fetchAndStoreOrdered(exposed);
+}
+
+Render::FrameGraphNode *Renderer::frameGraphRoot() const
+{
+ Q_ASSERT(m_settings);
+ if (m_nodesManager && m_nodesManager->frameGraphManager() && m_settings)
+ return m_nodesManager->frameGraphManager()->lookupNode(m_settings->activeFrameGraphID());
+ return nullptr;
+}
+
+// QAspectThread context
+// Order of execution :
+// 1) RenderThread is created -> release 1 of m_waitForInitializationToBeCompleted when started
+// 2) setSceneRoot waits to acquire initialization
+// 3) submitRenderView -> check for surface
+// -> make surface current + create proper glHelper if needed
+void Renderer::setSceneRoot(Entity *sgRoot)
+{
+ Q_ASSERT(sgRoot);
+
+ // If initialization hasn't been completed we must wait
+ m_waitForInitializationToBeCompleted.acquire();
+
+ m_renderSceneRoot = sgRoot;
+ if (!m_renderSceneRoot)
+ qCWarning(Backend) << "Failed to build render scene";
+ m_renderSceneRoot->dump();
+ qCDebug(Backend) << Q_FUNC_INFO << "DUMPING SCENE";
+
+ // Set the scene root on the jobs
+ m_cleanupJob->setRoot(m_renderSceneRoot);
+
+ // Set all flags to dirty
+ m_dirtyBits.marked |= AbstractRenderer::AllDirty;
+}
+
+void Renderer::setSettings(RenderSettings *settings)
+{
+ m_settings = settings;
+}
+
+RenderSettings *Renderer::settings() const
+{
+ return m_settings;
+}
+
+void Renderer::render()
+{
+ // Traversing the framegraph tree from root to lead node
+ // Allows us to define the rendering set up
+ // Camera, RenderTarget ...
+
+ // Utimately the renderer should be a framework
+ // For the processing of the list of renderviews
+
+ // Matrice update, bounding volumes computation ...
+ // Should be jobs
+
+ // namespace Qt3DCore has 2 distincts node trees
+ // One scene description
+ // One framegraph description
+
+ while (m_running.loadRelaxed() > 0) {
+ doRender();
+ // TO DO: Restore windows exposed detection
+ // Probably needs to happens some place else though
+ }
+}
+
+// Either called by render if Qt3D is in charge of the RenderThread
+// or by QRenderAspectPrivate::renderSynchronous (for Scene3D)
+void Renderer::doRender(bool swapBuffers)
+{
+ Renderer::ViewSubmissionResultData submissionData;
+ bool hasCleanedQueueAndProceeded = false;
+ bool preprocessingComplete = false;
+ bool beganDrawing = false;
+
+ // Blocking until RenderQueue is full
+ const bool canSubmit = isReadyToSubmit();
+ m_shouldSwapBuffers = swapBuffers;
+
+ // Lock the mutex to protect access to the renderQueue while we look for its state
+ QMutexLocker locker(m_renderQueue->mutex());
+ const bool queueIsComplete = m_renderQueue->isFrameQueueComplete();
+ const bool queueIsEmpty = m_renderQueue->targetRenderViewCount() == 0;
+
+ bool mustCleanResources = false;
+
+ // When using synchronous rendering (QtQuick)
+ // We are not sure that the frame queue is actually complete
+ // Since a call to render may not be synched with the completions
+ // of the RenderViewJobs
+ // In such a case we return early, waiting for a next call with
+ // the frame queue complete at this point
+
+ // RenderQueue is complete (but that means it may be of size 0)
+ if (canSubmit && (queueIsComplete && !queueIsEmpty)) {
+ const QVector<Render::Rhi::RenderView *> renderViews = m_renderQueue->nextFrameQueue();
+ QTaskLogger submissionStatsPart1(m_services->systemInformation(),
+ { JobTypes::FrameSubmissionPart1, 0 },
+ QTaskLogger::Submission);
+ QTaskLogger submissionStatsPart2(m_services->systemInformation(),
+ { JobTypes::FrameSubmissionPart2, 0 },
+ QTaskLogger::Submission);
+
+ QVector<RHIPassInfo> rhiPassesInfo;
+
+ if (canRender()) {
+ QSurface *surface = nullptr;
+ for (const RenderView *rv : renderViews) {
+ surface = rv->surface();
+ if (surface)
+ break;
+ }
+
+ // In case we did not draw because e.g. there wase no swapchain,
+ // we keep the resource updates from the previous frame.
+ if (!m_submissionContext->m_currentUpdates) {
+ m_submissionContext->m_currentUpdates =
+ m_submissionContext->rhi()->nextResourceUpdateBatch();
+ }
+
+ // 1) Execute commands for buffer uploads, texture updates, shader loading first
+ updateResources();
+
+ rhiPassesInfo = prepareCommandsSubmission(renderViews);
+ // 2) Update Pipelines and copy data into commands to allow concurrent submission
+ preprocessingComplete = true;
+
+ bool hasCommands = false;
+ for (const RenderView *rv : renderViews) {
+ const auto &commands = rv->commands();
+ hasCommands = std::any_of(commands.begin(), commands.end(),
+ [](const RenderCommand &cmd) { return cmd.isValid(); });
+ if (hasCommands)
+ break;
+ }
+
+ if (hasCommands) {
+ // Scoped to destroy surfaceLock
+ SurfaceLocker surfaceLock(surface);
+ const bool surfaceIsValid = (surface && surfaceLock.isSurfaceValid());
+ if (surfaceIsValid) {
+ beganDrawing = m_submissionContext->beginDrawing(surface);
+ if (beganDrawing) {
+ // Purge shader which aren't used any longer
+ static int callCount = 0;
+ ++callCount;
+ const int shaderPurgePeriod = 600;
+ if (callCount % shaderPurgePeriod == 0)
+ m_RHIResourceManagers->rhiShaderManager()->purge();
+ }
+ }
+ }
+ // 2) Proceed to next frame and start preparing frame n + 1
+ m_renderQueue->reset();
+ locker.unlock(); // Done protecting RenderQueue
+ m_vsyncFrameAdvanceService->proceedToNextFrame();
+ hasCleanedQueueAndProceeded = true;
+
+ // Only try to submit the RenderViews if the preprocessing was successful
+ // This part of the submission is happening in parallel to the RV building for the next
+ // frame
+ if (beganDrawing) {
+ submissionStatsPart1.end(submissionStatsPart2.restart());
+
+ // 3) Submit the render commands for frame n (making sure we never reference
+ // something that could be changing) Render using current device state and renderer
+ // configuration
+ submissionData = submitRenderViews(rhiPassesInfo);
+
+ // Perform any required cleanup of the Graphics resources (Buffers deleted, Shader
+ // deleted...)
+ mustCleanResources = true;
+ }
+ }
+
+ // Execute the pending shell commands
+ m_commandExecuter->performAsynchronousCommandExecution(renderViews);
+
+ // Delete all the RenderViews which will clear the allocators
+ // that were used for their allocation
+ qDeleteAll(renderViews);
+ }
+
+ // If hasCleanedQueueAndProceeded isn't true this implies that something went wrong
+ // with the rendering and/or the renderqueue is incomplete from some reason
+ // or alternatively it could be complete but empty (RenderQueue of size 0)
+
+ if (!hasCleanedQueueAndProceeded) {
+ // RenderQueue was full but something bad happened when
+ // trying to render it and therefore proceedToNextFrame was not called
+ // Note: in this case the renderQueue mutex is still locked
+
+ // Reset the m_renderQueue so that we won't try to render
+ // with a queue used by a previous frame with corrupted content
+ // if the current queue was correctly submitted
+ m_renderQueue->reset();
+
+ // We allow the RenderTickClock service to proceed to the next frame
+ // In turn this will allow the aspect manager to request a new set of jobs
+ // to be performed for each aspect
+ m_vsyncFrameAdvanceService->proceedToNextFrame();
+ }
+
+ // Perform the last swapBuffers calls after the proceedToNextFrame
+ // as this allows us to gain a bit of time for the preparation of the
+ // next frame
+ // Finish up with last surface used in the list of RenderViews
+ if (beganDrawing) {
+ SurfaceLocker surfaceLock(submissionData.surface);
+ // Finish up with last surface used in the list of RenderViews
+ const bool swapBuffers = submissionData.lastBoundFBOId == m_submissionContext->defaultFBO()
+ && surfaceLock.isSurfaceValid() && m_shouldSwapBuffers;
+ m_submissionContext->endDrawing(swapBuffers);
+
+ if (mustCleanResources)
+ cleanGraphicsResources();
+ }
+}
+
+// Called by RenderViewJobs
+// When the frameQueue is complete and we are using a renderThread
+// we allow the render thread to proceed
+void Renderer::enqueueRenderView(RenderView *renderView, int submitOrder)
+{
+ QMutexLocker locker(m_renderQueue->mutex()); // Prevent out of order execution
+ // We cannot use a lock free primitive here because:
+ // - QVector is not thread safe
+ // - Even if the insert is made correctly, the isFrameComplete call
+ // could be invalid since depending on the order of execution
+ // the counter could be complete but the renderview not yet added to the
+ // buffer depending on whichever order the cpu decides to process this
+ const bool isQueueComplete = m_renderQueue->queueRenderView(renderView, submitOrder);
+ locker.unlock(); // We're done protecting the queue at this point
+ if (isQueueComplete) {
+ if (m_renderThread && m_running.loadRelaxed())
+ Q_ASSERT(m_submitRenderViewsSemaphore.available() == 0);
+ m_submitRenderViewsSemaphore.release(1);
+ }
+}
+
+bool Renderer::canRender() const
+
+{
+ // Make sure that we've not been told to terminate
+ if (m_renderThread && !m_running.loadRelaxed()) {
+ qCDebug(Rendering) << "RenderThread termination requested whilst waiting";
+ return false;
+ }
+
+ // TO DO: Check if all surfaces have been destroyed...
+ // It may be better if the last window to be closed trigger a call to shutdown
+ // Rather than having checks for the surface everywhere
+
+ return true;
+}
+
+bool Renderer::isReadyToSubmit()
+{
+ // Make sure that we've been told to render before rendering
+ // Prevent ouf of order execution
+ m_submitRenderViewsSemaphore.acquire(1);
+
+ // Check if shutdown has been requested
+ if (m_running.loadRelaxed() == 0)
+ return false;
+
+ // The semaphore should only
+ // be released when the frame queue is complete and there's
+ // something to render
+ // The case of shutdown should have been handled just before
+ Q_ASSERT(m_renderQueue->isFrameQueueComplete());
+ return true;
+}
+
+// Main thread
+QVariant Renderer::executeCommand(const QStringList &args)
+{
+ return m_commandExecuter->executeCommand(args);
+}
+
+/*!
+ \internal
+ Called in the context of the aspect thread from QRenderAspect::onRegistered
+*/
+void Renderer::setOffscreenSurfaceHelper(OffscreenSurfaceHelper *helper)
+{
+ QMutexLocker locker(&m_offscreenSurfaceMutex);
+ m_offscreenHelper = helper;
+}
+
+QSurfaceFormat Renderer::format()
+{
+ return m_submissionContext->format();
+}
+
+void Renderer::updateGraphicsPipeline(RenderCommand &cmd, RenderView *rv, int renderViewIndex)
+{
+ if (!cmd.m_rhiShader) {
+ qDebug() << "Warning: command has no shader";
+ return;
+ }
+
+ // The Graphics Pipeline defines
+ // - Render State (Depth, Culling, Stencil, Blending)
+ // - Shader Resources Binding
+ // - Shader Vertex Attribute Layout
+
+ // This means we need to have one GraphicsPipeline per
+ // - geometry
+ // - material
+ // - state (RV + RC)
+
+ RenderStateSet *renderState = nullptr;
+ {
+ RenderStateSet *globalState =
+ (rv->stateSet() != nullptr) ? rv->stateSet() : m_defaultRenderStateSet;
+
+ // Merge global state into local state
+ RenderStateSet *localState = cmd.m_stateSet.data();
+ if (localState != nullptr) {
+ localState->merge(globalState);
+ renderState = localState;
+ } else {
+ renderState = globalState;
+ }
+ }
+
+ // Try to retrieve existing pipeline
+ auto &pipelineManager = *m_RHIResourceManagers->rhiGraphicsPipelineManager();
+ const GraphicsPipelineIdentifier pipelineKey { cmd.m_geometry, cmd.m_shaderId,
+ renderViewIndex };
+ RHIGraphicsPipeline *graphicsPipeline = pipelineManager.getOrCreateResource(pipelineKey);
+ // TO DO: Ensure we find a way to know when the state is dirty to trigger a rebuild
+
+ if (!graphicsPipeline) {
+ qDebug() << "Warning : could not create a graphics pipeline";
+ return;
+ }
+
+ // Increase score so that we know the pipeline was used for this frame and shouldn't be
+ // destroyed
+ graphicsPipeline->increaseScore();
+
+ // TO DO: Set to true if geometry, shader or render state dirty
+ bool requiredRebuild = false;
+
+ // Note: we can rebuild add/remove things from the QRhiShaderResourceBindings after having
+ // created the pipeline and rebuild it. Changes should be picked up automatically
+
+ // Create pipeline if it doesn't exist or needs to be updated
+ if (graphicsPipeline->pipeline() == nullptr || requiredRebuild) {
+ bool ok = true;
+
+ const SubmissionContext::SwapChainInfo *swapchain =
+ m_submissionContext->swapChainForSurface(rv->surface());
+ if (!swapchain || !swapchain->swapChain || !swapchain->renderPassDescriptor)
+ return;
+
+ // TO DO: Find a way to recycle those
+ // Create Per Command UBO
+ QRhiBuffer *commandUBO = m_submissionContext->rhi()->newBuffer(
+ QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, sizeof(CommandUBO));
+ QRhiBuffer *rvUBO = m_submissionContext->rhi()->newBuffer(
+ QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, sizeof(RenderViewUBO));
+ commandUBO->build();
+ rvUBO->build();
+ graphicsPipeline->setCommandUBO(commandUBO);
+ graphicsPipeline->setRenderViewUBO(rvUBO);
+
+ QVector<QRhiShaderResourceBinding> uboBindings;
+ uboBindings << QRhiShaderResourceBinding::uniformBuffer(
+ 0,
+ QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage,
+ rvUBO)
+ << QRhiShaderResourceBinding::uniformBuffer(
+ 1,
+ QRhiShaderResourceBinding::VertexStage
+ | QRhiShaderResourceBinding::FragmentStage,
+ commandUBO);
+
+ // Create additional empty UBO Buffer for UBO with binding point > 1 (since we assume 0 and
+ // 1 and for Qt3D standard values)
+ const QVector<ShaderUniformBlock> uniformBlocks = cmd.m_rhiShader->uniformBlocks();
+ QHash<int, RHIGraphicsPipeline::UBOBuffer> uboBuffers;
+ for (const ShaderUniformBlock &block : uniformBlocks) {
+ if (block.m_binding > 1) {
+ auto handle = m_RHIResourceManagers->rhiBufferManager()->allocateResource();
+ RHIBuffer *ubo = m_RHIResourceManagers->rhiBufferManager()->data(handle);
+ Q_ASSERT(ubo);
+ const QByteArray rawData(block.m_size, '\0');
+ ubo->allocate(m_submissionContext.data(), rawData, true);
+ ok = ubo->bind(m_submissionContext.data(), RHIBuffer::UniformBuffer);
+ uboBuffers[block.m_binding] = { handle, ubo };
+ uboBindings << QRhiShaderResourceBinding::uniformBuffer(
+ block.m_binding,
+ QRhiShaderResourceBinding::VertexStage
+ | QRhiShaderResourceBinding::FragmentStage,
+ ubo->rhiBuffer());
+ }
+ }
+ graphicsPipeline->setUBOs(uboBuffers);
+
+ // Samplers
+ for (const auto &textureParameter : cmd.m_parameterPack.textures()) {
+ const auto handle = m_RHIResourceManagers->rhiTextureManager()->getOrAcquireHandle(
+ textureParameter.nodeId);
+ const auto textureData = m_RHIResourceManagers->rhiTextureManager()->data(handle);
+
+ for (const ShaderAttribute &samplerAttribute : cmd.m_rhiShader->samplers()) {
+ if (samplerAttribute.m_nameId == textureParameter.glslNameId) {
+ const auto rhiTexture = textureData->getRhiTexture();
+ const auto rhiSampler = textureData->getRhiSampler();
+ if (rhiTexture && rhiSampler) {
+ uboBindings.push_back(QRhiShaderResourceBinding::sampledTexture(
+ samplerAttribute.m_location,
+ QRhiShaderResourceBinding::FragmentStage, rhiTexture, rhiSampler));
+ }
+ }
+ }
+ }
+
+ QRhiShaderResourceBindings *shaderResourceBindings =
+ m_submissionContext->rhi()->newShaderResourceBindings();
+ assert(shaderResourceBindings);
+
+ shaderResourceBindings->setBindings(uboBindings.cbegin(), uboBindings.cend());
+ ok = shaderResourceBindings->build();
+ assert(ok);
+
+ // Create pipeline
+ QRhiGraphicsPipeline *pipeline = m_submissionContext->rhi()->newGraphicsPipeline();
+ graphicsPipeline->setShaderResourceBindings(shaderResourceBindings);
+ graphicsPipeline->setPipeline(pipeline);
+ assert(pipeline);
+
+ const QShader vertexShader = cmd.m_rhiShader->shaderStage(QShader::VertexStage);
+ const QShader fragmentShader = cmd.m_rhiShader->shaderStage(QShader::FragmentStage);
+
+ assert(vertexShader.isValid());
+ assert(fragmentShader.isValid());
+
+ pipeline->setShaderStages({ { QRhiShaderStage::Vertex, vertexShader },
+ { QRhiShaderStage::Fragment, fragmentShader } });
+
+ QVarLengthArray<QRhiVertexInputBinding, 8> inputBindings;
+ QVarLengthArray<QRhiVertexInputAttribute, 8> rhiAttributes;
+
+ const auto geom = cmd.m_geometry;
+ const auto &attributes = geom->attributes();
+
+ struct BufferBinding
+ {
+ Qt3DCore::QNodeId bufferId;
+ uint stride;
+ QRhiVertexInputBinding::Classification classification;
+ uint attributeDivisor;
+ };
+ QVector<BufferBinding> uniqueBindings;
+
+ auto rhiAttributeType = [](Attribute *attr) {
+ switch (attr->vertexBaseType()) {
+ case QAttribute::Byte:
+ case QAttribute::UnsignedByte: {
+ if (attr->vertexSize() == 1)
+ return QRhiVertexInputAttribute::UNormByte;
+ if (attr->vertexSize() == 2)
+ return QRhiVertexInputAttribute::UNormByte2;
+ if (attr->vertexSize() == 4)
+ return QRhiVertexInputAttribute::UNormByte4;
+ Q_FALLTHROUGH();
+ }
+ case QAttribute::Float: {
+ if (attr->vertexSize() == 1)
+ return QRhiVertexInputAttribute::Float;
+ if (attr->vertexSize() == 2)
+ return QRhiVertexInputAttribute::Float2;
+ if (attr->vertexSize() == 3)
+ return QRhiVertexInputAttribute::Float3;
+ if (attr->vertexSize() == 4)
+ return QRhiVertexInputAttribute::Float4;
+ Q_FALLTHROUGH();
+ }
+ default:
+ qWarning() << "Attribute type not handles by RHI";
+ Q_UNREACHABLE();
+ }
+ };
+
+ // QRhiVertexInputBinding -> specifies the stride of an attribute, whether it's per vertex
+ // or per instance and the instance divisor QRhiVertexInputAttribute -> specifies the format
+ // of the attribute (offset, type), the shader location and the index of the binding
+ // QRhiCommandBuffer::VertexInput -> binds a buffer to a binding
+
+ QHash<int, int> attributeNameToBinding;
+
+ for (Qt3DCore::QNodeId attribute_id : attributes) {
+ Attribute *attrib = m_nodesManager->attributeManager()->lookupResource(attribute_id);
+ if (attrib->attributeType() == QAttribute::VertexAttribute) {
+ const bool isPerInstanceAttr = attrib->divisor() != 0;
+ const QRhiVertexInputBinding::Classification classification = isPerInstanceAttr
+ ? QRhiVertexInputBinding::PerInstance
+ : QRhiVertexInputBinding::PerVertex;
+ const BufferBinding binding { attrib->bufferId(), attrib->byteStride(),
+ classification,
+ isPerInstanceAttr ? attrib->divisor() : 1U };
+
+ const auto it =
+ std::find_if(uniqueBindings.begin(), uniqueBindings.end(),
+ [binding](const BufferBinding &a) {
+ return binding.bufferId == a.bufferId
+ && binding.stride == a.stride
+ && binding.classification == a.classification
+ && binding.attributeDivisor == a.attributeDivisor;
+ });
+
+ int bindingIndex = uniqueBindings.size();
+ if (it == uniqueBindings.end())
+ uniqueBindings.push_back(binding);
+ else
+ bindingIndex = std::distance(uniqueBindings.begin(), it);
+
+ rhiAttributes.push_back({ bindingIndex,
+ locationForAttribute(attrib, cmd.m_rhiShader),
+ rhiAttributeType(attrib), attrib->byteOffset() });
+
+ attributeNameToBinding.insert(attrib->nameId(), bindingIndex);
+ }
+ }
+
+ inputBindings.resize(uniqueBindings.size());
+ for (int i = 0, m = uniqueBindings.size(); i < m; ++i) {
+ const BufferBinding binding = uniqueBindings.at(i);
+ /*
+ qDebug() << binding.bufferId
+ << binding.stride
+ << binding.classification
+ << binding.attributeDivisor;
+ */
+ inputBindings[i] = QRhiVertexInputBinding { binding.stride, binding.classification,
+ int(binding.attributeDivisor) };
+ }
+
+ QRhiVertexInputLayout inputLayout;
+ inputLayout.setBindings(inputBindings.begin(), inputBindings.end());
+ inputLayout.setAttributes(rhiAttributes.begin(), rhiAttributes.end());
+
+ pipeline->setVertexInputLayout(inputLayout);
+ pipeline->setShaderResourceBindings(shaderResourceBindings);
+
+ pipeline->setRenderPassDescriptor(swapchain->renderPassDescriptor);
+
+ graphicsPipeline->setAttributesToBindingHash(attributeNameToBinding);
+
+ // Render States
+ m_submissionContext->applyStateSet(renderState, pipeline);
+
+ ok = pipeline->build();
+ assert(ok);
+ }
+
+ // Record RHIGraphicsPipeline into command for later use
+ if (graphicsPipeline && graphicsPipeline->pipeline())
+ cmd.pipeline = graphicsPipeline;
+}
+
+// When this function is called, we must not be processing the commands for frame n+1
+QVector<Renderer::RHIPassInfo>
+Renderer::prepareCommandsSubmission(const QVector<RenderView *> &renderViews)
+{
+ // TO DO: Find a central place to initialize RHI resources
+ const int renderViewCount = renderViews.size();
+
+ // We need to have a single RHI RenderPass per RenderTarget
+ // as creating the pass clears the buffers
+ // 1) We need to find all adjacents RenderViews that have the same renderTarget
+ // and submit all of these as part of the same RHI pass
+ QVector<RHIPassInfo> rhiPassesInfo;
+
+ for (int i = 0; i < renderViewCount;) {
+ QVector<RenderView *> sameRenderTargetRVs;
+ QVector<QRhiBuffer *> rvUbos;
+ RenderView *refRV = renderViews.at(i);
+ sameRenderTargetRVs.push_back(refRV);
+
+ for (i = i + 1; i < renderViewCount; ++i) {
+ RenderView *curRV = renderViews.at(i);
+ if (refRV->renderTargetId() == curRV->renderTargetId()) {
+ sameRenderTargetRVs.push_back(curRV);
+ } else
+ break;
+ }
+
+ RHIPassInfo bucket;
+ bucket.rvs = std::move(sameRenderTargetRVs);
+ bucket.surface = refRV->surface();
+ bucket.renderTargetId = refRV->renderTargetId();
+ bucket.attachmentPack = refRV->attachmentPack();
+ rhiPassesInfo.push_back(bucket);
+ }
+
+ for (int i = 0; i < renderViewCount; ++i) {
+ RenderView *rv = renderViews.at(i);
+ QVector<RenderCommand> &commands = rv->commands();
+ for (RenderCommand &command : commands) {
+ // Update/Create GraphicsPipelines
+ if (command.m_type == RenderCommand::Draw) {
+ Geometry *rGeometry =
+ m_nodesManager->data<Geometry, GeometryManager>(command.m_geometry);
+ GeometryRenderer *rGeometryRenderer =
+ m_nodesManager->data<GeometryRenderer, GeometryRendererManager>(
+ command.m_geometryRenderer);
+ // By this time shaders should have been loaded
+ RHIShader *shader = m_RHIResourceManagers->rhiShaderManager()->lookupResource(
+ command.m_shaderId);
+ if (!shader) {
+ qDebug() << "Warning: could not find shader";
+ continue;
+ }
+
+ // We should never have inserted a command for which these are null
+ // in the first place
+ Q_ASSERT(rGeometry && rGeometryRenderer && shader);
+
+ // Unset dirtiness on rGeometryRenderer only
+ // The rGeometry may be shared by several rGeometryRenderer
+ // so we cannot unset its dirtiness at this point
+ if (rGeometryRenderer->isDirty())
+ rGeometryRenderer->unsetDirty();
+
+ // Prepare the ShaderParameterPack based on the active uniforms of the shader
+ // shader->prepareUniforms(command.m_parameterPack);
+
+ updateGraphicsPipeline(command, rv, i);
+
+ } else if (command.m_type == RenderCommand::Compute) {
+ RHI_UNIMPLEMENTED;
+ // By this time shaders have been loaded
+ RHIShader *shader = m_RHIResourceManagers->rhiShaderManager()->lookupResource(
+ command.m_shaderId);
+ command.m_rhiShader = shader;
+ Q_ASSERT(shader);
+
+ // Prepare the ShaderParameterPack based on the active uniforms of the shader
+ // shader->prepareUniforms(command.m_parameterPack);
+ }
+ }
+ }
+
+ // Unset dirtiness on Geometry and Attributes
+ // Note: we cannot do it in the loop above as we want to be sure that all
+ // the VAO which reference the geometry/attributes are properly updated
+ RHI_UNIMPLEMENTED;
+ for (Attribute *attribute : qAsConst(m_dirtyAttributes))
+ attribute->unsetDirty();
+ m_dirtyAttributes.clear();
+
+ for (Geometry *geometry : qAsConst(m_dirtyGeometry))
+ geometry->unsetDirty();
+ m_dirtyGeometry.clear();
+
+ return rhiPassesInfo;
+}
+
+// Executed in a job
+void Renderer::lookForDirtyBuffers()
+{
+ const QVector<HBuffer> activeBufferHandles = m_nodesManager->bufferManager()->activeHandles();
+ for (const HBuffer &handle : activeBufferHandles) {
+ Buffer *buffer = m_nodesManager->bufferManager()->data(handle);
+ if (buffer->isDirty())
+ m_dirtyBuffers.push_back(handle);
+ }
+}
+
+// Called in prepareSubmission
+void Renderer::lookForDownloadableBuffers()
+{
+ m_downloadableBuffers.clear();
+ const QVector<HBuffer> activeBufferHandles = m_nodesManager->bufferManager()->activeHandles();
+ for (const HBuffer &handle : activeBufferHandles) {
+ Buffer *buffer = m_nodesManager->bufferManager()->data(handle);
+ if (buffer->access() & QBuffer::Read)
+ m_downloadableBuffers.push_back(buffer->peerId());
+ }
+}
+
+// Executed in a job
+void Renderer::lookForDirtyTextures()
+{
+ // To avoid having Texture or TextureImage maintain relationships between
+ // one another, we instead perform a lookup here to check if a texture
+ // image has been updated to then notify textures referencing the image
+ // that they need to be updated
+ TextureImageManager *imageManager = m_nodesManager->textureImageManager();
+ const QVector<HTextureImage> activeTextureImageHandles = imageManager->activeHandles();
+ Qt3DCore::QNodeIdVector dirtyImageIds;
+ for (const HTextureImage &handle : activeTextureImageHandles) {
+ TextureImage *image = imageManager->data(handle);
+ if (image->isDirty()) {
+ dirtyImageIds.push_back(image->peerId());
+ image->unsetDirty();
+ }
+ }
+
+ TextureManager *textureManager = m_nodesManager->textureManager();
+ const QVector<HTexture> activeTextureHandles = textureManager->activeHandles();
+ for (const HTexture &handle : activeTextureHandles) {
+ Texture *texture = textureManager->data(handle);
+ const QNodeIdVector imageIds = texture->textureImageIds();
+
+ // Does the texture reference any of the dirty texture images?
+ for (const QNodeId imageId : imageIds) {
+ if (dirtyImageIds.contains(imageId)) {
+ texture->addDirtyFlag(Texture::DirtyImageGenerators);
+ break;
+ }
+ }
+
+ // Dirty meaning that something has changed on the texture
+ // either properties, parameters, shared texture id, generator or a texture image
+ if (texture->dirtyFlags() != Texture::NotDirty)
+ m_dirtyTextures.push_back(handle);
+ // Note: texture dirty flags are reset when actually updating the
+ // textures in updateGLResources() as resetting flags here would make
+ // us lose information about what was dirty exactly.
+ }
+}
+
+// Executed in a job
+void Renderer::reloadDirtyShaders()
+{
+ Q_ASSERT(isRunning());
+ const QVector<HTechnique> activeTechniques =
+ m_nodesManager->techniqueManager()->activeHandles();
+ const QVector<HShaderBuilder> activeBuilders =
+ m_nodesManager->shaderBuilderManager()->activeHandles();
+ for (const HTechnique &techniqueHandle : activeTechniques) {
+ Technique *technique = m_nodesManager->techniqueManager()->data(techniqueHandle);
+ // If api of the renderer matches the one from the technique
+ if (technique->isCompatibleWithRenderer()) {
+ const auto passIds = technique->renderPasses();
+ for (const QNodeId &passId : passIds) {
+ RenderPass *renderPass =
+ m_nodesManager->renderPassManager()->lookupResource(passId);
+ HShader shaderHandle =
+ m_nodesManager->shaderManager()->lookupHandle(renderPass->shaderProgram());
+ Shader *shader = m_nodesManager->shaderManager()->data(shaderHandle);
+
+ ShaderBuilder *shaderBuilder = nullptr;
+ for (const HShaderBuilder &builderHandle : activeBuilders) {
+ ShaderBuilder *builder =
+ m_nodesManager->shaderBuilderManager()->data(builderHandle);
+ if (builder->shaderProgramId() == shader->peerId()) {
+ shaderBuilder = builder;
+ break;
+ }
+ }
+
+ if (shaderBuilder) {
+ shaderBuilder->setGraphicsApi(*technique->graphicsApiFilter());
+
+ for (int i = 0; i <= QShaderProgram::Compute; i++) {
+ const auto shaderType = static_cast<QShaderProgram::ShaderType>(i);
+ if (!shaderBuilder->shaderGraph(shaderType).isValid())
+ continue;
+
+ if (shaderBuilder->isShaderCodeDirty(shaderType)) {
+ shaderBuilder->generateCode(shaderType);
+ m_shaderBuilderUpdates.append(shaderBuilder->takePendingUpdates());
+ }
+
+ const auto code = shaderBuilder->shaderCode(shaderType);
+ shader->setShaderCode(shaderType, code);
+ }
+ }
+
+ if (shader != nullptr && shader->isDirty())
+ loadShader(shader, shaderHandle);
+ }
+ }
+ }
+}
+
+// Executed in job (in main thread when jobs are done)
+void Renderer::sendShaderChangesToFrontend(Qt3DCore::QAspectManager *manager)
+{
+ Q_ASSERT(isRunning());
+
+ // Sync Shader
+ const QVector<HShader> activeShaders = m_nodesManager->shaderManager()->activeHandles();
+ for (const HShader &handle : activeShaders) {
+ Shader *s = m_nodesManager->shaderManager()->data(handle);
+ if (s->requiresFrontendSync()) {
+ QShaderProgram *frontend =
+ static_cast<decltype(frontend)>(manager->lookupNode(s->peerId()));
+ QShaderProgramPrivate *dFrontend =
+ static_cast<decltype(dFrontend)>(QNodePrivate::get(frontend));
+ s->unsetRequiresFrontendSync();
+ dFrontend->setStatus(s->status());
+ dFrontend->setLog(s->log());
+ }
+ }
+
+ // Sync ShaderBuilder
+ const QVector<ShaderBuilderUpdate> shaderBuilderUpdates = std::move(m_shaderBuilderUpdates);
+ for (const ShaderBuilderUpdate &update : shaderBuilderUpdates) {
+ QShaderProgramBuilder *builder =
+ static_cast<decltype(builder)>(manager->lookupNode(update.builderId));
+ QShaderProgramBuilderPrivate *dBuilder =
+ static_cast<decltype(dBuilder)>(QNodePrivate::get(builder));
+ dBuilder->setShaderCode(update.shaderCode, update.shaderType);
+ }
+}
+
+// Executed in a job (in main thread when jobs are done)
+void Renderer::sendTextureChangesToFrontend(Qt3DCore::QAspectManager *manager)
+{
+ const QVector<QPair<Texture::TextureUpdateInfo, Qt3DCore::QNodeIdVector>>
+ updateTextureProperties = std::move(m_updatedTextureProperties);
+ for (const auto &pair : updateTextureProperties) {
+ const Qt3DCore::QNodeIdVector targetIds = pair.second;
+ for (const Qt3DCore::QNodeId &targetId : targetIds) {
+ // Lookup texture
+ Texture *t = m_nodesManager->textureManager()->lookupResource(targetId);
+ // If backend texture is Dirty, some property has changed and the properties we are
+ // about to send are already outdate
+ if (t == nullptr || t->dirtyFlags() != Texture::NotDirty)
+ continue;
+
+ QAbstractTexture *texture =
+ static_cast<QAbstractTexture *>(manager->lookupNode(targetId));
+ if (!texture)
+ continue;
+ const TextureProperties &properties = pair.first.properties;
+
+ const bool blocked = texture->blockNotifications(true);
+ texture->setWidth(properties.width);
+ texture->setHeight(properties.height);
+ texture->setDepth(properties.depth);
+ texture->setLayers(properties.layers);
+ texture->setFormat(properties.format);
+ texture->blockNotifications(blocked);
+
+ QAbstractTexturePrivate *dTexture =
+ static_cast<QAbstractTexturePrivate *>(QNodePrivate::get(texture));
+
+ dTexture->setStatus(properties.status);
+ dTexture->setHandleType(pair.first.handleType);
+ dTexture->setHandle(pair.first.handle);
+ }
+ }
+}
+
+// Executed in a job (in main thread when jobs done)
+void Renderer::sendDisablesToFrontend(Qt3DCore::QAspectManager *manager)
+{
+ // SubtreeEnabled
+ const auto updatedDisables = std::move(m_updatedDisableSubtreeEnablers);
+ for (const auto &nodeId : updatedDisables) {
+ QSubtreeEnabler *frontend = static_cast<decltype(frontend)>(manager->lookupNode(nodeId));
+ frontend->setEnabled(false);
+ }
+
+ // Compute Commands
+ const QVector<HComputeCommand> activeCommands =
+ m_nodesManager->computeJobManager()->activeHandles();
+ for (const HComputeCommand &handle : activeCommands) {
+ ComputeCommand *c = m_nodesManager->computeJobManager()->data(handle);
+ if (c->hasReachedFrameCount()) {
+ QComputeCommand *frontend =
+ static_cast<decltype(frontend)>(manager->lookupNode(c->peerId()));
+ frontend->setEnabled(false);
+ c->resetHasReachedFrameCount();
+ }
+ }
+}
+
+// Render Thread (or QtQuick RenderThread when using Scene3D)
+// Scene3D: When using Scene3D rendering, we can't assume that when
+// updateGLResources is called, the resource handles points to still existing
+// objects. This is because Scene3D calls doRender independently of whether all
+// jobs have completed or not which in turn calls proceedToNextFrame under some
+// conditions. Such conditions are usually met on startup to avoid deadlocks.
+// proceedToNextFrame triggers the syncChanges calls for the next frame, which
+// may contain destruction changes targeting resources. When the above
+// happens, this can result in the dirtyResource vectors containing handles of
+// objects that may already have been destroyed
+void Renderer::updateResources()
+{
+ {
+ const QVector<HBuffer> dirtyBufferHandles = std::move(m_dirtyBuffers);
+ for (const HBuffer &handle : dirtyBufferHandles) {
+ Buffer *buffer = m_nodesManager->bufferManager()->data(handle);
+
+ // Can be null when using Scene3D rendering
+ if (buffer == nullptr)
+ continue;
+
+ // Forces creation if it doesn't exit
+ // Also note the binding point doesn't really matter here, we just upload data
+ if (!m_submissionContext->hasRHIBufferForBuffer(buffer))
+ m_submissionContext->rhiBufferForRenderBuffer(buffer);
+ // Update the RHIBuffer data
+ m_submissionContext->updateBuffer(buffer);
+ buffer->unsetDirty();
+ }
+ }
+
+#ifndef SHADER_LOADING_IN_COMMAND_THREAD
+ {
+ const QVector<HShader> dirtyShaderHandles = std::move(m_dirtyShaders);
+ ShaderManager *shaderManager = m_nodesManager->shaderManager();
+ for (const HShader &handle : dirtyShaderHandles) {
+ Shader *shader = shaderManager->data(handle);
+
+ // Can be null when using Scene3D rendering
+ if (shader == nullptr)
+ continue;
+
+ // Compile shader
+ m_submissionContext->loadShader(shader, shaderManager,
+ m_RHIResourceManagers->rhiShaderManager());
+ }
+ }
+#endif
+
+ {
+ const QVector<HTexture> activeTextureHandles = std::move(m_dirtyTextures);
+ for (const HTexture &handle : activeTextureHandles) {
+ Texture *texture = m_nodesManager->textureManager()->data(handle);
+
+ // Can be null when using Scene3D rendering
+ if (texture == nullptr)
+ continue;
+
+ // Create or Update RHITexture (the RHITexture instance is created if required
+ // and all things that can take place without a GL context are done here)
+ updateTexture(texture);
+ }
+ // We want to upload textures data at this point as the SubmissionThread and
+ // AspectThread are locked ensuring no races between Texture/TextureImage and
+ // RHITexture
+ if (m_submissionContext != nullptr) {
+ RHITextureManager *rhiTextureManager = m_RHIResourceManagers->rhiTextureManager();
+ const QVector<HRHITexture> glTextureHandles = rhiTextureManager->activeHandles();
+ // Upload texture data
+ for (const HRHITexture &glTextureHandle : glTextureHandles) {
+ RHI_UNIMPLEMENTED;
+ RHITexture *glTexture = rhiTextureManager->data(glTextureHandle);
+
+ // We create/update the actual GL texture using the GL context at this point
+ const RHITexture::TextureUpdateInfo info =
+ glTexture->createOrUpdateRhiTexture(m_submissionContext.data());
+
+ // RHITexture creation provides us width/height/format ... information
+ // for textures which had not initially specified these information
+ // (TargetAutomatic...) Gather these information and store them to be distributed by
+ // a change next frame
+ const QNodeIdVector referenceTextureIds = {
+ rhiTextureManager->texNodeIdForRHITexture.value(glTexture)
+ };
+ // Store properties and referenceTextureIds
+ if (info.wasUpdated) {
+ Texture::TextureUpdateInfo updateInfo;
+ updateInfo.properties = info.properties;
+ updateInfo.handleType = QAbstractTexture::OpenGLTextureId;
+ // updateInfo.handle = info.texture ?
+ // QVariant(info.texture->textureId()) : QVariant();
+ m_updatedTextureProperties.push_back({ updateInfo, referenceTextureIds });
+ }
+ }
+ }
+
+ // Record ids of texture to cleanup while we are still blocking the aspect thread
+ m_textureIdsToCleanup += m_nodesManager->textureManager()->takeTexturesIdsToCleanup();
+ }
+
+ // Record list of buffer that might need uploading
+ lookForDownloadableBuffers();
+}
+
+// Render Thread
+void Renderer::updateTexture(Texture *texture)
+{
+ RHI_UNIMPLEMENTED;
+ // Check that the current texture images are still in place, if not, do not update
+ const bool isValid = texture->isValid(m_nodesManager->textureImageManager());
+ if (!isValid) {
+ qWarning() << Q_FUNC_INFO << "QTexture referencing invalid QTextureImages";
+ return;
+ }
+
+ // All textures are unique, if you instanciate twice the exact same texture
+ // this will create 2 identical GLTextures, no sharing will take place
+
+ // Try to find the associated RHITexture for the backend Texture
+ RHITextureManager *rhiTextureManager = m_RHIResourceManagers->rhiTextureManager();
+ RHITexture *rhiTexture = rhiTextureManager->lookupResource(texture->peerId());
+
+ // No RHITexture associated yet -> create it
+ if (rhiTexture == nullptr) {
+ rhiTexture = rhiTextureManager->getOrCreateResource(texture->peerId());
+ rhiTextureManager->texNodeIdForRHITexture.insert(rhiTexture, texture->peerId());
+ }
+
+ // Update RHITexture to match Texture instance
+ const Texture::DirtyFlags dirtyFlags = texture->dirtyFlags();
+ if (dirtyFlags.testFlag(Texture::DirtySharedTextureId))
+ rhiTexture->setSharedTextureId(texture->sharedTextureId());
+
+ if (dirtyFlags.testFlag(Texture::DirtyProperties))
+ rhiTexture->setProperties(texture->properties());
+
+ if (dirtyFlags.testFlag(Texture::DirtyParameters))
+ rhiTexture->setParameters(texture->parameters());
+
+ // Will make the texture requestUpload
+ if (dirtyFlags.testFlag(Texture::DirtyImageGenerators)) {
+ const QNodeIdVector textureImageIds = texture->textureImageIds();
+ QVector<RHITexture::Image> images;
+ images.reserve(textureImageIds.size());
+ // TODO: Move this into RHITexture directly
+ for (const QNodeId textureImageId : textureImageIds) {
+ const TextureImage *img =
+ m_nodesManager->textureImageManager()->lookupResource(textureImageId);
+ if (img == nullptr) {
+ qWarning() << Q_FUNC_INFO << "invalid TextureImage handle";
+ } else {
+ RHITexture::Image glImg { img->dataGenerator(), img->layer(), img->mipLevel(),
+ img->face() };
+ images.push_back(glImg);
+ }
+ }
+ rhiTexture->setImages(images);
+ }
+
+ // Will make the texture requestUpload
+ if (dirtyFlags.testFlag(Texture::DirtyDataGenerator))
+ rhiTexture->setGenerator(texture->dataGenerator());
+
+ // Will make the texture requestUpload
+ if (dirtyFlags.testFlag(Texture::DirtyPendingDataUpdates))
+ rhiTexture->addTextureDataUpdates(texture->takePendingTextureDataUpdates());
+
+ // Unset the dirty flag on the texture
+ texture->unsetDirty();
+}
+
+// Render Thread
+void Renderer::cleanupTexture(Qt3DCore::QNodeId cleanedUpTextureId)
+{
+ RHITextureManager *rhiTextureManager = m_RHIResourceManagers->rhiTextureManager();
+ RHITexture *glTexture = rhiTextureManager->lookupResource(cleanedUpTextureId);
+
+ // Destroying the RHITexture implicitely also destroy the GL resources
+ if (glTexture != nullptr) {
+ rhiTextureManager->releaseResource(cleanedUpTextureId);
+ rhiTextureManager->texNodeIdForRHITexture.remove(glTexture);
+ }
+}
+
+// Render Thread
+void Renderer::cleanupShader(const Shader *shader)
+{
+ RHIShaderManager *rhiShaderManager = m_RHIResourceManagers->rhiShaderManager();
+ RHIShader *glShader = rhiShaderManager->lookupResource(shader->peerId());
+
+ if (glShader != nullptr)
+ rhiShaderManager->abandon(glShader, shader);
+}
+
+// Called by SubmitRenderView
+void Renderer::downloadGLBuffers()
+{
+ const QVector<Qt3DCore::QNodeId> downloadableHandles = std::move(m_downloadableBuffers);
+ for (const Qt3DCore::QNodeId &bufferId : downloadableHandles) {
+ BufferManager *bufferManager = m_nodesManager->bufferManager();
+ BufferManager::ReadLocker locker(const_cast<const BufferManager *>(bufferManager));
+ Buffer *buffer = bufferManager->lookupResource(bufferId);
+ // Buffer could have been destroyed at this point
+ if (!buffer)
+ continue;
+ // locker is protecting us from the buffer being destroy while we're looking
+ // up its content
+ const QByteArray content = m_submissionContext->downloadBufferContent(buffer);
+ m_sendBufferCaptureJob->addRequest(QPair<Qt3DCore::QNodeId, QByteArray>(bufferId, content));
+ }
+}
+
+// Happens in RenderThread context when all RenderViewJobs are done
+// Returns the id of the last bound FBO
+Renderer::ViewSubmissionResultData
+Renderer::submitRenderViews(const QVector<RHIPassInfo> &rhiPassesInfo)
+{
+ QElapsedTimer timer;
+ quint64 queueElapsed = 0;
+ timer.start();
+
+ quint64 frameElapsed = queueElapsed;
+ m_lastFrameCorrect.storeRelaxed(1); // everything fine until now.....
+
+ qCDebug(Memory) << Q_FUNC_INFO << "rendering frame ";
+
+ // We might not want to render on the default FBO
+ uint lastBoundFBOId = 0; // m_submissionContext->boundFrameBufferObject();
+ QSurface *surface = nullptr;
+ QSurface *previousSurface = nullptr;
+ QSurface *lastUsedSurface = nullptr;
+
+ const int rhiPassesCount = rhiPassesInfo.size();
+
+ for (int i = 0; i < rhiPassesCount; ++i) {
+ // Initialize GraphicsContext for drawing
+ const RHIPassInfo &rhiPassInfo = rhiPassesInfo.at(i);
+
+ // Initialize Previous surface the first time we enter this loop
+ if (i == 0) {
+ for (const RenderView *rv : rhiPassInfo.rvs) {
+ previousSurface = rv->surface();
+ if (previousSurface)
+ break;
+ }
+ }
+
+ // Check if using the same surface as the previous RHIPassInfo.
+ // If not, we have to free up the context from the previous surface
+ // and make the context current on the new surface
+ surface = rhiPassInfo.surface;
+ SurfaceLocker surfaceLock(surface);
+
+ // TO DO: Make sure that the surface we are rendering too has not been unset
+
+ // For now, if we do not have a surface, skip this rhipassinfo
+ // TODO: Investigate if it's worth providing a fallback offscreen surface
+ // to use when surface is null. Or if we should instead expose an
+ // offscreensurface to Qt3D.
+ if (!surface || !surfaceLock.isSurfaceValid()) {
+ m_lastFrameCorrect.storeRelaxed(0);
+ continue;
+ }
+
+ lastUsedSurface = surface;
+ const bool surfaceHasChanged = surface != previousSurface;
+
+ if (surfaceHasChanged && previousSurface) {
+ const bool swapBuffers = lastBoundFBOId == m_submissionContext->defaultFBO()
+ && surfaceLock.isSurfaceValid() && m_shouldSwapBuffers;
+ // We only call swap buffer if we are sure the previous surface is still valid
+ m_submissionContext->endDrawing(swapBuffers);
+ }
+
+ if (surfaceHasChanged) {
+ // If we can't make the context current on the surface, skip to the
+ // next RenderView. We won't get the full frame but we may get something
+ if (!m_submissionContext->beginDrawing(surface)) {
+ qWarning() << "Failed to make OpenGL context current on surface";
+ m_lastFrameCorrect.storeRelaxed(0);
+ continue;
+ }
+
+ previousSurface = surface;
+ // lastBoundFBOId = m_submissionContext->boundFrameBufferObject();
+ }
+
+ // Apply Memory Barrier if needed
+ // if (renderView->memoryBarrier() != QMemoryBarrier::None)
+ // qWarning() << "RHI Doesn't support MemoryBarrier";
+
+ // Set RenderTarget ...
+ // Activate RenderTarget
+ {
+ m_submissionContext->activateRenderTarget(rhiPassInfo.renderTargetId,
+ rhiPassInfo.attachmentPack, lastBoundFBOId);
+ }
+
+ // Execute the render commands
+ if (!executeCommandsSubmission(rhiPassInfo))
+ m_lastFrameCorrect.storeRelaxed(
+ 0); // something went wrong; make sure to render the next frame!
+
+ // executeCommandsSubmission takes care of restoring the stateset to the value
+ // of gc->currentContext() at the moment it was called (either
+ // renderViewStateSet or m_defaultRenderStateSet)
+ // if (!renderView->renderCaptureNodeId().isNull()) {
+ // RHI_UNIMPLEMENTED;
+ //* const QRenderCaptureRequest request = renderView->renderCaptureRequest();
+ //* const QSize size =
+ //m_submissionContext->renderTargetSize(renderView->surfaceSize());
+ //* QRect rect(QPoint(0, 0), size);
+ //* if (!request.rect.isEmpty())
+ //* rect = rect.intersected(request.rect);
+ //* QImage image;
+ //* if (!rect.isEmpty()) {
+ //* // Bind fbo as read framebuffer
+ //* m_submissionContext->bindFramebuffer(m_submissionContext->activeFBO(),
+ //GraphicsHelperInterface::FBORead);
+ //* image = m_submissionContext->readFramebuffer(rect);
+ //* } else {
+ //* qWarning() << "Requested capture rectangle is outside framebuffer";
+ //* }
+ //* Render::RenderCapture *renderCapture =
+ //*
+ //static_cast<Render::RenderCapture*>(m_nodesManager->frameGraphManager()->lookupNode(renderView->renderCaptureNodeId()));
+ //* renderCapture->addRenderCapture(request.captureId, image);
+ //* if
+ //(!m_pendingRenderCaptureSendRequests.contains(renderView->renderCaptureNodeId()))
+ //* m_pendingRenderCaptureSendRequests.push_back(renderView->renderCaptureNodeId());
+ // }
+
+ // if (renderView->isDownloadBuffersEnable())
+ // {
+ // RHI_UNIMPLEMENTED;
+ ////* downloadGLBuffers();
+ // }
+
+ // // Perform BlitFramebuffer operations
+ // if (renderView->hasBlitFramebufferInfo()) {
+ // RHI_UNIMPLEMENTED;
+ ////* const auto &blitFramebufferInfo = renderView->blitFrameBufferInfo();
+ ////* const QNodeId inputTargetId = blitFramebufferInfo.sourceRenderTargetId;
+ ////* const QNodeId outputTargetId =
+ ///blitFramebufferInfo.destinationRenderTargetId;
+ ////* const QRect inputRect = blitFramebufferInfo.sourceRect;
+ ////* const QRect outputRect = blitFramebufferInfo.destinationRect;
+ ////* const QRenderTargetOutput::AttachmentPoint inputAttachmentPoint =
+ ///blitFramebufferInfo.sourceAttachmentPoint;
+ ////* const QRenderTargetOutput::AttachmentPoint outputAttachmentPoint =
+ ///blitFramebufferInfo.destinationAttachmentPoint;
+ ////* const QBlitFramebuffer::InterpolationMethod interpolationMethod =
+ ///blitFramebufferInfo.interpolationMethod;
+ ////* m_submissionContext->blitFramebuffer(inputTargetId, outputTargetId,
+ ///inputRect, outputRect, lastBoundFBOId,
+ ////* inputAttachmentPoint,
+ ///outputAttachmentPoint,
+ ////* interpolationMethod);
+ // }
+
+ frameElapsed = timer.elapsed() - frameElapsed;
+ qCDebug(Rendering) << Q_FUNC_INFO << "Submitted RHI Passes " << i + 1 << "/"
+ << rhiPassesCount << "in " << frameElapsed << "ms";
+ frameElapsed = timer.elapsed();
+ }
+
+ // Bind lastBoundFBOId back. Needed also in threaded mode.
+ // lastBoundFBOId != m_graphicsContext->activeFBO() when the last FrameGraph leaf
+ // node/renderView contains RenderTargetSelector/RenderTarget
+ if (lastBoundFBOId != m_submissionContext->activeFBO()) {
+ RHI_UNIMPLEMENTED;
+ // m_submissionContext->bindFramebuffer(lastBoundFBOId,
+ // GraphicsHelperInterface::FBOReadAndDraw);
+ }
+
+ // Reset state and call doneCurrent if the surface
+ // is valid and was actually activated
+ if (lastUsedSurface) {
+ RHI_UNIMPLEMENTED;
+ // Reset state to the default state if the last stateset is not the
+ // defaultRenderStateSet
+ // if (m_submissionContext->currentStateSet() != m_defaultRenderStateSet)
+ // m_submissionContext->setCurrentStateSet(m_defaultRenderStateSet);
+ }
+
+ queueElapsed = timer.elapsed() - queueElapsed;
+ qCDebug(Rendering) << Q_FUNC_INFO << "Submission Completed in " << timer.elapsed() << "ms";
+
+ // Stores the necessary information to safely perform
+ // the last swap buffer call
+ ViewSubmissionResultData resultData;
+ resultData.lastBoundFBOId = lastBoundFBOId;
+ resultData.surface = lastUsedSurface;
+ return resultData;
+}
+
+void Renderer::markDirty(BackendNodeDirtySet changes, BackendNode *node)
+{
+ Q_UNUSED(node)
+ m_dirtyBits.marked |= changes;
+}
+
+Renderer::BackendNodeDirtySet Renderer::dirtyBits()
+{
+ return m_dirtyBits.marked;
+}
+
+#if defined(QT_BUILD_INTERNAL)
+void Renderer::clearDirtyBits(BackendNodeDirtySet changes)
+{
+ m_dirtyBits.remaining &= ~changes;
+ m_dirtyBits.marked &= ~changes;
+}
+#endif
+
+bool Renderer::shouldRender() const
+{
+ // Only render if something changed during the last frame, or the last frame
+ // was not rendered successfully (or render-on-demand is disabled)
+ return (m_settings->renderPolicy() == QRenderSettings::Always || m_dirtyBits.marked != 0
+ || m_dirtyBits.remaining != 0 || !m_lastFrameCorrect.loadRelaxed());
+}
+
+void Renderer::skipNextFrame()
+{
+ Q_ASSERT(m_settings->renderPolicy() != QRenderSettings::Always);
+
+ // make submitRenderViews() actually run
+ m_renderQueue->setNoRender();
+ m_submitRenderViewsSemaphore.release(1);
+}
+
+void Renderer::jobsDone(Qt3DCore::QAspectManager *manager)
+{
+ // called in main thread once all jobs are done running
+
+ // sync captured renders to frontend
+ const QVector<Qt3DCore::QNodeId> pendingCaptureIds =
+ std::move(m_pendingRenderCaptureSendRequests);
+ for (const Qt3DCore::QNodeId &id : qAsConst(pendingCaptureIds)) {
+ auto *backend = static_cast<Qt3DRender::Render::RenderCapture *>(
+ m_nodesManager->frameGraphManager()->lookupNode(id));
+ backend->syncRenderCapturesToFrontend(manager);
+ }
+
+ // Do we need to notify any texture about property changes?
+ if (m_updatedTextureProperties.size() > 0)
+ sendTextureChangesToFrontend(manager);
+
+ sendDisablesToFrontend(manager);
+}
+
+void Renderer::setPendingEvents(const QList<QPair<QObject *, QMouseEvent>> &mouseEvents,
+ const QList<QKeyEvent> &keyEvents)
+{
+ QMutexLocker l(&m_frameEventsMutex);
+ m_frameMouseEvents = mouseEvents;
+ m_frameKeyEvents = keyEvents;
+}
+
+// Jobs we may have to run even if no rendering will happen
+QVector<QAspectJobPtr> Renderer::preRenderingJobs()
+{
+ if (m_sendBufferCaptureJob->hasRequests())
+ return { m_sendBufferCaptureJob };
+ else
+ return {};
+}
+
+// Waits to be told to create jobs for the next frame
+// Called by QRenderAspect jobsToExecute context of QAspectThread
+// Returns all the jobs (and with proper dependency chain) required
+// for the rendering of the scene
+QVector<Qt3DCore::QAspectJobPtr> Renderer::renderBinJobs()
+{
+ QVector<QAspectJobPtr> renderBinJobs;
+
+ // Remove previous dependencies
+ m_cleanupJob->removeDependency(QWeakPointer<QAspectJob>());
+
+ const BackendNodeDirtySet dirtyBitsForFrame = m_dirtyBits.marked | m_dirtyBits.remaining;
+ m_dirtyBits.marked = {};
+ m_dirtyBits.remaining = {};
+ BackendNodeDirtySet notCleared = {};
+
+ // Add jobs
+ if (dirtyBitsForFrame & AbstractRenderer::TransformDirty) {
+ renderBinJobs.push_back(m_updateShaderDataTransformJob);
+ }
+
+ // TO DO: Conditionally add if skeletons dirty
+ renderBinJobs.push_back(m_cleanupJob);
+
+ // Jobs to prepare RHI Resource upload
+ if (dirtyBitsForFrame & AbstractRenderer::BuffersDirty)
+ renderBinJobs.push_back(m_bufferGathererJob);
+
+ if (dirtyBitsForFrame & AbstractRenderer::TexturesDirty)
+ renderBinJobs.push_back(m_textureGathererJob);
+
+ // Layer cache is dependent on layers, layer filters (hence FG structure
+ // changes) and the enabled flag on entities
+ const bool entitiesEnabledDirty = dirtyBitsForFrame & AbstractRenderer::EntityEnabledDirty;
+ const bool frameGraphDirty = dirtyBitsForFrame & AbstractRenderer::FrameGraphDirty;
+ const bool layersDirty = dirtyBitsForFrame & AbstractRenderer::LayersDirty;
+ const bool layersCacheNeedsToBeRebuilt = layersDirty || entitiesEnabledDirty || frameGraphDirty;
+ const bool shadersDirty = dirtyBitsForFrame & AbstractRenderer::ShadersDirty;
+ const bool materialDirty = dirtyBitsForFrame & AbstractRenderer::MaterialDirty;
+ const bool lightsDirty = dirtyBitsForFrame & AbstractRenderer::LightsDirty;
+ const bool computeableDirty = dirtyBitsForFrame & AbstractRenderer::ComputeDirty;
+ const bool renderableDirty = dirtyBitsForFrame & AbstractRenderer::GeometryDirty;
+ const bool materialCacheNeedsToBeRebuilt = shadersDirty || materialDirty || frameGraphDirty;
+ const bool renderCommandsDirty =
+ materialCacheNeedsToBeRebuilt || renderableDirty || computeableDirty;
+
+ // Rebuild Entity Layers list if layers are dirty
+
+ if (renderableDirty)
+ renderBinJobs.push_back(m_renderableEntityFilterJob);
+
+ if (computeableDirty)
+ renderBinJobs.push_back(m_computableEntityFilterJob);
+
+ if (lightsDirty)
+ renderBinJobs.push_back(m_lightGathererJob);
+
+ QMutexLocker lock(m_renderQueue->mutex());
+ if (m_renderQueue->wasReset()) { // Have we rendered yet? (Scene3D case)
+ // Traverse the current framegraph. For each leaf node create a
+ // RenderView and set its configuration then create a job to
+ // populate the RenderView with a set of RenderCommands that get
+ // their details from the RenderNodes that are visible to the
+ // Camera selected by the framegraph configuration
+ if (frameGraphDirty) {
+ FrameGraphVisitor visitor(m_nodesManager->frameGraphManager());
+ m_frameGraphLeaves = visitor.traverse(frameGraphRoot());
+ // Remove leaf nodes that no longer exist from cache
+ const QList<FrameGraphNode *> keys = m_cache.leafNodeCache.keys();
+ for (FrameGraphNode *leafNode : keys) {
+ if (!m_frameGraphLeaves.contains(leafNode))
+ m_cache.leafNodeCache.remove(leafNode);
+ }
+
+ // Handle single shot subtree enablers
+ const auto subtreeEnablers = visitor.takeEnablersToDisable();
+ for (auto *node : subtreeEnablers)
+ m_updatedDisableSubtreeEnablers.push_back(node->peerId());
+ }
+
+ const int fgBranchCount = m_frameGraphLeaves.size();
+ for (int i = 0; i < fgBranchCount; ++i) {
+ FrameGraphNode *leaf = m_frameGraphLeaves.at(i);
+ RenderViewBuilder builder(leaf, i, this);
+ // If we have a new RV (wasn't in the cache before, then it contains no cached data)
+ const bool isNewRV = !m_cache.leafNodeCache.contains(leaf);
+ builder.setLayerCacheNeedsToBeRebuilt(layersCacheNeedsToBeRebuilt || isNewRV);
+ builder.setMaterialGathererCacheNeedsToBeRebuilt(materialCacheNeedsToBeRebuilt
+ || isNewRV);
+ builder.setRenderCommandCacheNeedsToBeRebuilt(renderCommandsDirty || isNewRV);
+
+ builder.prepareJobs();
+ renderBinJobs.append(builder.buildJobHierachy());
+ }
+
+ // Set target number of RenderViews
+ m_renderQueue->setTargetRenderViewCount(fgBranchCount);
+ } else {
+ // FilterLayerEntityJob is part of the RenderViewBuilder jobs and must be run later
+ // if none of those jobs are started this frame
+ notCleared |= AbstractRenderer::EntityEnabledDirty;
+ notCleared |= AbstractRenderer::FrameGraphDirty;
+ notCleared |= AbstractRenderer::LayersDirty;
+ }
+
+ if (isRunning() && m_submissionContext->isInitialized()) {
+ if (dirtyBitsForFrame & AbstractRenderer::TechniquesDirty)
+ renderBinJobs.push_back(m_filterCompatibleTechniqueJob);
+ if (dirtyBitsForFrame & AbstractRenderer::ShadersDirty)
+ renderBinJobs.push_back(m_introspectShaderJob);
+ } else {
+ notCleared |= AbstractRenderer::TechniquesDirty;
+ notCleared |= AbstractRenderer::ShadersDirty;
+ }
+
+ m_dirtyBits.remaining = dirtyBitsForFrame & notCleared;
+
+ return renderBinJobs;
+}
+
+QAbstractFrameAdvanceService *Renderer::frameAdvanceService() const
+{
+ return static_cast<Qt3DCore::QAbstractFrameAdvanceService *>(m_vsyncFrameAdvanceService.data());
+}
+
+// Called by executeCommands
+void Renderer::performDraw(RenderCommand *command)
+{
+ QRhiCommandBuffer *cb = m_submissionContext->currentFrameCommandBuffer();
+ // Indirect Draw Calls
+ if (command->m_drawIndirect) {
+ RHI_UNIMPLEMENTED;
+ } else { // Direct Draw Calls
+
+ // TO DO: Add glMulti Draw variants
+ if (command->m_primitiveType == QGeometryRenderer::Patches) {
+ RHI_UNIMPLEMENTED;
+ //* m_submissionContext->setVerticesPerPatch(command->m_verticesPerPatch);
+ }
+
+ if (command->m_primitiveRestartEnabled) {
+ RHI_UNIMPLEMENTED;
+ //* m_submissionContext->enablePrimitiveRestart(command->m_restartIndexValue);
+ }
+
+ // TO DO: Add glMulti Draw variants
+ if (command->m_drawIndexed) {
+ cb->drawIndexed(command->m_primitiveCount, command->m_instanceCount,
+ command->m_indexOffset, command->m_indexAttributeByteOffset,
+ command->m_firstInstance);
+ } else {
+ cb->draw(command->m_primitiveCount, command->m_instanceCount, command->m_firstVertex,
+ command->m_firstInstance);
+ }
+ }
+
+#if defined(QT3D_RENDER_ASPECT_RHI_DEBUG)
+ int err = m_submissionContext->openGLContext()->functions()->glGetError();
+ if (err)
+ qCWarning(Rendering) << "GL error after drawing mesh:" << QString::number(err, 16);
+#endif
+
+ // if (command->m_primitiveRestartEnabled)
+ // m_submissionContext->disablePrimitiveRestart();
+}
+
+void Renderer::performCompute(const RenderView *, RenderCommand *command)
+{
+ RHI_UNIMPLEMENTED;
+ //* {
+ //* RHIShader *shader =
+ //m_RHIResourceManagers->rhiShaderManager()->lookupResource(command->m_shaderId);
+ //* m_submissionContext->activateShader(shader);
+ //* }
+ //* {
+ //* m_submissionContext->setParameters(command->m_parameterPack);
+ //* }
+ //* {
+ //* m_submissionContext->dispatchCompute(command->m_workGroups[0],
+ //* command->m_workGroups[1],
+ //* command->m_workGroups[2]);
+ //* }
+ //* // HACK: Reset the compute flag to dirty
+ //* m_dirtyBits.marked |= AbstractRenderer::ComputeDirty;
+
+ //* #if defined(QT3D_RENDER_ASPECT_RHI_DEBUG)
+ //* int err = m_submissionContext->openGLContext()->functions()->glGetError();
+ //* if (err)
+ //* qCWarning(Rendering) << "GL error after drawing mesh:" << QString::number(err, 16);
+ //* #endif
+}
+
+static auto rhiIndexFormat(QAttribute::VertexBaseType type)
+{
+ switch (type) {
+ case QAttribute::VertexBaseType ::UnsignedShort:
+ return QRhiCommandBuffer::IndexUInt16;
+ case QAttribute::VertexBaseType ::UnsignedInt:
+ return QRhiCommandBuffer::IndexUInt32;
+ default:
+ std::abort();
+ }
+}
+
+bool Renderer::uploadBuffersForCommand(QRhiCommandBuffer *cb, const RenderView *rv,
+ RenderCommand &command)
+{
+ RHIGraphicsPipeline *graphicsPipeline = command.pipeline;
+ if (!graphicsPipeline)
+ return true;
+
+ // Create the vertex input description
+
+ // Note: we have to bind the buffers here -> which will trigger the actual
+ // upload, as this is the only place where we know about the usage type of the buffers
+
+ const auto geom = command.m_geometry;
+ const auto &attributes = geom->attributes();
+ const QRhiVertexInputLayout layout = graphicsPipeline->pipeline()->vertexInputLayout();
+ const int bindingAttributeCount = std::distance(layout.cbeginBindings(), layout.cendBindings());
+ command.vertex_input.resize(bindingAttributeCount);
+
+ for (Qt3DCore::QNodeId attribute_id : attributes) {
+ // TODO isn't there a more efficient way than doing three hash lookups ?
+ Attribute *attrib = m_nodesManager->attributeManager()->lookupResource(attribute_id);
+ Buffer *buffer = m_nodesManager->bufferManager()->lookupResource(attrib->bufferId());
+ RHIBuffer *hbuf =
+ m_RHIResourceManagers->rhiBufferManager()->lookupResource(buffer->peerId());
+ switch (attrib->attributeType()) {
+ case QAttribute::VertexAttribute: {
+ hbuf->bind(&*m_submissionContext, RHIBuffer::Type::ArrayBuffer);
+ assert(hbuf->rhiBuffer());
+ // Find Binding for Attribute
+ const int bindingIndex = graphicsPipeline->bindingIndexForAttribute(attrib->nameId());
+ // We need to reference a binding, a buffer and an offset which is always = 0
+ // as Qt3D only assumes interleaved or continuous data but not
+ // buffer where first half of it is attribute1 and second half attribute2
+ command.vertex_input[bindingIndex] = { hbuf->rhiBuffer(), 0 };
+ break;
+ }
+ case QAttribute::IndexAttribute: {
+ hbuf->bind(&*m_submissionContext, RHIBuffer::Type::IndexBuffer);
+ assert(hbuf->rhiBuffer());
+ assert(command.indexBuffer == nullptr);
+
+ command.indexBuffer = hbuf->rhiBuffer();
+ command.indexAttribute = attrib;
+ break;
+ }
+ case QAttribute::DrawIndirectAttribute:
+ RHI_UNIMPLEMENTED;
+ break;
+ }
+ }
+
+ for (const BlockToUBO &pack : command.m_parameterPack.uniformBuffers()) {
+ Buffer *cpuBuffer = nodeManagers()->bufferManager()->lookupResource(pack.m_bufferID);
+ RHIBuffer *ubo = m_submissionContext->rhiBufferForRenderBuffer(cpuBuffer);
+ ubo->bind(&*m_submissionContext, RHIBuffer::UniformBuffer);
+ }
+
+ return true;
+}
+
+namespace {
+void printUpload(const UniformValue &value, const QShaderDescription::BlockVariable &member)
+{
+ switch (member.type) {
+ case QShaderDescription::VariableType::Int:
+ qDebug() << "Updating" << member.name << "with int data: " << *value.constData<int>()
+ << " (offset: " << member.offset << ", size: " << member.size << ")";
+ break;
+ case QShaderDescription::VariableType::Float:
+ qDebug() << "Updating" << member.name << "with float data: " << *value.constData<float>()
+ << " (offset: " << member.offset << ", size: " << member.size << ")";
+ break;
+ case QShaderDescription::VariableType::Vec2:
+ qDebug() << "Updating" << member.name << "with vec2 data: " << value.constData<float>()[0]
+ << ", " << value.constData<float>()[1] << " (offset: " << member.offset
+ << ", size: " << member.size << ")";
+ ;
+ break;
+ case QShaderDescription::VariableType::Vec3:
+ qDebug() << "Updating" << member.name << "with vec3 data: " << value.constData<float>()[0]
+ << ", " << value.constData<float>()[1] << ", " << value.constData<float>()[2]
+ << " (offset: " << member.offset << ", size: " << member.size << ")";
+ ;
+ break;
+ case QShaderDescription::VariableType::Vec4:
+ qDebug() << "Updating" << member.name << "with vec4 data: " << value.constData<float>()[0]
+ << ", " << value.constData<float>()[1] << ", " << value.constData<float>()[2]
+ << ", " << value.constData<float>()[3] << " (offset: " << member.offset
+ << ", size: " << member.size << ")";
+ ;
+ break;
+ default:
+ qDebug() << "Updating" << member.name << "with data: " << value.constData<char>();
+ break;
+ }
+}
+
+void uploadUniform(SubmissionContext &submissionContext, const PackUniformHash &uniforms,
+ const RHIShader::UBO_Member &uboMember,
+ const QHash<int, RHIGraphicsPipeline::UBOBuffer> &uboBuffers,
+ const QString &uniformName, const QShaderDescription::BlockVariable &member,
+ int arrayOffset = 0)
+{
+ const int uniformNameId = StringToInt::lookupId(uniformName);
+
+ if (!uniforms.contains(uniformNameId))
+ return;
+
+ const UniformValue value = uniforms.value(uniformNameId);
+ const ShaderUniformBlock block = uboMember.block;
+
+ // Update UBO with uniform value
+ Q_ASSERT(uboBuffers.contains(block.m_binding));
+ const RHIGraphicsPipeline::UBOBuffer &ubo = uboBuffers[block.m_binding];
+ RHIBuffer *buffer = ubo.buffer;
+
+ // TODO we should maybe have this thread_local to not reallocate memory every time
+ QByteArray rawData;
+ rawData.resize(member.size);
+ memcpy(rawData.data(), value.constData<char>(), std::min(value.byteSize(), member.size));
+ buffer->update(&submissionContext, rawData, member.offset + arrayOffset);
+
+ // printUpload(value, member);
+}
+}
+
+bool Renderer::uploadUBOsForCommand(QRhiCommandBuffer *cb, const RenderView *rv,
+ const RenderCommand &command)
+{
+ RHIGraphicsPipeline *pipeline = command.pipeline;
+ if (!pipeline)
+ return true;
+
+ // Upload UBO data for the Command
+ QRhiBuffer *commandUBO = pipeline->commandUBO();
+ m_submissionContext->m_currentUpdates->updateDynamicBuffer(commandUBO, 0, sizeof(CommandUBO),
+ &command.m_commandUBO);
+
+ // We have to update the RV UBO once per graphics pipeline
+ QRhiBuffer *rvUBO = pipeline->renderViewUBO();
+ m_submissionContext->m_currentUpdates->updateDynamicBuffer(rvUBO, 0, sizeof(RenderViewUBO),
+ rv->renderViewUBO());
+
+ // Upload UBO for custom parameters
+ {
+ RHIShader *shader =
+ m_RHIResourceManagers->rhiShaderManager()->lookupResource(command.m_shaderId);
+ if (!shader)
+ return true;
+
+ const QVector<RHIShader::UBO_Member> &uboMembers = shader->uboMembers();
+ const QHash<int, RHIGraphicsPipeline::UBOBuffer> &uboBuffers = pipeline->ubos();
+ const ShaderParameterPack &parameterPack = command.m_parameterPack;
+ const PackUniformHash &uniforms = parameterPack.uniforms();
+
+ // Update Buffer CPU side data based on uniforms being set
+ for (const RHIShader::UBO_Member &uboMember : uboMembers) {
+ for (const QShaderDescription::BlockVariable &member : qAsConst(uboMember.members)) {
+
+ if (!member.arrayDims.empty()) {
+ if (!member.structMembers.empty()) {
+ const int arr0 = member.arrayDims[0];
+ for (int i = 0; i < arr0; i++) {
+ for (const QShaderDescription::BlockVariable &structMember :
+ member.structMembers) {
+ const QString processedName = member.name + "[" + QString::number(i)
+ + "]." + structMember.name;
+ uploadUniform(*m_submissionContext, uniforms, uboMember, uboBuffers,
+ processedName, structMember, i * member.size / arr0);
+ }
+ }
+ } else {
+ uploadUniform(*m_submissionContext, uniforms, uboMember, uboBuffers,
+ member.name, member);
+ }
+ } else {
+ uploadUniform(*m_submissionContext, uniforms, uboMember, uboBuffers,
+ member.name, member);
+ }
+ }
+ }
+ // Upload changes to GPU Buffer
+ for (const RHIGraphicsPipeline::UBOBuffer &ubo : uboBuffers) {
+ // Binding triggers the upload
+ ubo.buffer->bind(m_submissionContext.data(), RHIBuffer::UniformBuffer);
+ }
+ }
+ return true;
+}
+
+bool Renderer::performDraw(QRhiCommandBuffer *cb, const QRhiViewport &vp,
+ const QRhiScissor *scissor, const RenderCommand &command)
+{
+ RHIGraphicsPipeline *pipeline = command.pipeline;
+ if (!pipeline)
+ return true;
+
+ // Setup the rendering pass
+ cb->setGraphicsPipeline(pipeline->pipeline());
+ cb->setViewport(vp);
+ if (scissor)
+ cb->setScissor(*scissor);
+ cb->setShaderResources(pipeline->pipeline()->shaderResourceBindings());
+
+ // Send the draw command
+ if (Q_UNLIKELY(!command.indexBuffer)) {
+ cb->setVertexInput(0, command.vertex_input.size(), command.vertex_input.data());
+ cb->draw(command.m_primitiveCount, command.m_instanceCount, command.m_firstVertex,
+ command.m_firstInstance);
+ } else {
+ auto indexFormat = rhiIndexFormat(command.indexAttribute->vertexBaseType());
+ auto indexOffset = command.indexAttribute->byteOffset();
+ cb->setVertexInput(0, command.vertex_input.size(), command.vertex_input.data(),
+ command.indexBuffer, indexOffset, indexFormat);
+ cb->drawIndexed(command.m_primitiveCount, command.m_instanceCount, command.m_indexOffset,
+ command.m_indexAttributeByteOffset, command.m_firstInstance);
+ }
+ return true;
+}
+
+// Called by RenderView->submit() in RenderThread context
+// Returns true, if all RenderCommands were sent to the GPU
+bool Renderer::executeCommandsSubmission(const RHIPassInfo &passInfo)
+{
+ bool allCommandsIssued = true;
+
+ const QVector<RenderView *> &renderViews = passInfo.rvs;
+ QColor clearColor;
+ QRhiDepthStencilClearValue clearDepthStencil;
+
+ // Submit the commands to the underlying graphics API (RHI)
+ QRhiCommandBuffer *cb = m_submissionContext->currentFrameCommandBuffer();
+
+ // Upload data for all RenderCommands
+ for (RenderView *rv : renderViews) {
+ // Render drawing commands
+
+ QVector<RenderCommand> &commands = rv->commands();
+
+ // Upload all the required data to rhi...
+ for (RenderCommand &command : commands) {
+ if (command.m_type == RenderCommand::Draw) {
+ uploadBuffersForCommand(cb, rv, command);
+ uploadUBOsForCommand(cb, rv, command);
+ }
+ }
+
+ // Record clear information
+ if (rv->clearTypes() != QClearBuffers::None) {
+ clearColor = [=] {
+ auto col = rv->globalClearColorBufferInfo().clearColor;
+ return QColor::fromRgbF(col.x(), col.y(), col.z(), col.w());
+ }();
+ clearDepthStencil = { rv->clearDepthValue(), (quint32)rv->clearStencilValue() };
+ }
+ }
+ // TO DO: should be moved elsewhere
+ // Perform compute actions
+ // cb->beginComputePass(m_submissionContext->m_currentUpdates);
+ // for (RenderCommand &command : commands) {
+ // if (command.m_type == RenderCommand::Compute) {
+ // performCompute(rv, &command);
+ // }
+ // }
+ // cb->endComputePass();
+ // m_submissionContext->m_currentUpdates =
+ // m_submissionContext->rhi()->nextResourceUpdateBatch();
+
+ // Draw the commands
+
+ // TO DO: Retrieve real renderTarget for RHIPassInfo
+ QRhiRenderTarget *renderTarget = m_submissionContext->currentFrameRenderTarget();
+
+ // Begin pass
+ cb->beginPass(renderTarget, clearColor, clearDepthStencil,
+ m_submissionContext->m_currentUpdates);
+
+ // Per Pass Global States
+ for (RenderView *rv : renderViews) {
+ // Viewport
+ QRhiViewport vp;
+ QRhiScissor scissor;
+ bool hasScissor = false;
+ {
+ const float x = rv->viewport().x() * rv->surfaceSize().width();
+ const float y = (1. - rv->viewport().y() - rv->viewport().height())
+ * rv->surfaceSize().height();
+ const float w = rv->viewport().width() * rv->surfaceSize().width();
+ const float h = rv->viewport().height() * rv->surfaceSize().height();
+ // qDebug() << x << y << w << h;
+ vp = { x, y, w, h };
+ }
+ // Scissoring
+ {
+ RenderStateSet *ss = rv->stateSet();
+ if (ss == nullptr)
+ ss = m_defaultRenderStateSet;
+ StateVariant *scissorTestSVariant =
+ m_submissionContext->getState(ss, StateMask::ScissorStateMask);
+ if (scissorTestSVariant) {
+ const ScissorTest *scissorTest =
+ static_cast<const ScissorTest *>(scissorTestSVariant->constState());
+ const auto &scissorValues = scissorTest->values();
+ scissor = { std::get<0>(scissorValues), std::get<1>(scissorValues),
+ std::get<2>(scissorValues), std::get<3>(scissorValues) };
+ hasScissor = true;
+ }
+ }
+
+ // Render drawing commands
+ const QVector<RenderCommand> &commands = rv->commands();
+
+ for (const RenderCommand &command : commands) {
+ if (command.m_type == RenderCommand::Draw) {
+ performDraw(cb, vp, hasScissor ? &scissor : nullptr, command);
+ }
+ }
+ }
+
+ cb->endPass();
+ m_submissionContext->m_currentUpdates = m_submissionContext->rhi()->nextResourceUpdateBatch();
+
+ return allCommandsIssued;
+}
+
+// Erase graphics related resources that may become unused after a frame
+void Renderer::cleanGraphicsResources()
+{
+ // Remove unused GraphicsPipeline
+ RHIGraphicsPipelineManager *pipelineManager =
+ m_RHIResourceManagers->rhiGraphicsPipelineManager();
+ const QVector<HRHIGraphicsPipeline> graphicsPipelinesHandles = pipelineManager->activeHandles();
+ for (HRHIGraphicsPipeline pipelineHandle : graphicsPipelinesHandles) {
+ RHIGraphicsPipeline *pipeline = pipelineManager->data(pipelineHandle);
+ pipeline->decreaseScore();
+ // Pipeline wasn't used recently, let's destroy it
+ if (pipeline->score() < 0) {
+ pipeline->cleanup();
+ }
+ }
+
+ // Clean buffers
+ const QVector<Qt3DCore::QNodeId> buffersToRelease =
+ m_nodesManager->bufferManager()->takeBuffersToRelease();
+ for (Qt3DCore::QNodeId bufferId : buffersToRelease)
+ m_submissionContext->releaseBuffer(bufferId);
+
+ // When Textures are cleaned up, their id is saved so that they can be
+ // cleaned up in the render thread
+ const QVector<Qt3DCore::QNodeId> cleanedUpTextureIds = std::move(m_textureIdsToCleanup);
+ for (const Qt3DCore::QNodeId textureCleanedUpId : cleanedUpTextureIds)
+ cleanupTexture(textureCleanedUpId);
+
+ // Abandon GL shaders when a Shader node is destroyed Note: We are sure
+ // that when this gets executed, all scene changes have been received and
+ // shader nodes updated
+ const QVector<Qt3DCore::QNodeId> cleanedUpShaderIds =
+ m_nodesManager->shaderManager()->takeShaderIdsToCleanup();
+ for (const Qt3DCore::QNodeId shaderCleanedUpId : cleanedUpShaderIds) {
+ cleanupShader(m_nodesManager->shaderManager()->lookupResource(shaderCleanedUpId));
+ // We can really release the texture at this point
+ m_nodesManager->shaderManager()->releaseResource(shaderCleanedUpId);
+ }
+}
+
+const GraphicsApiFilterData *Renderer::contextInfo() const
+{
+ return m_submissionContext->contextInfo();
+}
+
+SubmissionContext *Renderer::submissionContext() const
+{
+ return m_submissionContext.data();
+}
+
+} // namespace Rhi
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
diff --git a/src/plugins/renderers/rhi/renderer/renderer.pri b/src/plugins/renderers/rhi/renderer/renderer.pri
new file mode 100644
index 000000000..4ec8cca9b
--- /dev/null
+++ b/src/plugins/renderers/rhi/renderer/renderer.pri
@@ -0,0 +1,27 @@
+INCLUDEPATH += $$PWD
+
+SOURCES += \
+ $$PWD/rendercommand.cpp \
+ $$PWD/renderer.cpp \
+ $$PWD/renderqueue.cpp \
+ $$PWD/renderview.cpp \
+ $$PWD/renderviewbuilder.cpp \
+ $$PWD/rhigraphicspipeline.cpp \
+ $$PWD/rhishader.cpp \
+ $$PWD/shaderparameterpack.cpp \
+ $$PWD/logging.cpp \
+ $$PWD/commandexecuter.cpp
+
+HEADERS += \
+ $$PWD/renderercache_p.h \
+ $$PWD/rendercommand_p.h \
+ $$PWD/renderer_p.h \
+ $$PWD/renderqueue_p.h \
+ $$PWD/renderview_p.h \
+ $$PWD/renderviewbuilder_p.h \
+ $$PWD/rhigraphicspipeline_p.h \
+ $$PWD/rhishader_p.h \
+ $$PWD/shaderparameterpack_p.h \
+ $$PWD/shadervariables_p.h \
+ $$PWD/logging_p.h \
+ $$PWD/commandexecuter_p.h
diff --git a/src/plugins/renderers/rhi/renderer/renderer_p.h b/src/plugins/renderers/rhi/renderer/renderer_p.h
new file mode 100644
index 000000000..8cceca801
--- /dev/null
+++ b/src/plugins/renderers/rhi/renderer/renderer_p.h
@@ -0,0 +1,443 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
+** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 QT3DRENDER_RENDER_RHI_RENDERER_H
+#define QT3DRENDER_RENDER_RHI_RENDERER_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <Qt3DRender/qrenderaspect.h>
+#include <Qt3DRender/qtechnique.h>
+#include <Qt3DRender/private/handle_types_p.h>
+#include <Qt3DRender/private/abstractrenderer_p.h>
+#include <Qt3DCore/qaspectjob.h>
+#include <Qt3DRender/private/qt3drender_global_p.h>
+#include <Qt3DRender/private/rendersettings_p.h>
+#include <Qt3DRender/private/updateshaderdatatransformjob_p.h>
+#include <Qt3DRender/private/framecleanupjob_p.h>
+#include <Qt3DRender/private/platformsurfacefilter_p.h>
+#include <Qt3DRender/private/sendbuffercapturejob_p.h>
+#include <Qt3DRender/private/genericlambdajob_p.h>
+#include <Qt3DRender/private/shaderbuilder_p.h>
+#include <Qt3DRender/private/lightgatherer_p.h>
+#include <Qt3DRender/private/texture_p.h>
+#include <Qt3DRender/private/attachmentpack_p.h>
+#include <Qt3DRender/private/filterentitybycomponentjob_p.h>
+
+#include <QtGui/private/qrhi_p.h>
+
+#include <shaderparameterpack_p.h>
+#include <renderviewinitializerjob_p.h>
+#include <filtercompatibletechniquejob_p.h>
+#include <renderercache_p.h>
+#include <logging_p.h>
+#include <rhihandle_types_p.h>
+#include <renderercache_p.h>
+
+#include <QHash>
+#include <QMatrix4x4>
+#include <QObject>
+
+#include <QOpenGLShaderProgram>
+#include <QOpenGLVertexArrayObject>
+#include <QOpenGLBuffer>
+#include <QMutex>
+#include <QWaitCondition>
+#include <QAtomicInt>
+#include <QScopedPointer>
+#include <QSemaphore>
+
+#include <functional>
+
+#if defined(QT_BUILD_INTERNAL)
+class tst_Renderer;
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class QSurface;
+class QMouseEvent;
+class QScreen;
+
+namespace Qt3DCore {
+class QEntity;
+class QFrameAllocator;
+class QEventFilterService;
+}
+
+namespace Qt3DRender {
+
+class QCamera;
+class QMaterial;
+class QShaderProgram;
+class QMesh;
+class QRenderPass;
+class QAbstractShapeMesh;
+struct GraphicsApiFilterData;
+class QSceneImporter;
+
+namespace Debug {
+class CommandExecuter;
+}
+
+namespace Render {
+
+class CameraLens;
+class FrameGraphNode;
+class Material;
+class Technique;
+class Shader;
+class Entity;
+class Effect;
+class RenderPass;
+class RenderThread;
+class RenderStateSet;
+class VSyncFrameAdvanceService;
+class NodeManagers;
+class ResourceAccessor;
+
+using ComputableEntityFilter = FilterEntityByComponentJob<Render::ComputeCommand, Render::Material>;
+using ComputableEntityFilterPtr = QSharedPointer<ComputableEntityFilter>;
+using RenderableEntityFilter =
+ FilterEntityByComponentJob<Render::GeometryRenderer, Render::Material>;
+using RenderableEntityFilterPtr = QSharedPointer<RenderableEntityFilter>;
+
+using SynchronizerJobPtr = GenericLambdaJobPtr<std::function<void()>>;
+using SynchronizerPostFramePtr =
+ GenericLambdaJobAndPostFramePtr<std::function<void()>,
+ std::function<void(Qt3DCore::QAspectManager *)>>;
+
+namespace Rhi {
+
+class CommandThread;
+class SubmissionContext;
+class RenderCommand;
+class RenderQueue;
+class RenderView;
+class RHIShader;
+class RHIResourceManagers;
+
+class Q_AUTOTEST_EXPORT Renderer : public AbstractRenderer
+{
+public:
+ explicit Renderer(QRenderAspect::RenderType type);
+ ~Renderer();
+
+ void dumpInfo() const override;
+ API api() const override;
+
+ qint64 time() const override;
+ void setTime(qint64 time) override;
+ void setJobsInLastFrame(int jobsInLastFrame) override;
+
+ void setAspect(QRenderAspect *aspect) override;
+ void setNodeManagers(NodeManagers *managers) override;
+ void setServices(Qt3DCore::QServiceLocator *services) override;
+ void setSurfaceExposed(bool exposed) override;
+
+ QRenderAspect *aspect() const override;
+ NodeManagers *nodeManagers() const override;
+ Qt3DCore::QServiceLocator *services() const override { return m_services; }
+
+ void initialize() override;
+ void shutdown() override;
+ void releaseGraphicsResources() override;
+
+ void render() override;
+ void doRender(bool swapBuffers = true) override;
+ void cleanGraphicsResources() override;
+
+ bool isRunning() const override { return m_running.loadRelaxed(); }
+
+ void setSceneRoot(Entity *sgRoot) override;
+ Entity *sceneRoot() const override { return m_renderSceneRoot; }
+
+ FrameGraphNode *frameGraphRoot() const override;
+ RenderQueue *renderQueue() const { return m_renderQueue; }
+
+ void markDirty(BackendNodeDirtySet changes, BackendNode *node) override;
+ BackendNodeDirtySet dirtyBits() override;
+
+#if defined(QT_BUILD_INTERNAL)
+ void clearDirtyBits(BackendNodeDirtySet changes) override;
+#endif
+ bool shouldRender() const override;
+ void skipNextFrame() override;
+ void jobsDone(Qt3DCore::QAspectManager *manager) override;
+
+ void setPendingEvents(const QList<QPair<QObject *, QMouseEvent>> &mouseEvents,
+ const QList<QKeyEvent> &keyEvents) override;
+
+ QVector<Qt3DCore::QAspectJobPtr> preRenderingJobs() override;
+ QVector<Qt3DCore::QAspectJobPtr> renderBinJobs() override;
+
+ inline FrameCleanupJobPtr frameCleanupJob() const { return m_cleanupJob; }
+ inline UpdateShaderDataTransformJobPtr updateShaderDataTransformJob() const
+ {
+ return m_updateShaderDataTransformJob;
+ }
+ inline FilterCompatibleTechniqueJobPtr filterCompatibleTechniqueJob() const
+ {
+ return m_filterCompatibleTechniqueJob;
+ }
+ inline SynchronizerPostFramePtr introspectShadersJob() const { return m_introspectShaderJob; }
+ inline Qt3DCore::QAspectJobPtr bufferGathererJob() const { return m_bufferGathererJob; }
+ inline Qt3DCore::QAspectJobPtr textureGathererJob() const { return m_textureGathererJob; }
+ inline LightGathererPtr lightGathererJob() const { return m_lightGathererJob; }
+ inline RenderableEntityFilterPtr renderableEntityFilterJob() const
+ {
+ return m_renderableEntityFilterJob;
+ }
+ inline ComputableEntityFilterPtr computableEntityFilterJob() const
+ {
+ return m_computableEntityFilterJob;
+ }
+
+ Qt3DCore::QAbstractFrameAdvanceService *frameAdvanceService() const override;
+
+ void setSettings(RenderSettings *settings) override;
+ RenderSettings *settings() const override;
+ QOpenGLContext *shareContext() const override;
+
+ inline RHIResourceManagers *rhiResourceManagers() const { return m_RHIResourceManagers; }
+
+ // Executed in secondary GL thread
+ void loadShader(Shader *shader, Qt3DRender::Render::HShader shaderHandle) override;
+
+ void updateResources();
+ void updateTexture(Texture *texture);
+ void cleanupTexture(Qt3DCore::QNodeId cleanedUpTextureId);
+ void cleanupShader(const Shader *shader);
+ void downloadGLBuffers();
+ void blitFramebuffer(Qt3DCore::QNodeId inputRenderTargetId,
+ Qt3DCore::QNodeId outputRenderTargetId, QRect inputRect, QRect outputRect,
+ GLuint defaultFramebuffer);
+
+ struct RHIPassInfo
+ {
+ QVector<RenderView *> rvs;
+ QSurface *surface = nullptr;
+ Qt3DCore::QNodeId renderTargetId;
+ AttachmentPack attachmentPack;
+ };
+
+ QVector<RHIPassInfo> prepareCommandsSubmission(const QVector<RenderView *> &renderViews);
+ bool executeCommandsSubmission(const RHIPassInfo &passInfo);
+
+ // For Scene2D rendering
+ void setOpenGLContext(QOpenGLContext *context) override;
+ bool accessOpenGLTexture(Qt3DCore::QNodeId nodeId, QOpenGLTexture **texture, QMutex **lock,
+ bool readonly) override;
+ QSharedPointer<RenderBackendResourceAccessor> resourceAccessor() const override;
+
+ const GraphicsApiFilterData *contextInfo() const;
+ SubmissionContext *submissionContext() const;
+
+ inline RenderStateSet *defaultRenderState() const { return m_defaultRenderStateSet; }
+
+ QList<QPair<QObject *, QMouseEvent>> pendingPickingEvents() const;
+ QList<QKeyEvent> pendingKeyEvents() const;
+
+ void enqueueRenderView(RenderView *renderView, int submitOrder);
+ bool isReadyToSubmit();
+
+ QVariant executeCommand(const QStringList &args) override;
+ void setOffscreenSurfaceHelper(OffscreenSurfaceHelper *helper) override;
+ QSurfaceFormat format() override;
+
+ struct ViewSubmissionResultData
+ {
+ ViewSubmissionResultData() : lastBoundFBOId(0), surface(nullptr) { }
+
+ uint lastBoundFBOId;
+ QSurface *surface;
+ };
+
+ ViewSubmissionResultData submitRenderViews(const QVector<RHIPassInfo> &rhiPassesInfo);
+
+ RendererCache *cache() { return &m_cache; }
+ void setScreen(QScreen *scr) override;
+ QScreen *screen() const override;
+
+ float *textureTransform() noexcept { return m_textureTransform; }
+ const float *textureTransform() const noexcept { return m_textureTransform; }
+#ifdef QT3D_RENDER_UNIT_TESTS
+public:
+#else
+
+private:
+#endif
+ bool canRender() const;
+
+ Qt3DCore::QServiceLocator *m_services;
+ QRenderAspect *m_aspect;
+ NodeManagers *m_nodesManager;
+
+ // Frame graph root
+ Qt3DCore::QNodeId m_frameGraphRootUuid;
+
+ Entity *m_renderSceneRoot;
+
+ // Fail safe values that we can use if a RenderCommand
+ // is missing a shader
+ RenderStateSet *m_defaultRenderStateSet;
+ ShaderParameterPack m_defaultUniformPack;
+
+ QScopedPointer<SubmissionContext> m_submissionContext;
+
+ RenderQueue *m_renderQueue;
+ QScopedPointer<RenderThread> m_renderThread;
+ QScopedPointer<VSyncFrameAdvanceService> m_vsyncFrameAdvanceService;
+
+ QSemaphore m_submitRenderViewsSemaphore;
+ QSemaphore m_waitForInitializationToBeCompleted;
+ QMutex m_hasBeenInitializedMutex;
+
+ QAtomicInt m_running;
+
+ QVector<Attribute *> m_dirtyAttributes;
+ QVector<Geometry *> m_dirtyGeometry;
+ QAtomicInt m_exposed;
+
+ struct DirtyBits
+ {
+ BackendNodeDirtySet marked; // marked dirty since last job build
+ BackendNodeDirtySet remaining; // remaining dirty after jobs have finished
+ };
+ DirtyBits m_dirtyBits;
+
+ QAtomicInt m_lastFrameCorrect;
+ QOpenGLContext *m_glContext;
+
+ qint64 m_time;
+
+ RenderSettings *m_settings;
+
+ UpdateShaderDataTransformJobPtr m_updateShaderDataTransformJob;
+ FrameCleanupJobPtr m_cleanupJob;
+ SendBufferCaptureJobPtr m_sendBufferCaptureJob;
+ FilterCompatibleTechniqueJobPtr m_filterCompatibleTechniqueJob;
+ LightGathererPtr m_lightGathererJob;
+ RenderableEntityFilterPtr m_renderableEntityFilterJob;
+ ComputableEntityFilterPtr m_computableEntityFilterJob;
+
+ QVector<Qt3DCore::QNodeId> m_pendingRenderCaptureSendRequests;
+
+ void performDraw(RenderCommand *command);
+ void performCompute(const RenderView *rv, RenderCommand *command);
+
+ SynchronizerJobPtr m_bufferGathererJob;
+ SynchronizerJobPtr m_textureGathererJob;
+ SynchronizerPostFramePtr m_introspectShaderJob;
+
+ void lookForDirtyBuffers();
+ void lookForDownloadableBuffers();
+ void lookForDirtyTextures();
+ void reloadDirtyShaders();
+ void sendShaderChangesToFrontend(Qt3DCore::QAspectManager *manager);
+ void sendTextureChangesToFrontend(Qt3DCore::QAspectManager *manager);
+ void sendSetFenceHandlesToFrontend();
+ void sendDisablesToFrontend(Qt3DCore::QAspectManager *manager);
+
+ QVector<HBuffer> m_dirtyBuffers;
+ QVector<Qt3DCore::QNodeId> m_downloadableBuffers;
+ QVector<HShader> m_dirtyShaders;
+ QVector<HTexture> m_dirtyTextures;
+ QVector<QPair<Texture::TextureUpdateInfo, Qt3DCore::QNodeIdVector>> m_updatedTextureProperties;
+ QVector<Qt3DCore::QNodeId> m_updatedDisableSubtreeEnablers;
+ Qt3DCore::QNodeIdVector m_textureIdsToCleanup;
+ QVector<ShaderBuilderUpdate> m_shaderBuilderUpdates;
+
+ bool m_ownedContext;
+
+ OffscreenSurfaceHelper *m_offscreenHelper;
+ RHIResourceManagers *m_RHIResourceManagers;
+ QMutex m_offscreenSurfaceMutex;
+
+ QScopedPointer<Qt3DRender::Debug::CommandExecuter> m_commandExecuter;
+
+#ifdef QT_BUILD_INTERNAL
+ friend class ::tst_Renderer;
+#endif
+
+ QMetaObject::Connection m_contextConnection;
+ RendererCache m_cache;
+ bool m_shouldSwapBuffers;
+
+ QVector<FrameGraphNode *> m_frameGraphLeaves;
+ QScreen *m_screen = nullptr;
+ QSharedPointer<ResourceAccessor> m_scene2DResourceAccessor;
+
+ QOffscreenSurface *m_fallbackSurface {};
+
+ bool m_hasSwapChain = false;
+
+ QList<QPair<QObject *, QMouseEvent>> m_frameMouseEvents;
+ QList<QKeyEvent> m_frameKeyEvents;
+ QMutex m_frameEventsMutex;
+ int m_jobsInLastFrame = 0;
+
+ float m_textureTransform[4];
+
+ void updateGraphicsPipeline(RenderCommand &command, RenderView *rv, int renderViewIndex);
+ bool uploadBuffersForCommand(QRhiCommandBuffer *cb, const RenderView *rv,
+ RenderCommand &command);
+ bool uploadUBOsForCommand(QRhiCommandBuffer *cb, const RenderView *rv,
+ const RenderCommand &command);
+ bool performDraw(QRhiCommandBuffer *cb, const QRhiViewport &vp, const QRhiScissor *scissor,
+ const RenderCommand &command);
+};
+
+} // namespace Rhi
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDER_RHI_RENDERER_H
diff --git a/src/plugins/renderers/rhi/renderer/renderercache_p.h b/src/plugins/renderers/rhi/renderer/renderercache_p.h
new file mode 100644
index 000000000..ec3223dd2
--- /dev/null
+++ b/src/plugins/renderers/rhi/renderer/renderercache_p.h
@@ -0,0 +1,101 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 QT3DRENDER_RENDER_RHI_RENDERERCACHE_P_H
+#define QT3DRENDER_RENDER_RHI_RENDERERCACHE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <Qt3DRender/QFrameGraphNode>
+
+#include <Qt3DRender/private/entity_p.h>
+#include <renderviewjobutils_p.h>
+#include <Qt3DRender/private/lightsource_p.h>
+#include <rendercommand_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+namespace Render {
+
+namespace Rhi {
+
+struct RendererCache
+{
+ struct LeafNodeData
+ {
+ QVector<Entity *> filterEntitiesByLayer;
+ MaterialParameterGathererData materialParameterGatherer;
+ EntityRenderCommandData renderCommandData;
+ };
+
+ // Shared amongst all RV cache
+ QVector<Entity *> renderableEntities;
+ QVector<Entity *> computeEntities;
+ QVector<LightSource> gatheredLights;
+ EnvironmentLight *environmentLight;
+
+ // Per RV cache
+ QHash<FrameGraphNode *, LeafNodeData> leafNodeCache;
+
+ QMutex *mutex() { return &m_mutex; }
+
+private:
+ QMutex m_mutex;
+};
+
+} // namespace Rhi
+
+} // namespace Render
+
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDER_RHI_RENDERERCACHE_P_H
diff --git a/src/plugins/renderers/rhi/renderer/renderqueue.cpp b/src/plugins/renderers/rhi/renderer/renderqueue.cpp
new file mode 100644
index 000000000..39eaddc9a
--- /dev/null
+++ b/src/plugins/renderers/rhi/renderer/renderqueue.cpp
@@ -0,0 +1,138 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 "renderqueue_p.h"
+#include <renderview_p.h>
+#include <QThread>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+namespace Render {
+
+namespace Rhi {
+
+RenderQueue::RenderQueue()
+ : m_noRender(false),
+ m_wasReset(true),
+ m_targetRenderViewCount(0),
+ m_currentRenderViewCount(0),
+ m_currentWorkQueue(1)
+{
+}
+
+int RenderQueue::currentRenderViewCount() const
+{
+ return m_currentRenderViewCount;
+}
+
+/*
+ * In case the framegraph changed or when the current number of render queue
+ * needs to be reset.
+ */
+void RenderQueue::reset()
+{
+ m_currentRenderViewCount = 0;
+ m_targetRenderViewCount = 0;
+ m_currentWorkQueue.clear();
+ m_noRender = false;
+ m_wasReset = true;
+}
+
+void RenderQueue::setNoRender()
+{
+ Q_ASSERT(m_targetRenderViewCount == 0);
+ m_noRender = true;
+}
+
+/*
+ * Queue up a RenderView for the frame being built.
+ * Thread safe as this is called from the renderer which is locked.
+ * Returns true if the renderView is complete
+ */
+bool RenderQueue::queueRenderView(RenderView *renderView, uint submissionOrderIndex)
+{
+ Q_ASSERT(!m_noRender);
+ m_currentWorkQueue[submissionOrderIndex] = renderView;
+ ++m_currentRenderViewCount;
+ Q_ASSERT(m_currentRenderViewCount <= m_targetRenderViewCount);
+ return isFrameQueueComplete();
+}
+
+/*
+ * Called by the Rendering Thread to retrieve the a frame queue to render.
+ * A call to reset is required after rendering of the frame. Otherwise under some
+ * conditions the current but then invalidated frame queue could be reused.
+ */
+QVector<RenderView *> RenderQueue::nextFrameQueue()
+{
+ return m_currentWorkQueue;
+}
+
+/*
+ * Sets the number \a targetRenderViewCount of RenderView objects that make up a frame.
+ */
+void RenderQueue::setTargetRenderViewCount(int targetRenderViewCount)
+{
+ Q_ASSERT(!m_noRender);
+ m_targetRenderViewCount = targetRenderViewCount;
+ m_currentWorkQueue.resize(targetRenderViewCount);
+ m_wasReset = false;
+}
+
+/*
+ * Returns true if all the RenderView objects making up the current frame have been queued.
+ * Returns false otherwise.
+ * \note a frameQueue or size 0 is considered incomplete.
+ */
+bool RenderQueue::isFrameQueueComplete() const
+{
+ return (m_noRender
+ || (m_targetRenderViewCount > 0
+ && m_targetRenderViewCount == m_currentRenderViewCount));
+}
+
+} // namespace Rhi
+
+} // namespace Render
+
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
diff --git a/src/plugins/renderers/rhi/renderer/renderqueue_p.h b/src/plugins/renderers/rhi/renderer/renderqueue_p.h
new file mode 100644
index 000000000..3b6eec13d
--- /dev/null
+++ b/src/plugins/renderers/rhi/renderer/renderqueue_p.h
@@ -0,0 +1,106 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 QT3DRENDER_RENDER_RHI_RENDERQUEUE_H
+#define QT3DRENDER_RENDER_RHI_RENDERQUEUE_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QVector>
+#include <QtGlobal>
+#include <QMutex>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+namespace Render {
+
+namespace Rhi {
+
+class RenderView;
+
+class Q_AUTOTEST_EXPORT RenderQueue
+{
+public:
+ RenderQueue();
+
+ void setTargetRenderViewCount(int targetRenderViewCount);
+ int targetRenderViewCount() const { return m_targetRenderViewCount; }
+ int currentRenderViewCount() const;
+ bool isFrameQueueComplete() const;
+
+ bool queueRenderView(RenderView *renderView, uint submissionOrderIndex);
+ QVector<RenderView *> nextFrameQueue();
+ void reset();
+
+ void setNoRender();
+ inline bool isNoRender() const { return m_noRender; }
+
+ inline bool wasReset() const { return m_wasReset; }
+
+ inline QMutex *mutex() { return &m_mutex; }
+
+private:
+ bool m_noRender;
+ bool m_wasReset;
+ int m_targetRenderViewCount;
+ int m_currentRenderViewCount;
+ QVector<RenderView *> m_currentWorkQueue;
+ QMutex m_mutex;
+};
+
+} // namespace Rhi
+
+} // namespace Render
+
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDER_RHI_RENDERQUEUE_H
diff --git a/src/plugins/renderers/rhi/renderer/renderview.cpp b/src/plugins/renderers/rhi/renderer/renderview.cpp
new file mode 100644
index 000000000..54295ef5f
--- /dev/null
+++ b/src/plugins/renderers/rhi/renderer/renderview.cpp
@@ -0,0 +1,1253 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
+** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 "renderview_p.h"
+#include <Qt3DRender/qmaterial.h>
+#include <Qt3DRender/qrenderaspect.h>
+#include <Qt3DRender/qrendertarget.h>
+#include <Qt3DRender/qabstractlight.h>
+#include <Qt3DRender/private/sphere_p.h>
+
+#include <Qt3DRender/private/cameraselectornode_p.h>
+#include <Qt3DRender/private/framegraphnode_p.h>
+#include <Qt3DRender/private/layerfilternode_p.h>
+#include <Qt3DRender/private/qparameter_p.h>
+#include <Qt3DRender/private/cameralens_p.h>
+#include <Qt3DRender/private/effect_p.h>
+#include <Qt3DRender/private/entity_p.h>
+#include <Qt3DRender/private/nodemanagers_p.h>
+#include <Qt3DRender/private/layer_p.h>
+#include <Qt3DRender/private/renderlogging_p.h>
+#include <Qt3DRender/private/renderpassfilternode_p.h>
+#include <Qt3DRender/private/renderpass_p.h>
+#include <Qt3DRender/private/geometryrenderer_p.h>
+#include <Qt3DRender/private/techniquefilternode_p.h>
+#include <Qt3DRender/private/viewportnode_p.h>
+#include <Qt3DRender/private/buffermanager_p.h>
+#include <Qt3DRender/private/geometryrenderermanager_p.h>
+#include <Qt3DRender/private/rendercapture_p.h>
+#include <Qt3DRender/private/buffercapture_p.h>
+#include <Qt3DRender/private/stringtoint_p.h>
+#include <Qt3DRender/private/renderlogging_p.h>
+#include <Qt3DRender/private/renderstateset_p.h>
+#include <rendercommand_p.h>
+#include <renderer_p.h>
+#include <submissioncontext_p.h>
+#include <rhiresourcemanagers_p.h>
+#include <Qt3DCore/qentity.h>
+#include <QtGui/qsurface.h>
+#include <algorithm>
+#include <atomic>
+#include <cstdlib>
+#include <QDebug>
+#if defined(QT3D_RENDER_VIEW_JOB_TIMINGS)
+#include <QElapsedTimer>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+namespace Render {
+namespace Rhi {
+
+namespace {
+
+// register our QNodeId's as a metatype during program loading
+const int Q_DECL_UNUSED qNodeIdTypeId = qMetaTypeId<Qt3DCore::QNodeId>();
+
+const int MAX_LIGHTS = 8;
+
+#define LIGHT_POSITION_NAME QLatin1String(".position")
+#define LIGHT_TYPE_NAME QLatin1String(".type")
+#define LIGHT_COLOR_NAME QLatin1String(".color")
+#define LIGHT_INTENSITY_NAME QLatin1String(".intensity")
+
+int LIGHT_COUNT_NAME_ID = 0;
+int LIGHT_POSITION_NAMES[MAX_LIGHTS];
+int LIGHT_TYPE_NAMES[MAX_LIGHTS];
+int LIGHT_COLOR_NAMES[MAX_LIGHTS];
+int LIGHT_INTENSITY_NAMES[MAX_LIGHTS];
+QString LIGHT_STRUCT_NAMES[MAX_LIGHTS];
+
+std::atomic_bool wasInitialized {};
+
+} // anonymous namespace
+
+// TODO: Move this somewhere global where GraphicsContext::setViewport() can use it too
+static QRectF resolveViewport(const QRectF &fractionalViewport, const QSize &surfaceSize)
+{
+ return QRectF(fractionalViewport.x() * surfaceSize.width(),
+ (1.0 - fractionalViewport.y() - fractionalViewport.height())
+ * surfaceSize.height(),
+ fractionalViewport.width() * surfaceSize.width(),
+ fractionalViewport.height() * surfaceSize.height());
+}
+
+static Matrix4x4 getProjectionMatrix(const CameraLens *lens, bool yIsUp)
+{
+ if (lens) {
+ if (yIsUp) {
+ // OpenGL
+ return lens->projection();
+ } else {
+ // Others. Note : this could likely be optimized...
+ auto p = lens->projection();
+ Matrix4x4 rev { 0, 0, 0, 0, 0, -2 * p.m22(), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ p += rev;
+ return p;
+ }
+ } else {
+ qWarning() << "[Qt3D Renderer] No Camera Lens found. Add a CameraSelector to your Frame "
+ "Graph or make sure that no entities will be rendered.";
+ return Matrix4x4();
+ }
+}
+
+RenderView::RenderView()
+ : m_isDownloadBuffersEnable(false),
+ m_hasBlitFramebufferInfo(false),
+ m_renderer(nullptr),
+ m_manager(nullptr),
+ m_devicePixelRatio(1.),
+ m_viewport(QRectF(0., 0., 1., 1.)),
+ m_gamma(2.2f),
+ m_surface(nullptr),
+ m_clearBuffer(QClearBuffers::None),
+ m_clearDepthValue(1.f),
+ m_clearStencilValue(0),
+ m_stateSet(nullptr),
+ m_noDraw(false),
+ m_compute(false),
+ m_frustumCulling(false),
+ m_environmentLight(nullptr)
+{
+ m_workGroups[0] = 1;
+ m_workGroups[1] = 1;
+ m_workGroups[2] = 1;
+
+ if (Q_UNLIKELY(!wasInitialized.exchange(true))) {
+ // Needed as we can control the init order of static/global variables across compile units
+ // and this hash relies on the static StringToInt class
+
+ LIGHT_COUNT_NAME_ID = StringToInt::lookupId(QLatin1String("lightCount"));
+ for (int i = 0; i < MAX_LIGHTS; ++i) {
+ Q_STATIC_ASSERT_X(MAX_LIGHTS < 10, "can't use the QChar trick anymore");
+ LIGHT_STRUCT_NAMES[i] =
+ QLatin1String("lights[") + QLatin1Char(char('0' + i)) + QLatin1Char(']');
+ LIGHT_POSITION_NAMES[i] =
+ StringToInt::lookupId(LIGHT_STRUCT_NAMES[i] + LIGHT_POSITION_NAME);
+ LIGHT_TYPE_NAMES[i] = StringToInt::lookupId(LIGHT_STRUCT_NAMES[i] + LIGHT_TYPE_NAME);
+ LIGHT_COLOR_NAMES[i] = StringToInt::lookupId(LIGHT_STRUCT_NAMES[i] + LIGHT_COLOR_NAME);
+ LIGHT_INTENSITY_NAMES[i] =
+ StringToInt::lookupId(LIGHT_STRUCT_NAMES[i] + LIGHT_INTENSITY_NAME);
+ }
+ }
+}
+
+RenderView::~RenderView()
+{
+ delete m_stateSet;
+}
+
+namespace {
+
+template<int SortType>
+struct AdjacentSubRangeFinder
+{
+ static bool adjacentSubRange(const RenderCommand &, const RenderCommand &)
+ {
+ Q_UNREACHABLE();
+ return false;
+ }
+};
+
+template<>
+struct AdjacentSubRangeFinder<QSortPolicy::StateChangeCost>
+{
+ static bool adjacentSubRange(const RenderCommand &a, const RenderCommand &b)
+ {
+ return a.m_changeCost == b.m_changeCost;
+ }
+};
+
+template<>
+struct AdjacentSubRangeFinder<QSortPolicy::BackToFront>
+{
+ static bool adjacentSubRange(const RenderCommand &a, const RenderCommand &b)
+ {
+ return a.m_depth == b.m_depth;
+ }
+};
+
+template<>
+struct AdjacentSubRangeFinder<QSortPolicy::Material>
+{
+ static bool adjacentSubRange(const RenderCommand &a, const RenderCommand &b)
+ {
+ return a.m_rhiShader == b.m_rhiShader;
+ }
+};
+
+template<>
+struct AdjacentSubRangeFinder<QSortPolicy::FrontToBack>
+{
+ static bool adjacentSubRange(const RenderCommand &a, const RenderCommand &b)
+ {
+ return a.m_depth == b.m_depth;
+ }
+};
+
+template<>
+struct AdjacentSubRangeFinder<QSortPolicy::Texture>
+{
+ static bool adjacentSubRange(const RenderCommand &a, const RenderCommand &b)
+ {
+ // Two renderCommands are adjacent if one contains all the other command's textures
+ QVector<ShaderParameterPack::NamedResource> texturesA = a.m_parameterPack.textures();
+ QVector<ShaderParameterPack::NamedResource> texturesB = b.m_parameterPack.textures();
+
+ if (texturesB.size() > texturesA.size())
+ qSwap(texturesA, texturesB);
+
+ // textureB.size() is always <= textureA.size()
+ for (const ShaderParameterPack::NamedResource &texB : qAsConst(texturesB)) {
+ if (!texturesA.contains(texB))
+ return false;
+ }
+ return true;
+ }
+};
+
+template<typename Predicate>
+int advanceUntilNonAdjacent(const QVector<RenderCommand> &commands, const int beg, const int end,
+ Predicate pred)
+{
+ int i = beg + 1;
+ while (i < end) {
+ if (!pred(*(commands.begin() + beg), *(commands.begin() + i)))
+ break;
+ ++i;
+ }
+ return i;
+}
+
+using CommandIt = QVector<RenderCommand>::iterator;
+
+template<int SortType>
+struct SubRangeSorter
+{
+ static void sortSubRange(CommandIt begin, const CommandIt end)
+ {
+ Q_UNUSED(begin);
+ Q_UNUSED(end);
+ Q_UNREACHABLE();
+ }
+};
+
+template<>
+struct SubRangeSorter<QSortPolicy::StateChangeCost>
+{
+ static void sortSubRange(CommandIt begin, const CommandIt end)
+ {
+ std::stable_sort(begin, end, [](const RenderCommand &a, const RenderCommand &b) {
+ return a.m_changeCost > b.m_changeCost;
+ });
+ }
+};
+
+template<>
+struct SubRangeSorter<QSortPolicy::BackToFront>
+{
+ static void sortSubRange(CommandIt begin, const CommandIt end)
+ {
+ std::stable_sort(begin, end, [](const RenderCommand &a, const RenderCommand &b) {
+ return a.m_depth > b.m_depth;
+ });
+ }
+};
+
+template<>
+struct SubRangeSorter<QSortPolicy::Material>
+{
+ static void sortSubRange(CommandIt begin, const CommandIt end)
+ {
+ // First we sort by shader
+ std::stable_sort(begin, end, [](const RenderCommand &a, const RenderCommand &b) {
+ return a.m_rhiShader > b.m_rhiShader;
+ });
+ }
+};
+
+template<>
+struct SubRangeSorter<QSortPolicy::FrontToBack>
+{
+ static void sortSubRange(CommandIt begin, const CommandIt end)
+ {
+ std::stable_sort(begin, end, [](const RenderCommand &a, const RenderCommand &b) {
+ return a.m_depth < b.m_depth;
+ });
+ }
+};
+
+template<>
+struct SubRangeSorter<QSortPolicy::Texture>
+{
+ static void sortSubRange(CommandIt begin, const CommandIt end)
+ {
+#ifndef Q_OS_WIN
+ std::stable_sort(begin, end, [] (const RenderCommand &a, const RenderCommand &b) {
+ QVector<ShaderParameterPack::NamedResource> texturesA = a.m_parameterPack.textures();
+ QVector<ShaderParameterPack::NamedResource> texturesB = b.m_parameterPack.textures();
+
+ const int originalTextureASize = texturesA.size();
+
+ if (texturesB.size() > texturesA.size())
+ qSwap(texturesA, texturesB);
+
+ int identicalTextureCount = 0;
+
+ for (const ShaderParameterPack::NamedResource &texB : qAsConst(texturesB)) {
+ if (texturesA.contains(texB))
+ ++identicalTextureCount;
+ }
+
+ return identicalTextureCount < originalTextureASize;
+ });
+#endif
+ }
+};
+
+int findSubRange(const QVector<RenderCommand> &commands, const int begin, const int end,
+ const QSortPolicy::SortType sortType)
+{
+ switch (sortType) {
+ case QSortPolicy::StateChangeCost:
+ return advanceUntilNonAdjacent(
+ commands, begin, end,
+ AdjacentSubRangeFinder<QSortPolicy::StateChangeCost>::adjacentSubRange);
+ case QSortPolicy::BackToFront:
+ return advanceUntilNonAdjacent(
+ commands, begin, end,
+ AdjacentSubRangeFinder<QSortPolicy::BackToFront>::adjacentSubRange);
+ case QSortPolicy::Material:
+ return advanceUntilNonAdjacent(
+ commands, begin, end,
+ AdjacentSubRangeFinder<QSortPolicy::Material>::adjacentSubRange);
+ case QSortPolicy::FrontToBack:
+ return advanceUntilNonAdjacent(
+ commands, begin, end,
+ AdjacentSubRangeFinder<QSortPolicy::FrontToBack>::adjacentSubRange);
+ case QSortPolicy::Texture:
+ return advanceUntilNonAdjacent(
+ commands, begin, end,
+ AdjacentSubRangeFinder<QSortPolicy::Texture>::adjacentSubRange);
+ default:
+ Q_UNREACHABLE();
+ return end;
+ }
+}
+
+void sortByMaterial(QVector<RenderCommand> &commands, int begin, const int end)
+{
+ // We try to arrange elements so that their rendering cost is minimized for a given shader
+ int rangeEnd = advanceUntilNonAdjacent(
+ commands, begin, end, AdjacentSubRangeFinder<QSortPolicy::Material>::adjacentSubRange);
+ while (begin != end) {
+ if (begin + 1 < rangeEnd) {
+ std::stable_sort(commands.begin() + begin + 1, commands.begin() + rangeEnd,
+ [](const RenderCommand &a, const RenderCommand &b) {
+ return a.m_material.handle() < b.m_material.handle();
+ });
+ }
+ begin = rangeEnd;
+ rangeEnd = advanceUntilNonAdjacent(
+ commands, begin, end,
+ AdjacentSubRangeFinder<QSortPolicy::Material>::adjacentSubRange);
+ }
+}
+
+void sortCommandRange(QVector<RenderCommand> &commands, int begin, const int end, const int level,
+ const QVector<Qt3DRender::QSortPolicy::SortType> &sortingTypes)
+{
+ if (level >= sortingTypes.size())
+ return;
+
+ switch (sortingTypes.at(level)) {
+ case QSortPolicy::StateChangeCost:
+ SubRangeSorter<QSortPolicy::StateChangeCost>::sortSubRange(commands.begin() + begin,
+ commands.begin() + end);
+ break;
+ case QSortPolicy::BackToFront:
+ SubRangeSorter<QSortPolicy::BackToFront>::sortSubRange(commands.begin() + begin,
+ commands.begin() + end);
+ break;
+ case QSortPolicy::Material:
+ // Groups all same shader DNA together
+ SubRangeSorter<QSortPolicy::Material>::sortSubRange(commands.begin() + begin,
+ commands.begin() + end);
+ // Group all same material together (same parameters most likely)
+ sortByMaterial(commands, begin, end);
+ break;
+ case QSortPolicy::FrontToBack:
+ SubRangeSorter<QSortPolicy::FrontToBack>::sortSubRange(commands.begin() + begin,
+ commands.begin() + end);
+ break;
+ case QSortPolicy::Texture:
+ SubRangeSorter<QSortPolicy::Texture>::sortSubRange(commands.begin() + begin,
+ commands.begin() + end);
+ break;
+ default:
+ Q_UNREACHABLE();
+ }
+
+ // For all sub ranges of adjacent item for sortType[i]
+ // Perform filtering with sortType[i + 1]
+ int rangeEnd = findSubRange(commands, begin, end, sortingTypes.at(level));
+ while (begin != end) {
+ sortCommandRange(commands, begin, rangeEnd, level + 1, sortingTypes);
+ begin = rangeEnd;
+ rangeEnd = findSubRange(commands, begin, end, sortingTypes.at(level));
+ }
+}
+
+} // anonymous
+
+void RenderView::sort()
+{
+ // Compares the bitsetKey of the RenderCommands
+ // Key[Depth | StateCost | Shader]
+ sortCommandRange(m_commands, 0, m_commands.size(), 0, m_data.m_sortingTypes);
+
+ // For RenderCommand with the same shader
+ // We compute the adjacent change cost
+
+ /*
+ // Minimize uniform changes
+ int i = 0;
+ const int commandSize = m_commands.size();
+ while (i < commandSize) {
+ int j = i;
+
+ // Advance while commands share the same shader
+ while (i < commandSize &&
+ m_commands[j].m_rhiShader == m_commands[i].m_rhiShader)
+ ++i;
+
+ if (i - j > 0) { // Several commands have the same shader, so we minimize uniform changes
+ PackUniformHash cachedUniforms = m_commands[j++].m_parameterPack.uniforms();
+
+ while (j < i) {
+ // We need the reference here as we are modifying the original container
+ // not the copy
+ PackUniformHash &uniforms = m_commands[j].m_parameterPack.m_uniforms;
+
+ for (int u = 0; u < uniforms.keys.size();) {
+ // We are comparing the values:
+ // - raw uniform values
+ // - the texture Node id if the uniform represents a texture
+ // since all textures are assigned texture units before the RenderCommands
+ // sharing the same material (shader) are rendered, we can't have the case
+ // where two uniforms, referencing the same texture eventually have 2 different
+ // texture unit values
+ const int uniformNameId = uniforms.keys.at(u);
+ const UniformValue &refValue = cachedUniforms.value(uniformNameId);
+ const UniformValue &newValue = uniforms.values.at(u);
+ if (newValue == refValue) {
+ uniforms.erase(u);
+ } else {
+ // Record updated value so that subsequent comparison
+ // for the next command will be made againts latest
+ // uniform value
+ cachedUniforms.insert(uniformNameId, newValue);
+ ++u;
+ }
+ }
+ ++j;
+ }
+ }
+ }
+ */
+}
+
+void RenderView::setRenderer(Renderer *renderer)
+{
+ m_renderer = renderer;
+ m_manager = renderer->nodeManagers();
+}
+
+void RenderView::addClearBuffers(const ClearBuffers *cb)
+{
+ QClearBuffers::BufferTypeFlags type = cb->type();
+
+ if (type & QClearBuffers::StencilBuffer) {
+ m_clearStencilValue = cb->clearStencilValue();
+ m_clearBuffer |= QClearBuffers::StencilBuffer;
+ }
+ if (type & QClearBuffers::DepthBuffer) {
+ m_clearDepthValue = cb->clearDepthValue();
+ m_clearBuffer |= QClearBuffers::DepthBuffer;
+ }
+ // keep track of global ClearColor (if set) and collect all DrawBuffer-specific
+ // ClearColors
+ if (type & QClearBuffers::ColorBuffer) {
+ ClearBufferInfo clearBufferInfo;
+ clearBufferInfo.clearColor = cb->clearColor();
+
+ if (cb->clearsAllColorBuffers()) {
+ m_globalClearColorBuffer = clearBufferInfo;
+ m_clearBuffer |= QClearBuffers::ColorBuffer;
+ } else {
+ if (cb->bufferId()) {
+ const RenderTargetOutput *targetOutput =
+ m_manager->attachmentManager()->lookupResource(cb->bufferId());
+ if (targetOutput) {
+ clearBufferInfo.attchmentPoint = targetOutput->point();
+ // Note: a job is later performed to find the drawIndex from the buffer
+ // attachment point using the AttachmentPack
+ m_specificClearColorBuffers.push_back(clearBufferInfo);
+ }
+ }
+ }
+ }
+}
+
+// If we are there, we know that entity had a GeometryRenderer + Material
+EntityRenderCommandData RenderView::buildDrawRenderCommands(const QVector<Entity *> &entities,
+ int offset, int count) const
+{
+ EntityRenderCommandData commands;
+ RHIShaderManager *rhiShaderManager = m_renderer->rhiResourceManagers()->rhiShaderManager();
+
+ commands.reserve(count);
+
+ for (int i = 0; i < count; ++i) {
+ const int idx = offset + i;
+ Entity *entity = entities.at(idx);
+ GeometryRenderer *geometryRenderer = nullptr;
+ HGeometryRenderer geometryRendererHandle = entity->componentHandle<GeometryRenderer>();
+
+ // There is a geometry renderer with geometry
+ if ((geometryRenderer = m_manager->geometryRendererManager()->data(geometryRendererHandle))
+ != nullptr
+ && geometryRenderer->isEnabled() && !geometryRenderer->geometryId().isNull()) {
+
+ const Qt3DCore::QNodeId materialComponentId = entity->componentUuid<Material>();
+ const HMaterial materialHandle = entity->componentHandle<Material>();
+ const QVector<RenderPassParameterData> renderPassData =
+ m_parameters.value(materialComponentId);
+
+ HGeometry geometryHandle =
+ m_manager->geometryManager()->lookupHandle(geometryRenderer->geometryId());
+ Geometry *geometry = m_manager->geometryManager()->data(geometryHandle);
+
+ if (geometry == nullptr)
+ continue;
+
+ // 1 RenderCommand per RenderPass pass on an Entity with a Mesh
+ for (const RenderPassParameterData &passData : renderPassData) {
+ // Add the RenderPass Parameters
+ RenderCommand command = {};
+ command.m_geometryRenderer = geometryRendererHandle;
+ command.m_geometry = geometryHandle;
+
+ command.m_material = materialHandle;
+ // For RenderPass based states we use the globally set RenderState
+ // if no renderstates are defined as part of the pass. That means:
+ // RenderPass { renderStates: [] } will use the states defined by
+ // StateSet in the FrameGraph
+ RenderPass *pass = passData.pass;
+ if (pass->hasRenderStates()) {
+ command.m_stateSet = RenderStateSetPtr::create();
+ addStatesToRenderStateSet(command.m_stateSet.data(), pass->renderStates(),
+ m_manager->renderStateManager());
+ if (m_stateSet != nullptr)
+ command.m_stateSet->merge(m_stateSet);
+ command.m_changeCost =
+ m_renderer->defaultRenderState()->changeCost(command.m_stateSet.data());
+ }
+ command.m_shaderId = pass->shaderProgram();
+ command.m_rhiShader = rhiShaderManager->lookupResource(command.m_shaderId);
+
+ // It takes two frames to have a valid command as we can only
+ // reference a glShader at frame n if it has been loaded at frame n - 1
+
+ if (!command.m_shaderId)
+ continue;
+
+ { // Scoped to show extent
+
+ // Update the draw command with what's going to be needed for the drawing
+ int primitiveCount = geometryRenderer->vertexCount();
+ int estimatedCount = 0;
+ Attribute *indexAttribute = nullptr;
+ Attribute *indirectAttribute = nullptr;
+
+ const QVector<Qt3DCore::QNodeId> attributeIds = geometry->attributes();
+ for (Qt3DCore::QNodeId attributeId : attributeIds) {
+ Attribute *attribute =
+ m_manager->attributeManager()->lookupResource(attributeId);
+ switch (attribute->attributeType()) {
+ case QAttribute::IndexAttribute:
+ indexAttribute = attribute;
+ break;
+ case QAttribute::DrawIndirectAttribute:
+ indirectAttribute = attribute;
+ break;
+ case QAttribute::VertexAttribute:
+ estimatedCount = std::max(int(attribute->count()), estimatedCount);
+ break;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+ }
+
+ command.m_drawIndexed = (indexAttribute != nullptr);
+ command.m_drawIndirect = (indirectAttribute != nullptr);
+
+ // Update the draw command with all the information required for the drawing
+ if (command.m_drawIndexed) {
+ command.m_indexAttributeDataType = indexAttribute->vertexBaseType();
+ command.m_indexAttributeByteOffset = indexAttribute->byteOffset()
+ + geometryRenderer->indexBufferByteOffset();
+ }
+
+ // Note: we only care about the primitiveCount when using direct draw calls
+ // For indirect draw calls it is assumed the buffer was properly set already
+ if (command.m_drawIndirect) {
+ command.m_indirectAttributeByteOffset = indirectAttribute->byteOffset();
+ command.m_indirectDrawBuffer = m_manager->bufferManager()->lookupHandle(
+ indirectAttribute->bufferId());
+ } else {
+ // Use the count specified by the GeometryRender
+ // If not specify use the indexAttribute count if present
+ // Otherwise tries to use the count from the attribute with the highest
+ // count
+ if (primitiveCount == 0) {
+ if (indexAttribute)
+ primitiveCount = indexAttribute->count();
+ else
+ primitiveCount = estimatedCount;
+ }
+ }
+
+ command.m_primitiveCount = primitiveCount;
+ command.m_primitiveType = geometryRenderer->primitiveType();
+ command.m_primitiveRestartEnabled = geometryRenderer->primitiveRestartEnabled();
+ command.m_restartIndexValue = geometryRenderer->restartIndexValue();
+ command.m_firstInstance = geometryRenderer->firstInstance();
+ command.m_instanceCount = geometryRenderer->instanceCount();
+ command.m_firstVertex = geometryRenderer->firstVertex();
+ command.m_indexOffset = geometryRenderer->indexOffset();
+ command.m_verticesPerPatch = geometryRenderer->verticesPerPatch();
+ } // scope
+
+ commands.push_back(entity, std::move(command), passData);
+ }
+ }
+ }
+
+ return commands;
+}
+
+EntityRenderCommandData RenderView::buildComputeRenderCommands(const QVector<Entity *> &entities,
+ int offset, int count) const
+{
+ // If the RenderView contains only a ComputeDispatch then it cares about
+ // A ComputeDispatch is also implicitely a NoDraw operation
+ // enabled flag
+ // layer component
+ // material/effect/technique/parameters/filters/
+ EntityRenderCommandData commands;
+ RHIShaderManager *rhiShaderManager = m_renderer->rhiResourceManagers()->rhiShaderManager();
+
+ commands.reserve(count);
+
+ for (int i = 0; i < count; ++i) {
+ const int idx = offset + i;
+ Entity *entity = entities.at(idx);
+ ComputeCommand *computeJob = nullptr;
+ HComputeCommand computeCommandHandle = entity->componentHandle<ComputeCommand>();
+ if ((computeJob = nodeManagers()->computeJobManager()->data(computeCommandHandle))
+ != nullptr
+ && computeJob->isEnabled()) {
+
+ const Qt3DCore::QNodeId materialComponentId = entity->componentUuid<Material>();
+ const QVector<RenderPassParameterData> &renderPassData =
+ m_parameters.value(materialComponentId);
+
+ // 1 RenderCommand per RenderPass pass on an Entity with a Mesh
+ for (const RenderPassParameterData &passData : renderPassData) {
+ // Add the RenderPass Parameters
+ RenderCommand command = {};
+ RenderPass *pass = passData.pass;
+
+ if (pass->hasRenderStates()) {
+ command.m_stateSet = RenderStateSetPtr::create();
+ addStatesToRenderStateSet(command.m_stateSet.data(), pass->renderStates(),
+ m_manager->renderStateManager());
+
+ // Merge per pass stateset with global stateset
+ // so that the local stateset only overrides
+ if (m_stateSet != nullptr)
+ command.m_stateSet->merge(m_stateSet);
+ command.m_changeCost =
+ m_renderer->defaultRenderState()->changeCost(command.m_stateSet.data());
+ }
+ command.m_shaderId = pass->shaderProgram();
+ command.m_rhiShader = rhiShaderManager->lookupResource(command.m_shaderId);
+
+ // It takes two frames to have a valid command as we can only
+ // reference a glShader at frame n if it has been loaded at frame n - 1
+ assert(command.m_rhiShader);
+ if (!command.m_rhiShader)
+ continue;
+
+ command.m_computeCommand = computeCommandHandle;
+ command.m_type = RenderCommand::Compute;
+ command.m_workGroups[0] = std::max(m_workGroups[0], computeJob->x());
+ command.m_workGroups[1] = std::max(m_workGroups[1], computeJob->y());
+ command.m_workGroups[2] = std::max(m_workGroups[2], computeJob->z());
+
+ commands.push_back(entity, std::move(command), passData);
+ }
+ }
+ }
+
+ return commands;
+}
+
+void RenderView::updateRenderCommand(EntityRenderCommandData *renderCommandData, int offset,
+ int count)
+{
+ // Note: since many threads can be building render commands
+ // we need to ensure that the UniformBlockValueBuilder they are using
+ // is only accessed from the same thread
+ UniformBlockValueBuilder *builder = new UniformBlockValueBuilder();
+ builder->shaderDataManager = m_manager->shaderDataManager();
+ builder->textureManager = m_manager->textureManager();
+ m_localData.setLocalData(builder);
+
+ // Update RenderViewUBO (Qt3D standard uniforms)
+ const bool yIsUp = m_renderer->submissionContext()->rhi()->isYUpInFramebuffer();
+ const Matrix4x4 projectionMatrix = getProjectionMatrix(m_data.m_renderCameraLens, yIsUp);
+ const Matrix4x4 inverseViewMatrix = m_data.m_viewMatrix.inverted();
+ const Matrix4x4 inversedProjectionMatrix = projectionMatrix.inverted();
+ const Matrix4x4 viewProjectionMatrix = (projectionMatrix * m_data.m_viewMatrix);
+ const Matrix4x4 inversedViewProjectionMatrix = viewProjectionMatrix.inverted();
+ {
+ memcpy(&m_renderViewUBO.viewMatrix, &m_data.m_viewMatrix, sizeof(Matrix4x4));
+ memcpy(&m_renderViewUBO.projectionMatrix, &projectionMatrix, sizeof(Matrix4x4));
+ memcpy(&m_renderViewUBO.viewProjectionMatrix, &viewProjectionMatrix, sizeof(Matrix4x4));
+ memcpy(&m_renderViewUBO.inverseViewMatrix, &inverseViewMatrix, sizeof(Matrix4x4));
+ memcpy(&m_renderViewUBO.inverseProjectionMatrix, &inversedProjectionMatrix,
+ sizeof(Matrix4x4));
+ memcpy(&m_renderViewUBO.inverseViewProjectionMatrix, &inversedViewProjectionMatrix,
+ sizeof(Matrix4x4));
+ {
+ QMatrix4x4 viewportMatrix;
+ // TO DO: Implement on Matrix4x4
+ viewportMatrix.viewport(resolveViewport(m_viewport, m_surfaceSize));
+ Matrix4x4 vpMatrix(viewportMatrix);
+ Matrix4x4 invVpMatrix = vpMatrix.inverted();
+ memcpy(&m_renderViewUBO.viewportMatrix, &vpMatrix, sizeof(Matrix4x4));
+ memcpy(&m_renderViewUBO.inverseViewportMatrix, &invVpMatrix, sizeof(Matrix4x4));
+ }
+ memcpy(&m_renderViewUBO.textureTransformMatrix, m_renderer->textureTransform(),
+ sizeof(float) * 4);
+
+ memcpy(&m_renderViewUBO.eyePosition, &m_data.m_eyePos, sizeof(float) * 3);
+ const float ratio =
+ float(m_surfaceSize.width()) / std::max(1.f, float(m_surfaceSize.height()));
+ memcpy(&m_renderViewUBO.aspectRatio, &ratio, sizeof(float));
+ memcpy(&m_renderViewUBO.gamma, &m_gamma, sizeof(float));
+ const float exposure =
+ m_data.m_renderCameraLens ? m_data.m_renderCameraLens->exposure() : 0.0f;
+ memcpy(&m_renderViewUBO.exposure, &exposure, sizeof(float));
+ const float timeValue = float(m_renderer->time() / 1000000000.0f);
+ memcpy(&m_renderViewUBO.time, &timeValue, sizeof(float));
+ }
+
+ for (int i = 0, m = count; i < m; ++i) {
+ const int idx = offset + i;
+ Entity *entity = renderCommandData->entities.at(idx);
+ const RenderPassParameterData passData = renderCommandData->passesData.at(idx);
+ RenderCommand &command = renderCommandData->commands[idx];
+
+ // Pick which lights to take in to account.
+ // For now decide based on the distance by taking the MAX_LIGHTS closest lights.
+ // Replace with more sophisticated mechanisms later.
+ // Copy vector so that we can sort it concurrently and we only want to sort the one for the
+ // current command
+ QVector<LightSource> lightSources;
+ EnvironmentLight *environmentLight = nullptr;
+
+ if (command.m_type == RenderCommand::Draw) {
+ // Project the camera-to-object-center vector onto the camera
+ // view vector. This gives a depth value suitable as the key
+ // for BackToFront sorting.
+ command.m_depth = Vector3D::dotProduct(
+ entity->worldBoundingVolume()->center() - m_data.m_eyePos, m_data.m_eyeViewDir);
+
+ environmentLight = m_environmentLight;
+ lightSources = m_lightSources;
+
+ if (lightSources.size() > 1) {
+ const Vector3D entityCenter = entity->worldBoundingVolume()->center();
+ std::sort(lightSources.begin(), lightSources.end(),
+ [&](const LightSource &a, const LightSource &b) {
+ const float distA = entityCenter.distanceToPoint(
+ a.entity->worldBoundingVolume()->center());
+ const float distB = entityCenter.distanceToPoint(
+ b.entity->worldBoundingVolume()->center());
+ return distA < distB;
+ });
+ }
+ lightSources = lightSources.mid(0, std::max(lightSources.size(), MAX_LIGHTS));
+ } else { // Compute
+ // Note: if frameCount has reached 0 in the previous frame, isEnabled
+ // would be false
+ ComputeCommand *computeJob =
+ m_manager->computeJobManager()->data(command.m_computeCommand);
+ if (computeJob->runType() == QComputeCommand::Manual)
+ computeJob->updateFrameCount();
+ }
+
+ ParameterInfoList globalParameters = passData.parameterInfo;
+ // setShaderAndUniforms can initialize a localData
+ // make sure this is cleared before we leave this function
+
+ setShaderAndUniforms(&command, globalParameters, entity, lightSources, environmentLight);
+
+ // Update CommandUBO (Qt3D standard uniforms)
+ const Matrix4x4 worldTransform = *(entity->worldTransform());
+ const Matrix4x4 inverseWorldTransform = worldTransform.inverted();
+ const QMatrix3x3 modelNormalMatrix = convertToQMatrix4x4(worldTransform).normalMatrix();
+ const Matrix4x4 modelViewMatrix = m_data.m_viewMatrix * worldTransform;
+ const Matrix4x4 inverseModelViewMatrix = modelViewMatrix.inverted();
+ const Matrix4x4 mvp = projectionMatrix * modelViewMatrix;
+ const Matrix4x4 inverseModelViewProjection = mvp.inverted();
+ {
+ memcpy(&command.m_commandUBO.modelMatrix, &worldTransform, sizeof(Matrix4x4));
+ memcpy(&command.m_commandUBO.inverseModelMatrix, &inverseWorldTransform,
+ sizeof(Matrix4x4));
+ memcpy(&command.m_commandUBO.modelViewMatrix, &modelViewMatrix, sizeof(Matrix4x4));
+ {
+ float(&normal)[12] = command.m_commandUBO.modelNormalMatrix;
+ normal[0] = modelNormalMatrix.constData()[0 * 3 + 0];
+ normal[1] = modelNormalMatrix.constData()[0 * 3 + 1];
+ normal[2] = modelNormalMatrix.constData()[0 * 3 + 2];
+
+ normal[4] = modelNormalMatrix.constData()[1 * 3 + 0];
+ normal[5] = modelNormalMatrix.constData()[1 * 3 + 1];
+ normal[6] = modelNormalMatrix.constData()[1 * 3 + 2];
+
+ normal[8] = modelNormalMatrix.constData()[2 * 3 + 0];
+ normal[9] = modelNormalMatrix.constData()[2 * 3 + 1];
+ normal[10] = modelNormalMatrix.constData()[2 * 3 + 2];
+ }
+ memcpy(&command.m_commandUBO.inverseModelViewMatrix, &inverseModelViewMatrix,
+ sizeof(Matrix4x4));
+ memcpy(&command.m_commandUBO.mvp, &mvp, sizeof(Matrix4x4));
+ memcpy(&command.m_commandUBO.inverseModelViewProjectionMatrix,
+ &inverseModelViewProjection, sizeof(Matrix4x4));
+ }
+ }
+
+ // We reset the local data once we are done with it
+ m_localData.setLocalData(nullptr);
+}
+
+void RenderView::updateMatrices()
+{
+ if (m_data.m_renderCameraNode && m_data.m_renderCameraLens
+ && m_data.m_renderCameraLens->isEnabled()) {
+ const Matrix4x4 cameraWorld = *(m_data.m_renderCameraNode->worldTransform());
+ setViewMatrix(m_data.m_renderCameraLens->viewMatrix(cameraWorld));
+
+ setViewProjectionMatrix(m_data.m_renderCameraLens->projection() * viewMatrix());
+ // To get the eyePosition of the camera, we need to use the inverse of the
+ // camera's worldTransform matrix.
+ const Matrix4x4 inverseWorldTransform = viewMatrix().inverted();
+ const Vector3D eyePosition(inverseWorldTransform.column(3));
+ setEyePosition(eyePosition);
+
+ // Get the viewing direction of the camera. Use the normal matrix to
+ // ensure non-uniform scale works too.
+ const QMatrix3x3 normalMat = convertToQMatrix4x4(m_data.m_viewMatrix).normalMatrix();
+ // dir = normalize(QVector3D(0, 0, -1) * normalMat)
+ setEyeViewDirection(
+ Vector3D(-normalMat(2, 0), -normalMat(2, 1), -normalMat(2, 2)).normalized());
+ }
+}
+
+void RenderView::setUniformValue(ShaderParameterPack &uniformPack, int nameId,
+ const UniformValue &value) const
+{
+ // At this point a uniform value can only be a scalar type
+ // or a Qt3DCore::QNodeId corresponding to a Texture or Image
+ // ShaderData/Buffers would be handled as UBO/SSBO and would therefore
+ // not be in the default uniform block
+ if (value.valueType() == UniformValue::NodeId) {
+ const Qt3DCore::QNodeId *nodeIds = value.constData<Qt3DCore::QNodeId>();
+
+ const int uniformArraySize = value.byteSize() / sizeof(Qt3DCore::QNodeId);
+ UniformValue::ValueType resourceType = UniformValue::TextureValue;
+
+ for (int i = 0; i < uniformArraySize; ++i) {
+ const Qt3DCore::QNodeId resourceId = nodeIds[i];
+
+ const Texture *tex = m_manager->textureManager()->lookupResource(resourceId);
+ if (tex != nullptr) {
+ uniformPack.setTexture(nameId, i, resourceId);
+ } else {
+ const ShaderImage *img =
+ m_manager->shaderImageManager()->lookupResource(resourceId);
+ if (img != nullptr) {
+ resourceType = UniformValue::ShaderImageValue;
+ uniformPack.setImage(nameId, i, resourceId);
+ }
+ }
+ }
+
+ // This uniform will be overridden in SubmissionContext::setParameters
+ // and -1 values will be replaced by valid Texture or Image units
+ UniformValue uniformValue(uniformArraySize * sizeof(int), resourceType);
+ std::fill(uniformValue.data<int>(), uniformValue.data<int>() + uniformArraySize, -1);
+ uniformPack.setUniform(nameId, uniformValue);
+ } else {
+ uniformPack.setUniform(nameId, value);
+ }
+}
+
+void RenderView::setUniformBlockValue(ShaderParameterPack &uniformPack, const RHIShader *shader,
+ const ShaderUniformBlock &block,
+ const UniformValue &value) const
+{
+ Q_UNUSED(shader)
+
+ if (value.valueType() == UniformValue::NodeId) {
+
+ Buffer *buffer = nullptr;
+ if ((buffer = m_manager->bufferManager()->lookupResource(
+ *value.constData<Qt3DCore::QNodeId>()))
+ != nullptr) {
+ BlockToUBO uniformBlockUBO;
+ uniformBlockUBO.m_blockIndex = block.m_index;
+ uniformBlockUBO.m_bufferID = buffer->peerId();
+ uniformBlockUBO.m_needsUpdate = false;
+ uniformPack.setUniformBuffer(std::move(uniformBlockUBO));
+ // Buffer update to GL buffer will be done at render time
+ }
+ }
+}
+
+void RenderView::setShaderStorageValue(ShaderParameterPack &uniformPack, const RHIShader *shader,
+ const ShaderStorageBlock &block,
+ const UniformValue &value) const
+{
+ Q_UNUSED(shader)
+ if (value.valueType() == UniformValue::NodeId) {
+ Buffer *buffer = nullptr;
+ if ((buffer = m_manager->bufferManager()->lookupResource(
+ *value.constData<Qt3DCore::QNodeId>()))
+ != nullptr) {
+ BlockToSSBO shaderStorageBlock;
+ shaderStorageBlock.m_blockIndex = block.m_index;
+ shaderStorageBlock.m_bufferID = buffer->peerId();
+ shaderStorageBlock.m_bindingIndex = block.m_binding;
+ uniformPack.setShaderStorageBuffer(shaderStorageBlock);
+ // Buffer update to GL buffer will be done at render time
+ }
+ }
+}
+
+void RenderView::setDefaultUniformBlockShaderDataValue(ShaderParameterPack &uniformPack,
+ const RHIShader *shader,
+ const ShaderData *shaderData,
+ const QString &structName) const
+{
+ UniformBlockValueBuilder *builder = m_localData.localData();
+ builder->activeUniformNamesToValue.clear();
+
+ // Set the view matrix to be used to transform "Transformed" properties in the ShaderData
+ builder->viewMatrix = m_data.m_viewMatrix;
+ // Force to update the whole block
+ builder->updatedPropertiesOnly = false;
+ // Retrieve names and description of each active uniforms in the uniform block
+ builder->uniforms = shader->unqualifiedUniformNames();
+ // Build name-value map for the block
+ builder->buildActiveUniformNameValueMapStructHelper(shaderData, structName);
+ // Set uniform values for each entrie of the block name-value map
+ QHash<int, QVariant>::const_iterator activeValuesIt =
+ builder->activeUniformNamesToValue.constBegin();
+ const QHash<int, QVariant>::const_iterator activeValuesEnd =
+ builder->activeUniformNamesToValue.constEnd();
+
+ // TO DO: Make the ShaderData store UniformValue
+ while (activeValuesIt != activeValuesEnd) {
+ setUniformValue(uniformPack, activeValuesIt.key(),
+ UniformValue::fromVariant(activeValuesIt.value()));
+ ++activeValuesIt;
+ }
+}
+
+void RenderView::applyParameter(const Parameter *param, RenderCommand *command,
+ const RHIShader *shader) const noexcept
+{
+ const int nameId = param->nameId();
+ const UniformValue &uniformValue = param->uniformValue();
+ switch (shader->categorizeVariable(nameId)) {
+ case RHIShader::Uniform: {
+ setUniformValue(command->m_parameterPack, nameId, uniformValue);
+ break;
+ }
+ case RHIShader::UBO: {
+ setUniformBlockValue(command->m_parameterPack, shader,
+ shader->uniformBlockForBlockNameId(nameId), uniformValue);
+ break;
+ }
+ case RHIShader::SSBO: {
+ setShaderStorageValue(command->m_parameterPack, shader,
+ shader->storageBlockForBlockNameId(nameId), uniformValue);
+ break;
+ }
+ case RHIShader::Struct: {
+ ShaderData *shaderData = nullptr;
+ if (uniformValue.valueType() == UniformValue::NodeId
+ && (shaderData = m_manager->shaderDataManager()->lookupResource(
+ *uniformValue.constData<Qt3DCore::QNodeId>()))
+ != nullptr) {
+ // Try to check if we have a struct or array matching a QShaderData parameter
+ setDefaultUniformBlockShaderDataValue(command->m_parameterPack, shader, shaderData,
+ StringToInt::lookupString(nameId));
+ }
+ break;
+ }
+ }
+}
+
+void RenderView::setShaderAndUniforms(RenderCommand *command, ParameterInfoList &parameters,
+ Entity *entity,
+ const QVector<LightSource> &activeLightSources,
+ EnvironmentLight *environmentLight) const
+{
+ // The VAO Handle is set directly in the renderer thread so as to avoid having to use a mutex
+ // here Set shader, technique, and effect by basically doing :
+ // ShaderProgramManager[MaterialManager[frontentEntity->id()]->Effect->Techniques[TechniqueFilter->name]->RenderPasses[RenderPassFilter->name]];
+ // The Renderer knows that if one of those is null, a default material / technique / effect as
+ // to be used
+
+ // Find all RenderPasses (in order) matching values set in the RenderPassFilter
+ // Get list of parameters for the Material, Effect, and Technique
+ // For each ParameterBinder in the RenderPass -> create a QUniformPack
+ // Once that works, improve that to try and minimize QUniformPack updates
+
+ RHIShader *shader = command->m_rhiShader;
+ if (shader != nullptr && shader->isLoaded()) {
+
+ // Builds the QUniformPack, sets shader standard uniforms and store attributes name / glname
+ // bindings If a parameter is defined and not found in the bindings it is assumed to be a
+ // binding of Uniform type with the glsl name equals to the parameter name
+
+ // Set fragData Name and index
+ // Later on we might want to relink the shader if attachments have changed
+ // But for now we set them once and for all
+ if (!m_renderTarget.isNull() && !shader->isLoaded()) {
+ QHash<QString, int> fragOutputs;
+ const auto atts = m_attachmentPack.attachments();
+ for (const Attachment &att : atts) {
+ if (att.m_point <= QRenderTargetOutput::Color15)
+ fragOutputs.insert(att.m_name, att.m_point);
+ }
+ // Set frag outputs in the shaders if hash not empty
+ if (!fragOutputs.isEmpty())
+ shader->setFragOutputs(fragOutputs);
+ }
+
+ if (shader->hasActiveVariables()) {
+
+ // Unlike the GL engine, the standard uniforms are set a bit before this function,
+ // in RenderView::updateRenderCommand
+
+ // Set default attributes
+ command->m_activeAttributes = shader->attributeNamesIds();
+
+ // At this point we know whether the command is a valid draw command or not
+ // We still need to process the uniforms as the command could be a compute command
+ command->m_isValid = !command->m_activeAttributes.empty();
+
+ // Parameters remaining could be
+ // -> uniform scalar / vector
+ // -> uniform struct / arrays
+ // -> uniform block / array (4.3)
+ // -> ssbo block / array (4.3)
+
+ ParameterInfoList::const_iterator it = parameters.cbegin();
+ const ParameterInfoList::const_iterator parametersEnd = parameters.cend();
+
+ while (it != parametersEnd) {
+ const Parameter *param = m_manager->data<Parameter, ParameterManager>(it->handle);
+ applyParameter(param, command, shader);
+ ++it;
+ }
+
+ // Lights
+ int lightIdx = 0;
+ for (const LightSource &lightSource : activeLightSources) {
+ if (lightIdx == MAX_LIGHTS)
+ break;
+ Entity *lightEntity = lightSource.entity;
+ const Matrix4x4 lightWorldTransform = *(lightEntity->worldTransform());
+ const Vector3D worldPos = lightWorldTransform * Vector3D(0.0f, 0.0f, 0.0f);
+ for (Light *light : lightSource.lights) {
+ if (!light->isEnabled())
+ continue;
+
+ ShaderData *shaderData =
+ m_manager->shaderDataManager()->lookupResource(light->shaderData());
+ if (!shaderData)
+ continue;
+
+ if (lightIdx == MAX_LIGHTS)
+ break;
+
+ // Note: implicit conversion of values to UniformValue
+ setUniformValue(command->m_parameterPack, LIGHT_POSITION_NAMES[lightIdx],
+ worldPos);
+ setUniformValue(command->m_parameterPack, LIGHT_TYPE_NAMES[lightIdx],
+ int(QAbstractLight::PointLight));
+ setUniformValue(command->m_parameterPack, LIGHT_COLOR_NAMES[lightIdx],
+ Vector3D(1.0f, 1.0f, 1.0f));
+ setUniformValue(command->m_parameterPack, LIGHT_INTENSITY_NAMES[lightIdx],
+ 0.5f);
+
+ // There is no risk in doing that even if multithreaded
+ // since we are sure that a shaderData is unique for a given light
+ // and won't ever be referenced as a Component either
+ Matrix4x4 *worldTransform = lightEntity->worldTransform();
+ if (worldTransform)
+ shaderData->updateWorldTransform(*worldTransform);
+
+ setDefaultUniformBlockShaderDataValue(command->m_parameterPack, shader,
+ shaderData, LIGHT_STRUCT_NAMES[lightIdx]);
+ ++lightIdx;
+ }
+ }
+
+ if (shader->hasUniform(LIGHT_COUNT_NAME_ID))
+ setUniformValue(command->m_parameterPack, LIGHT_COUNT_NAME_ID,
+ UniformValue(qMax((environmentLight ? 0 : 1), lightIdx)));
+
+ // If no active light sources and no environment light, add a default light
+ if (activeLightSources.isEmpty() && !environmentLight) {
+ // Note: implicit conversion of values to UniformValue
+ setUniformValue(command->m_parameterPack, LIGHT_POSITION_NAMES[0],
+ Vector3D(10.0f, 10.0f, 0.0f));
+ setUniformValue(command->m_parameterPack, LIGHT_TYPE_NAMES[0],
+ int(QAbstractLight::PointLight));
+ setUniformValue(command->m_parameterPack, LIGHT_COLOR_NAMES[0],
+ Vector3D(1.0f, 1.0f, 1.0f));
+ setUniformValue(command->m_parameterPack, LIGHT_INTENSITY_NAMES[0], 0.5f);
+ }
+
+ // Environment Light
+ int envLightCount = 0;
+ if (environmentLight && environmentLight->isEnabled()) {
+ static const int irradianceId =
+ StringToInt::lookupId(QLatin1String("envLight_irradiance"));
+ static const int specularId =
+ StringToInt::lookupId(QLatin1String("envLight_specular"));
+ ShaderData *shaderData = m_manager->shaderDataManager()->lookupResource(
+ environmentLight->shaderData());
+ if (shaderData) {
+ envLightCount = 1;
+
+ // ("specularSize", "irradiance", "irradianceSize", "specular")
+ auto irr =
+ shaderData->properties()["irradiance"].value.value<Qt3DCore::QNodeId>();
+ auto spec =
+ shaderData->properties()["specular"].value.value<Qt3DCore::QNodeId>();
+
+ setUniformValue(command->m_parameterPack, irradianceId, irr);
+ setUniformValue(command->m_parameterPack, specularId, spec);
+ }
+ }
+ setUniformValue(command->m_parameterPack,
+ StringToInt::lookupId(QStringLiteral("envLightCount")), envLightCount);
+ }
+ }
+}
+
+bool RenderView::hasBlitFramebufferInfo() const
+{
+ return m_hasBlitFramebufferInfo;
+}
+
+void RenderView::setHasBlitFramebufferInfo(bool hasBlitFramebufferInfo)
+{
+ m_hasBlitFramebufferInfo = hasBlitFramebufferInfo;
+}
+
+BlitFramebufferInfo RenderView::blitFrameBufferInfo() const
+{
+ return m_blitFrameBufferInfo;
+}
+
+void RenderView::setBlitFrameBufferInfo(const BlitFramebufferInfo &blitFrameBufferInfo)
+{
+ m_blitFrameBufferInfo = blitFrameBufferInfo;
+}
+
+bool RenderView::isDownloadBuffersEnable() const
+{
+ return m_isDownloadBuffersEnable;
+}
+
+void RenderView::setIsDownloadBuffersEnable(bool isDownloadBuffersEnable)
+{
+ m_isDownloadBuffersEnable = isDownloadBuffersEnable;
+}
+
+} // namespace Rhi
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
diff --git a/src/plugins/renderers/rhi/renderer/renderview_p.h b/src/plugins/renderers/rhi/renderer/renderview_p.h
new file mode 100644
index 000000000..3206b6e66
--- /dev/null
+++ b/src/plugins/renderers/rhi/renderer/renderview_p.h
@@ -0,0 +1,455 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
+** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 QT3DRENDER_RENDER_RHI_RENDERVIEW_H
+#define QT3DRENDER_RENDER_RHI_RENDERVIEW_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <Qt3DRender/qparameter.h>
+#include <Qt3DRender/qclearbuffers.h>
+#include <Qt3DRender/qlayerfilter.h>
+#include <Qt3DRender/private/clearbuffers_p.h>
+#include <Qt3DRender/private/cameralens_p.h>
+#include <Qt3DRender/private/attachmentpack_p.h>
+#include <Qt3DRender/private/handle_types_p.h>
+#include <Qt3DRender/private/qsortpolicy_p.h>
+#include <Qt3DRender/private/lightsource_p.h>
+#include <Qt3DRender/private/qmemorybarrier_p.h>
+#include <Qt3DRender/private/qrendercapture_p.h>
+#include <Qt3DRender/private/qblitframebuffer_p.h>
+#include <Qt3DRender/private/qwaitfence_p.h>
+
+#include <Qt3DCore/private/qframeallocator_p.h>
+#include <Qt3DRender/private/aligned_malloc_p.h>
+
+#include <renderer_p.h>
+// TODO: Move out once this is all refactored
+#include <renderviewjobutils_p.h>
+
+#include <QVector>
+#include <QSurface>
+#include <QMutex>
+#include <QColor>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+class QRenderPass;
+
+namespace Render {
+
+class NodeManagers;
+class RenderPassFilter;
+class TechniqueFilter;
+class ViewportNode;
+class Effect;
+class RenderPass;
+
+namespace Rhi {
+
+class Renderer;
+class RenderCommand;
+
+typedef QPair<ShaderUniform, QVariant> ActivePropertyContent;
+typedef QPair<QString, ActivePropertyContent> ActiveProperty;
+
+struct Q_AUTOTEST_EXPORT ClearBufferInfo
+{
+ int drawBufferIndex = 0;
+ QRenderTargetOutput::AttachmentPoint attchmentPoint = QRenderTargetOutput::Color0;
+ QVector4D clearColor;
+};
+
+struct Q_AUTOTEST_EXPORT BlitFramebufferInfo
+{
+ Qt3DCore::QNodeId sourceRenderTargetId;
+ Qt3DCore::QNodeId destinationRenderTargetId;
+ QRect sourceRect;
+ QRect destinationRect;
+ Qt3DRender::QRenderTargetOutput::AttachmentPoint sourceAttachmentPoint;
+ Qt3DRender::QRenderTargetOutput::AttachmentPoint destinationAttachmentPoint;
+ QBlitFramebuffer::InterpolationMethod interpolationMethod;
+};
+
+// This class is kind of analogous to RenderBin but I want to avoid trampling
+// on that until we get this working
+
+struct RenderViewUBO
+{
+ float viewMatrix[16];
+ float projectionMatrix[16];
+ float viewProjectionMatrix[16];
+ float inverseViewMatrix[16];
+ float inverseProjectionMatrix[16];
+ float inverseViewProjectionMatrix[16];
+ float viewportMatrix[16];
+ float inverseViewportMatrix[16];
+ float textureTransformMatrix[4];
+ float eyePosition[3];
+ float aspectRatio;
+ float gamma;
+ float exposure;
+ float time;
+};
+static_assert(sizeof(RenderViewUBO) == sizeof(float) * (8 * 16 + 1 * 4 + 1 * 3 + 4 * 1),
+ "UBO doesn't match std140");
+
+class Q_AUTOTEST_EXPORT RenderView
+{
+public:
+ RenderView();
+ ~RenderView();
+
+ QT3D_ALIGNED_MALLOC_AND_FREE()
+
+ // TODO: Add a way to specify a sort predicate for the RenderCommands
+ void sort();
+
+ void setRenderer(Renderer *renderer);
+ inline void setSurfaceSize(const QSize &size) Q_DECL_NOTHROW { m_surfaceSize = size; }
+ inline Renderer *renderer() const Q_DECL_NOTHROW { return m_renderer; }
+ inline NodeManagers *nodeManagers() const Q_DECL_NOTHROW { return m_manager; }
+ inline const QSize &surfaceSize() const Q_DECL_NOTHROW { return m_surfaceSize; }
+ inline void setDevicePixelRatio(qreal r) Q_DECL_NOTHROW { m_devicePixelRatio = r; }
+ inline qreal devicePixelRatio() const Q_DECL_NOTHROW { return m_devicePixelRatio; }
+
+ inline void setRenderCameraLens(CameraLens *renderCameraLens) Q_DECL_NOTHROW
+ {
+ m_data.m_renderCameraLens = renderCameraLens;
+ }
+ inline CameraLens *renderCameraLens() const Q_DECL_NOTHROW { return m_data.m_renderCameraLens; }
+
+ inline void setRenderCameraEntity(Entity *renderCameraNode) Q_DECL_NOTHROW
+ {
+ m_data.m_renderCameraNode = renderCameraNode;
+ }
+ inline Entity *renderCameraEntity() const Q_DECL_NOTHROW { return m_data.m_renderCameraNode; }
+
+ inline void setViewMatrix(const Matrix4x4 &viewMatrix) Q_DECL_NOTHROW
+ {
+ m_data.m_viewMatrix = viewMatrix;
+ }
+ inline Matrix4x4 viewMatrix() const Q_DECL_NOTHROW { return m_data.m_viewMatrix; }
+
+ inline void setViewProjectionMatrix(const Matrix4x4 &viewProjectionMatrix) Q_DECL_NOTHROW
+ {
+ m_data.m_viewProjectionMatrix = viewProjectionMatrix;
+ }
+ inline Matrix4x4 viewProjectionMatrix() const Q_DECL_NOTHROW
+ {
+ return m_data.m_viewProjectionMatrix;
+ }
+
+ inline void setEyePosition(const Vector3D &eyePos) Q_DECL_NOTHROW { m_data.m_eyePos = eyePos; }
+ inline Vector3D eyePosition() const Q_DECL_NOTHROW { return m_data.m_eyePos; }
+
+ inline void setEyeViewDirection(const Vector3D &dir) Q_DECL_NOTHROW
+ {
+ m_data.m_eyeViewDir = dir;
+ }
+ inline Vector3D eyeViewDirection() const Q_DECL_NOTHROW { return m_data.m_eyeViewDir; }
+
+ inline void appendLayerFilter(const Qt3DCore::QNodeId layerFilterId) Q_DECL_NOTHROW
+ {
+ m_data.m_layerFilterIds.push_back(layerFilterId);
+ }
+ inline Qt3DCore::QNodeIdVector layerFilters() const Q_DECL_NOTHROW
+ {
+ return m_data.m_layerFilterIds;
+ }
+
+ inline void appendProximityFilterId(const Qt3DCore::QNodeId proximityFilterId)
+ {
+ m_data.m_proximityFilterIds.push_back(proximityFilterId);
+ }
+ inline Qt3DCore::QNodeIdVector proximityFilterIds() const
+ {
+ return m_data.m_proximityFilterIds;
+ }
+
+ inline void setRenderPassFilter(const RenderPassFilter *rpFilter) Q_DECL_NOTHROW
+ {
+ m_data.m_passFilter = rpFilter;
+ }
+ inline const RenderPassFilter *renderPassFilter() const Q_DECL_NOTHROW
+ {
+ return m_data.m_passFilter;
+ }
+
+ inline void setTechniqueFilter(const TechniqueFilter *filter) Q_DECL_NOTHROW
+ {
+ m_data.m_techniqueFilter = filter;
+ }
+ inline const TechniqueFilter *techniqueFilter() const Q_DECL_NOTHROW
+ {
+ return m_data.m_techniqueFilter;
+ }
+
+ inline RenderStateSet *stateSet() const Q_DECL_NOTHROW { return m_stateSet; }
+ void setStateSet(RenderStateSet *stateSet) Q_DECL_NOTHROW { m_stateSet = stateSet; }
+
+ inline bool noDraw() const Q_DECL_NOTHROW { return m_noDraw; }
+ void setNoDraw(bool noDraw) Q_DECL_NOTHROW { m_noDraw = noDraw; }
+
+ inline bool isCompute() const Q_DECL_NOTHROW { return m_compute; }
+ void setCompute(bool compute) Q_DECL_NOTHROW { m_compute = compute; }
+
+ void setComputeWorkgroups(int x, int y, int z) Q_DECL_NOTHROW
+ {
+ m_workGroups[0] = x;
+ m_workGroups[1] = y;
+ m_workGroups[2] = z;
+ }
+ const int *computeWorkGroups() const Q_DECL_NOTHROW { return m_workGroups; }
+ inline bool frustumCulling() const Q_DECL_NOTHROW { return m_frustumCulling; }
+ void setFrustumCulling(bool frustumCulling) Q_DECL_NOTHROW
+ {
+ m_frustumCulling = frustumCulling;
+ }
+
+ inline void
+ setMaterialParameterTable(const MaterialParameterGathererData &parameters) Q_DECL_NOTHROW
+ {
+ m_parameters = parameters;
+ }
+
+ // TODO: Get rid of this overly complex memory management by splitting out the
+ // InnerData as a RenderViewConfig struct. This can be created by
+ // setRenderViewConfigFromFrameGraphLeafNode and passed along with the RenderView to the
+ // functions that populate the renderview
+ inline void setViewport(const QRectF &vp) Q_DECL_NOTHROW { m_viewport = vp; }
+ inline QRectF viewport() const Q_DECL_NOTHROW { return m_viewport; }
+
+ inline float gamma() const Q_DECL_NOTHROW { return m_gamma; }
+ inline void setGamma(float gamma) Q_DECL_NOTHROW { m_gamma = gamma; }
+
+ // depth and stencil ClearBuffers are cached locally
+ // color ClearBuffers are collected, as there may be multiple
+ // color buffers to be cleared. we need to apply all these at rendering
+ void addClearBuffers(const ClearBuffers *cb);
+ inline QVector<ClearBufferInfo> specificClearColorBufferInfo() const
+ {
+ return m_specificClearColorBuffers;
+ }
+ inline QVector<ClearBufferInfo> &specificClearColorBufferInfo()
+ {
+ return m_specificClearColorBuffers;
+ }
+ inline ClearBufferInfo globalClearColorBufferInfo() const { return m_globalClearColorBuffer; }
+
+ inline QClearBuffers::BufferTypeFlags clearTypes() const { return m_clearBuffer; }
+ inline float clearDepthValue() const { return m_clearDepthValue; }
+ inline int clearStencilValue() const { return m_clearStencilValue; }
+
+ inline const RenderViewUBO *renderViewUBO() const { return &m_renderViewUBO; }
+
+ RenderPassList passesAndParameters(ParameterInfoList *parameter, Entity *node,
+ bool useDefaultMaterials = true);
+
+ EntityRenderCommandData buildDrawRenderCommands(const QVector<Entity *> &entities, int offset,
+ int count) const;
+ EntityRenderCommandData buildComputeRenderCommands(const QVector<Entity *> &entities,
+ int offset, int count) const;
+
+ void updateRenderCommand(EntityRenderCommandData *renderCommandData, int offset, int count);
+
+ void setCommands(const QVector<RenderCommand> &commands) Q_DECL_NOTHROW
+ {
+ m_commands = commands;
+ }
+ QVector<RenderCommand> &commands() { return m_commands; }
+ QVector<RenderCommand> commands() const { return m_commands; }
+
+ void setAttachmentPack(const AttachmentPack &pack) { m_attachmentPack = pack; }
+ const AttachmentPack &attachmentPack() const { return m_attachmentPack; }
+
+ void setRenderTargetId(Qt3DCore::QNodeId renderTargetId) Q_DECL_NOTHROW
+ {
+ m_renderTarget = renderTargetId;
+ }
+ Qt3DCore::QNodeId renderTargetId() const Q_DECL_NOTHROW { return m_renderTarget; }
+
+ void addSortType(const QVector<Qt3DRender::QSortPolicy::SortType> &sortTypes)
+ {
+ m_data.m_sortingTypes.append(sortTypes);
+ }
+
+ void setSurface(QSurface *surface) { m_surface = surface; }
+ QSurface *surface() const { return m_surface; }
+
+ void setLightSources(const QVector<LightSource> &lightSources) Q_DECL_NOTHROW
+ {
+ m_lightSources = lightSources;
+ }
+ void setEnvironmentLight(EnvironmentLight *environmentLight) Q_DECL_NOTHROW
+ {
+ m_environmentLight = environmentLight;
+ }
+
+ void updateMatrices();
+
+ inline void setRenderCaptureNodeId(const Qt3DCore::QNodeId nodeId) Q_DECL_NOTHROW
+ {
+ m_renderCaptureNodeId = nodeId;
+ }
+ inline const Qt3DCore::QNodeId renderCaptureNodeId() const Q_DECL_NOTHROW
+ {
+ return m_renderCaptureNodeId;
+ }
+ inline void setRenderCaptureRequest(const QRenderCaptureRequest &request) Q_DECL_NOTHROW
+ {
+ m_renderCaptureRequest = request;
+ }
+ inline const QRenderCaptureRequest renderCaptureRequest() const Q_DECL_NOTHROW
+ {
+ return m_renderCaptureRequest;
+ }
+
+ // Helps making the size of RenderView smaller
+ // Contains all the data needed for the actual building of the RenderView
+ // But that aren't used later by the Renderer
+ struct InnerData
+ {
+ InnerData()
+ : m_renderCameraLens(nullptr),
+ m_renderCameraNode(nullptr),
+ m_techniqueFilter(nullptr),
+ m_passFilter(nullptr)
+ {
+ }
+ CameraLens *m_renderCameraLens;
+ Entity *m_renderCameraNode;
+ const TechniqueFilter *m_techniqueFilter;
+ const RenderPassFilter *m_passFilter;
+ Matrix4x4 m_viewMatrix;
+ Matrix4x4 m_viewProjectionMatrix;
+ Qt3DCore::QNodeIdVector m_layerFilterIds;
+ QVector<Qt3DRender::QSortPolicy::SortType> m_sortingTypes;
+ Vector3D m_eyePos;
+ Vector3D m_eyeViewDir;
+ Qt3DCore::QNodeIdVector m_proximityFilterIds;
+ };
+
+ bool isDownloadBuffersEnable() const;
+ void setIsDownloadBuffersEnable(bool isDownloadBuffersEnable);
+
+ BlitFramebufferInfo blitFrameBufferInfo() const;
+ void setBlitFrameBufferInfo(const BlitFramebufferInfo &blitFrameBufferInfo);
+
+ bool hasBlitFramebufferInfo() const;
+ void setHasBlitFramebufferInfo(bool hasBlitFramebufferInfo);
+
+private:
+ void setShaderAndUniforms(RenderCommand *command, ParameterInfoList &parameters, Entity *entity,
+ const QVector<LightSource> &activeLightSources,
+ EnvironmentLight *environmentLight) const;
+ mutable QThreadStorage<UniformBlockValueBuilder *> m_localData;
+
+ Qt3DCore::QNodeId m_renderCaptureNodeId;
+ QRenderCaptureRequest m_renderCaptureRequest;
+ bool m_isDownloadBuffersEnable;
+
+ bool m_hasBlitFramebufferInfo;
+ BlitFramebufferInfo m_blitFrameBufferInfo;
+
+ Renderer *m_renderer;
+ NodeManagers *m_manager;
+ QSize m_surfaceSize;
+ qreal m_devicePixelRatio;
+
+ InnerData m_data;
+
+ QRectF m_viewport;
+ float m_gamma;
+ Qt3DCore::QNodeId m_renderTarget;
+ QSurface *m_surface;
+ AttachmentPack m_attachmentPack;
+ QClearBuffers::BufferTypeFlags m_clearBuffer;
+ float m_clearDepthValue;
+ int m_clearStencilValue;
+ ClearBufferInfo m_globalClearColorBuffer; // global ClearColor
+ QVector<ClearBufferInfo>
+ m_specificClearColorBuffers; // different draw buffers with distinct colors
+ RenderStateSet *m_stateSet;
+ bool m_noDraw : 1;
+ bool m_compute : 1;
+ bool m_frustumCulling : 1;
+ int m_workGroups[3];
+
+ RenderViewUBO m_renderViewUBO;
+
+ QVector<RenderCommand> m_commands;
+ mutable QVector<LightSource> m_lightSources;
+ EnvironmentLight *m_environmentLight;
+
+ MaterialParameterGathererData m_parameters;
+
+ void setUniformValue(ShaderParameterPack &uniformPack, int nameId,
+ const UniformValue &value) const;
+ void setUniformBlockValue(ShaderParameterPack &uniformPack, const RHIShader *shader,
+ const ShaderUniformBlock &block, const UniformValue &value) const;
+ void setShaderStorageValue(ShaderParameterPack &uniformPack, const RHIShader *shader,
+ const ShaderStorageBlock &block, const UniformValue &value) const;
+ void setDefaultUniformBlockShaderDataValue(ShaderParameterPack &uniformPack,
+ const RHIShader *shader,
+ const ShaderData *shaderData,
+ const QString &structName) const;
+ void applyParameter(const Parameter *param, RenderCommand *command,
+ const RHIShader *shader) const noexcept;
+};
+
+} // namespace Rhi
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDER_RHI_ENDERVIEW_H
diff --git a/src/plugins/renderers/rhi/renderer/renderviewbuilder.cpp b/src/plugins/renderers/rhi/renderer/renderviewbuilder.cpp
new file mode 100644
index 000000000..6455d2e10
--- /dev/null
+++ b/src/plugins/renderers/rhi/renderer/renderviewbuilder.cpp
@@ -0,0 +1,841 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 "renderviewbuilder_p.h"
+#include <Qt3DRender/private/qrenderaspect_p.h>
+
+#include <QThread>
+
+namespace Qt3DRender {
+
+namespace Render {
+namespace Rhi {
+
+// In some cases having less jobs is better (especially on fast cpus where
+// splitting just adds more overhead). Ideally, we should try to set the value
+// depending on the platform/CPU/nbr of cores
+const int RenderViewBuilder::m_optimalParallelJobCount = QThread::idealThreadCount();
+
+namespace {
+
+int findIdealNumberOfWorkers(int elementCount, int packetSize = 100)
+{
+ if (elementCount == 0 || packetSize == 0)
+ return 0;
+ return std::min(std::max(elementCount / packetSize, 1), RenderViewBuilder::optimalJobCount());
+}
+
+class SyncPreCommandBuilding
+{
+public:
+ explicit SyncPreCommandBuilding(
+ RenderViewInitializerJobPtr renderViewInitializerJob,
+ const QVector<RenderViewCommandBuilderJobPtr> &renderViewCommandBuilderJobs,
+ Renderer *renderer, FrameGraphNode *leafNode)
+ : m_renderViewInitializer(std::move(renderViewInitializerJob)),
+ m_renderViewCommandBuilderJobs(renderViewCommandBuilderJobs),
+ m_renderer(renderer),
+ m_leafNode(leafNode)
+ {
+ }
+
+ void operator()()
+ {
+ // Split commands to build among jobs
+ QMutexLocker lock(m_renderer->cache()->mutex());
+ // Rebuild RenderCommands for all entities in RV (ignoring filtering)
+ RendererCache *cache = m_renderer->cache();
+ const RendererCache::LeafNodeData &dataCacheForLeaf = cache->leafNodeCache[m_leafNode];
+ RenderView *rv = m_renderViewInitializer->renderView();
+ const auto entities = !rv->isCompute() ? cache->renderableEntities : cache->computeEntities;
+
+ rv->setMaterialParameterTable(dataCacheForLeaf.materialParameterGatherer);
+
+ lock.unlock();
+
+ // Split among the ideal number of command builders
+ const int idealPacketSize =
+ std::min(std::max(100, entities.size() / RenderViewBuilder::optimalJobCount()),
+ entities.size());
+ // Try to split work into an ideal number of workers
+ const int m = findIdealNumberOfWorkers(entities.size(), idealPacketSize);
+
+ for (int i = 0; i < m; ++i) {
+ const RenderViewCommandBuilderJobPtr renderViewCommandBuilder =
+ m_renderViewCommandBuilderJobs.at(i);
+ const int count =
+ (i == m - 1) ? entities.size() - (i * idealPacketSize) : idealPacketSize;
+ renderViewCommandBuilder->setEntities(entities, i * idealPacketSize, count);
+ }
+ }
+
+private:
+ RenderViewInitializerJobPtr m_renderViewInitializer;
+ QVector<RenderViewCommandBuilderJobPtr> m_renderViewCommandBuilderJobs;
+ Renderer *m_renderer;
+ FrameGraphNode *m_leafNode;
+};
+
+class SyncRenderViewPostCommandUpdate
+{
+public:
+ explicit SyncRenderViewPostCommandUpdate(
+ const RenderViewInitializerJobPtr &renderViewJob,
+ const QVector<RenderViewCommandUpdaterJobPtr> &renderViewCommandUpdateJobs,
+ Renderer *renderer)
+ : m_renderViewJob(renderViewJob),
+ m_renderViewCommandUpdaterJobs(renderViewCommandUpdateJobs),
+ m_renderer(renderer)
+ {
+ }
+
+ void operator()()
+ {
+ // Append all the commands and sort them
+ RenderView *rv = m_renderViewJob->renderView();
+
+ const EntityRenderCommandDataPtr commandData =
+ m_renderViewCommandUpdaterJobs.first()->renderables();
+
+ if (commandData) {
+ const QVector<RenderCommand> commands = std::move(commandData->commands);
+ rv->setCommands(commands);
+
+ // TO DO: Find way to store commands once or at least only when required
+ // Sort the commands
+ rv->sort();
+ }
+
+ // Enqueue our fully populated RenderView with the RenderThread
+ m_renderer->enqueueRenderView(rv, m_renderViewJob->submitOrderIndex());
+ }
+
+private:
+ RenderViewInitializerJobPtr m_renderViewJob;
+ QVector<RenderViewCommandUpdaterJobPtr> m_renderViewCommandUpdaterJobs;
+ Renderer *m_renderer;
+};
+
+class SyncPreFrustumCulling
+{
+public:
+ explicit SyncPreFrustumCulling(const RenderViewInitializerJobPtr &renderViewJob,
+ const FrustumCullingJobPtr &frustumCulling)
+ : m_renderViewJob(renderViewJob), m_frustumCullingJob(frustumCulling)
+ {
+ }
+
+ void operator()()
+ {
+ RenderView *rv = m_renderViewJob->renderView();
+
+ // Update matrices now that all transforms have been updated
+ rv->updateMatrices();
+
+ // Frustum culling
+ m_frustumCullingJob->setViewProjection(rv->viewProjectionMatrix());
+ }
+
+private:
+ RenderViewInitializerJobPtr m_renderViewJob;
+ FrustumCullingJobPtr m_frustumCullingJob;
+};
+
+class SyncRenderViewPostInitialization
+{
+public:
+ explicit SyncRenderViewPostInitialization(
+ const RenderViewInitializerJobPtr &renderViewJob,
+ const FrustumCullingJobPtr &frustumCullingJob,
+ const FilterLayerEntityJobPtr &filterEntityByLayerJob,
+ const FilterProximityDistanceJobPtr &filterProximityJob,
+ const QVector<MaterialParameterGathererJobPtr> &materialGathererJobs,
+ const QVector<RenderViewCommandUpdaterJobPtr> &renderViewCommandUpdaterJobs,
+ const QVector<RenderViewCommandBuilderJobPtr> &renderViewCommandBuilderJobs)
+ : m_renderViewJob(renderViewJob),
+ m_frustumCullingJob(frustumCullingJob),
+ m_filterEntityByLayerJob(filterEntityByLayerJob),
+ m_filterProximityJob(filterProximityJob),
+ m_materialGathererJobs(materialGathererJobs),
+ m_renderViewCommandUpdaterJobs(renderViewCommandUpdaterJobs),
+ m_renderViewCommandBuilderJobs(renderViewCommandBuilderJobs)
+ {
+ }
+
+ void operator()()
+ {
+ RenderView *rv = m_renderViewJob->renderView();
+
+ // Layer filtering
+ if (!m_filterEntityByLayerJob.isNull())
+ m_filterEntityByLayerJob->setLayerFilters(rv->layerFilters());
+
+ // Proximity filtering
+ m_filterProximityJob->setProximityFilterIds(rv->proximityFilterIds());
+
+ // Material Parameter building
+ for (const auto &materialGatherer : qAsConst(m_materialGathererJobs)) {
+ materialGatherer->setRenderPassFilter(
+ const_cast<RenderPassFilter *>(rv->renderPassFilter()));
+ materialGatherer->setTechniqueFilter(
+ const_cast<TechniqueFilter *>(rv->techniqueFilter()));
+ }
+
+ // Command builders and updates
+ for (const auto &renderViewCommandUpdater : qAsConst(m_renderViewCommandUpdaterJobs))
+ renderViewCommandUpdater->setRenderView(rv);
+ for (const auto &renderViewCommandBuilder : qAsConst(m_renderViewCommandBuilderJobs))
+ renderViewCommandBuilder->setRenderView(rv);
+
+ // Set whether frustum culling is enabled or not
+ m_frustumCullingJob->setActive(rv->frustumCulling());
+ }
+
+private:
+ RenderViewInitializerJobPtr m_renderViewJob;
+ FrustumCullingJobPtr m_frustumCullingJob;
+ FilterLayerEntityJobPtr m_filterEntityByLayerJob;
+ FilterProximityDistanceJobPtr m_filterProximityJob;
+ QVector<MaterialParameterGathererJobPtr> m_materialGathererJobs;
+ QVector<RenderViewCommandUpdaterJobPtr> m_renderViewCommandUpdaterJobs;
+ QVector<RenderViewCommandBuilderJobPtr> m_renderViewCommandBuilderJobs;
+};
+
+class SyncRenderViewPreCommandUpdate
+{
+public:
+ explicit SyncRenderViewPreCommandUpdate(
+ const RenderViewInitializerJobPtr &renderViewJob,
+ const FrustumCullingJobPtr &frustumCullingJob,
+ const FilterProximityDistanceJobPtr &filterProximityJob,
+ const QVector<MaterialParameterGathererJobPtr> &materialGathererJobs,
+ const QVector<RenderViewCommandUpdaterJobPtr> &renderViewCommandUpdaterJobs,
+ const QVector<RenderViewCommandBuilderJobPtr> &renderViewCommandBuilderJobs,
+ Renderer *renderer, FrameGraphNode *leafNode, bool fullCommandRebuild)
+ : m_renderViewJob(renderViewJob),
+ m_frustumCullingJob(frustumCullingJob),
+ m_filterProximityJob(filterProximityJob),
+ m_materialGathererJobs(materialGathererJobs),
+ m_renderViewCommandUpdaterJobs(renderViewCommandUpdaterJobs),
+ m_renderViewCommandBuilderJobs(renderViewCommandBuilderJobs),
+ m_renderer(renderer),
+ m_leafNode(leafNode),
+ m_fullRebuild(fullCommandRebuild)
+ {
+ }
+
+ void operator()()
+ {
+ // Set the result of previous job computations
+ // for final RenderCommand building
+ RenderView *rv = m_renderViewJob->renderView();
+
+ if (!rv->noDraw()) {
+ ///////// CACHE LOCKED ////////////
+ // Retrieve Data from Cache
+ RendererCache *cache = m_renderer->cache();
+ QMutexLocker lock(cache->mutex());
+ Q_ASSERT(cache->leafNodeCache.contains(m_leafNode));
+
+ const bool isDraw = !rv->isCompute();
+ const RendererCache::LeafNodeData &dataCacheForLeaf = cache->leafNodeCache[m_leafNode];
+
+ // Rebuild RenderCommands if required
+ // This should happen fairly infrequently (FrameGraph Change, Geometry/Material change)
+ // and allow to skip that step most of the time
+ if (m_fullRebuild) {
+ EntityRenderCommandData commandData;
+ // Reduction
+ {
+ int totalCommandCount = 0;
+ for (const RenderViewCommandBuilderJobPtr &renderViewCommandBuilder :
+ qAsConst(m_renderViewCommandBuilderJobs))
+ totalCommandCount += renderViewCommandBuilder->commandData().size();
+ commandData.reserve(totalCommandCount);
+ // assert(totalCommandCount != 0);
+ for (const RenderViewCommandBuilderJobPtr &renderViewCommandBuilder :
+ qAsConst(m_renderViewCommandBuilderJobs))
+ commandData += std::move(renderViewCommandBuilder->commandData());
+ }
+
+ // Store new cache
+ RendererCache::LeafNodeData &writableCacheForLeaf =
+ cache->leafNodeCache[m_leafNode];
+ writableCacheForLeaf.renderCommandData = std::move(commandData);
+ }
+ const EntityRenderCommandData commandData = dataCacheForLeaf.renderCommandData;
+ const QVector<Entity *> filteredEntities = dataCacheForLeaf.filterEntitiesByLayer;
+ QVector<Entity *> renderableEntities =
+ isDraw ? cache->renderableEntities : cache->computeEntities;
+ QVector<LightSource> lightSources = cache->gatheredLights;
+
+ rv->setMaterialParameterTable(dataCacheForLeaf.materialParameterGatherer);
+ rv->setEnvironmentLight(cache->environmentLight);
+ lock.unlock();
+ ///////// END OF CACHE LOCKED ////////////
+
+ // Filter out entities that weren't selected by the layer filters
+ // Remove all entities from the compute and renderable vectors that aren't in the
+ // filtered layer vector
+ renderableEntities =
+ RenderViewBuilder::entitiesInSubset(renderableEntities, filteredEntities);
+
+ // Set the light sources, with layer filters applied.
+ for (int i = 0; i < lightSources.count(); ++i) {
+ if (!filteredEntities.contains(lightSources[i].entity))
+ lightSources.removeAt(i--);
+ }
+ rv->setLightSources(lightSources);
+
+ if (isDraw) {
+ // Filter out frustum culled entity for drawable entities
+ if (rv->frustumCulling())
+ renderableEntities = RenderViewBuilder::entitiesInSubset(
+ renderableEntities, m_frustumCullingJob->visibleEntities());
+ // Filter out entities which didn't satisfy proximity filtering
+ if (!rv->proximityFilterIds().empty())
+ renderableEntities = RenderViewBuilder::entitiesInSubset(
+ renderableEntities, m_filterProximityJob->filteredEntities());
+ }
+
+ // Early return in case we have nothing to filter
+ if (renderableEntities.size() == 0)
+ return;
+
+ // Filter out Render commands for which the Entity wasn't selected because
+ // of frustum, proximity or layer filtering
+ EntityRenderCommandDataPtr filteredCommandData = EntityRenderCommandDataPtr::create();
+ filteredCommandData->reserve(renderableEntities.size());
+ // Because dataCacheForLeaf.renderableEntities or computeEntities are sorted
+ // What we get out of EntityRenderCommandData is also sorted by Entity
+ auto eIt = std::cbegin(renderableEntities);
+ const auto eEnd = std::cend(renderableEntities);
+ int cIt = 0;
+ const int cEnd = commandData.size();
+
+ while (eIt != eEnd) {
+ const Entity *targetEntity = *eIt;
+ // Advance until we have commands whose Entity has a lower address
+ // than the selected filtered entity
+ while (cIt != cEnd && commandData.entities.at(cIt) < targetEntity)
+ ++cIt;
+
+ // Push pointers to command data for all commands that match the
+ // entity
+ while (cIt != cEnd && commandData.entities.at(cIt) == targetEntity) {
+ filteredCommandData->push_back(commandData.entities.at(cIt),
+ commandData.commands.at(cIt),
+ commandData.passesData.at(cIt));
+ ++cIt;
+ }
+ ++eIt;
+ }
+
+ // Split among the number of command builders
+ // The idealPacketSize is at least 100 entities per worker
+ const int idealPacketSize = std::min(
+ std::max(100,
+ filteredCommandData->size() / RenderViewBuilder::optimalJobCount()),
+ filteredCommandData->size());
+ const int m = findIdealNumberOfWorkers(filteredCommandData->size(), idealPacketSize);
+
+ for (int i = 0; i < m; ++i) {
+ const RenderViewCommandUpdaterJobPtr renderViewCommandBuilder =
+ m_renderViewCommandUpdaterJobs.at(i);
+ const int count = (i == m - 1) ? filteredCommandData->size() - (i * idealPacketSize)
+ : idealPacketSize;
+ renderViewCommandBuilder->setRenderables(filteredCommandData, i * idealPacketSize,
+ count);
+ }
+ }
+ }
+
+private:
+ RenderViewInitializerJobPtr m_renderViewJob;
+ FrustumCullingJobPtr m_frustumCullingJob;
+ FilterProximityDistanceJobPtr m_filterProximityJob;
+ QVector<MaterialParameterGathererJobPtr> m_materialGathererJobs;
+ QVector<RenderViewCommandUpdaterJobPtr> m_renderViewCommandUpdaterJobs;
+ QVector<RenderViewCommandBuilderJobPtr> m_renderViewCommandBuilderJobs;
+ Renderer *m_renderer;
+ FrameGraphNode *m_leafNode;
+ bool m_fullRebuild;
+};
+
+class SetClearDrawBufferIndex
+{
+public:
+ explicit SetClearDrawBufferIndex(const RenderViewInitializerJobPtr &renderViewJob)
+ : m_renderViewJob(renderViewJob)
+ {
+ }
+
+ void operator()()
+ {
+ RenderView *rv = m_renderViewJob->renderView();
+ QVector<ClearBufferInfo> &clearBuffersInfo = rv->specificClearColorBufferInfo();
+ const AttachmentPack &attachmentPack = rv->attachmentPack();
+ for (ClearBufferInfo &clearBufferInfo : clearBuffersInfo)
+ clearBufferInfo.drawBufferIndex =
+ attachmentPack.getDrawBufferIndex(clearBufferInfo.attchmentPoint);
+ }
+
+private:
+ RenderViewInitializerJobPtr m_renderViewJob;
+};
+
+class SyncFilterEntityByLayer
+{
+public:
+ explicit SyncFilterEntityByLayer(const FilterLayerEntityJobPtr &filterEntityByLayerJob,
+ Renderer *renderer, FrameGraphNode *leafNode)
+ : m_filterEntityByLayerJob(filterEntityByLayerJob),
+ m_renderer(renderer),
+ m_leafNode(leafNode)
+ {
+ }
+
+ void operator()()
+ {
+ QMutexLocker lock(m_renderer->cache()->mutex());
+ // Save the filtered by layer subset into the cache
+ const QVector<Entity *> filteredEntities = m_filterEntityByLayerJob->filteredEntities();
+ RendererCache::LeafNodeData &dataCacheForLeaf =
+ m_renderer->cache()->leafNodeCache[m_leafNode];
+ dataCacheForLeaf.filterEntitiesByLayer = filteredEntities;
+ }
+
+private:
+ FilterLayerEntityJobPtr m_filterEntityByLayerJob;
+ Renderer *m_renderer;
+ FrameGraphNode *m_leafNode;
+};
+
+class SyncMaterialParameterGatherer
+{
+public:
+ explicit SyncMaterialParameterGatherer(
+ const QVector<MaterialParameterGathererJobPtr> &materialParameterGathererJobs,
+ Renderer *renderer, FrameGraphNode *leafNode)
+ : m_materialParameterGathererJobs(materialParameterGathererJobs),
+ m_renderer(renderer),
+ m_leafNode(leafNode)
+ {
+ }
+
+ void operator()()
+ {
+ QMutexLocker lock(m_renderer->cache()->mutex());
+ RendererCache::LeafNodeData &dataCacheForLeaf =
+ m_renderer->cache()->leafNodeCache[m_leafNode];
+ dataCacheForLeaf.materialParameterGatherer.clear();
+
+ for (const auto &materialGatherer : qAsConst(m_materialParameterGathererJobs))
+ dataCacheForLeaf.materialParameterGatherer.unite(
+ materialGatherer->materialToPassAndParameter());
+ }
+
+private:
+ QVector<MaterialParameterGathererJobPtr> m_materialParameterGathererJobs;
+ Renderer *m_renderer;
+ FrameGraphNode *m_leafNode;
+};
+
+} // anonymous
+
+RenderViewBuilder::RenderViewBuilder(Render::FrameGraphNode *leafNode, int renderViewIndex,
+ Renderer *renderer)
+ : m_leafNode(leafNode),
+ m_renderViewIndex(renderViewIndex),
+ m_renderer(renderer),
+ m_layerCacheNeedsToBeRebuilt(false),
+ m_materialGathererCacheNeedsToBeRebuilt(false),
+ m_renderCommandCacheNeedsToBeRebuilt(false),
+ m_renderViewJob(RenderViewInitializerJobPtr::create()),
+ m_filterEntityByLayerJob(),
+ m_frustumCullingJob(new Render::FrustumCullingJob()),
+ m_syncPreFrustumCullingJob(SynchronizerJobPtr::create(
+ SyncPreFrustumCulling(m_renderViewJob, m_frustumCullingJob),
+ JobTypes::SyncFrustumCulling)),
+ m_setClearDrawBufferIndexJob(SynchronizerJobPtr::create(
+ SetClearDrawBufferIndex(m_renderViewJob), JobTypes::ClearBufferDrawIndex)),
+ m_syncFilterEntityByLayerJob(),
+ m_filterProximityJob(Render::FilterProximityDistanceJobPtr::create())
+{
+}
+
+RenderViewInitializerJobPtr RenderViewBuilder::renderViewJob() const
+{
+ return m_renderViewJob;
+}
+
+FilterLayerEntityJobPtr RenderViewBuilder::filterEntityByLayerJob() const
+{
+ return m_filterEntityByLayerJob;
+}
+
+FrustumCullingJobPtr RenderViewBuilder::frustumCullingJob() const
+{
+ return m_frustumCullingJob;
+}
+
+QVector<RenderViewCommandUpdaterJobPtr> RenderViewBuilder::renderViewCommandUpdaterJobs() const
+{
+ return m_renderViewCommandUpdaterJobs;
+}
+
+QVector<RenderViewCommandBuilderJobPtr> RenderViewBuilder::renderViewCommandBuilderJobs() const
+{
+ return m_renderViewCommandBuilderJobs;
+}
+
+QVector<MaterialParameterGathererJobPtr> RenderViewBuilder::materialGathererJobs() const
+{
+ return m_materialGathererJobs;
+}
+
+SynchronizerJobPtr RenderViewBuilder::syncRenderViewPostInitializationJob() const
+{
+ return m_syncRenderViewPostInitializationJob;
+}
+
+SynchronizerJobPtr RenderViewBuilder::syncPreFrustumCullingJob() const
+{
+ return m_syncPreFrustumCullingJob;
+}
+
+SynchronizerJobPtr RenderViewBuilder::syncRenderViewPreCommandBuildingJob() const
+{
+ return m_syncRenderViewPreCommandBuildingJob;
+}
+
+SynchronizerJobPtr RenderViewBuilder::syncRenderViewPreCommandUpdateJob() const
+{
+ return m_syncRenderViewPreCommandUpdateJob;
+}
+
+SynchronizerJobPtr RenderViewBuilder::syncRenderViewPostCommandUpdateJob() const
+{
+ return m_syncRenderViewPostCommandUpdateJob;
+}
+
+SynchronizerJobPtr RenderViewBuilder::setClearDrawBufferIndexJob() const
+{
+ return m_setClearDrawBufferIndexJob;
+}
+
+SynchronizerJobPtr RenderViewBuilder::syncFilterEntityByLayerJob() const
+{
+ return m_syncFilterEntityByLayerJob;
+}
+
+SynchronizerJobPtr RenderViewBuilder::syncMaterialGathererJob() const
+{
+ return m_syncMaterialGathererJob;
+}
+
+FilterProximityDistanceJobPtr RenderViewBuilder::filterProximityJob() const
+{
+ return m_filterProximityJob;
+}
+
+void RenderViewBuilder::prepareJobs()
+{
+ // Init what we can here
+ m_filterProximityJob->setManager(m_renderer->nodeManagers());
+ m_frustumCullingJob->setRoot(m_renderer->sceneRoot());
+
+ if (m_renderCommandCacheNeedsToBeRebuilt) {
+
+ m_renderViewCommandBuilderJobs.reserve(RenderViewBuilder::m_optimalParallelJobCount);
+ for (auto i = 0; i < RenderViewBuilder::m_optimalParallelJobCount; ++i) {
+ auto renderViewCommandBuilder = Render::Rhi::RenderViewCommandBuilderJobPtr::create();
+ m_renderViewCommandBuilderJobs.push_back(renderViewCommandBuilder);
+ }
+ m_syncRenderViewPreCommandBuildingJob = SynchronizerJobPtr::create(
+ SyncPreCommandBuilding(m_renderViewJob, m_renderViewCommandBuilderJobs, m_renderer,
+ m_leafNode),
+ JobTypes::SyncRenderViewPreCommandBuilding);
+ }
+
+ m_renderViewJob->setRenderer(m_renderer);
+ m_renderViewJob->setFrameGraphLeafNode(m_leafNode);
+ m_renderViewJob->setSubmitOrderIndex(m_renderViewIndex);
+
+ // RenderCommand building is the most consuming task -> split it
+ // Estimate the number of jobs to create based on the number of entities
+ m_renderViewCommandUpdaterJobs.reserve(RenderViewBuilder::m_optimalParallelJobCount);
+ for (auto i = 0; i < RenderViewBuilder::m_optimalParallelJobCount; ++i) {
+ auto renderViewCommandUpdater = Render::Rhi::RenderViewCommandUpdaterJobPtr::create();
+ renderViewCommandUpdater->setRenderer(m_renderer);
+ m_renderViewCommandUpdaterJobs.push_back(renderViewCommandUpdater);
+ }
+
+ if (m_materialGathererCacheNeedsToBeRebuilt) {
+ // Since Material gathering is an heavy task, we split it
+ const QVector<HMaterial> materialHandles =
+ m_renderer->nodeManagers()->materialManager()->activeHandles();
+ const int elementsPerJob =
+ materialHandles.size() / RenderViewBuilder::m_optimalParallelJobCount;
+ const int lastRemaingElements =
+ materialHandles.size() % RenderViewBuilder::m_optimalParallelJobCount;
+ m_materialGathererJobs.reserve(RenderViewBuilder::m_optimalParallelJobCount);
+ for (auto i = 0; i < RenderViewBuilder::m_optimalParallelJobCount; ++i) {
+ auto materialGatherer = MaterialParameterGathererJobPtr::create();
+ materialGatherer->setNodeManagers(m_renderer->nodeManagers());
+ if (i == RenderViewBuilder::m_optimalParallelJobCount - 1)
+ materialGatherer->setHandles(materialHandles.mid(
+ i * elementsPerJob, elementsPerJob + lastRemaingElements));
+ else
+ materialGatherer->setHandles(
+ materialHandles.mid(i * elementsPerJob, elementsPerJob));
+ m_materialGathererJobs.push_back(materialGatherer);
+ }
+ m_syncMaterialGathererJob = SynchronizerJobPtr::create(
+ SyncMaterialParameterGatherer(m_materialGathererJobs, m_renderer, m_leafNode),
+ JobTypes::SyncMaterialGatherer);
+ }
+
+ if (m_layerCacheNeedsToBeRebuilt) {
+ m_filterEntityByLayerJob = Render::FilterLayerEntityJobPtr::create();
+ m_filterEntityByLayerJob->setManager(m_renderer->nodeManagers());
+ m_syncFilterEntityByLayerJob = SynchronizerJobPtr::create(
+ SyncFilterEntityByLayer(m_filterEntityByLayerJob, m_renderer, m_leafNode),
+ JobTypes::SyncFilterEntityByLayer);
+ }
+
+ m_syncRenderViewPreCommandUpdateJob = SynchronizerJobPtr::create(
+ SyncRenderViewPreCommandUpdate(m_renderViewJob, m_frustumCullingJob,
+ m_filterProximityJob, m_materialGathererJobs,
+ m_renderViewCommandUpdaterJobs,
+ m_renderViewCommandBuilderJobs, m_renderer, m_leafNode,
+ m_renderCommandCacheNeedsToBeRebuilt),
+ JobTypes::SyncRenderViewPreCommandUpdate);
+
+ m_syncRenderViewPostCommandUpdateJob = SynchronizerJobPtr::create(
+ SyncRenderViewPostCommandUpdate(m_renderViewJob, m_renderViewCommandUpdaterJobs,
+ m_renderer),
+ JobTypes::SyncRenderViewPostCommandUpdate);
+
+ m_syncRenderViewPostInitializationJob = SynchronizerJobPtr::create(
+ SyncRenderViewPostInitialization(m_renderViewJob, m_frustumCullingJob,
+ m_filterEntityByLayerJob, m_filterProximityJob,
+ m_materialGathererJobs, m_renderViewCommandUpdaterJobs,
+ m_renderViewCommandBuilderJobs),
+ JobTypes::SyncRenderViewInitialization);
+}
+
+QVector<Qt3DCore::QAspectJobPtr> RenderViewBuilder::buildJobHierachy() const
+{
+ QVector<Qt3DCore::QAspectJobPtr> jobs;
+ auto daspect = QRenderAspectPrivate::get(m_renderer->aspect());
+ auto expandBVJob = daspect->m_expandBoundingVolumeJob;
+ auto worldTransformJob = daspect->m_worldTransformJob;
+ auto updateTreeEnabledJob = daspect->m_updateTreeEnabledJob;
+ auto updateSkinningPaletteJob = daspect->m_updateSkinningPaletteJob;
+ auto updateEntityLayersJob = daspect->m_updateEntityLayersJob;
+
+ jobs.reserve(m_materialGathererJobs.size() + m_renderViewCommandUpdaterJobs.size() + 11);
+
+ // Set dependencies
+
+ // Finish the skinning palette job before processing renderviews
+ // TODO: Maybe only update skinning palettes for non-culled entities
+ m_renderViewJob->addDependency(updateSkinningPaletteJob);
+
+ m_syncPreFrustumCullingJob->addDependency(worldTransformJob);
+ m_syncPreFrustumCullingJob->addDependency(m_renderer->updateShaderDataTransformJob());
+ m_syncPreFrustumCullingJob->addDependency(m_syncRenderViewPostInitializationJob);
+
+ m_frustumCullingJob->addDependency(expandBVJob);
+ m_frustumCullingJob->addDependency(m_syncPreFrustumCullingJob);
+
+ m_setClearDrawBufferIndexJob->addDependency(m_syncRenderViewPostInitializationJob);
+
+ m_syncRenderViewPostInitializationJob->addDependency(m_renderViewJob);
+
+ m_filterProximityJob->addDependency(expandBVJob);
+ m_filterProximityJob->addDependency(m_syncRenderViewPostInitializationJob);
+
+ m_syncRenderViewPreCommandUpdateJob->addDependency(m_syncRenderViewPostInitializationJob);
+ m_syncRenderViewPreCommandUpdateJob->addDependency(m_filterProximityJob);
+ m_syncRenderViewPreCommandUpdateJob->addDependency(m_frustumCullingJob);
+
+ // Ensure the RenderThread won't be able to process dirtyResources
+ // before they have been completely gathered
+ m_syncRenderViewPreCommandUpdateJob->addDependency(m_renderer->introspectShadersJob());
+ m_syncRenderViewPreCommandUpdateJob->addDependency(m_renderer->bufferGathererJob());
+ m_syncRenderViewPreCommandUpdateJob->addDependency(m_renderer->textureGathererJob());
+ m_syncRenderViewPreCommandUpdateJob->addDependency(m_renderer->lightGathererJob());
+
+ for (const auto &renderViewCommandUpdater : qAsConst(m_renderViewCommandUpdaterJobs)) {
+ renderViewCommandUpdater->addDependency(m_syncRenderViewPreCommandUpdateJob);
+ m_syncRenderViewPostCommandUpdateJob->addDependency(renderViewCommandUpdater);
+ }
+
+ m_renderer->frameCleanupJob()->addDependency(m_syncRenderViewPostCommandUpdateJob);
+ m_renderer->frameCleanupJob()->addDependency(m_setClearDrawBufferIndexJob);
+
+ // Add jobs
+ jobs.push_back(m_renderViewJob); // Step 1
+
+ jobs.push_back(m_syncRenderViewPostInitializationJob); // Step 2
+
+ if (m_renderCommandCacheNeedsToBeRebuilt) { // Step 3
+ m_syncRenderViewPreCommandBuildingJob->addDependency(
+ m_renderer->computableEntityFilterJob());
+ m_syncRenderViewPreCommandBuildingJob->addDependency(
+ m_renderer->renderableEntityFilterJob());
+ m_syncRenderViewPreCommandBuildingJob->addDependency(m_syncRenderViewPostInitializationJob);
+
+ if (m_materialGathererCacheNeedsToBeRebuilt)
+ m_syncRenderViewPreCommandBuildingJob->addDependency(m_syncMaterialGathererJob);
+
+ jobs.push_back(m_syncRenderViewPreCommandBuildingJob);
+
+ for (const auto &renderViewCommandBuilder : qAsConst(m_renderViewCommandBuilderJobs)) {
+ renderViewCommandBuilder->addDependency(m_syncRenderViewPreCommandBuildingJob);
+ m_syncRenderViewPreCommandUpdateJob->addDependency(renderViewCommandBuilder);
+ jobs.push_back(renderViewCommandBuilder);
+ }
+ }
+
+ if (m_layerCacheNeedsToBeRebuilt) {
+ m_filterEntityByLayerJob->addDependency(updateEntityLayersJob);
+ m_filterEntityByLayerJob->addDependency(m_syncRenderViewPostInitializationJob);
+ m_filterEntityByLayerJob->addDependency(updateTreeEnabledJob);
+
+ m_syncFilterEntityByLayerJob->addDependency(m_filterEntityByLayerJob);
+ m_syncRenderViewPreCommandUpdateJob->addDependency(m_syncFilterEntityByLayerJob);
+
+ jobs.push_back(m_filterEntityByLayerJob); // Step 3
+ jobs.push_back(m_syncFilterEntityByLayerJob); // Step 4
+ }
+ jobs.push_back(m_syncPreFrustumCullingJob); // Step 3
+ jobs.push_back(m_filterProximityJob); // Step 3
+ jobs.push_back(m_setClearDrawBufferIndexJob); // Step 3
+
+ if (m_materialGathererCacheNeedsToBeRebuilt) {
+ for (const auto &materialGatherer : qAsConst(m_materialGathererJobs)) {
+ materialGatherer->addDependency(m_syncRenderViewPostInitializationJob);
+ materialGatherer->addDependency(m_renderer->introspectShadersJob());
+ materialGatherer->addDependency(m_renderer->filterCompatibleTechniqueJob());
+ jobs.push_back(materialGatherer); // Step3
+ m_syncMaterialGathererJob->addDependency(materialGatherer);
+ }
+ m_syncRenderViewPreCommandUpdateJob->addDependency(m_syncMaterialGathererJob);
+
+ jobs.push_back(m_syncMaterialGathererJob); // Step 3
+ }
+
+ jobs.push_back(m_frustumCullingJob); // Step 4
+ jobs.push_back(m_syncRenderViewPreCommandUpdateJob); // Step 5
+
+ // Build RenderCommands or Update RenderCommand Uniforms
+ for (const auto &renderViewCommandBuilder : qAsConst(m_renderViewCommandUpdaterJobs)) // Step 6
+ jobs.push_back(renderViewCommandBuilder);
+
+ jobs.push_back(m_syncRenderViewPostCommandUpdateJob); // Step 7
+
+ return jobs;
+}
+
+Renderer *RenderViewBuilder::renderer() const
+{
+ return m_renderer;
+}
+
+int RenderViewBuilder::renderViewIndex() const
+{
+ return m_renderViewIndex;
+}
+
+void RenderViewBuilder::setLayerCacheNeedsToBeRebuilt(bool needsToBeRebuilt)
+{
+ m_layerCacheNeedsToBeRebuilt = needsToBeRebuilt;
+}
+
+bool RenderViewBuilder::layerCacheNeedsToBeRebuilt() const
+{
+ return m_layerCacheNeedsToBeRebuilt;
+}
+
+void RenderViewBuilder::setMaterialGathererCacheNeedsToBeRebuilt(bool needsToBeRebuilt)
+{
+ m_materialGathererCacheNeedsToBeRebuilt = needsToBeRebuilt;
+}
+
+bool RenderViewBuilder::materialGathererCacheNeedsToBeRebuilt() const
+{
+ return m_materialGathererCacheNeedsToBeRebuilt;
+}
+
+void RenderViewBuilder::setRenderCommandCacheNeedsToBeRebuilt(bool needsToBeRebuilt)
+{
+ m_renderCommandCacheNeedsToBeRebuilt = needsToBeRebuilt;
+}
+
+bool RenderViewBuilder::renderCommandCacheNeedsToBeRebuilt() const
+{
+ return m_renderCommandCacheNeedsToBeRebuilt;
+}
+
+int RenderViewBuilder::optimalJobCount()
+{
+ return RenderViewBuilder::m_optimalParallelJobCount;
+}
+
+QVector<Entity *> RenderViewBuilder::entitiesInSubset(const QVector<Entity *> &entities,
+ const QVector<Entity *> &subset)
+{
+ QVector<Entity *> intersection;
+ intersection.reserve(qMin(entities.size(), subset.size()));
+ std::set_intersection(entities.begin(), entities.end(), subset.begin(), subset.end(),
+ std::back_inserter(intersection));
+
+ return intersection;
+}
+
+} // Rhi
+
+} // Render
+
+} // Qt3DRender
+
+QT_END_NAMESPACE
diff --git a/src/plugins/renderers/rhi/renderer/renderviewbuilder_p.h b/src/plugins/renderers/rhi/renderer/renderviewbuilder_p.h
new file mode 100644
index 000000000..4df57b139
--- /dev/null
+++ b/src/plugins/renderers/rhi/renderer/renderviewbuilder_p.h
@@ -0,0 +1,153 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 QT3DRENDER_RENDER_RHI_RENDERVIEWBUILDER_H
+#define QT3DRENDER_RENDER_RHI_RENDERVIEWBUILDER_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <functional>
+#include <Qt3DCore/qaspectjob.h>
+#include <Qt3DRender/private/filterlayerentityjob_p.h>
+#include <Qt3DRender/private/genericlambdajob_p.h>
+#include <Qt3DRender/private/nodemanagers_p.h>
+#include <Qt3DRender/private/frustumcullingjob_p.h>
+#include <Qt3DRender/private/filterproximitydistancejob_p.h>
+#include <renderviewcommandbuilderjob_p.h>
+#include <renderviewcommandupdaterjob_p.h>
+#include <materialparametergathererjob_p.h>
+#include <renderview_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+namespace Render {
+
+namespace Rhi {
+
+class Renderer;
+
+using SynchronizerJobPtr = GenericLambdaJobPtr<std::function<void()>>;
+
+class Q_AUTOTEST_EXPORT RenderViewBuilder
+{
+public:
+ explicit RenderViewBuilder(Render::FrameGraphNode *leafNode, int renderViewIndex,
+ Renderer *renderer);
+
+ RenderViewInitializerJobPtr renderViewJob() const;
+ FilterLayerEntityJobPtr filterEntityByLayerJob() const;
+ FrustumCullingJobPtr frustumCullingJob() const;
+ QVector<RenderViewCommandBuilderJobPtr> renderViewCommandBuilderJobs() const;
+ QVector<RenderViewCommandUpdaterJobPtr> renderViewCommandUpdaterJobs() const;
+ QVector<MaterialParameterGathererJobPtr> materialGathererJobs() const;
+ SynchronizerJobPtr syncRenderViewPostInitializationJob() const;
+ SynchronizerJobPtr syncPreFrustumCullingJob() const;
+ SynchronizerJobPtr syncRenderViewPreCommandBuildingJob() const;
+ SynchronizerJobPtr syncRenderViewPreCommandUpdateJob() const;
+ SynchronizerJobPtr syncRenderViewPostCommandUpdateJob() const;
+ SynchronizerJobPtr setClearDrawBufferIndexJob() const;
+ SynchronizerJobPtr syncFilterEntityByLayerJob() const;
+ FilterProximityDistanceJobPtr filterProximityJob() const;
+ SynchronizerJobPtr syncMaterialGathererJob() const;
+
+ void prepareJobs();
+ QVector<Qt3DCore::QAspectJobPtr> buildJobHierachy() const;
+
+ Renderer *renderer() const;
+ int renderViewIndex() const;
+
+ void setLayerCacheNeedsToBeRebuilt(bool needsToBeRebuilt);
+ bool layerCacheNeedsToBeRebuilt() const;
+ void setMaterialGathererCacheNeedsToBeRebuilt(bool needsToBeRebuilt);
+ bool materialGathererCacheNeedsToBeRebuilt() const;
+ void setRenderCommandCacheNeedsToBeRebuilt(bool needsToBeRebuilt);
+ bool renderCommandCacheNeedsToBeRebuilt() const;
+
+ static int optimalJobCount();
+ static QVector<Entity *> entitiesInSubset(const QVector<Entity *> &entities,
+ const QVector<Entity *> &subset);
+
+private:
+ Render::FrameGraphNode *m_leafNode;
+ const int m_renderViewIndex;
+ Renderer *m_renderer;
+ bool m_layerCacheNeedsToBeRebuilt;
+ bool m_materialGathererCacheNeedsToBeRebuilt;
+ bool m_renderCommandCacheNeedsToBeRebuilt;
+
+ RenderViewInitializerJobPtr m_renderViewJob;
+ FilterLayerEntityJobPtr m_filterEntityByLayerJob;
+ FrustumCullingJobPtr m_frustumCullingJob;
+ QVector<RenderViewCommandBuilderJobPtr> m_renderViewCommandBuilderJobs;
+ QVector<RenderViewCommandUpdaterJobPtr> m_renderViewCommandUpdaterJobs;
+ QVector<MaterialParameterGathererJobPtr> m_materialGathererJobs;
+
+ SynchronizerJobPtr m_syncRenderViewPostInitializationJob;
+ SynchronizerJobPtr m_syncPreFrustumCullingJob;
+ SynchronizerJobPtr m_syncRenderViewPreCommandBuildingJob;
+ SynchronizerJobPtr m_syncRenderViewPreCommandUpdateJob;
+ SynchronizerJobPtr m_syncRenderViewPostCommandUpdateJob;
+ SynchronizerJobPtr m_setClearDrawBufferIndexJob;
+ SynchronizerJobPtr m_syncFilterEntityByLayerJob;
+ SynchronizerJobPtr m_syncMaterialGathererJob;
+ FilterProximityDistanceJobPtr m_filterProximityJob;
+
+ static const int m_optimalParallelJobCount;
+};
+
+} // Rhi
+
+} // Render
+
+} // Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDER_RHI_RENDERVIEWBUILDER_H
diff --git a/src/quick3d/imports/scene3d/scene3dcleaner.cpp b/src/plugins/renderers/rhi/renderer/rhigraphicspipeline.cpp
index ec371410d..2a76f9c10 100644
--- a/src/quick3d/imports/scene3d/scene3dcleaner.cpp
+++ b/src/plugins/renderers/rhi/renderer/rhigraphicspipeline.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB).
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt3D module of the Qt Toolkit.
@@ -37,39 +37,45 @@
**
****************************************************************************/
-#include "scene3dcleaner_p.h"
-
-#include <Qt3DCore/qaspectengine.h>
-#include <QtCore/qthread.h>
-
-#include <scene3dlogging_p.h>
-#include <scene3drenderer_p.h>
+#include "rhigraphicspipeline_p.h"
QT_BEGIN_NAMESPACE
namespace Qt3DRender {
-Scene3DCleaner::Scene3DCleaner(QObject *parent)
- : QObject(parent)
- , m_renderer(nullptr)
-{
-}
+namespace Render {
-Scene3DCleaner::~Scene3DCleaner()
+namespace Rhi {
+
+RHIGraphicsPipeline::RHIGraphicsPipeline()
+ : m_rvUbo(nullptr),
+ m_cmdUbo(nullptr),
+ m_pipeline(nullptr),
+ m_shaderResourceBindings(nullptr),
+ m_score(0)
{
- qCDebug(Scene3D) << Q_FUNC_INFO << QThread::currentThread();
}
-void Scene3DCleaner::cleanup()
+RHIGraphicsPipeline::~RHIGraphicsPipeline() { }
+
+void RHIGraphicsPipeline::cleanup()
{
- Q_ASSERT(m_renderer);
- delete m_renderer->m_aspectEngine; // also deletes m_renderer->m_renderAspect
- m_renderer->m_aspectEngine = nullptr;
- m_renderer->m_renderAspect = nullptr;
- m_renderer->deleteLater();
- deleteLater();
+ delete m_shaderResourceBindings;
+ delete m_rvUbo;
+ delete m_cmdUbo;
+ delete m_pipeline;
+ m_rvUbo = nullptr;
+ m_cmdUbo = nullptr;
+ m_pipeline = nullptr;
+ m_shaderResourceBindings = nullptr;
+ m_ubos.clear();
+ m_attributeNameIdToBindingIndex.clear();
}
-} // namespace Qt3DRender
+} // Rhi
+
+} // Render
+
+} // Qt3DRender
QT_END_NAMESPACE
diff --git a/src/plugins/renderers/rhi/renderer/rhigraphicspipeline_p.h b/src/plugins/renderers/rhi/renderer/rhigraphicspipeline_p.h
new file mode 100644
index 000000000..a13bf50d3
--- /dev/null
+++ b/src/plugins/renderers/rhi/renderer/rhigraphicspipeline_p.h
@@ -0,0 +1,127 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 QT3DRENDER_RENDER_RHI_RHIGRAPHICSPIPELINE_H
+#define QT3DRENDER_RENDER_RHI_RHIGRAPHICSPIPELINE_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qrhi_p.h>
+#include <rhihandle_types_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+namespace Render {
+
+namespace Rhi {
+
+class RHIBuffer;
+
+class RHIGraphicsPipeline
+{
+public:
+ struct UBOBuffer
+ {
+ HRHIBuffer handle {};
+ RHIBuffer *buffer {};
+ };
+
+ RHIGraphicsPipeline();
+ ~RHIGraphicsPipeline();
+
+ QRhiBuffer *commandUBO() const { return m_cmdUbo; }
+ QRhiBuffer *renderViewUBO() const { return m_rvUbo; }
+ QRhiGraphicsPipeline *pipeline() const { return m_pipeline; }
+ QRhiShaderResourceBindings *shaderResourceBindings() const { return m_shaderResourceBindings; }
+ QHash<int, UBOBuffer> ubos() const { return m_ubos; }
+ int score() const { return m_score; }
+
+ void setPipeline(QRhiGraphicsPipeline *pipeline) { m_pipeline = pipeline; }
+ void setCommandUBO(QRhiBuffer *commandUBO) { m_cmdUbo = commandUBO; }
+ void setRenderViewUBO(QRhiBuffer *rvUBO) { m_rvUbo = rvUBO; }
+ void setShaderResourceBindings(QRhiShaderResourceBindings *shaderResourceBindings)
+ {
+ m_shaderResourceBindings = shaderResourceBindings;
+ }
+ void setUBOs(const QHash<int, UBOBuffer> ubos) { m_ubos = ubos; }
+
+ void setAttributesToBindingHash(const QHash<int, int> &attributeNameToBinding)
+ {
+ m_attributeNameIdToBindingIndex = attributeNameToBinding;
+ }
+ int bindingIndexForAttribute(int attributeNameId) const
+ {
+ return m_attributeNameIdToBindingIndex.value(attributeNameId, -1);
+ }
+ void increaseScore() { ++m_score; }
+ void decreaseScore() { --m_score; }
+
+ void cleanup();
+
+private:
+ QRhiBuffer *m_rvUbo;
+ QRhiBuffer *m_cmdUbo;
+ QRhiGraphicsPipeline *m_pipeline;
+ QRhiShaderResourceBindings *m_shaderResourceBindings;
+ // For user defined uniforms
+ QHash<int, UBOBuffer> m_ubos;
+ QHash<int, int> m_attributeNameIdToBindingIndex;
+ int m_score;
+};
+
+} // Rhi
+
+} // Render
+
+} // Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDER_RHI_RHIGRAPHICSPIPELINE_H
diff --git a/src/plugins/renderers/rhi/renderer/rhishader.cpp b/src/plugins/renderers/rhi/renderer/rhishader.cpp
new file mode 100644
index 000000000..20da46347
--- /dev/null
+++ b/src/plugins/renderers/rhi/renderer/rhishader.cpp
@@ -0,0 +1,681 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 "rhishader_p.h"
+#include <QMutexLocker>
+#include <Qt3DRender/private/stringtoint_p.h>
+#include <submissioncontext_p.h>
+#include <logging_p.h>
+#include <QRegularExpression>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+namespace Render {
+
+namespace Rhi {
+
+RHIShader::RHIShader() : m_isLoaded(false)
+{
+ m_shaderCode.resize(static_cast<int>(QShaderProgram::Compute) + 1);
+}
+
+QVector<QString> RHIShader::uniformsNames() const
+{
+ return m_uniformsNames;
+}
+
+QVector<QString> RHIShader::attributesNames() const
+{
+ return m_attributesNames;
+}
+
+QVector<QString> RHIShader::uniformBlockNames() const
+{
+ return m_uniformBlockNames;
+}
+
+QVector<QString> RHIShader::storageBlockNames() const
+{
+ return m_shaderStorageBlockNames;
+}
+
+QVector<QString> RHIShader::samplerNames() const
+{
+ return m_samplerNames;
+}
+
+QVector<QString> RHIShader::imagesNames() const
+{
+ return m_imageNames;
+}
+
+QVector<QByteArray> RHIShader::shaderCode() const
+{
+ return m_shaderCode;
+}
+
+namespace {
+static constexpr QRhiVertexInputAttribute::Format
+rhiInputType(QShaderDescription::VariableType type)
+{
+ switch (type) {
+ case QShaderDescription::Vec4:
+ return QRhiVertexInputAttribute::Float4;
+ case QShaderDescription::Vec3:
+ return QRhiVertexInputAttribute::Float3;
+ case QShaderDescription::Vec2:
+ return QRhiVertexInputAttribute::Float2;
+ case QShaderDescription::Float:
+ return QRhiVertexInputAttribute::Float;
+ default:
+ // TODO UNormByte4, UNormByte2, UNormByte
+ RHI_UNIMPLEMENTED;
+ return QRhiVertexInputAttribute::UNormByte;
+ break;
+ }
+}
+
+static constexpr int rhiTypeSize(QShaderDescription::VariableType type)
+{
+ switch (type) {
+ case QShaderDescription::Unknown:
+ return 0;
+
+ case QShaderDescription::Float:
+ return 1;
+ case QShaderDescription::Vec2:
+ return 2;
+ case QShaderDescription::Vec3:
+ return 3;
+ case QShaderDescription::Vec4:
+ return 4;
+ case QShaderDescription::Mat2:
+ return 2 * 2;
+ case QShaderDescription::Mat2x3:
+ return 2 * 3;
+ case QShaderDescription::Mat2x4:
+ return 2 * 4;
+ case QShaderDescription::Mat3:
+ return 3 * 3;
+ case QShaderDescription::Mat3x2:
+ return 3 * 2;
+ case QShaderDescription::Mat3x4:
+ return 3 * 4;
+ case QShaderDescription::Mat4:
+ return 4 * 4;
+ case QShaderDescription::Mat4x2:
+ return 4 * 2;
+ case QShaderDescription::Mat4x3:
+ return 4 * 3;
+
+ case QShaderDescription::Int:
+ return 1;
+ case QShaderDescription::Int2:
+ return 2;
+ case QShaderDescription::Int3:
+ return 3;
+ case QShaderDescription::Int4:
+ return 4;
+
+ case QShaderDescription::Uint:
+ return 1;
+ case QShaderDescription::Uint2:
+ return 2;
+ case QShaderDescription::Uint3:
+ return 3;
+ case QShaderDescription::Uint4:
+ return 4;
+
+ case QShaderDescription::Bool:
+ return 1;
+ case QShaderDescription::Bool2:
+ return 2;
+ case QShaderDescription::Bool3:
+ return 3;
+ case QShaderDescription::Bool4:
+ return 4;
+
+ case QShaderDescription::Double:
+ return 1;
+ case QShaderDescription::Double2:
+ return 2;
+ case QShaderDescription::Double3:
+ return 3;
+ case QShaderDescription::Double4:
+ return 4;
+ case QShaderDescription::DMat2:
+ return 2 * 2;
+ case QShaderDescription::DMat2x3:
+ return 2 * 3;
+ case QShaderDescription::DMat2x4:
+ return 2 * 4;
+ case QShaderDescription::DMat3:
+ return 3 * 3;
+ case QShaderDescription::DMat3x2:
+ return 3 * 2;
+ case QShaderDescription::DMat3x4:
+ return 3 * 4;
+ case QShaderDescription::DMat4:
+ return 4 * 4;
+ case QShaderDescription::DMat4x2:
+ return 4 * 2;
+ case QShaderDescription::DMat4x3:
+ return 4 * 3;
+
+ case QShaderDescription::Sampler1D:
+ return 0;
+ case QShaderDescription::Sampler2D:
+ return 0;
+ case QShaderDescription::Sampler2DMS:
+ return 0;
+ case QShaderDescription::Sampler3D:
+ return 0;
+ case QShaderDescription::SamplerCube:
+ return 0;
+ case QShaderDescription::Sampler1DArray:
+ return 0;
+ case QShaderDescription::Sampler2DArray:
+ return 0;
+ case QShaderDescription::Sampler2DMSArray:
+ return 0;
+ case QShaderDescription::Sampler3DArray:
+ return 0;
+ case QShaderDescription::SamplerCubeArray:
+ return 0;
+ case QShaderDescription::SamplerRect:
+ return 0;
+ case QShaderDescription::SamplerBuffer:
+ return 0;
+
+ case QShaderDescription::Image1D:
+ return 0;
+ case QShaderDescription::Image2D:
+ return 0;
+ case QShaderDescription::Image2DMS:
+ return 0;
+ case QShaderDescription::Image3D:
+ return 0;
+ case QShaderDescription::ImageCube:
+ return 0;
+ case QShaderDescription::Image1DArray:
+ return 0;
+ case QShaderDescription::Image2DArray:
+ return 0;
+ case QShaderDescription::Image2DMSArray:
+ return 0;
+ case QShaderDescription::Image3DArray:
+ return 0;
+ case QShaderDescription::ImageCubeArray:
+ return 0;
+ case QShaderDescription::ImageRect:
+ return 0;
+ case QShaderDescription::ImageBuffer:
+ return 0;
+
+ case QShaderDescription::Struct:
+ return 0;
+ default:
+ return 0;
+ }
+}
+
+template<typename T, typename Pred>
+QVector<T> stableRemoveDuplicates(QVector<T> in, Pred predicate)
+{
+ QVector<T> out;
+ for (const auto &element : in) {
+ if (std::none_of(out.begin(), out.end(),
+ [&](T &other) { return predicate(element, other); }))
+ out.push_back(element);
+ }
+ return out;
+}
+
+// Utility function to enumerate an array of dimensions
+// Given dims == [0, 3, 2] and maxs == [4, 4, 4]
+// changes dims into [0, 3, 3]
+// Given dims == [0, 3, 3] and maxs == [4, 4, 4]
+// changes dims into [1, 0, 0]
+bool incrementArray(QVarLengthArray<int> &dims, const QVector<int> &maxs)
+{
+ const int n = dims.size();
+ int i = n;
+ for (; i-- > 0;) {
+ if (dims[i] == maxs[i] - 1) {
+ if (i == 0) {
+ // we're done
+ return false;
+ }
+ continue;
+
+ } else {
+ dims[i]++;
+ for (int j = i + 1; j < n; j++) {
+ dims[j] = 0;
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+// Call a function with a string such as [0][3][2]
+// for all valable array values, given an array of dimension sizes.
+// Dimensions must all be >= 1
+template<typename F>
+void forEachArrayAccessor(const QVector<int> &maxs, F f)
+{
+ if (std::any_of(maxs.begin(), maxs.end(), [](int v) { return v <= 0; }))
+ return;
+
+ QVarLengthArray<int> dims;
+ dims.resize(maxs.size());
+
+ // QVarLengthArray does not initialize ints
+ std::fill(dims.begin(), dims.end(), 0);
+
+ QString str;
+
+ do {
+ str.resize(0);
+ for (int k : dims) {
+ str += QStringLiteral("[%1]").arg(k);
+ }
+ f(str);
+ } while (incrementArray(dims, maxs));
+}
+}
+
+void RHIShader::recordAllUniforms(const QShaderDescription::BlockVariable &member,
+ QString parentName)
+{
+ const bool isStruct = !member.structMembers.empty();
+ const bool isArray = !member.arrayDims.empty();
+
+ // "foo.bar"
+ const QString fullMemberName = parentName + member.name;
+ m_unqualifiedUniformNames << fullMemberName;
+
+ if (isStruct && !isArray) {
+ m_structNames << fullMemberName;
+ m_structNamesIds << StringToInt::lookupId(fullMemberName);
+
+ for (const QShaderDescription::BlockVariable& bv : member.structMembers) {
+ // recordAllUniforms("baz", "foo.bar.")
+ recordAllUniforms(bv, fullMemberName + QLatin1Char('.'));
+ }
+ } else if (!isStruct && isArray) {
+ // We iterate through all the [l][n][m] by building [0][0][0] and incrementing
+ forEachArrayAccessor(member.arrayDims, [&](const QString &str) {
+ // "foo.bar[1][2]"
+ m_unqualifiedUniformNames << (fullMemberName + str);
+ // Question : does it make sense to also record foo[0], foo[0][0], etc...
+ // if there are e.g. 3 dimensions ?
+ });
+ }
+ else if (isStruct && isArray) {
+ // Record the struct names
+ forEachArrayAccessor(member.arrayDims, [&] (const QString& str) {
+ m_structNames << (fullMemberName + str);
+ m_structNamesIds << StringToInt::lookupId(m_structNames.back());
+ });
+
+ // Record the struct members
+ for (const QShaderDescription::BlockVariable& bv : member.structMembers) {
+ forEachArrayAccessor(member.arrayDims, [&] (const QString& str) {
+ //recordAllUniforms("baz", "foo.bar[1][2].")
+ recordAllUniforms(bv, fullMemberName + str + QLatin1Char('.'));
+ });
+ }
+ }
+}
+
+void RHIShader::introspect()
+{
+ const thread_local QRegularExpression generatedUBOName { "_[0-9]+" };
+ QVector<QShaderDescription::UniformBlock> rhiUBO;
+ QVector<QShaderDescription::StorageBlock> rhiSSBO;
+
+ QVector<ShaderUniformBlock> uniformBlocks;
+ QVector<ShaderStorageBlock> storageBlocks;
+ QVector<ShaderAttribute> attributes;
+ QVector<ShaderAttribute> samplers;
+ QVector<ShaderAttribute> images;
+
+ // Introspect shader vertex input
+ if (m_stages[QShader::VertexStage].isValid()) {
+ const QShaderDescription &vtx = m_stages[QShader::VertexStage].description();
+
+ for (const QShaderDescription::InOutVariable &input : vtx.inputVariables()) {
+ attributes.push_back(ShaderAttribute { input.name, StringToInt::lookupId(input.name),
+ input.type, rhiTypeSize(input.type),
+ input.location });
+ }
+
+ rhiUBO += vtx.uniformBlocks();
+ rhiSSBO += vtx.storageBlocks();
+ }
+
+ // Introspect shader uniforms
+
+ if (m_stages[QShader::FragmentStage].isValid()) {
+ const QShaderDescription &frag = m_stages[QShader::FragmentStage].description();
+ for (const QShaderDescription::InOutVariable &sampler : frag.combinedImageSamplers()) {
+ samplers.push_back(ShaderAttribute { sampler.name, StringToInt::lookupId(sampler.name),
+ sampler.type, rhiTypeSize(sampler.type),
+ sampler.binding });
+ }
+ for (const QShaderDescription::InOutVariable &image : frag.storageImages()) {
+ images.push_back(ShaderAttribute { image.name, StringToInt::lookupId(image.name),
+ image.type, rhiTypeSize(image.type),
+ image.binding });
+ }
+
+ rhiUBO += frag.uniformBlocks();
+ rhiSSBO += frag.storageBlocks();
+ }
+
+ rhiUBO = stableRemoveDuplicates(rhiUBO,
+ [](const QShaderDescription::UniformBlock &lhs,
+ const QShaderDescription::UniformBlock &rhs) {
+ return lhs.blockName == rhs.blockName;
+ });
+ rhiSSBO = stableRemoveDuplicates(rhiSSBO,
+ [](const QShaderDescription::StorageBlock &lhs,
+ const QShaderDescription::StorageBlock &rhs) {
+ return lhs.blockName == rhs.blockName;
+ });
+
+ for (const QShaderDescription::UniformBlock &ubo : rhiUBO) {
+ uniformBlocks.push_back(ShaderUniformBlock { ubo.blockName,
+ StringToInt::lookupId(ubo.blockName), -1,
+ ubo.binding, ubo.members.size(), ubo.size });
+ const bool addUnqualifiedUniforms = ubo.structName.contains(generatedUBOName);
+
+ // Parse Uniform Block members so that we can later on map a Parameter name to an actual
+ // member
+ const QVector<QShaderDescription::BlockVariable> members = ubo.members;
+
+ QVector<int> namesIds;
+ namesIds.reserve(members.size());
+
+ for (const QShaderDescription::BlockVariable &member : members) {
+ namesIds << StringToInt::lookupId(member.name);
+ if (addUnqualifiedUniforms) {
+ recordAllUniforms(member, QStringLiteral(""));
+ }
+ }
+ m_uniformsNamesIds += namesIds;
+ m_uboMembers.push_back({ uniformBlocks.last(), members });
+ }
+
+ for (const QShaderDescription::StorageBlock &ssbo : rhiSSBO) {
+ storageBlocks.push_back(ShaderStorageBlock { ssbo.blockName, -1, -1, ssbo.binding, 0, 0 });
+ }
+
+ initializeAttributes(attributes);
+ initializeUniformBlocks(uniformBlocks);
+ initializeShaderStorageBlocks(storageBlocks);
+ initializeSamplers(samplers);
+ initializeImages(images);
+}
+
+QHash<QString, ShaderUniform> RHIShader::activeUniformsForUniformBlock(int blockIndex) const
+{
+ return m_uniformBlockIndexToShaderUniforms.value(blockIndex);
+}
+
+ShaderUniformBlock RHIShader::uniformBlockForBlockIndex(int blockIndex) const noexcept
+{
+ for (int i = 0, m = m_uniformBlocks.size(); i < m; ++i) {
+ if (m_uniformBlocks[i].m_index == blockIndex) {
+ return m_uniformBlocks[i];
+ }
+ }
+ return ShaderUniformBlock();
+}
+
+ShaderUniformBlock RHIShader::uniformBlockForBlockNameId(int blockNameId) const noexcept
+{
+ for (int i = 0, m = m_uniformBlocks.size(); i < m; ++i) {
+ if (m_uniformBlocks[i].m_nameId == blockNameId) {
+ return m_uniformBlocks[i];
+ }
+ }
+ return ShaderUniformBlock();
+}
+
+ShaderUniformBlock RHIShader::uniformBlockForBlockName(const QString &blockName) const noexcept
+{
+ for (int i = 0, m = m_uniformBlocks.size(); i < m; ++i) {
+ if (m_uniformBlocks[i].m_name == blockName) {
+ return m_uniformBlocks[i];
+ }
+ }
+ return ShaderUniformBlock();
+}
+
+ShaderStorageBlock RHIShader::storageBlockForBlockIndex(int blockIndex) const noexcept
+{
+ for (int i = 0, m = m_shaderStorageBlockNames.size(); i < m; ++i) {
+ if (m_shaderStorageBlocks[i].m_index == blockIndex)
+ return m_shaderStorageBlocks[i];
+ }
+ return ShaderStorageBlock();
+}
+
+ShaderStorageBlock RHIShader::storageBlockForBlockNameId(int blockNameId) const noexcept
+{
+ for (int i = 0, m = m_shaderStorageBlockNames.size(); i < m; ++i) {
+ if (m_shaderStorageBlocks[i].m_nameId == blockNameId)
+ return m_shaderStorageBlocks[i];
+ }
+ return ShaderStorageBlock();
+}
+
+ShaderStorageBlock RHIShader::storageBlockForBlockName(const QString &blockName) const noexcept
+{
+ for (int i = 0, m = m_shaderStorageBlockNames.size(); i < m; ++i) {
+ if (m_shaderStorageBlocks[i].m_name == blockName)
+ return m_shaderStorageBlocks[i];
+ }
+ return ShaderStorageBlock();
+}
+
+RHIShader::ParameterKind RHIShader::categorizeVariable(int nameId) const noexcept
+{
+ if (m_uniformsNamesIds.contains(nameId))
+ return ParameterKind::Uniform;
+ else if (m_uniformBlockNamesIds.contains(nameId))
+ return ParameterKind::UBO;
+ else if (m_shaderStorageBlockNamesIds.contains(nameId))
+ return ParameterKind::SSBO;
+ else if (m_structNamesIds.contains(nameId))
+ return ParameterKind::Struct;
+ return ParameterKind::Uniform;
+}
+
+bool RHIShader::hasUniform(int nameId) const noexcept
+{
+ return m_uniformsNamesIds.contains(nameId);
+}
+
+bool RHIShader::hasActiveVariables() const noexcept
+{
+ return !m_attributeNamesIds.empty() || !m_uniformsNamesIds.empty()
+ || !m_uniformBlockNamesIds.empty() || !m_shaderStorageBlockNamesIds.empty();
+}
+
+void RHIShader::prepareUniforms(ShaderParameterPack &pack)
+{
+ const PackUniformHash &values = pack.uniforms();
+
+ auto it = values.keys.cbegin();
+ const auto end = values.keys.cend();
+
+ while (it != end) {
+ // Find if there's a uniform with the same name id
+ for (const ShaderUniform &uniform : qAsConst(m_uniforms)) {
+ if (uniform.m_nameId == *it) {
+ pack.setSubmissionUniform(uniform);
+ break;
+ }
+ }
+ ++it;
+ }
+}
+
+void RHIShader::setFragOutputs(const QHash<QString, int> &fragOutputs)
+{
+ {
+ QMutexLocker lock(&m_mutex);
+ m_fragOutputs = fragOutputs;
+ }
+ // updateDNA();
+}
+
+const QHash<QString, int> RHIShader::fragOutputs() const
+{
+ QMutexLocker lock(&m_mutex);
+ return m_fragOutputs;
+}
+
+void RHIShader::initializeAttributes(const QVector<ShaderAttribute> &attributesDescription)
+{
+ m_attributes = attributesDescription;
+ m_attributesNames.resize(attributesDescription.size());
+ m_attributeNamesIds.resize(attributesDescription.size());
+ for (int i = 0, m = attributesDescription.size(); i < m; i++) {
+ m_attributesNames[i] = attributesDescription[i].m_name;
+ m_attributes[i].m_nameId = StringToInt::lookupId(m_attributesNames[i]);
+ m_attributeNamesIds[i] = m_attributes[i].m_nameId;
+ qCDebug(Shaders) << "Active Attribute " << attributesDescription[i].m_name;
+ }
+}
+
+void RHIShader::initializeSamplers(const QVector<ShaderAttribute> &samplersDescription)
+{
+ m_samplers = samplersDescription;
+ m_samplerNames.resize(samplersDescription.size());
+ m_samplerIds.resize(samplersDescription.size());
+ for (int i = 0, m = samplersDescription.size(); i < m; i++) {
+ m_samplerNames[i] = samplersDescription[i].m_name;
+ m_samplers[i].m_nameId = StringToInt::lookupId(m_samplerNames[i]);
+ m_samplerIds[i] = m_samplers[i].m_nameId;
+ qCDebug(Shaders) << "Active sampler " << samplersDescription[i].m_name;
+ }
+}
+
+void RHIShader::initializeImages(const QVector<ShaderAttribute> &imagesDescription)
+{
+ m_images = imagesDescription;
+ m_imageNames.resize(imagesDescription.size());
+ m_imageIds.resize(imagesDescription.size());
+ for (int i = 0, m = imagesDescription.size(); i < m; i++) {
+ m_imageNames[i] = imagesDescription[i].m_name;
+ m_images[i].m_nameId = StringToInt::lookupId(m_imageNames[i]);
+ m_imageIds[i] = m_images[i].m_nameId;
+ qCDebug(Shaders) << "Active image " << imagesDescription[i].m_name;
+ }
+}
+
+void RHIShader::initializeUniformBlocks(const QVector<ShaderUniformBlock> &uniformBlockDescription)
+{
+ m_uniformBlocks = uniformBlockDescription;
+ m_uniformBlockNames.resize(uniformBlockDescription.size());
+ m_uniformBlockNamesIds.resize(uniformBlockDescription.size());
+ for (int i = 0, m = uniformBlockDescription.size(); i < m; ++i) {
+ m_uniformBlockNames[i] = m_uniformBlocks[i].m_name;
+ m_uniformBlockNamesIds[i] = StringToInt::lookupId(m_uniformBlockNames[i]);
+ m_uniformBlocks[i].m_nameId = m_uniformBlockNamesIds[i];
+ qCDebug(Shaders) << "Initializing Uniform Block {" << m_uniformBlockNames[i] << "}";
+
+ // Find all active uniforms for the shader block
+ QVector<ShaderUniform>::const_iterator uniformsIt = m_uniforms.cbegin();
+ const QVector<ShaderUniform>::const_iterator uniformsEnd = m_uniforms.cend();
+
+ QVector<QString>::const_iterator uniformNamesIt = m_uniformsNames.cbegin();
+ const QVector<QString>::const_iterator uniformNamesEnd = m_attributesNames.cend();
+
+ QHash<QString, ShaderUniform> activeUniformsInBlock;
+
+ while (uniformsIt != uniformsEnd && uniformNamesIt != uniformNamesEnd) {
+ if (uniformsIt->m_blockIndex == uniformBlockDescription[i].m_index) {
+ QString uniformName = *uniformNamesIt;
+ if (!m_uniformBlockNames[i].isEmpty()
+ && !uniformName.startsWith(m_uniformBlockNames[i]))
+ uniformName = m_uniformBlockNames[i] + QLatin1Char('.') + *uniformNamesIt;
+ activeUniformsInBlock.insert(uniformName, *uniformsIt);
+ qCDebug(Shaders) << "Active Uniform Block " << uniformName << " in block "
+ << m_uniformBlockNames[i] << " at index "
+ << uniformsIt->m_blockIndex;
+ }
+ ++uniformsIt;
+ ++uniformNamesIt;
+ }
+ m_uniformBlockIndexToShaderUniforms.insert(uniformBlockDescription[i].m_index,
+ activeUniformsInBlock);
+ }
+}
+
+void RHIShader::initializeShaderStorageBlocks(
+ const QVector<ShaderStorageBlock> &shaderStorageBlockDescription)
+{
+ m_shaderStorageBlocks = shaderStorageBlockDescription;
+ m_shaderStorageBlockNames.resize(shaderStorageBlockDescription.size());
+ m_shaderStorageBlockNamesIds.resize(shaderStorageBlockDescription.size());
+
+ for (int i = 0, m = shaderStorageBlockDescription.size(); i < m; ++i) {
+ m_shaderStorageBlockNames[i] = m_shaderStorageBlocks[i].m_name;
+ m_shaderStorageBlockNamesIds[i] = StringToInt::lookupId(m_shaderStorageBlockNames[i]);
+ m_shaderStorageBlocks[i].m_nameId = m_shaderStorageBlockNamesIds[i];
+ qCDebug(Shaders) << "Initializing Shader Storage Block {" << m_shaderStorageBlockNames[i]
+ << "}";
+ }
+}
+
+} // Rhi
+
+} // Render
+
+} // Qt3DRender
+
+QT_END_NAMESPACE
diff --git a/src/plugins/renderers/rhi/renderer/rhishader_p.h b/src/plugins/renderers/rhi/renderer/rhishader_p.h
new file mode 100644
index 000000000..774dcbdb0
--- /dev/null
+++ b/src/plugins/renderers/rhi/renderer/rhishader_p.h
@@ -0,0 +1,198 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 QT3DRENDER_RENDER_RHI_GLSHADER_P_H
+#define QT3DRENDER_RENDER_RHI_GLSHADER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <shadervariables_p.h>
+#include <shaderparameterpack_p.h>
+#include <Qt3DRender/qshaderprogram.h>
+#include <QMutex>
+#include <QtGui/private/qshader_p.h>
+#include <QtGui/private/qrhi_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+namespace Render {
+
+namespace Rhi {
+
+class Q_AUTOTEST_EXPORT RHIShader
+{
+public:
+ struct UBO_Member
+ {
+ ShaderUniformBlock block;
+ QVector<QShaderDescription::BlockVariable> members;
+ };
+
+ RHIShader();
+
+ bool isLoaded() const { return m_isLoaded; }
+ void setLoaded(bool loaded) { m_isLoaded = loaded; }
+
+ void prepareUniforms(ShaderParameterPack &pack);
+
+ void setFragOutputs(const QHash<QString, int> &fragOutputs);
+ const QHash<QString, int> fragOutputs() const;
+
+ inline QVector<int> uniformsNamesIds() const { return m_uniformsNamesIds; }
+ inline QVector<int> standardUniformNameIds() const { return m_standardUniformNamesIds; }
+ inline QVector<int> uniformBlockNamesIds() const { return m_uniformBlockNamesIds; }
+ inline QVector<int> storageBlockNamesIds() const { return m_shaderStorageBlockNamesIds; }
+ inline QVector<int> attributeNamesIds() const { return m_attributeNamesIds; }
+
+ QVector<QString> uniformsNames() const;
+ QVector<QString> attributesNames() const;
+ QVector<QString> uniformBlockNames() const;
+ QVector<QString> storageBlockNames() const;
+ QVector<QString> samplerNames() const;
+ QVector<QString> imagesNames() const;
+
+ inline QVector<ShaderUniform> uniforms() const { return m_uniforms; }
+ inline QVector<ShaderAttribute> attributes() const { return m_attributes; }
+ inline QVector<ShaderUniformBlock> uniformBlocks() const { return m_uniformBlocks; }
+ inline QVector<ShaderStorageBlock> storageBlocks() const { return m_shaderStorageBlocks; }
+ inline QVector<ShaderAttribute> samplers() const { return m_samplers; }
+ inline QVector<ShaderAttribute> images() const { return m_images; }
+
+ QHash<QString, ShaderUniform> activeUniformsForUniformBlock(int blockIndex) const;
+
+ ShaderUniformBlock uniformBlockForBlockIndex(int blockNameId) const noexcept;
+ ShaderUniformBlock uniformBlockForBlockNameId(int blockIndex) const noexcept;
+ ShaderUniformBlock uniformBlockForBlockName(const QString &blockName) const noexcept;
+
+ ShaderStorageBlock storageBlockForBlockIndex(int blockIndex) const noexcept;
+ ShaderStorageBlock storageBlockForBlockNameId(int blockNameId) const noexcept;
+ ShaderStorageBlock storageBlockForBlockName(const QString &blockName) const noexcept;
+
+ enum ParameterKind { Uniform, UBO, SSBO, Struct };
+ ParameterKind categorizeVariable(int nameId) const noexcept;
+
+ bool hasUniform(int nameId) const noexcept;
+ bool hasActiveVariables() const noexcept;
+
+ void setShaderCode(const QVector<QByteArray> shaderCode) { m_shaderCode = shaderCode; }
+ QVector<QByteArray> shaderCode() const;
+
+ const QShader &shaderStage(QShader::Stage stage) const noexcept { return m_stages[stage]; }
+ QVector<UBO_Member> uboMembers() const { return m_uboMembers; }
+
+ const QSet<QString> &unqualifiedUniformNames() const noexcept
+ {
+ return m_unqualifiedUniformNames;
+ }
+
+ void introspect();
+
+private:
+ bool m_isLoaded;
+ QShader m_stages[6];
+
+ QVector<QString> m_uniformsNames;
+ QVector<int> m_uniformsNamesIds;
+ QVector<int> m_standardUniformNamesIds;
+ QVector<ShaderUniform> m_uniforms;
+
+ QVector<QString> m_attributesNames;
+ QVector<int> m_attributeNamesIds;
+ QVector<ShaderAttribute> m_attributes;
+
+ QVector<QString> m_uniformBlockNames;
+ QVector<int> m_uniformBlockNamesIds;
+ QVector<ShaderUniformBlock> m_uniformBlocks;
+ QHash<int, QHash<QString, ShaderUniform>> m_uniformBlockIndexToShaderUniforms;
+ QSet<QString> m_unqualifiedUniformNames;
+
+ QVector<QString> m_shaderStorageBlockNames;
+ QVector<int> m_shaderStorageBlockNamesIds;
+ QVector<ShaderStorageBlock> m_shaderStorageBlocks;
+
+ QVector<QString> m_samplerNames;
+ QVector<int> m_samplerIds;
+ QVector<ShaderAttribute> m_samplers;
+
+ QVector<QString> m_imageNames;
+ QVector<int> m_imageIds;
+ QVector<ShaderAttribute> m_images;
+
+ QVector<QString> m_structNames;
+ QVector<int> m_structNamesIds;
+
+ QHash<QString, int> m_fragOutputs;
+ QVector<QByteArray> m_shaderCode;
+
+ // Private so that only SubmissionContext can call it
+ friend class SubmissionContext;
+ void initializeAttributes(const QVector<ShaderAttribute> &attributesDescription);
+ void initializeUniformBlocks(const QVector<ShaderUniformBlock> &uniformBlockDescription);
+ void
+ initializeShaderStorageBlocks(const QVector<ShaderStorageBlock> &shaderStorageBlockDescription);
+ void initializeSamplers(const QVector<ShaderAttribute> &samplerDescription);
+ void initializeImages(const QVector<ShaderAttribute> &imageDescription);
+ void recordAllUniforms(const QShaderDescription::BlockVariable &ubo, QString parentName);
+
+ QVector<UBO_Member> m_uboMembers;
+
+ mutable QMutex m_mutex;
+ QMetaObject::Connection m_contextConnection;
+};
+
+} // Rhi
+
+} // Render
+
+} // Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDER_RHI_GLSHADER_P_H
diff --git a/src/plugins/renderers/rhi/renderer/shaderparameterpack.cpp b/src/plugins/renderers/rhi/renderer/shaderparameterpack.cpp
new file mode 100644
index 000000000..5a28d28d4
--- /dev/null
+++ b/src/plugins/renderers/rhi/renderer/shaderparameterpack.cpp
@@ -0,0 +1,114 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 "shaderparameterpack_p.h"
+
+#include <submissioncontext_p.h>
+#include <Qt3DRender/private/texture_p.h>
+
+#include <Qt3DCore/private/qframeallocator_p.h>
+
+#include <QOpenGLShaderProgram>
+#include <QDebug>
+#include <QColor>
+#include <QQuaternion>
+#include <Qt3DRender/private/renderlogging_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+namespace Render {
+namespace Rhi {
+
+ShaderParameterPack::~ShaderParameterPack() { }
+
+void ShaderParameterPack::setUniform(const int glslNameId, const UniformValue &val)
+{
+ m_uniforms.insert(glslNameId, val);
+}
+
+void ShaderParameterPack::setTexture(const int glslNameId, int uniformArrayIndex,
+ Qt3DCore::QNodeId texId)
+{
+ for (NamedResource &texture : m_textures) {
+ if (texture.glslNameId != glslNameId || texture.uniformArrayIndex != uniformArrayIndex)
+ continue;
+
+ texture.nodeId = texId;
+ return;
+ }
+
+ m_textures.append(NamedResource(glslNameId, texId, uniformArrayIndex, NamedResource::Texture));
+}
+
+void ShaderParameterPack::setImage(const int glslNameId, int uniformArrayIndex,
+ Qt3DCore::QNodeId id)
+{
+ for (NamedResource &image : m_images) {
+ if (image.glslNameId != glslNameId || image.uniformArrayIndex != uniformArrayIndex)
+ continue;
+
+ image.nodeId = id;
+ return;
+ }
+
+ m_images.append(NamedResource(glslNameId, id, uniformArrayIndex, NamedResource::Image));
+}
+
+// Contains Uniform Block Index and QNodeId of the ShaderData (UBO)
+void ShaderParameterPack::setUniformBuffer(BlockToUBO blockToUBO)
+{
+ m_uniformBuffers.append(std::move(blockToUBO));
+}
+
+void ShaderParameterPack::setShaderStorageBuffer(BlockToSSBO blockToSSBO)
+{
+ m_shaderStorageBuffers.push_back(blockToSSBO);
+}
+
+void ShaderParameterPack::setSubmissionUniform(const ShaderUniform &uniform)
+{
+ m_submissionUniforms.push_back(uniform);
+}
+
+} // namespace Rhi
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
diff --git a/src/plugins/renderers/rhi/renderer/shaderparameterpack_p.h b/src/plugins/renderers/rhi/renderer/shaderparameterpack_p.h
new file mode 100644
index 000000000..94c485a94
--- /dev/null
+++ b/src/plugins/renderers/rhi/renderer/shaderparameterpack_p.h
@@ -0,0 +1,215 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 QT3DRENDER_RENDER_RHI_SHADERPARAMETERPACK_P_H
+#define QT3DRENDER_RENDER_RHI_SHADERPARAMETERPACK_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QVariant>
+#include <QByteArray>
+#include <QVector>
+#include <QOpenGLShaderProgram>
+#include <Qt3DCore/qnodeid.h>
+#include <Qt3DRender/private/renderlogging_p.h>
+#include <Qt3DRender/private/uniform_p.h>
+#include <shadervariables_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QOpenGLShaderProgram;
+
+namespace Qt3DCore {
+class QFrameAllocator;
+}
+
+namespace Qt3DRender {
+namespace Render {
+namespace Rhi {
+
+class GraphicsContext;
+
+struct BlockToUBO
+{
+ int m_blockIndex;
+ Qt3DCore::QNodeId m_bufferID;
+ bool m_needsUpdate;
+ QHash<QString, QVariant> m_updatedProperties;
+};
+QT3D_DECLARE_TYPEINFO_3(Qt3DRender, Render, Rhi, BlockToUBO, Q_MOVABLE_TYPE)
+
+struct BlockToSSBO
+{
+ int m_blockIndex;
+ int m_bindingIndex;
+ Qt3DCore::QNodeId m_bufferID;
+};
+QT3D_DECLARE_TYPEINFO_3(Qt3DRender, Render, Rhi, BlockToSSBO, Q_PRIMITIVE_TYPE)
+
+struct PackUniformHash
+{
+ QVector<int> keys;
+ QVector<UniformValue> values;
+
+ PackUniformHash()
+ {
+ keys.reserve(10);
+ values.reserve(10);
+ }
+
+ void insert(int key, const UniformValue &value)
+ {
+ const int idx = keys.indexOf(key);
+ if (idx != -1) {
+ values[idx] = value;
+ } else {
+ keys.push_back(key);
+ values.push_back(value);
+ }
+ }
+
+ UniformValue value(int key) const
+ {
+ const int idx = keys.indexOf(key);
+ if (idx != -1)
+ return values.at(idx);
+ return UniformValue();
+ }
+
+ UniformValue &value(int key)
+ {
+ const int idx = keys.indexOf(key);
+ if (idx != -1)
+ return values[idx];
+ insert(key, UniformValue());
+ return value(key);
+ }
+
+ void erase(int idx)
+ {
+ keys.removeAt(idx);
+ values.removeAt(idx);
+ }
+
+ bool contains(int key) const { return keys.contains(key); }
+};
+
+class Q_AUTOTEST_EXPORT ShaderParameterPack
+{
+public:
+ ~ShaderParameterPack();
+
+ void setUniform(const int glslNameId, const UniformValue &val);
+ void setTexture(const int glslNameId, int uniformArrayIndex, Qt3DCore::QNodeId id);
+ void setImage(const int glslNameId, int uniformArrayIndex, Qt3DCore::QNodeId id);
+
+ void setUniformBuffer(BlockToUBO blockToUBO);
+ void setShaderStorageBuffer(BlockToSSBO blockToSSBO);
+ void setSubmissionUniform(const ShaderUniform &uniform);
+
+ inline PackUniformHash &uniforms() { return m_uniforms; }
+ inline const PackUniformHash &uniforms() const { return m_uniforms; }
+ UniformValue uniform(const int glslNameId) const { return m_uniforms.value(glslNameId); }
+
+ struct NamedResource
+ {
+ enum Type { Texture = 0, Image };
+
+ NamedResource() { }
+ NamedResource(const int glslNameId, Qt3DCore::QNodeId texId, int uniformArrayIndex,
+ Type type)
+ : glslNameId(glslNameId),
+ nodeId(texId),
+ uniformArrayIndex(uniformArrayIndex),
+ type(type)
+ {
+ }
+
+ int glslNameId;
+ Qt3DCore::QNodeId nodeId;
+ int uniformArrayIndex;
+ Type type;
+
+ bool operator==(const NamedResource &other) const
+ {
+ return glslNameId == other.glslNameId && nodeId == other.nodeId
+ && uniformArrayIndex == other.uniformArrayIndex && type == other.type;
+ }
+
+ bool operator!=(const NamedResource &other) const { return !(*this == other); }
+ };
+
+ inline QVector<NamedResource> textures() const { return m_textures; }
+ inline QVector<NamedResource> images() const { return m_images; }
+ inline QVector<BlockToUBO> uniformBuffers() const { return m_uniformBuffers; }
+ inline QVector<BlockToSSBO> shaderStorageBuffers() const { return m_shaderStorageBuffers; }
+ inline QVector<ShaderUniform> submissionUniforms() const { return m_submissionUniforms; }
+
+private:
+ PackUniformHash m_uniforms;
+
+ QVector<NamedResource> m_textures;
+ QVector<NamedResource> m_images;
+ QVector<BlockToUBO> m_uniformBuffers;
+ QVector<BlockToSSBO> m_shaderStorageBuffers;
+ QVector<ShaderUniform> m_submissionUniforms;
+
+ friend class RenderView;
+};
+QT3D_DECLARE_TYPEINFO_3(Qt3DRender, Render, Rhi, ShaderParameterPack::NamedResource,
+ Q_PRIMITIVE_TYPE)
+
+} // namespace Rhi
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+Q_DECLARE_METATYPE(Qt3DRender::Render::Rhi::ShaderParameterPack)
+
+#endif // QT3DRENDER_RENDER_RHI_SHADERPARAMETERPACK_P_H
diff --git a/src/plugins/renderers/rhi/renderer/shadervariables_p.h b/src/plugins/renderers/rhi/renderer/shadervariables_p.h
new file mode 100644
index 000000000..4e710a92c
--- /dev/null
+++ b/src/plugins/renderers/rhi/renderer/shadervariables_p.h
@@ -0,0 +1,126 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 QT3DRENDER_RENDER_RHI_SHADERVARIABLES_P_H
+#define QT3DRENDER_RENDER_RHI_SHADERVARIABLES_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <Qt3DRender/qt3drender_global.h>
+#include <QtGui/private/qshaderdescription_p.h>
+#include <QOpenGLContext>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+namespace Render {
+
+namespace Rhi {
+
+struct ShaderAttribute
+{
+ QString m_name;
+ int m_nameId { -1 };
+ QShaderDescription::VariableType m_type {};
+ int m_size {};
+ int m_location { -1 };
+};
+QT3D_DECLARE_TYPEINFO_3(Qt3DRender, Render, Rhi, ShaderAttribute, Q_MOVABLE_TYPE)
+
+struct ShaderUniform
+{
+ QString m_name;
+ int m_nameId { -1 };
+ QShaderDescription::VariableType m_type { QShaderDescription::Unknown };
+ int m_size { 0 };
+ int m_offset { -1 }; // -1 default, >= 0 if uniform defined in uniform block
+ int m_location { -1 }; // -1 if uniform defined in a uniform block
+ int m_blockIndex { -1 }; // -1 is the default, >= 0 if uniform defined in uniform block
+ int m_arrayStride {
+ -1
+ }; // -1 is the default, >= 0 if uniform defined in uniform block and if it's an array
+ int m_matrixStride {
+ -1
+ }; // -1 is the default, >= 0 uniform defined in uniform block and is a matrix
+ uint m_rawByteSize { 0 }; // contains byte size (size / type / strides)
+ // size, offset and strides are in bytes
+};
+QT3D_DECLARE_TYPEINFO_3(Qt3DRender, Render, Rhi, ShaderUniform, Q_MOVABLE_TYPE)
+
+struct ShaderUniformBlock
+{
+ QString m_name;
+ int m_nameId { -1 };
+ int m_index { -1 };
+ int m_binding { -1 };
+ int m_activeUniformsCount { 0 };
+ int m_size { 0 };
+};
+QT3D_DECLARE_TYPEINFO_3(Qt3DRender, Render, Rhi, ShaderUniformBlock, Q_MOVABLE_TYPE)
+
+struct ShaderStorageBlock
+{
+ QString m_name;
+ int m_nameId { -1 };
+ int m_index { -1 };
+ int m_binding { -1 };
+ int m_size { 0 };
+ int m_activeVariablesCount { 0 };
+};
+QT3D_DECLARE_TYPEINFO_3(Qt3DRender, Render, Rhi, ShaderStorageBlock, Q_MOVABLE_TYPE)
+
+} // namespace Rhi
+
+} // namespace Render
+
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDER_RHI_SHADERVARIABLES_P_H
diff --git a/src/plugins/renderers/rhi/rhi.pri b/src/plugins/renderers/rhi/rhi.pri
new file mode 100644
index 000000000..8ae4a11af
--- /dev/null
+++ b/src/plugins/renderers/rhi/rhi.pri
@@ -0,0 +1,17 @@
+
+include (renderer/renderer.pri)
+include (jobs/jobs.pri)
+include (io/io.pri)
+include (textures/textures.pri)
+include (graphicshelpers/graphicshelpers.pri)
+include (managers/managers.pri)
+
+INCLUDEPATH += $$PWD
+
+# Qt3D is free of Q_FOREACH - make sure it stays that way:
+DEFINES += QT_NO_FOREACH
+
+gcov {
+ QMAKE_CXXFLAGS += -fprofile-arcs -ftest-coverage
+ QMAKE_LFLAGS += -fprofile-arcs -ftest-coverage
+}
diff --git a/src/plugins/renderers/rhi/rhi.pro b/src/plugins/renderers/rhi/rhi.pro
new file mode 100644
index 000000000..786749a71
--- /dev/null
+++ b/src/plugins/renderers/rhi/rhi.pro
@@ -0,0 +1,32 @@
+TARGET = rhirenderer
+
+PLUGIN_TYPE = renderers
+PLUGIN_CLASS_NAME = RhiRendererPlugin
+load(qt_plugin)
+
+QT += core-private gui-private 3dcore 3dcore-private 3drender 3drender-private shadertools shadertools-private
+
+# Qt3D is free of Q_FOREACH - make sure it stays that way:
+DEFINES += QT_NO_FOREACH
+
+# We use QT_AUTOTEST_EXPORT to test the plug-ins, which needs QT_BUILDING_QT
+DEFINES += QT_BUILDING_QT
+
+SOURCES += \
+ main.cpp
+
+DISTFILES += \
+ rhirenderer.json
+
+include(rhi.pri)
+
+qtConfig(qt3d-simd-avx2) {
+ CONFIG += simd
+ QMAKE_CXXFLAGS += $$QMAKE_CFLAGS_AVX2
+}
+
+qtConfig(qt3d-simd-sse2):!qtConfig(qt3d-simd-avx2) {
+ CONFIG += simd
+ QMAKE_CXXFLAGS += $$QMAKE_CFLAGS_SSE2
+}
+
diff --git a/src/plugins/renderers/rhi/rhirenderer.json b/src/plugins/renderers/rhi/rhirenderer.json
new file mode 100644
index 000000000..145b20636
--- /dev/null
+++ b/src/plugins/renderers/rhi/rhirenderer.json
@@ -0,0 +1,3 @@
+{
+ "Keys": ["rhi"]
+}
diff --git a/src/plugins/renderers/rhi/textures/renderbuffer.cpp b/src/plugins/renderers/rhi/textures/renderbuffer.cpp
new file mode 100644
index 000000000..8df256ad1
--- /dev/null
+++ b/src/plugins/renderers/rhi/textures/renderbuffer.cpp
@@ -0,0 +1,111 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 "renderbuffer_p.h"
+#include <QtGui/QOpenGLContext>
+#include <QtGui/QOpenGLFunctions>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+namespace Render {
+namespace Rhi {
+
+RenderBuffer::RenderBuffer(int width, int height, QAbstractTexture::TextureFormat format)
+ : m_size(width, height), m_format(format), m_renderBuffer(0), m_context(nullptr)
+{
+ QOpenGLContext *ctx = QOpenGLContext::currentContext();
+ if (!ctx) {
+ qWarning("Renderbuffer requires an OpenGL context");
+ return;
+ }
+
+ m_context = ctx;
+ QOpenGLFunctions *f = ctx->functions();
+ f->glGenRenderbuffers(1, &m_renderBuffer);
+ if (!m_renderBuffer)
+ return;
+
+ f->glBindRenderbuffer(GL_RENDERBUFFER, m_renderBuffer);
+ while (f->glGetError() != GL_NO_ERROR) { }
+ f->glRenderbufferStorage(GL_RENDERBUFFER, format, width, height);
+ GLint err = f->glGetError();
+ if (err != GL_NO_ERROR)
+ qWarning("Failed to set renderbuffer storage: error 0x%x", err);
+ f->glBindRenderbuffer(GL_RENDERBUFFER, 0);
+}
+
+RenderBuffer::~RenderBuffer()
+{
+ if (m_renderBuffer) {
+ QOpenGLContext *ctx = QOpenGLContext::currentContext();
+
+ // Ignore the fact that renderbuffers are sharable resources and let's
+ // just expect that the context is the same as when the resource was
+ // created. QOpenGLTexture suffers from the same limitation anyway, and
+ // this is unlikely to become an issue within Qt 3D.
+ if (ctx == m_context) {
+ ctx->functions()->glDeleteRenderbuffers(1, &m_renderBuffer);
+ } else {
+ qWarning("Wrong current context; renderbuffer not destroyed");
+ }
+ }
+}
+
+void RenderBuffer::bind()
+{
+ if (!m_renderBuffer)
+ return;
+
+ m_context->functions()->glBindRenderbuffer(GL_RENDERBUFFER, m_renderBuffer);
+}
+
+void RenderBuffer::release()
+{
+ if (!m_context)
+ return;
+
+ m_context->functions()->glBindRenderbuffer(GL_RENDERBUFFER, 0);
+}
+
+} // namespace Rhi
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
diff --git a/src/plugins/renderers/rhi/textures/renderbuffer_p.h b/src/plugins/renderers/rhi/textures/renderbuffer_p.h
new file mode 100644
index 000000000..ed043366e
--- /dev/null
+++ b/src/plugins/renderers/rhi/textures/renderbuffer_p.h
@@ -0,0 +1,92 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 QT3DRENDER_RENDER_RHI_RENDERBUFFER_P_H
+#define QT3DRENDER_RENDER_RHI_RENDERBUFFER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <Qt3DRender/qabstracttexture.h>
+
+QT_BEGIN_NAMESPACE
+
+class QOpenGLContext;
+
+namespace Qt3DRender {
+namespace Render {
+namespace Rhi {
+
+class Q_AUTOTEST_EXPORT RenderBuffer
+{
+public:
+ RenderBuffer(int width, int height, QAbstractTexture::TextureFormat format);
+ ~RenderBuffer();
+
+ int width() const { return m_size.width(); }
+ int height() const { return m_size.height(); }
+ QSize size() const { return m_size; }
+ QAbstractTexture::TextureFormat format() const { return m_format; }
+ GLuint renderBufferId() const { return m_renderBuffer; }
+
+ void bind();
+ void release();
+
+private:
+ QSize m_size;
+ QAbstractTexture::TextureFormat m_format;
+ GLuint m_renderBuffer;
+ QOpenGLContext *m_context;
+};
+
+} // namespace Rhi
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDER_RHI_RENDERBUFFER_P_H
diff --git a/src/plugins/renderers/rhi/textures/texture.cpp b/src/plugins/renderers/rhi/textures/texture.cpp
new file mode 100644
index 000000000..53f9e62ca
--- /dev/null
+++ b/src/plugins/renderers/rhi/textures/texture.cpp
@@ -0,0 +1,918 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 <QtCore/qhash.h>
+#include "texture_p.h"
+
+#include <private/qdebug_p.h>
+#include <private/qrhi_p.h>
+#include <QDebug>
+#include <Qt3DRender/qtexture.h>
+#include <Qt3DRender/qtexturedata.h>
+#include <Qt3DRender/qtextureimagedata.h>
+#include <Qt3DRender/private/managers_p.h>
+#include <Qt3DRender/private/qabstracttexture_p.h>
+#include <Qt3DRender/private/qtextureimagedata_p.h>
+#include <renderbuffer_p.h>
+#include <submissioncontext_p.h>
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt3DCore;
+
+namespace Qt3DRender {
+namespace Render {
+namespace Rhi {
+
+namespace {
+
+bool issRGBFormat(QAbstractTexture::TextureFormat format) noexcept
+{
+ switch (format) {
+ case QAbstractTexture::SRGB8:
+ case QAbstractTexture::SRGB8_ETC2:
+ case QAbstractTexture::SRGB8_PunchThrough_Alpha1_ETC2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+QRhiTexture::Format rhiFormatFromTextureFormat(QAbstractTexture::TextureFormat format) noexcept
+{
+ switch (format) {
+ case QAbstractTexture::RGBA8_UNorm:
+ case QAbstractTexture::SRGB8:
+ return QRhiTexture::RGBA8;
+ case QAbstractTexture::R8_UNorm:
+ return QRhiTexture::R8;
+ case QAbstractTexture::R16_UNorm:
+ return QRhiTexture::R16;
+ case QAbstractTexture::RGBA16F:
+ return QRhiTexture::RGBA16F;
+ case QAbstractTexture::RGBA32F:
+ return QRhiTexture::RGBA32F;
+ case QAbstractTexture::R16F:
+ return QRhiTexture::R16F;
+ case QAbstractTexture::R32F:
+ return QRhiTexture::R32F;
+ case QAbstractTexture::D16:
+ return QRhiTexture::D16;
+ case QAbstractTexture::D32F:
+ return QRhiTexture::D32F;
+ case QAbstractTexture::RGB_DXT1:
+ case QAbstractTexture::RGBA_DXT1:
+ return QRhiTexture::BC1;
+ case QAbstractTexture::RGBA_DXT3:
+ return QRhiTexture::BC2;
+ case QAbstractTexture::RGBA_DXT5:
+ return QRhiTexture::BC3;
+ case QAbstractTexture::RGB8_ETC2:
+ case QAbstractTexture::SRGB8_ETC2:
+ return QRhiTexture::ETC2_RGB8;
+ case QAbstractTexture::RGB8_PunchThrough_Alpha1_ETC2:
+ case QAbstractTexture::SRGB8_PunchThrough_Alpha1_ETC2:
+ return QRhiTexture::ETC2_RGB8A1;
+ case QAbstractTexture::RGBA8_ETC2_EAC:
+ return QRhiTexture::ETC2_RGBA8;
+ default:
+ Q_UNREACHABLE();
+ return QRhiTexture::UnknownFormat;
+ }
+}
+
+QRhiSampler::Filter rhiFilterFromTextureFilter(QAbstractTexture::Filter filter) noexcept
+{
+ switch (filter) {
+ case QAbstractTexture::Nearest:
+ case QAbstractTexture::NearestMipMapNearest:
+ case QAbstractTexture::NearestMipMapLinear:
+ return QRhiSampler::Nearest;
+ case QAbstractTexture::Linear:
+ case QAbstractTexture::LinearMipMapNearest:
+ case QAbstractTexture::LinearMipMapLinear:
+ return QRhiSampler::Linear;
+ default:
+ Q_UNREACHABLE();
+ return QRhiSampler::Nearest;
+ }
+}
+
+QRhiSampler::Filter rhiMipMapFilterFromTextureFilter(QAbstractTexture::Filter filter) noexcept
+{
+ switch (filter) {
+ case QAbstractTexture::NearestMipMapNearest:
+ case QAbstractTexture::LinearMipMapNearest:
+ return QRhiSampler::Nearest;
+ case QAbstractTexture::Linear:
+ case QAbstractTexture::NearestMipMapLinear:
+ case QAbstractTexture::LinearMipMapLinear:
+ return QRhiSampler::Linear;
+ default:
+ Q_UNREACHABLE();
+ return QRhiSampler::None;
+ }
+}
+
+std::tuple<QRhiSampler::AddressMode, QRhiSampler::AddressMode, QRhiSampler::AddressMode>
+rhiWrapModeFromTextureWrapMode(QTextureWrapMode::WrapMode x, QTextureWrapMode::WrapMode y,
+ QTextureWrapMode::WrapMode z) noexcept
+{
+ auto toRhiAddress = [](QTextureWrapMode::WrapMode mode) noexcept {
+ switch (mode) {
+ case Qt3DRender::QTextureWrapMode::Repeat:
+ return QRhiSampler::Repeat;
+ case Qt3DRender::QTextureWrapMode::ClampToEdge:
+ case Qt3DRender::QTextureWrapMode::ClampToBorder:
+ return QRhiSampler::ClampToEdge;
+ case Qt3DRender::QTextureWrapMode::MirroredRepeat:
+ return QRhiSampler::Mirror;
+ default:
+ Q_UNREACHABLE();
+ }
+ };
+
+ return { toRhiAddress(x), toRhiAddress(y), toRhiAddress(z) };
+}
+
+QRhiSampler::CompareOp
+rhiCompareOpFromTextureCompareOp(QAbstractTexture::ComparisonFunction mode) noexcept
+{
+ switch (mode) {
+ case QAbstractTexture::CompareLessEqual:
+ return QRhiSampler::LessOrEqual;
+ case QAbstractTexture::CompareGreaterEqual:
+ return QRhiSampler::GreaterOrEqual;
+ case QAbstractTexture::CompareLess:
+ return QRhiSampler::Less;
+ case QAbstractTexture::CompareGreater:
+ return QRhiSampler::Greater;
+ case QAbstractTexture::CompareEqual:
+ return QRhiSampler::Equal;
+ case QAbstractTexture::CommpareNotEqual:
+ return QRhiSampler::NotEqual;
+ case QAbstractTexture::CompareAlways:
+ return QRhiSampler::Always;
+ case QAbstractTexture::CompareNever:
+ return QRhiSampler::Never;
+ default:
+ return QRhiSampler::Always;
+ }
+}
+
+// This uploadGLData where the data is a fullsize subimage
+// as QOpenGLTexture doesn't allow partial subimage uploads
+QRhiTextureUploadEntry createUploadEntry(int level, int layer, const QByteArray &bytes) noexcept
+{
+ QRhiTextureSubresourceUploadDescription description;
+ description.setData(bytes);
+ return QRhiTextureUploadEntry(layer, level, description);
+}
+
+template<typename F>
+void filterLayersAndFaces(const QTextureImageData &data, F f)
+{
+ const int layers = data.layers();
+ const int faces = data.faces();
+ const int miplevels = data.mipLevels();
+
+ if (layers == 1 && faces == 1) {
+ for (int level = 0; level < miplevels; level++) {
+ f(createUploadEntry(level, 0, data.data(0, 0, level)));
+ }
+ } else if (layers > 1 && faces == 1) {
+ qWarning() << Q_FUNC_INFO << "Unsupported case, see QTBUG-83343";
+ /*
+ for (int layer = 0; layer < data.layers(); layer++) {
+ for (int level = 0; level < mipLevels; level++) {
+ f(createUploadEntry(level, layer, data.data(layer, 0, level)));
+ }
+ }
+ */
+ } else if (faces > 1 && layers == 1) {
+ // Mip levels do not seem to be supported by cubemaps...
+ for (int face = 0; face < data.faces(); face++) {
+ f(createUploadEntry(0, face, data.data(0, face, 0)));
+ }
+ } else {
+ qWarning() << Q_FUNC_INFO << "Unsupported case";
+ }
+}
+
+template<typename F>
+void filterLayerAndFace(int layer, int face, F f)
+{
+ if (layer == 0 && face == 0) {
+ f(0);
+ } else if (layer > 0 && face == 0) {
+ qWarning() << Q_FUNC_INFO << "Unsupported case, see QTBUG-83343";
+ // f(layer);
+ } else if (layer == 0 && face > 0) {
+ f(face);
+ } else {
+ qWarning() << Q_FUNC_INFO << "Unsupported case";
+ }
+}
+
+// For partial sub image uploads
+QRhiTextureUploadEntry createUploadEntry(int mipLevel, int layer, int xOffset, int yOffset,
+ int zOffset, const QByteArray &bytes,
+ const QTextureImageDataPtr &data) noexcept
+{
+ QRhiTextureSubresourceUploadDescription description;
+ description.setData(bytes);
+ description.setSourceTopLeft(QPoint(xOffset, yOffset));
+ return QRhiTextureUploadEntry(layer, mipLevel, description);
+}
+
+} // anonymous
+
+RHITexture::RHITexture()
+ : m_dirtyFlags(None),
+ m_rhi(nullptr),
+ m_rhiSampler(nullptr),
+ m_renderBuffer(nullptr),
+ m_dataFunctor(),
+ m_pendingDataFunctor(nullptr),
+ m_sharedTextureId(-1),
+ m_externalRendering(false),
+ m_wasTextureRecreated(false)
+{
+}
+
+RHITexture::~RHITexture() { }
+
+// Must be called from RenderThread with active GL context
+void RHITexture::destroy()
+{
+ delete m_rhi;
+ m_rhi = nullptr;
+ delete m_rhiSampler;
+ m_rhiSampler = nullptr;
+ delete m_renderBuffer;
+ m_renderBuffer = nullptr;
+
+ m_dirtyFlags = None;
+ m_sharedTextureId = -1;
+ m_externalRendering = false;
+ m_wasTextureRecreated = false;
+ m_dataFunctor.reset();
+ m_pendingDataFunctor = nullptr;
+
+ m_properties = {};
+ m_parameters = {};
+ m_textureData.reset();
+ m_images.clear();
+ m_imageData.clear();
+ m_pendingTextureDataUpdates.clear();
+}
+
+bool RHITexture::loadTextureDataFromGenerator()
+{
+ m_textureData = m_dataFunctor->operator()();
+ // if there is a texture generator, most properties will be defined by it
+ if (m_textureData) {
+ const QAbstractTexture::Target target = m_textureData->target();
+
+ // If both target and functor return Automatic we are still
+ // probably loading the texture, return false
+ if (m_properties.target == QAbstractTexture::TargetAutomatic
+ && target == QAbstractTexture::TargetAutomatic) {
+ m_textureData.reset();
+ return false;
+ }
+
+ if (m_properties.target != QAbstractTexture::TargetAutomatic
+ && target != QAbstractTexture::TargetAutomatic && m_properties.target != target) {
+ qWarning() << Q_FUNC_INFO
+ << "Generator and Properties not requesting the same texture target";
+ m_textureData.reset();
+ return false;
+ }
+
+ // We take target type from generator if it wasn't explicitly set by the user
+ if (m_properties.target == QAbstractTexture::TargetAutomatic)
+ m_properties.target = target;
+ m_properties.width = m_textureData->width();
+ m_properties.height = m_textureData->height();
+ m_properties.depth = m_textureData->depth();
+ m_properties.layers = m_textureData->layers();
+ m_properties.format = m_textureData->format();
+
+ const QVector<QTextureImageDataPtr> imageData = m_textureData->imageData();
+
+ if (!imageData.empty()) {
+ // Set the mips level based on the first image if autoMipMapGeneration is disabled
+ if (!m_properties.generateMipMaps)
+ m_properties.mipLevels = imageData.first()->mipLevels();
+ }
+ }
+ return !m_textureData.isNull();
+}
+
+void RHITexture::loadTextureDataFromImages()
+{
+ int maxMipLevel = 0;
+ for (const Image &img : qAsConst(m_images)) {
+ const QTextureImageDataPtr imgData = img.generator->operator()();
+ // imgData may be null in the following cases:
+ // - Texture is created with TextureImages which have yet to be
+ // loaded (skybox where you don't yet know the path, source set by
+ // a property binding, queued connection ...)
+ // - TextureImage whose generator failed to return a valid data
+ // (invalid url, error opening file...)
+ if (imgData.isNull())
+ continue;
+
+ m_imageData.push_back(imgData);
+ maxMipLevel = qMax(maxMipLevel, img.mipLevel);
+
+ // If the texture doesn't have a texture generator, we will
+ // derive some properties from the first TextureImage (layer=0, miplvl=0, face=0)
+ if (!m_textureData && img.layer == 0 && img.mipLevel == 0
+ && img.face == QAbstractTexture::CubeMapPositiveX) {
+ if (imgData->width() != -1 && imgData->height() != -1 && imgData->depth() != -1) {
+ m_properties.width = imgData->width();
+ m_properties.height = imgData->height();
+ m_properties.depth = imgData->depth();
+ }
+ // Set the format of the texture if the texture format is set to Automatic
+ if (m_properties.format == QAbstractTexture::Automatic) {
+ m_properties.format =
+ static_cast<QAbstractTexture::TextureFormat>(imgData->format());
+ }
+ setDirtyFlag(Properties, true);
+ }
+ }
+
+ // make sure the number of mip levels is set when there is no texture data generator
+ if (!m_dataFunctor) {
+ m_properties.mipLevels = maxMipLevel + 1;
+ setDirtyFlag(Properties, true);
+ }
+}
+
+// Called from RenderThread
+RHITexture::TextureUpdateInfo RHITexture::createOrUpdateRhiTexture(SubmissionContext *ctx)
+{
+ TextureUpdateInfo textureInfo;
+ m_wasTextureRecreated = false;
+
+ const bool hasSharedTextureId = m_sharedTextureId > 0;
+ // Only load texture data if we are not using a sharedTextureId
+ // Check if dataFunctor or images have changed
+ if (!hasSharedTextureId) {
+ // If dataFunctor exists and we have no data and it hasn´t run yet
+ if (m_dataFunctor && !m_textureData && m_dataFunctor.get() != m_pendingDataFunctor) {
+ const bool successfullyLoadedTextureData = loadTextureDataFromGenerator();
+ // If successful, m_textureData has content
+ if (successfullyLoadedTextureData) {
+ setDirtyFlag(Properties, true);
+ setDirtyFlag(TextureData, true);
+ } else {
+ if (m_pendingDataFunctor != m_dataFunctor.get()) {
+ qWarning() << "[Qt3DRender::RHITexture] No QTextureData generated from Texture "
+ "Generator yet. Texture will be invalid for this frame";
+ m_pendingDataFunctor = m_dataFunctor.get();
+ }
+ textureInfo.properties.status = QAbstractTexture::Loading;
+ return textureInfo;
+ }
+ }
+
+ // If images have changed, clear previous images data
+ // and regenerate m_imageData for the images
+ if (testDirtyFlag(TextureImageData)) {
+ m_imageData.clear();
+ loadTextureDataFromImages();
+ // Mark for upload if we actually have something to upload
+ if (!m_imageData.empty()) {
+ setDirtyFlag(TextureData, true);
+ }
+ // Reset image flag
+ setDirtyFlag(TextureImageData, false);
+ }
+
+ // Don't try to create the texture if the target or format was still not set
+ // Format should either be set by user or if Automatic
+ // by either the dataGenerator of the texture or the first Image
+ // Target should explicitly be set by the user or the dataGenerator
+ if (m_properties.target == QAbstractTexture::TargetAutomatic
+ || m_properties.format == QAbstractTexture::Automatic
+ || m_properties.format == QAbstractTexture::NoFormat) {
+ textureInfo.properties.status = QAbstractTexture::Error;
+ return textureInfo;
+ }
+ }
+
+ // If the properties changed or texture has become a shared texture from a
+ // 3rd party engine, we need to destroy and maybe re-allocate the texture
+ if (testDirtyFlag(Properties) || testDirtyFlag(SharedTextureId)) {
+ delete m_rhi;
+ m_rhi = nullptr;
+ textureInfo.wasUpdated = true;
+ // If we are destroyed because of some property change but still have (some) of
+ // our content data make sure we are marked for upload
+ // TO DO: We should actually check if the textureData is still correct
+ // in regard to the size, target and format of the texture though.
+ if (!testDirtyFlag(SharedTextureId)
+ && (m_textureData || !m_imageData.empty() || !m_pendingTextureDataUpdates.empty()))
+ setDirtyFlag(TextureData, true);
+ }
+
+ m_properties.status = QAbstractTexture::Ready;
+
+ if (testDirtyFlag(SharedTextureId) || hasSharedTextureId) {
+ // Update m_properties by doing introspection on the texture
+ if (hasSharedTextureId)
+ introspectPropertiesFromSharedTextureId();
+ setDirtyFlag(SharedTextureId, false);
+ } else {
+ // We only build a QOpenGLTexture if we have no shared textureId set
+ if (!m_rhi) {
+ m_rhi = buildRhiTexture(ctx);
+ if (!m_rhi) {
+ qWarning() << "[Qt3DRender::RHITexture] failed to create texture";
+ textureInfo.properties.status = QAbstractTexture::Error;
+ return textureInfo;
+ }
+ m_wasTextureRecreated = true;
+ }
+
+ textureInfo.texture = m_rhi;
+
+ // need to (re-)upload texture data?
+ const bool needsUpload = testDirtyFlag(TextureData);
+ if (needsUpload) {
+ uploadRhiTextureData(ctx);
+ setDirtyFlag(TextureData, false);
+ }
+
+ // need to set texture parameters?
+ if (testDirtyFlag(Properties) || testDirtyFlag(Parameters)) {
+ updateRhiTextureParameters(ctx);
+ setDirtyFlag(Properties, false);
+ setDirtyFlag(Parameters, false);
+ }
+ }
+
+ textureInfo.properties = m_properties;
+
+ return textureInfo;
+}
+
+RenderBuffer *RHITexture::getOrCreateRenderBuffer()
+{
+ if (m_dataFunctor && !m_textureData) {
+ m_textureData = m_dataFunctor->operator()();
+ if (m_textureData) {
+ if (m_properties.target != QAbstractTexture::TargetAutomatic)
+ qWarning() << "[Qt3DRender::RHITexture] [renderbuffer] When a texture provides a "
+ "generator, it's target is expected to be TargetAutomatic";
+
+ m_properties.width = m_textureData->width();
+ m_properties.height = m_textureData->height();
+ m_properties.format = m_textureData->format();
+
+ setDirtyFlag(Properties);
+ } else {
+ if (m_pendingDataFunctor != m_dataFunctor.get()) {
+ qWarning() << "[Qt3DRender::RHITexture] [renderbuffer] No QTextureData generated "
+ "from Texture Generator yet. Texture will be invalid for this frame";
+ m_pendingDataFunctor = m_dataFunctor.get();
+ }
+ return nullptr;
+ }
+ }
+
+ if (testDirtyFlag(Properties)) {
+ delete m_renderBuffer;
+ m_renderBuffer = nullptr;
+ }
+
+ if (!m_renderBuffer)
+ m_renderBuffer =
+ new RenderBuffer(m_properties.width, m_properties.height, m_properties.format);
+
+ setDirtyFlag(Properties, false);
+ setDirtyFlag(Parameters, false);
+
+ return m_renderBuffer;
+}
+
+// This must be called from the RenderThread
+// So RHITexture release from the manager can only be done from that thread
+void RHITexture::cleanup()
+{
+ destroy();
+}
+
+void RHITexture::setParameters(const TextureParameters &params)
+{
+ if (m_parameters != params) {
+ m_parameters = params;
+ setDirtyFlag(Parameters);
+ }
+}
+
+void RHITexture::setProperties(const TextureProperties &props)
+{
+ if (m_properties != props) {
+ m_properties = props;
+ setDirtyFlag(Properties);
+ }
+}
+
+void RHITexture::setImages(const QVector<Image> &images)
+{
+ // check if something has changed at all
+ bool same = (images.size() == m_images.size());
+ if (same) {
+ for (int i = 0; i < images.size(); i++) {
+ if (images[i] != m_images[i]) {
+ same = false;
+ break;
+ }
+ }
+ }
+
+ if (!same) {
+ m_images = images;
+ requestImageUpload();
+ }
+}
+
+void RHITexture::setGenerator(const QTextureGeneratorPtr &generator)
+{
+ m_textureData.reset();
+ m_dataFunctor = generator;
+ m_pendingDataFunctor = nullptr;
+ requestUpload();
+}
+
+void RHITexture::setSharedTextureId(int textureId)
+{
+ if (m_sharedTextureId != textureId) {
+ m_sharedTextureId = textureId;
+ setDirtyFlag(SharedTextureId);
+ }
+}
+
+void RHITexture::addTextureDataUpdates(const QVector<QTextureDataUpdate> &updates)
+{
+ m_pendingTextureDataUpdates += updates;
+ requestUpload();
+}
+
+// Return nullptr if
+// - context cannot be obtained
+// - texture hasn't yet been loaded
+QRhiTexture *RHITexture::buildRhiTexture(SubmissionContext *ctx)
+{
+ const QAbstractTexture::Target actualTarget = m_properties.target;
+ if (actualTarget == QAbstractTexture::TargetAutomatic) {
+ // If the target is automatic at this point, it means that the texture
+ // hasn't been loaded yet (case of remote urls) and that loading failed
+ // and that target format couldn't be deduced
+ return nullptr;
+ }
+
+ const QRhiTexture::Format rhiFormat = rhiFormatFromTextureFormat(m_properties.format);
+ const QSize pixelSize(m_properties.width, m_properties.height);
+ QRhiTexture::Flags rhiFlags {};
+ int sampleCount = 1;
+
+ const bool issRGB8Format = issRGBFormat(m_properties.format);
+ if (issRGB8Format)
+ rhiFlags |= QRhiTexture::sRGB;
+
+ if (actualTarget == QAbstractTexture::Target2DMultisample
+ || actualTarget == QAbstractTexture::Target2DMultisampleArray) {
+ // Set samples count if multisampled texture
+ // (multisampled textures don't have mipmaps)
+ sampleCount = m_properties.samples;
+ }
+
+ switch (actualTarget) {
+ case QAbstractTexture::TargetCubeMap:
+ case QAbstractTexture::TargetCubeMapArray: {
+ rhiFlags |= QRhiTexture::CubeMap;
+ break;
+ }
+ default: {
+ // Mipmaps don't see to work with cubemaps at the moment
+ if (m_properties.generateMipMaps) {
+ rhiFlags |= QRhiTexture::UsedWithGenerateMips;
+ rhiFlags |= QRhiTexture::MipMapped;
+ } else if (m_properties.mipLevels > 1) {
+ rhiFlags |= QRhiTexture::MipMapped;
+ }
+ break;
+ }
+ }
+
+ QRhiTexture *rhiTexture = ctx->rhi()->newTexture(rhiFormat, pixelSize, sampleCount, rhiFlags);
+
+ if (!rhiTexture->build()) {
+ qWarning() << Q_FUNC_INFO << "creating QRhiTexture failed";
+ delete rhiTexture;
+ return nullptr;
+ }
+ return rhiTexture;
+}
+
+void RHITexture::uploadRhiTextureData(SubmissionContext *ctx)
+{
+ QVarLengthArray<QRhiTextureUploadEntry> uploadEntries;
+
+ // Upload all QTexImageData set by the QTextureGenerator
+ if (m_textureData) {
+ const QVector<QTextureImageDataPtr> &imgData = m_textureData->imageData();
+
+ for (const QTextureImageDataPtr &data : imgData) {
+ const int mipLevels = data->mipLevels();
+ Q_ASSERT(mipLevels <= ctx->rhi()->mipLevelsForSize({ data->width(), data->height() }));
+
+ filterLayersAndFaces(*data, [&](QRhiTextureUploadEntry &&entry) {
+ uploadEntries.push_back(std::move(entry));
+ });
+ }
+ }
+
+ // Upload all QTexImageData references by the TextureImages
+ for (int i = 0; i < std::min(m_images.size(), m_imageData.size()); i++) {
+ const QTextureImageDataPtr &imgData = m_imageData.at(i);
+ // Here the bytes in the QTextureImageData contain data for a single
+ // layer, face or mip level, unlike the QTextureGenerator case where
+ // they are in a single blob. Hence QTextureImageData::data() is not suitable.
+ const QByteArray bytes(QTextureImageDataPrivate::get(imgData.get())->m_data);
+ const int layer = m_images[i].layer;
+ const int face = m_images[i].face;
+ filterLayerAndFace(layer, face, [&](int rhiLayer) {
+ uploadEntries.push_back(createUploadEntry(m_images[i].mipLevel, rhiLayer, bytes));
+ });
+ }
+
+ // Free up image data once content has been uploaded
+ // Note: if data functor stores the data, this won't really free anything though
+ m_imageData.clear();
+
+ // Update data from TextureUpdates
+ const QVector<QTextureDataUpdate> textureDataUpdates = std::move(m_pendingTextureDataUpdates);
+ for (const QTextureDataUpdate &update : textureDataUpdates) {
+ const QTextureImageDataPtr imgData = update.data();
+
+ if (!imgData) {
+ qWarning() << Q_FUNC_INFO << "QTextureDataUpdate no QTextureImageData set";
+ continue;
+ }
+
+ // TO DO: There's currently no way to check the depth of an existing QRhiTexture
+ const int xOffset = update.x();
+ const int yOffset = update.y();
+ const int xExtent = xOffset + imgData->width();
+ const int yExtent = yOffset + imgData->height();
+
+ // Check update is compatible with our texture
+ if (xOffset >= m_rhi->pixelSize().width() || yOffset >= m_rhi->pixelSize().height()
+ || xExtent > m_rhi->pixelSize().width() || yExtent > m_rhi->pixelSize().height()) {
+ qWarning() << Q_FUNC_INFO << "QTextureDataUpdate incompatible with texture";
+ continue;
+ }
+
+ const QByteArray bytes = (QTextureImageDataPrivate::get(imgData.get())->m_data);
+ // Here the bytes in the QTextureImageData contain data for a single
+ // layer, face or mip level, unlike the QTextureGenerator case where
+ // they are in a single blob. Hence QTextureImageData::data() is not suitable.
+
+ const int layer = update.layer();
+ const int face = update.face();
+ filterLayerAndFace(layer, face, [&](int rhiLayer) {
+ const QRhiTextureUploadEntry entry = createUploadEntry(
+ update.mipLevel(), rhiLayer, xOffset, yOffset, 0, bytes, imgData);
+ uploadEntries.push_back(entry);
+ });
+ }
+
+ QRhiTextureUploadDescription uploadDescription;
+ uploadDescription.setEntries(uploadEntries.begin(), uploadEntries.end());
+
+ ctx->m_currentUpdates->uploadTexture(m_rhi, uploadDescription);
+ if (m_properties.generateMipMaps)
+ ctx->m_currentUpdates->generateMips(m_rhi);
+}
+
+void RHITexture::updateRhiTextureParameters(SubmissionContext *ctx)
+{
+ const QAbstractTexture::Target actualTarget = m_properties.target;
+ const bool isMultisampledTexture =
+ (actualTarget == QAbstractTexture::Target2DMultisample
+ || actualTarget == QAbstractTexture::Target2DMultisampleArray);
+ // Multisampled textures can only be accessed by texelFetch in shaders
+ // and don't support wrap modes and mig/mag filtes
+ if (isMultisampledTexture)
+ return;
+
+ // TO DO:
+ if (m_rhiSampler) {
+ delete m_rhiSampler;
+ m_rhiSampler = nullptr;
+ }
+
+ const QRhiSampler::Filter magFilter =
+ rhiFilterFromTextureFilter(m_parameters.magnificationFilter);
+ const QRhiSampler::Filter minFilter =
+ rhiFilterFromTextureFilter(m_parameters.minificationFilter);
+ const QRhiSampler::Filter mipMapFilter =
+ rhiMipMapFilterFromTextureFilter(m_parameters.magnificationFilter);
+ const auto wrapMode = rhiWrapModeFromTextureWrapMode(
+ m_parameters.wrapModeX, m_parameters.wrapModeY, m_parameters.wrapModeZ);
+ const QRhiSampler::CompareOp compareOp =
+ rhiCompareOpFromTextureCompareOp(m_parameters.comparisonFunction);
+ m_rhiSampler = ctx->rhi()->newSampler(magFilter, minFilter, mipMapFilter, std::get<0>(wrapMode),
+ std::get<1>(wrapMode), std::get<2>(wrapMode));
+
+ m_rhiSampler->setTextureCompareOp(compareOp);
+
+ if (!m_rhiSampler->build()) {
+ qDebug("Could not build RHI texture sampler");
+ }
+}
+
+void RHITexture::introspectPropertiesFromSharedTextureId()
+{
+ // // We know that the context is active when this function is called
+ // QOpenGLContext *ctx = QOpenGLContext::currentContext();
+ // if (!ctx) {
+ // qWarning() << Q_FUNC_INFO << "requires an OpenGL context";
+ // return;
+ // }
+ // QOpenGLFunctions *gl = ctx->functions();
+
+ // // If the user has set the target format himself, we won't try to deduce it
+ // if (m_properties.target != QAbstractTexture::TargetAutomatic)
+ // return;
+
+ // const QAbstractTexture::Target targets[] = {
+ // QAbstractTexture::Target2D,
+ // QAbstractTexture::TargetCubeMap,
+ //#ifndef QT_OPENGL_ES_2
+ // QAbstractTexture::Target1D,
+ // QAbstractTexture::Target1DArray,
+ // QAbstractTexture::Target3D,
+ // QAbstractTexture::Target2DArray,
+ // QAbstractTexture::TargetCubeMapArray,
+ // QAbstractTexture::Target2DMultisample,
+ // QAbstractTexture::Target2DMultisampleArray,
+ // QAbstractTexture::TargetRectangle,
+ // QAbstractTexture::TargetBuffer,
+ //#endif
+ // };
+
+ //#ifndef QT_OPENGL_ES_2
+ // // Try to find texture target with GL 4.5 functions
+ // const QPair<int, int> ctxGLVersion = ctx->format().version();
+ // if (ctxGLVersion.first > 4 || (ctxGLVersion.first == 4 && ctxGLVersion.second >= 5)) {
+ // // Only for GL 4.5+
+ //#ifdef GL_TEXTURE_TARGET
+ // QOpenGLFunctions_4_5_Core *gl5 = ctx->versionFunctions<QOpenGLFunctions_4_5_Core>();
+ // if (gl5 != nullptr)
+ // gl5->glGetTextureParameteriv(m_sharedTextureId, GL_TEXTURE_TARGET,
+ // reinterpret_cast<int *>(&m_properties.target));
+ //#endif
+ // }
+ //#endif
+
+ // // If GL 4.5 function unavailable or not working, try a slower way
+ // if (m_properties.target == QAbstractTexture::TargetAutomatic) {
+ // // // OpenGL offers no proper way of querying for the target of a texture given its
+ // id gl->glActiveTexture(GL_TEXTURE0);
+
+ // const GLenum targetBindings[] = {
+ // GL_TEXTURE_BINDING_2D,
+ // GL_TEXTURE_BINDING_CUBE_MAP,
+ //#ifndef QT_OPENGL_ES_2
+ // GL_TEXTURE_BINDING_1D,
+ // GL_TEXTURE_BINDING_1D_ARRAY,
+ // GL_TEXTURE_BINDING_3D,
+ // GL_TEXTURE_BINDING_2D_ARRAY,
+ // GL_TEXTURE_BINDING_CUBE_MAP_ARRAY,
+ // GL_TEXTURE_BINDING_2D_MULTISAMPLE,
+ // GL_TEXTURE_BINDING_2D_MULTISAMPLE_ARRAY,
+ // GL_TEXTURE_BINDING_RECTANGLE,
+ // GL_TEXTURE_BINDING_BUFFER
+ //#endif
+ // };
+
+ // Q_ASSERT(sizeof(targetBindings) / sizeof(targetBindings[0] == sizeof(targets) /
+ // sizeof(targets[0])));
+
+ // for (uint i = 0; i < sizeof(targetBindings) / sizeof(targetBindings[0]); ++i) {
+ // const int target = targets[i];
+ // gl->glBindTexture(target, m_sharedTextureId);
+ // int boundId = 0;
+ // gl->glGetIntegerv(targetBindings[i], &boundId);
+ // gl->glBindTexture(target, 0);
+ // if (boundId == m_sharedTextureId) {
+ // m_properties.target = static_cast<QAbstractTexture::Target>(target);
+ // break;
+ // }
+ // }
+ // }
+
+ // // Return early if we weren't able to find texture target
+ // if (std::find(std::begin(targets), std::end(targets), m_properties.target) ==
+ // std::end(targets)) {
+ // qWarning() << "Unable to determine texture target for shared GL texture";
+ // return;
+ // }
+
+ // // Bind texture once we know its target
+ // gl->glBindTexture(m_properties.target, m_sharedTextureId);
+
+ // // TO DO: Improve by using glGetTextureParameters when available which
+ // // support direct state access
+ //#ifndef GL_TEXTURE_MAX_LEVEL
+ //#define GL_TEXTURE_MAX_LEVEL 0x813D
+ //#endif
+
+ //#ifndef GL_TEXTURE_WRAP_R
+ //#define GL_TEXTURE_WRAP_R 0x8072
+ //#endif
+
+ // gl->glGetTexParameteriv(int(m_properties.target), GL_TEXTURE_MAX_LEVEL,
+ // reinterpret_cast<int *>(&m_properties.mipLevels));
+ // gl->glGetTexParameteriv(int(m_properties.target), GL_TEXTURE_MIN_FILTER,
+ // reinterpret_cast<int *>(&m_parameters.minificationFilter));
+ // gl->glGetTexParameteriv(int(m_properties.target), GL_TEXTURE_MAG_FILTER,
+ // reinterpret_cast<int *>(&m_parameters.magnificationFilter));
+ // gl->glGetTexParameteriv(int(m_properties.target), GL_TEXTURE_WRAP_R, reinterpret_cast<int
+ // *>(&m_parameters.wrapModeX)); gl->glGetTexParameteriv(int(m_properties.target),
+ // GL_TEXTURE_WRAP_S, reinterpret_cast<int *>(&m_parameters.wrapModeY));
+ // gl->glGetTexParameteriv(int(m_properties.target), GL_TEXTURE_WRAP_T, reinterpret_cast<int
+ // *>(&m_parameters.wrapModeZ));
+
+ //#ifndef QT_OPENGL_ES_2
+ // // Try to retrieve dimensions (not available on ES 2.0)
+ // if (!ctx->isOpenGLES()) {
+ // QOpenGLFunctions_3_1 *gl3 = ctx->versionFunctions<QOpenGLFunctions_3_1>();
+ // if (!gl3) {
+ // qWarning() << "Failed to retrieve shared texture dimensions";
+ // return;
+ // }
+
+ // gl3->glGetTexLevelParameteriv(int(m_properties.target), 0, GL_TEXTURE_WIDTH,
+ // reinterpret_cast<int *>(&m_properties.width));
+ // gl3->glGetTexLevelParameteriv(int(m_properties.target), 0, GL_TEXTURE_HEIGHT,
+ // reinterpret_cast<int *>(&m_properties.height));
+ // gl3->glGetTexLevelParameteriv(int(m_properties.target), 0, GL_TEXTURE_DEPTH,
+ // reinterpret_cast<int *>(&m_properties.depth));
+ // gl3->glGetTexLevelParameteriv(int(m_properties.target), 0, GL_TEXTURE_INTERNAL_FORMAT,
+ // reinterpret_cast<int *>(&m_properties.format));
+ // }
+ //#endif
+
+ // gl->glBindTexture(m_properties.target, 0);
+}
+
+} // namespace Rhi
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
diff --git a/src/plugins/renderers/rhi/textures/texture_p.h b/src/plugins/renderers/rhi/textures/texture_p.h
new file mode 100644
index 000000000..536cb962e
--- /dev/null
+++ b/src/plugins/renderers/rhi/textures/texture_p.h
@@ -0,0 +1,249 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 QT3DRENDER_RENDER_RHI_GLTEXTURE_H
+#define QT3DRENDER_RENDER_RHI_GLTEXTURE_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <Qt3DRender/qtexture.h>
+#include <Qt3DRender/qtextureimagedata.h>
+#include <Qt3DRender/qtexturegenerator.h>
+#include <Qt3DRender/private/backendnode_p.h>
+#include <Qt3DRender/private/handle_types_p.h>
+#include <Qt3DRender/private/texture_p.h>
+#include <QFlags>
+#include <QMutex>
+#include <QSize>
+
+QT_BEGIN_NAMESPACE
+
+class QRhiTexture;
+class QRhiSampler;
+
+namespace Qt3DRender {
+namespace Render {
+
+template<class APITexture, class APITextureImage>
+class APITextureManager;
+
+class TextureImageManager;
+class TextureDataManager;
+class TextureImageDataManager;
+
+namespace Rhi {
+class RenderBuffer;
+class SubmissionContext;
+
+/**
+ * @brief
+ * Actual implementation of the OpenGL texture object. Makes sure the
+ * QOpenGLTexture is up-to-date with the generators, properties and parameters
+ * that were set for this RHITexture.
+ *
+ * Can be shared among multiple QTexture backend nodes through the
+ * RHITextureManager, which will make sure that there are no two GLTextures
+ * sharing the same texture data.
+ *
+ * A RHITexture can be unique though. In that case, it will not be shared
+ * between QTextures, but private to one QTexture only.
+ *
+ * A RHITexture can also represent an OpenGL renderbuffer object. This is used
+ * only in certain special cases, mainly to provide a packed depth-stencil
+ * renderbuffer suitable as an FBO attachment with OpenGL ES 3.1 and earlier.
+ * Such a RHITexture will have no texture object under the hood, and therefore
+ * the only valid operation is getOrCreateRenderBuffer().
+ */
+class Q_AUTOTEST_EXPORT RHITexture
+{
+public:
+ RHITexture();
+ ~RHITexture();
+
+ enum DirtyFlag {
+ None = 0,
+ TextureData = (1 << 0), // texture data needs uploading to GPU
+ Properties = (1 << 1), // texture needs to be (re-)created
+ Parameters = (1 << 2), // texture parameters need to be (re-)set
+ SharedTextureId = (1 << 3), // texture id from shared context
+ TextureImageData = (1 << 4) // texture image data needs uploading
+ };
+
+ /**
+ * Helper class to hold the defining properties of TextureImages
+ */
+ struct Image
+ {
+ QTextureImageDataGeneratorPtr generator;
+ int layer;
+ int mipLevel;
+ QAbstractTexture::CubeMapFace face;
+
+ inline bool operator==(const Image &o) const
+ {
+ bool sameGenerators = (generator == o.generator)
+ || (!generator.isNull() && !o.generator.isNull() && *generator == *o.generator);
+ return sameGenerators && layer == o.layer && mipLevel == o.mipLevel && face == o.face;
+ }
+ inline bool operator!=(const Image &o) const { return !(*this == o); }
+ };
+
+ inline TextureProperties properties() const { return m_properties; }
+ inline TextureParameters parameters() const { return m_parameters; }
+ inline QTextureGeneratorPtr textureGenerator() const { return m_dataFunctor; }
+ inline int sharedTextureId() const { return m_sharedTextureId; }
+ inline QVector<Image> images() const { return m_images; }
+
+ inline QSize size() const { return QSize(m_properties.width, m_properties.height); }
+ inline QRhiTexture *getRhiTexture() const { return m_rhi; }
+ inline QRhiSampler *getRhiSampler() const { return m_rhiSampler; }
+
+ /**
+ * @brief
+ * Returns the QOpenGLTexture for this RHITexture. If necessary,
+ * the GL texture will be created from the TextureImageDatas associated
+ * with the texture and image functors. If no functors are provided,
+ * the texture will be created without images.
+ *
+ * If the texture properties or parameters have changed, these changes
+ * will be applied to the resulting OpenGL texture.
+ */
+ struct TextureUpdateInfo
+ {
+ QRhiTexture *texture = nullptr;
+ bool wasUpdated = false;
+ TextureProperties properties;
+ };
+
+ TextureUpdateInfo createOrUpdateRhiTexture(SubmissionContext *ctx);
+
+ /**
+ * @brief
+ * Returns the RenderBuffer for this RHITexture. If this is the first
+ * call, the OpenGL renderbuffer object will be created.
+ */
+ RenderBuffer *getOrCreateRenderBuffer();
+
+ void destroy();
+
+ void cleanup();
+
+ bool isDirty() const { return m_dirtyFlags != None; }
+
+ bool hasTextureData() const { return !m_textureData.isNull(); }
+ bool hasImagesData() const { return !m_imageData.isEmpty(); }
+
+ QFlags<DirtyFlag> dirtyFlags() const { return m_dirtyFlags; }
+
+ QMutex *externalRenderingLock() { return &m_externalRenderingMutex; }
+
+ void setExternalRenderingEnabled(bool enable) { m_externalRendering = enable; }
+
+ bool isExternalRenderingEnabled() const { return m_externalRendering; }
+
+ // Purely for unit testing purposes
+ bool wasTextureRecreated() const { return m_wasTextureRecreated; }
+
+ void setParameters(const TextureParameters &params);
+ void setProperties(const TextureProperties &props);
+ void setImages(const QVector<Image> &images);
+ void setGenerator(const QTextureGeneratorPtr &generator);
+ void setSharedTextureId(int textureId);
+ void addTextureDataUpdates(const QVector<QTextureDataUpdate> &updates);
+
+ QVector<QTextureDataUpdate> textureDataUpdates() const { return m_pendingTextureDataUpdates; }
+ QTextureGeneratorPtr dataGenerator() const { return m_dataFunctor; }
+
+private:
+ void requestImageUpload() { m_dirtyFlags |= TextureImageData; }
+
+ void requestUpload() { m_dirtyFlags |= TextureData; }
+
+ bool testDirtyFlag(DirtyFlag flag) { return m_dirtyFlags.testFlag(flag); }
+
+ void setDirtyFlag(DirtyFlag flag, bool value = true) { m_dirtyFlags.setFlag(flag, value); }
+
+ QRhiTexture *buildRhiTexture(SubmissionContext *ctx);
+ bool loadTextureDataFromGenerator();
+ void loadTextureDataFromImages();
+ void uploadRhiTextureData(SubmissionContext *ctx);
+ void updateRhiTextureParameters(SubmissionContext *ctx);
+ void introspectPropertiesFromSharedTextureId();
+ void destroyResources();
+
+ QFlags<DirtyFlag> m_dirtyFlags;
+ QMutex m_externalRenderingMutex;
+ QRhiTexture *m_rhi;
+ QRhiSampler *m_rhiSampler;
+ RenderBuffer *m_renderBuffer;
+
+ // target which is actually used for GL texture
+ TextureProperties m_properties;
+ TextureParameters m_parameters;
+
+ QTextureGeneratorPtr m_dataFunctor;
+ QTextureGenerator *m_pendingDataFunctor;
+ QVector<Image> m_images;
+
+ // cache actual image data generated by the functors
+ QTextureDataPtr m_textureData;
+ QVector<QTextureImageDataPtr> m_imageData;
+ QVector<QTextureDataUpdate> m_pendingTextureDataUpdates;
+
+ int m_sharedTextureId;
+ bool m_externalRendering;
+ bool m_wasTextureRecreated;
+};
+
+} // namespace Rhi
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDER_RHI_GLTEXTURE_H
diff --git a/src/plugins/renderers/rhi/textures/textures.pri b/src/plugins/renderers/rhi/textures/textures.pri
new file mode 100644
index 000000000..dd9e6404f
--- /dev/null
+++ b/src/plugins/renderers/rhi/textures/textures.pri
@@ -0,0 +1,9 @@
+INCLUDEPATH += $$PWD
+
+SOURCES += \
+ $$PWD/renderbuffer.cpp \
+ $$PWD/texture.cpp
+
+HEADERS += \
+ $$PWD/renderbuffer_p.h \
+ $$PWD/texture_p.h
diff --git a/src/quick3d/imports/animation/plugins.qmltypes b/src/quick3d/imports/animation/plugins.qmltypes
index aff5ccb9c..beca47875 100644
--- a/src/quick3d/imports/animation/plugins.qmltypes
+++ b/src/quick3d/imports/animation/plugins.qmltypes
@@ -4,7 +4,7 @@ import QtQuick.tooling 1.2
// It is used for QML tooling purposes only.
//
// This file was auto-generated by:
-// 'qmlplugindump -nonrelocatable -dependencies dependencies.json Qt3D.Animation 2.14'
+// 'qmlplugindump -nonrelocatable -dependencies dependencies.json Qt3D.Animation 2.15'
Module {
dependencies: ["Qt3D.Core 2.0"]
diff --git a/src/quick3d/imports/core/plugins.qmltypes b/src/quick3d/imports/core/plugins.qmltypes
index 476bb99a7..e8cb1b43a 100644
--- a/src/quick3d/imports/core/plugins.qmltypes
+++ b/src/quick3d/imports/core/plugins.qmltypes
@@ -4,7 +4,7 @@ import QtQuick.tooling 1.2
// It is used for QML tooling purposes only.
//
// This file was auto-generated by:
-// 'qmlplugindump -nonrelocatable Qt3D.Core 2.14'
+// 'qmlplugindump -nonrelocatable Qt3D.Core 2.15'
Module {
dependencies: ["QtQuick 2.0"]
diff --git a/src/quick3d/imports/extras/plugins.qmltypes b/src/quick3d/imports/extras/plugins.qmltypes
index fcc15e022..a4e5d4503 100644
--- a/src/quick3d/imports/extras/plugins.qmltypes
+++ b/src/quick3d/imports/extras/plugins.qmltypes
@@ -4,7 +4,7 @@ import QtQuick.tooling 1.2
// It is used for QML tooling purposes only.
//
// This file was auto-generated by:
-// 'qmlplugindump -nonrelocatable -dependencies dependencies.json Qt3D.Extras 2.14'
+// 'qmlplugindump -nonrelocatable -dependencies dependencies.json Qt3D.Extras 2.15'
Module {
dependencies: ["Qt3D.Logic 2.0", "Qt3D.Render 2.0"]
@@ -800,9 +800,10 @@ Module {
exports: [
"Qt3D.Extras/ForwardRenderer 2.0",
"Qt3D.Extras/ForwardRenderer 2.14",
+ "Qt3D.Extras/ForwardRenderer 2.15",
"Qt3D.Extras/ForwardRenderer 2.9"
]
- exportMetaObjectRevisions: [0, 14, 9]
+ exportMetaObjectRevisions: [0, 14, 15, 9]
Property { name: "surface"; type: "QObject"; isPointer: true }
Property { name: "window"; type: "QObject"; isPointer: true }
Property { name: "viewportRect"; type: "QRectF" }
@@ -812,6 +813,7 @@ Module {
Property { name: "externalRenderTargetSize"; type: "QSize" }
Property { name: "frustumCulling"; type: "bool" }
Property { name: "gamma"; revision: 9; type: "float" }
+ Property { name: "showDebugOverlay"; revision: 15; type: "bool" }
Signal {
name: "viewportRectChanged"
Parameter { name: "viewportRect"; type: "QRectF" }
@@ -844,6 +846,10 @@ Module {
name: "gammaChanged"
Parameter { name: "gamma"; type: "float" }
}
+ Signal {
+ name: "showDebugOverlayChanged"
+ Parameter { name: "showDebugOverlay"; type: "bool" }
+ }
Method {
name: "setViewportRect"
Parameter { name: "viewportRect"; type: "QRectF" }
@@ -876,6 +882,10 @@ Module {
name: "setGamma"
Parameter { name: "gamma"; type: "float" }
}
+ Method {
+ name: "setShowDebugOverlay"
+ Parameter { name: "showDebugOverlay"; type: "bool" }
+ }
}
Component {
name: "Qt3DExtras::QGoochMaterial"
diff --git a/src/quick3d/imports/input/plugins.qmltypes b/src/quick3d/imports/input/plugins.qmltypes
index 0a70b97c3..649c4a173 100644
--- a/src/quick3d/imports/input/plugins.qmltypes
+++ b/src/quick3d/imports/input/plugins.qmltypes
@@ -4,7 +4,7 @@ import QtQuick.tooling 1.2
// It is used for QML tooling purposes only.
//
// This file was auto-generated by:
-// 'qmlplugindump -nonrelocatable -dependencies dependencies.json Qt3D.Input 2.14'
+// 'qmlplugindump -nonrelocatable -dependencies dependencies.json Qt3D.Input 2.15'
Module {
dependencies: ["Qt3D.Core 2.0"]
@@ -562,8 +562,8 @@ Module {
Component {
name: "Qt3DInput::QMouseDevice"
prototype: "Qt3DInput::QAbstractPhysicalDevice"
- exports: ["Qt3D.Input/MouseDevice 2.0"]
- exportMetaObjectRevisions: [0]
+ exports: ["Qt3D.Input/MouseDevice 2.0", "Qt3D.Input/MouseDevice 2.15"]
+ exportMetaObjectRevisions: [0, 15]
Enum {
name: "Axis"
values: {
@@ -574,14 +574,23 @@ Module {
}
}
Property { name: "sensitivity"; type: "float" }
+ Property { name: "updateAxesContinuously"; revision: 15; type: "bool" }
Signal {
name: "sensitivityChanged"
Parameter { name: "value"; type: "float" }
}
+ Signal {
+ name: "updateAxesContinuouslyChanged"
+ Parameter { name: "updateAxesContinuously"; type: "bool" }
+ }
Method {
name: "setSensitivity"
Parameter { name: "value"; type: "float" }
}
+ Method {
+ name: "setUpdateAxesContinuously"
+ Parameter { name: "updateAxesContinuously"; type: "bool" }
+ }
}
Component {
name: "Qt3DInput::QMouseEvent"
diff --git a/src/quick3d/imports/logic/plugins.qmltypes b/src/quick3d/imports/logic/plugins.qmltypes
index 1ad7b1f58..f6e7cc9c7 100644
--- a/src/quick3d/imports/logic/plugins.qmltypes
+++ b/src/quick3d/imports/logic/plugins.qmltypes
@@ -4,7 +4,7 @@ import QtQuick.tooling 1.2
// It is used for QML tooling purposes only.
//
// This file was auto-generated by:
-// 'qmlplugindump -nonrelocatable -dependencies dependencies.json Qt3D.Logic 2.14'
+// 'qmlplugindump -nonrelocatable -dependencies dependencies.json Qt3D.Logic 2.15'
Module {
dependencies: ["Qt3D.Core 2.0"]
diff --git a/src/quick3d/imports/render/plugins.qmltypes b/src/quick3d/imports/render/plugins.qmltypes
index 6408abb27..8ea988efe 100644
--- a/src/quick3d/imports/render/plugins.qmltypes
+++ b/src/quick3d/imports/render/plugins.qmltypes
@@ -4,7 +4,7 @@ import QtQuick.tooling 1.2
// It is used for QML tooling purposes only.
//
// This file was auto-generated by:
-// 'qmlplugindump -nonrelocatable -dependencies dependencies.json Qt3D.Render 2.14'
+// 'qmlplugindump -nonrelocatable -dependencies dependencies.json Qt3D.Render 2.15'
Module {
dependencies: ["Qt3D.Core 2.0"]
@@ -141,6 +141,12 @@ Module {
Method { name: "raise" }
Method { name: "lower" }
Method {
+ name: "startSystemResize"
+ type: "bool"
+ Parameter { name: "edges"; type: "Qt::Edges" }
+ }
+ Method { name: "startSystemMove"; type: "bool" }
+ Method {
name: "setTitle"
Parameter { type: "string" }
}
@@ -2069,7 +2075,8 @@ Module {
"OpenGLES": 2,
"OpenGL": 1,
"Vulkan": 3,
- "DirectX": 4
+ "DirectX": 4,
+ "RHI": 5
}
}
Enum {
@@ -2799,6 +2806,59 @@ Module {
}
}
Component {
+ name: "Qt3DRender::QRenderCapabilities"
+ prototype: "QObject"
+ exports: ["Qt3D.Render/RenderCapabilities 2.15"]
+ isCreatable: false
+ exportMetaObjectRevisions: [0]
+ Enum {
+ name: "API"
+ values: {
+ "OpenGL": 1,
+ "OpenGLES": 2
+ }
+ }
+ Enum {
+ name: "Profile"
+ values: {
+ "NoProfile": 0,
+ "CoreProfile": 1,
+ "CompatibilityProfile": 2
+ }
+ }
+ Property { name: "valid"; type: "bool"; isReadonly: true }
+ Property { name: "api"; type: "API"; isReadonly: true }
+ Property { name: "profile"; type: "Profile"; isReadonly: true }
+ Property { name: "majorVersion"; type: "int"; isReadonly: true }
+ Property { name: "minorVersion"; type: "int"; isReadonly: true }
+ Property { name: "extensions"; type: "QStringList"; isReadonly: true }
+ Property { name: "vendor"; type: "string"; isReadonly: true }
+ Property { name: "renderer"; type: "string"; isReadonly: true }
+ Property { name: "driverVersion"; type: "string"; isReadonly: true }
+ Property { name: "glslVersion"; type: "string"; isReadonly: true }
+ Property { name: "maxSamples"; type: "int"; isReadonly: true }
+ Property { name: "maxTextureSize"; type: "int"; isReadonly: true }
+ Property { name: "maxTextureUnits"; type: "int"; isReadonly: true }
+ Property { name: "maxTextureLayers"; type: "int"; isReadonly: true }
+ Property { name: "supportsUBO"; type: "bool"; isReadonly: true }
+ Property { name: "maxUBOSize"; type: "int"; isReadonly: true }
+ Property { name: "maxUBOBindings"; type: "int"; isReadonly: true }
+ Property { name: "supportsSSBO"; type: "bool"; isReadonly: true }
+ Property { name: "maxSSBOSize"; type: "int"; isReadonly: true }
+ Property { name: "maxSSBOBindings"; type: "int"; isReadonly: true }
+ Property { name: "supportsImageStore"; type: "bool"; isReadonly: true }
+ Property { name: "maxImageUnits"; type: "int"; isReadonly: true }
+ Property { name: "supportsCompute"; type: "bool"; isReadonly: true }
+ Property { name: "maxWorkGroupCountX"; type: "int"; isReadonly: true }
+ Property { name: "maxWorkGroupCountY"; type: "int"; isReadonly: true }
+ Property { name: "maxWorkGroupCountZ"; type: "int"; isReadonly: true }
+ Property { name: "maxWorkGroupSizeX"; type: "int"; isReadonly: true }
+ Property { name: "maxWorkGroupSizeY"; type: "int"; isReadonly: true }
+ Property { name: "maxWorkGroupSizeZ"; type: "int"; isReadonly: true }
+ Property { name: "maxComputeInvocations"; type: "int"; isReadonly: true }
+ Property { name: "maxComputeSharedMemorySize"; type: "int"; isReadonly: true }
+ }
+ Component {
name: "Qt3DRender::QRenderCapture"
prototype: "Qt3DRender::QFrameGraphNode"
exports: [
@@ -2905,8 +2965,11 @@ Module {
name: "Qt3DRender::QRenderSettings"
defaultProperty: "activeFrameGraph"
prototype: "Qt3DCore::QComponent"
- exports: ["Qt3D.Render/RenderSettings 2.0"]
- exportMetaObjectRevisions: [0]
+ exports: [
+ "Qt3D.Render/RenderSettings 2.0",
+ "Qt3D.Render/RenderSettings 2.15"
+ ]
+ exportMetaObjectRevisions: [0, 15]
Enum {
name: "RenderPolicy"
values: {
@@ -2915,6 +2978,13 @@ Module {
}
}
Property {
+ name: "renderCapabilities"
+ revision: 15
+ type: "Qt3DRender::QRenderCapabilities"
+ isReadonly: true
+ isPointer: true
+ }
+ Property {
name: "pickingSettings"
type: "Qt3DRender::QPickingSettings"
isReadonly: true
@@ -3376,9 +3446,10 @@ Module {
prototype: "Qt3DCore::QNode"
exports: [
"Qt3D.Render/ShaderProgram 2.0",
+ "Qt3D.Render/ShaderProgram 2.15",
"Qt3D.Render/ShaderProgram 2.9"
]
- exportMetaObjectRevisions: [0, 9]
+ exportMetaObjectRevisions: [0, 15, 9]
Enum {
name: "ShaderType"
values: {
@@ -3398,6 +3469,13 @@ Module {
"Error": 2
}
}
+ Enum {
+ name: "Format"
+ values: {
+ "GLSL": 0,
+ "SPIRV": 1
+ }
+ }
Property { name: "vertexShaderCode"; type: "QByteArray" }
Property { name: "tessellationControlShaderCode"; type: "QByteArray" }
Property { name: "tessellationEvaluationShaderCode"; type: "QByteArray" }
@@ -3406,6 +3484,7 @@ Module {
Property { name: "computeShaderCode"; type: "QByteArray" }
Property { name: "log"; revision: 9; type: "string"; isReadonly: true }
Property { name: "status"; revision: 9; type: "Status"; isReadonly: true }
+ Property { name: "format"; revision: 15; type: "Format" }
Signal {
name: "vertexShaderCodeChanged"
Parameter { name: "vertexShaderCode"; type: "QByteArray" }
@@ -3438,6 +3517,10 @@ Module {
name: "statusChanged"
Parameter { name: "status"; type: "Status" }
}
+ Signal {
+ name: "formatChanged"
+ Parameter { name: "format"; type: "Format" }
+ }
Method {
name: "setVertexShaderCode"
Parameter { name: "vertexShaderCode"; type: "QByteArray" }
@@ -3622,7 +3705,8 @@ Module {
"BackToFront": 2,
"Material": 4,
"FrontToBack": 8,
- "Texture": 16
+ "Texture": 16,
+ "Uniform": 32
}
}
Property { name: "sortTypes"; type: "QVector<int>" }
diff --git a/src/quick3d/imports/scene2d/plugins.qmltypes b/src/quick3d/imports/scene2d/plugins.qmltypes
index 32fb8122f..16b892728 100644
--- a/src/quick3d/imports/scene2d/plugins.qmltypes
+++ b/src/quick3d/imports/scene2d/plugins.qmltypes
@@ -4,7 +4,7 @@ import QtQuick.tooling 1.2
// It is used for QML tooling purposes only.
//
// This file was auto-generated by:
-// 'qmlplugindump -nonrelocatable -dependencies dependencies.json QtQuick.Scene2D 2.14'
+// 'qmlplugindump -nonrelocatable -dependencies dependencies.json QtQuick.Scene2D 2.15'
Module {
dependencies: ["Qt3D.Core 2.0", "Qt3D.Render 2.0"]
diff --git a/src/quick3d/imports/scene3d/importsscene3d.pro b/src/quick3d/imports/scene3d/importsscene3d.pro
index e29f7b831..c4c1b7cc8 100644
--- a/src/quick3d/imports/scene3d/importsscene3d.pro
+++ b/src/quick3d/imports/scene3d/importsscene3d.pro
@@ -13,7 +13,6 @@ HEADERS += \
qtquickscene3dplugin.h \
scene3dlogging_p.h \
scene3ditem_p.h \
- scene3dcleaner_p.h \
scene3drenderer_p.h \
scene3dsgnode_p.h \
scene3dsgmaterialshader_p.h \
@@ -24,7 +23,6 @@ SOURCES += \
qtquickscene3dplugin.cpp \
scene3ditem.cpp \
scene3dlogging.cpp \
- scene3dcleaner.cpp \
scene3drenderer.cpp \
scene3dsgnode.cpp \
scene3dsgmaterialshader.cpp \
diff --git a/src/quick3d/imports/scene3d/plugins.qmltypes b/src/quick3d/imports/scene3d/plugins.qmltypes
index aca01ac25..f0ce9a334 100644
--- a/src/quick3d/imports/scene3d/plugins.qmltypes
+++ b/src/quick3d/imports/scene3d/plugins.qmltypes
@@ -4,7 +4,7 @@ import QtQuick.tooling 1.2
// It is used for QML tooling purposes only.
//
// This file was auto-generated by:
-// 'qmlplugindump -nonrelocatable -dependencies dependencies.json QtQuick.Scene3D 2.14'
+// 'qmlplugindump -nonrelocatable -dependencies dependencies.json QtQuick.Scene3D 2.15'
Module {
dependencies: ["Qt3D.Core 2.0", "QtQuick 2.0"]
diff --git a/src/quick3d/imports/scene3d/scene3ditem.cpp b/src/quick3d/imports/scene3d/scene3ditem.cpp
index 295217f98..36569f0f7 100644
--- a/src/quick3d/imports/scene3d/scene3ditem.cpp
+++ b/src/quick3d/imports/scene3d/scene3ditem.cpp
@@ -68,7 +68,6 @@
#include <Qt3DRender/private/qrendersurfaceselector_p.h>
#include <Qt3DRender/private/qrenderaspect_p.h>
#include <Qt3DRender/private/rendersettings_p.h>
-#include <scene3dcleaner_p.h>
#include <scene3dlogging_p.h>
#include <scene3drenderer_p.h>
#include <scene3dsgnode_p.h>
@@ -150,8 +149,8 @@ Scene3DItem::Scene3DItem(QQuickItem *parent)
, m_viewHolderFG(nullptr)
, m_aspectEngine(new Qt3DCore::QAspectEngine())
, m_renderAspect(nullptr)
+ , m_aspectToDelete(nullptr)
, m_renderer(nullptr)
- , m_rendererCleaner(new Scene3DCleaner())
, m_multisample(true)
, m_dirty(true)
, m_dirtyViews(false)
@@ -163,6 +162,7 @@ Scene3DItem::Scene3DItem(QQuickItem *parent)
{
setFlag(QQuickItem::ItemHasContents, true);
setAcceptedMouseButtons(Qt::MouseButtonMask);
+ setAcceptHoverEvents(true);
// TO DO: register the event source in the main thread
// Use manual drive mode when using Scene3D
@@ -179,6 +179,8 @@ Scene3DItem::~Scene3DItem()
// When the window is closed, it first destroys all of its children. At
// this point, Scene3DItem is destroyed but the Renderer, AspectEngine and
// Scene3DSGNode still exist and will perform their cleanup on their own.
+ m_aspectEngine->deleteLater();
+ m_renderer->deleteLater();
}
/*!
@@ -204,18 +206,11 @@ QStringList Scene3DItem::aspects() const
*/
Qt3DCore::QEntity *Scene3DItem::entity() const
{
- return m_entity;
+ return m_entity.data();
}
-void Scene3DItem::setAspects(const QStringList &aspects)
+void Scene3DItem::applyAspects()
{
- if (!m_aspects.isEmpty()) {
- qCWarning(Scene3D) << "Aspects already set on the Scene3D, ignoring";
- return;
- }
-
- m_aspects = aspects;
-
// Aspects are owned by the aspect engine
for (const QString &aspect : qAsConst(m_aspects)) {
if (aspect == QLatin1String("render")) // This one is hardwired anyway
@@ -246,16 +241,27 @@ void Scene3DItem::setAspects(const QStringList &aspects)
}
m_aspectEngine->registerAspect(aspect);
}
+}
+
+void Scene3DItem::setAspects(const QStringList &aspects)
+{
+ if (!m_aspects.isEmpty()) {
+ qWarning() << "Aspects already set on the Scene3D, ignoring";
+ return;
+ }
+
+ m_aspects = aspects;
+ applyAspects();
emit aspectsChanged();
}
void Scene3DItem::setEntity(Qt3DCore::QEntity *entity)
{
- if (entity == m_entity)
+ if (entity == m_entity.data())
return;
- m_entity = entity;
+ m_entity.reset(entity);
emit entityChanged();
}
@@ -292,14 +298,12 @@ void Scene3DItem::setHoverEnabled(bool enabled)
\value Underlay
Suitable for full screen 3D scenes where using an FBO might be too
resource intensive. Scene3D behaves as a QtQuick underlay.
-
Please note that when using this mode, the size of the Scene3D and
its transformations are ignored and the rendering will occupy the
whole screen. The position of the Scene3D in the QML file won't have
any effect either. The Qt 3D content will be drawn prior to any Qt
Quick content. Care has to be taken not to overdraw and hide the Qt
3D content by overlapping Qt Quick content.
-
Additionally when using this mode, the window clearBeforeRendering
will be set to false automatically.
@@ -398,14 +402,21 @@ void Scene3DItem::removeView(Scene3DView *view)
void Scene3DItem::applyRootEntityChange()
{
- if (m_aspectEngine->rootEntity() != m_entity) {
- m_aspectEngine->setRootEntity(Qt3DCore::QEntityPtr(m_entity));
+ if (m_aspectEngine->rootEntity() != m_entity.data()) {
+ m_aspectEngine->setRootEntity(m_entity);
+
+ /* If we changed window, the old aspect engine must be deleted only after we have set
+ the root entity for the new one so that it doesn't delete the root node. */
+ if (m_aspectToDelete) {
+ delete m_aspectToDelete;
+ m_aspectToDelete = nullptr;
+ }
// Set the render surface
if (!m_entity)
return;
- setWindowSurface(m_entity);
+ setWindowSurface(entity());
if (m_cameraAspectRatioMode == AutomaticAspectRatio) {
// Set aspect ratio of first camera to match the window
@@ -481,6 +492,8 @@ void Scene3DItem::onBeforeSync()
// if the Scene3D item is not visible
if (!isVisible() && dontRenderWhenHidden)
return;
+ if (m_renderer->m_resetRequested)
+ return;
Q_ASSERT(QThread::currentThread() == thread());
@@ -496,7 +509,7 @@ void Scene3DItem::onBeforeSync()
// Make renderer aware of any Scene3DView we are dealing with
if (m_dirtyViews) {
// Scene3DViews checks
- if (m_entity != m_viewHolderEntity) {
+ if (entity() != m_viewHolderEntity) {
qCWarning(Scene3D) << "Scene3DView is not supported if the Scene3D entity property has been set";
}
if (!usesFBO) {
@@ -544,6 +557,20 @@ void Scene3DItem::requestUpdate()
}
}
+void Scene3DItem::updateWindowSurface()
+{
+ if (!m_entity || !m_dummySurface)
+ return;
+ Qt3DRender::QRenderSurfaceSelector *surfaceSelector =
+ Qt3DRender::QRenderSurfaceSelectorPrivate::find(entity());
+ if (surfaceSelector) {
+ if (QWindow *rw = QQuickRenderControl::renderWindowFor(this->window())) {
+ m_dummySurface->deleteLater();
+ createDummySurface(rw, surfaceSelector);
+ }
+ }
+}
+
void Scene3DItem::setWindowSurface(QObject *rootObject)
{
Qt3DRender::QRenderSurfaceSelector *surfaceSelector = Qt3DRender::QRenderSurfaceSelectorPrivate::find(rootObject);
@@ -554,20 +581,25 @@ void Scene3DItem::setWindowSurface(QObject *rootObject)
// We may not have a real, exposed QQuickWindow when the Quick rendering
// is redirected via QQuickRenderControl (f.ex. QQuickWidget).
if (QWindow *rw = QQuickRenderControl::renderWindowFor(this->window())) {
- // rw is the top-level window that is backed by a native window. Do
- // not use that though since we must not clash with e.g. the widget
- // backingstore compositor in the gui thread.
- m_dummySurface = new QOffscreenSurface;
- m_dummySurface->setParent(qGuiApp); // parent to something suitably long-living
- m_dummySurface->setFormat(rw->format());
- m_dummySurface->setScreen(rw->screen());
- m_dummySurface->create();
- surfaceSelector->setSurface(m_dummySurface);
+ createDummySurface(rw, surfaceSelector);
} else {
surfaceSelector->setSurface(this->window());
}
}
}
+
+void Scene3DItem::createDummySurface(QWindow *rw, Qt3DRender::QRenderSurfaceSelector *surfaceSelector)
+{
+ // rw is the top-level window that is backed by a native window. Do
+ // not use that though since we must not clash with e.g. the widget
+ // backingstore compositor in the gui thread.
+ m_dummySurface = new QOffscreenSurface;
+ m_dummySurface->setParent(qGuiApp); // parent to something suitably long-living
+ m_dummySurface->setFormat(rw->format());
+ m_dummySurface->setScreen(rw->screen());
+ m_dummySurface->create();
+ surfaceSelector->setSurface(m_dummySurface);
+}
/*!
\qmlmethod void Scene3D::setItemAreaAndDevicePixelRatio(size area, real devicePixelRatio)
@@ -575,7 +607,8 @@ void Scene3DItem::setWindowSurface(QObject *rootObject)
*/
void Scene3DItem::setItemAreaAndDevicePixelRatio(QSize area, qreal devicePixelRatio)
{
- Qt3DRender::QRenderSurfaceSelector *surfaceSelector = Qt3DRender::QRenderSurfaceSelectorPrivate::find(m_entity);
+ Qt3DRender::QRenderSurfaceSelector *surfaceSelector
+ = Qt3DRender::QRenderSurfaceSelectorPrivate::find(entity());
if (surfaceSelector) {
surfaceSelector->setExternalRenderTargetSize(area);
surfaceSelector->setSurfacePixelRatio(devicePixelRatio);
@@ -665,6 +698,22 @@ void Scene3DItem::setMultisample(bool enable)
QSGNode *Scene3DItem::updatePaintNode(QSGNode *node, QQuickItem::UpdatePaintNodeData *)
{
+ // m_resetRequested is set to true by Scene3DRenderer::shutdown()
+ if (m_renderer && m_renderer->m_resetRequested) {
+ qCWarning(Scene3D) << "Renderer for Scene3DItem has requested a reset due to the item "
+ "moving to another window";
+ QObject::disconnect(m_windowConnection);
+ m_aspectEngine->unregisterAspect(m_renderAspect); // Deletes the renderAspect
+ m_renderAspect = nullptr;
+ m_aspectToDelete = m_aspectEngine;
+ m_aspectEngine = new Qt3DCore::QAspectEngine();
+ m_aspectEngine->setRunMode(Qt3DCore::QAspectEngine::Manual);
+ applyAspects();
+ // Needs to belong in the same thread as the item which is the same as the original
+ // QAspectEngine
+ m_aspectEngine->moveToThread(thread());
+ m_renderer->m_resetRequested = false;
+ }
// If the render aspect wasn't created yet, do so now
if (m_renderAspect == nullptr) {
m_renderAspect = new QRenderAspect(QRenderAspect::Synchronous);
@@ -675,16 +724,24 @@ QSGNode *Scene3DItem::updatePaintNode(QSGNode *node, QQuickItem::UpdatePaintNode
// Before Synchronizing is in the SG Thread, we want beforeSync to be triggered
// in the context of the main thread
- QObject::connect(window(), &QQuickWindow::afterAnimating,
- this, &Scene3DItem::onBeforeSync, Qt::DirectConnection);
+ m_windowConnection = QObject::connect(window(), &QQuickWindow::afterAnimating,
+ this, &Scene3DItem::onBeforeSync, Qt::DirectConnection);
auto renderAspectPriv = static_cast<QRenderAspectPrivate*>(QRenderAspectPrivate::get(m_renderAspect));
QObject::connect(renderAspectPriv->m_aspectManager->changeArbiter(), &Qt3DCore::QChangeArbiter::receivedChange,
this, [this] { m_dirty = true; }, Qt::DirectConnection);
}
if (m_renderer == nullptr) {
- m_renderer = new Scene3DRenderer(this, m_aspectEngine, m_renderAspect);
- m_renderer->setCleanerHelper(m_rendererCleaner);
+ m_renderer = new Scene3DRenderer();
+ m_renderer->init(this, m_aspectEngine, m_renderAspect);
+ } else if (m_renderer->renderAspect() != m_renderAspect) {
+ // If the renderer's renderAspect is not equal to the aspect used
+ // by the item, then it means that we have created a new one due to
+ // the fact that shutdown() was called on the renderer previously.
+ // This is a typical situation when the window the item is in has
+ // moved from one screen to another.
+ updateWindowSurface();
+ m_renderer->init(this, m_aspectEngine, m_renderAspect);
}
const bool usesFBO = m_compositingMode == FBO;
const bool hasScene3DViews = !m_views.empty();
diff --git a/src/quick3d/imports/scene3d/scene3ditem_p.h b/src/quick3d/imports/scene3d/scene3ditem_p.h
index e46bb20af..0beaf94c0 100644
--- a/src/quick3d/imports/scene3d/scene3ditem_p.h
+++ b/src/quick3d/imports/scene3d/scene3ditem_p.h
@@ -71,6 +71,7 @@ class Scene3DRenderer;
class Scene3DCleaner;
class Scene3DView;
class QFrameGraphNode;
+class QRenderSurfaceSelector;
class Scene3DItem : public QQuickItem
{
@@ -138,16 +139,20 @@ private:
void updateCameraAspectRatio();
void mousePressEvent(QMouseEvent *event) override;
bool needsRender();
+ void updateWindowSurface();
+ void createDummySurface(QWindow *window, QRenderSurfaceSelector *surfaceSelector);
+ void applyAspects();
QStringList m_aspects;
- Qt3DCore::QEntity *m_entity;
+ // Store as shared pointer so that aspect engine doesn't delete it.
+ QSharedPointer<Qt3DCore::QEntity> m_entity;
Qt3DCore::QEntity *m_viewHolderEntity;
Qt3DRender::QFrameGraphNode *m_viewHolderFG;
Qt3DCore::QAspectEngine *m_aspectEngine;
+ Qt3DCore::QAspectEngine *m_aspectToDelete;
QRenderAspect *m_renderAspect;
Scene3DRenderer *m_renderer;
- Scene3DCleaner *m_rendererCleaner;
bool m_multisample;
bool m_dirty;
@@ -160,6 +165,7 @@ private:
CompositingMode m_compositingMode;
QOffscreenSurface *m_dummySurface;
QVector<Scene3DView *> m_views;
+ QMetaObject::Connection m_windowConnection;
};
} // Qt3DRender
diff --git a/src/quick3d/imports/scene3d/scene3drenderer.cpp b/src/quick3d/imports/scene3d/scene3drenderer.cpp
index f78271bea..01c94b998 100644
--- a/src/quick3d/imports/scene3d/scene3drenderer.cpp
+++ b/src/quick3d/imports/scene3d/scene3drenderer.cpp
@@ -53,7 +53,6 @@
#include <Qt3DCore/private/qchangearbiter_p.h>
#include <Qt3DCore/private/qservicelocator_p.h>
-#include <scene3dcleaner_p.h>
#include <scene3ditem_p.h>
#include <scene3dlogging_p.h>
#include <scene3dsgnode_p.h>
@@ -144,16 +143,15 @@ private:
signal of the window is not called. Therefore the cleanup method is invoked
to properly destroy the aspect engine.
*/
-Scene3DRenderer::Scene3DRenderer(Scene3DItem *item, Qt3DCore::QAspectEngine *aspectEngine, QRenderAspect *renderAspect)
+Scene3DRenderer::Scene3DRenderer()
: QObject()
- , m_item(item)
- , m_aspectEngine(aspectEngine)
- , m_renderAspect(renderAspect)
+ , m_item(nullptr)
+ , m_aspectEngine(nullptr)
+ , m_renderAspect(nullptr)
, m_multisampledFBO(nullptr)
, m_finalFBO(nullptr)
, m_texture(nullptr)
, m_node(nullptr)
- , m_cleaner(nullptr)
, m_window(nullptr)
, m_multisample(false) // this value is not used, will be synced from the Scene3DItem instead
, m_lastMultisample(false)
@@ -165,6 +163,17 @@ Scene3DRenderer::Scene3DRenderer(Scene3DItem *item, Qt3DCore::QAspectEngine *asp
, m_allowRendering(0)
, m_compositingMode(Scene3DItem::FBO)
{
+}
+
+void Scene3DRenderer::init(Scene3DItem *item, Qt3DCore::QAspectEngine *aspectEngine,
+ QRenderAspect *renderAspect)
+{
+ m_item = item;
+ m_window = m_item->window();
+ m_aspectEngine = aspectEngine;
+ m_renderAspect = renderAspect;
+ m_needsShutdown = true;
+
Q_CHECK_PTR(m_item);
Q_CHECK_PTR(m_item->window());
@@ -215,21 +224,14 @@ void Scene3DRenderer::scheduleRootEntityChange()
QMetaObject::invokeMethod(m_item, "applyRootEntityChange", Qt::QueuedConnection);
}
-void Scene3DRenderer::setCleanerHelper(Scene3DCleaner *cleaner)
-{
- m_cleaner = cleaner;
- if (m_cleaner) {
- // Window closed case
- QObject::connect(m_item->window(), &QQuickWindow::destroyed, m_cleaner, &Scene3DCleaner::cleanup);
- m_cleaner->setRenderer(this);
- }
-}
-
// Executed in the QtQuick render thread (which may even be the gui/main with QQuickWidget / RenderControl).
void Scene3DRenderer::shutdown()
{
qCDebug(Scene3D) << Q_FUNC_INFO << QThread::currentThread();
+ // In case the same item is rendered on another window reset it
+ m_resetRequested = true;
+
// Set to null so that subsequent calls to render
// would return early
m_item = nullptr;
@@ -243,8 +245,13 @@ void Scene3DRenderer::shutdown()
// Shutdown the Renderer Aspect while the OpenGL context
// is still valid
- if (m_renderAspect)
+ if (m_renderAspect) {
static_cast<QRenderAspectPrivate*>(QRenderAspectPrivate::get(m_renderAspect))->renderShutdown();
+ m_renderAspect = nullptr;
+ }
+ m_aspectEngine = nullptr;
+ m_finalFBO.reset();
+ m_multisampledFBO.reset();
}
// QtQuick render thread (which may also be the gui/main thread with QQuickWidget / RenderControl)
@@ -254,7 +261,6 @@ void Scene3DRenderer::onSceneGraphInvalidated()
if (m_needsShutdown) {
m_needsShutdown = false;
shutdown();
- QMetaObject::invokeMethod(m_cleaner, "cleanup");
}
}
@@ -265,7 +271,6 @@ void Scene3DRenderer::onWindowChanged(QQuickWindow *w)
if (m_needsShutdown) {
m_needsShutdown = false;
shutdown();
- QMetaObject::invokeMethod(m_cleaner, "cleanup");
}
}
}
@@ -288,6 +293,7 @@ void Scene3DRenderer::beforeSynchronize()
// SceneGraph update for nothing
if (m_skipFrame) {
m_skipFrame = false;
+ ContextSaver saver;
static_cast<QRenderAspectPrivate*>(QRenderAspectPrivate::get(m_renderAspect))->renderSynchronous(false);
return;
}
diff --git a/src/quick3d/imports/scene3d/scene3drenderer_p.h b/src/quick3d/imports/scene3d/scene3drenderer_p.h
index b558359c6..e6e7b0036 100644
--- a/src/quick3d/imports/scene3d/scene3drenderer_p.h
+++ b/src/quick3d/imports/scene3d/scene3drenderer_p.h
@@ -78,9 +78,7 @@ class Scene3DRenderer : public QObject
{
Q_OBJECT
public:
- Scene3DRenderer(Scene3DItem *item,
- Qt3DCore::QAspectEngine *aspectEngine,
- QRenderAspect *renderAspect);
+ Scene3DRenderer();
~Scene3DRenderer();
void setSGNode(Scene3DSGNode *node);
@@ -89,7 +87,12 @@ public:
void setCompositingMode(Scene3DItem::CompositingMode mode);
void setSkipFrame(bool skip);
void setScene3DViews(const QVector<Scene3DView *> views);
+ void init(Scene3DItem *item, Qt3DCore::QAspectEngine *aspectEngine, QRenderAspect *renderAspect);
+ QRenderAspect *renderAspect() const
+ {
+ return m_renderAspect;
+ }
public Q_SLOTS:
void render();
void shutdown();
@@ -103,13 +106,12 @@ private:
void scheduleRootEntityChange();
Scene3DItem *m_item; // Will be released by the QQuickWindow/QML Engine
- Qt3DCore::QAspectEngine *m_aspectEngine; // Will be released by the Scene3DRendererCleaner
+ Qt3DCore::QAspectEngine *m_aspectEngine; // Will be released by the Scene3DItem
QRenderAspect *m_renderAspect; // Will be released by the aspectEngine
QScopedPointer<QOpenGLFramebufferObject> m_multisampledFBO;
QScopedPointer<QOpenGLFramebufferObject> m_finalFBO;
QScopedPointer<QSGTexture> m_texture;
Scene3DSGNode *m_node; // Will be released by the QtQuick SceneGraph
- Scene3DCleaner *m_cleaner;
QQuickWindow *m_window;
QMutex m_windowMutex;
QSize m_lastSize;
@@ -123,9 +125,10 @@ private:
QSemaphore m_allowRendering;
Scene3DItem::CompositingMode m_compositingMode;
QVector<Scene3DView *> m_views;
+ bool m_resetRequested = false;
quint32 m_textureId;
- friend class Scene3DCleaner;
+ friend class Scene3DItem;
};
} // namespace Qt3DRender
diff --git a/src/quick3d/quick3dextras/qt3dquickwindow.cpp b/src/quick3d/quick3dextras/qt3dquickwindow.cpp
index 38f0df857..93ec47835 100644
--- a/src/quick3d/quick3dextras/qt3dquickwindow.cpp
+++ b/src/quick3d/quick3dextras/qt3dquickwindow.cpp
@@ -49,6 +49,7 @@
****************************************************************************/
#include <Qt3DQuickExtras/qt3dquickwindow.h>
+#include <Qt3DExtras/Qt3DWindow>
#include "qt3dquickwindow_p.h"
#include <Qt3DQuick/QQmlAspectEngine>
#include <Qt3DQuickExtras/qt3dquickwindow.h>
@@ -114,24 +115,10 @@ Qt3DQuickWindow::Qt3DQuickWindow(QWindow *parent)
: QWindow(*new Qt3DQuickWindowPrivate(), parent)
{
Q_D(Qt3DQuickWindow);
- setSurfaceType(QSurface::OpenGLSurface);
resize(1024, 768);
- QSurfaceFormat format = QSurfaceFormat::defaultFormat();
-#ifdef QT_OPENGL_ES_2
- format.setRenderableType(QSurfaceFormat::OpenGLES);
-#else
- if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL) {
- format.setVersion(4, 3);
- format.setProfile(QSurfaceFormat::CoreProfile);
- }
-#endif
- format.setDepthBufferSize(24);
- format.setSamples(4);
- format.setStencilBufferSize(8);
- setFormat(format);
- QSurfaceFormat::setDefaultFormat(format);
+ Qt3DExtras::setupWindowSurface(this, Qt3DRender::API::OpenGL);
auto coreAspect = new Qt3DCore::QCoreAspect;
d->m_renderAspect = new Qt3DRender::QRenderAspect;
diff --git a/src/quick3d/quick3dextras/quick3dextras.pro b/src/quick3d/quick3dextras/quick3dextras.pro
index f7c956f8a..c57e7af0c 100644
--- a/src/quick3d/quick3dextras/quick3dextras.pro
+++ b/src/quick3d/quick3dextras/quick3dextras.pro
@@ -1,7 +1,7 @@
TARGET = Qt3DQuickExtras
MODULE = 3dquickextras
-QT += core core-private qml qml-private 3dcore 3dinput 3dquick 3dquick-private 3drender 3drender-private 3dlogic 3dextras
+QT += core core-private qml qml-private 3dcore 3dinput 3dquick 3dquick-private 3drender 3drender-private 3dlogic 3dextras 3dextras-private
CONFIG -= precompile_header
diff --git a/src/render/backend/abstractrenderer_p.h b/src/render/backend/abstractrenderer_p.h
index 020aa6c9f..0dd24dcd8 100644
--- a/src/render/backend/abstractrenderer_p.h
+++ b/src/render/backend/abstractrenderer_p.h
@@ -54,6 +54,7 @@
#include <QtCore/qmutex.h>
#include <Qt3DRender/private/qt3drender_global_p.h>
#include <Qt3DRender/private/handle_types_p.h>
+#include <Qt3DRender/qrenderapi.h>
#include <Qt3DCore/qaspectjob.h>
#include <Qt3DCore/qnodeid.h>
#include <QtGui/qsurfaceformat.h>
@@ -80,6 +81,7 @@ class QAspectManager;
namespace Qt3DRender {
class QRenderAspect;
+struct GraphicsApiFilterData;
namespace Render {
@@ -91,18 +93,11 @@ class BackendNode;
class OffscreenSurfaceHelper;
class Shader;
class RenderBackendResourceAccessor;
-
class Q_3DRENDERSHARED_PRIVATE_EXPORT AbstractRenderer
{
public:
virtual ~AbstractRenderer() {}
- enum API {
- OpenGL,
- Vulkan,
- DirectX
- };
-
// Changes made to backend nodes are reported to the Renderer
enum BackendNodeDirtyFlag {
TransformDirty = 1 << 0,
@@ -190,7 +185,7 @@ public:
virtual void setOffscreenSurfaceHelper(OffscreenSurfaceHelper *helper) = 0;
virtual QSurfaceFormat format() = 0;
virtual QOpenGLContext *shareContext() const = 0;
-
+ virtual const GraphicsApiFilterData *contextInfo() const = 0;
// These commands are executed in a dedicated command thread
// More will be added later
diff --git a/src/render/backend/cameralens.cpp b/src/render/backend/cameralens.cpp
index c635ddb61..c8d1d110b 100644
--- a/src/render/backend/cameralens.cpp
+++ b/src/render/backend/cameralens.cpp
@@ -119,8 +119,6 @@ Matrix4x4 CameraLens::viewMatrix(const Matrix4x4 &worldTransform)
convertToQVector3D(Vector3D(position + viewDirection)),
convertToQVector3D(Vector3D(upVector)));
- qDebug(Jobs) << Q_FUNC_INFO << "Transform Matrix" << worldTransform << "View Matrix" << m;
-
return Matrix4x4(m);
}
diff --git a/src/render/backend/resourceaccessor.cpp b/src/render/backend/resourceaccessor.cpp
index ebc55c7ca..e6ed4262b 100644
--- a/src/render/backend/resourceaccessor.cpp
+++ b/src/render/backend/resourceaccessor.cpp
@@ -80,7 +80,7 @@ bool ResourceAccessor::accessResource(ResourceType type,
Q_FALLTHROUGH();
case RenderBackendResourceAccessor::OGLTextureRead:
{
- if (m_renderer->api() != AbstractRenderer::OpenGL) {
+ if (m_renderer->api() != API::OpenGL) {
qWarning() << "Renderer plugin is not compatible with Scene2D";
return false;
}
diff --git a/src/render/backend/visitorutils_p.h b/src/render/backend/visitorutils_p.h
index 476416ab1..364a8f069 100644
--- a/src/render/backend/visitorutils_p.h
+++ b/src/render/backend/visitorutils_p.h
@@ -111,6 +111,24 @@ void visitPrimitives(NodeManagers *manager, const GeometryProvider *renderer, Vi
Buffer *positionBuffer = nullptr;
Buffer *indexBuffer = nullptr;
+ auto updateStride = [](BufferInfo &info, int stride) {
+ if (stride) {
+ info.byteStride = stride;
+ return;
+ }
+ switch (info.type) {
+ case Qt3DCore::QAttribute::Byte: info.byteStride = sizeof(qint8) * info.dataSize; return;
+ case Qt3DCore::QAttribute::UnsignedByte: info.byteStride = sizeof(quint8) * info.dataSize; return;
+ case Qt3DCore::QAttribute::Short: info.byteStride = sizeof(qint16) * info.dataSize; return;
+ case Qt3DCore::QAttribute::UnsignedShort: info.byteStride = sizeof(quint16) * info.dataSize; return;
+ case Qt3DCore::QAttribute::Int: info.byteStride = sizeof(qint32) * info.dataSize; return;
+ case Qt3DCore::QAttribute::UnsignedInt: info.byteStride = sizeof(quint32) * info.dataSize; return;
+ case Qt3DCore::QAttribute::Float: info.byteStride = sizeof(float) * info.dataSize; return;
+ case Qt3DCore::QAttribute::Double: info.byteStride = sizeof(double) * info.dataSize; return;
+ default: return;
+ }
+ };
+
if (geom) {
positionAttribute = manager->lookupResource<Attribute, AttributeManager>(geom->boundingPositionAttribute());
@@ -137,9 +155,9 @@ void visitPrimitives(NodeManagers *manager, const GeometryProvider *renderer, Vi
vertexBufferInfo.data = positionBuffer->data();
vertexBufferInfo.type = positionAttribute->vertexBaseType();
vertexBufferInfo.byteOffset = positionAttribute->byteOffset();
- vertexBufferInfo.byteStride = positionAttribute->byteStride();
vertexBufferInfo.dataSize = positionAttribute->vertexSize();
vertexBufferInfo.count = positionAttribute->count();
+ updateStride(vertexBufferInfo, positionAttribute->byteStride());
if (indexBuffer) { // Indexed
@@ -147,10 +165,10 @@ void visitPrimitives(NodeManagers *manager, const GeometryProvider *renderer, Vi
indexBufferInfo.data = indexBuffer->data();
indexBufferInfo.type = indexAttribute->vertexBaseType();
indexBufferInfo.byteOffset = indexAttribute->byteOffset();
- indexBufferInfo.byteStride = indexAttribute->byteStride();
indexBufferInfo.count = indexAttribute->count();
indexBufferInfo.restartEnabled = renderer->primitiveRestartEnabled();
indexBufferInfo.restartIndexValue = renderer->restartIndexValue();
+ updateStride(indexBufferInfo, indexAttribute->byteStride());
IndexExecutor executor;
executor.m_vertexBufferInfo = vertexBufferInfo;
diff --git a/src/render/configure.json b/src/render/configure.json
index 02a6e3747..6e4e07404 100644
--- a/src/render/configure.json
+++ b/src/render/configure.json
@@ -8,7 +8,8 @@
"commandline": {
"options": {
- "qt3d-opengl-renderer": "boolean"
+ "qt3d-opengl-renderer": "boolean",
+ "qt3d-rhi-renderer": "boolean"
}
},
@@ -18,6 +19,13 @@
"purpose": "Use the OpenGL renderer",
"section": "Qt 3D Renderers",
"output": [ "privateFeature" ]
+ },
+ "qt3d-rhi-renderer": {
+ "label": "RHI Renderer",
+ "purpose": "Use the RHI renderer",
+ "section": "Qt 3D Renderers",
+ "autoDetect": false,
+ "output": [ "privateFeature" ]
}
},
@@ -28,7 +36,8 @@
{
"section": "Qt 3D Renderers",
"entries": [
- "qt3d-opengl-renderer"
+ "qt3d-opengl-renderer",
+ "qt3d-rhi-renderer"
]
}
]
diff --git a/src/render/framegraph/qframegraphnode.cpp b/src/render/framegraph/qframegraphnode.cpp
index 489fa03b4..d28030cb0 100644
--- a/src/render/framegraph/qframegraphnode.cpp
+++ b/src/render/framegraph/qframegraphnode.cpp
@@ -39,6 +39,9 @@
#include "qframegraphnode.h"
#include "qframegraphnode_p.h"
+#include <Qt3DRender/qfilterkey.h>
+#include <Qt3DRender/qtechniquefilter.h>
+#include <Qt3DRender/qrenderpassfilter.h>
#include <Qt3DCore/QNode>
#include <QVector>
@@ -59,6 +62,20 @@ QString dumpNode(const Qt3DRender::QFrameGraphNode *n) {
return res;
}
+QString dumpNodeFilters(const Qt3DRender::QFrameGraphNode *n, const QVector<Qt3DRender::QFilterKey*> &filters) {
+ QString res = QLatin1String(n->metaObject()->className());
+ if (!n->objectName().isEmpty())
+ res += QString(QLatin1String(" (%1)")).arg(n->objectName());
+
+ QStringList kv;
+ for (auto filter: filters)
+ kv.push_back(QString(QLatin1String("%1: %2")).arg(filter->name(), filter->value().toString()));
+ if (kv.size())
+ res += QString(QLatin1String(" <%1>")).arg(kv.join(QLatin1String(", ")));
+
+ return res;
+}
+
QStringList dumpFG(const Qt3DCore::QNode *n, int level = 0)
{
QStringList reply;
@@ -132,14 +149,53 @@ void dumpFGPaths(const Qt3DRender::QFrameGraphNode *n, QStringList &result)
findFGLeaves(rootHFg, fgLeaves);
// Traverse back to root
+ int rv = 1;
for (const Qt3DRender::QFrameGraphNode *fgNode : fgLeaves) {
QStringList parents;
while (fgNode != nullptr) {
parents.prepend(dumpNode(fgNode));
fgNode = fgNode->parentFrameGraphNode();
}
- if (parents.size())
- result << QLatin1String("[ ") + parents.join(QLatin1String(", ")) + QLatin1String(" ]");
+ if (parents.size()) {
+ result << QString(QLatin1String("%1 [ %2 ]")).arg(QString::number(rv), parents.join(QLatin1String(", ")));
+ ++rv;
+ }
+ }
+}
+
+void dumpFGFilterState(const Qt3DRender::QFrameGraphNode *n, QStringList &result)
+{
+ // Build FG node hierarchy
+ const HierarchyFGNodePtr rootHFg = buildFGHierarchy(n);
+
+ // Gather FG leaves
+ QVector<const Qt3DRender::QFrameGraphNode *> fgLeaves;
+ findFGLeaves(rootHFg, fgLeaves);
+
+ // Traverse back to root
+ int rv = 1;
+ for (const Qt3DRender::QFrameGraphNode *fgNode : fgLeaves) {
+ int parents = 0;
+ QStringList filters;
+ while (fgNode != nullptr) {
+ ++parents;
+ if (fgNode->isEnabled()) {
+ auto techniqueFilter = qobject_cast<const Qt3DRender::QTechniqueFilter *>(fgNode);
+ if (techniqueFilter && techniqueFilter->matchAll().size())
+ filters.prepend(dumpNodeFilters(techniqueFilter, techniqueFilter->matchAll()));
+ auto renderPassFilter = qobject_cast<const Qt3DRender::QRenderPassFilter *>(fgNode);
+ if (renderPassFilter)
+ filters.prepend(dumpNodeFilters(renderPassFilter, renderPassFilter->matchAny()));
+ }
+ fgNode = fgNode->parentFrameGraphNode();
+ }
+ if (parents) {
+ if (filters.size())
+ result << QString(QLatin1String("%1 [ %2 ]")).arg(QString::number(rv), filters.join(QLatin1String(", ")));
+ else
+ result << QString(QObject::tr("%1 [ No Filters ]")).arg(rv);
+ ++rv;
+ }
}
}
@@ -350,6 +406,14 @@ QStringList QFrameGraphNodePrivate::dumpFrameGraphPaths() const
return result;
}
+QStringList QFrameGraphNodePrivate::dumpFrameGraphFilterState() const
+{
+ Q_Q(const QFrameGraphNode);
+ QStringList result;
+ dumpFGFilterState(q, result);
+ return result;
+}
+
/*! \internal */
QFrameGraphNode::QFrameGraphNode(QFrameGraphNodePrivate &dd, QNode *parent)
: QNode(dd, parent)
diff --git a/src/render/framegraph/qframegraphnode_p.h b/src/render/framegraph/qframegraphnode_p.h
index 4d9516b88..b9a60d79a 100644
--- a/src/render/framegraph/qframegraphnode_p.h
+++ b/src/render/framegraph/qframegraphnode_p.h
@@ -72,6 +72,7 @@ public:
QString dumpFrameGraph() const;
QStringList dumpFrameGraphPaths() const;
+ QStringList dumpFrameGraphFilterState() const;
Q_DECLARE_PUBLIC(QFrameGraphNode)
};
diff --git a/src/render/framegraph/qlayerfilter.cpp b/src/render/framegraph/qlayerfilter.cpp
index 50f3eb429..ef87b1c2e 100644
--- a/src/render/framegraph/qlayerfilter.cpp
+++ b/src/render/framegraph/qlayerfilter.cpp
@@ -213,8 +213,9 @@ void QLayerFilter::removeLayer(QLayer *layer)
{
Q_ASSERT(layer);
Q_D(QLayerFilter);
+ if (!d->m_layers.removeOne(layer))
+ return;
d->update();
- d->m_layers.removeOne(layer);
// Remove bookkeeping connection
d->unregisterDestructionHelper(layer);
}
diff --git a/src/render/framegraph/qrendercapture.cpp b/src/render/framegraph/qrendercapture.cpp
index 12afbeee8..97fd5ed8d 100644
--- a/src/render/framegraph/qrendercapture.cpp
+++ b/src/render/framegraph/qrendercapture.cpp
@@ -250,8 +250,7 @@ QRenderCaptureReply *QRenderCapturePrivate::takeReply(int captureId)
QMutexLocker lock(&m_mutex);
for (int i = 0; i < m_waitingReplies.size(); ++i) {
if (m_waitingReplies[i]->d_func()->m_captureId == captureId) {
- reply = m_waitingReplies[i];
- m_waitingReplies.remove(i);
+ reply = m_waitingReplies.takeAt(i);
break;
}
}
@@ -285,12 +284,12 @@ QRenderCapture::QRenderCapture(Qt3DCore::QNode *parent)
}
/*!
- * \deprecated
- * Used to request render capture. User can specify a \a captureId to identify
- * the request. The requestId does not have to be unique. Only one render capture result
- * is produced per requestCapture call even if the frame graph has multiple leaf nodes.
- * The function returns a QRenderCaptureReply object, which receives the captured image
- * when it is done. The user is responsible for deallocating the returned object.
+ * \deprecated Used to request render capture. User can specify a \a captureId
+ * to identify the request. The requestId does not have to be unique. Only one
+ * render capture result is produced per requestCapture call even if the frame
+ * graph has multiple leaf nodes. The function returns a QRenderCaptureReply
+ * object, which receives the captured image when it is done. The user is
+ * responsible for deallocating the returned object by calling deleteLater().
*/
QRenderCaptureReply *QRenderCapture::requestCapture(int captureId)
{
@@ -309,10 +308,11 @@ QRenderCaptureReply *QRenderCapture::requestCapture(int captureId)
}
/*!
- * Used to request render capture from a specified \a rect. Only one render capture result
- * is produced per requestCapture call even if the frame graph has multiple leaf nodes.
- * The function returns a QRenderCaptureReply object, which receives the captured image
- * when it is done. The user is responsible for deallocating the returned object.
+ * Used to request render capture from a specified \a rect. Only one render
+ * capture result is produced per requestCapture call even if the frame graph
+ * has multiple leaf nodes. The function returns a QRenderCaptureReply object,
+ * which receives the captured image when it is done. The user is responsible
+ * for deallocating the returned object by calling deleteLater().
*/
QRenderCaptureReply *QRenderCapture::requestCapture(const QRect &rect)
{
@@ -334,10 +334,11 @@ QRenderCaptureReply *QRenderCapture::requestCapture(const QRect &rect)
}
/*!
- * Used to request render capture. Only one render capture result is produced per
- * requestCapture call even if the frame graph has multiple leaf nodes.
- * The function returns a QRenderCaptureReply object, which receives the captured image
- * when it is done. The user is responsible for deallocating the returned object.
+ * Used to request render capture. Only one render capture result is produced
+ * per requestCapture call even if the frame graph has multiple leaf nodes. The
+ * function returns a QRenderCaptureReply object, which receives the captured
+ * image when it is done. The user is responsible for deallocating the returned
+ * object by calling deleterLater().
*/
Qt3DRender::QRenderCaptureReply *QRenderCapture::requestCapture()
{
diff --git a/src/render/framegraph/qrenderpassfilter.cpp b/src/render/framegraph/qrenderpassfilter.cpp
index dc6cefd00..304da32d9 100644
--- a/src/render/framegraph/qrenderpassfilter.cpp
+++ b/src/render/framegraph/qrenderpassfilter.cpp
@@ -150,8 +150,9 @@ void QRenderPassFilter::removeMatch(QFilterKey *filterKey)
Q_ASSERT(filterKey);
Q_D(QRenderPassFilter);
+ if (!d->m_matchList.removeOne(filterKey))
+ return;
d->update();
- d->m_matchList.removeOne(filterKey);
// Remove bookkeeping connection
d->unregisterDestructionHelper(filterKey);
}
@@ -188,8 +189,9 @@ void QRenderPassFilter::removeParameter(QParameter *parameter)
Q_ASSERT(parameter);
Q_D(QRenderPassFilter);
+ if (!d->m_parameters.removeOne(parameter))
+ return;
d->update();
- d->m_parameters.removeOne(parameter);
// Remove bookkeeping connection
d->unregisterDestructionHelper(parameter);
}
diff --git a/src/render/framegraph/qrenderstateset.cpp b/src/render/framegraph/qrenderstateset.cpp
index 6923e8034..d8bde2ec4 100644
--- a/src/render/framegraph/qrenderstateset.cpp
+++ b/src/render/framegraph/qrenderstateset.cpp
@@ -202,8 +202,9 @@ void QRenderStateSet::removeRenderState(QRenderState *state)
Q_ASSERT(state);
Q_D(QRenderStateSet);
+ if (!d->m_renderStates.removeOne(state))
+ return;
d->update();
- d->m_renderStates.removeOne(state);
// Remove bookkeeping connection
d->unregisterDestructionHelper(state);
}
diff --git a/src/render/framegraph/qtechniquefilter.cpp b/src/render/framegraph/qtechniquefilter.cpp
index d00d04da9..17a79ba25 100644
--- a/src/render/framegraph/qtechniquefilter.cpp
+++ b/src/render/framegraph/qtechniquefilter.cpp
@@ -154,8 +154,9 @@ void QTechniqueFilter::removeMatch(QFilterKey *filterKey)
{
Q_ASSERT(filterKey);
Q_D(QTechniqueFilter);
+ if (!d->m_matchList.removeOne(filterKey))
+ return;
d->update();
- d->m_matchList.removeOne(filterKey);
// Remove bookkeeping connection
d->unregisterDestructionHelper(filterKey);
}
@@ -191,8 +192,9 @@ void QTechniqueFilter::removeParameter(QParameter *parameter)
{
Q_ASSERT(parameter);
Q_D(QTechniqueFilter);
+ if (!d->m_parameters.removeOne(parameter))
+ return;
d->update();
- d->m_parameters.removeOne(parameter);
// Remove bookkeeping connection
d->unregisterDestructionHelper(parameter);
}
diff --git a/src/render/framegraph/rendercapture.cpp b/src/render/framegraph/rendercapture.cpp
index 4d8ad0591..fea0acad1 100644
--- a/src/render/framegraph/rendercapture.cpp
+++ b/src/render/framegraph/rendercapture.cpp
@@ -111,7 +111,8 @@ void RenderCapture::syncRenderCapturesToFrontend(Qt3DCore::QAspectManager *manag
QMutexLocker lock(&m_mutex);
for (const RenderCaptureDataPtr &data : qAsConst(m_renderCaptureData)) {
QPointer<QRenderCaptureReply> reply = dfrontend->takeReply(data.data()->captureId);
- if (reply) {
+ // Note: QPointer has no operator bool, we must use isNull() to check it
+ if (!reply.isNull()) {
dfrontend->setImage(reply, data.data()->image);
emit reply->completed();
}
diff --git a/src/render/frontend/qrenderapi.h b/src/render/frontend/qrenderapi.h
new file mode 100644
index 000000000..fc046642a
--- /dev/null
+++ b/src/render/frontend/qrenderapi.h
@@ -0,0 +1,61 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 QT3DRENDER_QRENDERAPI_H
+#define QT3DRENDER_QRENDERAPI_H
+
+#include <Qt3DRender/qt3drender_global.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+enum class API {
+ OpenGL,
+ Vulkan,
+ DirectX,
+ Metal,
+ Null
+};
+
+} // namespace Qt3Drender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_QRENDERAPI_H
diff --git a/src/render/frontend/qrenderaspect.cpp b/src/render/frontend/qrenderaspect.cpp
index 9412d3526..015f5bba2 100644
--- a/src/render/frontend/qrenderaspect.cpp
+++ b/src/render/frontend/qrenderaspect.cpp
@@ -165,6 +165,8 @@
#include <Qt3DRender/private/updatelevelofdetailjob_p.h>
#include <Qt3DRender/private/job_common_p.h>
#include <Qt3DRender/private/pickeventfilter_p.h>
+#include <Qt3DRender/private/techniquemanager_p.h>
+#include <Qt3DRender/private/qgraphicsapifilter_p.h>
#include <private/qrenderpluginfactory_p.h>
#include <private/qrenderplugin_p.h>
@@ -189,6 +191,81 @@ QT_BEGIN_NAMESPACE
using namespace Qt3DCore;
+namespace {
+
+QString dumpNode(const Qt3DCore::QEntity *n) {
+ auto formatNode = [](const Qt3DCore::QNode *n) {
+ QString res = QString(QLatin1String("%1{%2}"))
+ .arg(QLatin1String(n->metaObject()->className()))
+ .arg(n->id().id());
+ if (!n->objectName().isEmpty())
+ res += QString(QLatin1String(" (%1)")).arg(n->objectName());
+ if (!n->isEnabled())
+ res += QLatin1String(" [D]");
+ return res;
+ };
+
+ return formatNode(n);
+}
+
+QString dumpNodeFilters(const QString &filterType, const QVector<Qt3DRender::QFilterKey*> &filters) {
+ QString res;
+
+ QStringList kv;
+ for (auto filter: filters)
+ kv.push_back(QString(QLatin1String("%1: %2")).arg(filter->name(), filter->value().toString()));
+ if (kv.size())
+ res += QString(QLatin1String("%1 <%2>")).arg(filterType, kv.join(QLatin1String(", ")));
+
+ return res;
+}
+
+QStringList dumpSGFilterState(Qt3DRender::Render::TechniqueManager *manager,
+ const Qt3DRender::GraphicsApiFilterData *contextData,
+ const Qt3DCore::QNode *n, int level = 0)
+{
+ using namespace Qt3DRender;
+
+ QStringList reply;
+ const auto *entity = qobject_cast<const Qt3DCore::QEntity *>(n);
+ if (entity != nullptr) {
+ QString res = dumpNode(entity);
+ auto materials = entity->componentsOfType<QMaterial>();
+ if (materials.size() && materials.front()->effect()) {
+ auto m = materials.front();
+ const auto techniques = m->effect()->techniques();
+ for (auto t: m->effect()->techniques()) {
+ auto apiFilter = t->graphicsApiFilter();
+ if (apiFilter) {
+ auto backendTechnique = manager->lookupResource(t->id());
+ if (backendTechnique &&
+ !(*contextData == *backendTechnique->graphicsApiFilter()))
+ continue; // skip technique that doesn't match current renderer
+ }
+
+ QStringList filters;
+ filters += dumpNodeFilters(QLatin1String("T"), t->filterKeys());
+
+ const auto &renderPasses = t->renderPasses();
+ for (auto r: renderPasses)
+ filters += dumpNodeFilters(QLatin1String("RP"), r->filterKeys());
+
+ if (filters.size())
+ res += QLatin1String(" [ %1 ]").arg(filters.join(QLatin1String(" ")));
+ }
+ }
+ reply += res.rightJustified(res.length() + level * 2, ' ');
+ level++;
+ }
+
+ const auto children = n->childNodes();
+ for (auto *child: children)
+ reply += dumpSGFilterState(manager, contextData, child, level);
+
+ return reply;
+}
+
+}
namespace Qt3DRender {
#define CreateSynchronizerJobPtr(lambda, type) \
@@ -240,8 +317,11 @@ QRenderAspectPrivate::QRenderAspectPrivate(QRenderAspect::RenderType type)
m_updateWorldBoundingVolumeJob->addDependency(m_worldTransformJob);
m_updateWorldBoundingVolumeJob->addDependency(m_calculateBoundingVolumeJob);
+ m_calculateBoundingVolumeJob->addDependency(m_updateTreeEnabledJob);
m_expandBoundingVolumeJob->addDependency(m_updateWorldBoundingVolumeJob);
m_updateLevelOfDetailJob->addDependency(m_expandBoundingVolumeJob);
+ m_pickBoundingVolumeJob->addDependency(m_expandBoundingVolumeJob);
+ m_rayCastingJob->addDependency(m_expandBoundingVolumeJob);
}
/*! \internal */
@@ -537,7 +617,7 @@ QRenderAspect::~QRenderAspect()
// Called by Scene3DRenderer only
void QRenderAspectPrivate::renderInitialize(QOpenGLContext *context)
{
- if (m_renderer->api() == Render::AbstractRenderer::OpenGL)
+ if (m_renderer->api() == API::OpenGL)
m_renderer->setOpenGLContext(context);
m_renderer->initialize();
}
@@ -612,6 +692,9 @@ QVector<Qt3DCore::QAspectJobPtr> QRenderAspect::jobsToExecute(qint64 time)
const QVector<QAspectJobPtr> geometryJobs = d->createGeometryRendererJobs();
jobs.append(geometryJobs);
+ const QVector<QAspectJobPtr> preRenderingJobs = d->createPreRendererJobs();
+ jobs.append(preRenderingJobs);
+
// Don't spawn any rendering jobs, if the renderer decides to skip this frame
// Note: this only affects rendering jobs (jobs that load buffers,
// perform picking,... must still be run)
@@ -630,12 +713,8 @@ QVector<Qt3DCore::QAspectJobPtr> QRenderAspect::jobsToExecute(qint64 time)
// Create the jobs to build the frame
const bool entitiesEnabledDirty = dirtyBitsForFrame & AbstractRenderer::EntityEnabledDirty;
- if (entitiesEnabledDirty) {
+ if (entitiesEnabledDirty)
jobs.push_back(d->m_updateTreeEnabledJob);
- // This dependency is added here because we clear all dependencies
- // at the start of this function.
- d->m_calculateBoundingVolumeJob->addDependency(d->m_updateTreeEnabledJob);
- }
if (dirtyBitsForFrame & AbstractRenderer::TransformDirty) {
jobs.push_back(d->m_worldTransformJob);
@@ -663,9 +742,6 @@ QVector<Qt3DCore::QAspectJobPtr> QRenderAspect::jobsToExecute(qint64 time)
if (layersDirty)
jobs.push_back(d->m_updateEntityLayersJob);
- const QVector<QAspectJobPtr> preRenderingJobs = d->createPreRendererJobs();
- jobs.append(preRenderingJobs);
-
const QVector<QAspectJobPtr> renderBinJobs = d->m_renderer->renderBinJobs();
jobs.append(renderBinJobs);
}
@@ -686,6 +762,13 @@ QVariant QRenderAspect::executeCommand(const QStringList &args)
return Qt3DRender::QFrameGraphNodePrivate::get(fg)->dumpFrameGraph();
if (args.front() == QLatin1String("framepaths"))
return Qt3DRender::QFrameGraphNodePrivate::get(fg)->dumpFrameGraphPaths().join(QLatin1String("\n"));
+ if (args.front() == QLatin1String("filterstates")) {
+ const auto activeContextInfo = d->m_renderer->contextInfo();
+ QString res = QLatin1String("Active Graphics API: ") + activeContextInfo->toString() + QLatin1String("\n");
+ res += QLatin1String("Render Views:\n ") + Qt3DRender::QFrameGraphNodePrivate::get(fg)->dumpFrameGraphFilterState().join(QLatin1String("\n ")) + QLatin1String("\n");
+ res += QLatin1String("Scene Graph:\n ") + dumpSGFilterState(d->m_nodeManagers->techniqueManager(), activeContextInfo, d->m_root).join(QLatin1String("\n "));
+ return res;
+ }
}
if (args.front() == QLatin1String("scenegraph"))
return droot->dumpSceneGraph();
@@ -803,7 +886,7 @@ QVector<QAspectJobPtr> QRenderAspectPrivate::createPreRendererJobs() const
const auto frameMouseEvents = m_pickEventFilter->pendingMouseEvents();
const auto frameKeyEvents = m_pickEventFilter->pendingKeyEvents();
- m_renderer->setPendingEvents(frameMouseEvents, m_pickEventFilter->pendingKeyEvents());
+ m_renderer->setPendingEvents(frameMouseEvents, frameKeyEvents);
auto jobs = m_renderer->preRenderingJobs();
diff --git a/src/render/frontend/qrendercapabilities.h b/src/render/frontend/qrendercapabilities.h
index 14e233f59..512be5434 100644
--- a/src/render/frontend/qrendercapabilities.h
+++ b/src/render/frontend/qrendercapabilities.h
@@ -86,8 +86,11 @@ class Q_3DRENDERSHARED_EXPORT QRenderCapabilities : public QObject
Q_PROPERTY(int maxComputeSharedMemorySize READ maxComputeSharedMemorySize CONSTANT)
public:
enum API {
- OpenGL = QSurfaceFormat::OpenGL,
- OpenGLES = QSurfaceFormat::OpenGLES,
+ OpenGL = QSurfaceFormat::OpenGL, // 1
+ OpenGLES = QSurfaceFormat::OpenGLES, // 2
+ Vulkan = 3, // 3
+ DirectX, // 4
+ RHI, // 5
};
Q_ENUM(API)
diff --git a/src/render/frontend/qrendersettings.h b/src/render/frontend/qrendersettings.h
index 5e63b1183..be4dd27de 100644
--- a/src/render/frontend/qrendersettings.h
+++ b/src/render/frontend/qrendersettings.h
@@ -43,9 +43,9 @@
#include <Qt3DCore/qcomponent.h>
#include <Qt3DRender/qt3drender_global.h>
#include <Qt3DRender/qpickingsettings.h>
+#include <QtGui/qtguiglobal.h>
QT_BEGIN_NAMESPACE
-
namespace Qt3DRender {
class QFrameGraphNode;
diff --git a/src/render/frontend/qrendertarget.cpp b/src/render/frontend/qrendertarget.cpp
index 4471b31fa..e359d9bdd 100644
--- a/src/render/frontend/qrendertarget.cpp
+++ b/src/render/frontend/qrendertarget.cpp
@@ -134,8 +134,9 @@ void QRenderTarget::removeOutput(QRenderTargetOutput *output)
{
Q_D(QRenderTarget);
+ if (!d->m_outputs.removeOne(output))
+ return;
d->update();
- d->m_outputs.removeOne(output);
// Remove bookkeeping connection
d->unregisterDestructionHelper(output);
}
diff --git a/src/render/frontend/render-frontend.pri b/src/render/frontend/render-frontend.pri
index 934672a01..02eb74ce1 100644
--- a/src/render/frontend/render-frontend.pri
+++ b/src/render/frontend/render-frontend.pri
@@ -28,6 +28,7 @@ HEADERS += \
$$PWD/qrenderpluginfactory_p.h \
$$PWD/qrenderpluginfactoryif_p.h \
$$PWD/qlevelofdetailboundingsphere.h \
+ $$PWD/qrenderapi.h \
$$PWD/qrendercapabilities.h \
$$PWD/qrendercapabilities_p.h
diff --git a/src/render/jobs/pickboundingvolumejob.cpp b/src/render/jobs/pickboundingvolumejob.cpp
index 96610f9f7..eebacc681 100644
--- a/src/render/jobs/pickboundingvolumejob.cpp
+++ b/src/render/jobs/pickboundingvolumejob.cpp
@@ -74,7 +74,7 @@ public:
PickBoundingVolumeJobPrivate(PickBoundingVolumeJob *q) : q_ptr(q) { }
~PickBoundingVolumeJobPrivate() override = default;
- bool isRequired() override;
+ bool isRequired() const override;
void postFrame(Qt3DCore::QAspectManager *manager) override;
enum CustomEventType {
@@ -94,9 +94,9 @@ public:
};
-bool PickBoundingVolumeJobPrivate::isRequired()
+bool PickBoundingVolumeJobPrivate::isRequired() const
{
- Q_Q(PickBoundingVolumeJob);
+ Q_Q(const PickBoundingVolumeJob);
return !q->m_pendingMouseEvents.isEmpty() || q->m_pickersDirty || q->m_oneEnabledAtLeast;
}
diff --git a/src/render/jobs/raycastingjob.cpp b/src/render/jobs/raycastingjob.cpp
index df01213f0..b7d4c4b7c 100644
--- a/src/render/jobs/raycastingjob.cpp
+++ b/src/render/jobs/raycastingjob.cpp
@@ -89,7 +89,7 @@ public:
RayCastingJobPrivate(RayCastingJob *q) : q_ptr(q) { }
~RayCastingJobPrivate() override { Q_ASSERT(dispatches.isEmpty()); }
- bool isRequired() override;
+ bool isRequired() const override;
void postFrame(Qt3DCore::QAspectManager *manager) override;
QVector<QPair<RayCaster *, QAbstractRayCaster::Hits>> dispatches;
@@ -99,9 +99,9 @@ public:
};
-bool RayCastingJobPrivate::isRequired()
+bool RayCastingJobPrivate::isRequired() const
{
- Q_Q(RayCastingJob);
+ Q_Q(const RayCastingJob);
return q->m_castersDirty || q->m_oneEnabledAtLeast;
}
diff --git a/src/render/jobs/updatelevelofdetailjob.cpp b/src/render/jobs/updatelevelofdetailjob.cpp
index 946d3bca8..d7d9c2f67 100644
--- a/src/render/jobs/updatelevelofdetailjob.cpp
+++ b/src/render/jobs/updatelevelofdetailjob.cpp
@@ -212,7 +212,7 @@ class UpdateLevelOfDetailJobPrivate : public Qt3DCore::QAspectJobPrivate
public:
UpdateLevelOfDetailJobPrivate(UpdateLevelOfDetailJob *q) : q_ptr(q) { }
- bool isRequired() override;
+ bool isRequired() const override;
void postFrame(Qt3DCore::QAspectManager *manager) override;
QVector<QPair<Qt3DCore::QNodeId, int>> m_updatedIndices;
@@ -266,9 +266,9 @@ void UpdateLevelOfDetailJob::run()
d->m_updatedIndices = visitor.updatedIndices();
}
-bool UpdateLevelOfDetailJobPrivate::isRequired()
+bool UpdateLevelOfDetailJobPrivate::isRequired() const
{
- Q_Q(UpdateLevelOfDetailJob);
+ Q_Q(const UpdateLevelOfDetailJob);
return q->m_manager->levelOfDetailManager()->count() > 0;
}
diff --git a/src/render/lights/qenvironmentlight.cpp b/src/render/lights/qenvironmentlight.cpp
index 52ee30cb2..143ee1978 100644
--- a/src/render/lights/qenvironmentlight.cpp
+++ b/src/render/lights/qenvironmentlight.cpp
@@ -42,6 +42,8 @@
#include "qabstracttexture.h"
#include <QVector3D>
+#include <cmath>
+
QT_BEGIN_NAMESPACE
namespace Qt3DRender
@@ -98,6 +100,9 @@ void QEnvironmentLightPrivate::_q_updateEnvMapsSize()
m_specular->height(),
m_specular->depth());
m_shaderData->setProperty("specularSize", QVariant::fromValue(specularSize));
+
+ const int levels = int(std::log2(specularSize.x() > 0.0f ? specularSize.x() : 1.0f)) + 1;
+ m_shaderData->setProperty("specularMipLevels", QVariant::fromValue(levels));
}
/*!
diff --git a/src/render/materialsystem/material.cpp b/src/render/materialsystem/material.cpp
index 6fda17ccd..62dc0958a 100644
--- a/src/render/materialsystem/material.cpp
+++ b/src/render/materialsystem/material.cpp
@@ -76,19 +76,23 @@ void Material::syncFromFrontEnd(const QNode *frontEnd, bool firstTime)
if (!node)
return;
+ AbstractRenderer::BackendNodeDirtySet dirty = firstTime ? AbstractRenderer::MaterialDirty : static_cast<AbstractRenderer::BackendNodeDirtyFlag>(0);
+
auto parameters = qIdsForNodes(node->parameters());
std::sort(std::begin(parameters), std::end(parameters));
- if (m_parameterPack.parameters() != parameters)
+ if (m_parameterPack.parameters() != parameters) {
m_parameterPack.setParameters(parameters);
+ dirty |= AbstractRenderer::AllDirty;
+ }
const auto effectId = node->effect() ? node->effect()->id() : QNodeId{};
- if (effectId != m_effectUuid)
+ if (effectId != m_effectUuid) {
m_effectUuid = effectId;
+ dirty |= AbstractRenderer::AllDirty;
+ }
- if (firstTime)
- markDirty(AbstractRenderer::MaterialDirty);
- else
- markDirty(AbstractRenderer::AllDirty);
+ if (dirty)
+ markDirty(dirty);
}
QVector<Qt3DCore::QNodeId> Material::parameters() const
diff --git a/src/render/materialsystem/prototypes/default.json b/src/render/materialsystem/prototypes/default.json
index 597de41c3..dfa51898f 100644
--- a/src/render/materialsystem/prototypes/default.json
+++ b/src/render/materialsystem/prototypes/default.json
@@ -32,6 +32,15 @@
},
"substitution": "$type $value = $name;",
"headerSnippets": [ "$qualifier $type $name;" ]
+ },
+ {
+ "format": {
+ "api": "RHI",
+ "major": 1,
+ "minor": 0
+ },
+ "substitution": "$type $value = $name;",
+ "headerSnippets": [ "add-input $qualifier $type $name" ]
}
]
},
@@ -62,6 +71,14 @@
"minor": 0
},
"substitution": "$type $value = $type($constant);"
+ },
+ {
+ "format": {
+ "api": "RHI",
+ "major": 1,
+ "minor": 0
+ },
+ "substitution": "$type $value = $type($constant);"
}
]
},
@@ -102,6 +119,15 @@
},
"substitution": "vec4 $color = texture($name, $coord);",
"headerSnippets": [ "uniform sampler2D $name;" ]
+ },
+ {
+ "format": {
+ "api": "RHI",
+ "major": 1,
+ "minor": 0
+ },
+ "substitution": "vec4 $color = texture($name, $coord);",
+ "headerSnippets": [ "add-sampler sampler2D $name" ]
}
]
},
@@ -135,6 +161,15 @@
},
"substitution": "fragColor = $fragColor;",
"headerSnippets": [ "out vec4 fragColor;" ]
+ },
+ {
+ "format": {
+ "api": "RHI",
+ "major": 1,
+ "minor": 0
+ },
+ "substitution": "fragColor = $fragColor;",
+ "headerSnippets": [ "layout(location = 0) out vec4 fragColor;" ]
}
]
},
@@ -160,6 +195,15 @@
},
"substitution": "vec3 $eyePosition = eyePosition;",
"headerSnippets": [ "uniform vec3 eyePosition;" ]
+ },
+ {
+ "format": {
+ "api": "RHI",
+ "major": 1,
+ "minor": 0
+ },
+ "substitution": "vec3 $eyePosition = eyePosition;",
+ "headerSnippets": [ ]
}
]
},
@@ -185,6 +229,15 @@
},
"substitution": "float $time = time;",
"headerSnippets": [ "uniform float time;" ]
+ },
+ {
+ "format": {
+ "api": "RHI",
+ "major": 1,
+ "minor": 0
+ },
+ "substitution": "float $time = time;",
+ "headerSnippets": [ "add-uniform float time" ]
}
]
},
@@ -217,6 +270,14 @@
"minor": 0
},
"substitution": "$type $output = transpose($input);"
+ },
+ {
+ "format": {
+ "api": "RHI",
+ "major": 1,
+ "minor": 0
+ },
+ "substitution": "$type $output = transpose($input);"
}
]
},
@@ -249,6 +310,14 @@
"minor": 0
},
"substitution": "$type $output = normalize($input);"
+ },
+ {
+ "format": {
+ "api": "RHI",
+ "major": 1,
+ "minor": 0
+ },
+ "substitution": "$type $output = normalize($input);"
}
]
},
@@ -282,6 +351,14 @@
"minor": 0
},
"substitution": "$type $difference = $minuend - $subtrahend;"
+ },
+ {
+ "format": {
+ "api": "RHI",
+ "major": 1,
+ "minor": 0
+ },
+ "substitution": "$type $difference = $minuend - $subtrahend;"
}
]
},
@@ -315,6 +392,14 @@
"minor": 0
},
"substitution": "$type $sum = $first + $second;"
+ },
+ {
+ "format": {
+ "api": "RHI",
+ "major": 1,
+ "minor": 0
+ },
+ "substitution": "$type $sum = $first + $second;"
}
]
},
@@ -348,6 +433,14 @@
"minor": 0
},
"substitution": "$type $product = $first * $second;"
+ },
+ {
+ "format": {
+ "api": "RHI",
+ "major": 1,
+ "minor": 0
+ },
+ "substitution": "$type $product = $first * $second;"
}
]
},
@@ -381,6 +474,14 @@
"minor": 0
},
"substitution": "$type $output = $input.$fields;"
+ },
+ {
+ "format": {
+ "api": "RHI",
+ "major": 1,
+ "minor": 0
+ },
+ "substitution": "$type $output = $input.$fields;"
}
]
},
@@ -419,6 +520,15 @@
},
"substitution": "mat3 $matrix = calcWorldSpaceToTangentSpaceMatrix($worldNormal, $worldTangent);",
"headerSnippets": [ "#pragma include :/shaders/gl3/coordinatesystems.inc" ]
+ },
+ {
+ "format": {
+ "api": "RHI",
+ "major": 1,
+ "minor": 0
+ },
+ "substitution": "mat3 $matrix = calcWorldSpaceToTangentSpaceMatrix($worldNormal, $worldTangent);",
+ "headerSnippets": [ "#pragma include :/shaders/rhi/coordinatesystems.inc" ]
}
]
},
@@ -453,6 +563,15 @@
},
"substitution": "vec4 $outputColor = phongFunction($ambient, $diffuse, $specular, $shininess, $worldPosition, $worldView, $worldNormal);",
"headerSnippets": [ "#pragma include :/shaders/gl3/phong.inc.frag" ]
+ },
+ {
+ "format": {
+ "api": "RHI",
+ "major": 1,
+ "minor": 0
+ },
+ "substitution": "vec4 $outputColor = phongFunction($ambient, $diffuse, $specular, $shininess, $worldPosition, $worldView, $worldNormal);",
+ "headerSnippets": [ "#pragma include :/shaders/rhi/phong.inc.frag" ]
}
]
},
@@ -487,6 +606,15 @@
},
"substitution": "vec4 $outputColor = metalRoughFunction($baseColor, $metalness, $roughness, $ambientOcclusion, $worldPosition, $worldView, $worldNormal);",
"headerSnippets": [ "#pragma include :/shaders/gl3/metalrough.inc.frag" ]
+ },
+ {
+ "format": {
+ "api": "RHI",
+ "major": 1,
+ "minor": 0
+ },
+ "substitution": "vec4 $outputColor = metalRoughFunction($baseColor, $metalness, $roughness, $ambientOcclusion, $worldPosition, $worldView, $worldNormal);",
+ "headerSnippets": [ "#pragma include :/shaders/rhi/metalrough.inc.frag" ]
}
]
},
@@ -520,6 +648,14 @@
"minor": 0
},
"substitution": "$type $output = $type($first, $second);"
+ },
+ {
+ "format": {
+ "api": "RHI",
+ "major": 1,
+ "minor": 0
+ },
+ "substitution": "$type $output = $type($first, $second);"
}
]
},
@@ -554,6 +690,14 @@
"minor": 0
},
"substitution": "$type $output = $type($first, $second, $third);"
+ },
+ {
+ "format": {
+ "api": "RHI",
+ "major": 1,
+ "minor": 0
+ },
+ "substitution": "$type $output = $type($first, $second, $third);"
}
]
},
@@ -589,6 +733,14 @@
"minor": 0
},
"substitution": "$type $output = $type($first, $second, $third, $fourth);"
+ },
+ {
+ "format": {
+ "api": "RHI",
+ "major": 1,
+ "minor": 0
+ },
+ "substitution": "$type $output = $type($first, $second, $third, $fourth);"
}
]
},
@@ -621,6 +773,14 @@
"minor": 0
},
"substitution": "$type $output = $type($input);"
+ },
+ {
+ "format": {
+ "api": "RHI",
+ "major": 1,
+ "minor": 0
+ },
+ "substitution": "$type $output = $type($input);"
}
]
}
diff --git a/src/render/materialsystem/qeffect.cpp b/src/render/materialsystem/qeffect.cpp
index ad3dde8f7..0e950cee4 100644
--- a/src/render/materialsystem/qeffect.cpp
+++ b/src/render/materialsystem/qeffect.cpp
@@ -203,7 +203,8 @@ void QEffect::removeParameter(QParameter *parameter)
{
Q_D(QEffect);
- d->m_parameters.removeOne(parameter);
+ if (!d->m_parameters.removeOne(parameter))
+ return;
// Remove bookkeeping connection
d->unregisterDestructionHelper(parameter);
d->update();
@@ -248,8 +249,9 @@ void QEffect::addTechnique(QTechnique *t)
void QEffect::removeTechnique(QTechnique *t)
{
Q_D(QEffect);
+ if (!d->m_techniques.removeOne(t))
+ return;
d->update();
- d->m_techniques.removeOne(t);
// Remove bookkeeping connection
d->unregisterDestructionHelper(t);
}
diff --git a/src/render/materialsystem/qgraphicsapifilter.cpp b/src/render/materialsystem/qgraphicsapifilter.cpp
index 9b5557930..e8b93de6e 100644
--- a/src/render/materialsystem/qgraphicsapifilter.cpp
+++ b/src/render/materialsystem/qgraphicsapifilter.cpp
@@ -53,6 +53,28 @@ GraphicsApiFilterData::GraphicsApiFilterData()
, m_major(0)
{}
+QString GraphicsApiFilterData::toString() const
+{
+ QLatin1String api;
+ switch (m_api) {
+ case QGraphicsApiFilter::OpenGL: api = QLatin1String("OpenGL"); break;
+ case QGraphicsApiFilter::OpenGLES: api = QLatin1String("OpenGL"); break;
+ case QGraphicsApiFilter::Vulkan: api = QLatin1String("Vulkan"); break;
+ case QGraphicsApiFilter::DirectX: api = QLatin1String("DirectX"); break;
+ case QGraphicsApiFilter::RHI: api = QLatin1String("RHI"); break;
+ default: Q_UNREACHABLE();
+ }
+
+ QLatin1String profile;
+ switch (m_profile) {
+ case QGraphicsApiFilter::CoreProfile: profile = QLatin1String(" (Core Profile)"); break;
+ case QGraphicsApiFilter::CompatibilityProfile: profile = QLatin1String(" (Compatibility Profile)"); break;
+ default: break;
+ }
+
+ return QString(QLatin1String("%1 %2.%3%4 (%5)").arg(api, QString::number(m_major), QString::number(m_minor), profile, m_vendor));
+}
+
bool GraphicsApiFilterData::operator ==(const GraphicsApiFilterData &other) const
{
// Check API
diff --git a/src/render/materialsystem/qgraphicsapifilter.h b/src/render/materialsystem/qgraphicsapifilter.h
index 337193673..80cfe026e 100644
--- a/src/render/materialsystem/qgraphicsapifilter.h
+++ b/src/render/materialsystem/qgraphicsapifilter.h
@@ -67,7 +67,8 @@ public:
OpenGLES = QSurfaceFormat::OpenGLES, // 2
OpenGL = QSurfaceFormat::OpenGL, // 1
Vulkan = 3, // 3
- DirectX // 4
+ DirectX, // 4
+ RHI, // 5
};
Q_ENUM(Api) // LCOV_EXCL_LINE
diff --git a/src/render/materialsystem/qgraphicsapifilter_p.h b/src/render/materialsystem/qgraphicsapifilter_p.h
index 435451c27..52c489785 100644
--- a/src/render/materialsystem/qgraphicsapifilter_p.h
+++ b/src/render/materialsystem/qgraphicsapifilter_p.h
@@ -70,6 +70,8 @@ struct Q_3DRENDERSHARED_PRIVATE_EXPORT GraphicsApiFilterData
QStringList m_extensions;
QString m_vendor;
+ QString toString() const;
+
bool operator ==(const GraphicsApiFilterData &other) const;
bool operator !=(const GraphicsApiFilterData &other) const;
bool operator <(const GraphicsApiFilterData &other) const;
diff --git a/src/render/materialsystem/qmaterial.cpp b/src/render/materialsystem/qmaterial.cpp
index 9026e55f1..50fcd59be 100644
--- a/src/render/materialsystem/qmaterial.cpp
+++ b/src/render/materialsystem/qmaterial.cpp
@@ -283,8 +283,9 @@ void QMaterial::removeParameter(QParameter *parameter)
{
Q_ASSERT(parameter);
Q_D(QMaterial);
+ if (!d->m_parameters.removeOne(parameter))
+ return;
d->update();
- d->m_parameters.removeOne(parameter);
}
/*!
diff --git a/src/render/materialsystem/qrenderpass.cpp b/src/render/materialsystem/qrenderpass.cpp
index 83a6e8255..40c9cb1cd 100644
--- a/src/render/materialsystem/qrenderpass.cpp
+++ b/src/render/materialsystem/qrenderpass.cpp
@@ -285,8 +285,9 @@ void QRenderPass::removeFilterKey(QFilterKey *filterKey)
{
Q_ASSERT(filterKey);
Q_D(QRenderPass);
+ if (!d->m_filterKeyList.removeOne(filterKey))
+ return;
d->update();
- d->m_filterKeyList.removeOne(filterKey);
// Remove bookkeeping connection
d->unregisterDestructionHelper(filterKey);
}
@@ -333,8 +334,9 @@ void QRenderPass::removeRenderState(QRenderState *state)
{
Q_ASSERT(state);
Q_D(QRenderPass);
+ if (!d->m_renderStates.removeOne(state))
+ return;
d->update();
- d->m_renderStates.removeOne(state);
// Remove bookkeeping connection
d->unregisterDestructionHelper(state);
}
@@ -380,8 +382,9 @@ void QRenderPass::removeParameter(QParameter *parameter)
{
Q_ASSERT(parameter);
Q_D(QRenderPass);
+ if (!d->m_parameters.removeOne(parameter))
+ return;
d->update();
- d->m_parameters.removeOne(parameter);
// Remove bookkeeping connection
d->unregisterDestructionHelper(parameter);
}
diff --git a/src/render/materialsystem/qtechnique.cpp b/src/render/materialsystem/qtechnique.cpp
index 088716fb9..eca8b88c6 100644
--- a/src/render/materialsystem/qtechnique.cpp
+++ b/src/render/materialsystem/qtechnique.cpp
@@ -269,8 +269,9 @@ void QTechnique::removeFilterKey(QFilterKey *filterKey)
{
Q_ASSERT(filterKey);
Q_D(QTechnique);
+ if (!d->m_filterKeys.removeOne(filterKey))
+ return;
d->update();
- d->m_filterKeys.removeOne(filterKey);
// Remove bookkeeping connection
d->unregisterDestructionHelper(filterKey);
}
@@ -316,8 +317,9 @@ void QTechnique::removeParameter(QParameter *parameter)
{
Q_ASSERT(parameter);
Q_D(QTechnique);
+ if (!d->m_parameters.removeOne(parameter))
+ return;
d->update();
- d->m_parameters.removeOne(parameter);
// Remove bookkeeping connection
d->unregisterDestructionHelper(parameter);
}
@@ -353,8 +355,9 @@ void QTechnique::removeRenderPass(QRenderPass *pass)
{
Q_ASSERT(pass);
Q_D(QTechnique);
+ if (!d->m_renderPasses.removeOne(pass))
+ return;
d->update();
- d->m_renderPasses.removeOne(pass);
// Remove bookkeeping connection
d->unregisterDestructionHelper(pass);
}
diff --git a/src/render/materialsystem/shader.cpp b/src/render/materialsystem/shader.cpp
index be7d0f1ea..ebdb4c64b 100644
--- a/src/render/materialsystem/shader.cpp
+++ b/src/render/materialsystem/shader.cpp
@@ -73,6 +73,7 @@ const int Shader::modelNormalMatrixNameId = StringToInt::lookupId(QLatin1String(
const int Shader::modelViewNormalNameId = StringToInt::lookupId(QLatin1String("modelViewNormal"));
const int Shader::viewportMatrixNameId = StringToInt::lookupId(QLatin1String("viewportMatrix"));
const int Shader::inverseViewportMatrixNameId = StringToInt::lookupId(QLatin1String("inverseViewportMatrix"));
+const int Shader::textureTransformMatrixNameId = StringToInt::lookupId(QLatin1String("textureTransformMatrix"));
const int Shader::aspectRatioNameId = StringToInt::lookupId(QLatin1String("aspectRatio"));
const int Shader::exposureNameId = StringToInt::lookupId(QLatin1String("exposure"));
const int Shader::gammaNameId = StringToInt::lookupId(QLatin1String("gamma"));
@@ -100,6 +101,7 @@ void Shader::cleanup()
m_status = QShaderProgram::NotReady;
m_format = QShaderProgram::GLSL;
m_log.clear();
+ m_requiresFrontendSync = false;
m_dirty = false;
}
diff --git a/src/render/materialsystem/shader_p.h b/src/render/materialsystem/shader_p.h
index 31603bcf7..0ac3227b7 100644
--- a/src/render/materialsystem/shader_p.h
+++ b/src/render/materialsystem/shader_p.h
@@ -86,6 +86,7 @@ public:
static const int modelViewNormalNameId;
static const int viewportMatrixNameId;
static const int inverseViewportMatrixNameId;
+ static const int textureTransformMatrixNameId;
static const int aspectRatioNameId;
static const int exposureNameId;
static const int gammaNameId;
diff --git a/src/render/materialsystem/shaderbuilder.cpp b/src/render/materialsystem/shaderbuilder.cpp
index 871b4cb4c..c7a2aa347 100644
--- a/src/render/materialsystem/shaderbuilder.cpp
+++ b/src/render/materialsystem/shaderbuilder.cpp
@@ -44,10 +44,10 @@
#include <Qt3DRender/private/qshaderprogram_p.h>
#include <Qt3DCore/private/qurlhelper_p.h>
-#include <QtGui/private/qshaderformat_p.h>
-#include <QtGui/private/qshadergraphloader_p.h>
-#include <QtGui/private/qshadergenerator_p.h>
-#include <QtGui/private/qshadernodesloader_p.h>
+#include <Qt3DRender/private/qshaderformat_p.h>
+#include <Qt3DRender/private/qshadergraphloader_p.h>
+#include <Qt3DRender/private/qshadergenerator_p.h>
+#include <Qt3DRender/private/qshadernodesloader_p.h>
#include <QFile>
#include <QFileInfo>
@@ -82,7 +82,7 @@ public:
load();
}
- QHash<QString, QShaderNode> prototypes() const
+ QHash<QString, Qt3DRender::QShaderNode> prototypes() const
{
return m_prototypes;
}
@@ -96,14 +96,14 @@ private:
return;
}
- QShaderNodesLoader loader;
+ Qt3DRender::QShaderNodesLoader loader;
loader.setDevice(&file);
loader.load();
m_prototypes = loader.nodes();
}
QString m_fileName;
- QHash<QString, QShaderNode> m_prototypes;
+ QHash<QString, Qt3DRender::QShaderNode> m_prototypes;
};
Q_GLOBAL_STATIC(GlobalShaderPrototypes, qt3dGlobalShaderPrototypes)
@@ -232,6 +232,8 @@ void ShaderBuilder::generateCode(QShaderProgram::ShaderType type)
auto format = QShaderFormat();
format.setApi(m_graphicsApi.m_api == QGraphicsApiFilter::OpenGLES ? QShaderFormat::OpenGLES
+ : m_graphicsApi.m_api == QGraphicsApiFilter::Vulkan ? QShaderFormat::VulkanFlavoredGLSL
+ : m_graphicsApi.m_api == QGraphicsApiFilter::RHI ? QShaderFormat::RHI
: m_graphicsApi.m_profile == QGraphicsApiFilter::CoreProfile ? QShaderFormat::OpenGLCoreProfile
: m_graphicsApi.m_profile == QGraphicsApiFilter::CompatibilityProfile ? QShaderFormat::OpenGLCompatibilityProfile
: QShaderFormat::OpenGLNoProfile);
@@ -244,7 +246,8 @@ void ShaderBuilder::generateCode(QShaderProgram::ShaderType type)
generator.graph = graph;
const auto code = generator.createShaderCode(m_enabledLayers);
- m_codes.insert(type, QShaderProgramPrivate::deincludify(code, graphPath + QStringLiteral(".glsl")));
+ const auto deincludified = QShaderProgramPrivate::deincludify(code, graphPath + QStringLiteral(".glsl"));
+ m_codes.insert(type, deincludified);
m_dirtyTypes.remove(type);
m_pendingUpdates.push_back({ peerId(),
@@ -276,7 +279,7 @@ void ShaderBuilder::syncFromFrontEnd(const QNode *frontEnd, bool firstTime)
markDirty(AbstractRenderer::ShadersDirty);
}
- static const QVector<std::pair<QShaderProgram::ShaderType, QUrl (QShaderProgramBuilder::*)() const>> shaderTypesToGetters = {
+ static const QVarLengthArray<std::pair<QShaderProgram::ShaderType, QUrl (QShaderProgramBuilder::*)() const>, 6> shaderTypesToGetters {
{QShaderProgram::Vertex, &QShaderProgramBuilder::vertexShaderGraph},
{QShaderProgram::TessellationControl, &QShaderProgramBuilder::tessellationControlShaderGraph},
{QShaderProgram::TessellationEvaluation, &QShaderProgramBuilder::tessellationEvaluationShaderGraph},
diff --git a/src/render/materialsystem/shaderdata.cpp b/src/render/materialsystem/shaderdata.cpp
index 44a41a240..6f113d592 100644
--- a/src/render/materialsystem/shaderdata.cpp
+++ b/src/render/materialsystem/shaderdata.cpp
@@ -160,7 +160,7 @@ ShaderData *ShaderData::lookupResource(QNodeId id)
}
// RenderCommand updater jobs
-QVariant ShaderData::getTransformedProperty(const QString &name, const Matrix4x4 &viewMatrix)
+QVariant ShaderData::getTransformedProperty(const QString &name, const Matrix4x4 &viewMatrix) const noexcept
{
// Note protecting m_worldMatrix at this point as we assume all world updates
// have been performed when reaching this point
diff --git a/src/render/materialsystem/shaderdata_p.h b/src/render/materialsystem/shaderdata_p.h
index 6fe811566..a1491b328 100644
--- a/src/render/materialsystem/shaderdata_p.h
+++ b/src/render/materialsystem/shaderdata_p.h
@@ -89,7 +89,7 @@ public:
// Called by FramePreparationJob
void updateWorldTransform(const Matrix4x4 &worldMatrix);
- QVariant getTransformedProperty(const QString &name, const Matrix4x4 &viewMatrix);
+ QVariant getTransformedProperty(const QString &name, const Matrix4x4 &viewMatrix) const noexcept;
// Unit tests purposes only
TransformType propertyTransformType(const QString &name) const;
diff --git a/src/render/picking/qabstractraycaster.cpp b/src/render/picking/qabstractraycaster.cpp
index 535c06f27..18658a858 100644
--- a/src/render/picking/qabstractraycaster.cpp
+++ b/src/render/picking/qabstractraycaster.cpp
@@ -345,8 +345,9 @@ void QAbstractRayCaster::removeLayer(QLayer *layer)
{
Q_ASSERT(layer);
Q_D(QAbstractRayCaster);
+ if (!d->m_layers.removeOne(layer))
+ return;
d->update();
- d->m_layers.removeOne(layer);
// Remove bookkeeping connection
d->unregisterDestructionHelper(layer);
}
diff --git a/src/render/render.pro b/src/render/render.pro
index 2b81e8a06..4ec58c992 100644
--- a/src/render/render.pro
+++ b/src/render/render.pro
@@ -17,7 +17,9 @@ include (io/io.pri)
include (picking/picking.pri)
include (raycasting/raycasting.pri)
include (services/services.pri)
+include (shadergraph/shadergraph.pri)
include (texture/texture.pri)
+include (surfaces/surfaces.pri)
gcov {
QMAKE_CXXFLAGS += -fprofile-arcs -ftest-coverage
diff --git a/src/render/renderstates/qblendequationarguments.h b/src/render/renderstates/qblendequationarguments.h
index 878534816..b8b0d5ff1 100644
--- a/src/render/renderstates/qblendequationarguments.h
+++ b/src/render/renderstates/qblendequationarguments.h
@@ -66,8 +66,8 @@ public:
One = 1,
SourceColor = 0x0300,
SourceAlpha = 0x0302,
- Source1Alpha,
- Source1Color,
+ Source1Alpha, // ### Qt 6: Fix -> has same value as OneMinusSourceAlpha
+ Source1Color, // ### Qt 6: Fix -> has same value as DestinationAlpha
DestinationColor = 0x0306,
DestinationAlpha = 0x0304,
SourceAlphaSaturate = 0x0308,
diff --git a/src/render/renderstates/renderstateset.cpp b/src/render/renderstates/renderstateset.cpp
index 6b66d2dd7..5b9c4e8b5 100644
--- a/src/render/renderstates/renderstateset.cpp
+++ b/src/render/renderstates/renderstateset.cpp
@@ -102,7 +102,7 @@ StateMaskSet RenderStateSet::stateMask() const
// This modifies our state to add states from others
// if we don't already contain a state with that type set
-void RenderStateSet::merge(RenderStateSet *other)
+void RenderStateSet::merge(const RenderStateSet *other)
{
m_stateMask |= other->stateMask();
const QVector<StateVariant> otherStates = other->states();
diff --git a/src/render/renderstates/renderstateset_p.h b/src/render/renderstates/renderstateset_p.h
index 119f1edca..667c2614d 100644
--- a/src/render/renderstates/renderstateset_p.h
+++ b/src/render/renderstates/renderstateset_p.h
@@ -89,9 +89,10 @@ public:
int changeCost(RenderStateSet* previousState);
StateMaskSet stateMask() const;
- void merge(RenderStateSet *other);
+ void merge(const RenderStateSet *other);
- QVector<StateVariant> states() const { return m_states; }
+ const QVector<StateVariant>& states() const noexcept { return m_states; }
+ QVector<StateVariant>& states() noexcept { return m_states; }
bool canAddStateOfType(StateMask type) const;
diff --git a/src/render/shadergraph/qshaderformat.cpp b/src/render/shadergraph/qshaderformat.cpp
new file mode 100644
index 000000000..98643fb24
--- /dev/null
+++ b/src/render/shadergraph/qshaderformat.cpp
@@ -0,0 +1,146 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 "qshaderformat_p.h"
+
+QT_BEGIN_NAMESPACE
+namespace Qt3DRender
+{
+QShaderFormat::QShaderFormat() noexcept
+ : m_api(NoApi)
+ , m_shaderType(Fragment)
+{
+}
+
+QShaderFormat::Api QShaderFormat::api() const noexcept
+{
+ return m_api;
+}
+
+void QShaderFormat::setApi(QShaderFormat::Api api) noexcept
+{
+ m_api = api;
+}
+
+QVersionNumber QShaderFormat::version() const noexcept
+{
+ return m_version;
+}
+
+void QShaderFormat::setVersion(const QVersionNumber &version) noexcept
+{
+ m_version = version;
+}
+
+QStringList QShaderFormat::extensions() const noexcept
+{
+ return m_extensions;
+}
+
+void QShaderFormat::setExtensions(const QStringList &extensions) noexcept
+{
+ m_extensions = extensions;
+ m_extensions.sort();
+}
+
+QString QShaderFormat::vendor() const noexcept
+{
+ return m_vendor;
+}
+
+void QShaderFormat::setVendor(const QString &vendor) noexcept
+{
+ m_vendor = vendor;
+}
+
+bool QShaderFormat::isValid() const noexcept
+{
+ return m_api != NoApi && m_version.majorVersion() > 0;
+}
+
+bool QShaderFormat::supports(const QShaderFormat &other) const noexcept
+{
+ if (!isValid() || !other.isValid())
+ return false;
+
+ if (m_api == OpenGLES && m_api != other.m_api)
+ return false;
+
+ if (m_api == OpenGLCoreProfile && m_api != other.m_api)
+ return false;
+
+ if (m_version < other.m_version)
+ return false;
+
+ if (m_shaderType != other.m_shaderType)
+ return false;
+
+ const auto containsAllExtensionsFromOther = std::includes(m_extensions.constBegin(),
+ m_extensions.constEnd(),
+ other.m_extensions.constBegin(),
+ other.m_extensions.constEnd());
+ if (!containsAllExtensionsFromOther)
+ return false;
+
+ if (!other.m_vendor.isEmpty() && m_vendor != other.m_vendor)
+ return false;
+
+ return true;
+}
+
+QShaderFormat::ShaderType QShaderFormat::shaderType() const Q_DECL_NOTHROW
+{
+ return m_shaderType;
+}
+
+void QShaderFormat::setShaderType(QShaderFormat::ShaderType shaderType) Q_DECL_NOTHROW
+{
+ m_shaderType = shaderType;
+}
+
+bool operator==(const QShaderFormat &lhs, const QShaderFormat &rhs) noexcept
+{
+ return lhs.api() == rhs.api()
+ && lhs.version() == rhs.version()
+ && lhs.extensions() == rhs.extensions()
+ && lhs.vendor() == rhs.vendor()
+ && lhs.shaderType() == rhs.shaderType();
+}
+}
+QT_END_NAMESPACE
diff --git a/src/render/shadergraph/qshaderformat_p.h b/src/render/shadergraph/qshaderformat_p.h
new file mode 100644
index 000000000..ad9898bd0
--- /dev/null
+++ b/src/render/shadergraph/qshaderformat_p.h
@@ -0,0 +1,126 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 QT3DRENDER_QSHADERFORMAT_P_H
+#define QT3DRENDER_QSHADERFORMAT_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 <Qt3DRender/private/qt3drender_global_p.h>
+
+#include <QtCore/qstringlist.h>
+#include <QtCore/qversionnumber.h>
+
+QT_BEGIN_NAMESPACE
+namespace Qt3DRender
+{
+class QShaderFormat
+{
+public:
+ enum Api : int {
+ NoApi,
+ OpenGLNoProfile,
+ OpenGLCoreProfile,
+ OpenGLCompatibilityProfile,
+ OpenGLES,
+ VulkanFlavoredGLSL,
+ RHI
+ };
+
+ enum ShaderType : int {
+ Vertex = 0,
+ TessellationControl,
+ TessellationEvaluation,
+ Geometry,
+ Fragment,
+ Compute
+ };
+
+ Q_3DRENDERSHARED_PRIVATE_EXPORT QShaderFormat() noexcept;
+
+ Q_3DRENDERSHARED_PRIVATE_EXPORT Api api() const noexcept;
+ Q_3DRENDERSHARED_PRIVATE_EXPORT void setApi(Api api) noexcept;
+
+ Q_3DRENDERSHARED_PRIVATE_EXPORT QVersionNumber version() const noexcept;
+ Q_3DRENDERSHARED_PRIVATE_EXPORT void setVersion(const QVersionNumber &version) noexcept;
+
+ Q_3DRENDERSHARED_PRIVATE_EXPORT QStringList extensions() const noexcept;
+ Q_3DRENDERSHARED_PRIVATE_EXPORT void setExtensions(const QStringList &extensions) noexcept;
+
+ Q_3DRENDERSHARED_PRIVATE_EXPORT QString vendor() const noexcept;
+ Q_3DRENDERSHARED_PRIVATE_EXPORT void setVendor(const QString &vendor) noexcept;
+
+ Q_3DRENDERSHARED_PRIVATE_EXPORT bool isValid() const noexcept;
+ Q_3DRENDERSHARED_PRIVATE_EXPORT bool supports(const QShaderFormat &other) const noexcept;
+
+ Q_3DRENDERSHARED_PRIVATE_EXPORT ShaderType shaderType() const Q_DECL_NOTHROW;
+ Q_3DRENDERSHARED_PRIVATE_EXPORT void setShaderType(ShaderType shaderType) Q_DECL_NOTHROW;
+
+private:
+ Api m_api;
+ QVersionNumber m_version;
+ QStringList m_extensions;
+ QString m_vendor;
+ ShaderType m_shaderType;
+};
+
+Q_3DRENDERSHARED_PRIVATE_EXPORT bool operator==(const QShaderFormat &lhs, const QShaderFormat &rhs) noexcept;
+
+inline bool operator!=(const QShaderFormat &lhs, const QShaderFormat &rhs) noexcept
+{
+ return !(lhs == rhs);
+}
+
+
+}
+Q_DECLARE_TYPEINFO(Qt3DRender::QShaderFormat, Q_MOVABLE_TYPE);
+QT_END_NAMESPACE
+
+Q_DECLARE_METATYPE(Qt3DRender::QShaderFormat)
+
+#endif // QT3DRENDER_QSHADERFORMAT_P_H
diff --git a/src/render/shadergraph/qshadergenerator.cpp b/src/render/shadergraph/qshadergenerator.cpp
new file mode 100644
index 000000000..92c69be72
--- /dev/null
+++ b/src/render/shadergraph/qshadergenerator.cpp
@@ -0,0 +1,861 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 "qshadergenerator_p.h"
+
+#include "qshaderlanguage_p.h"
+#include <QRegularExpression>
+
+#include <cctype>
+#include <qshaderprogram_p.h>
+
+QT_BEGIN_NAMESPACE
+namespace Qt3DRender {
+Q_LOGGING_CATEGORY(ShaderGenerator, "ShaderGenerator", QtWarningMsg)
+
+namespace
+{
+ QByteArray toGlsl(QShaderLanguage::StorageQualifier qualifier, const QShaderFormat &format) noexcept
+ {
+ if (format.version().majorVersion() <= 2 && format.api() != QShaderFormat::RHI) {
+ // Note we're assuming fragment shader only here, it'd be different
+ // values for vertex shader, will need to be fixed properly at some
+ // point but isn't necessary yet (this problem already exists in past
+ // commits anyway)
+ switch (qualifier) {
+ case QShaderLanguage::Const:
+ return "const";
+ case QShaderLanguage::Input:
+ if (format.shaderType() == QShaderFormat::Vertex)
+ return "attribute";
+ else
+ return "varying";
+ case QShaderLanguage::Output:
+ return ""; // Although fragment shaders for <=2 only have fixed outputs
+ case QShaderLanguage::Uniform:
+ return "uniform";
+ case QShaderLanguage::BuiltIn:
+ return "//";
+ }
+ } else {
+ switch (qualifier) {
+ case QShaderLanguage::Const:
+ return "const";
+ case QShaderLanguage::Input:
+ return "in";
+ case QShaderLanguage::Output:
+ return "out";
+ case QShaderLanguage::Uniform:
+ return "uniform";
+ case QShaderLanguage::BuiltIn:
+ return "//";
+ }
+ }
+
+ Q_UNREACHABLE();
+ }
+
+ QByteArray toGlsl(QShaderLanguage::VariableType type) noexcept
+ {
+ switch (type) {
+ case QShaderLanguage::Bool:
+ return "bool";
+ case QShaderLanguage::Int:
+ return "int";
+ case QShaderLanguage::Uint:
+ return "uint";
+ case QShaderLanguage::Float:
+ return "float";
+ case QShaderLanguage::Double:
+ return "double";
+ case QShaderLanguage::Vec2:
+ return "vec2";
+ case QShaderLanguage::Vec3:
+ return "vec3";
+ case QShaderLanguage::Vec4:
+ return "vec4";
+ case QShaderLanguage::DVec2:
+ return "dvec2";
+ case QShaderLanguage::DVec3:
+ return "dvec3";
+ case QShaderLanguage::DVec4:
+ return "dvec4";
+ case QShaderLanguage::BVec2:
+ return "bvec2";
+ case QShaderLanguage::BVec3:
+ return "bvec3";
+ case QShaderLanguage::BVec4:
+ return "bvec4";
+ case QShaderLanguage::IVec2:
+ return "ivec2";
+ case QShaderLanguage::IVec3:
+ return "ivec3";
+ case QShaderLanguage::IVec4:
+ return "ivec4";
+ case QShaderLanguage::UVec2:
+ return "uvec2";
+ case QShaderLanguage::UVec3:
+ return "uvec3";
+ case QShaderLanguage::UVec4:
+ return "uvec4";
+ case QShaderLanguage::Mat2:
+ return "mat2";
+ case QShaderLanguage::Mat3:
+ return "mat3";
+ case QShaderLanguage::Mat4:
+ return "mat4";
+ case QShaderLanguage::Mat2x2:
+ return "mat2x2";
+ case QShaderLanguage::Mat2x3:
+ return "mat2x3";
+ case QShaderLanguage::Mat2x4:
+ return "mat2x4";
+ case QShaderLanguage::Mat3x2:
+ return "mat3x2";
+ case QShaderLanguage::Mat3x3:
+ return "mat3x3";
+ case QShaderLanguage::Mat3x4:
+ return "mat3x4";
+ case QShaderLanguage::Mat4x2:
+ return "mat4x2";
+ case QShaderLanguage::Mat4x3:
+ return "mat4x3";
+ case QShaderLanguage::Mat4x4:
+ return "mat4x4";
+ case QShaderLanguage::DMat2:
+ return "dmat2";
+ case QShaderLanguage::DMat3:
+ return "dmat3";
+ case QShaderLanguage::DMat4:
+ return "dmat4";
+ case QShaderLanguage::DMat2x2:
+ return "dmat2x2";
+ case QShaderLanguage::DMat2x3:
+ return "dmat2x3";
+ case QShaderLanguage::DMat2x4:
+ return "dmat2x4";
+ case QShaderLanguage::DMat3x2:
+ return "dmat3x2";
+ case QShaderLanguage::DMat3x3:
+ return "dmat3x3";
+ case QShaderLanguage::DMat3x4:
+ return "dmat3x4";
+ case QShaderLanguage::DMat4x2:
+ return "dmat4x2";
+ case QShaderLanguage::DMat4x3:
+ return "dmat4x3";
+ case QShaderLanguage::DMat4x4:
+ return "dmat4x4";
+ case QShaderLanguage::Sampler1D:
+ return "sampler1D";
+ case QShaderLanguage::Sampler2D:
+ return "sampler2D";
+ case QShaderLanguage::Sampler3D:
+ return "sampler3D";
+ case QShaderLanguage::SamplerCube:
+ return "samplerCube";
+ case QShaderLanguage::Sampler2DRect:
+ return "sampler2DRect";
+ case QShaderLanguage::Sampler2DMs:
+ return "sampler2DMS";
+ case QShaderLanguage::SamplerBuffer:
+ return "samplerBuffer";
+ case QShaderLanguage::Sampler1DArray:
+ return "sampler1DArray";
+ case QShaderLanguage::Sampler2DArray:
+ return "sampler2DArray";
+ case QShaderLanguage::Sampler2DMsArray:
+ return "sampler2DMSArray";
+ case QShaderLanguage::SamplerCubeArray:
+ return "samplerCubeArray";
+ case QShaderLanguage::Sampler1DShadow:
+ return "sampler1DShadow";
+ case QShaderLanguage::Sampler2DShadow:
+ return "sampler2DShadow";
+ case QShaderLanguage::Sampler2DRectShadow:
+ return "sampler2DRectShadow";
+ case QShaderLanguage::Sampler1DArrayShadow:
+ return "sampler1DArrayShadow";
+ case QShaderLanguage::Sampler2DArrayShadow:
+ return "sample2DArrayShadow";
+ case QShaderLanguage::SamplerCubeShadow:
+ return "samplerCubeShadow";
+ case QShaderLanguage::SamplerCubeArrayShadow:
+ return "samplerCubeArrayShadow";
+ case QShaderLanguage::ISampler1D:
+ return "isampler1D";
+ case QShaderLanguage::ISampler2D:
+ return "isampler2D";
+ case QShaderLanguage::ISampler3D:
+ return "isampler3D";
+ case QShaderLanguage::ISamplerCube:
+ return "isamplerCube";
+ case QShaderLanguage::ISampler2DRect:
+ return "isampler2DRect";
+ case QShaderLanguage::ISampler2DMs:
+ return "isampler2DMS";
+ case QShaderLanguage::ISamplerBuffer:
+ return "isamplerBuffer";
+ case QShaderLanguage::ISampler1DArray:
+ return "isampler1DArray";
+ case QShaderLanguage::ISampler2DArray:
+ return "isampler2DArray";
+ case QShaderLanguage::ISampler2DMsArray:
+ return "isampler2DMSArray";
+ case QShaderLanguage::ISamplerCubeArray:
+ return "isamplerCubeArray";
+ case QShaderLanguage::USampler1D:
+ return "usampler1D";
+ case QShaderLanguage::USampler2D:
+ return "usampler2D";
+ case QShaderLanguage::USampler3D:
+ return "usampler3D";
+ case QShaderLanguage::USamplerCube:
+ return "usamplerCube";
+ case QShaderLanguage::USampler2DRect:
+ return "usampler2DRect";
+ case QShaderLanguage::USampler2DMs:
+ return "usampler2DMS";
+ case QShaderLanguage::USamplerBuffer:
+ return "usamplerBuffer";
+ case QShaderLanguage::USampler1DArray:
+ return "usampler1DArray";
+ case QShaderLanguage::USampler2DArray:
+ return "usampler2DArray";
+ case QShaderLanguage::USampler2DMsArray:
+ return "usampler2DMSArray";
+ case QShaderLanguage::USamplerCubeArray:
+ return "usamplerCubeArray";
+ }
+
+ Q_UNREACHABLE();
+ }
+
+ QByteArray replaceParameters(const QByteArray &original, const QShaderNode &node,
+ const QShaderFormat &format) noexcept
+ {
+ QByteArray result = original;
+
+ const QStringList parameterNames = node.parameterNames();
+ for (const QString &parameterName : parameterNames) {
+ const QByteArray placeholder = QByteArray(QByteArrayLiteral("$") + parameterName.toUtf8());
+ const QVariant parameter = node.parameter(parameterName);
+ if (parameter.userType() == qMetaTypeId<QShaderLanguage::StorageQualifier>()) {
+ const QShaderLanguage::StorageQualifier qualifier =
+ qvariant_cast<QShaderLanguage::StorageQualifier>(parameter);
+ const QByteArray value = toGlsl(qualifier, format);
+ result.replace(placeholder, value);
+ } else if (parameter.userType() == qMetaTypeId<QShaderLanguage::VariableType>()) {
+ const QShaderLanguage::VariableType type =
+ qvariant_cast<QShaderLanguage::VariableType>(parameter);
+ const QByteArray value = toGlsl(type);
+ result.replace(placeholder, value);
+ } else {
+ const QByteArray value = parameter.toString().toUtf8();
+ result.replace(placeholder, value);
+ }
+ }
+
+ return result;
+ }
+
+ bool intersectsEnabledLayers(const QStringList &enabledLayers, const QStringList &layers) noexcept
+ {
+ return layers.isEmpty()
+ || std::any_of(layers.cbegin(), layers.cend(),
+ [enabledLayers](const QString &s) { return enabledLayers.contains(s); });
+ }
+
+ struct ShaderGenerationState
+ {
+ ShaderGenerationState(const QShaderGenerator & gen, QStringList layers, QVector<QShaderNode> nodes)
+ : generator{gen}
+ , enabledLayers{layers}
+ , nodes{nodes}
+ {
+
+ }
+
+ const QShaderGenerator &generator;
+ QStringList enabledLayers;
+ QVector<QShaderNode> nodes;
+ QByteArrayList code;
+
+ QVector<QString> globalInputVariables;
+ const QRegularExpression globalInputExtractRegExp { QStringLiteral("^.*\\s+(\\w+).*;$") };
+ };
+
+ class GLSL45HeaderWriter
+ {
+ public:
+ void writeHeader(ShaderGenerationState &state)
+ {
+ const auto &format = state.generator.format;
+ auto &code = state.code;
+ for (const QShaderNode &node : state.nodes) {
+ if (intersectsEnabledLayers(state.enabledLayers, node.layers())) {
+ const QByteArrayList& headerSnippets = node.rule(format).headerSnippets;
+ for (const QByteArray &snippet : headerSnippets) {
+ auto replacedSnippet = replaceParameters(snippet, node, format).trimmed();
+
+ if (replacedSnippet.startsWith(QByteArrayLiteral("add-input"))) {
+ onInOut(code, replacedSnippet);
+ } else if (replacedSnippet.startsWith(QByteArrayLiteral("add-uniform"))) {
+ onNamedUniform(ubo, replacedSnippet);
+ } else if (replacedSnippet.startsWith(QByteArrayLiteral("add-sampler"))) {
+ onNamedSampler(code, replacedSnippet);
+ } else if (replacedSnippet.startsWith(QByteArrayLiteral("#pragma include "))) {
+ onInclude(code, replacedSnippet);
+ } else {
+ code << replacedSnippet;
+ }
+ // If node is an input, record the variable name into the globalInputVariables
+ // vector
+ if (node.type() == QShaderNode::Input) {
+ const QRegularExpressionMatch match = state.globalInputExtractRegExp.match(
+ QString::fromUtf8(code.last()));
+ if (match.hasMatch())
+ state.globalInputVariables.push_back(match.captured(1));
+ }
+ }
+ }
+ }
+
+ if (!ubo.isEmpty()) {
+ code << QByteArrayLiteral("layout(std140, binding = ")
+ + QByteArray::number(currentBinding++)
+ + QByteArrayLiteral(") uniform qt3d_shadergraph_generated_uniforms {");
+ code << ubo;
+ code << "};";
+ }
+ }
+
+ private:
+ void onInOut(QByteArrayList &code, const QByteArray &snippet) noexcept
+ {
+ const auto split = snippet.split(' ');
+ if (split.size() < 4) {
+ qDebug() << "Invalid header snippet: " << snippet;
+ return;
+ }
+ const auto &qualifier = split[1];
+ const auto &type = split[2];
+ const auto &name = split[3];
+
+ if (qualifier == QByteArrayLiteral("in")) {
+ code << (QByteArrayLiteral("layout(location = ")
+ + QByteArray::number(currentInputLocation++) + QByteArrayLiteral(") in ")
+ + type + ' ' + name + QByteArrayLiteral(";"));
+ } else if (qualifier == QByteArrayLiteral("out")) {
+ code << (QByteArrayLiteral("layout(location = ")
+ + QByteArray::number(currentOutputLocation++) + QByteArrayLiteral(") out ")
+ + type + ' ' + name + QByteArrayLiteral(";"));
+ } else if (qualifier == QByteArrayLiteral("uniform")) {
+ ubo << (type + ' ' + name + ';');
+ }
+ }
+
+ void onNamedUniform(QByteArrayList &ubo, const QByteArray &snippet) noexcept
+ {
+ const auto split = snippet.split(' ');
+ if (split.size() < 3) {
+ qDebug() << "Invalid header snippet: " << snippet;
+ return;
+ }
+
+ const auto &type = split[1];
+ const auto &name = split[2];
+
+ ubo << (type + ' ' + name + ';');
+ }
+
+ void onNamedSampler(QByteArrayList &code, const QByteArray &snippet) noexcept
+ {
+ const auto split = snippet.split(' ');
+ if (split.size() < 3) {
+ qDebug() << "Invalid header snippet: " << snippet;
+ return;
+ }
+ const auto binding = QByteArray::number(currentBinding++);
+ const auto &type = split[1];
+ const auto &name = split[2];
+
+ code << (QByteArrayLiteral("layout(binding = ") + binding + QByteArrayLiteral(") uniform ")
+ + type + ' ' + name + QByteArrayLiteral(";"));
+ }
+
+ void onInclude(QByteArrayList &code, const QByteArray &snippet) noexcept
+ {
+ const auto filepath = QString::fromUtf8(snippet.mid(strlen("#pragma include ")));
+ QString deincluded = QString::fromUtf8(QShaderProgramPrivate::deincludify(filepath));
+
+ // This lambda will replace all occurrences of a string (e.g. "binding = auto") by another,
+ // with the incremented int passed as argument (e.g. "binding = 1", "binding = 2" ...)
+ const auto replaceAndIncrement = [&deincluded](const QRegularExpression &regexp,
+ int &variable,
+ const QString &replacement) noexcept {
+ int matchStart = 0;
+ do {
+ matchStart = deincluded.indexOf(regexp, matchStart);
+ if (matchStart != -1) {
+ const auto match = regexp.match(deincluded.midRef(matchStart));
+ const auto length = match.capturedLength(0);
+
+ deincluded.replace(matchStart, length, replacement.arg(variable++));
+ }
+ } while (matchStart != -1);
+ };
+
+ // 1. Handle uniforms
+ {
+ thread_local const QRegularExpression bindings(
+ QStringLiteral("binding\\s?+=\\s?+auto"));
+
+ replaceAndIncrement(bindings, currentBinding, QStringLiteral("binding = %1"));
+ }
+
+ // 2. Handle inputs
+ {
+ thread_local const QRegularExpression inLocations(
+ QStringLiteral("location\\s?+=\\s?+auto\\s?+\\)\\s?+in\\s+"));
+
+ replaceAndIncrement(inLocations, currentInputLocation,
+ QStringLiteral("location = %1) in "));
+ }
+
+ // 3. Handle outputs
+ {
+ thread_local const QRegularExpression outLocations(
+ QStringLiteral("location\\s?+=\\s?+auto\\s?+\\)\\s?+out\\s+"));
+
+ replaceAndIncrement(outLocations, currentOutputLocation,
+ QStringLiteral("location = %1) out "));
+ }
+
+ code << deincluded.toUtf8();
+ }
+
+ int currentInputLocation { 0 };
+ int currentOutputLocation { 0 };
+ int currentBinding { 2 };
+ QByteArrayList ubo;
+ };
+
+ struct GLSLHeaderWriter
+ {
+ void writeHeader(ShaderGenerationState &state)
+ {
+ const auto &format = state.generator.format;
+ auto &code = state.code;
+ for (const QShaderNode &node : state.nodes) {
+ if (intersectsEnabledLayers(state.enabledLayers, node.layers())) {
+ const QByteArrayList& headerSnippets = node.rule(format).headerSnippets;
+ for (const QByteArray &snippet : headerSnippets) {
+ code << replaceParameters(snippet, node, format);
+
+ // If node is an input, record the variable name into the globalInputVariables
+ // vector
+ if (node.type() == QShaderNode::Input) {
+ const QRegularExpressionMatch match = state.globalInputExtractRegExp.match(
+ QString::fromUtf8(code.last()));
+ if (match.hasMatch())
+ state.globalInputVariables.push_back(match.captured(1));
+ }
+ }
+ }
+ }
+ }
+ };
+
+ QByteArray versionString(const QShaderFormat &format) noexcept
+ {
+ if (!format.isValid())
+ return {};
+
+ switch (format.api()) {
+ case QShaderFormat::RHI: {
+ return QByteArrayLiteral("#version 450");
+ }
+ case QShaderFormat::VulkanFlavoredGLSL: {
+ const int major = format.version().majorVersion();
+ const int minor = format.version().minorVersion();
+ return (QByteArrayLiteral("#version ") + QByteArray::number(major * 100 + minor * 10));
+ }
+ default: {
+ const bool isGLES = format.api() == QShaderFormat::OpenGLES;
+ const int major = format.version().majorVersion();
+ const int minor = format.version().minorVersion();
+
+ const int version = major == 2 && isGLES ? 100
+ : major == 3 && isGLES ? 300
+ : major == 2 ? 100 + 10 * (minor + 1)
+ : major == 3 && minor <= 2 ? 100 + 10 * (minor + 3)
+ : major * 100 + minor * 10;
+
+ const QByteArray profile =
+ isGLES && version > 100 ? QByteArrayLiteral(" es")
+ : version >= 150 && format.api() == QShaderFormat::OpenGLCoreProfile ? QByteArrayLiteral(" core")
+ : version >= 150 && format.api() == QShaderFormat::OpenGLCompatibilityProfile ? QByteArrayLiteral(" compatibility")
+ : QByteArray();
+
+ return (QByteArrayLiteral("#version ") + QByteArray::number(version) + profile);
+ }
+ }
+ }
+}
+
+QByteArray QShaderGenerator::createShaderCode(const QStringList &enabledLayers) const
+{
+ const QVector<QShaderNode> nodes = graph.nodes();
+ ShaderGenerationState state(*this, enabledLayers, nodes);
+ QByteArrayList &code = state.code;
+
+ code << versionString(format);
+ code << QByteArray();
+
+ if (format.api() == QShaderFormat::VulkanFlavoredGLSL || format.api() == QShaderFormat::RHI) {
+ GLSL45HeaderWriter builder;
+ builder.writeHeader(state);
+ } else {
+ GLSLHeaderWriter builder;
+ builder.writeHeader(state);
+ }
+
+ code << QByteArray();
+ code << QByteArrayLiteral("void main()");
+ code << QByteArrayLiteral("{");
+
+ const QRegularExpression temporaryVariableToAssignmentRegExp(
+ QStringLiteral("([^;]*\\s+(v\\d+))\\s*=\\s*([^;]*);"));
+ const QRegularExpression temporaryVariableInAssignmentRegExp(QStringLiteral("\\W*(v\\d+)\\W*"));
+ const QRegularExpression statementRegExp(QStringLiteral("\\s*(\\w+)\\s*=\\s*([^;]*);"));
+
+ struct Variable;
+
+ struct Assignment
+ {
+ QString expression;
+ QVector<Variable *> referencedVariables;
+ };
+
+ struct Variable
+ {
+ enum Type { GlobalInput, TemporaryAssignment, Output };
+
+ QString name;
+ QString declaration;
+ int referenceCount = 0;
+ Assignment assignment;
+ Type type = TemporaryAssignment;
+ bool substituted = false;
+
+ static void substitute(Variable *v)
+ {
+ if (v->substituted)
+ return;
+
+ qCDebug(ShaderGenerator)
+ << "Begin Substituting " << v->name << " = " << v->assignment.expression;
+ for (Variable *ref : qAsConst(v->assignment.referencedVariables)) {
+ // Recursively substitute
+ Variable::substitute(ref);
+
+ // Replace all variables referenced only once in the assignment
+ // by their actual expression
+ if (ref->referenceCount == 1 || ref->type == Variable::GlobalInput) {
+ const QRegularExpression r(QStringLiteral("(.*\\b)(%1)(\\b.*)").arg(ref->name));
+ if (v->assignment.referencedVariables.size() == 1)
+ v->assignment.expression.replace(
+ r, QStringLiteral("\\1%2\\3").arg(ref->assignment.expression));
+ else
+ v->assignment.expression.replace(
+ r, QStringLiteral("(\\1%2\\3)").arg(ref->assignment.expression));
+ }
+ }
+ qCDebug(ShaderGenerator)
+ << "Done Substituting " << v->name << " = " << v->assignment.expression;
+ v->substituted = true;
+ }
+ };
+
+ struct LineContent
+ {
+ QByteArray rawContent;
+ Variable *var = nullptr;
+ };
+
+ // Table to store temporary variables that should be replaced:
+ // - If variable references a a global variables
+ // -> we will use the global variable directly
+ // - If variable references a function results
+ // -> will be kept only if variable is referenced more than once.
+ // This avoids having vec3 v56 = vertexPosition; when we could
+ // just use vertexPosition directly.
+ // The added benefit is when having arrays, we don't try to create
+ // mat4 v38 = skinningPalelette[100] which would be invalid
+ QVector<Variable> temporaryVariables;
+ // Reserve more than enough space to ensure no reallocation will take place
+ temporaryVariables.reserve(nodes.size() * 8);
+
+ QVector<LineContent> lines;
+
+ auto createVariable = [&] () -> Variable * {
+ Q_ASSERT(temporaryVariables.capacity() > 0);
+ temporaryVariables.resize(temporaryVariables.size() + 1);
+ return &temporaryVariables.last();
+ };
+
+ auto findVariable = [&] (const QString &name) -> Variable * {
+ const auto end = temporaryVariables.end();
+ auto it = std::find_if(temporaryVariables.begin(), end,
+ [=] (const Variable &a) { return a.name == name; });
+ if (it != end)
+ return &(*it);
+ return nullptr;
+ };
+
+ auto gatherTemporaryVariablesFromAssignment = [&](Variable *v,
+ const QString &assignmentContent) {
+ QRegularExpressionMatchIterator subMatchIt =
+ temporaryVariableInAssignmentRegExp.globalMatch(assignmentContent);
+ while (subMatchIt.hasNext()) {
+ const QRegularExpressionMatch subMatch = subMatchIt.next();
+ const QString variableName = subMatch.captured(1);
+
+ // Variable we care about should already exists -> an expression cannot reference a
+ // variable that hasn't been defined
+ Variable *u = findVariable(variableName);
+ Q_ASSERT(u);
+
+ // Increase reference count for u
+ ++u->referenceCount;
+ // Insert u as reference for variable v
+ v->assignment.referencedVariables.push_back(u);
+ }
+ };
+
+ for (const QShaderGraph::Statement &statement : graph.createStatements(enabledLayers)) {
+ const QShaderNode node = statement.node;
+ QByteArray line = node.rule(format).substitution;
+ const QVector<QShaderNodePort> ports = node.ports();
+
+ struct VariableReplacement
+ {
+ QByteArray placeholder;
+ QByteArray variable;
+ };
+
+ QVector<VariableReplacement> variableReplacements;
+
+ // Generate temporary variable names vN
+ for (const QShaderNodePort &port : ports) {
+ const QString portName = port.name;
+ const QShaderNodePort::Direction portDirection = port.direction;
+ const bool isInput = port.direction == QShaderNodePort::Input;
+
+ const int portIndex = statement.portIndex(portDirection, portName);
+
+ Q_ASSERT(portIndex >= 0);
+
+ const int variableIndex =
+ isInput ? statement.inputs.at(portIndex) : statement.outputs.at(portIndex);
+ if (variableIndex < 0)
+ continue;
+
+ VariableReplacement replacement;
+ replacement.placeholder = QByteArrayLiteral("$") + portName.toUtf8();
+ replacement.variable = QByteArrayLiteral("v") + QByteArray::number(variableIndex);
+
+ variableReplacements.append(std::move(replacement));
+ }
+
+ int begin = 0;
+ while ((begin = line.indexOf('$', begin)) != -1) {
+ int end = begin + 1;
+ char endChar = line.at(end);
+ const int size = line.size();
+ while (end < size && (std::isalnum(endChar) || endChar == '_')) {
+ ++end;
+ endChar = line.at(end);
+ }
+
+ const int placeholderLength = end - begin;
+
+ const QByteArray variableName = line.mid(begin, placeholderLength);
+ const auto replacementIt =
+ std::find_if(variableReplacements.cbegin(), variableReplacements.cend(),
+ [&variableName](const VariableReplacement &replacement) {
+ return variableName == replacement.placeholder;
+ });
+
+ if (replacementIt != variableReplacements.cend()) {
+ line.replace(begin, placeholderLength, replacementIt->variable);
+ begin += replacementIt->variable.length();
+ } else {
+ begin = end;
+ }
+ }
+
+ // Substitute variable names by generated vN variable names
+ const QByteArray substitutionedLine = replaceParameters(line, node, format);
+
+ QRegularExpressionMatchIterator matches;
+
+ switch (node.type()) {
+ case QShaderNode::Input:
+ case QShaderNode::Output:
+ matches = statementRegExp.globalMatch(QString::fromUtf8(substitutionedLine));
+ break;
+ case QShaderNode::Function:
+ matches = temporaryVariableToAssignmentRegExp.globalMatch(
+ QString::fromUtf8(substitutionedLine));
+ break;
+ case QShaderNode::Invalid:
+ break;
+ }
+
+ while (matches.hasNext()) {
+ QRegularExpressionMatch match = matches.next();
+
+ Variable *v = nullptr;
+
+ switch (node.type()) {
+ // Record name of temporary variable that possibly references a global input
+ // We will replace the temporary variables by the matching global variables later
+ case QShaderNode::Input: {
+ const QString localVariable = match.captured(1);
+ const QString globalVariable = match.captured(2);
+
+ v = createVariable();
+ v->name = localVariable;
+ v->type = Variable::GlobalInput;
+
+ Assignment assignment;
+ assignment.expression = globalVariable;
+ v->assignment = assignment;
+ break;
+ }
+
+ case QShaderNode::Function: {
+ const QString localVariableDeclaration = match.captured(1);
+ const QString localVariableName = match.captured(2);
+ const QString assignmentContent = match.captured(3);
+
+ // Add new variable -> it cannot exist already
+ v = createVariable();
+ v->name = localVariableName;
+ v->declaration = localVariableDeclaration;
+ v->assignment.expression = assignmentContent;
+
+ // Find variables that may be referenced in the assignment
+ gatherTemporaryVariablesFromAssignment(v, assignmentContent);
+ break;
+ }
+
+ case QShaderNode::Output: {
+ const QString outputDeclaration = match.captured(1);
+ const QString assignmentContent = match.captured(2);
+
+ v = createVariable();
+ v->name = outputDeclaration;
+ v->declaration = outputDeclaration;
+ v->type = Variable::Output;
+
+ Assignment assignment;
+ assignment.expression = assignmentContent;
+ v->assignment = assignment;
+
+ // Find variables that may be referenced in the assignment
+ gatherTemporaryVariablesFromAssignment(v, assignmentContent);
+ break;
+ }
+ case QShaderNode::Invalid:
+ break;
+ }
+
+ LineContent lineContent;
+ lineContent.rawContent = QByteArray(QByteArrayLiteral(" ") + substitutionedLine);
+ lineContent.var = v;
+ lines << lineContent;
+ }
+ }
+
+ // Go through all lines
+ // Perform substitution of line with temporary variables substitution
+ for (LineContent &lineContent : lines) {
+ Variable *v = lineContent.var;
+ qCDebug(ShaderGenerator) << lineContent.rawContent;
+ if (v != nullptr) {
+ Variable::substitute(v);
+
+ qCDebug(ShaderGenerator)
+ << "Line " << lineContent.rawContent << "is assigned to temporary" << v->name;
+
+ // Check number of occurrences a temporary variable is referenced
+ if (v->referenceCount == 1 || v->type == Variable::GlobalInput) {
+ // If it is referenced only once, no point in creating a temporary
+ // Clear content for current line
+ lineContent.rawContent.clear();
+ // We assume expression that were referencing vN will have vN properly substituted
+ } else {
+ lineContent.rawContent = QStringLiteral(" %1 = %2;")
+ .arg(v->declaration)
+ .arg(v->assignment.expression)
+ .toUtf8();
+ }
+
+ qCDebug(ShaderGenerator) << "Updated Line is " << lineContent.rawContent;
+ }
+ }
+
+ // Go throug all lines and insert content
+ for (const LineContent &lineContent : qAsConst(lines)) {
+ if (!lineContent.rawContent.isEmpty()) {
+ code << lineContent.rawContent;
+ }
+ }
+
+ code << QByteArrayLiteral("}");
+ code << QByteArray();
+
+ return code.join('\n');
+}
+
+}
+QT_END_NAMESPACE
diff --git a/src/quick3d/imports/scene3d/scene3dcleaner_p.h b/src/render/shadergraph/qshadergenerator_p.h
index a246cbde7..aebeaa8f2 100644
--- a/src/quick3d/imports/scene3d/scene3dcleaner_p.h
+++ b/src/render/shadergraph/qshadergenerator_p.h
@@ -1,9 +1,9 @@
/****************************************************************************
**
-** Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB).
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
** Contact: https://www.qt.io/licensing/
**
-** This file is part of the Qt3D module of the Qt Toolkit.
+** This file is part of the QtGui module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
@@ -37,8 +37,8 @@
**
****************************************************************************/
-#ifndef QT3DRENDER_SCENE3DCLEANER_P_H
-#define QT3DRENDER_SCENE3DCLEANER_P_H
+#ifndef QT3DRENDER_QSHADERGENERATOR_P_H
+#define QT3DRENDER_QSHADERGENERATOR_P_H
//
// W A R N I N G
@@ -51,32 +51,31 @@
// We mean it.
//
-#include <QtCore/QObject>
+#include <Qt3DRender/private/qt3drender_global_p.h>
-QT_BEGIN_NAMESPACE
+#include <Qt3DRender/private/qshadergraph_p.h>
+#include <QtCore/QLoggingCategory>
-namespace Qt3DRender {
-class Scene3DRenderer;
+QT_BEGIN_NAMESPACE
-class Scene3DCleaner : public QObject
+namespace Qt3DRender
{
- Q_OBJECT
-public:
- explicit Scene3DCleaner(QObject *parent = 0);
- ~Scene3DCleaner();
-
- void setRenderer(Scene3DRenderer *renderer) { m_renderer = renderer; }
+Q_DECLARE_LOGGING_CATEGORY(ShaderGenerator)
-public Q_SLOTS:
- void cleanup();
+class QShaderGenerator
+{
+public:
+ Q_3DRENDERSHARED_PRIVATE_EXPORT QByteArray createShaderCode(const QStringList &enabledLayers = QStringList()) const;
-private:
- Scene3DRenderer *m_renderer;
+ QShaderGraph graph;
+ QShaderFormat format;
};
-} // namespace Qt3DRender
-
+}
+Q_DECLARE_TYPEINFO(Qt3DRender::QShaderGenerator, Q_MOVABLE_TYPE);
QT_END_NAMESPACE
-#endif // QT3DRENDER_SCENE3DCLEANER_H
+Q_DECLARE_METATYPE(Qt3DRender::QShaderGenerator)
+
+#endif // QT3DRENDER_QSHADERGENERATOR_P_H
diff --git a/src/render/shadergraph/qshadergraph.cpp b/src/render/shadergraph/qshadergraph.cpp
new file mode 100644
index 000000000..c2f3c343e
--- /dev/null
+++ b/src/render/shadergraph/qshadergraph.cpp
@@ -0,0 +1,317 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 "qshadergraph_p.h"
+
+QT_BEGIN_NAMESPACE
+namespace Qt3DRender
+{
+
+namespace
+{
+ QVector<QShaderNode> copyOutputNodes(const QVector<QShaderNode> &nodes, const QVector<QShaderGraph::Edge> &edges)
+ {
+ auto res = QVector<QShaderNode>();
+ std::copy_if(nodes.cbegin(), nodes.cend(),
+ std::back_inserter(res),
+ [&edges] (const QShaderNode &node) {
+ return node.type() == QShaderNode::Output ||
+ (node.type() == QShaderNode::Function &&
+ !std::any_of(edges.cbegin(),
+ edges.cend(),
+ [&node] (const QShaderGraph::Edge &edge) {
+ return edge.sourceNodeUuid ==
+ node.uuid();
+ }));
+ });
+ return res;
+ }
+
+ QVector<QShaderGraph::Edge> incomingEdges(const QVector<QShaderGraph::Edge> &edges, const QUuid &uuid)
+ {
+ auto res = QVector<QShaderGraph::Edge>();
+ std::copy_if(edges.cbegin(), edges.cend(),
+ std::back_inserter(res),
+ [uuid] (const QShaderGraph::Edge &edge) {
+ return edge.sourceNodeUuid == uuid;
+ });
+ return res;
+ }
+
+ QVector<QShaderGraph::Edge> outgoingEdges(const QVector<QShaderGraph::Edge> &edges, const QUuid &uuid)
+ {
+ auto res = QVector<QShaderGraph::Edge>();
+ std::copy_if(edges.cbegin(), edges.cend(),
+ std::back_inserter(res),
+ [uuid] (const QShaderGraph::Edge &edge) {
+ return edge.targetNodeUuid == uuid;
+ });
+ return res;
+ }
+
+ QShaderGraph::Statement nodeToStatement(const QShaderNode &node, int &nextVarId)
+ {
+ auto statement = QShaderGraph::Statement();
+ statement.node = node;
+
+ const QVector<QShaderNodePort> ports = node.ports();
+ for (const QShaderNodePort &port : ports) {
+ if (port.direction == QShaderNodePort::Input) {
+ statement.inputs.append(-1);
+ } else {
+ statement.outputs.append(nextVarId);
+ nextVarId++;
+ }
+ }
+ return statement;
+ }
+
+ QShaderGraph::Statement completeStatement(const QHash<QUuid, QShaderGraph::Statement> &idHash,
+ const QVector<QShaderGraph::Edge> edges,
+ const QUuid &uuid)
+ {
+ auto targetStatement = idHash.value(uuid);
+ for (const QShaderGraph::Edge &edge : edges) {
+ if (edge.targetNodeUuid != uuid)
+ continue;
+
+ const QShaderGraph::Statement sourceStatement = idHash.value(edge.sourceNodeUuid);
+ const int sourcePortIndex = sourceStatement.portIndex(QShaderNodePort::Output, edge.sourcePortName);
+ const int targetPortIndex = targetStatement.portIndex(QShaderNodePort::Input, edge.targetPortName);
+
+ if (sourcePortIndex < 0 || targetPortIndex < 0)
+ continue;
+
+ const QVector<int> sourceOutputs = sourceStatement.outputs;
+ QVector<int> &targetInputs = targetStatement.inputs;
+ targetInputs[targetPortIndex] = sourceOutputs[sourcePortIndex];
+ }
+ return targetStatement;
+ }
+
+ void removeNodesWithUnboundInputs(QVector<QShaderGraph::Statement> &statements,
+ const QVector<QShaderGraph::Edge> &allEdges)
+ {
+ // A node is invalid if any of its input ports is disconected
+ // or connected to the output port of another invalid node.
+
+ // Keeps track of the edges from the nodes we know to be valid
+ // to unvisited nodes
+ auto currentEdges = QVector<QShaderGraph::Edge>();
+
+ statements.erase(std::remove_if(statements.begin(),
+ statements.end(),
+ [&currentEdges, &allEdges] (const QShaderGraph::Statement &statement) {
+ const QShaderNode &node = statement.node;
+ const QVector<QShaderGraph::Edge> outgoing = outgoingEdges(currentEdges, node.uuid());
+ const QVector<QShaderNodePort> ports = node.ports();
+
+ bool allInputsConnected = true;
+ for (const QShaderNodePort &port : node.ports()) {
+ if (port.direction == QShaderNodePort::Output)
+ continue;
+
+ const auto edgeIt = std::find_if(outgoing.cbegin(),
+ outgoing.cend(),
+ [&port] (const QShaderGraph::Edge &edge) {
+ return edge.targetPortName == port.name;
+ });
+
+ if (edgeIt != outgoing.cend())
+ currentEdges.removeAll(*edgeIt);
+ else
+ allInputsConnected = false;
+ }
+
+ if (allInputsConnected) {
+ const QVector<QShaderGraph::Edge> incoming = incomingEdges(allEdges, node.uuid());
+ currentEdges.append(incoming);
+ }
+
+ return !allInputsConnected;
+ }),
+ statements.end());
+ }
+}
+
+QUuid QShaderGraph::Statement::uuid() const noexcept
+{
+ return node.uuid();
+}
+
+int QShaderGraph::Statement::portIndex(QShaderNodePort::Direction direction, const QString &portName) const noexcept
+{
+ const QVector<QShaderNodePort> ports = node.ports();
+ int index = 0;
+ for (const QShaderNodePort &port : ports) {
+ if (port.name == portName && port.direction == direction)
+ return index;
+ else if (port.direction == direction)
+ index++;
+ }
+ return -1;
+}
+
+void QShaderGraph::addNode(const QShaderNode &node)
+{
+ removeNode(node);
+ m_nodes.append(node);
+}
+
+void QShaderGraph::removeNode(const QShaderNode &node)
+{
+ const auto it = std::find_if(m_nodes.begin(), m_nodes.end(),
+ [node] (const QShaderNode &n) { return n.uuid() == node.uuid(); });
+ if (it != m_nodes.end())
+ m_nodes.erase(it);
+}
+
+QVector<QShaderNode> QShaderGraph::nodes() const noexcept
+{
+ return m_nodes;
+}
+
+void QShaderGraph::addEdge(const QShaderGraph::Edge &edge)
+{
+ if (m_edges.contains(edge))
+ return;
+ m_edges.append(edge);
+}
+
+void QShaderGraph::removeEdge(const QShaderGraph::Edge &edge)
+{
+ m_edges.removeAll(edge);
+}
+
+QVector<QShaderGraph::Edge> QShaderGraph::edges() const noexcept
+{
+ return m_edges;
+}
+
+QVector<QShaderGraph::Statement> QShaderGraph::createStatements(const QStringList &enabledLayers) const
+{
+ const auto intersectsEnabledLayers = [enabledLayers] (const QStringList &layers) {
+ return layers.isEmpty()
+ || std::any_of(layers.cbegin(), layers.cend(),
+ [enabledLayers] (const QString &s) { return enabledLayers.contains(s); });
+ };
+
+ const QVector<QShaderNode> enabledNodes = [this, intersectsEnabledLayers] {
+ auto res = QVector<QShaderNode>();
+ std::copy_if(m_nodes.cbegin(), m_nodes.cend(),
+ std::back_inserter(res),
+ [intersectsEnabledLayers] (const QShaderNode &node) {
+ return intersectsEnabledLayers(node.layers());
+ });
+ return res;
+ }();
+
+ const QVector<Edge> enabledEdges = [this, intersectsEnabledLayers] {
+ auto res = QVector<Edge>();
+ std::copy_if(m_edges.cbegin(), m_edges.cend(),
+ std::back_inserter(res),
+ [intersectsEnabledLayers] (const Edge &edge) {
+ return intersectsEnabledLayers(edge.layers);
+ });
+ return res;
+ }();
+
+ const QHash<QUuid, Statement> idHash = [enabledNodes] {
+ auto nextVarId = 0;
+ auto res = QHash<QUuid, Statement>();
+ for (const QShaderNode &node : enabledNodes)
+ res.insert(node.uuid(), nodeToStatement(node, nextVarId));
+ return res;
+ }();
+
+ auto result = QVector<Statement>();
+ QVector<Edge> currentEdges = enabledEdges;
+ QVector<QUuid> currentUuids = [enabledNodes, enabledEdges] {
+ const QVector<QShaderNode> inputs = copyOutputNodes(enabledNodes, enabledEdges);
+ auto res = QVector<QUuid>();
+ std::transform(inputs.cbegin(), inputs.cend(),
+ std::back_inserter(res),
+ [](const QShaderNode &node) { return node.uuid(); });
+ return res;
+ }();
+
+ // Implements Kahn's algorithm to flatten the graph
+ // https://en.wikipedia.org/wiki/Topological_sorting#Kahn.27s_algorithm
+ //
+ // We implement it with a small twist though, we follow the edges backward
+ // because we want to track the dependencies from the output nodes and not the
+ // input nodes
+ while (!currentUuids.isEmpty()) {
+ const QUuid uuid = currentUuids.takeFirst();
+ result.append(completeStatement(idHash, enabledEdges, uuid));
+
+ const QVector<QShaderGraph::Edge> outgoing = outgoingEdges(currentEdges, uuid);
+ for (const QShaderGraph::Edge &outgoingEdge : outgoing) {
+ currentEdges.removeAll(outgoingEdge);
+ const QUuid nextUuid = outgoingEdge.sourceNodeUuid;
+ const QVector<QShaderGraph::Edge> incoming = incomingEdges(currentEdges, nextUuid);
+ if (incoming.isEmpty()) {
+ currentUuids.append(nextUuid);
+ }
+ }
+ }
+
+ std::reverse(result.begin(), result.end());
+
+ removeNodesWithUnboundInputs(result, enabledEdges);
+
+ return result;
+}
+
+bool operator==(const QShaderGraph::Edge &lhs, const QShaderGraph::Edge &rhs) noexcept
+{
+ return lhs.sourceNodeUuid == rhs.sourceNodeUuid
+ && lhs.sourcePortName == rhs.sourcePortName
+ && lhs.targetNodeUuid == rhs.targetNodeUuid
+ && lhs.targetPortName == rhs.targetPortName;
+}
+
+bool operator==(const QShaderGraph::Statement &lhs, const QShaderGraph::Statement &rhs) noexcept
+{
+ return lhs.inputs == rhs.inputs
+ && lhs.outputs == rhs.outputs
+ && lhs.node.uuid() == rhs.node.uuid();
+}
+}
+QT_END_NAMESPACE
diff --git a/src/render/shadergraph/qshadergraph_p.h b/src/render/shadergraph/qshadergraph_p.h
new file mode 100644
index 000000000..de746f7b2
--- /dev/null
+++ b/src/render/shadergraph/qshadergraph_p.h
@@ -0,0 +1,126 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 QT3DRENDER_QSHADERGRAPH_P_H
+#define QT3DRENDER_QSHADERGRAPH_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 <Qt3DRender/private/qt3drender_global_p.h>
+
+#include <Qt3DRender/private/qshadernode_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender
+{
+class QShaderGraph
+{
+public:
+ class Edge
+ {
+ public:
+ QStringList layers;
+ QUuid sourceNodeUuid;
+ QString sourcePortName;
+ QUuid targetNodeUuid;
+ QString targetPortName;
+ };
+
+ class Statement
+ {
+ public:
+ Q_3DRENDERSHARED_PRIVATE_EXPORT QUuid uuid() const noexcept;
+ Q_3DRENDERSHARED_PRIVATE_EXPORT int portIndex(QShaderNodePort::Direction direction, const QString &portName) const noexcept;
+
+ QShaderNode node;
+ QVector<int> inputs;
+ QVector<int> outputs;
+ };
+
+ Q_3DRENDERSHARED_PRIVATE_EXPORT void addNode(const QShaderNode &node);
+ Q_3DRENDERSHARED_PRIVATE_EXPORT void removeNode(const QShaderNode &node);
+ Q_3DRENDERSHARED_PRIVATE_EXPORT QVector<QShaderNode> nodes() const noexcept;
+
+ Q_3DRENDERSHARED_PRIVATE_EXPORT void addEdge(const Edge &edge);
+ Q_3DRENDERSHARED_PRIVATE_EXPORT void removeEdge(const Edge &edge);
+ Q_3DRENDERSHARED_PRIVATE_EXPORT QVector<Edge> edges() const noexcept;
+
+ Q_3DRENDERSHARED_PRIVATE_EXPORT QVector<Statement> createStatements(const QStringList &enabledLayers = QStringList()) const;
+
+private:
+ QVector<QShaderNode> m_nodes;
+ QVector<Edge> m_edges;
+};
+
+Q_3DRENDERSHARED_PRIVATE_EXPORT bool operator==(const QShaderGraph::Edge &lhs, const QShaderGraph::Edge &rhs) noexcept;
+
+inline bool operator!=(const QShaderGraph::Edge &lhs, const QShaderGraph::Edge &rhs) noexcept
+{
+ return !(lhs == rhs);
+}
+
+Q_3DRENDERSHARED_PRIVATE_EXPORT bool operator==(const QShaderGraph::Statement &lhs, const QShaderGraph::Statement &rhs) noexcept;
+
+inline bool operator!=(const QShaderGraph::Statement &lhs, const QShaderGraph::Statement &rhs) noexcept
+{
+ return !(lhs == rhs);
+}
+
+}
+
+Q_DECLARE_TYPEINFO(Qt3DRender::QShaderGraph, Q_MOVABLE_TYPE);
+Q_DECLARE_TYPEINFO(Qt3DRender::QShaderGraph::Edge, Q_MOVABLE_TYPE);
+Q_DECLARE_TYPEINFO(Qt3DRender::QShaderGraph::Statement, Q_MOVABLE_TYPE);
+QT_END_NAMESPACE
+
+Q_DECLARE_METATYPE(Qt3DRender::QShaderGraph)
+Q_DECLARE_METATYPE(Qt3DRender::QShaderGraph::Edge)
+Q_DECLARE_METATYPE(Qt3DRender::QShaderGraph::Statement)
+
+#endif // QT3DRENDER_QSHADERGRAPH_P_H
diff --git a/src/render/shadergraph/qshadergraphloader.cpp b/src/render/shadergraph/qshadergraphloader.cpp
new file mode 100644
index 000000000..64f159e7b
--- /dev/null
+++ b/src/render/shadergraph/qshadergraphloader.cpp
@@ -0,0 +1,271 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 "qshadergraphloader_p.h"
+
+#include "qshadernodesloader_p.h"
+
+#include <QtCore/qdebug.h>
+#include <QtCore/qiodevice.h>
+#include <QtCore/qjsonarray.h>
+#include <QtCore/qjsondocument.h>
+#include <QtCore/qjsonobject.h>
+#include <QtCore/qmetaobject.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender
+{
+void qt_register_ShaderLanguage_enums();
+
+QShaderGraphLoader::QShaderGraphLoader() noexcept
+ : m_status(Null),
+ m_device(nullptr)
+{
+ qt_register_ShaderLanguage_enums();
+}
+
+QShaderGraphLoader::Status QShaderGraphLoader::status() const noexcept
+{
+ return m_status;
+}
+
+QShaderGraph QShaderGraphLoader::graph() const noexcept
+{
+ return m_graph;
+}
+
+QIODevice *QShaderGraphLoader::device() const noexcept
+{
+ return m_device;
+}
+
+void QShaderGraphLoader::setDevice(QIODevice *device) noexcept
+{
+ m_device = device;
+ m_graph = QShaderGraph();
+ m_status = !m_device ? Null
+ : (m_device->openMode() & QIODevice::ReadOnly) ? Waiting
+ : Error;
+}
+
+QHash<QString, QShaderNode> QShaderGraphLoader::prototypes() const noexcept
+{
+ return m_prototypes;
+}
+
+void QShaderGraphLoader::setPrototypes(const QHash<QString, QShaderNode> &prototypes) noexcept
+{
+ m_prototypes = prototypes;
+}
+
+void QShaderGraphLoader::load()
+{
+ if (m_status == Error)
+ return;
+
+ auto error = QJsonParseError();
+ const QJsonDocument document = QJsonDocument::fromJson(m_device->readAll(), &error);
+
+ if (error.error != QJsonParseError::NoError) {
+ qWarning() << "Invalid JSON document:" << error.errorString();
+ m_status = Error;
+ return;
+ }
+
+ if (document.isEmpty() || !document.isObject()) {
+ qWarning() << "Invalid JSON document, root should be an object";
+ m_status = Error;
+ return;
+ }
+
+ const QJsonObject root = document.object();
+
+ const QJsonValue nodesValue = root.value(QStringLiteral("nodes"));
+ if (!nodesValue.isArray()) {
+ qWarning() << "Invalid nodes property, should be an array";
+ m_status = Error;
+ return;
+ }
+
+ const QJsonValue edgesValue = root.value(QStringLiteral("edges"));
+ if (!edgesValue.isArray()) {
+ qWarning() << "Invalid edges property, should be an array";
+ m_status = Error;
+ return;
+ }
+
+ bool hasError = false;
+
+ const QJsonValue prototypesValue = root.value(QStringLiteral("prototypes"));
+ if (!prototypesValue.isUndefined()) {
+ if (prototypesValue.isObject()) {
+ QShaderNodesLoader loader;
+ loader.load(prototypesValue.toObject());
+ m_prototypes.insert(loader.nodes());
+ } else {
+ qWarning() << "Invalid prototypes property, should be an object";
+ m_status = Error;
+ return;
+ }
+ }
+
+ const QJsonArray nodes = nodesValue.toArray();
+ for (const QJsonValue &nodeValue : nodes) {
+ if (!nodeValue.isObject()) {
+ qWarning() << "Invalid node found";
+ hasError = true;
+ continue;
+ }
+
+ const QJsonObject nodeObject = nodeValue.toObject();
+
+ const QString uuidString = nodeObject.value(QStringLiteral("uuid")).toString();
+ const QUuid uuid = QUuid(uuidString);
+ if (uuid.isNull()) {
+ qWarning() << "Invalid UUID found in node:" << uuidString;
+ hasError = true;
+ continue;
+ }
+
+ const QString type = nodeObject.value(QStringLiteral("type")).toString();
+ if (!m_prototypes.contains(type)) {
+ qWarning() << "Unsupported node type found:" << type;
+ hasError = true;
+ continue;
+ }
+
+ const QJsonArray layersArray = nodeObject.value(QStringLiteral("layers")).toArray();
+ auto layers = QStringList();
+ for (const QJsonValue &layerValue : layersArray) {
+ layers.append(layerValue.toString());
+ }
+
+ QShaderNode node = m_prototypes.value(type);
+ node.setUuid(uuid);
+ node.setLayers(layers);
+
+ const QJsonValue parametersValue = nodeObject.value(QStringLiteral("parameters"));
+ if (parametersValue.isObject()) {
+ const QJsonObject parametersObject = parametersValue.toObject();
+ for (const QString &parameterName : parametersObject.keys()) {
+ const QJsonValue parameterValue = parametersObject.value(parameterName);
+ if (parameterValue.isObject()) {
+ const QJsonObject parameterObject = parameterValue.toObject();
+ const QString type = parameterObject.value(QStringLiteral("type")).toString();
+ const int typeId = QMetaType::type(type.toUtf8());
+
+ const QString value = parameterObject.value(QStringLiteral("value")).toString();
+ auto variant = QVariant(value);
+
+ if (QMetaType::typeFlags(typeId) & QMetaType::IsEnumeration) {
+ const QMetaObject *metaObject = QMetaType::metaObjectForType(typeId);
+ const char *className = metaObject->className();
+ const QByteArray enumName = type.mid(static_cast<int>(qstrlen(className)) + 2).toUtf8();
+ const QMetaEnum metaEnum = metaObject->enumerator(metaObject->indexOfEnumerator(enumName));
+ const int enumValue = metaEnum.keyToValue(value.toUtf8());
+ variant = QVariant(enumValue);
+ variant.convert(typeId);
+ } else {
+ variant.convert(typeId);
+ }
+ node.setParameter(parameterName, variant);
+ } else {
+ node.setParameter(parameterName, parameterValue.toVariant());
+ }
+ }
+ }
+
+ m_graph.addNode(node);
+ }
+
+ const QJsonArray edges = edgesValue.toArray();
+ for (const QJsonValue &edgeValue : edges) {
+ if (!edgeValue.isObject()) {
+ qWarning() << "Invalid edge found";
+ hasError = true;
+ continue;
+ }
+
+ const QJsonObject edgeObject = edgeValue.toObject();
+
+ const QString sourceUuidString = edgeObject.value(QStringLiteral("sourceUuid")).toString();
+ const QUuid sourceUuid = QUuid(sourceUuidString);
+ if (sourceUuid.isNull()) {
+ qWarning() << "Invalid source UUID found in edge:" << sourceUuidString;
+ hasError = true;
+ continue;
+ }
+
+ const QString sourcePort = edgeObject.value(QStringLiteral("sourcePort")).toString();
+
+ const QString targetUuidString = edgeObject.value(QStringLiteral("targetUuid")).toString();
+ const QUuid targetUuid = QUuid(targetUuidString);
+ if (targetUuid.isNull()) {
+ qWarning() << "Invalid target UUID found in edge:" << targetUuidString;
+ hasError = true;
+ continue;
+ }
+
+ const QString targetPort = edgeObject.value(QStringLiteral("targetPort")).toString();
+
+ const QJsonArray layersArray = edgeObject.value(QStringLiteral("layers")).toArray();
+ auto layers = QStringList();
+ for (const QJsonValue &layerValue : layersArray) {
+ layers.append(layerValue.toString());
+ }
+
+ auto edge = QShaderGraph::Edge();
+ edge.sourceNodeUuid = sourceUuid;
+ edge.sourcePortName = sourcePort;
+ edge.targetNodeUuid = targetUuid;
+ edge.targetPortName = targetPort;
+ edge.layers = layers;
+ m_graph.addEdge(edge);
+ }
+
+ if (hasError) {
+ m_status = Error;
+ m_graph = QShaderGraph();
+ } else {
+ m_status = Ready;
+ }
+}
+}
+QT_END_NAMESPACE
diff --git a/src/render/shadergraph/qshadergraphloader_p.h b/src/render/shadergraph/qshadergraphloader_p.h
new file mode 100644
index 000000000..20589d724
--- /dev/null
+++ b/src/render/shadergraph/qshadergraphloader_p.h
@@ -0,0 +1,102 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 QT3DRENDER_QSHADERGRAPHLOADER_P_H
+#define QT3DRENDER_QSHADERGRAPHLOADER_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 <Qt3DRender/private/qt3drender_global_p.h>
+
+#include <Qt3DRender/private/qshadergraph_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QIODevice;
+namespace Qt3DRender
+{
+
+class QShaderGraphLoader
+{
+public:
+ enum Status : char {
+ Null,
+ Waiting,
+ Ready,
+ Error
+ };
+
+ Q_3DRENDERSHARED_PRIVATE_EXPORT QShaderGraphLoader() noexcept;
+
+ Q_3DRENDERSHARED_PRIVATE_EXPORT Status status() const noexcept;
+ Q_3DRENDERSHARED_PRIVATE_EXPORT QShaderGraph graph() const noexcept;
+
+ Q_3DRENDERSHARED_PRIVATE_EXPORT QIODevice *device() const noexcept;
+ Q_3DRENDERSHARED_PRIVATE_EXPORT void setDevice(QIODevice *device) noexcept;
+
+ Q_3DRENDERSHARED_PRIVATE_EXPORT QHash<QString, QShaderNode> prototypes() const noexcept;
+ Q_3DRENDERSHARED_PRIVATE_EXPORT void setPrototypes(const QHash<QString, QShaderNode> &prototypes) noexcept;
+
+ Q_3DRENDERSHARED_PRIVATE_EXPORT void load();
+
+private:
+ Status m_status;
+ QIODevice *m_device;
+ QHash<QString, QShaderNode> m_prototypes;
+ QShaderGraph m_graph;
+};
+
+
+}
+Q_DECLARE_TYPEINFO(Qt3DRender::QShaderGraphLoader, Q_MOVABLE_TYPE);
+QT_END_NAMESPACE
+
+Q_DECLARE_METATYPE(Qt3DRender::QShaderGraphLoader)
+Q_DECLARE_METATYPE(Qt3DRender::QShaderGraphLoader::Status)
+
+#endif // QT3DRENDER_QSHADERGRAPHLOADER_P_H
diff --git a/src/render/shadergraph/qshaderlanguage.cpp b/src/render/shadergraph/qshaderlanguage.cpp
new file mode 100644
index 000000000..1630e3de8
--- /dev/null
+++ b/src/render/shadergraph/qshaderlanguage.cpp
@@ -0,0 +1,57 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 "qshaderlanguage_p.h"
+
+#include <QtCore/qcoreapplication.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender
+{
+// Note: to be invoked explicitly. Relying for example on
+// Q_COREAPP_STARTUP_FUNCTION would not be acceptable in static builds.
+void qt_register_ShaderLanguage_enums()
+{
+ qRegisterMetaType<QShaderLanguage::StorageQualifier>();
+ qRegisterMetaType<QShaderLanguage::VariableType>();
+}
+}
+
+QT_END_NAMESPACE
diff --git a/src/render/shadergraph/qshaderlanguage_p.h b/src/render/shadergraph/qshaderlanguage_p.h
new file mode 100644
index 000000000..1a56d7476
--- /dev/null
+++ b/src/render/shadergraph/qshaderlanguage_p.h
@@ -0,0 +1,164 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 QT3DRENDER_QSHADERLANGUAGE_P_H
+#define QT3DRENDER_QSHADERLANGUAGE_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 <Qt3DRender/private/qt3drender_global_p.h>
+
+#include <QtCore/qmetatype.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QShaderLanguage
+{
+ Q_NAMESPACE_EXPORT(Q_3DRENDERSHARED_PRIVATE_EXPORT)
+
+ enum StorageQualifier : char {
+ Const = 1,
+ Input,
+ BuiltIn,
+ Output,
+ Uniform
+ };
+ Q_ENUM_NS(StorageQualifier)
+
+ enum VariableType : int {
+ Bool = 1,
+ Int,
+ Uint,
+ Float,
+ Double,
+ Vec2,
+ Vec3,
+ Vec4,
+ DVec2,
+ DVec3,
+ DVec4,
+ BVec2,
+ BVec3,
+ BVec4,
+ IVec2,
+ IVec3,
+ IVec4,
+ UVec2,
+ UVec3,
+ UVec4,
+ Mat2,
+ Mat3,
+ Mat4,
+ Mat2x2,
+ Mat2x3,
+ Mat2x4,
+ Mat3x2,
+ Mat3x3,
+ Mat3x4,
+ Mat4x2,
+ Mat4x3,
+ Mat4x4,
+ DMat2,
+ DMat3,
+ DMat4,
+ DMat2x2,
+ DMat2x3,
+ DMat2x4,
+ DMat3x2,
+ DMat3x3,
+ DMat3x4,
+ DMat4x2,
+ DMat4x3,
+ DMat4x4,
+ Sampler1D,
+ Sampler2D,
+ Sampler3D,
+ SamplerCube,
+ Sampler2DRect,
+ Sampler2DMs,
+ SamplerBuffer,
+ Sampler1DArray,
+ Sampler2DArray,
+ Sampler2DMsArray,
+ SamplerCubeArray,
+ Sampler1DShadow,
+ Sampler2DShadow,
+ Sampler2DRectShadow,
+ Sampler1DArrayShadow,
+ Sampler2DArrayShadow,
+ SamplerCubeShadow,
+ SamplerCubeArrayShadow,
+ ISampler1D,
+ ISampler2D,
+ ISampler3D,
+ ISamplerCube,
+ ISampler2DRect,
+ ISampler2DMs,
+ ISamplerBuffer,
+ ISampler1DArray,
+ ISampler2DArray,
+ ISampler2DMsArray,
+ ISamplerCubeArray,
+ USampler1D,
+ USampler2D,
+ USampler3D,
+ USamplerCube,
+ USampler2DRect,
+ USampler2DMs,
+ USamplerBuffer,
+ USampler1DArray,
+ USampler2DArray,
+ USampler2DMsArray,
+ USamplerCubeArray
+ };
+ Q_ENUM_NS(VariableType)
+}
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_QSHADERLANGUAGE_P_H
diff --git a/src/render/shadergraph/qshadernode.cpp b/src/render/shadergraph/qshadernode.cpp
new file mode 100644
index 000000000..e0421e006
--- /dev/null
+++ b/src/render/shadergraph/qshadernode.cpp
@@ -0,0 +1,174 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 "qshadernode_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender
+{
+QShaderNode::Type QShaderNode::type() const noexcept
+{
+ int inputCount = 0;
+ int outputCount = 0;
+ for (const auto &port : qAsConst(m_ports)) {
+ switch (port.direction) {
+ case QShaderNodePort::Input:
+ inputCount++;
+ break;
+ case QShaderNodePort::Output:
+ outputCount++;
+ break;
+ }
+ }
+
+ return (inputCount == 0 && outputCount == 0) ? Invalid
+ : (inputCount > 0 && outputCount == 0) ? Output
+ : (inputCount == 0 && outputCount > 0) ? Input
+ : Function;
+}
+
+QUuid QShaderNode::uuid() const noexcept
+{
+ return m_uuid;
+}
+
+void QShaderNode::setUuid(const QUuid &uuid) noexcept
+{
+ m_uuid = uuid;
+}
+
+QStringList QShaderNode::layers() const noexcept
+{
+ return m_layers;
+}
+
+void QShaderNode::setLayers(const QStringList &layers) noexcept
+{
+ m_layers = layers;
+}
+
+QVector<QShaderNodePort> QShaderNode::ports() const noexcept
+{
+ return m_ports;
+}
+
+void QShaderNode::addPort(const QShaderNodePort &port)
+{
+ removePort(port);
+ m_ports.append(port);
+}
+
+void QShaderNode::removePort(const QShaderNodePort &port)
+{
+ const auto it = std::find_if(m_ports.begin(), m_ports.end(),
+ [port](const QShaderNodePort &p) {
+ return p.name == port.name;
+ });
+ if (it != m_ports.end())
+ m_ports.erase(it);
+}
+
+QStringList QShaderNode::parameterNames() const
+{
+ return m_parameters.keys();
+}
+
+QVariant QShaderNode::parameter(const QString &name) const
+{
+ return m_parameters.value(name);
+}
+
+void QShaderNode::setParameter(const QString &name, const QVariant &value)
+{
+ m_parameters.insert(name, value);
+}
+
+void QShaderNode::clearParameter(const QString &name)
+{
+ m_parameters.remove(name);
+}
+
+void QShaderNode::addRule(const QShaderFormat &format, const QShaderNode::Rule &rule)
+{
+ removeRule(format);
+ m_rules << qMakePair(format, rule);
+}
+
+void QShaderNode::removeRule(const QShaderFormat &format)
+{
+ const auto it = std::find_if(m_rules.begin(), m_rules.end(),
+ [format](const QPair<QShaderFormat, Rule> &entry) {
+ return entry.first == format;
+ });
+ if (it != m_rules.end())
+ m_rules.erase(it);
+}
+
+QVector<QShaderFormat> QShaderNode::availableFormats() const
+{
+ auto res = QVector<QShaderFormat>();
+ std::transform(m_rules.cbegin(), m_rules.cend(),
+ std::back_inserter(res),
+ [](const QPair<QShaderFormat, Rule> &entry) { return entry.first; });
+ return res;
+}
+
+QShaderNode::Rule QShaderNode::rule(const QShaderFormat &format) const
+{
+ const auto it = std::find_if(m_rules.crbegin(), m_rules.crend(),
+ [format](const QPair<QShaderFormat, Rule> &entry) {
+ return format.supports(entry.first);
+ });
+ return it != m_rules.crend() ? it->second : Rule();
+}
+
+QShaderNode::Rule::Rule(const QByteArray &subs, const QByteArrayList &snippets) noexcept
+ : substitution(subs),
+ headerSnippets(snippets)
+{
+}
+
+bool operator==(const QShaderNode::Rule &lhs, const QShaderNode::Rule &rhs) noexcept
+{
+ return lhs.substitution == rhs.substitution
+ && lhs.headerSnippets == rhs.headerSnippets;
+}
+}
+QT_END_NAMESPACE
diff --git a/src/render/shadergraph/qshadernode_p.h b/src/render/shadergraph/qshadernode_p.h
new file mode 100644
index 000000000..92e0e8e14
--- /dev/null
+++ b/src/render/shadergraph/qshadernode_p.h
@@ -0,0 +1,131 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 QT3DRENDER_QSHADERNODE_P_H
+#define QT3DRENDER_QSHADERNODE_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 <Qt3DRender/private/qt3drender_global_p.h>
+
+#include <Qt3DRender/private/qshaderformat_p.h>
+#include <Qt3DRender/private/qshadernodeport_p.h>
+
+#include <QtCore/quuid.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender
+{
+class QShaderNode
+{
+public:
+ enum Type : char {
+ Invalid,
+ Input,
+ Output,
+ Function
+ };
+
+ class Rule
+ {
+ public:
+ Q_3DRENDERSHARED_PRIVATE_EXPORT Rule(const QByteArray &substitution = QByteArray(), const QByteArrayList &headerSnippets = QByteArrayList()) noexcept;
+
+ QByteArray substitution;
+ QByteArrayList headerSnippets;
+ };
+
+ Q_3DRENDERSHARED_PRIVATE_EXPORT Type type() const noexcept;
+
+ Q_3DRENDERSHARED_PRIVATE_EXPORT QUuid uuid() const noexcept;
+ Q_3DRENDERSHARED_PRIVATE_EXPORT void setUuid(const QUuid &uuid) noexcept;
+
+ Q_3DRENDERSHARED_PRIVATE_EXPORT QStringList layers() const noexcept;
+ Q_3DRENDERSHARED_PRIVATE_EXPORT void setLayers(const QStringList &layers) noexcept;
+
+ Q_3DRENDERSHARED_PRIVATE_EXPORT QVector<QShaderNodePort> ports() const noexcept;
+ Q_3DRENDERSHARED_PRIVATE_EXPORT void addPort(const QShaderNodePort &port);
+ Q_3DRENDERSHARED_PRIVATE_EXPORT void removePort(const QShaderNodePort &port);
+
+ Q_3DRENDERSHARED_PRIVATE_EXPORT QStringList parameterNames() const;
+ Q_3DRENDERSHARED_PRIVATE_EXPORT QVariant parameter(const QString &name) const;
+ Q_3DRENDERSHARED_PRIVATE_EXPORT void setParameter(const QString &name, const QVariant &value);
+ Q_3DRENDERSHARED_PRIVATE_EXPORT void clearParameter(const QString &name);
+
+ Q_3DRENDERSHARED_PRIVATE_EXPORT void addRule(const QShaderFormat &format, const Rule &rule);
+ Q_3DRENDERSHARED_PRIVATE_EXPORT void removeRule(const QShaderFormat &format);
+
+ Q_3DRENDERSHARED_PRIVATE_EXPORT QVector<QShaderFormat> availableFormats() const;
+ Q_3DRENDERSHARED_PRIVATE_EXPORT Rule rule(const QShaderFormat &format) const;
+
+private:
+ QUuid m_uuid;
+ QStringList m_layers;
+ QVector<QShaderNodePort> m_ports;
+ QHash<QString, QVariant> m_parameters;
+ QVector<QPair<QShaderFormat, QShaderNode::Rule>> m_rules;
+};
+
+Q_3DRENDERSHARED_PRIVATE_EXPORT bool operator==(const QShaderNode::Rule &lhs, const QShaderNode::Rule &rhs) noexcept;
+
+inline bool operator!=(const QShaderNode::Rule &lhs, const QShaderNode::Rule &rhs) noexcept
+{
+ return !(lhs == rhs);
+}
+
+}
+
+Q_DECLARE_TYPEINFO(Qt3DRender::QShaderNode, Q_MOVABLE_TYPE);
+Q_DECLARE_TYPEINFO(Qt3DRender::QShaderNode::Rule, Q_MOVABLE_TYPE);
+QT_END_NAMESPACE
+
+Q_DECLARE_METATYPE(Qt3DRender::QShaderNode)
+Q_DECLARE_METATYPE(Qt3DRender::QShaderNode::Rule)
+
+#endif // QT3DRENDER_QSHADERNODE_P_H
diff --git a/src/render/shadergraph/qshadernodeport.cpp b/src/render/shadergraph/qshadernodeport.cpp
new file mode 100644
index 000000000..a79c32234
--- /dev/null
+++ b/src/render/shadergraph/qshadernodeport.cpp
@@ -0,0 +1,58 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 "qshadernodeport_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender
+{
+QShaderNodePort::QShaderNodePort() noexcept
+ : direction(Output)
+{
+}
+
+bool operator==(const QShaderNodePort &lhs, const QShaderNodePort &rhs) noexcept
+{
+ return lhs.direction == rhs.direction
+ && lhs.name == rhs.name;
+}
+}
+
+QT_END_NAMESPACE
diff --git a/src/render/shadergraph/qshadernodeport_p.h b/src/render/shadergraph/qshadernodeport_p.h
new file mode 100644
index 000000000..d83cdb8c4
--- /dev/null
+++ b/src/render/shadergraph/qshadernodeport_p.h
@@ -0,0 +1,91 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 QT3DRENDER_QSHADERNODEPORT_P_H
+#define QT3DRENDER_QSHADERNODEPORT_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 <Qt3DRender/private/qt3drender_global_p.h>
+
+#include <QtCore/qstring.h>
+#include <QtCore/qvariant.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender
+{
+class QShaderNodePort
+{
+public:
+ enum Direction : char {
+ Input,
+ Output
+ };
+
+ Q_3DRENDERSHARED_PRIVATE_EXPORT QShaderNodePort() noexcept;
+
+ QShaderNodePort::Direction direction;
+ QString name;
+};
+
+Q_3DRENDERSHARED_PRIVATE_EXPORT bool operator==(const QShaderNodePort &lhs, const QShaderNodePort &rhs) noexcept;
+
+inline bool operator!=(const QShaderNodePort &lhs, const QShaderNodePort &rhs) noexcept
+{
+ return !(lhs == rhs);
+}
+
+
+}
+Q_DECLARE_TYPEINFO(Qt3DRender::QShaderNodePort, Q_MOVABLE_TYPE);
+QT_END_NAMESPACE
+
+Q_DECLARE_METATYPE(Qt3DRender::QShaderNodePort)
+
+#endif // QT3DRENDER_QSHADERNODEPORT_P_H
diff --git a/src/render/shadergraph/qshadernodesloader.cpp b/src/render/shadergraph/qshadernodesloader.cpp
new file mode 100644
index 000000000..df34fd1f7
--- /dev/null
+++ b/src/render/shadergraph/qshadernodesloader.cpp
@@ -0,0 +1,293 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 "qshadernodesloader_p.h"
+
+#include <QtCore/qdebug.h>
+#include <QtCore/qiodevice.h>
+#include <QtCore/qjsonarray.h>
+#include <QtCore/qjsondocument.h>
+#include <QtCore/qjsonobject.h>
+#include <QtCore/qmetaobject.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender
+{
+QShaderNodesLoader::QShaderNodesLoader() noexcept
+ : m_status(Null),
+ m_device(nullptr)
+{
+}
+
+QShaderNodesLoader::Status QShaderNodesLoader::status() const noexcept
+{
+ return m_status;
+}
+
+QHash<QString, QShaderNode> QShaderNodesLoader::nodes() const noexcept
+{
+ return m_nodes;
+}
+
+QIODevice *QShaderNodesLoader::device() const noexcept
+{
+ return m_device;
+}
+
+void QShaderNodesLoader::setDevice(QIODevice *device) noexcept
+{
+ m_device = device;
+ m_nodes.clear();
+ m_status = !m_device ? Null
+ : (m_device->openMode() & QIODevice::ReadOnly) ? Waiting
+ : Error;
+}
+
+void QShaderNodesLoader::load()
+{
+ if (m_status == Error)
+ return;
+
+ auto error = QJsonParseError();
+ const QJsonDocument document = QJsonDocument::fromJson(m_device->readAll(), &error);
+
+ if (error.error != QJsonParseError::NoError) {
+ qWarning() << "Invalid JSON document:" << error.errorString();
+ m_status = Error;
+ return;
+ }
+
+ if (document.isEmpty() || !document.isObject()) {
+ qWarning() << "Invalid JSON document, root should be an object";
+ m_status = Error;
+ return;
+ }
+
+ const QJsonObject root = document.object();
+ load(root);
+}
+
+void QShaderNodesLoader::load(const QJsonObject &prototypesObject)
+{
+ bool hasError = false;
+
+ for (const QString &property : prototypesObject.keys()) {
+ const QJsonValue nodeValue = prototypesObject.value(property);
+ if (!nodeValue.isObject()) {
+ qWarning() << "Invalid node found";
+ hasError = true;
+ break;
+ }
+
+ const QJsonObject nodeObject = nodeValue.toObject();
+
+ auto node = QShaderNode();
+
+ const QJsonValue inputsValue = nodeObject.value(QStringLiteral("inputs"));
+ if (inputsValue.isArray()) {
+ const QJsonArray inputsArray = inputsValue.toArray();
+ for (const QJsonValue &inputValue : inputsArray) {
+ if (!inputValue.isString()) {
+ qWarning() << "Non-string value in inputs";
+ hasError = true;
+ break;
+ }
+
+ auto input = QShaderNodePort();
+ input.direction = QShaderNodePort::Input;
+ input.name = inputValue.toString();
+ node.addPort(input);
+ }
+ }
+
+ const QJsonValue outputsValue = nodeObject.value(QStringLiteral("outputs"));
+ if (outputsValue.isArray()) {
+ const QJsonArray outputsArray = outputsValue.toArray();
+ for (const QJsonValue &outputValue : outputsArray) {
+ if (!outputValue.isString()) {
+ qWarning() << "Non-string value in outputs";
+ hasError = true;
+ break;
+ }
+
+ auto output = QShaderNodePort();
+ output.direction = QShaderNodePort::Output;
+ output.name = outputValue.toString();
+ node.addPort(output);
+ }
+ }
+
+ const QJsonValue parametersValue = nodeObject.value(QStringLiteral("parameters"));
+ if (parametersValue.isObject()) {
+ const QJsonObject parametersObject = parametersValue.toObject();
+ for (const QString &parameterName : parametersObject.keys()) {
+ const QJsonValue parameterValue = parametersObject.value(parameterName);
+ if (parameterValue.isObject()) {
+ const QJsonObject parameterObject = parameterValue.toObject();
+ const QString type = parameterObject.value(QStringLiteral("type")).toString();
+ const int typeId = QMetaType::type(type.toUtf8());
+
+ const QString value = parameterObject.value(QStringLiteral("value")).toString();
+ auto variant = QVariant(value);
+
+ if (QMetaType::typeFlags(typeId) & QMetaType::IsEnumeration) {
+ const QMetaObject *metaObject = QMetaType::metaObjectForType(typeId);
+ const char *className = metaObject->className();
+ const QByteArray enumName = type.mid(static_cast<int>(qstrlen(className)) + 2).toUtf8();
+ const QMetaEnum metaEnum = metaObject->enumerator(metaObject->indexOfEnumerator(enumName));
+ const int enumValue = metaEnum.keyToValue(value.toUtf8());
+ variant = QVariant(enumValue);
+ variant.convert(typeId);
+ } else {
+ variant.convert(typeId);
+ }
+ node.setParameter(parameterName, variant);
+ } else {
+ node.setParameter(parameterName, parameterValue.toVariant());
+ }
+ }
+ }
+
+ const QJsonValue rulesValue = nodeObject.value(QStringLiteral("rules"));
+ if (rulesValue.isArray()) {
+ const QJsonArray rulesArray = rulesValue.toArray();
+ for (const QJsonValue &ruleValue : rulesArray) {
+ if (!ruleValue.isObject()) {
+ qWarning() << "Rules should be objects";
+ hasError = true;
+ break;
+ }
+
+ const QJsonObject ruleObject = ruleValue.toObject();
+
+ const QJsonValue formatValue = ruleObject.value(QStringLiteral("format"));
+ if (!formatValue.isObject()) {
+ qWarning() << "Format is mandatory in rules and should be an object";
+ hasError = true;
+ break;
+ }
+
+ const QJsonObject formatObject = formatValue.toObject();
+ auto format = QShaderFormat();
+
+ const QJsonValue apiValue = formatObject.value(QStringLiteral("api"));
+ if (!apiValue.isString()) {
+ qWarning() << "Format API must be a string";
+ hasError = true;
+ break;
+ }
+
+ const QString api = apiValue.toString();
+ format.setApi(api == QStringLiteral("OpenGLES") ? QShaderFormat::OpenGLES
+ : api == QStringLiteral("OpenGLNoProfile") ? QShaderFormat::OpenGLNoProfile
+ : api == QStringLiteral("OpenGLCoreProfile") ? QShaderFormat::OpenGLCoreProfile
+ : api == QStringLiteral("OpenGLCompatibilityProfile") ? QShaderFormat::OpenGLCompatibilityProfile
+ : api == QStringLiteral("VulkanFlavoredGLSL") ? QShaderFormat::VulkanFlavoredGLSL
+ : api == QStringLiteral("RHI") ? QShaderFormat::RHI
+ : QShaderFormat::NoApi);
+ if (format.api() == QShaderFormat::NoApi) {
+ qWarning() << "Format API must be one of: OpenGLES, OpenGLNoProfile, OpenGLCoreProfile OpenGLCompatibilityProfile, VulkanFlavoredGLSL or RHI";
+ hasError = true;
+ break;
+ }
+
+ const QJsonValue majorValue = formatObject.value(QStringLiteral("major"));
+ const QJsonValue minorValue = formatObject.value(QStringLiteral("minor"));
+ if (!majorValue.isDouble() || !minorValue.isDouble()) {
+ qWarning() << "Format major and minor version must be values";
+ hasError = true;
+ break;
+ }
+ format.setVersion(QVersionNumber(majorValue.toInt(), minorValue.toInt()));
+
+ const QJsonValue extensionsValue = formatObject.value(QStringLiteral("extensions"));
+ const QJsonArray extensionsArray = extensionsValue.toArray();
+ auto extensions = QStringList();
+ std::transform(extensionsArray.constBegin(), extensionsArray.constEnd(),
+ std::back_inserter(extensions),
+ [] (const QJsonValue &extensionValue) { return extensionValue.toString(); });
+ format.setExtensions(extensions);
+
+ const QString vendor = formatObject.value(QStringLiteral("vendor")).toString();
+ format.setVendor(vendor);
+
+ const QJsonValue substitutionValue = ruleObject.value(QStringLiteral("substitution"));
+ if (!substitutionValue.isString()) {
+ qWarning() << "Substitution needs to be a string";
+ hasError = true;
+ break;
+ }
+
+ // We default out to a Fragment ShaderType if nothing is specified
+ // as that was the initial behavior we introduced
+ const QString shaderType = formatObject.value(QStringLiteral("shaderType")).toString();
+ format.setShaderType(shaderType == QStringLiteral("Fragment") ? QShaderFormat::Fragment
+ : shaderType == QStringLiteral("Vertex") ? QShaderFormat::Vertex
+ : shaderType == QStringLiteral("TessellationControl") ? QShaderFormat::TessellationControl
+ : shaderType == QStringLiteral("TessellationEvaluation") ? QShaderFormat::TessellationEvaluation
+ : shaderType == QStringLiteral("Geometry") ? QShaderFormat::Geometry
+ : shaderType == QStringLiteral("Compute") ? QShaderFormat::Compute
+ : QShaderFormat::Fragment);
+
+ const QByteArray substitution = substitutionValue.toString().toUtf8();
+
+ const QJsonValue snippetsValue = ruleObject.value(QStringLiteral("headerSnippets"));
+ const QJsonArray snippetsArray = snippetsValue.toArray();
+ auto snippets = QByteArrayList();
+ std::transform(snippetsArray.constBegin(), snippetsArray.constEnd(),
+ std::back_inserter(snippets),
+ [] (const QJsonValue &snippetValue) { return snippetValue.toString().toUtf8(); });
+
+ node.addRule(format, QShaderNode::Rule(substitution, snippets));
+ }
+ }
+
+ m_nodes.insert(property, node);
+ }
+
+ if (hasError) {
+ m_status = Error;
+ m_nodes.clear();
+ } else {
+ m_status = Ready;
+ }
+}
+}
+QT_END_NAMESPACE
diff --git a/src/render/shadergraph/qshadernodesloader_p.h b/src/render/shadergraph/qshadernodesloader_p.h
new file mode 100644
index 000000000..80d75a0bc
--- /dev/null
+++ b/src/render/shadergraph/qshadernodesloader_p.h
@@ -0,0 +1,99 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 QT3DRENDER_QSHADERNODESLOADER_P_H
+#define QT3DRENDER_QSHADERNODESLOADER_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 <Qt3DRender/private/qt3drender_global_p.h>
+
+#include <Qt3DRender/private/qshadergraph_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QIODevice;
+
+namespace Qt3DRender
+{
+class QShaderNodesLoader
+{
+public:
+ enum Status : char {
+ Null,
+ Waiting,
+ Ready,
+ Error
+ };
+
+ Q_3DRENDERSHARED_PRIVATE_EXPORT QShaderNodesLoader() noexcept;
+
+ Q_3DRENDERSHARED_PRIVATE_EXPORT Status status() const noexcept;
+ Q_3DRENDERSHARED_PRIVATE_EXPORT QHash<QString, QShaderNode> nodes() const noexcept;
+
+ Q_3DRENDERSHARED_PRIVATE_EXPORT QIODevice *device() const noexcept;
+ Q_3DRENDERSHARED_PRIVATE_EXPORT void setDevice(QIODevice *device) noexcept;
+
+ Q_3DRENDERSHARED_PRIVATE_EXPORT void load();
+ Q_3DRENDERSHARED_PRIVATE_EXPORT void load(const QJsonObject &prototypesObject);
+
+private:
+ Status m_status;
+ QIODevice *m_device;
+ QHash<QString, QShaderNode> m_nodes;
+};
+
+
+}
+Q_DECLARE_TYPEINFO(Qt3DRender::QShaderNodesLoader, Q_MOVABLE_TYPE);
+QT_END_NAMESPACE
+
+Q_DECLARE_METATYPE(Qt3DRender::QShaderNodesLoader)
+Q_DECLARE_METATYPE(Qt3DRender::QShaderNodesLoader::Status)
+
+#endif // QT3DRENDER_QSHADERNODESLOADER_P_H
diff --git a/src/render/shadergraph/shadergraph.pri b/src/render/shadergraph/shadergraph.pri
new file mode 100644
index 000000000..27bb8c6e9
--- /dev/null
+++ b/src/render/shadergraph/shadergraph.pri
@@ -0,0 +1,21 @@
+INCLUDEPATH += $$PWD
+
+HEADERS += \
+ $$PWD/qshaderformat_p.h \
+ $$PWD/qshadergenerator_p.h \
+ $$PWD/qshadergraphloader_p.h \
+ $$PWD/qshadergraph_p.h \
+ $$PWD/qshaderlanguage_p.h \
+ $$PWD/qshadernode_p.h \
+ $$PWD/qshadernodeport_p.h \
+ $$PWD/qshadernodesloader_p.h
+
+SOURCES += \
+ $$PWD/qshaderformat.cpp \
+ $$PWD/qshadergenerator.cpp \
+ $$PWD/qshadergraph.cpp \
+ $$PWD/qshadergraphloader.cpp \
+ $$PWD/qshaderlanguage.cpp \
+ $$PWD/qshadernode.cpp \
+ $$PWD/qshadernodeport.cpp \
+ $$PWD/qshadernodesloader.cpp
diff --git a/src/render/surfaces/surfaces.pri b/src/render/surfaces/surfaces.pri
new file mode 100644
index 000000000..0d25a36c6
--- /dev/null
+++ b/src/render/surfaces/surfaces.pri
@@ -0,0 +1,9 @@
+INCLUDEPATH += $$PWD
+
+qtConfig(vulkan) {
+HEADERS += \
+ $$PWD/vulkaninstance_p.h \
+
+SOURCES += \
+ $$PWD/vulkaninstance.cpp
+}
diff --git a/src/render/surfaces/vulkaninstance.cpp b/src/render/surfaces/vulkaninstance.cpp
new file mode 100644
index 000000000..adbeb3074
--- /dev/null
+++ b/src/render/surfaces/vulkaninstance.cpp
@@ -0,0 +1,75 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 "vulkaninstance_p.h"
+#include <QVulkanInstance>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+QVulkanInstance &staticVulkanInstance() noexcept
+{
+ static QVulkanInstance* vkInstance = []
+ {
+ QVulkanInstance* v = new QVulkanInstance;
+#ifndef Q_OS_ANDROID
+ v->setLayers({ "VK_LAYER_LUNARG_standard_validation" });
+#else
+ v->setLayers(QByteArrayList()
+ << "VK_LAYER_GOOGLE_threading"
+ << "VK_LAYER_LUNARG_parameter_validation"
+ << "VK_LAYER_LUNARG_object_tracker"
+ << "VK_LAYER_LUNARG_core_validation"
+ << "VK_LAYER_LUNARG_image"
+ << "VK_LAYER_LUNARG_swapchain"
+ << "VK_LAYER_GOOGLE_unique_objects");
+#endif
+
+ if (!v->create()) {
+ qWarning("Failed to create Vulkan instance");
+ }
+ return v;
+ }();
+ return *vkInstance;
+}
+
+} // Qt3DRender
+
+QT_END_NAMESPACE
diff --git a/src/render/surfaces/vulkaninstance_p.h b/src/render/surfaces/vulkaninstance_p.h
new file mode 100644
index 000000000..f46939ce6
--- /dev/null
+++ b/src/render/surfaces/vulkaninstance_p.h
@@ -0,0 +1,55 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 QT3DRENDER_VULKANINSTANCE_P_H
+#define QT3DRENDER_VULKANINSTANCE_P_H
+
+#include <QtGui/qtguiglobal.h>
+#include <Qt3DRender/private/qt3drender_global_p.h>
+QT_BEGIN_NAMESPACE
+#if QT_CONFIG(vulkan)
+class QVulkanInstance;
+namespace Qt3DRender {
+Q_3DRENDERSHARED_PRIVATE_EXPORT
+QVulkanInstance& staticVulkanInstance() noexcept;
+} // Qt3DRender
+#endif
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_VULKANINSTANCE_P_H
diff --git a/src/render/texture/qabstracttexture.cpp b/src/render/texture/qabstracttexture.cpp
index c8ed9186e..885f2d809 100644
--- a/src/render/texture/qabstracttexture.cpp
+++ b/src/render/texture/qabstracttexture.cpp
@@ -788,8 +788,9 @@ void QAbstractTexture::removeTextureImage(QAbstractTextureImage *textureImage)
{
Q_ASSERT(textureImage);
Q_D(QAbstractTexture);
+ if (!d->m_textureImages.removeOne(textureImage))
+ return;
d->update();
- d->m_textureImages.removeOne(textureImage);
// Remove bookkeeping connection
d->unregisterDestructionHelper(textureImage);
}
diff --git a/src/src.pro b/src/src.pro
index 047e67bf2..0a16af003 100644
--- a/src/src.pro
+++ b/src/src.pro
@@ -114,7 +114,7 @@ qtHaveModule(quick) {
SUBDIRS += src_core
-QT_FOR_CONFIG += 3dcore
+QT_FOR_CONFIG += 3dcore shadertools
include($$OUT_PWD/core/qt3dcore-config.pri)
qtConfig(qt3d-input): SUBDIRS += src_input
diff --git a/tests/auto/animation/animationutils/tst_animationutils.cpp b/tests/auto/animation/animationutils/tst_animationutils.cpp
index 8c340f56e..9692fb339 100644
--- a/tests/auto/animation/animationutils/tst_animationutils.cpp
+++ b/tests/auto/animation/animationutils/tst_animationutils.cpp
@@ -2059,6 +2059,114 @@ private Q_SLOTS:
QTest::newRow("clip1.json, elapsedTime = duration + 1, loops = 2, current_loop = 1")
<< handler << clip << animatorData << clipData;
}
+
+ {
+ handler = new Handler();
+ clip = createAnimationClipLoader(handler, QUrl("qrc:/clip1.json"));
+ const qint64 globalStartTimeNS = clip->duration();
+ const int loops = 1;
+ auto animator = createClipAnimator(handler, globalStartTimeNS, loops);
+ animator->setCurrentLoop(1);
+ clipData.currentLoop = animator->currentLoop();
+ const qint64 elapsedTimeNS = toNsecs(clip->duration() * 0.5); // +1 to ensure beyond end of clip
+
+ Clock clock;
+ clock.setPlaybackRate(-1.0);
+
+ animatorData = evaluationDataForAnimator(animator, &clock, elapsedTimeNS); // Tested elsewhere
+
+ clipData.localTime = localTimeFromElapsedTime(animatorData.currentTime,
+ animatorData.elapsedTime,
+ animatorData.playbackRate,
+ clip->duration(),
+ animatorData.loopCount,
+ clipData.currentLoop); // Tested elsewhere
+ clipData.isFinalFrame = false;
+
+ QTest::newRow("clip1.json, elapsedTime = duration / 2, loops = 1, current_loop = 1, playback_rate = -1")
+ << handler << clip << animatorData << clipData;
+ }
+
+ {
+ handler = new Handler();
+ clip = createAnimationClipLoader(handler, QUrl("qrc:/clip1.json"));
+ const qint64 globalStartTimeNS = clip->duration();
+ const int loops = 1;
+ auto animator = createClipAnimator(handler, globalStartTimeNS, loops);
+ animator->setCurrentLoop(1);
+ clipData.currentLoop = animator->currentLoop();
+ const qint64 elapsedTimeNS = toNsecs(clip->duration() + 1); // +1 to ensure beyond end of clip
+
+ Clock clock;
+ clock.setPlaybackRate(-1.0);
+
+ animatorData = evaluationDataForAnimator(animator, &clock, elapsedTimeNS); // Tested elsewhere
+
+ clipData.localTime = localTimeFromElapsedTime(animatorData.currentTime,
+ animatorData.elapsedTime,
+ animatorData.playbackRate,
+ clip->duration(),
+ animatorData.loopCount,
+ clipData.currentLoop); // Tested elsewhere
+ clipData.isFinalFrame = true;
+
+ QTest::newRow("clip1.json, elapsedTime = duration + 1, loops = 1, current_loop = 1, playback_rate = -1")
+ << handler << clip << animatorData << clipData;
+ }
+
+ {
+ handler = new Handler();
+ clip = createAnimationClipLoader(handler, QUrl("qrc:/clip1.json"));
+ const qint64 globalStartTimeNS = clip->duration();
+ const int loops = 2;
+ auto animator = createClipAnimator(handler, globalStartTimeNS, loops);
+ animator->setCurrentLoop(0);
+ clipData.currentLoop = animator->currentLoop();
+ const qint64 elapsedTimeNS = toNsecs(clip->duration() + 1); // +1 to ensure beyond end of clip
+
+ Clock clock;
+ clock.setPlaybackRate(-1.0);
+
+ animatorData = evaluationDataForAnimator(animator, &clock, elapsedTimeNS); // Tested elsewhere
+
+ clipData.localTime = localTimeFromElapsedTime(animatorData.currentTime,
+ animatorData.elapsedTime,
+ animatorData.playbackRate,
+ clip->duration(),
+ animatorData.loopCount,
+ clipData.currentLoop); // Tested elsewhere
+ clipData.isFinalFrame = true;
+
+ QTest::newRow("clip1.json, elapsedTime = duration + 1, loops = 2, current_loop = 0, playback_rate = -1")
+ << handler << clip << animatorData << clipData;
+ }
+
+ {
+ handler = new Handler();
+ clip = createAnimationClipLoader(handler, QUrl("qrc:/clip1.json"));
+ const qint64 globalStartTimeNS = clip->duration();
+ const int loops = 2;
+ auto animator = createClipAnimator(handler, globalStartTimeNS, loops);
+ animator->setCurrentLoop(1);
+ clipData.currentLoop = animator->currentLoop();
+ const qint64 elapsedTimeNS = toNsecs(clip->duration() * 2.0 + 1); // +1 to ensure beyond end of clip
+
+ Clock clock;
+ clock.setPlaybackRate(-1.0);
+
+ animatorData = evaluationDataForAnimator(animator, &clock, elapsedTimeNS); // Tested elsewhere
+
+ clipData.localTime = localTimeFromElapsedTime(animatorData.currentTime,
+ animatorData.elapsedTime,
+ animatorData.playbackRate,
+ clip->duration(),
+ animatorData.loopCount,
+ clipData.currentLoop); // Tested elsewhere
+ clipData.isFinalFrame = true;
+
+ QTest::newRow("clip1.json, elapsedTime = duration + 1, loops = 2, current_loop = 1, playback_rate = -1")
+ << handler << clip << animatorData << clipData;
+ }
}
void checkEvaluationDataForClip()
diff --git a/tests/auto/auto.pro b/tests/auto/auto.pro
index 5c1060a7c..6f46eff4b 100644
--- a/tests/auto/auto.pro
+++ b/tests/auto/auto.pro
@@ -14,5 +14,9 @@ qtConfig(qt3d-animation): SUBDIRS += animation
qtConfig(qt3d-extras): SUBDIRS += extras
qtConfig(qt3d-render) {
SUBDIRS += geometryloaders
- qtConfig(qt3d-input): SUBDIRS += quick3d
+ qtConfig(qt3d-input) {
+ qtHaveModule(quick) {
+ SUBDIRS += quick3d
+ }
+ }
}
diff --git a/tests/auto/extras/extras.pro b/tests/auto/extras/extras.pro
index e5ae28e97..a9fd78369 100644
--- a/tests/auto/extras/extras.pro
+++ b/tests/auto/extras/extras.pro
@@ -6,6 +6,10 @@ contains(QT_CONFIG, private_tests) {
qtorusgeometry \
qforwardrenderer \
qfirstpersoncameracontroller \
- qorbitcameracontroller \
+ qorbitcameracontroller
+}
+
+qtHaveModule(quick) {
+ SUBDIRS += \
qtext2dentity
}
diff --git a/tests/auto/render/commons/testrenderer.h b/tests/auto/render/commons/testrenderer.h
index 8d5d87a98..87c452ad8 100644
--- a/tests/auto/render/commons/testrenderer.h
+++ b/tests/auto/render/commons/testrenderer.h
@@ -41,7 +41,7 @@ public:
~TestRenderer();
void dumpInfo() const override {}
- API api() const override { return AbstractRenderer::OpenGL; }
+ Qt3DRender::API api() const override { return Qt3DRender::API::OpenGL; }
qint64 time() const override { return 0; }
void setTime(qint64 time) override { Q_UNUSED(time) }
void setAspect(Qt3DRender::QRenderAspect *aspect) override { m_aspect = aspect; }
@@ -85,6 +85,7 @@ public:
void resetDirty();
QVariant executeCommand(const QStringList &args) override;
QOpenGLContext *shareContext() const override;
+ const Qt3DRender::GraphicsApiFilterData *contextInfo() const override { return nullptr; }
void setOffscreenSurfaceHelper(Qt3DRender::Render::OffscreenSurfaceHelper *helper) override;
QSurfaceFormat format() override;
diff --git a/tests/auto/render/opengl/opengl.pro b/tests/auto/render/opengl/opengl.pro
index 5299ebd36..fead9b5ff 100644
--- a/tests/auto/render/opengl/opengl.pro
+++ b/tests/auto/render/opengl/opengl.pro
@@ -6,7 +6,6 @@ SUBDIRS += \
graphicshelpergl3_2 \
graphicshelpergl2 \
glshadermanager \
- materialparametergathererjob \
textures \
renderer \
renderviewutils \
@@ -16,4 +15,8 @@ SUBDIRS += \
qgraphicsutils \
computecommand
+qtHaveModule(quick) {
+ SUBDIRS += \
+ materialparametergathererjob
+}
!macos: SUBDIRS += graphicshelpergl4
diff --git a/tests/auto/render/opengl/renderer/tst_renderer.cpp b/tests/auto/render/opengl/renderer/tst_renderer.cpp
index 85582d178..93a788921 100644
--- a/tests/auto/render/opengl/renderer/tst_renderer.cpp
+++ b/tests/auto/render/opengl/renderer/tst_renderer.cpp
@@ -37,6 +37,7 @@
#include <Qt3DRender/private/viewportnode_p.h>
#include <Qt3DRender/private/offscreensurfacehelper_p.h>
#include <Qt3DRender/private/qrenderaspect_p.h>
+#include <Qt3DRender/qmaterial.h>
#include "testaspect.h"
@@ -114,6 +115,10 @@ private Q_SLOTS:
Qt3DRender::Render::ViewportNode *fgRoot = new Qt3DRender::Render::ViewportNode();
const Qt3DCore::QNodeId fgRootId = Qt3DCore::QNodeId::createId();
+ // Create fake material so that we crean materialGathererJobs
+ const Qt3DCore::QNodeId materialId = Qt3DCore::QNodeId::createId();
+ nodeManagers.materialManager()->getOrCreateResource(materialId);
+
nodeManagers.frameGraphManager()->appendNode(fgRootId, fgRoot);
settings.setActiveFrameGraphId(fgRootId);
@@ -128,16 +133,16 @@ private Q_SLOTS:
// NOTE: FilterCompatibleTechniqueJob and ShaderGathererJob cannot run because the context
// is not initialized in this test
- const int renderViewBuilderMaterialCacheJobCount = 1 + Qt3DRender::Render::OpenGL::RenderViewBuilder::optimalJobCount();
+ const int renderViewBuilderMaterialCacheJobCount = 1 + 1;
// syncMaterialGathererJob
- // n * materialGathererJob
+ // n * materialGathererJob (where n depends on the numbers of available threads and the number of materials)
const int layerCacheJobCount = 2;
// filterEntityByLayerJob,
// syncFilterEntityByLayerJob
- const int singleRenderViewCommandRebuildJobCount = 1 + Qt3DRender::Render::OpenGL::RenderViewBuilder::optimalJobCount();
+ const int singleRenderViewCommandRebuildJobCount = 1 + Qt3DRender::Render::OpenGL::RenderViewBuilder::defaultJobCount();
- const int singleRenderViewJobCount = 8 + 1 * Qt3DRender::Render::OpenGL::RenderViewBuilder::optimalJobCount();
+ const int singleRenderViewJobCount = 8 + 1 * Qt3DRender::Render::OpenGL::RenderViewBuilder::defaultJobCount();
// RenderViewBuilder renderViewJob,
// syncRenderViewInitializationJob,
// syncFrustumCullingJob,
diff --git a/tests/auto/render/opengl/renderviewbuilder/tst_renderviewbuilder.cpp b/tests/auto/render/opengl/renderviewbuilder/tst_renderviewbuilder.cpp
index 19ab51c8c..f092795e3 100644
--- a/tests/auto/render/opengl/renderviewbuilder/tst_renderviewbuilder.cpp
+++ b/tests/auto/render/opengl/renderviewbuilder/tst_renderviewbuilder.cpp
@@ -248,14 +248,15 @@ private Q_SLOTS:
QVERIFY(renderViewBuilder.filterEntityByLayerJob().isNull());
QVERIFY(renderViewBuilder.syncFilterEntityByLayerJob().isNull());
- QCOMPARE(renderViewBuilder.renderViewCommandUpdaterJobs().size(), Qt3DRender::Render::OpenGL::RenderViewBuilder::optimalJobCount());
+ QCOMPARE(renderViewBuilder.renderViewCommandUpdaterJobs().size(), Qt3DRender::Render::OpenGL::RenderViewBuilder::defaultJobCount());
QCOMPARE(renderViewBuilder.materialGathererJobs().size(), 0);
- QCOMPARE(renderViewBuilder.buildJobHierachy().size(), 8 + 1 * Qt3DRender::Render::OpenGL::RenderViewBuilder::optimalJobCount());
+ QCOMPARE(renderViewBuilder.buildJobHierachy().size(), 8 + 1 * Qt3DRender::Render::OpenGL::RenderViewBuilder::defaultJobCount());
}
{
// WHEN
Qt3DRender::Render::OpenGL::RenderViewBuilder renderViewBuilder(leafNode, 0, testAspect.renderer());
+ renderViewBuilder.setOptimalJobCount(2);
renderViewBuilder.setLayerCacheNeedsToBeRebuilt(true);
renderViewBuilder.prepareJobs();
@@ -265,22 +266,25 @@ private Q_SLOTS:
QVERIFY(!renderViewBuilder.syncFilterEntityByLayerJob().isNull());
// mark jobs dirty and recheck
- QCOMPARE(renderViewBuilder.buildJobHierachy().size(), 10 + 1 * Qt3DRender::Render::OpenGL::RenderViewBuilder::optimalJobCount());
+ QCOMPARE(renderViewBuilder.buildJobHierachy().size(), 10 + renderViewBuilder.optimalJobCount());
}
{
// WHEN
Qt3DRender::Render::OpenGL::RenderViewBuilder renderViewBuilder(leafNode, 0, testAspect.renderer());
+ renderViewBuilder.setOptimalJobCount(2);
renderViewBuilder.setMaterialGathererCacheNeedsToBeRebuilt(true);
renderViewBuilder.prepareJobs();
// THEN
QCOMPARE(renderViewBuilder.materialGathererCacheNeedsToBeRebuilt(), true);
- QCOMPARE(renderViewBuilder.materialGathererJobs().size(), Qt3DRender::Render::OpenGL::RenderViewBuilder::optimalJobCount());
+ // We have elementsPerJob = qMax(materialHandles.size() / m_optimalParallelJobCount, 1)
+ // Given we have 2 materials -> 1 element per job -> so we need 2 jobs
+ QCOMPARE(renderViewBuilder.materialGathererJobs().size(), 2);
QVERIFY(!renderViewBuilder.syncMaterialGathererJob().isNull());
// mark jobs dirty and recheck
- QCOMPARE(renderViewBuilder.buildJobHierachy().size(), 9 + 2 * Qt3DRender::Render::OpenGL::RenderViewBuilder::optimalJobCount());
+ QCOMPARE(renderViewBuilder.buildJobHierachy().size(), 13);
}
}
diff --git a/tests/auto/render/render.pro b/tests/auto/render/render.pro
index c03f93d6c..e772e3ddd 100644
--- a/tests/auto/render/render.pro
+++ b/tests/auto/render/render.pro
@@ -97,7 +97,6 @@ qtConfig(private_tests) {
qraycaster \
raycaster \
qscreenraycaster \
- raycastingjob \
qcamera \
qsetfence \
qwaitfence \
@@ -105,7 +104,8 @@ qtConfig(private_tests) {
waitfence \
qtexturedataupdate \
qshaderimage \
- shaderimage
+ shaderimage \
+ shadergraph
QT_FOR_CONFIG = 3dcore-private
# TO DO: These could be restored to be executed in all cases
@@ -116,6 +116,11 @@ qtConfig(private_tests) {
raycasting \
triangleboundingvolume \
}
+
+ qtHaveModule(quick) {
+ SUBDIRS += \
+ raycastingjob
+ }
}
# Tests related to the OpenGL renderer
@@ -124,24 +129,31 @@ QT_FOR_CONFIG += 3drender-private
qtConfig(qt3d-opengl-renderer):qtConfig(private_tests) {
SUBDIRS += \
- opengl \
- scene2d
+ opengl
qtConfig(qt3d-extras) {
+ qtHaveModule(quick) {
+ SUBDIRS += \
+ boundingsphere \
+ pickboundingvolumejob \
+ updateshaderdatatransformjob
+ }
+
SUBDIRS += \
qmaterial \
geometryloaders \
picking \
- boundingsphere \
pickboundingvolumejob \
gltfplugins \
updateshaderdatatransformjob
}
qtConfig(qt3d-input) {
- SUBDIRS += \
- qscene2d \
- scene2d
+ qtHaveModule(quick) {
+ SUBDIRS += \
+ qscene2d \
+ scene2d
+ }
}
qtConfig(qt3d-simd-avx2): SUBDIRS += alignedresourcesmanagers-avx
diff --git a/tests/auto/render/shadergraph/qshadergenerator/qshadergenerator.pro b/tests/auto/render/shadergraph/qshadergenerator/qshadergenerator.pro
new file mode 100644
index 000000000..edd0be9eb
--- /dev/null
+++ b/tests/auto/render/shadergraph/qshadergenerator/qshadergenerator.pro
@@ -0,0 +1,5 @@
+CONFIG += testcase
+QT += testlib 3drender-private
+
+SOURCES += tst_qshadergenerator.cpp
+TARGET = tst_qshadergenerator
diff --git a/tests/auto/render/shadergraph/qshadergenerator/tst_qshadergenerator.cpp b/tests/auto/render/shadergraph/qshadergenerator/tst_qshadergenerator.cpp
new file mode 100644
index 000000000..90906f4e9
--- /dev/null
+++ b/tests/auto/render/shadergraph/qshadergenerator/tst_qshadergenerator.cpp
@@ -0,0 +1,1428 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** 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/QtTest>
+
+#include <QtCore/qmetaobject.h>
+#include <Qt3DRender/private/qshadergenerator_p.h>
+#include <Qt3DRender/private/qshaderlanguage_p.h>
+
+using namespace Qt3DRender;
+namespace
+{
+ QShaderFormat createFormat(QShaderFormat::Api api, int majorVersion, int minorVersion,
+ QShaderFormat::ShaderType shaderType= QShaderFormat::Fragment)
+ {
+ auto format = QShaderFormat();
+ format.setApi(api);
+ format.setVersion(QVersionNumber(majorVersion, minorVersion));
+ format.setShaderType(shaderType);
+ return format;
+ }
+
+ QShaderNodePort createPort(QShaderNodePort::Direction portDirection, const QString &portName)
+ {
+ auto port = QShaderNodePort();
+ port.direction = portDirection;
+ port.name = portName;
+ return port;
+ }
+
+ QShaderNode createNode(const QVector<QShaderNodePort> &ports, const QStringList &layers = QStringList())
+ {
+ auto node = QShaderNode();
+ node.setUuid(QUuid::createUuid());
+ node.setLayers(layers);
+ for (const auto &port : ports)
+ node.addPort(port);
+ return node;
+ }
+
+ QShaderGraph::Edge createEdge(const QUuid &sourceUuid, const QString &sourceName,
+ const QUuid &targetUuid, const QString &targetName,
+ const QStringList &layers = QStringList())
+ {
+ auto edge = QShaderGraph::Edge();
+ edge.sourceNodeUuid = sourceUuid;
+ edge.sourcePortName = sourceName;
+ edge.targetNodeUuid = targetUuid;
+ edge.targetPortName = targetName;
+ edge.layers = layers;
+ return edge;
+ }
+
+ QShaderGraph createFragmentShaderGraph()
+ {
+ const auto openGLES2 = createFormat(QShaderFormat::OpenGLES, 2, 0);
+ const auto openGL3 = createFormat(QShaderFormat::OpenGLCoreProfile, 3, 0);
+
+ auto graph = QShaderGraph();
+
+ auto worldPosition = createNode({
+ createPort(QShaderNodePort::Output, "value")
+ });
+ worldPosition.setParameter("name", "worldPosition");
+ worldPosition.addRule(openGLES2, QShaderNode::Rule("highp vec3 $value = $name;",
+ QByteArrayList() << "varying highp vec3 $name;"));
+ worldPosition.addRule(openGL3, QShaderNode::Rule("vec3 $value = $name;",
+ QByteArrayList() << "in vec3 $name;"));
+
+ auto texture = createNode({
+ createPort(QShaderNodePort::Output, "texture")
+ });
+ texture.addRule(openGLES2, QShaderNode::Rule("sampler2D $texture = texture;",
+ QByteArrayList() << "uniform sampler2D texture;"));
+ texture.addRule(openGL3, QShaderNode::Rule("sampler2D $texture = texture;",
+ QByteArrayList() << "uniform sampler2D texture;"));
+
+ auto texCoord = createNode({
+ createPort(QShaderNodePort::Output, "texCoord")
+ });
+ texCoord.addRule(openGLES2, QShaderNode::Rule("highp vec2 $texCoord = texCoord;",
+ QByteArrayList() << "varying highp vec2 texCoord;"));
+ texCoord.addRule(openGL3, QShaderNode::Rule("vec2 $texCoord = texCoord;",
+ QByteArrayList() << "in vec2 texCoord;"));
+
+ auto lightIntensity = createNode({
+ createPort(QShaderNodePort::Output, "lightIntensity")
+ });
+ lightIntensity.addRule(openGLES2, QShaderNode::Rule("highp float $lightIntensity = lightIntensity;",
+ QByteArrayList() << "uniform highp float lightIntensity;"));
+ lightIntensity.addRule(openGL3, QShaderNode::Rule("float $lightIntensity = lightIntensity;",
+ QByteArrayList() << "uniform float lightIntensity;"));
+
+ auto exposure = createNode({
+ createPort(QShaderNodePort::Output, "exposure")
+ });
+ exposure.addRule(openGLES2, QShaderNode::Rule("highp float $exposure = exposure;",
+ QByteArrayList() << "uniform highp float exposure;"));
+ exposure.addRule(openGL3, QShaderNode::Rule("float $exposure = exposure;",
+ QByteArrayList() << "uniform float exposure;"));
+
+ auto fragColor = createNode({
+ createPort(QShaderNodePort::Input, "fragColor")
+ });
+ fragColor.addRule(openGLES2, QShaderNode::Rule("gl_fragColor = $fragColor;"));
+ fragColor.addRule(openGL3, QShaderNode::Rule("fragColor = $fragColor;",
+ QByteArrayList() << "out vec4 fragColor;"));
+
+ auto sampleTexture = createNode({
+ createPort(QShaderNodePort::Input, "sampler"),
+ createPort(QShaderNodePort::Input, "coord"),
+ createPort(QShaderNodePort::Output, "color")
+ });
+ sampleTexture.addRule(openGLES2, QShaderNode::Rule("highp vec4 $color = texture2D($sampler, $coord);"));
+ sampleTexture.addRule(openGL3, QShaderNode::Rule("vec4 $color = texture($sampler, $coord);"));
+
+ auto lightFunction = createNode({
+ createPort(QShaderNodePort::Input, "baseColor"),
+ createPort(QShaderNodePort::Input, "position"),
+ createPort(QShaderNodePort::Input, "lightIntensity"),
+ createPort(QShaderNodePort::Output, "outputColor")
+ });
+ lightFunction.addRule(openGLES2, QShaderNode::Rule("highp vec4 $outputColor = lightModel($baseColor, $position, $lightIntensity);",
+ QByteArrayList() << "#pragma include es2/lightmodel.frag.inc"));
+ lightFunction.addRule(openGL3, QShaderNode::Rule("vec4 $outputColor = lightModel($baseColor, $position, $lightIntensity);",
+ QByteArrayList() << "#pragma include gl3/lightmodel.frag.inc"));
+
+ auto exposureFunction = createNode({
+ createPort(QShaderNodePort::Input, "inputColor"),
+ createPort(QShaderNodePort::Input, "exposure"),
+ createPort(QShaderNodePort::Output, "outputColor")
+ });
+ exposureFunction.addRule(openGLES2, QShaderNode::Rule("highp vec4 $outputColor = $inputColor * pow(2.0, $exposure);"));
+ exposureFunction.addRule(openGL3, QShaderNode::Rule("vec4 $outputColor = $inputColor * pow(2.0, $exposure);"));
+
+ graph.addNode(worldPosition);
+ graph.addNode(texture);
+ graph.addNode(texCoord);
+ graph.addNode(lightIntensity);
+ graph.addNode(exposure);
+ graph.addNode(fragColor);
+ graph.addNode(sampleTexture);
+ graph.addNode(lightFunction);
+ graph.addNode(exposureFunction);
+
+ graph.addEdge(createEdge(texture.uuid(), "texture", sampleTexture.uuid(), "sampler"));
+ graph.addEdge(createEdge(texCoord.uuid(), "texCoord", sampleTexture.uuid(), "coord"));
+
+ graph.addEdge(createEdge(worldPosition.uuid(), "value", lightFunction.uuid(), "position"));
+ graph.addEdge(createEdge(sampleTexture.uuid(), "color", lightFunction.uuid(), "baseColor"));
+ graph.addEdge(createEdge(lightIntensity.uuid(), "lightIntensity", lightFunction.uuid(), "lightIntensity"));
+
+ graph.addEdge(createEdge(lightFunction.uuid(), "outputColor", exposureFunction.uuid(), "inputColor"));
+ graph.addEdge(createEdge(exposure.uuid(), "exposure", exposureFunction.uuid(), "exposure"));
+
+ graph.addEdge(createEdge(exposureFunction.uuid(), "outputColor", fragColor.uuid(), "fragColor"));
+
+ return graph;
+ }
+}
+
+class tst_QShaderGenerator : public QObject
+{
+ Q_OBJECT
+private slots:
+ void shouldHaveDefaultState();
+ void shouldGenerateShaderCode_data();
+ void shouldGenerateShaderCode();
+ void shouldGenerateVersionCommands_data();
+ void shouldGenerateVersionCommands();
+ void shouldProcessLanguageQualifierAndTypeEnums_data();
+ void shouldProcessLanguageQualifierAndTypeEnums();
+ void shouldGenerateDifferentCodeDependingOnActiveLayers();
+ void shouldUseGlobalVariableRatherThanTemporaries();
+ void shouldGenerateTemporariesWisely();
+ void shouldHandlePortNamesPrefixingOneAnother();
+ void shouldHandleNodesWithMultipleOutputPorts();
+ void shouldHandleExpressionsInInputNodes();
+};
+
+void tst_QShaderGenerator::shouldHaveDefaultState()
+{
+ // GIVEN
+ auto generator = QShaderGenerator();
+
+ // THEN
+ QVERIFY(generator.graph.nodes().isEmpty());
+ QVERIFY(generator.graph.edges().isEmpty());
+ QVERIFY(!generator.format.isValid());
+}
+
+void tst_QShaderGenerator::shouldGenerateShaderCode_data()
+{
+ QTest::addColumn<QShaderGraph>("graph");
+ QTest::addColumn<QShaderFormat>("format");
+ QTest::addColumn<QByteArray>("expectedCode");
+
+ const auto graph = createFragmentShaderGraph();
+
+ const auto openGLES2 = createFormat(QShaderFormat::OpenGLES, 2, 0);
+ const auto openGL3 = createFormat(QShaderFormat::OpenGLCoreProfile, 3, 0);
+ const auto openGL32 = createFormat(QShaderFormat::OpenGLCoreProfile, 3, 2);
+ const auto openGL4 = createFormat(QShaderFormat::OpenGLCoreProfile, 4, 0);
+
+ const auto versionGLES2 = QByteArrayList() << "#version 100" << "";
+ const auto versionGL3 = QByteArrayList() << "#version 130" << "";
+ const auto versionGL32 = QByteArrayList() << "#version 150 core" << "";
+ const auto versionGL4 = QByteArrayList() << "#version 400 core" << "";
+
+ const auto es2Code = QByteArrayList() << "varying highp vec3 worldPosition;"
+ << "uniform sampler2D texture;"
+ << "varying highp vec2 texCoord;"
+ << "uniform highp float lightIntensity;"
+ << "uniform highp float exposure;"
+ << "#pragma include es2/lightmodel.frag.inc"
+ << ""
+ << "void main()"
+ << "{"
+ << " gl_fragColor = (((((lightModel(((texture2D(texture, texCoord))), worldPosition, lightIntensity)))) * pow(2.0, exposure)));"
+ << "}"
+ << "";
+
+ const auto gl3Code = QByteArrayList() << "in vec3 worldPosition;"
+ << "uniform sampler2D texture;"
+ << "in vec2 texCoord;"
+ << "uniform float lightIntensity;"
+ << "uniform float exposure;"
+ << "out vec4 fragColor;"
+ << "#pragma include gl3/lightmodel.frag.inc"
+ << ""
+ << "void main()"
+ << "{"
+ << " fragColor = (((((lightModel(((texture(texture, texCoord))), worldPosition, lightIntensity)))) * pow(2.0, exposure)));"
+ << "}"
+ << "";
+
+ QTest::newRow("EmptyGraphAndFormat") << QShaderGraph() << QShaderFormat() << QByteArrayLiteral("\n\n\nvoid main()\n{\n}\n");
+ QTest::newRow("LightExposureGraphAndES2") << graph << openGLES2 << (versionGLES2 + es2Code).join('\n');
+ QTest::newRow("LightExposureGraphAndGL3") << graph << openGL3 << (versionGL3 + gl3Code).join('\n');
+ QTest::newRow("LightExposureGraphAndGL32") << graph << openGL32 << (versionGL32 + gl3Code).join('\n');
+ QTest::newRow("LightExposureGraphAndGL4") << graph << openGL4 << (versionGL4 + gl3Code).join('\n');
+}
+
+void tst_QShaderGenerator::shouldGenerateShaderCode()
+{
+ // GIVEN
+ QFETCH(QShaderGraph, graph);
+ QFETCH(QShaderFormat, format);
+
+ auto generator = QShaderGenerator();
+ generator.graph = graph;
+ generator.format = format;
+
+ // WHEN
+ const auto code = generator.createShaderCode();
+
+ // THEN
+ QFETCH(QByteArray, expectedCode);
+ QCOMPARE(code, expectedCode);
+}
+
+void tst_QShaderGenerator::shouldGenerateVersionCommands_data()
+{
+ QTest::addColumn<QShaderFormat>("format");
+ QTest::addColumn<QByteArray>("version");
+
+ QTest::newRow("GLES2") << createFormat(QShaderFormat::OpenGLES, 2, 0) << QByteArrayLiteral("#version 100");
+ QTest::newRow("GLES3") << createFormat(QShaderFormat::OpenGLES, 3, 0) << QByteArrayLiteral("#version 300 es");
+
+ QTest::newRow("GL20") << createFormat(QShaderFormat::OpenGLNoProfile, 2, 0) << QByteArrayLiteral("#version 110");
+ QTest::newRow("GL21") << createFormat(QShaderFormat::OpenGLNoProfile, 2, 1) << QByteArrayLiteral("#version 120");
+ QTest::newRow("GL30") << createFormat(QShaderFormat::OpenGLNoProfile, 3, 0) << QByteArrayLiteral("#version 130");
+ QTest::newRow("GL31") << createFormat(QShaderFormat::OpenGLNoProfile, 3, 1) << QByteArrayLiteral("#version 140");
+ QTest::newRow("GL32") << createFormat(QShaderFormat::OpenGLNoProfile, 3, 2) << QByteArrayLiteral("#version 150");
+ QTest::newRow("GL33") << createFormat(QShaderFormat::OpenGLNoProfile, 3, 3) << QByteArrayLiteral("#version 330");
+ QTest::newRow("GL40") << createFormat(QShaderFormat::OpenGLNoProfile, 4, 0) << QByteArrayLiteral("#version 400");
+ QTest::newRow("GL41") << createFormat(QShaderFormat::OpenGLNoProfile, 4, 1) << QByteArrayLiteral("#version 410");
+ QTest::newRow("GL42") << createFormat(QShaderFormat::OpenGLNoProfile, 4, 2) << QByteArrayLiteral("#version 420");
+ QTest::newRow("GL43") << createFormat(QShaderFormat::OpenGLNoProfile, 4, 3) << QByteArrayLiteral("#version 430");
+
+ QTest::newRow("GL20core") << createFormat(QShaderFormat::OpenGLCoreProfile, 2, 0) << QByteArrayLiteral("#version 110");
+ QTest::newRow("GL21core") << createFormat(QShaderFormat::OpenGLCoreProfile, 2, 1) << QByteArrayLiteral("#version 120");
+ QTest::newRow("GL30core") << createFormat(QShaderFormat::OpenGLCoreProfile, 3, 0) << QByteArrayLiteral("#version 130");
+ QTest::newRow("GL31core") << createFormat(QShaderFormat::OpenGLCoreProfile, 3, 1) << QByteArrayLiteral("#version 140");
+ QTest::newRow("GL32core") << createFormat(QShaderFormat::OpenGLCoreProfile, 3, 2) << QByteArrayLiteral("#version 150 core");
+ QTest::newRow("GL33core") << createFormat(QShaderFormat::OpenGLCoreProfile, 3, 3) << QByteArrayLiteral("#version 330 core");
+ QTest::newRow("GL40core") << createFormat(QShaderFormat::OpenGLCoreProfile, 4, 0) << QByteArrayLiteral("#version 400 core");
+ QTest::newRow("GL41core") << createFormat(QShaderFormat::OpenGLCoreProfile, 4, 1) << QByteArrayLiteral("#version 410 core");
+ QTest::newRow("GL42core") << createFormat(QShaderFormat::OpenGLCoreProfile, 4, 2) << QByteArrayLiteral("#version 420 core");
+ QTest::newRow("GL43core") << createFormat(QShaderFormat::OpenGLCoreProfile, 4, 3) << QByteArrayLiteral("#version 430 core");
+
+ QTest::newRow("GL20compatibility") << createFormat(QShaderFormat::OpenGLCompatibilityProfile, 2, 0) << QByteArrayLiteral("#version 110");
+ QTest::newRow("GL21compatibility") << createFormat(QShaderFormat::OpenGLCompatibilityProfile, 2, 1) << QByteArrayLiteral("#version 120");
+ QTest::newRow("GL30compatibility") << createFormat(QShaderFormat::OpenGLCompatibilityProfile, 3, 0) << QByteArrayLiteral("#version 130");
+ QTest::newRow("GL31compatibility") << createFormat(QShaderFormat::OpenGLCompatibilityProfile, 3, 1) << QByteArrayLiteral("#version 140");
+ QTest::newRow("GL32compatibility") << createFormat(QShaderFormat::OpenGLCompatibilityProfile, 3, 2) << QByteArrayLiteral("#version 150 compatibility");
+ QTest::newRow("GL33compatibility") << createFormat(QShaderFormat::OpenGLCompatibilityProfile, 3, 3) << QByteArrayLiteral("#version 330 compatibility");
+ QTest::newRow("GL40compatibility") << createFormat(QShaderFormat::OpenGLCompatibilityProfile, 4, 0) << QByteArrayLiteral("#version 400 compatibility");
+ QTest::newRow("GL41compatibility") << createFormat(QShaderFormat::OpenGLCompatibilityProfile, 4, 1) << QByteArrayLiteral("#version 410 compatibility");
+ QTest::newRow("GL42compatibility") << createFormat(QShaderFormat::OpenGLCompatibilityProfile, 4, 2) << QByteArrayLiteral("#version 420 compatibility");
+ QTest::newRow("GL43compatibility") << createFormat(QShaderFormat::OpenGLCompatibilityProfile, 4, 3) << QByteArrayLiteral("#version 430 compatibility");
+}
+
+void tst_QShaderGenerator::shouldGenerateVersionCommands()
+{
+ // GIVEN
+ QFETCH(QShaderFormat, format);
+
+ auto generator = QShaderGenerator();
+ generator.format = format;
+
+ // WHEN
+ const auto code = generator.createShaderCode();
+
+ // THEN
+ QFETCH(QByteArray, version);
+ const auto expectedCode = (QByteArrayList() << version
+ << ""
+ << ""
+ << "void main()"
+ << "{"
+ << "}"
+ << "").join('\n');
+ QCOMPARE(code, expectedCode);
+}
+
+
+namespace {
+ QString toGlsl(QShaderLanguage::StorageQualifier qualifier, const QShaderFormat &format)
+ {
+ if (format.version().majorVersion() <= 2) {
+ // Note we're assuming fragment shader only here, it'd be different
+ // values for vertex shader, will need to be fixed properly at some
+ // point but isn't necessary yet (this problem already exists in past
+ // commits anyway)
+ switch (qualifier) {
+ case QShaderLanguage::Const:
+ return "const";
+ case QShaderLanguage::Input:
+ return "varying";
+ case QShaderLanguage::BuiltIn:
+ return "//";
+ case QShaderLanguage::Output:
+ return ""; // Although fragment shaders for <=2 only have fixed outputs
+ case QShaderLanguage::Uniform:
+ return "uniform";
+ }
+ } else {
+ switch (qualifier) {
+ case QShaderLanguage::Const:
+ return "const";
+ case QShaderLanguage::Input:
+ return "in";
+ case QShaderLanguage::BuiltIn:
+ return "//";
+ case QShaderLanguage::Output:
+ return "out";
+ case QShaderLanguage::Uniform:
+ return "uniform";
+ }
+ }
+
+ Q_UNREACHABLE();
+ }
+
+ QString toGlsl(QShaderLanguage::VariableType type)
+ {
+ switch (type) {
+ case QShaderLanguage::Bool:
+ return "bool";
+ case QShaderLanguage::Int:
+ return "int";
+ case QShaderLanguage::Uint:
+ return "uint";
+ case QShaderLanguage::Float:
+ return "float";
+ case QShaderLanguage::Double:
+ return "double";
+ case QShaderLanguage::Vec2:
+ return "vec2";
+ case QShaderLanguage::Vec3:
+ return "vec3";
+ case QShaderLanguage::Vec4:
+ return "vec4";
+ case QShaderLanguage::DVec2:
+ return "dvec2";
+ case QShaderLanguage::DVec3:
+ return "dvec3";
+ case QShaderLanguage::DVec4:
+ return "dvec4";
+ case QShaderLanguage::BVec2:
+ return "bvec2";
+ case QShaderLanguage::BVec3:
+ return "bvec3";
+ case QShaderLanguage::BVec4:
+ return "bvec4";
+ case QShaderLanguage::IVec2:
+ return "ivec2";
+ case QShaderLanguage::IVec3:
+ return "ivec3";
+ case QShaderLanguage::IVec4:
+ return "ivec4";
+ case QShaderLanguage::UVec2:
+ return "uvec2";
+ case QShaderLanguage::UVec3:
+ return "uvec3";
+ case QShaderLanguage::UVec4:
+ return "uvec4";
+ case QShaderLanguage::Mat2:
+ return "mat2";
+ case QShaderLanguage::Mat3:
+ return "mat3";
+ case QShaderLanguage::Mat4:
+ return "mat4";
+ case QShaderLanguage::Mat2x2:
+ return "mat2x2";
+ case QShaderLanguage::Mat2x3:
+ return "mat2x3";
+ case QShaderLanguage::Mat2x4:
+ return "mat2x4";
+ case QShaderLanguage::Mat3x2:
+ return "mat3x2";
+ case QShaderLanguage::Mat3x3:
+ return "mat3x3";
+ case QShaderLanguage::Mat3x4:
+ return "mat3x4";
+ case QShaderLanguage::Mat4x2:
+ return "mat4x2";
+ case QShaderLanguage::Mat4x3:
+ return "mat4x3";
+ case QShaderLanguage::Mat4x4:
+ return "mat4x4";
+ case QShaderLanguage::DMat2:
+ return "dmat2";
+ case QShaderLanguage::DMat3:
+ return "dmat3";
+ case QShaderLanguage::DMat4:
+ return "dmat4";
+ case QShaderLanguage::DMat2x2:
+ return "dmat2x2";
+ case QShaderLanguage::DMat2x3:
+ return "dmat2x3";
+ case QShaderLanguage::DMat2x4:
+ return "dmat2x4";
+ case QShaderLanguage::DMat3x2:
+ return "dmat3x2";
+ case QShaderLanguage::DMat3x3:
+ return "dmat3x3";
+ case QShaderLanguage::DMat3x4:
+ return "dmat3x4";
+ case QShaderLanguage::DMat4x2:
+ return "dmat4x2";
+ case QShaderLanguage::DMat4x3:
+ return "dmat4x3";
+ case QShaderLanguage::DMat4x4:
+ return "dmat4x4";
+ case QShaderLanguage::Sampler1D:
+ return "sampler1D";
+ case QShaderLanguage::Sampler2D:
+ return "sampler2D";
+ case QShaderLanguage::Sampler3D:
+ return "sampler3D";
+ case QShaderLanguage::SamplerCube:
+ return "samplerCube";
+ case QShaderLanguage::Sampler2DRect:
+ return "sampler2DRect";
+ case QShaderLanguage::Sampler2DMs:
+ return "sampler2DMS";
+ case QShaderLanguage::SamplerBuffer:
+ return "samplerBuffer";
+ case QShaderLanguage::Sampler1DArray:
+ return "sampler1DArray";
+ case QShaderLanguage::Sampler2DArray:
+ return "sampler2DArray";
+ case QShaderLanguage::Sampler2DMsArray:
+ return "sampler2DMSArray";
+ case QShaderLanguage::SamplerCubeArray:
+ return "samplerCubeArray";
+ case QShaderLanguage::Sampler1DShadow:
+ return "sampler1DShadow";
+ case QShaderLanguage::Sampler2DShadow:
+ return "sampler2DShadow";
+ case QShaderLanguage::Sampler2DRectShadow:
+ return "sampler2DRectShadow";
+ case QShaderLanguage::Sampler1DArrayShadow:
+ return "sampler1DArrayShadow";
+ case QShaderLanguage::Sampler2DArrayShadow:
+ return "sample2DArrayShadow";
+ case QShaderLanguage::SamplerCubeShadow:
+ return "samplerCubeShadow";
+ case QShaderLanguage::SamplerCubeArrayShadow:
+ return "samplerCubeArrayShadow";
+ case QShaderLanguage::ISampler1D:
+ return "isampler1D";
+ case QShaderLanguage::ISampler2D:
+ return "isampler2D";
+ case QShaderLanguage::ISampler3D:
+ return "isampler3D";
+ case QShaderLanguage::ISamplerCube:
+ return "isamplerCube";
+ case QShaderLanguage::ISampler2DRect:
+ return "isampler2DRect";
+ case QShaderLanguage::ISampler2DMs:
+ return "isampler2DMS";
+ case QShaderLanguage::ISamplerBuffer:
+ return "isamplerBuffer";
+ case QShaderLanguage::ISampler1DArray:
+ return "isampler1DArray";
+ case QShaderLanguage::ISampler2DArray:
+ return "isampler2DArray";
+ case QShaderLanguage::ISampler2DMsArray:
+ return "isampler2DMSArray";
+ case QShaderLanguage::ISamplerCubeArray:
+ return "isamplerCubeArray";
+ case QShaderLanguage::USampler1D:
+ return "usampler1D";
+ case QShaderLanguage::USampler2D:
+ return "usampler2D";
+ case QShaderLanguage::USampler3D:
+ return "usampler3D";
+ case QShaderLanguage::USamplerCube:
+ return "usamplerCube";
+ case QShaderLanguage::USampler2DRect:
+ return "usampler2DRect";
+ case QShaderLanguage::USampler2DMs:
+ return "usampler2DMS";
+ case QShaderLanguage::USamplerBuffer:
+ return "usamplerBuffer";
+ case QShaderLanguage::USampler1DArray:
+ return "usampler1DArray";
+ case QShaderLanguage::USampler2DArray:
+ return "usampler2DArray";
+ case QShaderLanguage::USampler2DMsArray:
+ return "usampler2DMSArray";
+ case QShaderLanguage::USamplerCubeArray:
+ return "usamplerCubeArray";
+ }
+
+ Q_UNREACHABLE();
+ }
+}
+
+void tst_QShaderGenerator::shouldProcessLanguageQualifierAndTypeEnums_data()
+{
+ QTest::addColumn<QShaderGraph>("graph");
+ QTest::addColumn<QShaderFormat>("format");
+ QTest::addColumn<QByteArray>("expectedCode");
+
+ {
+ const auto es2 = createFormat(QShaderFormat::OpenGLES, 2, 0);
+ const auto es3 = createFormat(QShaderFormat::OpenGLES, 3, 0);
+ const auto gl2 = createFormat(QShaderFormat::OpenGLNoProfile, 2, 0);
+ const auto gl3 = createFormat(QShaderFormat::OpenGLCoreProfile, 3, 0);
+ const auto gl4 = createFormat(QShaderFormat::OpenGLCoreProfile, 4, 0);
+
+ const auto qualifierEnum = QMetaEnum::fromType<QShaderLanguage::StorageQualifier>();
+ const auto typeEnum = QMetaEnum::fromType<QShaderLanguage::VariableType>();
+
+ for (int qualifierIndex = 0; qualifierIndex < qualifierEnum.keyCount(); qualifierIndex++) {
+ const auto qualifierName = qualifierEnum.key(qualifierIndex);
+ const auto qualifierValue = static_cast<QShaderLanguage::StorageQualifier>(qualifierEnum.value(qualifierIndex));
+
+ for (int typeIndex = 0; typeIndex < typeEnum.keyCount(); typeIndex++) {
+ const auto typeName = typeEnum.key(typeIndex);
+ const auto typeValue = static_cast<QShaderLanguage::VariableType>(typeEnum.value(typeIndex));
+
+ auto graph = QShaderGraph();
+
+ auto worldPosition = createNode({
+ createPort(QShaderNodePort::Output, "value")
+ });
+ worldPosition.setParameter("name", "worldPosition");
+ worldPosition.setParameter("qualifier", QVariant::fromValue<QShaderLanguage::StorageQualifier>(qualifierValue));
+ worldPosition.setParameter("type", QVariant::fromValue<QShaderLanguage::VariableType>(typeValue));
+ worldPosition.addRule(es2, QShaderNode::Rule("highp $type $value = $name;",
+ QByteArrayList() << "$qualifier highp $type $name;"));
+ worldPosition.addRule(gl2, QShaderNode::Rule("$type $value = $name;",
+ QByteArrayList() << "$qualifier $type $name;"));
+ worldPosition.addRule(gl3, QShaderNode::Rule("$type $value = $name;",
+ QByteArrayList() << "$qualifier $type $name;"));
+
+ auto fragColor = createNode({
+ createPort(QShaderNodePort::Input, "fragColor")
+ });
+ fragColor.addRule(es2, QShaderNode::Rule("gl_fragColor = $fragColor;"));
+ fragColor.addRule(gl2, QShaderNode::Rule("gl_fragColor = $fragColor;"));
+ fragColor.addRule(gl3, QShaderNode::Rule("fragColor = $fragColor;",
+ QByteArrayList() << "out vec4 fragColor;"));
+
+ graph.addNode(worldPosition);
+ graph.addNode(fragColor);
+
+ graph.addEdge(createEdge(worldPosition.uuid(), "value", fragColor.uuid(), "fragColor"));
+
+ const auto gl2Code = (QByteArrayList() << "#version 110"
+ << ""
+ << QStringLiteral("%1 %2 worldPosition;").arg(toGlsl(qualifierValue, gl2))
+ .arg(toGlsl(typeValue))
+ .toUtf8()
+ << ""
+ << "void main()"
+ << "{"
+ << " gl_fragColor = worldPosition;"
+ << "}"
+ << "").join("\n");
+ const auto gl3Code = (QByteArrayList() << "#version 130"
+ << ""
+ << QStringLiteral("%1 %2 worldPosition;").arg(toGlsl(qualifierValue, gl3))
+ .arg(toGlsl(typeValue))
+ .toUtf8()
+ << "out vec4 fragColor;"
+ << ""
+ << "void main()"
+ << "{"
+ << " fragColor = worldPosition;"
+ << "}"
+ << "").join("\n");
+ const auto gl4Code = (QByteArrayList() << "#version 400 core"
+ << ""
+ << QStringLiteral("%1 %2 worldPosition;").arg(toGlsl(qualifierValue, gl4))
+ .arg(toGlsl(typeValue))
+ .toUtf8()
+ << "out vec4 fragColor;"
+ << ""
+ << "void main()"
+ << "{"
+ << " fragColor = worldPosition;"
+ << "}"
+ << "").join("\n");
+ const auto es2Code = (QByteArrayList() << "#version 100"
+ << ""
+ << QStringLiteral("%1 highp %2 worldPosition;").arg(toGlsl(qualifierValue, es2))
+ .arg(toGlsl(typeValue))
+ .toUtf8()
+ << ""
+ << "void main()"
+ << "{"
+ << " gl_fragColor = worldPosition;"
+ << "}"
+ << "").join("\n");
+ const auto es3Code = (QByteArrayList() << "#version 300 es"
+ << ""
+ << QStringLiteral("%1 highp %2 worldPosition;").arg(toGlsl(qualifierValue, es3))
+ .arg(toGlsl(typeValue))
+ .toUtf8()
+ << ""
+ << "void main()"
+ << "{"
+ << " gl_fragColor = worldPosition;"
+ << "}"
+ << "").join("\n");
+
+ QTest::addRow("%s %s ES2", qualifierName, typeName) << graph << es2 << es2Code;
+ QTest::addRow("%s %s ES3", qualifierName, typeName) << graph << es3 << es3Code;
+ QTest::addRow("%s %s GL2", qualifierName, typeName) << graph << gl2 << gl2Code;
+ QTest::addRow("%s %s GL3", qualifierName, typeName) << graph << gl3 << gl3Code;
+ QTest::addRow("%s %s GL4", qualifierName, typeName) << graph << gl4 << gl4Code;
+ }
+ }
+ }
+
+ {
+ const auto es2 = createFormat(QShaderFormat::OpenGLES, 2, 0, QShaderFormat::Vertex);
+ const auto es3 = createFormat(QShaderFormat::OpenGLES, 3, 0, QShaderFormat::Vertex);
+ const auto gl2 = createFormat(QShaderFormat::OpenGLNoProfile, 2, 0, QShaderFormat::Vertex);
+ const auto gl3 = createFormat(QShaderFormat::OpenGLCoreProfile, 3, 0, QShaderFormat::Vertex);
+ const auto gl4 = createFormat(QShaderFormat::OpenGLCoreProfile, 4, 0, QShaderFormat::Vertex);
+
+ auto graph = QShaderGraph();
+
+ auto vertexPosition = createNode({
+ createPort(QShaderNodePort::Output, "value")
+ });
+ vertexPosition.setParameter("name", "vertexPosition");
+ vertexPosition.setParameter("qualifier", QVariant::fromValue<QShaderLanguage::StorageQualifier>(QShaderLanguage::Input));
+ vertexPosition.setParameter("type", QVariant::fromValue<QShaderLanguage::VariableType>(QShaderLanguage::Vec4));
+
+ vertexPosition.addRule(es2, QShaderNode::Rule("",
+ QByteArrayList() << "$qualifier highp $type $name;"));
+ vertexPosition.addRule(gl2, QShaderNode::Rule("",
+ QByteArrayList() << "$qualifier $type $name;"));
+ vertexPosition.addRule(gl3, QShaderNode::Rule("",
+ QByteArrayList() << "$qualifier $type $name;"));
+
+ graph.addNode(vertexPosition);
+
+ const auto gl2Code = (QByteArrayList() << "#version 110"
+ << ""
+ << "attribute vec4 vertexPosition;"
+ << ""
+ << "void main()"
+ << "{"
+ << "}"
+ << "").join("\n");
+ const auto gl3Code = (QByteArrayList() << "#version 130"
+ << ""
+ << "in vec4 vertexPosition;"
+ << ""
+ << "void main()"
+ << "{"
+ << "}"
+ << "").join("\n");
+ const auto gl4Code = (QByteArrayList() << "#version 400 core"
+ << ""
+ << "in vec4 vertexPosition;"
+ << ""
+ << "void main()"
+ << "{"
+ << "}"
+ << "").join("\n");
+ const auto es2Code = (QByteArrayList() << "#version 100"
+ << ""
+ << "attribute highp vec4 vertexPosition;"
+ << ""
+ << "void main()"
+ << "{"
+ << "}"
+ << "").join("\n");
+ const auto es3Code = (QByteArrayList() << "#version 300 es"
+ << ""
+ << "in highp vec4 vertexPosition;"
+ << ""
+ << "void main()"
+ << "{"
+ << "}"
+ << "").join("\n");
+
+ QTest::addRow("Attribute header substitution ES2") << graph << es2 << es2Code;
+ QTest::addRow("Attribute header substitution ES3") << graph << es3 << es3Code;
+ QTest::addRow("Attribute header substitution GL2") << graph << gl2 << gl2Code;
+ QTest::addRow("Attribute header substitution GL3") << graph << gl3 << gl3Code;
+ QTest::addRow("Attribute header substitution GL4") << graph << gl4 << gl4Code;
+ }
+}
+
+void tst_QShaderGenerator::shouldProcessLanguageQualifierAndTypeEnums()
+{
+ // GIVEN
+ QFETCH(QShaderGraph, graph);
+ QFETCH(QShaderFormat, format);
+
+ auto generator = QShaderGenerator();
+ generator.graph = graph;
+ generator.format = format;
+
+ // WHEN
+ const auto code = generator.createShaderCode();
+
+ // THEN
+ QFETCH(QByteArray, expectedCode);
+ QCOMPARE(code, expectedCode);
+}
+
+void tst_QShaderGenerator::shouldGenerateDifferentCodeDependingOnActiveLayers()
+{
+ // GIVEN
+ const auto gl4 = createFormat(QShaderFormat::OpenGLCoreProfile, 4, 0);
+
+ auto texCoord = createNode({
+ createPort(QShaderNodePort::Output, "texCoord")
+ }, {
+ "diffuseTexture",
+ "normalTexture"
+ });
+ texCoord.addRule(gl4, QShaderNode::Rule("vec2 $texCoord = texCoord;",
+ QByteArrayList() << "in vec2 texCoord;"));
+ auto diffuseUniform = createNode({
+ createPort(QShaderNodePort::Output, "color")
+ }, {"diffuseUniform"});
+ diffuseUniform.addRule(gl4, QShaderNode::Rule("vec4 $color = diffuseUniform;",
+ QByteArrayList() << "uniform vec4 diffuseUniform;"));
+ auto diffuseTexture = createNode({
+ createPort(QShaderNodePort::Input, "coord"),
+ createPort(QShaderNodePort::Output, "color")
+ }, {"diffuseTexture"});
+ diffuseTexture.addRule(gl4, QShaderNode::Rule("vec4 $color = texture2D(diffuseTexture, $coord);",
+ QByteArrayList() << "uniform sampler2D diffuseTexture;"));
+ auto normalUniform = createNode({
+ createPort(QShaderNodePort::Output, "normal")
+ }, {"normalUniform"});
+ normalUniform.addRule(gl4, QShaderNode::Rule("vec3 $normal = normalUniform;",
+ QByteArrayList() << "uniform vec3 normalUniform;"));
+ auto normalTexture = createNode({
+ createPort(QShaderNodePort::Input, "coord"),
+ createPort(QShaderNodePort::Output, "normal")
+ }, {"normalTexture"});
+ normalTexture.addRule(gl4, QShaderNode::Rule("vec3 $normal = texture2D(normalTexture, $coord).rgb;",
+ QByteArrayList() << "uniform sampler2D normalTexture;"));
+ auto lightFunction = createNode({
+ createPort(QShaderNodePort::Input, "color"),
+ createPort(QShaderNodePort::Input, "normal"),
+ createPort(QShaderNodePort::Output, "output")
+ });
+ lightFunction.addRule(gl4, QShaderNode::Rule("vec4 $output = lightModel($color, $normal);",
+ QByteArrayList() << "#pragma include gl4/lightmodel.frag.inc"));
+ auto fragColor = createNode({
+ createPort(QShaderNodePort::Input, "fragColor")
+ });
+ fragColor.addRule(gl4, QShaderNode::Rule("fragColor = $fragColor;",
+ QByteArrayList() << "out vec4 fragColor;"));
+
+ const auto graph = [=] {
+ auto res = QShaderGraph();
+
+ res.addNode(texCoord);
+ res.addNode(diffuseUniform);
+ res.addNode(diffuseTexture);
+ res.addNode(normalUniform);
+ res.addNode(normalTexture);
+ res.addNode(lightFunction);
+ res.addNode(fragColor);
+
+ res.addEdge(createEdge(diffuseUniform.uuid(), "color", lightFunction.uuid(), "color", {"diffuseUniform"}));
+ res.addEdge(createEdge(texCoord.uuid(), "texCoord", diffuseTexture.uuid(), "coord", {"diffuseTexture"}));
+ res.addEdge(createEdge(diffuseTexture.uuid(), "color", lightFunction.uuid(), "color", {"diffuseTexture"}));
+
+ res.addEdge(createEdge(normalUniform.uuid(), "normal", lightFunction.uuid(), "normal", {"normalUniform"}));
+ res.addEdge(createEdge(texCoord.uuid(), "texCoord", normalTexture.uuid(), "coord", {"normalTexture"}));
+ res.addEdge(createEdge(normalTexture.uuid(), "normal", lightFunction.uuid(), "normal", {"normalTexture"}));
+
+ res.addEdge(createEdge(lightFunction.uuid(), "output", fragColor.uuid(), "fragColor"));
+
+ return res;
+ }();
+
+ auto generator = QShaderGenerator();
+ generator.graph = graph;
+ generator.format = gl4;
+
+ {
+ // WHEN
+ const auto code = generator.createShaderCode({"diffuseUniform", "normalUniform"});
+
+ // THEN
+ const auto expected = QByteArrayList()
+ << "#version 400 core"
+ << ""
+ << "uniform vec4 diffuseUniform;"
+ << "uniform vec3 normalUniform;"
+ << "#pragma include gl4/lightmodel.frag.inc"
+ << "out vec4 fragColor;"
+ << ""
+ << "void main()"
+ << "{"
+ << " fragColor = ((lightModel(diffuseUniform, normalUniform)));"
+ << "}"
+ << "";
+ QCOMPARE(code, expected.join("\n"));
+ }
+
+ {
+ // WHEN
+ const auto code = generator.createShaderCode({"diffuseUniform", "normalTexture"});
+
+ // THEN
+ const auto expected = QByteArrayList()
+ << "#version 400 core"
+ << ""
+ << "in vec2 texCoord;"
+ << "uniform vec4 diffuseUniform;"
+ << "uniform sampler2D normalTexture;"
+ << "#pragma include gl4/lightmodel.frag.inc"
+ << "out vec4 fragColor;"
+ << ""
+ << "void main()"
+ << "{"
+ << " fragColor = ((lightModel(diffuseUniform, texture2D(normalTexture, texCoord).rgb)));"
+ << "}"
+ << "";
+ QCOMPARE(code, expected.join("\n"));
+ }
+
+ {
+ // WHEN
+ const auto code = generator.createShaderCode({"diffuseTexture", "normalUniform"});
+
+ // THEN
+ const auto expected = QByteArrayList()
+ << "#version 400 core"
+ << ""
+ << "in vec2 texCoord;"
+ << "uniform sampler2D diffuseTexture;"
+ << "uniform vec3 normalUniform;"
+ << "#pragma include gl4/lightmodel.frag.inc"
+ << "out vec4 fragColor;"
+ << ""
+ << "void main()"
+ << "{"
+ << " fragColor = ((lightModel(texture2D(diffuseTexture, texCoord), normalUniform)));"
+ << "}"
+ << "";
+ QCOMPARE(code, expected.join("\n"));
+ }
+
+ {
+ // WHEN
+ const auto code = generator.createShaderCode({"diffuseTexture", "normalTexture"});
+
+ // THEN
+ const auto expected = QByteArrayList()
+ << "#version 400 core"
+ << ""
+ << "in vec2 texCoord;"
+ << "uniform sampler2D diffuseTexture;"
+ << "uniform sampler2D normalTexture;"
+ << "#pragma include gl4/lightmodel.frag.inc"
+ << "out vec4 fragColor;"
+ << ""
+ << "void main()"
+ << "{"
+ << " fragColor = ((lightModel(texture2D(diffuseTexture, texCoord), texture2D(normalTexture, texCoord).rgb)));"
+ << "}"
+ << "";
+ QCOMPARE(code, expected.join("\n"));
+ }
+}
+
+void tst_QShaderGenerator::shouldUseGlobalVariableRatherThanTemporaries()
+{
+ // GIVEN
+ const auto gl4 = createFormat(QShaderFormat::OpenGLCoreProfile, 4, 0);
+
+ {
+ // WHEN
+ auto vertexPosition = createNode({
+ createPort(QShaderNodePort::Output, "vertexPosition")
+ });
+ vertexPosition.addRule(gl4, QShaderNode::Rule("vec4 $vertexPosition = vertexPosition;",
+ QByteArrayList() << "in vec4 vertexPosition;"));
+
+ auto fakeMultiPlyNoSpace = createNode({
+ createPort(QShaderNodePort::Input, "varName"),
+ createPort(QShaderNodePort::Output, "out")
+ });
+ fakeMultiPlyNoSpace.addRule(gl4, QShaderNode::Rule("vec4 $out = $varName*speed;"));
+
+ auto fakeMultiPlySpace = createNode({
+ createPort(QShaderNodePort::Input, "varName"),
+ createPort(QShaderNodePort::Output, "out")
+ });
+ fakeMultiPlySpace.addRule(gl4, QShaderNode::Rule("vec4 $out = $varName * speed;"));
+
+ auto fakeJoinNoSpace = createNode({
+ createPort(QShaderNodePort::Input, "varName"),
+ createPort(QShaderNodePort::Output, "out")
+ });
+ fakeJoinNoSpace.addRule(gl4, QShaderNode::Rule("vec4 $out = vec4($varName.xyz,$varName.w);"));
+
+ auto fakeJoinSpace = createNode({
+ createPort(QShaderNodePort::Input, "varName"),
+ createPort(QShaderNodePort::Output, "out")
+ });
+ fakeJoinSpace.addRule(gl4, QShaderNode::Rule("vec4 $out = vec4($varName.xyz, $varName.w);"));
+
+ auto fakeAdd = createNode({
+ createPort(QShaderNodePort::Input, "varName"),
+ createPort(QShaderNodePort::Output, "out")
+ });
+ fakeAdd.addRule(gl4, QShaderNode::Rule("vec4 $out = $varName.xyzw + $varName;"));
+
+ auto fakeSub = createNode({
+ createPort(QShaderNodePort::Input, "varName"),
+ createPort(QShaderNodePort::Output, "out")
+ });
+ fakeSub.addRule(gl4, QShaderNode::Rule("vec4 $out = $varName.xyzw - $varName;"));
+
+ auto fakeDiv = createNode({
+ createPort(QShaderNodePort::Input, "varName"),
+ createPort(QShaderNodePort::Output, "out")
+ });
+ fakeDiv.addRule(gl4, QShaderNode::Rule("vec4 $out = $varName / v0;"));
+
+ auto fragColor = createNode({
+ createPort(QShaderNodePort::Input, "input1"),
+ createPort(QShaderNodePort::Input, "input2"),
+ createPort(QShaderNodePort::Input, "input3"),
+ createPort(QShaderNodePort::Input, "input4"),
+ createPort(QShaderNodePort::Input, "input5"),
+ createPort(QShaderNodePort::Input, "input6"),
+ createPort(QShaderNodePort::Input, "input7")
+ });
+ fragColor.addRule(gl4, QShaderNode::Rule("fragColor = $input1 + $input2 + $input3 + $input4 + $input5 + $input6 + $input7;",
+ QByteArrayList() << "out vec4 fragColor;"));
+
+ const auto graph = [=] {
+ auto res = QShaderGraph();
+
+ res.addNode(vertexPosition);
+ res.addNode(fakeMultiPlyNoSpace);
+ res.addNode(fakeMultiPlySpace);
+ res.addNode(fakeJoinNoSpace);
+ res.addNode(fakeJoinSpace);
+ res.addNode(fakeAdd);
+ res.addNode(fakeSub);
+ res.addNode(fakeDiv);
+ res.addNode(fragColor);
+
+ res.addEdge(createEdge(vertexPosition.uuid(), "vertexPosition", fakeMultiPlyNoSpace.uuid(), "varName"));
+ res.addEdge(createEdge(vertexPosition.uuid(), "vertexPosition", fakeMultiPlySpace.uuid(), "varName"));
+ res.addEdge(createEdge(vertexPosition.uuid(), "vertexPosition", fakeJoinNoSpace.uuid(), "varName"));
+ res.addEdge(createEdge(vertexPosition.uuid(), "vertexPosition", fakeJoinSpace.uuid(), "varName"));
+ res.addEdge(createEdge(vertexPosition.uuid(), "vertexPosition", fakeAdd.uuid(), "varName"));
+ res.addEdge(createEdge(vertexPosition.uuid(), "vertexPosition", fakeSub.uuid(), "varName"));
+ res.addEdge(createEdge(vertexPosition.uuid(), "vertexPosition", fakeDiv.uuid(), "varName"));
+ res.addEdge(createEdge(fakeMultiPlyNoSpace.uuid(), "out", fragColor.uuid(), "input1"));
+ res.addEdge(createEdge(fakeMultiPlySpace.uuid(), "out", fragColor.uuid(), "input2"));
+ res.addEdge(createEdge(fakeJoinNoSpace.uuid(), "out", fragColor.uuid(), "input3"));
+ res.addEdge(createEdge(fakeJoinSpace.uuid(), "out", fragColor.uuid(), "input4"));
+ res.addEdge(createEdge(fakeAdd.uuid(), "out", fragColor.uuid(), "input5"));
+ res.addEdge(createEdge(fakeSub.uuid(), "out", fragColor.uuid(), "input6"));
+ res.addEdge(createEdge(fakeDiv.uuid(), "out", fragColor.uuid(), "input7"));
+
+ return res;
+ }();
+
+ auto generator = QShaderGenerator();
+ generator.graph = graph;
+ generator.format = gl4;
+
+ const auto code = generator.createShaderCode({"diffuseUniform", "normalUniform"});
+
+ // THEN
+ const auto expected = QByteArrayList()
+ << "#version 400 core"
+ << ""
+ << "in vec4 vertexPosition;"
+ << "out vec4 fragColor;"
+ << ""
+ << "void main()"
+ << "{"
+ << " fragColor = (((((((vertexPosition*speed + vertexPosition * speed + ((vec4(vertexPosition.xyz,vertexPosition.w))) + ((vec4(vertexPosition.xyz, vertexPosition.w))) + ((vertexPosition.xyzw + vertexPosition)) + ((vertexPosition.xyzw - vertexPosition)) + ((vertexPosition / vertexPosition)))))))));"
+ << "}"
+ << "";
+ QCOMPARE(code, expected.join("\n"));
+ }
+}
+
+void tst_QShaderGenerator::shouldGenerateTemporariesWisely()
+{
+ // GIVEN
+ const auto gl4 = createFormat(QShaderFormat::OpenGLCoreProfile, 4, 0);
+
+ {
+ auto attribute = createNode({
+ createPort(QShaderNodePort::Output, "vertexPosition")
+ });
+ attribute.addRule(gl4, QShaderNode::Rule("vec4 $vertexPosition = vertexPosition;",
+ QByteArrayList() << "in vec4 vertexPosition;"));
+
+ auto complexFunction = createNode({
+ createPort(QShaderNodePort::Input, "inputVarName"),
+ createPort(QShaderNodePort::Output, "out")
+ });
+ complexFunction.addRule(gl4, QShaderNode::Rule("vec4 $out = $inputVarName * 2.0;"));
+
+ auto complexFunction2 = createNode({
+ createPort(QShaderNodePort::Input, "inputVarName"),
+ createPort(QShaderNodePort::Output, "out")
+ });
+ complexFunction2.addRule(gl4, QShaderNode::Rule("vec4 $out = $inputVarName * 4.0;"));
+
+ auto complexFunction3 = createNode({
+ createPort(QShaderNodePort::Input, "a"),
+ createPort(QShaderNodePort::Input, "b"),
+ createPort(QShaderNodePort::Output, "out")
+ });
+ complexFunction3.addRule(gl4, QShaderNode::Rule("vec4 $out = $a + $b;"));
+
+ auto shaderOutput1 = createNode({
+ createPort(QShaderNodePort::Input, "input")
+ });
+
+ shaderOutput1.addRule(gl4, QShaderNode::Rule("shaderOutput1 = $input;",
+ QByteArrayList() << "out vec4 shaderOutput1;"));
+
+ auto shaderOutput2 = createNode({
+ createPort(QShaderNodePort::Input, "input")
+ });
+
+ shaderOutput2.addRule(gl4, QShaderNode::Rule("shaderOutput2 = $input;",
+ QByteArrayList() << "out vec4 shaderOutput2;"));
+
+ {
+ // WHEN
+ const auto graph = [=] {
+ auto res = QShaderGraph();
+
+ res.addNode(attribute);
+ res.addNode(complexFunction);
+ res.addNode(shaderOutput1);
+
+ res.addEdge(createEdge(attribute.uuid(), "vertexPosition", complexFunction.uuid(), "inputVarName"));
+ res.addEdge(createEdge(complexFunction.uuid(), "out", shaderOutput1.uuid(), "input"));
+
+ return res;
+ }();
+
+ auto generator = QShaderGenerator();
+ generator.graph = graph;
+ generator.format = gl4;
+
+ const auto code = generator.createShaderCode();
+
+ // THEN
+ const auto expected = QByteArrayList()
+ << "#version 400 core"
+ << ""
+ << "in vec4 vertexPosition;"
+ << "out vec4 shaderOutput1;"
+ << ""
+ << "void main()"
+ << "{"
+ << " shaderOutput1 = vertexPosition * 2.0;"
+ << "}"
+ << "";
+ QCOMPARE(code, expected.join("\n"));
+ }
+
+ {
+ // WHEN
+ const auto graph = [=] {
+ auto res = QShaderGraph();
+
+ res.addNode(attribute);
+ res.addNode(complexFunction);
+ res.addNode(shaderOutput1);
+ res.addNode(shaderOutput2);
+
+ res.addEdge(createEdge(attribute.uuid(), "vertexPosition", complexFunction.uuid(), "inputVarName"));
+ res.addEdge(createEdge(complexFunction.uuid(), "out", shaderOutput1.uuid(), "input"));
+ res.addEdge(createEdge(complexFunction.uuid(), "out", shaderOutput2.uuid(), "input"));
+
+ return res;
+ }();
+
+ auto generator = QShaderGenerator();
+ generator.graph = graph;
+ generator.format = gl4;
+
+ const auto code = generator.createShaderCode();
+
+ // THEN
+ const auto expected = QByteArrayList()
+ << "#version 400 core"
+ << ""
+ << "in vec4 vertexPosition;"
+ << "out vec4 shaderOutput1;"
+ << "out vec4 shaderOutput2;"
+ << ""
+ << "void main()"
+ << "{"
+ << " vec4 v1 = vertexPosition * 2.0;"
+ << " shaderOutput2 = v1;"
+ << " shaderOutput1 = v1;"
+ << "}"
+ << "";
+ QCOMPARE(code, expected.join("\n"));
+ }
+
+ {
+ // WHEN
+ const auto graph = [=] {
+ auto res = QShaderGraph();
+
+ res.addNode(attribute);
+ res.addNode(complexFunction);
+ res.addNode(complexFunction2);
+ res.addNode(complexFunction3);
+ res.addNode(shaderOutput1);
+ res.addNode(shaderOutput2);
+
+ res.addEdge(createEdge(attribute.uuid(), "vertexPosition", complexFunction.uuid(), "inputVarName"));
+ res.addEdge(createEdge(attribute.uuid(), "vertexPosition", complexFunction2.uuid(), "inputVarName"));
+
+ res.addEdge(createEdge(complexFunction.uuid(), "out", complexFunction3.uuid(), "a"));
+ res.addEdge(createEdge(complexFunction2.uuid(), "out", complexFunction3.uuid(), "b"));
+
+ res.addEdge(createEdge(complexFunction3.uuid(), "out", shaderOutput1.uuid(), "input"));
+ res.addEdge(createEdge(complexFunction2.uuid(), "out", shaderOutput2.uuid(), "input"));
+
+ return res;
+ }();
+
+ auto generator = QShaderGenerator();
+ generator.graph = graph;
+ generator.format = gl4;
+
+ const auto code = generator.createShaderCode();
+
+ // THEN
+ const auto expected = QByteArrayList()
+ << "#version 400 core"
+ << ""
+ << "in vec4 vertexPosition;"
+ << "out vec4 shaderOutput1;"
+ << "out vec4 shaderOutput2;"
+ << ""
+ << "void main()"
+ << "{"
+ << " vec4 v2 = vertexPosition * 4.0;"
+ << " shaderOutput2 = v2;"
+ << " shaderOutput1 = (vertexPosition * 2.0 + v2);"
+ << "}"
+ << "";
+ QCOMPARE(code, expected.join("\n"));
+ }
+ }
+}
+
+void tst_QShaderGenerator::shouldHandlePortNamesPrefixingOneAnother()
+{
+ // GIVEN
+ const auto gl4 = createFormat(QShaderFormat::OpenGLCoreProfile, 4, 0);
+
+ auto color1 = createNode({
+ createPort(QShaderNodePort::Output, "output")
+ });
+ color1.addRule(gl4, QShaderNode::Rule("vec4 $output = color1;",
+ QByteArrayList() << "in vec4 color1;"));
+
+ auto color2 = createNode({
+ createPort(QShaderNodePort::Output, "output")
+ });
+ color2.addRule(gl4, QShaderNode::Rule("vec4 $output = color2;",
+ QByteArrayList() << "in vec4 color2;"));
+
+ auto addColor = createNode({
+ createPort(QShaderNodePort::Output, "color"),
+ createPort(QShaderNodePort::Input, "color1"),
+ createPort(QShaderNodePort::Input, "color2"),
+ });
+ addColor.addRule(gl4, QShaderNode::Rule("vec4 $color = $color1 + $color2;"));
+
+ auto shaderOutput = createNode({
+ createPort(QShaderNodePort::Input, "input")
+ });
+
+ shaderOutput.addRule(gl4, QShaderNode::Rule("shaderOutput = $input;",
+ QByteArrayList() << "out vec4 shaderOutput;"));
+
+ // WHEN
+ const auto graph = [=] {
+ auto res = QShaderGraph();
+
+ res.addNode(color1);
+ res.addNode(color2);
+ res.addNode(addColor);
+ res.addNode(shaderOutput);
+
+ res.addEdge(createEdge(color1.uuid(), "output", addColor.uuid(), "color1"));
+ res.addEdge(createEdge(color2.uuid(), "output", addColor.uuid(), "color2"));
+ res.addEdge(createEdge(addColor.uuid(), "color", shaderOutput.uuid(), "input"));
+
+ return res;
+ }();
+
+ auto generator = QShaderGenerator();
+ generator.graph = graph;
+ generator.format = gl4;
+
+ const auto code = generator.createShaderCode();
+
+ // THEN
+ const auto expected = QByteArrayList()
+ << "#version 400 core"
+ << ""
+ << "in vec4 color1;"
+ << "in vec4 color2;"
+ << "out vec4 shaderOutput;"
+ << ""
+ << "void main()"
+ << "{"
+ << " shaderOutput = ((color1 + color2));"
+ << "}"
+ << "";
+ QCOMPARE(code, expected.join("\n"));
+}
+
+void tst_QShaderGenerator::shouldHandleNodesWithMultipleOutputPorts()
+{
+ // GIVEN
+ const auto gl4 = createFormat(QShaderFormat::OpenGLCoreProfile, 4, 0);
+
+ auto input = createNode({
+ createPort(QShaderNodePort::Output, "output0"),
+ createPort(QShaderNodePort::Output, "output1")
+ });
+ input.addRule(gl4, QShaderNode::Rule("vec4 $output0 = globalIn0;"
+ "float $output1 = globalIn1;",
+ QByteArrayList() << "in vec4 globalIn0;" << "in float globalIn1;"));
+
+ auto function = createNode({
+ createPort(QShaderNodePort::Input, "input0"),
+ createPort(QShaderNodePort::Input, "input1"),
+ createPort(QShaderNodePort::Output, "output0"),
+ createPort(QShaderNodePort::Output, "output1")
+ });
+ function.addRule(gl4, QShaderNode::Rule("vec4 $output0 = $input0;"
+ "float $output1 = $input1;"));
+
+ auto output = createNode({
+ createPort(QShaderNodePort::Input, "input0"),
+ createPort(QShaderNodePort::Input, "input1")
+ });
+
+ output.addRule(gl4, QShaderNode::Rule("globalOut0 = $input0;"
+ "globalOut1 = $input1;",
+ QByteArrayList() << "out vec4 globalOut0;" << "out float globalOut1;"));
+
+ // WHEN
+ const auto graph = [=] {
+ auto res = QShaderGraph();
+
+ res.addNode(input);
+ res.addNode(function);
+ res.addNode(output);
+
+ res.addEdge(createEdge(input.uuid(), "output0", function.uuid(), "input0"));
+ res.addEdge(createEdge(input.uuid(), "output1", function.uuid(), "input1"));
+
+ res.addEdge(createEdge(function.uuid(), "output0", output.uuid(), "input0"));
+ res.addEdge(createEdge(function.uuid(), "output1", output.uuid(), "input1"));
+
+ return res;
+ }();
+
+ auto generator = QShaderGenerator();
+ generator.graph = graph;
+ generator.format = gl4;
+
+ const auto code = generator.createShaderCode();
+
+ // THEN
+ const auto expected = QByteArrayList()
+ << "#version 400 core"
+ << ""
+ << "in vec4 globalIn0;"
+ << "in float globalIn1;"
+ << "out vec4 globalOut0;"
+ << "out float globalOut1;"
+ << ""
+ << "void main()"
+ << "{"
+ << " globalOut0 = globalIn0;"
+ << " globalOut1 = globalIn1;"
+ << "}"
+ << "";
+ QCOMPARE(code, expected.join("\n"));
+}
+
+void tst_QShaderGenerator::shouldHandleExpressionsInInputNodes()
+{
+ // GIVEN
+ const auto gl4 = createFormat(QShaderFormat::OpenGLCoreProfile, 4, 0);
+
+ auto input = createNode({
+ createPort(QShaderNodePort::Output, "output")
+ });
+ input.addRule(gl4, QShaderNode::Rule("float $output = 3 + 4;"));
+
+ auto output = createNode({
+ createPort(QShaderNodePort::Input, "input")
+ });
+
+ output.addRule(gl4, QShaderNode::Rule("globalOut = $input;",
+ QByteArrayList() << "out float globalOut;"));
+
+ // WHEN
+ const auto graph = [=] {
+ auto res = QShaderGraph();
+
+ res.addNode(input);
+ res.addNode(output);
+
+ res.addEdge(createEdge(input.uuid(), "output", output.uuid(), "input"));
+
+ return res;
+ }();
+
+ auto generator = QShaderGenerator();
+ generator.graph = graph;
+ generator.format = gl4;
+
+ const auto code = generator.createShaderCode();
+
+ // THEN
+ const auto expected = QByteArrayList()
+ << "#version 400 core"
+ << ""
+ << "out float globalOut;"
+ << ""
+ << "void main()"
+ << "{"
+ << " globalOut = 3 + 4;"
+ << "}"
+ << "";
+ QCOMPARE(code, expected.join("\n"));
+}
+
+QTEST_MAIN(tst_QShaderGenerator)
+
+#include "tst_qshadergenerator.moc"
diff --git a/tests/auto/render/shadergraph/qshadergraph/qshadergraph.pro b/tests/auto/render/shadergraph/qshadergraph/qshadergraph.pro
new file mode 100644
index 000000000..2348ccb24
--- /dev/null
+++ b/tests/auto/render/shadergraph/qshadergraph/qshadergraph.pro
@@ -0,0 +1,5 @@
+CONFIG += testcase
+QT += testlib 3drender-private
+
+SOURCES += tst_qshadergraph.cpp
+TARGET = tst_qshadergraph
diff --git a/tests/auto/render/shadergraph/qshadergraph/tst_qshadergraph.cpp b/tests/auto/render/shadergraph/qshadergraph/tst_qshadergraph.cpp
new file mode 100644
index 000000000..6336763f5
--- /dev/null
+++ b/tests/auto/render/shadergraph/qshadergraph/tst_qshadergraph.cpp
@@ -0,0 +1,821 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** 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/QtTest>
+
+#include <Qt3DRender/private/qshadergraph_p.h>
+
+using namespace Qt3DRender;
+namespace
+{
+ QShaderNodePort createPort(QShaderNodePort::Direction portDirection, const QString &portName)
+ {
+ auto port = QShaderNodePort();
+ port.direction = portDirection;
+ port.name = portName;
+ return port;
+ }
+
+ QShaderNode createNode(const QVector<QShaderNodePort> &ports, const QStringList &layers = QStringList())
+ {
+ auto node = QShaderNode();
+ node.setUuid(QUuid::createUuid());
+ node.setLayers(layers);
+ for (const auto &port : ports)
+ node.addPort(port);
+ return node;
+ }
+
+ QShaderGraph::Edge createEdge(const QUuid &sourceUuid, const QString &sourceName,
+ const QUuid &targetUuid, const QString &targetName,
+ const QStringList &layers = QStringList())
+ {
+ auto edge = QShaderGraph::Edge();
+ edge.sourceNodeUuid = sourceUuid;
+ edge.sourcePortName = sourceName;
+ edge.targetNodeUuid = targetUuid;
+ edge.targetPortName = targetName;
+ edge.layers = layers;
+ return edge;
+ }
+
+ QShaderGraph::Statement createStatement(const QShaderNode &node,
+ const QVector<int> &inputs = QVector<int>(),
+ const QVector<int> &outputs = QVector<int>())
+ {
+ auto statement = QShaderGraph::Statement();
+ statement.node = node;
+ statement.inputs = inputs;
+ statement.outputs = outputs;
+ return statement;
+ }
+
+ void debugStatement(const QString &prefix, const QShaderGraph::Statement &statement)
+ {
+ qDebug() << prefix << statement.inputs << statement.uuid().toString() << statement.outputs;
+ }
+
+ void dumpStatementsIfNeeded(const QVector<QShaderGraph::Statement> &statements, const QVector<QShaderGraph::Statement> &expected)
+ {
+ if (statements != expected) {
+ for (int i = 0; i < qMax(statements.size(), expected.size()); i++) {
+ qDebug() << "----" << i << "----";
+ if (i < statements.size())
+ debugStatement("A:", statements.at(i));
+ if (i < expected.size())
+ debugStatement("E:", expected.at(i));
+ qDebug() << "-----------";
+ }
+ }
+ }
+}
+
+class tst_QShaderGraph : public QObject
+{
+ Q_OBJECT
+private slots:
+ void shouldHaveEdgeDefaultState();
+ void shouldTestEdgesEquality_data();
+ void shouldTestEdgesEquality();
+ void shouldManipulateStatementMembers();
+ void shouldTestStatementsEquality_data();
+ void shouldTestStatementsEquality();
+ void shouldFindIndexFromPortNameInStatements_data();
+ void shouldFindIndexFromPortNameInStatements();
+ void shouldManageNodeList();
+ void shouldManageEdgeList();
+ void shouldSerializeGraphForCodeGeneration();
+ void shouldHandleUnboundPortsDuringGraphSerialization();
+ void shouldSurviveCyclesDuringGraphSerialization();
+ void shouldDealWithEdgesJumpingOverLayers();
+ void shouldGenerateDifferentStatementsDependingOnActiveLayers();
+ void shouldDealWithBranchesWithoutOutput();
+};
+
+void tst_QShaderGraph::shouldHaveEdgeDefaultState()
+{
+ // GIVEN
+ auto edge = QShaderGraph::Edge();
+
+ // THEN
+ QVERIFY(edge.sourceNodeUuid.isNull());
+ QVERIFY(edge.sourcePortName.isEmpty());
+ QVERIFY(edge.targetNodeUuid.isNull());
+ QVERIFY(edge.targetPortName.isEmpty());
+}
+
+void tst_QShaderGraph::shouldTestEdgesEquality_data()
+{
+ QTest::addColumn<QShaderGraph::Edge>("left");
+ QTest::addColumn<QShaderGraph::Edge>("right");
+ QTest::addColumn<bool>("expected");
+
+ const auto sourceUuid1 = QUuid::createUuid();
+ const auto sourceUuid2 = QUuid::createUuid();
+ const auto targetUuid1 = QUuid::createUuid();
+ const auto targetUuid2 = QUuid::createUuid();
+
+ QTest::newRow("Equals") << createEdge(sourceUuid1, "foo", targetUuid1, "bar")
+ << createEdge(sourceUuid1, "foo", targetUuid1, "bar")
+ << true;
+ QTest::newRow("SourceUuid") << createEdge(sourceUuid1, "foo", targetUuid1, "bar")
+ << createEdge(sourceUuid2, "foo", targetUuid1, "bar")
+ << false;
+ QTest::newRow("SourceName") << createEdge(sourceUuid1, "foo", targetUuid1, "bar")
+ << createEdge(sourceUuid1, "bleh", targetUuid1, "bar")
+ << false;
+ QTest::newRow("TargetUuid") << createEdge(sourceUuid1, "foo", targetUuid1, "bar")
+ << createEdge(sourceUuid1, "foo", targetUuid2, "bar")
+ << false;
+ QTest::newRow("TargetName") << createEdge(sourceUuid1, "foo", targetUuid1, "bar")
+ << createEdge(sourceUuid1, "foo", targetUuid1, "bleh")
+ << false;
+}
+
+void tst_QShaderGraph::shouldTestEdgesEquality()
+{
+ // GIVEN
+ QFETCH(QShaderGraph::Edge, left);
+ QFETCH(QShaderGraph::Edge, right);
+
+ // WHEN
+ const auto equal = (left == right);
+ const auto notEqual = (left != right);
+
+ // THEN
+ QFETCH(bool, expected);
+ QCOMPARE(equal, expected);
+ QCOMPARE(notEqual, !expected);
+}
+
+void tst_QShaderGraph::shouldManipulateStatementMembers()
+{
+ // GIVEN
+ auto statement = QShaderGraph::Statement();
+
+ // THEN (default state)
+ QVERIFY(statement.inputs.isEmpty());
+ QVERIFY(statement.outputs.isEmpty());
+ QVERIFY(statement.node.uuid().isNull());
+ QVERIFY(statement.uuid().isNull());
+
+ // WHEN
+ const auto node = createNode({});
+ statement.node = node;
+
+ // THEN
+ QCOMPARE(statement.uuid(), node.uuid());
+
+ // WHEN
+ statement.node = QShaderNode();
+
+ // THEN
+ QVERIFY(statement.uuid().isNull());
+}
+
+void tst_QShaderGraph::shouldTestStatementsEquality_data()
+{
+ QTest::addColumn<QShaderGraph::Statement>("left");
+ QTest::addColumn<QShaderGraph::Statement>("right");
+ QTest::addColumn<bool>("expected");
+
+ const auto node1 = createNode({});
+ const auto node2 = createNode({});
+
+ QTest::newRow("EqualNodes") << createStatement(node1, {1, 2}, {3, 4})
+ << createStatement(node1, {1, 2}, {3, 4})
+ << true;
+ QTest::newRow("EqualInvalids") << createStatement(QShaderNode(), {1, 2}, {3, 4})
+ << createStatement(QShaderNode(), {1, 2}, {3, 4})
+ << true;
+ QTest::newRow("Nodes") << createStatement(node1, {1, 2}, {3, 4})
+ << createStatement(node2, {1, 2}, {3, 4})
+ << false;
+ QTest::newRow("Inputs") << createStatement(node1, {1, 2}, {3, 4})
+ << createStatement(node1, {1, 2, 0}, {3, 4})
+ << false;
+ QTest::newRow("Outputs") << createStatement(node1, {1, 2}, {3, 4})
+ << createStatement(node1, {1, 2}, {3, 0, 4})
+ << false;
+}
+
+void tst_QShaderGraph::shouldTestStatementsEquality()
+{
+ // GIVEN
+ QFETCH(QShaderGraph::Statement, left);
+ QFETCH(QShaderGraph::Statement, right);
+
+ // WHEN
+ const auto equal = (left == right);
+ const auto notEqual = (left != right);
+
+ // THEN
+ QFETCH(bool, expected);
+ QCOMPARE(equal, expected);
+ QCOMPARE(notEqual, !expected);
+}
+
+void tst_QShaderGraph::shouldFindIndexFromPortNameInStatements_data()
+{
+ QTest::addColumn<QShaderGraph::Statement>("statement");
+ QTest::addColumn<QString>("portName");
+ QTest::addColumn<int>("expectedInputIndex");
+ QTest::addColumn<int>("expectedOutputIndex");
+
+ const auto inputNodeStatement = createStatement(createNode({
+ createPort(QShaderNodePort::Output, "input")
+ }));
+ const auto outputNodeStatement = createStatement(createNode({
+ createPort(QShaderNodePort::Input, "output")
+ }));
+ const auto functionNodeStatement = createStatement(createNode({
+ createPort(QShaderNodePort::Input, "input1"),
+ createPort(QShaderNodePort::Output, "output1"),
+ createPort(QShaderNodePort::Input, "input2"),
+ createPort(QShaderNodePort::Output, "output2"),
+ createPort(QShaderNodePort::Output, "output3"),
+ createPort(QShaderNodePort::Input, "input3")
+ }));
+
+ QTest::newRow("Invalid") << QShaderGraph::Statement() << "foo" << -1 << -1;
+ QTest::newRow("InputNodeWrongName") << inputNodeStatement << "foo" << -1 << -1;
+ QTest::newRow("InputNodeExistingName") << inputNodeStatement << "input" << -1 << 0;
+ QTest::newRow("OutputNodeWrongName") << outputNodeStatement << "foo" << -1 << -1;
+ QTest::newRow("OutputNodeExistingName") << outputNodeStatement << "output" << 0 << -1;
+ QTest::newRow("FunctionNodeWrongName") << functionNodeStatement << "foo" << -1 << -1;
+ QTest::newRow("FunctionNodeInput1") << functionNodeStatement << "input1" << 0 << -1;
+ QTest::newRow("FunctionNodeOutput1") << functionNodeStatement << "output1" << -1 << 0;
+ QTest::newRow("FunctionNodeInput2") << functionNodeStatement << "input2" << 1 << -1;
+ QTest::newRow("FunctionNodeOutput2") << functionNodeStatement << "output2" << -1 << 1;
+ QTest::newRow("FunctionNodeInput3") << functionNodeStatement << "input3" << 2 << -1;
+ QTest::newRow("FunctionNodeOutput3") << functionNodeStatement << "output3" << -1 << 2;
+}
+
+void tst_QShaderGraph::shouldFindIndexFromPortNameInStatements()
+{
+ // GIVEN
+ QFETCH(QShaderGraph::Statement, statement);
+ QFETCH(QString, portName);
+ QFETCH(int, expectedInputIndex);
+ QFETCH(int, expectedOutputIndex);
+
+ // WHEN
+ const auto inputIndex = statement.portIndex(QShaderNodePort::Input, portName);
+ const auto outputIndex = statement.portIndex(QShaderNodePort::Output, portName);
+
+ // THEN
+ QCOMPARE(inputIndex, expectedInputIndex);
+ QCOMPARE(outputIndex, expectedOutputIndex);
+}
+
+void tst_QShaderGraph::shouldManageNodeList()
+{
+ // GIVEN
+ const auto node1 = createNode({createPort(QShaderNodePort::Output, "node1")});
+ const auto node2 = createNode({createPort(QShaderNodePort::Output, "node2")});
+
+ auto graph = QShaderGraph();
+
+ // THEN (default state)
+ QVERIFY(graph.nodes().isEmpty());
+
+ // WHEN
+ graph.addNode(node1);
+
+ // THEN
+ QCOMPARE(graph.nodes().size(), 1);
+ QCOMPARE(graph.nodes().at(0).uuid(), node1.uuid());
+ QCOMPARE(graph.nodes().at(0).ports().at(0).name, node1.ports().at(0).name);
+
+ // WHEN
+ graph.addNode(node2);
+
+ // THEN
+ QCOMPARE(graph.nodes().size(), 2);
+ QCOMPARE(graph.nodes().at(0).uuid(), node1.uuid());
+ QCOMPARE(graph.nodes().at(0).ports().at(0).name, node1.ports().at(0).name);
+ QCOMPARE(graph.nodes().at(1).uuid(), node2.uuid());
+ QCOMPARE(graph.nodes().at(1).ports().at(0).name, node2.ports().at(0).name);
+
+
+ // WHEN
+ graph.removeNode(node2);
+
+ // THEN
+ QCOMPARE(graph.nodes().size(), 1);
+ QCOMPARE(graph.nodes().at(0).uuid(), node1.uuid());
+ QCOMPARE(graph.nodes().at(0).ports().at(0).name, node1.ports().at(0).name);
+
+ // WHEN
+ graph.addNode(node2);
+
+ // THEN
+ QCOMPARE(graph.nodes().size(), 2);
+ QCOMPARE(graph.nodes().at(0).uuid(), node1.uuid());
+ QCOMPARE(graph.nodes().at(0).ports().at(0).name, node1.ports().at(0).name);
+ QCOMPARE(graph.nodes().at(1).uuid(), node2.uuid());
+ QCOMPARE(graph.nodes().at(1).ports().at(0).name, node2.ports().at(0).name);
+
+ // WHEN
+ const auto node1bis = [node1] {
+ auto res = node1;
+ auto port = res.ports().at(0);
+ port.name = QStringLiteral("node1bis");
+ res.addPort(port);
+ return res;
+ }();
+ graph.addNode(node1bis);
+
+ // THEN
+ QCOMPARE(graph.nodes().size(), 2);
+ QCOMPARE(graph.nodes().at(0).uuid(), node2.uuid());
+ QCOMPARE(graph.nodes().at(0).ports().at(0).name, node2.ports().at(0).name);
+ QCOMPARE(graph.nodes().at(1).uuid(), node1bis.uuid());
+ QCOMPARE(graph.nodes().at(1).ports().at(0).name, node1bis.ports().at(0).name);
+}
+
+void tst_QShaderGraph::shouldManageEdgeList()
+{
+ // GIVEN
+ const auto edge1 = createEdge(QUuid::createUuid(), "foo", QUuid::createUuid(), "bar");
+ const auto edge2 = createEdge(QUuid::createUuid(), "baz", QUuid::createUuid(), "boo");
+
+ auto graph = QShaderGraph();
+
+ // THEN (default state)
+ QVERIFY(graph.edges().isEmpty());
+
+ // WHEN
+ graph.addEdge(edge1);
+
+ // THEN
+ QCOMPARE(graph.edges().size(), 1);
+ QCOMPARE(graph.edges().at(0), edge1);
+
+ // WHEN
+ graph.addEdge(edge2);
+
+ // THEN
+ QCOMPARE(graph.edges().size(), 2);
+ QCOMPARE(graph.edges().at(0), edge1);
+ QCOMPARE(graph.edges().at(1), edge2);
+
+
+ // WHEN
+ graph.removeEdge(edge2);
+
+ // THEN
+ QCOMPARE(graph.edges().size(), 1);
+ QCOMPARE(graph.edges().at(0), edge1);
+
+ // WHEN
+ graph.addEdge(edge2);
+
+ // THEN
+ QCOMPARE(graph.edges().size(), 2);
+ QCOMPARE(graph.edges().at(0), edge1);
+ QCOMPARE(graph.edges().at(1), edge2);
+
+ // WHEN
+ graph.addEdge(edge1);
+
+ // THEN
+ QCOMPARE(graph.edges().size(), 2);
+ QCOMPARE(graph.edges().at(0), edge1);
+ QCOMPARE(graph.edges().at(1), edge2);
+}
+
+void tst_QShaderGraph::shouldSerializeGraphForCodeGeneration()
+{
+ // GIVEN
+ const auto input1 = createNode({
+ createPort(QShaderNodePort::Output, "input1Value")
+ });
+ const auto input2 = createNode({
+ createPort(QShaderNodePort::Output, "input2Value")
+ });
+ const auto output1 = createNode({
+ createPort(QShaderNodePort::Input, "output1Value")
+ });
+ const auto output2 = createNode({
+ createPort(QShaderNodePort::Input, "output2Value")
+ });
+ const auto function1 = createNode({
+ createPort(QShaderNodePort::Input, "function1Input"),
+ createPort(QShaderNodePort::Output, "function1Output")
+ });
+ const auto function2 = createNode({
+ createPort(QShaderNodePort::Input, "function2Input1"),
+ createPort(QShaderNodePort::Input, "function2Input2"),
+ createPort(QShaderNodePort::Output, "function2Output")
+ });
+ const auto function3 = createNode({
+ createPort(QShaderNodePort::Input, "function3Input1"),
+ createPort(QShaderNodePort::Input, "function3Input2"),
+ createPort(QShaderNodePort::Output, "function3Output1"),
+ createPort(QShaderNodePort::Output, "function3Output2")
+ });
+
+ const auto graph = [=] {
+ auto res = QShaderGraph();
+ res.addNode(input1);
+ res.addNode(input2);
+ res.addNode(output1);
+ res.addNode(output2);
+ res.addNode(function1);
+ res.addNode(function2);
+ res.addNode(function3);
+ res.addEdge(createEdge(input1.uuid(), "input1Value", function1.uuid(), "function1Input"));
+ res.addEdge(createEdge(input1.uuid(), "input1Value", function2.uuid(), "function2Input1"));
+ res.addEdge(createEdge(input2.uuid(), "input2Value", function2.uuid(), "function2Input2"));
+ res.addEdge(createEdge(function1.uuid(), "function1Output", function3.uuid(), "function3Input1"));
+ res.addEdge(createEdge(function2.uuid(), "function2Output", function3.uuid(), "function3Input2"));
+ res.addEdge(createEdge(function3.uuid(), "function3Output1", output1.uuid(), "output1Value"));
+ res.addEdge(createEdge(function3.uuid(), "function3Output2", output2.uuid(), "output2Value"));
+ return res;
+ }();
+
+ // WHEN
+ const auto statements = graph.createStatements();
+
+ // THEN
+ const auto expected = QVector<QShaderGraph::Statement>()
+ << createStatement(input2, {}, {1})
+ << createStatement(input1, {}, {0})
+ << createStatement(function2, {0, 1}, {3})
+ << createStatement(function1, {0}, {2})
+ << createStatement(function3, {2, 3}, {4, 5})
+ << createStatement(output2, {5}, {})
+ << createStatement(output1, {4}, {});
+ dumpStatementsIfNeeded(statements, expected);
+ QCOMPARE(statements, expected);
+}
+
+void tst_QShaderGraph::shouldHandleUnboundPortsDuringGraphSerialization()
+{
+ // GIVEN
+ const auto input = createNode({
+ createPort(QShaderNodePort::Output, "input")
+ });
+ const auto unboundInput = createNode({
+ createPort(QShaderNodePort::Output, "unbound")
+ });
+ const auto output = createNode({
+ createPort(QShaderNodePort::Input, "output")
+ });
+ const auto unboundOutput = createNode({
+ createPort(QShaderNodePort::Input, "unbound")
+ });
+ const auto function = createNode({
+ createPort(QShaderNodePort::Input, "functionInput1"),
+ createPort(QShaderNodePort::Input, "functionInput2"),
+ createPort(QShaderNodePort::Input, "functionInput3"),
+ createPort(QShaderNodePort::Output, "functionOutput1"),
+ createPort(QShaderNodePort::Output, "functionOutput2"),
+ createPort(QShaderNodePort::Output, "functionOutput3")
+ });
+
+ const auto graph = [=] {
+ auto res = QShaderGraph();
+ res.addNode(input);
+ res.addNode(unboundInput);
+ res.addNode(output);
+ res.addNode(unboundOutput);
+ res.addNode(function);
+ res.addEdge(createEdge(input.uuid(), "input", function.uuid(), "functionInput2"));
+ res.addEdge(createEdge(function.uuid(), "functionOutput2", output.uuid(), "output"));
+ return res;
+ }();
+
+ // WHEN
+ const auto statements = graph.createStatements();
+
+ // THEN
+ // Note that no statement has any unbound input
+ const auto expected = QVector<QShaderGraph::Statement>()
+ << createStatement(input, {}, {0});
+ dumpStatementsIfNeeded(statements, expected);
+ QCOMPARE(statements, expected);
+}
+
+void tst_QShaderGraph::shouldSurviveCyclesDuringGraphSerialization()
+{
+ // GIVEN
+ const auto input = createNode({
+ createPort(QShaderNodePort::Output, "input")
+ });
+ const auto output = createNode({
+ createPort(QShaderNodePort::Input, "output")
+ });
+ const auto function1 = createNode({
+ createPort(QShaderNodePort::Input, "function1Input1"),
+ createPort(QShaderNodePort::Input, "function1Input2"),
+ createPort(QShaderNodePort::Output, "function1Output")
+ });
+ const auto function2 = createNode({
+ createPort(QShaderNodePort::Input, "function2Input"),
+ createPort(QShaderNodePort::Output, "function2Output")
+ });
+ const auto function3 = createNode({
+ createPort(QShaderNodePort::Input, "function3Input"),
+ createPort(QShaderNodePort::Output, "function3Output")
+ });
+
+ const auto graph = [=] {
+ auto res = QShaderGraph();
+ res.addNode(input);
+ res.addNode(output);
+ res.addNode(function1);
+ res.addNode(function2);
+ res.addNode(function3);
+ res.addEdge(createEdge(input.uuid(), "input", function1.uuid(), "function1Input1"));
+ res.addEdge(createEdge(function1.uuid(), "function1Output", function2.uuid(), "function2Input"));
+ res.addEdge(createEdge(function2.uuid(), "function2Output", function3.uuid(), "function3Input"));
+ res.addEdge(createEdge(function3.uuid(), "function3Output", function1.uuid(), "function1Input2"));
+ res.addEdge(createEdge(function2.uuid(), "function2Output", output.uuid(), "output"));
+ return res;
+ }();
+
+ // WHEN
+ const auto statements = graph.createStatements();
+
+ // THEN
+ // The cycle is ignored
+ const auto expected = QVector<QShaderGraph::Statement>();
+ dumpStatementsIfNeeded(statements, expected);
+ QCOMPARE(statements, expected);
+}
+
+void tst_QShaderGraph::shouldDealWithEdgesJumpingOverLayers()
+{
+ // GIVEN
+ const auto worldPosition = createNode({
+ createPort(QShaderNodePort::Output, "worldPosition")
+ });
+ const auto texture = createNode({
+ createPort(QShaderNodePort::Output, "texture")
+ });
+ const auto texCoord = createNode({
+ createPort(QShaderNodePort::Output, "texCoord")
+ });
+ const auto lightIntensity = createNode({
+ createPort(QShaderNodePort::Output, "lightIntensity")
+ });
+ const auto exposure = createNode({
+ createPort(QShaderNodePort::Output, "exposure")
+ });
+ const auto fragColor = createNode({
+ createPort(QShaderNodePort::Input, "fragColor")
+ });
+ const auto sampleTexture = createNode({
+ createPort(QShaderNodePort::Input, "sampler"),
+ createPort(QShaderNodePort::Input, "coord"),
+ createPort(QShaderNodePort::Output, "color")
+ });
+ const auto lightFunction = createNode({
+ createPort(QShaderNodePort::Input, "baseColor"),
+ createPort(QShaderNodePort::Input, "position"),
+ createPort(QShaderNodePort::Input, "lightIntensity"),
+ createPort(QShaderNodePort::Output, "outputColor")
+ });
+ const auto exposureFunction = createNode({
+ createPort(QShaderNodePort::Input, "inputColor"),
+ createPort(QShaderNodePort::Input, "exposure"),
+ createPort(QShaderNodePort::Output, "outputColor")
+ });
+
+ const auto graph = [=] {
+ auto res = QShaderGraph();
+
+ res.addNode(worldPosition);
+ res.addNode(texture);
+ res.addNode(texCoord);
+ res.addNode(lightIntensity);
+ res.addNode(exposure);
+ res.addNode(fragColor);
+ res.addNode(sampleTexture);
+ res.addNode(lightFunction);
+ res.addNode(exposureFunction);
+
+ res.addEdge(createEdge(texture.uuid(), "texture", sampleTexture.uuid(), "sampler"));
+ res.addEdge(createEdge(texCoord.uuid(), "texCoord", sampleTexture.uuid(), "coord"));
+
+ res.addEdge(createEdge(worldPosition.uuid(), "worldPosition", lightFunction.uuid(), "position"));
+ res.addEdge(createEdge(sampleTexture.uuid(), "color", lightFunction.uuid(), "baseColor"));
+ res.addEdge(createEdge(lightIntensity.uuid(), "lightIntensity", lightFunction.uuid(), "lightIntensity"));
+
+ res.addEdge(createEdge(lightFunction.uuid(), "outputColor", exposureFunction.uuid(), "inputColor"));
+ res.addEdge(createEdge(exposure.uuid(), "exposure", exposureFunction.uuid(), "exposure"));
+
+ res.addEdge(createEdge(exposureFunction.uuid(), "outputColor", fragColor.uuid(), "fragColor"));
+
+ return res;
+ }();
+
+ // WHEN
+ const auto statements = graph.createStatements();
+
+ // THEN
+ const auto expected = QVector<QShaderGraph::Statement>()
+ << createStatement(texCoord, {}, {2})
+ << createStatement(texture, {}, {1})
+ << createStatement(lightIntensity, {}, {3})
+ << createStatement(sampleTexture, {1, 2}, {5})
+ << createStatement(worldPosition, {}, {0})
+ << createStatement(exposure, {}, {4})
+ << createStatement(lightFunction, {5, 0, 3}, {6})
+ << createStatement(exposureFunction, {6, 4}, {7})
+ << createStatement(fragColor, {7}, {});
+ dumpStatementsIfNeeded(statements, expected);
+ QCOMPARE(statements, expected);
+}
+
+void tst_QShaderGraph::shouldGenerateDifferentStatementsDependingOnActiveLayers()
+{
+ // GIVEN
+ const auto texCoord = createNode({
+ createPort(QShaderNodePort::Output, "texCoord")
+ }, {
+ "diffuseTexture",
+ "normalTexture"
+ });
+ const auto diffuseUniform = createNode({
+ createPort(QShaderNodePort::Output, "color")
+ }, {"diffuseUniform"});
+ const auto diffuseTexture = createNode({
+ createPort(QShaderNodePort::Input, "coord"),
+ createPort(QShaderNodePort::Output, "color")
+ }, {"diffuseTexture"});
+ const auto normalUniform = createNode({
+ createPort(QShaderNodePort::Output, "normal")
+ }, {"normalUniform"});
+ const auto normalTexture = createNode({
+ createPort(QShaderNodePort::Input, "coord"),
+ createPort(QShaderNodePort::Output, "normal")
+ }, {"normalTexture"});
+ const auto lightFunction = createNode({
+ createPort(QShaderNodePort::Input, "color"),
+ createPort(QShaderNodePort::Input, "normal"),
+ createPort(QShaderNodePort::Output, "output")
+ });
+ const auto fragColor = createNode({
+ createPort(QShaderNodePort::Input, "fragColor")
+ });
+
+ const auto graph = [=] {
+ auto res = QShaderGraph();
+
+ res.addNode(texCoord);
+ res.addNode(diffuseUniform);
+ res.addNode(diffuseTexture);
+ res.addNode(normalUniform);
+ res.addNode(normalTexture);
+ res.addNode(lightFunction);
+ res.addNode(fragColor);
+
+ res.addEdge(createEdge(diffuseUniform.uuid(), "color", lightFunction.uuid(), "color", {"diffuseUniform"}));
+ res.addEdge(createEdge(texCoord.uuid(), "texCoord", diffuseTexture.uuid(), "coord", {"diffuseTexture"}));
+ res.addEdge(createEdge(diffuseTexture.uuid(), "color", lightFunction.uuid(), "color", {"diffuseTexture"}));
+
+ res.addEdge(createEdge(normalUniform.uuid(), "normal", lightFunction.uuid(), "normal", {"normalUniform"}));
+ res.addEdge(createEdge(texCoord.uuid(), "texCoord", normalTexture.uuid(), "coord", {"normalTexture"}));
+ res.addEdge(createEdge(normalTexture.uuid(), "normal", lightFunction.uuid(), "normal", {"normalTexture"}));
+
+ res.addEdge(createEdge(lightFunction.uuid(), "output", fragColor.uuid(), "fragColor"));
+
+ return res;
+ }();
+
+ {
+ // WHEN
+ const auto statements = graph.createStatements({"diffuseUniform", "normalUniform"});
+
+ // THEN
+ const auto expected = QVector<QShaderGraph::Statement>()
+ << createStatement(normalUniform, {}, {1})
+ << createStatement(diffuseUniform, {}, {0})
+ << createStatement(lightFunction, {0, 1}, {2})
+ << createStatement(fragColor, {2}, {});
+ dumpStatementsIfNeeded(statements, expected);
+ QCOMPARE(statements, expected);
+ }
+
+ {
+ // WHEN
+ const auto statements = graph.createStatements({"diffuseUniform", "normalTexture"});
+
+ // THEN
+ const auto expected = QVector<QShaderGraph::Statement>()
+ << createStatement(texCoord, {}, {0})
+ << createStatement(normalTexture, {0}, {2})
+ << createStatement(diffuseUniform, {}, {1})
+ << createStatement(lightFunction, {1, 2}, {3})
+ << createStatement(fragColor, {3}, {});
+ dumpStatementsIfNeeded(statements, expected);
+ QCOMPARE(statements, expected);
+ }
+
+ {
+ // WHEN
+ const auto statements = graph.createStatements({"diffuseTexture", "normalUniform"});
+
+ // THEN
+ const auto expected = QVector<QShaderGraph::Statement>()
+ << createStatement(texCoord, {}, {0})
+ << createStatement(normalUniform, {}, {2})
+ << createStatement(diffuseTexture, {0}, {1})
+ << createStatement(lightFunction, {1, 2}, {3})
+ << createStatement(fragColor, {3}, {});
+ dumpStatementsIfNeeded(statements, expected);
+ QCOMPARE(statements, expected);
+ }
+
+ {
+ // WHEN
+ const auto statements = graph.createStatements({"diffuseTexture", "normalTexture"});
+
+ // THEN
+ const auto expected = QVector<QShaderGraph::Statement>()
+ << createStatement(texCoord, {}, {0})
+ << createStatement(normalTexture, {0}, {2})
+ << createStatement(diffuseTexture, {0}, {1})
+ << createStatement(lightFunction, {1, 2}, {3})
+ << createStatement(fragColor, {3}, {});
+ dumpStatementsIfNeeded(statements, expected);
+ QCOMPARE(statements, expected);
+ }
+}
+
+void tst_QShaderGraph::shouldDealWithBranchesWithoutOutput()
+{
+ // GIVEN
+ const auto input = createNode({
+ createPort(QShaderNodePort::Output, "input")
+ });
+ const auto output = createNode({
+ createPort(QShaderNodePort::Input, "output")
+ });
+ const auto danglingFunction = createNode({
+ createPort(QShaderNodePort::Input, "functionInput"),
+ createPort(QShaderNodePort::Output, "unbound")
+ });
+ const auto function = createNode({
+ createPort(QShaderNodePort::Input, "functionInput"),
+ createPort(QShaderNodePort::Output, "functionOutput")
+ });
+
+ const auto graph = [=] {
+ auto res = QShaderGraph();
+ res.addNode(input);
+ res.addNode(function);
+ res.addNode(danglingFunction);
+ res.addNode(output);
+ res.addEdge(createEdge(input.uuid(), "input", function.uuid(), "functionInput"));
+ res.addEdge(createEdge(input.uuid(), "input", danglingFunction.uuid(), "functionInput"));
+ res.addEdge(createEdge(function.uuid(), "functionOutput", output.uuid(), "output"));
+ return res;
+ }();
+
+ // WHEN
+ const auto statements = graph.createStatements();
+
+ // THEN
+ // Note that no edge leads to the unbound input
+ const auto expected = QVector<QShaderGraph::Statement>()
+ << createStatement(input, {}, {0})
+ << createStatement(function, {0}, {1})
+ << createStatement(output, {1}, {})
+ << createStatement(danglingFunction, {0}, {2});
+ dumpStatementsIfNeeded(statements, expected);
+ QCOMPARE(statements, expected);
+}
+
+QTEST_MAIN(tst_QShaderGraph)
+
+#include "tst_qshadergraph.moc"
diff --git a/tests/auto/render/shadergraph/qshadergraphloader/qshadergraphloader.pro b/tests/auto/render/shadergraph/qshadergraphloader/qshadergraphloader.pro
new file mode 100644
index 000000000..754f801e0
--- /dev/null
+++ b/tests/auto/render/shadergraph/qshadergraphloader/qshadergraphloader.pro
@@ -0,0 +1,5 @@
+CONFIG += testcase
+QT += testlib 3drender-private
+
+SOURCES += tst_qshadergraphloader.cpp
+TARGET = tst_qshadergraphloader
diff --git a/tests/auto/render/shadergraph/qshadergraphloader/tst_qshadergraphloader.cpp b/tests/auto/render/shadergraph/qshadergraphloader/tst_qshadergraphloader.cpp
new file mode 100644
index 000000000..25e71d50b
--- /dev/null
+++ b/tests/auto/render/shadergraph/qshadergraphloader/tst_qshadergraphloader.cpp
@@ -0,0 +1,627 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** 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/QtTest>
+
+#include <QtCore/qbuffer.h>
+
+#include <Qt3DRender/private/qshadergraphloader_p.h>
+#include <Qt3DRender/private/qshaderlanguage_p.h>
+
+using namespace Qt3DRender;
+
+using QBufferPointer = QSharedPointer<QBuffer>;
+Q_DECLARE_METATYPE(QBufferPointer);
+
+using PrototypeHash = QHash<QString, QShaderNode>;
+Q_DECLARE_METATYPE(PrototypeHash);
+
+namespace
+{
+ QBufferPointer createBuffer(const QByteArray &data, QIODevice::OpenMode openMode = QIODevice::ReadOnly)
+ {
+ auto buffer = QBufferPointer::create();
+ buffer->setData(data);
+ if (openMode != QIODevice::NotOpen)
+ buffer->open(openMode);
+ return buffer;
+ }
+
+ QShaderFormat createFormat(QShaderFormat::Api api, int majorVersion, int minorVersion)
+ {
+ auto format = QShaderFormat();
+ format.setApi(api);
+ format.setVersion(QVersionNumber(majorVersion, minorVersion));
+ return format;
+ }
+
+ QShaderNodePort createPort(QShaderNodePort::Direction portDirection, const QString &portName)
+ {
+ auto port = QShaderNodePort();
+ port.direction = portDirection;
+ port.name = portName;
+ return port;
+ }
+
+ QShaderNode createNode(const QVector<QShaderNodePort> &ports, const QStringList &layers = QStringList())
+ {
+ auto node = QShaderNode();
+ node.setUuid(QUuid::createUuid());
+ node.setLayers(layers);
+ for (const auto &port : ports)
+ node.addPort(port);
+ return node;
+ }
+
+ QShaderGraph::Edge createEdge(const QUuid &sourceUuid, const QString &sourceName,
+ const QUuid &targetUuid, const QString &targetName,
+ const QStringList &layers = QStringList())
+ {
+ auto edge = QShaderGraph::Edge();
+ edge.sourceNodeUuid = sourceUuid;
+ edge.sourcePortName = sourceName;
+ edge.targetNodeUuid = targetUuid;
+ edge.targetPortName = targetName;
+ edge.layers = layers;
+ return edge;
+ }
+
+ QShaderGraph createGraph()
+ {
+ const auto openGLES2 = createFormat(QShaderFormat::OpenGLES, 2, 0);
+ const auto openGL3 = createFormat(QShaderFormat::OpenGLCoreProfile, 3, 0);
+
+ auto graph = QShaderGraph();
+
+ auto worldPosition = createNode({
+ createPort(QShaderNodePort::Output, "value")
+ });
+ worldPosition.setUuid(QUuid("{00000000-0000-0000-0000-000000000001}"));
+ worldPosition.setParameter("name", "worldPosition");
+ worldPosition.setParameter("qualifier", QVariant::fromValue<QShaderLanguage::StorageQualifier>(QShaderLanguage::Input));
+ worldPosition.setParameter("type", QVariant::fromValue<QShaderLanguage::VariableType>(QShaderLanguage::Vec3));
+ worldPosition.addRule(openGLES2, QShaderNode::Rule("highp $type $value = $name;",
+ QByteArrayList() << "$qualifier highp $type $name;"));
+ worldPosition.addRule(openGL3, QShaderNode::Rule("$type $value = $name;",
+ QByteArrayList() << "$qualifier $type $name;"));
+
+ auto texture = createNode({
+ createPort(QShaderNodePort::Output, "texture")
+ });
+ texture.setUuid(QUuid("{00000000-0000-0000-0000-000000000002}"));
+ texture.addRule(openGLES2, QShaderNode::Rule("sampler2D $texture = texture;",
+ QByteArrayList() << "uniform sampler2D texture;"));
+ texture.addRule(openGL3, QShaderNode::Rule("sampler2D $texture = texture;",
+ QByteArrayList() << "uniform sampler2D texture;"));
+
+ auto texCoord = createNode({
+ createPort(QShaderNodePort::Output, "texCoord")
+ });
+ texCoord.setUuid(QUuid("{00000000-0000-0000-0000-000000000003}"));
+ texCoord.addRule(openGLES2, QShaderNode::Rule("highp vec2 $texCoord = texCoord;",
+ QByteArrayList() << "varying highp vec2 texCoord;"));
+ texCoord.addRule(openGL3, QShaderNode::Rule("vec2 $texCoord = texCoord;",
+ QByteArrayList() << "in vec2 texCoord;"));
+
+ auto lightIntensity = createNode({
+ createPort(QShaderNodePort::Output, "value")
+ });
+ lightIntensity.setUuid(QUuid("{00000000-0000-0000-0000-000000000004}"));
+ lightIntensity.setParameter("name", "defaultName");
+ lightIntensity.setParameter("qualifier", QVariant::fromValue<QShaderLanguage::StorageQualifier>(QShaderLanguage::Uniform));
+ lightIntensity.setParameter("type", QVariant::fromValue<QShaderLanguage::VariableType>(QShaderLanguage::Float));
+ lightIntensity.addRule(openGLES2, QShaderNode::Rule("highp $type $value = $name;",
+ QByteArrayList() << "$qualifier highp $type $name;"));
+ lightIntensity.addRule(openGL3, QShaderNode::Rule("$type $value = $name;",
+ QByteArrayList() << "$qualifier $type $name;"));
+
+ auto exposure = createNode({
+ createPort(QShaderNodePort::Output, "exposure")
+ });
+ exposure.setUuid(QUuid("{00000000-0000-0000-0000-000000000005}"));
+ exposure.addRule(openGLES2, QShaderNode::Rule("highp float $exposure = exposure;",
+ QByteArrayList() << "uniform highp float exposure;"));
+ exposure.addRule(openGL3, QShaderNode::Rule("float $exposure = exposure;",
+ QByteArrayList() << "uniform float exposure;"));
+
+ auto fragColor = createNode({
+ createPort(QShaderNodePort::Input, "fragColor")
+ });
+ fragColor.setUuid(QUuid("{00000000-0000-0000-0000-000000000006}"));
+ fragColor.addRule(openGLES2, QShaderNode::Rule("gl_fragColor = $fragColor;"));
+ fragColor.addRule(openGL3, QShaderNode::Rule("fragColor = $fragColor;",
+ QByteArrayList() << "out vec4 fragColor;"));
+
+ auto sampleTexture = createNode({
+ createPort(QShaderNodePort::Input, "sampler"),
+ createPort(QShaderNodePort::Input, "coord"),
+ createPort(QShaderNodePort::Output, "color")
+ });
+ sampleTexture.setUuid(QUuid("{00000000-0000-0000-0000-000000000007}"));
+ sampleTexture.addRule(openGLES2, QShaderNode::Rule("highp vec4 $color = texture2D($sampler, $coord);"));
+ sampleTexture.addRule(openGL3, QShaderNode::Rule("vec4 $color = texture2D($sampler, $coord);"));
+
+ auto lightFunction = createNode({
+ createPort(QShaderNodePort::Input, "baseColor"),
+ createPort(QShaderNodePort::Input, "position"),
+ createPort(QShaderNodePort::Input, "lightIntensity"),
+ createPort(QShaderNodePort::Output, "outputColor")
+ });
+ lightFunction.setUuid(QUuid("{00000000-0000-0000-0000-000000000008}"));
+ lightFunction.addRule(openGLES2, QShaderNode::Rule("highp vec4 $outputColor = lightModel($baseColor, $position, $lightIntensity);",
+ QByteArrayList() << "#pragma include es2/lightmodel.frag.inc"));
+ lightFunction.addRule(openGL3, QShaderNode::Rule("vec4 $outputColor = lightModel($baseColor, $position, $lightIntensity);",
+ QByteArrayList() << "#pragma include gl3/lightmodel.frag.inc"));
+
+ auto exposureFunction = createNode({
+ createPort(QShaderNodePort::Input, "inputColor"),
+ createPort(QShaderNodePort::Input, "exposure"),
+ createPort(QShaderNodePort::Output, "outputColor")
+ });
+ exposureFunction.setUuid(QUuid("{00000000-0000-0000-0000-000000000009}"));
+ exposureFunction.addRule(openGLES2, QShaderNode::Rule("highp vec4 $outputColor = $inputColor * pow(2.0, $exposure);"));
+ exposureFunction.addRule(openGL3, QShaderNode::Rule("vec4 $outputColor = $inputColor * pow(2.0, $exposure);"));
+
+ graph.addNode(worldPosition);
+ graph.addNode(texture);
+ graph.addNode(texCoord);
+ graph.addNode(lightIntensity);
+ graph.addNode(exposure);
+ graph.addNode(fragColor);
+ graph.addNode(sampleTexture);
+ graph.addNode(lightFunction);
+ graph.addNode(exposureFunction);
+
+ graph.addEdge(createEdge(texture.uuid(), "texture", sampleTexture.uuid(), "sampler"));
+ graph.addEdge(createEdge(texCoord.uuid(), "texCoord", sampleTexture.uuid(), "coord"));
+
+ graph.addEdge(createEdge(worldPosition.uuid(), "value", lightFunction.uuid(), "position"));
+ graph.addEdge(createEdge(sampleTexture.uuid(), "color", lightFunction.uuid(), "baseColor"));
+ graph.addEdge(createEdge(lightIntensity.uuid(), "value", lightFunction.uuid(), "lightIntensity"));
+
+ graph.addEdge(createEdge(lightFunction.uuid(), "outputColor", exposureFunction.uuid(), "inputColor"));
+ graph.addEdge(createEdge(exposure.uuid(), "exposure", exposureFunction.uuid(), "exposure"));
+
+ graph.addEdge(createEdge(exposureFunction.uuid(), "outputColor", fragColor.uuid(), "fragColor"));
+
+ return graph;
+ }
+
+ void debugStatement(const QString &prefix, const QShaderGraph::Statement &statement)
+ {
+ qDebug() << prefix << statement.inputs << statement.uuid().toString() << statement.outputs;
+ }
+
+ void dumpStatementsIfNeeded(const QVector<QShaderGraph::Statement> &statements, const QVector<QShaderGraph::Statement> &expected)
+ {
+ if (statements != expected) {
+ for (int i = 0; i < qMax(statements.size(), expected.size()); i++) {
+ qDebug() << "----" << i << "----";
+ if (i < statements.size())
+ debugStatement("A:", statements.at(i));
+ if (i < expected.size())
+ debugStatement("E:", expected.at(i));
+ qDebug() << "-----------";
+ }
+ }
+ }
+}
+
+class tst_QShaderGraphLoader : public QObject
+{
+ Q_OBJECT
+private slots:
+ void shouldManipulateLoaderMembers();
+ void shouldLoadFromJsonStream_data();
+ void shouldLoadFromJsonStream();
+};
+
+void tst_QShaderGraphLoader::shouldManipulateLoaderMembers()
+{
+ // GIVEN
+ auto loader = QShaderGraphLoader();
+
+ // THEN (default state)
+ QCOMPARE(loader.status(), QShaderGraphLoader::Null);
+ QVERIFY(!loader.device());
+ QVERIFY(loader.graph().nodes().isEmpty());
+ QVERIFY(loader.graph().edges().isEmpty());
+ QVERIFY(loader.prototypes().isEmpty());
+
+ // WHEN
+ auto device1 = createBuffer(QByteArray("..........."), QIODevice::NotOpen);
+ loader.setDevice(device1.data());
+
+ // THEN
+ QCOMPARE(loader.status(), QShaderGraphLoader::Error);
+ QCOMPARE(loader.device(), device1.data());
+ QVERIFY(loader.graph().nodes().isEmpty());
+ QVERIFY(loader.graph().edges().isEmpty());
+
+ // WHEN
+ auto device2 = createBuffer(QByteArray("..........."), QIODevice::ReadOnly);
+ loader.setDevice(device2.data());
+
+ // THEN
+ QCOMPARE(loader.status(), QShaderGraphLoader::Waiting);
+ QCOMPARE(loader.device(), device2.data());
+ QVERIFY(loader.graph().nodes().isEmpty());
+ QVERIFY(loader.graph().edges().isEmpty());
+
+
+ // WHEN
+ const auto prototypes = [this]{
+ auto res = QHash<QString, QShaderNode>();
+ res.insert("foo", createNode({}));
+ return res;
+ }();
+ loader.setPrototypes(prototypes);
+
+ // THEN
+ QCOMPARE(loader.prototypes().size(), prototypes.size());
+ QVERIFY(loader.prototypes().contains("foo"));
+ QCOMPARE(loader.prototypes().value("foo").uuid(), prototypes.value("foo").uuid());
+}
+
+void tst_QShaderGraphLoader::shouldLoadFromJsonStream_data()
+{
+ QTest::addColumn<QBufferPointer>("device");
+ QTest::addColumn<PrototypeHash>("prototypes");
+ QTest::addColumn<QShaderGraph>("graph");
+ QTest::addColumn<QShaderGraphLoader::Status>("status");
+
+ QTest::newRow("empty") << createBuffer("", QIODevice::ReadOnly) << PrototypeHash()
+ << QShaderGraph() << QShaderGraphLoader::Error;
+
+ const auto smallJson = "{"
+ " \"nodes\": ["
+ " {"
+ " \"uuid\": \"{00000000-0000-0000-0000-000000000001}\","
+ " \"type\": \"MyInput\","
+ " \"layers\": [\"foo\", \"bar\"]"
+ " },"
+ " {"
+ " \"uuid\": \"{00000000-0000-0000-0000-000000000002}\","
+ " \"type\": \"MyOutput\""
+ " },"
+ " {"
+ " \"uuid\": \"{00000000-0000-0000-0000-000000000003}\","
+ " \"type\": \"MyFunction\""
+ " }"
+ " ],"
+ " \"edges\": ["
+ " {"
+ " \"sourceUuid\": \"{00000000-0000-0000-0000-000000000001}\","
+ " \"sourcePort\": \"input\","
+ " \"targetUuid\": \"{00000000-0000-0000-0000-000000000003}\","
+ " \"targetPort\": \"functionInput\","
+ " \"layers\": [\"bar\", \"baz\"]"
+ " },"
+ " {"
+ " \"sourceUuid\": \"{00000000-0000-0000-0000-000000000003}\","
+ " \"sourcePort\": \"functionOutput\","
+ " \"targetUuid\": \"{00000000-0000-0000-0000-000000000002}\","
+ " \"targetPort\": \"output\""
+ " }"
+ " ]"
+ "}";
+
+ const auto smallProtos = [this]{
+ auto protos = PrototypeHash();
+
+ auto input = createNode({
+ createPort(QShaderNodePort::Output, "input")
+ });
+ protos.insert("MyInput", input);
+
+ auto output = createNode({
+ createPort(QShaderNodePort::Input, "output")
+ });
+ protos.insert("MyOutput", output);
+
+ auto function = createNode({
+ createPort(QShaderNodePort::Input, "functionInput"),
+ createPort(QShaderNodePort::Output, "functionOutput")
+ });
+ protos.insert("MyFunction", function);
+ return protos;
+ }();
+
+ const auto smallGraph = [this]{
+ auto graph = QShaderGraph();
+
+ auto input = createNode({
+ createPort(QShaderNodePort::Output, "input")
+ }, {"foo", "bar"});
+ input.setUuid(QUuid("{00000000-0000-0000-0000-000000000001}"));
+ auto output = createNode({
+ createPort(QShaderNodePort::Input, "output")
+ });
+ output.setUuid(QUuid("{00000000-0000-0000-0000-000000000002}"));
+ auto function = createNode({
+ createPort(QShaderNodePort::Input, "functionInput"),
+ createPort(QShaderNodePort::Output, "functionOutput")
+ });
+ function.setUuid(QUuid("{00000000-0000-0000-0000-000000000003}"));
+
+ graph.addNode(input);
+ graph.addNode(output);
+ graph.addNode(function);
+ graph.addEdge(createEdge(input.uuid(), "input", function.uuid(), "functionInput", {"bar", "baz"}));
+ graph.addEdge(createEdge(function.uuid(), "functionOutput", output.uuid(), "output"));
+
+ return graph;
+ }();
+
+ QTest::newRow("TwoNodesOneEdge") << createBuffer(smallJson) << smallProtos << smallGraph << QShaderGraphLoader::Ready;
+ QTest::newRow("NotOpen") << createBuffer(smallJson, QIODevice::NotOpen) << smallProtos << QShaderGraph() << QShaderGraphLoader::Error;
+ QTest::newRow("NoPrototype") << createBuffer(smallJson) << PrototypeHash() << QShaderGraph() << QShaderGraphLoader::Error;
+
+ const auto complexJson = "{"
+ " \"nodes\": ["
+ " {"
+ " \"uuid\": \"{00000000-0000-0000-0000-000000000001}\","
+ " \"type\": \"inputValue\","
+ " \"parameters\": {"
+ " \"name\": \"worldPosition\","
+ " \"qualifier\": {"
+ " \"type\": \"QShaderLanguage::StorageQualifier\","
+ " \"value\": \"QShaderLanguage::Input\""
+ " },"
+ " \"type\": {"
+ " \"type\": \"QShaderLanguage::VariableType\","
+ " \"value\": \"QShaderLanguage::Vec3\""
+ " }"
+ " }"
+ " },"
+ " {"
+ " \"uuid\": \"{00000000-0000-0000-0000-000000000002}\","
+ " \"type\": \"texture\""
+ " },"
+ " {"
+ " \"uuid\": \"{00000000-0000-0000-0000-000000000003}\","
+ " \"type\": \"texCoord\""
+ " },"
+ " {"
+ " \"uuid\": \"{00000000-0000-0000-0000-000000000004}\","
+ " \"type\": \"inputValue\""
+ " },"
+ " {"
+ " \"uuid\": \"{00000000-0000-0000-0000-000000000005}\","
+ " \"type\": \"exposure\""
+ " },"
+ " {"
+ " \"uuid\": \"{00000000-0000-0000-0000-000000000006}\","
+ " \"type\": \"fragColor\""
+ " },"
+ " {"
+ " \"uuid\": \"{00000000-0000-0000-0000-000000000007}\","
+ " \"type\": \"sampleTexture\""
+ " },"
+ " {"
+ " \"uuid\": \"{00000000-0000-0000-0000-000000000008}\","
+ " \"type\": \"lightModel\""
+ " },"
+ " {"
+ " \"uuid\": \"{00000000-0000-0000-0000-000000000009}\","
+ " \"type\": \"exposureFunction\""
+ " }"
+ " ],"
+ " \"edges\": ["
+ " {"
+ " \"sourceUuid\": \"{00000000-0000-0000-0000-000000000002}\","
+ " \"sourcePort\": \"texture\","
+ " \"targetUuid\": \"{00000000-0000-0000-0000-000000000007}\","
+ " \"targetPort\": \"sampler\""
+ " },"
+ " {"
+ " \"sourceUuid\": \"{00000000-0000-0000-0000-000000000003}\","
+ " \"sourcePort\": \"texCoord\","
+ " \"targetUuid\": \"{00000000-0000-0000-0000-000000000007}\","
+ " \"targetPort\": \"coord\""
+ " },"
+ " {"
+ " \"sourceUuid\": \"{00000000-0000-0000-0000-000000000001}\","
+ " \"sourcePort\": \"value\","
+ " \"targetUuid\": \"{00000000-0000-0000-0000-000000000008}\","
+ " \"targetPort\": \"position\""
+ " },"
+ " {"
+ " \"sourceUuid\": \"{00000000-0000-0000-0000-000000000007}\","
+ " \"sourcePort\": \"color\","
+ " \"targetUuid\": \"{00000000-0000-0000-0000-000000000008}\","
+ " \"targetPort\": \"baseColor\""
+ " },"
+ " {"
+ " \"sourceUuid\": \"{00000000-0000-0000-0000-000000000004}\","
+ " \"sourcePort\": \"value\","
+ " \"targetUuid\": \"{00000000-0000-0000-0000-000000000008}\","
+ " \"targetPort\": \"lightIntensity\""
+ " },"
+ " {"
+ " \"sourceUuid\": \"{00000000-0000-0000-0000-000000000008}\","
+ " \"sourcePort\": \"outputColor\","
+ " \"targetUuid\": \"{00000000-0000-0000-0000-000000000009}\","
+ " \"targetPort\": \"inputColor\""
+ " },"
+ " {"
+ " \"sourceUuid\": \"{00000000-0000-0000-0000-000000000005}\","
+ " \"sourcePort\": \"exposure\","
+ " \"targetUuid\": \"{00000000-0000-0000-0000-000000000009}\","
+ " \"targetPort\": \"exposure\""
+ " },"
+ " {"
+ " \"sourceUuid\": \"{00000000-0000-0000-0000-000000000009}\","
+ " \"sourcePort\": \"outputColor\","
+ " \"targetUuid\": \"{00000000-0000-0000-0000-000000000006}\","
+ " \"targetPort\": \"fragColor\""
+ " }"
+ " ]"
+ "}";
+
+ const auto complexProtos = [this]{
+ const auto openGLES2 = createFormat(QShaderFormat::OpenGLES, 2, 0);
+ const auto openGL3 = createFormat(QShaderFormat::OpenGLCoreProfile, 3, 0);
+
+ auto protos = PrototypeHash();
+
+ auto inputValue = createNode({
+ createPort(QShaderNodePort::Output, "value")
+ });
+ inputValue.setParameter("name", "defaultName");
+ inputValue.setParameter("qualifier", QVariant::fromValue<QShaderLanguage::StorageQualifier>(QShaderLanguage::Uniform));
+ inputValue.setParameter("type", QVariant::fromValue<QShaderLanguage::VariableType>(QShaderLanguage::Float));
+ inputValue.addRule(openGLES2, QShaderNode::Rule("highp $type $value = $name;",
+ QByteArrayList() << "$qualifier highp $type $name;"));
+ inputValue.addRule(openGL3, QShaderNode::Rule("$type $value = $name;",
+ QByteArrayList() << "$qualifier $type $name;"));
+ protos.insert("inputValue", inputValue);
+
+ auto texture = createNode({
+ createPort(QShaderNodePort::Output, "texture")
+ });
+ texture.addRule(openGLES2, QShaderNode::Rule("sampler2D $texture = texture;",
+ QByteArrayList() << "uniform sampler2D texture;"));
+ texture.addRule(openGL3, QShaderNode::Rule("sampler2D $texture = texture;",
+ QByteArrayList() << "uniform sampler2D texture;"));
+ protos.insert("texture", texture);
+
+ auto texCoord = createNode({
+ createPort(QShaderNodePort::Output, "texCoord")
+ });
+ texCoord.addRule(openGLES2, QShaderNode::Rule("highp vec2 $texCoord = texCoord;",
+ QByteArrayList() << "varying highp vec2 texCoord;"));
+ texCoord.addRule(openGL3, QShaderNode::Rule("vec2 $texCoord = texCoord;",
+ QByteArrayList() << "in vec2 texCoord;"));
+ protos.insert("texCoord", texCoord);
+
+ auto exposure = createNode({
+ createPort(QShaderNodePort::Output, "exposure")
+ });
+ exposure.addRule(openGLES2, QShaderNode::Rule("highp float $exposure = exposure;",
+ QByteArrayList() << "uniform highp float exposure;"));
+ exposure.addRule(openGL3, QShaderNode::Rule("float $exposure = exposure;",
+ QByteArrayList() << "uniform float exposure;"));
+ protos.insert("exposure", exposure);
+
+ auto fragColor = createNode({
+ createPort(QShaderNodePort::Input, "fragColor")
+ });
+ fragColor.addRule(openGLES2, QShaderNode::Rule("gl_fragColor = $fragColor;"));
+ fragColor.addRule(openGL3, QShaderNode::Rule("fragColor = $fragColor;",
+ QByteArrayList() << "out vec4 fragColor;"));
+ protos.insert("fragColor", fragColor);
+
+ auto sampleTexture = createNode({
+ createPort(QShaderNodePort::Input, "sampler"),
+ createPort(QShaderNodePort::Input, "coord"),
+ createPort(QShaderNodePort::Output, "color")
+ });
+ sampleTexture.addRule(openGLES2, QShaderNode::Rule("highp vec4 $color = texture2D($sampler, $coord);"));
+ sampleTexture.addRule(openGL3, QShaderNode::Rule("vec4 $color = texture2D($sampler, $coord);"));
+ protos.insert("sampleTexture", sampleTexture);
+
+ auto lightModel = createNode({
+ createPort(QShaderNodePort::Input, "baseColor"),
+ createPort(QShaderNodePort::Input, "position"),
+ createPort(QShaderNodePort::Input, "lightIntensity"),
+ createPort(QShaderNodePort::Output, "outputColor")
+ });
+ lightModel.addRule(openGLES2, QShaderNode::Rule("highp vec4 $outputColor = lightModel($baseColor, $position, $lightIntensity);",
+ QByteArrayList() << "#pragma include es2/lightmodel.frag.inc"));
+ lightModel.addRule(openGL3, QShaderNode::Rule("vec4 $outputColor = lightModel($baseColor, $position, $lightIntensity);",
+ QByteArrayList() << "#pragma include gl3/lightmodel.frag.inc"));
+ protos.insert("lightModel", lightModel);
+
+ auto exposureFunction = createNode({
+ createPort(QShaderNodePort::Input, "inputColor"),
+ createPort(QShaderNodePort::Input, "exposure"),
+ createPort(QShaderNodePort::Output, "outputColor")
+ });
+ exposureFunction.addRule(openGLES2, QShaderNode::Rule("highp vec4 $outputColor = $inputColor * pow(2.0, $exposure);"));
+ exposureFunction.addRule(openGL3, QShaderNode::Rule("vec4 $outputColor = $inputColor * pow(2.0, $exposure);"));
+ protos.insert("exposureFunction", exposureFunction);
+
+ return protos;
+ }();
+
+ const auto complexGraph = createGraph();
+
+ QTest::newRow("ComplexGraph") << createBuffer(complexJson) << complexProtos << complexGraph << QShaderGraphLoader::Ready;
+}
+
+void tst_QShaderGraphLoader::shouldLoadFromJsonStream()
+{
+ // GIVEN
+ QFETCH(QBufferPointer, device);
+ QFETCH(PrototypeHash, prototypes);
+
+ auto loader = QShaderGraphLoader();
+
+ // WHEN
+ loader.setPrototypes(prototypes);
+ loader.setDevice(device.data());
+ loader.load();
+
+ // THEN
+ QFETCH(QShaderGraphLoader::Status, status);
+ QCOMPARE(loader.status(), status);
+
+ QFETCH(QShaderGraph, graph);
+ const auto statements = loader.graph().createStatements({"foo", "bar", "baz"});
+ const auto expected = graph.createStatements({"foo", "bar", "baz"});
+ dumpStatementsIfNeeded(statements, expected);
+ QCOMPARE(statements, expected);
+
+ const auto sortedParameters = [](const QShaderNode &node) {
+ auto res = node.parameterNames();
+ res.sort();
+ return res;
+ };
+
+ for (int i = 0; i < statements.size(); i++) {
+ const auto actualNode = statements.at(i).node;
+ const auto expectedNode = expected.at(i).node;
+
+ QCOMPARE(actualNode.layers(), expectedNode.layers());
+ QCOMPARE(actualNode.ports(), expectedNode.ports());
+ QCOMPARE(sortedParameters(actualNode), sortedParameters(expectedNode));
+ for (const auto &name : expectedNode.parameterNames()) {
+ QCOMPARE(actualNode.parameter(name), expectedNode.parameter(name));
+ }
+ QCOMPARE(actualNode.availableFormats(), expectedNode.availableFormats());
+ for (const auto &format : expectedNode.availableFormats()) {
+ QCOMPARE(actualNode.rule(format), expectedNode.rule(format));
+ }
+ }
+}
+
+QTEST_MAIN(tst_QShaderGraphLoader)
+
+#include "tst_qshadergraphloader.moc"
diff --git a/tests/auto/render/shadergraph/qshadernodes/qshadernodes.pro b/tests/auto/render/shadergraph/qshadernodes/qshadernodes.pro
new file mode 100644
index 000000000..754cde9e1
--- /dev/null
+++ b/tests/auto/render/shadergraph/qshadernodes/qshadernodes.pro
@@ -0,0 +1,5 @@
+CONFIG += testcase
+QT += testlib 3drender-private
+
+SOURCES += tst_qshadernodes.cpp
+TARGET = tst_qshadernodes
diff --git a/tests/auto/render/shadergraph/qshadernodes/tst_qshadernodes.cpp b/tests/auto/render/shadergraph/qshadernodes/tst_qshadernodes.cpp
new file mode 100644
index 000000000..2cd2ff90d
--- /dev/null
+++ b/tests/auto/render/shadergraph/qshadernodes/tst_qshadernodes.cpp
@@ -0,0 +1,549 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** 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/QtTest>
+
+#include <Qt3DRender/private/qshaderformat_p.h>
+#include <Qt3DRender/private/qshadernode_p.h>
+#include <Qt3DRender/private/qshadernodeport_p.h>
+
+using namespace Qt3DRender;
+namespace
+{
+ QShaderFormat createFormat(QShaderFormat::Api api, int majorVersion, int minorVersion,
+ const QStringList &extensions = QStringList(),
+ const QString &vendor = QString())
+ {
+ auto format = QShaderFormat();
+ format.setApi(api);
+ format.setVersion(QVersionNumber(majorVersion, minorVersion));
+ format.setExtensions(extensions);
+ format.setVendor(vendor);
+ return format;
+ }
+
+ QShaderNodePort createPort(QShaderNodePort::Direction direction, const QString &name)
+ {
+ auto port = QShaderNodePort();
+ port.direction = direction;
+ port.name = name;
+ return port;
+ }
+}
+
+class tst_QShaderNodes : public QObject
+{
+ Q_OBJECT
+private slots:
+ void shouldManipulateFormatMembers();
+ void shouldVerifyFormatsEquality_data();
+ void shouldVerifyFormatsEquality();
+ void shouldVerifyFormatsCompatibilities_data();
+ void shouldVerifyFormatsCompatibilities();
+
+ void shouldHaveDefaultPortState();
+ void shouldVerifyPortsEquality_data();
+ void shouldVerifyPortsEquality();
+
+ void shouldManipulateNodeMembers();
+ void shouldHandleNodeRulesSupportAndOrder();
+};
+
+void tst_QShaderNodes::shouldManipulateFormatMembers()
+{
+ // GIVEN
+ auto format = QShaderFormat();
+
+ // THEN (default state)
+ QCOMPARE(format.api(), QShaderFormat::NoApi);
+ QCOMPARE(format.version().majorVersion(), 0);
+ QCOMPARE(format.version().minorVersion(), 0);
+ QCOMPARE(format.extensions(), QStringList());
+ QCOMPARE(format.vendor(), QString());
+ QVERIFY(!format.isValid());
+
+ // WHEN
+ format.setApi(QShaderFormat::OpenGLES);
+
+ // THEN
+ QCOMPARE(format.api(), QShaderFormat::OpenGLES);
+ QCOMPARE(format.version().majorVersion(), 0);
+ QCOMPARE(format.version().minorVersion(), 0);
+ QCOMPARE(format.extensions(), QStringList());
+ QCOMPARE(format.vendor(), QString());
+ QVERIFY(!format.isValid());
+
+ // WHEN
+ format.setVersion(QVersionNumber(3));
+
+ // THEN
+ QCOMPARE(format.api(), QShaderFormat::OpenGLES);
+ QCOMPARE(format.version().majorVersion(), 3);
+ QCOMPARE(format.version().minorVersion(), 0);
+ QCOMPARE(format.extensions(), QStringList());
+ QCOMPARE(format.vendor(), QString());
+ QVERIFY(format.isValid());
+
+ // WHEN
+ format.setVersion(QVersionNumber(3, 2));
+
+ // THEN
+ QCOMPARE(format.api(), QShaderFormat::OpenGLES);
+ QCOMPARE(format.version().majorVersion(), 3);
+ QCOMPARE(format.version().minorVersion(), 2);
+ QCOMPARE(format.extensions(), QStringList());
+ QCOMPARE(format.vendor(), QString());
+ QVERIFY(format.isValid());
+
+ // WHEN
+ format.setExtensions({"foo", "bar"});
+
+ // THEN
+ QCOMPARE(format.api(), QShaderFormat::OpenGLES);
+ QCOMPARE(format.version().majorVersion(), 3);
+ QCOMPARE(format.version().minorVersion(), 2);
+ QCOMPARE(format.extensions(), QStringList({"bar", "foo"}));
+ QCOMPARE(format.vendor(), QString());
+ QVERIFY(format.isValid());
+
+ // WHEN
+ format.setVendor(QStringLiteral("KDAB"));
+
+ // THEN
+ QCOMPARE(format.api(), QShaderFormat::OpenGLES);
+ QCOMPARE(format.version().majorVersion(), 3);
+ QCOMPARE(format.version().minorVersion(), 2);
+ QCOMPARE(format.extensions(), QStringList({"bar", "foo"}));
+ QCOMPARE(format.vendor(), QStringLiteral("KDAB"));
+ QVERIFY(format.isValid());
+}
+
+void tst_QShaderNodes::shouldVerifyFormatsEquality_data()
+{
+ QTest::addColumn<QShaderFormat>("left");
+ QTest::addColumn<QShaderFormat>("right");
+ QTest::addColumn<bool>("expected");
+
+ QTest::newRow("Equals") << createFormat(QShaderFormat::OpenGLCoreProfile, 3, 0, {"foo", "bar"}, "KDAB")
+ << createFormat(QShaderFormat::OpenGLCoreProfile, 3, 0, {"foo", "bar"}, "KDAB")
+ << true;
+ QTest::newRow("Apis") << createFormat(QShaderFormat::OpenGLCoreProfile, 3, 0, {"foo", "bar"}, "KDAB")
+ << createFormat(QShaderFormat::OpenGLNoProfile, 3, 0, {"foo", "bar"}, "KDAB")
+ << false;
+ QTest::newRow("Major") << createFormat(QShaderFormat::OpenGLCoreProfile, 3, 0, {"foo", "bar"}, "KDAB")
+ << createFormat(QShaderFormat::OpenGLCoreProfile, 2, 0, {"foo", "bar"}, "KDAB")
+ << false;
+ QTest::newRow("Minor") << createFormat(QShaderFormat::OpenGLCoreProfile, 3, 0, {"foo", "bar"}, "KDAB")
+ << createFormat(QShaderFormat::OpenGLCoreProfile, 3, 1, {"foo", "bar"}, "KDAB")
+ << false;
+ QTest::newRow("Extensions") << createFormat(QShaderFormat::OpenGLCoreProfile, 3, 0, {"foo", "bar"}, "KDAB")
+ << createFormat(QShaderFormat::OpenGLCoreProfile, 3, 0, {"foo"}, "KDAB")
+ << false;
+ QTest::newRow("Vendor") << createFormat(QShaderFormat::OpenGLCoreProfile, 3, 0, {"foo", "bar"}, "KDAB")
+ << createFormat(QShaderFormat::OpenGLCoreProfile, 3, 0, {"foo", "bar"})
+ << false;
+}
+
+void tst_QShaderNodes::shouldVerifyFormatsEquality()
+{
+ // GIVEN
+ QFETCH(QShaderFormat, left);
+ QFETCH(QShaderFormat, right);
+
+ // WHEN
+ const auto equal = (left == right);
+ const auto notEqual = (left != right);
+
+ // THEN
+ QFETCH(bool, expected);
+ QCOMPARE(equal, expected);
+ QCOMPARE(notEqual, !expected);
+}
+
+void tst_QShaderNodes::shouldVerifyFormatsCompatibilities_data()
+{
+ QTest::addColumn<QShaderFormat>("reference");
+ QTest::addColumn<QShaderFormat>("tested");
+ QTest::addColumn<bool>("expected");
+
+ QTest::newRow("NoProfileVsES") << createFormat(QShaderFormat::OpenGLNoProfile, 2, 0)
+ << createFormat(QShaderFormat::OpenGLES, 2, 0)
+ << true;
+ QTest::newRow("CoreProfileVsES") << createFormat(QShaderFormat::OpenGLCoreProfile, 2, 0)
+ << createFormat(QShaderFormat::OpenGLES, 2, 0)
+ << false;
+ QTest::newRow("CompatProfileVsES") << createFormat(QShaderFormat::OpenGLCompatibilityProfile, 2, 0)
+ << createFormat(QShaderFormat::OpenGLES, 2, 0)
+ << true;
+
+ QTest::newRow("ESVsNoProfile") << createFormat(QShaderFormat::OpenGLES, 2, 0)
+ << createFormat(QShaderFormat::OpenGLNoProfile, 2, 0)
+ << false;
+ QTest::newRow("ESVsCoreProfile") << createFormat(QShaderFormat::OpenGLES, 2, 0)
+ << createFormat(QShaderFormat::OpenGLCoreProfile, 2, 0)
+ << false;
+ QTest::newRow("ESVsCompatProfile") << createFormat(QShaderFormat::OpenGLES, 2, 0)
+ << createFormat(QShaderFormat::OpenGLCompatibilityProfile, 2, 0)
+ << false;
+
+ QTest::newRow("CoreVsNoProfile") << createFormat(QShaderFormat::OpenGLCoreProfile, 2, 0)
+ << createFormat(QShaderFormat::OpenGLNoProfile, 2, 0)
+ << false;
+ QTest::newRow("CoreVsCompat") << createFormat(QShaderFormat::OpenGLCoreProfile, 2, 0)
+ << createFormat(QShaderFormat::OpenGLCompatibilityProfile, 2, 0)
+ << false;
+ QTest::newRow("CoreVsCore") << createFormat(QShaderFormat::OpenGLCoreProfile, 2, 0)
+ << createFormat(QShaderFormat::OpenGLCoreProfile, 2, 0)
+ << true;
+
+ QTest::newRow("NoProfileVsCore") << createFormat(QShaderFormat::OpenGLNoProfile, 2, 0)
+ << createFormat(QShaderFormat::OpenGLCoreProfile, 2, 0)
+ << true;
+ QTest::newRow("NoProvileVsCompat") << createFormat(QShaderFormat::OpenGLNoProfile, 2, 0)
+ << createFormat(QShaderFormat::OpenGLCompatibilityProfile, 2, 0)
+ << true;
+ QTest::newRow("NoProfileVsNoProfile") << createFormat(QShaderFormat::OpenGLNoProfile, 2, 0)
+ << createFormat(QShaderFormat::OpenGLNoProfile, 2, 0)
+ << true;
+
+ QTest::newRow("CompatVsCore") << createFormat(QShaderFormat::OpenGLCompatibilityProfile, 2, 0)
+ << createFormat(QShaderFormat::OpenGLCoreProfile, 2, 0)
+ << true;
+ QTest::newRow("CompatVsCompat") << createFormat(QShaderFormat::OpenGLCompatibilityProfile, 2, 0)
+ << createFormat(QShaderFormat::OpenGLCompatibilityProfile, 2, 0)
+ << true;
+ QTest::newRow("CompatVsNoProfile") << createFormat(QShaderFormat::OpenGLCompatibilityProfile, 2, 0)
+ << createFormat(QShaderFormat::OpenGLNoProfile, 2, 0)
+ << true;
+
+ QTest::newRow("MajorForwardCompat_1") << createFormat(QShaderFormat::OpenGLCoreProfile, 3, 0)
+ << createFormat(QShaderFormat::OpenGLCoreProfile, 2, 0)
+ << true;
+ QTest::newRow("MajorForwardCompat_2") << createFormat(QShaderFormat::OpenGLCoreProfile, 3, 0)
+ << createFormat(QShaderFormat::OpenGLCoreProfile, 2, 4)
+ << true;
+ QTest::newRow("MajorForwardCompat_3") << createFormat(QShaderFormat::OpenGLCoreProfile, 2, 0)
+ << createFormat(QShaderFormat::OpenGLCoreProfile, 3, 0)
+ << false;
+ QTest::newRow("MajorForwardCompat_4") << createFormat(QShaderFormat::OpenGLCoreProfile, 2, 4)
+ << createFormat(QShaderFormat::OpenGLCoreProfile, 3, 0)
+ << false;
+
+ QTest::newRow("MinorForwardCompat_1") << createFormat(QShaderFormat::OpenGLCoreProfile, 3, 1)
+ << createFormat(QShaderFormat::OpenGLCoreProfile, 3, 0)
+ << true;
+ QTest::newRow("MinorForwardCompat_2") << createFormat(QShaderFormat::OpenGLCoreProfile, 3, 0)
+ << createFormat(QShaderFormat::OpenGLCoreProfile, 3, 1)
+ << false;
+
+ QTest::newRow("Extensions_1") << createFormat(QShaderFormat::OpenGLCoreProfile, 3, 0, {"foo", "bar"})
+ << createFormat(QShaderFormat::OpenGLCoreProfile, 3, 0, {"foo"})
+ << true;
+ QTest::newRow("Extensions_2") << createFormat(QShaderFormat::OpenGLCoreProfile, 3, 0, {"foo"})
+ << createFormat(QShaderFormat::OpenGLCoreProfile, 3, 0, {"foo", "bar"})
+ << false;
+
+ QTest::newRow("Vendor_1") << createFormat(QShaderFormat::OpenGLCoreProfile, 3, 0, {}, "KDAB")
+ << createFormat(QShaderFormat::OpenGLCoreProfile, 3, 0, {})
+ << true;
+ QTest::newRow("Vendor_2") << createFormat(QShaderFormat::OpenGLCoreProfile, 3, 0, {})
+ << createFormat(QShaderFormat::OpenGLCoreProfile, 3, 0, {}, "KDAB")
+ << false;
+ QTest::newRow("Vendor_2") << createFormat(QShaderFormat::OpenGLCoreProfile, 3, 0, {}, "KDAB")
+ << createFormat(QShaderFormat::OpenGLCoreProfile, 3, 0, {}, "KDAB")
+ << true;
+}
+
+void tst_QShaderNodes::shouldVerifyFormatsCompatibilities()
+{
+ // GIVEN
+ QFETCH(QShaderFormat, reference);
+ QFETCH(QShaderFormat, tested);
+
+ // WHEN
+ const auto supported = reference.supports(tested);
+
+ // THEN
+ QFETCH(bool, expected);
+ QCOMPARE(supported, expected);
+}
+
+void tst_QShaderNodes::shouldHaveDefaultPortState()
+{
+ // GIVEN
+ auto port = QShaderNodePort();
+
+ // THEN
+ QCOMPARE(port.direction, QShaderNodePort::Output);
+ QVERIFY(port.name.isEmpty());
+}
+
+void tst_QShaderNodes::shouldVerifyPortsEquality_data()
+{
+ QTest::addColumn<QShaderNodePort>("left");
+ QTest::addColumn<QShaderNodePort>("right");
+ QTest::addColumn<bool>("expected");
+
+ QTest::newRow("Equals") << createPort(QShaderNodePort::Input, "foo")
+ << createPort(QShaderNodePort::Input, "foo")
+ << true;
+ QTest::newRow("Direction") << createPort(QShaderNodePort::Input, "foo")
+ << createPort(QShaderNodePort::Output, "foo")
+ << false;
+ QTest::newRow("Name") << createPort(QShaderNodePort::Input, "foo")
+ << createPort(QShaderNodePort::Input, "bar")
+ << false;
+}
+
+void tst_QShaderNodes::shouldVerifyPortsEquality()
+{
+ // GIVEN
+ QFETCH(QShaderNodePort, left);
+ QFETCH(QShaderNodePort, right);
+
+ // WHEN
+ const auto equal = (left == right);
+ const auto notEqual = (left != right);
+
+ // THEN
+ QFETCH(bool, expected);
+ QCOMPARE(equal, expected);
+ QCOMPARE(notEqual, !expected);
+}
+
+void tst_QShaderNodes::shouldManipulateNodeMembers()
+{
+ // GIVEN
+ const auto openGLES2 = createFormat(QShaderFormat::OpenGLES, 2, 0);
+ const auto openGL3 = createFormat(QShaderFormat::OpenGLCoreProfile, 3, 0);
+
+ const auto es2Rule = QShaderNode::Rule(QByteArrayLiteral("gles2"), {"#pragma include es2/foo.inc", "#pragma include es2/bar.inc"});
+ const auto gl3Rule = QShaderNode::Rule(QByteArrayLiteral("gl3"), {"#pragma include gl3/foo.inc", "#pragma include gl3/bar.inc"});
+ const auto gl3bisRule = QShaderNode::Rule(QByteArrayLiteral("gl3bis"), {"#pragma include gl3/foo.inc", "#pragma include gl3/bar.inc"});
+
+ auto node = QShaderNode();
+
+ // THEN (default state)
+ QCOMPARE(node.type(), QShaderNode::Invalid);
+ QVERIFY(node.uuid().isNull());
+ QVERIFY(node.layers().isEmpty());
+ QVERIFY(node.ports().isEmpty());
+ QVERIFY(node.parameterNames().isEmpty());
+ QVERIFY(node.availableFormats().isEmpty());
+
+ // WHEN
+ const auto uuid = QUuid::createUuid();
+ node.setUuid(uuid);
+
+ // THEN
+ QCOMPARE(node.uuid(), uuid);
+
+ // WHEN
+ node.setLayers({"foo", "bar"});
+
+ // THEN
+ QCOMPARE(node.layers(), QStringList({"foo", "bar"}));
+
+ // WHEN
+ auto firstPort = QShaderNodePort();
+ firstPort.direction = QShaderNodePort::Input;
+ firstPort.name = QStringLiteral("foo");
+ node.addPort(firstPort);
+
+ // THEN
+ QCOMPARE(node.type(), QShaderNode::Output);
+ QCOMPARE(node.ports().size(), 1);
+ QCOMPARE(node.ports().at(0), firstPort);
+ QVERIFY(node.availableFormats().isEmpty());
+
+ // WHEN
+ auto secondPort = QShaderNodePort();
+ secondPort.direction = QShaderNodePort::Output;
+ secondPort.name = QStringLiteral("bar");
+ node.addPort(secondPort);
+
+ // THEN
+ QCOMPARE(node.type(), QShaderNode::Function);
+ QCOMPARE(node.ports().size(), 2);
+ QCOMPARE(node.ports().at(0), firstPort);
+ QCOMPARE(node.ports().at(1), secondPort);
+ QVERIFY(node.availableFormats().isEmpty());
+
+ // WHEN
+ node.removePort(firstPort);
+
+ // THEN
+ QCOMPARE(node.type(), QShaderNode::Input);
+ QCOMPARE(node.ports().size(), 1);
+ QCOMPARE(node.ports().at(0), secondPort);
+ QVERIFY(node.availableFormats().isEmpty());
+
+ // WHEN
+ node.setParameter(QStringLiteral("baz"), 42);
+
+ // THEN
+ QCOMPARE(node.type(), QShaderNode::Input);
+ QCOMPARE(node.ports().size(), 1);
+ QCOMPARE(node.ports().at(0), secondPort);
+ auto parameterNames = node.parameterNames();
+ parameterNames.sort();
+ QCOMPARE(parameterNames.size(), 1);
+ QCOMPARE(parameterNames.at(0), QStringLiteral("baz"));
+ QCOMPARE(node.parameter(QStringLiteral("baz")), QVariant(42));
+ QVERIFY(node.availableFormats().isEmpty());
+
+ // WHEN
+ node.setParameter(QStringLiteral("bleh"), QStringLiteral("value"));
+
+ // THEN
+ QCOMPARE(node.type(), QShaderNode::Input);
+ QCOMPARE(node.ports().size(), 1);
+ QCOMPARE(node.ports().at(0), secondPort);
+ parameterNames = node.parameterNames();
+ parameterNames.sort();
+ QCOMPARE(parameterNames.size(), 2);
+ QCOMPARE(parameterNames.at(0), QStringLiteral("baz"));
+ QCOMPARE(parameterNames.at(1), QStringLiteral("bleh"));
+ QCOMPARE(node.parameter(QStringLiteral("baz")), QVariant(42));
+ QCOMPARE(node.parameter(QStringLiteral("bleh")), QVariant(QStringLiteral("value")));
+ QVERIFY(node.availableFormats().isEmpty());
+
+ // WHEN
+ node.clearParameter(QStringLiteral("baz"));
+
+ // THEN
+ QCOMPARE(node.type(), QShaderNode::Input);
+ QCOMPARE(node.ports().size(), 1);
+ QCOMPARE(node.ports().at(0), secondPort);
+ parameterNames = node.parameterNames();
+ parameterNames.sort();
+ QCOMPARE(parameterNames.size(), 1);
+ QCOMPARE(parameterNames.at(0), QStringLiteral("bleh"));
+ QCOMPARE(node.parameter(QStringLiteral("baz")), QVariant());
+ QCOMPARE(node.parameter(QStringLiteral("bleh")), QVariant(QStringLiteral("value")));
+ QVERIFY(node.availableFormats().isEmpty());
+
+ // WHEN
+ node.addRule(openGLES2, es2Rule);
+ node.addRule(openGL3, gl3Rule);
+
+ // THEN
+ QCOMPARE(node.availableFormats().size(), 2);
+ QCOMPARE(node.availableFormats().at(0), openGLES2);
+ QCOMPARE(node.availableFormats().at(1), openGL3);
+ QCOMPARE(node.rule(openGLES2), es2Rule);
+ QCOMPARE(node.rule(openGL3), gl3Rule);
+
+ // WHEN
+ node.removeRule(openGLES2);
+
+ // THEN
+ QCOMPARE(node.availableFormats().size(), 1);
+ QCOMPARE(node.availableFormats().at(0), openGL3);
+ QCOMPARE(node.rule(openGL3), gl3Rule);
+
+ // WHEN
+ node.addRule(openGLES2, es2Rule);
+
+ // THEN
+ QCOMPARE(node.availableFormats().size(), 2);
+ QCOMPARE(node.availableFormats().at(0), openGL3);
+ QCOMPARE(node.availableFormats().at(1), openGLES2);
+ QCOMPARE(node.rule(openGLES2), es2Rule);
+ QCOMPARE(node.rule(openGL3), gl3Rule);
+
+ // WHEN
+ node.addRule(openGL3, gl3bisRule);
+
+ // THEN
+ QCOMPARE(node.availableFormats().size(), 2);
+ QCOMPARE(node.availableFormats().at(0), openGLES2);
+ QCOMPARE(node.availableFormats().at(1), openGL3);
+ QCOMPARE(node.rule(openGLES2), es2Rule);
+ QCOMPARE(node.rule(openGL3), gl3bisRule);
+}
+
+void tst_QShaderNodes::shouldHandleNodeRulesSupportAndOrder()
+{
+ // GIVEN
+ const auto openGLES2 = createFormat(QShaderFormat::OpenGLES, 2, 0);
+ const auto openGL3 = createFormat(QShaderFormat::OpenGLCoreProfile, 3, 0);
+ const auto openGL32 = createFormat(QShaderFormat::OpenGLCoreProfile, 3, 2);
+ const auto openGL4 = createFormat(QShaderFormat::OpenGLCoreProfile, 4, 0);
+
+ const auto es2Rule = QShaderNode::Rule(QByteArrayLiteral("gles2"), {"#pragma include es2/foo.inc", "#pragma include es2/bar.inc"});
+ const auto gl3Rule = QShaderNode::Rule(QByteArrayLiteral("gl3"), {"#pragma include gl3/foo.inc", "#pragma include gl3/bar.inc"});
+ const auto gl32Rule = QShaderNode::Rule(QByteArrayLiteral("gl32"), {"#pragma include gl32/foo.inc", "#pragma include gl32/bar.inc"});
+ const auto gl3bisRule = QShaderNode::Rule(QByteArrayLiteral("gl3bis"), {"#pragma include gl3/foo.inc", "#pragma include gl3/bar.inc"});
+
+ auto node = QShaderNode();
+
+ // WHEN
+ node.addRule(openGLES2, es2Rule);
+ node.addRule(openGL3, gl3Rule);
+
+ // THEN
+ QCOMPARE(node.availableFormats().size(), 2);
+ QCOMPARE(node.availableFormats().at(0), openGLES2);
+ QCOMPARE(node.availableFormats().at(1), openGL3);
+ QCOMPARE(node.rule(openGLES2), es2Rule);
+ QCOMPARE(node.rule(openGL3), gl3Rule);
+ QCOMPARE(node.rule(openGL32), gl3Rule);
+ QCOMPARE(node.rule(openGL4), gl3Rule);
+
+ // WHEN
+ node.addRule(openGL32, gl32Rule);
+
+ // THEN
+ QCOMPARE(node.availableFormats().size(), 3);
+ QCOMPARE(node.availableFormats().at(0), openGLES2);
+ QCOMPARE(node.availableFormats().at(1), openGL3);
+ QCOMPARE(node.availableFormats().at(2), openGL32);
+ QCOMPARE(node.rule(openGLES2), es2Rule);
+ QCOMPARE(node.rule(openGL3), gl3Rule);
+ QCOMPARE(node.rule(openGL32), gl32Rule);
+ QCOMPARE(node.rule(openGL4), gl32Rule);
+
+ // WHEN
+ node.addRule(openGL3, gl3bisRule);
+
+ // THEN
+ QCOMPARE(node.availableFormats().size(), 3);
+ QCOMPARE(node.availableFormats().at(0), openGLES2);
+ QCOMPARE(node.availableFormats().at(1), openGL32);
+ QCOMPARE(node.availableFormats().at(2), openGL3);
+ QCOMPARE(node.rule(openGLES2), es2Rule);
+ QCOMPARE(node.rule(openGL3), gl3bisRule);
+ QCOMPARE(node.rule(openGL32), gl3bisRule);
+ QCOMPARE(node.rule(openGL4), gl3bisRule);
+}
+
+QTEST_MAIN(tst_QShaderNodes)
+
+#include "tst_qshadernodes.moc"
diff --git a/tests/auto/render/shadergraph/qshadernodesloader/qshadernodesloader.pro b/tests/auto/render/shadergraph/qshadernodesloader/qshadernodesloader.pro
new file mode 100644
index 000000000..7da336b5e
--- /dev/null
+++ b/tests/auto/render/shadergraph/qshadernodesloader/qshadernodesloader.pro
@@ -0,0 +1,5 @@
+CONFIG += testcase
+QT += testlib 3drender-private
+
+SOURCES += tst_qshadernodesloader.cpp
+TARGET = tst_qshadernodesloader
diff --git a/tests/auto/render/shadergraph/qshadernodesloader/tst_qshadernodesloader.cpp b/tests/auto/render/shadergraph/qshadernodesloader/tst_qshadernodesloader.cpp
new file mode 100644
index 000000000..27cf7f425
--- /dev/null
+++ b/tests/auto/render/shadergraph/qshadernodesloader/tst_qshadernodesloader.cpp
@@ -0,0 +1,325 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** 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/QtTest>
+
+#include <QtCore/qbuffer.h>
+
+#include <Qt3DRender/private/qshadernodesloader_p.h>
+#include <Qt3DRender/private/qshaderlanguage_p.h>
+
+using namespace Qt3DRender;
+
+using QBufferPointer = QSharedPointer<QBuffer>;
+Q_DECLARE_METATYPE(QBufferPointer);
+
+using NodeHash = QHash<QString, QShaderNode>;
+Q_DECLARE_METATYPE(NodeHash);
+
+namespace
+{
+ QBufferPointer createBuffer(const QByteArray &data, QIODevice::OpenMode openMode = QIODevice::ReadOnly)
+ {
+ auto buffer = QBufferPointer::create();
+ buffer->setData(data);
+ if (openMode != QIODevice::NotOpen)
+ buffer->open(openMode);
+ return buffer;
+ }
+
+ QShaderFormat createFormat(QShaderFormat::Api api, int majorVersion, int minorVersion,
+ const QStringList &extensions = QStringList(),
+ const QString &vendor = QString())
+ {
+ auto format = QShaderFormat();
+ format.setApi(api);
+ format.setVersion(QVersionNumber(majorVersion, minorVersion));
+ format.setExtensions(extensions);
+ format.setVendor(vendor);
+ return format;
+ }
+
+ QShaderNodePort createPort(QShaderNodePort::Direction portDirection, const QString &portName)
+ {
+ auto port = QShaderNodePort();
+ port.direction = portDirection;
+ port.name = portName;
+ return port;
+ }
+
+ QShaderNode createNode(const QVector<QShaderNodePort> &ports)
+ {
+ auto node = QShaderNode();
+ for (const auto &port : ports)
+ node.addPort(port);
+ return node;
+ }
+}
+
+class tst_QShaderNodesLoader : public QObject
+{
+ Q_OBJECT
+private slots:
+ void shouldManipulateLoaderMembers();
+ void shouldLoadFromJsonStream_data();
+ void shouldLoadFromJsonStream();
+};
+
+void tst_QShaderNodesLoader::shouldManipulateLoaderMembers()
+{
+ // GIVEN
+ auto loader = QShaderNodesLoader();
+
+ // THEN (default state)
+ QCOMPARE(loader.status(), QShaderNodesLoader::Null);
+ QVERIFY(!loader.device());
+ QVERIFY(loader.nodes().isEmpty());
+
+ // WHEN
+ auto device1 = createBuffer(QByteArray("..........."), QIODevice::NotOpen);
+ loader.setDevice(device1.data());
+
+ // THEN
+ QCOMPARE(loader.status(), QShaderNodesLoader::Error);
+ QCOMPARE(loader.device(), device1.data());
+ QVERIFY(loader.nodes().isEmpty());
+
+ // WHEN
+ auto device2 = createBuffer(QByteArray("..........."), QIODevice::ReadOnly);
+ loader.setDevice(device2.data());
+
+ // THEN
+ QCOMPARE(loader.status(), QShaderNodesLoader::Waiting);
+ QCOMPARE(loader.device(), device2.data());
+ QVERIFY(loader.nodes().isEmpty());
+}
+
+void tst_QShaderNodesLoader::shouldLoadFromJsonStream_data()
+{
+ QTest::addColumn<QBufferPointer>("device");
+ QTest::addColumn<NodeHash>("nodes");
+ QTest::addColumn<QShaderNodesLoader::Status>("status");
+
+ QTest::newRow("empty") << createBuffer("", QIODevice::ReadOnly) << NodeHash() << QShaderNodesLoader::Error;
+
+ const auto smallJson = "{"
+ " \"inputValue\": {"
+ " \"outputs\": ["
+ " \"value\""
+ " ],"
+ " \"parameters\": {"
+ " \"name\": \"defaultName\","
+ " \"qualifier\": {"
+ " \"type\": \"QShaderLanguage::StorageQualifier\","
+ " \"value\": \"QShaderLanguage::Uniform\""
+ " },"
+ " \"type\": {"
+ " \"type\": \"QShaderLanguage::VariableType\","
+ " \"value\": \"QShaderLanguage::Vec3\""
+ " },"
+ " \"defaultValue\": {"
+ " \"type\": \"float\","
+ " \"value\": \"1.25\""
+ " }"
+ " },"
+ " \"rules\": ["
+ " {"
+ " \"format\": {"
+ " \"api\": \"OpenGLES\","
+ " \"major\": 2,"
+ " \"minor\": 0"
+ " },"
+ " \"substitution\": \"highp vec3 $value = $name;\","
+ " \"headerSnippets\": [ \"varying highp vec3 $name;\" ]"
+ " },"
+ " {"
+ " \"format\": {"
+ " \"api\": \"OpenGLCompatibilityProfile\","
+ " \"major\": 2,"
+ " \"minor\": 1"
+ " },"
+ " \"substitution\": \"vec3 $value = $name;\","
+ " \"headerSnippets\": [ \"in vec3 $name;\" ]"
+ " }"
+ " ]"
+ " },"
+ " \"fragColor\": {"
+ " \"inputs\": ["
+ " \"fragColor\""
+ " ],"
+ " \"rules\": ["
+ " {"
+ " \"format\": {"
+ " \"api\": \"OpenGLES\","
+ " \"major\": 2,"
+ " \"minor\": 0"
+ " },"
+ " \"substitution\": \"gl_fragColor = $fragColor;\""
+ " },"
+ " {"
+ " \"format\": {"
+ " \"api\": \"OpenGLNoProfile\","
+ " \"major\": 4,"
+ " \"minor\": 0"
+ " },"
+ " \"substitution\": \"fragColor = $fragColor;\","
+ " \"headerSnippets\": [ \"out vec4 fragColor;\" ]"
+ " }"
+ " ]"
+ " },"
+ " \"lightModel\": {"
+ " \"inputs\": ["
+ " \"baseColor\","
+ " \"position\","
+ " \"lightIntensity\""
+ " ],"
+ " \"outputs\": ["
+ " \"outputColor\""
+ " ],"
+ " \"rules\": ["
+ " {"
+ " \"format\": {"
+ " \"api\": \"OpenGLES\","
+ " \"major\": 2,"
+ " \"minor\": 0,"
+ " \"extensions\": [ \"ext1\", \"ext2\" ],"
+ " \"vendor\": \"kdab\""
+ " },"
+ " \"substitution\": \"highp vec4 $outputColor = lightModel($baseColor, $position, $lightIntensity);\","
+ " \"headerSnippets\": [ \"#pragma include es2/lightmodel.frag.inc\" ]"
+ " },"
+ " {"
+ " \"format\": {"
+ " \"api\": \"OpenGLCoreProfile\","
+ " \"major\": 3,"
+ " \"minor\": 3"
+ " },"
+ " \"substitution\": \"vec4 $outputColor = lightModel($baseColor, $position, $lightIntensity);\","
+ " \"headerSnippets\": [ \"#pragma include gl3/lightmodel.frag.inc\" ]"
+ " }"
+ " ]"
+ " }"
+ "}";
+
+ const auto smallProtos = [this]{
+ const auto openGLES2 = createFormat(QShaderFormat::OpenGLES, 2, 0);
+ const auto openGLES2Extended = createFormat(QShaderFormat::OpenGLES, 2, 0, {"ext1", "ext2"}, "kdab");
+ const auto openGL2 = createFormat(QShaderFormat::OpenGLCompatibilityProfile, 2, 1);
+ const auto openGL3 = createFormat(QShaderFormat::OpenGLCoreProfile, 3, 3);
+ const auto openGL4 = createFormat(QShaderFormat::OpenGLNoProfile, 4, 0);
+
+ auto protos = NodeHash();
+
+ auto inputValue = createNode({
+ createPort(QShaderNodePort::Output, "value")
+ });
+ inputValue.setParameter("name", "defaultName");
+ inputValue.setParameter("qualifier", QVariant::fromValue<QShaderLanguage::StorageQualifier>(QShaderLanguage::Uniform));
+ inputValue.setParameter("type", QVariant::fromValue<QShaderLanguage::VariableType>(QShaderLanguage::Vec3));
+ inputValue.setParameter("defaultValue", QVariant(1.25f));
+ inputValue.addRule(openGLES2, QShaderNode::Rule("highp vec3 $value = $name;",
+ QByteArrayList() << "varying highp vec3 $name;"));
+ inputValue.addRule(openGL2, QShaderNode::Rule("vec3 $value = $name;",
+ QByteArrayList() << "in vec3 $name;"));
+ protos.insert("inputValue", inputValue);
+
+ auto fragColor = createNode({
+ createPort(QShaderNodePort::Input, "fragColor")
+ });
+ fragColor.addRule(openGLES2, QShaderNode::Rule("gl_fragColor = $fragColor;"));
+ fragColor.addRule(openGL4, QShaderNode::Rule("fragColor = $fragColor;",
+ QByteArrayList() << "out vec4 fragColor;"));
+ protos.insert(QStringLiteral("fragColor"), fragColor);
+
+ auto lightModel = createNode({
+ createPort(QShaderNodePort::Input, "baseColor"),
+ createPort(QShaderNodePort::Input, "position"),
+ createPort(QShaderNodePort::Input, "lightIntensity"),
+ createPort(QShaderNodePort::Output, "outputColor")
+ });
+ lightModel.addRule(openGLES2Extended, QShaderNode::Rule("highp vec4 $outputColor = lightModel($baseColor, $position, $lightIntensity);",
+ QByteArrayList() << "#pragma include es2/lightmodel.frag.inc"));
+ lightModel.addRule(openGL3, QShaderNode::Rule("vec4 $outputColor = lightModel($baseColor, $position, $lightIntensity);",
+ QByteArrayList() << "#pragma include gl3/lightmodel.frag.inc"));
+ protos.insert("lightModel", lightModel);
+
+ return protos;
+ }();
+
+ QTest::newRow("NotOpen") << createBuffer(smallJson, QIODevice::NotOpen) << NodeHash() << QShaderNodesLoader::Error;
+ QTest::newRow("CorrectJSON") << createBuffer(smallJson) << smallProtos << QShaderNodesLoader::Ready;
+}
+
+void tst_QShaderNodesLoader::shouldLoadFromJsonStream()
+{
+ // GIVEN
+ QFETCH(QBufferPointer, device);
+
+ auto loader = QShaderNodesLoader();
+
+ // WHEN
+ loader.setDevice(device.data());
+ loader.load();
+
+ // THEN
+ QFETCH(QShaderNodesLoader::Status, status);
+ QCOMPARE(loader.status(), status);
+
+ QFETCH(NodeHash, nodes);
+ const auto sortedKeys = [](const NodeHash &nodes) {
+ auto res = nodes.keys();
+ res.sort();
+ return res;
+ };
+ const auto sortedParameters = [](const QShaderNode &node) {
+ auto res = node.parameterNames();
+ res.sort();
+ return res;
+ };
+ QCOMPARE(sortedKeys(loader.nodes()), sortedKeys(nodes));
+ for (const auto &key : nodes.keys()) {
+ const auto actual = loader.nodes().value(key);
+ const auto expected = nodes.value(key);
+
+ QVERIFY(actual.uuid().isNull());
+ QCOMPARE(actual.ports(), expected.ports());
+ QCOMPARE(sortedParameters(actual), sortedParameters(expected));
+ for (const auto &name : expected.parameterNames()) {
+ QCOMPARE(actual.parameter(name), expected.parameter(name));
+ }
+ QCOMPARE(actual.availableFormats(), expected.availableFormats());
+ for (const auto &format : expected.availableFormats()) {
+ QCOMPARE(actual.rule(format), expected.rule(format));
+ }
+ }
+}
+
+QTEST_MAIN(tst_QShaderNodesLoader)
+
+#include "tst_qshadernodesloader.moc"
diff --git a/tests/auto/render/shadergraph/shadergraph.pro b/tests/auto/render/shadergraph/shadergraph.pro
new file mode 100644
index 000000000..653a9a694
--- /dev/null
+++ b/tests/auto/render/shadergraph/shadergraph.pro
@@ -0,0 +1,10 @@
+TEMPLATE = subdirs
+
+qtConfig(private_tests) {
+ SUBDIRS += \
+ qshadergenerator \
+ qshadergraph \
+ qshadergraphloader \
+ qshadernodes \
+ qshadernodesloader
+}
diff --git a/tests/benchmarks/render/opengl/opengl.pro b/tests/benchmarks/render/opengl/opengl.pro
new file mode 100644
index 000000000..4fdf80256
--- /dev/null
+++ b/tests/benchmarks/render/opengl/opengl.pro
@@ -0,0 +1,4 @@
+TEMPLATE = subdirs
+
+SUBDIRS += \
+ shaderparameterpack
diff --git a/tests/benchmarks/render/opengl/opengl_render_plugin.pri b/tests/benchmarks/render/opengl/opengl_render_plugin.pri
new file mode 100644
index 000000000..50fade878
--- /dev/null
+++ b/tests/benchmarks/render/opengl/opengl_render_plugin.pri
@@ -0,0 +1,18 @@
+# Found no way of having the test runner include the correct
+# library path as runtime
+
+#PLUGIN_SRC_PATH = $$PWD/../../../../src/plugins/renderers/opengl
+
+#INCLUDEPATH += \
+# $$PLUGIN_SRC_PATH/graphicshelpers \
+# $$PLUGIN_SRC_PATH/io \
+# $$PLUGIN_SRC_PATH/jobs \
+# $$PLUGIN_SRC_PATH/managers \
+# $$PLUGIN_SRC_PATH/renderer \
+# $$PLUGIN_SRC_PATH/renderstates \
+# $$PLUGIN_SRC_PATH/textures
+
+#LIBS += -L$$[QT_INSTALL_PLUGINS]/renderers/ -lopenglrenderer
+
+# Instead we link against the sources directly
+include(../../../../src/plugins/renderers/opengl/opengl.pri)
diff --git a/tests/benchmarks/render/opengl/shaderparameterpack/shaderparameterpack.pro b/tests/benchmarks/render/opengl/shaderparameterpack/shaderparameterpack.pro
new file mode 100644
index 000000000..4943a5e91
--- /dev/null
+++ b/tests/benchmarks/render/opengl/shaderparameterpack/shaderparameterpack.pro
@@ -0,0 +1,17 @@
+TEMPLATE = app
+
+TARGET = tst_bench_shaderparameterpack
+
+QT += core-private 3dcore 3dcore-private 3drender 3drender-private testlib
+
+CONFIG += testcase
+
+SOURCES += tst_bench_shaderparameterpack.cpp
+
+include(../../../../auto/render/commons/commons.pri)
+
+# Needed to use the TestAspect
+DEFINES += QT_BUILD_INTERNAL
+
+# Link Against OpenGL Renderer Plugin
+include(../opengl_render_plugin.pri)
diff --git a/tests/benchmarks/render/opengl/shaderparameterpack/tst_bench_shaderparameterpack.cpp b/tests/benchmarks/render/opengl/shaderparameterpack/tst_bench_shaderparameterpack.cpp
new file mode 100644
index 000000000..d992e03af
--- /dev/null
+++ b/tests/benchmarks/render/opengl/shaderparameterpack/tst_bench_shaderparameterpack.cpp
@@ -0,0 +1,95 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest/QTest>
+#include <Qt3DRender/private/uniform_p.h>
+#include <shaderparameterpack_p.h>
+#include <glshader_p.h>
+#include <shadervariables_p.h>
+#include <QRandomGenerator>
+
+using namespace Qt3DRender::Render;
+using namespace Qt3DRender::Render::OpenGL;
+
+
+class tst_BenchShaderParameterPack : public QObject
+{
+ Q_OBJECT
+private Q_SLOTS:
+
+ void checkPackUniformInsert()
+ {
+ // GIVEN
+ PackUniformHash pack;
+
+ QVector<int> randKeys(64);
+ QRandomGenerator gen;
+
+ for (int i = 0; i < 64; ++i)
+ randKeys[i] = gen.generate();
+
+ QBENCHMARK {
+ for (const int key : qAsConst(randKeys))
+ pack.insert(key, UniformValue(key));
+ }
+ }
+
+ void prepareUniforms()
+ {
+ // GIVEN
+ GLShader shader;
+ QVector<ShaderUniform> uniformDescriptions;
+ for (int i = 0; i < 30; i++) {
+ ShaderUniform u;
+ u.m_name = QString::number(i);
+ uniformDescriptions << u;
+ }
+ shader.initializeUniforms(uniformDescriptions);
+
+ // THEN
+ QCOMPARE(shader.uniforms().size(), 30);
+
+ // WHEN
+ QVector<int> testNames;
+ for (int i = 0; i < 10; ++i)
+ testNames << shader.uniforms()[i * 3].m_nameId;
+
+ // WHEN
+ ShaderParameterPack pack;
+ for (const int nameId : qAsConst(testNames))
+ pack.setUniform(nameId, UniformValue(nameId));
+
+ QBENCHMARK {
+ shader.prepareUniforms(pack);
+ }
+ }
+};
+
+QTEST_MAIN(tst_BenchShaderParameterPack)
+
+#include "tst_bench_shaderparameterpack.moc"
diff --git a/tests/benchmarks/render/render.pro b/tests/benchmarks/render/render.pro
index 7720a2d4a..98d2ba1e7 100644
--- a/tests/benchmarks/render/render.pro
+++ b/tests/benchmarks/render/render.pro
@@ -3,5 +3,6 @@ TEMPLATE=subdirs
qtConfig(private_tests) {
SUBDIRS += jobs \
layerfiltering \
- materialparametergathering
+ materialparametergathering \
+ opengl
}
diff --git a/tests/manual/bigscene-cpp/main.cpp b/tests/manual/bigscene-cpp/main.cpp
index 5e61758db..5a66cb213 100644
--- a/tests/manual/bigscene-cpp/main.cpp
+++ b/tests/manual/bigscene-cpp/main.cpp
@@ -70,6 +70,11 @@
#include <qmath.h>
#include <Qt3DExtras/qt3dwindow.h>
#include <Qt3DExtras/qfirstpersoncameracontroller.h>
+#include <Qt3DRender/QRenderSurfaceSelector>
+#include <Qt3DRender/QCameraSelector>
+#include <Qt3DRender/QViewport>
+#include <Qt3DRender/QNoDraw>
+#include <Qt3DRender/QDebugOverlay>
using namespace Qt3DCore;
using namespace Qt3DRender;
@@ -78,7 +83,46 @@ int main(int ac, char **av)
{
QGuiApplication app(ac, av);
Qt3DExtras::Qt3DWindow view;
- view.defaultFrameGraph()->setClearColor(Qt::black);
+
+ // FrameGraph
+ {
+ QRenderSurfaceSelector *surfaceSelector = new QRenderSurfaceSelector();
+ QCameraSelector *cameraSelector = new Qt3DRender::QCameraSelector(surfaceSelector);
+ cameraSelector->setCamera(view.camera());
+ QClearBuffers *clearBuffers = new Qt3DRender::QClearBuffers(cameraSelector);
+ clearBuffers->setClearColor(QColor(Qt::gray));
+ clearBuffers->setBuffers(QClearBuffers::ColorDepthBuffer);
+ new QNoDraw(clearBuffers);
+
+ const QRectF viewports[] = {
+ {0.0f, 0.0f, 0.25f, 0.25f},
+ {0.25f, 0.0f, 0.25f, 0.25f},
+ {0.5f, 0.0f, 0.25f, 0.25f},
+ {0.75f, 0.0f, 0.25f, 0.25f},
+ {0.0f, 0.25f, 0.25f, 0.25f},
+ {0.25f, 0.25f, 0.25f, 0.25f},
+ {0.5f, 0.25f, 0.25f, 0.25f},
+ {0.75f, 0.25f, 0.25f, 0.25f},
+ {0.0f, 0.5f, 0.25f, 0.25f},
+ {0.25f, 0.5f, 0.25f, 0.25f},
+ {0.5f, 0.5f, 0.25f, 0.25f},
+ {0.75f, 0.5f, 0.25f, 0.25f},
+ {0.0f, 0.75f, 0.25f, 0.25f},
+ {0.25f, 0.75f, 0.25f, 0.25f},
+ {0.5f, 0.75f, 0.25f, 0.25f},
+ {0.75f, 0.75f, 0.25f, 0.25f},
+ };
+
+ for (const QRectF &vp : viewports) {
+ QViewport *viewport = new Qt3DRender::QViewport(cameraSelector);
+ viewport->setNormalizedRect(vp);
+ }
+
+ new QDebugOverlay(qobject_cast<Qt3DCore::QNode *>(cameraSelector->children().last()));
+
+ view.setActiveFrameGraph(surfaceSelector);
+ }
+
QEntity *root = new QEntity();
diff --git a/tests/manual/manual.pro b/tests/manual/manual.pro
index 2a639afdb..c64044464 100644
--- a/tests/manual/manual.pro
+++ b/tests/manual/manual.pro
@@ -1,85 +1,29 @@
TEMPLATE = subdirs
SUBDIRS += \
- assimp \
bigscene-cpp \
- bigmodel-qml \
- bigscene-instanced-qml \
- clip-planes-qml \
component-changes \
custom-mesh-cpp \
custom-mesh-cpp-indirect \
- custom-mesh-qml \
custom-mesh-update-data-cpp \
- custom-mesh-update-data-qml \
cylinder-cpp \
cylinder-parent-test \
- cylinder-qml \
deferred-renderer-cpp \
- deferred-renderer-qml \
- downloading \
- dragging \
- dynamicscene-cpp \
- enabled-qml \
- gltf \
- gooch-qml \
- keyboardinput-qml \
- loader-qml \
- lod \
- mouseinput-qml \
- multiplewindows-qml \
- picking-qml \
- plasma \
- pointlinesize \
- scene3d-loader \
- simple-shaders-qml \
- skybox \
- tessellation-modes \
- transforms-qml \
- spritegrid \
- transparency-qml \
- transparency-qml-scene3d \
- rendercapture-qml \
- additional-attributes-qml \
- dynamic-model-loader-qml \
- buffercapture-qml \
- render-qml-to-texture \
- render-qml-to-texture-qml \
- video-texture-qml \
- animation-keyframe-simple \
- animation-keyframe-blendtree \
- distancefieldtext \
- mesh-morphing \
- anim-viewer \
- animation-keyframe-programmatic \
- layerfilter-qml \
- skinned-mesh \
- rigged-simple \
- proximityfilter \
- rendercapture-qml-fbo \
- blitframebuffer-qml \
- raycasting-qml \
- shared_texture_image \
- texture_property_updates \
raster-cpp \
- raster-qml \
qtbug-72236 \
- qtbug-76766 \
- shader-image-qml \
- scene3d-in-sync \
- compressed_textures \
- subtree-enabler-qml \
- scene3d-visibility \
manual-renderloop \
+ rhi \
boundingvolumes
-!macos:!uikit: SUBDIRS += compute-manual
-
qtHaveModule(multimedia): {
SUBDIRS += \
- sharedtexture \
- sharedtextureqml
+ sharedtexture
+
+ qtHaveModule(quick) {
+ SUBDIRS += \
+ sharedtextureqml
+ }
}
qtHaveModule(widgets): {
@@ -89,3 +33,70 @@ qtHaveModule(widgets): {
rendercapture-cpp \
texture-updates-cpp
}
+
+qtHaveModule(quick) {
+ !macos:!uikit: SUBDIRS += compute-manual
+
+ SUBDIRS += \
+ assimp \
+ animation-keyframe-simple \
+ animation-keyframe-blendtree \
+ animation-keyframe-programmatic \
+ bigmodel-qml \
+ bigscene-instanced-qml \
+ clip-planes-qml \
+ custom-mesh-qml \
+ custom-mesh-update-data-qml \
+ cylinder-qml \
+ deferred-renderer-qml \
+ downloading \
+ dynamicscene-cpp \
+ dragging \
+ enabled-qml \
+ gltf \
+ gooch-qml \
+ keyboardinput-qml \
+ lod \
+ loader-qml \
+ mouseinput-qml \
+ multiplewindows-qml \
+ plasma \
+ pointlinesize \
+ scene3d-loader \
+ picking-qml \
+ skybox \
+ simple-shaders-qml \
+ transparency-qml \
+ transparency-qml-scene3d \
+ rendercapture-qml \
+ additional-attributes-qml \
+ dynamic-model-loader-qml \
+ buffercapture-qml \
+ render-qml-to-texture \
+ render-qml-to-texture-qml \
+ video-texture-qml \
+ transforms-qml \
+ layerfilter-qml \
+ tessellation-modes \
+ rendercapture-qml-fbo \
+ blitframebuffer-qml \
+ raycasting-qml \
+ raster-qml \
+ shader-image-qml \
+ spritegrid \
+ subtree-enabler-qml \
+ distancefieldtext \
+ mesh-morphing \
+ anim-viewer \
+ skinned-mesh \
+ rigged-simple \
+ proximityfilter \
+ scene3d-visibility \
+ shared_texture_image \
+ texture_property_updates \
+ qtbug-76766 \
+ scene3d-in-sync \
+ compressed_textures \
+}
+
+qtHaveModule(quickwidgets): SUBDIRS += quickwidget-switch
diff --git a/tests/manual/quickwidget-switch/main.cpp b/tests/manual/quickwidget-switch/main.cpp
new file mode 100644
index 000000000..ebadc458d
--- /dev/null
+++ b/tests/manual/quickwidget-switch/main.cpp
@@ -0,0 +1,125 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QDesktopWidget>
+#include <QScreen>
+#include <QApplication>
+#include <QMainWindow>
+#include <QMdiArea>
+#include <QMdiSubWindow>
+#include <QQuickWidget>
+#include <QVBoxLayout>
+#include <QPushButton>
+
+void configureMainWindow(QMainWindow *w, QMdiArea *mdiArea, QPushButton *button)
+{
+ auto widget = new QWidget;
+ auto layout = new QVBoxLayout;
+ layout->addWidget(mdiArea);
+ layout->addWidget(button);
+ widget->setLayout(layout);
+ w->setCentralWidget(widget);
+}
+
+int main(int argc, char* argv[])
+{
+ QApplication app(argc, argv);
+
+ QMainWindow w1;
+ w1.winId();
+ w1.windowHandle()->setScreen(QGuiApplication::screens().at(0));
+ auto mdiArea1 = new QMdiArea;
+ auto button1 = new QPushButton("Switch to this window");
+ configureMainWindow(&w1, mdiArea1, button1);
+ w1.setGeometry(0, 0, 800, 800);
+ w1.show();
+
+ QMainWindow w2;
+ w2.winId();
+ w2.windowHandle()->setScreen(QGuiApplication::screens().at(1));
+ auto mdiArea2 = new QMdiArea;
+ auto button2 = new QPushButton("Switch to this window");
+ configureMainWindow(&w2, mdiArea2, button2);
+ w2.setGeometry(0, 0, 800, 800);
+ w2.show();
+
+ QMdiSubWindow* subWindow = new QMdiSubWindow();
+
+ QQuickWidget *quickWidget = new QQuickWidget();
+ quickWidget->resize(QSize(400, 400));
+ quickWidget->setResizeMode(QQuickWidget::SizeRootObjectToView);
+ quickWidget->setSource(QUrl("qrc:/main.qml"));
+
+ subWindow->setWidget(quickWidget);
+
+ QObject::connect(button1, &QPushButton::clicked,
+ [mdiArea1, mdiArea2, subWindow, button1, button2]() {
+ mdiArea2->removeSubWindow(subWindow);
+ mdiArea1->addSubWindow(subWindow);
+ subWindow->show();
+ button1->setEnabled(false);
+ button2->setEnabled(true);
+ });
+
+ QObject::connect(button2, &QPushButton::clicked,
+ [mdiArea1, mdiArea2, subWindow, button1, button2]() {
+ mdiArea1->removeSubWindow(subWindow);
+ mdiArea2->addSubWindow(subWindow);
+ subWindow->show();
+ button1->setEnabled(true);
+ button2->setEnabled(false);
+ });
+
+ mdiArea2->addSubWindow(subWindow);
+ button2->setEnabled(false);
+ subWindow->show();
+
+ return app.exec();
+}
diff --git a/tests/manual/quickwidget-switch/main.qml b/tests/manual/quickwidget-switch/main.qml
new file mode 100644
index 000000000..d4bce020c
--- /dev/null
+++ b/tests/manual/quickwidget-switch/main.qml
@@ -0,0 +1,152 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.2 as QQ2
+import Qt3D.Core 2.0
+import Qt3D.Render 2.0
+import Qt3D.Input 2.0
+import Qt3D.Extras 2.0
+
+import QtQuick.Scene3D 2.0
+
+QQ2.Item {
+ id: mioitem
+ width: 300
+ height: 300
+ Scene3D {
+ id: scene3d
+ anchors.fill: parent
+ Entity {
+ id: sceneRoot
+
+ Camera {
+ id: camera
+ projectionType: CameraLens.PerspectiveProjection
+ fieldOfView: 45
+ aspectRatio: 16/9
+ nearPlane : 0.1
+ farPlane : 1000.0
+ position: Qt.vector3d( 0.0, 0.0, -40.0 )
+ upVector: Qt.vector3d( 0.0, 1.0, 0.0 )
+ viewCenter: Qt.vector3d( 0.0, 0.0, 0.0 )
+ }
+
+ OrbitCameraController {
+ camera: camera
+ }
+
+ components: [
+ RenderSettings {
+ activeFrameGraph: ForwardRenderer {
+ clearColor: Qt.rgba(0, 0.5, 1, 1)
+ camera: camera
+ }
+ },
+ // Event Source will be set by the Qt3DQuickWindow
+ InputSettings { }
+ ]
+
+ PhongMaterial {
+ id: material
+ }
+
+ TorusMesh {
+ id: torusMesh
+ radius: 5
+ minorRadius: 1
+ rings: 100
+ slices: 20
+ }
+
+ Transform {
+ id: torusTransform
+ scale3D: Qt.vector3d(1.5, 1, 0.5)
+ rotation: fromAxisAndAngle(Qt.vector3d(1, 0, 0), 45)
+ }
+
+ Entity {
+ id: torusEntity
+ components: [ torusMesh, material, torusTransform ]
+ }
+
+ SphereMesh {
+ id: sphereMesh
+ radius: 3
+ }
+
+ Transform {
+ id: sphereTransform
+ property real userAngle: 0.0
+ matrix: {
+ var m = Qt.matrix4x4();
+ m.rotate(userAngle, Qt.vector3d(0, 1, 0));
+ m.translate(Qt.vector3d(20, 0, 0));
+ return m;
+ }
+ }
+
+ QQ2.NumberAnimation {
+ target: sphereTransform
+ property: "userAngle"
+ duration: 10000
+ from: 0
+ to: 360
+
+ loops: QQ2.Animation.Infinite
+ running: true
+ }
+
+ Entity {
+ id: sphereEntity
+ components: [ sphereMesh, material, sphereTransform ]
+ }
+ }
+ }
+}
diff --git a/tests/manual/quickwidget-switch/quickwidget-switch.pro b/tests/manual/quickwidget-switch/quickwidget-switch.pro
new file mode 100644
index 000000000..2f1cb98f5
--- /dev/null
+++ b/tests/manual/quickwidget-switch/quickwidget-switch.pro
@@ -0,0 +1,13 @@
+TEMPLATE = app
+
+QT += 3dextras
+CONFIG += resources_big
+
+QT += 3dcore 3drender 3dinput 3dquick 3dlogic qml quick 3dquickextras widgets quickwidgets
+
+SOURCES += \
+ main.cpp
+
+RESOURCES += \
+ quickwidget-switch.qrc
+
diff --git a/tests/manual/quickwidget-switch/quickwidget-switch.qrc b/tests/manual/quickwidget-switch/quickwidget-switch.qrc
new file mode 100644
index 000000000..5f6483ac3
--- /dev/null
+++ b/tests/manual/quickwidget-switch/quickwidget-switch.qrc
@@ -0,0 +1,5 @@
+<RCC>
+ <qresource prefix="/">
+ <file>main.qml</file>
+ </qresource>
+</RCC>
diff --git a/tests/manual/quickwindow-switch/main.cpp b/tests/manual/quickwindow-switch/main.cpp
new file mode 100644
index 000000000..7bebe0c75
--- /dev/null
+++ b/tests/manual/quickwindow-switch/main.cpp
@@ -0,0 +1,68 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QGuiApplication>
+#include <QQmlApplicationEngine>
+
+int main(int argc, char* argv[])
+{
+ QGuiApplication app(argc, argv);
+
+ QQmlApplicationEngine engine;
+ const QUrl url(QStringLiteral("qrc:/main.qml"));
+ QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
+ &app, [url](QObject *obj, const QUrl &objUrl) {
+ if (!obj && url == objUrl)
+ QCoreApplication::exit(-1);
+ }, Qt::QueuedConnection);
+ engine.load(url);
+
+ return app.exec();
+}
diff --git a/tests/manual/quickwindow-switch/main.qml b/tests/manual/quickwindow-switch/main.qml
new file mode 100644
index 000000000..4b474bd11
--- /dev/null
+++ b/tests/manual/quickwindow-switch/main.qml
@@ -0,0 +1,181 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.2
+import QtQuick.Window 2.12 as Win
+import Qt3D.Core 2.0
+import Qt3D.Render 2.0
+import Qt3D.Input 2.0
+import Qt3D.Extras 2.0
+
+import QtQuick.Scene3D 2.0
+
+Win.Window {
+ id: win
+ width: 300
+ height: 350
+ visible: true
+ x: Win.Screen.width / 2 - width / 2
+ y: Win.Screen.height / 2 - height / 2
+ Item {
+ id: mioitem
+ width: 300
+ height: 300
+ Scene3D {
+ id: scene3d
+ anchors.fill: parent
+ Entity {
+ id: sceneRoot
+
+ Camera {
+ id: camera
+ projectionType: CameraLens.PerspectiveProjection
+ fieldOfView: 45
+ aspectRatio: 16/9
+ nearPlane : 0.1
+ farPlane : 1000.0
+ position: Qt.vector3d( 0.0, 0.0, -40.0 )
+ upVector: Qt.vector3d( 0.0, 1.0, 0.0 )
+ viewCenter: Qt.vector3d( 0.0, 0.0, 0.0 )
+ }
+
+ OrbitCameraController {
+ camera: camera
+ }
+
+ components: [
+ RenderSettings {
+ activeFrameGraph: ForwardRenderer {
+ clearColor: Qt.rgba(0, 0.5, 1, 1)
+ camera: camera
+ }
+ },
+ // Event Source will be set by the Qt3DQuickWindow
+ InputSettings { }
+ ]
+
+ PhongMaterial {
+ id: material
+ }
+
+ TorusMesh {
+ id: torusMesh
+ radius: 5
+ minorRadius: 1
+ rings: 100
+ slices: 20
+ }
+
+ Transform {
+ id: torusTransform
+ scale3D: Qt.vector3d(1.5, 1, 0.5)
+ rotation: fromAxisAndAngle(Qt.vector3d(1, 0, 0), 45)
+ }
+
+ Entity {
+ id: torusEntity
+ components: [ torusMesh, material, torusTransform ]
+ }
+
+ SphereMesh {
+ id: sphereMesh
+ radius: 3
+ }
+
+ Transform {
+ id: sphereTransform
+ property real userAngle: 0.0
+ matrix: {
+ var m = Qt.matrix4x4();
+ m.rotate(userAngle, Qt.vector3d(0, 1, 0));
+ m.translate(Qt.vector3d(20, 0, 0));
+ return m;
+ }
+ }
+
+ NumberAnimation {
+ target: sphereTransform
+ property: "userAngle"
+ duration: 10000
+ from: 0
+ to: 360
+
+ loops: Animation.Infinite
+ running: true
+ }
+
+ Entity {
+ id: sphereEntity
+ components: [ sphereMesh, material, sphereTransform ]
+ }
+ }
+ }
+ }
+ Rectangle {
+ height: 50
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.bottom: parent.bottom
+ color: "yellow"
+ MouseArea {
+ anchors.fill: parent
+ z: 5
+ onClicked: {
+ win.screen = (win.screen === Qt.application.screens[0] ? Qt.application.screens[1] : Qt.application.screens[0])
+ }
+ }
+ Text {
+ anchors.centerIn: parent
+ minimumPointSize: 12
+ fontSizeMode: Text.Fit
+ text: "Move to the other screen"
+ }
+ }
+}
diff --git a/tests/manual/quickwindow-switch/quickwindow-switch.pro b/tests/manual/quickwindow-switch/quickwindow-switch.pro
new file mode 100644
index 000000000..2f338d36d
--- /dev/null
+++ b/tests/manual/quickwindow-switch/quickwindow-switch.pro
@@ -0,0 +1,13 @@
+TEMPLATE = app
+
+QT += 3dextras
+CONFIG += resources_big
+
+QT += 3dcore 3drender 3dinput 3dquick 3dlogic qml quick 3dquickextras
+
+SOURCES += \
+ main.cpp
+
+RESOURCES += \
+ quickwindow-switch.qrc
+
diff --git a/tests/manual/quickwindow-switch/quickwindow-switch.qrc b/tests/manual/quickwindow-switch/quickwindow-switch.qrc
new file mode 100644
index 000000000..5f6483ac3
--- /dev/null
+++ b/tests/manual/quickwindow-switch/quickwindow-switch.qrc
@@ -0,0 +1,5 @@
+<RCC>
+ <qresource prefix="/">
+ <file>main.qml</file>
+ </qresource>
+</RCC>
diff --git a/tests/manual/rendercapture-cpp/mycapture.h b/tests/manual/rendercapture-cpp/mycapture.h
index fea1abe46..f7893c8ad 100644
--- a/tests/manual/rendercapture-cpp/mycapture.h
+++ b/tests/manual/rendercapture-cpp/mycapture.h
@@ -75,7 +75,7 @@ public slots:
m_reply->saveImage("capture.bmp");
- delete m_reply;
+ m_reply->deleteLater();
m_reply = nullptr;
if (m_continuous)
diff --git a/tests/manual/rhi/main.cpp b/tests/manual/rhi/main.cpp
new file mode 100644
index 000000000..62ea0ab93
--- /dev/null
+++ b/tests/manual/rhi/main.cpp
@@ -0,0 +1,445 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QGuiApplication>
+
+#include <Qt3DCore/QEntity>
+#include <Qt3DRender/QCamera>
+#include <Qt3DRender/QCameraLens>
+#include <Qt3DCore/QTransform>
+#include <Qt3DCore/QAspectEngine>
+#include <Qt3DCore/QGeometry>
+#include <Qt3DCore/QAttribute>
+#include <Qt3DCore/QBuffer>
+
+#include <Qt3DInput/QInputAspect>
+
+#include <Qt3DRender/QRenderStateSet>
+#include <Qt3DRender/QRenderAspect>
+#include <Qt3DExtras/QForwardRenderer>
+#include <Qt3DExtras/QPerVertexColorMaterial>
+
+#include <Qt3DRender/QGeometryRenderer>
+
+#include <QPropertyAnimation>
+#include <Qt3DExtras/qt3dwindow.h>
+#include <Qt3DExtras/qorbitcameracontroller.h>
+#include <Qt3DRender/QParameter>
+#include <Qt3DRender/QEffect>
+#include <Qt3DRender/QTechnique>
+#include <Qt3DRender/QAbstractTexture>
+#include <Qt3DRender/QShaderProgram>
+#include <Qt3DRender/QRenderPass>
+#include <Qt3DRender/QBlendEquation>
+#include <Qt3DRender/QBlendEquationArguments>
+#include <Qt3DRender/QFilterKey>
+#include <Qt3DRender/QGraphicsApiFilter>
+#include <Qt3DRender/QRenderSurfaceSelector>
+#include <Qt3DRender/QViewport>
+#include <Qt3DRender/QCameraSelector>
+#include <Qt3DRender/QNoDraw>
+#include <QColor>
+#include <QVector2D>
+#include <QUrl>
+#include <QTimer>
+#include <Qt3DRender/QMaterial>
+#include <Qt3DRender/QFilterKey>
+#include <Qt3DRender/QTechnique>
+#include <Qt3DRender/QMaterial>
+#include <Qt3DRender/QTexture>
+#include <qmath.h>
+
+static const constexpr auto vertex_shader = R"_(#version 450
+
+layout(location = 0) in vec3 vertexPosition;
+layout(location = 1) in vec3 vertexColor;
+layout(location = 0) out vec3 color;
+
+layout(std140, binding = 0) uniform qt3d_render_view_uniforms {
+ mat4 viewMatrix;
+ mat4 projectionMatrix;
+ mat4 viewProjectionMatrix;
+ mat4 inverseViewMatrix;
+ mat4 inverseProjectionMatrix;
+ mat4 inverseViewProjectionMatrix;
+ mat4 viewportMatrix;
+ mat4 inverseViewportMatrix;
+ vec4 textureTransformMatrix;
+ vec3 eyePosition;
+ float aspectRatio;
+ float gamma;
+ float exposure;
+ float time;
+};
+layout(std140, binding = 1) uniform qt3d_command_uniforms {
+ mat4 modelMatrix;
+ mat4 inverseModelMatrix;
+ mat4 modelViewMatrix;
+ mat3 modelNormalMatrix;
+ mat4 inverseModelViewMatrix;
+ mat4 mvp;
+ mat4 inverseModelViewProjectionMatrix;
+};
+void main()
+{
+ color = vertexColor;
+ gl_Position = mvp * vec4(vertexPosition, 1.0);
+}
+)_";
+
+static const constexpr auto fragment_shader = R"_(#version 450
+
+layout(location = 0) out vec4 fragColor;
+layout(location = 0) in vec3 color;
+
+layout(std140, binding = 0) uniform qt3d_render_view_uniforms {
+ mat4 viewMatrix;
+ mat4 projectionMatrix;
+ mat4 viewProjectionMatrix;
+ mat4 inverseViewMatrix;
+ mat4 inverseProjectionMatrix;
+ mat4 inverseViewProjectionMatrix;
+ mat4 viewportMatrix;
+ mat4 inverseViewportMatrix;
+ vec4 textureTransformMatrix;
+ vec3 eyePosition;
+ float aspectRatio;
+ float gamma;
+ float exposure;
+ float time;
+};
+layout(std140, binding = 1) uniform qt3d_command_uniforms {
+ mat4 modelMatrix;
+ mat4 inverseModelMatrix;
+ mat4 modelViewMatrix;
+ mat3 modelNormalMatrix;
+ mat4 inverseModelViewMatrix;
+ mat4 mvp;
+ mat4 inverseModelViewProjectionMatrix;
+};
+layout(std140, binding = 2) uniform custom_ubo {
+ vec3 colorFactor;
+};
+
+layout(binding = 3) uniform sampler2D myTexture;
+void main()
+{
+ vec2 texCoord = color.xz;
+ vec2 rhiTexCoord = textureTransformMatrix.xy * texCoord+ textureTransformMatrix.zw;
+ fragColor = vec4(color * colorFactor, 1.0);
+
+ fragColor *= texture(myTexture, rhiTexCoord);
+}
+
+)_";
+
+class Material : public Qt3DRender::QMaterial
+{
+public:
+ explicit Material(Qt3DCore::QNode *parent = nullptr)
+ : QMaterial(parent)
+ , m_effect(new Qt3DRender::QEffect(this))
+{
+ setEffect(m_effect);
+
+ m_testParam = new Qt3DRender::QParameter(QStringLiteral("example"), float(0.5));
+
+ m_effect->addParameter(m_testParam);
+
+ m_filter = new Qt3DRender::QFilterKey(this);
+ m_filter->setName(QStringLiteral("renderingStyle"));
+ m_filter->setValue(QStringLiteral("forward"));
+
+ m_technique = new Qt3DRender::QTechnique(m_effect);
+ m_technique->addFilterKey(m_filter);
+
+ m_effect->addTechnique(m_technique);
+
+ m_program = new Qt3DRender::QShaderProgram(m_effect);
+ m_program->setVertexShaderCode(vertex_shader);
+ m_program->setFragmentShaderCode(fragment_shader);
+
+ m_renderPass = new Qt3DRender::QRenderPass(m_effect);
+
+ m_renderPass->setShaderProgram(m_program);
+
+ m_technique->addRenderPass(m_renderPass);
+
+ m_technique->graphicsApiFilter()->setApi(Qt3DRender::QGraphicsApiFilter::RHI);
+}
+private:
+ Qt3DRender::QEffect *m_effect{};
+ Qt3DRender::QParameter *m_testParam{};
+ Qt3DRender::QFilterKey *m_filter{};
+ Qt3DRender::QTechnique *m_technique{};
+ Qt3DRender::QShaderProgram *m_program{};
+ Qt3DRender::QRenderPass *m_renderPass{};
+};
+
+int main(int argc, char* argv[])
+{
+ qputenv("QT3D_RENDERER", "rhi");
+ QGuiApplication app(argc, argv);
+
+ auto api = Qt3DRender::API::OpenGL;
+ if (argc >= 2) {
+
+#ifdef Q_OS_WIN
+ if (argv[1] == QByteArrayLiteral("--d3d11")) api = Qt3DRender::API::DirectX;
+#endif
+
+#if QT_CONFIG(vulkan)
+ if (argv[1] == QByteArrayLiteral("--vulkan")) api = Qt3DRender::API::Vulkan;
+#endif
+
+#if defined(Q_OS_MACOS) || defined(Q_OS_IOS)
+ if (argv[1] == QByteArrayLiteral("--metal")) api = Qt3DRender::API::Metal;
+#endif
+ }
+
+ Qt3DExtras::Qt3DWindow view{nullptr, api};
+
+ // Root entity
+ Qt3DCore::QEntity *rootEntity = new Qt3DCore::QEntity();
+
+ // Camera
+ Qt3DRender::QCamera *topViewCamera = new Qt3DRender::QCamera(rootEntity);
+ topViewCamera->setPosition(QVector3D(0, 40, 0));
+ topViewCamera->setViewCenter(QVector3D(0, 0, 0));
+ topViewCamera->setUpVector(QVector3D(0, 0, 1));
+ topViewCamera->lens()->setPerspectiveProjection(45.0f, 16.0f/9.0f, 0.1f, 1000.0f);
+
+ Qt3DRender::QCamera *cameraEntity = view.camera();
+ cameraEntity->lens()->setPerspectiveProjection(45.0f, 16.0f/9.0f, 0.1f, 1000.0f);
+ cameraEntity->setPosition(QVector3D(0, 0, 40.0f));
+ cameraEntity->setUpVector(QVector3D(0, 1, 0));
+ cameraEntity->setViewCenter(QVector3D(0, 0, 0));
+
+ {
+ // Custom FG
+ auto *surfaceSelector = new Qt3DRender::QRenderSurfaceSelector();
+ surfaceSelector->setSurface(&view);
+
+ // RV 1
+ auto *clearBuffer = new Qt3DRender::QClearBuffers(surfaceSelector);
+ clearBuffer->setBuffers(Qt3DRender::QClearBuffers::ColorDepthBuffer);
+ clearBuffer->setClearColor(QColor::fromRgbF(0.1, 0.5, 0.0, 1.0));
+ auto *noDraw = new Qt3DRender::QNoDraw(clearBuffer);
+
+ // RV 2
+ auto *cameraSelector1 = new Qt3DRender::QCameraSelector(surfaceSelector);
+ cameraSelector1->setCamera(view.camera());
+ auto *viewport1 = new Qt3DRender::QViewport(cameraSelector1);
+ viewport1->setNormalizedRect(QRectF(0.0f, 0.0f, 0.5f, 0.5f));
+
+ // RV3
+ auto *cameraSelector2 = new Qt3DRender::QCameraSelector(surfaceSelector);
+ cameraSelector2->setCamera(topViewCamera);
+ auto *viewport2 = new Qt3DRender::QViewport(cameraSelector2);
+ viewport2->setNormalizedRect(QRectF(0.5f, 0.5f, 0.5f, 0.5f));
+
+ view.setActiveFrameGraph(surfaceSelector);
+ }
+
+ QTimer *cameraAnimationTimer = new QTimer(&view);
+ QObject::connect(cameraAnimationTimer, &QTimer::timeout,
+ [cameraEntity] {
+ static int angle = 0;
+ const float radius = 40.0f;
+ const float anglef = qDegreesToRadians(float(angle));
+ cameraEntity->setPosition(QVector3D(qSin(anglef), 0.0f, qCos(anglef)) * radius);
+ angle += 1;
+ });
+ cameraAnimationTimer->start(16);
+
+ // For camera controls
+ Qt3DExtras::QOrbitCameraController *camController = new Qt3DExtras::QOrbitCameraController(rootEntity);
+ camController->setCamera(cameraEntity);
+
+ // Material
+ Qt3DRender::QMaterial *material = new Material(rootEntity);
+ Qt3DRender::QParameter *parameter = new Qt3DRender::QParameter(QStringLiteral("colorFactor"), QColor(Qt::white));
+ material->addParameter(parameter);
+
+ Qt3DRender::QTextureLoader *textureLoader = new Qt3DRender::QTextureLoader{};
+ textureLoader->setSource(QUrl{QStringLiteral("qrc:///qtlogo.png")});
+ Qt3DRender::QParameter *texture = new Qt3DRender::QParameter(QStringLiteral("myTexture"), textureLoader);
+ material->addParameter(texture);
+
+ QTimer *parameterAnimationTimer = new QTimer(&view);
+ QObject::connect(parameterAnimationTimer, &QTimer::timeout,
+ [parameter] {
+ static int angle = 0;
+ const float anglef = qDegreesToRadians(float(angle));
+ parameter->setValue(QColor::fromRgbF(fabs(qCos(anglef)), fabs(qSin(anglef)), 1.0f));
+ angle += 10;
+ });
+ parameterAnimationTimer->start(16);
+
+ // Torus
+ Qt3DCore::QEntity *customMeshEntity = new Qt3DCore::QEntity(rootEntity);
+
+ // Transform
+ Qt3DCore::QTransform *transform = new Qt3DCore::QTransform;
+ transform->setScale(8.0f);
+
+ // Custom Mesh (TetraHedron)
+ Qt3DRender::QGeometryRenderer *customMeshRenderer = new Qt3DRender::QGeometryRenderer;
+ Qt3DCore::QGeometryView *customMeshView = new Qt3DCore::QGeometryView;
+ Qt3DCore::QGeometry *customGeometry = new Qt3DCore::QGeometry;
+
+ Qt3DCore::QBuffer *vertexDataBuffer = new Qt3DCore::QBuffer(customGeometry);
+ Qt3DCore::QBuffer *indexDataBuffer = new Qt3DCore::QBuffer(customGeometry);
+
+ // 5 vertices of 3 vertices + 3 colors
+ QByteArray vertexBufferData;
+ vertexBufferData.resize(5 * (3 + 3) * sizeof(float));
+
+ // Vertices
+ QVector3D v0(-1.0f, 0.0f, -1.0f);
+ QVector3D v1(1.0f, 0.0f, -1.0f);
+ QVector3D v2(-1.0f, 0.0f, 1.0f);
+ QVector3D v3(1.0f, 0.0f, 1.0f);
+ QVector3D v4(0.0f, 2.0f, 0.0f);
+
+ QVector3D red(1.0f, 0.0f, 0.0f);
+ QVector3D green(0.0f, 1.0f, 0.0f);
+ QVector3D blue(0.0f, 0.0f, 1.0f);
+ QVector3D white(1.0f, 1.0f, 1.0f);
+ QVector3D grey(0.5f, 0.5f, 0.5f);
+
+ const QVector<QVector3D> vertices = QVector<QVector3D>()
+ << v0 << red
+ << v1 << green
+ << v2 << blue
+ << v3 << grey
+ << v4 << white;
+
+ memcpy(vertexBufferData.data(), vertices.constData(), vertices.size() * sizeof(QVector3D));
+ vertexDataBuffer->setData(vertexBufferData);
+
+ QByteArray indexBufferData;
+ // 6 triangle faces
+ indexBufferData.resize(6 * 3 * sizeof(ushort));
+ ushort *rawIndexArray = reinterpret_cast<ushort *>(indexBufferData.data());
+
+ rawIndexArray[0] = 0;
+ rawIndexArray[1] = 1;
+ rawIndexArray[2] = 2;
+
+ rawIndexArray[3] = 2;
+ rawIndexArray[4] = 1;
+ rawIndexArray[5] = 3;
+
+ rawIndexArray[6] = 2;
+ rawIndexArray[7] = 3;
+ rawIndexArray[8] = 4;
+
+ rawIndexArray[9] = 3;
+ rawIndexArray[10] = 1;
+ rawIndexArray[11] = 4;
+
+ rawIndexArray[12] = 1;
+ rawIndexArray[13] = 0;
+ rawIndexArray[14] = 4;
+
+ rawIndexArray[15] = 0;
+ rawIndexArray[16] = 2;
+ rawIndexArray[17] = 4;
+
+ indexDataBuffer->setData(indexBufferData);
+
+ // Attributes
+ Qt3DCore::QAttribute *positionAttribute = new Qt3DCore::QAttribute();
+ positionAttribute->setAttributeType(Qt3DCore::QAttribute::VertexAttribute);
+ positionAttribute->setBuffer(vertexDataBuffer);
+ positionAttribute->setVertexBaseType(Qt3DCore::QAttribute::Float);
+ positionAttribute->setVertexSize(3);
+ positionAttribute->setByteOffset(0);
+ positionAttribute->setByteStride(6 * sizeof(float));
+ positionAttribute->setCount(5);
+ positionAttribute->setName(Qt3DCore::QAttribute::defaultPositionAttributeName());
+
+ Qt3DCore::QAttribute *colorAttribute = new Qt3DCore::QAttribute();
+ colorAttribute->setAttributeType(Qt3DCore::QAttribute::VertexAttribute);
+ colorAttribute->setBuffer(vertexDataBuffer);
+ colorAttribute->setVertexBaseType(Qt3DCore::QAttribute::Float);
+ colorAttribute->setVertexSize(3);
+ colorAttribute->setByteOffset(3 * sizeof(float));
+ colorAttribute->setByteStride(6 * sizeof(float));
+ colorAttribute->setCount(5);
+ colorAttribute->setName(Qt3DCore::QAttribute::defaultColorAttributeName());
+
+ Qt3DCore::QAttribute *indexAttribute = new Qt3DCore::QAttribute();
+ indexAttribute->setAttributeType(Qt3DCore::QAttribute::IndexAttribute);
+ indexAttribute->setBuffer(indexDataBuffer);
+ indexAttribute->setVertexBaseType(Qt3DCore::QAttribute::UnsignedShort);
+ indexAttribute->setVertexSize(1);
+ indexAttribute->setByteOffset(0);
+ indexAttribute->setByteStride(0);
+ indexAttribute->setCount(18);
+
+ customGeometry->addAttribute(positionAttribute);
+ customGeometry->addAttribute(colorAttribute);
+ customGeometry->addAttribute(indexAttribute);
+
+ customMeshView->setPrimitiveType(Qt3DCore::QGeometryView::Triangles);
+ customMeshView->setGeometry(customGeometry);
+ customMeshRenderer->setView(customMeshView);
+
+ customMeshEntity->addComponent(customMeshRenderer);
+ customMeshEntity->addComponent(transform);
+ customMeshEntity->addComponent(material);
+
+ view.setRootEntity(rootEntity);
+ view.show();
+
+ return app.exec();
+}
diff --git a/tests/manual/rhi/qtlogo.png b/tests/manual/rhi/qtlogo.png
new file mode 100644
index 000000000..19eecfabf
--- /dev/null
+++ b/tests/manual/rhi/qtlogo.png
Binary files differ
diff --git a/tests/manual/rhi/rhi.pro b/tests/manual/rhi/rhi.pro
new file mode 100644
index 000000000..94e560391
--- /dev/null
+++ b/tests/manual/rhi/rhi.pro
@@ -0,0 +1,10 @@
+!include( ../manual.pri ) {
+ error( "Couldn't find the manual.pri file!" )
+}
+
+QT += 3dcore 3drender 3dinput 3dextras
+
+SOURCES += \
+ main.cpp
+
+RESOURCES += qtlogo.png
diff --git a/tools/qgltf/qgltf.cpp b/tools/qgltf/qgltf.cpp
index 20073677b..559ad7691 100644
--- a/tools/qgltf/qgltf.cpp
+++ b/tools/qgltf/qgltf.cpp
@@ -152,7 +152,7 @@ static QIODevice::OpenMode openModeFromText(const char *name) noexcept
{
static const struct OpenModeMapping {
char name[2];
- ushort mode;
+ int mode;
} openModeMapping[] = {
{ { 'r', 0 }, QIODevice::ReadOnly },
{ { 'r', '+' }, QIODevice::ReadWrite },