summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul Lemire <paul.lemire@kdab.com>2019-04-02 15:08:50 +0200
committerPaul Lemire <paul.lemire@kdab.com>2019-04-02 15:25:12 +0200
commitf3259300bde381b10cb737735fe19b039c969782 (patch)
tree43c70cfe34cbf08947752653e98ccb777c1c9521
parent6f7ac29268df048b5f23ad26c47efcbfdfdb3585 (diff)
parent59d26d3e9411150c7ed0fb0cf68d48988c8dbf59 (diff)
Merge branch '5.12' into 5.13
-rw-r--r--dist/changes-5.12.220
-rw-r--r--src/animation/backend/animationutils.cpp61
-rw-r--r--src/animation/backend/fcurve.cpp63
-rw-r--r--src/animation/backend/fcurve_p.h3
-rw-r--r--src/animation/frontend/qchannel.cpp16
-rw-r--r--src/core/nodes/qentity.cpp2
-rw-r--r--src/core/nodes/qnode.cpp35
-rw-r--r--src/core/nodes/qnode_p.h1
-rw-r--r--src/extras/shaders/es2/light.inc.frag4
-rw-r--r--src/extras/shaders/es2/light.inc.frag1004
-rw-r--r--src/extras/shaders/es2/phong.inc.frag6
-rw-r--r--src/extras/shaders/es2/phong.inc.frag10012
-rw-r--r--src/quick3d/imports/scene3d/scene3ditem.cpp44
-rw-r--r--src/quick3d/quick3dscene2d/items/qscene2d.cpp7
-rw-r--r--src/render/frontend/qrenderpluginfactory.cpp12
-rw-r--r--src/render/geometry/buffer.cpp1
-rw-r--r--src/render/io/qsceneexportfactory.cpp12
-rw-r--r--src/render/materialsystem/qshaderprogram.cpp25
-rw-r--r--src/render/materialsystem/qshaderprogram_p.h3
-rw-r--r--src/render/materialsystem/shaderbuilder.cpp44
-rw-r--r--src/render/renderers/opengl/graphicshelpers/graphicscontext.cpp10
-rw-r--r--src/render/renderers/opengl/graphicshelpers/graphicscontext_p.h1
-rw-r--r--src/render/renderers/opengl/graphicshelpers/graphicshelpers.pri6
-rw-r--r--src/render/renderers/opengl/graphicshelpers/submissioncontext.cpp205
-rw-r--r--src/render/renderers/opengl/graphicshelpers/submissioncontext_p.h39
-rw-r--r--src/render/renderers/opengl/graphicshelpers/texturesubmissioncontext.cpp225
-rw-r--r--src/render/renderers/opengl/graphicshelpers/texturesubmissioncontext_p.h106
-rw-r--r--src/render/renderers/opengl/renderer/renderer.cpp12
-rw-r--r--src/render/renderers/opengl/renderer/renderer_p.h1
-rw-r--r--src/render/renderers/opengl/renderer/renderview.cpp3
-rw-r--r--src/render/renderers/opengl/renderer/renderviewbuilder.cpp5
-rw-r--r--tests/auto/animation/animationutils/animationutils.qrc1
-rw-r--r--tests/auto/animation/animationutils/clip2.json4
-rw-r--r--tests/auto/animation/animationutils/clip6.json82
-rw-r--r--tests/auto/animation/animationutils/tst_animationutils.cpp11
-rw-r--r--tests/auto/core/nodes/tst_nodes.cpp218
-rw-r--r--tests/auto/render/buffer/tst_buffer.cpp2
-rw-r--r--tests/auto/render/qshaderprogram/included.frag1
-rw-r--r--tests/auto/render/qshaderprogram/main.frag1
-rw-r--r--tests/auto/render/qshaderprogram/mainabsolute.frag1
-rw-r--r--tests/auto/render/qshaderprogram/qshaderprogram.pro3
-rw-r--r--tests/auto/render/qshaderprogram/shaders.qrc7
-rw-r--r--tests/auto/render/qshaderprogram/tst_qshaderprogram.cpp18
-rw-r--r--tests/auto/render/render.pro1
-rw-r--r--tests/auto/render/renderer/tst_renderer.cpp61
-rw-r--r--tests/auto/render/renderviewbuilder/tst_renderviewbuilder.cpp39
-rw-r--r--tests/manual/manual.pro3
-rw-r--r--tests/manual/qtbug-72236/main.cpp149
-rw-r--r--tests/manual/qtbug-72236/qtbug-72236.pro9
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 &parameterPack)
// 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 &parameterPack)
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 &parameterPack);
- // 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
+
+