diff options
author | Qt Forward Merge Bot <qt_forward_merge_bot@qt-project.org> | 2020-04-22 10:11:08 +0200 |
---|---|---|
committer | Qt Forward Merge Bot <qt_forward_merge_bot@qt-project.org> | 2020-04-22 10:11:08 +0200 |
commit | 153ad09effc7388d331c478f0cfe697232a89705 (patch) | |
tree | f8cc97601e2a238188b71fc8b2c2f6692fdc576b | |
parent | 652bedbc0824d7887c8983924e54c08eb8833ffb (diff) | |
parent | a01dbe5b0e1b912c7210abc304700020e685aff0 (diff) | |
download | qt3d-153ad09effc7388d331c478f0cfe697232a89705.tar.gz |
Merge remote-tracking branch 'origin/5.15' into 5.15.0
Change-Id: I7eabf19598b9f23919ee803c2e784fcf653eb741
79 files changed, 2851 insertions, 497 deletions
diff --git a/dist/changes-5.14.2 b/dist/changes-5.14.2 new file mode 100644 index 000000000..68a005176 --- /dev/null +++ b/dist/changes-5.14.2 @@ -0,0 +1,20 @@ +Qt 5.14.2 is a bug-fix release. It maintains both forward and backward +compatibility (source and binary) with Qt 5.14.0 through 5.14.1. + +For more details, refer to the online documentation included in this +distribution. The documentation is also available online: + +https://doc.qt.io/qt-5/index.html + +The Qt version 5.14 series is binary compatible with the 5.13.x series. +Applications compiled for 5.13 will continue to run with 5.14. + +Some of the changes listed in this file include issue tracking numbers +corresponding to tasks in the Qt Bug Tracker: + +https://bugreports.qt.io/ + +Each of these identifiers can be entered in the bug tracker to obtain more +information about a particular change. + + - This release contains only minor code improvements. diff --git a/examples/qt3d/basicshapes-cpp/main.cpp b/examples/qt3d/basicshapes-cpp/main.cpp index fffb83a20..a347e9fb2 100644 --- a/examples/qt3d/basicshapes-cpp/main.cpp +++ b/examples/qt3d/basicshapes-cpp/main.cpp @@ -63,8 +63,6 @@ #include <QtWidgets/QCommandLinkButton> #include <QtGui/QScreen> -#include <Qt3DInput/QInputAspect> - #include <Qt3DExtras/qtorusmesh.h> #include <Qt3DRender/qmesh.h> #include <Qt3DRender/qtechnique.h> @@ -103,9 +101,6 @@ int main(int argc, char **argv) widget->setWindowTitle(QStringLiteral("Basic shapes")); - Qt3DInput::QInputAspect *input = new Qt3DInput::QInputAspect; - view->registerAspect(input); - // Root entity Qt3DCore::QEntity *rootEntity = new Qt3DCore::QEntity(); diff --git a/src/animation/backend/handler.cpp b/src/animation/backend/handler.cpp index 84543c264..95363d56f 100644 --- a/src/animation/backend/handler.cpp +++ b/src/animation/backend/handler.cpp @@ -44,6 +44,7 @@ #include <Qt3DAnimation/private/animationlogging_p.h> #include <Qt3DAnimation/private/buildblendtreesjob_p.h> #include <Qt3DAnimation/private/evaluateblendclipanimatorjob_p.h> +#include <Qt3DCore/private/qaspectjob_p.h> QT_BEGIN_NAMESPACE @@ -115,11 +116,7 @@ void Handler::setClipAnimatorRunning(const HClipAnimator &handle, bool running) // If being marked as not running, remove from set of running clips if (!running) { - const auto it = std::find_if(m_runningClipAnimators.begin(), - m_runningClipAnimators.end(), - [handle](const HClipAnimator &h) { return h == handle; }); - if (it != m_runningClipAnimators.end()) - m_runningClipAnimators.erase(it); + m_runningClipAnimators.removeAll(handle); } } @@ -205,13 +202,14 @@ QVector<Qt3DCore::QAspectJobPtr> Handler::jobsToExecute(qint64 time) const bool hasFindRunningClipAnimatorsJob = !m_dirtyClipAnimators.isEmpty(); if (hasFindRunningClipAnimatorsJob) { qCDebug(HandlerLogic) << "Added FindRunningClipAnimatorsJob"; - m_findRunningClipAnimatorsJob->removeDependency(QWeakPointer<Qt3DCore::QAspectJob>()); cleanupHandleList(&m_dirtyClipAnimators); m_findRunningClipAnimatorsJob->setDirtyClipAnimators(m_dirtyClipAnimators); + // Only set the dependency once + if (Q_UNLIKELY(m_findRunningClipAnimatorsJob->dependencies().empty())) + m_findRunningClipAnimatorsJob->addDependency(m_loadAnimationClipJob); jobs.push_back(m_findRunningClipAnimatorsJob); if (hasLoadAnimationClipJob) - m_findRunningClipAnimatorsJob->addDependency(m_loadAnimationClipJob); - m_dirtyClipAnimators.clear(); + m_dirtyClipAnimators.clear(); } // Rebuild blending trees if a blend tree is dirty @@ -244,13 +242,10 @@ QVector<Qt3DCore::QAspectJobPtr> Handler::jobsToExecute(qint64 time) // Set each job up with an animator to process and set dependencies for (int i = 0; i < newSize; ++i) { m_evaluateClipAnimatorJobs[i]->setClipAnimator(m_runningClipAnimators[i]); - m_evaluateClipAnimatorJobs[i]->removeDependency(QWeakPointer<Qt3DCore::QAspectJob>()); - if (hasLoadAnimationClipJob && - !m_evaluateClipAnimatorJobs[i]->dependencies().contains(m_loadAnimationClipJob)) + Qt3DCore::QAspectJobPrivate::get(m_evaluateClipAnimatorJobs[i].data())->clearDependencies(); + if (hasLoadAnimationClipJob) m_evaluateClipAnimatorJobs[i]->addDependency(m_loadAnimationClipJob); - - if (hasFindRunningClipAnimatorsJob && - !m_evaluateClipAnimatorJobs[i]->dependencies().contains(m_findRunningClipAnimatorsJob)) + if (hasFindRunningClipAnimatorsJob) m_evaluateClipAnimatorJobs[i]->addDependency(m_findRunningClipAnimatorsJob); jobs.push_back(m_evaluateClipAnimatorJobs[i]); } @@ -273,7 +268,7 @@ QVector<Qt3DCore::QAspectJobPtr> Handler::jobsToExecute(qint64 time) // Set each job up with an animator to process and set dependencies for (int i = 0; i < newSize; ++i) { m_evaluateBlendClipAnimatorJobs[i]->setBlendClipAnimator(m_runningBlendedClipAnimators[i]); - m_evaluateBlendClipAnimatorJobs[i]->removeDependency(QWeakPointer<Qt3DCore::QAspectJob>()); + Qt3DCore::QAspectJobPrivate::get(m_evaluateBlendClipAnimatorJobs[i].data())->clearDependencies(); if (hasLoadAnimationClipJob) m_evaluateBlendClipAnimatorJobs[i]->addDependency(m_loadAnimationClipJob); if (hasBuildBlendTreesJob) diff --git a/src/animation/frontend/qchannelmapper.cpp b/src/animation/frontend/qchannelmapper.cpp index c5909adbc..aa88add1a 100644 --- a/src/animation/frontend/qchannelmapper.cpp +++ b/src/animation/frontend/qchannelmapper.cpp @@ -94,7 +94,8 @@ void QChannelMapper::removeMapping(QAbstractChannelMapping *mapping) { Q_ASSERT(mapping); Q_D(QChannelMapper); - d->m_mappings.removeOne(mapping); + if (!d->m_mappings.removeOne(mapping)) + return; d->updateNode(mapping, "mappings", Qt3DCore::PropertyValueRemoved); // Remove bookkeeping connection d->unregisterDestructionHelper(mapping); diff --git a/src/core/jobs/qaspectjob_p.h b/src/core/jobs/qaspectjob_p.h index 0337fa107..0c7802b02 100644 --- a/src/core/jobs/qaspectjob_p.h +++ b/src/core/jobs/qaspectjob_p.h @@ -75,6 +75,8 @@ public: virtual bool isRequired() const; virtual void postFrame(QAspectManager *aspectManager); + void clearDependencies() { m_dependencies.clear(); } + QVector<QWeakPointer<QAspectJob> > m_dependencies; JobId m_jobId; QString m_jobName; diff --git a/src/core/transforms/matrix4x4_avx2_p.h b/src/core/transforms/matrix4x4_avx2_p.h index 0b35f0016..de40ee2a0 100644 --- a/src/core/transforms/matrix4x4_avx2_p.h +++ b/src/core/transforms/matrix4x4_avx2_p.h @@ -485,8 +485,8 @@ public: friend Vector4D operator*(const Vector4D &vector, const Matrix4x4_AVX2 &matrix); friend Vector4D operator*(const Matrix4x4_AVX2 &matrix, const Vector4D &vector); - friend Q_3DCORE_PRIVATE_EXPORT Vector3D operator*(const Vector3D &vector, const Matrix4x4_AVX2 &matrix); - friend Q_3DCORE_PRIVATE_EXPORT Vector3D operator*(const Matrix4x4_AVX2 &matrix, const Vector3D &vector); + friend Vector3D operator*(const Vector3D &vector, const Matrix4x4_AVX2 &matrix); + friend Vector3D operator*(const Matrix4x4_AVX2 &matrix, const Vector3D &vector); friend Q_3DCORE_PRIVATE_EXPORT QDebug operator<<(QDebug dbg, const Matrix4x4_AVX2 &m); diff --git a/src/extras/defaults/qmetalroughmaterial.cpp b/src/extras/defaults/qmetalroughmaterial.cpp index a969593db..7d5ee61ea 100644 --- a/src/extras/defaults/qmetalroughmaterial.cpp +++ b/src/extras/defaults/qmetalroughmaterial.cpp @@ -143,6 +143,11 @@ void QMetalRoughMaterialPrivate::init() m_metalRoughES3Technique->addRenderPass(m_metalRoughES3RenderPass); m_metalRoughEffect->addTechnique(m_metalRoughES3Technique); + // Given parameters a parent + m_baseColorMapParameter->setParent(m_metalRoughEffect); + m_metalnessMapParameter->setParent(m_metalRoughEffect); + m_roughnessMapParameter->setParent(m_metalRoughEffect); + m_metalRoughEffect->addParameter(m_baseColorParameter); m_metalRoughEffect->addParameter(m_metalnessParameter); m_metalRoughEffect->addParameter(m_roughnessParameter); @@ -338,11 +343,13 @@ void QMetalRoughMaterial::setBaseColor(const QVariant &baseColor) layers.removeAll(QStringLiteral("baseColor")); layers.append(QStringLiteral("baseColorMap")); d->m_metalRoughEffect->addParameter(d->m_baseColorMapParameter); - d->m_metalRoughEffect->removeParameter(d->m_baseColorParameter); + if (d->m_metalRoughEffect->parameters().contains(d->m_baseColorParameter)) + d->m_metalRoughEffect->removeParameter(d->m_baseColorParameter); } else { layers.removeAll(QStringLiteral("baseColorMap")); layers.append(QStringLiteral("baseColor")); - d->m_metalRoughEffect->removeParameter(d->m_baseColorMapParameter); + if (d->m_metalRoughEffect->parameters().contains(d->m_baseColorMapParameter)) + d->m_metalRoughEffect->removeParameter(d->m_baseColorMapParameter); d->m_metalRoughEffect->addParameter(d->m_baseColorParameter); } d->m_metalRoughGL3ShaderBuilder->setEnabledLayers(layers); @@ -360,11 +367,13 @@ void QMetalRoughMaterial::setMetalness(const QVariant &metalness) layers.removeAll(QStringLiteral("metalness")); layers.append(QStringLiteral("metalnessMap")); d->m_metalRoughEffect->addParameter(d->m_metalnessMapParameter); - d->m_metalRoughEffect->removeParameter(d->m_metalnessParameter); + if (d->m_metalRoughEffect->parameters().contains(d->m_metalnessParameter)) + d->m_metalRoughEffect->removeParameter(d->m_metalnessParameter); } else { layers.removeAll(QStringLiteral("metalnessMap")); layers.append(QStringLiteral("metalness")); - d->m_metalRoughEffect->removeParameter(d->m_metalnessMapParameter); + if (d->m_metalRoughEffect->parameters().contains(d->m_metalnessMapParameter)) + d->m_metalRoughEffect->removeParameter(d->m_metalnessMapParameter); d->m_metalRoughEffect->addParameter(d->m_metalnessParameter); } d->m_metalRoughGL3ShaderBuilder->setEnabledLayers(layers); @@ -382,11 +391,13 @@ void QMetalRoughMaterial::setRoughness(const QVariant &roughness) layers.removeAll(QStringLiteral("roughness")); layers.append(QStringLiteral("roughnessMap")); d->m_metalRoughEffect->addParameter(d->m_roughnessMapParameter); - d->m_metalRoughEffect->removeParameter(d->m_roughnessParameter); + if (d->m_metalRoughEffect->parameters().contains(d->m_roughnessParameter)) + d->m_metalRoughEffect->removeParameter(d->m_roughnessParameter); } else { layers.removeAll(QStringLiteral("roughnessMap")); layers.append(QStringLiteral("roughness")); - d->m_metalRoughEffect->removeParameter(d->m_roughnessMapParameter); + if (d->m_metalRoughEffect->parameters().contains(d->m_roughnessMapParameter)) + d->m_metalRoughEffect->removeParameter(d->m_roughnessMapParameter); d->m_metalRoughEffect->addParameter(d->m_roughnessParameter); } d->m_metalRoughGL3ShaderBuilder->setEnabledLayers(layers); @@ -406,7 +417,8 @@ void QMetalRoughMaterial::setAmbientOcclusion(const QVariant &ambientOcclusion) } else { layers.removeAll(QStringLiteral("ambientOcclusionMap")); layers.append(QStringLiteral("ambientOcclusion")); - d->m_metalRoughEffect->removeParameter(d->m_ambientOcclusionMapParameter); + if (d->m_metalRoughEffect->parameters().contains(d->m_ambientOcclusionMapParameter)) + d->m_metalRoughEffect->removeParameter(d->m_ambientOcclusionMapParameter); } d->m_metalRoughGL3ShaderBuilder->setEnabledLayers(layers); d->m_metalRoughES3ShaderBuilder->setEnabledLayers(layers); @@ -425,7 +437,8 @@ void QMetalRoughMaterial::setNormal(const QVariant &normal) } else { layers.removeAll(QStringLiteral("normalMap")); layers.append(QStringLiteral("normal")); - d->m_metalRoughEffect->removeParameter(d->m_normalMapParameter); + if (d->m_metalRoughEffect->parameters().contains(d->m_normalMapParameter)) + d->m_metalRoughEffect->removeParameter(d->m_normalMapParameter); } d->m_metalRoughGL3ShaderBuilder->setEnabledLayers(layers); d->m_metalRoughES3ShaderBuilder->setEnabledLayers(layers); diff --git a/src/extras/defaults/qt3dwindow.cpp b/src/extras/defaults/qt3dwindow.cpp index eb0961113..4cf3af250 100644 --- a/src/extras/defaults/qt3dwindow.cpp +++ b/src/extras/defaults/qt3dwindow.cpp @@ -183,6 +183,12 @@ Qt3DWindow::Qt3DWindow(QScreen *screen, Qt3DRender::API api) format.setProfile(QSurfaceFormat::CoreProfile); } #endif + + if (!userRequestedApi.isEmpty()) { + // This is used for RHI + format.setVersion(1, 0); + } + format.setDepthBufferSize(24); format.setSamples(4); format.setStencilBufferSize(8); diff --git a/src/extras/extras.qrc b/src/extras/extras.qrc index 2aedc6622..8d7085264 100644 --- a/src/extras/extras.qrc +++ b/src/extras/extras.qrc @@ -39,5 +39,22 @@ <file>shaders/es2/distancefieldtext.vert</file> <file>shaders/gl3/morphphong.vert</file> <file>shaders/es2/morphphong.vert</file> + <file>shaders/graphs/phong.graph</file> + <file>shaders/rhi/unlittexture.vert</file> + <file>shaders/rhi/unlittexture.frag</file> + <file>shaders/rhi/skybox.vert</file> + <file>shaders/rhi/skybox.frag</file> + <file>shaders/rhi/phong.inc.frag</file> + <file>shaders/rhi/pervertexcolor.vert</file> + <file>shaders/rhi/pervertexcolor.frag</file> + <file>shaders/rhi/morphphong.vert</file> + <file>shaders/rhi/metalrough.inc.frag</file> + <file>shaders/rhi/light.inc.frag</file> + <file>shaders/rhi/gooch.vert</file> + <file>shaders/rhi/gooch.frag</file> + <file>shaders/rhi/distancefieldtext.vert</file> + <file>shaders/rhi/distancefieldtext.frag</file> + <file>shaders/rhi/default.vert</file> + <file>shaders/rhi/coordinatesystems.inc</file> </qresource> </RCC> diff --git a/src/extras/shaders/es2/phong.inc.frag100 b/src/extras/shaders/es2/phong.inc.frag100 index 0c326d0b6..c68c8e41a 100644 --- a/src/extras/shaders/es2/phong.inc.frag100 +++ b/src/extras/shaders/es2/phong.inc.frag100 @@ -66,8 +66,8 @@ void adsModel(const in FP vec3 vpos, const in FP vec3 vnormal, const in FP vec3 if ( lights[0].type != TYPE_DIRECTIONAL ) { s = lights[0].position - vpos; if (lights[0].constantAttenuation != 0.0 - || light[0].linearAttenuation != 0.0 - || light[0].quadraticAttenuation != 0.0) { + || lights[0].linearAttenuation != 0.0 + || lights[0].quadraticAttenuation != 0.0) { FP float dist = length(s); att = 1.0 / (lights[0].constantAttenuation + lights[0].linearAttenuation * dist + lights[0].quadraticAttenuation * dist * dist); } @@ -99,8 +99,8 @@ void adsModel(const in FP vec3 vpos, const in FP vec3 vnormal, const in FP vec3 if ( lights[1].type != TYPE_DIRECTIONAL ) { s = lights[1].position - vpos; if (lights[1].constantAttenuation != 0.0 - || light[1].linearAttenuation != 0.0 - || light[1].quadraticAttenuation != 0.0) { + || lights[1].linearAttenuation != 0.0 + || lights[1].quadraticAttenuation != 0.0) { FP float dist = length(s); att = 1.0 / (lights[1].constantAttenuation + lights[1].linearAttenuation * dist + lights[1].quadraticAttenuation * dist * dist); } diff --git a/src/extras/shaders/graphs/phong.graph b/src/extras/shaders/graphs/phong.graph new file mode 100644 index 000000000..dedeb1067 --- /dev/null +++ b/src/extras/shaders/graphs/phong.graph @@ -0,0 +1,466 @@ +{ + "nodes": [ + { + "uuid": "{00000000-0000-0000-0000-000000000001}", + "type": "input", + "parameters": { + "name": "worldPosition", + "qualifier": { + "type": "QShaderLanguage::StorageQualifier", + "value": "QShaderLanguage::Input" + }, + "type": { + "type": "QShaderLanguage::VariableType", + "value": "QShaderLanguage::Vec3" + } + } + }, + { + "uuid": "{00000000-0000-0000-0000-000000000002}", + "type": "eyePosition" + }, + { + "uuid": "{00000000-0000-0000-0000-000000000003}", + "type": "input", + "parameters": { + "name": "worldNormal", + "qualifier": { + "type": "QShaderLanguage::StorageQualifier", + "value": "QShaderLanguage::Input" + }, + "type": { + "type": "QShaderLanguage::VariableType", + "value": "QShaderLanguage::Vec3" + } + } + }, + { + "uuid": "{00000000-0000-0000-0000-000000000004}", + "type": "input", + "layers": ["normalTexture"], + "parameters": { + "name": "worldTangent", + "qualifier": { + "type": "QShaderLanguage::StorageQualifier", + "value": "QShaderLanguage::Input" + }, + "type": { + "type": "QShaderLanguage::VariableType", + "value": "QShaderLanguage::Vec4" + } + } + }, + { + "uuid": "{00000000-0000-0000-0000-000000000005}", + "type": "input", + "layers": ["diffuseTexture", "specularTexture", "normalTexture"], + "parameters": { + "name": "texCoord", + "qualifier": { + "type": "QShaderLanguage::StorageQualifier", + "value": "QShaderLanguage::Input" + }, + "type": { + "type": "QShaderLanguage::VariableType", + "value": "QShaderLanguage::Vec2" + } + } + }, + { + "uuid": "{00000000-0000-0000-0000-000000000006}", + "type": "input", + "parameters": { + "name": "ka", + "qualifier": { + "type": "QShaderLanguage::StorageQualifier", + "value": "QShaderLanguage::Uniform" + }, + "type": { + "type": "QShaderLanguage::VariableType", + "value": "QShaderLanguage::Vec4" + } + } + }, + { + "uuid": "{00000000-0000-0000-0000-000000000007}", + "type": "input", + "layers": ["diffuse"], + "parameters": { + "name": "kd", + "qualifier": { + "type": "QShaderLanguage::StorageQualifier", + "value": "QShaderLanguage::Uniform" + }, + "type": { + "type": "QShaderLanguage::VariableType", + "value": "QShaderLanguage::Vec4" + } + } + }, + { + "uuid": "{00000000-0000-0000-0000-000000000008}", + "type": "sampleTexture", + "layers": ["diffuseTexture"], + "parameters": { + "name": "diffuseTexture" + } + }, + { + "uuid": "{00000000-0000-0000-0000-000000000009}", + "type": "input", + "layers": ["specular"], + "parameters": { + "name": "ks", + "qualifier": { + "type": "QShaderLanguage::StorageQualifier", + "value": "QShaderLanguage::Uniform" + }, + "type": { + "type": "QShaderLanguage::VariableType", + "value": "QShaderLanguage::Vec4" + } + } + }, + { + "uuid": "{00000000-0000-0000-0000-000000000010}", + "layers": ["specularTexture"], + "type": "sampleTexture", + "parameters": { + "name": "specularTexture" + } + }, + { + "uuid": "{00000000-0000-0000-0000-000000000011}", + "type": "input", + "parameters": { + "name": "shininess", + "qualifier": { + "type": "QShaderLanguage::StorageQualifier", + "value": "QShaderLanguage::Uniform" + }, + "type": { + "type": "QShaderLanguage::VariableType", + "value": "QShaderLanguage::Float" + } + } + }, + { + "uuid": "{00000000-0000-0000-0000-000000000012}", + "type": "subtract", + "parameters": { + "type": { + "type": "QShaderLanguage::VariableType", + "value": "QShaderLanguage::Vec3" + } + } + }, + { + "uuid": "{00000000-0000-0000-0000-000000000013}", + "type": "normalize", + "parameters": { + "type": { + "type": "QShaderLanguage::VariableType", + "value": "QShaderLanguage::Vec3" + } + } + }, + { + "uuid": "{00000000-0000-0000-0000-000000000014}", + "type": "normalize", + "parameters": { + "type": { + "type": "QShaderLanguage::VariableType", + "value": "QShaderLanguage::Vec3" + } + } + }, + { + "uuid": "{00000000-0000-0000-0000-000000000015}", + "type": "worldSpaceToTangentSpaceMatrix", + "layers": ["normalTexture"] + }, + { + "uuid": "{00000000-0000-0000-0000-000000000016}", + "type": "transpose", + "layers": ["normalTexture"], + "parameters": { + "type": { + "type": "QShaderLanguage::VariableType", + "value": "QShaderLanguage::Mat3" + } + } + }, + { + "uuid": "{00000000-0000-0000-0000-000000000017}", + "type": "sampleTexture", + "layers": ["normalTexture"], + "parameters": { + "name": "normalTexture" + } + }, + { + "uuid": "{00000000-0000-0000-0000-000000000018}", + "type": "swizzle", + "layers": ["normalTexture"], + "parameters": { + "fields": "rgb", + "type": { + "type": "QShaderLanguage::VariableType", + "value": "QShaderLanguage::Vec3" + } + } + }, + { + "uuid": "{00000000-0000-0000-0000-000000000019}", + "type": "constant", + "layers": ["normalTexture"], + "parameters": { + "constant": "2.0", + "type": { + "type": "QShaderLanguage::VariableType", + "value": "QShaderLanguage::Float" + } + } + }, + { + "uuid": "{00000000-0000-0000-0000-000000000020}", + "type": "multiply", + "layers": ["normalTexture"], + "parameters": { + "type": { + "type": "QShaderLanguage::VariableType", + "value": "QShaderLanguage::Vec3" + } + } + }, + { + "uuid": "{00000000-0000-0000-0000-000000000021}", + "type": "constant", + "layers": ["normalTexture"], + "parameters": { + "constant": "1.0", + "type": { + "type": "QShaderLanguage::VariableType", + "value": "QShaderLanguage::Vec3" + } + } + }, + { + "uuid": "{00000000-0000-0000-0000-000000000022}", + "type": "subtract", + "parameters": { + "type": { + "type": "QShaderLanguage::VariableType", + "value": "QShaderLanguage::Vec3" + } + } + }, + { + "uuid": "{00000000-0000-0000-0000-000000000023}", + "type": "multiply", + "parameters": { + "type": { + "type": "QShaderLanguage::VariableType", + "value": "QShaderLanguage::Vec3" + } + } + }, + { + "uuid": "{00000000-0000-0000-0000-000000000024}", + "type": "phongFunction" + }, + { + "uuid": "{00000000-0000-0000-0000-000000000025}", + "type": "fragColor" + } + ], + "edges": [ + { + "sourceUuid": "{00000000-0000-0000-0000-000000000001}", + "sourcePort": "value", + "targetUuid": "{00000000-0000-0000-0000-000000000024}", + "targetPort": "worldPosition" + }, + { + "sourceUuid": "{00000000-0000-0000-0000-000000000001}", + "sourcePort": "value", + "targetUuid": "{00000000-0000-0000-0000-000000000012}", + "targetPort": "subtrahend" + }, + { + "sourceUuid": "{00000000-0000-0000-0000-000000000002}", + "sourcePort": "eyePosition", + "targetUuid": "{00000000-0000-0000-0000-000000000012}", + "targetPort": "minuend" + }, + { + "sourceUuid": "{00000000-0000-0000-0000-000000000012}", + "sourcePort": "difference", + "targetUuid": "{00000000-0000-0000-0000-000000000013}", + "targetPort": "input" + }, + { + "sourceUuid": "{00000000-0000-0000-0000-000000000013}", + "sourcePort": "output", + "targetUuid": "{00000000-0000-0000-0000-000000000024}", + "targetPort": "worldView" + }, + { + "sourceUuid": "{00000000-0000-0000-0000-000000000003}", + "sourcePort": "value", + "targetUuid": "{00000000-0000-0000-0000-000000000014}", + "targetPort": "input", + "layers": ["normal"] + }, + { + "sourceUuid": "{00000000-0000-0000-0000-000000000003}", + "sourcePort": "value", + "targetUuid": "{00000000-0000-0000-0000-000000000015}", + "targetPort": "worldNormal", + "layers": ["normalTexture"] + }, + { + "sourceUuid": "{00000000-0000-0000-0000-000000000004}", + "sourcePort": "value", + "targetUuid": "{00000000-0000-0000-0000-000000000015}", + "targetPort": "worldTangent", + "layers": ["normalTexture"] + }, + { + "sourceUuid": "{00000000-0000-0000-0000-000000000015}", + "sourcePort": "matrix", + "targetUuid": "{00000000-0000-0000-0000-000000000016}", + "targetPort": "input", + "layers": ["normalTexture"] + }, + { + "sourceUuid": "{00000000-0000-0000-0000-000000000016}", + "sourcePort": "output", + "targetUuid": "{00000000-0000-0000-0000-000000000023}", + "targetPort": "first", + "layers": ["normalTexture"] + }, + { + "sourceUuid": "{00000000-0000-0000-0000-000000000005}", + "sourcePort": "value", + "targetUuid": "{00000000-0000-0000-0000-000000000017}", + "targetPort": "coord", + "layers": ["normalTexture"] + }, + { + "sourceUuid": "{00000000-0000-0000-0000-000000000017}", + "sourcePort": "color", + "targetUuid": "{00000000-0000-0000-0000-000000000018}", + "targetPort": "input", + "layers": ["normalTexture"] + }, + { + "sourceUuid": "{00000000-0000-0000-0000-000000000018}", + "sourcePort": "output", + "targetUuid": "{00000000-0000-0000-0000-000000000020}", + "targetPort": "first", + "layers": ["normalTexture"] + }, + { + "sourceUuid": "{00000000-0000-0000-0000-000000000019}", + "sourcePort": "value", + "targetUuid": "{00000000-0000-0000-0000-000000000020}", + "targetPort": "second", + "layers": ["normalTexture"] + }, + { + "sourceUuid": "{00000000-0000-0000-0000-000000000020}", + "sourcePort": "product", + "targetUuid": "{00000000-0000-0000-0000-000000000022}", + "targetPort": "minuend", + "layers": ["normalTexture"] + }, + { + "sourceUuid": "{00000000-0000-0000-0000-000000000021}", + "sourcePort": "value", + "targetUuid": "{00000000-0000-0000-0000-000000000022}", + "targetPort": "subtrahend", + "layers": ["normalTexture"] + }, + { + "sourceUuid": "{00000000-0000-0000-0000-000000000022}", + "sourcePort": "difference", + "targetUuid": "{00000000-0000-0000-0000-000000000023}", + "targetPort": "second", + "layers": ["normalTexture"] + }, + { + "sourceUuid": "{00000000-0000-0000-0000-000000000023}", + "sourcePort": "product", + "targetUuid": "{00000000-0000-0000-0000-000000000014}", + "targetPort": "input", + "layers": ["normalTexture"] + }, + { + "sourceUuid": "{00000000-0000-0000-0000-000000000014}", + "sourcePort": "output", + "targetUuid": "{00000000-0000-0000-0000-000000000024}", + "targetPort": "worldNormal" + }, + { + "sourceUuid": "{00000000-0000-0000-0000-000000000006}", + "sourcePort": "value", + "targetUuid": "{00000000-0000-0000-0000-000000000024}", + "targetPort": "ambient" + }, + { + "sourceUuid": "{00000000-0000-0000-0000-000000000007}", + "sourcePort": "value", + "targetUuid": "{00000000-0000-0000-0000-000000000024}", + "targetPort": "diffuse", + "layers": ["diffuse"] + }, + { + "sourceUuid": "{00000000-0000-0000-0000-000000000005}", + "sourcePort": "value", + "targetUuid": "{00000000-0000-0000-0000-000000000008}", + "targetPort": "coord", + "layers": ["diffuseTexture"] + }, + { + "sourceUuid": "{00000000-0000-0000-0000-000000000008}", + "sourcePort": "color", + "targetUuid": "{00000000-0000-0000-0000-000000000024}", + "targetPort": "diffuse", + "layers": ["diffuseTexture"] + }, + { + "sourceUuid": "{00000000-0000-0000-0000-000000000009}", + "sourcePort": "value", + "targetUuid": "{00000000-0000-0000-0000-000000000024}", + "targetPort": "specular", + "layers": ["specular"] + }, + { + "sourceUuid": "{00000000-0000-0000-0000-000000000005}", + "sourcePort": "value", + "targetUuid": "{00000000-0000-0000-0000-000000000010}", + "targetPort": "coord", + "layers": ["specularTexture"] + }, + { + "sourceUuid": "{00000000-0000-0000-0000-000000000010}", + "sourcePort": "color", + "targetUuid": "{00000000-0000-0000-0000-000000000024}", + "targetPort": "specular", + "layers": ["specularTexture"] + }, + { + "sourceUuid": "{00000000-0000-0000-0000-000000000011}", + "sourcePort": "value", + "targetUuid": "{00000000-0000-0000-0000-000000000024}", + "targetPort": "shininess" + }, + { + "sourceUuid": "{00000000-0000-0000-0000-000000000024}", + "sourcePort": "outputColor", + "targetUuid": "{00000000-0000-0000-0000-000000000025}", + "targetPort": "fragColor" + } + ] +} diff --git a/src/extras/shaders/rhi/coordinatesystems.inc b/src/extras/shaders/rhi/coordinatesystems.inc new file mode 100644 index 000000000..ed3d2cb92 --- /dev/null +++ b/src/extras/shaders/rhi/coordinatesystems.inc @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +mat3 calcWorldSpaceToTangentSpaceMatrix(const in vec3 wNormal, const in vec4 wTangent) +{ + // Make the tangent truly orthogonal to the normal by using Gram-Schmidt. + // This allows to build the tangentMatrix below by simply transposing the + // tangent -> eyespace matrix (which would now be orthogonal) + vec3 wFixedTangent = normalize(wTangent.xyz - dot(wTangent.xyz, wNormal) * wNormal); + + // Calculate binormal vector. No "real" need to renormalize it, + // as built by crossing two normal vectors. + // To orient the binormal correctly, use the fourth coordinate of the tangent, + // which is +1 for a right hand system, and -1 for a left hand system. + vec3 wBinormal = cross(wNormal, wFixedTangent.xyz) * wTangent.w; + + // Construct matrix to transform from world space to tangent space + // This is the transpose of the tangentToWorld transformation matrix + mat3 tangentToWorldMatrix = mat3(wFixedTangent, wBinormal, wNormal); + mat3 worldToTangentMatrix = transpose(tangentToWorldMatrix); + return worldToTangentMatrix; +} + diff --git a/src/extras/shaders/rhi/default.vert b/src/extras/shaders/rhi/default.vert new file mode 100644 index 000000000..f97cd099d --- /dev/null +++ b/src/extras/shaders/rhi/default.vert @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#version 150 core + +in vec3 vertexPosition; +in vec3 vertexNormal; +in vec4 vertexTangent; +in vec2 vertexTexCoord; + +out vec3 worldPosition; +out vec3 worldNormal; +out vec4 worldTangent; +out vec2 texCoord; + +uniform mat4 modelMatrix; +uniform mat3 modelNormalMatrix; +uniform mat4 modelViewProjection; + +uniform float texCoordScale; + +void main() +{ + // Pass through scaled texture coordinates + texCoord = vertexTexCoord * texCoordScale; + + // Transform position, normal, and tangent to world space + worldPosition = vec3(modelMatrix * vec4(vertexPosition, 1.0)); + worldNormal = normalize(modelNormalMatrix * vertexNormal); + worldTangent.xyz = normalize(vec3(modelMatrix * vec4(vertexTangent.xyz, 0.0))); + worldTangent.w = vertexTangent.w; + + // Calculate vertex position in clip coordinates + gl_Position = modelViewProjection * vec4(vertexPosition, 1.0); +} diff --git a/src/extras/shaders/rhi/distancefieldtext.frag b/src/extras/shaders/rhi/distancefieldtext.frag new file mode 100644 index 000000000..998fa6e79 --- /dev/null +++ b/src/extras/shaders/rhi/distancefieldtext.frag @@ -0,0 +1,39 @@ +#version 150 core + +uniform sampler2D distanceFieldTexture; +uniform float minAlpha; +uniform float maxAlpha; +uniform float textureSize; +uniform vec4 color; + +in vec2 texCoord; +in float zValue; + +out vec4 fragColor; + +void main() +{ + // determine the scale of the glyph texture within pixel-space coordinates + // (that is, how many pixels are drawn for each texel) + vec2 texelDeltaX = abs(dFdx(texCoord)); + vec2 texelDeltaY = abs(dFdy(texCoord)); + float avgTexelDelta = textureSize * 0.5 * (texelDeltaX.x + texelDeltaX.y + texelDeltaY.x + texelDeltaY.y); + float texScale = 1.0 / avgTexelDelta; + + // scaled to interval [0.0, 0.15] + float devScaleMin = 0.00; + float devScaleMax = 0.15; + float scaled = (clamp(texScale, devScaleMin, devScaleMax) - devScaleMin) / (devScaleMax - devScaleMin); + + // thickness of glyphs should increase a lot for very small glyphs to make them readable + float base = 0.5; + float threshold = base * scaled; + float range = 0.06 / texScale; + + float minAlpha = threshold - range; + float maxAlpha = threshold + range; + + float distVal = texture(distanceFieldTexture, texCoord).r; + fragColor = vec4(color.rgb, color.a * smoothstep(minAlpha, maxAlpha, distVal)); + gl_FragDepth = gl_FragCoord.z - zValue * 0.000001; +} diff --git a/src/extras/shaders/rhi/distancefieldtext.vert b/src/extras/shaders/rhi/distancefieldtext.vert new file mode 100644 index 000000000..f6743001c --- /dev/null +++ b/src/extras/shaders/rhi/distancefieldtext.vert @@ -0,0 +1,19 @@ +#version 150 core + +in vec3 vertexPosition; +in vec2 vertexTexCoord; + +out vec2 texCoord; +out float zValue; + +uniform mat4 modelView; +uniform mat4 mvp; + +void main() +{ + texCoord = vertexTexCoord; + zValue = vertexPosition.z; + + gl_Position = mvp * vec4(vertexPosition.xy, 0.0, 1.0); +} + diff --git a/src/extras/shaders/rhi/gooch.frag b/src/extras/shaders/rhi/gooch.frag new file mode 100644 index 000000000..168a862f8 --- /dev/null +++ b/src/extras/shaders/rhi/gooch.frag @@ -0,0 +1,63 @@ +#version 150 core + +// TODO: Replace with a struct +uniform vec3 kd; // Diffuse reflectivity +uniform vec3 ks; // Specular reflectivity +uniform vec3 kblue; // Cool color +uniform vec3 kyellow; // Warm color +uniform float alpha; // Fraction of diffuse added to kblue +uniform float beta; // Fraction of diffuse added to kyellow +uniform float shininess; // Specular shininess factor + +uniform vec3 eyePosition; + +in vec3 worldPosition; +in vec3 worldNormal; + +out vec4 fragColor; + +#pragma include light.inc.frag + +vec3 goochModel( const in vec3 pos, const in vec3 n ) +{ + // Based upon the original Gooch lighting model paper at: + // http://www.cs.northwestern.edu/~ago820/SIG98/abstract.html + + // Calculate kcool and kwarm from equation (3) + vec3 kcool = clamp(kblue + alpha * kd, 0.0, 1.0); + vec3 kwarm = clamp(kyellow + beta * kd, 0.0, 1.0); + + vec3 result = vec3(0.0); + for (int i = 0; i < lightCount; ++i) { + // Calculate the vector from the light to the fragment + vec3 s = normalize( vec3( lights[i].position ) - pos ); + + // Calculate the cos theta factor mapped onto the range [0,1] + float sDotNFactor = ( 1.0 + dot( s, n ) ) / 2.0; + + // Calculate the tone by blending the kcool and kwarm contributions + // as per equation (2) + vec3 intensity = mix( kcool, kwarm, sDotNFactor ); + + // Calculate the vector from the fragment to the eye position + vec3 v = normalize( eyePosition - pos ); + + // Reflect the light beam using the normal at this fragment + vec3 r = reflect( -s, n ); + + // Calculate the specular component + float specular = 0.0; + if ( dot( s, n ) > 0.0 ) + specular = pow( max( dot( r, v ), 0.0 ), shininess ); + + // Sum the blended tone and specular highlight + result += intensity + ks * specular; + } + + return result; +} + +void main() +{ + fragColor = vec4( goochModel( worldPosition, normalize( worldNormal ) ), 1.0 ); +} diff --git a/src/extras/shaders/rhi/gooch.vert b/src/extras/shaders/rhi/gooch.vert new file mode 100644 index 000000000..5230fb70e --- /dev/null +++ b/src/extras/shaders/rhi/gooch.vert @@ -0,0 +1,19 @@ +#version 150 core + +in vec3 vertexPosition; +in vec3 vertexNormal; + +out vec3 worldPosition; +out vec3 worldNormal; + +uniform mat4 modelMatrix; +uniform mat3 modelNormalMatrix; +uniform mat4 mvp; + +void main() +{ + worldNormal = normalize( modelNormalMatrix * vertexNormal ); + worldPosition = vec3( modelMatrix * vec4( vertexPosition, 1.0 ) ); + + gl_Position = mvp * vec4( vertexPosition, 1.0 ); +} diff --git a/src/extras/shaders/rhi/light.inc.frag b/src/extras/shaders/rhi/light.inc.frag new file mode 100644 index 000000000..861e1ba4a --- /dev/null +++ b/src/extras/shaders/rhi/light.inc.frag @@ -0,0 +1,26 @@ +const int MAX_LIGHTS = 8; +const int TYPE_POINT = 0; +const int TYPE_DIRECTIONAL = 1; +const int TYPE_SPOT = 2; +struct Light { + int type; + vec3 position; + vec3 color; + float intensity; + vec3 direction; + float constantAttenuation; + float linearAttenuation; + float quadraticAttenuation; + float cutOffAngle; +}; + + +layout(std140, binding = auto) uniform qt3d_light_uniforms { + uniform Light lights[MAX_LIGHTS]; + uniform int lightCount; + uniform int envLightCount; +}; + +// Pre-convolved environment maps +layout(binding = auto) uniform samplerCube envLight_irradiance; // For diffuse contribution +layout(binding = auto) uniform samplerCube envLight_specular; // For specular contribution diff --git a/src/extras/shaders/rhi/metalrough.inc.frag b/src/extras/shaders/rhi/metalrough.inc.frag new file mode 100644 index 000000000..cf85f3b22 --- /dev/null +++ b/src/extras/shaders/rhi/metalrough.inc.frag @@ -0,0 +1,346 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// Exposure correction +uniform float exposure = 0.0; +// Gamma correction +uniform float gamma = 2.2; + +#pragma include light.inc.frag + +int mipLevelCount(const in samplerCube cube) +{ + int baseSize = textureSize(cube, 0).x; + int nMips = int(log2(float(baseSize > 0 ? baseSize : 1))) + 1; + return nMips; +} + +float remapRoughness(const in float roughness) +{ + // As per page 14 of + // http://www.frostbite.com/wp-content/uploads/2014/11/course_notes_moving_frostbite_to_pbr.pdf + // we remap the roughness to give a more perceptually linear response + // of "bluriness" as a function of the roughness specified by the user. + // r = roughness^2 + const float maxSpecPower = 999999.0; + const float minRoughness = sqrt(2.0 / (maxSpecPower + 2)); + return max(roughness * roughness, minRoughness); +} + +float alphaToMipLevel(float alpha) +{ + float specPower = 2.0 / (alpha * alpha) - 2.0; + + // We use the mip level calculation from Lys' default power drop, which in + // turn is a slight modification of that used in Marmoset Toolbag. See + // https://docs.knaldtech.com/doku.php?id=specular_lys for details. + // For now we assume a max specular power of 999999 which gives + // maxGlossiness = 1. + const float k0 = 0.00098; + const float k1 = 0.9921; + float glossiness = (pow(2.0, -10.0 / sqrt(specPower)) - k0) / k1; + + // TODO: Optimize by doing this on CPU and set as + // uniform int envLight.specularMipLevels say (if present in shader). + // Lookup the number of mips in the specular envmap + int mipLevels = mipLevelCount(envLight_specular); + + // Offset of smallest miplevel we should use (corresponds to specular + // power of 1). I.e. in the 32x32 sized mip. + const float mipOffset = 5.0; + + // The final factor is really 1 - g / g_max but as mentioned above g_max + // is 1 by definition here so we can avoid the division. If we make the + // max specular power for the spec map configurable, this will need to + // be handled properly. + float mipLevel = (mipLevels - 1.0 - mipOffset) * (1.0 - glossiness); + return mipLevel; +} + +float normalDistribution(const in vec3 n, const in vec3 h, const in float alpha) +{ + // Blinn-Phong approximation - see + // http://graphicrants.blogspot.co.uk/2013/08/specular-brdf-reference.html + float specPower = 2.0 / (alpha * alpha) - 2.0; + return (specPower + 2.0) / (2.0 * 3.14159) * pow(max(dot(n, h), 0.0), specPower); +} + +vec3 fresnelFactor(const in vec3 color, const in float cosineFactor) +{ + // Calculate the Fresnel effect value + vec3 f = color; + vec3 F = f + (1.0 - f) * pow(1.0 - cosineFactor, 5.0); + return clamp(F, f, vec3(1.0)); +} + +float geometricModel(const in float lDotN, + const in float vDotN, + const in vec3 h) +{ + // Implicit geometric model (equal to denominator in specular model). + // This currently assumes that there is no attenuation by geometric shadowing or + // masking according to the microfacet theory. + return lDotN * vDotN; +} + +vec3 specularModel(const in vec3 F0, + const in float sDotH, + const in float sDotN, + const in float vDotN, + const in vec3 n, + const in vec3 h) +{ + // Clamp sDotN and vDotN to small positive value to prevent the + // denominator in the reflection equation going to infinity. Balance this + // by using the clamped values in the geometric factor function to + // avoid ugly seams in the specular lighting. + float sDotNPrime = max(sDotN, 0.001); + float vDotNPrime = max(vDotN, 0.001); + + vec3 F = fresnelFactor(F0, sDotH); + float G = geometricModel(sDotNPrime, vDotNPrime, h); + + vec3 cSpec = F * G / (4.0 * sDotNPrime * vDotNPrime); + return clamp(cSpec, vec3(0.0), vec3(1.0)); +} + +vec3 pbrModel(const in int lightIndex, + const in vec3 wPosition, + const in vec3 wNormal, + const in vec3 wView, + const in vec3 baseColor, + const in float metalness, + const in float alpha, + const in float ambientOcclusion) +{ + // Calculate some useful quantities + vec3 n = wNormal; + vec3 s = vec3(0.0); + vec3 v = wView; + vec3 h = vec3(0.0); + + float vDotN = dot(v, n); + float sDotN = 0.0; + float sDotH = 0.0; + float att = 1.0; + + if (lights[lightIndex].type != TYPE_DIRECTIONAL) { + // Point and Spot lights + vec3 sUnnormalized = vec3(lights[lightIndex].position) - wPosition; + s = normalize(sUnnormalized); + + // Calculate the attenuation factor + sDotN = dot(s, n); + if (sDotN > 0.0) { + if (lights[lightIndex].constantAttenuation != 0.0 + || lights[lightIndex].linearAttenuation != 0.0 + || lights[lightIndex].quadraticAttenuation != 0.0) { + float dist = length(sUnnormalized); + att = 1.0 / (lights[lightIndex].constantAttenuation + + lights[lightIndex].linearAttenuation * dist + + lights[lightIndex].quadraticAttenuation * dist * dist); + } + + // The light direction is in world space already + if (lights[lightIndex].type == TYPE_SPOT) { + // Check if fragment is inside or outside of the spot light cone + if (degrees(acos(dot(-s, lights[lightIndex].direction))) > lights[lightIndex].cutOffAngle) + sDotN = 0.0; + } + } + } else { + // Directional lights + // The light direction is in world space already + s = normalize(-lights[lightIndex].direction); + sDotN = dot(s, n); + } + + h = normalize(s + v); + sDotH = dot(s, h); + + // Calculate diffuse component + vec3 diffuseColor = (1.0 - metalness) * baseColor * lights[lightIndex].color; + vec3 diffuse = diffuseColor * max(sDotN, 0.0) / 3.14159; + + // Calculate specular component + vec3 dielectricColor = vec3(0.04); + vec3 F0 = mix(dielectricColor, baseColor, metalness); + vec3 specularFactor = vec3(0.0); + if (sDotN > 0.0) { + specularFactor = specularModel(F0, sDotH, sDotN, vDotN, n, h); + specularFactor *= normalDistribution(n, h, alpha); + } + vec3 specularColor = lights[lightIndex].color; + vec3 specular = specularColor * specularFactor; + + // Blend between diffuse and specular to conserver energy + vec3 color = att * lights[lightIndex].intensity * (specular + diffuse * (vec3(1.0) - specular)); + + // Reduce by ambient occlusion amount + color *= ambientOcclusion; + + return color; +} + +vec3 pbrIblModel(const in vec3 wNormal, + const in vec3 wView, + const in vec3 baseColor, + const in float metalness, + const in float alpha, + const in float ambientOcclusion) +{ + // Calculate reflection direction of view vector about surface normal + // vector in world space. This is used in the fragment shader to sample + // from the environment textures for a light source. This is equivalent + // to the l vector for punctual light sources. Armed with this, calculate + // the usual factors needed + vec3 n = wNormal; + vec3 l = reflect(-wView, n); + vec3 v = wView; + vec3 h = normalize(l + v); + float vDotN = dot(v, n); + float lDotN = dot(l, n); + float lDotH = dot(l, h); + + // Calculate diffuse component + vec3 diffuseColor = (1.0 - metalness) * baseColor; + vec3 diffuse = diffuseColor * texture(envLight_irradiance, l).rgb; + + // Calculate specular component + vec3 dielectricColor = vec3(0.04); + vec3 F0 = mix(dielectricColor, baseColor, metalness); + vec3 specularFactor = specularModel(F0, lDotH, lDotN, vDotN, n, h); + + float lod = alphaToMipLevel(alpha); +//#define DEBUG_SPECULAR_LODS +#ifdef DEBUG_SPECULAR_LODS + if (lod > 7.0) + return vec3(1.0, 0.0, 0.0); + else if (lod > 6.0) + return vec3(1.0, 0.333, 0.0); + else if (lod > 5.0) + return vec3(1.0, 1.0, 0.0); + else if (lod > 4.0) + return vec3(0.666, 1.0, 0.0); + else if (lod > 3.0) + return vec3(0.0, 1.0, 0.666); + else if (lod > 2.0) + return vec3(0.0, 0.666, 1.0); + else if (lod > 1.0) + return vec3(0.0, 0.0, 1.0); + else if (lod > 0.0) + return vec3(1.0, 0.0, 1.0); +#endif + vec3 specularSkyColor = textureLod(envLight_specular, l, lod).rgb; + vec3 specular = specularSkyColor * specularFactor; + + // Blend between diffuse and specular to conserve energy + vec3 color = specular + diffuse * (vec3(1.0) - specularFactor); + + // Reduce by ambient occlusion amount + color *= ambientOcclusion; + + return color; +} + +vec3 toneMap(const in vec3 c) +{ + return c / (c + vec3(1.0)); +} + +vec3 gammaCorrect(const in vec3 color) +{ + return pow(color, vec3(1.0 / gamma)); +} + +vec4 metalRoughFunction(const in vec4 baseColor, + const in float metalness, + const in float roughness, + const in float ambientOcclusion, + const in vec3 worldPosition, + const in vec3 worldView, + const in vec3 worldNormal) +{ + vec3 cLinear = vec3(0.0); + + // Remap roughness for a perceptually more linear correspondence + float alpha = remapRoughness(roughness); + + for (int i = 0; i < envLightCount; ++i) { + cLinear += pbrIblModel(worldNormal, + worldView, + baseColor.rgb, + metalness, + alpha, + ambientOcclusion); + } + + for (int i = 0; i < lightCount; ++i) { + cLinear += pbrModel(i, + worldPosition, + worldNormal, + worldView, + baseColor.rgb, + metalness, + alpha, + ambientOcclusion); + } + + // Apply exposure correction + cLinear *= pow(2.0, exposure); + + // Apply simple (Reinhard) tonemap transform to get into LDR range [0, 1] + vec3 cToneMapped = toneMap(cLinear); + + // Apply gamma correction prior to display + vec3 cGamma = gammaCorrect(cToneMapped); + + return vec4(cGamma, 1.0); +} diff --git a/src/extras/shaders/rhi/morphphong.vert b/src/extras/shaders/rhi/morphphong.vert new file mode 100644 index 000000000..7a8bdd097 --- /dev/null +++ b/src/extras/shaders/rhi/morphphong.vert @@ -0,0 +1,34 @@ +#version 150 core + +in vec3 vertexPosition; +in vec3 vertexNormal; +in vec3 vertexPositionTarget; +in vec3 vertexNormalTarget; + +out vec3 worldPosition; +out vec3 worldNormal; + +uniform mat4 modelMatrix; +uniform mat3 modelNormalMatrix; +uniform mat4 modelViewProjection; +uniform float interpolator; + +void main() +{ + vec3 morphPos; + vec3 morphNormal; + if (interpolator > 0.0) { + // normalized + morphPos = mix(vertexPosition, vertexPositionTarget, interpolator); + morphNormal = normalize(mix(vertexNormal, vertexNormalTarget, interpolator)); + } else { + // relative + morphPos = vertexPosition + vertexPositionTarget * abs(interpolator); + morphNormal = normalize(vertexNormal + vertexNormalTarget * abs(interpolator)); + } + + worldNormal = normalize( modelNormalMatrix * morphNormal ); + worldPosition = vec3( modelMatrix * vec4( morphPos, 1.0 ) ); + + gl_Position = modelViewProjection * vec4( morphPos, 1.0 ); +} diff --git a/src/extras/shaders/rhi/pervertexcolor.frag b/src/extras/shaders/rhi/pervertexcolor.frag new file mode 100644 index 000000000..40fc066d6 --- /dev/null +++ b/src/extras/shaders/rhi/pervertexcolor.frag @@ -0,0 +1,17 @@ +#version 150 core + +in vec3 worldPosition; +in vec3 worldNormal; +in vec4 color; + +out vec4 fragColor; + +uniform vec3 eyePosition; + +#pragma include phong.inc.frag + +void main() +{ + vec3 worldView = normalize(eyePosition - worldPosition); + fragColor = phongFunction(color, color, vec4(0.0), 0.0, worldPosition, worldView, worldNormal); +} diff --git a/src/extras/shaders/rhi/pervertexcolor.vert b/src/extras/shaders/rhi/pervertexcolor.vert new file mode 100644 index 000000000..1d721e945 --- /dev/null +++ b/src/extras/shaders/rhi/pervertexcolor.vert @@ -0,0 +1,22 @@ +#version 150 core + +in vec3 vertexPosition; +in vec3 vertexNormal; +in vec4 vertexColor; + +out vec3 worldPosition; +out vec3 worldNormal; +out vec4 color; + +uniform mat4 modelMatrix; +uniform mat3 modelNormalMatrix; +uniform mat4 mvp; + +void main() +{ + worldNormal = normalize( modelNormalMatrix * vertexNormal ); + worldPosition = vec3( modelMatrix * vec4( vertexPosition, 1.0 ) ); + color = vertexColor; + + gl_Position = mvp * vec4( vertexPosition, 1.0 ); +} diff --git a/src/extras/shaders/rhi/phong.inc.frag b/src/extras/shaders/rhi/phong.inc.frag new file mode 100644 index 000000000..47a6ecd4a --- /dev/null +++ b/src/extras/shaders/rhi/phong.inc.frag @@ -0,0 +1,138 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#pragma include light.inc.frag + +void adsModel(const in vec3 worldPos, + const in vec3 worldNormal, + const in vec3 worldView, + const in float shininess, + out vec3 diffuseColor, + out vec3 specularColor) +{ + diffuseColor = vec3(0.0); + specularColor = vec3(0.0); + + // We perform all work in world space + vec3 n = normalize(worldNormal); + vec3 s = vec3(0.0); + + for (int i = 0; i < lightCount; ++i) { + float att = 1.0; + float sDotN = 0.0; + + if (lights[i].type != TYPE_DIRECTIONAL) { + // Point and Spot lights + + // Light position is already in world space + vec3 sUnnormalized = lights[i].position - worldPos; + s = normalize(sUnnormalized); // Light direction + + // Calculate the attenuation factor + sDotN = dot(s, n); + if (sDotN > 0.0) { + if (lights[i].constantAttenuation != 0.0 + || lights[i].linearAttenuation != 0.0 + || lights[i].quadraticAttenuation != 0.0) { + float dist = length(sUnnormalized); + att = 1.0 / (lights[i].constantAttenuation + + lights[i].linearAttenuation * dist + + lights[i].quadraticAttenuation * dist * dist); + } + + // The light direction is in world space already + if (lights[i].type == TYPE_SPOT) { + // Check if fragment is inside or outside of the spot light cone + if (degrees(acos(dot(-s, lights[i].direction))) > lights[i].cutOffAngle) + sDotN = 0.0; + } + } + } else { + // Directional lights + // The light direction is in world space already + s = normalize(-lights[i].direction); + sDotN = dot(s, n); + } + + // Calculate the diffuse factor + float diffuse = max(sDotN, 0.0); + + // Calculate the specular factor + float specular = 0.0; + if (diffuse > 0.0 && shininess > 0.0) { + float normFactor = (shininess + 2.0) / 2.0; + vec3 r = reflect(-s, n); // Reflection direction in world space + specular = normFactor * pow(max(dot(r, worldView), 0.0), shininess); + } + + // Accumulate the diffuse and specular contributions + diffuseColor += att * lights[i].intensity * diffuse * lights[i].color; + specularColor += att * lights[i].intensity * specular * lights[i].color; + } +} + +vec4 phongFunction(const in vec4 ambient, + const in vec4 diffuse, + const in vec4 specular, + const in float shininess, + const in vec3 worldPosition, + const in vec3 worldView, + const in vec3 worldNormal) +{ + // Calculate the lighting model, keeping the specular component separate + vec3 diffuseColor, specularColor; + adsModel(worldPosition, worldNormal, worldView, shininess, diffuseColor, specularColor); + + // Combine spec with ambient+diffuse for final fragment color + vec3 color = (ambient.rgb + diffuseColor) * diffuse.rgb + + specularColor * specular.rgb; + + return vec4(color, diffuse.a); +} diff --git a/src/extras/shaders/rhi/skybox.frag b/src/extras/shaders/rhi/skybox.frag new file mode 100644 index 000000000..ceb13b628 --- /dev/null +++ b/src/extras/shaders/rhi/skybox.frag @@ -0,0 +1,24 @@ +#version 330 + +in vec3 texCoord0; +out vec4 fragColor; +uniform samplerCube skyboxTexture; + +// Gamma correction +uniform float gamma = 2.2; + +uniform float gammaStrength; + +vec3 gammaCorrect(const in vec3 color) +{ + return pow(color, vec3(1.0 / gamma)); +} + +void main() +{ + vec4 baseColor = texture(skyboxTexture, texCoord0); + vec4 gammaColor = vec4(gammaCorrect(baseColor.rgb), 1.0); + // This is an odd way to enable or not gamma correction, + // but this is a way to avoid branching until we can generate shaders + fragColor = mix(baseColor, gammaColor, gammaStrength); +} diff --git a/src/extras/shaders/rhi/skybox.vert b/src/extras/shaders/rhi/skybox.vert new file mode 100644 index 000000000..cac49893a --- /dev/null +++ b/src/extras/shaders/rhi/skybox.vert @@ -0,0 +1,16 @@ +#version 330 + +in vec3 vertexPosition; +out vec3 texCoord0; + +uniform mat4 modelMatrix; +uniform mat4 viewMatrix; +uniform mat4 projectionMatrix; + +void main() +{ + texCoord0 = vertexPosition.xyz; + // Converting the viewMatrix to a mat3, then back to a mat4 + // removes the translation component from it + gl_Position = vec4(projectionMatrix * mat4(mat3(viewMatrix)) * modelMatrix * vec4(vertexPosition, 1.0)).xyww; +} diff --git a/src/extras/shaders/rhi/unlittexture.frag b/src/extras/shaders/rhi/unlittexture.frag new file mode 100644 index 000000000..8abbeee8f --- /dev/null +++ b/src/extras/shaders/rhi/unlittexture.frag @@ -0,0 +1,13 @@ +#version 150 core + +uniform sampler2D diffuseTexture; + +in vec3 position; +in vec2 texCoord; + +out vec4 fragColor; + +void main() +{ + fragColor = texture( diffuseTexture, texCoord ); +} diff --git a/src/extras/shaders/rhi/unlittexture.vert b/src/extras/shaders/rhi/unlittexture.vert new file mode 100644 index 000000000..7e245bd7f --- /dev/null +++ b/src/extras/shaders/rhi/unlittexture.vert @@ -0,0 +1,20 @@ +#version 150 core + +in vec3 vertexPosition; +in vec2 vertexTexCoord; + +out vec3 position; +out vec2 texCoord; + +uniform mat4 modelView; +uniform mat4 mvp; +uniform mat3 texCoordTransform; + +void main() +{ + vec3 tt = texCoordTransform * vec3(vertexTexCoord, 1.0); + texCoord = (tt / tt.z).xy; + position = vec3( modelView * vec4( vertexPosition, 1.0 ) ); + + gl_Position = mvp * vec4( vertexPosition, 1.0 ); +} diff --git a/src/plugins/renderers/opengl/graphicshelpers/submissioncontext.cpp b/src/plugins/renderers/opengl/graphicshelpers/submissioncontext.cpp index 1e2aba0fc..7c1467f6e 100644 --- a/src/plugins/renderers/opengl/graphicshelpers/submissioncontext.cpp +++ b/src/plugins/renderers/opengl/graphicshelpers/submissioncontext.cpp @@ -1160,7 +1160,7 @@ void SubmissionContext::setUpdatedTexture(const Qt3DCore::QNodeIdVector &updated // It will be easier if the QGraphicContext applies the QUniformPack // than the other way around -bool SubmissionContext::setParameters(ShaderParameterPack ¶meterPack) +bool SubmissionContext::setParameters(ShaderParameterPack ¶meterPack, GLShader *shader) { static const int irradianceId = StringToInt::lookupId(QLatin1String("envLight.irradiance")); static const int specularId = StringToInt::lookupId(QLatin1String("envLight.specular")); @@ -1225,7 +1225,7 @@ bool SubmissionContext::setParameters(ShaderParameterPack ¶meterPack) } } - QOpenGLShaderProgram *shader = activeShader(); + QOpenGLShaderProgram *glShader = activeShader(); // TO DO: We could cache the binding points somehow and only do the binding when necessary // for SSBO and UBO @@ -1239,7 +1239,7 @@ bool SubmissionContext::setParameters(ShaderParameterPack ¶meterPack) // This is currently not required as we are introspecting the bindingIndex // value from the shaders and not replacing them, making such a call useless // bindShaderStorageBlock(shader->programId(), b.m_blockIndex, b.m_bindingIndex); - bindShaderStorageBlock(shader->programId(), b.m_blockIndex, b.m_bindingIndex); + bindShaderStorageBlock(glShader->programId(), b.m_blockIndex, b.m_bindingIndex); // Needed to avoid conflict where the buffer would already // be bound as a VertexArray bindGLBuffer(ssbo, GLBuffer::ShaderStorageBuffer); @@ -1254,7 +1254,7 @@ bool SubmissionContext::setParameters(ShaderParameterPack ¶meterPack) for (const BlockToUBO &b : blockToUBOs) { Buffer *cpuBuffer = m_renderer->nodeManagers()->bufferManager()->lookupResource(b.m_bufferID); GLBuffer *ubo = glBufferForRenderBuffer(cpuBuffer); - bindUniformBlock(shader->programId(), b.m_blockIndex, uboIndex); + bindUniformBlock(glShader->programId(), b.m_blockIndex, uboIndex); // Needed to avoid conflict where the buffer would already // be bound as a VertexArray bindGLBuffer(ubo, GLBuffer::UniformBuffer); @@ -1264,11 +1264,11 @@ bool SubmissionContext::setParameters(ShaderParameterPack ¶meterPack) // Update uniforms in the Default Uniform Block const PackUniformHash values = parameterPack.uniforms(); - const QVector<ShaderUniform> activeUniforms = parameterPack.submissionUniforms(); + const QVector<int> &activeUniformsIndices = parameterPack.submissionUniformIndices(); + const QVector<ShaderUniform> &shaderUniforms = shader->uniforms(); - for (const ShaderUniform &uniform : activeUniforms) { - // We can use [] as we are sure the the uniform wouldn't - // be un activeUniforms if there wasn't a matching value + for (const int shaderUniformIndex : activeUniformsIndices) { + const ShaderUniform &uniform = shaderUniforms[shaderUniformIndex]; const UniformValue &v = values.value(uniform.m_nameId); // skip invalid textures/images diff --git a/src/plugins/renderers/opengl/graphicshelpers/submissioncontext_p.h b/src/plugins/renderers/opengl/graphicshelpers/submissioncontext_p.h index d43cae4f8..6e2ac4e18 100644 --- a/src/plugins/renderers/opengl/graphicshelpers/submissioncontext_p.h +++ b/src/plugins/renderers/opengl/graphicshelpers/submissioncontext_p.h @@ -138,7 +138,7 @@ public: GLBuffer *glBufferForRenderBuffer(Buffer *buf); // Parameters - bool setParameters(ShaderParameterPack ¶meterPack); + bool setParameters(ShaderParameterPack ¶meterPack, GLShader *shader); // RenderState void setCurrentStateSet(RenderStateSet* ss); diff --git a/src/plugins/renderers/opengl/jobs/renderviewjobutils.cpp b/src/plugins/renderers/opengl/jobs/renderviewjobutils.cpp index d4835054b..8774ac368 100644 --- a/src/plugins/renderers/opengl/jobs/renderviewjobutils.cpp +++ b/src/plugins/renderers/opengl/jobs/renderviewjobutils.cpp @@ -473,7 +473,10 @@ UniformBlockValueBuilder::~UniformBlockValueBuilder() { } -void UniformBlockValueBuilder::buildActiveUniformNameValueMapHelper(ShaderData *currentShaderData, const QString &blockName, const QString &qmlPropertyName, const QVariant &value) +void UniformBlockValueBuilder::buildActiveUniformNameValueMapHelper(const ShaderData *currentShaderData, + const QString &blockName, + const QString &qmlPropertyName, + const QVariant &value) { // In the end, values are either scalar or a scalar array // Composed elements (structs, structs array) are simplified into simple scalars @@ -534,7 +537,9 @@ void UniformBlockValueBuilder::buildActiveUniformNameValueMapHelper(ShaderData * } } -void UniformBlockValueBuilder::buildActiveUniformNameValueMapStructHelper(ShaderData *rShaderData, const QString &blockName, const QString &qmlPropertyName) +void UniformBlockValueBuilder::buildActiveUniformNameValueMapStructHelper(const ShaderData *rShaderData, + const QString &blockName, + const QString &qmlPropertyName) { const QHash<QString, ShaderData::PropertyValue> &properties = rShaderData->properties(); auto it = properties.begin(); diff --git a/src/plugins/renderers/opengl/jobs/renderviewjobutils_p.h b/src/plugins/renderers/opengl/jobs/renderviewjobutils_p.h index 5b1413108..f9fab6185 100644 --- a/src/plugins/renderers/opengl/jobs/renderviewjobutils_p.h +++ b/src/plugins/renderers/opengl/jobs/renderviewjobutils_p.h @@ -166,11 +166,11 @@ struct Q_AUTOTEST_EXPORT UniformBlockValueBuilder QT3D_ALIGNED_MALLOC_AND_FREE() - void buildActiveUniformNameValueMapHelper(ShaderData *currentShaderData, + void buildActiveUniformNameValueMapHelper(const ShaderData *currentShaderData, const QString &blockName, const QString &qmlPropertyName, const QVariant &value); - void buildActiveUniformNameValueMapStructHelper(ShaderData *rShaderData, + void buildActiveUniformNameValueMapStructHelper(const ShaderData *rShaderData, const QString &blockName, const QString &qmlPropertyName = QString()); diff --git a/src/plugins/renderers/opengl/renderer/glshader.cpp b/src/plugins/renderers/opengl/renderer/glshader.cpp index 2bc861928..564e78a8e 100644 --- a/src/plugins/renderers/opengl/renderer/glshader.cpp +++ b/src/plugins/renderers/opengl/renderer/glshader.cpp @@ -84,11 +84,19 @@ QVector<int> getLightUniformNameIds() return names; } +template<typename Vector> +bool fastContains(const Vector &v, int value) +{ + return std::binary_search(v.cbegin(), v.cend(), value); +} + } GLShader::GLShader() : m_isLoaded(false) , m_graphicsContext(nullptr) + , m_parameterPackSize(0) + , m_hasActiveVariables(false) { m_shaderCode.resize(static_cast<int>(QShaderProgram::Compute) + 1); } @@ -141,7 +149,7 @@ QHash<QString, ShaderUniform> GLShader::activeUniformsForUniformBlock(int blockI return m_uniformBlockIndexToShaderUniforms.value(blockIndex); } -ShaderUniformBlock GLShader::uniformBlockForBlockIndex(int blockIndex) +ShaderUniformBlock GLShader::uniformBlockForBlockIndex(int blockIndex) const noexcept { for (int i = 0, m = m_uniformBlocks.size(); i < m; ++i) { if (m_uniformBlocks[i].m_index == blockIndex) { @@ -151,7 +159,7 @@ ShaderUniformBlock GLShader::uniformBlockForBlockIndex(int blockIndex) return ShaderUniformBlock(); } -ShaderUniformBlock GLShader::uniformBlockForBlockNameId(int blockNameId) +ShaderUniformBlock GLShader::uniformBlockForBlockNameId(int blockNameId) const noexcept { for (int i = 0, m = m_uniformBlocks.size(); i < m; ++i) { if (m_uniformBlocks[i].m_nameId == blockNameId) { @@ -161,7 +169,7 @@ ShaderUniformBlock GLShader::uniformBlockForBlockNameId(int blockNameId) return ShaderUniformBlock(); } -ShaderUniformBlock GLShader::uniformBlockForBlockName(const QString &blockName) +ShaderUniformBlock GLShader::uniformBlockForBlockName(const QString &blockName) const noexcept { for (int i = 0, m = m_uniformBlocks.size(); i < m; ++i) { if (m_uniformBlocks[i].m_name == blockName) { @@ -171,7 +179,7 @@ ShaderUniformBlock GLShader::uniformBlockForBlockName(const QString &blockName) return ShaderUniformBlock(); } -ShaderStorageBlock GLShader::storageBlockForBlockIndex(int blockIndex) +ShaderStorageBlock GLShader::storageBlockForBlockIndex(int blockIndex) const noexcept { for (int i = 0, m = m_shaderStorageBlockNames.size(); i < m; ++i) { if (m_shaderStorageBlocks[i].m_index == blockIndex) @@ -180,7 +188,7 @@ ShaderStorageBlock GLShader::storageBlockForBlockIndex(int blockIndex) return ShaderStorageBlock(); } -ShaderStorageBlock GLShader::storageBlockForBlockNameId(int blockNameId) +ShaderStorageBlock GLShader::storageBlockForBlockNameId(int blockNameId) const noexcept { for (int i = 0, m = m_shaderStorageBlockNames.size(); i < m; ++i) { if (m_shaderStorageBlocks[i].m_nameId == blockNameId) @@ -189,7 +197,7 @@ ShaderStorageBlock GLShader::storageBlockForBlockNameId(int blockNameId) return ShaderStorageBlock(); } -ShaderStorageBlock GLShader::storageBlockForBlockName(const QString &blockName) +ShaderStorageBlock GLShader::storageBlockForBlockName(const QString &blockName) const noexcept { for (int i = 0, m = m_shaderStorageBlockNames.size(); i < m; ++i) { if (m_shaderStorageBlocks[i].m_name == blockName) @@ -198,6 +206,22 @@ ShaderStorageBlock GLShader::storageBlockForBlockName(const QString &blockName) return ShaderStorageBlock(); } +GLShader::ParameterKind GLShader::categorizeVariable(int nameId) const noexcept +{ + if (fastContains(m_uniformsNamesIds, nameId)) + return ParameterKind::Uniform; + if (fastContains(m_uniformBlockNamesIds, nameId)) + return ParameterKind::UBO; + if (fastContains(m_shaderStorageBlockNamesIds, nameId)) + return ParameterKind::SSBO; + return ParameterKind::Struct; +} + +bool GLShader::hasUniform(int nameId) const noexcept +{ + return m_uniformsNamesIds.contains(nameId); +} + void GLShader::prepareUniforms(ShaderParameterPack &pack) { const PackUniformHash &values = pack.uniforms(); @@ -205,14 +229,20 @@ void GLShader::prepareUniforms(ShaderParameterPack &pack) auto it = values.keys.cbegin(); const auto end = values.keys.cend(); + const int shaderUniformsCount = m_uniforms.size(); + const auto uIt = m_uniforms.cbegin(); + while (it != end) { // Find if there's a uniform with the same name id - for (const ShaderUniform &uniform : qAsConst(m_uniforms)) { - if (uniform.m_nameId == *it) { - pack.setSubmissionUniform(uniform); - break; - } - } + + int i = 0; + const int targetNameId = *it; + while (i < shaderUniformsCount && (uIt + i)->m_nameId < targetNameId) + ++i; + + if (i < shaderUniformsCount && (uIt + i)->m_nameId == targetNameId) + pack.setSubmissionUniformIndex(i); + ++it; } } @@ -223,7 +253,6 @@ void GLShader::setFragOutputs(const QHash<QString, int> &fragOutputs) QMutexLocker lock(&m_mutex); m_fragOutputs = fragOutputs; } -// updateDNA(); } const QHash<QString, int> GLShader::fragOutputs() const @@ -288,6 +317,18 @@ void GLShader::initializeUniforms(const QVector<ShaderUniform> &uniformsDescript } } m_uniformBlockIndexToShaderUniforms.insert(-1, activeUniformsInDefaultBlock); + + m_parameterPackSize += m_standardUniformNamesIds.size() + m_lightUniformsNamesIds.size() + m_uniformsNamesIds.size(); + m_hasActiveVariables |= (m_parameterPackSize > 0); + + // Sort by ascending order to make contains check faster + std::sort(m_uniformsNamesIds.begin(), m_uniformsNamesIds.end()); + std::sort(m_lightUniformsNamesIds.begin(), m_lightUniformsNamesIds.end()); + std::sort(m_standardUniformNamesIds.begin(), m_standardUniformNamesIds.end()); + std::sort(m_uniforms.begin(), m_uniforms.end(), + [] (const ShaderUniform &a, const ShaderUniform &b) { + return a.m_nameId < b.m_nameId; + }); } void GLShader::initializeAttributes(const QVector<ShaderAttribute> &attributesDescription) @@ -301,6 +342,7 @@ void GLShader::initializeAttributes(const QVector<ShaderAttribute> &attributesDe m_attributeNamesIds[i] = m_attributes[i].m_nameId; qCDebug(Shaders) << "Active Attribute " << attributesDescription[i].m_name; } + m_hasActiveVariables |= (m_attributeNamesIds.size() > 0); } void GLShader::initializeUniformBlocks(const QVector<ShaderUniformBlock> &uniformBlockDescription) @@ -336,6 +378,12 @@ void GLShader::initializeUniformBlocks(const QVector<ShaderUniformBlock> &unifor } m_uniformBlockIndexToShaderUniforms.insert(uniformBlockDescription[i].m_index, activeUniformsInBlock); } + + m_parameterPackSize += m_uniformsNamesIds.size(); + m_hasActiveVariables |= (m_parameterPackSize > 0); + + // Sort by ascending order to make contains check faster + std::sort(m_uniformBlockNamesIds.begin(), m_uniformBlockNamesIds.end()); } void GLShader::initializeShaderStorageBlocks(const QVector<ShaderStorageBlock> &shaderStorageBlockDescription) @@ -350,6 +398,12 @@ void GLShader::initializeShaderStorageBlocks(const QVector<ShaderStorageBlock> & m_shaderStorageBlocks[i].m_nameId =m_shaderStorageBlockNamesIds[i]; qCDebug(Shaders) << "Initializing Shader Storage Block {" << m_shaderStorageBlockNames[i] << "}"; } + + m_parameterPackSize += m_shaderStorageBlockNamesIds.size(); + m_hasActiveVariables |= (m_parameterPackSize > 0); + + // Sort by ascending order to make contains check faster + std::sort(m_shaderStorageBlockNamesIds.begin(), m_shaderStorageBlockNamesIds.end()); } } // OpenGL diff --git a/src/plugins/renderers/opengl/renderer/glshader_p.h b/src/plugins/renderers/opengl/renderer/glshader_p.h index 07d40c724..ae447cd18 100644 --- a/src/plugins/renderers/opengl/renderer/glshader_p.h +++ b/src/plugins/renderers/opengl/renderer/glshader_p.h @@ -99,20 +99,32 @@ public: QVector<QString> uniformBlockNames() const; QVector<QString> storageBlockNames() const; - inline QVector<ShaderUniform> uniforms() const { return m_uniforms; } - inline QVector<ShaderAttribute> attributes() const { return m_attributes; } - inline QVector<ShaderUniformBlock> uniformBlocks() const { return m_uniformBlocks; } - inline QVector<ShaderStorageBlock> storageBlocks() const { return m_shaderStorageBlocks; } + inline const QVector<ShaderUniform> &uniforms() const { return m_uniforms; } + inline const QVector<ShaderAttribute> &attributes() const { return m_attributes; } + inline const QVector<ShaderUniformBlock> &uniformBlocks() const { return m_uniformBlocks; } + inline const QVector<ShaderStorageBlock> &storageBlocks() const { return m_shaderStorageBlocks; } QHash<QString, ShaderUniform> activeUniformsForUniformBlock(int blockIndex) const; - ShaderUniformBlock uniformBlockForBlockIndex(int blockNameId); - ShaderUniformBlock uniformBlockForBlockNameId(int blockIndex); - ShaderUniformBlock uniformBlockForBlockName(const QString &blockName); + ShaderUniformBlock uniformBlockForBlockIndex(int blockNameId) const noexcept; + ShaderUniformBlock uniformBlockForBlockNameId(int blockIndex) const noexcept; + ShaderUniformBlock uniformBlockForBlockName(const QString &blockName) const noexcept; - ShaderStorageBlock storageBlockForBlockIndex(int blockIndex); - ShaderStorageBlock storageBlockForBlockNameId(int blockNameId); - ShaderStorageBlock storageBlockForBlockName(const QString &blockName); + ShaderStorageBlock storageBlockForBlockIndex(int blockIndex) const noexcept; + ShaderStorageBlock storageBlockForBlockNameId(int blockNameId) const noexcept; + ShaderStorageBlock storageBlockForBlockName(const QString &blockName) const noexcept; + + enum ParameterKind { + Uniform, + UBO, + SSBO, + Struct + }; + ParameterKind categorizeVariable(int nameId) const noexcept; + + bool hasUniform(int nameId) const noexcept; + inline bool hasActiveVariables() const noexcept { return m_hasActiveVariables; } + inline int parameterPackSize() const noexcept { return m_parameterPackSize; } QOpenGLShaderProgram *shaderProgram() { return &m_shader; } @@ -146,6 +158,9 @@ private: QHash<QString, int> m_fragOutputs; QVector<QByteArray> m_shaderCode; + int m_parameterPackSize; + int m_hasActiveVariables; + // Private so that only GraphicContext can call it void initializeUniforms(const QVector<ShaderUniform> &uniformsDescription); void initializeAttributes(const QVector<ShaderAttribute> &attributesDescription); diff --git a/src/plugins/renderers/opengl/renderer/renderer.cpp b/src/plugins/renderers/opengl/renderer/renderer.cpp index 35f1d6bf4..fdcc9fd09 100644 --- a/src/plugins/renderers/opengl/renderer/renderer.cpp +++ b/src/plugins/renderers/opengl/renderer/renderer.cpp @@ -995,16 +995,9 @@ void Renderer::prepareCommandsSubmission(const QVector<RenderView *> &renderView // so we cannot unset its dirtiness at this point if (rGeometryRenderer->isDirty()) rGeometryRenderer->unsetDirty(); - - // Prepare the ShaderParameterPack based on the active uniforms of the shader - shader->prepareUniforms(command.m_parameterPack); - } else if (command.m_type == RenderCommand::Compute) { GLShader *shader = command.m_glShader; Q_ASSERT(shader); - - // Prepare the ShaderParameterPack based on the active uniforms of the shader - shader->prepareUniforms(command.m_parameterPack); } } } @@ -2075,7 +2068,7 @@ void Renderer::performCompute(const RenderView *, RenderCommand *command) } { Profiling::GLTimeRecorder recorder(Profiling::UniformUpdate, activeProfiler()); - m_submissionContext->setParameters(command->m_parameterPack); + m_submissionContext->setParameters(command->m_parameterPack, command->m_glShader); } { Profiling::GLTimeRecorder recorder(Profiling::DispatchCompute, activeProfiler()); @@ -2169,7 +2162,7 @@ bool Renderer::executeCommandsSubmission(const RenderView *rv) { Profiling::GLTimeRecorder recorder(Profiling::UniformUpdate, activeProfiler()); //// Update program uniforms - if (!m_submissionContext->setParameters(command.m_parameterPack)) { + if (!m_submissionContext->setParameters(command.m_parameterPack, command.m_glShader)) { allCommandsIssued = false; // If we have failed to set uniform (e.g unable to bind a texture) // we won't perform the draw call which could show invalid content diff --git a/src/plugins/renderers/opengl/renderer/renderview.cpp b/src/plugins/renderers/opengl/renderer/renderview.cpp index 5757c7851..6707c6c2f 100644 --- a/src/plugins/renderers/opengl/renderer/renderview.cpp +++ b/src/plugins/renderers/opengl/renderer/renderview.cpp @@ -148,9 +148,10 @@ static Matrix4x4 getProjectionMatrix(const CameraLens *lens) } UniformValue RenderView::standardUniformValue(RenderView::StandardUniform standardUniformType, - Entity *entity, - const Matrix4x4 &model) const + const Entity *entity) const { + const Matrix4x4 &model = *(entity->worldTransform()); + switch (standardUniformType) { case ModelMatrix: return UniformValue(model); @@ -925,21 +926,16 @@ void RenderView::setUniformValue(ShaderParameterPack &uniformPack, int nameId, c } void RenderView::setStandardUniformValue(ShaderParameterPack &uniformPack, - int glslNameId, int nameId, - Entity *entity, - const Matrix4x4 &worldTransform) const + const Entity *entity) const { - uniformPack.setUniform(glslNameId, standardUniformValue(ms_standardUniformSetters[nameId], entity, worldTransform)); + uniformPack.setUniform(nameId, standardUniformValue(ms_standardUniformSetters[nameId], entity)); } void RenderView::setUniformBlockValue(ShaderParameterPack &uniformPack, - GLShader *shader, const ShaderUniformBlock &block, const UniformValue &value) const { - Q_UNUSED(shader) - if (value.valueType() == UniformValue::NodeId) { Buffer *buffer = nullptr; @@ -955,11 +951,9 @@ void RenderView::setUniformBlockValue(ShaderParameterPack &uniformPack, } void RenderView::setShaderStorageValue(ShaderParameterPack &uniformPack, - GLShader *shader, const ShaderStorageBlock &block, const UniformValue &value) const { - Q_UNUSED(shader) if (value.valueType() == UniformValue::NodeId) { Buffer *buffer = nullptr; if ((buffer = m_manager->bufferManager()->lookupResource(*value.constData<Qt3DCore::QNodeId>())) != nullptr) { @@ -973,7 +967,10 @@ void RenderView::setShaderStorageValue(ShaderParameterPack &uniformPack, } } -void RenderView::setDefaultUniformBlockShaderDataValue(ShaderParameterPack &uniformPack, GLShader *shader, ShaderData *shaderData, const QString &structName) const +void RenderView::setDefaultUniformBlockShaderDataValue(ShaderParameterPack &uniformPack, + const GLShader *shader, + const ShaderData *shaderData, + const QString &structName) const { UniformBlockValueBuilder *builder = m_localData.localData(); builder->activeUniformNamesToValue.clear(); @@ -997,6 +994,42 @@ void RenderView::setDefaultUniformBlockShaderDataValue(ShaderParameterPack &unif } } +void RenderView::applyParameter(const Parameter *param, + RenderCommand *command, + const GLShader *shader) const noexcept +{ + const int nameId = param->nameId(); + const UniformValue &uniformValue = param->uniformValue(); + const GLShader::ParameterKind kind = shader->categorizeVariable(nameId); + + switch (kind) { + case GLShader::Uniform: { + setUniformValue(command->m_parameterPack, nameId, uniformValue); + break; + } + case GLShader::UBO: { + setUniformBlockValue(command->m_parameterPack, shader->uniformBlockForBlockNameId(nameId), uniformValue); + break; + } + case GLShader::SSBO: { + setShaderStorageValue(command->m_parameterPack, shader->storageBlockForBlockNameId(nameId), uniformValue); + break; + } + case GLShader::Struct: { + ShaderData *shaderData = nullptr; + if (uniformValue.valueType() == UniformValue::NodeId && + (shaderData = m_manager->shaderDataManager()->lookupResource(*uniformValue.constData<Qt3DCore::QNodeId>())) != nullptr) { + // Try to check if we have a struct or array matching a QShaderData parameter + setDefaultUniformBlockShaderDataValue(command->m_parameterPack, shader, shaderData, StringToInt::lookupString(nameId)); + } + break; + } + default: + break; + } +} + + void RenderView::setShaderAndUniforms(RenderCommand *command, ParameterInfoList ¶meters, Entity *entity, @@ -1019,12 +1052,6 @@ void RenderView::setShaderAndUniforms(RenderCommand *command, // Builds the QUniformPack, sets shader standard uniforms and store attributes name / glname bindings // If a parameter is defined and not found in the bindings it is assumed to be a binding of Uniform type with the glsl name // equals to the parameter name - const QVector<int> &uniformNamesIds = shader->uniformsNamesIds(); - const QVector<int> &standardUniformNamesIds = shader->standardUniformNameIds(); - const QVector<int> &lightUniformNamesIds = shader->lightUniformsNamesIds(); - const QVector<int> &uniformBlockNamesIds = shader->uniformBlockNamesIds(); - const QVector<int> &shaderStorageBlockNamesIds = shader->storageBlockNamesIds(); - const QVector<int> &attributeNamesIds = shader->attributeNamesIds(); // Set fragData Name and index // Later on we might want to relink the shader if attachments have changed @@ -1042,56 +1069,32 @@ void RenderView::setShaderAndUniforms(RenderCommand *command, } // Set default attributes - command->m_activeAttributes = attributeNamesIds; + command->m_activeAttributes = shader->attributeNamesIds(); // At this point we know whether the command is a valid draw command or not // We still need to process the uniforms as the command could be a compute command command->m_isValid = !command->m_activeAttributes.empty(); - if (!uniformNamesIds.isEmpty() || !standardUniformNamesIds.isEmpty() || - !attributeNamesIds.isEmpty() || !lightUniformNamesIds.isEmpty() || - !shaderStorageBlockNamesIds.isEmpty() || command->m_isValid) { - - // Set default standard uniforms without bindings - const Matrix4x4 worldTransform = *(entity->worldTransform()); + if (shader->hasActiveVariables()) { // Reserve amount of uniforms we are going to need - command->m_parameterPack.reserve(uniformNamesIds.size() + standardUniformNamesIds.size() + lightUniformNamesIds.size() + uniformBlockNamesIds.size() + shaderStorageBlockNamesIds.size()); + command->m_parameterPack.reserve(shader->parameterPackSize()); + const QVector<int> &standardUniformNamesIds = shader->standardUniformNameIds(); for (const int uniformNameId : standardUniformNamesIds) - setStandardUniformValue(command->m_parameterPack, uniformNameId, uniformNameId, entity, worldTransform); - - // Parameters remaining could be - // -> uniform scalar / vector - // -> uniform struct / arrays - // -> uniform block / array (4.3) - // -> ssbo block / array (4.3) + setStandardUniformValue(command->m_parameterPack, uniformNameId, entity); ParameterInfoList::const_iterator it = parameters.cbegin(); const ParameterInfoList::const_iterator parametersEnd = parameters.cend(); while (it != parametersEnd) { - Parameter *param = m_manager->data<Parameter, ParameterManager>(it->handle); - const UniformValue &uniformValue = param->uniformValue(); - if (uniformNamesIds.contains(it->nameId)) { // Parameter is a regular uniform - setUniformValue(command->m_parameterPack, it->nameId, uniformValue); - } else if (uniformBlockNamesIds.indexOf(it->nameId) != -1) { // Parameter is a uniform block - setUniformBlockValue(command->m_parameterPack, shader, shader->uniformBlockForBlockNameId(it->nameId), uniformValue); - } else if (shaderStorageBlockNamesIds.indexOf(it->nameId) != -1) { // Parameters is a SSBO - setShaderStorageValue(command->m_parameterPack, shader, shader->storageBlockForBlockNameId(it->nameId), uniformValue); - } else { // Parameter is a struct - ShaderData *shaderData = nullptr; - if (uniformValue.valueType() == UniformValue::NodeId && - (shaderData = m_manager->shaderDataManager()->lookupResource(*uniformValue.constData<Qt3DCore::QNodeId>())) != nullptr) { - // Try to check if we have a struct or array matching a QShaderData parameter - setDefaultUniformBlockShaderDataValue(command->m_parameterPack, shader, shaderData, StringToInt::lookupString(it->nameId)); - } - // Otherwise: param unused by current shader - } + const Parameter *param = m_manager->data<Parameter, ParameterManager>(it->handle); + applyParameter(param, command, shader); ++it; } // Lights + const QVector<int> &lightUniformNamesIds = shader->lightUniformsNamesIds(); if (!lightUniformNamesIds.empty()) { int lightIdx = 0; for (const LightSource &lightSource : activeLightSources) { @@ -1174,6 +1177,9 @@ void RenderView::setShaderAndUniforms(RenderCommand *command, } setUniformValue(command->m_parameterPack, StringToInt::lookupId(QStringLiteral("envLightCount")), envLightCount); } + + // Prepare the ShaderParameterPack based on the active uniforms of the shader + shader->prepareUniforms(command->m_parameterPack); } } diff --git a/src/plugins/renderers/opengl/renderer/renderview_p.h b/src/plugins/renderers/opengl/renderer/renderview_p.h index c9e69d507..85dafe3fd 100644 --- a/src/plugins/renderers/opengl/renderer/renderview_p.h +++ b/src/plugins/renderers/opengl/renderer/renderview_p.h @@ -371,27 +371,25 @@ private: static StandardUniformsNameToTypeHash initializeStandardUniformSetters(); UniformValue standardUniformValue(StandardUniform standardUniformType, - Entity *entity, - const Matrix4x4 &model) const; + const Entity *entity) const; void setUniformValue(ShaderParameterPack &uniformPack, int nameId, const UniformValue &value) const; void setStandardUniformValue(ShaderParameterPack &uniformPack, - int glslNameId, int nameId, - Entity *entity, - const Matrix4x4 &worldTransform) const; + const Entity *entity) const; void setUniformBlockValue(ShaderParameterPack &uniformPack, - GLShader *shader, const ShaderUniformBlock &block, const UniformValue &value) const; void setShaderStorageValue(ShaderParameterPack &uniformPack, - GLShader *shader, const ShaderStorageBlock &block, const UniformValue &value) const; void setDefaultUniformBlockShaderDataValue(ShaderParameterPack &uniformPack, - GLShader *shader, - ShaderData *shaderData, + const GLShader *shader, + const ShaderData *shaderData, const QString &structName) const; + void applyParameter(const Parameter *param, + RenderCommand *command, + const GLShader *shader) const noexcept; }; } // namespace OpenGL diff --git a/src/plugins/renderers/opengl/renderer/renderviewbuilder.cpp b/src/plugins/renderers/opengl/renderer/renderviewbuilder.cpp index 35c200239..0667c21d3 100644 --- a/src/plugins/renderers/opengl/renderer/renderviewbuilder.cpp +++ b/src/plugins/renderers/opengl/renderer/renderviewbuilder.cpp @@ -350,7 +350,7 @@ public: // Split among the number of command builders // The idealPacketSize is at least 100 entities per worker const int idealPacketSize = std::min(std::max(100, filteredCommandData->size() / RenderViewBuilder::defaultJobCount()), filteredCommandData->size()); - const int m = findIdealNumberOfWorkers(filteredCommandData->size(), idealPacketSize); + const int m = findIdealNumberOfWorkers(filteredCommandData->size(), idealPacketSize, m_renderViewCommandUpdaterJobs.size()); for (int i = 0; i < m; ++i) { const RenderViewCommandUpdaterJobPtr renderViewCommandBuilder = m_renderViewCommandUpdaterJobs.at(i); diff --git a/src/plugins/renderers/opengl/renderer/shaderparameterpack.cpp b/src/plugins/renderers/opengl/renderer/shaderparameterpack.cpp index 082220349..467ac34c5 100644 --- a/src/plugins/renderers/opengl/renderer/shaderparameterpack.cpp +++ b/src/plugins/renderers/opengl/renderer/shaderparameterpack.cpp @@ -63,6 +63,7 @@ ShaderParameterPack::~ShaderParameterPack() void ShaderParameterPack::reserve(int uniformCount) { m_uniforms.reserve(uniformCount); + m_submissionUniformIndices.reserve(uniformCount); } void ShaderParameterPack::setUniform(const int glslNameId, const UniformValue &val) @@ -107,9 +108,9 @@ void ShaderParameterPack::setShaderStorageBuffer(BlockToSSBO blockToSSBO) m_shaderStorageBuffers.push_back(std::move(blockToSSBO)); } -void ShaderParameterPack::setSubmissionUniform(const ShaderUniform &uniform) +void ShaderParameterPack::setSubmissionUniformIndex(const int uniformIdx) { - m_submissionUniforms.push_back(uniform); + m_submissionUniformIndices.push_back(uniformIdx); } } // namespace OpenGL diff --git a/src/plugins/renderers/opengl/renderer/shaderparameterpack_p.h b/src/plugins/renderers/opengl/renderer/shaderparameterpack_p.h index 10c4da5d4..2ed80ef2a 100644 --- a/src/plugins/renderers/opengl/renderer/shaderparameterpack_p.h +++ b/src/plugins/renderers/opengl/renderer/shaderparameterpack_p.h @@ -157,7 +157,7 @@ public: void setUniformBuffer(BlockToUBO blockToUBO); void setShaderStorageBuffer(BlockToSSBO blockToSSBO); - void setSubmissionUniform(const ShaderUniform &uniform); + void setSubmissionUniformIndex(const int shaderUniformIndex); inline PackUniformHash &uniforms() { return m_uniforms; } inline const PackUniformHash &uniforms() const { return m_uniforms; } @@ -203,7 +203,7 @@ public: inline QVector<NamedResource> images() const { return m_images; } inline QVector<BlockToUBO> uniformBuffers() const { return m_uniformBuffers; } inline QVector<BlockToSSBO> shaderStorageBuffers() const { return m_shaderStorageBuffers; } - inline QVector<ShaderUniform> submissionUniforms() const { return m_submissionUniforms; } + inline QVector<int> submissionUniformIndices() const { return m_submissionUniformIndices; } private: PackUniformHash m_uniforms; @@ -211,7 +211,7 @@ private: QVector<NamedResource> m_images; QVector<BlockToUBO> m_uniformBuffers; QVector<BlockToSSBO> m_shaderStorageBuffers; - QVector<ShaderUniform> m_submissionUniforms; + QVector<int> m_submissionUniformIndices; friend class RenderView; }; diff --git a/src/plugins/renderers/rhi/renderer/renderview.cpp b/src/plugins/renderers/rhi/renderer/renderview.cpp index 1898a0346..df6345896 100644 --- a/src/plugins/renderers/rhi/renderer/renderview.cpp +++ b/src/plugins/renderers/rhi/renderer/renderview.cpp @@ -107,12 +107,6 @@ int LIGHT_COLOR_NAMES[MAX_LIGHTS]; int LIGHT_INTENSITY_NAMES[MAX_LIGHTS]; QString LIGHT_STRUCT_NAMES[MAX_LIGHTS]; -int LIGHT_POSITION_UNROLL_NAMES[MAX_LIGHTS]; -int LIGHT_TYPE_UNROLL_NAMES[MAX_LIGHTS]; -int LIGHT_COLOR_UNROLL_NAMES[MAX_LIGHTS]; -int LIGHT_INTENSITY_UNROLL_NAMES[MAX_LIGHTS]; -QString LIGHT_STRUCT_UNROLL_NAMES[MAX_LIGHTS]; - std::atomic_bool wasInitialized{}; } // anonymous namespace @@ -161,11 +155,30 @@ static QRectF resolveViewport(const QRectF &fractionalViewport, const QSize &sur fractionalViewport.height() * surfaceSize.height()); } -static Matrix4x4 getProjectionMatrix(const CameraLens *lens) +static Matrix4x4 getProjectionMatrix(const CameraLens *lens, bool yIsUp) { if (!lens) qWarning() << "[Qt3D Renderer] No Camera Lens found. Add a CameraSelector to your Frame Graph or make sure that no entities will be rendered."; - return lens ? lens->projection() : Matrix4x4(); + if (lens) + { + if (yIsUp) + { + // OpenGL + return lens->projection(); + } + else + { + // Others. Note : this could likely be optimized... + auto p = lens->projection(); + Matrix4x4 rev{0, 0, 0, 0, 0, - 2* p.m22(), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + p += rev; + return p; + } + } + else + { + return Matrix4x4(); + } } RenderView::RenderView() @@ -202,12 +215,6 @@ RenderView::RenderView() LIGHT_TYPE_NAMES[i] = StringToInt::lookupId(LIGHT_STRUCT_NAMES[i] + LIGHT_TYPE_NAME); LIGHT_COLOR_NAMES[i] = StringToInt::lookupId(LIGHT_STRUCT_NAMES[i] + LIGHT_COLOR_NAME); LIGHT_INTENSITY_NAMES[i] = StringToInt::lookupId(LIGHT_STRUCT_NAMES[i] + LIGHT_INTENSITY_NAME); - - LIGHT_STRUCT_UNROLL_NAMES[i] = QLatin1String("light_") + QLatin1Char(char('0' + i)); - LIGHT_POSITION_UNROLL_NAMES[i] = StringToInt::lookupId(LIGHT_STRUCT_UNROLL_NAMES[i] + LIGHT_POSITION_NAME); - LIGHT_TYPE_UNROLL_NAMES[i] = StringToInt::lookupId(LIGHT_STRUCT_UNROLL_NAMES[i] + LIGHT_TYPE_NAME); - LIGHT_COLOR_UNROLL_NAMES[i] = StringToInt::lookupId(LIGHT_STRUCT_UNROLL_NAMES[i] + LIGHT_COLOR_NAME); - LIGHT_INTENSITY_UNROLL_NAMES[i] = StringToInt::lookupId(LIGHT_STRUCT_UNROLL_NAMES[i] + LIGHT_INTENSITY_NAME); } } } @@ -363,6 +370,7 @@ struct SubRangeSorter<QSortPolicy::Texture> { static void sortSubRange(CommandIt begin, const CommandIt end) { +#ifndef Q_OS_WIN std::stable_sort(begin, end, [] (const RenderCommand &a, const RenderCommand &b) { QVector<ShaderParameterPack::NamedResource> texturesA = a.m_parameterPack.textures(); QVector<ShaderParameterPack::NamedResource> texturesB = b.m_parameterPack.textures(); @@ -381,6 +389,7 @@ struct SubRangeSorter<QSortPolicy::Texture> return identicalTextureCount < originalTextureASize; }); +#endif } }; @@ -583,6 +592,9 @@ EntityRenderCommandData RenderView::buildDrawRenderCommands(const QVector<Entity HGeometry geometryHandle = m_manager->geometryManager()->lookupHandle(geometryRenderer->geometryId()); Geometry *geometry = m_manager->geometryManager()->data(geometryHandle); + if (geometry == nullptr) + continue; + // 1 RenderCommand per RenderPass pass on an Entity with a Mesh for (const RenderPassParameterData &passData : renderPassData) { // Add the RenderPass Parameters @@ -765,9 +777,10 @@ void RenderView::updateRenderCommand(EntityRenderCommandData *renderCommandData, m_localData.setLocalData(builder); // Update RenderViewUBO (Qt3D standard uniforms) - const Matrix4x4 projectionMatrix = getProjectionMatrix(m_data.m_renderCameraLens); + const bool yIsUp = m_renderer->submissionContext()->rhi()->isYUpInFramebuffer(); + const Matrix4x4 projectionMatrix = getProjectionMatrix(m_data.m_renderCameraLens, yIsUp); const Matrix4x4 inverseViewMatrix = m_data.m_viewMatrix.inverted(); - const Matrix4x4 inversedProjectionMatrix = getProjectionMatrix(m_data.m_renderCameraLens).inverted(); + const Matrix4x4 inversedProjectionMatrix = projectionMatrix.inverted(); const Matrix4x4 viewProjectionMatrix = (projectionMatrix * m_data.m_viewMatrix); const Matrix4x4 inversedViewProjectionMatrix = viewProjectionMatrix.inverted(); { @@ -1099,11 +1112,6 @@ void RenderView::setShaderAndUniforms(RenderCommand *command, setUniformValue(command->m_parameterPack, LIGHT_COLOR_NAMES[lightIdx], Vector3D(1.0f, 1.0f, 1.0f)); setUniformValue(command->m_parameterPack, LIGHT_INTENSITY_NAMES[lightIdx], 0.5f); - setUniformValue(command->m_parameterPack, LIGHT_POSITION_UNROLL_NAMES[lightIdx], worldPos); - setUniformValue(command->m_parameterPack, LIGHT_TYPE_UNROLL_NAMES[lightIdx], int(QAbstractLight::PointLight)); - setUniformValue(command->m_parameterPack, LIGHT_COLOR_UNROLL_NAMES[lightIdx], Vector3D(1.0f, 1.0f, 1.0f)); - setUniformValue(command->m_parameterPack, LIGHT_INTENSITY_UNROLL_NAMES[lightIdx], 0.5f); - // There is no risk in doing that even if multithreaded // since we are sure that a shaderData is unique for a given light @@ -1113,7 +1121,6 @@ void RenderView::setShaderAndUniforms(RenderCommand *command, shaderData->updateWorldTransform(*worldTransform); setDefaultUniformBlockShaderDataValue(command->m_parameterPack, shader, shaderData, LIGHT_STRUCT_NAMES[lightIdx]); - setDefaultUniformBlockShaderDataValue(command->m_parameterPack, shader, shaderData, LIGHT_STRUCT_UNROLL_NAMES[lightIdx]); ++lightIdx; } } @@ -1128,11 +1135,6 @@ void RenderView::setShaderAndUniforms(RenderCommand *command, setUniformValue(command->m_parameterPack, LIGHT_TYPE_NAMES[0], int(QAbstractLight::PointLight)); setUniformValue(command->m_parameterPack, LIGHT_COLOR_NAMES[0], Vector3D(1.0f, 1.0f, 1.0f)); setUniformValue(command->m_parameterPack, LIGHT_INTENSITY_NAMES[0], 0.5f); - - setUniformValue(command->m_parameterPack, LIGHT_POSITION_UNROLL_NAMES[0], Vector3D(10.0f, 10.0f, 0.0f)); - setUniformValue(command->m_parameterPack, LIGHT_TYPE_UNROLL_NAMES[0], int(QAbstractLight::PointLight)); - setUniformValue(command->m_parameterPack, LIGHT_COLOR_UNROLL_NAMES[0], Vector3D(1.0f, 1.0f, 1.0f)); - setUniformValue(command->m_parameterPack, LIGHT_INTENSITY_UNROLL_NAMES[0], 0.5f); } // Environment Light diff --git a/src/quick3d/imports/scene3d/importsscene3d.pro b/src/quick3d/imports/scene3d/importsscene3d.pro index 6bf4f5d91..969b7449b 100644 --- a/src/quick3d/imports/scene3d/importsscene3d.pro +++ b/src/quick3d/imports/scene3d/importsscene3d.pro @@ -14,7 +14,6 @@ HEADERS += \ qtquickscene3dplugin.h \ scene3dlogging_p.h \ scene3ditem_p.h \ - scene3dcleaner_p.h \ scene3drenderer_p.h \ scene3dsgnode_p.h \ scene3dsgmaterialshader_p.h \ @@ -25,7 +24,6 @@ SOURCES += \ qtquickscene3dplugin.cpp \ scene3ditem.cpp \ scene3dlogging.cpp \ - scene3dcleaner.cpp \ scene3drenderer.cpp \ scene3dsgnode.cpp \ scene3dsgmaterialshader.cpp \ diff --git a/src/quick3d/imports/scene3d/scene3dcleaner.cpp b/src/quick3d/imports/scene3d/scene3dcleaner.cpp deleted file mode 100644 index ec371410d..000000000 --- a/src/quick3d/imports/scene3d/scene3dcleaner.cpp +++ /dev/null @@ -1,75 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 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 "scene3dcleaner_p.h" - -#include <Qt3DCore/qaspectengine.h> -#include <QtCore/qthread.h> - -#include <scene3dlogging_p.h> -#include <scene3drenderer_p.h> - -QT_BEGIN_NAMESPACE - -namespace Qt3DRender { - -Scene3DCleaner::Scene3DCleaner(QObject *parent) - : QObject(parent) - , m_renderer(nullptr) -{ -} - -Scene3DCleaner::~Scene3DCleaner() -{ - qCDebug(Scene3D) << Q_FUNC_INFO << QThread::currentThread(); -} - -void Scene3DCleaner::cleanup() -{ - Q_ASSERT(m_renderer); - delete m_renderer->m_aspectEngine; // also deletes m_renderer->m_renderAspect - m_renderer->m_aspectEngine = nullptr; - m_renderer->m_renderAspect = nullptr; - m_renderer->deleteLater(); - deleteLater(); -} - -} // namespace Qt3DRender - -QT_END_NAMESPACE diff --git a/src/quick3d/imports/scene3d/scene3dcleaner_p.h b/src/quick3d/imports/scene3d/scene3dcleaner_p.h deleted file mode 100644 index a246cbde7..000000000 --- a/src/quick3d/imports/scene3d/scene3dcleaner_p.h +++ /dev/null @@ -1,82 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 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_SCENE3DCLEANER_P_H -#define QT3DRENDER_SCENE3DCLEANER_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <QtCore/QObject> - -QT_BEGIN_NAMESPACE - -namespace Qt3DRender { - -class Scene3DRenderer; - -class Scene3DCleaner : public QObject -{ - Q_OBJECT -public: - explicit Scene3DCleaner(QObject *parent = 0); - ~Scene3DCleaner(); - - void setRenderer(Scene3DRenderer *renderer) { m_renderer = renderer; } - -public Q_SLOTS: - void cleanup(); - -private: - Scene3DRenderer *m_renderer; -}; - -} // namespace Qt3DRender - -QT_END_NAMESPACE - -#endif // QT3DRENDER_SCENE3DCLEANER_H diff --git a/src/quick3d/imports/scene3d/scene3ditem.cpp b/src/quick3d/imports/scene3d/scene3ditem.cpp index 37310a071..36569f0f7 100644 --- a/src/quick3d/imports/scene3d/scene3ditem.cpp +++ b/src/quick3d/imports/scene3d/scene3ditem.cpp @@ -68,7 +68,6 @@ #include <Qt3DRender/private/qrendersurfaceselector_p.h> #include <Qt3DRender/private/qrenderaspect_p.h> #include <Qt3DRender/private/rendersettings_p.h> -#include <scene3dcleaner_p.h> #include <scene3dlogging_p.h> #include <scene3drenderer_p.h> #include <scene3dsgnode_p.h> @@ -150,8 +149,8 @@ Scene3DItem::Scene3DItem(QQuickItem *parent) , m_viewHolderFG(nullptr) , m_aspectEngine(new Qt3DCore::QAspectEngine()) , m_renderAspect(nullptr) + , m_aspectToDelete(nullptr) , m_renderer(nullptr) - , m_rendererCleaner(new Scene3DCleaner()) , m_multisample(true) , m_dirty(true) , m_dirtyViews(false) @@ -180,6 +179,8 @@ Scene3DItem::~Scene3DItem() // When the window is closed, it first destroys all of its children. At // this point, Scene3DItem is destroyed but the Renderer, AspectEngine and // Scene3DSGNode still exist and will perform their cleanup on their own. + m_aspectEngine->deleteLater(); + m_renderer->deleteLater(); } /*! @@ -205,18 +206,11 @@ QStringList Scene3DItem::aspects() const */ Qt3DCore::QEntity *Scene3DItem::entity() const { - return m_entity; + return m_entity.data(); } -void Scene3DItem::setAspects(const QStringList &aspects) +void Scene3DItem::applyAspects() { - if (!m_aspects.isEmpty()) { - qCWarning(Scene3D) << "Aspects already set on the Scene3D, ignoring"; - return; - } - - m_aspects = aspects; - // Aspects are owned by the aspect engine for (const QString &aspect : qAsConst(m_aspects)) { if (aspect == QLatin1String("render")) // This one is hardwired anyway @@ -247,16 +241,27 @@ void Scene3DItem::setAspects(const QStringList &aspects) } m_aspectEngine->registerAspect(aspect); } +} + +void Scene3DItem::setAspects(const QStringList &aspects) +{ + if (!m_aspects.isEmpty()) { + qWarning() << "Aspects already set on the Scene3D, ignoring"; + return; + } + + m_aspects = aspects; + applyAspects(); emit aspectsChanged(); } void Scene3DItem::setEntity(Qt3DCore::QEntity *entity) { - if (entity == m_entity) + if (entity == m_entity.data()) return; - m_entity = entity; + m_entity.reset(entity); emit entityChanged(); } @@ -397,14 +402,21 @@ void Scene3DItem::removeView(Scene3DView *view) void Scene3DItem::applyRootEntityChange() { - if (m_aspectEngine->rootEntity() != m_entity) { - m_aspectEngine->setRootEntity(Qt3DCore::QEntityPtr(m_entity)); + if (m_aspectEngine->rootEntity() != m_entity.data()) { + m_aspectEngine->setRootEntity(m_entity); + + /* If we changed window, the old aspect engine must be deleted only after we have set + the root entity for the new one so that it doesn't delete the root node. */ + if (m_aspectToDelete) { + delete m_aspectToDelete; + m_aspectToDelete = nullptr; + } // Set the render surface if (!m_entity) return; - setWindowSurface(m_entity); + setWindowSurface(entity()); if (m_cameraAspectRatioMode == AutomaticAspectRatio) { // Set aspect ratio of first camera to match the window @@ -480,6 +492,8 @@ void Scene3DItem::onBeforeSync() // if the Scene3D item is not visible if (!isVisible() && dontRenderWhenHidden) return; + if (m_renderer->m_resetRequested) + return; Q_ASSERT(QThread::currentThread() == thread()); @@ -495,7 +509,7 @@ void Scene3DItem::onBeforeSync() // Make renderer aware of any Scene3DView we are dealing with if (m_dirtyViews) { // Scene3DViews checks - if (m_entity != m_viewHolderEntity) { + if (entity() != m_viewHolderEntity) { qCWarning(Scene3D) << "Scene3DView is not supported if the Scene3D entity property has been set"; } if (!usesFBO) { @@ -543,6 +557,20 @@ void Scene3DItem::requestUpdate() } } +void Scene3DItem::updateWindowSurface() +{ + if (!m_entity || !m_dummySurface) + return; + Qt3DRender::QRenderSurfaceSelector *surfaceSelector = + Qt3DRender::QRenderSurfaceSelectorPrivate::find(entity()); + if (surfaceSelector) { + if (QWindow *rw = QQuickRenderControl::renderWindowFor(this->window())) { + m_dummySurface->deleteLater(); + createDummySurface(rw, surfaceSelector); + } + } +} + void Scene3DItem::setWindowSurface(QObject *rootObject) { Qt3DRender::QRenderSurfaceSelector *surfaceSelector = Qt3DRender::QRenderSurfaceSelectorPrivate::find(rootObject); @@ -553,20 +581,25 @@ void Scene3DItem::setWindowSurface(QObject *rootObject) // We may not have a real, exposed QQuickWindow when the Quick rendering // is redirected via QQuickRenderControl (f.ex. QQuickWidget). if (QWindow *rw = QQuickRenderControl::renderWindowFor(this->window())) { - // rw is the top-level window that is backed by a native window. Do - // not use that though since we must not clash with e.g. the widget - // backingstore compositor in the gui thread. - m_dummySurface = new QOffscreenSurface; - m_dummySurface->setParent(qGuiApp); // parent to something suitably long-living - m_dummySurface->setFormat(rw->format()); - m_dummySurface->setScreen(rw->screen()); - m_dummySurface->create(); - surfaceSelector->setSurface(m_dummySurface); + createDummySurface(rw, surfaceSelector); } else { surfaceSelector->setSurface(this->window()); } } } + +void Scene3DItem::createDummySurface(QWindow *rw, Qt3DRender::QRenderSurfaceSelector *surfaceSelector) +{ + // rw is the top-level window that is backed by a native window. Do + // not use that though since we must not clash with e.g. the widget + // backingstore compositor in the gui thread. + m_dummySurface = new QOffscreenSurface; + m_dummySurface->setParent(qGuiApp); // parent to something suitably long-living + m_dummySurface->setFormat(rw->format()); + m_dummySurface->setScreen(rw->screen()); + m_dummySurface->create(); + surfaceSelector->setSurface(m_dummySurface); +} /*! \qmlmethod void Scene3D::setItemAreaAndDevicePixelRatio(size area, real devicePixelRatio) @@ -574,7 +607,8 @@ void Scene3DItem::setWindowSurface(QObject *rootObject) */ void Scene3DItem::setItemAreaAndDevicePixelRatio(QSize area, qreal devicePixelRatio) { - Qt3DRender::QRenderSurfaceSelector *surfaceSelector = Qt3DRender::QRenderSurfaceSelectorPrivate::find(m_entity); + Qt3DRender::QRenderSurfaceSelector *surfaceSelector + = Qt3DRender::QRenderSurfaceSelectorPrivate::find(entity()); if (surfaceSelector) { surfaceSelector->setExternalRenderTargetSize(area); surfaceSelector->setSurfacePixelRatio(devicePixelRatio); @@ -664,6 +698,22 @@ void Scene3DItem::setMultisample(bool enable) QSGNode *Scene3DItem::updatePaintNode(QSGNode *node, QQuickItem::UpdatePaintNodeData *) { + // m_resetRequested is set to true by Scene3DRenderer::shutdown() + if (m_renderer && m_renderer->m_resetRequested) { + qCWarning(Scene3D) << "Renderer for Scene3DItem has requested a reset due to the item " + "moving to another window"; + QObject::disconnect(m_windowConnection); + m_aspectEngine->unregisterAspect(m_renderAspect); // Deletes the renderAspect + m_renderAspect = nullptr; + m_aspectToDelete = m_aspectEngine; + m_aspectEngine = new Qt3DCore::QAspectEngine(); + m_aspectEngine->setRunMode(Qt3DCore::QAspectEngine::Manual); + applyAspects(); + // Needs to belong in the same thread as the item which is the same as the original + // QAspectEngine + m_aspectEngine->moveToThread(thread()); + m_renderer->m_resetRequested = false; + } // If the render aspect wasn't created yet, do so now if (m_renderAspect == nullptr) { m_renderAspect = new QRenderAspect(QRenderAspect::Synchronous); @@ -674,16 +724,24 @@ QSGNode *Scene3DItem::updatePaintNode(QSGNode *node, QQuickItem::UpdatePaintNode // Before Synchronizing is in the SG Thread, we want beforeSync to be triggered // in the context of the main thread - QObject::connect(window(), &QQuickWindow::afterAnimating, - this, &Scene3DItem::onBeforeSync, Qt::DirectConnection); + m_windowConnection = QObject::connect(window(), &QQuickWindow::afterAnimating, + this, &Scene3DItem::onBeforeSync, Qt::DirectConnection); auto renderAspectPriv = static_cast<QRenderAspectPrivate*>(QRenderAspectPrivate::get(m_renderAspect)); QObject::connect(renderAspectPriv->m_aspectManager->changeArbiter(), &Qt3DCore::QChangeArbiter::receivedChange, this, [this] { m_dirty = true; }, Qt::DirectConnection); } if (m_renderer == nullptr) { - m_renderer = new Scene3DRenderer(this, m_aspectEngine, m_renderAspect); - m_renderer->setCleanerHelper(m_rendererCleaner); + m_renderer = new Scene3DRenderer(); + m_renderer->init(this, m_aspectEngine, m_renderAspect); + } else if (m_renderer->renderAspect() != m_renderAspect) { + // If the renderer's renderAspect is not equal to the aspect used + // by the item, then it means that we have created a new one due to + // the fact that shutdown() was called on the renderer previously. + // This is a typical situation when the window the item is in has + // moved from one screen to another. + updateWindowSurface(); + m_renderer->init(this, m_aspectEngine, m_renderAspect); } const bool usesFBO = m_compositingMode == FBO; const bool hasScene3DViews = !m_views.empty(); diff --git a/src/quick3d/imports/scene3d/scene3ditem_p.h b/src/quick3d/imports/scene3d/scene3ditem_p.h index e46bb20af..0beaf94c0 100644 --- a/src/quick3d/imports/scene3d/scene3ditem_p.h +++ b/src/quick3d/imports/scene3d/scene3ditem_p.h @@ -71,6 +71,7 @@ class Scene3DRenderer; class Scene3DCleaner; class Scene3DView; class QFrameGraphNode; +class QRenderSurfaceSelector; class Scene3DItem : public QQuickItem { @@ -138,16 +139,20 @@ private: void updateCameraAspectRatio(); void mousePressEvent(QMouseEvent *event) override; bool needsRender(); + void updateWindowSurface(); + void createDummySurface(QWindow *window, QRenderSurfaceSelector *surfaceSelector); + void applyAspects(); QStringList m_aspects; - Qt3DCore::QEntity *m_entity; + // Store as shared pointer so that aspect engine doesn't delete it. + QSharedPointer<Qt3DCore::QEntity> m_entity; Qt3DCore::QEntity *m_viewHolderEntity; Qt3DRender::QFrameGraphNode *m_viewHolderFG; Qt3DCore::QAspectEngine *m_aspectEngine; + Qt3DCore::QAspectEngine *m_aspectToDelete; QRenderAspect *m_renderAspect; Scene3DRenderer *m_renderer; - Scene3DCleaner *m_rendererCleaner; bool m_multisample; bool m_dirty; @@ -160,6 +165,7 @@ private: CompositingMode m_compositingMode; QOffscreenSurface *m_dummySurface; QVector<Scene3DView *> m_views; + QMetaObject::Connection m_windowConnection; }; } // Qt3DRender diff --git a/src/quick3d/imports/scene3d/scene3drenderer.cpp b/src/quick3d/imports/scene3d/scene3drenderer.cpp index 71cf6c240..96514b32d 100644 --- a/src/quick3d/imports/scene3d/scene3drenderer.cpp +++ b/src/quick3d/imports/scene3d/scene3drenderer.cpp @@ -53,7 +53,6 @@ #include <Qt3DCore/private/qchangearbiter_p.h> #include <Qt3DCore/private/qservicelocator_p.h> -#include <scene3dcleaner_p.h> #include <scene3ditem_p.h> #include <scene3dlogging_p.h> #include <scene3dsgnode_p.h> @@ -144,16 +143,15 @@ private: signal of the window is not called. Therefore the cleanup method is invoked to properly destroy the aspect engine. */ -Scene3DRenderer::Scene3DRenderer(Scene3DItem *item, Qt3DCore::QAspectEngine *aspectEngine, QRenderAspect *renderAspect) +Scene3DRenderer::Scene3DRenderer() : QObject() - , m_item(item) - , m_aspectEngine(aspectEngine) - , m_renderAspect(renderAspect) + , m_item(nullptr) + , m_aspectEngine(nullptr) + , m_renderAspect(nullptr) , m_multisampledFBO(nullptr) , m_finalFBO(nullptr) , m_texture(nullptr) , m_node(nullptr) - , m_cleaner(nullptr) , m_window(nullptr) , m_multisample(false) // this value is not used, will be synced from the Scene3DItem instead , m_lastMultisample(false) @@ -165,6 +163,17 @@ Scene3DRenderer::Scene3DRenderer(Scene3DItem *item, Qt3DCore::QAspectEngine *asp , m_allowRendering(0) , m_compositingMode(Scene3DItem::FBO) { +} + +void Scene3DRenderer::init(Scene3DItem *item, Qt3DCore::QAspectEngine *aspectEngine, + QRenderAspect *renderAspect) +{ + m_item = item; + m_window = m_item->window(); + m_aspectEngine = aspectEngine; + m_renderAspect = renderAspect; + m_needsShutdown = true; + Q_CHECK_PTR(m_item); Q_CHECK_PTR(m_item->window()); @@ -215,21 +224,14 @@ void Scene3DRenderer::scheduleRootEntityChange() QMetaObject::invokeMethod(m_item, "applyRootEntityChange", Qt::QueuedConnection); } -void Scene3DRenderer::setCleanerHelper(Scene3DCleaner *cleaner) -{ - m_cleaner = cleaner; - if (m_cleaner) { - // Window closed case - QObject::connect(m_item->window(), &QQuickWindow::destroyed, m_cleaner, &Scene3DCleaner::cleanup); - m_cleaner->setRenderer(this); - } -} - // Executed in the QtQuick render thread (which may even be the gui/main with QQuickWidget / RenderControl). void Scene3DRenderer::shutdown() { qCDebug(Scene3D) << Q_FUNC_INFO << QThread::currentThread(); + // In case the same item is rendered on another window reset it + m_resetRequested = true; + // Set to null so that subsequent calls to render // would return early m_item = nullptr; @@ -243,8 +245,13 @@ void Scene3DRenderer::shutdown() // Shutdown the Renderer Aspect while the OpenGL context // is still valid - if (m_renderAspect) + if (m_renderAspect) { static_cast<QRenderAspectPrivate*>(QRenderAspectPrivate::get(m_renderAspect))->renderShutdown(); + m_renderAspect = nullptr; + } + m_aspectEngine = nullptr; + m_finalFBO.reset(); + m_multisampledFBO.reset(); } // QtQuick render thread (which may also be the gui/main thread with QQuickWidget / RenderControl) @@ -254,7 +261,6 @@ void Scene3DRenderer::onSceneGraphInvalidated() if (m_needsShutdown) { m_needsShutdown = false; shutdown(); - QMetaObject::invokeMethod(m_cleaner, "cleanup"); } } @@ -265,7 +271,6 @@ void Scene3DRenderer::onWindowChanged(QQuickWindow *w) if (m_needsShutdown) { m_needsShutdown = false; shutdown(); - QMetaObject::invokeMethod(m_cleaner, "cleanup"); } } } @@ -288,6 +293,7 @@ void Scene3DRenderer::beforeSynchronize() // SceneGraph update for nothing if (m_skipFrame) { m_skipFrame = false; + ContextSaver saver; static_cast<QRenderAspectPrivate*>(QRenderAspectPrivate::get(m_renderAspect))->renderSynchronous(false); return; } diff --git a/src/quick3d/imports/scene3d/scene3drenderer_p.h b/src/quick3d/imports/scene3d/scene3drenderer_p.h index 08a2c60a3..5674f21c2 100644 --- a/src/quick3d/imports/scene3d/scene3drenderer_p.h +++ b/src/quick3d/imports/scene3d/scene3drenderer_p.h @@ -78,9 +78,7 @@ class Scene3DRenderer : public QObject { Q_OBJECT public: - Scene3DRenderer(Scene3DItem *item, - Qt3DCore::QAspectEngine *aspectEngine, - QRenderAspect *renderAspect); + Scene3DRenderer(); ~Scene3DRenderer(); void setSGNode(Scene3DSGNode *node); @@ -89,7 +87,12 @@ public: void setCompositingMode(Scene3DItem::CompositingMode mode); void setSkipFrame(bool skip); void setScene3DViews(const QVector<Scene3DView *> views); + void init(Scene3DItem *item, Qt3DCore::QAspectEngine *aspectEngine, QRenderAspect *renderAspect); + QRenderAspect *renderAspect() const + { + return m_renderAspect; + } public Q_SLOTS: void render(); void shutdown(); @@ -103,13 +106,12 @@ private: void scheduleRootEntityChange(); Scene3DItem *m_item; // Will be released by the QQuickWindow/QML Engine - Qt3DCore::QAspectEngine *m_aspectEngine; // Will be released by the Scene3DRendererCleaner + Qt3DCore::QAspectEngine *m_aspectEngine; // Will be released by the Scene3DItem QRenderAspect *m_renderAspect; // Will be released by the aspectEngine QScopedPointer<QOpenGLFramebufferObject> m_multisampledFBO; QScopedPointer<QOpenGLFramebufferObject> m_finalFBO; QScopedPointer<QSGTexture> m_texture; Scene3DSGNode *m_node; // Will be released by the QtQuick SceneGraph - Scene3DCleaner *m_cleaner; QQuickWindow *m_window; QMutex m_windowMutex; QSize m_lastSize; @@ -123,8 +125,9 @@ private: QSemaphore m_allowRendering; Scene3DItem::CompositingMode m_compositingMode; QVector<Scene3DView *> m_views; + bool m_resetRequested = false; - friend class Scene3DCleaner; + friend class Scene3DItem; }; } // namespace Qt3DRender diff --git a/src/render/framegraph/qlayerfilter.cpp b/src/render/framegraph/qlayerfilter.cpp index 8bad46f5d..5557687ce 100644 --- a/src/render/framegraph/qlayerfilter.cpp +++ b/src/render/framegraph/qlayerfilter.cpp @@ -214,8 +214,9 @@ void QLayerFilter::removeLayer(QLayer *layer) { Q_ASSERT(layer); Q_D(QLayerFilter); + if (!d->m_layers.removeOne(layer)) + return; d->updateNode(layer, "layer", Qt3DCore::PropertyValueRemoved); - d->m_layers.removeOne(layer); // Remove bookkeeping connection d->unregisterDestructionHelper(layer); } diff --git a/src/render/framegraph/qrenderpassfilter.cpp b/src/render/framegraph/qrenderpassfilter.cpp index 952657eb6..34462ebdf 100644 --- a/src/render/framegraph/qrenderpassfilter.cpp +++ b/src/render/framegraph/qrenderpassfilter.cpp @@ -151,8 +151,9 @@ void QRenderPassFilter::removeMatch(QFilterKey *filterKey) Q_ASSERT(filterKey); Q_D(QRenderPassFilter); + if (!d->m_matchList.removeOne(filterKey)) + return; d->updateNode(filterKey, "match", Qt3DCore::PropertyValueRemoved); - d->m_matchList.removeOne(filterKey); // Remove bookkeeping connection d->unregisterDestructionHelper(filterKey); } @@ -189,8 +190,9 @@ void QRenderPassFilter::removeParameter(QParameter *parameter) Q_ASSERT(parameter); Q_D(QRenderPassFilter); + if (!d->m_parameters.removeOne(parameter)) + return; d->updateNode(parameter, "parameter", Qt3DCore::PropertyValueRemoved); - d->m_parameters.removeOne(parameter); // Remove bookkeeping connection d->unregisterDestructionHelper(parameter); } diff --git a/src/render/framegraph/qrenderstateset.cpp b/src/render/framegraph/qrenderstateset.cpp index 5341b3c7d..ac82954c8 100644 --- a/src/render/framegraph/qrenderstateset.cpp +++ b/src/render/framegraph/qrenderstateset.cpp @@ -203,8 +203,9 @@ void QRenderStateSet::removeRenderState(QRenderState *state) Q_ASSERT(state); Q_D(QRenderStateSet); + if (!d->m_renderStates.removeOne(state)) + return; d->updateNode(state, "renderState", Qt3DCore::PropertyValueRemoved); - d->m_renderStates.removeOne(state); // Remove bookkeeping connection d->unregisterDestructionHelper(state); } diff --git a/src/render/framegraph/qtechniquefilter.cpp b/src/render/framegraph/qtechniquefilter.cpp index c22e83381..5377e1297 100644 --- a/src/render/framegraph/qtechniquefilter.cpp +++ b/src/render/framegraph/qtechniquefilter.cpp @@ -155,8 +155,9 @@ void QTechniqueFilter::removeMatch(QFilterKey *filterKey) { Q_ASSERT(filterKey); Q_D(QTechniqueFilter); + if (!d->m_matchList.removeOne(filterKey)) + return; d->updateNode(filterKey, "matchAll", Qt3DCore::PropertyValueRemoved); - d->m_matchList.removeOne(filterKey); // Remove bookkeeping connection d->unregisterDestructionHelper(filterKey); } @@ -192,8 +193,9 @@ void QTechniqueFilter::removeParameter(QParameter *parameter) { Q_ASSERT(parameter); Q_D(QTechniqueFilter); + if (!d->m_parameters.removeOne(parameter)) + return; d->updateNode(parameter, "parameter", Qt3DCore::PropertyValueRemoved); - d->m_parameters.removeOne(parameter); // Remove bookkeeping connection d->unregisterDestructionHelper(parameter); } diff --git a/src/render/frontend/qrenderaspect.cpp b/src/render/frontend/qrenderaspect.cpp index ec9417c00..d9b53cb8d 100644 --- a/src/render/frontend/qrenderaspect.cpp +++ b/src/render/frontend/qrenderaspect.cpp @@ -763,9 +763,10 @@ QVariant QRenderAspect::executeCommand(const QStringList &args) if (args.front() == QLatin1String("framepaths")) return Qt3DRender::QFrameGraphNodePrivate::get(fg)->dumpFrameGraphPaths().join(QLatin1String("\n")); if (args.front() == QLatin1String("filterstates")) { - auto res = Qt3DRender::QFrameGraphNodePrivate::get(fg)->dumpFrameGraphFilterState().join(QLatin1String("\n")); - res += dumpSGFilterState(d->m_nodeManagers->techniqueManager(), - d->m_renderer->contextInfo(), d->m_root).join(QLatin1String("\n")); + const auto activeContextInfo = d->m_renderer->contextInfo(); + QString res = QLatin1String("Active Graphics API: ") + activeContextInfo->toString() + QLatin1String("\n"); + res += QLatin1String("Render Views:\n ") + Qt3DRender::QFrameGraphNodePrivate::get(fg)->dumpFrameGraphFilterState().join(QLatin1String("\n ")) + QLatin1String("\n"); + res += QLatin1String("Scene Graph:\n ") + dumpSGFilterState(d->m_nodeManagers->techniqueManager(), activeContextInfo, d->m_root).join(QLatin1String("\n ")); return res; } } diff --git a/src/render/frontend/qrendertarget.cpp b/src/render/frontend/qrendertarget.cpp index 57eecd795..7e0403c13 100644 --- a/src/render/frontend/qrendertarget.cpp +++ b/src/render/frontend/qrendertarget.cpp @@ -134,8 +134,9 @@ void QRenderTarget::removeOutput(QRenderTargetOutput *output) { Q_D(QRenderTarget); + if (!d->m_outputs.removeOne(output)) + return; d->updateNode(output, "output", Qt3DCore::PropertyValueRemoved); - d->m_outputs.removeOne(output); // Remove bookkeeping connection d->unregisterDestructionHelper(output); } diff --git a/src/render/geometry/qgeometry.cpp b/src/render/geometry/qgeometry.cpp index 48f054bce..ac22f2db1 100644 --- a/src/render/geometry/qgeometry.cpp +++ b/src/render/geometry/qgeometry.cpp @@ -206,7 +206,8 @@ void QGeometry::removeAttribute(QAttribute *attribute) { Q_ASSERT(attribute); Q_D(QGeometry); - d->m_attributes.removeOne(attribute); + if (!d->m_attributes.removeOne(attribute)) + return; // Remove bookkeeping connection d->unregisterDestructionHelper(attribute); d->updateNode(attribute, "attribute", Qt3DCore::PropertyValueRemoved); diff --git a/src/render/materialsystem/prototypes/default.json b/src/render/materialsystem/prototypes/default.json index 597de41c3..6a25477e0 100644 --- a/src/render/materialsystem/prototypes/default.json +++ b/src/render/materialsystem/prototypes/default.json @@ -32,6 +32,15 @@ }, "substitution": "$type $value = $name;", "headerSnippets": [ "$qualifier $type $name;" ] + }, + { + "format": { + "api": "RHI", + "major": 4, + "minor": 5 + }, + "substitution": "$type $value = $name;", + "headerSnippets": [ "add-input $qualifier $type $name" ] } ] }, @@ -102,6 +111,15 @@ }, "substitution": "vec4 $color = texture($name, $coord);", "headerSnippets": [ "uniform sampler2D $name;" ] + }, + { + "format": { + "api": "RHI", + "major": 4, + "minor": 5 + }, + "substitution": "vec4 $color = texture($name, $coord);", + "headerSnippets": [ "add-sampler sampler2D $name" ] } ] }, @@ -135,6 +153,15 @@ }, "substitution": "fragColor = $fragColor;", "headerSnippets": [ "out vec4 fragColor;" ] + }, + { + "format": { + "api": "RHI", + "major": 4, + "minor": 5 + }, + "substitution": "fragColor = $fragColor;", + "headerSnippets": [ "layout(location = 0) out vec4 fragColor;" ] } ] }, @@ -160,6 +187,15 @@ }, "substitution": "vec3 $eyePosition = eyePosition;", "headerSnippets": [ "uniform vec3 eyePosition;" ] + }, + { + "format": { + "api": "RHI", + "major": 4, + "minor": 5 + }, + "substitution": "vec3 $eyePosition = eyePosition;", + "headerSnippets": [ "add-uniform vec3 eyePosition" ] } ] }, @@ -185,6 +221,15 @@ }, "substitution": "float $time = time;", "headerSnippets": [ "uniform float time;" ] + }, + { + "format": { + "api": "RHI", + "major": 4, + "minor": 5 + }, + "substitution": "float $time = time;", + "headerSnippets": [ "add-uniform float time" ] } ] }, @@ -419,6 +464,15 @@ }, "substitution": "mat3 $matrix = calcWorldSpaceToTangentSpaceMatrix($worldNormal, $worldTangent);", "headerSnippets": [ "#pragma include :/shaders/gl3/coordinatesystems.inc" ] + }, + { + "format": { + "api": "RHI", + "major": 4, + "minor": 5 + }, + "substitution": "mat3 $matrix = calcWorldSpaceToTangentSpaceMatrix($worldNormal, $worldTangent);", + "headerSnippets": [ "#pragma include :/shaders/rhi/coordinatesystems.inc" ] } ] }, @@ -453,6 +507,15 @@ }, "substitution": "vec4 $outputColor = phongFunction($ambient, $diffuse, $specular, $shininess, $worldPosition, $worldView, $worldNormal);", "headerSnippets": [ "#pragma include :/shaders/gl3/phong.inc.frag" ] + }, + { + "format": { + "api": "RHI", + "major": 4, + "minor": 5 + }, + "substitution": "vec4 $outputColor = phongFunction($ambient, $diffuse, $specular, $shininess, $worldPosition, $worldView, $worldNormal);", + "headerSnippets": [ "#pragma include :/shaders/rhi/phong.inc.frag" ] } ] }, @@ -487,6 +550,15 @@ }, "substitution": "vec4 $outputColor = metalRoughFunction($baseColor, $metalness, $roughness, $ambientOcclusion, $worldPosition, $worldView, $worldNormal);", "headerSnippets": [ "#pragma include :/shaders/gl3/metalrough.inc.frag" ] + }, + { + "format": { + "api": "RHI", + "major": 4, + "minor": 5 + }, + "substitution": "vec4 $outputColor = metalRoughFunction($baseColor, $metalness, $roughness, $ambientOcclusion, $worldPosition, $worldView, $worldNormal);", + "headerSnippets": [ "#pragma include :/shaders/rhi/metalrough.inc.frag" ] } ] }, diff --git a/src/render/materialsystem/qeffect.cpp b/src/render/materialsystem/qeffect.cpp index 8637a92d4..9f4b3db94 100644 --- a/src/render/materialsystem/qeffect.cpp +++ b/src/render/materialsystem/qeffect.cpp @@ -203,7 +203,8 @@ void QEffect::removeParameter(QParameter *parameter) { Q_D(QEffect); - d->m_parameters.removeOne(parameter); + if (!d->m_parameters.removeOne(parameter)) + return; // Remove bookkeeping connection d->unregisterDestructionHelper(parameter); d->updateNode(parameter, "parameter", Qt3DCore::PropertyValueRemoved); @@ -248,9 +249,9 @@ void QEffect::addTechnique(QTechnique *t) void QEffect::removeTechnique(QTechnique *t) { Q_D(QEffect); - if (t) - d->updateNode(t, "technique", Qt3DCore::PropertyValueRemoved); - d->m_techniques.removeOne(t); + if (!d->m_techniques.removeOne(t)) + return; + d->updateNode(t, "technique", Qt3DCore::PropertyValueRemoved); // Remove bookkeeping connection d->unregisterDestructionHelper(t); } diff --git a/src/render/materialsystem/qgraphicsapifilter.cpp b/src/render/materialsystem/qgraphicsapifilter.cpp index 9b5557930..e8b93de6e 100644 --- a/src/render/materialsystem/qgraphicsapifilter.cpp +++ b/src/render/materialsystem/qgraphicsapifilter.cpp @@ -53,6 +53,28 @@ GraphicsApiFilterData::GraphicsApiFilterData() , m_major(0) {} +QString GraphicsApiFilterData::toString() const +{ + QLatin1String api; + switch (m_api) { + case QGraphicsApiFilter::OpenGL: api = QLatin1String("OpenGL"); break; + case QGraphicsApiFilter::OpenGLES: api = QLatin1String("OpenGL"); break; + case QGraphicsApiFilter::Vulkan: api = QLatin1String("Vulkan"); break; + case QGraphicsApiFilter::DirectX: api = QLatin1String("DirectX"); break; + case QGraphicsApiFilter::RHI: api = QLatin1String("RHI"); break; + default: Q_UNREACHABLE(); + } + + QLatin1String profile; + switch (m_profile) { + case QGraphicsApiFilter::CoreProfile: profile = QLatin1String(" (Core Profile)"); break; + case QGraphicsApiFilter::CompatibilityProfile: profile = QLatin1String(" (Compatibility Profile)"); break; + default: break; + } + + return QString(QLatin1String("%1 %2.%3%4 (%5)").arg(api, QString::number(m_major), QString::number(m_minor), profile, m_vendor)); +} + bool GraphicsApiFilterData::operator ==(const GraphicsApiFilterData &other) const { // Check API diff --git a/src/render/materialsystem/qgraphicsapifilter_p.h b/src/render/materialsystem/qgraphicsapifilter_p.h index 435451c27..52c489785 100644 --- a/src/render/materialsystem/qgraphicsapifilter_p.h +++ b/src/render/materialsystem/qgraphicsapifilter_p.h @@ -70,6 +70,8 @@ struct Q_3DRENDERSHARED_PRIVATE_EXPORT GraphicsApiFilterData QStringList m_extensions; QString m_vendor; + QString toString() const; + bool operator ==(const GraphicsApiFilterData &other) const; bool operator !=(const GraphicsApiFilterData &other) const; bool operator <(const GraphicsApiFilterData &other) const; diff --git a/src/render/materialsystem/qmaterial.cpp b/src/render/materialsystem/qmaterial.cpp index edd227500..a1bec183b 100644 --- a/src/render/materialsystem/qmaterial.cpp +++ b/src/render/materialsystem/qmaterial.cpp @@ -283,8 +283,10 @@ void QMaterial::removeParameter(QParameter *parameter) { Q_ASSERT(parameter); Q_D(QMaterial); + if (!d->m_parameters.removeOne(parameter)) + return; + d->unregisterDestructionHelper(parameter); d->updateNode(parameter, "parameter", Qt3DCore::PropertyValueRemoved); - d->m_parameters.removeOne(parameter); } /*! diff --git a/src/render/materialsystem/qrenderpass.cpp b/src/render/materialsystem/qrenderpass.cpp index dcb831b13..33181f4fe 100644 --- a/src/render/materialsystem/qrenderpass.cpp +++ b/src/render/materialsystem/qrenderpass.cpp @@ -285,8 +285,9 @@ void QRenderPass::removeFilterKey(QFilterKey *filterKey) { Q_ASSERT(filterKey); Q_D(QRenderPass); + if (!d->m_filterKeyList.removeOne(filterKey)) + return; d->updateNode(filterKey, "filterKeys", Qt3DCore::PropertyValueRemoved); - d->m_filterKeyList.removeOne(filterKey); // Remove bookkeeping connection d->unregisterDestructionHelper(filterKey); } @@ -333,8 +334,9 @@ void QRenderPass::removeRenderState(QRenderState *state) { Q_ASSERT(state); Q_D(QRenderPass); + if (!d->m_renderStates.removeOne(state)) + return; d->updateNode(state, "renderState", Qt3DCore::PropertyValueRemoved); - d->m_renderStates.removeOne(state); // Remove bookkeeping connection d->unregisterDestructionHelper(state); } @@ -380,8 +382,9 @@ void QRenderPass::removeParameter(QParameter *parameter) { Q_ASSERT(parameter); Q_D(QRenderPass); + if (!d->m_parameters.removeOne(parameter)) + return; d->updateNode(parameter, "parameter", Qt3DCore::PropertyValueRemoved); - d->m_parameters.removeOne(parameter); // Remove bookkeeping connection d->unregisterDestructionHelper(parameter); } diff --git a/src/render/materialsystem/qtechnique.cpp b/src/render/materialsystem/qtechnique.cpp index a9b7ca82b..0e1787938 100644 --- a/src/render/materialsystem/qtechnique.cpp +++ b/src/render/materialsystem/qtechnique.cpp @@ -269,8 +269,9 @@ void QTechnique::removeFilterKey(QFilterKey *filterKey) { Q_ASSERT(filterKey); Q_D(QTechnique); + if (!d->m_filterKeys.removeOne(filterKey)) + return; d->updateNode(filterKey, "filterKeys", Qt3DCore::PropertyValueRemoved); - d->m_filterKeys.removeOne(filterKey); // Remove bookkeeping connection d->unregisterDestructionHelper(filterKey); } @@ -316,8 +317,9 @@ void QTechnique::removeParameter(QParameter *parameter) { Q_ASSERT(parameter); Q_D(QTechnique); + if (!d->m_parameters.removeOne(parameter)) + return; d->updateNode(parameter, "parameter", Qt3DCore::PropertyValueRemoved); - d->m_parameters.removeOne(parameter); // Remove bookkeeping connection d->unregisterDestructionHelper(parameter); } @@ -353,8 +355,9 @@ void QTechnique::removeRenderPass(QRenderPass *pass) { Q_ASSERT(pass); Q_D(QTechnique); + if (!d->m_renderPasses.removeOne(pass)) + return; d->updateNode(pass, "pass", Qt3DCore::PropertyValueAdded); - d->m_renderPasses.removeOne(pass); // Remove bookkeeping connection d->unregisterDestructionHelper(pass); } diff --git a/src/render/materialsystem/shaderbuilder.cpp b/src/render/materialsystem/shaderbuilder.cpp index b902e3d8c..996270b9e 100644 --- a/src/render/materialsystem/shaderbuilder.cpp +++ b/src/render/materialsystem/shaderbuilder.cpp @@ -246,7 +246,8 @@ void ShaderBuilder::generateCode(QShaderProgram::ShaderType type) generator.graph = graph; const auto code = generator.createShaderCode(m_enabledLayers); - m_codes.insert(type, QShaderProgramPrivate::deincludify(code, graphPath + QStringLiteral(".glsl"))); + const auto deincludified = QShaderProgramPrivate::deincludify(code, graphPath + QStringLiteral(".glsl")); + m_codes.insert(type, deincludified); m_dirtyTypes.remove(type); m_pendingUpdates.push_back({ peerId(), @@ -278,7 +279,7 @@ void ShaderBuilder::syncFromFrontEnd(const QNode *frontEnd, bool firstTime) markDirty(AbstractRenderer::ShadersDirty); } - static const QVector<std::pair<QShaderProgram::ShaderType, QUrl (QShaderProgramBuilder::*)() const>> shaderTypesToGetters = { + static const QVarLengthArray<std::pair<QShaderProgram::ShaderType, QUrl (QShaderProgramBuilder::*)() const>, 6> shaderTypesToGetters { {QShaderProgram::Vertex, &QShaderProgramBuilder::vertexShaderGraph}, {QShaderProgram::TessellationControl, &QShaderProgramBuilder::tessellationControlShaderGraph}, {QShaderProgram::TessellationEvaluation, &QShaderProgramBuilder::tessellationEvaluationShaderGraph}, diff --git a/src/render/materialsystem/shaderdata.cpp b/src/render/materialsystem/shaderdata.cpp index 67c8f35f7..97db303c2 100644 --- a/src/render/materialsystem/shaderdata.cpp +++ b/src/render/materialsystem/shaderdata.cpp @@ -162,7 +162,7 @@ ShaderData *ShaderData::lookupResource(QNodeId id) } // RenderCommand updater jobs -QVariant ShaderData::getTransformedProperty(const QString &name, const Matrix4x4 &viewMatrix) +QVariant ShaderData::getTransformedProperty(const QString &name, const Matrix4x4 &viewMatrix) const { // Note protecting m_worldMatrix at this point as we assume all world updates // have been performed when reaching this point diff --git a/src/render/materialsystem/shaderdata_p.h b/src/render/materialsystem/shaderdata_p.h index 5c6352eba..080d2c85e 100644 --- a/src/render/materialsystem/shaderdata_p.h +++ b/src/render/materialsystem/shaderdata_p.h @@ -89,7 +89,7 @@ public: // Called by FramePreparationJob void updateWorldTransform(const Matrix4x4 &worldMatrix); - QVariant getTransformedProperty(const QString &name, const Matrix4x4 &viewMatrix); + QVariant getTransformedProperty(const QString &name, const Matrix4x4 &viewMatrix) const; // Unit tests purposes only TransformType propertyTransformType(const QString &name) const; diff --git a/src/render/picking/qabstractraycaster.cpp b/src/render/picking/qabstractraycaster.cpp index f26c67e99..42c7bc1e5 100644 --- a/src/render/picking/qabstractraycaster.cpp +++ b/src/render/picking/qabstractraycaster.cpp @@ -348,8 +348,9 @@ void QAbstractRayCaster::removeLayer(QLayer *layer) { Q_ASSERT(layer); Q_D(QAbstractRayCaster); + if (!d->m_layers.removeOne(layer)) + return; d->updateNode(layer, "layer", Qt3DCore::PropertyValueRemoved); - d->m_layers.removeOne(layer); // Remove bookkeeping connection d->unregisterDestructionHelper(layer); } diff --git a/src/render/shadergraph/qshadergenerator.cpp b/src/render/shadergraph/qshadergenerator.cpp index 4f37cfef1..15178fc71 100644 --- a/src/render/shadergraph/qshadergenerator.cpp +++ b/src/render/shadergraph/qshadergenerator.cpp @@ -43,17 +43,17 @@ #include <QRegularExpression> #include <cctype> +#include <qshaderprogram_p.h> QT_BEGIN_NAMESPACE -namespace Qt3DRender -{ +namespace Qt3DRender { Q_LOGGING_CATEGORY(ShaderGenerator, "ShaderGenerator", QtWarningMsg) namespace { - QByteArray toGlsl(QShaderLanguage::StorageQualifier qualifier, const QShaderFormat &format) + QByteArray toGlsl(QShaderLanguage::StorageQualifier qualifier, const QShaderFormat &format) noexcept { - if (format.version().majorVersion() <= 2) { + if (format.version().majorVersion() <= 2 && format.api() != QShaderFormat::RHI) { // Note we're assuming fragment shader only here, it'd be different // values for vertex shader, will need to be fixed properly at some // point but isn't necessary yet (this problem already exists in past @@ -91,7 +91,7 @@ namespace Q_UNREACHABLE(); } - QByteArray toGlsl(QShaderLanguage::VariableType type) + QByteArray toGlsl(QShaderLanguage::VariableType type) noexcept { switch (type) { case QShaderLanguage::Bool: @@ -267,7 +267,8 @@ namespace Q_UNREACHABLE(); } - QByteArray replaceParameters(const QByteArray &original, const QShaderNode &node, const QShaderFormat &format) + QByteArray replaceParameters(const QByteArray &original, const QShaderNode &node, + const QShaderFormat &format) noexcept { QByteArray result = original; @@ -276,11 +277,13 @@ namespace const QByteArray placeholder = QByteArray(QByteArrayLiteral("$") + parameterName.toUtf8()); const QVariant parameter = node.parameter(parameterName); if (parameter.userType() == qMetaTypeId<QShaderLanguage::StorageQualifier>()) { - const QShaderLanguage::StorageQualifier qualifier = qvariant_cast<QShaderLanguage::StorageQualifier>(parameter); + const QShaderLanguage::StorageQualifier qualifier = + qvariant_cast<QShaderLanguage::StorageQualifier>(parameter); const QByteArray value = toGlsl(qualifier, format); result.replace(placeholder, value); } else if (parameter.userType() == qMetaTypeId<QShaderLanguage::VariableType>()) { - const QShaderLanguage::VariableType type = qvariant_cast<QShaderLanguage::VariableType>(parameter); + const QShaderLanguage::VariableType type = + qvariant_cast<QShaderLanguage::VariableType>(parameter); const QByteArray value = toGlsl(type); result.replace(placeholder, value); } else { @@ -291,64 +294,275 @@ namespace return result; } -} -QByteArray QShaderGenerator::createShaderCode(const QStringList &enabledLayers) const -{ - auto code = QByteArrayList(); - - if (format.isValid()) { - const bool isGLES = format.api() == QShaderFormat::OpenGLES; - const int major = format.version().majorVersion(); - const int minor = format.version().minorVersion(); - - const int version = major == 2 && isGLES ? 100 - : major == 3 && isGLES ? 300 - : major == 2 ? 100 + 10 * (minor + 1) - : major == 3 && minor <= 2 ? 100 + 10 * (minor + 3) - : major * 100 + minor * 10 - ; - - const QByteArray profile = isGLES && version > 100 ? QByteArrayLiteral(" es") - : version >= 150 && format.api() == QShaderFormat::OpenGLCoreProfile ? QByteArrayLiteral(" core") - : version >= 150 && format.api() == QShaderFormat::OpenGLCompatibilityProfile ? QByteArrayLiteral(" compatibility") - : QByteArray(); - - code << (QByteArrayLiteral("#version ") + QByteArray::number(version) + profile); - code << QByteArray(); + bool intersectsEnabledLayers(const QStringList &enabledLayers, const QStringList &layers) noexcept + { + return layers.isEmpty() + || std::any_of(layers.cbegin(), layers.cend(), + [enabledLayers](const QString &s) { return enabledLayers.contains(s); }); } - const auto intersectsEnabledLayers = [enabledLayers] (const QStringList &layers) { - return layers.isEmpty() - || std::any_of(layers.cbegin(), layers.cend(), - [enabledLayers] (const QString &s) { return enabledLayers.contains(s); }); + struct ShaderGenerationState + { + ShaderGenerationState(const QShaderGenerator & gen, QStringList layers, QVector<QShaderNode> nodes) + : generator{gen} + , enabledLayers{layers} + , nodes{nodes} + { + + } + + const QShaderGenerator &generator; + QStringList enabledLayers; + QVector<QShaderNode> nodes; + QByteArrayList code; + + QVector<QString> globalInputVariables; + const QRegularExpression globalInputExtractRegExp { QStringLiteral("^.*\\s+(\\w+).*;$") }; }; - QVector<QString> globalInputVariables; - const QRegularExpression globalInputExtractRegExp(QStringLiteral("^.*\\s+(\\w+).*;$")); + class GLSL45HeaderWriter + { + public: + void writeHeader(ShaderGenerationState &state) + { + const auto &format = state.generator.format; + auto &code = state.code; + for (const QShaderNode &node : state.nodes) { + if (intersectsEnabledLayers(state.enabledLayers, node.layers())) { + const QByteArrayList& headerSnippets = node.rule(format).headerSnippets; + for (const QByteArray &snippet : headerSnippets) { + auto replacedSnippet = replaceParameters(snippet, node, format).trimmed(); + + if (replacedSnippet.startsWith(QByteArrayLiteral("add-input"))) { + onInOut(code, replacedSnippet); + } else if (replacedSnippet.startsWith(QByteArrayLiteral("add-uniform"))) { + onNamedUniform(ubo, replacedSnippet); + } else if (replacedSnippet.startsWith(QByteArrayLiteral("add-sampler"))) { + onNamedSampler(code, replacedSnippet); + } else if (replacedSnippet.startsWith(QByteArrayLiteral("#pragma include "))) { + onInclude(code, replacedSnippet); + } else { + code << replacedSnippet; + } + // If node is an input, record the variable name into the globalInputVariables + // vector + if (node.type() == QShaderNode::Input) { + const QRegularExpressionMatch match = state.globalInputExtractRegExp.match( + QString::fromUtf8(code.last())); + if (match.hasMatch()) + state.globalInputVariables.push_back(match.captured(1)); + } + } + } + } - const QVector<QShaderNode> nodes = graph.nodes(); - for (const QShaderNode &node : nodes) { - if (intersectsEnabledLayers(node.layers())) { - const QByteArrayList headerSnippets = node.rule(format).headerSnippets; - for (const QByteArray &snippet : headerSnippets) { - code << replaceParameters(snippet, node, format); - - // If node is an input, record the variable name into the globalInputVariables vector - if (node.type() == QShaderNode::Input) { - const QRegularExpressionMatch match = globalInputExtractRegExp.match(QString::fromUtf8(code.last())); - if (match.hasMatch()) - globalInputVariables.push_back(match.captured(1)); + if (!ubo.isEmpty()) { + code << QByteArrayLiteral("layout(std140, binding = ") + + QByteArray::number(currentBinding++) + + QByteArrayLiteral(") uniform qt3d_shadergraph_generated_uniforms {"); + code << ubo; + code << "};"; + } + } + + private: + void onInOut(QByteArrayList &code, const QByteArray &snippet) noexcept + { + const auto split = snippet.split(' '); + if (split.size() < 4) { + qDebug() << "Invalid header snippet: " << snippet; + return; + } + const auto &qualifier = split[1]; + const auto &type = split[2]; + const auto &name = split[3]; + + if (qualifier == QByteArrayLiteral("in")) { + code << (QByteArrayLiteral("layout(location = ") + + QByteArray::number(currentInputLocation++) + QByteArrayLiteral(") in ") + + type + ' ' + name + QByteArrayLiteral(";")); + } else if (qualifier == QByteArrayLiteral("out")) { + code << (QByteArrayLiteral("layout(location = ") + + QByteArray::number(currentOutputLocation++) + QByteArrayLiteral(") out ") + + type + ' ' + name + QByteArrayLiteral(";")); + } else if (qualifier == QByteArrayLiteral("uniform")) { + ubo << (type + ' ' + name + ';'); + } + } + + void onNamedUniform(QByteArrayList &ubo, const QByteArray &snippet) noexcept + { + const auto split = snippet.split(' '); + if (split.size() < 3) { + qDebug() << "Invalid header snippet: " << snippet; + return; + } + + const auto &type = split[1]; + const auto &name = split[2]; + + ubo << (type + ' ' + name + ';'); + } + + void onNamedSampler(QByteArrayList &code, const QByteArray &snippet) noexcept + { + const auto split = snippet.split(' '); + if (split.size() < 3) { + qDebug() << "Invalid header snippet: " << snippet; + return; + } + const auto binding = QByteArray::number(currentBinding++); + const auto &type = split[1]; + const auto &name = split[2]; + + code << (QByteArrayLiteral("layout(binding = ") + binding + QByteArrayLiteral(") uniform ") + + type + ' ' + name + QByteArrayLiteral(";")); + } + + void onInclude(QByteArrayList &code, const QByteArray &snippet) noexcept + { + const auto filepath = QString::fromUtf8(snippet.mid(strlen("#pragma include "))); + QString deincluded = QString::fromUtf8(QShaderProgramPrivate::deincludify(filepath)); + + // This lambda will replace all occurrences of a string (e.g. "binding = auto") by another, + // with the incremented int passed as argument (e.g. "binding = 1", "binding = 2" ...) + const auto replaceAndIncrement = [&deincluded](const QRegularExpression ®exp, + int &variable, + const QString &replacement) noexcept { + int matchStart = 0; + do { + matchStart = deincluded.indexOf(regexp, matchStart); + if (matchStart != -1) { + const auto match = regexp.match(deincluded.midRef(matchStart)); + const auto length = match.capturedLength(0); + + deincluded.replace(matchStart, length, replacement.arg(variable++)); + } + } while (matchStart != -1); + }; + + // 1. Handle uniforms + { + thread_local const QRegularExpression bindings( + QStringLiteral("binding\\s?+=\\s?+auto")); + + replaceAndIncrement(bindings, currentBinding, QStringLiteral("binding = %1")); + } + + // 2. Handle inputs + { + thread_local const QRegularExpression inLocations( + QStringLiteral("location\\s?+=\\s?+auto\\s?+\\)\\s?+in\\s+")); + + replaceAndIncrement(inLocations, currentInputLocation, + QStringLiteral("location = %1) in ")); + } + + // 3. Handle outputs + { + thread_local const QRegularExpression outLocations( + QStringLiteral("location\\s?+=\\s?+auto\\s?+\\)\\s?+out\\s+")); + + replaceAndIncrement(outLocations, currentOutputLocation, + QStringLiteral("location = %1) out ")); + } + + code << deincluded.toUtf8(); + } + + int currentInputLocation { 0 }; + int currentOutputLocation { 0 }; + int currentBinding { 0 }; + QByteArrayList ubo; + }; + + struct GLSLHeaderWriter + { + void writeHeader(ShaderGenerationState &state) + { + const auto &format = state.generator.format; + auto &code = state.code; + for (const QShaderNode &node : state.nodes) { + if (intersectsEnabledLayers(state.enabledLayers, node.layers())) { + const QByteArrayList& headerSnippets = node.rule(format).headerSnippets; + for (const QByteArray &snippet : headerSnippets) { + code << replaceParameters(snippet, node, format); + + // If node is an input, record the variable name into the globalInputVariables + // vector + if (node.type() == QShaderNode::Input) { + const QRegularExpressionMatch match = state.globalInputExtractRegExp.match( + QString::fromUtf8(code.last())); + if (match.hasMatch()) + state.globalInputVariables.push_back(match.captured(1)); + } + } } } } + }; + + QByteArray versionString(const QShaderFormat &format) noexcept + { + if (!format.isValid()) + return {}; + + switch (format.api()) { + case QShaderFormat::RHI: { + return QByteArrayLiteral("#version 450"); + } + case QShaderFormat::VulkanFlavoredGLSL: { + const int major = format.version().majorVersion(); + const int minor = format.version().minorVersion(); + return (QByteArrayLiteral("#version ") + QByteArray::number(major * 100 + minor * 10)); + } + default: { + const bool isGLES = format.api() == QShaderFormat::OpenGLES; + const int major = format.version().majorVersion(); + const int minor = format.version().minorVersion(); + + const int version = major == 2 && isGLES ? 100 + : major == 3 && isGLES ? 300 + : major == 2 ? 100 + 10 * (minor + 1) + : major == 3 && minor <= 2 ? 100 + 10 * (minor + 3) + : major * 100 + minor * 10; + + const QByteArray profile = + isGLES && version > 100 ? QByteArrayLiteral(" es") + : version >= 150 && format.api() == QShaderFormat::OpenGLCoreProfile ? QByteArrayLiteral(" core") + : version >= 150 && format.api() == QShaderFormat::OpenGLCompatibilityProfile ? QByteArrayLiteral(" compatibility") + : QByteArray(); + + return (QByteArrayLiteral("#version ") + QByteArray::number(version) + profile); + } + } + } +} + +QByteArray QShaderGenerator::createShaderCode(const QStringList &enabledLayers) const +{ + const QVector<QShaderNode> nodes = graph.nodes(); + ShaderGenerationState state(*this, enabledLayers, nodes); + QByteArrayList &code = state.code; + + code << versionString(format); + code << QByteArray(); + + if (format.api() == QShaderFormat::VulkanFlavoredGLSL || format.api() == QShaderFormat::RHI) { + GLSL45HeaderWriter builder; + builder.writeHeader(state); + } else { + GLSLHeaderWriter builder; + builder.writeHeader(state); } code << QByteArray(); code << QByteArrayLiteral("void main()"); code << QByteArrayLiteral("{"); - const QRegularExpression temporaryVariableToAssignmentRegExp(QStringLiteral("([^;]*\\s+(v\\d+))\\s*=\\s*([^;]*);")); + const QRegularExpression temporaryVariableToAssignmentRegExp( + QStringLiteral("([^;]*\\s+(v\\d+))\\s*=\\s*([^;]*);")); const QRegularExpression temporaryVariableInAssignmentRegExp(QStringLiteral("\\W*(v\\d+)\\W*")); const QRegularExpression statementRegExp(QStringLiteral("\\s*(\\w+)\\s*=\\s*([^;]*);")); @@ -362,11 +576,7 @@ QByteArray QShaderGenerator::createShaderCode(const QStringList &enabledLayers) struct Variable { - enum Type { - GlobalInput, - TemporaryAssignment, - Output - }; + enum Type { GlobalInput, TemporaryAssignment, Output }; QString name; QString declaration; @@ -380,7 +590,8 @@ QByteArray QShaderGenerator::createShaderCode(const QStringList &enabledLayers) if (v->substituted) return; - qCDebug(ShaderGenerator) << "Begin Substituting " << v->name << " = " << v->assignment.expression; + qCDebug(ShaderGenerator) + << "Begin Substituting " << v->name << " = " << v->assignment.expression; for (Variable *ref : qAsConst(v->assignment.referencedVariables)) { // Recursively substitute Variable::substitute(ref); @@ -390,14 +601,15 @@ QByteArray QShaderGenerator::createShaderCode(const QStringList &enabledLayers) if (ref->referenceCount == 1 || ref->type == Variable::GlobalInput) { const QRegularExpression r(QStringLiteral("(.*\\b)(%1)(\\b.*)").arg(ref->name)); if (v->assignment.referencedVariables.size() == 1) - v->assignment.expression.replace(r, - QStringLiteral("\\1%2\\3").arg(ref->assignment.expression)); + v->assignment.expression.replace( + r, QStringLiteral("\\1%2\\3").arg(ref->assignment.expression)); else - v->assignment.expression.replace(r, - QStringLiteral("(\\1%2\\3)").arg(ref->assignment.expression)); + v->assignment.expression.replace( + r, QStringLiteral("(\\1%2\\3)").arg(ref->assignment.expression)); } } - qCDebug(ShaderGenerator) << "Done Substituting " << v->name << " = " << v->assignment.expression; + qCDebug(ShaderGenerator) + << "Done Substituting " << v->name << " = " << v->assignment.expression; v->substituted = true; } }; @@ -438,13 +650,16 @@ QByteArray QShaderGenerator::createShaderCode(const QStringList &enabledLayers) return nullptr; }; - auto gatherTemporaryVariablesFromAssignment = [&] (Variable *v, const QString &assignmentContent) { - QRegularExpressionMatchIterator subMatchIt = temporaryVariableInAssignmentRegExp.globalMatch(assignmentContent); + auto gatherTemporaryVariablesFromAssignment = [&](Variable *v, + const QString &assignmentContent) { + QRegularExpressionMatchIterator subMatchIt = + temporaryVariableInAssignmentRegExp.globalMatch(assignmentContent); while (subMatchIt.hasNext()) { const QRegularExpressionMatch subMatch = subMatchIt.next(); const QString variableName = subMatch.captured(1); - // Variable we care about should already exists -> an expression cannot reference a variable that hasn't been defined + // Variable we care about should already exists -> an expression cannot reference a + // variable that hasn't been defined Variable *u = findVariable(variableName); Q_ASSERT(u); @@ -460,7 +675,8 @@ QByteArray QShaderGenerator::createShaderCode(const QStringList &enabledLayers) QByteArray line = node.rule(format).substitution; const QVector<QShaderNodePort> ports = node.ports(); - struct VariableReplacement { + struct VariableReplacement + { QByteArray placeholder; QByteArray variable; }; @@ -477,8 +693,8 @@ QByteArray QShaderGenerator::createShaderCode(const QStringList &enabledLayers) Q_ASSERT(portIndex >= 0); - const int variableIndex = isInput ? statement.inputs.at(portIndex) - : statement.outputs.at(portIndex); + const int variableIndex = + isInput ? statement.inputs.at(portIndex) : statement.outputs.at(portIndex); if (variableIndex < 0) continue; @@ -502,10 +718,11 @@ QByteArray QShaderGenerator::createShaderCode(const QStringList &enabledLayers) const int placeholderLength = end - begin; const QByteArray variableName = line.mid(begin, placeholderLength); - const auto replacementIt = std::find_if(variableReplacements.cbegin(), variableReplacements.cend(), - [&variableName](const VariableReplacement &replacement) { - return variableName == replacement.placeholder; - }); + const auto replacementIt = + std::find_if(variableReplacements.cbegin(), variableReplacements.cend(), + [&variableName](const VariableReplacement &replacement) { + return variableName == replacement.placeholder; + }); if (replacementIt != variableReplacements.cend()) { line.replace(begin, placeholderLength, replacementIt->variable); @@ -526,7 +743,8 @@ QByteArray QShaderGenerator::createShaderCode(const QStringList &enabledLayers) matches = statementRegExp.globalMatch(QString::fromUtf8(substitutionedLine)); break; case QShaderNode::Function: - matches = temporaryVariableToAssignmentRegExp.globalMatch(QString::fromUtf8(substitutionedLine)); + matches = temporaryVariableToAssignmentRegExp.globalMatch( + QString::fromUtf8(substitutionedLine)); break; case QShaderNode::Invalid: break; @@ -606,7 +824,8 @@ QByteArray QShaderGenerator::createShaderCode(const QStringList &enabledLayers) if (v != nullptr) { Variable::substitute(v); - qCDebug(ShaderGenerator) << "Line " << lineContent.rawContent << "is assigned to temporary" << v->name; + qCDebug(ShaderGenerator) + << "Line " << lineContent.rawContent << "is assigned to temporary" << v->name; // Check number of occurrences a temporary variable is referenced if (v->referenceCount == 1 || v->type == Variable::GlobalInput) { @@ -615,9 +834,10 @@ QByteArray QShaderGenerator::createShaderCode(const QStringList &enabledLayers) lineContent.rawContent.clear(); // We assume expression that were referencing vN will have vN properly substituted } else { - lineContent.rawContent = QStringLiteral(" %1 = %2;").arg(v->declaration) - .arg(v->assignment.expression) - .toUtf8(); + lineContent.rawContent = QStringLiteral(" %1 = %2;") + .arg(v->declaration) + .arg(v->assignment.expression) + .toUtf8(); } qCDebug(ShaderGenerator) << "Updated Line is " << lineContent.rawContent; @@ -636,5 +856,6 @@ QByteArray QShaderGenerator::createShaderCode(const QStringList &enabledLayers) return code.join('\n'); } + } QT_END_NAMESPACE diff --git a/src/render/shadergraph/qshadernodesloader.cpp b/src/render/shadergraph/qshadernodesloader.cpp index 8346ed3d5..df34fd1f7 100644 --- a/src/render/shadergraph/qshadernodesloader.cpp +++ b/src/render/shadergraph/qshadernodesloader.cpp @@ -220,9 +220,10 @@ void QShaderNodesLoader::load(const QJsonObject &prototypesObject) : api == QStringLiteral("OpenGLCoreProfile") ? QShaderFormat::OpenGLCoreProfile : api == QStringLiteral("OpenGLCompatibilityProfile") ? QShaderFormat::OpenGLCompatibilityProfile : api == QStringLiteral("VulkanFlavoredGLSL") ? QShaderFormat::VulkanFlavoredGLSL + : api == QStringLiteral("RHI") ? QShaderFormat::RHI : QShaderFormat::NoApi); if (format.api() == QShaderFormat::NoApi) { - qWarning() << "Format API must be one of: OpenGLES, OpenGLNoProfile, OpenGLCoreProfile or OpenGLCompatibilityProfile, VulkanFlavoredGLSL"; + qWarning() << "Format API must be one of: OpenGLES, OpenGLNoProfile, OpenGLCoreProfile OpenGLCompatibilityProfile, VulkanFlavoredGLSL or RHI"; hasError = true; break; } diff --git a/src/render/texture/qabstracttexture.cpp b/src/render/texture/qabstracttexture.cpp index 2e7fd0f63..2a955c5d8 100644 --- a/src/render/texture/qabstracttexture.cpp +++ b/src/render/texture/qabstracttexture.cpp @@ -791,8 +791,9 @@ void QAbstractTexture::removeTextureImage(QAbstractTextureImage *textureImage) { Q_ASSERT(textureImage); Q_D(QAbstractTexture); + if (!d->m_textureImages.removeOne(textureImage)) + return; d->updateNode(textureImage, "textureImage", PropertyValueRemoved); - d->m_textureImages.removeOne(textureImage); // Remove bookkeeping connection d->unregisterDestructionHelper(textureImage); } diff --git a/tests/auto/render/shadergraph/qshadergenerator/tst_qshadergenerator.cpp b/tests/auto/render/shadergraph/qshadergenerator/tst_qshadergenerator.cpp index 780038976..90906f4e9 100644 --- a/tests/auto/render/shadergraph/qshadergenerator/tst_qshadergenerator.cpp +++ b/tests/auto/render/shadergraph/qshadergenerator/tst_qshadergenerator.cpp @@ -260,7 +260,7 @@ void tst_QShaderGenerator::shouldGenerateShaderCode_data() << "}" << ""; - QTest::newRow("EmptyGraphAndFormat") << QShaderGraph() << QShaderFormat() << QByteArrayLiteral("\nvoid main()\n{\n}\n"); + QTest::newRow("EmptyGraphAndFormat") << QShaderGraph() << QShaderFormat() << QByteArrayLiteral("\n\n\nvoid main()\n{\n}\n"); QTest::newRow("LightExposureGraphAndES2") << graph << openGLES2 << (versionGLES2 + es2Code).join('\n'); QTest::newRow("LightExposureGraphAndGL3") << graph << openGL3 << (versionGL3 + gl3Code).join('\n'); QTest::newRow("LightExposureGraphAndGL32") << graph << openGL32 << (versionGL32 + gl3Code).join('\n'); diff --git a/tests/manual/manual.pro b/tests/manual/manual.pro index 633c18006..38805c37a 100644 --- a/tests/manual/manual.pro +++ b/tests/manual/manual.pro @@ -97,3 +97,5 @@ qtHaveModule(quick) { scene3d-in-sync \ compressed_textures \ } + +qtHaveModule(quickwidgets): SUBDIRS += quickwidget-switch diff --git a/tests/manual/quickwidget-switch/main.cpp b/tests/manual/quickwidget-switch/main.cpp new file mode 100644 index 000000000..ebadc458d --- /dev/null +++ b/tests/manual/quickwidget-switch/main.cpp @@ -0,0 +1,125 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QDesktopWidget> +#include <QScreen> +#include <QApplication> +#include <QMainWindow> +#include <QMdiArea> +#include <QMdiSubWindow> +#include <QQuickWidget> +#include <QVBoxLayout> +#include <QPushButton> + +void configureMainWindow(QMainWindow *w, QMdiArea *mdiArea, QPushButton *button) +{ + auto widget = new QWidget; + auto layout = new QVBoxLayout; + layout->addWidget(mdiArea); + layout->addWidget(button); + widget->setLayout(layout); + w->setCentralWidget(widget); +} + +int main(int argc, char* argv[]) +{ + QApplication app(argc, argv); + + QMainWindow w1; + w1.winId(); + w1.windowHandle()->setScreen(QGuiApplication::screens().at(0)); + auto mdiArea1 = new QMdiArea; + auto button1 = new QPushButton("Switch to this window"); + configureMainWindow(&w1, mdiArea1, button1); + w1.setGeometry(0, 0, 800, 800); + w1.show(); + + QMainWindow w2; + w2.winId(); + w2.windowHandle()->setScreen(QGuiApplication::screens().at(1)); + auto mdiArea2 = new QMdiArea; + auto button2 = new QPushButton("Switch to this window"); + configureMainWindow(&w2, mdiArea2, button2); + w2.setGeometry(0, 0, 800, 800); + w2.show(); + + QMdiSubWindow* subWindow = new QMdiSubWindow(); + + QQuickWidget *quickWidget = new QQuickWidget(); + quickWidget->resize(QSize(400, 400)); + quickWidget->setResizeMode(QQuickWidget::SizeRootObjectToView); + quickWidget->setSource(QUrl("qrc:/main.qml")); + + subWindow->setWidget(quickWidget); + + QObject::connect(button1, &QPushButton::clicked, + [mdiArea1, mdiArea2, subWindow, button1, button2]() { + mdiArea2->removeSubWindow(subWindow); + mdiArea1->addSubWindow(subWindow); + subWindow->show(); + button1->setEnabled(false); + button2->setEnabled(true); + }); + + QObject::connect(button2, &QPushButton::clicked, + [mdiArea1, mdiArea2, subWindow, button1, button2]() { + mdiArea1->removeSubWindow(subWindow); + mdiArea2->addSubWindow(subWindow); + subWindow->show(); + button1->setEnabled(true); + button2->setEnabled(false); + }); + + mdiArea2->addSubWindow(subWindow); + button2->setEnabled(false); + subWindow->show(); + + return app.exec(); +} diff --git a/tests/manual/quickwidget-switch/main.qml b/tests/manual/quickwidget-switch/main.qml new file mode 100644 index 000000000..d4bce020c --- /dev/null +++ b/tests/manual/quickwidget-switch/main.qml @@ -0,0 +1,152 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.2 as QQ2 +import Qt3D.Core 2.0 +import Qt3D.Render 2.0 +import Qt3D.Input 2.0 +import Qt3D.Extras 2.0 + +import QtQuick.Scene3D 2.0 + +QQ2.Item { + id: mioitem + width: 300 + height: 300 + Scene3D { + id: scene3d + anchors.fill: parent + Entity { + id: sceneRoot + + Camera { + id: camera + projectionType: CameraLens.PerspectiveProjection + fieldOfView: 45 + aspectRatio: 16/9 + nearPlane : 0.1 + farPlane : 1000.0 + position: Qt.vector3d( 0.0, 0.0, -40.0 ) + upVector: Qt.vector3d( 0.0, 1.0, 0.0 ) + viewCenter: Qt.vector3d( 0.0, 0.0, 0.0 ) + } + + OrbitCameraController { + camera: camera + } + + components: [ + RenderSettings { + activeFrameGraph: ForwardRenderer { + clearColor: Qt.rgba(0, 0.5, 1, 1) + camera: camera + } + }, + // Event Source will be set by the Qt3DQuickWindow + InputSettings { } + ] + + PhongMaterial { + id: material + } + + TorusMesh { + id: torusMesh + radius: 5 + minorRadius: 1 + rings: 100 + slices: 20 + } + + Transform { + id: torusTransform + scale3D: Qt.vector3d(1.5, 1, 0.5) + rotation: fromAxisAndAngle(Qt.vector3d(1, 0, 0), 45) + } + + Entity { + id: torusEntity + components: [ torusMesh, material, torusTransform ] + } + + SphereMesh { + id: sphereMesh + radius: 3 + } + + Transform { + id: sphereTransform + property real userAngle: 0.0 + matrix: { + var m = Qt.matrix4x4(); + m.rotate(userAngle, Qt.vector3d(0, 1, 0)); + m.translate(Qt.vector3d(20, 0, 0)); + return m; + } + } + + QQ2.NumberAnimation { + target: sphereTransform + property: "userAngle" + duration: 10000 + from: 0 + to: 360 + + loops: QQ2.Animation.Infinite + running: true + } + + Entity { + id: sphereEntity + components: [ sphereMesh, material, sphereTransform ] + } + } + } +} diff --git a/tests/manual/quickwidget-switch/quickwidget-switch.pro b/tests/manual/quickwidget-switch/quickwidget-switch.pro new file mode 100644 index 000000000..2f1cb98f5 --- /dev/null +++ b/tests/manual/quickwidget-switch/quickwidget-switch.pro @@ -0,0 +1,13 @@ +TEMPLATE = app + +QT += 3dextras +CONFIG += resources_big + +QT += 3dcore 3drender 3dinput 3dquick 3dlogic qml quick 3dquickextras widgets quickwidgets + +SOURCES += \ + main.cpp + +RESOURCES += \ + quickwidget-switch.qrc + diff --git a/tests/manual/quickwidget-switch/quickwidget-switch.qrc b/tests/manual/quickwidget-switch/quickwidget-switch.qrc new file mode 100644 index 000000000..5f6483ac3 --- /dev/null +++ b/tests/manual/quickwidget-switch/quickwidget-switch.qrc @@ -0,0 +1,5 @@ +<RCC> + <qresource prefix="/"> + <file>main.qml</file> + </qresource> +</RCC> diff --git a/tests/manual/quickwindow-switch/main.cpp b/tests/manual/quickwindow-switch/main.cpp new file mode 100644 index 000000000..7bebe0c75 --- /dev/null +++ b/tests/manual/quickwindow-switch/main.cpp @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QGuiApplication> +#include <QQmlApplicationEngine> + +int main(int argc, char* argv[]) +{ + QGuiApplication app(argc, argv); + + QQmlApplicationEngine engine; + const QUrl url(QStringLiteral("qrc:/main.qml")); + QObject::connect(&engine, &QQmlApplicationEngine::objectCreated, + &app, [url](QObject *obj, const QUrl &objUrl) { + if (!obj && url == objUrl) + QCoreApplication::exit(-1); + }, Qt::QueuedConnection); + engine.load(url); + + return app.exec(); +} diff --git a/tests/manual/quickwindow-switch/main.qml b/tests/manual/quickwindow-switch/main.qml new file mode 100644 index 000000000..4b474bd11 --- /dev/null +++ b/tests/manual/quickwindow-switch/main.qml @@ -0,0 +1,181 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.2 +import QtQuick.Window 2.12 as Win +import Qt3D.Core 2.0 +import Qt3D.Render 2.0 +import Qt3D.Input 2.0 +import Qt3D.Extras 2.0 + +import QtQuick.Scene3D 2.0 + +Win.Window { + id: win + width: 300 + height: 350 + visible: true + x: Win.Screen.width / 2 - width / 2 + y: Win.Screen.height / 2 - height / 2 + Item { + id: mioitem + width: 300 + height: 300 + Scene3D { + id: scene3d + anchors.fill: parent + Entity { + id: sceneRoot + + Camera { + id: camera + projectionType: CameraLens.PerspectiveProjection + fieldOfView: 45 + aspectRatio: 16/9 + nearPlane : 0.1 + farPlane : 1000.0 + position: Qt.vector3d( 0.0, 0.0, -40.0 ) + upVector: Qt.vector3d( 0.0, 1.0, 0.0 ) + viewCenter: Qt.vector3d( 0.0, 0.0, 0.0 ) + } + + OrbitCameraController { + camera: camera + } + + components: [ + RenderSettings { + activeFrameGraph: ForwardRenderer { + clearColor: Qt.rgba(0, 0.5, 1, 1) + camera: camera + } + }, + // Event Source will be set by the Qt3DQuickWindow + InputSettings { } + ] + + PhongMaterial { + id: material + } + + TorusMesh { + id: torusMesh + radius: 5 + minorRadius: 1 + rings: 100 + slices: 20 + } + + Transform { + id: torusTransform + scale3D: Qt.vector3d(1.5, 1, 0.5) + rotation: fromAxisAndAngle(Qt.vector3d(1, 0, 0), 45) + } + + Entity { + id: torusEntity + components: [ torusMesh, material, torusTransform ] + } + + SphereMesh { + id: sphereMesh + radius: 3 + } + + Transform { + id: sphereTransform + property real userAngle: 0.0 + matrix: { + var m = Qt.matrix4x4(); + m.rotate(userAngle, Qt.vector3d(0, 1, 0)); + m.translate(Qt.vector3d(20, 0, 0)); + return m; + } + } + + NumberAnimation { + target: sphereTransform + property: "userAngle" + duration: 10000 + from: 0 + to: 360 + + loops: Animation.Infinite + running: true + } + + Entity { + id: sphereEntity + components: [ sphereMesh, material, sphereTransform ] + } + } + } + } + Rectangle { + height: 50 + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + color: "yellow" + MouseArea { + anchors.fill: parent + z: 5 + onClicked: { + win.screen = (win.screen === Qt.application.screens[0] ? Qt.application.screens[1] : Qt.application.screens[0]) + } + } + Text { + anchors.centerIn: parent + minimumPointSize: 12 + fontSizeMode: Text.Fit + text: "Move to the other screen" + } + } +} diff --git a/tests/manual/quickwindow-switch/quickwindow-switch.pro b/tests/manual/quickwindow-switch/quickwindow-switch.pro new file mode 100644 index 000000000..2f338d36d --- /dev/null +++ b/tests/manual/quickwindow-switch/quickwindow-switch.pro @@ -0,0 +1,13 @@ +TEMPLATE = app + +QT += 3dextras +CONFIG += resources_big + +QT += 3dcore 3drender 3dinput 3dquick 3dlogic qml quick 3dquickextras + +SOURCES += \ + main.cpp + +RESOURCES += \ + quickwindow-switch.qrc + diff --git a/tests/manual/quickwindow-switch/quickwindow-switch.qrc b/tests/manual/quickwindow-switch/quickwindow-switch.qrc new file mode 100644 index 000000000..5f6483ac3 --- /dev/null +++ b/tests/manual/quickwindow-switch/quickwindow-switch.qrc @@ -0,0 +1,5 @@ +<RCC> + <qresource prefix="/"> + <file>main.qml</file> + </qresource> +</RCC> diff --git a/tools/qgltf/qgltf.cpp b/tools/qgltf/qgltf.cpp index 91ecd5ba2..6e1761a33 100644 --- a/tools/qgltf/qgltf.cpp +++ b/tools/qgltf/qgltf.cpp @@ -151,7 +151,7 @@ static QIODevice::OpenMode openModeFromText(const char *name) noexcept { static const struct OpenModeMapping { char name[2]; - ushort mode; + int mode; } openModeMapping[] = { { { 'r', 0 }, QIODevice::ReadOnly }, { { 'r', '+' }, QIODevice::ReadWrite }, |