diff options
author | Paul Lemire <paul.lemire@kdab.com> | 2019-04-02 15:08:50 +0200 |
---|---|---|
committer | Paul Lemire <paul.lemire@kdab.com> | 2019-04-02 15:25:12 +0200 |
commit | f3259300bde381b10cb737735fe19b039c969782 (patch) | |
tree | 43c70cfe34cbf08947752653e98ccb777c1c9521 | |
parent | 6f7ac29268df048b5f23ad26c47efcbfdfdb3585 (diff) | |
parent | 59d26d3e9411150c7ed0fb0cf68d48988c8dbf59 (diff) |
Merge branch '5.12' into 5.13
Change-Id: If17511da64dd666a536408aa3cb3178ef6db0403
49 files changed, 1217 insertions, 382 deletions
diff --git a/dist/changes-5.12.2 b/dist/changes-5.12.2 new file mode 100644 index 000000000..0c8bd8579 --- /dev/null +++ b/dist/changes-5.12.2 @@ -0,0 +1,20 @@ +Qt 5.12.2 is a bug-fix release. It maintains both forward and backward +compatibility (source and binary) with Qt 5.12.0 through 5.12.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.12 series is binary compatible with the 5.11.x series. +Applications compiled for 5.11 will continue to run with 5.12. + +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/src/animation/backend/animationutils.cpp b/src/animation/backend/animationutils.cpp index c6147fe7e..c876fcb87 100644 --- a/src/animation/backend/animationutils.cpp +++ b/src/animation/backend/animationutils.cpp @@ -259,8 +259,65 @@ ClipResults evaluateClipAtLocalTime(AnimationClip *clip, float localTime) const QVector<Channel> &channels = clip->channels(); int i = 0; for (const Channel &channel : channels) { - for (const auto &channelComponent : qAsConst(channel.channelComponents)) - channelResults[i++] = channelComponent.fcurve.evaluateAtTime(localTime); + if (channel.name.contains(QStringLiteral("Rotation")) && + channel.channelComponents.size() == 4) { + + // Try to SLERP + const int nbKeyframes = channel.channelComponents[0].fcurve.keyframeCount(); + const bool canSlerp = std::find_if(std::begin(channel.channelComponents)+1, + std::end(channel.channelComponents), + [nbKeyframes](const ChannelComponent &v) { + return v.fcurve.keyframeCount() != nbKeyframes; + }) == std::end(channel.channelComponents); + + if (!canSlerp) { + // Interpolate per component + for (const auto &channelComponent : qAsConst(channel.channelComponents)) { + const int lowerKeyframeBound = channelComponent.fcurve.lowerKeyframeBound(localTime); + channelResults[i++] = channelComponent.fcurve.evaluateAtTime(localTime, lowerKeyframeBound); + } + } else { + // There's only one keyframe. We cant compute omega. Interpolate per component + const int lowerKeyframeBound = channel.channelComponents[0].fcurve.lowerKeyframeBound(localTime); + if (lowerKeyframeBound + 1 >= channel.channelComponents[0].fcurve.keyframeCount()) { + for (const auto &channelComponent : qAsConst(channel.channelComponents)) + channelResults[i++] = channelComponent.fcurve.evaluateAtTime(localTime, lowerKeyframeBound); + } else { + auto quaternionFromChannel = [channel](const int keyframe) { + const float w = channel.channelComponents[0].fcurve.keyframe(keyframe).value; + const float x = channel.channelComponents[1].fcurve.keyframe(keyframe).value; + const float y = channel.channelComponents[2].fcurve.keyframe(keyframe).value; + const float z = channel.channelComponents[3].fcurve.keyframe(keyframe).value; + QQuaternion quat{w,x,y,z}; + quat.normalize(); + return quat; + }; + + const auto lowerQuat = quaternionFromChannel(lowerKeyframeBound); + const auto higherQuat = quaternionFromChannel(lowerKeyframeBound + 1); + const float omega = std::acos(qBound(-1.0f, QQuaternion::dotProduct(lowerQuat, higherQuat), 1.0f)); + + if (qFuzzyIsNull(omega)) { + // If the two keyframe quaternions are equal, just return the first one as the interpolated value. + channelResults[0] = lowerQuat.scalar(); + channelResults[1] = lowerQuat.x(); + channelResults[2] = lowerQuat.y(); + channelResults[3] = lowerQuat.z(); + } else { + for (const auto &channelComponent : qAsConst(channel.channelComponents)) + channelResults[i++] = channelComponent.fcurve.evaluateAtTimeAsSlerp(localTime, lowerKeyframeBound, omega); + } + } + } + } else { + // If the channel is not a Rotation, apply linear interpolation per channel component + // TODO How do we handle other interpolations. For exammple, color interpolation + // in a linear perceptual way or other non linear spaces? + for (const auto &channelComponent : qAsConst(channel.channelComponents)) { + const int lowerKeyframeBound = channelComponent.fcurve.lowerKeyframeBound(localTime); + channelResults[i++] = channelComponent.fcurve.evaluateAtTime(localTime, lowerKeyframeBound); + } + } } return channelResults; } diff --git a/src/animation/backend/fcurve.cpp b/src/animation/backend/fcurve.cpp index c4361a37a..490866d54 100644 --- a/src/animation/backend/fcurve.cpp +++ b/src/animation/backend/fcurve.cpp @@ -53,6 +53,12 @@ FCurve::FCurve() float FCurve::evaluateAtTime(float localTime) const { + return evaluateAtTime(localTime, lowerKeyframeBound(localTime)); +} + + +float FCurve::evaluateAtTime(float localTime, int lowerBound) const +{ // TODO: Implement extrapolation beyond first/last keyframes if (localTime < m_localTimes.first()) { return m_keyframes.first().value; @@ -60,14 +66,13 @@ float FCurve::evaluateAtTime(float localTime) const return m_keyframes.last().value; } else { // Find keyframes that sandwich the requested localTime - const int idx = m_rangeFinder.findLowerBound(localTime); - if (idx < 0) // only one keyframe + if (lowerBound < 0) // only one keyframe return m_keyframes.first().value; - const float t0 = m_localTimes[idx]; - const float t1 = m_localTimes[idx + 1]; - const Keyframe &keyframe0(m_keyframes[idx]); - const Keyframe &keyframe1(m_keyframes[idx + 1]); + const float t0 = m_localTimes[lowerBound]; + const float t1 = m_localTimes[lowerBound + 1]; + const Keyframe &keyframe0(m_keyframes[lowerBound]); + const Keyframe &keyframe1(m_keyframes[lowerBound + 1]); switch (keyframe0.interpolation) { case QKeyFrame::ConstantInterpolation: @@ -92,6 +97,52 @@ float FCurve::evaluateAtTime(float localTime) const return m_keyframes.first().value; } +float FCurve::evaluateAtTimeAsSlerp(float localTime, int lowerBound, float omega) const +{ + // TODO: Implement extrapolation beyond first/last keyframes + if (localTime < m_localTimes.first()) + return m_keyframes.first().value; + + if (localTime > m_localTimes.last()) + return m_keyframes.last().value; + // Find keyframes that sandwich the requested localTime + if (lowerBound < 0) // only one keyframe + return m_keyframes.first().value; + + const float t0 = m_localTimes[lowerBound]; + const float t1 = m_localTimes[lowerBound + 1]; + const Keyframe &keyframe0(m_keyframes[lowerBound]); + const Keyframe &keyframe1(m_keyframes[lowerBound + 1]); + + switch (keyframe0.interpolation) { + case QKeyFrame::ConstantInterpolation: + return keyframe0.value; + case QKeyFrame::LinearInterpolation: + if (localTime >= t0 && localTime <= t1 && t1 > t0) { + const float t = (localTime - t0) / (t1 - t0); + const float div = 1.0f / std::sin(omega); + return std::sin((1 - t) * omega) * div * keyframe0.value + + std::sin(t * omega) * div * keyframe1.value; + } + break; + case QKeyFrame::BezierInterpolation: + // TODO implement a proper slerp bezier interpolation + BezierEvaluator evaluator(t0, keyframe0, t1, keyframe1); + return evaluator.valueForTime(localTime); + } + + return m_keyframes.first().value; +} + +int FCurve::lowerKeyframeBound(float localTime) const +{ + if (localTime < m_localTimes.first()) + return 0; + if (localTime > m_localTimes.last()) + return 0; + return m_rangeFinder.findLowerBound(localTime); +} + float FCurve::startTime() const { if (!m_localTimes.isEmpty()) diff --git a/src/animation/backend/fcurve_p.h b/src/animation/backend/fcurve_p.h index f96c98c1c..337eb615d 100644 --- a/src/animation/backend/fcurve_p.h +++ b/src/animation/backend/fcurve_p.h @@ -83,6 +83,9 @@ public: float endTime() const; float evaluateAtTime(float localTime) const; + float evaluateAtTime(float localTime, int lowerBound) const; + float evaluateAtTimeAsSlerp(float localTime, int lowerBound, float omega) const; + int lowerKeyframeBound(float localTime) const; void read(const QJsonObject &json); void setFromQChannelComponent(const QChannelComponent &qcc); diff --git a/src/animation/frontend/qchannel.cpp b/src/animation/frontend/qchannel.cpp index 801ad385b..eab7f0df4 100644 --- a/src/animation/frontend/qchannel.cpp +++ b/src/animation/frontend/qchannel.cpp @@ -53,6 +53,16 @@ public: int m_jointIndex = -1; }; +/*! + \class QChannel + \inmodule Qt3DAnimation + \brief Defines a channel for a QAnimationClipData. + The animation system interpolates each channel component independently + except in the case the QChannel is called "Rotation" (case sensitive), + it has four QChannelComponents and the same number of keyframes for + each QChannelComponent. In that case the interpolation will be performed + using SLERP. +*/ QChannel::QChannel() : d(new QChannelPrivate) { @@ -138,14 +148,12 @@ QChannel::const_iterator QChannel::end() const Q_DECL_NOTHROW bool operator==(const QChannel &lhs, const QChannel &rhs) Q_DECL_NOTHROW { - return lhs.d->m_name == rhs.d->m_name && - lhs.d->m_channelComponents == rhs.d->m_channelComponents; + return lhs.d->m_name == rhs.d->m_name && lhs.d->m_channelComponents == rhs.d->m_channelComponents; } bool operator!=(const QChannel &lhs, const QChannel &rhs) Q_DECL_NOTHROW { - return lhs.d->m_name != rhs.d->m_name || - lhs.d->m_channelComponents != rhs.d->m_channelComponents; + return lhs.d->m_name != rhs.d->m_name || lhs.d->m_channelComponents != rhs.d->m_channelComponents; } } // namespace Qt3DAnimation diff --git a/src/core/nodes/qentity.cpp b/src/core/nodes/qentity.cpp index 3c8805a67..69d774dd1 100644 --- a/src/core/nodes/qentity.cpp +++ b/src/core/nodes/qentity.cpp @@ -159,6 +159,8 @@ void QEntity::addComponent(QComponent *comp) if (!comp->parent()) comp->setParent(this); + QNodePrivate::get(comp)->_q_ensureBackendNodeCreated(); + d->m_components.append(comp); // Ensures proper bookkeeping diff --git a/src/core/nodes/qnode.cpp b/src/core/nodes/qnode.cpp index 83ac49471..c2373b805 100644 --- a/src/core/nodes/qnode.cpp +++ b/src/core/nodes/qnode.cpp @@ -394,15 +394,15 @@ void QNodePrivate::propertyChanged(int propertyIndex) if (data.canConvert<QNode*>()) { QNode *node = data.value<QNode*>(); - // Ensure the node has issued a node creation change. We can end - // up here if a newly created node with a parent is immediately set + // Ensure the node and all ancestors have issued their node creation changes. + // We can end up here if a newly created node with a parent is immediately set // as a property on another node. In this case the deferred call to // _q_postConstructorInit() will not have happened yet as the event - // loop will still be blocked. So force it here and we catch this - // eventuality in the _q_postConstructorInit() function so that we - // do not repeat the creation and new child scene change events. + // loop will still be blocked. We need to do this for all ancestors, + // since the subtree of this node otherwise can end up on the backend + // with a reference to a non-existent parent. if (node) - QNodePrivate::get(node)->_q_postConstructorInit(); + QNodePrivate::get(node)->_q_ensureBackendNodeCreated(); const QNodeId id = node ? node->id() : QNodeId(); return QVariant::fromValue(id); @@ -506,6 +506,29 @@ void QNodePrivate::setArbiter(QLockableObserverInterface *arbiter) } /*! + * \internal + * Makes sure this node has a backend by traversing the tree up to the most distant ancestor + * without a backend node and initializing that node. This is done to make sure the parent nodes + * are always created before the child nodes, since child nodes reference parent nodes at creation + * time. + */ +void QNodePrivate::_q_ensureBackendNodeCreated() +{ + if (m_hasBackendNode) + return; + + Q_Q(QNode); + + QNode *nextNode = q; + QNode *topNodeWithoutBackend = nullptr; + while (nextNode != nullptr && !QNodePrivate::get(nextNode)->m_hasBackendNode) { + topNodeWithoutBackend = nextNode; + nextNode = nextNode->parentNode(); + } + QNodePrivate::get(topNodeWithoutBackend)->_q_postConstructorInit(); +} + +/*! \class Qt3DCore::QNode \inherits QObject diff --git a/src/core/nodes/qnode_p.h b/src/core/nodes/qnode_p.h index 73893cc1e..6ffb19ce8 100644 --- a/src/core/nodes/qnode_p.h +++ b/src/core/nodes/qnode_p.h @@ -152,6 +152,7 @@ public: static const QMetaObject *findStaticMetaObject(const QMetaObject *metaObject); void _q_postConstructorInit(); + void _q_ensureBackendNodeCreated(); private: void notifyCreationChange(); diff --git a/src/extras/shaders/es2/light.inc.frag b/src/extras/shaders/es2/light.inc.frag index 167ff306e..465138971 100644 --- a/src/extras/shaders/es2/light.inc.frag +++ b/src/extras/shaders/es2/light.inc.frag @@ -10,7 +10,9 @@ struct Light { FP vec3 color; FP float intensity; FP vec3 direction; - FP vec3 attenuation; + FP float constantAttenuation; + FP float linearAttenuation; + FP float quadraticAttenuation; FP float cutOffAngle; }; uniform Light lights[MAX_LIGHTS]; diff --git a/src/extras/shaders/es2/light.inc.frag100 b/src/extras/shaders/es2/light.inc.frag100 index 8680ee423..0b792b98e 100644 --- a/src/extras/shaders/es2/light.inc.frag100 +++ b/src/extras/shaders/es2/light.inc.frag100 @@ -10,7 +10,9 @@ struct Light { FP vec3 color; FP float intensity; FP vec3 direction; - FP vec3 attenuation; + FP float constantAttenuation; + FP float linearAttenuation; + FP float quadraticAttenuation; FP float cutOffAngle; }; uniform Light lights[MAX_LIGHTS]; diff --git a/src/extras/shaders/es2/phong.inc.frag b/src/extras/shaders/es2/phong.inc.frag index 9d17b68b5..a68e8124c 100644 --- a/src/extras/shaders/es2/phong.inc.frag +++ b/src/extras/shaders/es2/phong.inc.frag @@ -81,9 +81,11 @@ void adsModel(const in FP vec3 vpos, const in FP vec3 vnormal, const in FP vec3 FP float att = 1.0; if ( light.type != TYPE_DIRECTIONAL ) { s = light.position - vpos; - if (length( light.attenuation ) != 0.0) { + if (light.constantAttenuation != 0.0 + || light.linearAttenuation != 0.0 + || light.quadraticAttenuation != 0.0) { FP float dist = length(s); - att = 1.0 / (light.attenuation.x + light.attenuation.y * dist + light.attenuation.z * dist * dist); + att = 1.0 / (light.constantAttenuation + light.linearAttenuation * dist + light.quadraticAttenuation * dist * dist); } s = normalize( s ); if ( light.type == TYPE_SPOT ) { diff --git a/src/extras/shaders/es2/phong.inc.frag100 b/src/extras/shaders/es2/phong.inc.frag100 index 507d8eaf5..0c326d0b6 100644 --- a/src/extras/shaders/es2/phong.inc.frag100 +++ b/src/extras/shaders/es2/phong.inc.frag100 @@ -65,9 +65,11 @@ void adsModel(const in FP vec3 vpos, const in FP vec3 vnormal, const in FP vec3 FP float att = 1.0; if ( lights[0].type != TYPE_DIRECTIONAL ) { s = lights[0].position - vpos; - if (length( lights[0].attenuation ) != 0.0) { + if (lights[0].constantAttenuation != 0.0 + || light[0].linearAttenuation != 0.0 + || light[0].quadraticAttenuation != 0.0) { FP float dist = length(s); - att = 1.0 / (lights[0].attenuation.x + lights[0].attenuation.y * dist + lights[0].attenuation.z * dist * dist); + att = 1.0 / (lights[0].constantAttenuation + lights[0].linearAttenuation * dist + lights[0].quadraticAttenuation * dist * dist); } s = normalize( s ); if ( lights[0].type == TYPE_SPOT ) { @@ -96,9 +98,11 @@ void adsModel(const in FP vec3 vpos, const in FP vec3 vnormal, const in FP vec3 att = 1.0; if ( lights[1].type != TYPE_DIRECTIONAL ) { s = lights[1].position - vpos; - if (length( lights[1].attenuation ) != 0.0) { + if (lights[1].constantAttenuation != 0.0 + || light[1].linearAttenuation != 0.0 + || light[1].quadraticAttenuation != 0.0) { FP float dist = length(s); - att = 1.0 / (lights[1].attenuation.x + lights[1].attenuation.y * dist + lights[1].attenuation.z * dist * dist); + att = 1.0 / (lights[1].constantAttenuation + lights[1].linearAttenuation * dist + lights[1].quadraticAttenuation * dist * dist); } s = normalize( s ); if ( lights[1].type == TYPE_SPOT ) { diff --git a/src/quick3d/imports/scene3d/scene3ditem.cpp b/src/quick3d/imports/scene3d/scene3ditem.cpp index ffb1d9112..67785c1e8 100644 --- a/src/quick3d/imports/scene3d/scene3ditem.cpp +++ b/src/quick3d/imports/scene3d/scene3ditem.cpp @@ -101,7 +101,21 @@ namespace Qt3DRender { The Scene3D type renders a Qt3D scene, provided by an \l Entity, into a multisampled Framebuffer object. This object is later blitted into a non-multisampled Framebuffer object, which is then rendered with - premultiplied alpha. + premultiplied alpha. If multisampling is not required, it can be avoided + by setting the \l multisample property to \c false. In this case + Scene3D will render directly into the non-multisampled Framebuffer object. + + If the scene to be rendered includes non-opaque materials, you may need to + modify those materials with custom blend arguments in order for them to be + rendered correctly. For example, if working with a \l PhongAlphaMaterial and + a scene with an opaque clear color, you will likely want to add: + + \qml + sourceAlphaArg: BlendEquationArguments.Zero + destinationAlphaArg: BlendEquationArguments.One + \qml + + to that material. */ Scene3DItem::Scene3DItem(QQuickItem *parent) : QQuickItem(parent) @@ -128,8 +142,12 @@ Scene3DItem::~Scene3DItem() /*! \qmlproperty list<string> Scene3D::aspects - \brief \TODO - */ + The list of aspects that should be registered for the 3D scene. + + For example, if the scene makes use of FrameAction, the \c "logic" aspect should be included in the list. + + The \c "render" aspect is hardwired and does not need to be explicitly listed. +*/ QStringList Scene3DItem::aspects() const { return m_aspects; @@ -140,7 +158,7 @@ QStringList Scene3DItem::aspects() const \default - \brief \TODO + The root entity of the 3D scene to be displayed. */ Qt3DCore::QEntity *Scene3DItem::entity() const { @@ -341,18 +359,7 @@ void Scene3DItem::updateCameraAspectRatio() /*! \qmlproperty bool Scene3D::multisample - \c true if a multi-sample render buffer is in use. - */ -/*! - \return \c true if a multisample renderbuffer is in use. - */ -bool Scene3DItem::multisample() const -{ - return m_multisample; -} - -/*! - Enables or disables the usage of multisample renderbuffers based on \a enable. + \c true if a multisample render buffer is requested. By default multisampling is enabled. If the OpenGL implementation has no support for multisample renderbuffers or framebuffer blits, the request to @@ -362,6 +369,11 @@ bool Scene3DItem::multisample() const and potentially slow initialization of framebuffers and other OpenGL resources. */ +bool Scene3DItem::multisample() const +{ + return m_multisample; +} + void Scene3DItem::setMultisample(bool enable) { if (m_multisample != enable) { diff --git a/src/quick3d/quick3dscene2d/items/qscene2d.cpp b/src/quick3d/quick3dscene2d/items/qscene2d.cpp index 06e9eecd4..ef06f39f1 100644 --- a/src/quick3d/quick3dscene2d/items/qscene2d.cpp +++ b/src/quick3d/quick3dscene2d/items/qscene2d.cpp @@ -36,6 +36,7 @@ #include "qscene2d.h" #include "qscene2d_p.h" +#include <private/qrenderaspect_p.h> #include "scene2d_p.h" #include "scene2dmanager_p.h" #include "scene2devent_p.h" @@ -205,6 +206,12 @@ void QScene2DPrivate::setScene(Qt3DCore::QScene *scene) QScene2D::QScene2D(Qt3DCore::QNode *parent) : Qt3DCore::QNode(*new QScene2DPrivate, parent) { +#ifdef QT_STATIC + static bool isInitialized = false; + if (!isInitialized) { + Qt3DRender::QRenderAspectPrivate::configurePlugin(QLatin1String("scene2d")); + } +#endif } /*! diff --git a/src/render/frontend/qrenderpluginfactory.cpp b/src/render/frontend/qrenderpluginfactory.cpp index 51fa068c6..548fa90e0 100644 --- a/src/render/frontend/qrenderpluginfactory.cpp +++ b/src/render/frontend/qrenderpluginfactory.cpp @@ -48,14 +48,14 @@ QT_BEGIN_NAMESPACE namespace Qt3DRender { namespace Render { -#ifndef QT_NO_LIBRARY Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader, (QRenderPluginFactoryInterface_iid, QLatin1String("/renderplugins"), Qt::CaseInsensitive)) +#if QT_CONFIG(library) Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, directLoader, (QRenderPluginFactoryInterface_iid, QLatin1String(""), Qt::CaseInsensitive)) #endif QStringList QRenderPluginFactory::keys(const QString &pluginPath) { -#ifndef QT_NO_LIBRARY +#if QT_CONFIG(library) QStringList list; if (!pluginPath.isEmpty()) { QCoreApplication::addLibraryPath(pluginPath); @@ -72,14 +72,14 @@ QStringList QRenderPluginFactory::keys(const QString &pluginPath) list.append(loader()->keyMap().values()); return list; #else - return QStringList(); + return loader()->keyMap().values(); #endif } QRenderPlugin *QRenderPluginFactory::create(const QString &name, const QStringList &args, const QString &pluginPath) { -#ifndef QT_NO_LIBRARY +#if QT_CONFIG(library) if (!pluginPath.isEmpty()) { QCoreApplication::addLibraryPath(pluginPath); if (QRenderPlugin *ret @@ -87,10 +87,8 @@ QRenderPlugin *QRenderPluginFactory::create(const QString &name, const QStringLi return ret; } } - if (QRenderPlugin *ret = qLoadPlugin<QRenderPlugin, QRenderPluginFactoryIf>(loader(), name, args)) - return ret; #endif - return nullptr; + return qLoadPlugin<QRenderPlugin, QRenderPluginFactoryIf>(loader(), name, args); } } // namespace Render diff --git a/src/render/geometry/buffer.cpp b/src/render/geometry/buffer.cpp index 3658ef335..d60f89c7d 100644 --- a/src/render/geometry/buffer.cpp +++ b/src/render/geometry/buffer.cpp @@ -159,6 +159,7 @@ void Buffer::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e) forceDataUpload(); } else if (propertyName == QByteArrayLiteral("updateData")) { Qt3DRender::QBufferUpdate updateData = propertyChange->value().value<Qt3DRender::QBufferUpdate>(); + m_data.replace(updateData.offset, updateData.data.size(), updateData.data); m_bufferUpdates.push_back(updateData); m_bufferDirty = true; } else if (propertyName == QByteArrayLiteral("usage")) { diff --git a/src/render/io/qsceneexportfactory.cpp b/src/render/io/qsceneexportfactory.cpp index 6e1126da8..10db614f4 100644 --- a/src/render/io/qsceneexportfactory.cpp +++ b/src/render/io/qsceneexportfactory.cpp @@ -50,14 +50,14 @@ QT_BEGIN_NAMESPACE namespace Qt3DRender { -#ifndef QT_NO_LIBRARY Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader, (QSceneExportFactoryInterface_iid, QLatin1String("/sceneparsers"), Qt::CaseInsensitive)) +#if QT_CONFIG(library) Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, directLoader, (QSceneExportFactoryInterface_iid, QLatin1String(""), Qt::CaseInsensitive)) #endif QStringList QSceneExportFactory::keys(const QString &pluginPath) { -#ifndef QT_NO_LIBRARY +#if QT_CONFIG(library) QStringList list; if (!pluginPath.isEmpty()) { QCoreApplication::addLibraryPath(pluginPath); @@ -74,14 +74,14 @@ QStringList QSceneExportFactory::keys(const QString &pluginPath) list.append(loader()->keyMap().values()); return list; #else - return QStringList(); + return loader()->keyMap().values(); #endif } QSceneExporter *QSceneExportFactory::create(const QString &name, const QStringList &args, const QString &pluginPath) { -#ifndef QT_NO_LIBRARY +#if QT_CONFIG(library) if (!pluginPath.isEmpty()) { QCoreApplication::addLibraryPath(pluginPath); if (QSceneExporter *ret = qLoadPlugin<QSceneExporter, @@ -89,10 +89,8 @@ QSceneExporter *QSceneExportFactory::create(const QString &name, const QStringLi return ret; } } - if (QSceneExporter *ret = qLoadPlugin<QSceneExporter, QSceneExportPlugin>(loader(), name, args)) - return ret; #endif - return nullptr; + return qLoadPlugin<QSceneExporter, QSceneExportPlugin>(loader(), name, args); } } // namespace Qt3DRender diff --git a/src/render/materialsystem/qshaderprogram.cpp b/src/render/materialsystem/qshaderprogram.cpp index 0ca8a9947..c4e14ea2c 100644 --- a/src/render/materialsystem/qshaderprogram.cpp +++ b/src/render/materialsystem/qshaderprogram.cpp @@ -635,7 +635,7 @@ QShaderProgram::Status QShaderProgram::status() const return d->m_status; } -static QByteArray deincludify(const QString &filePath) +QByteArray QShaderProgramPrivate::deincludify(const QString &filePath) { QFile f(filePath); if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) { @@ -644,20 +644,27 @@ static QByteArray deincludify(const QString &filePath) } QByteArray contents = f.readAll(); + return deincludify(contents, filePath); +} + +QByteArray QShaderProgramPrivate::deincludify(const QByteArray &contents, const QString &filePath) +{ QByteArrayList lines = contents.split('\n'); const QByteArray includeDirective = QByteArrayLiteral("#pragma include"); for (int i = 0; i < lines.count(); ++i) { - if (lines[i].startsWith(includeDirective)) { - QString includeFileName = QFileInfo(filePath).absolutePath() - + QLatin1Char('/') - + QString::fromUtf8(lines[i].mid(includeDirective.count() + 1)); + const auto line = lines[i].simplified(); + if (line.startsWith(includeDirective)) { + const QString includePartialPath = QString::fromUtf8(line.mid(includeDirective.count() + 1)); + + QString includePath = QFileInfo(includePartialPath).isAbsolute() ? includePartialPath + : QFileInfo(filePath).absolutePath() + QLatin1Char('/') + includePartialPath; if (qEnvironmentVariableIsSet("QT3D_GLSL100_WORKAROUND")) { - QString candidate = includeFileName + QLatin1String("100"); + QString candidate = includePath + QLatin1String("100"); if (QFile::exists(candidate)) - includeFileName = candidate; + includePath = candidate; } lines.removeAt(i); - QByteArray includedContents = deincludify(includeFileName); + QByteArray includedContents = deincludify(includePath); lines.insert(i, includedContents); QString lineDirective = QString(QStringLiteral("#line %1")).arg(i + 2); lines.insert(i + 1, lineDirective.toUtf8()); @@ -678,7 +685,7 @@ static QByteArray deincludify(const QString &filePath) QByteArray QShaderProgram::loadSource(const QUrl &sourceUrl) { // TO DO: Handle remote path - return deincludify(Qt3DRender::QUrlHelper::urlToLocalFileOrQrc(sourceUrl)); + return QShaderProgramPrivate::deincludify(Qt3DRender::QUrlHelper::urlToLocalFileOrQrc(sourceUrl)); } Qt3DCore::QNodeCreatedChangeBasePtr QShaderProgram::createNodeCreationChange() const diff --git a/src/render/materialsystem/qshaderprogram_p.h b/src/render/materialsystem/qshaderprogram_p.h index 92520c3c8..f09b2a30e 100644 --- a/src/render/materialsystem/qshaderprogram_p.h +++ b/src/render/materialsystem/qshaderprogram_p.h @@ -75,6 +75,9 @@ public: void setLog(const QString &log); void setStatus(QShaderProgram::Status status); + + static QByteArray deincludify(const QByteArray &contents, const QString &filePath); + static QByteArray deincludify(const QString &filePath); }; struct QShaderProgramData diff --git a/src/render/materialsystem/shaderbuilder.cpp b/src/render/materialsystem/shaderbuilder.cpp index e0683332f..c1ec7f75a 100644 --- a/src/render/materialsystem/shaderbuilder.cpp +++ b/src/render/materialsystem/shaderbuilder.cpp @@ -41,6 +41,7 @@ #include <Qt3DRender/private/qshaderprogrambuilder_p.h> #include <Qt3DRender/qshaderprogram.h> +#include <Qt3DRender/private/qshaderprogram_p.h> #include <Qt3DRender/private/qurlhelper_p.h> #include <QtGui/private/qshaderformat_p.h> @@ -234,47 +235,6 @@ bool ShaderBuilder::isShaderCodeDirty(ShaderBuilder::ShaderType type) const return m_dirtyTypes.contains(type); } -static QByteArray deincludify(const QByteArray &contents, const QString &filePath); - -static QByteArray deincludify(const QString &filePath) -{ - QFile f(filePath); - if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) { - qWarning() << "Could not read shader source file:" << f.fileName(); - return QByteArray(); - } - - QByteArray contents = f.readAll(); - return deincludify(contents, filePath); -} - -static QByteArray deincludify(const QByteArray &contents, const QString &filePath) -{ - QByteArrayList lines = contents.split('\n'); - const QByteArray includeDirective = QByteArrayLiteral("#pragma include"); - for (int i = 0; i < lines.count(); ++i) { - const auto line = lines[i].simplified(); - if (line.startsWith(includeDirective)) { - const QString includePartialPath = QString::fromUtf8(line.mid(includeDirective.count() + 1)); - - QString includePath = QFileInfo(includePartialPath).isAbsolute() ? includePartialPath - : QFileInfo(filePath).absolutePath() + QLatin1Char('/') + includePartialPath; - if (qEnvironmentVariableIsSet("QT3D_GLSL100_WORKAROUND")) { - QString candidate = includePath + QLatin1String("100"); - if (QFile::exists(candidate)) - includePath = candidate; - } - lines.removeAt(i); - QByteArray includedContents = deincludify(includePath); - lines.insert(i, includedContents); - QString lineDirective = QString(QStringLiteral("#line %1")).arg(i + 2); - lines.insert(i + 1, lineDirective.toUtf8()); - } - } - - return lines.join('\n'); -} - void ShaderBuilder::generateCode(ShaderBuilder::ShaderType type) { const auto graphPath = QUrlHelper::urlToLocalFileOrQrc(shaderGraph(type)); @@ -308,7 +268,7 @@ void ShaderBuilder::generateCode(ShaderBuilder::ShaderType type) generator.graph = graph; const auto code = generator.createShaderCode(m_enabledLayers); - m_codes.insert(type, deincludify(code, graphPath + QStringLiteral(".glsl"))); + m_codes.insert(type, QShaderProgramPrivate::deincludify(code, graphPath + QStringLiteral(".glsl"))); m_dirtyTypes.remove(type); // Send notification to the frontend diff --git a/src/render/renderers/opengl/graphicshelpers/graphicscontext.cpp b/src/render/renderers/opengl/graphicshelpers/graphicscontext.cpp index 59b5701f8..2b8076336 100644 --- a/src/render/renderers/opengl/graphicshelpers/graphicscontext.cpp +++ b/src/render/renderers/opengl/graphicshelpers/graphicscontext.cpp @@ -130,6 +130,7 @@ GraphicsContext::GraphicsContext() , m_glHelper(nullptr) , m_shaderCache(nullptr) , m_debugLogger(nullptr) + , m_currentVAO(nullptr) { } @@ -199,15 +200,20 @@ bool GraphicsContext::makeCurrent(QSurface *surface) return false; } + initializeHelpers(surface); + + return true; +} + +void GraphicsContext::initializeHelpers(QSurface *surface) +{ // Set the correct GL Helper depending on the surface // If no helper exists, create one - m_glHelper = m_glHelpers.value(surface); if (!m_glHelper) { m_glHelper = resolveHighestOpenGLFunctions(); m_glHelpers.insert(surface, m_glHelper); } - return true; } void GraphicsContext::doneCurrent() diff --git a/src/render/renderers/opengl/graphicshelpers/graphicscontext_p.h b/src/render/renderers/opengl/graphicshelpers/graphicscontext_p.h index 7bc79996c..73d1f316c 100644 --- a/src/render/renderers/opengl/graphicshelpers/graphicscontext_p.h +++ b/src/render/renderers/opengl/graphicshelpers/graphicscontext_p.h @@ -177,6 +177,7 @@ public: bool supportsVAO() const { return m_supportsVAO; } void initialize(); + void initializeHelpers(QSurface *surface); GraphicsHelperInterface *resolveHighestOpenGLFunctions(); bool m_initialized; diff --git a/src/render/renderers/opengl/graphicshelpers/graphicshelpers.pri b/src/render/renderers/opengl/graphicshelpers/graphicshelpers.pri index 5c9479d2b..ad08038c9 100644 --- a/src/render/renderers/opengl/graphicshelpers/graphicshelpers.pri +++ b/src/render/renderers/opengl/graphicshelpers/graphicshelpers.pri @@ -3,6 +3,7 @@ INCLUDEPATH += $$PWD HEADERS += \ + $$PWD/glfence_p.h \ $$PWD/graphicscontext_p.h \ $$PWD/graphicshelperinterface_p.h \ $$PWD/graphicshelperes2_p.h \ @@ -14,7 +15,7 @@ HEADERS += \ $$PWD/graphicshelpergl4_p.h \ $$PWD/graphicshelpergl3_2_p.h \ $$PWD/submissioncontext_p.h \ - $$PWD/glfence_p.h + $$PWD/texturesubmissioncontext_p.h SOURCES += \ $$PWD/graphicscontext.cpp \ @@ -26,4 +27,5 @@ SOURCES += \ $$PWD/graphicshelpergl3_3.cpp \ $$PWD/graphicshelpergl4.cpp \ $$PWD/graphicshelpergl3_2.cpp \ - $$PWD/submissioncontext.cpp + $$PWD/submissioncontext.cpp \ + $$PWD/texturesubmissioncontext.cpp diff --git a/src/render/renderers/opengl/graphicshelpers/submissioncontext.cpp b/src/render/renderers/opengl/graphicshelpers/submissioncontext.cpp index d1ac853ea..51f7cebd1 100644 --- a/src/render/renderers/opengl/graphicshelpers/submissioncontext.cpp +++ b/src/render/renderers/opengl/graphicshelpers/submissioncontext.cpp @@ -49,6 +49,7 @@ #include <Qt3DRender/private/buffer_p.h> #include <Qt3DRender/private/attribute_p.h> #include <Qt3DRender/private/rendercommand_p.h> +#include <Qt3DRender/private/renderstates_p.h> #include <Qt3DRender/private/renderstateset_p.h> #include <Qt3DRender/private/rendertarget_p.h> #include <Qt3DRender/private/graphicshelperinterface_p.h> @@ -96,40 +97,18 @@ namespace Qt3DRender { namespace Render { -class TextureExtRendererLocker +static QHash<unsigned int, SubmissionContext*> static_contexts; + +unsigned int nextFreeContextId() { -public: - static void lock(GLTexture *tex) - { - if (!tex->isExternalRenderingEnabled()) - return; - if (s_lockHash.keys().contains(tex)) { - ++s_lockHash[tex]; - } else { - tex->externalRenderingLock()->lock(); - s_lockHash[tex] = 1; - } - } - static void unlock(GLTexture *tex) - { - if (!tex->isExternalRenderingEnabled()) - return; - if (!s_lockHash.keys().contains(tex)) - return; - - --s_lockHash[tex]; - if (s_lockHash[tex] == 0) { - s_lockHash.remove(tex); - tex->externalRenderingLock()->unlock(); - } + for (unsigned int i=0; i < 0xffff; ++i) { + if (!static_contexts.contains(i)) + return i; } -private: - static QHash<GLTexture*, int> s_lockHash; -}; - -QHash<GLTexture*, int> TextureExtRendererLocker::s_lockHash = QHash<GLTexture*, int>(); -static QHash<unsigned int, SubmissionContext*> static_contexts; + qFatal("Couldn't find free context ID"); + return 0; +} namespace { @@ -366,16 +345,6 @@ void applyStateHelper<LineWidth>(const LineWidth *state, SubmissionContext *gc) } // anonymous -unsigned int nextFreeContextId() -{ - for (unsigned int i=0; i < 0xffff; ++i) { - if (!static_contexts.contains(i)) - return i; - } - - qFatal("Couldn't find free context ID"); - return 0; -} SubmissionContext::SubmissionContext() : GraphicsContext() @@ -394,7 +363,6 @@ SubmissionContext::SubmissionContext() , m_stateSet(nullptr) , m_renderer(nullptr) , m_uboTempArray(QByteArray(1024, 0)) - , m_currentVAO(nullptr) { static_contexts[m_id] = this; } @@ -410,7 +378,7 @@ SubmissionContext::~SubmissionContext() void SubmissionContext::initialize() { GraphicsContext::initialize(); - m_activeTextures.resize(maxTextureUnitsCount()); + m_textureContext.initialize(this); } void SubmissionContext::resolveRenderTargetFormat() @@ -462,10 +430,6 @@ bool SubmissionContext::beginDrawing(QSurface *surface) // TODO: cache surface format somewhere rather than doing this every time render surface changes resolveRenderTargetFormat(); - // Sets or Create the correct m_glHelper - // for the current surface - activateGLHelper(); - #if defined(QT3D_RENDER_ASPECT_OPENGL_DEBUG) GLint err = m_gl->functions()->glGetError(); if (err != 0) { @@ -475,22 +439,18 @@ bool SubmissionContext::beginDrawing(QSurface *surface) if (!isInitialized()) initialize(); + initializeHelpers(m_surface); // need to reset these values every frame, may get overwritten elsewhere m_gl->functions()->glClearColor(m_currClearColorValue.redF(), m_currClearColorValue.greenF(), m_currClearColorValue.blueF(), m_currClearColorValue.alphaF()); m_gl->functions()->glClearDepthf(m_currClearDepthValue); m_gl->functions()->glClearStencil(m_currClearStencilValue); - if (m_activeShader) { m_activeShader = nullptr; m_activeShaderDNA = 0; } - // reset active textures - for (int u = 0; u < m_activeTextures.size(); ++u) - m_activeTextures[u].texture = nullptr; - m_boundArrayBuffer = nullptr; static int callCount = 0; @@ -508,10 +468,7 @@ void SubmissionContext::endDrawing(bool swapBuffers) m_gl->swapBuffers(m_surface); if (m_ownCurrent) m_gl->doneCurrent(); - decayTextureScores(); - for (int i = 0; i < m_activeTextures.size(); ++i) - if (m_activeTextures[i].texture) - TextureExtRendererLocker::unlock(m_activeTextures[i].texture); + m_textureContext.endDrawing(); } void SubmissionContext::activateRenderTarget(Qt3DCore::QNodeId renderTargetNodeId, const AttachmentPack &attachments, GLuint defaultFboId) @@ -815,20 +772,6 @@ void SubmissionContext::setOpenGLContext(QOpenGLContext* ctx) m_gl = ctx; } -void SubmissionContext::activateGLHelper() -{ - // Sets the correct GL Helper depending on the surface - // If no helper exists, create one - m_glHelper = m_glHelpers.value(m_surface); - if (!m_glHelper) { - m_glHelper = resolveHighestOpenGLFunctions(); - m_glHelpers.insert(m_surface, m_glHelper); - // Note: OpenGLContext is current at this point - m_gl->functions()->glDisable(GL_DITHER); - } -} - - // Called only from RenderThread bool SubmissionContext::activateShader(ProgramDNA shaderDNA) { @@ -910,126 +853,10 @@ void SubmissionContext::setActiveMaterial(Material *rmat) if (m_material == rmat) return; - deactivateTexturesWithScope(TextureScopeMaterial); + m_textureContext.deactivateTexturesWithScope(TextureSubmissionContext::TextureScopeMaterial); m_material = rmat; } -int SubmissionContext::activateTexture(TextureScope scope, GLTexture *tex, int onUnit) -{ - // Returns the texture unit to use for the texture - // This always return a valid unit, unless there are more textures than - // texture unit available for the current material - onUnit = assignUnitForTexture(tex); - - // check we didn't overflow the available units - if (onUnit == -1) - return -1; - - // actually re-bind if required, the tex->dna on the unit not being the same - // Note: tex->dna() could be 0 if the texture has not been created yet - if (m_activeTextures[onUnit].texture != tex) { - // Texture must have been created and updated at this point - - const int sharedTextureId = tex->sharedTextureId(); - - // We have a valid texture id provided by a shared context - if (sharedTextureId > 0) { - m_gl->functions()->glActiveTexture(GL_TEXTURE0 + onUnit); - const QAbstractTexture::Target target = tex->properties().target; - // For now we know that target values correspond to the GL values - m_gl->functions()->glBindTexture(target, tex->sharedTextureId()); - } else { - QOpenGLTexture *glTex = tex->getGLTexture(); - if (glTex == nullptr) - return -1; - glTex->bind(onUnit); - } - - if (m_activeTextures[onUnit].texture) - TextureExtRendererLocker::unlock(m_activeTextures[onUnit].texture); - m_activeTextures[onUnit].texture = tex; - TextureExtRendererLocker::lock(tex); - } - -#if defined(QT3D_RENDER_ASPECT_OPENGL_DEBUG) - int err = m_gl->functions()->glGetError(); - if (err) - qCWarning(Backend) << "GL error after activating texture" << QString::number(err, 16) - << tex->getGLTexture()->textureId() << "on unit" << onUnit; -#endif - - m_activeTextures[onUnit].score = 200; - m_activeTextures[onUnit].pinned = true; - m_activeTextures[onUnit].scope = scope; - - return onUnit; -} - -void SubmissionContext::deactivateTexturesWithScope(TextureScope ts) -{ - for (int u=0; u<m_activeTextures.size(); ++u) { - if (!m_activeTextures[u].pinned) - continue; // inactive, ignore - - if (m_activeTextures[u].scope == ts) { - m_activeTextures[u].pinned = false; - m_activeTextures[u].score = qMax(m_activeTextures[u].score, 1) - 1; - } - } // of units iteration -} - -void SubmissionContext::deactivateTexture(GLTexture* tex) -{ - for (int u=0; u<m_activeTextures.size(); ++u) { - if (m_activeTextures[u].texture == tex) { - Q_ASSERT(m_activeTextures[u].pinned); - m_activeTextures[u].pinned = false; - return; - } - } // of units iteration - - qCWarning(Backend) << Q_FUNC_INFO << "texture not active:" << tex; -} - -/*! - \internal - Returns a texture unit for a texture, -1 if all texture units are assigned. - Tries to use the texture unit with the texture that hasn't been used for the longest time - if the texture happens not to be already pinned on a texture unit. - */ -GLint SubmissionContext::assignUnitForTexture(GLTexture *tex) -{ - int lowestScoredUnit = -1; - int lowestScore = 0xfffffff; - - for (int u=0; u<m_activeTextures.size(); ++u) { - if (m_activeTextures[u].texture == tex) - return u; - - // No texture is currently active on the texture unit - // we save the texture unit with the texture that has been on there - // the longest time while not being used - if (!m_activeTextures[u].pinned) { - int score = m_activeTextures[u].score; - if (score < lowestScore) { - lowestScore = score; - lowestScoredUnit = u; - } - } - } // of units iteration - - if (lowestScoredUnit == -1) - qCWarning(Backend) << Q_FUNC_INFO << "No free texture units!"; - - return lowestScoredUnit; -} - -void SubmissionContext::decayTextureScores() -{ - for (int u = 0; u < m_activeTextures.size(); u++) - m_activeTextures[u].score = qMax(m_activeTextures[u].score - 1, 0); -} - void SubmissionContext::setCurrentStateSet(RenderStateSet *ss) { if (ss == m_stateSet) @@ -1301,7 +1128,7 @@ bool SubmissionContext::setParameters(ShaderParameterPack ¶meterPack) // to pinable so that we should easily find an available texture unit NodeManagers *manager = m_renderer->nodeManagers(); - deactivateTexturesWithScope(TextureScopeMaterial); + m_textureContext.deactivateTexturesWithScope(TextureSubmissionContext::TextureScopeMaterial); // Update the uniforms with the correct texture unit id's PackUniformHash &uniformValues = parameterPack.uniforms(); @@ -1313,7 +1140,7 @@ bool SubmissionContext::setParameters(ShaderParameterPack ¶meterPack) if (t != nullptr) { UniformValue &texUniform = uniformValues[namedTex.glslNameId]; if (texUniform.valueType() == UniformValue::TextureValue) { - const int texUnit = activateTexture(TextureScopeMaterial, t); + const int texUnit = m_textureContext.activateTexture(TextureSubmissionContext::TextureScopeMaterial, m_gl, t); texUniform.data<int>()[namedTex.uniformArrayIndex] = texUnit; if (texUnit == -1) { if (namedTex.glslNameId != irradianceId && diff --git a/src/render/renderers/opengl/graphicshelpers/submissioncontext_p.h b/src/render/renderers/opengl/graphicshelpers/submissioncontext_p.h index dbfaef148..844e62f15 100644 --- a/src/render/renderers/opengl/graphicshelpers/submissioncontext_p.h +++ b/src/render/renderers/opengl/graphicshelpers/submissioncontext_p.h @@ -54,6 +54,7 @@ #include <Qt3DRender/private/graphicscontext_p.h> +#include <Qt3DRender/private/texturesubmissioncontext_p.h> #include <Qt3DRender/qclearbuffers.h> #include <Qt3DRender/private/glbuffer_p.h> #include <Qt3DRender/qattribute.h> @@ -82,13 +83,6 @@ class Buffer; class ShaderManager; struct StateVariant; -enum TextureScope -{ - TextureScopeMaterial = 0, - TextureScopeTechnique - // per-pass for deferred rendering? -}; - typedef QPair<QString, int> NamedUniformLocation; class Q_AUTOTEST_EXPORT SubmissionContext : public GraphicsContext @@ -102,7 +96,6 @@ public: bool beginDrawing(QSurface *surface); void endDrawing(bool swapBuffers); - void activateGLHelper(); void releaseOpenGL(); void setOpenGLContext(QOpenGLContext* ctx); @@ -126,11 +119,6 @@ public: QRenderTargetOutput::AttachmentPoint outputAttachmentPoint, QBlitFramebuffer::InterpolationMethod interpolationMethod); - - // Material - Material* activeMaterial() const { return m_material; } - void setActiveMaterial(Material* rmat); - // Attributes void specifyAttribute(const Attribute *attribute, Buffer *buffer, @@ -147,10 +135,6 @@ public: // Parameters bool setParameters(ShaderParameterPack ¶meterPack); - // Textures - int activateTexture(TextureScope scope, GLTexture* tex, int onUnit = -1); - void deactivateTexture(GLTexture *tex); - // RenderState void setCurrentStateSet(RenderStateSet* ss); RenderStateSet *currentStateSet() const; @@ -175,10 +159,9 @@ public: private: void initialize(); - // Textures - void decayTextureScores(); - GLint assignUnitForTexture(GLTexture* tex); - void deactivateTexturesWithScope(TextureScope ts); + // Material + Material* activeMaterial() const { return m_material; } + void setActiveMaterial(Material* rmat); // FBO void bindFrameBufferAttachmentHelper(GLuint fboId, const AttachmentPack &attachments); @@ -187,7 +170,6 @@ private: GLuint createRenderTarget(Qt3DCore::QNodeId renderTargetNodeId, const AttachmentPack &attachments); GLuint updateRenderTarget(Qt3DCore::QNodeId renderTargetNodeId, const AttachmentPack &attachments, bool isActiveRenderTarget); - // Buffers HGLBuffer createGLBufferFor(Buffer *buffer, GLBuffer::Type type); void uploadDataToGLBuffer(Buffer *buffer, GLBuffer *b, bool releaseBuffer = false); @@ -207,17 +189,6 @@ private: QHash<GLuint, QSize> m_renderTargetsSize; QAbstractTexture::TextureFormat m_renderTargetFormat; - QHash<QSurface *, GraphicsHelperInterface*> m_glHelpers; - - // active textures, indexed by texture unit - struct ActiveTexture { - GLTexture *texture = nullptr; - int score = 0; - TextureScope scope = TextureScopeMaterial; - bool pinned = false; - }; - QVector<ActiveTexture> m_activeTextures; - // cache some current state, to make sure we don't issue unnecessary GL calls int m_currClearStencilValue; float m_currClearDepthValue; @@ -232,10 +203,10 @@ private: Renderer *m_renderer; QByteArray m_uboTempArray; + TextureSubmissionContext m_textureContext; // Attributes friend class OpenGLVertexArrayObject; - OpenGLVertexArrayObject *m_currentVAO; struct VAOVertexAttribute { diff --git a/src/render/renderers/opengl/graphicshelpers/texturesubmissioncontext.cpp b/src/render/renderers/opengl/graphicshelpers/texturesubmissioncontext.cpp new file mode 100644 index 000000000..67d0f9976 --- /dev/null +++ b/src/render/renderers/opengl/graphicshelpers/texturesubmissioncontext.cpp @@ -0,0 +1,225 @@ +/**************************************************************************** +** +** Copyright (C) 2019 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** 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 "texturesubmissioncontext_p.h" + +#include <Qt3DRender/private/graphicscontext_p.h> +#include <Qt3DRender/private/gltexture_p.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3DRender { +namespace Render { + +class TextureExtRendererLocker +{ +public: + static void lock(GLTexture *tex) + { + if (!tex->isExternalRenderingEnabled()) + return; + if (s_lockHash.keys().contains(tex)) { + ++s_lockHash[tex]; + } else { + tex->externalRenderingLock()->lock(); + s_lockHash[tex] = 1; + } + } + static void unlock(GLTexture *tex) + { + if (!tex->isExternalRenderingEnabled()) + return; + if (!s_lockHash.keys().contains(tex)) + return; + + --s_lockHash[tex]; + if (s_lockHash[tex] == 0) { + s_lockHash.remove(tex); + tex->externalRenderingLock()->unlock(); + } + } +private: + static QHash<GLTexture*, int> s_lockHash; +}; + +QHash<GLTexture*, int> TextureExtRendererLocker::s_lockHash = QHash<GLTexture*, int>(); + + +TextureSubmissionContext::TextureSubmissionContext() +{ + +} + +TextureSubmissionContext::~TextureSubmissionContext() +{ + +} + +void TextureSubmissionContext::initialize(GraphicsContext *context) +{ + m_activeTextures.resize(context->maxTextureUnitsCount()); +} + +void TextureSubmissionContext::endDrawing() +{ + decayTextureScores(); + for (int i = 0; i < m_activeTextures.size(); ++i) + if (m_activeTextures[i].texture) + TextureExtRendererLocker::unlock(m_activeTextures[i].texture); +} + +int TextureSubmissionContext::activateTexture(TextureSubmissionContext::TextureScope scope, + QOpenGLContext *m_gl, + GLTexture *tex) +{ + // Returns the texture unit to use for the texture + // This always return a valid unit, unless there are more textures than + // texture unit available for the current material + const int onUnit = assignUnitForTexture(tex); + + // check we didn't overflow the available units + if (onUnit == -1) + return -1; + + const int sharedTextureId = tex->sharedTextureId(); + // We have a valid texture id provided by a shared context + if (sharedTextureId > 0) { + m_gl->functions()->glActiveTexture(GL_TEXTURE0 + onUnit); + const QAbstractTexture::Target target = tex->properties().target; + // For now we know that target values correspond to the GL values + m_gl->functions()->glBindTexture(target, tex->sharedTextureId()); + } else { + // Texture must have been created and updated at this point + QOpenGLTexture *glTex = tex->getGLTexture(); + if (glTex == nullptr) + return -1; + glTex->bind(uint(onUnit)); + } + if (m_activeTextures[onUnit].texture != tex) { + if (m_activeTextures[onUnit].texture) + TextureExtRendererLocker::unlock(m_activeTextures[onUnit].texture); + m_activeTextures[onUnit].texture = tex; + TextureExtRendererLocker::lock(tex); + } + +#if defined(QT3D_RENDER_ASPECT_OPENGL_DEBUG) + int err = m_gl->functions()->glGetError(); + if (err) + qCWarning(Backend) << "GL error after activating texture" << QString::number(err, 16) + << tex->getGLTexture()->textureId() << "on unit" << onUnit; +#endif + + m_activeTextures[onUnit].score = 200; + m_activeTextures[onUnit].pinned = true; + m_activeTextures[onUnit].scope = scope; + + return onUnit; +} + +void TextureSubmissionContext::deactivateTexturesWithScope(TextureSubmissionContext::TextureScope ts) +{ + for (int u=0; u<m_activeTextures.size(); ++u) { + if (!m_activeTextures[u].pinned) + continue; // inactive, ignore + + if (m_activeTextures[u].scope == ts) { + m_activeTextures[u].pinned = false; + m_activeTextures[u].score = qMax(m_activeTextures[u].score, 1) - 1; + } + } // of units iteration +} + +void TextureSubmissionContext::deactivateTexture(GLTexture* tex) +{ + for (int u=0; u<m_activeTextures.size(); ++u) { + if (m_activeTextures[u].texture == tex) { + Q_ASSERT(m_activeTextures[u].pinned); + m_activeTextures[u].pinned = false; + return; + } + } // of units iteration + + qCWarning(Backend) << Q_FUNC_INFO << "texture not active:" << tex; +} + +/*! + \internal + Returns a texture unit for a texture, -1 if all texture units are assigned. + Tries to use the texture unit with the texture that hasn't been used for the longest time + if the texture happens not to be already pinned on a texture unit. + */ +int TextureSubmissionContext::assignUnitForTexture(GLTexture *tex) +{ + int lowestScoredUnit = -1; + int lowestScore = 0xfffffff; + + for (int u=0; u<m_activeTextures.size(); ++u) { + if (m_activeTextures[u].texture == tex) + return u; + } + + for (int u=0; u<m_activeTextures.size(); ++u) { + // No texture is currently active on the texture unit + // we save the texture unit with the texture that has been on there + // the longest time while not being used + if (!m_activeTextures[u].pinned) { + int score = m_activeTextures[u].score; + if (score < lowestScore) { + lowestScore = score; + lowestScoredUnit = u; + } + } + } // of units iteration + + if (lowestScoredUnit == -1) + qCWarning(Backend) << Q_FUNC_INFO << "No free texture units!"; + + return lowestScoredUnit; +} + +void TextureSubmissionContext::decayTextureScores() +{ + for (int u = 0; u < m_activeTextures.size(); u++) + m_activeTextures[u].score = qMax(m_activeTextures[u].score - 1, 0); +} + +} // namespace Render +} // namespace Qt3DRender of namespace + +QT_END_NAMESPACE diff --git a/src/render/renderers/opengl/graphicshelpers/texturesubmissioncontext_p.h b/src/render/renderers/opengl/graphicshelpers/texturesubmissioncontext_p.h new file mode 100644 index 000000000..3c84fe558 --- /dev/null +++ b/src/render/renderers/opengl/graphicshelpers/texturesubmissioncontext_p.h @@ -0,0 +1,106 @@ +/**************************************************************************** +** +** Copyright (C) 2019 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DRENDER_RENDER_TEXTURESUBMISSIONCONTEXT_H +#define QT3DRENDER_RENDER_TEXTURESUBMISSIONCONTEXT_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + + +#include <qglobal.h> +#include <QVector> + +QT_BEGIN_NAMESPACE + +class QOpenGLContext; + +namespace Qt3DRender { +namespace Render { + +class GraphicsContext; +class GLTexture; + +class Q_AUTOTEST_EXPORT TextureSubmissionContext +{ +public: + enum TextureScope + { + TextureScopeMaterial = 0, + TextureScopeTechnique + // per-pass for deferred rendering? + }; + + TextureSubmissionContext(); + ~TextureSubmissionContext(); + + void initialize(GraphicsContext *context); + void endDrawing(); + int activateTexture(TextureScope scope, QOpenGLContext *gl, GLTexture* tex); + void deactivateTexture(GLTexture *tex); + void deactivateTexturesWithScope(TextureScope ts); + +private: + void decayTextureScores(); + int assignUnitForTexture(GLTexture* tex); + + // active textures, indexed by texture unit + struct ActiveTexture { + GLTexture *texture = nullptr; + int score = 0; + TextureScope scope = TextureScopeMaterial; + bool pinned = false; + }; + QVector<ActiveTexture> m_activeTextures; +}; + +} // namespace Render +} // namespace Qt3DRender + +QT_END_NAMESPACE + +#endif // QT3DRENDER_RENDER_TEXTURESUBMISSIONCONTEXT_H diff --git a/src/render/renderers/opengl/renderer/renderer.cpp b/src/render/renderers/opengl/renderer/renderer.cpp index 4abe62bab..746ca514f 100644 --- a/src/render/renderers/opengl/renderer/renderer.cpp +++ b/src/render/renderers/opengl/renderer/renderer.cpp @@ -1687,7 +1687,6 @@ Renderer::ViewSubmissionResultData Renderer::submitRenderViews(const QVector<Ren ViewSubmissionResultData resultData; resultData.lastBoundFBOId = lastBoundFBOId; resultData.surface = lastUsedSurface; - return resultData; } @@ -1827,11 +1826,13 @@ QVector<Qt3DCore::QAspectJobPtr> Renderer::renderBinJobs() } - // Layer cache is dependent on layers, layer filters and the enabled flag - // on entities + // Layer cache is dependent on layers, layer filters (hence FG structure + // changes) and the enabled flag on entities + const bool frameGraphDirty = dirtyBitsForFrame & AbstractRenderer::FrameGraphDirty; const bool layersDirty = dirtyBitsForFrame & AbstractRenderer::LayersDirty; - const bool layersCacheNeedsToBeRebuilt = layersDirty || entitiesEnabledDirty; + const bool layersCacheNeedsToBeRebuilt = layersDirty || entitiesEnabledDirty || frameGraphDirty; const bool materialDirty = dirtyBitsForFrame & AbstractRenderer::MaterialDirty; + const bool materialCacheNeedsToBeRebuilt = materialDirty || frameGraphDirty; // Rebuild Entity Layers list if layers are dirty if (layersDirty) @@ -1858,7 +1859,7 @@ QVector<Qt3DCore::QAspectJobPtr> Renderer::renderBinJobs() for (int i = 0; i < fgBranchCount; ++i) { RenderViewBuilder builder(fgLeaves.at(i), i, this); builder.setLayerCacheNeedsToBeRebuilt(layersCacheNeedsToBeRebuilt); - builder.setMaterialGathererCacheNeedsToBeRebuilt(materialDirty); + builder.setMaterialGathererCacheNeedsToBeRebuilt(materialCacheNeedsToBeRebuilt); builder.prepareJobs(); renderBinJobs.append(builder.buildJobHierachy()); } @@ -1869,6 +1870,7 @@ QVector<Qt3DCore::QAspectJobPtr> Renderer::renderBinJobs() // FilterLayerEntityJob is part of the RenderViewBuilder jobs and must be run later // if none of those jobs are started this frame notCleared |= AbstractRenderer::EntityEnabledDirty; + notCleared |= AbstractRenderer::FrameGraphDirty; notCleared |= AbstractRenderer::LayersDirty; } diff --git a/src/render/renderers/opengl/renderer/renderer_p.h b/src/render/renderers/opengl/renderer/renderer_p.h index 93d6fdbfa..33ecfe269 100644 --- a/src/render/renderers/opengl/renderer/renderer_p.h +++ b/src/render/renderers/opengl/renderer/renderer_p.h @@ -190,6 +190,7 @@ public: Entity *sceneRoot() const override { return m_renderSceneRoot; } FrameGraphNode *frameGraphRoot() const override; + RenderQueue *renderQueue() const { return m_renderQueue; } void markDirty(BackendNodeDirtySet changes, BackendNode *node) override; BackendNodeDirtySet dirtyBits() override; diff --git a/src/render/renderers/opengl/renderer/renderview.cpp b/src/render/renderers/opengl/renderer/renderview.cpp index 3aa45c836..de25f55d1 100644 --- a/src/render/renderers/opengl/renderer/renderview.cpp +++ b/src/render/renderers/opengl/renderer/renderview.cpp @@ -102,9 +102,10 @@ int LIGHT_COLOR_NAMES[MAX_LIGHTS]; int LIGHT_INTENSITY_NAMES[MAX_LIGHTS]; QString LIGHT_STRUCT_NAMES[MAX_LIGHTS]; +bool wasInitialized = false; + } // anonymous namespace -bool wasInitialized = false; RenderView::StandardUniformsNameToTypeHash RenderView::ms_standardUniformSetters; diff --git a/src/render/renderers/opengl/renderer/renderviewbuilder.cpp b/src/render/renderers/opengl/renderer/renderviewbuilder.cpp index c256337db..d3d2d2dc4 100644 --- a/src/render/renderers/opengl/renderer/renderviewbuilder.cpp +++ b/src/render/renderers/opengl/renderer/renderviewbuilder.cpp @@ -47,7 +47,10 @@ namespace Qt3DRender { namespace Render { -const int RenderViewBuilder::m_optimalParallelJobCount = std::max(QThread::idealThreadCount(), 2); +// In some cases having less jobs is better (especially on fast cpus where +// splitting just adds more overhead). Ideally, we should try to set the value +// depending on the platform/CPU/nbr of cores +const int RenderViewBuilder::m_optimalParallelJobCount = std::max(std::min(4, QThread::idealThreadCount()), 2); namespace { diff --git a/tests/auto/animation/animationutils/animationutils.qrc b/tests/auto/animation/animationutils/animationutils.qrc index 0b499ed76..af041e8a3 100644 --- a/tests/auto/animation/animationutils/animationutils.qrc +++ b/tests/auto/animation/animationutils/animationutils.qrc @@ -5,5 +5,6 @@ <file>clip3.json</file> <file>clip4.json</file> <file>clip5.json</file> + <file>clip6.json</file> </qresource> </RCC> diff --git a/tests/auto/animation/animationutils/clip2.json b/tests/auto/animation/animationutils/clip2.json index 3faff409c..4c70f8493 100644 --- a/tests/auto/animation/animationutils/clip2.json +++ b/tests/auto/animation/animationutils/clip2.json @@ -242,9 +242,9 @@ ] } ], - "channelName": "Rotation" + "channelName": "rotation" } ] } ] -}
\ No newline at end of file +} diff --git a/tests/auto/animation/animationutils/clip6.json b/tests/auto/animation/animationutils/clip6.json new file mode 100644 index 000000000..ba521df55 --- /dev/null +++ b/tests/auto/animation/animationutils/clip6.json @@ -0,0 +1,82 @@ +{ + "animations": [ + { + "animationName": "Rotation", + "channels": [ + { + "channelComponents": [ + { + "channelComponentName": "W", + "keyFrames": [ + { + "coords": [ + 0.0, + 1.0 + ] + }, + { + "coords": [ + 10.0, + 0.707 + ] + } + ] + }, + { + "channelComponentName": "X", + "keyFrames": [ + { + "coords": [ + 0.0, + 0.0 + ] + }, + { + "coords": [ + 10.0, + 0.707 + ] + } + ] + }, + { + "channelComponentName": "Y", + "keyFrames": [ + { + "coords": [ + 0.0, + 0.0 + ] + }, + { + "coords": [ + 10.0, + 0.0 + ] + } + ] + }, + { + "channelComponentName": "Z", + "keyFrames": [ + { + "coords": [ + 0.0, + 0.0 + ] + }, + { + "coords": [ + 10.0, + 0.0 + ] + } + ] + } + ], + "channelName": "Rotation" + } + ] + } + ] +} diff --git a/tests/auto/animation/animationutils/tst_animationutils.cpp b/tests/auto/animation/animationutils/tst_animationutils.cpp index 75ae65ea7..ee393b366 100644 --- a/tests/auto/animation/animationutils/tst_animationutils.cpp +++ b/tests/auto/animation/animationutils/tst_animationutils.cpp @@ -1248,6 +1248,17 @@ private Q_SLOTS: << handler << clip << localTime << expectedResults; expectedResults.clear(); } + { + // a clip with slerp interpolation + handler = new Handler(); + clip = createAnimationClipLoader(handler, QUrl("qrc:/clip6.json")); + localTime = clip->duration() / 2.0f; + expectedResults = QVector<float>() << 0.923822f << 0.382626f << 0.0f << 0.0f; + + QTest::newRow("clip6.json, slerp, t = duration/2") + << handler << clip << localTime << expectedResults; + expectedResults.clear(); + } } void checkEvaluateClipAtLocalTime() diff --git a/tests/auto/core/nodes/tst_nodes.cpp b/tests/auto/core/nodes/tst_nodes.cpp index 75d7a7799..3f7fb4a75 100644 --- a/tests/auto/core/nodes/tst_nodes.cpp +++ b/tests/auto/core/nodes/tst_nodes.cpp @@ -82,11 +82,14 @@ private slots: void checkConstructionSetParentMix(); // QTBUG-60612 void checkConstructionWithParent(); + void checkConstructionWithNonRootParent(); // QTBUG-73986 void checkConstructionAsListElement(); void checkSceneIsSetOnConstructionWithParent(); // QTBUG-69352 void appendingComponentToEntity(); - void appendingParentlessComponentToEntity(); + void appendingParentlessComponentToEntityWithoutScene(); + void appendingParentlessComponentToEntityWithScene(); + void appendingParentlessComponentToNonRootEntity(); void removingComponentFromEntity(); void changeCustomProperty(); @@ -1119,6 +1122,61 @@ void tst_Nodes::checkConstructionWithParent() QCOMPARE(propertyEvent->value().value<Qt3DCore::QNodeId>(), node->id()); } +void tst_Nodes::checkConstructionWithNonRootParent() +{ + // GIVEN + ObserverSpy spy; + Qt3DCore::QScene scene; + QScopedPointer<MyQNode> root(new MyQNode()); + + // WHEN + root->setArbiterAndScene(&spy, &scene); + root->setSimulateBackendCreated(true); + QScopedPointer<MyQNode> parent(new MyQNode(root.data())); + + // THEN + QVERIFY(Qt3DCore::QNodePrivate::get(root.data())->scene() != nullptr); + QVERIFY(Qt3DCore::QNodePrivate::get(parent.data())->scene() != nullptr); + + // WHEN we create a child and then set it as a Node* property + auto *child = new MyQNode(parent.data()); + root->setNodeProperty(child); + + // THEN we should get + // - one creation change for parent, + // - one creation change for child, + // - one child added change for root->parent, + // - and one property change event, + // in that order. + QCoreApplication::processEvents(); + QCOMPARE(root->children().count(), 1); + QCOMPARE(parent->children().count(), 1); + + QCOMPARE(spy.events.size(), 4); // 2 creation changes, 1 child added changes, 1 property change + + // Ensure first event is parent node's creation change + const auto parentCreationEvent = spy.events.takeFirst().change().dynamicCast<Qt3DCore::QNodeCreatedChangeBase>(); + QVERIFY(!parentCreationEvent.isNull()); + QCOMPARE(parentCreationEvent->subjectId(), parent->id()); + + const auto childCreationEvent = spy.events.takeFirst().change().dynamicCast<Qt3DCore::QNodeCreatedChangeBase>(); + QVERIFY(!childCreationEvent.isNull()); + QCOMPARE(childCreationEvent->subjectId(), child->id()); + + const auto parentNewChildEvent = spy.events.takeFirst().change().dynamicCast<Qt3DCore::QPropertyNodeAddedChange>(); + QVERIFY(!parentNewChildEvent.isNull()); + QCOMPARE(parentNewChildEvent->subjectId(), root->id()); + QCOMPARE(parentNewChildEvent->propertyName(), "children"); + QCOMPARE(parentNewChildEvent->addedNodeId(), parent->id()); + + // Ensure second and last event is property set change + const auto propertyEvent = spy.events.takeFirst().change().dynamicCast<Qt3DCore::QPropertyUpdatedChange>(); + QVERIFY(!propertyEvent.isNull()); + QCOMPARE(propertyEvent->subjectId(), root->id()); + QCOMPARE(propertyEvent->propertyName(), "nodeProperty"); + QCOMPARE(propertyEvent->value().value<Qt3DCore::QNodeId>(), child->id()); +} + void tst_Nodes::checkConstructionAsListElement() { // GIVEN @@ -1209,7 +1267,7 @@ void tst_Nodes::checkSceneIsSetOnConstructionWithParent() QCOMPARE(spy.events.size(), 10); // 5 QComponentAddedChange(entity, cmp) and 5 QComponentAddedChange(cmp, entity) } -void tst_Nodes::appendingParentlessComponentToEntity() +void tst_Nodes::appendingParentlessComponentToEntityWithoutScene() { // GIVEN ObserverSpy entitySpy; @@ -1265,6 +1323,162 @@ void tst_Nodes::appendingParentlessComponentToEntity() } } +void tst_Nodes::appendingParentlessComponentToNonRootEntity() +{ + // GIVEN + ObserverSpy eventSpy; + Qt3DCore::QScene scene; + + { + QScopedPointer<MyQEntity> root(new MyQEntity()); + root->setArbiterAndScene(&eventSpy, &scene); + root->setSimulateBackendCreated(true); + + QCoreApplication::processEvents(); + + QScopedPointer<MyQEntity> entity(new MyQEntity(root.data())); + MyQComponent *comp = new MyQComponent(); + + // THEN + QVERIFY(root->parentNode() == nullptr); + QVERIFY(root->children().count() == 1); + QVERIFY(root->components().empty()); + QVERIFY(entity->parentNode() == root.data()); + QVERIFY(entity->children().count() == 0); + QVERIFY(entity->components().empty()); + QVERIFY(comp->parentNode() == nullptr); + + // WHEN + entity->addComponent(comp); + QCoreApplication::processEvents(); + + // THEN + QVERIFY(entity->components().count() == 1); + QVERIFY(entity->components().first() == comp); + QVERIFY(comp->parentNode() == entity.data()); + + QCOMPARE(eventSpy.events.size(), 5); + // - entity created + // - comp created + // - entity added as child to root + // - component added for entity + // - component added for compontent + QVERIFY(eventSpy.events.first().wasLocked()); + + { + const auto event = eventSpy.events.takeFirst().change().dynamicCast<Qt3DCore::QNodeCreatedChangeBase>(); + QVERIFY(!event.isNull()); + QCOMPARE(event->type(), Qt3DCore::NodeCreated); + QCOMPARE(event->subjectId(), entity->id()); + } + { + const auto event = eventSpy.events.takeFirst().change().dynamicCast<Qt3DCore::QNodeCreatedChangeBase>(); + QVERIFY(!event.isNull()); + QCOMPARE(event->type(), Qt3DCore::NodeCreated); + QCOMPARE(event->subjectId(), comp->id()); + } + { + const auto event = eventSpy.events.takeFirst().change().dynamicCast<Qt3DCore::QPropertyNodeAddedChange>(); + QVERIFY(!event.isNull()); + QCOMPARE(event->type(), Qt3DCore::PropertyValueAdded); + QCOMPARE(event->subjectId(), root->id()); + QCOMPARE(event->propertyName(), QByteArrayLiteral("children")); + QCOMPARE(event->addedNodeId(), entity->id()); + } + { + const auto event = eventSpy.events.takeFirst().change().dynamicCast<Qt3DCore::QComponentAddedChange>(); + QVERIFY(!event.isNull()); + QCOMPARE(event->type(), Qt3DCore::ComponentAdded); + QCOMPARE(event->subjectId(), entity->id()); + QCOMPARE(event->entityId(), entity->id()); + QCOMPARE(event->componentId(), comp->id()); + QCOMPARE(event->componentMetaObject(), comp->metaObject()); + } + { + const auto event = eventSpy.events.takeFirst().change().dynamicCast<Qt3DCore::QComponentAddedChange>(); + QVERIFY(!event.isNull()); + QCOMPARE(event->type(), Qt3DCore::ComponentAdded); + QCOMPARE(event->subjectId(), comp->id()); + QCOMPARE(event->entityId(), entity->id()); + QCOMPARE(event->componentId(), comp->id()); + QCOMPARE(event->componentMetaObject(), comp->metaObject()); + } + } +} + +void tst_Nodes::appendingParentlessComponentToEntityWithScene() +{ + // GIVEN + ObserverSpy eventSpy; + Qt3DCore::QScene scene; + + { + QScopedPointer<MyQEntity> entity(new MyQEntity()); + entity->setArbiterAndScene(&eventSpy, &scene); + entity->setSimulateBackendCreated(true); + + QCoreApplication::processEvents(); + + MyQComponent *comp = new MyQComponent(); + + // THEN + QVERIFY(entity->parentNode() == nullptr); + QVERIFY(entity->children().count() == 0); + QVERIFY(entity->components().empty()); + QVERIFY(comp->parentNode() == nullptr); + + // WHEN + entity->addComponent(comp); + QCoreApplication::processEvents(); + + // THEN + QVERIFY(entity->components().count() == 1); + QVERIFY(entity->components().first() == comp); + QVERIFY(comp->parentNode() == entity.data()); + + QCOMPARE(eventSpy.events.size(), 4); + // - entity created + // - child added + // - component added for entity + // - component added for compontent + QVERIFY(eventSpy.events.first().wasLocked()); + + { + const auto event = eventSpy.events.takeFirst().change().dynamicCast<Qt3DCore::QNodeCreatedChangeBase>(); + QVERIFY(!event.isNull()); + QCOMPARE(event->type(), Qt3DCore::NodeCreated); + QCOMPARE(event->subjectId(), comp->id()); + } + { + const auto event = eventSpy.events.takeFirst().change().dynamicCast<Qt3DCore::QPropertyNodeAddedChange>(); + QVERIFY(!event.isNull()); + QCOMPARE(event->type(), Qt3DCore::PropertyValueAdded); + QCOMPARE(event->subjectId(), entity->id()); + QCOMPARE(event->propertyName(), QByteArrayLiteral("children")); + QCOMPARE(event->addedNodeId(), comp->id()); + } + { + const auto event = eventSpy.events.takeFirst().change().dynamicCast<Qt3DCore::QComponentAddedChange>(); + QVERIFY(!event.isNull()); + QCOMPARE(event->type(), Qt3DCore::ComponentAdded); + QCOMPARE(event->subjectId(), entity->id()); + QCOMPARE(event->entityId(), entity->id()); + QCOMPARE(event->componentId(), comp->id()); + QCOMPARE(event->componentMetaObject(), comp->metaObject()); + } + { + const auto event = eventSpy.events.takeFirst().change().dynamicCast<Qt3DCore::QComponentAddedChange>(); + QVERIFY(!event.isNull()); + QCOMPARE(event->type(), Qt3DCore::ComponentAdded); + QCOMPARE(event->subjectId(), comp->id()); + QCOMPARE(event->entityId(), entity->id()); + QCOMPARE(event->componentId(), comp->id()); + QCOMPARE(event->componentMetaObject(), comp->metaObject()); + } + } +} + + void tst_Nodes::appendingComponentToEntity() { // GIVEN diff --git a/tests/auto/render/buffer/tst_buffer.cpp b/tests/auto/render/buffer/tst_buffer.cpp index 72ed399b3..da853d4e9 100644 --- a/tests/auto/render/buffer/tst_buffer.cpp +++ b/tests/auto/render/buffer/tst_buffer.cpp @@ -132,7 +132,7 @@ private Q_SLOTS: // THEN QCOMPARE(renderBuffer.usage(), Qt3DRender::QBuffer::DynamicCopy); QCOMPARE(renderBuffer.isDirty(), true); - QCOMPARE(renderBuffer.data(), QByteArrayLiteral("C7")); + QCOMPARE(renderBuffer.data(), QByteArrayLiteral("C7LS5")); QVERIFY(!renderBuffer.dataGenerator().isNull()); QVERIFY(!renderBuffer.pendingBufferUpdates().empty()); diff --git a/tests/auto/render/qshaderprogram/included.frag b/tests/auto/render/qshaderprogram/included.frag new file mode 100644 index 000000000..0c954ed15 --- /dev/null +++ b/tests/auto/render/qshaderprogram/included.frag @@ -0,0 +1 @@ +out vec4 fragColor; diff --git a/tests/auto/render/qshaderprogram/main.frag b/tests/auto/render/qshaderprogram/main.frag new file mode 100644 index 000000000..f30bd8c45 --- /dev/null +++ b/tests/auto/render/qshaderprogram/main.frag @@ -0,0 +1 @@ +#pragma include included.frag diff --git a/tests/auto/render/qshaderprogram/mainabsolute.frag b/tests/auto/render/qshaderprogram/mainabsolute.frag new file mode 100644 index 000000000..85a013111 --- /dev/null +++ b/tests/auto/render/qshaderprogram/mainabsolute.frag @@ -0,0 +1 @@ +#pragma include :/included.frag diff --git a/tests/auto/render/qshaderprogram/qshaderprogram.pro b/tests/auto/render/qshaderprogram/qshaderprogram.pro index 6f40caee9..4ab65c455 100644 --- a/tests/auto/render/qshaderprogram/qshaderprogram.pro +++ b/tests/auto/render/qshaderprogram/qshaderprogram.pro @@ -8,5 +8,8 @@ CONFIG += testcase SOURCES += tst_qshaderprogram.cpp +RESOURCES += \ + shaders.qrc + include(../../core/common/common.pri) include(../commons/commons.pri) diff --git a/tests/auto/render/qshaderprogram/shaders.qrc b/tests/auto/render/qshaderprogram/shaders.qrc new file mode 100644 index 000000000..34a5cd22c --- /dev/null +++ b/tests/auto/render/qshaderprogram/shaders.qrc @@ -0,0 +1,7 @@ +<RCC> + <qresource prefix="/"> + <file>included.frag</file> + <file>main.frag</file> + <file>mainabsolute.frag</file> + </qresource> +</RCC> diff --git a/tests/auto/render/qshaderprogram/tst_qshaderprogram.cpp b/tests/auto/render/qshaderprogram/tst_qshaderprogram.cpp index 901ee7349..3fcc49067 100644 --- a/tests/auto/render/qshaderprogram/tst_qshaderprogram.cpp +++ b/tests/auto/render/qshaderprogram/tst_qshaderprogram.cpp @@ -511,6 +511,24 @@ private Q_SLOTS: QCOMPARE(status(), newStatus); } + void checkIncludes() + { + // GIVEN + Qt3DRender::QShaderProgram shaderProgram; + QByteArray includedContent = shaderProgram.loadSource(QUrl(QStringLiteral("qrc:/included.frag"))); + + // WHEN (test relative include) + QByteArray mainContent = shaderProgram.loadSource(QUrl(QStringLiteral("qrc:/main.frag"))); + + // THEN + QVERIFY(mainContent.indexOf(includedContent) == 0); + + // WHEN (test absolute include) + mainContent = shaderProgram.loadSource(QUrl(QStringLiteral("qrc:/mainabsolute.frag"))); + + // THEN + QVERIFY(mainContent.indexOf(includedContent) == 0); + } }; QTEST_MAIN(tst_QShaderProgram) diff --git a/tests/auto/render/render.pro b/tests/auto/render/render.pro index b737d328b..edf6fa101 100644 --- a/tests/auto/render/render.pro +++ b/tests/auto/render/render.pro @@ -129,7 +129,6 @@ qtConfig(qt3d-opengl-renderer):qtConfig(private_tests) { graphicshelpergl2 \ materialparametergathererjob \ textures \ - scene2d \ renderer \ renderviewutils \ renderviews \ diff --git a/tests/auto/render/renderer/tst_renderer.cpp b/tests/auto/render/renderer/tst_renderer.cpp index c89805464..ce643a81e 100644 --- a/tests/auto/render/renderer/tst_renderer.cpp +++ b/tests/auto/render/renderer/tst_renderer.cpp @@ -35,6 +35,7 @@ #include <Qt3DRender/private/renderview_p.h> #include <Qt3DRender/private/renderviewbuilder_p.h> #include <Qt3DRender/private/offscreensurfacehelper_p.h> +#include <Qt3DRender/private/renderqueue_p.h> class tst_Renderer : public QObject { @@ -131,6 +132,7 @@ private Q_SLOTS: // GIVEN Qt3DRender::Render::NodeManagers nodeManagers; Qt3DRender::Render::Renderer renderer(Qt3DRender::QRenderAspect::Synchronous); + Qt3DRender::Render::RenderQueue *renderQueue = renderer.renderQueue(); Qt3DRender::Render::OffscreenSurfaceHelper offscreenHelper(&renderer); Qt3DRender::Render::RenderSettings settings; // owned by FG manager @@ -151,7 +153,13 @@ private Q_SLOTS: // NOTE: FilterCompatibleTechniqueJob and ShaderGathererJob cannot run because the context // is not initialized in this test - const int singleRenderViewJobCount = 11 + 1 * Qt3DRender::Render::RenderViewBuilder::optimalJobCount(); + const int renderViewBuilderMaterialCacheJobCount = 1 + Qt3DRender::Render::RenderViewBuilder::optimalJobCount(); + // syncMaterialGathererJob + // n * materialGathererJob + const int layerCacheJobCount = 2; + // filterEntityByLayerJob, + // syncFilterEntityByLayerJob + const int singleRenderViewJobCount = 11 + Qt3DRender::Render::RenderViewBuilder::optimalJobCount(); // RenderViewBuilder renderViewJob, // renderableEntityFilterJob, // lightGatherJob, @@ -177,6 +185,7 @@ private Q_SLOTS: singleRenderViewJobCount); // Only valid for the first call to renderBinJobs(), since subsequent calls won't have the renderqueue reset renderer.clearDirtyBits(Qt3DRender::Render::AbstractRenderer::AllDirty); + renderQueue->reset(); // WHEN renderer.markDirty(Qt3DRender::Render::AbstractRenderer::EntityEnabledDirty, nullptr); @@ -188,9 +197,12 @@ private Q_SLOTS: 1 + // cleanupJob 1 + // VAOGatherer 1 + // updateSkinningPaletteJob - 1); // EntityEnabledDirty + 1 + // EntityEnabledDirty + singleRenderViewJobCount + + layerCacheJobCount); renderer.clearDirtyBits(Qt3DRender::Render::AbstractRenderer::AllDirty); + renderQueue->reset(); // WHEN renderer.markDirty(Qt3DRender::Render::AbstractRenderer::TransformDirty, nullptr); @@ -205,9 +217,27 @@ private Q_SLOTS: 1 + // UpdateWorldBoundingVolume 1 + // UpdateShaderDataTransform 1 + // updateSkinningPaletteJob - 1); // ExpandBoundingVolumeJob + 1 + // ExpandBoundingVolumeJob + singleRenderViewJobCount); + + renderer.clearDirtyBits(Qt3DRender::Render::AbstractRenderer::AllDirty); + renderQueue->reset(); + + // WHEN + renderer.markDirty(Qt3DRender::Render::AbstractRenderer::MaterialDirty, nullptr); + jobs = renderer.renderBinJobs(); + + // THEN (level + QCOMPARE(jobs.size(), + 1 + // updateLevelOfDetailJob + 1 + // cleanupJob + 1 + // VAOGatherer + 1 + // updateSkinningPaletteJob + singleRenderViewJobCount + + renderViewBuilderMaterialCacheJobCount); renderer.clearDirtyBits(Qt3DRender::Render::AbstractRenderer::AllDirty); + renderQueue->reset(); // WHEN renderer.markDirty(Qt3DRender::Render::AbstractRenderer::GeometryDirty, nullptr); @@ -221,9 +251,11 @@ private Q_SLOTS: 1 + // CalculateBoundingVolumeJob 1 + // UpdateMeshTriangleListJob 1 + // updateSkinningPaletteJob - 1); // ExpandBoundingVolumeJob + 1 + // ExpandBoundingVolumeJob + singleRenderViewJobCount); renderer.clearDirtyBits(Qt3DRender::Render::AbstractRenderer::AllDirty); + renderQueue->reset(); // WHEN renderer.markDirty(Qt3DRender::Render::AbstractRenderer::BuffersDirty, nullptr); @@ -237,9 +269,11 @@ private Q_SLOTS: 1 + // updateSkinningPaletteJob 1 + // CalculateBoundingVolumeJob 1 + // UpdateMeshTriangleListJob - 1); // BufferGathererJob + 1 + // BufferGathererJob + singleRenderViewJobCount); renderer.clearDirtyBits(Qt3DRender::Render::AbstractRenderer::AllDirty); + renderQueue->reset(); // WHEN renderer.markDirty(Qt3DRender::Render::AbstractRenderer::TexturesDirty, nullptr); @@ -252,10 +286,11 @@ private Q_SLOTS: 1 + // VAOGatherer 1 + // TexturesGathererJob 1 + // updateSkinningPaletteJob - 1); // SyncTexturesGathererJob + 1 + // SyncTexturesGathererJob + singleRenderViewJobCount); renderer.clearDirtyBits(Qt3DRender::Render::AbstractRenderer::AllDirty); - + renderQueue->reset(); // WHEN renderer.markDirty(Qt3DRender::Render::AbstractRenderer::FrameGraphDirty, nullptr); @@ -265,9 +300,13 @@ private Q_SLOTS: 1 + // updateLevelOfDetailJob 1 + // cleanupJob 1 + // VAOGatherer - 1); // updateSkinningPaletteJob + 1 + // updateSkinningPaletteJob + singleRenderViewJobCount + + layerCacheJobCount + + renderViewBuilderMaterialCacheJobCount); renderer.clearDirtyBits(Qt3DRender::Render::AbstractRenderer::AllDirty); + renderQueue->reset(); // WHEN renderer.markDirty(Qt3DRender::Render::AbstractRenderer::AllDirty, nullptr); @@ -290,9 +329,13 @@ private Q_SLOTS: 1 + // BufferGathererJob 1 + // TexturesGathererJob 1 + // SyncTextureLoadingJob - 1); // UpdateEntityLayersJob + 1 + // UpdateEntityLayersJob + singleRenderViewJobCount + + layerCacheJobCount + + renderViewBuilderMaterialCacheJobCount); renderer.clearDirtyBits(Qt3DRender::Render::AbstractRenderer::AllDirty); + renderQueue->reset(); // Properly shutdown command thread renderer.shutdown(); diff --git a/tests/auto/render/renderviewbuilder/tst_renderviewbuilder.cpp b/tests/auto/render/renderviewbuilder/tst_renderviewbuilder.cpp index 81bca0158..24f56f25b 100644 --- a/tests/auto/render/renderviewbuilder/tst_renderviewbuilder.cpp +++ b/tests/auto/render/renderviewbuilder/tst_renderviewbuilder.cpp @@ -294,20 +294,12 @@ private Q_SLOTS: QVERIFY(renderViewBuilder.syncFrustumCullingJob()->dependencies().contains(testAspect.renderer()->updateWorldTransformJob())); QVERIFY(renderViewBuilder.syncFrustumCullingJob()->dependencies().contains(testAspect.renderer()->updateShaderDataTransformJob())); - for (const auto materialGatherer : renderViewBuilder.materialGathererJobs()) { - QCOMPARE(materialGatherer->dependencies().size(), 3); - QVERIFY(materialGatherer->dependencies().contains(testAspect.renderer()->introspectShadersJob())); - QVERIFY(materialGatherer->dependencies().contains(renderViewBuilder.syncRenderViewInitializationJob())); - QVERIFY(materialGatherer->dependencies().contains(testAspect.renderer()->filterCompatibleTechniqueJob())); - } - // Step 4 QCOMPARE(renderViewBuilder.frustumCullingJob()->dependencies().size(), 2); QVERIFY(renderViewBuilder.frustumCullingJob()->dependencies().contains(renderViewBuilder.syncFrustumCullingJob())); QVERIFY(renderViewBuilder.frustumCullingJob()->dependencies().contains(testAspect.renderer()->expandBoundingVolumeJob())); - QCOMPARE(renderViewBuilder.syncRenderCommandBuildingJob()->dependencies().size(), renderViewBuilder.materialGathererJobs().size() + 9); QVERIFY(renderViewBuilder.syncRenderCommandBuildingJob()->dependencies().contains(renderViewBuilder.syncRenderViewInitializationJob())); QVERIFY(renderViewBuilder.syncRenderCommandBuildingJob()->dependencies().contains(renderViewBuilder.renderableEntityFilterJob())); QVERIFY(renderViewBuilder.syncRenderCommandBuildingJob()->dependencies().contains(renderViewBuilder.computableEntityFilterJob())); @@ -317,19 +309,16 @@ private Q_SLOTS: QVERIFY(renderViewBuilder.syncRenderCommandBuildingJob()->dependencies().contains(testAspect.renderer()->introspectShadersJob())); QVERIFY(renderViewBuilder.syncRenderCommandBuildingJob()->dependencies().contains(testAspect.renderer()->bufferGathererJob())); QVERIFY(renderViewBuilder.syncRenderCommandBuildingJob()->dependencies().contains(testAspect.renderer()->textureGathererJob())); - for (const auto materialGatherer : renderViewBuilder.materialGathererJobs()) { - QVERIFY(renderViewBuilder.syncRenderCommandBuildingJob()->dependencies().contains(materialGatherer)); - } // Step 5 - for (const auto renderViewBuilderJob : renderViewBuilder.renderViewBuilderJobs()) { + for (const auto &renderViewBuilderJob : renderViewBuilder.renderViewBuilderJobs()) { QCOMPARE(renderViewBuilderJob->dependencies().size(), 1); QCOMPARE(renderViewBuilderJob->dependencies().first().data(), renderViewBuilder.syncRenderCommandBuildingJob().data()); } // Step 6 QCOMPARE(renderViewBuilder.syncRenderViewCommandBuildersJob()->dependencies().size(), renderViewBuilder.renderViewBuilderJobs().size()); - for (const auto renderViewBuilderJob : renderViewBuilder.renderViewBuilderJobs()) { + for (const auto &renderViewBuilderJob : renderViewBuilder.renderViewBuilderJobs()) { QVERIFY(renderViewBuilder.syncRenderViewCommandBuildersJob()->dependencies().contains(renderViewBuilderJob)); } } @@ -337,6 +326,7 @@ private Q_SLOTS: // WHEN Qt3DRender::Render::RenderViewBuilder renderViewBuilder(leafNode, 0, testAspect.renderer()); renderViewBuilder.setLayerCacheNeedsToBeRebuilt(true); + renderViewBuilder.setMaterialGathererCacheNeedsToBeRebuilt(true); renderViewBuilder.prepareJobs(); renderViewBuilder.buildJobHierachy(); @@ -372,9 +362,10 @@ private Q_SLOTS: QVERIFY(renderViewBuilder.syncFrustumCullingJob()->dependencies().contains(testAspect.renderer()->updateWorldTransformJob())); QVERIFY(renderViewBuilder.syncFrustumCullingJob()->dependencies().contains(testAspect.renderer()->updateShaderDataTransformJob())); - for (const auto materialGatherer : renderViewBuilder.materialGathererJobs()) { - QCOMPARE(materialGatherer->dependencies().size(), 2); + for (const auto &materialGatherer : renderViewBuilder.materialGathererJobs()) { + QCOMPARE(materialGatherer->dependencies().size(), 3); QVERIFY(materialGatherer->dependencies().contains(renderViewBuilder.syncRenderViewInitializationJob())); + QVERIFY(materialGatherer->dependencies().contains(testAspect.renderer()->introspectShadersJob())); QVERIFY(materialGatherer->dependencies().contains(testAspect.renderer()->filterCompatibleTechniqueJob())); } @@ -383,7 +374,8 @@ private Q_SLOTS: QVERIFY(renderViewBuilder.frustumCullingJob()->dependencies().contains(renderViewBuilder.syncFrustumCullingJob())); QVERIFY(renderViewBuilder.frustumCullingJob()->dependencies().contains(testAspect.renderer()->expandBoundingVolumeJob())); - QCOMPARE(renderViewBuilder.syncRenderCommandBuildingJob()->dependencies().size(), renderViewBuilder.materialGathererJobs().size() + 10); + QCOMPARE(renderViewBuilder.syncMaterialGathererJob()->dependencies().size(), renderViewBuilder.materialGathererJobs().size()); + QVERIFY(renderViewBuilder.syncRenderCommandBuildingJob()->dependencies().contains(renderViewBuilder.syncMaterialGathererJob())); QVERIFY(renderViewBuilder.syncRenderCommandBuildingJob()->dependencies().contains(renderViewBuilder.syncRenderViewInitializationJob())); QVERIFY(renderViewBuilder.syncRenderCommandBuildingJob()->dependencies().contains(renderViewBuilder.renderableEntityFilterJob())); QVERIFY(renderViewBuilder.syncRenderCommandBuildingJob()->dependencies().contains(renderViewBuilder.computableEntityFilterJob())); @@ -394,19 +386,16 @@ private Q_SLOTS: QVERIFY(renderViewBuilder.syncRenderCommandBuildingJob()->dependencies().contains(testAspect.renderer()->introspectShadersJob())); QVERIFY(renderViewBuilder.syncRenderCommandBuildingJob()->dependencies().contains(testAspect.renderer()->bufferGathererJob())); QVERIFY(renderViewBuilder.syncRenderCommandBuildingJob()->dependencies().contains(testAspect.renderer()->textureGathererJob())); - for (const auto materialGatherer : renderViewBuilder.materialGathererJobs()) { - QVERIFY(renderViewBuilder.syncRenderCommandBuildingJob()->dependencies().contains(materialGatherer)); - } // Step 5 - for (const auto renderViewBuilderJob : renderViewBuilder.renderViewBuilderJobs()) { + for (const auto &renderViewBuilderJob : renderViewBuilder.renderViewBuilderJobs()) { QCOMPARE(renderViewBuilderJob->dependencies().size(), 1); QCOMPARE(renderViewBuilderJob->dependencies().first().data(), renderViewBuilder.syncRenderCommandBuildingJob().data()); } // Step 6 QCOMPARE(renderViewBuilder.syncRenderViewCommandBuildersJob()->dependencies().size(), renderViewBuilder.renderViewBuilderJobs().size()); - for (const auto renderViewBuilderJob : renderViewBuilder.renderViewBuilderJobs()) { + for (const auto &renderViewBuilderJob : renderViewBuilder.renderViewBuilderJobs()) { QVERIFY(renderViewBuilder.syncRenderViewCommandBuildersJob()->dependencies().contains(renderViewBuilderJob)); } } @@ -501,7 +490,7 @@ private Q_SLOTS: // THEN QCOMPARE(renderViewBuilder.frustumCullingJob()->isActive(), false); - for (const auto materialGatherer : renderViewBuilder.materialGathererJobs()) { + for (const auto &materialGatherer : renderViewBuilder.materialGathererJobs()) { QVERIFY(materialGatherer->techniqueFilter() == nullptr); QVERIFY(materialGatherer->renderPassFilter() == nullptr); } @@ -512,7 +501,7 @@ private Q_SLOTS: // THEN QCOMPARE(renderViewBuilder.frustumCullingJob()->isActive(), true); - for (const auto materialGatherer : renderViewBuilder.materialGathererJobs()) { + for (const auto &materialGatherer : renderViewBuilder.materialGathererJobs()) { QVERIFY(materialGatherer->techniqueFilter() != nullptr); QVERIFY(materialGatherer->renderPassFilter() != nullptr); } @@ -528,7 +517,7 @@ private Q_SLOTS: QCOMPARE(renderViewBuilder.frustumCullingJob()->isActive(), false); QCOMPARE(renderViewBuilder.filterEntityByLayerJob()->hasLayerFilter(), false); QCOMPARE(renderViewBuilder.filterEntityByLayerJob()->layerFilters().size(), 0); - for (const auto materialGatherer : renderViewBuilder.materialGathererJobs()) { + for (const auto &materialGatherer : renderViewBuilder.materialGathererJobs()) { QVERIFY(materialGatherer->techniqueFilter() == nullptr); QVERIFY(materialGatherer->renderPassFilter() == nullptr); } @@ -541,7 +530,7 @@ private Q_SLOTS: QCOMPARE(renderViewBuilder.frustumCullingJob()->isActive(), true); QCOMPARE(renderViewBuilder.filterEntityByLayerJob()->hasLayerFilter(), true); QCOMPARE(renderViewBuilder.filterEntityByLayerJob()->layerFilters().size(), 1); - for (const auto materialGatherer : renderViewBuilder.materialGathererJobs()) { + for (const auto &materialGatherer : renderViewBuilder.materialGathererJobs()) { QVERIFY(materialGatherer->techniqueFilter() != nullptr); QVERIFY(materialGatherer->renderPassFilter() != nullptr); } diff --git a/tests/manual/manual.pro b/tests/manual/manual.pro index 585928237..a53437fe2 100644 --- a/tests/manual/manual.pro +++ b/tests/manual/manual.pro @@ -59,7 +59,8 @@ SUBDIRS += \ blitframebuffer-qml \ raycasting-qml \ shared_texture_image \ - texture_property_updates + texture_property_updates \ + qtbug-72236 qtHaveModule(multimedia): { SUBDIRS += \ diff --git a/tests/manual/qtbug-72236/main.cpp b/tests/manual/qtbug-72236/main.cpp new file mode 100644 index 000000000..2bf90c03f --- /dev/null +++ b/tests/manual/qtbug-72236/main.cpp @@ -0,0 +1,149 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB). +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE: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 <QThread> +#include <QTimer> + +#include <Qt3DInput/QInputAspect> + +#include <Qt3DRender/qcamera.h> +#include <Qt3DRender/qcameralens.h> +#include <Qt3DExtras/qcylindermesh.h> +#include <Qt3DRender/qmesh.h> +#include <Qt3DRender/qtechnique.h> +#include <Qt3DExtras/qphongmaterial.h> +#include <Qt3DRender/qeffect.h> +#include <Qt3DRender/qtexture.h> +#include <Qt3DRender/qrenderpass.h> +#include <Qt3DRender/qrenderaspect.h> +#include <Qt3DExtras/qforwardrenderer.h> + +#include <Qt3DCore/qentity.h> +#include <Qt3DCore/qtransform.h> +#include <Qt3DCore/qaspectengine.h> + +#include <Qt3DExtras/qt3dwindow.h> +#include <Qt3DExtras/qorbitcameracontroller.h> + +#include <Qt3DRender/QLayer> +#include <Qt3DRender/QLayerFilter> + +using namespace Qt3DRender; + +int main(int argc, char **argv) +{ + QGuiApplication app(argc, argv); + Qt3DExtras::Qt3DWindow view; + + // Root entity + Qt3DCore::QEntity *rootEntity = new Qt3DCore::QEntity(); + + // Camera + Qt3DRender::QCamera *camera = view.camera(); + camera->lens()->setPerspectiveProjection(45.0f, 16.0f/9.0f, 0.1f, 1000.0f); + camera->setPosition(QVector3D(0, 0, 20.0f)); + camera->setUpVector(QVector3D(0, 1, 0)); + camera->setViewCenter(QVector3D(0, 0, 0)); + + // For camera controls + Qt3DExtras::QOrbitCameraController *cameraController = new Qt3DExtras::QOrbitCameraController(rootEntity); + cameraController->setCamera(camera); + + // Cylinder shape data + Qt3DExtras::QCylinderMesh *mesh = new Qt3DExtras::QCylinderMesh(); + mesh->setRadius(1); + mesh->setLength(3); + mesh->setRings(100); + mesh->setSlices(20); + + // Transform for cylinder + Qt3DCore::QTransform *transform = new Qt3DCore::QTransform; + transform->setScale(1.5f); + transform->setRotation(QQuaternion::fromAxisAndAngle(QVector3D(1, 0, 0), 45.0f)); + + // Material + Qt3DExtras::QPhongMaterial *material = new Qt3DExtras::QPhongMaterial(rootEntity); + material->setDiffuse(Qt::red); + + // Cylinder + Qt3DCore::QEntity *cylinder = new Qt3DCore::QEntity(rootEntity); + cylinder->addComponent(mesh); + cylinder->addComponent(transform); + cylinder->addComponent(material); + + QTimer *timer = new QTimer(rootEntity); + QObject::connect(timer, &QTimer::timeout, [=](){ + for (int i = 0; i < 2; i++) { + auto *dummy = new Qt3DCore::QNode(rootEntity); + auto *dummy2 = new Qt3DCore::QNode(dummy); + auto *layerFilter = new QLayerFilter(dummy2); + auto *layer = new QLayer(); + + layerFilter->addLayer(layer); + + cylinder->addComponent(layer); + } + }); + timer->start(1000); + + QTimer *timer2 = new QTimer(rootEntity); + QObject::connect(timer2, &QTimer::timeout, [](){ + QThread::msleep(100); + }); + timer2->start(100); + + // Set root object of the scene + view.setRootEntity(rootEntity); + view.show(); + + return app.exec(); +} diff --git a/tests/manual/qtbug-72236/qtbug-72236.pro b/tests/manual/qtbug-72236/qtbug-72236.pro new file mode 100644 index 000000000..d3db3bc76 --- /dev/null +++ b/tests/manual/qtbug-72236/qtbug-72236.pro @@ -0,0 +1,9 @@ +!include( ../manual.pri ) { + error( "Couldn't find the manual.pri file!" ) +} + +QT += 3dcore 3drender 3dinput 3dextras + +SOURCES += main.cpp + + |