summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.qmake.conf2
-rw-r--r--src/animation/backend/findrunningclipanimatorsjob.cpp2
-rw-r--r--src/core/nodes/qentity.cpp7
-rw-r--r--src/core/nodes/qentity.h13
-rw-r--r--src/plugins/sceneparsers/gltf/gltfimporter.cpp772
-rw-r--r--src/plugins/sceneparsers/gltf/gltfimporter.h15
-rw-r--r--src/quick3d/imports/render/qt3dquick3drenderplugin.cpp9
-rw-r--r--src/render/backend/abstractrenderer_p.h1
-rw-r--r--src/render/backend/computecommand.cpp30
-rw-r--r--src/render/backend/computecommand_p.h8
-rw-r--r--src/render/backend/managers_p.h5
-rw-r--r--src/render/backend/nodemanagers.cpp2
-rw-r--r--src/render/backend/nodemanagers_p.h3
-rw-r--r--src/render/framegraph/blitframebuffer.cpp8
-rw-r--r--src/render/framegraph/cameraselectornode.cpp5
-rw-r--r--src/render/framegraph/clearbuffers.cpp6
-rw-r--r--src/render/framegraph/dispatchcompute.cpp11
-rw-r--r--src/render/framegraph/framegraph.pri14
-rw-r--r--src/render/framegraph/framegraphnode.cpp19
-rw-r--r--src/render/framegraph/framegraphnode_p.h4
-rw-r--r--src/render/framegraph/layerfilternode.cpp5
-rw-r--r--src/render/framegraph/memorybarrier.cpp2
-rw-r--r--src/render/framegraph/proximityfilter.cpp8
-rw-r--r--src/render/framegraph/qsetfence.cpp174
-rw-r--r--src/render/framegraph/qsetfence.h86
-rw-r--r--src/render/framegraph/qsetfence_p.h82
-rw-r--r--src/render/framegraph/qwaitfence.cpp219
-rw-r--r--src/render/framegraph/qwaitfence.h98
-rw-r--r--src/render/framegraph/qwaitfence_p.h85
-rw-r--r--src/render/framegraph/rendercapture.cpp1
-rw-r--r--src/render/framegraph/renderpassfilternode.cpp8
-rw-r--r--src/render/framegraph/rendersurfaceselector.cpp17
-rw-r--r--src/render/framegraph/rendertargetselectornode.cpp8
-rw-r--r--src/render/framegraph/setfence.cpp79
-rw-r--r--src/render/framegraph/setfence_p.h80
-rw-r--r--src/render/framegraph/sortpolicy.cpp2
-rw-r--r--src/render/framegraph/statesetnode.cpp4
-rw-r--r--src/render/framegraph/techniquefilternode.cpp8
-rw-r--r--src/render/framegraph/viewportnode.cpp3
-rw-r--r--src/render/framegraph/waitfence.cpp107
-rw-r--r--src/render/framegraph/waitfence_p.h85
-rw-r--r--src/render/frontend/qcomputecommand.cpp102
-rw-r--r--src/render/frontend/qcomputecommand.h13
-rw-r--r--src/render/frontend/qcomputecommand_p.h7
-rw-r--r--src/render/frontend/qpickingsettings.cpp8
-rw-r--r--src/render/frontend/qpickingsettings.h3
-rw-r--r--src/render/frontend/qrenderaspect.cpp15
-rw-r--r--src/render/geometry/geometry.cpp39
-rw-r--r--src/render/geometry/geometry_p.h10
-rw-r--r--src/render/geometry/qgeometry.cpp56
-rw-r--r--src/render/geometry/qgeometry.h8
-rw-r--r--src/render/geometry/qgeometry_p.h3
-rw-r--r--src/render/jobs/calcboundingvolumejob.cpp58
-rw-r--r--src/render/jobs/job_common_p.h3
-rw-r--r--src/render/jobs/pickboundingvolumejob.cpp16
-rw-r--r--src/render/jobs/pickboundingvolumeutils.cpp132
-rw-r--r--src/render/jobs/pickboundingvolumeutils_p.h14
-rw-r--r--src/render/jobs/raycastingjob.cpp6
-rw-r--r--src/render/jobs/sendbuffercapturejob.cpp6
-rw-r--r--src/render/jobs/sendbuffercapturejob_p.h1
-rw-r--r--src/render/materialsystem/qshaderprogrambuilder.cpp155
-rw-r--r--src/render/materialsystem/qshaderprogrambuilder.h19
-rw-r--r--src/render/materialsystem/qshaderprogrambuilder_p.h6
-rw-r--r--src/render/materialsystem/shaderbuilder.cpp33
-rw-r--r--src/render/picking/objectpicker.cpp15
-rw-r--r--src/render/picking/objectpicker_p.h5
-rw-r--r--src/render/picking/qobjectpicker.cpp37
-rw-r--r--src/render/picking/qobjectpicker.h5
-rw-r--r--src/render/picking/qobjectpicker_p.h3
-rw-r--r--src/render/renderers/opengl/graphicshelpers/glfence_p.h73
-rw-r--r--src/render/renderers/opengl/graphicshelpers/graphicshelperes2.cpp27
-rw-r--r--src/render/renderers/opengl/graphicshelpers/graphicshelperes2_p.h6
-rw-r--r--src/render/renderers/opengl/graphicshelpers/graphicshelperes3.cpp52
-rw-r--r--src/render/renderers/opengl/graphicshelpers/graphicshelperes3_p.h6
-rw-r--r--src/render/renderers/opengl/graphicshelpers/graphicshelpergl2.cpp28
-rw-r--r--src/render/renderers/opengl/graphicshelpers/graphicshelpergl2_p.h6
-rw-r--r--src/render/renderers/opengl/graphicshelpers/graphicshelpergl3_2.cpp32
-rw-r--r--src/render/renderers/opengl/graphicshelpers/graphicshelpergl3_2_p.h6
-rw-r--r--src/render/renderers/opengl/graphicshelpers/graphicshelpergl3_3.cpp32
-rw-r--r--src/render/renderers/opengl/graphicshelpers/graphicshelpergl3_3_p.h6
-rw-r--r--src/render/renderers/opengl/graphicshelpers/graphicshelpergl4.cpp35
-rw-r--r--src/render/renderers/opengl/graphicshelpers/graphicshelpergl4_p.h6
-rw-r--r--src/render/renderers/opengl/graphicshelpers/graphicshelperinterface_p.h9
-rw-r--r--src/render/renderers/opengl/graphicshelpers/graphicshelpers.pri3
-rw-r--r--src/render/renderers/opengl/graphicshelpers/submissioncontext.cpp47
-rw-r--r--src/render/renderers/opengl/graphicshelpers/submissioncontext_p.h9
-rw-r--r--src/render/renderers/opengl/jobs/renderviewjobutils.cpp12
-rw-r--r--src/render/renderers/opengl/renderer/renderer.cpp159
-rw-r--r--src/render/renderers/opengl/renderer/renderer_p.h15
-rw-r--r--src/render/renderers/opengl/renderer/renderview.cpp5
-rw-r--r--src/render/renderers/opengl/renderer/renderview_p.h10
-rw-r--r--src/render/renderers/opengl/textures/gltexture.cpp260
-rw-r--r--src/render/renderers/opengl/textures/gltexture_p.h8
-rw-r--r--src/render/texture/apitexturemanager_p.h14
-rw-r--r--src/render/texture/qabstracttexture.cpp85
-rw-r--r--src/render/texture/qabstracttexture.h14
-rw-r--r--src/render/texture/qabstracttexture_p.h6
-rw-r--r--src/render/texture/qtexture.cpp63
-rw-r--r--src/render/texture/qtexture.h17
-rw-r--r--src/render/texture/texture.cpp61
-rw-r--r--src/render/texture/texture_p.h14
-rw-r--r--tests/auto/animation/findrunningclipanimatorsjob/tst_findrunningclipanimatorsjob.cpp56
-rw-r--r--tests/auto/core/qentity/tst_qentity.cpp33
-rw-r--r--tests/auto/core/threadpooler/tst_threadpooler.cpp6
-rw-r--r--tests/auto/quick3d/3drender/3drender.qml4
-rw-r--r--tests/auto/render/blitframebuffer/tst_blitframebuffer.cpp14
-rw-r--r--tests/auto/render/commons/testrenderer.h1
-rw-r--r--tests/auto/render/computecommand/tst_computecommand.cpp75
-rw-r--r--tests/auto/render/geometry/tst_geometry.cpp28
-rw-r--r--tests/auto/render/graphicshelpergl2/tst_graphicshelpergl2.cpp39
-rw-r--r--tests/auto/render/graphicshelpergl3_2/tst_graphicshelpergl3_2.cpp104
-rw-r--r--tests/auto/render/graphicshelpergl3_3/tst_graphicshelpergl3_3.cpp104
-rw-r--r--tests/auto/render/graphicshelpergl4/tst_graphicshelpergl4.cpp106
-rw-r--r--tests/auto/render/memorybarrier/tst_memorybarrier.cpp4
-rw-r--r--tests/auto/render/objectpicker/tst_objectpicker.cpp36
-rw-r--r--tests/auto/render/pickboundingvolumejob/pickboundingvolumejob.qrc1
-rw-r--r--tests/auto/render/pickboundingvolumejob/testscene_priorityoverlapping.qml135
-rw-r--r--tests/auto/render/pickboundingvolumejob/tst_pickboundingvolumejob.cpp105
-rw-r--r--tests/auto/render/proximityfilter/tst_proximityfilter.cpp6
-rw-r--r--tests/auto/render/qabstracttexture/tst_qabstracttexture.cpp249
-rw-r--r--tests/auto/render/qcomputecommand/tst_qcomputecommand.cpp163
-rw-r--r--tests/auto/render/qgeometry/tst_qgeometry.cpp55
-rw-r--r--tests/auto/render/qobjectpicker/tst_qobjectpicker.cpp160
-rw-r--r--tests/auto/render/qsetfence/qsetfence.pro12
-rw-r--r--tests/auto/render/qsetfence/tst_qsetfence.cpp190
-rw-r--r--tests/auto/render/qshaderprogrambuilder/tst_qshaderprogrambuilder.cpp92
-rw-r--r--tests/auto/render/qsharedgltexture/qsharedgltexture.pro12
-rw-r--r--tests/auto/render/qsharedgltexture/tst_qsharedgltexture.cpp171
-rw-r--r--tests/auto/render/qwaitfence/qwaitfence.pro12
-rw-r--r--tests/auto/render/qwaitfence/tst_qwaitfence.cpp349
-rw-r--r--tests/auto/render/render.pro9
-rw-r--r--tests/auto/render/renderer/tst_renderer.cpp115
-rw-r--r--tests/auto/render/setfence/setfence.pro12
-rw-r--r--tests/auto/render/setfence/tst_setfence.cpp163
-rw-r--r--tests/auto/render/shaderbuilder/tst_shaderbuilder.cpp79
-rw-r--r--tests/auto/render/texture/tst_texture.cpp13
-rw-r--r--tests/auto/render/textures/tst_textures.cpp94
-rw-r--r--tests/auto/render/waitfence/tst_waitfence.cpp186
-rw-r--r--tests/auto/render/waitfence/waitfence.pro12
-rw-r--r--tests/manual/manual.pro6
-rw-r--r--tests/manual/sharedtexture/main.cpp159
-rw-r--r--tests/manual/sharedtexture/sharedtexture.pro12
-rw-r--r--tests/manual/sharedtexture/videoplayer.cpp233
-rw-r--r--tests/manual/sharedtexture/videoplayer.h122
-rw-r--r--tests/manual/sharedtextureqml/main.cpp131
-rw-r--r--tests/manual/sharedtextureqml/main.qml144
-rw-r--r--tests/manual/sharedtextureqml/qml.qrc5
-rw-r--r--tests/manual/sharedtextureqml/sharedtextureqml.pro17
-rw-r--r--tests/manual/texture_property_updates/main.cpp2
-rw-r--r--tests/manual/texture_property_updates/main.qml4
150 files changed, 7412 insertions, 429 deletions
diff --git a/.qmake.conf b/.qmake.conf
index a0ca2b98b..b5c63a38f 100644
--- a/.qmake.conf
+++ b/.qmake.conf
@@ -3,4 +3,4 @@ QT3D_BUILD_ROOT = $$shadowed($$PWD)
load(qt_build_config)
-MODULE_VERSION = 5.12.2
+MODULE_VERSION = 5.13.0
diff --git a/src/animation/backend/findrunningclipanimatorsjob.cpp b/src/animation/backend/findrunningclipanimatorsjob.cpp
index b55c84e77..fde3f7db8 100644
--- a/src/animation/backend/findrunningclipanimatorsjob.cpp
+++ b/src/animation/backend/findrunningclipanimatorsjob.cpp
@@ -65,6 +65,8 @@ void FindRunningClipAnimatorsJob::run()
for (const auto &clipAnimatorHandle : qAsConst(m_clipAnimatorHandles)) {
ClipAnimator *clipAnimator = clipAnimatorManager->data(clipAnimatorHandle);
Q_ASSERT(clipAnimator);
+ if (!clipAnimator->isEnabled())
+ continue;
const bool canRun = clipAnimator->canRun();
const bool running = clipAnimator->isRunning();
diff --git a/src/core/nodes/qentity.cpp b/src/core/nodes/qentity.cpp
index 64ea65087..3c8805a67 100644
--- a/src/core/nodes/qentity.cpp
+++ b/src/core/nodes/qentity.cpp
@@ -77,6 +77,13 @@ namespace Qt3DCore {
\sa Qt3DCore::QComponent, Qt3DCore::QTransform
*/
+/*!
+ \fn template<typename T> QVector<T *> QEntity::componentsOfType() const
+
+ Returns all the components added to this entity that can be cast to
+ type T or an empty vector if there are no such components.
+*/
+
/*! \internal */
QEntityPrivate::QEntityPrivate()
: QNodePrivate()
diff --git a/src/core/nodes/qentity.h b/src/core/nodes/qentity.h
index dc7dc62c1..ef6aedc4d 100644
--- a/src/core/nodes/qentity.h
+++ b/src/core/nodes/qentity.h
@@ -62,6 +62,19 @@ public:
QComponentVector components() const;
+ template<class T>
+ QVector<T *> componentsOfType() const
+ {
+ QVector<T*> matchComponents;
+ const QComponentVector components = this->components();
+ for (QComponent *component : components) {
+ T *typedComponent = qobject_cast<T*>(component);
+ if (typedComponent != nullptr)
+ matchComponents.append(typedComponent);
+ }
+ return matchComponents;
+ }
+
void addComponent(QComponent *comp);
void removeComponent(QComponent *comp);
diff --git a/src/plugins/sceneparsers/gltf/gltfimporter.cpp b/src/plugins/sceneparsers/gltf/gltfimporter.cpp
index 996077b16..79f2bf2d3 100644
--- a/src/plugins/sceneparsers/gltf/gltfimporter.cpp
+++ b/src/plugins/sceneparsers/gltf/gltfimporter.cpp
@@ -84,6 +84,7 @@
#include <Qt3DRender/qshaderprogram.h>
#include <Qt3DRender/qtechnique.h>
#include <Qt3DRender/qtexture.h>
+#include <Qt3DRender/qtextureimagedatagenerator.h>
#include <Qt3DRender/qdirectionallight.h>
#include <Qt3DRender/qspotlight.h>
#include <Qt3DRender/qpointlight.h>
@@ -97,6 +98,7 @@
#include <Qt3DExtras/qnormaldiffusespecularmapmaterial.h>
#include <Qt3DExtras/qgoochmaterial.h>
#include <Qt3DExtras/qpervertexcolormaterial.h>
+#include <Qt3DExtras/qmetalroughmaterial.h>
#include <Qt3DExtras/qconemesh.h>
#include <Qt3DExtras/qcuboidmesh.h>
#include <Qt3DExtras/qcylindermesh.h>
@@ -106,17 +108,60 @@
#include <private/qurlhelper_p.h>
+/**
+ * glTF 2.0 conformance report
+ *
+ * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0
+ * Samples: https://github.com/KhronosGroup/glTF-Sample-Models/tree/master/2.0
+ *
+ * Most of the reference samples are rendered correctly, with the following exceptions:
+ *
+ * 'extensions' and 'extras' are ignored everywhere except in nodes.
+ *
+ * asset
+ * generator, copyright, minVersion: not parsed
+ * accessors
+ * min, max, normalized, sparse: not parsed
+ * animations
+ * the whole object is not parsed
+ * buffers
+ * all parsed
+ * bufferViews
+ * all parsed
+ * cameras
+ * all parsed
+ * images
+ * mimeType, bufferView, name: not parsed
+ * materials
+ * emissiveTexture, emissiveFactor: not parsed
+ * alphaMode, alphaCutoff, doubleSided: not parsed
+ * texCoord, strength: not parsed
+ * meshes
+ * weights: not parsed
+ * nodes
+ * skin, weights: not parsed
+ * samplers
+ * all parsed
+ * scenes
+ * all parsed
+ * textures
+ * all parsed
+ */
+
#ifndef qUtf16PrintableImpl // -Impl is a Qt 5.8 feature
# define qUtf16PrintableImpl(string) \
static_cast<const wchar_t*>(static_cast<const void*>(string.utf16()))
#endif
+#define KEY_ASSET QLatin1String("asset")
+#define KEY_VERSION QLatin1String("version")
#define KEY_CAMERA QLatin1String("camera")
#define KEY_CAMERAS QLatin1String("cameras")
#define KEY_SCENES QLatin1String("scenes")
#define KEY_NODES QLatin1String("nodes")
#define KEY_MESHES QLatin1String("meshes")
#define KEY_CHILDREN QLatin1String("children")
+#define KEY_MESH QLatin1String("mesh")
#define KEY_MATRIX QLatin1String("matrix")
#define KEY_ROTATION QLatin1String("rotation")
#define KEY_SCALE QLatin1String("scale")
@@ -181,6 +226,15 @@
#define KEY_LINEAR_ATTENUATION QLatin1String("linearAttenuation")
#define KEY_QUAD_ATTENUATION QLatin1String("quadraticAttenuation")
#define KEY_INTENSITY QLatin1String("intensity")
+#define KEY_PBR_METAL_ROUGH QLatin1String("pbrMetallicRoughness")
+#define KEY_BASE_COLOR QLatin1String("baseColorFactor")
+#define KEY_BASE_COLOR_TEX QLatin1String("baseColorTexture")
+#define KEY_METAL_FACTOR QLatin1String("metallicFactor")
+#define KEY_METAL_ROUGH_TEX QLatin1String("metallicRoughnessTexture")
+#define KEY_ROUGH_FACTOR QLatin1String("roughnessFactor")
+#define KEY_NORMAL_TEX QLatin1String("normalTexture")
+#define KEY_OCCLUSION_TEX QLatin1String("occlusionTexture")
+#define KEY_INDEX QLatin1String("index")
#define KEY_INSTANCE_TECHNIQUE QLatin1String("instanceTechnique")
#define KEY_INSTANCE_PROGRAM QLatin1String("instanceProgram")
@@ -229,6 +283,18 @@ inline QVector3D jsonArrToVec3(const QJsonArray &array)
return QVector3D(array[0].toDouble(), array[1].toDouble(), array[2].toDouble());
}
+inline QVector4D jsonArrToVec4(const QJsonArray &array)
+{
+ return QVector4D(array[0].toDouble(), array[1].toDouble(),
+ array[2].toDouble(), array[3].toDouble());
+}
+
+inline QVariant jsonArrToColorVariant(const QJsonArray &array)
+{
+ return QVariant(QColor::fromRgbF(array[0].toDouble(), array[1].toDouble(),
+ array[2].toDouble(), array[3].toDouble()));
+}
+
inline QColor vec4ToQColor(const QVariant &vec4Var)
{
const QVector4D v = vec4Var.value<QVector4D>();
@@ -255,11 +321,40 @@ Qt3DRender::QFilterKey *buildFilterKey(const QString &key, const QJsonValue &val
namespace Qt3DRender {
-Q_LOGGING_CATEGORY(GLTFImporterLog, "Qt3D.GLTFImport", QtWarningMsg)
+Q_LOGGING_CATEGORY(GLTFImporterLog, "Qt3D.GLTFImport", QtWarningMsg);
+class GLTFRawTextureImage : public QAbstractTextureImage
+{
+ Q_OBJECT
+public:
+ explicit GLTFRawTextureImage(QNode *parent = nullptr);
+
+ QTextureImageDataGeneratorPtr dataGenerator() const final;
+
+ void setImage(const QImage &image);
+
+private:
+ QImage m_image;
-GLTFImporter::GLTFImporter() : QSceneImporter(),
- m_parseDone(false)
+ class GLTFRawTextureImageFunctor : public QTextureImageDataGenerator
+ {
+ public:
+ explicit GLTFRawTextureImageFunctor(const QImage &image);
+
+ QTextureImageDataPtr operator()() final;
+ bool operator ==(const QTextureImageDataGenerator &other) const final;
+
+ QT3D_FUNCTOR(GLTFRawTextureImageFunctor)
+ private:
+ QImage m_image;
+ };
+};
+
+GLTFImporter::GLTFImporter()
+ : QSceneImporter()
+ , m_parseDone(false)
+ , m_majorVersion(1)
+ , m_minorVersion(0)
{
}
@@ -286,9 +381,9 @@ void GLTFImporter::setBasePath(const QString& path)
Set a \a json document as the file used for importing a scene.
Returns true if the operation is successful.
*/
-bool GLTFImporter::setJSON(const QJsonDocument &json )
+bool GLTFImporter::setJSON(const QJsonDocument &json)
{
- if ( !json.isObject() ) {
+ if (!json.isObject()) {
return false;
}
@@ -360,12 +455,24 @@ bool GLTFImporter::areFileTypesSupported(const QStringList &extensions) const
*/
Qt3DCore::QEntity* GLTFImporter::node(const QString &id)
{
- QJsonObject nodes = m_json.object().value(KEY_NODES).toObject();
- const auto jsonVal = nodes.value(id);
- if (Q_UNLIKELY(jsonVal.isUndefined())) {
- qCWarning(GLTFImporterLog, "unknown node %ls in GLTF file %ls",
- qUtf16PrintableImpl(id), qUtf16PrintableImpl(m_basePath));
- return NULL;
+ QJsonValue jsonVal;
+
+ if (m_majorVersion > 1) {
+ const QJsonArray nodes = m_json.object().value(KEY_NODES).toArray();
+ if (Q_UNLIKELY(id.toInt() >= nodes.count())) {
+ qCWarning(GLTFImporterLog, "unknown node %ls in GLTF file %ls",
+ qUtf16PrintableImpl(id), qUtf16PrintableImpl(m_basePath));
+ return nullptr;
+ }
+ jsonVal = nodes[id.toInt()];
+ } else {
+ const QJsonObject nodes = m_json.object().value(KEY_NODES).toObject();
+ jsonVal = nodes.value(id);
+ if (Q_UNLIKELY(jsonVal.isUndefined())) {
+ qCWarning(GLTFImporterLog, "unknown node %ls in GLTF file %ls",
+ qUtf16PrintableImpl(id), qUtf16PrintableImpl(m_basePath));
+ return nullptr;
+ }
}
const QJsonObject jsonObj = jsonVal.toObject();
@@ -376,27 +483,44 @@ Qt3DCore::QEntity* GLTFImporter::node(const QString &id)
// Otherwise if there are n meshes, there is 1 QEntity, with n children for each mesh/material combo
{
QVector<QEntity *> entities;
-
- const auto meshes = jsonObj.value(KEY_MESHES).toArray();
- for (const QJsonValue &mesh : meshes) {
- const QString meshName = mesh.toString();
- const auto geometryRenderers = qAsConst(m_meshDict).equal_range(meshName);
- if (Q_UNLIKELY(geometryRenderers.first == geometryRenderers.second)) {
- qCWarning(GLTFImporterLog, "node %ls references unknown mesh %ls",
- qUtf16PrintableImpl(id), qUtf16PrintableImpl(meshName));
- continue;
+ const QJsonValue meshesValue = jsonObj.value(KEY_MESHES);
+
+ if (meshesValue.isUndefined()) {
+ const QJsonValue mesh = jsonObj.value(KEY_MESH);
+ if (!mesh.isUndefined()) {
+ const QString meshName = QString::number(mesh.toInt());
+ const auto geometryRenderers = qAsConst(m_meshDict).equal_range(meshName);
+ for (auto it = geometryRenderers.first; it != geometryRenderers.second; ++it) {
+ QGeometryRenderer *geometryRenderer = it.value();
+ QEntity *entity = new QEntity;
+ entity->addComponent(geometryRenderer);
+ QMaterial *mat = material(m_meshMaterialDict[geometryRenderer]);
+ if (mat)
+ entity->addComponent(mat);
+ entities.append(entity);
+ }
}
+ } else {
+ const auto meshes = meshesValue.toArray();
+ for (const QJsonValue &mesh : meshes) {
+ const QString meshName = mesh.toString();
+ const auto geometryRenderers = qAsConst(m_meshDict).equal_range(meshName);
+ if (Q_UNLIKELY(geometryRenderers.first == geometryRenderers.second)) {
+ qCWarning(GLTFImporterLog, "node %ls references unknown mesh %ls",
+ qUtf16PrintableImpl(id), qUtf16PrintableImpl(meshName));
+ continue;
+ }
- for (auto it = geometryRenderers.first; it != geometryRenderers.second; ++it) {
- QGeometryRenderer *geometryRenderer = it.value();
- QEntity *entity = new QEntity;
- entity->addComponent(geometryRenderer);
- QMaterial *mat = material(m_meshMaterialDict[geometryRenderer]);
- if (mat)
- entity->addComponent(mat);
- entities.append(entity);
+ for (auto it = geometryRenderers.first; it != geometryRenderers.second; ++it) {
+ QGeometryRenderer *geometryRenderer = it.value();
+ QEntity *entity = new QEntity;
+ entity->addComponent(geometryRenderer);
+ QMaterial *mat = material(m_meshMaterialDict[geometryRenderer]);
+ if (mat)
+ entity->addComponent(mat);
+ entities.append(entity);
+ }
}
-
}
switch (entities.size()) {
@@ -437,14 +561,13 @@ Qt3DCore::QEntity* GLTFImporter::node(const QString &id)
}
}
- {
- const auto children = jsonObj.value(KEY_CHILDREN).toArray();
- for (const QJsonValue &c : children) {
- QEntity* child = node(c.toString());
- if (!child)
- continue;
- child->setParent(result);
- }
+ // recursively retrieve children
+ const auto children = jsonObj.value(KEY_CHILDREN).toArray();
+ for (const QJsonValue &c : children) {
+ QEntity* child = node((m_majorVersion > 1) ? QString::number(c.toInt()) : c.toString());
+ if (!child)
+ continue;
+ child->setParent(result);
}
renameFromJson(jsonObj, result);
@@ -454,8 +577,8 @@ Qt3DCore::QEntity* GLTFImporter::node(const QString &id)
QMatrix4x4 m(Qt::Uninitialized);
QJsonArray matrixValues = matrix.toArray();
- for (int i=0; i<16; ++i) {
- double v = matrixValues.at( i ).toDouble();
+ for (int i = 0; i < 16; ++i) {
+ double v = matrixValues.at(i).toDouble();
m(i % 4, i >> 2) = v;
}
@@ -469,11 +592,7 @@ Qt3DCore::QEntity* GLTFImporter::node(const QString &id)
if (!trans)
trans = new Qt3DCore::QTransform;
- const QJsonArray quaternionValues = rotation.toArray();
- QQuaternion quaternion(quaternionValues[0].toDouble(),
- quaternionValues[1].toDouble(),
- quaternionValues[2].toDouble(),
- quaternionValues[3].toDouble());
+ QQuaternion quaternion(jsonArrToVec4(rotation.toArray()));
trans->setRotation(quaternion);
}
@@ -499,9 +618,10 @@ Qt3DCore::QEntity* GLTFImporter::node(const QString &id)
const bool newLens = cameraLens == nullptr;
if (newLens)
cameraLens = new QCameraLens;
- if (!fillCamera(*cameraLens, cameraEntity, cameraVal.toString())) {
+ const QString cameraID = (m_majorVersion > 1) ? QString::number(cameraVal.toInt()) : cameraVal.toString();
+ if (!fillCamera(*cameraLens, cameraEntity, cameraID)) {
qCWarning(GLTFImporterLog, "failed to build camera: %ls on node %ls",
- qUtf16PrintableImpl(cameraVal.toString()), qUtf16PrintableImpl(id));
+ qUtf16PrintableImpl(cameraID), qUtf16PrintableImpl(id));
} else if (newLens) {
result->addComponent(cameraLens);
}
@@ -511,7 +631,8 @@ Qt3DCore::QEntity* GLTFImporter::node(const QString &id)
if (!extensionsVal.isUndefined()) {
const auto commonMat = extensionsVal.toObject().value(KEY_COMMON_MAT);
if (!commonMat.isUndefined()) {
- const auto lightId = commonMat.toObject().value(KEY_LIGHT).toString();
+ const QJsonValue lightVal = commonMat.toObject().value(KEY_LIGHT);
+ const QString lightId = (m_majorVersion > 1) ? QString::number(lightVal.toInt()) : lightVal.toString();
QAbstractLight *lightComp = m_lights.value(lightId);
if (Q_UNLIKELY(!lightComp)) {
qCWarning(GLTFImporterLog, "failed to find light: %ls for node %ls",
@@ -532,24 +653,46 @@ Qt3DCore::QEntity* GLTFImporter::scene(const QString &id)
{
parse();
- QJsonObject scenes = m_json.object().value(KEY_SCENES).toObject();
- const auto sceneVal = scenes.value(id);
- if (Q_UNLIKELY(sceneVal.isUndefined())) {
- if (Q_UNLIKELY(!id.isNull()))
- qCWarning(GLTFImporterLog, "GLTF: no such scene %ls in file %ls",
- qUtf16PrintableImpl(id), qUtf16PrintableImpl(m_basePath));
- return defaultScene();
- }
+ QEntity* sceneEntity = nullptr;
- QJsonObject sceneObj = sceneVal.toObject();
- QEntity* sceneEntity = new QEntity;
- const auto nodes = sceneObj.value(KEY_NODES).toArray();
- for (const QJsonValue &nnv : nodes) {
- QString nodeName = nnv.toString();
- QEntity* child = node(nodeName);
- if (!child)
- continue;
- child->setParent(sceneEntity);
+ if (m_majorVersion > 1) {
+ const QJsonArray scenes = m_json.object().value(KEY_SCENES).toArray();
+ const auto sceneVal = scenes.first();
+ if (Q_UNLIKELY(sceneVal.isUndefined())) {
+ if (Q_UNLIKELY(!id.isNull()))
+ qCWarning(GLTFImporterLog, "GLTF: no such scene %ls in file %ls",
+ qUtf16PrintableImpl(id), qUtf16PrintableImpl(m_basePath));
+ return defaultScene();
+ }
+ const QJsonObject sceneObj = sceneVal.toObject();
+ sceneEntity = new QEntity;
+ const auto nodes = sceneObj.value(KEY_NODES).toArray();
+ for (const QJsonValue &n : nodes) {
+ QEntity* child = node(QString::number(n.toInt()));
+ if (!child)
+ continue;
+ child->setParent(sceneEntity);
+ }
+ } else {
+ const QJsonObject scenes = m_json.object().value(KEY_SCENES).toObject();
+ const auto sceneVal = scenes.value(id);
+ if (Q_UNLIKELY(sceneVal.isUndefined())) {
+ if (Q_UNLIKELY(!id.isNull()))
+ qCWarning(GLTFImporterLog, "GLTF: no such scene %ls in file %ls",
+ qUtf16PrintableImpl(id), qUtf16PrintableImpl(m_basePath));
+ return defaultScene();
+ }
+
+ const QJsonObject sceneObj = sceneVal.toObject();
+ sceneEntity = new QEntity;
+ const auto nodes = sceneObj.value(KEY_NODES).toArray();
+ for (const QJsonValue &nnv : nodes) {
+ QString nodeName = nnv.toString();
+ QEntity* child = node(nodeName);
+ if (!child)
+ continue;
+ child->setParent(sceneEntity);
+ }
}
cleanup();
@@ -592,14 +735,21 @@ GLTFImporter::AccessorData::AccessorData()
}
-GLTFImporter::AccessorData::AccessorData(const QJsonObject &json)
- : bufferViewName(json.value(KEY_BUFFER_VIEW).toString()),
- type(accessorTypeFromJSON(json.value(KEY_COMPONENT_TYPE).toInt())),
+GLTFImporter::AccessorData::AccessorData(const QJsonObject &json, int major, int minor)
+ : type(accessorTypeFromJSON(json.value(KEY_COMPONENT_TYPE).toInt())),
dataSize(accessorDataSizeFromJson(json.value(KEY_TYPE).toString())),
count(json.value(KEY_COUNT).toInt()),
offset(0),
stride(0)
{
+ Q_UNUSED(minor)
+
+ if (major > 1) {
+ bufferViewName = QString::number(json.value(KEY_BUFFER_VIEW).toInt());
+ } else {
+ bufferViewName = json.value(KEY_BUFFER_VIEW).toString();
+ }
+
const auto byteOffset = json.value(KEY_BYTE_OFFSET);
if (!byteOffset.isUndefined())
offset = byteOffset.toInt();
@@ -618,6 +768,11 @@ bool GLTFImporter::isGLTFSupported(const QStringList &extensions)
return false;
}
+bool GLTFImporter::isEmbeddedResource(const QString &url)
+{
+ return url.startsWith("data:");
+}
+
void GLTFImporter::renameFromJson(const QJsonObject &json, QObject * const object)
{
const auto name = json.value(KEY_NAME);
@@ -690,7 +845,7 @@ Qt3DCore::QEntity* GLTFImporter::defaultScene()
{
if (Q_UNLIKELY(m_defaultScene.isEmpty())) {
qCWarning(GLTFImporterLog, "no default scene");
- return NULL;
+ return nullptr;
}
return scene(m_defaultScene);
@@ -708,7 +863,7 @@ QMaterial *GLTFImporter::materialWithCustomShader(const QString &id, const QJson
if (Q_UNLIKELY(it == m_techniques.cend())) {
qCWarning(GLTFImporterLog, "unknown technique %ls for material %ls in GLTF file %ls",
qUtf16PrintableImpl(techniqueName), qUtf16PrintableImpl(id), qUtf16PrintableImpl(m_basePath));
- return NULL;
+ return nullptr;
}
QTechnique *technique = *it;
technique->graphicsApiFilter()->setApi(QGraphicsApiFilter::OpenGLES);
@@ -814,7 +969,7 @@ QMaterial *GLTFImporter::commonMaterial(const QJsonObject &jsonObj)
{
const auto jsonExt =
jsonObj.value(KEY_EXTENSIONS).toObject().value(KEY_COMMON_MAT).toObject();
- if (jsonExt.isEmpty())
+ if (m_majorVersion == 1 && jsonExt.isEmpty())
return nullptr;
QVariantHash params;
@@ -823,6 +978,13 @@ QMaterial *GLTFImporter::commonMaterial(const QJsonObject &jsonObj)
bool hasNormalMap = false;
bool hasAlpha = false;
+ if (m_majorVersion > 1) {
+ QMaterial *mat = pbrMaterial(jsonObj);
+
+ if (mat)
+ return mat;
+ }
+
const QJsonObject values = jsonExt.value(KEY_VALUES).toObject();
for (auto it = values.begin(), end = values.end(); it != end; ++it) {
const QString vName = it.key();
@@ -938,18 +1100,146 @@ QMaterial *GLTFImporter::commonMaterial(const QJsonObject &jsonObj)
return mat;
}
+QMaterial *GLTFImporter::pbrMaterial(const QJsonObject &jsonObj)
+{
+ // check for pbrMetallicRoughness material
+ QMetalRoughMaterial *mrMaterial = nullptr;
+ QJsonValue jsonValue = jsonObj.value(KEY_PBR_METAL_ROUGH);
+
+ if (!jsonValue.isUndefined()) {
+ const QJsonObject pbrObj = jsonValue.toObject();
+ mrMaterial = new QMetalRoughMaterial;
+ jsonValue = pbrObj.value(KEY_BASE_COLOR);
+ if (!jsonValue.isUndefined())
+ mrMaterial->setBaseColor(jsonArrToColorVariant(jsonValue.toArray()));
+
+ jsonValue = pbrObj.value(KEY_BASE_COLOR_TEX);
+ if (!jsonValue.isUndefined()) {
+ const QJsonObject texObj = jsonValue.toObject();
+ const QString textureId = QString::number(texObj.value(KEY_INDEX).toInt());
+ const auto it = m_textures.find(textureId);
+ if (Q_UNLIKELY(it == m_textures.end())) {
+ qCWarning(GLTFImporterLog, "unknown texture %ls", qUtf16PrintableImpl(textureId));
+ } else {
+ mrMaterial->setBaseColor(QVariant::fromValue(it.value()));
+ }
+ }
+
+ jsonValue = pbrObj.value(KEY_METAL_FACTOR);
+ if (!jsonValue.isUndefined())
+ mrMaterial->setMetalness(jsonValue.toVariant());
+
+ jsonValue = pbrObj.value(KEY_METAL_ROUGH_TEX);
+ if (!jsonValue.isUndefined()) {
+ const QJsonObject texObj = jsonValue.toObject();
+ const QString textureId = QString::number(texObj.value(KEY_INDEX).toInt());
+ const auto it = m_textures.find(textureId);
+ if (Q_UNLIKELY(it == m_textures.end())) {
+ qCWarning(GLTFImporterLog, "unknown texture %ls", qUtf16PrintableImpl(textureId));
+ } else {
+ // find the texture again
+ const QJsonArray texArray = m_json.object().value(KEY_TEXTURES).toArray();
+ const QJsonObject tObj = texArray.at(texObj.value(KEY_INDEX).toInt()).toObject();
+ const QString sourceId = QString::number(tObj.value(KEY_SOURCE).toInt());
+ QImage image;
+ if (m_imagePaths.contains(sourceId)) {
+ image.load(m_imagePaths.value(sourceId));
+ } else if (m_imageData.contains(sourceId)) {
+ image = m_imageData.value(sourceId);
+ } else {
+ return mrMaterial;
+ }
+
+ // at this point, in image there is data for metalness (on B) and
+ // roughness (on G) bytes. 2 new textures are created
+ // to make Qt3D happy, since it samples only on R.
+
+ QTexture2D* metalTex = new QTexture2D;
+ QTexture2D* roughTex = new QTexture2D;
+ GLTFRawTextureImage* metalImgTex = new GLTFRawTextureImage();
+ GLTFRawTextureImage* roughImgTex = new GLTFRawTextureImage();
+ QImage metalness(image.size(), image.format());
+ QImage roughness(image.size(), image.format());
+
+ const uchar *imgData = image.constBits();
+ const int pixelBytes = image.depth() / 8;
+ Q_ASSERT_X(pixelBytes < 3, "GLTFImporter::pbrMaterial", "Unsupported texture format");
+
+ for (int y = 0; y < image.height(); y++) {
+ for (int x = 0; x < image.width(); x++) {
+ metalness.setPixel(x, y, qRgb(imgData[0], imgData[0], imgData[0]));
+ roughness.setPixel(x, y, qRgb(imgData[1], imgData[1], imgData[1]));
+ imgData += pixelBytes;
+ }
+ }
+
+ metalImgTex->setImage(metalness);
+ metalTex->addTextureImage(metalImgTex);
+ roughImgTex->setImage(roughness);
+ roughTex->addTextureImage(roughImgTex);
+
+ setTextureSamplerInfo("", tObj, metalTex);
+ setTextureSamplerInfo("", tObj, roughTex);
+
+ mrMaterial->setMetalness(QVariant::fromValue(metalTex));
+ mrMaterial->setRoughness(QVariant::fromValue(roughTex));
+ }
+ }
+
+ jsonValue = pbrObj.value(KEY_ROUGH_FACTOR);
+ if (!jsonValue.isUndefined())
+ mrMaterial->setRoughness(jsonValue.toVariant());
+ }
+
+ jsonValue = jsonObj.value(KEY_NORMAL_TEX);
+ if (!jsonValue.isUndefined()) {
+ const QJsonObject texObj = jsonValue.toObject();
+ const QString textureId = QString::number(texObj.value(KEY_INDEX).toInt());
+ const auto it = m_textures.find(textureId);
+ if (Q_UNLIKELY(it == m_textures.end())) {
+ qCWarning(GLTFImporterLog, "unknown texture %ls", qUtf16PrintableImpl(textureId));
+ } else {
+ if (mrMaterial)
+ mrMaterial->setNormal(QVariant::fromValue(it.value()));
+ }
+ }
+
+ jsonValue = jsonObj.value(KEY_OCCLUSION_TEX);
+ if (!jsonValue.isUndefined()) {
+ const QJsonObject texObj = jsonValue.toObject();
+ const QString textureId = QString::number(texObj.value(KEY_INDEX).toInt());
+ const auto it = m_textures.find(textureId);
+ if (Q_UNLIKELY(it == m_textures.end())) {
+ qCWarning(GLTFImporterLog, "unknown texture %ls", qUtf16PrintableImpl(textureId));
+ } else {
+ if (mrMaterial)
+ mrMaterial->setAmbientOcclusion(QVariant::fromValue(it.value()));
+ }
+ }
+
+ return mrMaterial;
+}
+
QMaterial* GLTFImporter::material(const QString &id)
{
const auto it = qAsConst(m_materialCache).find(id);
if (it != m_materialCache.cend())
return it.value();
- QJsonObject mats = m_json.object().value(KEY_MATERIALS).toObject();
- const auto jsonVal = mats.value(id);
+ QJsonValue jsonVal;
+
+ if (m_majorVersion > 1) {
+ const QJsonArray mats = m_json.object().value(KEY_MATERIALS).toArray();
+ jsonVal = mats.at(id.toInt());
+ } else {
+ const QJsonObject mats = m_json.object().value(KEY_MATERIALS).toObject();
+ jsonVal = mats.value(id);
+ }
+
if (Q_UNLIKELY(jsonVal.isUndefined())) {
qCWarning(GLTFImporterLog, "unknown material %ls in GLTF file %ls",
qUtf16PrintableImpl(id), qUtf16PrintableImpl(m_basePath));
- return NULL;
+ return nullptr;
}
const QJsonObject jsonObj = jsonVal.toObject();
@@ -967,14 +1257,26 @@ QMaterial* GLTFImporter::material(const QString &id)
bool GLTFImporter::fillCamera(QCameraLens &lens, QCamera *cameraEntity, const QString &id) const
{
- const auto jsonVal = m_json.object().value(KEY_CAMERAS).toObject().value(id);
- if (Q_UNLIKELY(jsonVal.isUndefined())) {
- qCWarning(GLTFImporterLog, "unknown camera %ls in GLTF file %ls",
- qUtf16PrintableImpl(id), qUtf16PrintableImpl(m_basePath));
- return false;
+ QJsonObject jsonObj;
+
+ if (m_majorVersion > 1) {
+ const QJsonArray camArray = m_json.object().value(KEY_CAMERAS).toArray();
+ if (id.toInt() >= camArray.count()) {
+ qCWarning(GLTFImporterLog, "unknown camera %ls in GLTF file %ls",
+ qUtf16PrintableImpl(id), qUtf16PrintableImpl(m_basePath));
+ return false;
+ }
+ jsonObj = camArray[id.toInt()].toObject();
+ } else {
+ const auto jsonVal = m_json.object().value(KEY_CAMERAS).toObject().value(id);
+ if (Q_UNLIKELY(jsonVal.isUndefined())) {
+ qCWarning(GLTFImporterLog, "unknown camera %ls in GLTF file %ls",
+ qUtf16PrintableImpl(id), qUtf16PrintableImpl(m_basePath));
+ return false;
+ }
+ jsonObj = jsonVal.toObject();
}
- QJsonObject jsonObj = jsonVal.toObject();
QString camTy = jsonObj.value(KEY_TYPE).toString();
if (camTy == QLatin1String("perspective")) {
@@ -1025,12 +1327,26 @@ bool GLTFImporter::fillCamera(QCameraLens &lens, QCamera *cameraEntity, const QS
return true;
}
-
void GLTFImporter::parse()
{
if (m_parseDone)
return;
+ const QJsonValue asset = m_json.object().value(KEY_ASSET);
+ if (!asset.isUndefined())
+ processJSONAsset(asset.toObject());
+
+ if (m_majorVersion > 1) {
+ parseV2();
+ } else {
+ parseV1();
+ }
+
+ m_parseDone = true;
+}
+
+void GLTFImporter::parseV1()
+{
const QJsonObject buffers = m_json.object().value(KEY_BUFFERS).toObject();
for (auto it = buffers.begin(), end = buffers.end(); it != end; ++it)
processJSONBuffer(it.key(), it.value().toObject());
@@ -1082,7 +1398,38 @@ void GLTFImporter::parse()
processJSONEffect(it.key(), it.value().toObject());
m_defaultScene = m_json.object().value(KEY_SCENE).toString();
- m_parseDone = true;
+}
+
+void GLTFImporter::parseV2()
+{
+ int i;
+ const QJsonArray buffers = m_json.object().value(KEY_BUFFERS).toArray();
+ for (i = 0; i < buffers.count(); i++)
+ processJSONBuffer(QString::number(i), buffers[i].toObject());
+
+ const QJsonArray views = m_json.object().value(KEY_BUFFER_VIEWS).toArray();
+ loadBufferData();
+ for (i = 0; i < views.count(); i++)
+ processJSONBufferView(QString::number(i), views[i].toObject());
+ unloadBufferData();
+
+ const QJsonArray accessors = m_json.object().value(KEY_ACCESSORS).toArray();
+ for (i = 0; i < accessors.count(); i++)
+ processJSONAccessor(QString::number(i), accessors[i].toObject());
+
+ const QJsonArray meshes = m_json.object().value(KEY_MESHES).toArray();
+ for (i = 0; i < meshes.count(); i++)
+ processJSONMesh(QString::number(i), meshes[i].toObject());
+
+ const QJsonArray images = m_json.object().value(KEY_IMAGES).toArray();
+ for (i = 0; i < images.count(); i++)
+ processJSONImage(QString::number(i), images[i].toObject());
+
+ const QJsonArray textures = m_json.object().value(KEY_TEXTURES).toArray();
+ for (i = 0; i < textures.count(); i++)
+ processJSONTexture(QString::number(i), textures[i].toObject());
+
+ m_defaultScene = QString::number(m_json.object().value(KEY_SCENE).toInt());
}
namespace {
@@ -1116,6 +1463,7 @@ void GLTFImporter::cleanup()
delete_if_without_parent(m_textures);
m_textures.clear();
m_imagePaths.clear();
+ m_imageData.clear();
m_defaultScene.clear();
m_parameterDataDict.clear();
delete_if_without_parent(m_renderPasses);
@@ -1124,6 +1472,18 @@ void GLTFImporter::cleanup()
m_effects.clear();
}
+void GLTFImporter::processJSONAsset(const QJsonObject &json)
+{
+ const QString version = json.value(KEY_VERSION).toString();
+ if (!version.isEmpty()) {
+ const QStringList verTokens = version.split('.');
+ if (verTokens.length() >= 2) {
+ m_majorVersion = verTokens[0].toInt();
+ m_minorVersion = verTokens[1].toInt();
+ }
+ }
+}
+
void GLTFImporter::processJSONBuffer(const QString &id, const QJsonObject& json)
{
// simply cache buffers for lookup by buffer-views
@@ -1132,7 +1492,12 @@ void GLTFImporter::processJSONBuffer(const QString &id, const QJsonObject& json)
void GLTFImporter::processJSONBufferView(const QString &id, const QJsonObject& json)
{
- QString bufName = json.value(KEY_BUFFER).toString();
+ QString bufName;
+ if (m_majorVersion > 1) {
+ bufName = QString::number(json.value(KEY_BUFFER).toInt());
+ } else {
+ bufName = json.value(KEY_BUFFER).toString();
+ }
const auto it = qAsConst(m_bufferDatas).find(bufName);
if (Q_UNLIKELY(it == m_bufferDatas.cend())) {
qCWarning(GLTFImporterLog, "unknown buffer: %ls processing view: %ls",
@@ -1141,7 +1506,13 @@ void GLTFImporter::processJSONBufferView(const QString &id, const QJsonObject& j
}
const auto &bufferData = *it;
- int target = json.value(KEY_TARGET).toInt();
+ const QJsonValue targetValue = json.value(KEY_TARGET);
+ int target;
+ if (targetValue.isUndefined()) {
+ target = GL_ARRAY_BUFFER;
+ } else {
+ target = targetValue.toInt();
+ }
Qt3DRender::QBuffer::BufferType ty(Qt3DRender::QBuffer::VertexBuffer);
switch (target) {
@@ -1180,14 +1551,21 @@ void GLTFImporter::processJSONShader(const QString &id, const QJsonObject &jsonO
// to the program section
QString path = jsonObject.value(KEY_URI).toString();
- QFileInfo info(m_basePath, path);
- if (Q_UNLIKELY(!info.exists())) {
- qCWarning(GLTFImporterLog, "can't find shader %ls from path %ls",
- qUtf16PrintableImpl(id), qUtf16PrintableImpl(path));
- return;
+ if (!isEmbeddedResource(path)) {
+ QFileInfo info(m_basePath, path);
+ if (Q_UNLIKELY(!info.exists())) {
+ qCWarning(GLTFImporterLog, "can't find shader %ls from path %ls",
+ qUtf16PrintableImpl(id), qUtf16PrintableImpl(path));
+ return;
+ }
+
+ m_shaderPaths[id] = info.absoluteFilePath();
+ } else {
+ const QByteArray base64Data = path.toLatin1().remove(0, path.indexOf(",") + 1);
+ m_shaderPaths[id] = QString(QByteArray::fromBase64(base64Data));
}
- m_shaderPaths[id] = info.absoluteFilePath();
+
}
void GLTFImporter::processJSONProgram(const QString &id, const QJsonObject &jsonObject)
@@ -1361,9 +1739,9 @@ void GLTFImporter::processJSONTechnique(const QString &id, const QJsonObject &js
m_techniques[id] = t;
}
-void GLTFImporter::processJSONAccessor( const QString &id, const QJsonObject& json )
+void GLTFImporter::processJSONAccessor(const QString &id, const QJsonObject& json)
{
- m_accessorDict[id] = AccessorData(json);
+ m_accessorDict[id] = AccessorData(json, m_majorVersion, m_minorVersion);
}
void GLTFImporter::processJSONMesh(const QString &id, const QJsonObject &json)
@@ -1374,22 +1752,27 @@ void GLTFImporter::processJSONMesh(const QString &id, const QJsonObject &json)
// Custom mesh
const QJsonArray primitivesArray = json.value(KEY_PRIMITIVES).toArray();
for (const QJsonValue &primitiveValue : primitivesArray) {
- QJsonObject primitiveObject = primitiveValue.toObject();
- int type = primitiveObject.value(KEY_MODE).toInt();
- QString material = primitiveObject.value(KEY_MATERIAL).toString();
+ const QJsonObject primitiveObject = primitiveValue.toObject();
+ const QJsonValue type = primitiveObject.value(KEY_MODE);
+ const QJsonValue matValue = primitiveObject.value(KEY_MATERIAL);
+ const QString material = (m_majorVersion > 1) ? QString::number(matValue.toInt()) : matValue.toString();
QGeometryRenderer *geometryRenderer = new QGeometryRenderer;
QGeometry *meshGeometry = new QGeometry(geometryRenderer);
//Set Primitive Type
- geometryRenderer->setPrimitiveType(static_cast<QGeometryRenderer::PrimitiveType>(type));
+ if (type.isUndefined()) {
+ geometryRenderer->setPrimitiveType(QGeometryRenderer::Triangles);
+ } else {
+ geometryRenderer->setPrimitiveType(static_cast<QGeometryRenderer::PrimitiveType>(type.toInt()));
+ }
//Save Material for mesh
m_meshMaterialDict[geometryRenderer] = material;
const QJsonObject attrs = primitiveObject.value(KEY_ATTRIBUTES).toObject();
for (auto it = attrs.begin(), end = attrs.end(); it != end; ++it) {
- QString k = it.value().toString();
+ const QString k = (m_majorVersion > 1) ? QString::number(it.value().toInt()) : it.value().toString();
const auto accessorIt = qAsConst(m_accessorDict).find(k);
if (Q_UNLIKELY(accessorIt == m_accessorDict.cend())) {
qCWarning(GLTFImporterLog, "unknown attribute accessor: %ls on mesh %ls",
@@ -1424,18 +1807,16 @@ void GLTFImporter::processJSONMesh(const QString &id, const QJsonObject &json)
const auto indices = primitiveObject.value(KEY_INDICES);
if (!indices.isUndefined()) {
- QString k = indices.toString();
- const auto accessorIt = qAsConst(m_accessorDict).find(k);
+ const QString accIndex = (m_majorVersion > 1) ? QString::number(indices.toInt()) : indices.toString();
+ const auto accessorIt = qAsConst(m_accessorDict).find(accIndex);
if (Q_UNLIKELY(accessorIt == m_accessorDict.cend())) {
qCWarning(GLTFImporterLog, "unknown index accessor: %ls on mesh %ls",
- qUtf16PrintableImpl(k), qUtf16PrintableImpl(id));
+ qUtf16PrintableImpl(accIndex), qUtf16PrintableImpl(id));
} else {
//Get buffer handle for accessor
- Qt3DRender::QBuffer *buffer = m_buffers.value(accessorIt->bufferViewName,
- nullptr);
+ Qt3DRender::QBuffer *buffer = m_buffers.value(accessorIt->bufferViewName, nullptr);
if (Q_UNLIKELY(!buffer)) {
- qCWarning(GLTFImporterLog,
- "unknown buffer-view: %ls processing accessor: %ls",
+ qCWarning(GLTFImporterLog, "unknown buffer-view: %ls processing accessor: %ls",
qUtf16PrintableImpl(accessorIt->bufferViewName),
qUtf16PrintableImpl(id));
continue;
@@ -1505,7 +1886,9 @@ void GLTFImporter::processJSONMesh(const QString &id, const QJsonObject &json)
}
}
mesh->setObjectName(meshName);
- m_meshMaterialDict[mesh] = json.value(KEY_MATERIAL).toString();
+ m_meshMaterialDict[mesh] = (m_majorVersion > 1) ?
+ QString::number(json.value(KEY_MATERIAL).toInt()) :
+ json.value(KEY_MATERIAL).toString();
m_meshDict.insert(id, mesh);
}
}
@@ -1514,66 +1897,73 @@ void GLTFImporter::processJSONMesh(const QString &id, const QJsonObject &json)
void GLTFImporter::processJSONImage(const QString &id, const QJsonObject &jsonObject)
{
QString path = jsonObject.value(KEY_URI).toString();
- QFileInfo info(m_basePath, path);
- if (Q_UNLIKELY(!info.exists())) {
- qCWarning(GLTFImporterLog, "can't find image %ls from path %ls",
- qUtf16PrintableImpl(id), qUtf16PrintableImpl(path));
- return;
- }
- m_imagePaths[id] = info.absoluteFilePath();
+ if (!isEmbeddedResource(path)) {
+ QFileInfo info(m_basePath, path);
+ if (Q_UNLIKELY(!info.exists())) {
+ qCWarning(GLTFImporterLog, "can't find image %ls from path %ls",
+ qUtf16PrintableImpl(id), qUtf16PrintableImpl(path));
+ return;
+ }
+
+ m_imagePaths[id] = info.absoluteFilePath();
+ } else {
+ const QByteArray base64Data = path.toLatin1().remove(0, path.indexOf(",") + 1);
+ QImage image;
+ image.loadFromData(QByteArray::fromBase64(base64Data));
+ m_imageData[id] = image;
+ }
}
void GLTFImporter::processJSONTexture(const QString &id, const QJsonObject &jsonObject)
{
- int target = jsonObject.value(KEY_TARGET).toInt(GL_TEXTURE_2D);
- //TODO: support other targets that GL_TEXTURE_2D (though the spec doesn't support anything else)
- if (Q_UNLIKELY(target != GL_TEXTURE_2D)) {
- qCWarning(GLTFImporterLog, "unsupported texture target: %d", target);
- return;
+ QJsonValue jsonVal = jsonObject.value(KEY_TARGET);
+ if (!jsonVal.isUndefined()) {
+ int target = jsonVal.toInt(GL_TEXTURE_2D);
+ //TODO: support other targets that GL_TEXTURE_2D (though the spec doesn't support anything else)
+ if (Q_UNLIKELY(target != GL_TEXTURE_2D)) {
+ qCWarning(GLTFImporterLog, "unsupported texture target: %d", target);
+ return;
+ }
}
QTexture2D* tex = new QTexture2D;
// TODO: Choose suitable internal format - may vary on OpenGL context type
//int pixelFormat = jsonObj.value(KEY_FORMAT).toInt(GL_RGBA);
- int internalFormat = jsonObject.value(KEY_INTERNAL_FORMAT).toInt(GL_RGBA);
+ int internalFormat = GL_RGBA;
+ jsonVal = jsonObject.value(KEY_INTERNAL_FORMAT);
+ if (!jsonVal.isUndefined())
+ internalFormat = jsonObject.value(KEY_INTERNAL_FORMAT).toInt(GL_RGBA);
tex->setFormat(static_cast<QAbstractTexture::TextureFormat>(internalFormat));
- QString samplerId = jsonObject.value(KEY_SAMPLER).toString();
- QString source = jsonObject.value(KEY_SOURCE).toString();
+ QJsonValue srcValue = jsonObject.value(KEY_SOURCE);
+ QString source = (m_majorVersion > 1) ? QString::number(srcValue.toInt()) : srcValue.toString();
+
const auto imagIt = qAsConst(m_imagePaths).find(source);
if (Q_UNLIKELY(imagIt == m_imagePaths.cend())) {
- qCWarning(GLTFImporterLog, "texture %ls references missing image %ls",
- qUtf16PrintableImpl(id), qUtf16PrintableImpl(source));
- return;
- }
-
- QTextureImage *texImage = new QTextureImage(tex);
- texImage->setMirrored(false);
- texImage->setSource(QUrl::fromLocalFile(imagIt.value()));
- tex->addTextureImage(texImage);
+ // if an image is not found in paths, it probably means
+ // it was an embedded resource, referenced in m_imageData
+ const auto embImgIt = qAsConst(m_imageData).find(source);
+ if (Q_UNLIKELY(embImgIt == m_imageData.cend())) {
+ qCWarning(GLTFImporterLog, "texture %ls references missing image %ls",
+ qUtf16PrintableImpl(id), qUtf16PrintableImpl(source));
+ return;
+ }
- const auto samplersDictValue = m_json.object().value(KEY_SAMPLERS).toObject().value(samplerId);
- if (Q_UNLIKELY(samplersDictValue.isUndefined())) {
- qCWarning(GLTFImporterLog, "texture %ls references unknown sampler %ls",
- qUtf16PrintableImpl(id), qUtf16PrintableImpl(samplerId));
- return;
+ QImage img = embImgIt.value();
+ GLTFRawTextureImage *imageData = new GLTFRawTextureImage();
+ imageData->setImage(img);
+ tex->addTextureImage(imageData);
+ } else {
+ QTextureImage *texImage = new QTextureImage(tex);
+ texImage->setMirrored(false);
+ texImage->setSource(QUrl::fromLocalFile(imagIt.value()));
+ tex->addTextureImage(texImage);
}
- QJsonObject sampler = samplersDictValue.toObject();
-
- tex->setWrapMode(QTextureWrapMode(static_cast<QTextureWrapMode::WrapMode>(sampler.value(KEY_WRAP_S).toInt())));
- tex->setMinificationFilter(static_cast<QAbstractTexture::Filter>(sampler.value(KEY_MIN_FILTER).toInt()));
- if (tex->minificationFilter() == QAbstractTexture::NearestMipMapLinear ||
- tex->minificationFilter() == QAbstractTexture::LinearMipMapNearest ||
- tex->minificationFilter() == QAbstractTexture::NearestMipMapNearest ||
- tex->minificationFilter() == QAbstractTexture::LinearMipMapLinear) {
-
- tex->setGenerateMipMaps(true);
- }
- tex->setMagnificationFilter(static_cast<QAbstractTexture::Filter>(sampler.value(KEY_MAG_FILTER).toInt()));
+ setTextureSamplerInfo(id, jsonObject, tex);
m_textures[id] = tex;
}
@@ -1708,10 +2098,16 @@ QByteArray GLTFImporter::resolveLocalData(const QString &path) const
QDir d(m_basePath);
Q_ASSERT(d.exists());
- QString absPath = d.absoluteFilePath(path);
- QFile f(absPath);
- f.open(QIODevice::ReadOnly);
- return f.readAll();
+ // check for embedded data
+ if (isEmbeddedResource(path)) {
+ const QByteArray base64Data = path.toLatin1().remove(0, path.indexOf(",") + 1);
+ return QByteArray::fromBase64(base64Data);
+ } else {
+ const QString absPath = d.absoluteFilePath(path);
+ QFile f(absPath);
+ f.open(QIODevice::ReadOnly);
+ return f.readAll();
+ }
}
QVariant GLTFImporter::parameterValueFromJSON(int type, const QJsonValue &value) const
@@ -2167,9 +2563,85 @@ void GLTFImporter::addProgramToPass(QRenderPass *pass, const QString &progName)
pass->setShaderProgram(progIt.value());
}
+void GLTFImporter::setTextureSamplerInfo(const QString &id, const QJsonObject &jsonObj, QTexture2D *tex)
+{
+ QJsonObject sampler;
+ const QJsonValue jsonValue = jsonObj.value(KEY_SAMPLER);
+ if (jsonValue.isUndefined())
+ return;
+
+ if (m_majorVersion > 1) {
+ const int samplerId = jsonValue.toInt();
+ const QJsonArray sArray = m_json.object().value(KEY_SAMPLERS).toArray();
+ if (Q_UNLIKELY(samplerId >= sArray.count())) {
+ qCWarning(GLTFImporterLog, "texture %ls references unknown sampler %d",
+ qUtf16PrintableImpl(id), samplerId);
+ return;
+ }
+ sampler = sArray[samplerId].toObject();
+ } else {
+ const QString samplerId = jsonValue.toString();
+ const QJsonValue samplersDictValue = m_json.object().value(KEY_SAMPLERS).toObject().value(samplerId);
+ if (Q_UNLIKELY(samplersDictValue.isUndefined())) {
+ qCWarning(GLTFImporterLog, "texture %ls references unknown sampler %ls",
+ qUtf16PrintableImpl(id), qUtf16PrintableImpl(samplerId));
+ return;
+ }
+ sampler = samplersDictValue.toObject();
+ }
+
+ tex->setWrapMode(QTextureWrapMode(static_cast<QTextureWrapMode::WrapMode>(sampler.value(KEY_WRAP_S).toInt())));
+ tex->setMinificationFilter(static_cast<QAbstractTexture::Filter>(sampler.value(KEY_MIN_FILTER).toInt()));
+ if (tex->minificationFilter() == QAbstractTexture::NearestMipMapLinear ||
+ tex->minificationFilter() == QAbstractTexture::LinearMipMapNearest ||
+ tex->minificationFilter() == QAbstractTexture::NearestMipMapNearest ||
+ tex->minificationFilter() == QAbstractTexture::LinearMipMapLinear) {
+
+ tex->setGenerateMipMaps(true);
+ }
+ tex->setMagnificationFilter(static_cast<QAbstractTexture::Filter>(sampler.value(KEY_MAG_FILTER).toInt()));
+}
+
+GLTFRawTextureImage::GLTFRawTextureImage(QNode *parent)
+ : QAbstractTextureImage(parent)
+{
+}
+
+QTextureImageDataGeneratorPtr GLTFRawTextureImage::dataGenerator() const
+{
+ return QTextureImageDataGeneratorPtr(new GLTFRawTextureImageFunctor(m_image));
+}
+
+void GLTFRawTextureImage::setImage(const QImage &image)
+{
+ if (image != m_image) {
+ m_image = image;
+ notifyDataGeneratorChanged();
+ }
+}
+
+GLTFRawTextureImage::GLTFRawTextureImageFunctor::GLTFRawTextureImageFunctor(const QImage &image)
+ : QTextureImageDataGenerator()
+ , m_image(image)
+{
+}
+
+QTextureImageDataPtr GLTFRawTextureImage::GLTFRawTextureImageFunctor::operator()()
+{
+ QTextureImageDataPtr dataPtr = QTextureImageDataPtr::create();
+ // Note: we assume 4 components per pixel and not compressed for now
+ dataPtr->setImage(m_image);
+ return dataPtr;
+}
+
+bool GLTFRawTextureImage::GLTFRawTextureImageFunctor::operator ==(const QTextureImageDataGenerator &other) const
+{
+ const GLTFRawTextureImageFunctor *otherFunctor = functor_cast<GLTFRawTextureImageFunctor>(&other);
+ return (otherFunctor != nullptr && otherFunctor->m_image == m_image);
+}
} // namespace Qt3DRender
QT_END_NAMESPACE
-#include "moc_gltfimporter.cpp"
+#include "gltfimporter.moc"
diff --git a/src/plugins/sceneparsers/gltf/gltfimporter.h b/src/plugins/sceneparsers/gltf/gltfimporter.h
index d47a6a729..271ce5979 100644
--- a/src/plugins/sceneparsers/gltf/gltfimporter.h
+++ b/src/plugins/sceneparsers/gltf/gltfimporter.h
@@ -57,6 +57,7 @@
#include <QtCore/qjsondocument.h>
#include <QtCore/qjsonobject.h>
#include <QtCore/qhash.h>
+#include <QtCore/qloggingcategory.h>
#include <Qt3DRender/private/qsceneimporter_p.h>
@@ -82,6 +83,7 @@ class QParameter;
class QGeometryRenderer;
class QAbstractLight;
class QRenderPass;
+class QTexture2D;
Q_DECLARE_LOGGING_CATEGORY(GLTFImporterLog)
@@ -94,7 +96,7 @@ public:
~GLTFImporter();
void setBasePath(const QString& path);
- bool setJSON( const QJsonDocument &json );
+ bool setJSON(const QJsonDocument &json);
// SceneParserInterface interface
void setSource(const QUrl &source) final;
@@ -130,7 +132,7 @@ private:
{
public:
AccessorData();
- explicit AccessorData(const QJsonObject& json);
+ explicit AccessorData(const QJsonObject& json, int major, int minor);
QString bufferViewName;
QAttribute::VertexBaseType type;
@@ -141,6 +143,7 @@ private:
};
static bool isGLTFSupported(const QStringList &extensions);
+ static bool isEmbeddedResource(const QString &url);
static void renameFromJson(const QJsonObject& json, QObject * const object );
static bool hasStandardUniformNameFromSemantic(const QString &semantic);
static QString standardAttributeNameFromSemantic(const QString &semantic);
@@ -151,8 +154,11 @@ private:
bool fillCamera(QCameraLens &lens, QCamera *cameraEntity, const QString &id) const;
void parse();
+ void parseV1();
+ void parseV2();
void cleanup();
+ void processJSONAsset(const QJsonObject &json);
void processJSONBuffer(const QString &id, const QJsonObject &json);
void processJSONBufferView(const QString &id, const QJsonObject &json);
void processJSONShader(const QString &id, const QJsonObject &jsonObject);
@@ -181,12 +187,16 @@ private:
void populateRenderStates(QRenderPass *pass, const QJsonObject &states);
void addProgramToPass(QRenderPass *pass, const QString &progName);
+ void setTextureSamplerInfo(const QString &id, const QJsonObject &jsonObj, QTexture2D *tex);
QMaterial *materialWithCustomShader(const QString &id, const QJsonObject &jsonObj);
QMaterial *commonMaterial(const QJsonObject &jsonObj);
+ QMaterial *pbrMaterial(const QJsonObject &jsonObj);
QJsonDocument m_json;
QString m_basePath;
bool m_parseDone;
+ int m_majorVersion;
+ int m_minorVersion;
QString m_defaultScene;
// multi-hash because our QMeshData corresponds to a single primitive
@@ -215,6 +225,7 @@ private:
QHash<QString, QAbstractTexture*> m_textures;
QHash<QString, QString> m_imagePaths;
+ QHash<QString, QImage> m_imageData;
QHash<QString, QAbstractLight *> m_lights;
};
diff --git a/src/quick3d/imports/render/qt3dquick3drenderplugin.cpp b/src/quick3d/imports/render/qt3dquick3drenderplugin.cpp
index 4d2d5d19c..9493b59ef 100644
--- a/src/quick3d/imports/render/qt3dquick3drenderplugin.cpp
+++ b/src/quick3d/imports/render/qt3dquick3drenderplugin.cpp
@@ -112,6 +112,8 @@
#include <Qt3DRender/qviewport.h>
#include <Qt3DRender/qproximityfilter.h>
#include <Qt3DRender/qblitframebuffer.h>
+#include <Qt3DRender/qsetfence.h>
+#include <Qt3DRender/qwaitfence.h>
#include <QtGui/qwindow.h>
@@ -178,6 +180,7 @@ void Qt3DQuick3DRenderPlugin::registerTypes(const char *uri)
qmlRegisterType<Qt3DRender::QShaderProgram>(uri, 2, 0, "ShaderProgram");
qmlRegisterType<Qt3DRender::QShaderProgram, 9>(uri, 2, 9, "ShaderProgram");
qmlRegisterType<Qt3DRender::QShaderProgramBuilder>(uri, 2, 10, "ShaderProgramBuilder");
+ qmlRegisterType<Qt3DRender::QShaderProgramBuilder>(uri, 2, 13, "ShaderProgramBuilder");
qmlRegisterUncreatableType<Qt3DRender::QShaderData>(uri, 2, 0, "QShaderData", "Quick3D should instantiate Quick3DShaderData only");
qmlRegisterType<Qt3DRender::Render::Quick::Quick3DShaderDataArray>(uri, 2, 0, "ShaderDataArray");
qmlRegisterType<Qt3DRender::Render::Quick::Quick3DShaderData>(uri, 2, 0, "ShaderData");
@@ -191,6 +194,7 @@ void Qt3DQuick3DRenderPlugin::registerTypes(const char *uri)
// Textures
qmlRegisterType<Qt3DRender::QTextureWrapMode>(uri, 2, 0, "WrapMode");//, QStringLiteral("QTextureWrapMode cannot be created from QML"));
qmlRegisterUncreatableType<Qt3DRender::QAbstractTexture>(uri, 2, 0, "Texture", QStringLiteral("Texture should be created from one of the subclasses"));
+ qmlRegisterUncreatableType<Qt3DRender::QAbstractTexture, 13>(uri, 2, 13, "Texture", QStringLiteral("Texture should be created from one of the subclasses"));
Qt3DRender::Quick::registerExtendedType<Qt3DRender::QTexture1D, Qt3DRender::Render::Quick::Quick3DTextureExtension>("QTexture1D", "Qt3D.Render/Texture1D", uri, 2, 0, "Texture1D");
Qt3DRender::Quick::registerExtendedType<Qt3DRender::QTexture1DArray, Qt3DRender::Render::Quick::Quick3DTextureExtension>("QTexture1DArray", "Qt3D.Render/Texture1DArray", uri, 2, 0, "Texture1DArray");
Qt3DRender::Quick::registerExtendedType<Qt3DRender::QTexture2D, Qt3DRender::Render::Quick::Quick3DTextureExtension>("QTexture2D", "Qt3D.Render/Texture2D", uri, 2, 0, "Texture2D");
@@ -205,6 +209,7 @@ void Qt3DQuick3DRenderPlugin::registerTypes(const char *uri)
Qt3DRender::Quick::registerExtendedType<Qt3DRender::QTextureLoader, Qt3DRender::Render::Quick::Quick3DTextureExtension>("QTextureLoader", "Qt3D.Render/TextureLoader", uri, 2, 0, "TextureLoader");
qmlRegisterUncreatableType<Qt3DRender::QAbstractTextureImage>(uri, 2, 0, "QAbstractTextureImage", QStringLiteral("QAbstractTextureImage is abstract"));
qmlRegisterType<Qt3DRender::QTextureImage>(uri, 2, 0, "TextureImage");
+ qmlRegisterType<Qt3DRender::QSharedGLTexture>(uri, 2, 13, "SharedGLTexture");
// Geometry
qmlRegisterType<Qt3DRender::QAttribute>(uri, 2, 0, "Attribute");
@@ -225,12 +230,14 @@ void Qt3DQuick3DRenderPlugin::registerTypes(const char *uri)
// Picking
qmlRegisterType<Qt3DRender::QObjectPicker>(uri, 2, 0, "ObjectPicker");
qmlRegisterType<Qt3DRender::QObjectPicker, 9>(uri, 2, 9, "ObjectPicker");
+ qmlRegisterType<Qt3DRender::QObjectPicker, 13>(uri, 2, 13, "ObjectPicker");
qmlRegisterUncreatableType<Qt3DRender::QPickEvent>(uri, 2, 0, "PickEvent", QStringLiteral("Events cannot be created"));
qmlRegisterType<Qt3DRender::Render::Quick::Quick3DRayCaster>(uri, 2, 11, "RayCaster");
qmlRegisterType<Qt3DRender::Render::Quick::Quick3DScreenRayCaster>(uri, 2, 11, "ScreenRayCaster");
// Compute Job
qmlRegisterType<Qt3DRender::QComputeCommand>(uri, 2, 0, "ComputeCommand");
+ qmlRegisterType<Qt3DRender::QComputeCommand, 13>(uri, 2, 13, "ComputeCommand");
// Layers
qmlRegisterType<Qt3DRender::QLayer>(uri, 2, 0, "Layer");
@@ -263,6 +270,8 @@ void Qt3DQuick3DRenderPlugin::registerTypes(const char *uri)
Qt3DRender::Quick::registerExtendedType<Qt3DRender::QMemoryBarrier, Qt3DRender::Render::Quick::Quick3DMemoryBarrier>("QMemoryBarrier", "Qt3D.Render/MemoryBarrier", uri, 2, 9, "MemoryBarrier");
qmlRegisterType<Qt3DRender::QProximityFilter>(uri, 2, 10, "ProximityFilter");
qmlRegisterType<Qt3DRender::QBlitFramebuffer>(uri, 2, 10, "BlitFramebuffer");
+ qmlRegisterType<Qt3DRender::QSetFence>(uri, 2, 13, "SetFence");
+ qmlRegisterType<Qt3DRender::QWaitFence>(uri, 2, 13, "WaitFence");
// RenderTarget
qmlRegisterType<Qt3DRender::QRenderTargetOutput>(uri, 2, 0, "RenderTargetOutput");
diff --git a/src/render/backend/abstractrenderer_p.h b/src/render/backend/abstractrenderer_p.h
index f1bdca7be..f19db066c 100644
--- a/src/render/backend/abstractrenderer_p.h
+++ b/src/render/backend/abstractrenderer_p.h
@@ -153,6 +153,7 @@ public:
virtual bool shouldRender() = 0;
virtual void skipNextFrame() = 0;
+ virtual QVector<Qt3DCore::QAspectJobPtr> preRenderingJobs() = 0;
virtual QVector<Qt3DCore::QAspectJobPtr> renderBinJobs() = 0;
virtual Qt3DCore::QAspectJobPtr pickBoundingVolumeJob() = 0;
virtual Qt3DCore::QAspectJobPtr rayCastingJob() = 0;
diff --git a/src/render/backend/computecommand.cpp b/src/render/backend/computecommand.cpp
index 2b23df9aa..349941965 100644
--- a/src/render/backend/computecommand.cpp
+++ b/src/render/backend/computecommand.cpp
@@ -40,7 +40,6 @@
#include "computecommand_p.h"
#include <Qt3DCore/qnode.h>
#include <Qt3DCore/qpropertyupdatedchange.h>
-#include <Qt3DRender/qcomputecommand.h>
#include <Qt3DRender/private/qcomputecommand_p.h>
#include <Qt3DRender/private/abstractrenderer_p.h>
@@ -51,7 +50,9 @@ namespace Qt3DRender {
namespace Render {
ComputeCommand::ComputeCommand()
- : BackendNode(ReadOnly)
+ : BackendNode(ReadWrite)
+ , m_frameCount(0)
+ , m_runType(QComputeCommand::Continuous)
{
m_workGroups[0] = 1;
m_workGroups[1] = 1;
@@ -68,6 +69,8 @@ void ComputeCommand::cleanup()
m_workGroups[0] = 1;
m_workGroups[1] = 1;
m_workGroups[2] = 1;
+ m_frameCount = 0;
+ m_runType = QComputeCommand::Continuous;
}
void ComputeCommand::initializeFromPeer(const Qt3DCore::QNodeCreatedChangeBasePtr &change)
@@ -77,6 +80,8 @@ void ComputeCommand::initializeFromPeer(const Qt3DCore::QNodeCreatedChangeBasePt
m_workGroups[0] = data.workGroupX;
m_workGroups[1] = data.workGroupY;
m_workGroups[2] = data.workGroupZ;
+ m_runType = data.runType;
+ m_frameCount = data.frameCount;
if (m_renderer != nullptr)
BackendNode::markDirty(AbstractRenderer::ComputeDirty);
}
@@ -91,11 +96,30 @@ void ComputeCommand::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e)
m_workGroups[1] = propertyChange->value().toInt();
else if (propertyChange->propertyName() == QByteArrayLiteral("workGroupZ"))
m_workGroups[2] = propertyChange->value().toInt();
- markDirty(AbstractRenderer::AllDirty);
+ else if (propertyChange->propertyName() == QByteArrayLiteral("frameCount"))
+ m_frameCount = propertyChange->value().toInt();
+ else if (propertyChange->propertyName() == QByteArrayLiteral("runType"))
+ m_runType = static_cast<QComputeCommand::RunType>(propertyChange->value().toInt());
+ markDirty(AbstractRenderer::ComputeDirty);
}
BackendNode::sceneChangeEvent(e);
}
+// Called from buildComputeRenderCommands in a job
+void ComputeCommand::updateFrameCount()
+{
+ // Disable frontend node when reaching 0
+ --m_frameCount;
+ if (m_frameCount <= 0) {
+ setEnabled(false);
+ auto e = Qt3DCore::QPropertyUpdatedChangePtr::create(peerId());
+ e->setDeliveryFlags(Qt3DCore::QSceneChange::DeliverToAll);
+ e->setPropertyName("enabled");
+ e->setValue(false);
+ notifyObservers(e);
+ }
+}
+
} // Render
} // Qt3DRender
diff --git a/src/render/backend/computecommand_p.h b/src/render/backend/computecommand_p.h
index fb8ca39ff..10e10fd25 100644
--- a/src/render/backend/computecommand_p.h
+++ b/src/render/backend/computecommand_p.h
@@ -52,6 +52,7 @@
//
#include <Qt3DRender/private/backendnode_p.h>
+#include <Qt3DRender/qcomputecommand.h>
QT_BEGIN_NAMESPACE
@@ -73,10 +74,17 @@ public:
inline int x() const Q_DECL_NOTHROW { return m_workGroups[0]; }
inline int y() const Q_DECL_NOTHROW { return m_workGroups[1]; }
inline int z() const Q_DECL_NOTHROW { return m_workGroups[2]; }
+ inline int frameCount() const Q_DECL_NOTHROW { return m_frameCount; }
+ inline QComputeCommand::RunType runType() const Q_DECL_NOTHROW { return m_runType; }
+
+ // Called from a job
+ void updateFrameCount();
private:
void initializeFromPeer(const Qt3DCore::QNodeCreatedChangeBasePtr &change) override;
int m_workGroups[3];
+ int m_frameCount;
+ QComputeCommand::RunType m_runType;
};
} // Render
diff --git a/src/render/backend/managers_p.h b/src/render/backend/managers_p.h
index b62e2f3e0..eb219fd1e 100644
--- a/src/render/backend/managers_p.h
+++ b/src/render/backend/managers_p.h
@@ -72,6 +72,7 @@
#include <Qt3DRender/private/shaderdata_p.h>
#include <Qt3DRender/private/handle_types_p.h>
#include <Qt3DRender/private/glbuffer_p.h>
+#include <Qt3DRender/private/glfence_p.h>
#include <Qt3DRender/private/textureimage_p.h>
#include <Qt3DRender/private/attribute_p.h>
#include <Qt3DRender/private/geometry_p.h>
@@ -317,6 +318,10 @@ class GLBufferManager : public Qt3DCore::QResourceManager<
{
};
+class GLFenceManager : public QHash<Qt3DCore::QNodeId, GLFence>
+{
+};
+
class TextureImageManager : public Qt3DCore::QResourceManager<
TextureImage,
Qt3DCore::QNodeId,
diff --git a/src/render/backend/nodemanagers.cpp b/src/render/backend/nodemanagers.cpp
index 5db35082d..584ddd65c 100644
--- a/src/render/backend/nodemanagers.cpp
+++ b/src/render/backend/nodemanagers.cpp
@@ -85,6 +85,7 @@ NodeManagers::NodeManagers()
, m_parameterManager(new ParameterManager())
, m_shaderDataManager(new ShaderDataManager())
, m_glBufferManager(new GLBufferManager())
+ , m_glFenceManager(new GLFenceManager())
, m_bufferManager(new BufferManager())
, m_attributeManager(new AttributeManager())
, m_geometryManager(new GeometryManager())
@@ -128,6 +129,7 @@ NodeManagers::~NodeManagers()
delete m_parameterManager;
delete m_shaderDataManager;
delete m_glBufferManager;
+ delete m_glFenceManager;
delete m_textureImageManager;
delete m_bufferManager;
delete m_attributeManager;
diff --git a/src/render/backend/nodemanagers_p.h b/src/render/backend/nodemanagers_p.h
index 9277d4385..2c4926894 100644
--- a/src/render/backend/nodemanagers_p.h
+++ b/src/render/backend/nodemanagers_p.h
@@ -87,6 +87,7 @@ class AttachmentManager;
class ParameterManager;
class ShaderDataManager;
class GLBufferManager;
+class GLFenceManager;
class TextureImageManager;
class FilterKeyManager;
class FrameGraphManager;
@@ -210,6 +211,7 @@ public:
inline ParameterManager *parameterManager() const Q_DECL_NOEXCEPT { return m_parameterManager; }
inline ShaderDataManager *shaderDataManager() const Q_DECL_NOEXCEPT { return m_shaderDataManager; }
inline GLBufferManager *glBufferManager() const Q_DECL_NOEXCEPT { return m_glBufferManager; }
+ inline GLFenceManager *glFenceManager() const Q_DECL_NOEXCEPT { return m_glFenceManager; }
inline TextureImageManager *textureImageManager() const Q_DECL_NOEXCEPT { return m_textureImageManager; }
inline BufferManager *bufferManager() const Q_DECL_NOEXCEPT { return m_bufferManager; }
inline AttributeManager *attributeManager() const Q_DECL_NOEXCEPT { return m_attributeManager; }
@@ -255,6 +257,7 @@ private:
ParameterManager *m_parameterManager;
ShaderDataManager *m_shaderDataManager;
GLBufferManager *m_glBufferManager;
+ GLFenceManager *m_glFenceManager;
BufferManager *m_bufferManager;
AttributeManager *m_attributeManager;
GeometryManager *m_geometryManager;
diff --git a/src/render/framegraph/blitframebuffer.cpp b/src/render/framegraph/blitframebuffer.cpp
index b9ff4bea3..342594baf 100644
--- a/src/render/framegraph/blitframebuffer.cpp
+++ b/src/render/framegraph/blitframebuffer.cpp
@@ -66,20 +66,26 @@ void BlitFramebuffer::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e)
QPropertyUpdatedChangePtr propertyChange = qSharedPointerCast<QPropertyUpdatedChange>(e);
if (propertyChange->propertyName() == QByteArrayLiteral("sourceRenderTarget")) {
m_sourceRenderTargetId = propertyChange->value().value<QNodeId>();
+ markDirty(AbstractRenderer::FrameGraphDirty);
} else if (propertyChange->propertyName() == QByteArrayLiteral("destinationRenderTarget")) {
m_destinationRenderTargetId = propertyChange->value().value<QNodeId>();
+ markDirty(AbstractRenderer::FrameGraphDirty);
} else if (propertyChange->propertyName() == QByteArrayLiteral("sourceRect")) {
m_sourceRect = propertyChange->value().toRect();
+ markDirty(AbstractRenderer::FrameGraphDirty);
} else if (propertyChange->propertyName() == QByteArrayLiteral("destinationRect")) {
m_destinationRect = propertyChange->value().toRect();
+ markDirty(AbstractRenderer::FrameGraphDirty);
} else if (propertyChange->propertyName() == QByteArrayLiteral("sourceAttachmentPoint")) {
m_sourceAttachmentPoint = propertyChange->value().value<Qt3DRender::QRenderTargetOutput::AttachmentPoint>();
+ markDirty(AbstractRenderer::FrameGraphDirty);
} else if (propertyChange->propertyName() == QByteArrayLiteral("destinationAttachmentPoint")) {
m_destinationAttachmentPoint = propertyChange->value().value<Qt3DRender::QRenderTargetOutput::AttachmentPoint>();
+ markDirty(AbstractRenderer::FrameGraphDirty);
} else if (propertyChange->propertyName() == QByteArrayLiteral("interpolationMethod")) {
m_interpolationMethod = propertyChange->value().value<QBlitFramebuffer::InterpolationMethod>();
+ markDirty(AbstractRenderer::FrameGraphDirty);
}
- markDirty(AbstractRenderer::AllDirty);
}
FrameGraphNode::sceneChangeEvent(e);
}
diff --git a/src/render/framegraph/cameraselectornode.cpp b/src/render/framegraph/cameraselectornode.cpp
index e21fdf02c..357611c7c 100644
--- a/src/render/framegraph/cameraselectornode.cpp
+++ b/src/render/framegraph/cameraselectornode.cpp
@@ -70,9 +70,10 @@ void CameraSelector::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e)
qCDebug(Render::Framegraph) << Q_FUNC_INFO;
if (e->type() == PropertyUpdated) {
QPropertyUpdatedChangePtr propertyChange = qSharedPointerCast<QPropertyUpdatedChange>(e);
- if (propertyChange->propertyName() == QByteArrayLiteral("camera"))
+ if (propertyChange->propertyName() == QByteArrayLiteral("camera")) {
m_cameraUuid = propertyChange->value().value<QNodeId>();
- markDirty(AbstractRenderer::AllDirty);
+ markDirty(AbstractRenderer::FrameGraphDirty);
+ }
}
FrameGraphNode::sceneChangeEvent(e);
}
diff --git a/src/render/framegraph/clearbuffers.cpp b/src/render/framegraph/clearbuffers.cpp
index d730123c3..ab6225a4b 100644
--- a/src/render/framegraph/clearbuffers.cpp
+++ b/src/render/framegraph/clearbuffers.cpp
@@ -80,17 +80,21 @@ void ClearBuffers::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e)
QPropertyUpdatedChangePtr propertyChange = qSharedPointerCast<QPropertyUpdatedChange>(e);
if (propertyChange->propertyName() == QByteArrayLiteral("buffers")) {
m_type = static_cast<QClearBuffers::BufferType>(propertyChange->value().toInt());
+ markDirty(AbstractRenderer::FrameGraphDirty);
} else if (propertyChange->propertyName() == QByteArrayLiteral("clearColor")) {
m_clearColorAsColor = propertyChange->value().value<QColor>();
m_clearColor = vec4dFromColor(m_clearColorAsColor);
+ markDirty(AbstractRenderer::FrameGraphDirty);
} else if (propertyChange->propertyName() == QByteArrayLiteral("clearDepthValue")) {
m_clearDepthValue = propertyChange->value().toFloat();
+ markDirty(AbstractRenderer::FrameGraphDirty);
} else if (propertyChange->propertyName() == QByteArrayLiteral("clearStencilValue")) {
m_clearStencilValue = propertyChange->value().toInt();
+ markDirty(AbstractRenderer::FrameGraphDirty);
} else if (propertyChange->propertyName() == QByteArrayLiteral("colorBuffer")) {
m_colorBufferId = propertyChange->value().value<QNodeId>();
+ markDirty(AbstractRenderer::FrameGraphDirty);
}
- markDirty(AbstractRenderer::AllDirty);
}
FrameGraphNode::sceneChangeEvent(e);
}
diff --git a/src/render/framegraph/dispatchcompute.cpp b/src/render/framegraph/dispatchcompute.cpp
index 6ffb42e71..f7e9dcff4 100644
--- a/src/render/framegraph/dispatchcompute.cpp
+++ b/src/render/framegraph/dispatchcompute.cpp
@@ -80,13 +80,16 @@ void DispatchCompute::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e)
{
if (e->type() == Qt3DCore::PropertyUpdated) {
Qt3DCore::QPropertyUpdatedChangePtr propertyChange = qSharedPointerCast<Qt3DCore::QPropertyUpdatedChange>(e);
- if (propertyChange->propertyName() == QByteArrayLiteral("workGroupX"))
+ if (propertyChange->propertyName() == QByteArrayLiteral("workGroupX")) {
m_workGroups[0] = propertyChange->value().toInt();
- else if (propertyChange->propertyName() == QByteArrayLiteral("workGroupY"))
+ markDirty(AbstractRenderer::FrameGraphDirty|AbstractRenderer::ComputeDirty);
+ } else if (propertyChange->propertyName() == QByteArrayLiteral("workGroupY")) {
m_workGroups[1] = propertyChange->value().toInt();
- else if (propertyChange->propertyName() == QByteArrayLiteral("workGroupZ"))
+ markDirty(AbstractRenderer::FrameGraphDirty|AbstractRenderer::ComputeDirty);
+ } else if (propertyChange->propertyName() == QByteArrayLiteral("workGroupZ")) {
m_workGroups[2] = propertyChange->value().toInt();
- markDirty(AbstractRenderer::AllDirty);
+ markDirty(AbstractRenderer::FrameGraphDirty|AbstractRenderer::ComputeDirty);
+ }
}
FrameGraphNode::sceneChangeEvent(e);
}
diff --git a/src/render/framegraph/framegraph.pri b/src/render/framegraph/framegraph.pri
index 9784a193c..b969c85e5 100644
--- a/src/render/framegraph/framegraph.pri
+++ b/src/render/framegraph/framegraph.pri
@@ -58,7 +58,13 @@ HEADERS += \
$$PWD/proximityfilter_p.h \
$$PWD/qblitframebuffer.h \
$$PWD/qblitframebuffer_p.h \
- $$PWD/blitframebuffer_p.h
+ $$PWD/blitframebuffer_p.h \
+ $$PWD/qsetfence.h \
+ $$PWD/qwaitfence.h \
+ $$PWD/qwaitfence_p.h \
+ $$PWD/qsetfence_p.h \
+ $$PWD/setfence_p.h \
+ $$PWD/waitfence_p.h
SOURCES += \
$$PWD/cameraselectornode.cpp \
@@ -100,4 +106,8 @@ SOURCES += \
$$PWD/qproximityfilter.cpp \
$$PWD/proximityfilter.cpp \
$$PWD/qblitframebuffer.cpp \
- $$PWD/blitframebuffer.cpp
+ $$PWD/blitframebuffer.cpp \
+ $$PWD/qsetfence.cpp \
+ $$PWD/qwaitfence.cpp \
+ $$PWD/setfence.cpp \
+ $$PWD/waitfence.cpp
diff --git a/src/render/framegraph/framegraphnode.cpp b/src/render/framegraph/framegraphnode.cpp
index f2ce1a50a..562548e46 100644
--- a/src/render/framegraph/framegraphnode.cpp
+++ b/src/render/framegraph/framegraphnode.cpp
@@ -157,23 +157,32 @@ void FrameGraphNode::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e)
case Qt3DCore::PropertyValueAdded: {
Qt3DCore::QPropertyNodeAddedChangePtr change = qSharedPointerCast<Qt3DCore::QPropertyNodeAddedChange>(e);
- if (change->metaObject()->inherits(&QFrameGraphNode::staticMetaObject))
+ if (change->metaObject()->inherits(&QFrameGraphNode::staticMetaObject)) {
appendChildId(change->addedNodeId());
+ markDirty(AbstractRenderer::FrameGraphDirty);
+ }
break;
}
case Qt3DCore::PropertyValueRemoved: {
Qt3DCore::QPropertyNodeRemovedChangePtr change = qSharedPointerCast<Qt3DCore::QPropertyNodeRemovedChange>(e);
- if (change->metaObject()->inherits(&QFrameGraphNode::staticMetaObject))
+ if (change->metaObject()->inherits(&QFrameGraphNode::staticMetaObject)) {
removeChildId(change->removedNodeId());
+ markDirty(AbstractRenderer::FrameGraphDirty);
+ }
+ break;
+ }
+ case Qt3DCore::PropertyUpdated: {
+ Qt3DCore::QPropertyUpdatedChangePtr propertyChange = qSharedPointerCast<Qt3DCore::QPropertyUpdatedChange>(e);
+ if (propertyChange->propertyName() == QByteArrayLiteral("enabled")) {
+ d_func()->m_enabled = propertyChange->value().toBool();
+ markDirty(AbstractRenderer::FrameGraphDirty);
+ }
break;
}
default:
break;
}
-
- markDirty(AbstractRenderer::AllDirty);
- BackendNode::sceneChangeEvent(e);
}
} // namespace Render
diff --git a/src/render/framegraph/framegraphnode_p.h b/src/render/framegraph/framegraphnode_p.h
index 19165f56f..87d5c79e8 100644
--- a/src/render/framegraph/framegraphnode_p.h
+++ b/src/render/framegraph/framegraphnode_p.h
@@ -101,7 +101,9 @@ public:
BufferCapture,
MemoryBarrier,
ProximityFilter,
- BlitFramebuffer
+ BlitFramebuffer,
+ SetFence,
+ WaitFence
};
FrameGraphNodeType nodeType() const { return m_nodeType; }
diff --git a/src/render/framegraph/layerfilternode.cpp b/src/render/framegraph/layerfilternode.cpp
index 4b6842015..f9881be0d 100644
--- a/src/render/framegraph/layerfilternode.cpp
+++ b/src/render/framegraph/layerfilternode.cpp
@@ -73,7 +73,7 @@ void LayerFilterNode::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e)
const auto change = qSharedPointerCast<QPropertyNodeAddedChange>(e);
if (change->propertyName() == QByteArrayLiteral("layer"))
m_layerIds.append(change->addedNodeId());
- markDirty(AbstractRenderer::LayersDirty);
+ markDirty(AbstractRenderer::FrameGraphDirty|AbstractRenderer::LayersDirty);
break;
}
@@ -81,7 +81,7 @@ void LayerFilterNode::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e)
const auto change = qSharedPointerCast<QPropertyNodeRemovedChange>(e);
if (change->propertyName() == QByteArrayLiteral("layer"))
m_layerIds.removeOne(change->removedNodeId());
- markDirty(AbstractRenderer::LayersDirty);
+ markDirty(AbstractRenderer::FrameGraphDirty|AbstractRenderer::LayersDirty);
break;
}
@@ -89,6 +89,7 @@ void LayerFilterNode::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e)
const auto change = qSharedPointerCast<QPropertyUpdatedChange>(e);
if (change->propertyName() == QByteArrayLiteral("filterMode")) {
m_filterMode = static_cast<QLayerFilter::FilterMode>(change->value().value<int>());
+ markDirty(AbstractRenderer::FrameGraphDirty|AbstractRenderer::LayersDirty);
break;
}
}
diff --git a/src/render/framegraph/memorybarrier.cpp b/src/render/framegraph/memorybarrier.cpp
index ee8f156e3..59b3071ab 100644
--- a/src/render/framegraph/memorybarrier.cpp
+++ b/src/render/framegraph/memorybarrier.cpp
@@ -68,7 +68,7 @@ void MemoryBarrier::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e)
Qt3DCore::QPropertyUpdatedChangePtr propertyChange = qSharedPointerCast<Qt3DCore::QPropertyUpdatedChange>(e);
if (propertyChange->propertyName() == QByteArrayLiteral("waitOperations")) {
m_waitOperations = propertyChange->value().value<QMemoryBarrier::Operations>();
- markDirty(AbstractRenderer::AllDirty);
+ markDirty(AbstractRenderer::FrameGraphDirty);
}
}
FrameGraphNode::sceneChangeEvent(e);
diff --git a/src/render/framegraph/proximityfilter.cpp b/src/render/framegraph/proximityfilter.cpp
index aed19828b..cdfd7e51e 100644
--- a/src/render/framegraph/proximityfilter.cpp
+++ b/src/render/framegraph/proximityfilter.cpp
@@ -67,11 +67,13 @@ void ProximityFilter::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e)
qCDebug(Render::Framegraph) << Q_FUNC_INFO;
if (e->type() == Qt3DCore::PropertyUpdated) {
Qt3DCore::QPropertyUpdatedChangePtr propertyChange = qSharedPointerCast<Qt3DCore::QPropertyUpdatedChange>(e);
- if (propertyChange->propertyName() == QByteArrayLiteral("entity"))
+ if (propertyChange->propertyName() == QByteArrayLiteral("entity")) {
m_entityId = propertyChange->value().value<Qt3DCore::QNodeId>();
- else if (propertyChange->propertyName() == QByteArrayLiteral("distanceThreshold"))
+ markDirty(AbstractRenderer::FrameGraphDirty);
+ } else if (propertyChange->propertyName() == QByteArrayLiteral("distanceThreshold")) {
m_distanceThreshold = propertyChange->value().toFloat();
- markDirty(AbstractRenderer::AllDirty);
+ markDirty(AbstractRenderer::FrameGraphDirty);
+ }
}
FrameGraphNode::sceneChangeEvent(e);
}
diff --git a/src/render/framegraph/qsetfence.cpp b/src/render/framegraph/qsetfence.cpp
new file mode 100644
index 000000000..5cb82f3db
--- /dev/null
+++ b/src/render/framegraph/qsetfence.cpp
@@ -0,0 +1,174 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 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 "qsetfence.h"
+#include "qsetfence_p.h"
+#include <Qt3DRender/private/qframegraphnodecreatedchange_p.h>
+#include <Qt3DCore/qpropertyupdatedchange.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+QSetFencePrivate::QSetFencePrivate()
+ : QFrameGraphNodePrivate()
+ , m_handleType(QSetFence::NoHandle)
+ , m_handle(QVariant())
+{
+}
+
+/*!
+ \class QSetFence
+ \brief FrameGraphNode used to insert a fence in the graphics command stream.
+
+ Fence allow to synchronosize GPU and CPU workloads. GPU commands usually
+ are non-blocking. When issued, commands are inserted in command buffers
+ which will be read at a later time by the GPU. In some cases, you want to
+ continue processing or issue specific command only when you are sure a
+ command has been executed by the hardware. Fences are a way to do so. This
+ is especially important when using 3rd party engines with Qt3D, Qt3D should
+ only access shared resources when we know the other engine command are done
+ modifying the resource.
+
+ QSetFence is a FrameGraph node that inserts a fence into the command
+ stream. It can then be used in conjunction with \l QWaitFence or by
+ extracting the underlying handle.
+
+ The handle property will be updated once the renderer has created the
+ underlying fence resource. The handle will remain valid as long as it
+ remains in the unsignaled state. Once it has reached the signaled state, it
+ will be destroyed and a new handle will be created. That means that
+ depending on how long it takes for the fence to be signaled, the same
+ handle could be used over several frames.
+
+ \since 5.13
+ */
+QSetFence::QSetFence(Qt3DCore::QNode *parent)
+ : QFrameGraphNode(*new QSetFencePrivate(), parent)
+{
+}
+
+QSetFence::~QSetFence()
+{
+}
+
+QSetFence::QSetFence(QSetFencePrivate &dd, Qt3DCore::QNode *parent)
+ : QFrameGraphNode(dd, parent)
+{
+}
+
+/*!
+ \qmlproperty HandleType SetFence::handleType
+
+ Specifies the type of handle being used. Currently only OpenGL Fence ids
+ are supported.
+*/
+/*!
+ \property QSetFence::handleType
+
+ Specifies the type of handle being used. Currently only OpenGL Fence ids
+ are supported.
+*/
+QSetFence::HandleType QSetFence::handleType() const
+{
+ Q_D(const QSetFence);
+ return d->m_handleType;
+}
+
+void QSetFencePrivate::setHandleType(QSetFence::HandleType type)
+{
+ Q_Q(QSetFence);
+ if (m_handleType != type) {
+ const bool blocked = q->blockNotifications(true);
+ m_handleType = type;
+ emit q->handleTypeChanged(type);
+ q->blockNotifications(blocked);
+ }
+}
+
+/*!
+ \qmlproperty variant AbstractFence::handle
+
+ Holds the underlying fence handle wrapped in a variant.
+*/
+/*!
+ \property QAbstractFence::handle
+
+ Holds the underlying fence handle wrapped in a QVariant.
+*/
+QVariant QSetFence::handle() const
+{
+ Q_D(const QSetFence);
+ return d->m_handle;
+}
+
+void QSetFencePrivate::setHandle(QVariant handle)
+{
+ Q_Q(QSetFence);
+ if (m_handle != handle) {
+ const bool blocked = q->blockNotifications(true);
+ m_handle = handle;
+ emit q->handleChanged(handle);
+ q->blockNotifications(blocked);
+ }
+}
+
+void QSetFence::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &change)
+{
+ Qt3DCore::QPropertyUpdatedChangePtr e = qSharedPointerCast<Qt3DCore::QPropertyUpdatedChange>(change);
+ if (e->type() == Qt3DCore::PropertyUpdated) {
+ Q_D(QSetFence);
+ if (e->propertyName() == QByteArrayLiteral("handle"))
+ d->setHandle(e->value());
+ else if (e->propertyName() == QByteArrayLiteral("handleType"))
+ d->setHandleType(static_cast<Qt3DRender::QSetFence::HandleType>(e->value().toInt()));
+ }
+}
+
+Qt3DCore::QNodeCreatedChangeBasePtr QSetFence::createNodeCreationChange() const
+{
+ auto creationChange = QFrameGraphNodeCreatedChangePtr<QSetFenceData>::create(this);
+ QSetFenceData &data = creationChange->data;
+ Q_UNUSED(data); // Might be of use later
+ return creationChange;
+}
+
+} // Qt3DRender
+
+QT_END_NAMESPACE
diff --git a/src/render/framegraph/qsetfence.h b/src/render/framegraph/qsetfence.h
new file mode 100644
index 000000000..4834601ea
--- /dev/null
+++ b/src/render/framegraph/qsetfence.h
@@ -0,0 +1,86 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 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/QFrameGraphNode>
+
+#ifndef QT3DRENDER_QSETFENCE_H
+#define QT3DRENDER_QSETFENCE_H
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+class QSetFencePrivate;
+
+class QT3DRENDERSHARED_EXPORT QSetFence : public QFrameGraphNode
+{
+ Q_OBJECT
+ Q_PROPERTY(HandleType handleType READ handleType NOTIFY handleTypeChanged)
+ Q_PROPERTY(QVariant handle READ handle NOTIFY handleChanged)
+public:
+ enum HandleType {
+ NoHandle,
+ OpenGLFenceId
+ };
+ Q_ENUM(HandleType) // LCOV_EXCL_LINE
+
+ explicit QSetFence(Qt3DCore::QNode *parent = nullptr);
+ ~QSetFence();
+
+ HandleType handleType() const;
+ QVariant handle() const;
+
+Q_SIGNALS:
+ void handleTypeChanged(HandleType handleType);
+ void handleChanged(QVariant handle);
+
+protected:
+ explicit QSetFence(QSetFencePrivate &dd, Qt3DCore::QNode *parent = nullptr);
+ void sceneChangeEvent(const Qt3DCore::QSceneChangePtr &change) override;
+
+private:
+ Q_DECLARE_PRIVATE(QSetFence)
+ Qt3DCore::QNodeCreatedChangeBasePtr createNodeCreationChange() const override;
+};
+
+} // Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_QSETFENCE_H
diff --git a/src/render/framegraph/qsetfence_p.h b/src/render/framegraph/qsetfence_p.h
new file mode 100644
index 000000000..026afffa5
--- /dev/null
+++ b/src/render/framegraph/qsetfence_p.h
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 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_QSETFENCE_P_H
+#define QT3DRENDER_QSETFENCE_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/qframegraphnode_p.h>
+#include <Qt3DRender/qsetfence.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+class QSetFencePrivate : public QFrameGraphNodePrivate
+{
+public:
+ QSetFencePrivate();
+ void setHandleType(QSetFence::HandleType type);
+ void setHandle(QVariant handle);
+
+ QSetFence::HandleType m_handleType;
+ QVariant m_handle;
+
+ Q_DECLARE_PUBLIC(QSetFence)
+};
+
+struct QSetFenceData
+{
+};
+
+} // Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_QSETFENCE_P_H
diff --git a/src/render/framegraph/qwaitfence.cpp b/src/render/framegraph/qwaitfence.cpp
new file mode 100644
index 000000000..5195653ce
--- /dev/null
+++ b/src/render/framegraph/qwaitfence.cpp
@@ -0,0 +1,219 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 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 "qwaitfence.h"
+#include "qwaitfence_p.h"
+#include <Qt3DRender/private/qframegraphnode_p.h>
+#include <Qt3DRender/private/qframegraphnodecreatedchange_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+QWaitFencePrivate::QWaitFencePrivate()
+ : QFrameGraphNodePrivate()
+ , m_handleType(QWaitFence::NoHandle)
+ , m_handle(QVariant())
+ , m_waitOnCPU(false)
+ , m_timeout(std::numeric_limits<quint64>::max())
+{
+}
+
+/*!
+ \class QWaitFence
+
+ \brief FrameGraphNode used to wait for a fence in the graphics command
+ stream to become signaled.
+
+ Fence allow to synchronosize GPU and CPU workloads. GPU commands usually
+ are non-blocking. When issued, commands are inserted in command buffers
+ which will be read at a later time by the GPU. In some cases, you want to
+ continue processing or issue specific command only when you are sure a
+ command has been executed by the hardware. Fences are a way to do so. This
+ is especially important when using 3rd party engines with Qt3D, Qt3D should
+ only access shared resources when we know the other engine command are done
+ modifying the resource.
+
+ QWaitFence is a FrameGraph node that will force to wait for it to become
+ signaled before subsequent commands are inserted into the command stream.
+ It can then be used in conjunction with \l QSetFence and contains
+ properties to configure how long it should wait and whether it should block
+ on the CPU side.
+
+ \note Qt 3D uploads GPU resources (Texture, Shaders, Buffers) before
+ issuing draw calls.
+
+ \since 5.13
+ */
+
+QWaitFence::QWaitFence(Qt3DCore::QNode *parent)
+ : QFrameGraphNode(*new QWaitFencePrivate(), parent)
+{
+}
+
+QWaitFence::~QWaitFence()
+{
+}
+
+/*!
+ \qmlproperty bool WaitFence::waitOnCPU
+
+ Specifies whether the CPU should be block while waiting for the fence to
+ become signaled. This is false by default.
+*/
+/*!
+ \property QWaitFence::waitOnCPU
+
+ Specifies whether the CPU should be block while waiting for the fence to
+ become signaled. This is false by default.
+*/
+bool QWaitFence::waitOnCPU() const
+{
+ Q_D(const QWaitFence);
+ return d->m_waitOnCPU;
+}
+
+void QWaitFence::setWaitOnCPU(bool waitOnCPU)
+{
+ Q_D(QWaitFence);
+ if (d->m_waitOnCPU == waitOnCPU)
+ return;
+ d->m_waitOnCPU = waitOnCPU;
+ emit waitOnCPUChanged(waitOnCPU);
+}
+
+/*!
+ \qmlproperty int WaitFence::timeout
+
+ Specifies the maximum amount of time in nanoseconds to wait for the fence
+ to become signaled.
+*/
+/*!
+ \property QWaitFence::timeout
+
+ Specifies the maximum amount of time in nanoseconds to wait for the fence
+ to become signaled.
+*/
+quint64 QWaitFence::timeout() const
+{
+ Q_D(const QWaitFence);
+ return d->m_timeout;
+}
+
+void QWaitFence::setTimeout(quint64 timeout)
+{
+ Q_D(QWaitFence);
+ if (d->m_timeout == timeout)
+ return;
+ d->m_timeout = timeout;
+ emit timeoutChanged(timeout);
+}
+
+QWaitFence::QWaitFence(QWaitFencePrivate &dd, Qt3DCore::QNode *parent)
+ : QFrameGraphNode(dd, parent)
+{
+}
+
+/*!
+ \qmlproperty HandleType WaitFence::handleType
+
+ Specifies the type of handle being used. Currently only OpenGL Fence ids
+ are supported.
+*/
+/*!
+ \property QWaitFence::handleType
+
+ Specifies the type of handle being used. Currently only OpenGL Fence ids
+ are supported.
+*/
+QWaitFence::HandleType QWaitFence::handleType() const
+{
+ Q_D(const QWaitFence);
+ return d->m_handleType;
+}
+
+void QWaitFence::setHandleType(QWaitFence::HandleType type)
+{
+ Q_D(QWaitFence);
+ if (d->m_handleType != type) {
+ d->m_handleType = type;
+ emit handleTypeChanged(type);
+ }
+}
+
+/*!
+ \qmlproperty variant WaitFence::handle
+
+ Holds the underlying fence handle wrapped in a variant.
+*/
+/*!
+ \property QWaitFence::handle
+
+ Holds the underlying fence handle wrapped in a QVariant.
+*/
+QVariant QWaitFence::handle() const
+{
+ Q_D(const QWaitFence);
+ return d->m_handle;
+}
+
+void QWaitFence::setHandle(QVariant handle)
+{
+ Q_D(QWaitFence);
+ if (d->m_handle != handle) {
+ d->m_handle = handle;
+ emit handleChanged(handle);
+ }
+}
+
+Qt3DCore::QNodeCreatedChangeBasePtr QWaitFence::createNodeCreationChange() const
+{
+ auto creationChange = QFrameGraphNodeCreatedChangePtr<QWaitFenceData>::create(this);
+ QWaitFenceData &data = creationChange->data;
+ Q_D(const QWaitFence);
+ data.handleType = d->m_handleType;
+ data.handle = d->m_handle;
+ data.timeout = d->m_timeout;
+ data.waitOnCPU = d->m_waitOnCPU;
+ return creationChange;
+}
+
+} // Qt3DRender
+
+QT_END_NAMESPACE
diff --git a/src/render/framegraph/qwaitfence.h b/src/render/framegraph/qwaitfence.h
new file mode 100644
index 000000000..4f3e7ae8b
--- /dev/null
+++ b/src/render/framegraph/qwaitfence.h
@@ -0,0 +1,98 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 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_QWAITFENCE_H
+#define QT3DRENDER_QWAITFENCE_H
+
+#include <Qt3DRender/QFrameGraphNode>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+class QWaitFencePrivate;
+
+class QT3DRENDERSHARED_EXPORT QWaitFence : public QFrameGraphNode
+{
+ Q_OBJECT
+ Q_PROPERTY(HandleType handleType READ handleType WRITE setHandleType NOTIFY handleTypeChanged)
+ Q_PROPERTY(QVariant handle READ handle WRITE setHandle NOTIFY handleChanged)
+ Q_PROPERTY(bool waitOnCPU READ waitOnCPU WRITE setWaitOnCPU NOTIFY waitOnCPUChanged)
+ Q_PROPERTY(quint64 timeout READ timeout WRITE setTimeout NOTIFY timeoutChanged)
+
+public:
+ enum HandleType {
+ NoHandle,
+ OpenGLFenceId
+ };
+ Q_ENUM(HandleType) // LCOV_EXCL_LINE
+ explicit QWaitFence(Qt3DCore::QNode *parent = nullptr);
+ ~QWaitFence();
+
+ void setHandleType(HandleType type);
+ void setHandle(QVariant handle);
+
+ HandleType handleType() const;
+ QVariant handle() const;
+
+ bool waitOnCPU() const;
+ void setWaitOnCPU(bool waitOnCPU);
+
+ quint64 timeout() const;
+ void setTimeout(quint64 timeout);
+
+Q_SIGNALS:
+ void waitOnCPUChanged(bool waitOnCPU);
+ void timeoutChanged(quint64 timeoutChanged);
+ void handleTypeChanged(HandleType handleType);
+ void handleChanged(QVariant handle);
+
+protected:
+ explicit QWaitFence(QWaitFencePrivate &dd, Qt3DCore::QNode *parent = nullptr);
+
+private:
+ Q_DECLARE_PRIVATE(QWaitFence)
+ Qt3DCore::QNodeCreatedChangeBasePtr createNodeCreationChange() const override;
+};
+
+} // Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_QWAITFENCE_H
diff --git a/src/render/framegraph/qwaitfence_p.h b/src/render/framegraph/qwaitfence_p.h
new file mode 100644
index 000000000..480d69cad
--- /dev/null
+++ b/src/render/framegraph/qwaitfence_p.h
@@ -0,0 +1,85 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 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_QWAITFENCE_P_H
+#define QT3DRENDER_QWAITFENCE_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/qframegraphnode_p.h>
+#include <Qt3DRender/qwaitfence.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+class QWaitFencePrivate : public QFrameGraphNodePrivate
+{
+public:
+ QWaitFencePrivate();
+
+ QWaitFence::HandleType m_handleType;
+ QVariant m_handle;
+ bool m_waitOnCPU;
+ quint64 m_timeout;
+};
+
+struct QWaitFenceData
+{
+ QWaitFence::HandleType handleType;
+ QVariant handle;
+ bool waitOnCPU;
+ quint64 timeout;
+};
+
+} // Qt3DRender
+
+QT_END_NAMESPACE
+
+
+#endif // QT3DRENDER_QWAITFENCE_P_H
diff --git a/src/render/framegraph/rendercapture.cpp b/src/render/framegraph/rendercapture.cpp
index d25a01b1f..166294889 100644
--- a/src/render/framegraph/rendercapture.cpp
+++ b/src/render/framegraph/rendercapture.cpp
@@ -76,6 +76,7 @@ void RenderCapture::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e)
Qt3DCore::QPropertyUpdatedChangePtr propertyChange = qSharedPointerCast<Qt3DCore::QPropertyUpdatedChange>(e);
if (propertyChange->propertyName() == QByteArrayLiteral("renderCaptureRequest")) {
requestCapture(propertyChange->value().value<QRenderCaptureRequest>());
+ markDirty(AbstractRenderer::FrameGraphDirty);
}
}
FrameGraphNode::sceneChangeEvent(e);
diff --git a/src/render/framegraph/renderpassfilternode.cpp b/src/render/framegraph/renderpassfilternode.cpp
index c9277b951..e3da1e36d 100644
--- a/src/render/framegraph/renderpassfilternode.cpp
+++ b/src/render/framegraph/renderpassfilternode.cpp
@@ -97,10 +97,10 @@ void RenderPassFilter::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e)
const auto change = qSharedPointerCast<QPropertyNodeAddedChange>(e);
if (change->propertyName() == QByteArrayLiteral("match")) {
appendFilter(change->addedNodeId());
- markDirty(AbstractRenderer::AllDirty);
+ markDirty(AbstractRenderer::FrameGraphDirty);
} else if (change->propertyName() == QByteArrayLiteral("parameter")) {
m_parameterPack.appendParameter(change->addedNodeId());
- markDirty(AbstractRenderer::AllDirty);
+ markDirty(AbstractRenderer::FrameGraphDirty);
}
break;
}
@@ -109,10 +109,10 @@ void RenderPassFilter::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e)
const auto change = qSharedPointerCast<QPropertyNodeRemovedChange>(e);
if (change->propertyName() == QByteArrayLiteral("match")) {
removeFilter(change->removedNodeId());
- markDirty(AbstractRenderer::AllDirty);
+ markDirty(AbstractRenderer::FrameGraphDirty);
} else if (change->propertyName() == QByteArrayLiteral("parameter")) {
m_parameterPack.removeParameter(change->removedNodeId());
- markDirty(AbstractRenderer::AllDirty);
+ markDirty(AbstractRenderer::FrameGraphDirty);
}
break;
}
diff --git a/src/render/framegraph/rendersurfaceselector.cpp b/src/render/framegraph/rendersurfaceselector.cpp
index 7f2fd6170..16a1199b5 100644
--- a/src/render/framegraph/rendersurfaceselector.cpp
+++ b/src/render/framegraph/rendersurfaceselector.cpp
@@ -101,17 +101,22 @@ void RenderSurfaceSelector::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e)
qCDebug(Render::Framegraph) << Q_FUNC_INFO;
if (e->type() == PropertyUpdated) {
QPropertyUpdatedChangePtr propertyChange = qSharedPointerCast<QPropertyUpdatedChange>(e);
- if (propertyChange->propertyName() == QByteArrayLiteral("surface"))
+ if (propertyChange->propertyName() == QByteArrayLiteral("surface")) {
m_surface = surfaceFromQObject(propertyChange->value().value<QObject *>());
- else if (propertyChange->propertyName() == QByteArrayLiteral("externalRenderTargetSize"))
+ markDirty(AbstractRenderer::FrameGraphDirty);
+ } else if (propertyChange->propertyName() == QByteArrayLiteral("externalRenderTargetSize")) {
setRenderTargetSize(propertyChange->value().toSize());
- else if (propertyChange->propertyName() == QByteArrayLiteral("width"))
+ markDirty(AbstractRenderer::FrameGraphDirty);
+ } else if (propertyChange->propertyName() == QByteArrayLiteral("width")) {
m_width = propertyChange->value().toInt();
- else if (propertyChange->propertyName() == QByteArrayLiteral("height"))
+ markDirty(AbstractRenderer::FrameGraphDirty);
+ } else if (propertyChange->propertyName() == QByteArrayLiteral("height")) {
m_height = propertyChange->value().toInt();
- else if (propertyChange->propertyName() == QByteArrayLiteral("surfacePixelRatio"))
+ markDirty(AbstractRenderer::FrameGraphDirty);
+ } else if (propertyChange->propertyName() == QByteArrayLiteral("surfacePixelRatio")) {
m_devicePixelRatio = propertyChange->value().toFloat();
- markDirty(AbstractRenderer::AllDirty);
+ markDirty(AbstractRenderer::FrameGraphDirty);
+ }
}
FrameGraphNode::sceneChangeEvent(e);
}
diff --git a/src/render/framegraph/rendertargetselectornode.cpp b/src/render/framegraph/rendertargetselectornode.cpp
index 4b6e2da84..615608bd2 100644
--- a/src/render/framegraph/rendertargetselectornode.cpp
+++ b/src/render/framegraph/rendertargetselectornode.cpp
@@ -73,11 +73,13 @@ void RenderTargetSelector::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e)
qCDebug(Render::Framegraph) << Q_FUNC_INFO;
if (e->type() == PropertyUpdated) {
QPropertyUpdatedChangePtr propertyChange = qSharedPointerCast<QPropertyUpdatedChange>(e);
- if (propertyChange->propertyName() == QByteArrayLiteral("target"))
+ if (propertyChange->propertyName() == QByteArrayLiteral("target")) {
m_renderTargetUuid = propertyChange->value().value<QNodeId>();
- else if (propertyChange->propertyName() == QByteArrayLiteral("outputs"))
+ markDirty(AbstractRenderer::FrameGraphDirty);
+ } else if (propertyChange->propertyName() == QByteArrayLiteral("outputs")) {
m_outputs = propertyChange->value().value<QVector<Qt3DRender::QRenderTargetOutput::AttachmentPoint> >();
- markDirty(AbstractRenderer::AllDirty);
+ markDirty(AbstractRenderer::FrameGraphDirty);
+ }
}
FrameGraphNode::sceneChangeEvent(e);
}
diff --git a/src/render/framegraph/setfence.cpp b/src/render/framegraph/setfence.cpp
new file mode 100644
index 000000000..b2e995b25
--- /dev/null
+++ b/src/render/framegraph/setfence.cpp
@@ -0,0 +1,79 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 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 "setfence_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+namespace Render {
+
+SetFence::SetFence()
+ : FrameGraphNode(FrameGraphNode::SetFence, QBackendNode::ReadWrite)
+{
+}
+
+SetFence::~SetFence()
+{
+}
+
+void SetFence::setHandle(const QVariant &handle)
+{
+ auto change = Qt3DCore::QPropertyUpdatedChangePtr::create(peerId());
+ change->setDeliveryFlags(Qt3DCore::QSceneChange::Nodes);
+ change->setPropertyName("handle");
+ change->setValue(handle);
+ notifyObservers(change);
+}
+
+void SetFence::setHandleType(QSetFence::HandleType type)
+{
+ auto change = Qt3DCore::QPropertyUpdatedChangePtr::create(peerId());
+ change->setDeliveryFlags(Qt3DCore::QSceneChange::Nodes);
+ change->setPropertyName("handleType");
+ change->setValue(QVariant::fromValue(type));
+ notifyObservers(change);
+}
+
+} // namespace Render
+
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
diff --git a/src/render/framegraph/setfence_p.h b/src/render/framegraph/setfence_p.h
new file mode 100644
index 000000000..31072581c
--- /dev/null
+++ b/src/render/framegraph/setfence_p.h
@@ -0,0 +1,80 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 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_SETFENCE_P_H
+#define QT3DRENDER_RENDER_SETFENCE_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/framegraphnode_p.h>
+#include <Qt3DRender/qsetfence.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+namespace Render {
+
+class Q_AUTOTEST_EXPORT SetFence : public FrameGraphNode
+{
+public:
+ SetFence();
+ ~SetFence();
+
+ // Called by a job
+ void setHandle(const QVariant &handle);
+ void setHandleType(QSetFence::HandleType type);
+};
+
+} // namespace Render
+
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDER_SETFENCE_P_H
diff --git a/src/render/framegraph/sortpolicy.cpp b/src/render/framegraph/sortpolicy.cpp
index 9631ebeab..b81d1f6cb 100644
--- a/src/render/framegraph/sortpolicy.cpp
+++ b/src/render/framegraph/sortpolicy.cpp
@@ -61,9 +61,9 @@ void SortPolicy::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e)
auto sortTypesInt = propertyChange->value().value<QVector<int>>();
m_sortTypes.clear();
transformVector(sortTypesInt, m_sortTypes);
+ markDirty(AbstractRenderer::FrameGraphDirty);
}
}
- markDirty(AbstractRenderer::AllDirty);
FrameGraphNode::sceneChangeEvent(e);
}
diff --git a/src/render/framegraph/statesetnode.cpp b/src/render/framegraph/statesetnode.cpp
index 2ffbb70c7..96551684e 100644
--- a/src/render/framegraph/statesetnode.cpp
+++ b/src/render/framegraph/statesetnode.cpp
@@ -84,7 +84,7 @@ void StateSetNode::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e)
const auto change = qSharedPointerCast<QPropertyNodeAddedChange>(e);
if (change->propertyName() == QByteArrayLiteral("renderState")) {
addRenderState(change->addedNodeId());
- markDirty(AbstractRenderer::AllDirty);
+ markDirty(AbstractRenderer::FrameGraphDirty);
}
break;
}
@@ -93,7 +93,7 @@ void StateSetNode::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e)
const auto propertyChange = qSharedPointerCast<QPropertyNodeRemovedChange>(e);
if (propertyChange->propertyName() == QByteArrayLiteral("renderState")) {
removeRenderState(propertyChange->removedNodeId());
- markDirty(AbstractRenderer::AllDirty);
+ markDirty(AbstractRenderer::FrameGraphDirty);
}
break;
}
diff --git a/src/render/framegraph/techniquefilternode.cpp b/src/render/framegraph/techniquefilternode.cpp
index 59f49af02..8816984ee 100644
--- a/src/render/framegraph/techniquefilternode.cpp
+++ b/src/render/framegraph/techniquefilternode.cpp
@@ -96,10 +96,10 @@ void TechniqueFilter::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e)
const auto change = qSharedPointerCast<QPropertyNodeAddedChange>(e);
if (change->propertyName() == QByteArrayLiteral("matchAll")) {
appendFilter(change->addedNodeId());
- markDirty(AbstractRenderer::AllDirty);
+ markDirty(AbstractRenderer::FrameGraphDirty);
} else if (change->propertyName() == QByteArrayLiteral("parameter")) {
m_parameterPack.appendParameter(change->addedNodeId());
- markDirty(AbstractRenderer::AllDirty);
+ markDirty(AbstractRenderer::FrameGraphDirty);
}
break;
}
@@ -108,10 +108,10 @@ void TechniqueFilter::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e)
const auto change = qSharedPointerCast<QPropertyNodeRemovedChange>(e);
if (change->propertyName() == QByteArrayLiteral("matchAll")) {
removeFilter(change->removedNodeId());
- markDirty(AbstractRenderer::AllDirty);
+ markDirty(AbstractRenderer::FrameGraphDirty);
} else if (change->propertyName() == QByteArrayLiteral("parameter")) {
m_parameterPack.removeParameter(change->removedNodeId());
- markDirty(AbstractRenderer::AllDirty);
+ markDirty(AbstractRenderer::FrameGraphDirty);
}
break;
}
diff --git a/src/render/framegraph/viewportnode.cpp b/src/render/framegraph/viewportnode.cpp
index bed87a13f..b3b53b0f9 100644
--- a/src/render/framegraph/viewportnode.cpp
+++ b/src/render/framegraph/viewportnode.cpp
@@ -128,10 +128,11 @@ void ViewportNode::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e)
setYMin(normalizedRect.y());
setXMax(normalizedRect.width());
setYMax(normalizedRect.height());
+ markDirty(AbstractRenderer::FrameGraphDirty);
} else if (propertyChange->propertyName() == QByteArrayLiteral("gamma")) {
setGamma(propertyChange->value().toFloat());
+ markDirty(AbstractRenderer::FrameGraphDirty);
}
- markDirty(AbstractRenderer::AllDirty);
}
FrameGraphNode::sceneChangeEvent(e);
}
diff --git a/src/render/framegraph/waitfence.cpp b/src/render/framegraph/waitfence.cpp
new file mode 100644
index 000000000..9480fb7a0
--- /dev/null
+++ b/src/render/framegraph/waitfence.cpp
@@ -0,0 +1,107 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 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 "waitfence_p.h"
+#include <Qt3DRender/private/qwaitfence_p.h>
+#include <Qt3DCore/qpropertyupdatedchange.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.
+//
+
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+namespace Render {
+
+WaitFence::WaitFence()
+ : FrameGraphNode(FrameGraphNode::WaitFence)
+{
+ m_data.handleType = QWaitFence::NoHandle;
+ m_data.waitOnCPU = false;
+ m_data.timeout = std::numeric_limits<quint64>::max();
+}
+
+WaitFence::~WaitFence()
+{
+}
+
+void WaitFence::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e)
+{
+ if (e->type() == Qt3DCore::PropertyUpdated) {
+ Qt3DCore::QPropertyUpdatedChangePtr propertyChange = qSharedPointerCast<Qt3DCore::QPropertyUpdatedChange>(e);
+ if (propertyChange->propertyName() == QByteArrayLiteral("handle")) {
+ m_data.handle = propertyChange->value();
+ markDirty(AbstractRenderer::FrameGraphDirty);
+ } else if (propertyChange->propertyName() == QByteArrayLiteral("handleType")) {
+ m_data.handleType = static_cast<QWaitFence::HandleType>(propertyChange->value().toInt());
+ markDirty(AbstractRenderer::FrameGraphDirty);
+ } else if (propertyChange->propertyName() == QByteArrayLiteral("timeout")) {
+ m_data.timeout = propertyChange->value().value<quint64>();
+ markDirty(AbstractRenderer::FrameGraphDirty);
+ } else if (propertyChange->propertyName() == QByteArrayLiteral("waitOnCPU")) {
+ m_data.waitOnCPU = propertyChange->value().toBool();
+ markDirty(AbstractRenderer::FrameGraphDirty);
+ }
+ }
+ FrameGraphNode::sceneChangeEvent(e);
+}
+
+void WaitFence::initializeFromPeer(const Qt3DCore::QNodeCreatedChangeBasePtr &change)
+{
+ FrameGraphNode::initializeFromPeer(change);
+ const auto typedChange = qSharedPointerCast<Qt3DCore::QNodeCreatedChange<QWaitFenceData>>(change);
+ const QWaitFenceData &data = typedChange->data;
+ m_data = data;
+}
+
+} // namespace Render
+
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
diff --git a/src/render/framegraph/waitfence_p.h b/src/render/framegraph/waitfence_p.h
new file mode 100644
index 000000000..dd48e0efa
--- /dev/null
+++ b/src/render/framegraph/waitfence_p.h
@@ -0,0 +1,85 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 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_WAITFENCE_P_H
+#define QT3DRENDER_RENDER_WAITFENCE_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/qwaitfence.h>
+#include <Qt3DRender/private/framegraphnode_p.h>
+#include <Qt3DRender/private/qwaitfence_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+namespace Render {
+
+class Q_AUTOTEST_EXPORT WaitFence : public FrameGraphNode
+{
+public:
+ WaitFence();
+ ~WaitFence();
+
+ inline QWaitFenceData data() const { return m_data; }
+ void sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e) override;
+
+private:
+ void initializeFromPeer(const Qt3DCore::QNodeCreatedChangeBasePtr &change) override;
+
+ QWaitFenceData m_data;
+};
+
+} // namespace Render
+
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDER_WAITFENCE_P_H
diff --git a/src/render/frontend/qcomputecommand.cpp b/src/render/frontend/qcomputecommand.cpp
index 5cb72a6ce..8b176cd4f 100644
--- a/src/render/frontend/qcomputecommand.cpp
+++ b/src/render/frontend/qcomputecommand.cpp
@@ -39,6 +39,7 @@
#include "qcomputecommand.h"
#include "qcomputecommand_p.h"
+#include <Qt3DCore/qpropertyupdatedchange.h>
QT_BEGIN_NAMESPACE
@@ -75,12 +76,14 @@ namespace Qt3DRender {
The compute shader is specified in the Material component of the same entity the
ComputeCommand is added to. The workGroupX, workGroupY and workGroupZ properties
specify the work group sizes for the compute shader invocation. DispatchCompute
- node needs to be present in the FrameGraph to actually issue the commands.
+ node needs to be present in the FrameGraph to actually issue the commands. The execution behavior
+ of the compute command can be controlled with the run type property.
- \note If the rendering policy is set to RenderSettings.OnDemand and there are no changes to the
- scene, the ComputeCommand will not be invoked repeatedly.
- The RenderSettings.Always render policy must be set for the ComputeCommand to be
- repeatedly invoked if there are no other changes to the scene that triggers rendering a new
+ \note If the rendering policy is set to RenderSettings.OnDemand, the run
+ type is set to Continuous and there are no changes to the scene, the
+ ComputeCommand will not be invoked repeatedly. The RenderSettings.Always
+ render policy must be set for the ComputeCommand to be repeatedly invoked
+ if there are no other changes to the scene that triggers rendering a new
frame.
*/
@@ -90,6 +93,19 @@ namespace Qt3DRender {
*/
/*!
+ \qmlproperty QComputeCommand::runType
+
+ Specifies whether the compute command should be performed every frame or
+ manually triggered.
+
+ \value Continuous Compute command is executed everyframe. This is the
+ default.
+
+ \value Manual CompouteCommand is executed for a given number of frames and
+ then the component disables itself.
+ */
+
+/*!
\qmlproperty int ComputeCommand::workGroupY
Specifies Y workgroup size.
*/
@@ -114,12 +130,36 @@ namespace Qt3DRender {
Specifies Z workgroup size.
*/
+/*!
+ \property QComputeCommand::runType
+
+ Specifies whether the compute command should be performed every frame or
+ manually triggered.
+
+ If set to Continuous, Compute command is executed everyframe. This is the
+ default.
+
+ If set to Manual CompouteCommand is executed for a given number of frames
+ and then the component disables itself.
+ */
+
QComputeCommandPrivate::QComputeCommandPrivate()
: Qt3DCore::QComponentPrivate()
, m_workGroupX(1)
, m_workGroupY(1)
, m_workGroupZ(1)
+ , m_runType(QComputeCommand::Continuous)
+ , m_frameCount(0)
+{
+}
+
+void QComputeCommandPrivate::setFrameCount(int frameCount)
{
+ m_frameCount = frameCount;
+ const auto propertyChange = Qt3DCore::QPropertyUpdatedChangePtr::create(m_id);
+ propertyChange->setPropertyName("frameCount");
+ propertyChange->setValue(m_frameCount);
+ notifyObservers(propertyChange);
}
/*!
@@ -154,6 +194,12 @@ int QComputeCommand::workGroupZ() const
return d->m_workGroupZ;
}
+QComputeCommand::RunType QComputeCommand::runType() const
+{
+ Q_D(const QComputeCommand);
+ return d->m_runType;
+}
+
/*!
Sets the workgroup for the first dimension to \a workGroupX.
*/
@@ -190,6 +236,50 @@ void QComputeCommand::setWorkGroupZ(int workGroupZ)
}
}
+void QComputeCommand::setRunType(QComputeCommand::RunType runType)
+{
+ Q_D(QComputeCommand);
+ if (d->m_runType != runType) {
+ d->m_runType = runType;
+ emit runTypeChanged();
+ }
+}
+
+/*!
+ When the run type is set to Manual, calling trigger will make the compute
+ command be executed for the next \a frameCount frames. Upon completion of
+ the execution, the enabled property will be set to false.
+ */
+void QComputeCommand::trigger(int frameCount)
+{
+ if (isEnabled())
+ qWarning() << Q_FUNC_INFO << "is triggered while it hasn't finished executing";
+
+ Q_D(QComputeCommand);
+ d->setFrameCount(frameCount);
+ setEnabled(true);
+}
+
+/*!
+ When the run type is set to Manual, calling trigger will make the compute
+ command be executed for the next \a frameCount frames. Upon completion of
+ the execution, the enabled property will be set to false. The size of the
+ workgroup previously set will be overridden with \a workGroupX, \a
+ workGroupY, \a workGroupZ.
+ */
+void QComputeCommand::trigger(int workGroupX, int workGroupY, int workGroupZ, int frameCount)
+{
+ if (isEnabled())
+ qWarning() << Q_FUNC_INFO << "is triggered while it hasn't finished executing";
+
+ setWorkGroupX(workGroupX);
+ setWorkGroupY(workGroupY);
+ setWorkGroupZ(workGroupZ);
+ Q_D(QComputeCommand);
+ d->setFrameCount(frameCount);
+ setEnabled(true);
+}
+
Qt3DCore::QNodeCreatedChangeBasePtr QComputeCommand::createNodeCreationChange() const
{
auto creationChange = Qt3DCore::QNodeCreatedChangePtr<QComputeCommandData>::create(this);
@@ -198,6 +288,8 @@ Qt3DCore::QNodeCreatedChangeBasePtr QComputeCommand::createNodeCreationChange()
data.workGroupX = d->m_workGroupX;
data.workGroupY = d->m_workGroupY;
data.workGroupZ = d->m_workGroupZ;
+ data.runType = d->m_runType;
+ data.frameCount = d->m_frameCount;
return creationChange;
}
diff --git a/src/render/frontend/qcomputecommand.h b/src/render/frontend/qcomputecommand.h
index c31082197..ad7f89a4d 100644
--- a/src/render/frontend/qcomputecommand.h
+++ b/src/render/frontend/qcomputecommand.h
@@ -55,24 +55,37 @@ class QT3DRENDERSHARED_EXPORT QComputeCommand : public Qt3DCore::QComponent
Q_PROPERTY(int workGroupX READ workGroupX WRITE setWorkGroupX NOTIFY workGroupXChanged)
Q_PROPERTY(int workGroupY READ workGroupY WRITE setWorkGroupY NOTIFY workGroupYChanged)
Q_PROPERTY(int workGroupZ READ workGroupZ WRITE setWorkGroupZ NOTIFY workGroupZChanged)
+ Q_PROPERTY(RunType runType READ runType WRITE setRunType NOTIFY runTypeChanged REVISION 13)
public:
+ enum RunType {
+ Continuous = 0,
+ Manual
+ };
+ Q_ENUM(RunType)
+
explicit QComputeCommand(Qt3DCore::QNode *parent = nullptr);
~QComputeCommand();
int workGroupX() const;
int workGroupY() const;
int workGroupZ() const;
+ RunType runType() const;
public Q_SLOTS:
void setWorkGroupX(int workGroupX);
void setWorkGroupY(int workGroupY);
void setWorkGroupZ(int workGroupZ);
+ void setRunType(RunType runType);
+
+ void trigger(int frameCount = 1);
+ void trigger(int workGroupX, int workGroupY, int workGroupZ, int frameCount = 1);
Q_SIGNALS:
void workGroupXChanged();
void workGroupYChanged();
void workGroupZChanged();
+ void runTypeChanged();
private:
Q_DECLARE_PRIVATE(QComputeCommand)
diff --git a/src/render/frontend/qcomputecommand_p.h b/src/render/frontend/qcomputecommand_p.h
index 874edb1fc..fc3376d5b 100644
--- a/src/render/frontend/qcomputecommand_p.h
+++ b/src/render/frontend/qcomputecommand_p.h
@@ -52,6 +52,7 @@
//
#include <Qt3DCore/private/qcomponent_p.h>
+#include <Qt3DRender/qcomputecommand.h>
#include <Qt3DRender/private/qt3drender_global_p.h>
QT_BEGIN_NAMESPACE
@@ -66,6 +67,10 @@ public:
int m_workGroupX;
int m_workGroupY;
int m_workGroupZ;
+ QComputeCommand::RunType m_runType;
+ int m_frameCount;
+
+ void setFrameCount(int frameCount);
};
struct QComputeCommandData
@@ -73,6 +78,8 @@ struct QComputeCommandData
int workGroupX;
int workGroupY;
int workGroupZ;
+ QComputeCommand::RunType runType;
+ int frameCount;
};
} // Qt3DRender
diff --git a/src/render/frontend/qpickingsettings.cpp b/src/render/frontend/qpickingsettings.cpp
index 66d3fc912..84e61e141 100644
--- a/src/render/frontend/qpickingsettings.cpp
+++ b/src/render/frontend/qpickingsettings.cpp
@@ -199,6 +199,9 @@ void QPickingSettings::setPickMethod(QPickingSettings::PickMethod pickMethod)
* \value NearestPick Only the nearest entity to picking ray origin intersected by the picking ray
* is picked (default).
* \value AllPicks All entities that intersect the picking ray are picked.
+ * \value PriorityPick Selects the entity whose object picker has the highest
+ * value. If several object pickers have the same priority, the closest one on
+ * the ray is selected.
*
* \sa Qt3DRender::QPickEvent
*/
@@ -211,6 +214,7 @@ void QPickingSettings::setPickMethod(QPickingSettings::PickMethod pickMethod)
\list
\li PickingSettings.NearestPick
\li PickingSettings.AllPicks
+ \li PickingSettings.NearestPriorityPick
\endlist
\sa Qt3DRender::QPickingSettings::PickResultMode
@@ -225,6 +229,10 @@ void QPickingSettings::setPickMethod(QPickingSettings::PickMethod pickMethod)
When setting the pick method to AllPicks, events will be triggered for all the
entities with a QObjectPicker along the ray.
+ When setting the pick method to NearestPriorityPick, events will be
+ triggered for the nearest highest priority picker. This can be used when a
+ given element should always be selected even if others are in front of it.
+
If a QObjectPicker is assigned to an entity with multiple children, an event will
be triggered for each child entity that intersects the ray.
*/
diff --git a/src/render/frontend/qpickingsettings.h b/src/render/frontend/qpickingsettings.h
index 9c8a2c856..741f918b0 100644
--- a/src/render/frontend/qpickingsettings.h
+++ b/src/render/frontend/qpickingsettings.h
@@ -73,7 +73,8 @@ public:
enum PickResultMode {
NearestPick,
- AllPicks
+ AllPicks,
+ NearestPriorityPick
};
Q_ENUM(PickResultMode) // LCOV_EXCL_LINE
diff --git a/src/render/frontend/qrenderaspect.cpp b/src/render/frontend/qrenderaspect.cpp
index ae84599e8..14fbe1754 100644
--- a/src/render/frontend/qrenderaspect.cpp
+++ b/src/render/frontend/qrenderaspect.cpp
@@ -88,6 +88,8 @@
#include <Qt3DRender/qproximityfilter.h>
#include <Qt3DRender/qshaderprogrambuilder.h>
#include <Qt3DRender/qblitframebuffer.h>
+#include <Qt3DRender/qsetfence.h>
+#include <Qt3DRender/qwaitfence.h>
#include <Qt3DCore/qarmature.h>
#include <Qt3DCore/qjoint.h>
#include <Qt3DCore/qskeletonloader.h>
@@ -150,6 +152,8 @@
#include <Qt3DRender/private/joint_p.h>
#include <Qt3DRender/private/loadskeletonjob_p.h>
#include <Qt3DRender/private/proximityfilter_p.h>
+#include <Qt3DRender/private/setfence_p.h>
+#include <Qt3DRender/private/waitfence_p.h>
#include <private/qrenderpluginfactory_p.h>
#include <private/qrenderplugin_p.h>
@@ -298,6 +302,8 @@ void QRenderAspectPrivate::registerBackendTypes()
q->registerBackendType<QMemoryBarrier>(QSharedPointer<Render::FrameGraphNodeFunctor<Render::MemoryBarrier, QMemoryBarrier> >::create(m_renderer));
q->registerBackendType<QProximityFilter>(QSharedPointer<Render::FrameGraphNodeFunctor<Render::ProximityFilter, QProximityFilter> >::create(m_renderer));
q->registerBackendType<QBlitFramebuffer>(QSharedPointer<Render::FrameGraphNodeFunctor<Render::BlitFramebuffer, QBlitFramebuffer> >::create(m_renderer));
+ q->registerBackendType<QSetFence>(QSharedPointer<Render::FrameGraphNodeFunctor<Render::SetFence, QSetFence> >::create(m_renderer));
+ q->registerBackendType<QWaitFence>(QSharedPointer<Render::FrameGraphNodeFunctor<Render::WaitFence, QWaitFence> >::create(m_renderer));
// Picking
q->registerBackendType<QObjectPicker>(QSharedPointer<Render::NodeFunctor<Render::ObjectPicker, Render::ObjectPickerManager> >::create(m_renderer));
@@ -368,6 +374,8 @@ void QRenderAspectPrivate::unregisterBackendTypes()
unregisterBackendType<QRenderCapture>();
unregisterBackendType<QBufferCapture>();
unregisterBackendType<QMemoryBarrier>();
+ unregisterBackendType<QSetFence>();
+ unregisterBackendType<QWaitFence>();
// Picking
unregisterBackendType<QObjectPicker>();
@@ -509,11 +517,8 @@ QVector<Qt3DCore::QAspectJobPtr> QRenderAspect::jobsToExecute(qint64 time)
const QVector<QAspectJobPtr> geometryJobs = d->createGeometryRendererJobs();
jobs.append(geometryJobs);
-
- // Add all jobs to queue
- // Note: the getter is also responsible for returning a job ready to run
- jobs.append(d->m_renderer->pickBoundingVolumeJob());
- jobs.append(d->m_renderer->rayCastingJob());
+ const QVector<QAspectJobPtr> preRenderingJobs = d->m_renderer->preRenderingJobs();
+ 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,
diff --git a/src/render/geometry/geometry.cpp b/src/render/geometry/geometry.cpp
index d87b4d8eb..4ee02a74d 100644
--- a/src/render/geometry/geometry.cpp
+++ b/src/render/geometry/geometry.cpp
@@ -53,8 +53,10 @@ namespace Qt3DRender {
namespace Render {
Geometry::Geometry()
- : BackendNode(ReadOnly)
+ : BackendNode(ReadWrite)
, m_geometryDirty(false)
+ , m_shouldNotifyMinExtentChanged(false)
+ , m_shouldNotifyMaxExtentChanged(false)
{
}
@@ -68,6 +70,10 @@ void Geometry::cleanup()
m_attributes.clear();
m_geometryDirty = false;
m_boundingPositionAttribute = Qt3DCore::QNodeId();
+ m_min = QVector3D();
+ m_max = QVector3D();
+ m_shouldNotifyMinExtentChanged = false;
+ m_shouldNotifyMaxExtentChanged = false;
}
void Geometry::initializeFromPeer(const Qt3DCore::QNodeCreatedChangeBasePtr &change)
@@ -122,6 +128,37 @@ void Geometry::unsetDirty()
m_geometryDirty = false;
}
+// Called from calcboundingvolumejob (in a QtConcurrent thead (can't send
+// update changes from such a thread))
+void Geometry::updateExtent(const QVector3D &min, const QVector3D &max)
+{
+ // Send notification to frontend
+ if (m_min != min) {
+ m_min = min;
+ m_shouldNotifyMinExtentChanged = true;
+ }
+
+ if (m_max != max) {
+ m_max = max;
+ m_shouldNotifyMaxExtentChanged = true;
+ }
+}
+
+// Called from calcboundingvolumejob after all bounding volumes have been
+// updated (in an aspect thread)
+void Geometry::notifyExtentChanged()
+{
+ if (m_shouldNotifyMinExtentChanged || m_shouldNotifyMaxExtentChanged) {
+ auto change = Qt3DCore::QPropertyUpdatedChangePtr::create(peerId());
+ change->setDeliveryFlags(Qt3DCore::QSceneChange::Nodes);
+ change->setPropertyName("extent");
+ change->setValue(QVariant::fromValue(QPair<QVector3D, QVector3D>(m_min, m_max)));
+ notifyObservers(change);
+ m_shouldNotifyMinExtentChanged = false;
+ m_shouldNotifyMaxExtentChanged = false;
+ }
+}
+
} // namespace Render
} // namespace Qt3DRender
diff --git a/src/render/geometry/geometry_p.h b/src/render/geometry/geometry_p.h
index b158648ad..e66524787 100644
--- a/src/render/geometry/geometry_p.h
+++ b/src/render/geometry/geometry_p.h
@@ -75,12 +75,22 @@ public:
inline Qt3DCore::QNodeId boundingPositionAttribute() const { return m_boundingPositionAttribute; }
void unsetDirty();
+ inline QVector3D min() const { return m_min; }
+ inline QVector3D max() const { return m_max; }
+
+ void updateExtent(const QVector3D &min, const QVector3D &max);
+ void notifyExtentChanged();
+
private:
void initializeFromPeer(const Qt3DCore::QNodeCreatedChangeBasePtr &change) final;
QVector<Qt3DCore::QNodeId> m_attributes;
bool m_geometryDirty;
Qt3DCore::QNodeId m_boundingPositionAttribute;
+ QVector3D m_min;
+ QVector3D m_max;
+ bool m_shouldNotifyMinExtentChanged;
+ bool m_shouldNotifyMaxExtentChanged;
};
} // namespace Render
diff --git a/src/render/geometry/qgeometry.cpp b/src/render/geometry/qgeometry.cpp
index c49dde822..ec80e2657 100644
--- a/src/render/geometry/qgeometry.cpp
+++ b/src/render/geometry/qgeometry.cpp
@@ -153,6 +153,28 @@ QGeometry::QGeometry(QGeometryPrivate &dd, QNode *parent)
{
}
+void QGeometry::sceneChangeEvent(const QSceneChangePtr &change)
+{
+ Q_D(QGeometry);
+ QPropertyUpdatedChangePtr e = qSharedPointerCast<QPropertyUpdatedChange>(change);
+ if (e->type() == PropertyUpdated) {
+ const bool blocked = blockNotifications(true);
+ if (e->propertyName() == QByteArrayLiteral("extent")) {
+ const QPair<QVector3D, QVector3D> extent = e->value().value<QPair<QVector3D, QVector3D>>();
+
+ if (extent.first != d->m_minExtent) {
+ d->m_minExtent = extent.first;
+ emit minExtentChanged(extent.first);
+ }
+ if (extent.second != d->m_maxExtent) {
+ d->m_maxExtent = extent.second;
+ emit maxExtentChanged(d->m_maxExtent);
+ }
+ }
+ blockNotifications(blocked);
+ }
+}
+
/*!
\fn void Qt3DRender::QGeometry::addAttribute(Qt3DRender::QAttribute *attribute)
Adds an \a attribute to this geometry.
@@ -216,6 +238,40 @@ QAttribute *QGeometry::boundingVolumePositionAttribute() const
}
/*!
+ \qmlproperty vector3d Geometry::minExtent
+
+ Holds the vertex with the lowest x, y, z position values.
+ */
+
+/*!
+ \property QGeometry::minExtent
+
+ Holds the vertex with the lowest x, y, z position values.
+ */
+QVector3D QGeometry::minExtent() const
+{
+ Q_D(const QGeometry);
+ return d->m_minExtent;
+}
+
+/*!
+ \qmlproperty vector3d Geometry::maxExtent
+
+ Holds the vertex with the highest x, y, z position values.
+ */
+
+/*!
+ \property QGeometry::maxExtent
+
+ Holds the vertex with the highest x, y, z position values.
+ */
+QVector3D QGeometry::maxExtent() const
+{
+ Q_D(const QGeometry);
+ return d->m_maxExtent;
+}
+
+/*!
Returns the list of attributes in this geometry.
*/
QVector<QAttribute *> QGeometry::attributes() const
diff --git a/src/render/geometry/qgeometry.h b/src/render/geometry/qgeometry.h
index 0e6f7d68e..61508c7d2 100644
--- a/src/render/geometry/qgeometry.h
+++ b/src/render/geometry/qgeometry.h
@@ -54,6 +54,8 @@ class QT3DRENDERSHARED_EXPORT QGeometry : public Qt3DCore::QNode
{
Q_OBJECT
Q_PROPERTY(Qt3DRender::QAttribute *boundingVolumePositionAttribute READ boundingVolumePositionAttribute WRITE setBoundingVolumePositionAttribute NOTIFY boundingVolumePositionAttributeChanged)
+ Q_PROPERTY(QVector3D minExtent READ minExtent NOTIFY minExtentChanged REVISION 13)
+ Q_PROPERTY(QVector3D maxExtent READ maxExtent NOTIFY maxExtentChanged REVISION 13)
public:
explicit QGeometry(Qt3DCore::QNode *parent = nullptr);
~QGeometry();
@@ -63,15 +65,19 @@ public:
Q_INVOKABLE void removeAttribute(Qt3DRender::QAttribute *attribute);
QAttribute *boundingVolumePositionAttribute() const;
+ QVector3D minExtent() const;
+ QVector3D maxExtent() const;
public Q_SLOTS:
void setBoundingVolumePositionAttribute(QAttribute *boundingVolumePositionAttribute);
Q_SIGNALS:
void boundingVolumePositionAttributeChanged(QAttribute *boundingVolumePositionAttribute);
-
+ void minExtentChanged(const QVector3D &minExtent);
+ void maxExtentChanged(const QVector3D &maxExtent);
protected:
explicit QGeometry(QGeometryPrivate &dd, Qt3DCore::QNode *parent = nullptr);
+ void sceneChangeEvent(const Qt3DCore::QSceneChangePtr &change) override;
private:
Q_DECLARE_PRIVATE(QGeometry)
diff --git a/src/render/geometry/qgeometry_p.h b/src/render/geometry/qgeometry_p.h
index e07b9ff0d..f53548e43 100644
--- a/src/render/geometry/qgeometry_p.h
+++ b/src/render/geometry/qgeometry_p.h
@@ -53,6 +53,7 @@
#include <Qt3DRender/private/qt3drender_global_p.h>
#include <Qt3DCore/private/qnode_p.h>
+#include <QVector3D>
QT_BEGIN_NAMESPACE
@@ -68,6 +69,8 @@ public:
QVector<QAttribute *> m_attributes;
QAttribute *m_boundingVolumePositionAttribute;
+ QVector3D m_minExtent;
+ QVector3D m_maxExtent;
};
struct QGeometryData
diff --git a/src/render/jobs/calcboundingvolumejob.cpp b/src/render/jobs/calcboundingvolumejob.cpp
index 66d59f812..68eb308c5 100644
--- a/src/render/jobs/calcboundingvolumejob.cpp
+++ b/src/render/jobs/calcboundingvolumejob.cpp
@@ -65,14 +65,25 @@ namespace Render {
namespace {
-void calculateLocalBoundingVolume(NodeManagers *manager, Entity *node);
+QVector<Geometry*> calculateLocalBoundingVolume(NodeManagers *manager, Entity *node);
-struct UpdateBoundFunctor {
+struct UpdateBoundFunctor
+{
NodeManagers *manager;
- void operator ()(Qt3DRender::Render::Entity *node)
+ // This define is required to work with QtConcurrent
+ typedef QVector<Geometry *> result_type;
+ QVector<Geometry *> operator ()(Qt3DRender::Render::Entity *node)
+ {
+ return calculateLocalBoundingVolume(manager, node);
+ }
+};
+
+struct ReduceUpdateBoundFunctor
+{
+ void operator ()(QVector<Geometry *> &result, const QVector<Geometry *> &values)
{
- calculateLocalBoundingVolume(manager, node);
+ result += values;
}
};
@@ -82,6 +93,8 @@ public:
BoundingVolumeCalculator(NodeManagers *manager) : m_manager(manager) { }
const Sphere& result() { return m_volume; }
+ const QVector3D min() const { return m_min; }
+ const QVector3D max() const { return m_max; }
bool apply(Qt3DRender::Render::Attribute *positionAttribute,
Qt3DRender::Render::Attribute *indexAttribute,
@@ -95,6 +108,9 @@ public:
return false;
}
+ m_min = QVector3D(findExtremePoints.xMin, findExtremePoints.yMin, findExtremePoints.zMin);
+ m_max = QVector3D(findExtremePoints.xMax, findExtremePoints.yMax, findExtremePoints.zMax);
+
// Calculate squared distance for the pairs of points
const float xDist2 = (findExtremePoints.xMaxPt - findExtremePoints.xMinPt).lengthSquared();
const float yDist2 = (findExtremePoints.yMaxPt - findExtremePoints.yMinPt).lengthSquared();
@@ -127,6 +143,8 @@ public:
private:
Sphere m_volume;
NodeManagers *m_manager;
+ QVector3D m_min;
+ QVector3D m_max;
class FindExtremePoints : public Buffer3fVisitor
{
@@ -191,17 +209,20 @@ private:
};
};
-void calculateLocalBoundingVolume(NodeManagers *manager, Entity *node)
+QVector<Geometry *> calculateLocalBoundingVolume(NodeManagers *manager, Entity *node)
{
// The Bounding volume will only be computed if the position Buffer
// isDirty
+ QVector<Geometry *> updatedGeometries;
+
if (!node->isTreeEnabled())
- return;
+ return updatedGeometries;
GeometryRenderer *gRenderer = node->renderComponent<GeometryRenderer>();
+ GeometryManager *geometryManager = manager->geometryManager();
if (gRenderer && gRenderer->primitiveType() != QGeometryRenderer::Patches) {
- Geometry *geom = manager->lookupResource<Geometry, GeometryManager>(gRenderer->geometryId());
+ Geometry *geom = geometryManager->lookupResource(gRenderer->geometryId());
if (geom) {
int drawVertexCount = gRenderer->vertexCount(); // may be 0, gets changed below if so
@@ -224,14 +245,14 @@ void calculateLocalBoundingVolume(NodeManagers *manager, Entity *node)
|| positionAttribute->vertexBaseType() != QAttribute::Float
|| positionAttribute->vertexSize() < 3) {
qWarning("calculateLocalBoundingVolume: Position attribute not suited for bounding volume computation");
- return;
+ return updatedGeometries;
}
Buffer *buf = manager->lookupResource<Buffer, BufferManager>(positionAttribute->bufferId());
// No point in continuing if the positionAttribute doesn't have a suitable buffer
if (!buf) {
qWarning("calculateLocalBoundingVolume: Position attribute not referencing a valid buffer");
- return;
+ return updatedGeometries;
}
// Check if there is an index attribute.
@@ -259,7 +280,7 @@ void calculateLocalBoundingVolume(NodeManagers *manager, Entity *node)
std::end(validIndexTypes),
indexAttribute->vertexBaseType()) == std::end(validIndexTypes)) {
qWarning() << "calculateLocalBoundingVolume: Unsupported index attribute type" << indexAttribute->name() << indexAttribute->vertexBaseType();
- return;
+ return updatedGeometries;
}
break;
@@ -287,6 +308,11 @@ void calculateLocalBoundingVolume(NodeManagers *manager, Entity *node)
node->localBoundingVolume()->setCenter(reader.result().center());
node->localBoundingVolume()->setRadius(reader.result().radius());
node->unsetBoundingVolumeDirty();
+
+ // Record min/max vertex in Geometry
+ geom->updateExtent(reader.min(), reader.max());
+ // Mark geometry as requiring a call to update its frontend
+ updatedGeometries.push_back(geom);
}
}
}
@@ -297,14 +323,16 @@ void calculateLocalBoundingVolume(NodeManagers *manager, Entity *node)
if (children.size() > 1) {
UpdateBoundFunctor functor;
functor.manager = manager;
- QtConcurrent::blockingMap(children, functor);
+ ReduceUpdateBoundFunctor reduceFunctor;
+ updatedGeometries += QtConcurrent::blockingMappedReduced<decltype(updatedGeometries)>(children, functor, reduceFunctor);
} else
#endif
{
const auto children = node->children();
for (Entity *child : children)
- calculateLocalBoundingVolume(manager, child);
+ updatedGeometries += calculateLocalBoundingVolume(manager, child);
}
+ return updatedGeometries;
}
} // anonymous
@@ -318,7 +346,11 @@ CalculateBoundingVolumeJob::CalculateBoundingVolumeJob()
void CalculateBoundingVolumeJob::run()
{
- calculateLocalBoundingVolume(m_manager, m_node);
+ const QVector<Geometry *> updatedGeometries = calculateLocalBoundingVolume(m_manager, m_node);
+
+ // Send extent updates to frontend
+ for (Geometry *geometry : updatedGeometries)
+ geometry->notifyExtentChanged();
}
void CalculateBoundingVolumeJob::setRoot(Entity *node)
diff --git a/src/render/jobs/job_common_p.h b/src/render/jobs/job_common_p.h
index 2ae7d81ff..e776d452d 100644
--- a/src/render/jobs/job_common_p.h
+++ b/src/render/jobs/job_common_p.h
@@ -107,7 +107,8 @@ namespace JobTypes {
SyncFilterEntityByLayer,
SyncMaterialGatherer,
UpdateLayerEntity,
- SendTextureChangesToFrontend
+ SendTextureChangesToFrontend,
+ SendSetFenceHandlesToFrontend,
};
} // JobTypes
diff --git a/src/render/jobs/pickboundingvolumejob.cpp b/src/render/jobs/pickboundingvolumejob.cpp
index 2050b8772..96ec11b4e 100644
--- a/src/render/jobs/pickboundingvolumejob.cpp
+++ b/src/render/jobs/pickboundingvolumejob.cpp
@@ -211,7 +211,6 @@ bool PickBoundingVolumeJob::runHelper()
const bool edgePickingRequested = (m_renderSettings->pickMethod() & QPickingSettings::LinePicking);
const bool pointPickingRequested = (m_renderSettings->pickMethod() & QPickingSettings::PointPicking);
const bool primitivePickingRequested = pointPickingRequested | edgePickingRequested | trianglePickingRequested;
- const bool allHitsRequested = (m_renderSettings->pickResultMode() == QPickingSettings::AllPicks);
const bool frontFaceRequested =
m_renderSettings->faceOrientationPickingMode() != QPickingSettings::BackFace;
const bool backFaceRequested =
@@ -237,7 +236,7 @@ bool PickBoundingVolumeJob::runHelper()
// has moved out of the viewport In case of a button released
// outside of the viewport, we still want to notify the
// lastCurrent entity about this.
- dispatchPickEvents(event.second, PickingUtils::HitList(), eventButton, eventButtons, eventModifiers, allHitsRequested);
+ dispatchPickEvents(event.second, PickingUtils::HitList(), eventButton, eventButtons, eventModifiers, m_renderSettings->pickResultMode());
continue;
}
@@ -249,14 +248,16 @@ bool PickBoundingVolumeJob::runHelper()
gathererFunctor.m_backFaceRequested = backFaceRequested;
gathererFunctor.m_manager = m_manager;
gathererFunctor.m_ray = ray;
- sphereHits << gathererFunctor.computeHits(entityPicker.entities(), allHitsRequested);
+ gathererFunctor.m_entityToPriorityTable = entityPicker.entityToPriorityTable();
+ sphereHits << gathererFunctor.computeHits(entityPicker.entities(), m_renderSettings->pickResultMode());
}
if (edgePickingRequested) {
PickingUtils::LineCollisionGathererFunctor gathererFunctor;
gathererFunctor.m_manager = m_manager;
gathererFunctor.m_ray = ray;
gathererFunctor.m_pickWorldSpaceTolerance = pickWorldSpaceTolerance;
- sphereHits << gathererFunctor.computeHits(entityPicker.entities(), allHitsRequested);
+ gathererFunctor.m_entityToPriorityTable = entityPicker.entityToPriorityTable();
+ sphereHits << gathererFunctor.computeHits(entityPicker.entities(), m_renderSettings->pickResultMode());
PickingUtils::AbstractCollisionGathererFunctor::sortHits(sphereHits);
}
if (pointPickingRequested) {
@@ -264,19 +265,20 @@ bool PickBoundingVolumeJob::runHelper()
gathererFunctor.m_manager = m_manager;
gathererFunctor.m_ray = ray;
gathererFunctor.m_pickWorldSpaceTolerance = pickWorldSpaceTolerance;
- sphereHits << gathererFunctor.computeHits(entityPicker.entities(), allHitsRequested);
+ gathererFunctor.m_entityToPriorityTable = entityPicker.entityToPriorityTable();
+ sphereHits << gathererFunctor.computeHits(entityPicker.entities(), m_renderSettings->pickResultMode());
PickingUtils::AbstractCollisionGathererFunctor::sortHits(sphereHits);
}
if (!primitivePickingRequested) {
sphereHits << entityPicker.hits();
PickingUtils::AbstractCollisionGathererFunctor::sortHits(sphereHits);
- if (!allHitsRequested)
+ if (m_renderSettings->pickResultMode() != QPickingSettings::AllPicks)
sphereHits = { sphereHits.front() };
}
}
// Dispatch events based on hit results
- dispatchPickEvents(event.second, sphereHits, eventButton, eventButtons, eventModifiers, allHitsRequested);
+ dispatchPickEvents(event.second, sphereHits, eventButton, eventButtons, eventModifiers, m_renderSettings->pickResultMode());
}
}
diff --git a/src/render/jobs/pickboundingvolumeutils.cpp b/src/render/jobs/pickboundingvolumeutils.cpp
index 5fed946d6..23e495ecb 100644
--- a/src/render/jobs/pickboundingvolumeutils.cpp
+++ b/src/render/jobs/pickboundingvolumeutils.cpp
@@ -55,6 +55,7 @@
#include <vector>
#include <algorithm>
+#include <functional>
QT_BEGIN_NAMESPACE
@@ -418,6 +419,46 @@ HitList reduceToFirstHit(HitList &result, const HitList &intermediate)
return result;
}
+
+struct HighestPriorityHitReducer
+{
+ // No need to protect this from concurrent access as the table
+ // is read only
+ const QHash<Qt3DCore::QNodeId, int> entityToPriorityTable;
+
+ HitList operator()(HitList &result, const HitList &intermediate)
+ {
+ // Sort by priority first
+ // If we have equal priorities, we then sort by distance
+
+ if (!intermediate.empty()) {
+ if (result.empty())
+ result.push_back(intermediate.front());
+ int currentPriority = entityToPriorityTable.value(result.front().m_entityId, 0);
+ float closest = result.front().m_distance;
+
+ for (const auto &v : intermediate) {
+ const int newEntryPriority = entityToPriorityTable.value(v.m_entityId, 0);
+ if (newEntryPriority > currentPriority) {
+ result.push_front(v);
+ currentPriority = newEntryPriority;
+ closest = v.m_distance;
+ } else if (newEntryPriority == currentPriority) {
+ if (v.m_distance < closest) {
+ result.push_front(v);
+ closest = v.m_distance;
+ currentPriority = newEntryPriority;
+ }
+ }
+ }
+
+ while (result.size() > 1)
+ result.pop_back();
+ }
+ return result;
+ }
+};
+
HitList reduceToAllHits(HitList &results, const HitList &intermediate)
{
if (!intermediate.empty())
@@ -492,9 +533,22 @@ struct MapFunctorHolder
} // anonymous
-HitList EntityCollisionGathererFunctor::computeHits(const QVector<Entity *> &entities, bool allHitsRequested)
-{
- const auto reducerOp = allHitsRequested ? PickingUtils::reduceToAllHits : PickingUtils::reduceToFirstHit;
+HitList EntityCollisionGathererFunctor::computeHits(const QVector<Entity *> &entities,
+ Qt3DRender::QPickingSettings::PickResultMode mode)
+{
+ std::function<HitList (HitList &, const HitList &)> reducerOp;
+ switch (mode) {
+ case QPickingSettings::AllPicks:
+ reducerOp = PickingUtils::reduceToAllHits;
+ break;
+ case QPickingSettings::NearestPriorityPick:
+ reducerOp = HighestPriorityHitReducer{ m_entityToPriorityTable };
+ break;
+ case QPickingSettings::NearestPick:
+ reducerOp = PickingUtils::reduceToFirstHit;
+ break;
+ }
+
const MapFunctorHolder holder(this);
#if QT_CONFIG(concurrent)
return QtConcurrent::blockingMappedReduced<HitList>(entities, holder, reducerOp);
@@ -519,9 +573,22 @@ HitList EntityCollisionGathererFunctor::pick(const Entity *entity) const
return result;
}
-HitList TriangleCollisionGathererFunctor::computeHits(const QVector<Entity *> &entities, bool allHitsRequested)
-{
- const auto reducerOp = allHitsRequested ? PickingUtils::reduceToAllHits : PickingUtils::reduceToFirstHit;
+HitList TriangleCollisionGathererFunctor::computeHits(const QVector<Entity *> &entities,
+ Qt3DRender::QPickingSettings::PickResultMode mode)
+{
+ std::function<HitList (HitList &, const HitList &)> reducerOp;
+ switch (mode) {
+ case QPickingSettings::AllPicks:
+ reducerOp = PickingUtils::reduceToAllHits;
+ break;
+ case QPickingSettings::NearestPriorityPick:
+ reducerOp = HighestPriorityHitReducer { m_entityToPriorityTable };
+ break;
+ case QPickingSettings::NearestPick:
+ reducerOp = PickingUtils::reduceToFirstHit;
+ break;
+ }
+
const MapFunctorHolder holder(this);
#if QT_CONFIG(concurrent)
return QtConcurrent::blockingMappedReduced<HitList>(entities, holder, reducerOp);
@@ -553,9 +620,22 @@ HitList TriangleCollisionGathererFunctor::pick(const Entity *entity) const
return result;
}
-HitList LineCollisionGathererFunctor::computeHits(const QVector<Entity *> &entities, bool allHitsRequested)
-{
- const auto reducerOp = allHitsRequested ? PickingUtils::reduceToAllHits : PickingUtils::reduceToFirstHit;
+HitList LineCollisionGathererFunctor::computeHits(const QVector<Entity *> &entities,
+ Qt3DRender::QPickingSettings::PickResultMode mode)
+{
+ std::function<HitList (HitList &, const HitList &)> reducerOp;
+ switch (mode) {
+ case QPickingSettings::AllPicks:
+ reducerOp = PickingUtils::reduceToAllHits;
+ break;
+ case QPickingSettings::NearestPriorityPick:
+ reducerOp = HighestPriorityHitReducer { m_entityToPriorityTable };
+ break;
+ case QPickingSettings::NearestPick:
+ reducerOp = PickingUtils::reduceToFirstHit;
+ break;
+ }
+
const MapFunctorHolder holder(this);
#if QT_CONFIG(concurrent)
return QtConcurrent::blockingMappedReduced<HitList>(entities, holder, reducerOp);
@@ -586,9 +666,22 @@ HitList LineCollisionGathererFunctor::pick(const Entity *entity) const
return result;
}
-HitList PointCollisionGathererFunctor::computeHits(const QVector<Entity *> &entities, bool allHitsRequested)
-{
- const auto reducerOp = allHitsRequested ? PickingUtils::reduceToAllHits : PickingUtils::reduceToFirstHit;
+HitList PointCollisionGathererFunctor::computeHits(const QVector<Entity *> &entities,
+ Qt3DRender::QPickingSettings::PickResultMode mode)
+{
+ std::function<HitList (HitList &, const HitList &)> reducerOp;
+ switch (mode) {
+ case QPickingSettings::AllPicks:
+ reducerOp = PickingUtils::reduceToAllHits;
+ break;
+ case QPickingSettings::NearestPriorityPick:
+ reducerOp = HighestPriorityHitReducer { m_entityToPriorityTable };
+ break;
+ case QPickingSettings::NearestPick:
+ reducerOp = PickingUtils::reduceToFirstHit;
+ break;
+ }
+
const MapFunctorHolder holder(this);
#if QT_CONFIG(concurrent)
return QtConcurrent::blockingMappedReduced<HitList>(entities, holder, reducerOp);
@@ -641,15 +734,17 @@ bool HierarchicalEntityPicker::collectHits(NodeManagers *manager, Entity *root)
{
m_hits.clear();
m_entities.clear();
+ m_entityToPriorityTable.clear();
QRayCastingService rayCasting;
struct EntityData {
Entity* entity;
bool hasObjectPicker;
Qt3DCore::QNodeIdVector recursiveLayers;
+ int priority;
};
std::vector<EntityData> worklist;
- worklist.push_back({root, !root->componentHandle<ObjectPicker>().isNull(), {}});
+ worklist.push_back({root, !root->componentHandle<ObjectPicker>().isNull(), {}, 0});
LayerManager *layerManager = manager->layerManager();
@@ -710,6 +805,8 @@ bool HierarchicalEntityPicker::collectHits(NodeManagers *manager, Entity *root)
if (accepted && queryResult.m_distance >= 0.f && (current.hasObjectPicker || !m_objectPickersRequired)) {
m_entities.push_back(current.entity);
m_hits.push_back(queryResult);
+ // Record entry for entity/priority
+ m_entityToPriorityTable.insert(current.entity->peerId(), current.priority);
}
Qt3DCore::QNodeIdVector recursiveLayers;
@@ -722,9 +819,12 @@ bool HierarchicalEntityPicker::collectHits(NodeManagers *manager, Entity *root)
// and pick children
const auto children = current.entity->children();
- for (auto child: children)
- worklist.push_back({child, current.hasObjectPicker || !child->componentHandle<ObjectPicker>().isNull(),
- current.recursiveLayers + recursiveLayers});
+ for (Entity *child: children) {
+ ObjectPicker *childPicker = child->renderComponent<ObjectPicker>();
+ worklist.push_back({child, current.hasObjectPicker || childPicker,
+ current.recursiveLayers + recursiveLayers,
+ (childPicker ? childPicker->priority() : current.priority)});
+ }
}
return !m_hits.empty();
diff --git a/src/render/jobs/pickboundingvolumeutils_p.h b/src/render/jobs/pickboundingvolumeutils_p.h
index 780c16cc8..3fc4517c3 100644
--- a/src/render/jobs/pickboundingvolumeutils_p.h
+++ b/src/render/jobs/pickboundingvolumeutils_p.h
@@ -55,6 +55,7 @@
#include <Qt3DRender/QAbstractRayCaster>
#include <Qt3DRender/private/qray3d_p.h>
#include <Qt3DRender/private/qraycastingservice_p.h>
+#include <Qt3DRender/qpickingsettings.h>
QT_BEGIN_NAMESPACE
@@ -124,6 +125,7 @@ public:
bool collectHits(NodeManagers *manager, Entity *root);
inline HitList hits() const { return m_hits; }
inline QVector<Entity *> entities() const { return m_entities; }
+ inline QHash<Qt3DCore::QNodeId, int> entityToPriorityTable() const { return m_entityToPriorityTable; }
private:
RayCasting::QRay3D m_ray;
@@ -132,6 +134,7 @@ private:
bool m_objectPickersRequired;
Qt3DCore::QNodeIdVector m_layerIds;
QAbstractRayCaster::FilterMode m_filterMode;
+ QHash<Qt3DCore::QNodeId, int> m_entityToPriorityTable;
};
struct Q_AUTOTEST_EXPORT AbstractCollisionGathererFunctor
@@ -142,8 +145,9 @@ struct Q_AUTOTEST_EXPORT AbstractCollisionGathererFunctor
bool m_objectPickersRequired = true;
NodeManagers *m_manager = nullptr;
RayCasting::QRay3D m_ray;
+ QHash<Qt3DCore::QNodeId, int> m_entityToPriorityTable;
- virtual HitList computeHits(const QVector<Entity *> &entities, bool allHitsRequested) = 0;
+ virtual HitList computeHits(const QVector<Entity *> &entities, Qt3DRender::QPickingSettings::PickResultMode mode) = 0;
// This define is required to work with QtConcurrent
typedef HitList result_type;
@@ -156,7 +160,7 @@ struct Q_AUTOTEST_EXPORT AbstractCollisionGathererFunctor
struct Q_AUTOTEST_EXPORT EntityCollisionGathererFunctor : public AbstractCollisionGathererFunctor
{
- HitList computeHits(const QVector<Entity *> &entities, bool allHitsRequested) override;
+ HitList computeHits(const QVector<Entity *> &entities, Qt3DRender::QPickingSettings::PickResultMode mode) override;
HitList pick(const Entity *entity) const override;
};
@@ -165,7 +169,7 @@ struct Q_AUTOTEST_EXPORT TriangleCollisionGathererFunctor : public AbstractColli
bool m_frontFaceRequested;
bool m_backFaceRequested;
- HitList computeHits(const QVector<Entity *> &entities, bool allHitsRequested) override;
+ HitList computeHits(const QVector<Entity *> &entities, Qt3DRender::QPickingSettings::PickResultMode mode) override;
HitList pick(const Entity *entity) const override;
};
@@ -173,7 +177,7 @@ struct Q_AUTOTEST_EXPORT LineCollisionGathererFunctor : public AbstractCollision
{
float m_pickWorldSpaceTolerance;
- HitList computeHits(const QVector<Entity *> &entities, bool allHitsRequested) override;
+ HitList computeHits(const QVector<Entity *> &entities, Qt3DRender::QPickingSettings::PickResultMode mode) override;
HitList pick(const Entity *entity) const override;
};
@@ -181,7 +185,7 @@ struct Q_AUTOTEST_EXPORT PointCollisionGathererFunctor : public AbstractCollisio
{
float m_pickWorldSpaceTolerance;
- HitList computeHits(const QVector<Entity *> &entities, bool allHitsRequested) override;
+ HitList computeHits(const QVector<Entity *> &entities, Qt3DRender::QPickingSettings::PickResultMode mode) override;
HitList pick(const Entity *entity) const override;
};
diff --git a/src/render/jobs/raycastingjob.cpp b/src/render/jobs/raycastingjob.cpp
index e76b9fe8d..70c7ac374 100644
--- a/src/render/jobs/raycastingjob.cpp
+++ b/src/render/jobs/raycastingjob.cpp
@@ -183,7 +183,7 @@ bool RayCastingJob::runHelper()
gathererFunctor.m_manager = m_manager;
gathererFunctor.m_ray = ray;
gathererFunctor.m_objectPickersRequired = false;
- sphereHits << gathererFunctor.computeHits(entityPicker.entities(), true);
+ sphereHits << gathererFunctor.computeHits(entityPicker.entities(), QPickingSettings::AllPicks);
}
if (edgePickingRequested) {
PickingUtils::LineCollisionGathererFunctor gathererFunctor;
@@ -191,7 +191,7 @@ bool RayCastingJob::runHelper()
gathererFunctor.m_ray = ray;
gathererFunctor.m_pickWorldSpaceTolerance = pickWorldSpaceTolerance;
gathererFunctor.m_objectPickersRequired = false;
- sphereHits << gathererFunctor.computeHits(entityPicker.entities(), true);
+ sphereHits << gathererFunctor.computeHits(entityPicker.entities(), QPickingSettings::AllPicks);
PickingUtils::AbstractCollisionGathererFunctor::sortHits(sphereHits);
}
if (pointPickingRequested) {
@@ -200,7 +200,7 @@ bool RayCastingJob::runHelper()
gathererFunctor.m_ray = ray;
gathererFunctor.m_pickWorldSpaceTolerance = pickWorldSpaceTolerance;
gathererFunctor.m_objectPickersRequired = false;
- sphereHits << gathererFunctor.computeHits(entityPicker.entities(), true);
+ sphereHits << gathererFunctor.computeHits(entityPicker.entities(), QPickingSettings::AllPicks);
PickingUtils::AbstractCollisionGathererFunctor::sortHits(sphereHits);
}
if (!primitivePickingRequested) {
diff --git a/src/render/jobs/sendbuffercapturejob.cpp b/src/render/jobs/sendbuffercapturejob.cpp
index eae26ba6c..8683ea9f2 100644
--- a/src/render/jobs/sendbuffercapturejob.cpp
+++ b/src/render/jobs/sendbuffercapturejob.cpp
@@ -67,6 +67,12 @@ void SendBufferCaptureJob::addRequest(QPair<Buffer *, QByteArray> request)
m_pendingSendBufferCaptures.push_back(request);
}
+// Called by aspect thread jobs to execute (no concurrency at that point)
+bool SendBufferCaptureJob::hasRequests() const
+{
+ return m_pendingSendBufferCaptures.size() > 0;
+}
+
void SendBufferCaptureJob::run()
{
QMutexLocker locker(&m_mutex);
diff --git a/src/render/jobs/sendbuffercapturejob_p.h b/src/render/jobs/sendbuffercapturejob_p.h
index 771497e2f..854414ec6 100644
--- a/src/render/jobs/sendbuffercapturejob_p.h
+++ b/src/render/jobs/sendbuffercapturejob_p.h
@@ -75,6 +75,7 @@ public:
~SendBufferCaptureJob();
void addRequest(QPair<Buffer*, QByteArray> request);
+ bool hasRequests() const;
void run() final;
diff --git a/src/render/materialsystem/qshaderprogrambuilder.cpp b/src/render/materialsystem/qshaderprogrambuilder.cpp
index 9318f96af..84dd33372 100644
--- a/src/render/materialsystem/qshaderprogrambuilder.cpp
+++ b/src/render/materialsystem/qshaderprogrambuilder.cpp
@@ -94,6 +94,53 @@ QShaderProgramBuilder::QShaderProgramBuilder(QShaderProgramBuilderPrivate &dd, Q
{
}
+void QShaderProgramBuilder::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &change)
+{
+ Q_D(QShaderProgramBuilder);
+ if (change->type() == Qt3DCore::PropertyUpdated) {
+ const Qt3DCore::QPropertyUpdatedChangePtr e = qSharedPointerCast<Qt3DCore::QPropertyUpdatedChange>(change);
+ if (e->propertyName() == QByteArrayLiteral("generatedShaderCode")) {
+ const bool blocked = blockNotifications(true);
+ const QPair<int, QByteArray> data = e->value().value<QPair<int, QByteArray>>();
+
+ switch (data.first) {
+ case QShaderProgram::Vertex: {
+ d->m_vertexShaderCode = data.second;
+ emit vertexShaderCodeChanged(d->m_vertexShaderCode);
+ break;
+ }
+ case QShaderProgram::Fragment:{
+ d->m_fragmentShaderCode = data.second;
+ emit fragmentShaderCodeChanged(d->m_fragmentShaderCode);
+ break;
+ }
+ case QShaderProgram::Geometry: {
+ d->m_geometryShaderCode = data.second;
+ emit geometryShaderCodeChanged(d->m_geometryShaderCode);
+ break;
+ }
+ case QShaderProgram::Compute: {
+ d->m_computeShaderCode = data.second;
+ emit computeShaderCodeChanged(d->m_computeShaderCode);
+ break;
+ }
+ case QShaderProgram::TessellationControl: {
+ d->m_tessControlShaderCode = data.second;
+ emit tessellationControlShaderCodeChanged(d->m_tessControlShaderCode);
+ break;
+ }
+ case QShaderProgram::TessellationEvaluation: {
+ d->m_tessEvalShaderCode = data.second;
+ emit tessellationEvaluationShaderCodeChanged(d->m_tessEvalShaderCode);
+ break;
+ }
+ }
+
+ blockNotifications(blocked);
+ }
+ }
+}
+
/*!
\qmlproperty string ShaderProgramBuilder::shaderProgram
@@ -311,6 +358,114 @@ QUrl QShaderProgramBuilder::computeShaderGraph() const
return d->m_computeShaderGraph;
}
+/*!
+ \qmlproperty string ShaderProgramBuilder:vertexShaderCode
+
+ Holds the generated vertex shader code
+ \since 2.13
+*/
+/*!
+ \property QShaderProgram:Builder:vertexShaderCode
+
+ Holds the generate vertex shader code.
+ \since 5.13
+*/
+QByteArray QShaderProgramBuilder::vertexShaderCode() const
+{
+ Q_D(const QShaderProgramBuilder);
+ return d->m_vertexShaderCode;
+}
+
+/*!
+ \qmlproperty string ShaderProgramBuilder:tessellationControlShaderCode
+
+ Holds the generated tessellation control shader code
+ \since 2.13
+*/
+/*!
+ \property QShaderProgram:Builder:tessellationControlShaderCode
+
+ Holds the generate tessellation control shader code.
+ \since 5.13
+*/
+QByteArray QShaderProgramBuilder::tessellationControlShaderCode() const
+{
+ Q_D(const QShaderProgramBuilder);
+ return d->m_tessControlShaderCode;
+}
+
+/*!
+ \qmlproperty string ShaderProgramBuilder:tessellationEvaluationShaderCode
+
+ Holds the generated tessellation evaluation shader code
+ \since 2.13
+*/
+/*!
+ \property QShaderProgram:Builder:tessellationEvaluationShaderCode
+
+ Holds the generate tessellation evaluation shader code.
+ \since 5.13
+*/
+QByteArray QShaderProgramBuilder::tessellationEvaluationShaderCode() const
+{
+ Q_D(const QShaderProgramBuilder);
+ return d->m_tessEvalShaderCode;
+}
+
+/*!
+ \qmlproperty string ShaderProgramBuilder:geometryShaderCode
+
+ Holds the generated geometry shader code
+ \since 2.13
+*/
+/*!
+ \property QShaderProgram:Builder:geometryShaderCode
+
+ Holds the generate geometry shader code.
+ \since 5.13
+*/
+QByteArray QShaderProgramBuilder::geometryShaderCode() const
+{
+ Q_D(const QShaderProgramBuilder);
+ return d->m_geometryShaderCode;
+}
+
+/*!
+ \qmlproperty string ShaderProgramBuilder::fragmentShaderCode
+
+ Holds the generated fragment shader code
+ \since 2.13
+*/
+/*!
+ \property QShaderProgram:Builder:fragmentShaderCode
+
+ Holds the generate fragment shader code.
+ \since 5.13
+*/
+QByteArray QShaderProgramBuilder::fragmentShaderCode() const
+{
+ Q_D(const QShaderProgramBuilder);
+ return d->m_fragmentShaderCode;
+}
+
+/*!
+ \qmlproperty string ShaderProgramBuilder::computeShaderCode
+
+ Holds the generated compute shader code
+ \since 2.13
+*/
+/*!
+ \property QShaderProgram:Builder:computeShaderCode
+
+ Holds the generate compute shader code.
+ \since 5.13
+*/
+QByteArray QShaderProgramBuilder::computeShaderCode() const
+{
+ Q_D(const QShaderProgramBuilder);
+ return d->m_computeShaderCode;
+}
+
Qt3DCore::QNodeCreatedChangeBasePtr QShaderProgramBuilder::createNodeCreationChange() const
{
auto creationChange = Qt3DCore::QNodeCreatedChangePtr<QShaderProgramBuilderData>::create(this);
diff --git a/src/render/materialsystem/qshaderprogrambuilder.h b/src/render/materialsystem/qshaderprogrambuilder.h
index fbbf7c752..532aa7d73 100644
--- a/src/render/materialsystem/qshaderprogrambuilder.h
+++ b/src/render/materialsystem/qshaderprogrambuilder.h
@@ -63,6 +63,12 @@ class QT3DRENDERSHARED_EXPORT QShaderProgramBuilder : public Qt3DCore::QNode
Q_PROPERTY(QUrl geometryShaderGraph READ geometryShaderGraph WRITE setGeometryShaderGraph NOTIFY geometryShaderGraphChanged)
Q_PROPERTY(QUrl fragmentShaderGraph READ fragmentShaderGraph WRITE setFragmentShaderGraph NOTIFY fragmentShaderGraphChanged)
Q_PROPERTY(QUrl computeShaderGraph READ computeShaderGraph WRITE setComputeShaderGraph NOTIFY computeShaderGraphChanged)
+ Q_PROPERTY(QByteArray vertexShaderCode READ vertexShaderCode NOTIFY vertexShaderCodeChanged REVISION 13)
+ Q_PROPERTY(QByteArray tessellationControlShaderCode READ tessellationControlShaderCode NOTIFY tessellationControlShaderCodeChanged REVISION 13)
+ Q_PROPERTY(QByteArray tessellationEvaluationShaderCode READ tessellationEvaluationShaderCode NOTIFY tessellationEvaluationShaderCodeChanged REVISION 13)
+ Q_PROPERTY(QByteArray geometryShaderCode READ geometryShaderCode NOTIFY geometryShaderCodeChanged REVISION 13)
+ Q_PROPERTY(QByteArray fragmentShaderCode READ fragmentShaderCode NOTIFY fragmentShaderCodeChanged REVISION 13)
+ Q_PROPERTY(QByteArray computeShaderCode READ computeShaderCode NOTIFY computeShaderCodeChanged REVISION 13)
public:
explicit QShaderProgramBuilder(Qt3DCore::QNode *parent = nullptr);
@@ -76,6 +82,12 @@ public:
QUrl geometryShaderGraph() const;
QUrl fragmentShaderGraph() const;
QUrl computeShaderGraph() const;
+ QByteArray vertexShaderCode() const;
+ QByteArray tessellationControlShaderCode() const;
+ QByteArray tessellationEvaluationShaderCode() const;
+ QByteArray geometryShaderCode() const;
+ QByteArray fragmentShaderCode() const;
+ QByteArray computeShaderCode() const;
public Q_SLOTS:
void setShaderProgram(Qt3DRender::QShaderProgram *program);
@@ -96,9 +108,16 @@ Q_SIGNALS:
void geometryShaderGraphChanged(const QUrl &geometryShaderGraph);
void fragmentShaderGraphChanged(const QUrl &fragmentShaderGraph);
void computeShaderGraphChanged(const QUrl &computeShaderGraph);
+ void vertexShaderCodeChanged(const QByteArray &vertexShaderCode);
+ void tessellationControlShaderCodeChanged(const QByteArray &tessellationControlShaderCode);
+ void tessellationEvaluationShaderCodeChanged(const QByteArray &tessellationEvaluationShaderCode);
+ void geometryShaderCodeChanged(const QByteArray &geometryShaderCode);
+ void fragmentShaderCodeChanged(const QByteArray &fragmentShaderCode);
+ void computeShaderCodeChanged(const QByteArray &computeShaderCode);
protected:
explicit QShaderProgramBuilder(QShaderProgramBuilderPrivate &dd, Qt3DCore::QNode *parent = nullptr);
+ void sceneChangeEvent(const Qt3DCore::QSceneChangePtr &change) override;
private:
Q_DECLARE_PRIVATE(QShaderProgramBuilder)
diff --git a/src/render/materialsystem/qshaderprogrambuilder_p.h b/src/render/materialsystem/qshaderprogrambuilder_p.h
index e1b470229..2ac765a1d 100644
--- a/src/render/materialsystem/qshaderprogrambuilder_p.h
+++ b/src/render/materialsystem/qshaderprogrambuilder_p.h
@@ -74,6 +74,12 @@ public:
QUrl m_geometryShaderGraph;
QUrl m_fragmentShaderGraph;
QUrl m_computeShaderGraph;
+ QByteArray m_vertexShaderCode;
+ QByteArray m_tessControlShaderCode;
+ QByteArray m_tessEvalShaderCode;
+ QByteArray m_geometryShaderCode;
+ QByteArray m_fragmentShaderCode;
+ QByteArray m_computeShaderCode;
};
struct QShaderProgramBuilderData
diff --git a/src/render/materialsystem/shaderbuilder.cpp b/src/render/materialsystem/shaderbuilder.cpp
index 283866d68..e0683332f 100644
--- a/src/render/materialsystem/shaderbuilder.cpp
+++ b/src/render/materialsystem/shaderbuilder.cpp
@@ -40,6 +40,7 @@
#include "shaderbuilder_p.h"
#include <Qt3DRender/private/qshaderprogrambuilder_p.h>
+#include <Qt3DRender/qshaderprogram.h>
#include <Qt3DRender/private/qurlhelper_p.h>
#include <QtGui/private/qshaderformat_p.h>
@@ -111,6 +112,31 @@ using namespace Qt3DCore;
namespace Qt3DRender {
namespace Render {
+
+namespace {
+
+QShaderProgram::ShaderType toQShaderProgramType(ShaderBuilder::ShaderType type)
+{
+ switch (type) {
+ case ShaderBuilder::ShaderType::Vertex:
+ return QShaderProgram::Vertex;
+ case ShaderBuilder::ShaderType::TessellationControl:
+ return QShaderProgram::TessellationControl;
+ case ShaderBuilder::ShaderType::TessellationEvaluation:
+ return QShaderProgram::TessellationEvaluation;
+ case ShaderBuilder::ShaderType::Geometry:
+ return QShaderProgram::Geometry;
+ case ShaderBuilder::ShaderType::Fragment:
+ return QShaderProgram::Fragment;
+ case ShaderBuilder::ShaderType::Compute:
+ return QShaderProgram::Compute;
+ default:
+ Q_UNREACHABLE();
+ }
+}
+
+} // anonymous
+
QString ShaderBuilder::getPrototypesFile()
{
return qt3dGlobalShaderPrototypes->prototypesFile();
@@ -284,6 +310,13 @@ void ShaderBuilder::generateCode(ShaderBuilder::ShaderType type)
const auto code = generator.createShaderCode(m_enabledLayers);
m_codes.insert(type, deincludify(code, graphPath + QStringLiteral(".glsl")));
m_dirtyTypes.remove(type);
+
+ // Send notification to the frontend
+ Qt3DCore::QPropertyUpdatedChangePtr propertyChange = Qt3DCore::QPropertyUpdatedChangePtr::create(peerId());
+ propertyChange->setDeliveryFlags(Qt3DCore::QSceneChange::DeliverToAll);
+ propertyChange->setPropertyName("generatedShaderCode");
+ propertyChange->setValue(QVariant::fromValue(qMakePair(int(toQShaderProgramType(type)), m_codes.value(type))));
+ notifyObservers(propertyChange);
}
void ShaderBuilder::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e)
diff --git a/src/render/picking/objectpicker.cpp b/src/render/picking/objectpicker.cpp
index 76f00993c..43e308d20 100644
--- a/src/render/picking/objectpicker.cpp
+++ b/src/render/picking/objectpicker.cpp
@@ -53,6 +53,7 @@ namespace Render {
ObjectPicker::ObjectPicker()
: BackendNode(QBackendNode::ReadWrite)
+ , m_priority(0)
, m_isPressed(false)
, m_hoverEnabled(false)
, m_dragEnabled(false)
@@ -70,6 +71,7 @@ void ObjectPicker::cleanup()
m_isPressed = false;
m_hoverEnabled = false;
m_dragEnabled = false;
+ m_priority = 0;
notifyJob();
}
@@ -79,6 +81,7 @@ void ObjectPicker::initializeFromPeer(const Qt3DCore::QNodeCreatedChangeBasePtr
const auto &data = typedChange->data;
m_hoverEnabled = data.hoverEnabled;
m_dragEnabled = data.dragEnabled;
+ m_priority = data.priority;
notifyJob();
}
@@ -97,6 +100,8 @@ void ObjectPicker::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e)
m_hoverEnabled = propertyChange->value().toBool();
} else if (propertyChange->propertyName() == QByteArrayLiteral("dragEnabled")) {
m_dragEnabled = propertyChange->value().toBool();
+ } else if (propertyChange->propertyName() == QByteArrayLiteral("priority")) {
+ m_priority = propertyChange->value().toInt();
}
markDirty(AbstractRenderer::AllDirty);
@@ -175,6 +180,16 @@ void ObjectPicker::onExited()
notifyObservers(e);
}
+void ObjectPicker::setPriority(int priority)
+{
+ m_priority = priority;
+}
+
+int ObjectPicker::priority() const
+{
+ return m_priority;
+}
+
} // Render
} // Qt3DRender
diff --git a/src/render/picking/objectpicker_p.h b/src/render/picking/objectpicker_p.h
index b9c308afb..7389a4b53 100644
--- a/src/render/picking/objectpicker_p.h
+++ b/src/render/picking/objectpicker_p.h
@@ -81,10 +81,15 @@ public:
void onEntered();
void onExited();
+ // Needed for unit tests
+ void setPriority(int priority);
+ int priority() const;
+
private:
void initializeFromPeer(const Qt3DCore::QNodeCreatedChangeBasePtr &change) final;
void notifyJob();
+ int m_priority;
bool m_isPressed;
bool m_hoverEnabled;
bool m_dragEnabled;
diff --git a/src/render/picking/qobjectpicker.cpp b/src/render/picking/qobjectpicker.cpp
index c3671d018..a0b6d8dcd 100644
--- a/src/render/picking/qobjectpicker.cpp
+++ b/src/render/picking/qobjectpicker.cpp
@@ -266,6 +266,23 @@ void QObjectPicker::setDragEnabled(bool dragEnabled)
}
/*!
+ * Sets the picker's priority to \a priority. This is used when the pick result
+ * mode on QPickingSettings is set to QPickingSettings::NearestPriorityPick.
+ * Picking results are sorted by highest priority and shortest picking
+ * distance.
+ *
+ * \since 5.13
+ */
+void QObjectPicker::setPriority(int priority)
+{
+ Q_D(QObjectPicker);
+ if (priority != d->m_priority) {
+ d->m_priority = priority;
+ emit priorityChanged(priority);
+ }
+}
+
+/*!
\qmlproperty bool Qt3D.Render::ObjectPicker::dragEnabled
*/
/*!
@@ -312,6 +329,25 @@ bool QObjectPicker::isPressed() const
return d->m_pressed;
}
+/*!
+ \qmlproperty int Qt3D.Render::ObjectPicker::priority
+
+ The priority to be used when filtering pick results by priority when
+ PickingSettings.pickResultMode is set to PickingSettings.PriorityPick.
+*/
+/*!
+ \property Qt3DRender::QObjectPicker::priority
+
+ The priority to be used when filtering pick results by priority when
+ QPickingSettings::pickResultMode is set to
+ QPickingSettings::NearestPriorityPick.
+*/
+int QObjectPicker::priority() const
+{
+ Q_D(const QObjectPicker);
+ return d->m_priority;
+}
+
/*! \internal */
void QObjectPicker::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &change)
{
@@ -465,6 +501,7 @@ Qt3DCore::QNodeCreatedChangeBasePtr QObjectPicker::createNodeCreationChange() co
Q_D(const QObjectPicker);
data.hoverEnabled = d->m_hoverEnabled;
data.dragEnabled = d->m_dragEnabled;
+ data.priority = d->m_priority;
return creationChange;
}
diff --git a/src/render/picking/qobjectpicker.h b/src/render/picking/qobjectpicker.h
index 9f3b138c3..1d15f6092 100644
--- a/src/render/picking/qobjectpicker.h
+++ b/src/render/picking/qobjectpicker.h
@@ -58,6 +58,7 @@ class QT3DRENDERSHARED_EXPORT QObjectPicker : public Qt3DCore::QComponent
Q_PROPERTY(bool dragEnabled READ isDragEnabled WRITE setDragEnabled NOTIFY dragEnabledChanged)
Q_PROPERTY(bool pressed READ isPressed NOTIFY pressedChanged)
Q_PROPERTY(bool containsMouse READ containsMouse NOTIFY containsMouseChanged)
+ Q_PROPERTY(int priority READ priority WRITE setPriority NOTIFY priorityChanged REVISION 13)
public:
explicit QObjectPicker(QNode *parent = nullptr);
@@ -69,9 +70,12 @@ public:
bool containsMouse() const;
bool isPressed() const;
+ int priority() const;
+
public Q_SLOTS:
void setHoverEnabled(bool hoverEnabled);
void setDragEnabled(bool dragEnabled);
+ void setPriority(int priority);
Q_SIGNALS:
void pressed(Qt3DRender::QPickEvent *pick);
@@ -84,6 +88,7 @@ Q_SIGNALS:
void dragEnabledChanged(bool dragEnabled);
void pressedChanged(bool pressed);
void containsMouseChanged(bool containsMouse);
+ void priorityChanged(int priority);
protected:
void sceneChangeEvent(const Qt3DCore::QSceneChangePtr &change) override;
diff --git a/src/render/picking/qobjectpicker_p.h b/src/render/picking/qobjectpicker_p.h
index 3c48b9419..384062bef 100644
--- a/src/render/picking/qobjectpicker_p.h
+++ b/src/render/picking/qobjectpicker_p.h
@@ -69,6 +69,7 @@ public:
, m_pressed(false)
, m_containsMouse(false)
, m_acceptedLastPressedEvent(true)
+ , m_priority(0)
{
m_shareable = false;
}
@@ -79,6 +80,7 @@ public:
bool m_pressed;
bool m_containsMouse;
bool m_acceptedLastPressedEvent;
+ int m_priority;
enum EventType {
Pressed,
@@ -102,6 +104,7 @@ struct QObjectPickerData
{
bool hoverEnabled;
bool dragEnabled;
+ int priority;
};
} // namespace Qt3DRender
diff --git a/src/render/renderers/opengl/graphicshelpers/glfence_p.h b/src/render/renderers/opengl/graphicshelpers/glfence_p.h
new file mode 100644
index 000000000..366065048
--- /dev/null
+++ b/src/render/renderers/opengl/graphicshelpers/glfence_p.h
@@ -0,0 +1,73 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 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 GLFENCE_P_H
+#define GLFENCE_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 <QtGlobal>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+namespace Render {
+
+// GLsync is a pointer to a struct (unlike the rest of GL which used int ids)
+// We cannot reference GLsync as it's only available since 3.2 We use FenceId
+// to wrap that around and trust the GLHelpers will convert them accordingly.
+using GLFence = void *;
+
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+
+#endif // GLFENCE_P_H
diff --git a/src/render/renderers/opengl/graphicshelpers/graphicshelperes2.cpp b/src/render/renderers/opengl/graphicshelpers/graphicshelperes2.cpp
index c5753195b..71540b1ad 100644
--- a/src/render/renderers/opengl/graphicshelpers/graphicshelperes2.cpp
+++ b/src/render/renderers/opengl/graphicshelpers/graphicshelperes2.cpp
@@ -318,6 +318,33 @@ void GraphicsHelperES2::drawBuffer(GLenum mode)
qWarning() << "glDrawBuffer is not supported with OpenGL ES 2";
}
+void *GraphicsHelperES2::fenceSync()
+{
+ qWarning() << "Fences are not supported by OpenGL ES 2.0 (since OpenGL ES 3.0)";
+ return nullptr;
+}
+
+void GraphicsHelperES2::clientWaitSync(void *, GLuint64 )
+{
+ qWarning() << "Fences are not supported by OpenGL ES 2.0 (since OpenGL ES 3.0)";
+}
+
+void GraphicsHelperES2::waitSync(void *)
+{
+ qWarning() << "Fences are not supported by OpenGL ES 2.0 (since OpenGL ES 3.0)";
+}
+
+bool GraphicsHelperES2::wasSyncSignaled(void *)
+{
+ qWarning() << "Fences are not supported by OpenGL ES 2.0 (since OpenGL ES 3.0)";
+ return false;
+}
+
+void GraphicsHelperES2::deleteSync(void *)
+{
+ qWarning() << "Fences are not supported by OpenGL ES 2.0 (since OpenGL ES 3.0)";
+}
+
void GraphicsHelperES2::blendEquation(GLenum mode)
{
m_funcs->glBlendEquation(mode);
diff --git a/src/render/renderers/opengl/graphicshelpers/graphicshelperes2_p.h b/src/render/renderers/opengl/graphicshelpers/graphicshelperes2_p.h
index 1c6df41b6..8c8dd34e9 100644
--- a/src/render/renderers/opengl/graphicshelpers/graphicshelperes2_p.h
+++ b/src/render/renderers/opengl/graphicshelpers/graphicshelperes2_p.h
@@ -132,6 +132,12 @@ public:
void readBuffer(GLenum mode) override;
void drawBuffer(GLenum mode) override;
+ void *fenceSync() override;
+ void clientWaitSync(void *sync, GLuint64 nanoSecTimeout) override;
+ void waitSync(void *sync) override;
+ bool wasSyncSignaled(void *sync) override;
+ void deleteSync(void *sync) override;
+
void glUniform1fv(GLint location, GLsizei count, const GLfloat *value) override;
void glUniform2fv(GLint location, GLsizei count, const GLfloat *value) override;
void glUniform3fv(GLint location, GLsizei count, const GLfloat *value) override;
diff --git a/src/render/renderers/opengl/graphicshelpers/graphicshelperes3.cpp b/src/render/renderers/opengl/graphicshelpers/graphicshelperes3.cpp
index 34c1e7448..5e5d2e001 100644
--- a/src/render/renderers/opengl/graphicshelpers/graphicshelperes3.cpp
+++ b/src/render/renderers/opengl/graphicshelpers/graphicshelperes3.cpp
@@ -147,6 +147,26 @@ QT_BEGIN_NAMESPACE
#define GL_READ_FRAMEBUFFER 0x8CA8
#endif
+#ifndef GL_SIGNALED
+#define GL_SIGNALED 0x9119
+#endif
+
+#ifndef GL_SYNC_STATUS
+#define GL_SYNC_STATUS 0x9114
+#endif
+
+#ifndef GL_TIMEOUT_IGNORED
+#define GL_TIMEOUT_IGNORED 0xFFFFFFFFFFFFFFFFull
+#endif
+
+#ifndef GL_SYNC_GPU_COMMANDS_COMPLETE
+#define GL_SYNC_GPU_COMMANDS_COMPLETE 0x9117
+#endif
+
+#ifndef GL_SYNC_FLUSH_COMMANDS_BIT
+#define GL_SYNC_FLUSH_COMMANDS_BIT 0x00000001
+#endif
+
namespace Qt3DRender {
namespace Render {
@@ -307,6 +327,7 @@ bool GraphicsHelperES3::supportsFeature(GraphicsHelperInterface::Feature feature
case BlitFramebuffer:
case UniformBufferObject:
case MapBuffer:
+ case Fences:
return true;
default:
return false;
@@ -439,6 +460,37 @@ uint GraphicsHelperES3::uniformByteSize(const ShaderUniform &description)
return arrayStride ? rawByteSize * arrayStride : rawByteSize;
}
+void *GraphicsHelperES3::fenceSync()
+{
+ return m_extraFuncs->glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
+}
+
+void GraphicsHelperES3::clientWaitSync(void *sync, GLuint64 nanoSecTimeout)
+{
+ m_extraFuncs->glClientWaitSync(static_cast<GLsync>(sync), GL_SYNC_FLUSH_COMMANDS_BIT, nanoSecTimeout);
+}
+
+void GraphicsHelperES3::waitSync(void *sync)
+{
+ m_extraFuncs->glWaitSync(static_cast<GLsync>(sync), 0, GL_TIMEOUT_IGNORED);
+}
+
+bool GraphicsHelperES3::wasSyncSignaled(void *sync)
+{
+ GLint v;
+ m_extraFuncs->glGetSynciv(static_cast<GLsync>(sync),
+ GL_SYNC_STATUS,
+ sizeof(v),
+ nullptr,
+ &v);
+ return v == GL_SIGNALED;
+}
+
+void GraphicsHelperES3::deleteSync(void *sync)
+{
+ m_extraFuncs->glDeleteSync(static_cast<GLsync>(sync));
+}
+
void GraphicsHelperES3::blitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter)
{
m_extraFuncs->glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
diff --git a/src/render/renderers/opengl/graphicshelpers/graphicshelperes3_p.h b/src/render/renderers/opengl/graphicshelpers/graphicshelperes3_p.h
index d4467cf7f..dc5cef10c 100644
--- a/src/render/renderers/opengl/graphicshelpers/graphicshelperes3_p.h
+++ b/src/render/renderers/opengl/graphicshelpers/graphicshelperes3_p.h
@@ -90,6 +90,12 @@ public:
UniformType uniformTypeFromGLType(GLenum glType) override;
uint uniformByteSize(const ShaderUniform &description) override;
+ void *fenceSync() override;
+ void clientWaitSync(void *sync, GLuint64 nanoSecTimeout) override;
+ void waitSync(void *sync) override;
+ bool wasSyncSignaled(void *sync) override;
+ void deleteSync(void *sync) override;
+
protected:
QOpenGLExtraFunctions *m_extraFuncs = nullptr;
};
diff --git a/src/render/renderers/opengl/graphicshelpers/graphicshelpergl2.cpp b/src/render/renderers/opengl/graphicshelpers/graphicshelpergl2.cpp
index 6da8a9b6f..b6f3412b2 100644
--- a/src/render/renderers/opengl/graphicshelpers/graphicshelpergl2.cpp
+++ b/src/render/renderers/opengl/graphicshelpers/graphicshelpergl2.cpp
@@ -272,6 +272,33 @@ void GraphicsHelperGL2::drawBuffer(GLenum mode)
m_funcs->glDrawBuffer(mode);
}
+void *GraphicsHelperGL2::fenceSync()
+{
+ qWarning() << "Fences are not supported by OpenGL 2.0 (since OpenGL 3.2)";
+ return nullptr;
+}
+
+void GraphicsHelperGL2::clientWaitSync(void *, GLuint64 )
+{
+ qWarning() << "Fences are not supported by OpenGL 2.0 (since OpenGL 3.2)";
+}
+
+void GraphicsHelperGL2::waitSync(void *)
+{
+ qWarning() << "Fences are not supported by OpenGL 2.0 (since OpenGL 3.2)";
+}
+
+bool GraphicsHelperGL2::wasSyncSignaled(void *)
+{
+ qWarning() << "Fences are not supported by OpenGL 2.0 (since OpenGL 3.2)";
+ return false;
+}
+
+void GraphicsHelperGL2::deleteSync(void *)
+{
+ qWarning() << "Fences are not supported by OpenGL 2.0 (since OpenGL 3.2)";
+}
+
void GraphicsHelperGL2::blendEquation(GLenum mode)
{
m_funcs->glBlendEquation(mode);
@@ -412,6 +439,7 @@ bool GraphicsHelperGL2::supportsFeature(GraphicsHelperInterface::Feature feature
case MRT:
return (m_fboFuncs != nullptr);
case TextureDimensionRetrieval:
+ case MapBuffer:
return true;
default:
return false;
diff --git a/src/render/renderers/opengl/graphicshelpers/graphicshelpergl2_p.h b/src/render/renderers/opengl/graphicshelpers/graphicshelpergl2_p.h
index 2db75004f..b142b2623 100644
--- a/src/render/renderers/opengl/graphicshelpers/graphicshelpergl2_p.h
+++ b/src/render/renderers/opengl/graphicshelpers/graphicshelpergl2_p.h
@@ -132,6 +132,12 @@ public:
void readBuffer(GLenum mode) override;
void drawBuffer(GLenum mode) override;
+ void *fenceSync() override;
+ void clientWaitSync(void *sync, GLuint64 nanoSecTimeout) override;
+ void waitSync(void *sync) override;
+ bool wasSyncSignaled(void *sync) override;
+ void deleteSync(void *sync) override;
+
void glUniform1fv(GLint location, GLsizei count, const GLfloat *value) override;
void glUniform2fv(GLint location, GLsizei count, const GLfloat *value) override;
void glUniform3fv(GLint location, GLsizei count, const GLfloat *value) override;
diff --git a/src/render/renderers/opengl/graphicshelpers/graphicshelpergl3_2.cpp b/src/render/renderers/opengl/graphicshelpers/graphicshelpergl3_2.cpp
index a35c4e37f..5ff1a2ba5 100644
--- a/src/render/renderers/opengl/graphicshelpers/graphicshelpergl3_2.cpp
+++ b/src/render/renderers/opengl/graphicshelpers/graphicshelpergl3_2.cpp
@@ -334,6 +334,37 @@ void GraphicsHelperGL3_2::drawBuffer(GLenum mode)
m_funcs->glDrawBuffer(mode);
}
+void *GraphicsHelperGL3_2::fenceSync()
+{
+ return m_funcs->glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
+}
+
+void GraphicsHelperGL3_2::clientWaitSync(void *sync, GLuint64 nanoSecTimeout)
+{
+ m_funcs->glClientWaitSync(static_cast<GLsync>(sync), GL_SYNC_FLUSH_COMMANDS_BIT, nanoSecTimeout);
+}
+
+void GraphicsHelperGL3_2::waitSync(void *sync)
+{
+ m_funcs->glWaitSync(static_cast<GLsync>(sync), 0, GL_TIMEOUT_IGNORED);
+}
+
+bool GraphicsHelperGL3_2::wasSyncSignaled(void *sync)
+{
+ GLint v;
+ m_funcs->glGetSynciv(static_cast<GLsync>(sync),
+ GL_SYNC_STATUS,
+ sizeof(v),
+ nullptr,
+ &v);
+ return v == GL_SIGNALED;
+}
+
+void GraphicsHelperGL3_2::deleteSync(void *sync)
+{
+ m_funcs->glDeleteSync(static_cast<GLsync>(sync));
+}
+
void GraphicsHelperGL3_2::blendEquation(GLenum mode)
{
m_funcs->glBlendEquation(mode);
@@ -481,6 +512,7 @@ bool GraphicsHelperGL3_2::supportsFeature(GraphicsHelperInterface::Feature featu
case TextureDimensionRetrieval:
case BindableFragmentOutputs:
case BlitFramebuffer:
+ case Fences:
return true;
case Tessellation:
return !m_tessFuncs.isNull();
diff --git a/src/render/renderers/opengl/graphicshelpers/graphicshelpergl3_2_p.h b/src/render/renderers/opengl/graphicshelpers/graphicshelpergl3_2_p.h
index 133295fd7..9e81345ad 100644
--- a/src/render/renderers/opengl/graphicshelpers/graphicshelpergl3_2_p.h
+++ b/src/render/renderers/opengl/graphicshelpers/graphicshelpergl3_2_p.h
@@ -134,6 +134,12 @@ public:
void readBuffer(GLenum mode) override;
void drawBuffer(GLenum mode) override;
+ void *fenceSync() override;
+ void clientWaitSync(void *sync, GLuint64 nanoSecTimeout) override;
+ void waitSync(void *sync) override;
+ bool wasSyncSignaled(void *sync) override;
+ void deleteSync(void *sync) override;
+
void glUniform1fv(GLint location, GLsizei count, const GLfloat *value) override;
void glUniform2fv(GLint location, GLsizei count, const GLfloat *value) override;
void glUniform3fv(GLint location, GLsizei count, const GLfloat *value) override;
diff --git a/src/render/renderers/opengl/graphicshelpers/graphicshelpergl3_3.cpp b/src/render/renderers/opengl/graphicshelpers/graphicshelpergl3_3.cpp
index b2512d84a..81081943d 100644
--- a/src/render/renderers/opengl/graphicshelpers/graphicshelpergl3_3.cpp
+++ b/src/render/renderers/opengl/graphicshelpers/graphicshelpergl3_3.cpp
@@ -330,6 +330,37 @@ void GraphicsHelperGL3_3::drawBuffer(GLenum mode)
m_funcs->glDrawBuffer(mode);
}
+void *GraphicsHelperGL3_3::fenceSync()
+{
+ return m_funcs->glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
+}
+
+void GraphicsHelperGL3_3::clientWaitSync(void *sync, GLuint64 nanoSecTimeout)
+{
+ m_funcs->glClientWaitSync(static_cast<GLsync>(sync), GL_SYNC_FLUSH_COMMANDS_BIT, nanoSecTimeout);
+}
+
+void GraphicsHelperGL3_3::waitSync(void *sync)
+{
+ m_funcs->glWaitSync(static_cast<GLsync>(sync), 0, GL_TIMEOUT_IGNORED);
+}
+
+bool GraphicsHelperGL3_3::wasSyncSignaled(void *sync)
+{
+ GLint v;
+ m_funcs->glGetSynciv(static_cast<GLsync>(sync),
+ GL_SYNC_STATUS,
+ sizeof(v),
+ nullptr,
+ &v);
+ return v == GL_SIGNALED;
+}
+
+void GraphicsHelperGL3_3::deleteSync(void *sync)
+{
+ m_funcs->glDeleteSync(static_cast<GLsync>(sync));
+}
+
void GraphicsHelperGL3_3::blendEquation(GLenum mode)
{
m_funcs->glBlendEquation(mode);
@@ -477,6 +508,7 @@ bool GraphicsHelperGL3_3::supportsFeature(GraphicsHelperInterface::Feature featu
case TextureDimensionRetrieval:
case BindableFragmentOutputs:
case BlitFramebuffer:
+ case Fences:
return true;
case Tessellation:
return !m_tessFuncs.isNull();
diff --git a/src/render/renderers/opengl/graphicshelpers/graphicshelpergl3_3_p.h b/src/render/renderers/opengl/graphicshelpers/graphicshelpergl3_3_p.h
index 0ecdd3620..c480e5258 100644
--- a/src/render/renderers/opengl/graphicshelpers/graphicshelpergl3_3_p.h
+++ b/src/render/renderers/opengl/graphicshelpers/graphicshelpergl3_3_p.h
@@ -134,6 +134,12 @@ public:
void readBuffer(GLenum mode) override;
void drawBuffer(GLenum mode) override;
+ void *fenceSync() override;
+ void clientWaitSync(void *sync, GLuint64 nanoSecTimeout) override;
+ void waitSync(void *sync) override;
+ bool wasSyncSignaled(void *sync) override;
+ void deleteSync(void *sync) override;
+
void glUniform1fv(GLint location, GLsizei count, const GLfloat *value) override;
void glUniform2fv(GLint location, GLsizei count, const GLfloat *value) override;
void glUniform3fv(GLint location, GLsizei count, const GLfloat *value) override;
diff --git a/src/render/renderers/opengl/graphicshelpers/graphicshelpergl4.cpp b/src/render/renderers/opengl/graphicshelpers/graphicshelpergl4.cpp
index ce1b8ac2b..22cbf7428 100644
--- a/src/render/renderers/opengl/graphicshelpers/graphicshelpergl4.cpp
+++ b/src/render/renderers/opengl/graphicshelpers/graphicshelpergl4.cpp
@@ -400,6 +400,39 @@ void GraphicsHelperGL4::drawBuffer(GLenum mode)
m_funcs->glDrawBuffer(mode);
}
+void *GraphicsHelperGL4::fenceSync()
+{
+ return m_funcs->glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
+}
+
+void GraphicsHelperGL4::clientWaitSync(void *sync, GLuint64 nanoSecTimeout)
+{
+ qDebug() << Q_FUNC_INFO << sync << static_cast<GLsync>(sync);
+ GLenum e = m_funcs->glClientWaitSync(static_cast<GLsync>(sync), GL_SYNC_FLUSH_COMMANDS_BIT, nanoSecTimeout);
+ qDebug() << e;
+}
+
+void GraphicsHelperGL4::waitSync(void *sync)
+{
+ m_funcs->glWaitSync(static_cast<GLsync>(sync), 0, GL_TIMEOUT_IGNORED);
+}
+
+bool GraphicsHelperGL4::wasSyncSignaled(void *sync)
+{
+ GLint v = 0;
+ m_funcs->glGetSynciv(static_cast<GLsync>(sync),
+ GL_SYNC_STATUS,
+ sizeof(v),
+ nullptr,
+ &v);
+ return v == GL_SIGNALED;
+}
+
+void GraphicsHelperGL4::deleteSync(void *sync)
+{
+ m_funcs->glDeleteSync(static_cast<GLsync>(sync));
+}
+
void GraphicsHelperGL4::glUniform1fv(GLint location, GLsizei count, const GLfloat *values)
{
m_funcs->glUniform1fv(location, count, values);
@@ -746,6 +779,8 @@ bool GraphicsHelperGL4::supportsFeature(GraphicsHelperInterface::Feature feature
case DrawBuffersBlend:
case BlitFramebuffer:
case IndirectDrawing:
+ case MapBuffer:
+ case Fences:
return true;
default:
return false;
diff --git a/src/render/renderers/opengl/graphicshelpers/graphicshelpergl4_p.h b/src/render/renderers/opengl/graphicshelpers/graphicshelpergl4_p.h
index 3020b16d8..da62f4212 100644
--- a/src/render/renderers/opengl/graphicshelpers/graphicshelpergl4_p.h
+++ b/src/render/renderers/opengl/graphicshelpers/graphicshelpergl4_p.h
@@ -132,6 +132,12 @@ public:
void readBuffer(GLenum mode) override;
void drawBuffer(GLenum mode) override;
+ void *fenceSync() override;
+ void clientWaitSync(void *sync, GLuint64 nanoSecTimeout) override;
+ void waitSync(void *sync) override;
+ bool wasSyncSignaled(void *sync) override;
+ void deleteSync(void *sync) override;
+
void glUniform1fv(GLint location, GLsizei count, const GLfloat *value) override;
void glUniform2fv(GLint location, GLsizei count, const GLfloat *value) override;
void glUniform3fv(GLint location, GLsizei count, const GLfloat *value) override;
diff --git a/src/render/renderers/opengl/graphicshelpers/graphicshelperinterface_p.h b/src/render/renderers/opengl/graphicshelpers/graphicshelperinterface_p.h
index e41325cb7..2a1688b7f 100644
--- a/src/render/renderers/opengl/graphicshelpers/graphicshelperinterface_p.h
+++ b/src/render/renderers/opengl/graphicshelpers/graphicshelperinterface_p.h
@@ -82,7 +82,8 @@ public:
DrawBuffersBlend,
BlitFramebuffer,
IndirectDrawing,
- MapBuffer
+ MapBuffer,
+ Fences
};
enum FBOBindMode {
@@ -155,6 +156,12 @@ public:
virtual void readBuffer(GLenum mode) = 0;
virtual void drawBuffer(GLenum mode) = 0;
+ virtual void *fenceSync() = 0;
+ virtual void clientWaitSync(void *sync, GLuint64 nanoSecTimeout) = 0;
+ virtual void waitSync(void *sync) = 0;
+ virtual bool wasSyncSignaled(void *sync) = 0;
+ virtual void deleteSync(void *sync) = 0;
+
virtual void glUniform1fv(GLint location, GLsizei count, const GLfloat *value) = 0;
virtual void glUniform2fv(GLint location, GLsizei count, const GLfloat *value) = 0;
virtual void glUniform3fv(GLint location, GLsizei count, const GLfloat *value) = 0;
diff --git a/src/render/renderers/opengl/graphicshelpers/graphicshelpers.pri b/src/render/renderers/opengl/graphicshelpers/graphicshelpers.pri
index 9b25be0eb..5c9479d2b 100644
--- a/src/render/renderers/opengl/graphicshelpers/graphicshelpers.pri
+++ b/src/render/renderers/opengl/graphicshelpers/graphicshelpers.pri
@@ -13,7 +13,8 @@ HEADERS += \
$$PWD/graphicshelpergl3_3_p.h \
$$PWD/graphicshelpergl4_p.h \
$$PWD/graphicshelpergl3_2_p.h \
- $$PWD/submissioncontext_p.h
+ $$PWD/submissioncontext_p.h \
+ $$PWD/glfence_p.h
SOURCES += \
$$PWD/graphicscontext.cpp \
diff --git a/src/render/renderers/opengl/graphicshelpers/submissioncontext.cpp b/src/render/renderers/opengl/graphicshelpers/submissioncontext.cpp
index dc1e85b8e..d1ac853ea 100644
--- a/src/render/renderers/opengl/graphicshelpers/submissioncontext.cpp
+++ b/src/render/renderers/opengl/graphicshelpers/submissioncontext.cpp
@@ -929,10 +929,22 @@ int SubmissionContext::activateTexture(TextureScope scope, GLTexture *tex, int o
// Note: tex->dna() could be 0 if the texture has not been created yet
if (m_activeTextures[onUnit].texture != tex) {
// Texture must have been created and updated at this point
- QOpenGLTexture *glTex = tex->getGLTexture();
- if (glTex == nullptr)
- return -1;
- glTex->bind(onUnit);
+
+ const int sharedTextureId = tex->sharedTextureId();
+
+ // We have a valid texture id provided by a shared context
+ if (sharedTextureId > 0) {
+ m_gl->functions()->glActiveTexture(GL_TEXTURE0 + onUnit);
+ const QAbstractTexture::Target target = tex->properties().target;
+ // For now we know that target values correspond to the GL values
+ m_gl->functions()->glBindTexture(target, tex->sharedTextureId());
+ } else {
+ QOpenGLTexture *glTex = tex->getGLTexture();
+ if (glTex == nullptr)
+ return -1;
+ glTex->bind(onUnit);
+ }
+
if (m_activeTextures[onUnit].texture)
TextureExtRendererLocker::unlock(m_activeTextures[onUnit].texture);
m_activeTextures[onUnit].texture = tex;
@@ -1249,6 +1261,33 @@ void SubmissionContext::clearStencilValue(int stencil)
}
}
+GLFence SubmissionContext::fenceSync()
+{
+ return m_glHelper->fenceSync();
+}
+
+void SubmissionContext::clientWaitSync(GLFence sync, GLuint64 nanoSecTimeout)
+{
+ qDebug() << Q_FUNC_INFO << sync;
+ m_glHelper->clientWaitSync(sync, nanoSecTimeout);
+}
+
+void SubmissionContext::waitSync(GLFence sync)
+{
+ qDebug() << Q_FUNC_INFO << sync;
+ m_glHelper->waitSync(sync);
+}
+
+bool SubmissionContext::wasSyncSignaled(GLFence sync)
+{
+ return m_glHelper->wasSyncSignaled(sync);
+}
+
+void SubmissionContext::deleteSync(GLFence sync)
+{
+ m_glHelper->deleteSync(sync);
+}
+
// It will be easier if the QGraphicContext applies the QUniformPack
// than the other way around
bool SubmissionContext::setParameters(ShaderParameterPack &parameterPack)
diff --git a/src/render/renderers/opengl/graphicshelpers/submissioncontext_p.h b/src/render/renderers/opengl/graphicshelpers/submissioncontext_p.h
index d502a8b27..dbfaef148 100644
--- a/src/render/renderers/opengl/graphicshelpers/submissioncontext_p.h
+++ b/src/render/renderers/opengl/graphicshelpers/submissioncontext_p.h
@@ -59,6 +59,7 @@
#include <Qt3DRender/qattribute.h>
#include <Qt3DRender/private/handle_types_p.h>
#include <Qt3DRender/private/shadercache_p.h>
+#include <Qt3DRender/private/glfence_p.h>
QT_BEGIN_NAMESPACE
@@ -163,6 +164,14 @@ public:
void clearDepthValue(float depth);
void clearStencilValue(int stencil);
+
+ // Fences
+ GLFence fenceSync();
+ void clientWaitSync(GLFence sync, GLuint64 nanoSecTimeout);
+ void waitSync(GLFence sync);
+ bool wasSyncSignaled(GLFence sync);
+ void deleteSync(GLFence sync);
+
private:
void initialize();
diff --git a/src/render/renderers/opengl/jobs/renderviewjobutils.cpp b/src/render/renderers/opengl/jobs/renderviewjobutils.cpp
index 629e7e935..9dd7faacc 100644
--- a/src/render/renderers/opengl/jobs/renderviewjobutils.cpp
+++ b/src/render/renderers/opengl/jobs/renderviewjobutils.cpp
@@ -68,6 +68,7 @@
#include <Qt3DRender/private/techniquemanager_p.h>
#include <Qt3DRender/private/memorybarrier_p.h>
#include <Qt3DRender/private/blitframebuffer_p.h>
+#include <Qt3DRender/private/waitfence_p.h>
QT_BEGIN_NAMESPACE
@@ -272,6 +273,17 @@ void setRenderViewConfigFromFrameGraphLeafNode(RenderView *rv, const FrameGraphN
break;
}
+ case FrameGraphNode::WaitFence: {
+ const Render::WaitFence *waitFence = static_cast<const Render::WaitFence *>(node);
+ rv->appendWaitFence(waitFence->data());
+ break;
+ }
+
+ case FrameGraphNode::SetFence: {
+ rv->appendInsertFenceId(node->peerId());
+ break;
+ }
+
default:
// Should never get here
qCWarning(Backend) << "Unhandled FrameGraphNode type";
diff --git a/src/render/renderers/opengl/renderer/renderer.cpp b/src/render/renderers/opengl/renderer/renderer.cpp
index 7ed766f8b..4abe62bab 100644
--- a/src/render/renderers/opengl/renderer/renderer.cpp
+++ b/src/render/renderers/opengl/renderer/renderer.cpp
@@ -91,6 +91,7 @@
#include <Qt3DRender/private/renderviewbuilder_p.h>
#include <Qt3DRender/private/commandthread_p.h>
#include <Qt3DRender/private/glcommands_p.h>
+#include <Qt3DRender/private/setfence_p.h>
#include <Qt3DRender/qcameralens.h>
#include <Qt3DCore/private/qeventfilterservice_p.h>
@@ -196,6 +197,7 @@ Renderer::Renderer(QRenderAspect::RenderType type)
, m_vaoGathererJob(Render::GenericLambdaJobPtr<std::function<void ()>>::create([this] { lookForAbandonedVaos(); }, JobTypes::DirtyVaoGathering))
, m_textureGathererJob(Render::GenericLambdaJobPtr<std::function<void ()>>::create([this] { lookForDirtyTextures(); }, JobTypes::DirtyTextureGathering))
, m_sendTextureChangesToFrontendJob(Render::GenericLambdaJobPtr<std::function<void ()>>::create([this] { sendTextureChangesToFrontend(); }, JobTypes::SendTextureChangesToFrontend))
+ , m_sendSetFenceHandlesToFrontendJob(Render::GenericLambdaJobPtr<std::function<void ()>>::create([this] { sendSetFenceHandlesToFrontend(); }, JobTypes::SendSetFenceHandlesToFrontend))
, m_introspectShaderJob(Render::GenericLambdaJobPtr<std::function<void ()>>::create([this] { reloadDirtyShaders(); }, JobTypes::DirtyShaderGathering))
, m_syncTextureLoadingJob(Render::GenericLambdaJobPtr<std::function<void ()>>::create([] {}, JobTypes::SyncTextureLoading))
, m_ownedContext(false)
@@ -388,6 +390,9 @@ void Renderer::initialize()
[this] { releaseGraphicsResources(); });
}
+ qCDebug(Backend) << "Qt3D shared context:" << ctx->shareContext();
+ qCDebug(Backend) << "Qt global shared context:" << qt_gl_global_share_context();
+
if (!ctx->shareContext()) {
m_shareContext = new QOpenGLContext;
m_shareContext->setFormat(ctx->format());
@@ -1092,7 +1097,7 @@ void Renderer::lookForDirtyTextures()
}
// Dirty meaning that something has changed on the texture
- // either properties, parameters, generator or a texture image
+ // 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
@@ -1178,7 +1183,7 @@ void Renderer::reloadDirtyShaders()
// Executed in a job
void Renderer::sendTextureChangesToFrontend()
{
- const QVector<QPair<TextureProperties, Qt3DCore::QNodeIdVector>> updateTextureProperties = std::move(m_updatedTextureProperties);
+ const QVector<QPair<Texture::TextureUpdateInfo, Qt3DCore::QNodeIdVector>> updateTextureProperties = std::move(m_updatedTextureProperties);
for (const auto &pair : updateTextureProperties) {
// Prepare change notification
@@ -1197,6 +1202,22 @@ void Renderer::sendTextureChangesToFrontend()
}
}
+// Executed in a job
+void Renderer::sendSetFenceHandlesToFrontend()
+{
+ const QVector<QPair<Qt3DCore::QNodeId, GLFence>> updatedSetFence = std::move(m_updatedSetFences);
+ FrameGraphManager *fgManager = m_nodesManager->frameGraphManager();
+ for (const auto &pair : updatedSetFence) {
+ FrameGraphNode *fgNode = fgManager->lookupNode(pair.first);
+ if (fgNode != nullptr) { // Node could have been deleted before we got a chance to notify it
+ Q_ASSERT(fgNode->nodeType() == FrameGraphNode::SetFence);
+ SetFence *setFenceNode = static_cast<SetFence *>(fgNode);
+ setFenceNode->setHandleType(QSetFence::OpenGLFenceId);
+ setFenceNode->setHandle(QVariant::fromValue(pair.second));
+ }
+ }
+}
+
// 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
@@ -1210,6 +1231,25 @@ void Renderer::sendTextureChangesToFrontend()
void Renderer::updateGLResources()
{
{
+ // Update active fence objects:
+ // - Destroy fences that have reached their signaled state
+ GLFenceManager *fenceManager = m_nodesManager->glFenceManager();
+ const auto end = fenceManager->end();
+ auto it = fenceManager->begin();
+ while (it != end) {
+ const GLFence fence = it.value();
+ if (m_submissionContext->wasSyncSignaled(fence)) {
+ // Fence was signaled, we delete it
+ // before removing the entry from the manager
+ m_submissionContext->deleteSync(fence);
+ it = fenceManager->erase(it);
+ } else {
+ ++it;
+ }
+ }
+ }
+
+ {
Profiling::GLTimeRecorder recorder(Profiling::BufferUpload);
const QVector<HBuffer> dirtyBufferHandles = std::move(m_dirtyBuffers);
for (const HBuffer &handle: dirtyBufferHandles) {
@@ -1275,8 +1315,13 @@ void Renderer::updateGLResources()
// Gather these information and store them to be distributed by a change next frame
const QNodeIdVector referenceTextureIds = glTextureManager->referencedTextureIds(glTexture);
// Store properties and referenceTextureIds
- if (info.wasUpdated)
- m_updatedTextureProperties.push_back({info.properties, 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});
+ }
}
}
}
@@ -1298,18 +1343,21 @@ void Renderer::updateTexture(Texture *texture)
return;
// For implementing unique, non-shared, non-cached textures.
- // for now, every texture is shared by default
+ // for now, every texture is shared by default except if:
+ // - texture is reference by a render attachment
+ // - texture is referencing a shared texture id
+ bool isUnique = texture->sharedTextureId() > 0;
- bool isUnique = false;
-
- // TO DO: Update the vector once per frame (or in a job)
- const QVector<HAttachment> activeRenderTargetOutputs = m_nodesManager->attachmentManager()->activeHandles();
- // A texture is unique if it's being reference by a render target output
- for (const HAttachment &attachmentHandle : activeRenderTargetOutputs) {
- RenderTargetOutput *attachment = m_nodesManager->attachmentManager()->data(attachmentHandle);
- if (attachment->textureUuid() == texture->peerId()) {
- isUnique = true;
- break;
+ if (!isUnique) {
+ // TO DO: Update the vector once per frame (or in a job)
+ const QVector<HAttachment> activeRenderTargetOutputs = m_nodesManager->attachmentManager()->activeHandles();
+ // A texture is unique if it's being reference by a render target output
+ for (const HAttachment &attachmentHandle : activeRenderTargetOutputs) {
+ RenderTargetOutput *attachment = m_nodesManager->attachmentManager()->data(attachmentHandle);
+ if (attachment->textureUuid() == texture->peerId()) {
+ isUnique = true;
+ break;
+ }
}
}
@@ -1356,6 +1404,9 @@ void Renderer::updateTexture(Texture *texture)
// we hold a reference to a unique or exclusive access to a shared texture
// we can thus modify the texture directly.
const Texture::DirtyFlags dirtyFlags = texture->dirtyFlags();
+ if (dirtyFlags.testFlag(Texture::DirtySharedTextureId) &&
+ !glTextureManager->setSharedTextureId(glTexture, texture->sharedTextureId()))
+ qWarning() << "[Qt3DRender::TextureNode] updateTexture: TextureImpl.setSharedTextureId failed, should be non-shared";
if (dirtyFlags.testFlag(Texture::DirtyProperties) &&
!glTextureManager->setProperties(glTexture, texture->properties()))
@@ -1389,6 +1440,7 @@ void Renderer::cleanupTexture(Qt3DCore::QNodeId cleanedUpTextureId)
glTextureManager->abandon(glTexture, cleanedUpTextureId);
}
+// Called by SubmitRenderView
void Renderer::downloadGLBuffers()
{
lookForDownloadableBuffers();
@@ -1473,6 +1525,45 @@ Renderer::ViewSubmissionResultData Renderer::submitRenderViews(const QVector<Ren
if (renderView->memoryBarrier() != QMemoryBarrier::None)
m_submissionContext->memoryBarrier(renderView->memoryBarrier());
+
+ // Insert Fence into command stream if needed
+ const Qt3DCore::QNodeIdVector insertFenceIds = renderView->insertFenceIds();
+ GLFenceManager *fenceManager = m_nodesManager->glFenceManager();
+ for (const Qt3DCore::QNodeId insertFenceId : insertFenceIds) {
+ // If the fence is not in the manager, then it hasn't been inserted
+ // into the command stream yet.
+ if (fenceManager->find(insertFenceId) == fenceManager->end()) {
+ // Insert fence into command stream
+ GLFence glFence = m_submissionContext->fenceSync();
+ // Record glFence
+ fenceManager->insert(insertFenceId, glFence);
+ // Add entry for notification changes to be sent
+ m_updatedSetFences.push_back({insertFenceId, glFence});
+ }
+ // If it is in the manager, then it hasn't been signaled yet,
+ // nothing we can do but try at the next frame
+ }
+
+ // Wait for fences if needed
+ const QVector<QWaitFenceData> waitFences = renderView->waitFences();
+ for (const QWaitFenceData &waitFence : waitFences) {
+ // TO DO
+ if (waitFence.handleType != QWaitFence::OpenGLFenceId) {
+ qWarning() << "WaitFence handleType should be OpenGLFenceId when using the Qt 3D OpenGL renderer";
+ continue;
+ }
+ GLFence fence = reinterpret_cast<GLFence>(waitFence.handle.value<qintptr>());
+ if (fence == nullptr)
+ continue;
+
+ if (waitFence.waitOnCPU) {
+ m_submissionContext->clientWaitSync(fence,
+ waitFence.timeout);
+ } else {
+ m_submissionContext->waitSync(fence);
+ }
+ }
+
// Note: the RenderStateSet is allocated once per RV if needed
// and it contains a list of StateVariant value types
RenderStateSet *renderViewStateSet = renderView->stateSet();
@@ -1638,6 +1729,33 @@ void Renderer::skipNextFrame()
m_submitRenderViewsSemaphore.release(1);
}
+// Jobs we may have to run even if no rendering will happen
+QVector<QAspectJobPtr> Renderer::preRenderingJobs()
+{
+ QVector<QAspectJobPtr> jobs;
+
+ // Do we need to notify any texture about property changes?
+ if (m_updatedTextureProperties.size() > 0)
+ jobs.push_back(m_sendTextureChangesToFrontendJob);
+
+ // Do we need to notify frontend about fence change?
+ if (m_updatedSetFences.size() > 0)
+ jobs.push_back(m_sendSetFenceHandlesToFrontendJob);
+
+ const QVector<Qt3DCore::QNodeId> pendingCaptureIds = takePendingRenderCaptureSendRequests();
+ if (pendingCaptureIds.size() > 0) {
+ m_sendRenderCaptureJob->setPendingCaptureRequests(pendingCaptureIds);
+ jobs.push_back(m_sendRenderCaptureJob);
+ }
+ if (m_sendBufferCaptureJob->hasRequests())
+ jobs.push_back(m_sendBufferCaptureJob);
+
+ jobs.append(pickBoundingVolumeJob());
+ jobs.append(rayCastingJob());
+
+ return jobs;
+}
+
// 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
@@ -1695,17 +1813,6 @@ QVector<Qt3DCore::QAspectJobPtr> Renderer::renderBinJobs()
renderBinJobs.push_back(m_updateLevelOfDetailJob);
renderBinJobs.push_back(m_cleanupJob);
- const QVector<Qt3DCore::QNodeId> pendingCaptureIds = takePendingRenderCaptureSendRequests();
- if (pendingCaptureIds.size() > 0) {
- m_sendRenderCaptureJob->setPendingCaptureRequests(pendingCaptureIds);
- renderBinJobs.push_back(m_sendRenderCaptureJob);
- }
-
- // Do we need to notify any texture about property changes?
- if (m_updatedTextureProperties.size() > 0)
- renderBinJobs.push_back(m_sendTextureChangesToFrontendJob);
-
- renderBinJobs.push_back(m_sendBufferCaptureJob);
renderBinJobs.append(bufferJobs);
// Jobs to prepare GL Resource upload
diff --git a/src/render/renderers/opengl/renderer/renderer_p.h b/src/render/renderers/opengl/renderer/renderer_p.h
index 23708e3e8..93d6fdbfa 100644
--- a/src/render/renderers/opengl/renderer/renderer_p.h
+++ b/src/render/renderers/opengl/renderer/renderer_p.h
@@ -80,6 +80,7 @@
#include <Qt3DRender/private/updateentitylayersjob_p.h>
#include <Qt3DRender/private/renderercache_p.h>
#include <Qt3DRender/private/texture_p.h>
+#include <Qt3DRender/private/glfence_p.h>
#include <QHash>
#include <QMatrix4x4>
@@ -96,6 +97,10 @@
#include <functional>
+#if defined(QT_BUILD_INTERNAL)
+class tst_Renderer;
+#endif
+
QT_BEGIN_NAMESPACE
class QSurface;
@@ -195,6 +200,7 @@ public:
bool shouldRender() override;
void skipNextFrame() override;
+ QVector<Qt3DCore::QAspectJobPtr> preRenderingJobs() override;
QVector<Qt3DCore::QAspectJobPtr> renderBinJobs() override;
Qt3DCore::QAspectJobPtr pickBoundingVolumeJob() override;
Qt3DCore::QAspectJobPtr rayCastingJob() override;
@@ -373,6 +379,7 @@ private:
GenericLambdaJobPtr<std::function<void ()>> m_vaoGathererJob;
GenericLambdaJobPtr<std::function<void ()>> m_textureGathererJob;
GenericLambdaJobPtr<std::function<void ()>> m_sendTextureChangesToFrontendJob;
+ GenericLambdaJobPtr<std::function<void ()>> m_sendSetFenceHandlesToFrontendJob;
IntrospectShadersJobPtr m_introspectShaderJob;
SynchronizerJobPtr m_syncTextureLoadingJob;
@@ -383,6 +390,7 @@ private:
void lookForDirtyTextures();
void reloadDirtyShaders();
void sendTextureChangesToFrontend();
+ void sendSetFenceHandlesToFrontend();
QMutex m_abandonedVaosMutex;
QVector<HVao> m_abandonedVaos;
@@ -391,7 +399,8 @@ private:
QVector<HBuffer> m_downloadableBuffers;
QVector<HShader> m_dirtyShaders;
QVector<HTexture> m_dirtyTextures;
- QVector<QPair<TextureProperties, Qt3DCore::QNodeIdVector>> m_updatedTextureProperties;
+ QVector<QPair<Texture::TextureUpdateInfo, Qt3DCore::QNodeIdVector>> m_updatedTextureProperties;
+ QVector<QPair<Qt3DCore::QNodeId, GLFence>> m_updatedSetFences;
bool m_ownedContext;
@@ -403,6 +412,10 @@ private:
friend class Qt3DRender::Debug::CommandExecuter;
#endif
+#ifdef QT_BUILD_INTERNAL
+ friend class ::tst_Renderer;
+#endif
+
QMetaObject::Connection m_contextConnection;
RendererCache m_cache;
};
diff --git a/src/render/renderers/opengl/renderer/renderview.cpp b/src/render/renderers/opengl/renderer/renderview.cpp
index 8e0c16938..3aa45c836 100644
--- a/src/render/renderers/opengl/renderer/renderview.cpp
+++ b/src/render/renderers/opengl/renderer/renderview.cpp
@@ -714,6 +714,11 @@ QVector<RenderCommand *> RenderView::buildComputeRenderCommands(const QVector<En
if ((computeJob = entity->renderComponent<ComputeCommand>()) != nullptr
&& computeJob->isEnabled()) {
+ // Note: if frameCount has reached 0 in the previous frame, isEnabled
+ // would be false
+ if (computeJob->runType() == QComputeCommand::Manual)
+ computeJob->updateFrameCount();
+
const Qt3DCore::QNodeId materialComponentId = entity->componentUuid<Material>();
const QVector<RenderPassParameterData> renderPassData = m_parameters.value(materialComponentId);
diff --git a/src/render/renderers/opengl/renderer/renderview_p.h b/src/render/renderers/opengl/renderer/renderview_p.h
index cb3c74917..7ebcdb6bd 100644
--- a/src/render/renderers/opengl/renderer/renderview_p.h
+++ b/src/render/renderers/opengl/renderer/renderview_p.h
@@ -65,6 +65,7 @@
#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>
@@ -174,6 +175,13 @@ public:
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 appendInsertFenceId(const Qt3DCore::QNodeId setFenceId) { m_insertFenceIds.push_back(setFenceId); }
+ // We prefix with get to avoid confusion when it is called
+ inline Qt3DCore::QNodeIdVector insertFenceIds() const { return m_insertFenceIds; }
+
+ inline void appendWaitFence(const QWaitFenceData &data) { m_waitFences.push_back(data); }
+ inline QVector<QWaitFenceData> waitFences() const { return m_waitFences; }
+
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; }
@@ -320,6 +328,8 @@ private:
bool m_frustumCulling:1;
int m_workGroups[3];
QMemoryBarrier::Operations m_memoryBarrier;
+ QVector<Qt3DCore::QNodeId> m_insertFenceIds;
+ QVector<QWaitFenceData> m_waitFences;
// We do not use pointers to RenderNodes or Drawable's here so that the
// render aspect is free to change the drawables on the next frame whilst
diff --git a/src/render/renderers/opengl/textures/gltexture.cpp b/src/render/renderers/opengl/textures/gltexture.cpp
index ce212de03..47a97c688 100644
--- a/src/render/renderers/opengl/textures/gltexture.cpp
+++ b/src/render/renderers/opengl/textures/gltexture.cpp
@@ -57,6 +57,11 @@
#include <Qt3DCore/qpropertynodeaddedchange.h>
#include <Qt3DCore/qpropertynoderemovedchange.h>
+#if !defined(QT_OPENGL_ES_2)
+#include <QOpenGLFunctions_3_1>
+#include <QOpenGLFunctions_4_5_Core>
+#endif
+
QT_BEGIN_NAMESPACE
using namespace Qt3DCore;
@@ -74,6 +79,7 @@ GLTexture::GLTexture(TextureDataManager *texDataMgr,
, m_textureDataManager(texDataMgr)
, m_textureImageDataManager(texImgDataMgr)
, m_dataFunctor(texGen)
+ , m_sharedTextureId(-1)
, m_pendingDataFunctor(nullptr)
, m_externalRendering(false)
{
@@ -181,39 +187,44 @@ GLTexture::TextureUpdateInfo GLTexture::createOrUpdateGLTexture()
m_properties.status = QAbstractTexture::Error;
- // on the first invocation in the render thread, make sure to
- // evaluate the texture data generator output
- // (this might change some property values)
- if (m_dataFunctor && !m_textureData) {
- const bool successfullyLoadedTextureData = loadTextureDataFromGenerator();
- if (successfullyLoadedTextureData) {
- setDirtyFlag(Properties, true);
- needUpload = true;
- } else {
- if (m_pendingDataFunctor != m_dataFunctor.get()) {
- qWarning() << "[Qt3DRender::GLTexture] No QTextureData generated from Texture Generator yet. Texture will be invalid for this frame";
- m_pendingDataFunctor = m_dataFunctor.get();
+ const bool hasSharedTextureId = m_sharedTextureId > 0;
+
+ // Only load texture data if we are not using a sharedTextureId
+ if (!hasSharedTextureId) {
+ // on the first invocation in the render thread, make sure to
+ // evaluate the texture data generator output
+ // (this might change some property values)
+ if (m_dataFunctor && !m_textureData) {
+ const bool successfullyLoadedTextureData = loadTextureDataFromGenerator();
+ if (successfullyLoadedTextureData) {
+ setDirtyFlag(Properties, true);
+ needUpload = true;
+ } else {
+ if (m_pendingDataFunctor != m_dataFunctor.get()) {
+ qWarning() << "[Qt3DRender::GLTexture] 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;
}
- textureInfo.properties.status = QAbstractTexture::Loading;
- return textureInfo;
}
- }
- // additional texture images may be defined through image data generators
- if (testDirtyFlag(TextureData)) {
- m_imageData.clear();
- loadTextureDataFromImages();
- needUpload = true;
- }
+ // additional texture images may be defined through image data generators
+ if (testDirtyFlag(TextureData)) {
+ m_imageData.clear();
+ loadTextureDataFromImages();
+ needUpload = true;
+ }
- // don't try to create the texture if the format was not set
- if (m_properties.format == QAbstractTexture::Automatic) {
- textureInfo.properties.status = QAbstractTexture::Error;
- return textureInfo;
+ // don't try to create the texture if the format was not set
+ if (m_properties.format == QAbstractTexture::Automatic) {
+ textureInfo.properties.status = QAbstractTexture::Error;
+ return textureInfo;
+ }
}
// if the properties changed, we need to re-allocate the texture
- if (testDirtyFlag(Properties)) {
+ if (testDirtyFlag(Properties) || testDirtyFlag(SharedTextureId)) {
delete m_gl;
m_gl = nullptr;
textureInfo.wasUpdated = true;
@@ -223,40 +234,48 @@ GLTexture::TextureUpdateInfo GLTexture::createOrUpdateGLTexture()
needUpload = true;
}
+ m_properties.status = QAbstractTexture::Ready;
- if (!m_gl) {
- m_gl = buildGLTexture();
+ if (hasSharedTextureId && testDirtyFlag(SharedTextureId)) {
+ // Update m_properties by doing introspection on the texture
+ introspectPropertiesFromSharedTextureId();
+ } else {
+ // We only build a QOpenGLTexture if we have no shared textureId set
if (!m_gl) {
- textureInfo.properties.status = QAbstractTexture::Error;
- return textureInfo;
- }
+ m_gl = buildGLTexture();
+ if (!m_gl) {
+ textureInfo.properties.status = QAbstractTexture::Error;
+ return textureInfo;
+ }
- m_gl->allocateStorage();
- if (!m_gl->isStorageAllocated()) {
- textureInfo.properties.status = QAbstractTexture::Error;
- return textureInfo;
+ m_gl->allocateStorage();
+ if (!m_gl->isStorageAllocated()) {
+ textureInfo.properties.status = QAbstractTexture::Error;
+ return textureInfo;
+ }
}
- }
- m_properties.status = QAbstractTexture::Ready;
- textureInfo.properties = m_properties;
- textureInfo.texture = m_gl;
+ textureInfo.texture = m_gl;
- // need to (re-)upload texture data?
- if (needUpload) {
- uploadGLTextureData();
- setDirtyFlag(TextureData, false);
- }
+ // need to (re-)upload texture data?
+ if (needUpload) {
+ uploadGLTextureData();
+ setDirtyFlag(TextureData, false);
+ }
- // need to set texture parameters?
- if (testDirtyFlag(Properties) || testDirtyFlag(Parameters)) {
- updateGLTextureParameters();
+ // need to set texture parameters?
+ if (testDirtyFlag(Properties) || testDirtyFlag(Parameters)) {
+ updateGLTextureParameters();
+ }
}
+ textureInfo.properties = m_properties;
+
// un-set properties and parameters. The TextureData flag might have been set by another thread
// in the meantime, so don't clear that.
setDirtyFlag(Properties, false);
setDirtyFlag(Parameters, false);
+ setDirtyFlag(SharedTextureId, false);
return textureInfo;
}
@@ -355,6 +374,14 @@ void GLTexture::setGenerator(const QTextureGeneratorPtr &generator)
}
}
+void GLTexture::setSharedTextureId(int textureId)
+{
+ if (m_sharedTextureId != textureId) {
+ m_sharedTextureId = textureId;
+ setDirtyFlag(SharedTextureId);
+ }
+}
+
// Return nullptr if
// - context cannot be obtained
// - texture hasn't yet been loaded
@@ -403,8 +430,8 @@ QOpenGLTexture *GLTexture::buildGLTexture()
// is written against GLES 1.0.
if (m_properties.format == QAbstractTexture::RGB8_ETC1) {
if ((ctx->isOpenGLES() && ctx->format().majorVersion() >= 3)
- || ctx->hasExtension(QByteArrayLiteral("GL_OES_compressed_ETC2_RGB8_texture"))
- || ctx->hasExtension(QByteArrayLiteral("GL_ARB_ES3_compatibility")))
+ || ctx->hasExtension(QByteArrayLiteral("GL_OES_compressed_ETC2_RGB8_texture"))
+ || ctx->hasExtension(QByteArrayLiteral("GL_ARB_ES3_compatibility")))
format = m_properties.format = QAbstractTexture::RGB8_ETC2;
}
@@ -414,14 +441,14 @@ QOpenGLTexture *GLTexture::buildGLTexture()
glTex->setSize(m_properties.width, m_properties.height, m_properties.depth);
// Set layers count if texture array
if (m_actualTarget == QAbstractTexture::Target1DArray ||
- m_actualTarget == QAbstractTexture::Target2DArray ||
- m_actualTarget == QAbstractTexture::Target2DMultisampleArray ||
- m_actualTarget == QAbstractTexture::TargetCubeMapArray) {
+ m_actualTarget == QAbstractTexture::Target2DArray ||
+ m_actualTarget == QAbstractTexture::Target2DMultisampleArray ||
+ m_actualTarget == QAbstractTexture::TargetCubeMapArray) {
glTex->setLayers(m_properties.layers);
}
if (m_actualTarget == QAbstractTexture::Target2DMultisample ||
- m_actualTarget == QAbstractTexture::Target2DMultisampleArray) {
+ m_actualTarget == QAbstractTexture::Target2DMultisampleArray) {
// Set samples count if multisampled texture
// (multisampled textures don't have mipmaps)
glTex->setSamples(m_properties.samples);
@@ -497,8 +524,8 @@ void GLTexture::updateGLTextureParameters()
{
m_gl->setWrapMode(QOpenGLTexture::DirectionS, static_cast<QOpenGLTexture::WrapMode>(m_parameters.wrapModeX));
if (m_actualTarget != QAbstractTexture::Target1D &&
- m_actualTarget != QAbstractTexture::Target1DArray &&
- m_actualTarget != QAbstractTexture::TargetBuffer)
+ m_actualTarget != QAbstractTexture::Target1DArray &&
+ m_actualTarget != QAbstractTexture::TargetBuffer)
m_gl->setWrapMode(QOpenGLTexture::DirectionT, static_cast<QOpenGLTexture::WrapMode>(m_parameters.wrapModeY));
if (m_actualTarget == QAbstractTexture::Target3D)
m_gl->setWrapMode(QOpenGLTexture::DirectionR, static_cast<QOpenGLTexture::WrapMode>(m_parameters.wrapModeZ));
@@ -512,6 +539,129 @@ void GLTexture::updateGLTextureParameters()
}
}
+void GLTexture::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+
+ QOpenGLFunctions_4_5_Core *gl5 = ctx->versionFunctions<QOpenGLFunctions_4_5_Core>();
+#ifdef GL_TEXTURE_TARGET
+ 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 Render
} // namespace Qt3DRender
diff --git a/src/render/renderers/opengl/textures/gltexture_p.h b/src/render/renderers/opengl/textures/gltexture_p.h
index ca9c0d5db..66f66926c 100644
--- a/src/render/renderers/opengl/textures/gltexture_p.h
+++ b/src/render/renderers/opengl/textures/gltexture_p.h
@@ -125,6 +125,7 @@ public:
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); }
@@ -204,14 +205,15 @@ protected:
void setProperties(const TextureProperties &props);
void setImages(const QVector<Image> &images);
void setGenerator(const QTextureGeneratorPtr &generator);
+ void setSharedTextureId(int textureId);
private:
enum DirtyFlag {
TextureData = 0x01, // one or more image generators have been executed, data needs uploading to GPU
Properties = 0x02, // texture needs to be (re-)created
- Parameters = 0x04 // texture parameters need to be (re-)set
-
+ Parameters = 0x04, // texture parameters need to be (re-)set
+ SharedTextureId = 0x08 // texture id from shared context
};
bool testDirtyFlag(DirtyFlag flag)
@@ -232,6 +234,7 @@ private:
void loadTextureDataFromImages();
void uploadGLTextureData();
void updateGLTextureParameters();
+ void introspectPropertiesFromSharedTextureId();
void destroyResources();
bool m_unique;
@@ -257,6 +260,7 @@ private:
QTextureDataPtr m_textureData;
QVector<QTextureImageDataPtr> m_imageData;
+ int m_sharedTextureId;
bool m_externalRendering;
};
diff --git a/src/render/texture/apitexturemanager_p.h b/src/render/texture/apitexturemanager_p.h
index 58e6e6420..79dc9af94 100644
--- a/src/render/texture/apitexturemanager_p.h
+++ b/src/render/texture/apitexturemanager_p.h
@@ -257,6 +257,19 @@ public:
return true;
}
+ // Change the texture's referenced texture Id from a shared context
+ bool setSharedTextureId(APITexture *tex, int textureId)
+ {
+ Q_ASSERT(tex);
+
+ if (isShared(tex))
+ return false;
+
+ tex->setSharedTextureId(textureId);
+ m_updatedTextures.push_back(tex);
+ return true;
+ }
+
// Retrieves abandoned textures. This should be regularly called from the OpenGL thread
// to make sure needed GL resources are de-allocated.
QVector<APITexture*> takeAbandonedTextures()
@@ -344,6 +357,7 @@ private:
newTex->setProperties(node->properties());
newTex->setParameters(node->parameters());
newTex->setImages(texImgs);
+ newTex->setSharedTextureId(node->sharedTextureId());
m_updatedTextures.push_back(newTex);
diff --git a/src/render/texture/qabstracttexture.cpp b/src/render/texture/qabstracttexture.cpp
index 03746620e..17c4a8f11 100644
--- a/src/render/texture/qabstracttexture.cpp
+++ b/src/render/texture/qabstracttexture.cpp
@@ -66,6 +66,9 @@ QAbstractTexturePrivate::QAbstractTexturePrivate()
, m_comparisonMode(QAbstractTexture::CompareNone)
, m_layers(1)
, m_samples(1)
+ , m_sharedTextureId(-1)
+ , m_handleType(QAbstractTexture::NoHandle)
+ , m_handle(QVariant())
{
}
@@ -561,6 +564,34 @@ void QAbstractTexture::setStatus(Status status)
}
/*!
+ * \internal
+ */
+void QAbstractTexture::setHandle(const QVariant &handle)
+{
+ Q_D(QAbstractTexture);
+ if (d->m_handle != handle) {
+ d->m_handle = handle;
+ const bool blocked = blockNotifications(true);
+ emit handleChanged(handle);
+ blockNotifications(blocked);
+ }
+}
+
+/*!
+ * \internal
+ */
+void QAbstractTexture::setHandleType(QAbstractTexture::HandleType type)
+{
+ Q_D(QAbstractTexture);
+ if (d->m_handleType != type) {
+ d->m_handleType = type;
+ const bool blocked = blockNotifications(true);
+ emit handleTypeChanged(type);
+ blockNotifications(blocked);
+ }
+}
+
+/*!
* \return the current status of the texture provider.
*/
QAbstractTexture::Status QAbstractTexture::status() const
@@ -886,6 +917,55 @@ QTextureGeneratorPtr QAbstractTexture::dataGenerator() const
return d->m_dataFunctor;
}
+/*!
+ * \property Qt3DRender::QAbstractTexture::handleType
+ *
+ * Holds the current texture handle type.
+ */
+
+/*!
+ * \qmlproperty handleType
+ *
+ * Holds the current texture handle type.
+ */
+
+/*!
+ * \return the current texture handle type.
+ * \since 5.12
+ */
+QAbstractTexture::HandleType QAbstractTexture::handleType() const
+{
+ Q_D(const QAbstractTexture);
+ return d->m_handleType;
+}
+
+
+/*!
+ * \property Qt3DRender::QAbstractTexture::handle
+ *
+ * Holds the current texture handle, if Qt 3D is using the OpenGL renderer,
+ * handle is a texture id integer.
+ */
+
+/*!
+ * \qmlproperty handle
+ *
+ * Holds the current texture handle, if Qt 3D is using the OpenGL renderer,
+ * handle is a texture id integer.
+ */
+
+/*!
+ * \return the current texture handle, if Qt 3D is using the OpenGL renderer,
+ * handle is a texture id integer.
+ *
+ * \since 5.12
+ */
+QVariant QAbstractTexture::handle() const
+{
+ Q_D(const QAbstractTexture);
+ return d->m_handle;
+}
+
Qt3DCore::QNodeCreatedChangeBasePtr QAbstractTexture::createNodeCreationChange() const
{
auto creationChange = Qt3DCore::QNodeCreatedChangePtr<QAbstractTextureData>::create(this);
@@ -909,6 +989,7 @@ Qt3DCore::QNodeCreatedChangeBasePtr QAbstractTexture::createNodeCreationChange()
data.layers = d->m_layers;
data.samples = d->m_samples;
data.dataFunctor = d->m_dataFunctor;
+ data.sharedTextureId = d->m_sharedTextureId;
return creationChange;
}
@@ -941,6 +1022,10 @@ void QAbstractTexture::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &change)
bool blocked = blockNotifications(true);
setStatus(static_cast<QAbstractTexture::Status>(propertyChange->value().toInt()));
blockNotifications(blocked);
+ } else if (propertyChange->propertyName() == QByteArrayLiteral("handleType")) {
+ setHandleType(static_cast<QAbstractTexture::HandleType>(propertyChange->value().toInt()));
+ } else if (propertyChange->propertyName() == QByteArrayLiteral("handle")) {
+ setHandle(propertyChange->value());
}
// TODO handle target changes, it's a CONSTANT property but can be affected by loader
break;
diff --git a/src/render/texture/qabstracttexture.h b/src/render/texture/qabstracttexture.h
index f17633710..e6833ffb4 100644
--- a/src/render/texture/qabstracttexture.h
+++ b/src/render/texture/qabstracttexture.h
@@ -73,6 +73,8 @@ class QT3DRENDERSHARED_EXPORT QAbstractTexture : public Qt3DCore::QNode
Q_PROPERTY(ComparisonMode comparisonMode READ comparisonMode WRITE setComparisonMode NOTIFY comparisonModeChanged)
Q_PROPERTY(int layers READ layers WRITE setLayers NOTIFY layersChanged)
Q_PROPERTY(int samples READ samples WRITE setSamples NOTIFY samplesChanged)
+ Q_PROPERTY(HandleType handleType READ handleType NOTIFY handleTypeChanged REVISION 13)
+ Q_PROPERTY(QVariant handle READ handle NOTIFY handleChanged REVISION 13)
public:
@@ -268,6 +270,12 @@ public:
};
Q_ENUM(ComparisonMode) // LCOV_EXCL_LINE
+ enum HandleType {
+ NoHandle,
+ OpenGLTextureId
+ };
+ Q_ENUM(HandleType) // LCOV_EXCL_LINE
+
~QAbstractTexture();
Target target() const;
@@ -298,6 +306,8 @@ public:
int layers() const;
int samples() const;
QTextureGeneratorPtr dataGenerator() const;
+ HandleType handleType() const;
+ QVariant handle() const;
public Q_SLOTS:
void setFormat(TextureFormat format);
@@ -327,6 +337,8 @@ Q_SIGNALS:
void comparisonModeChanged(ComparisonMode comparisonMode);
void layersChanged(int layers);
void samplesChanged(int samples);
+ void handleTypeChanged(HandleType handleType);
+ void handleChanged(QVariant handle);
protected:
explicit QAbstractTexture(Qt3DCore::QNode *parent = nullptr);
@@ -335,6 +347,8 @@ protected:
void sceneChangeEvent(const Qt3DCore::QSceneChangePtr &change) override;
void setStatus(Status status);
+ void setHandle(const QVariant &handle);
+ void setHandleType(HandleType type);
private:
Q_DECLARE_PRIVATE(QAbstractTexture)
diff --git a/src/render/texture/qabstracttexture_p.h b/src/render/texture/qabstracttexture_p.h
index a27ae3729..0ad1d0737 100644
--- a/src/render/texture/qabstracttexture_p.h
+++ b/src/render/texture/qabstracttexture_p.h
@@ -87,6 +87,11 @@ public :
int m_layers;
int m_samples;
+ int m_sharedTextureId;
+
+ QAbstractTexture::HandleType m_handleType;
+ QVariant m_handle;
+
QTextureGeneratorPtr dataFunctor() const;
void setDataFunctor(const QTextureGeneratorPtr &generator);
@@ -113,6 +118,7 @@ struct QAbstractTextureData
Qt3DCore::QNodeIdVector textureImageIds;
int layers;
int samples;
+ int sharedTextureId;
QTextureGeneratorPtr dataFunctor;
};
diff --git a/src/render/texture/qtexture.cpp b/src/render/texture/qtexture.cpp
index 029e47817..cf93f872f 100644
--- a/src/render/texture/qtexture.cpp
+++ b/src/render/texture/qtexture.cpp
@@ -1475,6 +1475,69 @@ bool QTextureFromSourceGenerator::isMirrored() const
return m_mirrored;
}
+/*!
+ * \class QSharedGLTexture
+ * \brief Allows to use a textureId from a separate OpenGL context in a Qt 3D scene.
+ *
+ * Depending on the rendering mode used by Qt 3D, the shared context will either be:
+ * \list
+ * \li qt_gl_global_share_context when letting Qt 3D drive the rendering. When
+ * setting the attribute Qt::AA_ShareOpenGLContexts on the QApplication class,
+ * this will automatically make QOpenGLWidget instances have their context shared
+ * with qt_gl_global_share_context.
+ * \li the shared context from the QtQuick scene. You might have to subclass
+ * QWindow or use QtQuickRenderControl to have control over what that shared
+ * context is though as of 5.13 it is qt_gl_global_share_context.
+ * \endlist
+ *
+ * \since 5.13
+ *
+ * Any 3rd party engine that shares its context with the Qt 3D renderer can now
+ * provide texture ids that will be referenced by the Qt 3D texture.
+ *
+ * You can omit specifying the texture properties, Qt 3D will try at runtime to
+ * determine what they are. If you know them, you can of course provide them,
+ * avoid additional work for Qt 3D.
+ *
+ * Keep in mind that if you are using custom materials and shaders, you need to
+ * specify the correct sampler type to be used.
+ */
+
+QSharedGLTexture::QSharedGLTexture(Qt3DCore::QNode *parent)
+ : QAbstractTexture(parent)
+{
+ QAbstractTexturePrivate *d = static_cast<QAbstractTexturePrivate *>(Qt3DCore::QNodePrivate::get(this));
+ d->m_target = TargetAutomatic;
+}
+
+QSharedGLTexture::~QSharedGLTexture()
+{
+}
+
+/*!
+ * \qmlproperty textureId
+ *
+ * The OpenGL texture id value that you want Qt3D to gain access to.
+ */
+/*!
+ *\property Qt3DRender::QSharedGLTexture::textureId
+ *
+ * The OpenGL texture id value that you want Qt3D to gain access to.
+ */
+int QSharedGLTexture::textureId() const
+{
+ return static_cast<QAbstractTexturePrivate *>(d_ptr.get())->m_sharedTextureId;
+}
+
+void QSharedGLTexture::setTextureId(int id)
+{
+ QAbstractTexturePrivate *d = static_cast<QAbstractTexturePrivate *>(Qt3DCore::QNodePrivate::get(this));
+ if (d->m_sharedTextureId != id) {
+ d->m_sharedTextureId = id;
+ emit textureIdChanged(id);
+ }
+}
+
} // namespace Qt3DRender
QT_END_NAMESPACE
diff --git a/src/render/texture/qtexture.h b/src/render/texture/qtexture.h
index 24d19fbcf..991725de2 100644
--- a/src/render/texture/qtexture.h
+++ b/src/render/texture/qtexture.h
@@ -169,6 +169,23 @@ private:
Q_DECLARE_PRIVATE(QTextureLoader)
};
+class QT3DRENDERSHARED_EXPORT QSharedGLTexture : public QAbstractTexture
+{
+ Q_OBJECT
+ Q_PROPERTY(int textureId READ textureId WRITE setTextureId NOTIFY textureIdChanged)
+public:
+ explicit QSharedGLTexture(Qt3DCore::QNode *parent = nullptr);
+ ~QSharedGLTexture();
+
+ int textureId() const;
+
+public Q_SLOTS:
+ void setTextureId(int id);
+
+Q_SIGNALS:
+ void textureIdChanged(int textureId);
+};
+
} // namespace Qt3DRender
QT_END_NAMESPACE
diff --git a/src/render/texture/texture.cpp b/src/render/texture/texture.cpp
index 749b85802..17fd47be3 100644
--- a/src/render/texture/texture.cpp
+++ b/src/render/texture/texture.cpp
@@ -115,6 +115,7 @@ void Texture::cleanup()
// texture is being referenced by a shared API specific texture (GLTexture)
m_dataFunctor.reset();
m_textureImageIds.clear();
+ m_sharedTextureId = -1;
// set default values
m_properties = {};
@@ -181,6 +182,9 @@ void Texture::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e)
dirty = DirtyProperties;
} else if (propertyChange->propertyName() == QByteArrayLiteral("generator")) {
setDataGenerator(propertyChange->value().value<QTextureGeneratorPtr>());
+ } else if (propertyChange->propertyName() == QByteArrayLiteral("textureId")) {
+ m_sharedTextureId = propertyChange->value().toInt();
+ dirty = DirtySharedTextureId;
}
}
break;
@@ -219,7 +223,7 @@ void Texture::setDataGenerator(const QTextureGeneratorPtr &generator)
// Called by sendTextureChangesToFrontendJob once GLTexture and sharing
// has been performed
-void Texture::updatePropertiesAndNotify(const TextureProperties &properties)
+void Texture::updatePropertiesAndNotify(const TextureUpdateInfo &updateInfo)
{
// If we are Dirty, some property has changed and the properties we have
// received are potentially already outdated
@@ -228,57 +232,73 @@ void Texture::updatePropertiesAndNotify(const TextureProperties &properties)
// Note we don't update target has it is constant for frontend nodes
- if (properties.width != m_properties.width) {
- m_properties.width = properties.width;
+ if (updateInfo.properties.width != m_properties.width) {
+ m_properties.width = updateInfo.properties.width;
auto change = Qt3DCore::QPropertyUpdatedChangePtr::create(peerId());
change->setDeliveryFlags(Qt3DCore::QSceneChange::Nodes);
change->setPropertyName("width");
- change->setValue(properties.width);
+ change->setValue(updateInfo.properties.width);
notifyObservers(change);
}
- if (properties.height != m_properties.height) {
- m_properties.height = properties.height;
+ if (updateInfo.properties.height != m_properties.height) {
+ m_properties.height = updateInfo.properties.height;
auto change = Qt3DCore::QPropertyUpdatedChangePtr::create(peerId());
change->setDeliveryFlags(Qt3DCore::QSceneChange::Nodes);
change->setPropertyName("height");
- change->setValue(properties.height);
+ change->setValue(updateInfo.properties.height);
notifyObservers(change);
}
- if (properties.depth != m_properties.depth) {
- m_properties.depth = properties.depth;
+ if (updateInfo.properties.depth != m_properties.depth) {
+ m_properties.depth = updateInfo.properties.depth;
auto change = Qt3DCore::QPropertyUpdatedChangePtr::create(peerId());
change->setDeliveryFlags(Qt3DCore::QSceneChange::Nodes);
change->setPropertyName("depth");
- change->setValue(properties.depth);
+ change->setValue(updateInfo.properties.depth);
notifyObservers(change);
}
- if (properties.layers != m_properties.layers) {
- m_properties.layers = properties.layers;
+ if (updateInfo.properties.layers != m_properties.layers) {
+ m_properties.layers = updateInfo.properties.layers;
auto change = Qt3DCore::QPropertyUpdatedChangePtr::create(peerId());
change->setDeliveryFlags(Qt3DCore::QSceneChange::Nodes);
change->setPropertyName("layers");
- change->setValue(properties.layers);
+ change->setValue(updateInfo.properties.layers);
notifyObservers(change);
}
- if (properties.format != m_properties.format) {
- m_properties.format = properties.format;
+ if (updateInfo.properties.format != m_properties.format) {
+ m_properties.format = updateInfo.properties.format;
auto change = Qt3DCore::QPropertyUpdatedChangePtr::create(peerId());
change->setDeliveryFlags(Qt3DCore::QSceneChange::Nodes);
change->setPropertyName("format");
- change->setValue(properties.format);
+ change->setValue(updateInfo.properties.format);
notifyObservers(change);
}
- if (properties.status != m_properties.status) {
- m_properties.status = properties.status;
+ if (updateInfo.properties.status != m_properties.status) {
+ m_properties.status = updateInfo.properties.status;
auto change = Qt3DCore::QPropertyUpdatedChangePtr::create(peerId());
change->setDeliveryFlags(Qt3DCore::QSceneChange::Nodes);
change->setPropertyName("status");
- change->setValue(properties.status);
+ change->setValue(updateInfo.properties.status);
+ notifyObservers(change);
+ }
+
+ {
+ auto change = Qt3DCore::QPropertyUpdatedChangePtr::create(peerId());
+ change->setDeliveryFlags(Qt3DCore::QSceneChange::Nodes);
+ change->setPropertyName("handleType");
+ change->setValue(updateInfo.handleType);
+ notifyObservers(change);
+ }
+
+ {
+ auto change = Qt3DCore::QPropertyUpdatedChangePtr::create(peerId());
+ change->setDeliveryFlags(Qt3DCore::QSceneChange::Nodes);
+ change->setPropertyName("handle");
+ change->setValue(updateInfo.handle);
notifyObservers(change);
}
}
@@ -315,11 +335,14 @@ void Texture::initializeFromPeer(const Qt3DCore::QNodeCreatedChangeBasePtr &chan
m_parameters.comparisonFunction = data.comparisonFunction;
m_parameters.comparisonMode = data.comparisonMode;
m_dataFunctor = data.dataFunctor;
+ m_sharedTextureId = data.sharedTextureId;
for (const QNodeId imgId : data.textureImageIds)
addTextureImage(imgId);
addDirtyFlag(DirtyFlags(DirtyImageGenerators|DirtyProperties|DirtyParameters));
+ if (m_sharedTextureId > 0)
+ addDirtyFlag(DirtySharedTextureId);
}
diff --git a/src/render/texture/texture_p.h b/src/render/texture/texture_p.h
index 86c49f695..c60e90181 100644
--- a/src/render/texture/texture_p.h
+++ b/src/render/texture/texture_p.h
@@ -137,10 +137,18 @@ public:
DirtyProperties = 0x1,
DirtyParameters = 0x2,
DirtyImageGenerators = 0x4,
- DirtyDataGenerator = 0x8
+ DirtyDataGenerator = 0x8,
+ DirtySharedTextureId = 0x16
};
Q_DECLARE_FLAGS(DirtyFlags, DirtyFlag)
+ struct TextureUpdateInfo
+ {
+ TextureProperties properties;
+ QVariant handle;
+ QAbstractTexture::HandleType handleType;
+ };
+
void addDirtyFlag(DirtyFlags flags);
DirtyFlags dirtyFlags();
void unsetDirty();
@@ -155,9 +163,10 @@ public:
inline const TextureParameters& parameters() const { return m_parameters; }
inline const Qt3DCore::QNodeIdVector textureImageIds() const { return m_textureImageIds; }
inline const QTextureGeneratorPtr& dataGenerator() const { return m_dataFunctor; }
+ inline int sharedTextureId() const { return m_sharedTextureId; }
void setDataGenerator(const QTextureGeneratorPtr &generator);
- void updatePropertiesAndNotify(const TextureProperties &propreties);
+ void updatePropertiesAndNotify(const TextureUpdateInfo &updateInfo);
bool isValid(TextureImageManager *manager) const;
private:
void initializeFromPeer(const Qt3DCore::QNodeCreatedChangeBasePtr &change) final;
@@ -165,6 +174,7 @@ private:
DirtyFlags m_dirty;
TextureProperties m_properties;
TextureParameters m_parameters;
+ int m_sharedTextureId;
QTextureGeneratorPtr m_dataFunctor;
Qt3DCore::QNodeIdVector m_textureImageIds;
diff --git a/tests/auto/animation/findrunningclipanimatorsjob/tst_findrunningclipanimatorsjob.cpp b/tests/auto/animation/findrunningclipanimatorsjob/tst_findrunningclipanimatorsjob.cpp
index ce37ffc76..50c0eb524 100644
--- a/tests/auto/animation/findrunningclipanimatorsjob/tst_findrunningclipanimatorsjob.cpp
+++ b/tests/auto/animation/findrunningclipanimatorsjob/tst_findrunningclipanimatorsjob.cpp
@@ -120,14 +120,14 @@ private Q_SLOTS:
QTest::addColumn<QVector<HClipAnimator>>("dirtyClipAnimators");
QTest::addColumn<MappingDataResults>("expectedResults");
- Handler *handler;
- AnimationClip *clip;
- ClipAnimator *animator;
- QVector<HClipAnimator> dirtyClipAnimators;
- ChannelMapper *channelMapper;
- MappingDataResults expectedResults;
{
+ Handler *handler;
+ AnimationClip *clip;
+ ClipAnimator *animator;
+ QVector<HClipAnimator> dirtyClipAnimators;
+ ChannelMapper *channelMapper;
+ MappingDataResults expectedResults;
handler = new Handler();
clip = createAnimationClipLoader(handler, QUrl("qrc:/clip1.json"));
@@ -150,6 +150,7 @@ private Q_SLOTS:
channelMapper = createChannelMapper(handler, QVector<Qt3DCore::QNodeId>() << channelMapping->peerId());
animator->setMapperId(channelMapper->peerId());
animator->setRunning(true); // Has to be marked as running for the job to process it
+ animator->setEnabled(true); // Has to be marked as enabled for the job to process it
const ComponentIndices locationIndices = { 0, 1, 2 };
MappingData expectedMapping;
@@ -160,9 +161,46 @@ private Q_SLOTS:
expectedResults.insert(animator, QVector<MappingData>() << expectedMapping);
QTest::newRow("single mapping")
- << handler
- << dirtyClipAnimators
- << expectedResults;
+ << handler
+ << dirtyClipAnimators
+ << expectedResults;
+ }
+
+ {
+ Handler *handler;
+ AnimationClip *clip;
+ ClipAnimator *animator;
+ QVector<HClipAnimator> dirtyClipAnimators;
+ ChannelMapper *channelMapper;
+ MappingDataResults expectedResults;
+ handler = new Handler();
+ clip = createAnimationClipLoader(handler, QUrl("qrc:/clip1.json"));
+
+ const qint64 globalStartTimeNS = 0;
+ const int loops = 1;
+ animator = createClipAnimator(handler, globalStartTimeNS, loops);
+ animator->setClipId(clip->peerId());
+ dirtyClipAnimators = (QVector<HClipAnimator>()
+ << handler->clipAnimatorManager()->getOrAcquireHandle(animator->peerId()));
+
+ auto channelMapping = createChannelMapping(handler,
+ QLatin1String("Location"),
+ Qt3DCore::QNodeId::createId(),
+ QLatin1String("translation"),
+ "translation",
+ static_cast<int>(QVariant::Vector3D));
+ QVector<ChannelMapping *> channelMappings;
+ channelMappings.push_back(channelMapping);
+
+ channelMapper = createChannelMapper(handler, QVector<Qt3DCore::QNodeId>() << channelMapping->peerId());
+ animator->setMapperId(channelMapper->peerId());
+ animator->setRunning(true); // Has to be marked as running for the job to process it
+ animator->setEnabled(false); // Has to be marked as enabled for the job to process it
+
+ QTest::newRow("disabled animator")
+ << handler
+ << dirtyClipAnimators
+ << expectedResults;
}
}
diff --git a/tests/auto/core/qentity/tst_qentity.cpp b/tests/auto/core/qentity/tst_qentity.cpp
index e27cd1fc9..d7847342b 100644
--- a/tests/auto/core/qentity/tst_qentity.cpp
+++ b/tests/auto/core/qentity/tst_qentity.cpp
@@ -53,6 +53,8 @@ private slots:
void addComponentsSeveralParentsSingleAggregations();
void addComponentsSeveralParentsSeveralAggregations();
+ void retrieveSingleComponent();
+
void removeComponentSingleParentSingleAggregation();
void removeComponentSingleParentSeveralAggregations();
void removeComponentsSeveralParentsSingleAggreation();
@@ -76,6 +78,14 @@ public:
{}
};
+class MyQ2Component : public Qt3DCore::QComponent
+{
+ Q_OBJECT
+public:
+ explicit MyQ2Component(Qt3DCore::QNode *parent = 0)
+ : QComponent(parent)
+ {}
+};
class MyEntity : public Qt3DCore::QEntity
{
@@ -504,6 +514,29 @@ void tst_Entity::removeComponentsSeveralParentsSeveralAggregations()
QCOMPARE(comp3->entities().size(), 0);
}
+void tst_Entity::retrieveSingleComponent()
+{
+ // GIVEN
+ QScopedPointer<Qt3DCore::QEntity> entity1(new QEntity());
+
+ MyQComponent *comp1 = new MyQComponent(entity1.data());
+ MyQComponent *comp2 = new MyQComponent(entity1.data());
+ QCoreApplication::processEvents();
+ entity1->addComponent(comp1);
+ entity1->addComponent(comp2);
+
+ // WHEN
+ QVector<MyQComponent*> myQComponentsInEntity = entity1->componentsOfType<MyQComponent>();
+ QVector<MyQ2Component*> myQ2ComponentsInEntity = entity1->componentsOfType<MyQ2Component>();
+
+ // THEN
+ QVERIFY(myQComponentsInEntity.size() == 2);
+ QVERIFY(myQComponentsInEntity[0] == comp1);
+ QVERIFY(myQComponentsInEntity[1] == comp2);
+
+ QVERIFY(myQ2ComponentsInEntity.size() == 0);
+}
+
void tst_Entity::addSeveralTimesSameComponent()
{
// GIVEN
diff --git a/tests/auto/core/threadpooler/tst_threadpooler.cpp b/tests/auto/core/threadpooler/tst_threadpooler.cpp
index bc4552c66..98cbbc92d 100644
--- a/tests/auto/core/threadpooler/tst_threadpooler.cpp
+++ b/tests/auto/core/threadpooler/tst_threadpooler.cpp
@@ -332,13 +332,13 @@ public:
m_globalAtomic.fetchAndAddOrdered(qPow(3, index));
}
- int globalAtomicValue() const
+ quint64 globalAtomicValue() const
{
return m_globalAtomic.load();
}
private:
- QAtomicInt m_globalAtomic;
+ QAtomicInteger<quint64> m_globalAtomic;
QAtomicInt m_currentIndex;
};
@@ -353,7 +353,7 @@ void tst_ThreadPooler::perThreadUniqueCall()
// GIVEN
PerThreadUniqueTester tester;
const int maxThreads = QThread::idealThreadCount();
- int maxValue = 0;
+ quint64 maxValue = 0;
for (int i = 0; i < maxThreads; ++i) {
maxValue += qPow(3, i);
}
diff --git a/tests/auto/quick3d/3drender/3drender.qml b/tests/auto/quick3d/3drender/3drender.qml
index 112031282..c20ce6c45 100644
--- a/tests/auto/quick3d/3drender/3drender.qml
+++ b/tests/auto/quick3d/3drender/3drender.qml
@@ -30,6 +30,7 @@
import Qt3D.Render 2.0 as QQ3Render20
import Qt3D.Render 2.1 as QQ3Render21
import Qt3D.Render 2.10 as QQ3Render210
+import Qt3D.Render 2.13 as QQ3Render213
import QtQuick 2.0
Item {
@@ -79,6 +80,9 @@ Item {
QQ3Render20.TextureLoader {} //Qt3DRender::QTextureLoader, Qt3DRender::Render::Quick::Quick3DTextureExtension
//QQ3Render20.QAbstractTextureImage // (uncreatable) Qt3DRender::QAbstractTextureImage
QQ3Render20.TextureImage {} //Qt3DRender::QTextureImage
+ QQ3Render213.SharedGLTexture {} //Qt3DRender::QSharedGLTexture
+ QQ3Render213.WaitFence {} //Qt3DRender::QWaitFence
+ QQ3Render213.SetFence {} //Qt3DRender::QSetFence
// Geometry
QQ3Render20.Attribute {} //Qt3DRender::QAttribute
diff --git a/tests/auto/render/blitframebuffer/tst_blitframebuffer.cpp b/tests/auto/render/blitframebuffer/tst_blitframebuffer.cpp
index 26c43b5c2..9232aa70f 100644
--- a/tests/auto/render/blitframebuffer/tst_blitframebuffer.cpp
+++ b/tests/auto/render/blitframebuffer/tst_blitframebuffer.cpp
@@ -118,6 +118,8 @@ private Q_SLOTS:
// THEN
QCOMPARE(backendBlitFramebuffer.isEnabled(), newValue);
+ QVERIFY(renderer.dirtyBits() & Qt3DRender::Render::AbstractRenderer::FrameGraphDirty);
+ renderer.clearDirtyBits(Qt3DRender::Render::AbstractRenderer::AllDirty);
}
{
// WHEN
@@ -130,6 +132,8 @@ private Q_SLOTS:
// THEN
QCOMPARE(backendBlitFramebuffer.sourceRenderTargetId(), newValue);
+ QVERIFY(renderer.dirtyBits() & Qt3DRender::Render::AbstractRenderer::FrameGraphDirty);
+ renderer.clearDirtyBits(Qt3DRender::Render::AbstractRenderer::AllDirty);
}
{
// WHEN
@@ -142,6 +146,8 @@ private Q_SLOTS:
// THEN
QCOMPARE(backendBlitFramebuffer.destinationRenderTargetId(), newValue);
+ QVERIFY(renderer.dirtyBits() & Qt3DRender::Render::AbstractRenderer::FrameGraphDirty);
+ renderer.clearDirtyBits(Qt3DRender::Render::AbstractRenderer::AllDirty);
}
{
// WHEN
@@ -153,6 +159,8 @@ private Q_SLOTS:
// THEN
QCOMPARE(backendBlitFramebuffer.sourceRect(), newValue);
+ QVERIFY(renderer.dirtyBits() & Qt3DRender::Render::AbstractRenderer::FrameGraphDirty);
+ renderer.clearDirtyBits(Qt3DRender::Render::AbstractRenderer::AllDirty);
}
{
// WHEN
@@ -164,6 +172,8 @@ private Q_SLOTS:
// THEN
QCOMPARE(backendBlitFramebuffer.destinationRect(), newValue);
+ QVERIFY(renderer.dirtyBits() & Qt3DRender::Render::AbstractRenderer::FrameGraphDirty);
+ renderer.clearDirtyBits(Qt3DRender::Render::AbstractRenderer::AllDirty);
}
{
// WHEN
@@ -175,6 +185,8 @@ private Q_SLOTS:
// THEN
QCOMPARE(backendBlitFramebuffer.sourceAttachmentPoint(), newValue);
+ QVERIFY(renderer.dirtyBits() & Qt3DRender::Render::AbstractRenderer::FrameGraphDirty);
+ renderer.clearDirtyBits(Qt3DRender::Render::AbstractRenderer::AllDirty);
}
{
// WHEN
@@ -186,6 +198,8 @@ private Q_SLOTS:
// THEN
QCOMPARE(backendBlitFramebuffer.destinationAttachmentPoint(), newValue);
+ QVERIFY(renderer.dirtyBits() & Qt3DRender::Render::AbstractRenderer::FrameGraphDirty);
+ renderer.clearDirtyBits(Qt3DRender::Render::AbstractRenderer::AllDirty);
}
}
diff --git a/tests/auto/render/commons/testrenderer.h b/tests/auto/render/commons/testrenderer.h
index 466cebe14..f19b3211b 100644
--- a/tests/auto/render/commons/testrenderer.h
+++ b/tests/auto/render/commons/testrenderer.h
@@ -57,6 +57,7 @@ public:
bool isRunning() const override { return true; }
bool shouldRender() override { return true; }
void skipNextFrame() override {}
+ QVector<Qt3DCore::QAspectJobPtr> preRenderingJobs() override { return QVector<Qt3DCore::QAspectJobPtr>(); }
QVector<Qt3DCore::QAspectJobPtr> renderBinJobs() override { return QVector<Qt3DCore::QAspectJobPtr>(); }
Qt3DCore::QAspectJobPtr pickBoundingVolumeJob() override { return Qt3DCore::QAspectJobPtr(); }
Qt3DCore::QAspectJobPtr rayCastingJob() override { return Qt3DCore::QAspectJobPtr(); }
diff --git a/tests/auto/render/computecommand/tst_computecommand.cpp b/tests/auto/render/computecommand/tst_computecommand.cpp
index 13688d299..1c8380f73 100644
--- a/tests/auto/render/computecommand/tst_computecommand.cpp
+++ b/tests/auto/render/computecommand/tst_computecommand.cpp
@@ -31,9 +31,11 @@
#include <Qt3DRender/qcomputecommand.h>
#include <Qt3DRender/private/qcomputecommand_p.h>
#include <Qt3DRender/private/computecommand_p.h>
+#include <Qt3DCore/private/qbackendnode_p.h>
#include <Qt3DCore/qpropertyupdatedchange.h>
#include "qbackendnodetester.h"
#include "testrenderer.h"
+#include "testpostmanarbiter.h"
class tst_ComputeCommand : public Qt3DCore::QBackendNodeTester
{
@@ -52,6 +54,8 @@ private Q_SLOTS:
QCOMPARE(backendComputeCommand.x(), 1);
QCOMPARE(backendComputeCommand.y(), 1);
QCOMPARE(backendComputeCommand.z(), 1);
+ QCOMPARE(backendComputeCommand.runType(), Qt3DRender::QComputeCommand::Continuous);
+ QCOMPARE(backendComputeCommand.frameCount(), 0);
}
void checkCleanupState()
@@ -75,6 +79,8 @@ private Q_SLOTS:
computeCommand.setWorkGroupX(256);
computeCommand.setWorkGroupY(512);
computeCommand.setWorkGroupZ(128);
+ computeCommand.setRunType(Qt3DRender::QComputeCommand::Manual);
+ computeCommand.trigger(6);
{
// WHEN
@@ -87,6 +93,8 @@ private Q_SLOTS:
QCOMPARE(backendComputeCommand.x(), computeCommand.workGroupX());
QCOMPARE(backendComputeCommand.y(), computeCommand.workGroupY());
QCOMPARE(backendComputeCommand.z(), computeCommand.workGroupZ());
+ QCOMPARE(backendComputeCommand.runType(), computeCommand.runType());
+ QCOMPARE(backendComputeCommand.frameCount(), 6);
}
{
// WHEN
@@ -151,8 +159,75 @@ private Q_SLOTS:
// THEN
QCOMPARE(backendComputeCommand.z(), newValue);
}
+ {
+ // WHEN
+ const Qt3DRender::QComputeCommand::RunType newValue = Qt3DRender::QComputeCommand::Manual;
+ const auto change = Qt3DCore::QPropertyUpdatedChangePtr::create(Qt3DCore::QNodeId());
+ change->setPropertyName("runType");
+ change->setValue(newValue);
+ backendComputeCommand.sceneChangeEvent(change);
+
+ // THEN
+ QCOMPARE(backendComputeCommand.runType(), newValue);
+ }
+ {
+ // WHEN
+ const int newValue = 32;
+ const auto change = Qt3DCore::QPropertyUpdatedChangePtr::create(Qt3DCore::QNodeId());
+ change->setPropertyName("frameCount");
+ change->setValue(newValue);
+ backendComputeCommand.sceneChangeEvent(change);
+
+ // THEN
+ QCOMPARE(backendComputeCommand.frameCount(), newValue);
+ }
}
+ void checkUpdateFrameCount()
+ {
+ // GIVEN
+ TestRenderer renderer;
+ TestArbiter arbiter;
+
+ Qt3DRender::QComputeCommand computeCommand;
+ Qt3DRender::Render::ComputeCommand backendComputeCommand;
+
+ computeCommand.setWorkGroupX(256);
+ computeCommand.setWorkGroupY(512);
+ computeCommand.setWorkGroupZ(128);
+ computeCommand.setRunType(Qt3DRender::QComputeCommand::Manual);
+ computeCommand.trigger(6);
+
+
+ Qt3DCore::QBackendNodePrivate::get(&backendComputeCommand)->setArbiter(&arbiter);
+
+ backendComputeCommand.setRenderer(&renderer);
+ simulateInitialization(&computeCommand, &backendComputeCommand);
+
+ for (int i = 0; i < 5; ++i) {
+ // WHEN
+ backendComputeCommand.updateFrameCount();
+
+ // THEN
+ QCOMPARE(backendComputeCommand.frameCount(), 6 - (i + 1));
+ QCOMPARE(backendComputeCommand.isEnabled(), true);
+ QCOMPARE(arbiter.events.size(), 0);
+ }
+
+ // WHEN
+ backendComputeCommand.updateFrameCount();
+
+ // THEN
+ QCOMPARE(backendComputeCommand.frameCount(), false);
+ QCOMPARE(backendComputeCommand.isEnabled(), false);
+ QCOMPARE(arbiter.events.size(), 1);
+ {
+ auto change = arbiter.events.first().staticCast<Qt3DCore::QPropertyUpdatedChange>();
+ QCOMPARE(change->propertyName(), "enabled");
+ QCOMPARE(change->value().value<int>(), false);
+ QCOMPARE(change->type(), Qt3DCore::PropertyUpdated);
+ }
+ }
};
QTEST_MAIN(tst_ComputeCommand)
diff --git a/tests/auto/render/geometry/tst_geometry.cpp b/tests/auto/render/geometry/tst_geometry.cpp
index 958edfd09..7e65d27aa 100644
--- a/tests/auto/render/geometry/tst_geometry.cpp
+++ b/tests/auto/render/geometry/tst_geometry.cpp
@@ -34,7 +34,9 @@
#include <Qt3DCore/qpropertyupdatedchange.h>
#include <Qt3DCore/qpropertynodeaddedchange.h>
#include <Qt3DCore/qpropertynoderemovedchange.h>
+#include <Qt3DCore/private/qbackendnode_p.h>
#include "testrenderer.h"
+#include "testpostmanarbiter.h"
class DummyAttribute : public Qt3DRender::QAttribute
{
@@ -187,6 +189,32 @@ private Q_SLOTS:
QVERIFY(renderer.dirtyBits() & Qt3DRender::Render::AbstractRenderer::GeometryDirty);
renderer.clearDirtyBits(Qt3DRender::Render::AbstractRenderer::AllDirty);
}
+
+ void checkExtentTransmission()
+ {
+ // GIVEN
+ TestRenderer renderer;
+ TestArbiter arbiter;
+ Qt3DRender::Render::Geometry renderGeometry;
+
+ Qt3DCore::QBackendNodePrivate::get(&renderGeometry)->setArbiter(&arbiter);
+ renderGeometry.setRenderer(&renderer);
+
+ // WHEN
+ renderGeometry.updateExtent(QVector3D(-1.0f, -1.0f, -1.0f), QVector3D(1.0f, 1.0f, 1.0f));
+ renderGeometry.notifyExtentChanged();
+
+ // THEN
+ QCOMPARE(arbiter.events.count(), 1);
+
+ Qt3DCore::QPropertyUpdatedChangePtr change = arbiter.events.first().staticCast<Qt3DCore::QPropertyUpdatedChange>();
+ QCOMPARE(change->propertyName(), "extent");
+ const QPair<QVector3D, QVector3D> v = change->value().value<QPair<QVector3D, QVector3D>>();
+ QCOMPARE(v.first, QVector3D(-1.0f, -1.0f, -1.0f));
+ QCOMPARE(v.second, QVector3D(1.0f, 1.0f, 1.0f));
+
+ arbiter.events.clear();
+ }
};
diff --git a/tests/auto/render/graphicshelpergl2/tst_graphicshelpergl2.cpp b/tests/auto/render/graphicshelpergl2/tst_graphicshelpergl2.cpp
index 584b675ee..6c756957f 100644
--- a/tests/auto/render/graphicshelpergl2/tst_graphicshelpergl2.cpp
+++ b/tests/auto/render/graphicshelpergl2/tst_graphicshelpergl2.cpp
@@ -926,6 +926,9 @@ private Q_SLOTS:
SUPPORTS_FEATURE(GraphicsHelperInterface::DrawBuffersBlend, false);
SUPPORTS_FEATURE(GraphicsHelperInterface::Tessellation, false);
SUPPORTS_FEATURE(GraphicsHelperInterface::BlitFramebuffer, false);
+ SUPPORTS_FEATURE(GraphicsHelperInterface::IndirectDrawing, false);
+ SUPPORTS_FEATURE(GraphicsHelperInterface::MapBuffer, true);
+ SUPPORTS_FEATURE(GraphicsHelperInterface::Fences, false);
}
@@ -1524,6 +1527,36 @@ private Q_SLOTS:
// Not supported by GL2
}
+ void fenceSync()
+ {
+ QSKIP("Initialization failed, OpenGL 2.0 functions not supported");
+ // Not supported by GL2
+ }
+
+ void clientWaitSync()
+ {
+ QSKIP("Initialization failed, OpenGL 2.0 functions not supported");
+ // Not supported by GL2
+ }
+
+ void waitSync()
+ {
+ QSKIP("Initialization failed, OpenGL 2.0 functions not supported");
+ // Not supported by GL2
+ }
+
+ void wasSyncSignaled()
+ {
+ QSKIP("Initialization failed, OpenGL 2.0 functions not supported");
+ // Not supported by GL2
+ }
+
+ void deleteSync()
+ {
+ QSKIP("Initialization failed, OpenGL 2.0 functions not supported");
+ // Not supported by GL2
+ }
+
private:
QScopedPointer<QWindow> m_window;
QOpenGLContext m_glContext;
@@ -1537,17 +1570,11 @@ QT_END_NAMESPACE
#endif
-
-QT_BEGIN_NAMESPACE
-QTEST_ADD_GPU_BLACKLIST_SUPPORT_DEFS
-QT_END_NAMESPACE
-
int main(int argc, char *argv[])
{
#ifdef TEST_SHOULD_BE_PERFORMED
QGuiApplication app(argc, argv);
app.setAttribute(Qt::AA_Use96Dpi, true);
- QTEST_ADD_GPU_BLACKLIST_SUPPORT
tst_GraphicsHelperGL2 tc;
QTEST_SET_MAIN_SOURCE_PATH
return QTest::qExec(&tc, argc, argv);
diff --git a/tests/auto/render/graphicshelpergl3_2/tst_graphicshelpergl3_2.cpp b/tests/auto/render/graphicshelpergl3_2/tst_graphicshelpergl3_2.cpp
index 648eaaddb..5eb171827 100644
--- a/tests/auto/render/graphicshelpergl3_2/tst_graphicshelpergl3_2.cpp
+++ b/tests/auto/render/graphicshelpergl3_2/tst_graphicshelpergl3_2.cpp
@@ -2151,6 +2151,105 @@ private Q_SLOTS:
QCOMPARE(p, GL_FRONT);
}
+ void fenceSync()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ m_func->glGetError();
+
+ // WHEN
+ GLsync sync = reinterpret_cast<GLsync>(m_glHelper.fenceSync());
+
+ // THEN
+ QVERIFY(sync != nullptr);
+ QCOMPARE(m_func->glIsSync(sync), GL_TRUE);
+ const GLint error = m_func->glGetError();
+ QVERIFY(error == 0);
+ }
+
+ void clientWaitSync()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ m_func->glGetError();
+
+ // WHEN
+ QElapsedTimer t;
+ t.start();
+
+ GLsync sync = reinterpret_cast<GLsync>(m_glHelper.fenceSync());
+
+ m_glHelper.clientWaitSync(sync, 1000000);
+
+ // THEN
+ const GLint error = m_func->glGetError();
+ QVERIFY(error == 0);
+ qDebug() << t.nsecsElapsed();
+ }
+
+ void waitSync()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ m_func->glGetError();
+
+ // WHEN
+ GLsync sync = reinterpret_cast<GLsync>(m_glHelper.fenceSync());
+ m_func->glFlush();
+ m_glHelper.waitSync(sync);
+
+ // THEN
+ const GLint error = m_func->glGetError();
+ QVERIFY(error == 0);
+ }
+
+ void wasSyncSignaled()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ m_func->glGetError();
+
+ // WHEN
+ GLsync sync = reinterpret_cast<GLsync>(m_glHelper.fenceSync());
+ m_func->glFlush();
+ m_glHelper.waitSync(sync);
+
+ // THEN
+ const GLint error = m_func->glGetError();
+ QVERIFY(error == 0);
+
+ // Shouldn't loop forever
+ while (!m_glHelper.wasSyncSignaled(sync))
+ ;
+ }
+
+ void deleteSync()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ m_func->glGetError();
+
+ // WHEN
+ GLsync sync = reinterpret_cast<GLsync>(m_glHelper.fenceSync());
+ m_glHelper.clientWaitSync(sync, GLuint64(-1));
+
+ // THEN
+ const GLint error = m_func->glGetError();
+ QVERIFY(error == 0);
+ QVERIFY(m_glHelper.wasSyncSignaled(sync) == true);
+
+ // WHEN
+ m_glHelper.deleteSync(sync);
+
+ // THEN
+ QCOMPARE(m_func->glIsSync(sync), GL_FALSE);
+ }
+
private:
QScopedPointer<QWindow> m_window;
QOpenGLContext m_glContext;
@@ -2161,16 +2260,11 @@ private:
#endif
-QT_BEGIN_NAMESPACE
-QTEST_ADD_GPU_BLACKLIST_SUPPORT_DEFS
-QT_END_NAMESPACE
-
int main(int argc, char *argv[])
{
#ifdef TEST_SHOULD_BE_PERFORMED
QGuiApplication app(argc, argv);
app.setAttribute(Qt::AA_Use96Dpi, true);
- QTEST_ADD_GPU_BLACKLIST_SUPPORT
tst_GraphicsHelperGL3_2 tc;
QTEST_SET_MAIN_SOURCE_PATH
return QTest::qExec(&tc, argc, argv);
diff --git a/tests/auto/render/graphicshelpergl3_3/tst_graphicshelpergl3_3.cpp b/tests/auto/render/graphicshelpergl3_3/tst_graphicshelpergl3_3.cpp
index 06a3c41cd..a88e36d5e 100644
--- a/tests/auto/render/graphicshelpergl3_3/tst_graphicshelpergl3_3.cpp
+++ b/tests/auto/render/graphicshelpergl3_3/tst_graphicshelpergl3_3.cpp
@@ -2251,6 +2251,105 @@ private Q_SLOTS:
QCOMPARE(p, GL_FRONT);
}
+ void fenceSync()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ m_func->glGetError();
+
+ // WHEN
+ GLsync sync = reinterpret_cast<GLsync>(m_glHelper.fenceSync());
+
+ // THEN
+ QVERIFY(sync != nullptr);
+ QCOMPARE(m_func->glIsSync(sync), GL_TRUE);
+ const GLint error = m_func->glGetError();
+ QVERIFY(error == 0);
+ }
+
+ void clientWaitSync()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ m_func->glGetError();
+
+ // WHEN
+ QElapsedTimer t;
+ t.start();
+
+ GLsync sync = reinterpret_cast<GLsync>(m_glHelper.fenceSync());
+
+ m_glHelper.clientWaitSync(sync, 1000000);
+
+ // THEN
+ const GLint error = m_func->glGetError();
+ QVERIFY(error == 0);
+ qDebug() << t.nsecsElapsed();
+ }
+
+ void waitSync()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ m_func->glGetError();
+
+ // WHEN
+ GLsync sync = reinterpret_cast<GLsync>(m_glHelper.fenceSync());
+ m_func->glFlush();
+ m_glHelper.waitSync(sync);
+
+ // THEN
+ const GLint error = m_func->glGetError();
+ QVERIFY(error == 0);
+ }
+
+ void wasSyncSignaled()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ m_func->glGetError();
+
+ // WHEN
+ GLsync sync = reinterpret_cast<GLsync>(m_glHelper.fenceSync());
+ m_func->glFlush();
+ m_glHelper.waitSync(sync);
+
+ // THEN
+ const GLint error = m_func->glGetError();
+ QVERIFY(error == 0);
+
+ // Shouldn't loop forever
+ while (!m_glHelper.wasSyncSignaled(sync))
+ ;
+ }
+
+ void deleteSync()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ m_func->glGetError();
+
+ // WHEN
+ GLsync sync = reinterpret_cast<GLsync>(m_glHelper.fenceSync());
+ m_glHelper.clientWaitSync(sync, GLuint64(-1));
+
+ // THEN
+ const GLint error = m_func->glGetError();
+ QVERIFY(error == 0);
+ QVERIFY(m_glHelper.wasSyncSignaled(sync) == true);
+
+ // WHEN
+ m_glHelper.deleteSync(sync);
+
+ // THEN
+ QCOMPARE(m_func->glIsSync(sync), GL_FALSE);
+ }
+
private:
QScopedPointer<QWindow> m_window;
QOpenGLContext m_glContext;
@@ -2261,16 +2360,11 @@ private:
#endif
-QT_BEGIN_NAMESPACE
-QTEST_ADD_GPU_BLACKLIST_SUPPORT_DEFS
-QT_END_NAMESPACE
-
int main(int argc, char *argv[])
{
#ifdef TEST_SHOULD_BE_PERFORMED
QGuiApplication app(argc, argv);
app.setAttribute(Qt::AA_Use96Dpi, true);
- QTEST_ADD_GPU_BLACKLIST_SUPPORT
tst_GraphicsHelperGL3_3 tc;
QTEST_SET_MAIN_SOURCE_PATH
return QTest::qExec(&tc, argc, argv);
diff --git a/tests/auto/render/graphicshelpergl4/tst_graphicshelpergl4.cpp b/tests/auto/render/graphicshelpergl4/tst_graphicshelpergl4.cpp
index 39bd15021..87eee19ac 100644
--- a/tests/auto/render/graphicshelpergl4/tst_graphicshelpergl4.cpp
+++ b/tests/auto/render/graphicshelpergl4/tst_graphicshelpergl4.cpp
@@ -1430,7 +1430,7 @@ private Q_SLOTS:
void supportsFeature()
{
- for (int i = 0; i <= GraphicsHelperInterface::BlitFramebuffer; ++i)
+ for (int i = 0; i <= GraphicsHelperInterface::Fences; ++i)
QVERIFY(m_glHelper.supportsFeature(static_cast<GraphicsHelperInterface::Feature>(i)));
}
@@ -2349,6 +2349,105 @@ private Q_SLOTS:
QCOMPARE(p, GL_FRONT);
}
+ void fenceSync()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ m_func->glGetError();
+
+ // WHEN
+ GLsync sync = reinterpret_cast<GLsync>(m_glHelper.fenceSync());
+
+ // THEN
+ QVERIFY(sync != nullptr);
+ QCOMPARE(m_func->glIsSync(sync), GL_TRUE);
+ const GLint error = m_func->glGetError();
+ QVERIFY(error == 0);
+ }
+
+ void clientWaitSync()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ m_func->glGetError();
+
+ // WHEN
+ QElapsedTimer t;
+ t.start();
+
+ GLsync sync = reinterpret_cast<GLsync>(m_glHelper.fenceSync());
+
+ m_glHelper.clientWaitSync(sync, 1000000);
+
+ // THEN
+ const GLint error = m_func->glGetError();
+ QVERIFY(error == 0);
+ qDebug() << t.nsecsElapsed();
+ }
+
+ void waitSync()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ m_func->glGetError();
+
+ // WHEN
+ GLsync sync = reinterpret_cast<GLsync>(m_glHelper.fenceSync());
+ m_func->glFlush();
+ m_glHelper.waitSync(sync);
+
+ // THEN
+ const GLint error = m_func->glGetError();
+ QVERIFY(error == 0);
+ }
+
+ void wasSyncSignaled()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ m_func->glGetError();
+
+ // WHEN
+ GLsync sync = reinterpret_cast<GLsync>(m_glHelper.fenceSync());
+ m_func->glFlush();
+ m_glHelper.waitSync(sync);
+
+ // THEN
+ const GLint error = m_func->glGetError();
+ QVERIFY(error == 0);
+
+ // Shouldn't loop forever
+ while (!m_glHelper.wasSyncSignaled(sync))
+ ;
+ }
+
+ void deleteSync()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ m_func->glGetError();
+
+ // WHEN
+ GLsync sync = reinterpret_cast<GLsync>(m_glHelper.fenceSync());
+ m_glHelper.clientWaitSync(sync, GLuint64(-1));
+
+ // THEN
+ const GLint error = m_func->glGetError();
+ QVERIFY(error == 0);
+ QVERIFY(m_glHelper.wasSyncSignaled(sync) == true);
+
+ // WHEN
+ m_glHelper.deleteSync(sync);
+
+ // THEN
+ QCOMPARE(m_func->glIsSync(sync), GL_FALSE);
+ }
+
private:
QScopedPointer<QWindow> m_window;
QOpenGLContext m_glContext;
@@ -2359,16 +2458,11 @@ private:
#endif
-QT_BEGIN_NAMESPACE
-QTEST_ADD_GPU_BLACKLIST_SUPPORT_DEFS
-QT_END_NAMESPACE
-
int main(int argc, char *argv[])
{
#ifdef TEST_SHOULD_BE_PERFORMED
QGuiApplication app(argc, argv);
app.setAttribute(Qt::AA_Use96Dpi, true);
- QTEST_ADD_GPU_BLACKLIST_SUPPORT
tst_GraphicsHelperGL4 tc;
QTEST_SET_MAIN_SOURCE_PATH
return QTest::qExec(&tc, argc, argv);
diff --git a/tests/auto/render/memorybarrier/tst_memorybarrier.cpp b/tests/auto/render/memorybarrier/tst_memorybarrier.cpp
index f0d4931d9..33a1247fa 100644
--- a/tests/auto/render/memorybarrier/tst_memorybarrier.cpp
+++ b/tests/auto/render/memorybarrier/tst_memorybarrier.cpp
@@ -98,6 +98,8 @@ private Q_SLOTS:
// THEN
QCOMPARE(backendMemoryBarrier.isEnabled(), newValue);
+ QVERIFY(renderer.dirtyBits() & Qt3DRender::Render::AbstractRenderer::FrameGraphDirty);
+ renderer.clearDirtyBits(Qt3DRender::Render::AbstractRenderer::AllDirty);
}
{
// WHEN
@@ -109,6 +111,8 @@ private Q_SLOTS:
// THEN
QCOMPARE(backendMemoryBarrier.waitOperations(), newValue);
+ QVERIFY(renderer.dirtyBits() & Qt3DRender::Render::AbstractRenderer::FrameGraphDirty);
+ renderer.clearDirtyBits(Qt3DRender::Render::AbstractRenderer::AllDirty);
}
}
diff --git a/tests/auto/render/objectpicker/tst_objectpicker.cpp b/tests/auto/render/objectpicker/tst_objectpicker.cpp
index c1b06ccd8..644849102 100644
--- a/tests/auto/render/objectpicker/tst_objectpicker.cpp
+++ b/tests/auto/render/objectpicker/tst_objectpicker.cpp
@@ -47,6 +47,7 @@ private Q_SLOTS:
Qt3DRender::Render::ObjectPicker objectPicker;
Qt3DRender::QObjectPicker picker;
picker.setHoverEnabled(true);
+ picker.setPriority(883);
// WHEN
simulateInitialization(&picker, &objectPicker);
@@ -54,6 +55,7 @@ private Q_SLOTS:
// THEN
QVERIFY(!objectPicker.peerId().isNull());
QCOMPARE(objectPicker.isHoverEnabled(), true);
+ QCOMPARE(objectPicker.priority(), 883);
}
void checkInitialAndCleanedUpState()
@@ -64,10 +66,14 @@ private Q_SLOTS:
// THEN
QVERIFY(objectPicker.peerId().isNull());
QCOMPARE(objectPicker.isHoverEnabled(), false);
+ QCOMPARE(objectPicker.isDragEnabled(), false);
+ QCOMPARE(objectPicker.priority(), 0);
// GIVEN
Qt3DRender::QObjectPicker picker;
picker.setHoverEnabled(true);
+ picker.setDragEnabled(true);
+ picker.setPriority(1584);
// WHEN
simulateInitialization(&picker, &objectPicker);
@@ -75,6 +81,8 @@ private Q_SLOTS:
// THEN
QCOMPARE(objectPicker.isHoverEnabled(), false);
+ QCOMPARE(objectPicker.isDragEnabled(), false);
+ QCOMPARE(objectPicker.priority(), 0);
}
void checkPropertyChanges()
@@ -95,6 +103,34 @@ private Q_SLOTS:
QCOMPARE(objectPicker.isHoverEnabled(), true);
QVERIFY(renderer.dirtyBits() != 0);
}
+ {
+ Qt3DRender::Render::ObjectPicker objectPicker;
+ objectPicker.setRenderer(&renderer);
+
+ // WHEN
+ Qt3DCore::QPropertyUpdatedChangePtr updateChange(new Qt3DCore::QPropertyUpdatedChange(Qt3DCore::QNodeId()));
+ updateChange->setValue(true);
+ updateChange->setPropertyName("dragEnabled");
+ objectPicker.sceneChangeEvent(updateChange);
+
+ // THEN
+ QCOMPARE(objectPicker.isDragEnabled(), true);
+ QVERIFY(renderer.dirtyBits() != 0);
+ }
+ {
+ Qt3DRender::Render::ObjectPicker objectPicker;
+ objectPicker.setRenderer(&renderer);
+
+ // WHEN
+ Qt3DCore::QPropertyUpdatedChangePtr updateChange(new Qt3DCore::QPropertyUpdatedChange(Qt3DCore::QNodeId()));
+ updateChange->setValue(15);
+ updateChange->setPropertyName("priority");
+ objectPicker.sceneChangeEvent(updateChange);
+
+ // THEN
+ QCOMPARE(objectPicker.priority(), 15);
+ QVERIFY(renderer.dirtyBits() != 0);
+ }
}
void checkBackendPropertyNotifications()
diff --git a/tests/auto/render/pickboundingvolumejob/pickboundingvolumejob.qrc b/tests/auto/render/pickboundingvolumejob/pickboundingvolumejob.qrc
index feef480e2..e1506de86 100644
--- a/tests/auto/render/pickboundingvolumejob/pickboundingvolumejob.qrc
+++ b/tests/auto/render/pickboundingvolumejob/pickboundingvolumejob.qrc
@@ -10,5 +10,6 @@
<file>testscene_parententity.qml</file>
<file>testscene_viewports.qml</file>
<file>testscene_cameraposition.qml</file>
+ <file>testscene_priorityoverlapping.qml</file>
</qresource>
</RCC>
diff --git a/tests/auto/render/pickboundingvolumejob/testscene_priorityoverlapping.qml b/tests/auto/render/pickboundingvolumejob/testscene_priorityoverlapping.qml
new file mode 100644
index 000000000..7cacb3d2d
--- /dev/null
+++ b/tests/auto/render/pickboundingvolumejob/testscene_priorityoverlapping.qml
@@ -0,0 +1,135 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 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$
+**
+****************************************************************************/
+
+import Qt3D.Core 2.0
+import Qt3D.Render 2.13
+import Qt3D.Extras 2.0
+import QtQuick.Window 2.0
+
+Entity {
+ id: sceneRoot
+
+ Window {
+ id: win
+ width: 600
+ height: 600
+ visible: true
+ }
+
+ Camera {
+ id: camera
+ projectionType: CameraLens.PerspectiveProjection
+ fieldOfView: 45
+ 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 )
+ }
+
+ components: [
+ RenderSettings {
+ activeFrameGraph: Viewport {
+ normalizedRect: Qt.rect(0.0, 0.0, 1.0, 1.0)
+
+ RenderSurfaceSelector {
+ surface: win
+
+ ClearBuffers {
+ buffers : ClearBuffers.ColorDepthBuffer
+ NoDraw {}
+ }
+
+ CameraSelector {
+ camera: camera
+ }
+ }
+ }
+ pickingSettings {
+ pickResultMode: PickingSettings.NearestPriorityPick
+ pickMethod: PickingSettings.TrianglePicking
+ faceOrientationPickingMode: PickingSettings.FrontAndBackFace
+ }
+ }
+ ]
+
+ CuboidMesh { id: cubeMesh }
+ PhongMaterial { id: material }
+
+ // Entity 1
+ Entity {
+ property ObjectPicker picker: ObjectPicker {
+ id: picker1
+ objectName: "Picker1"
+ }
+
+ property Transform transform: Transform {
+ translation: Qt.vector3d(0, 0, 0)
+ scale: 2.0
+ }
+
+ components: [cubeMesh, material, picker1, transform]
+ }
+
+ // Entity 2
+ Entity {
+ property ObjectPicker picker: ObjectPicker {
+ id: picker2
+ objectName: "Picker2"
+ }
+
+ property Transform transform: Transform {
+ translation: Qt.vector3d(0, 0, 10)
+ scale: 2.5
+ }
+
+ components: [cubeMesh, material, picker2, transform]
+ }
+}
diff --git a/tests/auto/render/pickboundingvolumejob/tst_pickboundingvolumejob.cpp b/tests/auto/render/pickboundingvolumejob/tst_pickboundingvolumejob.cpp
index 60b60eb6e..5e51c8aa7 100644
--- a/tests/auto/render/pickboundingvolumejob/tst_pickboundingvolumejob.cpp
+++ b/tests/auto/render/pickboundingvolumejob/tst_pickboundingvolumejob.cpp
@@ -1473,6 +1473,111 @@ private Q_SLOTS:
arbiter.events.clear();
}
+ void checkPriorityPicking()
+ {
+ // GIVEN
+ QmlSceneReader sceneReader(QUrl("qrc:/testscene_priorityoverlapping.qml"));
+ QScopedPointer<Qt3DCore::QNode> root(qobject_cast<Qt3DCore::QNode *>(sceneReader.root()));
+ QVERIFY(root);
+
+ QScopedPointer<Qt3DRender::TestAspect> test(new Qt3DRender::TestAspect(root.data()));
+ TestArbiter arbiter1;
+ TestArbiter arbiter2;
+
+ // Runs Required jobs
+ runRequiredJobs(test.data());
+
+ // THEN
+ QList<Qt3DRender::QObjectPicker *> pickers = root->findChildren<Qt3DRender::QObjectPicker *>();
+ QCOMPARE(pickers.size(), 2);
+
+ Qt3DRender::QObjectPicker *picker1 = nullptr;
+ Qt3DRender::QObjectPicker *picker2 = nullptr;
+ if (pickers.first()->objectName() == QLatin1String("Picker1")) {
+ picker1 = pickers.first();
+ picker2 = pickers.last();
+ } else {
+ picker1 = pickers.last();
+ picker2 = pickers.first();
+ }
+
+ Qt3DRender::Render::ObjectPicker *backendPicker1 = test->nodeManagers()->objectPickerManager()->lookupResource(picker1->id());
+ QVERIFY(backendPicker1);
+ Qt3DCore::QBackendNodePrivate::get(backendPicker1)->setArbiter(&arbiter1);
+
+ Qt3DRender::Render::ObjectPicker *backendPicker2 = test->nodeManagers()->objectPickerManager()->lookupResource(picker2->id());
+ QVERIFY(backendPicker2);
+ Qt3DCore::QBackendNodePrivate::get(backendPicker2)->setArbiter(&arbiter2);
+
+
+ // WHEN both have priority == 0, select closest
+ {
+ Qt3DRender::Render::PickBoundingVolumeJob pickBVJob;
+ initializePickBoundingVolumeJob(&pickBVJob, test.data());
+
+ // WHEN -> Pressed on object
+ QList<QPair<QObject *, QMouseEvent>> events;
+ events.push_back({nullptr, QMouseEvent(QMouseEvent::MouseButtonPress, QPointF(300.0f, 300.0f),
+ Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)});
+ pickBVJob.setMouseEvents(events);
+ bool earlyReturn = !pickBVJob.runHelper();
+
+ // THEN -> Select picker with highest priority
+ QVERIFY(!earlyReturn);
+ QVERIFY(backendPicker1->isPressed());
+ Qt3DCore::QPropertyUpdatedChangePtr change = arbiter1.events.first().staticCast<Qt3DCore::QPropertyUpdatedChange>();
+ QCOMPARE(change->propertyName(), "pressed");
+
+ QVERIFY(!backendPicker2->isPressed());
+ QVERIFY(arbiter2.events.isEmpty());
+
+ events.push_back({nullptr, QMouseEvent(QMouseEvent::MouseButtonRelease, QPointF(300.0f, 300.0f),
+ Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)});
+ pickBVJob.setMouseEvents(events);
+ pickBVJob.runHelper();
+ arbiter1.events.clear();
+ arbiter2.events.clear();
+
+ QVERIFY(!backendPicker1->isPressed());
+ QVERIFY(!backendPicker2->isPressed());
+ }
+
+ // WHEN furthest one has higher priority, select furthest one
+ {
+ backendPicker2->setPriority(1000);
+ QCOMPARE(backendPicker2->priority(), 1000);
+
+ Qt3DRender::Render::PickBoundingVolumeJob pickBVJob;
+ initializePickBoundingVolumeJob(&pickBVJob, test.data());
+
+ // WHEN -> Pressed on object
+ QList<QPair<QObject *, QMouseEvent>> events;
+ events.push_back({nullptr, QMouseEvent(QMouseEvent::MouseButtonPress, QPointF(300.0f, 300.0f),
+ Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)});
+ pickBVJob.setMouseEvents(events);
+ bool earlyReturn = !pickBVJob.runHelper();
+
+ // THEN -> Select picker with highest priority
+ QVERIFY(!earlyReturn);
+ QVERIFY(backendPicker2->isPressed());
+ Qt3DCore::QPropertyUpdatedChangePtr change = arbiter2.events.first().staticCast<Qt3DCore::QPropertyUpdatedChange>();
+ QCOMPARE(change->propertyName(), "pressed");
+
+ QVERIFY(!backendPicker1->isPressed());
+ QVERIFY(arbiter1.events.isEmpty());
+
+ events.push_back({nullptr, QMouseEvent(QMouseEvent::MouseButtonRelease, QPointF(300.0f, 300.0f),
+ Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)});
+ pickBVJob.setMouseEvents(events);
+ pickBVJob.runHelper();
+ arbiter1.events.clear();
+ arbiter2.events.clear();
+
+ QVERIFY(!backendPicker1->isPressed());
+ QVERIFY(!backendPicker2->isPressed());
+ }
+ }
+
};
QTEST_MAIN(tst_PickBoundingVolumeJob)
diff --git a/tests/auto/render/proximityfilter/tst_proximityfilter.cpp b/tests/auto/render/proximityfilter/tst_proximityfilter.cpp
index 774e2dd1f..ac9cc610c 100644
--- a/tests/auto/render/proximityfilter/tst_proximityfilter.cpp
+++ b/tests/auto/render/proximityfilter/tst_proximityfilter.cpp
@@ -101,6 +101,8 @@ private Q_SLOTS:
// THEN
QCOMPARE(backendProximityFilter.isEnabled(), newValue);
+ QVERIFY(renderer.dirtyBits() & Qt3DRender::Render::AbstractRenderer::FrameGraphDirty);
+ renderer.clearDirtyBits(Qt3DRender::Render::AbstractRenderer::AllDirty);
}
{
// WHEN
@@ -112,6 +114,8 @@ private Q_SLOTS:
// THEN
QCOMPARE(backendProximityFilter.distanceThreshold(), newValue);
+ QVERIFY(renderer.dirtyBits() & Qt3DRender::Render::AbstractRenderer::FrameGraphDirty);
+ renderer.clearDirtyBits(Qt3DRender::Render::AbstractRenderer::AllDirty);
}
{
// WHEN
@@ -123,6 +127,8 @@ private Q_SLOTS:
// THEN
QCOMPARE(backendProximityFilter.entityId(), newValue);
+ QVERIFY(renderer.dirtyBits() & Qt3DRender::Render::AbstractRenderer::FrameGraphDirty);
+ renderer.clearDirtyBits(Qt3DRender::Render::AbstractRenderer::AllDirty);
}
}
diff --git a/tests/auto/render/qabstracttexture/tst_qabstracttexture.cpp b/tests/auto/render/qabstracttexture/tst_qabstracttexture.cpp
index c7158f4de..73c882619 100644
--- a/tests/auto/render/qabstracttexture/tst_qabstracttexture.cpp
+++ b/tests/auto/render/qabstracttexture/tst_qabstracttexture.cpp
@@ -42,6 +42,17 @@
class FakeTexture : public Qt3DRender::QAbstractTexture
{
+public:
+ int sharedTextureId() const
+ {
+ return static_cast<Qt3DRender::QAbstractTexturePrivate *>(d_ptr.get())->m_sharedTextureId;
+ }
+
+ void sceneChangeEvent(const Qt3DCore::QSceneChangePtr &change) override
+ {
+ Qt3DRender::QAbstractTexture::sceneChangeEvent(change);
+ }
+
};
class FakeTextureImage : public Qt3DRender::QAbstractTextureImage
@@ -91,6 +102,10 @@ private Q_SLOTS:
QCOMPARE(abstractTexture.layers(), 1);
QCOMPARE(abstractTexture.samples(), 1);
QCOMPARE(abstractTexture.textureImages().size(), 0);
+ QCOMPARE(abstractTexture.samples(), 1);
+ QCOMPARE(abstractTexture.sharedTextureId(), -1);
+ QCOMPARE(abstractTexture.handleType(), Qt3DRender::QAbstractTexture::NoHandle);
+ QCOMPARE(abstractTexture.handle(), QVariant());
}
void checkPropertyChanges()
@@ -383,6 +398,7 @@ private Q_SLOTS:
QCOMPARE(abstractTexture.comparisonMode(), cloneData.comparisonMode);
QCOMPARE(abstractTexture.layers(), cloneData.layers);
QCOMPARE(abstractTexture.samples(), cloneData.samples);
+ QCOMPARE(abstractTexture.sharedTextureId(), cloneData.sharedTextureId);
QCOMPARE(abstractTexture.textureImages().size(), cloneData.textureImageIds.size());
for (int i = 0, m = abstractTexture.textureImages().size(); i < m; ++i)
@@ -425,6 +441,7 @@ private Q_SLOTS:
QCOMPARE(abstractTexture.comparisonMode(), cloneData.comparisonMode);
QCOMPARE(abstractTexture.layers(), cloneData.layers);
QCOMPARE(abstractTexture.samples(), cloneData.samples);
+ QCOMPARE(abstractTexture.sharedTextureId(), cloneData.sharedTextureId);
QCOMPARE(abstractTexture.textureImages().size(), cloneData.textureImageIds.size());
for (int i = 0, m = abstractTexture.textureImages().size(); i < m; ++i)
@@ -882,6 +899,238 @@ private Q_SLOTS:
}
}
+ void checkSceneChangedEvent()
+ {
+ // GIVEN
+ TestArbiter arbiter;
+ FakeTexture abstractTexture;
+ arbiter.setArbiterOnNode(&abstractTexture);
+
+ qRegisterMetaType<Qt3DRender::QAbstractTexture::Status>("Status");
+ qRegisterMetaType<Qt3DRender::QAbstractTexture::TextureFormat>("TextureFormat");
+ qRegisterMetaType<Qt3DRender::QAbstractTexture::HandleType>("HandleType");
+
+
+ {
+ QSignalSpy spy(&abstractTexture, SIGNAL(widthChanged(int)));
+
+ // THEN
+ QVERIFY(spy.isValid());
+
+ // WHEN
+ Qt3DCore::QPropertyUpdatedChangePtr valueChange(new Qt3DCore::QPropertyUpdatedChange(Qt3DCore::QNodeId()));
+ valueChange->setPropertyName("width");
+ valueChange->setValue(883);
+ abstractTexture.sceneChangeEvent(valueChange);
+
+ // THEN
+ QCOMPARE(spy.count(), 1);
+ QCOMPARE(arbiter.events.size(), 0);
+ QCOMPARE(abstractTexture.width(), 883);
+
+ // WHEN
+ spy.clear();
+ abstractTexture.sceneChangeEvent(valueChange);
+
+ // THEN
+ QCOMPARE(spy.count(), 0);
+ QCOMPARE(arbiter.events.size(), 0);
+ QCOMPARE(abstractTexture.width(), 883);
+ }
+
+ {
+ QSignalSpy spy(&abstractTexture, SIGNAL(heightChanged(int)));
+
+ // THEN
+ QVERIFY(spy.isValid());
+
+ // WHEN
+ Qt3DCore::QPropertyUpdatedChangePtr valueChange(new Qt3DCore::QPropertyUpdatedChange(Qt3DCore::QNodeId()));
+ valueChange->setPropertyName("height");
+ valueChange->setValue(1584);
+ abstractTexture.sceneChangeEvent(valueChange);
+
+ // THEN
+ QCOMPARE(spy.count(), 1);
+ QCOMPARE(arbiter.events.size(), 0);
+ QCOMPARE(abstractTexture.height(), 1584);
+
+ // WHEN
+ spy.clear();
+ abstractTexture.sceneChangeEvent(valueChange);
+
+ // THEN
+ QCOMPARE(spy.count(), 0);
+ QCOMPARE(arbiter.events.size(), 0);
+ QCOMPARE(abstractTexture.height(), 1584);
+ }
+
+ {
+ QSignalSpy spy(&abstractTexture, SIGNAL(depthChanged(int)));
+
+ // THEN
+ QVERIFY(spy.isValid());
+
+ // WHEN
+ Qt3DCore::QPropertyUpdatedChangePtr valueChange(new Qt3DCore::QPropertyUpdatedChange(Qt3DCore::QNodeId()));
+ valueChange->setPropertyName("depth");
+ valueChange->setValue(8);
+ abstractTexture.sceneChangeEvent(valueChange);
+
+ // THEN
+ QCOMPARE(spy.count(), 1);
+ QCOMPARE(arbiter.events.size(), 0);
+ QCOMPARE(abstractTexture.depth(), 8);
+
+ // WHEN
+ spy.clear();
+ abstractTexture.sceneChangeEvent(valueChange);
+
+ // THEN
+ QCOMPARE(spy.count(), 0);
+ QCOMPARE(arbiter.events.size(), 0);
+ QCOMPARE(abstractTexture.depth(), 8);
+ }
+
+ {
+ QSignalSpy spy(&abstractTexture, SIGNAL(layersChanged(int)));
+
+ // THEN
+ QVERIFY(spy.isValid());
+
+ // WHEN
+ Qt3DCore::QPropertyUpdatedChangePtr valueChange(new Qt3DCore::QPropertyUpdatedChange(Qt3DCore::QNodeId()));
+ valueChange->setPropertyName("layers");
+ valueChange->setValue(256);
+ abstractTexture.sceneChangeEvent(valueChange);
+
+ // THEN
+ QCOMPARE(spy.count(), 1);
+ QCOMPARE(arbiter.events.size(), 0);
+ QCOMPARE(abstractTexture.layers(), 256);
+
+ // WHEN
+ spy.clear();
+ abstractTexture.sceneChangeEvent(valueChange);
+
+ // THEN
+ QCOMPARE(spy.count(), 0);
+ QCOMPARE(arbiter.events.size(), 0);
+ QCOMPARE(abstractTexture.layers(), 256);
+ }
+
+ {
+ QSignalSpy spy(&abstractTexture, SIGNAL(formatChanged(TextureFormat)));
+
+ // THEN
+ QVERIFY(spy.isValid());
+
+ // WHEN
+ Qt3DCore::QPropertyUpdatedChangePtr valueChange(new Qt3DCore::QPropertyUpdatedChange(Qt3DCore::QNodeId()));
+ valueChange->setPropertyName("format");
+ const auto newFormat = Qt3DRender::QAbstractTexture::R8I;
+ valueChange->setValue(QVariant::fromValue(newFormat));
+ abstractTexture.sceneChangeEvent(valueChange);
+
+ // THEN
+ QCOMPARE(spy.count(), 1);
+ QCOMPARE(arbiter.events.size(), 0);
+ QCOMPARE(abstractTexture.format(), newFormat);
+
+ // WHEN
+ spy.clear();
+ abstractTexture.sceneChangeEvent(valueChange);
+
+ // THEN
+ QCOMPARE(spy.count(), 0);
+ QCOMPARE(arbiter.events.size(), 0);
+ QCOMPARE(abstractTexture.format(), newFormat);
+ }
+
+ {
+ QSignalSpy spy(&abstractTexture, SIGNAL(statusChanged(Status)));
+
+ // THEN
+ QVERIFY(spy.isValid());
+
+ // WHEN
+ Qt3DCore::QPropertyUpdatedChangePtr valueChange(new Qt3DCore::QPropertyUpdatedChange(Qt3DCore::QNodeId()));
+ valueChange->setPropertyName("status");
+ const auto newStatus = Qt3DRender::QAbstractTexture::Error;
+ valueChange->setValue(QVariant::fromValue(newStatus));
+ abstractTexture.sceneChangeEvent(valueChange);
+
+ // THEN
+ QCOMPARE(spy.count(), 1);
+ QCOMPARE(arbiter.events.size(), 0);
+ QCOMPARE(abstractTexture.status(), newStatus);
+
+ // WHEN
+ spy.clear();
+ abstractTexture.sceneChangeEvent(valueChange);
+
+ // THEN
+ QCOMPARE(spy.count(), 0);
+ QCOMPARE(arbiter.events.size(), 0);
+ QCOMPARE(abstractTexture.status(), newStatus);
+ }
+
+ {
+ QSignalSpy spy(&abstractTexture, SIGNAL(handleTypeChanged(HandleType)));
+
+ // THEN
+ QVERIFY(spy.isValid());
+
+ // WHEN
+ Qt3DCore::QPropertyUpdatedChangePtr valueChange(new Qt3DCore::QPropertyUpdatedChange(Qt3DCore::QNodeId()));
+ valueChange->setPropertyName("handleType");
+ const auto newType = Qt3DRender::QAbstractTexture::OpenGLTextureId;
+ valueChange->setValue(QVariant::fromValue(newType));
+ abstractTexture.sceneChangeEvent(valueChange);
+
+ // THEN
+ QCOMPARE(spy.count(), 1);
+ QCOMPARE(arbiter.events.size(), 0);
+ QCOMPARE(abstractTexture.handleType(), newType);
+
+ // WHEN
+ spy.clear();
+ abstractTexture.sceneChangeEvent(valueChange);
+
+ // THEN
+ QCOMPARE(spy.count(), 0);
+ QCOMPARE(arbiter.events.size(), 0);
+ QCOMPARE(abstractTexture.handleType(), newType);
+ }
+
+ {
+ QSignalSpy spy(&abstractTexture, SIGNAL(handleChanged(QVariant)));
+
+ // THEN
+ QVERIFY(spy.isValid());
+
+ // WHEN
+ Qt3DCore::QPropertyUpdatedChangePtr valueChange(new Qt3DCore::QPropertyUpdatedChange(Qt3DCore::QNodeId()));
+ valueChange->setPropertyName("handle");
+ valueChange->setValue(QVariant(1));
+ abstractTexture.sceneChangeEvent(valueChange);
+
+ // THEN
+ QCOMPARE(spy.count(), 1);
+ QCOMPARE(arbiter.events.size(), 0);
+ QCOMPARE(abstractTexture.handle(), QVariant(1));
+
+ // WHEN
+ spy.clear();
+ abstractTexture.sceneChangeEvent(valueChange);
+
+ // THEN
+ QCOMPARE(spy.count(), 0);
+ QCOMPARE(arbiter.events.size(), 0);
+ QCOMPARE(abstractTexture.handle(), QVariant(1));
+ }
+ }
+
};
QTEST_MAIN(tst_QAbstractTexture)
diff --git a/tests/auto/render/qcomputecommand/tst_qcomputecommand.cpp b/tests/auto/render/qcomputecommand/tst_qcomputecommand.cpp
index 94609c129..cc07120a2 100644
--- a/tests/auto/render/qcomputecommand/tst_qcomputecommand.cpp
+++ b/tests/auto/render/qcomputecommand/tst_qcomputecommand.cpp
@@ -52,6 +52,7 @@ private Q_SLOTS:
QCOMPARE(computeCommand.workGroupX(), 1);
QCOMPARE(computeCommand.workGroupY(), 1);
QCOMPARE(computeCommand.workGroupZ(), 1);
+ QCOMPARE(computeCommand.runType(), Qt3DRender::QComputeCommand::Continuous);
}
void checkPropertyChanges()
@@ -116,6 +117,25 @@ private Q_SLOTS:
QCOMPARE(computeCommand.workGroupZ(), newValue);
QCOMPARE(spy.count(), 0);
}
+ {
+ // WHEN
+ QSignalSpy spy(&computeCommand, SIGNAL(runTypeChanged()));
+ const Qt3DRender::QComputeCommand::RunType newValue = Qt3DRender::QComputeCommand::Manual;
+ computeCommand.setRunType(newValue);
+
+ // THEN
+ QVERIFY(spy.isValid());
+ QCOMPARE(computeCommand.runType(), newValue);
+ QCOMPARE(spy.count(), 1);
+
+ // WHEN
+ spy.clear();
+ computeCommand.setRunType(newValue);
+
+ // THEN
+ QCOMPARE(computeCommand.runType(), newValue);
+ QCOMPARE(spy.count(), 0);
+ }
}
void checkCreationData()
@@ -126,6 +146,7 @@ private Q_SLOTS:
computeCommand.setWorkGroupX(128);
computeCommand.setWorkGroupY(512);
computeCommand.setWorkGroupZ(1024);
+ computeCommand.setRunType(Qt3DRender::QComputeCommand::Manual);
// WHEN
QVector<Qt3DCore::QNodeCreatedChangeBasePtr> creationChanges;
@@ -145,6 +166,8 @@ private Q_SLOTS:
QCOMPARE(computeCommand.workGroupX(), cloneData.workGroupX);
QCOMPARE(computeCommand.workGroupY(), cloneData.workGroupY);
QCOMPARE(computeCommand.workGroupZ(), cloneData.workGroupZ);
+ QCOMPARE(computeCommand.runType(), cloneData.runType);
+ QCOMPARE(0, cloneData.frameCount);
QCOMPARE(computeCommand.id(), creationChangeData->subjectId());
QCOMPARE(computeCommand.isEnabled(), true);
QCOMPARE(computeCommand.isEnabled(), creationChangeData->isNodeEnabled());
@@ -171,6 +194,8 @@ private Q_SLOTS:
QCOMPARE(computeCommand.workGroupZ(), cloneData.workGroupZ);
QCOMPARE(computeCommand.id(), creationChangeData->subjectId());
QCOMPARE(computeCommand.isEnabled(), false);
+ QCOMPARE(computeCommand.runType(), cloneData.runType);
+ QCOMPARE(0, cloneData.frameCount);
QCOMPARE(computeCommand.isEnabled(), creationChangeData->isNodeEnabled());
QCOMPARE(computeCommand.metaObject(), creationChangeData->metaObject());
}
@@ -275,6 +300,144 @@ private Q_SLOTS:
}
+ void checkRunTypeUpdate()
+ {
+ // GIVEN
+ TestArbiter arbiter;
+ Qt3DRender::QComputeCommand computeCommand;
+ arbiter.setArbiterOnNode(&computeCommand);
+
+ {
+ // WHEN
+ computeCommand.setRunType(Qt3DRender::QComputeCommand::Manual);
+ QCoreApplication::processEvents();
+
+ // THEN
+ QCOMPARE(arbiter.events.size(), 1);
+ auto change = arbiter.events.first().staticCast<Qt3DCore::QPropertyUpdatedChange>();
+ QCOMPARE(change->propertyName(), "runType");
+ QCOMPARE(change->value().value<int>(), int(computeCommand.runType()));
+ QCOMPARE(change->type(), Qt3DCore::PropertyUpdated);
+
+ arbiter.events.clear();
+ }
+
+ {
+ // WHEN
+ computeCommand.setRunType(Qt3DRender::QComputeCommand::Manual);
+ QCoreApplication::processEvents();
+
+ // THEN
+ QCOMPARE(arbiter.events.size(), 0);
+ }
+ }
+
+ void checkTrigger()
+ {
+ // GIVEN
+ TestArbiter arbiter;
+ Qt3DRender::QComputeCommand computeCommand;
+ arbiter.setArbiterOnNode(&computeCommand);
+ computeCommand.setRunType(Qt3DRender::QComputeCommand::Manual);
+ computeCommand.setEnabled(false);
+ QCoreApplication::processEvents();
+ arbiter.events.clear();
+
+ {
+ // WHEN
+ computeCommand.trigger(1);
+ QCoreApplication::processEvents();
+
+ // THEN
+ QCOMPARE(arbiter.events.size(), 2);
+ {
+ auto change = arbiter.events.first().staticCast<Qt3DCore::QPropertyUpdatedChange>();
+ QCOMPARE(change->propertyName(), "frameCount");
+ QCOMPARE(change->value().value<int>(), 1);
+ QCOMPARE(change->type(), Qt3DCore::PropertyUpdated);
+ }
+ {
+ auto change = arbiter.events.last().staticCast<Qt3DCore::QPropertyUpdatedChange>();
+ QCOMPARE(change->propertyName(), "enabled");
+ QCOMPARE(change->value().value<bool>(), true);
+ QCOMPARE(change->type(), Qt3DCore::PropertyUpdated);
+ }
+
+ computeCommand.setEnabled(false);
+ QCoreApplication::processEvents();
+ arbiter.events.clear();
+ }
+
+ {
+ // WHEN
+ computeCommand.trigger(2);
+ QCoreApplication::processEvents();
+
+ // THEN
+ QCOMPARE(arbiter.events.size(), 2);
+ {
+ auto change = arbiter.events.first().staticCast<Qt3DCore::QPropertyUpdatedChange>();
+ QCOMPARE(change->propertyName(), "frameCount");
+ QCOMPARE(change->value().value<int>(), 2);
+ QCOMPARE(change->type(), Qt3DCore::PropertyUpdated);
+ }
+ {
+ auto change = arbiter.events.last().staticCast<Qt3DCore::QPropertyUpdatedChange>();
+ QCOMPARE(change->propertyName(), "enabled");
+ QCOMPARE(change->value().value<bool>(), true);
+ QCOMPARE(change->type(), Qt3DCore::PropertyUpdated);
+ }
+
+
+ computeCommand.setEnabled(false);
+ QCoreApplication::processEvents();
+ arbiter.events.clear();
+ }
+
+ {
+ // WHEN
+ computeCommand.trigger(10, 11, 12, 1);
+ QCoreApplication::processEvents();
+
+ // THEN
+ QCOMPARE(arbiter.events.size(), 5);
+ {
+ auto change = arbiter.events.at(0).staticCast<Qt3DCore::QPropertyUpdatedChange>();
+ QCOMPARE(change->propertyName(), "workGroupX");
+ QCOMPARE(change->value().value<int>(), 10);
+ QCOMPARE(change->type(), Qt3DCore::PropertyUpdated);
+ }
+ {
+ auto change = arbiter.events.at(1).staticCast<Qt3DCore::QPropertyUpdatedChange>();
+ QCOMPARE(change->propertyName(), "workGroupY");
+ QCOMPARE(change->value().value<int>(), 11);
+ QCOMPARE(change->type(), Qt3DCore::PropertyUpdated);
+ }
+ {
+ auto change = arbiter.events.at(2).staticCast<Qt3DCore::QPropertyUpdatedChange>();
+ QCOMPARE(change->propertyName(), "workGroupZ");
+ QCOMPARE(change->value().value<int>(), 12);
+ QCOMPARE(change->type(), Qt3DCore::PropertyUpdated);
+ }
+ {
+ auto change = arbiter.events.at(3).staticCast<Qt3DCore::QPropertyUpdatedChange>();
+ QCOMPARE(change->propertyName(), "frameCount");
+ QCOMPARE(change->value().value<int>(), 1);
+ QCOMPARE(change->type(), Qt3DCore::PropertyUpdated);
+ }
+ {
+ auto change = arbiter.events.last().staticCast<Qt3DCore::QPropertyUpdatedChange>();
+ QCOMPARE(change->propertyName(), "enabled");
+ QCOMPARE(change->value().value<bool>(), true);
+ QCOMPARE(change->type(), Qt3DCore::PropertyUpdated);
+ }
+
+ computeCommand.setEnabled(false);
+ QCoreApplication::processEvents();
+ arbiter.events.clear();
+ }
+ }
+
};
QTEST_MAIN(tst_QComputeCommand)
diff --git a/tests/auto/render/qgeometry/tst_qgeometry.cpp b/tests/auto/render/qgeometry/tst_qgeometry.cpp
index b9271a8c0..55b7e752c 100644
--- a/tests/auto/render/qgeometry/tst_qgeometry.cpp
+++ b/tests/auto/render/qgeometry/tst_qgeometry.cpp
@@ -39,9 +39,21 @@
#include <Qt3DCore/QPropertyUpdatedChange>
#include <Qt3DCore/QPropertyNodeAddedChange>
#include <Qt3DCore/QPropertyNodeRemovedChange>
+#include <QSignalSpy>
#include "testpostmanarbiter.h"
+class FakeGeometry : public Qt3DRender::QGeometry
+{
+ Q_OBJECT
+
+public:
+ void sceneChangeEvent(const Qt3DCore::QSceneChangePtr &change) override
+ {
+ Qt3DRender::QGeometry::sceneChangeEvent(change);
+ }
+};
+
class tst_QGeometry: public QObject
{
Q_OBJECT
@@ -180,6 +192,49 @@ private Q_SLOTS:
// THEN Should not crash when the attribute is destroyed (tests for failed removal of destruction helper)
}
}
+
+ void checkExtentUpdates()
+ {
+ // GIVEN
+ TestArbiter arbiter;
+ QScopedPointer<FakeGeometry> geometry(new FakeGeometry());
+ arbiter.setArbiterOnNode(geometry.data());
+ QSignalSpy spyMinExtent(geometry.data(), SIGNAL(minExtentChanged(QVector3D)));
+ QSignalSpy spyMaxExtent(geometry.data(), SIGNAL(maxExtentChanged(QVector3D)));
+
+ // THEN
+ QVERIFY(spyMinExtent.isValid());
+ QVERIFY(spyMaxExtent.isValid());
+ QCOMPARE(geometry->minExtent(), QVector3D());
+ QCOMPARE(geometry->maxExtent(), QVector3D());
+
+ // WHEN
+ Qt3DCore::QPropertyUpdatedChangePtr valueChange(new Qt3DCore::QPropertyUpdatedChange(Qt3DCore::QNodeId()));
+ valueChange->setPropertyName("extent");
+ valueChange->setValue(QVariant::fromValue(QPair<QVector3D, QVector3D>(QVector3D(10.0f, 10.f, 10.0f),
+ QVector3D())));
+ geometry->sceneChangeEvent(valueChange);
+
+ // THEN
+ QCOMPARE(spyMinExtent.count(), 1);
+ QCOMPARE(spyMaxExtent.count(), 0);
+ QCOMPARE(geometry->minExtent(), QVector3D(10.0f, 10.0f, 10.0f));
+
+ spyMinExtent.clear();
+
+ // WHEN
+ valueChange->setPropertyName("extent");
+ valueChange->setValue(QVariant::fromValue(QPair<QVector3D, QVector3D>(QVector3D(10.0f, 10.f, 10.0f),
+ QVector3D(11.0f, 11.f, 11.0f))));
+ geometry->sceneChangeEvent(valueChange);
+
+ // THEN
+ QCOMPARE(spyMinExtent.count(), 0);
+ QCOMPARE(spyMaxExtent.count(), 1);
+ QCOMPARE(geometry->maxExtent(), QVector3D(11.0f, 11.0f, 11.0f));
+
+ spyMaxExtent.clear();
+ }
};
QTEST_MAIN(tst_QGeometry)
diff --git a/tests/auto/render/qobjectpicker/tst_qobjectpicker.cpp b/tests/auto/render/qobjectpicker/tst_qobjectpicker.cpp
index 6714d8a06..bd486774c 100644
--- a/tests/auto/render/qobjectpicker/tst_qobjectpicker.cpp
+++ b/tests/auto/render/qobjectpicker/tst_qobjectpicker.cpp
@@ -31,8 +31,10 @@
#include <Qt3DCore/private/qnode_p.h>
#include <Qt3DCore/private/qscene_p.h>
#include <Qt3DRender/QObjectPicker>
+#include <Qt3DRender/private/qobjectpicker_p.h>
#include <Qt3DRender/QPickEvent>
-
+#include <Qt3DCore/private/qnodecreatedchangegenerator_p.h>
+#include <Qt3DCore/qnodecreatedchange.h>
#include "testpostmanarbiter.h"
class MyObjectPicker : public Qt3DRender::QObjectPicker
@@ -71,6 +73,162 @@ public:
private Q_SLOTS:
+ void checkInitialState()
+ {
+ // GIVEN
+ Qt3DRender::QObjectPicker picker;
+
+ // THEN
+ QCOMPARE(picker.priority(), 0);
+ QCOMPARE(picker.isDragEnabled(), false);
+ QCOMPARE(picker.isHoverEnabled(), false);
+ }
+
+ void checkCreationData()
+ {
+ // GIVEN
+ Qt3DRender::QObjectPicker picker;
+
+ picker.setPriority(1584);
+ picker.setDragEnabled(true);
+ picker.setHoverEnabled(true);
+
+ // WHEN
+ QVector<Qt3DCore::QNodeCreatedChangeBasePtr> creationChanges;
+
+ {
+ Qt3DCore::QNodeCreatedChangeGenerator creationChangeGenerator(&picker);
+ creationChanges = creationChangeGenerator.creationChanges();
+ }
+
+ // THEN
+ {
+ QCOMPARE(creationChanges.size(), 1);
+
+ const auto creationChangeData = qSharedPointerCast<Qt3DCore::QNodeCreatedChange<Qt3DRender::QObjectPickerData>>(creationChanges.first());
+ const Qt3DRender::QObjectPickerData cloneData = creationChangeData->data;
+
+ QCOMPARE(cloneData.priority, 1584);
+ QCOMPARE(cloneData.hoverEnabled, true);
+ QCOMPARE(cloneData.dragEnabled, true);
+ QCOMPARE(picker.id(), creationChangeData->subjectId());
+ QCOMPARE(picker.isEnabled(), true);
+ QCOMPARE(picker.isEnabled(), creationChangeData->isNodeEnabled());
+ QCOMPARE(picker.metaObject(), creationChangeData->metaObject());
+ }
+
+ // WHEN
+ picker.setEnabled(false);
+
+ {
+ Qt3DCore::QNodeCreatedChangeGenerator creationChangeGenerator(&picker);
+ creationChanges = creationChangeGenerator.creationChanges();
+ }
+
+ // THEN
+ {
+ QCOMPARE(creationChanges.size(), 1);
+
+ const auto creationChangeData = qSharedPointerCast<Qt3DCore::QNodeCreatedChange<Qt3DRender::QObjectPickerData>>(creationChanges.first());
+ const Qt3DRender::QObjectPickerData cloneData = creationChangeData->data;
+
+ QCOMPARE(cloneData.priority, 1584);
+ QCOMPARE(cloneData.hoverEnabled, true);
+ QCOMPARE(cloneData.dragEnabled, true);
+ QCOMPARE(picker.id(), creationChangeData->subjectId());
+ QCOMPARE(picker.isEnabled(), false);
+ QCOMPARE(picker.isEnabled(), creationChangeData->isNodeEnabled());
+ QCOMPARE(picker.metaObject(), creationChangeData->metaObject());
+ }
+ }
+
+ void checkPropertyUpdate()
+ {
+ // GIVEN
+ TestArbiter arbiter;
+ Qt3DRender::QObjectPicker picker;
+ arbiter.setArbiterOnNode(&picker);
+
+ {
+ {
+ // WHEN
+ picker.setPriority(883);
+ QCoreApplication::processEvents();
+
+ // THEN
+ QCOMPARE(arbiter.events.size(), 1);
+ QCOMPARE(picker.priority(), 883);
+ auto change = arbiter.events.first().staticCast<Qt3DCore::QPropertyUpdatedChange>();
+ QCOMPARE(change->propertyName(), "priority");
+ QCOMPARE(change->value().value<int>(), picker.priority());
+ QCOMPARE(change->type(), Qt3DCore::PropertyUpdated);
+
+ arbiter.events.clear();
+ }
+
+ {
+ // WHEN
+ picker.setPriority(883);
+ QCoreApplication::processEvents();
+
+ // THEN
+ QCOMPARE(arbiter.events.size(), 0);
+ }
+ }
+ {
+ {
+ // WHEN
+ picker.setDragEnabled(true);
+ QCoreApplication::processEvents();
+
+ // THEN
+ QCOMPARE(arbiter.events.size(), 1);
+ QCOMPARE(picker.isDragEnabled(), true);
+ auto change = arbiter.events.first().staticCast<Qt3DCore::QPropertyUpdatedChange>();
+ QCOMPARE(change->propertyName(), "dragEnabled");
+ QCOMPARE(change->value().value<bool>(), picker.isDragEnabled());
+ QCOMPARE(change->type(), Qt3DCore::PropertyUpdated);
+
+ arbiter.events.clear();
+ }
+
+ {
+ // WHEN
+ picker.setDragEnabled(true);
+ QCoreApplication::processEvents();
+
+ // THEN
+ QCOMPARE(arbiter.events.size(), 0);
+ }
+ }
+ {
+ {
+ // WHEN
+ picker.setHoverEnabled(true);
+ QCoreApplication::processEvents();
+
+ // THEN
+ QCOMPARE(arbiter.events.size(), 1);
+ QCOMPARE(picker.isHoverEnabled(), true);
+ auto change = arbiter.events.first().staticCast<Qt3DCore::QPropertyUpdatedChange>();
+ QCOMPARE(change->propertyName(), "hoverEnabled");
+ QCOMPARE(change->value().value<bool>(), picker.isHoverEnabled());
+ QCOMPARE(change->type(), Qt3DCore::PropertyUpdated);
+
+ arbiter.events.clear();
+ }
+
+ {
+ // WHEN
+ picker.setHoverEnabled(true);
+ QCoreApplication::processEvents();
+
+ // THEN
+ QCOMPARE(arbiter.events.size(), 0);
+ }
+ }
+ }
+
void checkCloning_data()
{
QTest::addColumn<Qt3DRender::QObjectPicker *>("objectPicker");
diff --git a/tests/auto/render/qsetfence/qsetfence.pro b/tests/auto/render/qsetfence/qsetfence.pro
new file mode 100644
index 000000000..4e70559c9
--- /dev/null
+++ b/tests/auto/render/qsetfence/qsetfence.pro
@@ -0,0 +1,12 @@
+TEMPLATE = app
+
+TARGET = tst_qsetfence
+
+QT += 3dcore 3dcore-private 3drender 3drender-private testlib
+
+CONFIG += testcase
+
+SOURCES += tst_qsetfence.cpp
+
+include(../../core/common/common.pri)
+include(../commons/commons.pri)
diff --git a/tests/auto/render/qsetfence/tst_qsetfence.cpp b/tests/auto/render/qsetfence/tst_qsetfence.cpp
new file mode 100644
index 000000000..c602e6f5c
--- /dev/null
+++ b/tests/auto/render/qsetfence/tst_qsetfence.cpp
@@ -0,0 +1,190 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 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 <QtTest/QTest>
+#include <Qt3DRender/qsetfence.h>
+#include <Qt3DRender/private/qsetfence_p.h>
+#include <QObject>
+#include <QSignalSpy>
+#include <Qt3DCore/qpropertyupdatedchange.h>
+#include <Qt3DCore/private/qnodecreatedchangegenerator_p.h>
+#include <Qt3DCore/qnodecreatedchange.h>
+#include <Qt3DCore/private/qscene_p.h>
+#include "testpostmanarbiter.h"
+
+class MySetFence : public Qt3DRender::QSetFence
+{
+public:
+ using Qt3DRender::QSetFence::sceneChangeEvent;
+};
+
+class tst_QSetFence : public QObject
+{
+ Q_OBJECT
+
+private Q_SLOTS:
+
+ void initTestCase()
+ {
+ qRegisterMetaType<Qt3DRender::QSetFence::HandleType>("HandleType");
+ }
+
+ void checkDefaultConstruction()
+ {
+ // GIVEN
+ Qt3DRender::QSetFence setFence;
+
+ // THEN
+ QCOMPARE(setFence.handleType(), Qt3DRender::QSetFence::NoHandle);
+ QCOMPARE(setFence.handle(), QVariant());
+ }
+
+ void checkPropertyChanges()
+ {
+ // GIVEN
+ Qt3DCore::QScene scene;
+ MySetFence setFence;
+
+ Qt3DCore::QNodePrivate::get(&setFence)->setScene(&scene);
+
+ {
+ // WHEN
+ QSignalSpy spy(&setFence, SIGNAL(handleTypeChanged(HandleType)));
+
+ // THEN
+ QVERIFY(spy.isValid());
+
+ // THEN
+ Qt3DCore::QPropertyUpdatedChangePtr valueChange(new Qt3DCore::QPropertyUpdatedChange(Qt3DCore::QNodeId()));
+ valueChange->setPropertyName("handleType");
+ valueChange->setValue(QVariant::fromValue(Qt3DRender::QSetFence::OpenGLFenceId));
+ setFence.sceneChangeEvent(valueChange);
+
+ // THEN
+ QCOMPARE(setFence.handleType(), Qt3DRender::QSetFence::OpenGLFenceId);
+ QCOMPARE(spy.count(), 1);
+
+ // WHEN
+ spy.clear();
+ setFence.sceneChangeEvent(valueChange);
+
+ // THEN
+ QCOMPARE(spy.count(), 0);
+ }
+
+ {
+ // WHEN
+ QSignalSpy spy(&setFence, SIGNAL(handleChanged(QVariant)));
+
+ // THEN
+ QVERIFY(spy.isValid());
+
+ // WHEN
+ Qt3DCore::QPropertyUpdatedChangePtr valueChange(new Qt3DCore::QPropertyUpdatedChange(Qt3DCore::QNodeId()));
+ valueChange->setPropertyName("handle");
+ valueChange->setValue(QVariant(984));
+ setFence.sceneChangeEvent(valueChange);
+
+ // THEN
+ QCOMPARE(setFence.handle(),QVariant(984));
+ QCOMPARE(spy.count(), 1);
+
+ // WHEN
+ spy.clear();
+ setFence.sceneChangeEvent(valueChange);
+
+ // THEN
+ QCOMPARE(spy.count(), 0);
+ }
+ }
+
+ void checkCreationData()
+ {
+ // GIVEN
+ Qt3DRender::QSetFence setFence;
+
+
+ // WHEN
+ QVector<Qt3DCore::QNodeCreatedChangeBasePtr> creationChanges;
+
+ {
+ Qt3DCore::QNodeCreatedChangeGenerator creationChangeGenerator(&setFence);
+ creationChanges = creationChangeGenerator.creationChanges();
+ }
+
+ // THEN
+ {
+ QCOMPARE(creationChanges.size(), 1);
+
+ const auto creationChangeData = qSharedPointerCast<Qt3DCore::QNodeCreatedChange<Qt3DRender::QSetFenceData>>(creationChanges.first());
+ const Qt3DRender::QSetFenceData cloneData = creationChangeData->data;
+
+ QCOMPARE(setFence.id(), creationChangeData->subjectId());
+ QCOMPARE(setFence.isEnabled(), true);
+ QCOMPARE(setFence.isEnabled(), creationChangeData->isNodeEnabled());
+ QCOMPARE(setFence.metaObject(), creationChangeData->metaObject());
+ }
+
+ // WHEN
+ setFence.setEnabled(false);
+
+ {
+ Qt3DCore::QNodeCreatedChangeGenerator creationChangeGenerator(&setFence);
+ creationChanges = creationChangeGenerator.creationChanges();
+ }
+
+ // THEN
+ {
+ QCOMPARE(creationChanges.size(), 1);
+
+ const auto creationChangeData = qSharedPointerCast<Qt3DCore::QNodeCreatedChange<Qt3DRender::QSetFenceData>>(creationChanges.first());
+ const Qt3DRender::QSetFenceData cloneData = creationChangeData->data;
+
+ QCOMPARE(setFence.id(), creationChangeData->subjectId());
+ QCOMPARE(setFence.isEnabled(), false);
+ QCOMPARE(setFence.isEnabled(), creationChangeData->isNodeEnabled());
+ QCOMPARE(setFence.metaObject(), creationChangeData->metaObject());
+ }
+ }
+
+};
+
+QTEST_MAIN(tst_QSetFence)
+
+#include "tst_qsetfence.moc"
diff --git a/tests/auto/render/qshaderprogrambuilder/tst_qshaderprogrambuilder.cpp b/tests/auto/render/qshaderprogrambuilder/tst_qshaderprogrambuilder.cpp
index 93bee22cc..021a3d6c1 100644
--- a/tests/auto/render/qshaderprogrambuilder/tst_qshaderprogrambuilder.cpp
+++ b/tests/auto/render/qshaderprogrambuilder/tst_qshaderprogrambuilder.cpp
@@ -38,12 +38,12 @@
#include <Qt3DCore/qnodecreatedchange.h>
#include "testpostmanarbiter.h"
-class tst_QShaderProgramBuilder : public QObject
+class tst_QShaderProgramBuilder : public Qt3DRender::QShaderProgramBuilder
{
Q_OBJECT
public:
tst_QShaderProgramBuilder()
- : QObject()
+ : Qt3DRender::QShaderProgramBuilder()
{
qRegisterMetaType<Qt3DRender::QShaderProgram*>("Qt3DRender::QShaderProgram*");
}
@@ -63,6 +63,12 @@ private Q_SLOTS:
QCOMPARE(builder.geometryShaderGraph(), QUrl());
QCOMPARE(builder.fragmentShaderGraph(), QUrl());
QCOMPARE(builder.computeShaderGraph(), QUrl());
+ QCOMPARE(builder.vertexShaderCode(), QByteArray());
+ QCOMPARE(builder.fragmentShaderCode(), QByteArray());
+ QCOMPARE(builder.computeShaderCode(), QByteArray());
+ QCOMPARE(builder.geometryShaderCode(), QByteArray());
+ QCOMPARE(builder.tessellationEvaluationShaderCode(), QByteArray());
+ QCOMPARE(builder.tessellationControlShaderCode(), QByteArray());
}
void checkPropertyChanges()
@@ -592,8 +598,90 @@ private Q_SLOTS:
// THEN
QCOMPARE(arbiter.events.size(), 0);
}
+ }
+
+ void checkGeneratedCodePropertyUpdates()
+ {
+ {
+ // WHEN
+ QSignalSpy spy(this, SIGNAL(vertexShaderCodeChanged(QByteArray)));
+ Qt3DCore::QPropertyUpdatedChangePtr valueChange(new Qt3DCore::QPropertyUpdatedChange(Qt3DCore::QNodeId()));
+ valueChange->setPropertyName("generatedShaderCode");
+ valueChange->setValue(QVariant::fromValue(QPair<int, QByteArray>(int(Qt3DRender::QShaderProgram::Vertex), QByteArrayLiteral("vertex"))));
+ sceneChangeEvent(valueChange);
+
+ // THEN
+ QVERIFY(spy.isValid());
+ QCOMPARE(spy.count(), 1);
+ QCOMPARE(vertexShaderCode(), QByteArrayLiteral("vertex"));
+ }
+ {
+ // WHEN
+ QSignalSpy spy(this, SIGNAL(fragmentShaderCodeChanged(QByteArray)));
+ Qt3DCore::QPropertyUpdatedChangePtr valueChange(new Qt3DCore::QPropertyUpdatedChange(Qt3DCore::QNodeId()));
+ valueChange->setPropertyName("generatedShaderCode");
+ valueChange->setValue(QVariant::fromValue(QPair<int, QByteArray>(int(Qt3DRender::QShaderProgram::Fragment), QByteArrayLiteral("fragment"))));
+ sceneChangeEvent(valueChange);
+ // THEN
+ QVERIFY(spy.isValid());
+ QCOMPARE(spy.count(), 1);
+ QCOMPARE(fragmentShaderCode(), QByteArrayLiteral("fragment"));
+ }
+ {
+ // WHEN
+ QSignalSpy spy(this, SIGNAL(geometryShaderCodeChanged(QByteArray)));
+ Qt3DCore::QPropertyUpdatedChangePtr valueChange(new Qt3DCore::QPropertyUpdatedChange(Qt3DCore::QNodeId()));
+ valueChange->setPropertyName("generatedShaderCode");
+ valueChange->setValue(QVariant::fromValue(QPair<int, QByteArray>(int(Qt3DRender::QShaderProgram::Geometry), QByteArrayLiteral("geometry"))));
+ sceneChangeEvent(valueChange);
+
+ // THEN
+ QVERIFY(spy.isValid());
+ QCOMPARE(spy.count(), 1);
+ QCOMPARE(geometryShaderCode(), QByteArrayLiteral("geometry"));
+ }
+ {
+ // WHEN
+ QSignalSpy spy(this, SIGNAL(computeShaderCodeChanged(QByteArray)));
+ Qt3DCore::QPropertyUpdatedChangePtr valueChange(new Qt3DCore::QPropertyUpdatedChange(Qt3DCore::QNodeId()));
+ valueChange->setPropertyName("generatedShaderCode");
+ valueChange->setValue(QVariant::fromValue(QPair<int, QByteArray>(int(Qt3DRender::QShaderProgram::Compute), QByteArrayLiteral("compute"))));
+ sceneChangeEvent(valueChange);
+
+ // THEN
+ QVERIFY(spy.isValid());
+ QCOMPARE(spy.count(), 1);
+ QCOMPARE(computeShaderCode(), QByteArrayLiteral("compute"));
+ }
+ {
+ // WHEN
+ QSignalSpy spy(this, SIGNAL(tessellationControlShaderCodeChanged(QByteArray)));
+ Qt3DCore::QPropertyUpdatedChangePtr valueChange(new Qt3DCore::QPropertyUpdatedChange(Qt3DCore::QNodeId()));
+ valueChange->setPropertyName("generatedShaderCode");
+ valueChange->setValue(QVariant::fromValue(QPair<int, QByteArray>(int(Qt3DRender::QShaderProgram::TessellationControl), QByteArrayLiteral("control"))));
+ sceneChangeEvent(valueChange);
+
+ // THEN
+ QVERIFY(spy.isValid());
+ QCOMPARE(spy.count(), 1);
+ QCOMPARE(tessellationControlShaderCode(), QByteArrayLiteral("control"));
+ }
+ {
+ // WHEN
+ QSignalSpy spy(this, SIGNAL(tessellationEvaluationShaderCodeChanged(QByteArray)));
+ Qt3DCore::QPropertyUpdatedChangePtr valueChange(new Qt3DCore::QPropertyUpdatedChange(Qt3DCore::QNodeId()));
+ valueChange->setPropertyName("generatedShaderCode");
+ valueChange->setValue(QVariant::fromValue(QPair<int, QByteArray>(int(Qt3DRender::QShaderProgram::TessellationEvaluation), QByteArrayLiteral("eval"))));
+ sceneChangeEvent(valueChange);
+
+ // THEN
+ QVERIFY(spy.isValid());
+ QCOMPARE(spy.count(), 1);
+ QCOMPARE(tessellationEvaluationShaderCode(), QByteArrayLiteral("eval"));
+ }
}
+
};
QTEST_MAIN(tst_QShaderProgramBuilder)
diff --git a/tests/auto/render/qsharedgltexture/qsharedgltexture.pro b/tests/auto/render/qsharedgltexture/qsharedgltexture.pro
new file mode 100644
index 000000000..a5c76aa7d
--- /dev/null
+++ b/tests/auto/render/qsharedgltexture/qsharedgltexture.pro
@@ -0,0 +1,12 @@
+TEMPLATE = app
+
+TARGET = tst_qsharedgltexture
+
+QT += 3dcore 3dcore-private 3drender 3drender-private testlib
+
+CONFIG += testcase
+
+SOURCES += tst_qsharedgltexture.cpp
+
+include(../../core/common/common.pri)
+include(../commons/commons.pri)
diff --git a/tests/auto/render/qsharedgltexture/tst_qsharedgltexture.cpp b/tests/auto/render/qsharedgltexture/tst_qsharedgltexture.cpp
new file mode 100644
index 000000000..1a1db60e4
--- /dev/null
+++ b/tests/auto/render/qsharedgltexture/tst_qsharedgltexture.cpp
@@ -0,0 +1,171 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 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/qtexture.h>
+#include <Qt3DRender/private/qtexture_p.h>
+#include <QObject>
+#include <QSignalSpy>
+#include <Qt3DCore/qpropertyupdatedchange.h>
+#include <Qt3DCore/private/qnodecreatedchangegenerator_p.h>
+#include <Qt3DCore/qnodecreatedchange.h>
+#include "testpostmanarbiter.h"
+
+class tst_QSharedGLTexture : public QObject
+{
+ Q_OBJECT
+
+private Q_SLOTS:
+
+ void checkDefaultConstruction()
+ {
+ // GIVEN
+ Qt3DRender::QSharedGLTexture glTexture;
+
+ // THEN
+ QCOMPARE(glTexture.textureId(), -1);
+ QCOMPARE(glTexture.target(), Qt3DRender::QAbstractTexture::TargetAutomatic);
+ }
+
+ void checkPropertyChanges()
+ {
+ // GIVEN
+ Qt3DRender::QSharedGLTexture glTexture;
+
+ {
+ // WHEN
+ QSignalSpy spy(&glTexture, SIGNAL(textureIdChanged(int)));
+ const int newValue = 883;
+ glTexture.setTextureId(newValue);
+
+ // THEN
+ QVERIFY(spy.isValid());
+ QCOMPARE(glTexture.textureId(), newValue);
+ QCOMPARE(spy.count(), 1);
+
+ // WHEN
+ spy.clear();
+ glTexture.setTextureId(newValue);
+
+ // THEN
+ QCOMPARE(glTexture.textureId(), newValue);
+ QCOMPARE(spy.count(), 0);
+ }
+ }
+
+ void checkCreationData()
+ {
+ // GIVEN
+ Qt3DRender::QSharedGLTexture glTexture;
+
+ glTexture.setTextureId(1200);
+
+ // WHEN
+ QVector<Qt3DCore::QNodeCreatedChangeBasePtr> creationChanges;
+
+ {
+ Qt3DCore::QNodeCreatedChangeGenerator creationChangeGenerator(&glTexture);
+ creationChanges = creationChangeGenerator.creationChanges();
+ }
+
+ // THEN
+ {
+ QCOMPARE(creationChanges.size(), 1);
+
+ const auto creationChangeData = qSharedPointerCast<Qt3DCore::QNodeCreatedChange<Qt3DRender::QAbstractTextureData>>(creationChanges.first());
+ const Qt3DRender::QAbstractTextureData cloneData = creationChangeData->data;
+
+ QCOMPARE(glTexture.id(), creationChangeData->subjectId());
+ QCOMPARE(glTexture.isEnabled(), true);
+ QCOMPARE(glTexture.isEnabled(), creationChangeData->isNodeEnabled());
+ QCOMPARE(glTexture.metaObject(), creationChangeData->metaObject());
+ QCOMPARE(cloneData.sharedTextureId, 1200);
+ }
+
+ // WHEN
+ glTexture.setEnabled(false);
+
+ {
+ Qt3DCore::QNodeCreatedChangeGenerator creationChangeGenerator(&glTexture);
+ creationChanges = creationChangeGenerator.creationChanges();
+ }
+
+ // THEN
+ {
+ QCOMPARE(creationChanges.size(), 1);
+
+ const auto creationChangeData = qSharedPointerCast<Qt3DCore::QNodeCreatedChange<Qt3DRender::QAbstractTextureData>>(creationChanges.first());
+ const Qt3DRender::QAbstractTextureData cloneData = creationChangeData->data;
+
+ QCOMPARE(glTexture.id(), creationChangeData->subjectId());
+ QCOMPARE(glTexture.isEnabled(), false);
+ QCOMPARE(glTexture.isEnabled(), creationChangeData->isNodeEnabled());
+ QCOMPARE(glTexture.metaObject(), creationChangeData->metaObject());
+ QCOMPARE(cloneData.sharedTextureId, 1200);
+ }
+ }
+
+ void checkTextureIdUpdate()
+ {
+ // GIVEN
+ TestArbiter arbiter;
+ Qt3DRender::QSharedGLTexture glTexture;
+ arbiter.setArbiterOnNode(&glTexture);
+
+ {
+ // WHEN
+ glTexture.setTextureId(1584);
+ QCoreApplication::processEvents();
+
+ // THEN
+ QCOMPARE(arbiter.events.size(), 1);
+ const auto change = arbiter.events.first().staticCast<Qt3DCore::QPropertyUpdatedChange>();
+ QCOMPARE(change->propertyName(), "textureId");
+ QCOMPARE(change->type(), Qt3DCore::PropertyUpdated);
+ QCOMPARE(change->value().toInt(), 1584);
+
+ arbiter.events.clear();
+ }
+
+ {
+ // WHEN
+ glTexture.setTextureId(1584);
+ QCoreApplication::processEvents();
+
+ // THEN
+ QCOMPARE(arbiter.events.size(), 0);
+ }
+
+ }
+
+};
+
+QTEST_MAIN(tst_QSharedGLTexture)
+
+#include "tst_qsharedgltexture.moc"
diff --git a/tests/auto/render/qwaitfence/qwaitfence.pro b/tests/auto/render/qwaitfence/qwaitfence.pro
new file mode 100644
index 000000000..18ac21088
--- /dev/null
+++ b/tests/auto/render/qwaitfence/qwaitfence.pro
@@ -0,0 +1,12 @@
+TEMPLATE = app
+
+TARGET = tst_qwaitfence
+
+QT += 3dcore 3dcore-private 3drender 3drender-private testlib
+
+CONFIG += testcase
+
+SOURCES += tst_qwaitfence.cpp
+
+include(../../core/common/common.pri)
+include(../commons/commons.pri)
diff --git a/tests/auto/render/qwaitfence/tst_qwaitfence.cpp b/tests/auto/render/qwaitfence/tst_qwaitfence.cpp
new file mode 100644
index 000000000..ab3ae9b4d
--- /dev/null
+++ b/tests/auto/render/qwaitfence/tst_qwaitfence.cpp
@@ -0,0 +1,349 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 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 <QtTest/QTest>
+#include <Qt3DRender/qwaitfence.h>
+#include <Qt3DRender/private/qwaitfence_p.h>
+#include <QObject>
+#include <QSignalSpy>
+#include <Qt3DCore/qpropertyupdatedchange.h>
+#include <Qt3DCore/private/qnodecreatedchangegenerator_p.h>
+#include <Qt3DCore/qnodecreatedchange.h>
+#include "testpostmanarbiter.h"
+
+class tst_QWaitFence : public QObject
+{
+ Q_OBJECT
+
+private Q_SLOTS:
+
+ void initTestCase()
+ {
+ qRegisterMetaType<Qt3DRender::QWaitFence::HandleType>("HandleType");
+ }
+
+ void checkDefaultConstruction()
+ {
+ // GIVEN
+ Qt3DRender::QWaitFence waitFence;
+
+ // THEN
+ QCOMPARE(waitFence.handleType(), Qt3DRender::QWaitFence::NoHandle);
+ QCOMPARE(waitFence.handle(), QVariant());
+ QCOMPARE(waitFence.waitOnCPU(), false);
+ QCOMPARE(waitFence.timeout(), quint64(-1));
+ }
+
+ void checkPropertyChanges()
+ {
+ // GIVEN
+ Qt3DRender::QWaitFence waitFence;
+
+ {
+ // WHEN
+ QSignalSpy spy(&waitFence, SIGNAL(handleTypeChanged(HandleType)));
+ const Qt3DRender::QWaitFence::HandleType newValue = Qt3DRender::QWaitFence::OpenGLFenceId;
+ waitFence.setHandleType(newValue);
+
+ // THEN
+ QVERIFY(spy.isValid());
+ QCOMPARE(waitFence.handleType(), newValue);
+ QCOMPARE(spy.count(), 1);
+
+ // WHEN
+ spy.clear();
+ waitFence.setHandleType(newValue);
+
+ // THEN
+ QCOMPARE(waitFence.handleType(), newValue);
+ QCOMPARE(spy.count(), 0);
+ }
+ {
+ // WHEN
+ QSignalSpy spy(&waitFence, SIGNAL(handleChanged(QVariant)));
+ const QVariant newValue(883);
+ waitFence.setHandle(newValue);
+
+ // THEN
+ QVERIFY(spy.isValid());
+ QCOMPARE(waitFence.handle(), newValue);
+ QCOMPARE(spy.count(), 1);
+
+ // WHEN
+ spy.clear();
+ waitFence.setHandle(newValue);
+
+ // THEN
+ QCOMPARE(waitFence.handle(), newValue);
+ QCOMPARE(spy.count(), 0);
+ }
+ {
+ // WHEN
+ QSignalSpy spy(&waitFence, SIGNAL(waitOnCPUChanged(bool)));
+ const bool newValue = true;
+ waitFence.setWaitOnCPU(newValue);
+
+ // THEN
+ QVERIFY(spy.isValid());
+ QCOMPARE(waitFence.waitOnCPU(), newValue);
+ QCOMPARE(spy.count(), 1);
+
+ // WHEN
+ spy.clear();
+ waitFence.setWaitOnCPU(newValue);
+
+ // THEN
+ QCOMPARE(waitFence.waitOnCPU(), newValue);
+ QCOMPARE(spy.count(), 0);
+ }
+ {
+ // WHEN
+ QSignalSpy spy(&waitFence, SIGNAL(timeoutChanged(quint64)));
+ const quint64 newValue = 984;
+ waitFence.setTimeout(newValue);
+
+ // THEN
+ QVERIFY(spy.isValid());
+ QCOMPARE(waitFence.timeout(), newValue);
+ QCOMPARE(spy.count(), 1);
+
+ // WHEN
+ spy.clear();
+ waitFence.setTimeout(newValue);
+
+ // THEN
+ QCOMPARE(waitFence.timeout(), newValue);
+ QCOMPARE(spy.count(), 0);
+ }
+ }
+
+ void checkCreationData()
+ {
+ // GIVEN
+ Qt3DRender::QWaitFence waitFence;
+
+ waitFence.setHandleType(Qt3DRender::QWaitFence::OpenGLFenceId);
+ waitFence.setHandle(QVariant(1200));
+ waitFence.setWaitOnCPU(true);
+ waitFence.setTimeout(1584);
+
+ // WHEN
+ QVector<Qt3DCore::QNodeCreatedChangeBasePtr> creationChanges;
+
+ {
+ Qt3DCore::QNodeCreatedChangeGenerator creationChangeGenerator(&waitFence);
+ creationChanges = creationChangeGenerator.creationChanges();
+ }
+
+ // THEN
+ {
+ QCOMPARE(creationChanges.size(), 1);
+
+ const auto creationChangeData = qSharedPointerCast<Qt3DCore::QNodeCreatedChange<Qt3DRender::QWaitFenceData>>(creationChanges.first());
+ const Qt3DRender::QWaitFenceData cloneData = creationChangeData->data;
+
+ QCOMPARE(waitFence.handleType(), cloneData.handleType);
+ QCOMPARE(waitFence.handle(), cloneData.handle);
+ QCOMPARE(waitFence.waitOnCPU(), cloneData.waitOnCPU);
+ QCOMPARE(waitFence.timeout(), cloneData.timeout);
+ QCOMPARE(waitFence.id(), creationChangeData->subjectId());
+ QCOMPARE(waitFence.isEnabled(), true);
+ QCOMPARE(waitFence.isEnabled(), creationChangeData->isNodeEnabled());
+ QCOMPARE(waitFence.metaObject(), creationChangeData->metaObject());
+ }
+
+ // WHEN
+ waitFence.setEnabled(false);
+
+ {
+ Qt3DCore::QNodeCreatedChangeGenerator creationChangeGenerator(&waitFence);
+ creationChanges = creationChangeGenerator.creationChanges();
+ }
+
+ // THEN
+ {
+ QCOMPARE(creationChanges.size(), 1);
+
+ const auto creationChangeData = qSharedPointerCast<Qt3DCore::QNodeCreatedChange<Qt3DRender::QWaitFenceData>>(creationChanges.first());
+ const Qt3DRender::QWaitFenceData cloneData = creationChangeData->data;
+
+ QCOMPARE(waitFence.handleType(), cloneData.handleType);
+ QCOMPARE(waitFence.handle(), cloneData.handle);
+ QCOMPARE(waitFence.waitOnCPU(), cloneData.waitOnCPU);
+ QCOMPARE(waitFence.timeout(), cloneData.timeout);
+ QCOMPARE(waitFence.id(), creationChangeData->subjectId());
+ QCOMPARE(waitFence.isEnabled(), false);
+ QCOMPARE(waitFence.isEnabled(), creationChangeData->isNodeEnabled());
+ QCOMPARE(waitFence.metaObject(), creationChangeData->metaObject());
+ }
+ }
+
+ void checkHandleTypeUpdate()
+ {
+ // GIVEN
+ TestArbiter arbiter;
+ Qt3DRender::QWaitFence waitFence;
+ arbiter.setArbiterOnNode(&waitFence);
+
+ {
+ // WHEN
+ waitFence.setHandleType(Qt3DRender::QWaitFence::OpenGLFenceId);
+ QCoreApplication::processEvents();
+
+ // THEN
+ QCOMPARE(arbiter.events.size(), 1);
+ auto change = arbiter.events.first().staticCast<Qt3DCore::QPropertyUpdatedChange>();
+ QCOMPARE(change->propertyName(), "handleType");
+ QCOMPARE(change->value().value<Qt3DRender::QWaitFence::HandleType>(), waitFence.handleType());
+ QCOMPARE(change->type(), Qt3DCore::PropertyUpdated);
+
+ arbiter.events.clear();
+ }
+
+ {
+ // WHEN
+ waitFence.setHandleType(Qt3DRender::QWaitFence::OpenGLFenceId);
+ QCoreApplication::processEvents();
+
+ // THEN
+ QCOMPARE(arbiter.events.size(), 0);
+ }
+ }
+
+ void checkHandleUpdate()
+ {
+ // GIVEN
+ TestArbiter arbiter;
+ Qt3DRender::QWaitFence waitFence;
+ arbiter.setArbiterOnNode(&waitFence);
+
+ {
+ // WHEN
+ waitFence.setHandle(QVariant(883));
+ QCoreApplication::processEvents();
+
+ // THEN
+ QCOMPARE(arbiter.events.size(), 1);
+ auto change = arbiter.events.first().staticCast<Qt3DCore::QPropertyUpdatedChange>();
+ QCOMPARE(change->propertyName(), "handle");
+ QCOMPARE(change->value().value<QVariant>(), waitFence.handle());
+ QCOMPARE(change->type(), Qt3DCore::PropertyUpdated);
+
+ arbiter.events.clear();
+ }
+
+ {
+ // WHEN
+ waitFence.setHandle(QVariant(883));
+ QCoreApplication::processEvents();
+
+ // THEN
+ QCOMPARE(arbiter.events.size(), 0);
+ }
+ }
+
+ void checkWaitOnCPUUpdate()
+ {
+ // GIVEN
+ TestArbiter arbiter;
+ Qt3DRender::QWaitFence waitFence;
+ arbiter.setArbiterOnNode(&waitFence);
+
+ {
+ // WHEN
+ waitFence.setWaitOnCPU(true);
+ QCoreApplication::processEvents();
+
+ // THEN
+ QCOMPARE(arbiter.events.size(), 1);
+ auto change = arbiter.events.first().staticCast<Qt3DCore::QPropertyUpdatedChange>();
+ QCOMPARE(change->propertyName(), "waitOnCPU");
+ QCOMPARE(change->value().value<bool>(), waitFence.waitOnCPU());
+ QCOMPARE(change->type(), Qt3DCore::PropertyUpdated);
+
+ arbiter.events.clear();
+ }
+
+ {
+ // WHEN
+ waitFence.setWaitOnCPU(true);
+ QCoreApplication::processEvents();
+
+ // THEN
+ QCOMPARE(arbiter.events.size(), 0);
+ }
+ }
+
+ void checkTimeoutUpdate()
+ {
+ // GIVEN
+ TestArbiter arbiter;
+ Qt3DRender::QWaitFence waitFence;
+ arbiter.setArbiterOnNode(&waitFence);
+
+ {
+ // WHEN
+ waitFence.setTimeout(quint64(600));
+ QCoreApplication::processEvents();
+
+ // THEN
+ QCOMPARE(arbiter.events.size(), 1);
+ auto change = arbiter.events.first().staticCast<Qt3DCore::QPropertyUpdatedChange>();
+ QCOMPARE(change->propertyName(), "timeout");
+ QCOMPARE(change->value().value<quint64>(), waitFence.timeout());
+ QCOMPARE(change->type(), Qt3DCore::PropertyUpdated);
+
+ arbiter.events.clear();
+ }
+
+ {
+ // WHEN
+ waitFence.setTimeout(quint64(600));
+ QCoreApplication::processEvents();
+
+ // THEN
+ QCOMPARE(arbiter.events.size(), 0);
+ }
+ }
+};
+
+QTEST_MAIN(tst_QWaitFence)
+
+#include "tst_qwaitfence.moc"
diff --git a/tests/auto/render/render.pro b/tests/auto/render/render.pro
index 1a510d069..b737d328b 100644
--- a/tests/auto/render/render.pro
+++ b/tests/auto/render/render.pro
@@ -59,6 +59,7 @@ qtConfig(private_tests) {
qparameter \
parameter \
qtextureloader \
+ qsharedgltexture \
qtextureimage \
qabstracttexture \
qabstracttextureimage \
@@ -98,7 +99,11 @@ qtConfig(private_tests) {
raycaster \
qscreenraycaster \
raycastingjob \
- qcamera
+ qcamera \
+ qsetfence \
+ qwaitfence \
+ setfence \
+ waitfence
QT_FOR_CONFIG = 3dcore-private
# TO DO: These could be restored to be executed in all cases
@@ -154,5 +159,5 @@ qtConfig(qt3d-opengl-renderer):qtConfig(private_tests) {
!macos: SUBDIRS += graphicshelpergl4
qtConfig(qt3d-simd-avx2): SUBDIRS += alignedresourcesmanagers-avx
- qtConfig(qt3d-simd-sse2): SUBDIRS += alignedresourcesmanagers-sse
+ qtConfig(qt3d-simd-sse2):!qtConfig(qt3d-simd-avx2): SUBDIRS += alignedresourcesmanagers-sse
}
diff --git a/tests/auto/render/renderer/tst_renderer.cpp b/tests/auto/render/renderer/tst_renderer.cpp
index 98bf6cc07..c89805464 100644
--- a/tests/auto/render/renderer/tst_renderer.cpp
+++ b/tests/auto/render/renderer/tst_renderer.cpp
@@ -44,6 +44,88 @@ public :
~tst_Renderer() {}
private Q_SLOTS:
+
+ void checkPreRenderBinJobs()
+ {
+ // GIVEN
+ Qt3DRender::Render::NodeManagers nodeManagers;
+ Qt3DRender::Render::Renderer renderer(Qt3DRender::QRenderAspect::Synchronous);
+ Qt3DRender::Render::OffscreenSurfaceHelper offscreenHelper(&renderer);
+ Qt3DRender::Render::RenderSettings settings;
+ // owned by FG manager
+ Qt3DRender::Render::ViewportNode *fgRoot = new Qt3DRender::Render::ViewportNode();
+ const Qt3DCore::QNodeId fgRootId = Qt3DCore::QNodeId::createId();
+
+ nodeManagers.frameGraphManager()->appendNode(fgRootId, fgRoot);
+ settings.setActiveFrameGraphId(fgRootId);
+
+ renderer.setNodeManagers(&nodeManagers);
+ renderer.setSettings(&settings);
+ renderer.setOffscreenSurfaceHelper(&offscreenHelper);
+ renderer.initialize();
+
+ // Ensure invoke calls are performed
+ QCoreApplication::processEvents();
+
+ // WHEN (nothing dirty, no buffers, no layers to be rebuilt, no materials to be rebuilt)
+ QVector<Qt3DCore::QAspectJobPtr> jobs = renderer.preRenderingJobs();
+
+ // THEN
+ QCOMPARE(jobs.size(),
+ 1 + // PickBoundingVolumeJob
+ 1); // RayCastingJob
+
+ // WHEN
+ renderer.addRenderCaptureSendRequest(Qt3DCore::QNodeId::createId());
+ jobs = renderer.preRenderingJobs();
+
+ // THEN
+ QCOMPARE(jobs.size(),
+ 1 + // PickBoundingVolumeJob
+ 1 + // RayCastingJob
+ 1); // SendRenderCaptureJob
+
+ // WHEN
+ renderer.m_sendBufferCaptureJob->addRequest({nullptr, {}});
+ jobs = renderer.preRenderingJobs();
+
+ // THEN
+ QCOMPARE(jobs.size(),
+ 1 + // PickBoundingVolumeJob
+ 1 + // RayCastingJob
+ 1); // SendBufferCaptureJob
+ // Note: pending render buffer captures are only cleared when the job is run
+
+ // WHEN
+ renderer.m_updatedSetFences.push_back({Qt3DCore::QNodeId(), nullptr});
+ jobs = renderer.preRenderingJobs();
+
+ // THEN
+ QCOMPARE(jobs.size(),
+ 1 + // PickBoundingVolumeJob
+ 1 + // RayCastingJob
+ 1 + // SendBufferCaptureJob
+ 1); // SendSetFenceHandlesJob
+ // Note: pending set fence handles are only cleared when the job is run
+
+ // WHEN
+ renderer.m_updatedTextureProperties.push_back({{}, {}});
+ jobs = renderer.preRenderingJobs();
+
+ // THEN
+ QCOMPARE(jobs.size(),
+ 1 + // PickBoundingVolumeJob
+ 1 + // RayCastingJob
+ 1 + // SendBufferCaptureJob
+ 1 + // SendSetFenceHandlesJob
+ 1); // SendTextureChangesToFrontend
+
+ // Note: pending texture changes are only cleared when the job is run
+
+ // Properly shutdown command thread
+ renderer.shutdown();
+ }
+
void checkRenderBinJobs()
{
// GIVEN
@@ -90,7 +172,6 @@ private Q_SLOTS:
QCOMPARE(jobs.size(),
1 + // updateLevelOfDetailJob
1 + // cleanupJob
- 1 + // sendBufferCaptureJob
1 + // VAOGatherer
1 + // updateSkinningPaletteJob
singleRenderViewJobCount); // Only valid for the first call to renderBinJobs(), since subsequent calls won't have the renderqueue reset
@@ -98,19 +179,6 @@ private Q_SLOTS:
renderer.clearDirtyBits(Qt3DRender::Render::AbstractRenderer::AllDirty);
// WHEN
- renderer.addRenderCaptureSendRequest(Qt3DCore::QNodeId::createId());
- jobs = renderer.renderBinJobs();
-
- // THEN
- QCOMPARE(jobs.size(),
- 1 + // updateLevelOfDetailJob
- 1 + // cleanupJob
- 1 + // sendBufferCaptureJob
- 1 + // sendRenderCaptureJob
- 1 + // VAOGatherer
- 1); // updateSkinningPaletteJob
-
- // WHEN
renderer.markDirty(Qt3DRender::Render::AbstractRenderer::EntityEnabledDirty, nullptr);
jobs = renderer.renderBinJobs();
@@ -118,7 +186,6 @@ private Q_SLOTS:
QCOMPARE(jobs.size(),
1 + // updateLevelOfDetailJob
1 + // cleanupJob
- 1 + // sendBufferCaptureJob
1 + // VAOGatherer
1 + // updateSkinningPaletteJob
1); // EntityEnabledDirty
@@ -133,7 +200,6 @@ private Q_SLOTS:
QCOMPARE(jobs.size(),
1 + // updateLevelOfDetailJob
1 + // cleanupJob
- 1 + // sendBufferCaptureJob
1 + // VAOGatherer
1 + // WorldTransformJob
1 + // UpdateWorldBoundingVolume
@@ -151,7 +217,6 @@ private Q_SLOTS:
QCOMPARE(jobs.size(),
1 + // updateLevelOfDetailJob
1 + // cleanupJob
- 1 + // sendBufferCaptureJob
1 + // VAOGatherer
1 + // CalculateBoundingVolumeJob
1 + // UpdateMeshTriangleListJob
@@ -168,7 +233,6 @@ private Q_SLOTS:
QCOMPARE(jobs.size(),
1 + // updateLevelOfDetailJob
1 + // cleanupJob
- 1 + // sendBufferCaptureJob
1 + // VAOGatherer
1 + // updateSkinningPaletteJob
1 + // CalculateBoundingVolumeJob
@@ -185,7 +249,6 @@ private Q_SLOTS:
QCOMPARE(jobs.size(),
1 + // updateLevelOfDetailJob
1 + // cleanupJob
- 1 + // sendBufferCaptureJob
1 + // VAOGatherer
1 + // TexturesGathererJob
1 + // updateSkinningPaletteJob
@@ -193,6 +256,19 @@ private Q_SLOTS:
renderer.clearDirtyBits(Qt3DRender::Render::AbstractRenderer::AllDirty);
+
+ // WHEN
+ renderer.markDirty(Qt3DRender::Render::AbstractRenderer::FrameGraphDirty, nullptr);
+ jobs = renderer.renderBinJobs();
+
+ QCOMPARE(jobs.size(),
+ 1 + // updateLevelOfDetailJob
+ 1 + // cleanupJob
+ 1 + // VAOGatherer
+ 1); // updateSkinningPaletteJob
+
+ renderer.clearDirtyBits(Qt3DRender::Render::AbstractRenderer::AllDirty);
+
// WHEN
renderer.markDirty(Qt3DRender::Render::AbstractRenderer::AllDirty, nullptr);
jobs = renderer.renderBinJobs();
@@ -210,7 +286,6 @@ private Q_SLOTS:
1 + // updateSkinningPaletteJob
1 + // updateLevelOfDetailJob
1 + // cleanupJob
- 1 + // sendBufferCaptureJob
1 + // VAOGatherer
1 + // BufferGathererJob
1 + // TexturesGathererJob
diff --git a/tests/auto/render/setfence/setfence.pro b/tests/auto/render/setfence/setfence.pro
new file mode 100644
index 000000000..5e9793c36
--- /dev/null
+++ b/tests/auto/render/setfence/setfence.pro
@@ -0,0 +1,12 @@
+TEMPLATE = app
+
+TARGET = tst_setfence
+
+QT += 3dcore 3dcore-private 3drender 3drender-private testlib
+
+CONFIG += testcase
+
+SOURCES += tst_setfence.cpp
+
+include(../../core/common/common.pri)
+include(../commons/commons.pri)
diff --git a/tests/auto/render/setfence/tst_setfence.cpp b/tests/auto/render/setfence/tst_setfence.cpp
new file mode 100644
index 000000000..82cee4b17
--- /dev/null
+++ b/tests/auto/render/setfence/tst_setfence.cpp
@@ -0,0 +1,163 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 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 <QtTest/QTest>
+#include <Qt3DRender/qsetfence.h>
+#include <Qt3DRender/private/qsetfence_p.h>
+#include <Qt3DRender/private/setfence_p.h>
+#include <Qt3DCore/qpropertyupdatedchange.h>
+#include "qbackendnodetester.h"
+#include "testrenderer.h"
+#include "testpostmanarbiter.h"
+
+class tst_SetFence : public Qt3DCore::QBackendNodeTester
+{
+ Q_OBJECT
+
+private Q_SLOTS:
+
+ void checkInitialState()
+ {
+ // GIVEN
+ Qt3DRender::Render::SetFence backendSetFence;
+
+ // THEN
+ QCOMPARE(backendSetFence.isEnabled(), false);
+ QVERIFY(backendSetFence.peerId().isNull());
+ QCOMPARE(backendSetFence.nodeType(), Qt3DRender::Render::FrameGraphNode::SetFence);
+ }
+
+ void checkInitializeFromPeer()
+ {
+ // GIVEN
+ Qt3DRender::QSetFence setFence;
+
+ {
+ // WHEN
+ Qt3DRender::Render::SetFence backendSetFence;
+ simulateInitialization(&setFence, &backendSetFence);
+
+ // THEN
+ QCOMPARE(backendSetFence.isEnabled(), true);
+ QCOMPARE(backendSetFence.peerId(), setFence.id());
+ }
+ {
+ // WHEN
+ Qt3DRender::Render::SetFence backendSetFence;
+ setFence.setEnabled(false);
+ simulateInitialization(&setFence, &backendSetFence);
+
+ // THEN
+ QCOMPARE(backendSetFence.peerId(), setFence.id());
+ QCOMPARE(backendSetFence.isEnabled(), false);
+ }
+ }
+
+ void checkSceneChangeEvents()
+ {
+ // GIVEN
+ Qt3DRender::Render::SetFence backendSetFence;
+ TestRenderer renderer;
+ backendSetFence.setRenderer(&renderer);
+
+ {
+ // WHEN
+ const bool newValue = false;
+ const auto change = Qt3DCore::QPropertyUpdatedChangePtr::create(Qt3DCore::QNodeId());
+ change->setPropertyName("enabled");
+ change->setValue(newValue);
+ backendSetFence.sceneChangeEvent(change);
+
+ // THEN
+ QCOMPARE(backendSetFence.isEnabled(), newValue);
+ QVERIFY(renderer.dirtyBits() & Qt3DRender::Render::AbstractRenderer::FrameGraphDirty);
+ renderer.clearDirtyBits(Qt3DRender::Render::AbstractRenderer::AllDirty);
+ }
+ }
+
+ void checkSetHandleType()
+ {
+ // GIVEN
+ Qt3DRender::Render::SetFence backendSetFence;
+ TestRenderer renderer;
+ TestArbiter arbiter;
+
+ Qt3DCore::QBackendNodePrivate::get(&backendSetFence)->setArbiter(&arbiter);
+ backendSetFence.setRenderer(&renderer);
+
+ // WHEN
+ backendSetFence.setHandleType(Qt3DRender::QSetFence::OpenGLFenceId);
+
+ // THEN
+ Qt3DCore::QPropertyUpdatedChangePtr change = arbiter.events.first().staticCast<Qt3DCore::QPropertyUpdatedChange>();
+ QCOMPARE(arbiter.events.count(), 1);
+ QCOMPARE(change->propertyName(), "handleType");
+ QCOMPARE(change->value().value<Qt3DRender::QSetFence::HandleType>(), Qt3DRender::QSetFence::OpenGLFenceId);
+
+ arbiter.events.clear();
+ }
+
+ void checkSetHandle()
+ {
+ // GIVEN
+ Qt3DRender::Render::SetFence backendSetFence;
+ TestRenderer renderer;
+ TestArbiter arbiter;
+
+ Qt3DCore::QBackendNodePrivate::get(&backendSetFence)->setArbiter(&arbiter);
+ backendSetFence.setRenderer(&renderer);
+
+ // WHEN
+ backendSetFence.setHandle(QVariant(984));
+
+ // THEN
+ Qt3DCore::QPropertyUpdatedChangePtr change = arbiter.events.first().staticCast<Qt3DCore::QPropertyUpdatedChange>();
+ QCOMPARE(arbiter.events.count(), 1);
+ QCOMPARE(change->propertyName(), "handle");
+ QCOMPARE(change->value(), QVariant(984));
+
+ arbiter.events.clear();
+ }
+};
+
+QTEST_MAIN(tst_SetFence)
+
+#include "tst_setfence.moc"
diff --git a/tests/auto/render/shaderbuilder/tst_shaderbuilder.cpp b/tests/auto/render/shaderbuilder/tst_shaderbuilder.cpp
index 6557acb6c..007bfac7c 100644
--- a/tests/auto/render/shaderbuilder/tst_shaderbuilder.cpp
+++ b/tests/auto/render/shaderbuilder/tst_shaderbuilder.cpp
@@ -28,12 +28,15 @@
#include <QtTest/QTest>
#include <qbackendnodetester.h>
+#include <Qt3DCore/private/qbackendnode_p.h>
#include <Qt3DRender/private/shaderbuilder_p.h>
#include <Qt3DRender/qshaderprogram.h>
#include <Qt3DRender/qshaderprogrambuilder.h>
#include "testrenderer.h"
+#include "testpostmanarbiter.h"
Q_DECLARE_METATYPE(Qt3DRender::Render::ShaderBuilder::ShaderType)
+Q_DECLARE_METATYPE(Qt3DRender::QShaderProgram::ShaderType)
class tst_ShaderBuilder : public Qt3DCore::QBackendNodeTester
{
@@ -539,7 +542,81 @@ private slots:
// THEN
QCOMPARE(backend.shaderGraph(type), graphUrl);
QVERIFY(!backend.isShaderCodeDirty(type));
-// QCOMPARE(backend.shaderCode(type), es2Code);
+ QCOMPARE(backend.shaderCode(type), es2Code);
+ }
+
+ void checkCodeUpdatedNotification_data()
+ {
+ QTest::addColumn<Qt3DRender::Render::ShaderBuilder::ShaderType>("type");
+ QTest::addColumn<Qt3DRender::QShaderProgram::ShaderType>("notificationType");
+
+ QTest::newRow("vertex") << Qt3DRender::Render::ShaderBuilder::Vertex << Qt3DRender::QShaderProgram::Vertex;
+ QTest::newRow("tessControl") << Qt3DRender::Render::ShaderBuilder::TessellationControl << Qt3DRender::QShaderProgram::TessellationControl;
+ QTest::newRow("tessEval") << Qt3DRender::Render::ShaderBuilder::TessellationEvaluation << Qt3DRender::QShaderProgram::TessellationEvaluation;
+ QTest::newRow("geometry") << Qt3DRender::Render::ShaderBuilder::Geometry << Qt3DRender::QShaderProgram::Geometry;
+ QTest::newRow("fragment") << Qt3DRender::Render::ShaderBuilder::Fragment << Qt3DRender::QShaderProgram::Fragment;
+ QTest::newRow("compute") << Qt3DRender::Render::ShaderBuilder::Compute << Qt3DRender::QShaderProgram::Compute;
+ }
+
+
+ void checkCodeUpdatedNotification()
+ {
+ // GIVEN
+ Qt3DRender::Render::ShaderBuilder::setPrototypesFile(":/prototypes.json");
+ QVERIFY(!Qt3DRender::Render::ShaderBuilder::getPrototypeNames().isEmpty());
+ QFETCH(Qt3DRender::Render::ShaderBuilder::ShaderType, type);
+ QFETCH(Qt3DRender::QShaderProgram::ShaderType, notificationType);
+
+ const auto gl3Api = []{
+ auto api = Qt3DRender::GraphicsApiFilterData();
+ api.m_api = Qt3DRender::QGraphicsApiFilter::OpenGL;
+ api.m_profile = Qt3DRender::QGraphicsApiFilter::CoreProfile;
+ api.m_major = 3;
+ api.m_minor = 2;
+ return api;
+ }();
+
+ const auto readCode = [](const QString &suffix) -> QString {
+ const auto filePath = QStringLiteral(":/output.") + suffix;
+ QFile file(filePath);
+ if (!file.open(QFile::ReadOnly | QFile::Text))
+ qFatal("File open failed: %s", qPrintable(filePath));
+ return file.readAll();
+ };
+
+ const auto gl3Code = readCode("gl3");
+
+ Qt3DRender::Render::ShaderBuilder backend;
+ TestArbiter arbiter;
+ Qt3DCore::QBackendNodePrivate::get(&backend)->setArbiter(&arbiter);
+
+
+ // WHEN
+ const auto graphUrl = QUrl::fromEncoded("qrc:/input.json");
+ backend.setShaderGraph(type, graphUrl);
+
+ // THEN
+ QCOMPARE(backend.shaderGraph(type), graphUrl);
+ QVERIFY(backend.isShaderCodeDirty(type));
+ QVERIFY(backend.shaderCode(type).isEmpty());
+
+ // WHEN
+ backend.setGraphicsApi(gl3Api);
+ backend.generateCode(type);
+
+ // THEN
+ QCOMPARE(backend.shaderGraph(type), graphUrl);
+ QVERIFY(!backend.isShaderCodeDirty(type));
+ QCOMPARE(backend.shaderCode(type), gl3Code);
+
+ Qt3DCore::QPropertyUpdatedChangePtr change = arbiter.events.first().staticCast<Qt3DCore::QPropertyUpdatedChange>();
+ QCOMPARE(arbiter.events.count(), 1);
+ QCOMPARE(change->propertyName(), "generatedShaderCode");
+ const QPair<int, QByteArray> value = change->value().value<QPair<int, QByteArray>>();
+ QCOMPARE(value.first, int(notificationType));
+ QCOMPARE(value.second, gl3Code);
+
+ arbiter.events.clear();
}
};
diff --git a/tests/auto/render/texture/tst_texture.cpp b/tests/auto/render/texture/tst_texture.cpp
index d64533732..b0ce782a0 100644
--- a/tests/auto/render/texture/tst_texture.cpp
+++ b/tests/auto/render/texture/tst_texture.cpp
@@ -506,6 +506,19 @@ void tst_RenderTexture::checkPropertyChanges()
backend.unsetDirty();
// WHEN
+ updateChange = QSharedPointer<Qt3DCore::QPropertyUpdatedChange>::create(Qt3DCore::QNodeId());
+ updateChange->setValue(883);
+ updateChange->setPropertyName("textureId");
+ backend.sceneChangeEvent(updateChange);
+
+ // THEN
+ QCOMPARE(backend.sharedTextureId(), 883);
+ QVERIFY(renderer.dirtyBits() & Qt3DRender::Render::AbstractRenderer::TexturesDirty);
+ QVERIFY(backend.dirtyFlags() == Qt3DRender::Render::Texture::DirtySharedTextureId);
+ renderer.clearDirtyBits(Qt3DRender::Render::AbstractRenderer::AllDirty);
+ backend.unsetDirty();
+
+ // WHEN
Qt3DRender::QTextureImage img;
const auto imageAddChange = Qt3DCore::QPropertyNodeAddedChangePtr::create(Qt3DCore::QNodeId(), &img);
imageAddChange->setPropertyName("textureImage");
diff --git a/tests/auto/render/textures/tst_textures.cpp b/tests/auto/render/textures/tst_textures.cpp
index 8bd4b355c..390853e77 100644
--- a/tests/auto/render/textures/tst_textures.cpp
+++ b/tests/auto/render/textures/tst_textures.cpp
@@ -112,6 +112,24 @@ private:
Q_DECLARE_PRIVATE(TestTexture)
};
+class TestSharedGLTexturePrivate : public Qt3DRender::QAbstractTexturePrivate
+{
+};
+
+class TestSharedGLTexture : public Qt3DRender::QAbstractTexture
+{
+public:
+ TestSharedGLTexture(int textureId, Qt3DCore::QNode *p = nullptr)
+ : QAbstractTexture(*new TestSharedGLTexturePrivate(), p)
+ {
+ d_func()->m_sharedTextureId = textureId;
+ }
+
+private:
+ Q_DECLARE_PRIVATE(TestSharedGLTexture)
+};
+
+
/**
* @brief Test QTextureImage
*/
@@ -163,6 +181,11 @@ class tst_RenderTextures : public Qt3DCore::QBackendNodeTester
return tex;
}
+ Qt3DRender::QAbstractTexture *createQTextureWithTextureId(int textureId)
+ {
+ return new TestSharedGLTexture(textureId);
+ }
+
Qt3DRender::Render::Texture *createBackendTexture(Qt3DRender::QAbstractTexture *frontend,
Qt3DRender::Render::TextureManager *texMgr,
Qt3DRender::Render::TextureImageManager *texImgMgr,
@@ -270,6 +293,77 @@ private Q_SLOTS:
renderer.shutdown();
}
+ void shouldCreateDifferentGLTexturesWhenUsingSharedTextureIds()
+ {
+ QScopedPointer<Qt3DRender::Render::NodeManagers> mgrs(new Qt3DRender::Render::NodeManagers());
+ Qt3DRender::Render::Renderer renderer(Qt3DRender::QRenderAspect::Synchronous);
+ renderer.setNodeManagers(mgrs.data());
+
+ // both texture having the same sharedTextureId
+ {
+ // GIVEN
+ Qt3DRender::QAbstractTexture *tex1a = createQTextureWithTextureId(1);
+ Qt3DRender::QAbstractTexture *tex1b = createQTextureWithTextureId(1);
+
+ // WHEN
+ Qt3DRender::Render::Texture *bt1 = createBackendTexture(tex1a,
+ mgrs->textureManager(),
+ mgrs->textureImageManager(),
+ mgrs->textureImageDataManager());
+ Qt3DRender::Render::Texture *bt2 = createBackendTexture(tex1b,
+ mgrs->textureManager(),
+ mgrs->textureImageManager(),
+ mgrs->textureImageDataManager());
+ // THEN
+ QCOMPARE(bt1->sharedTextureId(), 1);
+ QCOMPARE(bt2->sharedTextureId(), 1);
+
+ // WHEN
+ renderer.updateTexture(bt1);
+ renderer.updateTexture(bt2);
+
+ // THEN
+ Qt3DRender::Render::GLTexture *glt1 = mgrs->glTextureManager()->lookupResource(bt1->peerId());
+ Qt3DRender::Render::GLTexture *glt2 = mgrs->glTextureManager()->lookupResource(bt2->peerId());
+ QVERIFY(glt1 != glt2);
+ QCOMPARE(glt1->sharedTextureId(), bt1->sharedTextureId());
+ QCOMPARE(glt2->sharedTextureId(), bt2->sharedTextureId());
+ }
+
+ // textures having a different sharedTextureId
+ {
+ // GIVEN
+ Qt3DRender::QAbstractTexture *tex1a = createQTextureWithTextureId(1);
+ Qt3DRender::QAbstractTexture *tex1b = createQTextureWithTextureId(2);
+
+ // WHEN
+ Qt3DRender::Render::Texture *bt1 = createBackendTexture(tex1a,
+ mgrs->textureManager(),
+ mgrs->textureImageManager(),
+ mgrs->textureImageDataManager());
+ Qt3DRender::Render::Texture *bt2 = createBackendTexture(tex1b,
+ mgrs->textureManager(),
+ mgrs->textureImageManager(),
+ mgrs->textureImageDataManager());
+ // THEN
+ QCOMPARE(bt1->sharedTextureId(), 1);
+ QCOMPARE(bt2->sharedTextureId(), 2);
+
+ // WHEN
+ renderer.updateTexture(bt1);
+ renderer.updateTexture(bt2);
+
+ // THEN
+ Qt3DRender::Render::GLTexture *glt1 = mgrs->glTextureManager()->lookupResource(bt1->peerId());
+ Qt3DRender::Render::GLTexture *glt2 = mgrs->glTextureManager()->lookupResource(bt2->peerId());
+ QVERIFY(glt1 != glt2);
+ QCOMPARE(glt1->sharedTextureId(), bt1->sharedTextureId());
+ QCOMPARE(glt2->sharedTextureId(), bt2->sharedTextureId());
+ }
+
+ renderer.shutdown();
+ }
+
void generatorsShouldCreateSameData()
{
QScopedPointer<Qt3DRender::Render::NodeManagers> mgrs(new Qt3DRender::Render::NodeManagers());
diff --git a/tests/auto/render/waitfence/tst_waitfence.cpp b/tests/auto/render/waitfence/tst_waitfence.cpp
new file mode 100644
index 000000000..8141fbb1c
--- /dev/null
+++ b/tests/auto/render/waitfence/tst_waitfence.cpp
@@ -0,0 +1,186 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 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 <QtTest/QTest>
+#include <Qt3DRender/qwaitfence.h>
+#include <Qt3DRender/private/qwaitfence_p.h>
+#include <Qt3DRender/private/waitfence_p.h>
+#include <Qt3DCore/qpropertyupdatedchange.h>
+#include "qbackendnodetester.h"
+#include "testrenderer.h"
+
+class tst_WaitFence : public Qt3DCore::QBackendNodeTester
+{
+ Q_OBJECT
+
+private Q_SLOTS:
+
+ void checkInitialState()
+ {
+ // GIVEN
+ Qt3DRender::Render::WaitFence backendWaitFence;
+
+ // THEN
+ QCOMPARE(backendWaitFence.isEnabled(), false);
+ QVERIFY(backendWaitFence.peerId().isNull());
+ QCOMPARE(backendWaitFence.nodeType(), Qt3DRender::Render::FrameGraphNode::WaitFence);
+ QCOMPARE(backendWaitFence.data().handleType, Qt3DRender::QWaitFence::NoHandle);
+ QCOMPARE(backendWaitFence.data().handle, QVariant());
+ QCOMPARE(backendWaitFence.data().waitOnCPU, false);
+ QCOMPARE(backendWaitFence.data().timeout, quint64(-1));
+ }
+
+ void checkInitializeFromPeer()
+ {
+ // GIVEN
+ Qt3DRender::QWaitFence waitFence;
+ waitFence.setHandle(QVariant(883));
+ waitFence.setWaitOnCPU(true);
+ waitFence.setTimeout(8);
+ waitFence.setHandleType(Qt3DRender::QWaitFence::OpenGLFenceId);
+
+ {
+ // WHEN
+ Qt3DRender::Render::WaitFence backendWaitFence;
+ simulateInitialization(&waitFence, &backendWaitFence);
+
+ // THEN
+ QCOMPARE(backendWaitFence.isEnabled(), true);
+ QCOMPARE(backendWaitFence.peerId(), waitFence.id());
+ QCOMPARE(backendWaitFence.data().handleType, Qt3DRender::QWaitFence::OpenGLFenceId);
+ QCOMPARE(backendWaitFence.data().handle, QVariant(883));
+ QCOMPARE(backendWaitFence.data().waitOnCPU, true);
+ QCOMPARE(backendWaitFence.data().timeout, quint64(8));
+ }
+ {
+ // WHEN
+ Qt3DRender::Render::WaitFence backendWaitFence;
+ waitFence.setEnabled(false);
+ simulateInitialization(&waitFence, &backendWaitFence);
+
+ // THEN
+ QCOMPARE(backendWaitFence.peerId(), waitFence.id());
+ QCOMPARE(backendWaitFence.isEnabled(), false);
+ QCOMPARE(backendWaitFence.data().handleType, Qt3DRender::QWaitFence::OpenGLFenceId);
+ QCOMPARE(backendWaitFence.data().handle, QVariant(883));
+ QCOMPARE(backendWaitFence.data().waitOnCPU, true);
+ QCOMPARE(backendWaitFence.data().timeout, quint64(8));
+ }
+ }
+
+ void checkSceneChangeEvents()
+ {
+ // GIVEN
+ Qt3DRender::Render::WaitFence backendWaitFence;
+ TestRenderer renderer;
+ backendWaitFence.setRenderer(&renderer);
+
+ {
+ // WHEN
+ const bool newValue = false;
+ const auto change = Qt3DCore::QPropertyUpdatedChangePtr::create(Qt3DCore::QNodeId());
+ change->setPropertyName("enabled");
+ change->setValue(newValue);
+ backendWaitFence.sceneChangeEvent(change);
+
+ // THEN
+ QCOMPARE(backendWaitFence.isEnabled(), newValue);
+ QVERIFY(renderer.dirtyBits() & Qt3DRender::Render::AbstractRenderer::FrameGraphDirty);
+ renderer.clearDirtyBits(Qt3DRender::Render::AbstractRenderer::AllDirty);
+ }
+ {
+ // WHEN
+ const QVariant newValue(984);
+ const auto change = Qt3DCore::QPropertyUpdatedChangePtr::create(Qt3DCore::QNodeId());
+ change->setPropertyName("handle");
+ change->setValue(QVariant::fromValue(newValue));
+ backendWaitFence.sceneChangeEvent(change);
+
+ // THEN
+ QCOMPARE(backendWaitFence.data().handle, QVariant(984));
+ QVERIFY(renderer.dirtyBits() & Qt3DRender::Render::AbstractRenderer::FrameGraphDirty);
+ renderer.clearDirtyBits(Qt3DRender::Render::AbstractRenderer::AllDirty);
+ }
+ {
+ // WHEN
+ const Qt3DRender::QWaitFence::HandleType newValue = Qt3DRender::QWaitFence::OpenGLFenceId;
+ const auto change = Qt3DCore::QPropertyUpdatedChangePtr::create(Qt3DCore::QNodeId());
+ change->setPropertyName("handleType");
+ change->setValue(QVariant::fromValue(newValue));
+ backendWaitFence.sceneChangeEvent(change);
+
+ // THEN
+ QCOMPARE(backendWaitFence.data().handleType, Qt3DRender::QWaitFence::OpenGLFenceId);
+ QVERIFY(renderer.dirtyBits() & Qt3DRender::Render::AbstractRenderer::FrameGraphDirty);
+ renderer.clearDirtyBits(Qt3DRender::Render::AbstractRenderer::AllDirty);
+ }
+ {
+ // WHEN
+ const bool newValue = true;
+ const auto change = Qt3DCore::QPropertyUpdatedChangePtr::create(Qt3DCore::QNodeId());
+ change->setPropertyName("waitOnCPU");
+ change->setValue(QVariant::fromValue(newValue));
+ backendWaitFence.sceneChangeEvent(change);
+
+ // THEN
+ QCOMPARE(backendWaitFence.data().waitOnCPU, true);
+ QVERIFY(renderer.dirtyBits() & Qt3DRender::Render::AbstractRenderer::FrameGraphDirty);
+ renderer.clearDirtyBits(Qt3DRender::Render::AbstractRenderer::AllDirty);
+ }
+ {
+ // WHEN
+ const quint64 newValue = 984;
+ const auto change = Qt3DCore::QPropertyUpdatedChangePtr::create(Qt3DCore::QNodeId());
+ change->setPropertyName("timeout");
+ change->setValue(QVariant::fromValue(newValue));
+ backendWaitFence.sceneChangeEvent(change);
+
+ // THEN
+ QCOMPARE(backendWaitFence.data().timeout, quint64(984));
+ QVERIFY(renderer.dirtyBits() & Qt3DRender::Render::AbstractRenderer::FrameGraphDirty);
+ renderer.clearDirtyBits(Qt3DRender::Render::AbstractRenderer::AllDirty);
+ }
+ }
+};
+
+QTEST_MAIN(tst_WaitFence)
+
+#include "tst_waitfence.moc"
diff --git a/tests/auto/render/waitfence/waitfence.pro b/tests/auto/render/waitfence/waitfence.pro
new file mode 100644
index 000000000..cb8f71272
--- /dev/null
+++ b/tests/auto/render/waitfence/waitfence.pro
@@ -0,0 +1,12 @@
+TEMPLATE = app
+
+TARGET = tst_waitfence
+
+QT += 3dcore 3dcore-private 3drender 3drender-private testlib
+
+CONFIG += testcase
+
+SOURCES += tst_waitfence.cpp
+
+include(../../core/common/common.pri)
+include(../commons/commons.pri)
diff --git a/tests/manual/manual.pro b/tests/manual/manual.pro
index 643d03e6e..585928237 100644
--- a/tests/manual/manual.pro
+++ b/tests/manual/manual.pro
@@ -61,6 +61,12 @@ SUBDIRS += \
shared_texture_image \
texture_property_updates
+qtHaveModule(multimedia): {
+ SUBDIRS += \
+ sharedtexture \
+ sharedtextureqml
+}
+
qtHaveModule(widgets): {
SUBDIRS += \
assimp-cpp \
diff --git a/tests/manual/sharedtexture/main.cpp b/tests/manual/sharedtexture/main.cpp
new file mode 100644
index 000000000..a85f90ee6
--- /dev/null
+++ b/tests/manual/sharedtexture/main.cpp
@@ -0,0 +1,159 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 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 <QApplication>
+#include <QPropertyAnimation>
+
+#include <Qt3DCore/QEntity>
+#include <Qt3DCore/QTransform>
+#include <Qt3DCore/QAspectEngine>
+
+#include <Qt3DRender/QCamera>
+#include <Qt3DRender/QCameraLens>
+#include <Qt3DRender/QRenderAspect>
+#include <Qt3DRender/QTexture>
+#include <Qt3DRender/QDirectionalLight>
+
+#include <Qt3DInput/QInputAspect>
+
+#include <Qt3DExtras/QForwardRenderer>
+#include <Qt3DExtras/QDiffuseMapMaterial>
+#include <Qt3DExtras/QCuboidMesh>
+#include <Qt3DExtras/QOrbitCameraController>
+#include <Qt3DExtras/Qt3DWindow>
+
+#include "videoplayer.h"
+
+Qt3DCore::QEntity *createScene(Qt3DExtras::Qt3DWindow *view, Qt3DRender::QAbstractTexture *diffuseTexture)
+{
+ // Root entity
+ Qt3DCore::QEntity *rootEntity = new Qt3DCore::QEntity;
+
+ // Material
+ Qt3DExtras::QDiffuseMapMaterial *material = new Qt3DExtras::QDiffuseMapMaterial(rootEntity);
+ material->setDiffuse(diffuseTexture);
+ material->setAmbient(QColor(30, 30, 30));
+
+ // Sphere
+ Qt3DCore::QEntity *sphereEntity = new Qt3DCore::QEntity(rootEntity);
+ Qt3DExtras::QCuboidMesh *cuboidMesh = new Qt3DExtras::QCuboidMesh;
+ Qt3DCore::QTransform *transform = new Qt3DCore::QTransform;
+
+ transform->setRotationX(180);
+
+ QPropertyAnimation *cubeRotateTransformAnimation = new QPropertyAnimation(transform);
+ cubeRotateTransformAnimation->setTargetObject(transform);
+ cubeRotateTransformAnimation->setPropertyName("rotationY");
+ cubeRotateTransformAnimation->setStartValue(QVariant::fromValue(0));
+ cubeRotateTransformAnimation->setEndValue(QVariant::fromValue(360));
+ cubeRotateTransformAnimation->setDuration(10000);
+ cubeRotateTransformAnimation->setLoopCount(-1);
+ cubeRotateTransformAnimation->start();
+
+ sphereEntity->addComponent(cuboidMesh);
+ sphereEntity->addComponent(transform);
+ sphereEntity->addComponent(material);
+
+ // Camera
+ Qt3DRender::QCamera *camera = view->camera();
+ camera->lens()->setPerspectiveProjection(45.0f, 16.0f/9.0f, 0.1f, 1000.0f);
+ camera->setPosition(QVector3D(0, 0, -5.0f));
+ camera->setViewCenter(QVector3D(0, 0, 0));
+
+ // For camera controls
+ Qt3DExtras::QOrbitCameraController *camController = new Qt3DExtras::QOrbitCameraController(rootEntity);
+ camController->setLinearSpeed( 50.0f );
+ camController->setLookSpeed( 180.0f );
+ camController->setCamera(camera);
+
+ Qt3DRender::QDirectionalLight *light = new Qt3DRender::QDirectionalLight();
+ light->setIntensity(0.8f);
+ light->setWorldDirection(camera->viewVector());
+ rootEntity->addComponent(light);
+
+ return rootEntity;
+}
+
+int main(int argc, char* argv[])
+{
+ QSurfaceFormat format = QSurfaceFormat::defaultFormat();
+ format.setMajorVersion(4);
+ format.setMinorVersion(5);
+ format.setProfile(QSurfaceFormat::CoreProfile);
+ format.setRenderableType(QSurfaceFormat::OpenGL);
+ QSurfaceFormat::setDefaultFormat(format);
+
+ // Will make Qt3D and QOpenGLWidget share a common context
+ QApplication::setAttribute(Qt::AA_ShareOpenGLContexts);
+
+ QApplication app(argc, argv);
+
+ // Multimedia player
+ TextureWidget textureWidget;
+ VideoPlayerThread *videoPlayer = new VideoPlayerThread(&textureWidget);
+ videoPlayer->start();
+
+ textureWidget.resize(800, 600);
+ textureWidget.show();
+
+ // Texture object that Qt3D uses to access the texture from the video player
+ Qt3DRender::QSharedGLTexture *sharedTexture = new Qt3DRender::QSharedGLTexture();
+
+ QObject::connect(&textureWidget, &TextureWidget::textureIdChanged,
+ sharedTexture, &Qt3DRender::QSharedGLTexture::setTextureId);
+
+ // Qt3D Scene
+ Qt3DExtras::Qt3DWindow view;
+ Qt3DCore::QEntity *scene = createScene(&view, sharedTexture);
+ view.setRootEntity(scene);
+ view.show();
+
+ return app.exec();
+}
diff --git a/tests/manual/sharedtexture/sharedtexture.pro b/tests/manual/sharedtexture/sharedtexture.pro
new file mode 100644
index 000000000..b41f43d71
--- /dev/null
+++ b/tests/manual/sharedtexture/sharedtexture.pro
@@ -0,0 +1,12 @@
+!include( ../manual.pri ) {
+ error( "Couldn't find the manual.pri file!" )
+}
+
+QT += widgets 3dcore 3drender 3dinput 3dextras multimedia
+
+SOURCES += \
+ videoplayer.cpp \
+ main.cpp
+
+HEADERS += \
+ videoplayer.h
diff --git a/tests/manual/sharedtexture/videoplayer.cpp b/tests/manual/sharedtexture/videoplayer.cpp
new file mode 100644
index 000000000..f970116b5
--- /dev/null
+++ b/tests/manual/sharedtexture/videoplayer.cpp
@@ -0,0 +1,233 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 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 <QMutexLocker>
+#include <QtMultimedia/QVideoFrame>
+
+#include "videoplayer.h"
+
+TextureWidget::TextureWidget(QWidget *parent)
+ : QOpenGLWidget(parent)
+ , m_texture(QOpenGLTexture::Target2D)
+{
+ // Lock mutex so that we never process a frame until we have been initialized
+ m_mutex.lock();
+}
+
+// Main thread
+void TextureWidget::initializeGL()
+{
+ initializeOpenGLFunctions();
+ glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
+
+ if (!m_shader.addShaderFromSourceCode(QOpenGLShader::Vertex,
+ "#version 330\n"
+ "out vec2 coords;\n"
+ "const vec2 positions[6] = vec2[] ("
+ " vec2(-1.0, 1.0),"
+ " vec2(-1.0, -1.0),"
+ " vec2(1.0, 1.0),"
+ " vec2(1.0, 1.0),"
+ " vec2(-1.0, -1.0),"
+ " vec2(1.0, -1.0));\n"
+ "const vec2 texCoords[6] = vec2[] ("
+ " vec2(0.0, 0.0),"
+ " vec2(0.0, 1.0),"
+ " vec2(1.0, 0.0),"
+ " vec2(1.0, 0.0),"
+ " vec2(0.0, 1.0),"
+ " vec2(1.0, 1.0));\n"
+ "void main() {\n"
+ " coords = texCoords[gl_VertexID];\n"
+ " gl_Position = vec4(positions[gl_VertexID], 0.0, 1.0);\n"
+ "}"))
+ qDebug() << "Failed to load vertex shader" << m_shader.log();
+ if (!m_shader.addShaderFromSourceCode(QOpenGLShader::Fragment,
+ "#version 330\n"
+ "in vec2 coords;\n"
+ "uniform sampler2D video_texture;\n"
+ "out vec4 fragColor;\n"
+ "void main() {\n"
+ " fragColor = texture(video_texture, coords);\n"
+ "}"))
+ qDebug() << "Failed to load fragment shader" << m_shader.log();
+ if (!m_shader.link())
+ qDebug() << "Failed to link shaders" << m_shader.log();
+
+ qDebug() << Q_FUNC_INFO << context()->shareContext();
+
+ m_vao.create();
+ // Allow rendering/frame acquisition to go on
+ m_mutex.unlock();
+}
+
+// Main thread
+void TextureWidget::paintGL()
+{
+ QMutexLocker lock(&m_mutex);
+ glViewport(0, 0, width(), height());
+
+ glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
+
+ if (!m_texture.isCreated())
+ return;
+
+ m_shader.bind();
+
+ m_texture.bind(0);
+ m_shader.setUniformValue("video_texture", 0);
+
+ m_vao.bind();
+ glDrawArrays(GL_TRIANGLES, 0, 6);
+
+ m_vao.release();
+ m_shader.release();
+}
+
+// Video Player thread
+void TextureWidget::setVideoFrame(const QVideoFrame &frame)
+{
+ // Ensure we won't be rendering while we are processing the frame
+ QMutexLocker lock(&m_mutex);
+
+ QVideoFrame f = frame;
+
+ // Map frame
+ if (!f.map(QAbstractVideoBuffer::ReadOnly))
+ return;
+
+ makeCurrent();
+
+ // Create or recreate texture
+ if (m_texture.width() != f.width() || m_texture.height() != f.height()) {
+ if (m_texture.isCreated())
+ m_texture.destroy();
+
+ m_texture.setSize(f.width(), f.height());
+ m_texture.setFormat(QOpenGLTexture::RGBA32F);
+ m_texture.setWrapMode(QOpenGLTexture::ClampToBorder);
+ m_texture.setMinificationFilter(QOpenGLTexture::Nearest);
+ m_texture.setMagnificationFilter(QOpenGLTexture::Nearest);
+ m_texture.allocateStorage();
+
+ m_texture.create();
+ emit textureIdChanged(m_texture.textureId());
+ }
+
+ const QVideoFrame::PixelFormat pFormat = f.pixelFormat();
+ if (pFormat == QVideoFrame::Format_RGB32) {
+ m_texture.setData(QOpenGLTexture::RGBA, QOpenGLTexture::UInt8, f.bits());
+ }
+
+ doneCurrent();
+
+ // Request display udpate
+ QOpenGLWidget::update();
+
+ // Unmap
+ f.unmap();
+}
+
+QList<QVideoFrame::PixelFormat> GLVideoSurface::supportedPixelFormats(QAbstractVideoBuffer::HandleType type) const
+{
+ if (type == QAbstractVideoBuffer::NoHandle)
+ return {
+ QVideoFrame::Format_RGB32,
+ QVideoFrame::Format_ARGB32,
+ QVideoFrame::Format_BGR32,
+ QVideoFrame::Format_BGRA32
+ };
+ return {};
+}
+
+// Video player thread
+bool GLVideoSurface::present(const QVideoFrame &frame)
+{
+ emit onNewFrame(frame);
+ return true;
+}
+
+VideoPlayerThread::VideoPlayerThread(TextureWidget *textureWidget)
+ : QThread(textureWidget)
+ , m_player(new QMediaPlayer(nullptr, QMediaPlayer::VideoSurface))
+ , m_surface(new GLVideoSurface())
+{
+ m_player->moveToThread(this);
+ m_player->setMedia(QUrl("https://www.sample-videos.com/video/mp4/720/big_buck_bunny_720p_1mb.mp4"));
+
+ // Tell player to render on GLVideoSurface
+ m_surface->moveToThread(this);
+ m_player->setVideoOutput(m_surface.get());
+
+ // Display errors
+ QObject::connect(m_player.get(), QOverload<QMediaPlayer::Error>::of(&QMediaPlayer::error),
+ m_player.get(), [this] (QMediaPlayer::Error e) {
+ qDebug() << Q_FUNC_INFO << e << m_player->errorString();
+ });
+
+ // Repeat video indefinitely
+ QObject::connect(m_player.get(), &QMediaPlayer::stateChanged, m_player.get(), [this] (QMediaPlayer::State state) {
+ if (state == QMediaPlayer::StoppedState)
+ m_player->play();
+ });
+
+ // Start playing when thread starts
+ QObject::connect(this, &QThread::started, this, [this] { m_player->play(); });
+
+ // Direct connection between 2 objects living in different threads
+ QObject::connect(m_surface.get(), &GLVideoSurface::onNewFrame,
+ textureWidget, &TextureWidget::setVideoFrame, Qt::DirectConnection);
+}
+
+VideoPlayerThread::~VideoPlayerThread()
+{
+ exit(0);
+ wait();
+}
diff --git a/tests/manual/sharedtexture/videoplayer.h b/tests/manual/sharedtexture/videoplayer.h
new file mode 100644
index 000000000..377ea57fe
--- /dev/null
+++ b/tests/manual/sharedtexture/videoplayer.h
@@ -0,0 +1,122 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 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 <QThread>
+#include <QMutex>
+
+#include <QOpenGLWidget>
+#include <QOpenGLFunctions>
+#include <QOpenGLVertexArrayObject>
+#include <QOpenGLBuffer>
+#include <QOpenGLTexture>
+#include <QOpenGLShaderProgram>
+
+#include <QtMultimedia/QAbstractVideoSurface>
+#include <QtMultimedia/QMediaPlayer>
+
+#include <memory>
+
+class TextureWidget : public QOpenGLWidget, private QOpenGLFunctions
+{
+ Q_OBJECT
+ Q_PROPERTY(int textureId READ textureId NOTIFY textureIdChanged)
+public:
+ TextureWidget(QWidget *parent = nullptr);
+
+ int textureId() { return m_texture.textureId(); }
+
+private:
+ // MainThread
+ void initializeGL() override;
+
+ // Main thread
+ void paintGL() override;
+
+public Q_SLOTS:
+ // Called from Video player thread
+ void setVideoFrame(const QVideoFrame &frame);
+
+Q_SIGNALS:
+ void textureIdChanged(int textureId);
+
+private:
+ QOpenGLVertexArrayObject m_vao;
+ QOpenGLShaderProgram m_shader;
+ QOpenGLTexture m_texture;
+ QMutex m_mutex;
+};
+
+
+class GLVideoSurface : public QAbstractVideoSurface
+{
+ Q_OBJECT
+public:
+ QList<QVideoFrame::PixelFormat> supportedPixelFormats(QAbstractVideoBuffer::HandleType type) const override;
+
+ // Call in VideaPlayerThread context
+ bool present(const QVideoFrame &frame) override;
+
+Q_SIGNALS:
+ void onNewFrame(const QVideoFrame &frame);
+};
+
+
+class VideoPlayerThread : public QThread
+{
+ Q_OBJECT
+public:
+ VideoPlayerThread(TextureWidget *textureWidget);
+ ~VideoPlayerThread();
+
+private:
+ TextureWidget *m_textureWidget;
+ std::unique_ptr<QMediaPlayer> m_player;
+ std::unique_ptr<GLVideoSurface> m_surface;
+};
diff --git a/tests/manual/sharedtextureqml/main.cpp b/tests/manual/sharedtextureqml/main.cpp
new file mode 100644
index 000000000..5c7ae9cff
--- /dev/null
+++ b/tests/manual/sharedtextureqml/main.cpp
@@ -0,0 +1,131 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 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 <QApplication>
+#include <QtQml/QQmlContext>
+#include <QtQuick/QQuickView>
+#include <Qt3DRender/QAbstractTexture>
+#include <QWindow>
+#include <QTimer>
+
+#include <QQuickView>
+#include "videoplayer.h"
+
+
+template<typename Obj>
+QHash<int, QString> enumToNameMap(const char *enumName)
+{
+ const QMetaObject metaObj = Obj::staticMetaObject;
+ const int indexOfEnum = metaObj.indexOfEnumerator(enumName);
+ const QMetaEnum metaEnum = metaObj.enumerator(indexOfEnum);
+ const int keysCount = metaEnum.keyCount();
+
+ QHash<int, QString> v;
+ v.reserve(keysCount);
+ for (int i = 0; i < keysCount; ++i)
+ v[metaEnum.value(i)] = metaEnum.key(i);
+ return v;
+}
+
+
+class EnumNameMapper : public QObject
+{
+ Q_OBJECT
+
+public:
+ Q_INVOKABLE QString statusName(int v) const { return m_statusMap.value(v); }
+ Q_INVOKABLE QString formatName(int v) const { return m_formatMap.value(v); }
+ Q_INVOKABLE QString targetName(int v) const { return m_targetMap.value(v); }
+
+private:
+ const QHash<int, QString> m_statusMap = enumToNameMap<Qt3DRender::QAbstractTexture>("Status");
+ const QHash<int, QString> m_formatMap = enumToNameMap<Qt3DRender::QAbstractTexture>("TextureFormat");
+ const QHash<int, QString> m_targetMap = enumToNameMap<Qt3DRender::QAbstractTexture>("Target");
+};
+
+int main(int argc, char* argv[])
+{
+ QSurfaceFormat format = QSurfaceFormat::defaultFormat();
+ format.setMajorVersion(4);
+ format.setMinorVersion(5);
+ format.setDepthBufferSize(16);
+ format.setStencilBufferSize(8);
+ format.setProfile(QSurfaceFormat::CoreProfile);
+ format.setRenderableType(QSurfaceFormat::OpenGL);
+ QSurfaceFormat::setDefaultFormat(format);
+
+
+ // Make the OpenGLWidget's shared context be qt_gl_global_share_context
+ QApplication::setAttribute(Qt::AA_ShareOpenGLContexts);
+ QApplication app(argc, argv);
+
+ // Multimedia player
+ TextureWidget textureWidget;
+ VideoPlayerThread *videoPlayer = new VideoPlayerThread(&textureWidget);
+ videoPlayer->start();
+
+ textureWidget.resize(800, 600);
+ textureWidget.show();
+
+ // Qt3D QtQuick Scene (uses qt_global_share_context by default)
+ QQuickView view;
+ QQmlContext *ctx = view.rootContext();
+ EnumNameMapper mapper;
+ ctx->setContextProperty(QStringLiteral("_nameMapper"), &mapper);
+ ctx->setContextProperty(QStringLiteral("_textureWidget"), &textureWidget);
+
+ view.resize(800, 600);
+ view.setSource(QUrl("qrc:/main.qml"));
+ view.show();
+
+ return app.exec();
+}
+
+#include "main.moc"
diff --git a/tests/manual/sharedtextureqml/main.qml b/tests/manual/sharedtextureqml/main.qml
new file mode 100644
index 000000000..fd529e51c
--- /dev/null
+++ b/tests/manual/sharedtextureqml/main.qml
@@ -0,0 +1,144 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 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$
+**
+****************************************************************************/
+
+import QtQuick 2.10
+import QtQuick.Scene3D 2.0
+import Qt3D.Core 2.10
+import Qt3D.Render 2.13
+import Qt3D.Input 2.0
+import Qt3D.Extras 2.0
+
+Item {
+ width: 800
+ height: 600
+
+ 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, -4.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 { },
+ DirectionalLight {
+ intensity: 0.8
+ worldDirection: camera.viewVector
+ }
+ ]
+
+
+ NumberAnimation {
+ target: cubeTransform
+ property: "rotationY"
+ duration: 10000
+ from: 0
+ to: 360
+
+ loops: Animation.Infinite
+ running: true
+ }
+
+ SharedGLTexture {
+ id: shaderGLTexture
+ textureId: _textureWidget.textureId
+ }
+
+ Entity {
+ id: cubeEntity
+ readonly property CuboidMesh cuboid: CuboidMesh {}
+ readonly property DiffuseMapMaterial material: DiffuseMapMaterial {
+ diffuse: shaderGLTexture
+ }
+ Transform {
+ id: cubeTransform
+ rotationX: 180
+ }
+
+ components: [ cuboid, material, cubeTransform ]
+ }
+ }
+ }
+
+ Grid {
+ spacing: 10
+ columns: 2
+ Text { text: "Target: " + _nameMapper.targetName(shaderGLTexture.target) }
+ Text { text: "Format: " + _nameMapper.formatName(shaderGLTexture.format) }
+ Text { text: "Width: " + shaderGLTexture.width }
+ Text { text: "Height: " + shaderGLTexture.height }
+ Text { text: "Depth: " + shaderGLTexture.depth }
+ Text { text: "Layers: " + shaderGLTexture.layers }
+ Text { text: "GL Texture Id: " + shaderGLTexture.textureId }
+ }
+
+}
diff --git a/tests/manual/sharedtextureqml/qml.qrc b/tests/manual/sharedtextureqml/qml.qrc
new file mode 100644
index 000000000..5f6483ac3
--- /dev/null
+++ b/tests/manual/sharedtextureqml/qml.qrc
@@ -0,0 +1,5 @@
+<RCC>
+ <qresource prefix="/">
+ <file>main.qml</file>
+ </qresource>
+</RCC>
diff --git a/tests/manual/sharedtextureqml/sharedtextureqml.pro b/tests/manual/sharedtextureqml/sharedtextureqml.pro
new file mode 100644
index 000000000..066bb8446
--- /dev/null
+++ b/tests/manual/sharedtextureqml/sharedtextureqml.pro
@@ -0,0 +1,17 @@
+!include( ../manual.pri ) {
+ error( "Couldn't find the manual.pri file!" )
+}
+
+QT += widgets gui-private 3dcore 3drender 3dinput 3dextras multimedia quick 3dquickextras
+
+SOURCES += \
+ main.cpp \
+ ../sharedtexture/videoplayer.cpp
+
+HEADERS += \
+ ../sharedtexture/videoplayer.h
+
+INCLUDEPATH += ../sharedtexture
+
+RESOURCES += \
+ qml.qrc
diff --git a/tests/manual/texture_property_updates/main.cpp b/tests/manual/texture_property_updates/main.cpp
index e145b0a26..725c6ccc1 100644
--- a/tests/manual/texture_property_updates/main.cpp
+++ b/tests/manual/texture_property_updates/main.cpp
@@ -77,11 +77,13 @@ public:
Q_INVOKABLE QString statusName(int v) const { return m_statusMap.value(v); }
Q_INVOKABLE QString formatName(int v) const { return m_formatMap.value(v); }
Q_INVOKABLE QString targetName(int v) const { return m_targetMap.value(v); }
+ Q_INVOKABLE QString handleTypeName(int v) const { return m_handleTypeMap.value(v); }
private:
const QHash<int, QString> m_statusMap = enumToNameMap<Qt3DRender::QAbstractTexture>("Status");
const QHash<int, QString> m_formatMap = enumToNameMap<Qt3DRender::QAbstractTexture>("TextureFormat");
const QHash<int, QString> m_targetMap = enumToNameMap<Qt3DRender::QAbstractTexture>("Target");
+ const QHash<int, QString> m_handleTypeMap = enumToNameMap<Qt3DRender::QAbstractTexture>("HandleType");
};
int main(int argc, char* argv[])
diff --git a/tests/manual/texture_property_updates/main.qml b/tests/manual/texture_property_updates/main.qml
index f1256c75b..7794dab55 100644
--- a/tests/manual/texture_property_updates/main.qml
+++ b/tests/manual/texture_property_updates/main.qml
@@ -51,7 +51,7 @@
import QtQuick 2.2 as QQ2
import QtQuick.Scene3D 2.0
import Qt3D.Core 2.0
-import Qt3D.Render 2.0
+import Qt3D.Render 2.12
import Qt3D.Input 2.0
import Qt3D.Extras 2.0
@@ -184,6 +184,8 @@ QQ2.Item {
QQ2.Text { text: "Depth: " + model.modelData.depth}
QQ2.Text { text: "Layers: " + model.modelData.layers}
QQ2.Text { text: "Status: " + nameMapper.statusName(model.modelData.status.toString()) }
+ QQ2.Text { text: "HandleType: " + nameMapper.handleTypeName(model.modelData.handleType) }
+ QQ2.Text { text: "Handle: " + model.modelData.handle }
}
}
}