summaryrefslogtreecommitdiffstats
path: root/src
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 /src
parent6f7ac29268df048b5f23ad26c47efcbfdfdb3585 (diff)
parent59d26d3e9411150c7ed0fb0cf68d48988c8dbf59 (diff)
Merge branch '5.12' into 5.13
Diffstat (limited to 'src')
-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
30 files changed, 627 insertions, 341 deletions
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 {