summaryrefslogtreecommitdiffstats
path: root/src/render
diff options
context:
space:
mode:
Diffstat (limited to 'src/render')
-rw-r--r--src/render/backend/abstractrenderer_p.h11
-rw-r--r--src/render/backend/cameralens.cpp2
-rw-r--r--src/render/backend/resourceaccessor.cpp2
-rw-r--r--src/render/backend/visitorutils_p.h22
-rw-r--r--src/render/configure.json13
-rw-r--r--src/render/framegraph/qframegraphnode.cpp68
-rw-r--r--src/render/framegraph/qframegraphnode_p.h1
-rw-r--r--src/render/framegraph/qlayerfilter.cpp3
-rw-r--r--src/render/framegraph/qrendercapture.cpp33
-rw-r--r--src/render/framegraph/qrenderpassfilter.cpp6
-rw-r--r--src/render/framegraph/qrenderstateset.cpp3
-rw-r--r--src/render/framegraph/qtechniquefilter.cpp6
-rw-r--r--src/render/framegraph/rendercapture.cpp3
-rw-r--r--src/render/frontend/qrenderapi.h61
-rw-r--r--src/render/frontend/qrenderaspect.cpp103
-rw-r--r--src/render/frontend/qrendercapabilities.h7
-rw-r--r--src/render/frontend/qrendersettings.h2
-rw-r--r--src/render/frontend/qrendertarget.cpp3
-rw-r--r--src/render/frontend/render-frontend.pri1
-rw-r--r--src/render/jobs/pickboundingvolumejob.cpp6
-rw-r--r--src/render/jobs/raycastingjob.cpp6
-rw-r--r--src/render/jobs/updatelevelofdetailjob.cpp6
-rw-r--r--src/render/lights/qenvironmentlight.cpp5
-rw-r--r--src/render/materialsystem/material.cpp16
-rw-r--r--src/render/materialsystem/prototypes/default.json160
-rw-r--r--src/render/materialsystem/qeffect.cpp6
-rw-r--r--src/render/materialsystem/qgraphicsapifilter.cpp22
-rw-r--r--src/render/materialsystem/qgraphicsapifilter.h3
-rw-r--r--src/render/materialsystem/qgraphicsapifilter_p.h2
-rw-r--r--src/render/materialsystem/qmaterial.cpp3
-rw-r--r--src/render/materialsystem/qrenderpass.cpp9
-rw-r--r--src/render/materialsystem/qtechnique.cpp9
-rw-r--r--src/render/materialsystem/shader.cpp2
-rw-r--r--src/render/materialsystem/shader_p.h1
-rw-r--r--src/render/materialsystem/shaderbuilder.cpp21
-rw-r--r--src/render/materialsystem/shaderdata.cpp2
-rw-r--r--src/render/materialsystem/shaderdata_p.h2
-rw-r--r--src/render/picking/qabstractraycaster.cpp3
-rw-r--r--src/render/render.pro2
-rw-r--r--src/render/renderstates/qblendequationarguments.h4
-rw-r--r--src/render/renderstates/renderstateset.cpp2
-rw-r--r--src/render/renderstates/renderstateset_p.h5
-rw-r--r--src/render/shadergraph/qshaderformat.cpp146
-rw-r--r--src/render/shadergraph/qshaderformat_p.h126
-rw-r--r--src/render/shadergraph/qshadergenerator.cpp861
-rw-r--r--src/render/shadergraph/qshadergenerator_p.h81
-rw-r--r--src/render/shadergraph/qshadergraph.cpp317
-rw-r--r--src/render/shadergraph/qshadergraph_p.h126
-rw-r--r--src/render/shadergraph/qshadergraphloader.cpp271
-rw-r--r--src/render/shadergraph/qshadergraphloader_p.h102
-rw-r--r--src/render/shadergraph/qshaderlanguage.cpp57
-rw-r--r--src/render/shadergraph/qshaderlanguage_p.h164
-rw-r--r--src/render/shadergraph/qshadernode.cpp174
-rw-r--r--src/render/shadergraph/qshadernode_p.h131
-rw-r--r--src/render/shadergraph/qshadernodeport.cpp58
-rw-r--r--src/render/shadergraph/qshadernodeport_p.h91
-rw-r--r--src/render/shadergraph/qshadernodesloader.cpp293
-rw-r--r--src/render/shadergraph/qshadernodesloader_p.h99
-rw-r--r--src/render/shadergraph/shadergraph.pri21
-rw-r--r--src/render/surfaces/surfaces.pri9
-rw-r--r--src/render/surfaces/vulkaninstance.cpp75
-rw-r--r--src/render/surfaces/vulkaninstance_p.h55
-rw-r--r--src/render/texture/qabstracttexture.cpp3
63 files changed, 3810 insertions, 97 deletions
diff --git a/src/render/backend/abstractrenderer_p.h b/src/render/backend/abstractrenderer_p.h
index 020aa6c9f..0dd24dcd8 100644
--- a/src/render/backend/abstractrenderer_p.h
+++ b/src/render/backend/abstractrenderer_p.h
@@ -54,6 +54,7 @@
#include <QtCore/qmutex.h>
#include <Qt3DRender/private/qt3drender_global_p.h>
#include <Qt3DRender/private/handle_types_p.h>
+#include <Qt3DRender/qrenderapi.h>
#include <Qt3DCore/qaspectjob.h>
#include <Qt3DCore/qnodeid.h>
#include <QtGui/qsurfaceformat.h>
@@ -80,6 +81,7 @@ class QAspectManager;
namespace Qt3DRender {
class QRenderAspect;
+struct GraphicsApiFilterData;
namespace Render {
@@ -91,18 +93,11 @@ class BackendNode;
class OffscreenSurfaceHelper;
class Shader;
class RenderBackendResourceAccessor;
-
class Q_3DRENDERSHARED_PRIVATE_EXPORT AbstractRenderer
{
public:
virtual ~AbstractRenderer() {}
- enum API {
- OpenGL,
- Vulkan,
- DirectX
- };
-
// Changes made to backend nodes are reported to the Renderer
enum BackendNodeDirtyFlag {
TransformDirty = 1 << 0,
@@ -190,7 +185,7 @@ public:
virtual void setOffscreenSurfaceHelper(OffscreenSurfaceHelper *helper) = 0;
virtual QSurfaceFormat format() = 0;
virtual QOpenGLContext *shareContext() const = 0;
-
+ virtual const GraphicsApiFilterData *contextInfo() const = 0;
// These commands are executed in a dedicated command thread
// More will be added later
diff --git a/src/render/backend/cameralens.cpp b/src/render/backend/cameralens.cpp
index c635ddb61..c8d1d110b 100644
--- a/src/render/backend/cameralens.cpp
+++ b/src/render/backend/cameralens.cpp
@@ -119,8 +119,6 @@ Matrix4x4 CameraLens::viewMatrix(const Matrix4x4 &worldTransform)
convertToQVector3D(Vector3D(position + viewDirection)),
convertToQVector3D(Vector3D(upVector)));
- qDebug(Jobs) << Q_FUNC_INFO << "Transform Matrix" << worldTransform << "View Matrix" << m;
-
return Matrix4x4(m);
}
diff --git a/src/render/backend/resourceaccessor.cpp b/src/render/backend/resourceaccessor.cpp
index ebc55c7ca..e6ed4262b 100644
--- a/src/render/backend/resourceaccessor.cpp
+++ b/src/render/backend/resourceaccessor.cpp
@@ -80,7 +80,7 @@ bool ResourceAccessor::accessResource(ResourceType type,
Q_FALLTHROUGH();
case RenderBackendResourceAccessor::OGLTextureRead:
{
- if (m_renderer->api() != AbstractRenderer::OpenGL) {
+ if (m_renderer->api() != API::OpenGL) {
qWarning() << "Renderer plugin is not compatible with Scene2D";
return false;
}
diff --git a/src/render/backend/visitorutils_p.h b/src/render/backend/visitorutils_p.h
index 476416ab1..364a8f069 100644
--- a/src/render/backend/visitorutils_p.h
+++ b/src/render/backend/visitorutils_p.h
@@ -111,6 +111,24 @@ void visitPrimitives(NodeManagers *manager, const GeometryProvider *renderer, Vi
Buffer *positionBuffer = nullptr;
Buffer *indexBuffer = nullptr;
+ auto updateStride = [](BufferInfo &info, int stride) {
+ if (stride) {
+ info.byteStride = stride;
+ return;
+ }
+ switch (info.type) {
+ case Qt3DCore::QAttribute::Byte: info.byteStride = sizeof(qint8) * info.dataSize; return;
+ case Qt3DCore::QAttribute::UnsignedByte: info.byteStride = sizeof(quint8) * info.dataSize; return;
+ case Qt3DCore::QAttribute::Short: info.byteStride = sizeof(qint16) * info.dataSize; return;
+ case Qt3DCore::QAttribute::UnsignedShort: info.byteStride = sizeof(quint16) * info.dataSize; return;
+ case Qt3DCore::QAttribute::Int: info.byteStride = sizeof(qint32) * info.dataSize; return;
+ case Qt3DCore::QAttribute::UnsignedInt: info.byteStride = sizeof(quint32) * info.dataSize; return;
+ case Qt3DCore::QAttribute::Float: info.byteStride = sizeof(float) * info.dataSize; return;
+ case Qt3DCore::QAttribute::Double: info.byteStride = sizeof(double) * info.dataSize; return;
+ default: return;
+ }
+ };
+
if (geom) {
positionAttribute = manager->lookupResource<Attribute, AttributeManager>(geom->boundingPositionAttribute());
@@ -137,9 +155,9 @@ void visitPrimitives(NodeManagers *manager, const GeometryProvider *renderer, Vi
vertexBufferInfo.data = positionBuffer->data();
vertexBufferInfo.type = positionAttribute->vertexBaseType();
vertexBufferInfo.byteOffset = positionAttribute->byteOffset();
- vertexBufferInfo.byteStride = positionAttribute->byteStride();
vertexBufferInfo.dataSize = positionAttribute->vertexSize();
vertexBufferInfo.count = positionAttribute->count();
+ updateStride(vertexBufferInfo, positionAttribute->byteStride());
if (indexBuffer) { // Indexed
@@ -147,10 +165,10 @@ void visitPrimitives(NodeManagers *manager, const GeometryProvider *renderer, Vi
indexBufferInfo.data = indexBuffer->data();
indexBufferInfo.type = indexAttribute->vertexBaseType();
indexBufferInfo.byteOffset = indexAttribute->byteOffset();
- indexBufferInfo.byteStride = indexAttribute->byteStride();
indexBufferInfo.count = indexAttribute->count();
indexBufferInfo.restartEnabled = renderer->primitiveRestartEnabled();
indexBufferInfo.restartIndexValue = renderer->restartIndexValue();
+ updateStride(indexBufferInfo, indexAttribute->byteStride());
IndexExecutor executor;
executor.m_vertexBufferInfo = vertexBufferInfo;
diff --git a/src/render/configure.json b/src/render/configure.json
index 02a6e3747..6e4e07404 100644
--- a/src/render/configure.json
+++ b/src/render/configure.json
@@ -8,7 +8,8 @@
"commandline": {
"options": {
- "qt3d-opengl-renderer": "boolean"
+ "qt3d-opengl-renderer": "boolean",
+ "qt3d-rhi-renderer": "boolean"
}
},
@@ -18,6 +19,13 @@
"purpose": "Use the OpenGL renderer",
"section": "Qt 3D Renderers",
"output": [ "privateFeature" ]
+ },
+ "qt3d-rhi-renderer": {
+ "label": "RHI Renderer",
+ "purpose": "Use the RHI renderer",
+ "section": "Qt 3D Renderers",
+ "autoDetect": false,
+ "output": [ "privateFeature" ]
}
},
@@ -28,7 +36,8 @@
{
"section": "Qt 3D Renderers",
"entries": [
- "qt3d-opengl-renderer"
+ "qt3d-opengl-renderer",
+ "qt3d-rhi-renderer"
]
}
]
diff --git a/src/render/framegraph/qframegraphnode.cpp b/src/render/framegraph/qframegraphnode.cpp
index 489fa03b4..d28030cb0 100644
--- a/src/render/framegraph/qframegraphnode.cpp
+++ b/src/render/framegraph/qframegraphnode.cpp
@@ -39,6 +39,9 @@
#include "qframegraphnode.h"
#include "qframegraphnode_p.h"
+#include <Qt3DRender/qfilterkey.h>
+#include <Qt3DRender/qtechniquefilter.h>
+#include <Qt3DRender/qrenderpassfilter.h>
#include <Qt3DCore/QNode>
#include <QVector>
@@ -59,6 +62,20 @@ QString dumpNode(const Qt3DRender::QFrameGraphNode *n) {
return res;
}
+QString dumpNodeFilters(const Qt3DRender::QFrameGraphNode *n, const QVector<Qt3DRender::QFilterKey*> &filters) {
+ QString res = QLatin1String(n->metaObject()->className());
+ if (!n->objectName().isEmpty())
+ res += QString(QLatin1String(" (%1)")).arg(n->objectName());
+
+ QStringList kv;
+ for (auto filter: filters)
+ kv.push_back(QString(QLatin1String("%1: %2")).arg(filter->name(), filter->value().toString()));
+ if (kv.size())
+ res += QString(QLatin1String(" <%1>")).arg(kv.join(QLatin1String(", ")));
+
+ return res;
+}
+
QStringList dumpFG(const Qt3DCore::QNode *n, int level = 0)
{
QStringList reply;
@@ -132,14 +149,53 @@ void dumpFGPaths(const Qt3DRender::QFrameGraphNode *n, QStringList &result)
findFGLeaves(rootHFg, fgLeaves);
// Traverse back to root
+ int rv = 1;
for (const Qt3DRender::QFrameGraphNode *fgNode : fgLeaves) {
QStringList parents;
while (fgNode != nullptr) {
parents.prepend(dumpNode(fgNode));
fgNode = fgNode->parentFrameGraphNode();
}
- if (parents.size())
- result << QLatin1String("[ ") + parents.join(QLatin1String(", ")) + QLatin1String(" ]");
+ if (parents.size()) {
+ result << QString(QLatin1String("%1 [ %2 ]")).arg(QString::number(rv), parents.join(QLatin1String(", ")));
+ ++rv;
+ }
+ }
+}
+
+void dumpFGFilterState(const Qt3DRender::QFrameGraphNode *n, QStringList &result)
+{
+ // Build FG node hierarchy
+ const HierarchyFGNodePtr rootHFg = buildFGHierarchy(n);
+
+ // Gather FG leaves
+ QVector<const Qt3DRender::QFrameGraphNode *> fgLeaves;
+ findFGLeaves(rootHFg, fgLeaves);
+
+ // Traverse back to root
+ int rv = 1;
+ for (const Qt3DRender::QFrameGraphNode *fgNode : fgLeaves) {
+ int parents = 0;
+ QStringList filters;
+ while (fgNode != nullptr) {
+ ++parents;
+ if (fgNode->isEnabled()) {
+ auto techniqueFilter = qobject_cast<const Qt3DRender::QTechniqueFilter *>(fgNode);
+ if (techniqueFilter && techniqueFilter->matchAll().size())
+ filters.prepend(dumpNodeFilters(techniqueFilter, techniqueFilter->matchAll()));
+ auto renderPassFilter = qobject_cast<const Qt3DRender::QRenderPassFilter *>(fgNode);
+ if (renderPassFilter)
+ filters.prepend(dumpNodeFilters(renderPassFilter, renderPassFilter->matchAny()));
+ }
+ fgNode = fgNode->parentFrameGraphNode();
+ }
+ if (parents) {
+ if (filters.size())
+ result << QString(QLatin1String("%1 [ %2 ]")).arg(QString::number(rv), filters.join(QLatin1String(", ")));
+ else
+ result << QString(QObject::tr("%1 [ No Filters ]")).arg(rv);
+ ++rv;
+ }
}
}
@@ -350,6 +406,14 @@ QStringList QFrameGraphNodePrivate::dumpFrameGraphPaths() const
return result;
}
+QStringList QFrameGraphNodePrivate::dumpFrameGraphFilterState() const
+{
+ Q_Q(const QFrameGraphNode);
+ QStringList result;
+ dumpFGFilterState(q, result);
+ return result;
+}
+
/*! \internal */
QFrameGraphNode::QFrameGraphNode(QFrameGraphNodePrivate &dd, QNode *parent)
: QNode(dd, parent)
diff --git a/src/render/framegraph/qframegraphnode_p.h b/src/render/framegraph/qframegraphnode_p.h
index 4d9516b88..b9a60d79a 100644
--- a/src/render/framegraph/qframegraphnode_p.h
+++ b/src/render/framegraph/qframegraphnode_p.h
@@ -72,6 +72,7 @@ public:
QString dumpFrameGraph() const;
QStringList dumpFrameGraphPaths() const;
+ QStringList dumpFrameGraphFilterState() const;
Q_DECLARE_PUBLIC(QFrameGraphNode)
};
diff --git a/src/render/framegraph/qlayerfilter.cpp b/src/render/framegraph/qlayerfilter.cpp
index 50f3eb429..ef87b1c2e 100644
--- a/src/render/framegraph/qlayerfilter.cpp
+++ b/src/render/framegraph/qlayerfilter.cpp
@@ -213,8 +213,9 @@ void QLayerFilter::removeLayer(QLayer *layer)
{
Q_ASSERT(layer);
Q_D(QLayerFilter);
+ if (!d->m_layers.removeOne(layer))
+ return;
d->update();
- d->m_layers.removeOne(layer);
// Remove bookkeeping connection
d->unregisterDestructionHelper(layer);
}
diff --git a/src/render/framegraph/qrendercapture.cpp b/src/render/framegraph/qrendercapture.cpp
index 12afbeee8..97fd5ed8d 100644
--- a/src/render/framegraph/qrendercapture.cpp
+++ b/src/render/framegraph/qrendercapture.cpp
@@ -250,8 +250,7 @@ QRenderCaptureReply *QRenderCapturePrivate::takeReply(int captureId)
QMutexLocker lock(&m_mutex);
for (int i = 0; i < m_waitingReplies.size(); ++i) {
if (m_waitingReplies[i]->d_func()->m_captureId == captureId) {
- reply = m_waitingReplies[i];
- m_waitingReplies.remove(i);
+ reply = m_waitingReplies.takeAt(i);
break;
}
}
@@ -285,12 +284,12 @@ QRenderCapture::QRenderCapture(Qt3DCore::QNode *parent)
}
/*!
- * \deprecated
- * Used to request render capture. User can specify a \a captureId to identify
- * the request. The requestId does not have to be unique. Only one render capture result
- * is produced per requestCapture call even if the frame graph has multiple leaf nodes.
- * The function returns a QRenderCaptureReply object, which receives the captured image
- * when it is done. The user is responsible for deallocating the returned object.
+ * \deprecated Used to request render capture. User can specify a \a captureId
+ * to identify the request. The requestId does not have to be unique. Only one
+ * render capture result is produced per requestCapture call even if the frame
+ * graph has multiple leaf nodes. The function returns a QRenderCaptureReply
+ * object, which receives the captured image when it is done. The user is
+ * responsible for deallocating the returned object by calling deleteLater().
*/
QRenderCaptureReply *QRenderCapture::requestCapture(int captureId)
{
@@ -309,10 +308,11 @@ QRenderCaptureReply *QRenderCapture::requestCapture(int captureId)
}
/*!
- * Used to request render capture from a specified \a rect. Only one render capture result
- * is produced per requestCapture call even if the frame graph has multiple leaf nodes.
- * The function returns a QRenderCaptureReply object, which receives the captured image
- * when it is done. The user is responsible for deallocating the returned object.
+ * Used to request render capture from a specified \a rect. Only one render
+ * capture result is produced per requestCapture call even if the frame graph
+ * has multiple leaf nodes. The function returns a QRenderCaptureReply object,
+ * which receives the captured image when it is done. The user is responsible
+ * for deallocating the returned object by calling deleteLater().
*/
QRenderCaptureReply *QRenderCapture::requestCapture(const QRect &rect)
{
@@ -334,10 +334,11 @@ QRenderCaptureReply *QRenderCapture::requestCapture(const QRect &rect)
}
/*!
- * Used to request render capture. Only one render capture result is produced per
- * requestCapture call even if the frame graph has multiple leaf nodes.
- * The function returns a QRenderCaptureReply object, which receives the captured image
- * when it is done. The user is responsible for deallocating the returned object.
+ * Used to request render capture. Only one render capture result is produced
+ * per requestCapture call even if the frame graph has multiple leaf nodes. The
+ * function returns a QRenderCaptureReply object, which receives the captured
+ * image when it is done. The user is responsible for deallocating the returned
+ * object by calling deleterLater().
*/
Qt3DRender::QRenderCaptureReply *QRenderCapture::requestCapture()
{
diff --git a/src/render/framegraph/qrenderpassfilter.cpp b/src/render/framegraph/qrenderpassfilter.cpp
index dc6cefd00..304da32d9 100644
--- a/src/render/framegraph/qrenderpassfilter.cpp
+++ b/src/render/framegraph/qrenderpassfilter.cpp
@@ -150,8 +150,9 @@ void QRenderPassFilter::removeMatch(QFilterKey *filterKey)
Q_ASSERT(filterKey);
Q_D(QRenderPassFilter);
+ if (!d->m_matchList.removeOne(filterKey))
+ return;
d->update();
- d->m_matchList.removeOne(filterKey);
// Remove bookkeeping connection
d->unregisterDestructionHelper(filterKey);
}
@@ -188,8 +189,9 @@ void QRenderPassFilter::removeParameter(QParameter *parameter)
Q_ASSERT(parameter);
Q_D(QRenderPassFilter);
+ if (!d->m_parameters.removeOne(parameter))
+ return;
d->update();
- d->m_parameters.removeOne(parameter);
// Remove bookkeeping connection
d->unregisterDestructionHelper(parameter);
}
diff --git a/src/render/framegraph/qrenderstateset.cpp b/src/render/framegraph/qrenderstateset.cpp
index 6923e8034..d8bde2ec4 100644
--- a/src/render/framegraph/qrenderstateset.cpp
+++ b/src/render/framegraph/qrenderstateset.cpp
@@ -202,8 +202,9 @@ void QRenderStateSet::removeRenderState(QRenderState *state)
Q_ASSERT(state);
Q_D(QRenderStateSet);
+ if (!d->m_renderStates.removeOne(state))
+ return;
d->update();
- d->m_renderStates.removeOne(state);
// Remove bookkeeping connection
d->unregisterDestructionHelper(state);
}
diff --git a/src/render/framegraph/qtechniquefilter.cpp b/src/render/framegraph/qtechniquefilter.cpp
index d00d04da9..17a79ba25 100644
--- a/src/render/framegraph/qtechniquefilter.cpp
+++ b/src/render/framegraph/qtechniquefilter.cpp
@@ -154,8 +154,9 @@ void QTechniqueFilter::removeMatch(QFilterKey *filterKey)
{
Q_ASSERT(filterKey);
Q_D(QTechniqueFilter);
+ if (!d->m_matchList.removeOne(filterKey))
+ return;
d->update();
- d->m_matchList.removeOne(filterKey);
// Remove bookkeeping connection
d->unregisterDestructionHelper(filterKey);
}
@@ -191,8 +192,9 @@ void QTechniqueFilter::removeParameter(QParameter *parameter)
{
Q_ASSERT(parameter);
Q_D(QTechniqueFilter);
+ if (!d->m_parameters.removeOne(parameter))
+ return;
d->update();
- d->m_parameters.removeOne(parameter);
// Remove bookkeeping connection
d->unregisterDestructionHelper(parameter);
}
diff --git a/src/render/framegraph/rendercapture.cpp b/src/render/framegraph/rendercapture.cpp
index 4d8ad0591..fea0acad1 100644
--- a/src/render/framegraph/rendercapture.cpp
+++ b/src/render/framegraph/rendercapture.cpp
@@ -111,7 +111,8 @@ void RenderCapture::syncRenderCapturesToFrontend(Qt3DCore::QAspectManager *manag
QMutexLocker lock(&m_mutex);
for (const RenderCaptureDataPtr &data : qAsConst(m_renderCaptureData)) {
QPointer<QRenderCaptureReply> reply = dfrontend->takeReply(data.data()->captureId);
- if (reply) {
+ // Note: QPointer has no operator bool, we must use isNull() to check it
+ if (!reply.isNull()) {
dfrontend->setImage(reply, data.data()->image);
emit reply->completed();
}
diff --git a/src/render/frontend/qrenderapi.h b/src/render/frontend/qrenderapi.h
new file mode 100644
index 000000000..fc046642a
--- /dev/null
+++ b/src/render/frontend/qrenderapi.h
@@ -0,0 +1,61 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 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_QRENDERAPI_H
+#define QT3DRENDER_QRENDERAPI_H
+
+#include <Qt3DRender/qt3drender_global.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+enum class API {
+ OpenGL,
+ Vulkan,
+ DirectX,
+ Metal,
+ Null
+};
+
+} // namespace Qt3Drender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_QRENDERAPI_H
diff --git a/src/render/frontend/qrenderaspect.cpp b/src/render/frontend/qrenderaspect.cpp
index 9412d3526..015f5bba2 100644
--- a/src/render/frontend/qrenderaspect.cpp
+++ b/src/render/frontend/qrenderaspect.cpp
@@ -165,6 +165,8 @@
#include <Qt3DRender/private/updatelevelofdetailjob_p.h>
#include <Qt3DRender/private/job_common_p.h>
#include <Qt3DRender/private/pickeventfilter_p.h>
+#include <Qt3DRender/private/techniquemanager_p.h>
+#include <Qt3DRender/private/qgraphicsapifilter_p.h>
#include <private/qrenderpluginfactory_p.h>
#include <private/qrenderplugin_p.h>
@@ -189,6 +191,81 @@ QT_BEGIN_NAMESPACE
using namespace Qt3DCore;
+namespace {
+
+QString dumpNode(const Qt3DCore::QEntity *n) {
+ auto formatNode = [](const Qt3DCore::QNode *n) {
+ QString res = QString(QLatin1String("%1{%2}"))
+ .arg(QLatin1String(n->metaObject()->className()))
+ .arg(n->id().id());
+ if (!n->objectName().isEmpty())
+ res += QString(QLatin1String(" (%1)")).arg(n->objectName());
+ if (!n->isEnabled())
+ res += QLatin1String(" [D]");
+ return res;
+ };
+
+ return formatNode(n);
+}
+
+QString dumpNodeFilters(const QString &filterType, const QVector<Qt3DRender::QFilterKey*> &filters) {
+ QString res;
+
+ QStringList kv;
+ for (auto filter: filters)
+ kv.push_back(QString(QLatin1String("%1: %2")).arg(filter->name(), filter->value().toString()));
+ if (kv.size())
+ res += QString(QLatin1String("%1 <%2>")).arg(filterType, kv.join(QLatin1String(", ")));
+
+ return res;
+}
+
+QStringList dumpSGFilterState(Qt3DRender::Render::TechniqueManager *manager,
+ const Qt3DRender::GraphicsApiFilterData *contextData,
+ const Qt3DCore::QNode *n, int level = 0)
+{
+ using namespace Qt3DRender;
+
+ QStringList reply;
+ const auto *entity = qobject_cast<const Qt3DCore::QEntity *>(n);
+ if (entity != nullptr) {
+ QString res = dumpNode(entity);
+ auto materials = entity->componentsOfType<QMaterial>();
+ if (materials.size() && materials.front()->effect()) {
+ auto m = materials.front();
+ const auto techniques = m->effect()->techniques();
+ for (auto t: m->effect()->techniques()) {
+ auto apiFilter = t->graphicsApiFilter();
+ if (apiFilter) {
+ auto backendTechnique = manager->lookupResource(t->id());
+ if (backendTechnique &&
+ !(*contextData == *backendTechnique->graphicsApiFilter()))
+ continue; // skip technique that doesn't match current renderer
+ }
+
+ QStringList filters;
+ filters += dumpNodeFilters(QLatin1String("T"), t->filterKeys());
+
+ const auto &renderPasses = t->renderPasses();
+ for (auto r: renderPasses)
+ filters += dumpNodeFilters(QLatin1String("RP"), r->filterKeys());
+
+ if (filters.size())
+ res += QLatin1String(" [ %1 ]").arg(filters.join(QLatin1String(" ")));
+ }
+ }
+ reply += res.rightJustified(res.length() + level * 2, ' ');
+ level++;
+ }
+
+ const auto children = n->childNodes();
+ for (auto *child: children)
+ reply += dumpSGFilterState(manager, contextData, child, level);
+
+ return reply;
+}
+
+}
namespace Qt3DRender {
#define CreateSynchronizerJobPtr(lambda, type) \
@@ -240,8 +317,11 @@ QRenderAspectPrivate::QRenderAspectPrivate(QRenderAspect::RenderType type)
m_updateWorldBoundingVolumeJob->addDependency(m_worldTransformJob);
m_updateWorldBoundingVolumeJob->addDependency(m_calculateBoundingVolumeJob);
+ m_calculateBoundingVolumeJob->addDependency(m_updateTreeEnabledJob);
m_expandBoundingVolumeJob->addDependency(m_updateWorldBoundingVolumeJob);
m_updateLevelOfDetailJob->addDependency(m_expandBoundingVolumeJob);
+ m_pickBoundingVolumeJob->addDependency(m_expandBoundingVolumeJob);
+ m_rayCastingJob->addDependency(m_expandBoundingVolumeJob);
}
/*! \internal */
@@ -537,7 +617,7 @@ QRenderAspect::~QRenderAspect()
// Called by Scene3DRenderer only
void QRenderAspectPrivate::renderInitialize(QOpenGLContext *context)
{
- if (m_renderer->api() == Render::AbstractRenderer::OpenGL)
+ if (m_renderer->api() == API::OpenGL)
m_renderer->setOpenGLContext(context);
m_renderer->initialize();
}
@@ -612,6 +692,9 @@ QVector<Qt3DCore::QAspectJobPtr> QRenderAspect::jobsToExecute(qint64 time)
const QVector<QAspectJobPtr> geometryJobs = d->createGeometryRendererJobs();
jobs.append(geometryJobs);
+ const QVector<QAspectJobPtr> preRenderingJobs = d->createPreRendererJobs();
+ jobs.append(preRenderingJobs);
+
// Don't spawn any rendering jobs, if the renderer decides to skip this frame
// Note: this only affects rendering jobs (jobs that load buffers,
// perform picking,... must still be run)
@@ -630,12 +713,8 @@ QVector<Qt3DCore::QAspectJobPtr> QRenderAspect::jobsToExecute(qint64 time)
// Create the jobs to build the frame
const bool entitiesEnabledDirty = dirtyBitsForFrame & AbstractRenderer::EntityEnabledDirty;
- if (entitiesEnabledDirty) {
+ if (entitiesEnabledDirty)
jobs.push_back(d->m_updateTreeEnabledJob);
- // This dependency is added here because we clear all dependencies
- // at the start of this function.
- d->m_calculateBoundingVolumeJob->addDependency(d->m_updateTreeEnabledJob);
- }
if (dirtyBitsForFrame & AbstractRenderer::TransformDirty) {
jobs.push_back(d->m_worldTransformJob);
@@ -663,9 +742,6 @@ QVector<Qt3DCore::QAspectJobPtr> QRenderAspect::jobsToExecute(qint64 time)
if (layersDirty)
jobs.push_back(d->m_updateEntityLayersJob);
- const QVector<QAspectJobPtr> preRenderingJobs = d->createPreRendererJobs();
- jobs.append(preRenderingJobs);
-
const QVector<QAspectJobPtr> renderBinJobs = d->m_renderer->renderBinJobs();
jobs.append(renderBinJobs);
}
@@ -686,6 +762,13 @@ QVariant QRenderAspect::executeCommand(const QStringList &args)
return Qt3DRender::QFrameGraphNodePrivate::get(fg)->dumpFrameGraph();
if (args.front() == QLatin1String("framepaths"))
return Qt3DRender::QFrameGraphNodePrivate::get(fg)->dumpFrameGraphPaths().join(QLatin1String("\n"));
+ if (args.front() == QLatin1String("filterstates")) {
+ const auto activeContextInfo = d->m_renderer->contextInfo();
+ QString res = QLatin1String("Active Graphics API: ") + activeContextInfo->toString() + QLatin1String("\n");
+ res += QLatin1String("Render Views:\n ") + Qt3DRender::QFrameGraphNodePrivate::get(fg)->dumpFrameGraphFilterState().join(QLatin1String("\n ")) + QLatin1String("\n");
+ res += QLatin1String("Scene Graph:\n ") + dumpSGFilterState(d->m_nodeManagers->techniqueManager(), activeContextInfo, d->m_root).join(QLatin1String("\n "));
+ return res;
+ }
}
if (args.front() == QLatin1String("scenegraph"))
return droot->dumpSceneGraph();
@@ -803,7 +886,7 @@ QVector<QAspectJobPtr> QRenderAspectPrivate::createPreRendererJobs() const
const auto frameMouseEvents = m_pickEventFilter->pendingMouseEvents();
const auto frameKeyEvents = m_pickEventFilter->pendingKeyEvents();
- m_renderer->setPendingEvents(frameMouseEvents, m_pickEventFilter->pendingKeyEvents());
+ m_renderer->setPendingEvents(frameMouseEvents, frameKeyEvents);
auto jobs = m_renderer->preRenderingJobs();
diff --git a/src/render/frontend/qrendercapabilities.h b/src/render/frontend/qrendercapabilities.h
index 14e233f59..512be5434 100644
--- a/src/render/frontend/qrendercapabilities.h
+++ b/src/render/frontend/qrendercapabilities.h
@@ -86,8 +86,11 @@ class Q_3DRENDERSHARED_EXPORT QRenderCapabilities : public QObject
Q_PROPERTY(int maxComputeSharedMemorySize READ maxComputeSharedMemorySize CONSTANT)
public:
enum API {
- OpenGL = QSurfaceFormat::OpenGL,
- OpenGLES = QSurfaceFormat::OpenGLES,
+ OpenGL = QSurfaceFormat::OpenGL, // 1
+ OpenGLES = QSurfaceFormat::OpenGLES, // 2
+ Vulkan = 3, // 3
+ DirectX, // 4
+ RHI, // 5
};
Q_ENUM(API)
diff --git a/src/render/frontend/qrendersettings.h b/src/render/frontend/qrendersettings.h
index 5e63b1183..be4dd27de 100644
--- a/src/render/frontend/qrendersettings.h
+++ b/src/render/frontend/qrendersettings.h
@@ -43,9 +43,9 @@
#include <Qt3DCore/qcomponent.h>
#include <Qt3DRender/qt3drender_global.h>
#include <Qt3DRender/qpickingsettings.h>
+#include <QtGui/qtguiglobal.h>
QT_BEGIN_NAMESPACE
-
namespace Qt3DRender {
class QFrameGraphNode;
diff --git a/src/render/frontend/qrendertarget.cpp b/src/render/frontend/qrendertarget.cpp
index 4471b31fa..e359d9bdd 100644
--- a/src/render/frontend/qrendertarget.cpp
+++ b/src/render/frontend/qrendertarget.cpp
@@ -134,8 +134,9 @@ void QRenderTarget::removeOutput(QRenderTargetOutput *output)
{
Q_D(QRenderTarget);
+ if (!d->m_outputs.removeOne(output))
+ return;
d->update();
- d->m_outputs.removeOne(output);
// Remove bookkeeping connection
d->unregisterDestructionHelper(output);
}
diff --git a/src/render/frontend/render-frontend.pri b/src/render/frontend/render-frontend.pri
index 934672a01..02eb74ce1 100644
--- a/src/render/frontend/render-frontend.pri
+++ b/src/render/frontend/render-frontend.pri
@@ -28,6 +28,7 @@ HEADERS += \
$$PWD/qrenderpluginfactory_p.h \
$$PWD/qrenderpluginfactoryif_p.h \
$$PWD/qlevelofdetailboundingsphere.h \
+ $$PWD/qrenderapi.h \
$$PWD/qrendercapabilities.h \
$$PWD/qrendercapabilities_p.h
diff --git a/src/render/jobs/pickboundingvolumejob.cpp b/src/render/jobs/pickboundingvolumejob.cpp
index 96610f9f7..eebacc681 100644
--- a/src/render/jobs/pickboundingvolumejob.cpp
+++ b/src/render/jobs/pickboundingvolumejob.cpp
@@ -74,7 +74,7 @@ public:
PickBoundingVolumeJobPrivate(PickBoundingVolumeJob *q) : q_ptr(q) { }
~PickBoundingVolumeJobPrivate() override = default;
- bool isRequired() override;
+ bool isRequired() const override;
void postFrame(Qt3DCore::QAspectManager *manager) override;
enum CustomEventType {
@@ -94,9 +94,9 @@ public:
};
-bool PickBoundingVolumeJobPrivate::isRequired()
+bool PickBoundingVolumeJobPrivate::isRequired() const
{
- Q_Q(PickBoundingVolumeJob);
+ Q_Q(const PickBoundingVolumeJob);
return !q->m_pendingMouseEvents.isEmpty() || q->m_pickersDirty || q->m_oneEnabledAtLeast;
}
diff --git a/src/render/jobs/raycastingjob.cpp b/src/render/jobs/raycastingjob.cpp
index df01213f0..b7d4c4b7c 100644
--- a/src/render/jobs/raycastingjob.cpp
+++ b/src/render/jobs/raycastingjob.cpp
@@ -89,7 +89,7 @@ public:
RayCastingJobPrivate(RayCastingJob *q) : q_ptr(q) { }
~RayCastingJobPrivate() override { Q_ASSERT(dispatches.isEmpty()); }
- bool isRequired() override;
+ bool isRequired() const override;
void postFrame(Qt3DCore::QAspectManager *manager) override;
QVector<QPair<RayCaster *, QAbstractRayCaster::Hits>> dispatches;
@@ -99,9 +99,9 @@ public:
};
-bool RayCastingJobPrivate::isRequired()
+bool RayCastingJobPrivate::isRequired() const
{
- Q_Q(RayCastingJob);
+ Q_Q(const RayCastingJob);
return q->m_castersDirty || q->m_oneEnabledAtLeast;
}
diff --git a/src/render/jobs/updatelevelofdetailjob.cpp b/src/render/jobs/updatelevelofdetailjob.cpp
index 946d3bca8..d7d9c2f67 100644
--- a/src/render/jobs/updatelevelofdetailjob.cpp
+++ b/src/render/jobs/updatelevelofdetailjob.cpp
@@ -212,7 +212,7 @@ class UpdateLevelOfDetailJobPrivate : public Qt3DCore::QAspectJobPrivate
public:
UpdateLevelOfDetailJobPrivate(UpdateLevelOfDetailJob *q) : q_ptr(q) { }
- bool isRequired() override;
+ bool isRequired() const override;
void postFrame(Qt3DCore::QAspectManager *manager) override;
QVector<QPair<Qt3DCore::QNodeId, int>> m_updatedIndices;
@@ -266,9 +266,9 @@ void UpdateLevelOfDetailJob::run()
d->m_updatedIndices = visitor.updatedIndices();
}
-bool UpdateLevelOfDetailJobPrivate::isRequired()
+bool UpdateLevelOfDetailJobPrivate::isRequired() const
{
- Q_Q(UpdateLevelOfDetailJob);
+ Q_Q(const UpdateLevelOfDetailJob);
return q->m_manager->levelOfDetailManager()->count() > 0;
}
diff --git a/src/render/lights/qenvironmentlight.cpp b/src/render/lights/qenvironmentlight.cpp
index 52ee30cb2..143ee1978 100644
--- a/src/render/lights/qenvironmentlight.cpp
+++ b/src/render/lights/qenvironmentlight.cpp
@@ -42,6 +42,8 @@
#include "qabstracttexture.h"
#include <QVector3D>
+#include <cmath>
+
QT_BEGIN_NAMESPACE
namespace Qt3DRender
@@ -98,6 +100,9 @@ void QEnvironmentLightPrivate::_q_updateEnvMapsSize()
m_specular->height(),
m_specular->depth());
m_shaderData->setProperty("specularSize", QVariant::fromValue(specularSize));
+
+ const int levels = int(std::log2(specularSize.x() > 0.0f ? specularSize.x() : 1.0f)) + 1;
+ m_shaderData->setProperty("specularMipLevels", QVariant::fromValue(levels));
}
/*!
diff --git a/src/render/materialsystem/material.cpp b/src/render/materialsystem/material.cpp
index 6fda17ccd..62dc0958a 100644
--- a/src/render/materialsystem/material.cpp
+++ b/src/render/materialsystem/material.cpp
@@ -76,19 +76,23 @@ void Material::syncFromFrontEnd(const QNode *frontEnd, bool firstTime)
if (!node)
return;
+ AbstractRenderer::BackendNodeDirtySet dirty = firstTime ? AbstractRenderer::MaterialDirty : static_cast<AbstractRenderer::BackendNodeDirtyFlag>(0);
+
auto parameters = qIdsForNodes(node->parameters());
std::sort(std::begin(parameters), std::end(parameters));
- if (m_parameterPack.parameters() != parameters)
+ if (m_parameterPack.parameters() != parameters) {
m_parameterPack.setParameters(parameters);
+ dirty |= AbstractRenderer::AllDirty;
+ }
const auto effectId = node->effect() ? node->effect()->id() : QNodeId{};
- if (effectId != m_effectUuid)
+ if (effectId != m_effectUuid) {
m_effectUuid = effectId;
+ dirty |= AbstractRenderer::AllDirty;
+ }
- if (firstTime)
- markDirty(AbstractRenderer::MaterialDirty);
- else
- markDirty(AbstractRenderer::AllDirty);
+ if (dirty)
+ markDirty(dirty);
}
QVector<Qt3DCore::QNodeId> Material::parameters() const
diff --git a/src/render/materialsystem/prototypes/default.json b/src/render/materialsystem/prototypes/default.json
index 597de41c3..dfa51898f 100644
--- a/src/render/materialsystem/prototypes/default.json
+++ b/src/render/materialsystem/prototypes/default.json
@@ -32,6 +32,15 @@
},
"substitution": "$type $value = $name;",
"headerSnippets": [ "$qualifier $type $name;" ]
+ },
+ {
+ "format": {
+ "api": "RHI",
+ "major": 1,
+ "minor": 0
+ },
+ "substitution": "$type $value = $name;",
+ "headerSnippets": [ "add-input $qualifier $type $name" ]
}
]
},
@@ -62,6 +71,14 @@
"minor": 0
},
"substitution": "$type $value = $type($constant);"
+ },
+ {
+ "format": {
+ "api": "RHI",
+ "major": 1,
+ "minor": 0
+ },
+ "substitution": "$type $value = $type($constant);"
}
]
},
@@ -102,6 +119,15 @@
},
"substitution": "vec4 $color = texture($name, $coord);",
"headerSnippets": [ "uniform sampler2D $name;" ]
+ },
+ {
+ "format": {
+ "api": "RHI",
+ "major": 1,
+ "minor": 0
+ },
+ "substitution": "vec4 $color = texture($name, $coord);",
+ "headerSnippets": [ "add-sampler sampler2D $name" ]
}
]
},
@@ -135,6 +161,15 @@
},
"substitution": "fragColor = $fragColor;",
"headerSnippets": [ "out vec4 fragColor;" ]
+ },
+ {
+ "format": {
+ "api": "RHI",
+ "major": 1,
+ "minor": 0
+ },
+ "substitution": "fragColor = $fragColor;",
+ "headerSnippets": [ "layout(location = 0) out vec4 fragColor;" ]
}
]
},
@@ -160,6 +195,15 @@
},
"substitution": "vec3 $eyePosition = eyePosition;",
"headerSnippets": [ "uniform vec3 eyePosition;" ]
+ },
+ {
+ "format": {
+ "api": "RHI",
+ "major": 1,
+ "minor": 0
+ },
+ "substitution": "vec3 $eyePosition = eyePosition;",
+ "headerSnippets": [ ]
}
]
},
@@ -185,6 +229,15 @@
},
"substitution": "float $time = time;",
"headerSnippets": [ "uniform float time;" ]
+ },
+ {
+ "format": {
+ "api": "RHI",
+ "major": 1,
+ "minor": 0
+ },
+ "substitution": "float $time = time;",
+ "headerSnippets": [ "add-uniform float time" ]
}
]
},
@@ -217,6 +270,14 @@
"minor": 0
},
"substitution": "$type $output = transpose($input);"
+ },
+ {
+ "format": {
+ "api": "RHI",
+ "major": 1,
+ "minor": 0
+ },
+ "substitution": "$type $output = transpose($input);"
}
]
},
@@ -249,6 +310,14 @@
"minor": 0
},
"substitution": "$type $output = normalize($input);"
+ },
+ {
+ "format": {
+ "api": "RHI",
+ "major": 1,
+ "minor": 0
+ },
+ "substitution": "$type $output = normalize($input);"
}
]
},
@@ -282,6 +351,14 @@
"minor": 0
},
"substitution": "$type $difference = $minuend - $subtrahend;"
+ },
+ {
+ "format": {
+ "api": "RHI",
+ "major": 1,
+ "minor": 0
+ },
+ "substitution": "$type $difference = $minuend - $subtrahend;"
}
]
},
@@ -315,6 +392,14 @@
"minor": 0
},
"substitution": "$type $sum = $first + $second;"
+ },
+ {
+ "format": {
+ "api": "RHI",
+ "major": 1,
+ "minor": 0
+ },
+ "substitution": "$type $sum = $first + $second;"
}
]
},
@@ -348,6 +433,14 @@
"minor": 0
},
"substitution": "$type $product = $first * $second;"
+ },
+ {
+ "format": {
+ "api": "RHI",
+ "major": 1,
+ "minor": 0
+ },
+ "substitution": "$type $product = $first * $second;"
}
]
},
@@ -381,6 +474,14 @@
"minor": 0
},
"substitution": "$type $output = $input.$fields;"
+ },
+ {
+ "format": {
+ "api": "RHI",
+ "major": 1,
+ "minor": 0
+ },
+ "substitution": "$type $output = $input.$fields;"
}
]
},
@@ -419,6 +520,15 @@
},
"substitution": "mat3 $matrix = calcWorldSpaceToTangentSpaceMatrix($worldNormal, $worldTangent);",
"headerSnippets": [ "#pragma include :/shaders/gl3/coordinatesystems.inc" ]
+ },
+ {
+ "format": {
+ "api": "RHI",
+ "major": 1,
+ "minor": 0
+ },
+ "substitution": "mat3 $matrix = calcWorldSpaceToTangentSpaceMatrix($worldNormal, $worldTangent);",
+ "headerSnippets": [ "#pragma include :/shaders/rhi/coordinatesystems.inc" ]
}
]
},
@@ -453,6 +563,15 @@
},
"substitution": "vec4 $outputColor = phongFunction($ambient, $diffuse, $specular, $shininess, $worldPosition, $worldView, $worldNormal);",
"headerSnippets": [ "#pragma include :/shaders/gl3/phong.inc.frag" ]
+ },
+ {
+ "format": {
+ "api": "RHI",
+ "major": 1,
+ "minor": 0
+ },
+ "substitution": "vec4 $outputColor = phongFunction($ambient, $diffuse, $specular, $shininess, $worldPosition, $worldView, $worldNormal);",
+ "headerSnippets": [ "#pragma include :/shaders/rhi/phong.inc.frag" ]
}
]
},
@@ -487,6 +606,15 @@
},
"substitution": "vec4 $outputColor = metalRoughFunction($baseColor, $metalness, $roughness, $ambientOcclusion, $worldPosition, $worldView, $worldNormal);",
"headerSnippets": [ "#pragma include :/shaders/gl3/metalrough.inc.frag" ]
+ },
+ {
+ "format": {
+ "api": "RHI",
+ "major": 1,
+ "minor": 0
+ },
+ "substitution": "vec4 $outputColor = metalRoughFunction($baseColor, $metalness, $roughness, $ambientOcclusion, $worldPosition, $worldView, $worldNormal);",
+ "headerSnippets": [ "#pragma include :/shaders/rhi/metalrough.inc.frag" ]
}
]
},
@@ -520,6 +648,14 @@
"minor": 0
},
"substitution": "$type $output = $type($first, $second);"
+ },
+ {
+ "format": {
+ "api": "RHI",
+ "major": 1,
+ "minor": 0
+ },
+ "substitution": "$type $output = $type($first, $second);"
}
]
},
@@ -554,6 +690,14 @@
"minor": 0
},
"substitution": "$type $output = $type($first, $second, $third);"
+ },
+ {
+ "format": {
+ "api": "RHI",
+ "major": 1,
+ "minor": 0
+ },
+ "substitution": "$type $output = $type($first, $second, $third);"
}
]
},
@@ -589,6 +733,14 @@
"minor": 0
},
"substitution": "$type $output = $type($first, $second, $third, $fourth);"
+ },
+ {
+ "format": {
+ "api": "RHI",
+ "major": 1,
+ "minor": 0
+ },
+ "substitution": "$type $output = $type($first, $second, $third, $fourth);"
}
]
},
@@ -621,6 +773,14 @@
"minor": 0
},
"substitution": "$type $output = $type($input);"
+ },
+ {
+ "format": {
+ "api": "RHI",
+ "major": 1,
+ "minor": 0
+ },
+ "substitution": "$type $output = $type($input);"
}
]
}
diff --git a/src/render/materialsystem/qeffect.cpp b/src/render/materialsystem/qeffect.cpp
index ad3dde8f7..0e950cee4 100644
--- a/src/render/materialsystem/qeffect.cpp
+++ b/src/render/materialsystem/qeffect.cpp
@@ -203,7 +203,8 @@ void QEffect::removeParameter(QParameter *parameter)
{
Q_D(QEffect);
- d->m_parameters.removeOne(parameter);
+ if (!d->m_parameters.removeOne(parameter))
+ return;
// Remove bookkeeping connection
d->unregisterDestructionHelper(parameter);
d->update();
@@ -248,8 +249,9 @@ void QEffect::addTechnique(QTechnique *t)
void QEffect::removeTechnique(QTechnique *t)
{
Q_D(QEffect);
+ if (!d->m_techniques.removeOne(t))
+ return;
d->update();
- d->m_techniques.removeOne(t);
// Remove bookkeeping connection
d->unregisterDestructionHelper(t);
}
diff --git a/src/render/materialsystem/qgraphicsapifilter.cpp b/src/render/materialsystem/qgraphicsapifilter.cpp
index 9b5557930..e8b93de6e 100644
--- a/src/render/materialsystem/qgraphicsapifilter.cpp
+++ b/src/render/materialsystem/qgraphicsapifilter.cpp
@@ -53,6 +53,28 @@ GraphicsApiFilterData::GraphicsApiFilterData()
, m_major(0)
{}
+QString GraphicsApiFilterData::toString() const
+{
+ QLatin1String api;
+ switch (m_api) {
+ case QGraphicsApiFilter::OpenGL: api = QLatin1String("OpenGL"); break;
+ case QGraphicsApiFilter::OpenGLES: api = QLatin1String("OpenGL"); break;
+ case QGraphicsApiFilter::Vulkan: api = QLatin1String("Vulkan"); break;
+ case QGraphicsApiFilter::DirectX: api = QLatin1String("DirectX"); break;
+ case QGraphicsApiFilter::RHI: api = QLatin1String("RHI"); break;
+ default: Q_UNREACHABLE();
+ }
+
+ QLatin1String profile;
+ switch (m_profile) {
+ case QGraphicsApiFilter::CoreProfile: profile = QLatin1String(" (Core Profile)"); break;
+ case QGraphicsApiFilter::CompatibilityProfile: profile = QLatin1String(" (Compatibility Profile)"); break;
+ default: break;
+ }
+
+ return QString(QLatin1String("%1 %2.%3%4 (%5)").arg(api, QString::number(m_major), QString::number(m_minor), profile, m_vendor));
+}
+
bool GraphicsApiFilterData::operator ==(const GraphicsApiFilterData &other) const
{
// Check API
diff --git a/src/render/materialsystem/qgraphicsapifilter.h b/src/render/materialsystem/qgraphicsapifilter.h
index 337193673..80cfe026e 100644
--- a/src/render/materialsystem/qgraphicsapifilter.h
+++ b/src/render/materialsystem/qgraphicsapifilter.h
@@ -67,7 +67,8 @@ public:
OpenGLES = QSurfaceFormat::OpenGLES, // 2
OpenGL = QSurfaceFormat::OpenGL, // 1
Vulkan = 3, // 3
- DirectX // 4
+ DirectX, // 4
+ RHI, // 5
};
Q_ENUM(Api) // LCOV_EXCL_LINE
diff --git a/src/render/materialsystem/qgraphicsapifilter_p.h b/src/render/materialsystem/qgraphicsapifilter_p.h
index 435451c27..52c489785 100644
--- a/src/render/materialsystem/qgraphicsapifilter_p.h
+++ b/src/render/materialsystem/qgraphicsapifilter_p.h
@@ -70,6 +70,8 @@ struct Q_3DRENDERSHARED_PRIVATE_EXPORT GraphicsApiFilterData
QStringList m_extensions;
QString m_vendor;
+ QString toString() const;
+
bool operator ==(const GraphicsApiFilterData &other) const;
bool operator !=(const GraphicsApiFilterData &other) const;
bool operator <(const GraphicsApiFilterData &other) const;
diff --git a/src/render/materialsystem/qmaterial.cpp b/src/render/materialsystem/qmaterial.cpp
index 9026e55f1..50fcd59be 100644
--- a/src/render/materialsystem/qmaterial.cpp
+++ b/src/render/materialsystem/qmaterial.cpp
@@ -283,8 +283,9 @@ void QMaterial::removeParameter(QParameter *parameter)
{
Q_ASSERT(parameter);
Q_D(QMaterial);
+ if (!d->m_parameters.removeOne(parameter))
+ return;
d->update();
- d->m_parameters.removeOne(parameter);
}
/*!
diff --git a/src/render/materialsystem/qrenderpass.cpp b/src/render/materialsystem/qrenderpass.cpp
index 83a6e8255..40c9cb1cd 100644
--- a/src/render/materialsystem/qrenderpass.cpp
+++ b/src/render/materialsystem/qrenderpass.cpp
@@ -285,8 +285,9 @@ void QRenderPass::removeFilterKey(QFilterKey *filterKey)
{
Q_ASSERT(filterKey);
Q_D(QRenderPass);
+ if (!d->m_filterKeyList.removeOne(filterKey))
+ return;
d->update();
- d->m_filterKeyList.removeOne(filterKey);
// Remove bookkeeping connection
d->unregisterDestructionHelper(filterKey);
}
@@ -333,8 +334,9 @@ void QRenderPass::removeRenderState(QRenderState *state)
{
Q_ASSERT(state);
Q_D(QRenderPass);
+ if (!d->m_renderStates.removeOne(state))
+ return;
d->update();
- d->m_renderStates.removeOne(state);
// Remove bookkeeping connection
d->unregisterDestructionHelper(state);
}
@@ -380,8 +382,9 @@ void QRenderPass::removeParameter(QParameter *parameter)
{
Q_ASSERT(parameter);
Q_D(QRenderPass);
+ if (!d->m_parameters.removeOne(parameter))
+ return;
d->update();
- d->m_parameters.removeOne(parameter);
// Remove bookkeeping connection
d->unregisterDestructionHelper(parameter);
}
diff --git a/src/render/materialsystem/qtechnique.cpp b/src/render/materialsystem/qtechnique.cpp
index 088716fb9..eca8b88c6 100644
--- a/src/render/materialsystem/qtechnique.cpp
+++ b/src/render/materialsystem/qtechnique.cpp
@@ -269,8 +269,9 @@ void QTechnique::removeFilterKey(QFilterKey *filterKey)
{
Q_ASSERT(filterKey);
Q_D(QTechnique);
+ if (!d->m_filterKeys.removeOne(filterKey))
+ return;
d->update();
- d->m_filterKeys.removeOne(filterKey);
// Remove bookkeeping connection
d->unregisterDestructionHelper(filterKey);
}
@@ -316,8 +317,9 @@ void QTechnique::removeParameter(QParameter *parameter)
{
Q_ASSERT(parameter);
Q_D(QTechnique);
+ if (!d->m_parameters.removeOne(parameter))
+ return;
d->update();
- d->m_parameters.removeOne(parameter);
// Remove bookkeeping connection
d->unregisterDestructionHelper(parameter);
}
@@ -353,8 +355,9 @@ void QTechnique::removeRenderPass(QRenderPass *pass)
{
Q_ASSERT(pass);
Q_D(QTechnique);
+ if (!d->m_renderPasses.removeOne(pass))
+ return;
d->update();
- d->m_renderPasses.removeOne(pass);
// Remove bookkeeping connection
d->unregisterDestructionHelper(pass);
}
diff --git a/src/render/materialsystem/shader.cpp b/src/render/materialsystem/shader.cpp
index be7d0f1ea..ebdb4c64b 100644
--- a/src/render/materialsystem/shader.cpp
+++ b/src/render/materialsystem/shader.cpp
@@ -73,6 +73,7 @@ const int Shader::modelNormalMatrixNameId = StringToInt::lookupId(QLatin1String(
const int Shader::modelViewNormalNameId = StringToInt::lookupId(QLatin1String("modelViewNormal"));
const int Shader::viewportMatrixNameId = StringToInt::lookupId(QLatin1String("viewportMatrix"));
const int Shader::inverseViewportMatrixNameId = StringToInt::lookupId(QLatin1String("inverseViewportMatrix"));
+const int Shader::textureTransformMatrixNameId = StringToInt::lookupId(QLatin1String("textureTransformMatrix"));
const int Shader::aspectRatioNameId = StringToInt::lookupId(QLatin1String("aspectRatio"));
const int Shader::exposureNameId = StringToInt::lookupId(QLatin1String("exposure"));
const int Shader::gammaNameId = StringToInt::lookupId(QLatin1String("gamma"));
@@ -100,6 +101,7 @@ void Shader::cleanup()
m_status = QShaderProgram::NotReady;
m_format = QShaderProgram::GLSL;
m_log.clear();
+ m_requiresFrontendSync = false;
m_dirty = false;
}
diff --git a/src/render/materialsystem/shader_p.h b/src/render/materialsystem/shader_p.h
index 31603bcf7..0ac3227b7 100644
--- a/src/render/materialsystem/shader_p.h
+++ b/src/render/materialsystem/shader_p.h
@@ -86,6 +86,7 @@ public:
static const int modelViewNormalNameId;
static const int viewportMatrixNameId;
static const int inverseViewportMatrixNameId;
+ static const int textureTransformMatrixNameId;
static const int aspectRatioNameId;
static const int exposureNameId;
static const int gammaNameId;
diff --git a/src/render/materialsystem/shaderbuilder.cpp b/src/render/materialsystem/shaderbuilder.cpp
index 871b4cb4c..c7a2aa347 100644
--- a/src/render/materialsystem/shaderbuilder.cpp
+++ b/src/render/materialsystem/shaderbuilder.cpp
@@ -44,10 +44,10 @@
#include <Qt3DRender/private/qshaderprogram_p.h>
#include <Qt3DCore/private/qurlhelper_p.h>
-#include <QtGui/private/qshaderformat_p.h>
-#include <QtGui/private/qshadergraphloader_p.h>
-#include <QtGui/private/qshadergenerator_p.h>
-#include <QtGui/private/qshadernodesloader_p.h>
+#include <Qt3DRender/private/qshaderformat_p.h>
+#include <Qt3DRender/private/qshadergraphloader_p.h>
+#include <Qt3DRender/private/qshadergenerator_p.h>
+#include <Qt3DRender/private/qshadernodesloader_p.h>
#include <QFile>
#include <QFileInfo>
@@ -82,7 +82,7 @@ public:
load();
}
- QHash<QString, QShaderNode> prototypes() const
+ QHash<QString, Qt3DRender::QShaderNode> prototypes() const
{
return m_prototypes;
}
@@ -96,14 +96,14 @@ private:
return;
}
- QShaderNodesLoader loader;
+ Qt3DRender::QShaderNodesLoader loader;
loader.setDevice(&file);
loader.load();
m_prototypes = loader.nodes();
}
QString m_fileName;
- QHash<QString, QShaderNode> m_prototypes;
+ QHash<QString, Qt3DRender::QShaderNode> m_prototypes;
};
Q_GLOBAL_STATIC(GlobalShaderPrototypes, qt3dGlobalShaderPrototypes)
@@ -232,6 +232,8 @@ void ShaderBuilder::generateCode(QShaderProgram::ShaderType type)
auto format = QShaderFormat();
format.setApi(m_graphicsApi.m_api == QGraphicsApiFilter::OpenGLES ? QShaderFormat::OpenGLES
+ : m_graphicsApi.m_api == QGraphicsApiFilter::Vulkan ? QShaderFormat::VulkanFlavoredGLSL
+ : m_graphicsApi.m_api == QGraphicsApiFilter::RHI ? QShaderFormat::RHI
: m_graphicsApi.m_profile == QGraphicsApiFilter::CoreProfile ? QShaderFormat::OpenGLCoreProfile
: m_graphicsApi.m_profile == QGraphicsApiFilter::CompatibilityProfile ? QShaderFormat::OpenGLCompatibilityProfile
: QShaderFormat::OpenGLNoProfile);
@@ -244,7 +246,8 @@ void ShaderBuilder::generateCode(QShaderProgram::ShaderType type)
generator.graph = graph;
const auto code = generator.createShaderCode(m_enabledLayers);
- m_codes.insert(type, QShaderProgramPrivate::deincludify(code, graphPath + QStringLiteral(".glsl")));
+ const auto deincludified = QShaderProgramPrivate::deincludify(code, graphPath + QStringLiteral(".glsl"));
+ m_codes.insert(type, deincludified);
m_dirtyTypes.remove(type);
m_pendingUpdates.push_back({ peerId(),
@@ -276,7 +279,7 @@ void ShaderBuilder::syncFromFrontEnd(const QNode *frontEnd, bool firstTime)
markDirty(AbstractRenderer::ShadersDirty);
}
- static const QVector<std::pair<QShaderProgram::ShaderType, QUrl (QShaderProgramBuilder::*)() const>> shaderTypesToGetters = {
+ static const QVarLengthArray<std::pair<QShaderProgram::ShaderType, QUrl (QShaderProgramBuilder::*)() const>, 6> shaderTypesToGetters {
{QShaderProgram::Vertex, &QShaderProgramBuilder::vertexShaderGraph},
{QShaderProgram::TessellationControl, &QShaderProgramBuilder::tessellationControlShaderGraph},
{QShaderProgram::TessellationEvaluation, &QShaderProgramBuilder::tessellationEvaluationShaderGraph},
diff --git a/src/render/materialsystem/shaderdata.cpp b/src/render/materialsystem/shaderdata.cpp
index 44a41a240..6f113d592 100644
--- a/src/render/materialsystem/shaderdata.cpp
+++ b/src/render/materialsystem/shaderdata.cpp
@@ -160,7 +160,7 @@ ShaderData *ShaderData::lookupResource(QNodeId id)
}
// RenderCommand updater jobs
-QVariant ShaderData::getTransformedProperty(const QString &name, const Matrix4x4 &viewMatrix)
+QVariant ShaderData::getTransformedProperty(const QString &name, const Matrix4x4 &viewMatrix) const noexcept
{
// Note protecting m_worldMatrix at this point as we assume all world updates
// have been performed when reaching this point
diff --git a/src/render/materialsystem/shaderdata_p.h b/src/render/materialsystem/shaderdata_p.h
index 6fe811566..a1491b328 100644
--- a/src/render/materialsystem/shaderdata_p.h
+++ b/src/render/materialsystem/shaderdata_p.h
@@ -89,7 +89,7 @@ public:
// Called by FramePreparationJob
void updateWorldTransform(const Matrix4x4 &worldMatrix);
- QVariant getTransformedProperty(const QString &name, const Matrix4x4 &viewMatrix);
+ QVariant getTransformedProperty(const QString &name, const Matrix4x4 &viewMatrix) const noexcept;
// Unit tests purposes only
TransformType propertyTransformType(const QString &name) const;
diff --git a/src/render/picking/qabstractraycaster.cpp b/src/render/picking/qabstractraycaster.cpp
index 535c06f27..18658a858 100644
--- a/src/render/picking/qabstractraycaster.cpp
+++ b/src/render/picking/qabstractraycaster.cpp
@@ -345,8 +345,9 @@ void QAbstractRayCaster::removeLayer(QLayer *layer)
{
Q_ASSERT(layer);
Q_D(QAbstractRayCaster);
+ if (!d->m_layers.removeOne(layer))
+ return;
d->update();
- d->m_layers.removeOne(layer);
// Remove bookkeeping connection
d->unregisterDestructionHelper(layer);
}
diff --git a/src/render/render.pro b/src/render/render.pro
index 2b81e8a06..4ec58c992 100644
--- a/src/render/render.pro
+++ b/src/render/render.pro
@@ -17,7 +17,9 @@ include (io/io.pri)
include (picking/picking.pri)
include (raycasting/raycasting.pri)
include (services/services.pri)
+include (shadergraph/shadergraph.pri)
include (texture/texture.pri)
+include (surfaces/surfaces.pri)
gcov {
QMAKE_CXXFLAGS += -fprofile-arcs -ftest-coverage
diff --git a/src/render/renderstates/qblendequationarguments.h b/src/render/renderstates/qblendequationarguments.h
index 878534816..b8b0d5ff1 100644
--- a/src/render/renderstates/qblendequationarguments.h
+++ b/src/render/renderstates/qblendequationarguments.h
@@ -66,8 +66,8 @@ public:
One = 1,
SourceColor = 0x0300,
SourceAlpha = 0x0302,
- Source1Alpha,
- Source1Color,
+ Source1Alpha, // ### Qt 6: Fix -> has same value as OneMinusSourceAlpha
+ Source1Color, // ### Qt 6: Fix -> has same value as DestinationAlpha
DestinationColor = 0x0306,
DestinationAlpha = 0x0304,
SourceAlphaSaturate = 0x0308,
diff --git a/src/render/renderstates/renderstateset.cpp b/src/render/renderstates/renderstateset.cpp
index 6b66d2dd7..5b9c4e8b5 100644
--- a/src/render/renderstates/renderstateset.cpp
+++ b/src/render/renderstates/renderstateset.cpp
@@ -102,7 +102,7 @@ StateMaskSet RenderStateSet::stateMask() const
// This modifies our state to add states from others
// if we don't already contain a state with that type set
-void RenderStateSet::merge(RenderStateSet *other)
+void RenderStateSet::merge(const RenderStateSet *other)
{
m_stateMask |= other->stateMask();
const QVector<StateVariant> otherStates = other->states();
diff --git a/src/render/renderstates/renderstateset_p.h b/src/render/renderstates/renderstateset_p.h
index 119f1edca..667c2614d 100644
--- a/src/render/renderstates/renderstateset_p.h
+++ b/src/render/renderstates/renderstateset_p.h
@@ -89,9 +89,10 @@ public:
int changeCost(RenderStateSet* previousState);
StateMaskSet stateMask() const;
- void merge(RenderStateSet *other);
+ void merge(const RenderStateSet *other);
- QVector<StateVariant> states() const { return m_states; }
+ const QVector<StateVariant>& states() const noexcept { return m_states; }
+ QVector<StateVariant>& states() noexcept { return m_states; }
bool canAddStateOfType(StateMask type) const;
diff --git a/src/render/shadergraph/qshaderformat.cpp b/src/render/shadergraph/qshaderformat.cpp
new file mode 100644
index 000000000..98643fb24
--- /dev/null
+++ b/src/render/shadergraph/qshaderformat.cpp
@@ -0,0 +1,146 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtGui 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 "qshaderformat_p.h"
+
+QT_BEGIN_NAMESPACE
+namespace Qt3DRender
+{
+QShaderFormat::QShaderFormat() noexcept
+ : m_api(NoApi)
+ , m_shaderType(Fragment)
+{
+}
+
+QShaderFormat::Api QShaderFormat::api() const noexcept
+{
+ return m_api;
+}
+
+void QShaderFormat::setApi(QShaderFormat::Api api) noexcept
+{
+ m_api = api;
+}
+
+QVersionNumber QShaderFormat::version() const noexcept
+{
+ return m_version;
+}
+
+void QShaderFormat::setVersion(const QVersionNumber &version) noexcept
+{
+ m_version = version;
+}
+
+QStringList QShaderFormat::extensions() const noexcept
+{
+ return m_extensions;
+}
+
+void QShaderFormat::setExtensions(const QStringList &extensions) noexcept
+{
+ m_extensions = extensions;
+ m_extensions.sort();
+}
+
+QString QShaderFormat::vendor() const noexcept
+{
+ return m_vendor;
+}
+
+void QShaderFormat::setVendor(const QString &vendor) noexcept
+{
+ m_vendor = vendor;
+}
+
+bool QShaderFormat::isValid() const noexcept
+{
+ return m_api != NoApi && m_version.majorVersion() > 0;
+}
+
+bool QShaderFormat::supports(const QShaderFormat &other) const noexcept
+{
+ if (!isValid() || !other.isValid())
+ return false;
+
+ if (m_api == OpenGLES && m_api != other.m_api)
+ return false;
+
+ if (m_api == OpenGLCoreProfile && m_api != other.m_api)
+ return false;
+
+ if (m_version < other.m_version)
+ return false;
+
+ if (m_shaderType != other.m_shaderType)
+ return false;
+
+ const auto containsAllExtensionsFromOther = std::includes(m_extensions.constBegin(),
+ m_extensions.constEnd(),
+ other.m_extensions.constBegin(),
+ other.m_extensions.constEnd());
+ if (!containsAllExtensionsFromOther)
+ return false;
+
+ if (!other.m_vendor.isEmpty() && m_vendor != other.m_vendor)
+ return false;
+
+ return true;
+}
+
+QShaderFormat::ShaderType QShaderFormat::shaderType() const Q_DECL_NOTHROW
+{
+ return m_shaderType;
+}
+
+void QShaderFormat::setShaderType(QShaderFormat::ShaderType shaderType) Q_DECL_NOTHROW
+{
+ m_shaderType = shaderType;
+}
+
+bool operator==(const QShaderFormat &lhs, const QShaderFormat &rhs) noexcept
+{
+ return lhs.api() == rhs.api()
+ && lhs.version() == rhs.version()
+ && lhs.extensions() == rhs.extensions()
+ && lhs.vendor() == rhs.vendor()
+ && lhs.shaderType() == rhs.shaderType();
+}
+}
+QT_END_NAMESPACE
diff --git a/src/render/shadergraph/qshaderformat_p.h b/src/render/shadergraph/qshaderformat_p.h
new file mode 100644
index 000000000..ad9898bd0
--- /dev/null
+++ b/src/render/shadergraph/qshaderformat_p.h
@@ -0,0 +1,126 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtGui 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_QSHADERFORMAT_P_H
+#define QT3DRENDER_QSHADERFORMAT_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <Qt3DRender/private/qt3drender_global_p.h>
+
+#include <QtCore/qstringlist.h>
+#include <QtCore/qversionnumber.h>
+
+QT_BEGIN_NAMESPACE
+namespace Qt3DRender
+{
+class QShaderFormat
+{
+public:
+ enum Api : int {
+ NoApi,
+ OpenGLNoProfile,
+ OpenGLCoreProfile,
+ OpenGLCompatibilityProfile,
+ OpenGLES,
+ VulkanFlavoredGLSL,
+ RHI
+ };
+
+ enum ShaderType : int {
+ Vertex = 0,
+ TessellationControl,
+ TessellationEvaluation,
+ Geometry,
+ Fragment,
+ Compute
+ };
+
+ Q_3DRENDERSHARED_PRIVATE_EXPORT QShaderFormat() noexcept;
+
+ Q_3DRENDERSHARED_PRIVATE_EXPORT Api api() const noexcept;
+ Q_3DRENDERSHARED_PRIVATE_EXPORT void setApi(Api api) noexcept;
+
+ Q_3DRENDERSHARED_PRIVATE_EXPORT QVersionNumber version() const noexcept;
+ Q_3DRENDERSHARED_PRIVATE_EXPORT void setVersion(const QVersionNumber &version) noexcept;
+
+ Q_3DRENDERSHARED_PRIVATE_EXPORT QStringList extensions() const noexcept;
+ Q_3DRENDERSHARED_PRIVATE_EXPORT void setExtensions(const QStringList &extensions) noexcept;
+
+ Q_3DRENDERSHARED_PRIVATE_EXPORT QString vendor() const noexcept;
+ Q_3DRENDERSHARED_PRIVATE_EXPORT void setVendor(const QString &vendor) noexcept;
+
+ Q_3DRENDERSHARED_PRIVATE_EXPORT bool isValid() const noexcept;
+ Q_3DRENDERSHARED_PRIVATE_EXPORT bool supports(const QShaderFormat &other) const noexcept;
+
+ Q_3DRENDERSHARED_PRIVATE_EXPORT ShaderType shaderType() const Q_DECL_NOTHROW;
+ Q_3DRENDERSHARED_PRIVATE_EXPORT void setShaderType(ShaderType shaderType) Q_DECL_NOTHROW;
+
+private:
+ Api m_api;
+ QVersionNumber m_version;
+ QStringList m_extensions;
+ QString m_vendor;
+ ShaderType m_shaderType;
+};
+
+Q_3DRENDERSHARED_PRIVATE_EXPORT bool operator==(const QShaderFormat &lhs, const QShaderFormat &rhs) noexcept;
+
+inline bool operator!=(const QShaderFormat &lhs, const QShaderFormat &rhs) noexcept
+{
+ return !(lhs == rhs);
+}
+
+
+}
+Q_DECLARE_TYPEINFO(Qt3DRender::QShaderFormat, Q_MOVABLE_TYPE);
+QT_END_NAMESPACE
+
+Q_DECLARE_METATYPE(Qt3DRender::QShaderFormat)
+
+#endif // QT3DRENDER_QSHADERFORMAT_P_H
diff --git a/src/render/shadergraph/qshadergenerator.cpp b/src/render/shadergraph/qshadergenerator.cpp
new file mode 100644
index 000000000..92c69be72
--- /dev/null
+++ b/src/render/shadergraph/qshadergenerator.cpp
@@ -0,0 +1,861 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtGui 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 "qshadergenerator_p.h"
+
+#include "qshaderlanguage_p.h"
+#include <QRegularExpression>
+
+#include <cctype>
+#include <qshaderprogram_p.h>
+
+QT_BEGIN_NAMESPACE
+namespace Qt3DRender {
+Q_LOGGING_CATEGORY(ShaderGenerator, "ShaderGenerator", QtWarningMsg)
+
+namespace
+{
+ QByteArray toGlsl(QShaderLanguage::StorageQualifier qualifier, const QShaderFormat &format) noexcept
+ {
+ if (format.version().majorVersion() <= 2 && format.api() != QShaderFormat::RHI) {
+ // Note we're assuming fragment shader only here, it'd be different
+ // values for vertex shader, will need to be fixed properly at some
+ // point but isn't necessary yet (this problem already exists in past
+ // commits anyway)
+ switch (qualifier) {
+ case QShaderLanguage::Const:
+ return "const";
+ case QShaderLanguage::Input:
+ if (format.shaderType() == QShaderFormat::Vertex)
+ return "attribute";
+ else
+ return "varying";
+ case QShaderLanguage::Output:
+ return ""; // Although fragment shaders for <=2 only have fixed outputs
+ case QShaderLanguage::Uniform:
+ return "uniform";
+ case QShaderLanguage::BuiltIn:
+ return "//";
+ }
+ } else {
+ switch (qualifier) {
+ case QShaderLanguage::Const:
+ return "const";
+ case QShaderLanguage::Input:
+ return "in";
+ case QShaderLanguage::Output:
+ return "out";
+ case QShaderLanguage::Uniform:
+ return "uniform";
+ case QShaderLanguage::BuiltIn:
+ return "//";
+ }
+ }
+
+ Q_UNREACHABLE();
+ }
+
+ QByteArray toGlsl(QShaderLanguage::VariableType type) noexcept
+ {
+ switch (type) {
+ case QShaderLanguage::Bool:
+ return "bool";
+ case QShaderLanguage::Int:
+ return "int";
+ case QShaderLanguage::Uint:
+ return "uint";
+ case QShaderLanguage::Float:
+ return "float";
+ case QShaderLanguage::Double:
+ return "double";
+ case QShaderLanguage::Vec2:
+ return "vec2";
+ case QShaderLanguage::Vec3:
+ return "vec3";
+ case QShaderLanguage::Vec4:
+ return "vec4";
+ case QShaderLanguage::DVec2:
+ return "dvec2";
+ case QShaderLanguage::DVec3:
+ return "dvec3";
+ case QShaderLanguage::DVec4:
+ return "dvec4";
+ case QShaderLanguage::BVec2:
+ return "bvec2";
+ case QShaderLanguage::BVec3:
+ return "bvec3";
+ case QShaderLanguage::BVec4:
+ return "bvec4";
+ case QShaderLanguage::IVec2:
+ return "ivec2";
+ case QShaderLanguage::IVec3:
+ return "ivec3";
+ case QShaderLanguage::IVec4:
+ return "ivec4";
+ case QShaderLanguage::UVec2:
+ return "uvec2";
+ case QShaderLanguage::UVec3:
+ return "uvec3";
+ case QShaderLanguage::UVec4:
+ return "uvec4";
+ case QShaderLanguage::Mat2:
+ return "mat2";
+ case QShaderLanguage::Mat3:
+ return "mat3";
+ case QShaderLanguage::Mat4:
+ return "mat4";
+ case QShaderLanguage::Mat2x2:
+ return "mat2x2";
+ case QShaderLanguage::Mat2x3:
+ return "mat2x3";
+ case QShaderLanguage::Mat2x4:
+ return "mat2x4";
+ case QShaderLanguage::Mat3x2:
+ return "mat3x2";
+ case QShaderLanguage::Mat3x3:
+ return "mat3x3";
+ case QShaderLanguage::Mat3x4:
+ return "mat3x4";
+ case QShaderLanguage::Mat4x2:
+ return "mat4x2";
+ case QShaderLanguage::Mat4x3:
+ return "mat4x3";
+ case QShaderLanguage::Mat4x4:
+ return "mat4x4";
+ case QShaderLanguage::DMat2:
+ return "dmat2";
+ case QShaderLanguage::DMat3:
+ return "dmat3";
+ case QShaderLanguage::DMat4:
+ return "dmat4";
+ case QShaderLanguage::DMat2x2:
+ return "dmat2x2";
+ case QShaderLanguage::DMat2x3:
+ return "dmat2x3";
+ case QShaderLanguage::DMat2x4:
+ return "dmat2x4";
+ case QShaderLanguage::DMat3x2:
+ return "dmat3x2";
+ case QShaderLanguage::DMat3x3:
+ return "dmat3x3";
+ case QShaderLanguage::DMat3x4:
+ return "dmat3x4";
+ case QShaderLanguage::DMat4x2:
+ return "dmat4x2";
+ case QShaderLanguage::DMat4x3:
+ return "dmat4x3";
+ case QShaderLanguage::DMat4x4:
+ return "dmat4x4";
+ case QShaderLanguage::Sampler1D:
+ return "sampler1D";
+ case QShaderLanguage::Sampler2D:
+ return "sampler2D";
+ case QShaderLanguage::Sampler3D:
+ return "sampler3D";
+ case QShaderLanguage::SamplerCube:
+ return "samplerCube";
+ case QShaderLanguage::Sampler2DRect:
+ return "sampler2DRect";
+ case QShaderLanguage::Sampler2DMs:
+ return "sampler2DMS";
+ case QShaderLanguage::SamplerBuffer:
+ return "samplerBuffer";
+ case QShaderLanguage::Sampler1DArray:
+ return "sampler1DArray";
+ case QShaderLanguage::Sampler2DArray:
+ return "sampler2DArray";
+ case QShaderLanguage::Sampler2DMsArray:
+ return "sampler2DMSArray";
+ case QShaderLanguage::SamplerCubeArray:
+ return "samplerCubeArray";
+ case QShaderLanguage::Sampler1DShadow:
+ return "sampler1DShadow";
+ case QShaderLanguage::Sampler2DShadow:
+ return "sampler2DShadow";
+ case QShaderLanguage::Sampler2DRectShadow:
+ return "sampler2DRectShadow";
+ case QShaderLanguage::Sampler1DArrayShadow:
+ return "sampler1DArrayShadow";
+ case QShaderLanguage::Sampler2DArrayShadow:
+ return "sample2DArrayShadow";
+ case QShaderLanguage::SamplerCubeShadow:
+ return "samplerCubeShadow";
+ case QShaderLanguage::SamplerCubeArrayShadow:
+ return "samplerCubeArrayShadow";
+ case QShaderLanguage::ISampler1D:
+ return "isampler1D";
+ case QShaderLanguage::ISampler2D:
+ return "isampler2D";
+ case QShaderLanguage::ISampler3D:
+ return "isampler3D";
+ case QShaderLanguage::ISamplerCube:
+ return "isamplerCube";
+ case QShaderLanguage::ISampler2DRect:
+ return "isampler2DRect";
+ case QShaderLanguage::ISampler2DMs:
+ return "isampler2DMS";
+ case QShaderLanguage::ISamplerBuffer:
+ return "isamplerBuffer";
+ case QShaderLanguage::ISampler1DArray:
+ return "isampler1DArray";
+ case QShaderLanguage::ISampler2DArray:
+ return "isampler2DArray";
+ case QShaderLanguage::ISampler2DMsArray:
+ return "isampler2DMSArray";
+ case QShaderLanguage::ISamplerCubeArray:
+ return "isamplerCubeArray";
+ case QShaderLanguage::USampler1D:
+ return "usampler1D";
+ case QShaderLanguage::USampler2D:
+ return "usampler2D";
+ case QShaderLanguage::USampler3D:
+ return "usampler3D";
+ case QShaderLanguage::USamplerCube:
+ return "usamplerCube";
+ case QShaderLanguage::USampler2DRect:
+ return "usampler2DRect";
+ case QShaderLanguage::USampler2DMs:
+ return "usampler2DMS";
+ case QShaderLanguage::USamplerBuffer:
+ return "usamplerBuffer";
+ case QShaderLanguage::USampler1DArray:
+ return "usampler1DArray";
+ case QShaderLanguage::USampler2DArray:
+ return "usampler2DArray";
+ case QShaderLanguage::USampler2DMsArray:
+ return "usampler2DMSArray";
+ case QShaderLanguage::USamplerCubeArray:
+ return "usamplerCubeArray";
+ }
+
+ Q_UNREACHABLE();
+ }
+
+ QByteArray replaceParameters(const QByteArray &original, const QShaderNode &node,
+ const QShaderFormat &format) noexcept
+ {
+ QByteArray result = original;
+
+ const QStringList parameterNames = node.parameterNames();
+ for (const QString &parameterName : parameterNames) {
+ const QByteArray placeholder = QByteArray(QByteArrayLiteral("$") + parameterName.toUtf8());
+ const QVariant parameter = node.parameter(parameterName);
+ if (parameter.userType() == qMetaTypeId<QShaderLanguage::StorageQualifier>()) {
+ const QShaderLanguage::StorageQualifier qualifier =
+ qvariant_cast<QShaderLanguage::StorageQualifier>(parameter);
+ const QByteArray value = toGlsl(qualifier, format);
+ result.replace(placeholder, value);
+ } else if (parameter.userType() == qMetaTypeId<QShaderLanguage::VariableType>()) {
+ const QShaderLanguage::VariableType type =
+ qvariant_cast<QShaderLanguage::VariableType>(parameter);
+ const QByteArray value = toGlsl(type);
+ result.replace(placeholder, value);
+ } else {
+ const QByteArray value = parameter.toString().toUtf8();
+ result.replace(placeholder, value);
+ }
+ }
+
+ return result;
+ }
+
+ bool intersectsEnabledLayers(const QStringList &enabledLayers, const QStringList &layers) noexcept
+ {
+ return layers.isEmpty()
+ || std::any_of(layers.cbegin(), layers.cend(),
+ [enabledLayers](const QString &s) { return enabledLayers.contains(s); });
+ }
+
+ struct ShaderGenerationState
+ {
+ ShaderGenerationState(const QShaderGenerator & gen, QStringList layers, QVector<QShaderNode> nodes)
+ : generator{gen}
+ , enabledLayers{layers}
+ , nodes{nodes}
+ {
+
+ }
+
+ const QShaderGenerator &generator;
+ QStringList enabledLayers;
+ QVector<QShaderNode> nodes;
+ QByteArrayList code;
+
+ QVector<QString> globalInputVariables;
+ const QRegularExpression globalInputExtractRegExp { QStringLiteral("^.*\\s+(\\w+).*;$") };
+ };
+
+ class GLSL45HeaderWriter
+ {
+ public:
+ void writeHeader(ShaderGenerationState &state)
+ {
+ const auto &format = state.generator.format;
+ auto &code = state.code;
+ for (const QShaderNode &node : state.nodes) {
+ if (intersectsEnabledLayers(state.enabledLayers, node.layers())) {
+ const QByteArrayList& headerSnippets = node.rule(format).headerSnippets;
+ for (const QByteArray &snippet : headerSnippets) {
+ auto replacedSnippet = replaceParameters(snippet, node, format).trimmed();
+
+ if (replacedSnippet.startsWith(QByteArrayLiteral("add-input"))) {
+ onInOut(code, replacedSnippet);
+ } else if (replacedSnippet.startsWith(QByteArrayLiteral("add-uniform"))) {
+ onNamedUniform(ubo, replacedSnippet);
+ } else if (replacedSnippet.startsWith(QByteArrayLiteral("add-sampler"))) {
+ onNamedSampler(code, replacedSnippet);
+ } else if (replacedSnippet.startsWith(QByteArrayLiteral("#pragma include "))) {
+ onInclude(code, replacedSnippet);
+ } else {
+ code << replacedSnippet;
+ }
+ // If node is an input, record the variable name into the globalInputVariables
+ // vector
+ if (node.type() == QShaderNode::Input) {
+ const QRegularExpressionMatch match = state.globalInputExtractRegExp.match(
+ QString::fromUtf8(code.last()));
+ if (match.hasMatch())
+ state.globalInputVariables.push_back(match.captured(1));
+ }
+ }
+ }
+ }
+
+ if (!ubo.isEmpty()) {
+ code << QByteArrayLiteral("layout(std140, binding = ")
+ + QByteArray::number(currentBinding++)
+ + QByteArrayLiteral(") uniform qt3d_shadergraph_generated_uniforms {");
+ code << ubo;
+ code << "};";
+ }
+ }
+
+ private:
+ void onInOut(QByteArrayList &code, const QByteArray &snippet) noexcept
+ {
+ const auto split = snippet.split(' ');
+ if (split.size() < 4) {
+ qDebug() << "Invalid header snippet: " << snippet;
+ return;
+ }
+ const auto &qualifier = split[1];
+ const auto &type = split[2];
+ const auto &name = split[3];
+
+ if (qualifier == QByteArrayLiteral("in")) {
+ code << (QByteArrayLiteral("layout(location = ")
+ + QByteArray::number(currentInputLocation++) + QByteArrayLiteral(") in ")
+ + type + ' ' + name + QByteArrayLiteral(";"));
+ } else if (qualifier == QByteArrayLiteral("out")) {
+ code << (QByteArrayLiteral("layout(location = ")
+ + QByteArray::number(currentOutputLocation++) + QByteArrayLiteral(") out ")
+ + type + ' ' + name + QByteArrayLiteral(";"));
+ } else if (qualifier == QByteArrayLiteral("uniform")) {
+ ubo << (type + ' ' + name + ';');
+ }
+ }
+
+ void onNamedUniform(QByteArrayList &ubo, const QByteArray &snippet) noexcept
+ {
+ const auto split = snippet.split(' ');
+ if (split.size() < 3) {
+ qDebug() << "Invalid header snippet: " << snippet;
+ return;
+ }
+
+ const auto &type = split[1];
+ const auto &name = split[2];
+
+ ubo << (type + ' ' + name + ';');
+ }
+
+ void onNamedSampler(QByteArrayList &code, const QByteArray &snippet) noexcept
+ {
+ const auto split = snippet.split(' ');
+ if (split.size() < 3) {
+ qDebug() << "Invalid header snippet: " << snippet;
+ return;
+ }
+ const auto binding = QByteArray::number(currentBinding++);
+ const auto &type = split[1];
+ const auto &name = split[2];
+
+ code << (QByteArrayLiteral("layout(binding = ") + binding + QByteArrayLiteral(") uniform ")
+ + type + ' ' + name + QByteArrayLiteral(";"));
+ }
+
+ void onInclude(QByteArrayList &code, const QByteArray &snippet) noexcept
+ {
+ const auto filepath = QString::fromUtf8(snippet.mid(strlen("#pragma include ")));
+ QString deincluded = QString::fromUtf8(QShaderProgramPrivate::deincludify(filepath));
+
+ // This lambda will replace all occurrences of a string (e.g. "binding = auto") by another,
+ // with the incremented int passed as argument (e.g. "binding = 1", "binding = 2" ...)
+ const auto replaceAndIncrement = [&deincluded](const QRegularExpression &regexp,
+ int &variable,
+ const QString &replacement) noexcept {
+ int matchStart = 0;
+ do {
+ matchStart = deincluded.indexOf(regexp, matchStart);
+ if (matchStart != -1) {
+ const auto match = regexp.match(deincluded.midRef(matchStart));
+ const auto length = match.capturedLength(0);
+
+ deincluded.replace(matchStart, length, replacement.arg(variable++));
+ }
+ } while (matchStart != -1);
+ };
+
+ // 1. Handle uniforms
+ {
+ thread_local const QRegularExpression bindings(
+ QStringLiteral("binding\\s?+=\\s?+auto"));
+
+ replaceAndIncrement(bindings, currentBinding, QStringLiteral("binding = %1"));
+ }
+
+ // 2. Handle inputs
+ {
+ thread_local const QRegularExpression inLocations(
+ QStringLiteral("location\\s?+=\\s?+auto\\s?+\\)\\s?+in\\s+"));
+
+ replaceAndIncrement(inLocations, currentInputLocation,
+ QStringLiteral("location = %1) in "));
+ }
+
+ // 3. Handle outputs
+ {
+ thread_local const QRegularExpression outLocations(
+ QStringLiteral("location\\s?+=\\s?+auto\\s?+\\)\\s?+out\\s+"));
+
+ replaceAndIncrement(outLocations, currentOutputLocation,
+ QStringLiteral("location = %1) out "));
+ }
+
+ code << deincluded.toUtf8();
+ }
+
+ int currentInputLocation { 0 };
+ int currentOutputLocation { 0 };
+ int currentBinding { 2 };
+ QByteArrayList ubo;
+ };
+
+ struct GLSLHeaderWriter
+ {
+ void writeHeader(ShaderGenerationState &state)
+ {
+ const auto &format = state.generator.format;
+ auto &code = state.code;
+ for (const QShaderNode &node : state.nodes) {
+ if (intersectsEnabledLayers(state.enabledLayers, node.layers())) {
+ const QByteArrayList& headerSnippets = node.rule(format).headerSnippets;
+ for (const QByteArray &snippet : headerSnippets) {
+ code << replaceParameters(snippet, node, format);
+
+ // If node is an input, record the variable name into the globalInputVariables
+ // vector
+ if (node.type() == QShaderNode::Input) {
+ const QRegularExpressionMatch match = state.globalInputExtractRegExp.match(
+ QString::fromUtf8(code.last()));
+ if (match.hasMatch())
+ state.globalInputVariables.push_back(match.captured(1));
+ }
+ }
+ }
+ }
+ }
+ };
+
+ QByteArray versionString(const QShaderFormat &format) noexcept
+ {
+ if (!format.isValid())
+ return {};
+
+ switch (format.api()) {
+ case QShaderFormat::RHI: {
+ return QByteArrayLiteral("#version 450");
+ }
+ case QShaderFormat::VulkanFlavoredGLSL: {
+ const int major = format.version().majorVersion();
+ const int minor = format.version().minorVersion();
+ return (QByteArrayLiteral("#version ") + QByteArray::number(major * 100 + minor * 10));
+ }
+ default: {
+ const bool isGLES = format.api() == QShaderFormat::OpenGLES;
+ const int major = format.version().majorVersion();
+ const int minor = format.version().minorVersion();
+
+ const int version = major == 2 && isGLES ? 100
+ : major == 3 && isGLES ? 300
+ : major == 2 ? 100 + 10 * (minor + 1)
+ : major == 3 && minor <= 2 ? 100 + 10 * (minor + 3)
+ : major * 100 + minor * 10;
+
+ const QByteArray profile =
+ isGLES && version > 100 ? QByteArrayLiteral(" es")
+ : version >= 150 && format.api() == QShaderFormat::OpenGLCoreProfile ? QByteArrayLiteral(" core")
+ : version >= 150 && format.api() == QShaderFormat::OpenGLCompatibilityProfile ? QByteArrayLiteral(" compatibility")
+ : QByteArray();
+
+ return (QByteArrayLiteral("#version ") + QByteArray::number(version) + profile);
+ }
+ }
+ }
+}
+
+QByteArray QShaderGenerator::createShaderCode(const QStringList &enabledLayers) const
+{
+ const QVector<QShaderNode> nodes = graph.nodes();
+ ShaderGenerationState state(*this, enabledLayers, nodes);
+ QByteArrayList &code = state.code;
+
+ code << versionString(format);
+ code << QByteArray();
+
+ if (format.api() == QShaderFormat::VulkanFlavoredGLSL || format.api() == QShaderFormat::RHI) {
+ GLSL45HeaderWriter builder;
+ builder.writeHeader(state);
+ } else {
+ GLSLHeaderWriter builder;
+ builder.writeHeader(state);
+ }
+
+ code << QByteArray();
+ code << QByteArrayLiteral("void main()");
+ code << QByteArrayLiteral("{");
+
+ const QRegularExpression temporaryVariableToAssignmentRegExp(
+ QStringLiteral("([^;]*\\s+(v\\d+))\\s*=\\s*([^;]*);"));
+ const QRegularExpression temporaryVariableInAssignmentRegExp(QStringLiteral("\\W*(v\\d+)\\W*"));
+ const QRegularExpression statementRegExp(QStringLiteral("\\s*(\\w+)\\s*=\\s*([^;]*);"));
+
+ struct Variable;
+
+ struct Assignment
+ {
+ QString expression;
+ QVector<Variable *> referencedVariables;
+ };
+
+ struct Variable
+ {
+ enum Type { GlobalInput, TemporaryAssignment, Output };
+
+ QString name;
+ QString declaration;
+ int referenceCount = 0;
+ Assignment assignment;
+ Type type = TemporaryAssignment;
+ bool substituted = false;
+
+ static void substitute(Variable *v)
+ {
+ if (v->substituted)
+ return;
+
+ qCDebug(ShaderGenerator)
+ << "Begin Substituting " << v->name << " = " << v->assignment.expression;
+ for (Variable *ref : qAsConst(v->assignment.referencedVariables)) {
+ // Recursively substitute
+ Variable::substitute(ref);
+
+ // Replace all variables referenced only once in the assignment
+ // by their actual expression
+ if (ref->referenceCount == 1 || ref->type == Variable::GlobalInput) {
+ const QRegularExpression r(QStringLiteral("(.*\\b)(%1)(\\b.*)").arg(ref->name));
+ if (v->assignment.referencedVariables.size() == 1)
+ v->assignment.expression.replace(
+ r, QStringLiteral("\\1%2\\3").arg(ref->assignment.expression));
+ else
+ v->assignment.expression.replace(
+ r, QStringLiteral("(\\1%2\\3)").arg(ref->assignment.expression));
+ }
+ }
+ qCDebug(ShaderGenerator)
+ << "Done Substituting " << v->name << " = " << v->assignment.expression;
+ v->substituted = true;
+ }
+ };
+
+ struct LineContent
+ {
+ QByteArray rawContent;
+ Variable *var = nullptr;
+ };
+
+ // Table to store temporary variables that should be replaced:
+ // - If variable references a a global variables
+ // -> we will use the global variable directly
+ // - If variable references a function results
+ // -> will be kept only if variable is referenced more than once.
+ // This avoids having vec3 v56 = vertexPosition; when we could
+ // just use vertexPosition directly.
+ // The added benefit is when having arrays, we don't try to create
+ // mat4 v38 = skinningPalelette[100] which would be invalid
+ QVector<Variable> temporaryVariables;
+ // Reserve more than enough space to ensure no reallocation will take place
+ temporaryVariables.reserve(nodes.size() * 8);
+
+ QVector<LineContent> lines;
+
+ auto createVariable = [&] () -> Variable * {
+ Q_ASSERT(temporaryVariables.capacity() > 0);
+ temporaryVariables.resize(temporaryVariables.size() + 1);
+ return &temporaryVariables.last();
+ };
+
+ auto findVariable = [&] (const QString &name) -> Variable * {
+ const auto end = temporaryVariables.end();
+ auto it = std::find_if(temporaryVariables.begin(), end,
+ [=] (const Variable &a) { return a.name == name; });
+ if (it != end)
+ return &(*it);
+ return nullptr;
+ };
+
+ auto gatherTemporaryVariablesFromAssignment = [&](Variable *v,
+ const QString &assignmentContent) {
+ QRegularExpressionMatchIterator subMatchIt =
+ temporaryVariableInAssignmentRegExp.globalMatch(assignmentContent);
+ while (subMatchIt.hasNext()) {
+ const QRegularExpressionMatch subMatch = subMatchIt.next();
+ const QString variableName = subMatch.captured(1);
+
+ // Variable we care about should already exists -> an expression cannot reference a
+ // variable that hasn't been defined
+ Variable *u = findVariable(variableName);
+ Q_ASSERT(u);
+
+ // Increase reference count for u
+ ++u->referenceCount;
+ // Insert u as reference for variable v
+ v->assignment.referencedVariables.push_back(u);
+ }
+ };
+
+ for (const QShaderGraph::Statement &statement : graph.createStatements(enabledLayers)) {
+ const QShaderNode node = statement.node;
+ QByteArray line = node.rule(format).substitution;
+ const QVector<QShaderNodePort> ports = node.ports();
+
+ struct VariableReplacement
+ {
+ QByteArray placeholder;
+ QByteArray variable;
+ };
+
+ QVector<VariableReplacement> variableReplacements;
+
+ // Generate temporary variable names vN
+ for (const QShaderNodePort &port : ports) {
+ const QString portName = port.name;
+ const QShaderNodePort::Direction portDirection = port.direction;
+ const bool isInput = port.direction == QShaderNodePort::Input;
+
+ const int portIndex = statement.portIndex(portDirection, portName);
+
+ Q_ASSERT(portIndex >= 0);
+
+ const int variableIndex =
+ isInput ? statement.inputs.at(portIndex) : statement.outputs.at(portIndex);
+ if (variableIndex < 0)
+ continue;
+
+ VariableReplacement replacement;
+ replacement.placeholder = QByteArrayLiteral("$") + portName.toUtf8();
+ replacement.variable = QByteArrayLiteral("v") + QByteArray::number(variableIndex);
+
+ variableReplacements.append(std::move(replacement));
+ }
+
+ int begin = 0;
+ while ((begin = line.indexOf('$', begin)) != -1) {
+ int end = begin + 1;
+ char endChar = line.at(end);
+ const int size = line.size();
+ while (end < size && (std::isalnum(endChar) || endChar == '_')) {
+ ++end;
+ endChar = line.at(end);
+ }
+
+ const int placeholderLength = end - begin;
+
+ const QByteArray variableName = line.mid(begin, placeholderLength);
+ const auto replacementIt =
+ std::find_if(variableReplacements.cbegin(), variableReplacements.cend(),
+ [&variableName](const VariableReplacement &replacement) {
+ return variableName == replacement.placeholder;
+ });
+
+ if (replacementIt != variableReplacements.cend()) {
+ line.replace(begin, placeholderLength, replacementIt->variable);
+ begin += replacementIt->variable.length();
+ } else {
+ begin = end;
+ }
+ }
+
+ // Substitute variable names by generated vN variable names
+ const QByteArray substitutionedLine = replaceParameters(line, node, format);
+
+ QRegularExpressionMatchIterator matches;
+
+ switch (node.type()) {
+ case QShaderNode::Input:
+ case QShaderNode::Output:
+ matches = statementRegExp.globalMatch(QString::fromUtf8(substitutionedLine));
+ break;
+ case QShaderNode::Function:
+ matches = temporaryVariableToAssignmentRegExp.globalMatch(
+ QString::fromUtf8(substitutionedLine));
+ break;
+ case QShaderNode::Invalid:
+ break;
+ }
+
+ while (matches.hasNext()) {
+ QRegularExpressionMatch match = matches.next();
+
+ Variable *v = nullptr;
+
+ switch (node.type()) {
+ // Record name of temporary variable that possibly references a global input
+ // We will replace the temporary variables by the matching global variables later
+ case QShaderNode::Input: {
+ const QString localVariable = match.captured(1);
+ const QString globalVariable = match.captured(2);
+
+ v = createVariable();
+ v->name = localVariable;
+ v->type = Variable::GlobalInput;
+
+ Assignment assignment;
+ assignment.expression = globalVariable;
+ v->assignment = assignment;
+ break;
+ }
+
+ case QShaderNode::Function: {
+ const QString localVariableDeclaration = match.captured(1);
+ const QString localVariableName = match.captured(2);
+ const QString assignmentContent = match.captured(3);
+
+ // Add new variable -> it cannot exist already
+ v = createVariable();
+ v->name = localVariableName;
+ v->declaration = localVariableDeclaration;
+ v->assignment.expression = assignmentContent;
+
+ // Find variables that may be referenced in the assignment
+ gatherTemporaryVariablesFromAssignment(v, assignmentContent);
+ break;
+ }
+
+ case QShaderNode::Output: {
+ const QString outputDeclaration = match.captured(1);
+ const QString assignmentContent = match.captured(2);
+
+ v = createVariable();
+ v->name = outputDeclaration;
+ v->declaration = outputDeclaration;
+ v->type = Variable::Output;
+
+ Assignment assignment;
+ assignment.expression = assignmentContent;
+ v->assignment = assignment;
+
+ // Find variables that may be referenced in the assignment
+ gatherTemporaryVariablesFromAssignment(v, assignmentContent);
+ break;
+ }
+ case QShaderNode::Invalid:
+ break;
+ }
+
+ LineContent lineContent;
+ lineContent.rawContent = QByteArray(QByteArrayLiteral(" ") + substitutionedLine);
+ lineContent.var = v;
+ lines << lineContent;
+ }
+ }
+
+ // Go through all lines
+ // Perform substitution of line with temporary variables substitution
+ for (LineContent &lineContent : lines) {
+ Variable *v = lineContent.var;
+ qCDebug(ShaderGenerator) << lineContent.rawContent;
+ if (v != nullptr) {
+ Variable::substitute(v);
+
+ qCDebug(ShaderGenerator)
+ << "Line " << lineContent.rawContent << "is assigned to temporary" << v->name;
+
+ // Check number of occurrences a temporary variable is referenced
+ if (v->referenceCount == 1 || v->type == Variable::GlobalInput) {
+ // If it is referenced only once, no point in creating a temporary
+ // Clear content for current line
+ lineContent.rawContent.clear();
+ // We assume expression that were referencing vN will have vN properly substituted
+ } else {
+ lineContent.rawContent = QStringLiteral(" %1 = %2;")
+ .arg(v->declaration)
+ .arg(v->assignment.expression)
+ .toUtf8();
+ }
+
+ qCDebug(ShaderGenerator) << "Updated Line is " << lineContent.rawContent;
+ }
+ }
+
+ // Go throug all lines and insert content
+ for (const LineContent &lineContent : qAsConst(lines)) {
+ if (!lineContent.rawContent.isEmpty()) {
+ code << lineContent.rawContent;
+ }
+ }
+
+ code << QByteArrayLiteral("}");
+ code << QByteArray();
+
+ return code.join('\n');
+}
+
+}
+QT_END_NAMESPACE
diff --git a/src/render/shadergraph/qshadergenerator_p.h b/src/render/shadergraph/qshadergenerator_p.h
new file mode 100644
index 000000000..aebeaa8f2
--- /dev/null
+++ b/src/render/shadergraph/qshadergenerator_p.h
@@ -0,0 +1,81 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtGui 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_QSHADERGENERATOR_P_H
+#define QT3DRENDER_QSHADERGENERATOR_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <Qt3DRender/private/qt3drender_global_p.h>
+
+#include <Qt3DRender/private/qshadergraph_p.h>
+#include <QtCore/QLoggingCategory>
+
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender
+{
+Q_DECLARE_LOGGING_CATEGORY(ShaderGenerator)
+
+class QShaderGenerator
+{
+public:
+ Q_3DRENDERSHARED_PRIVATE_EXPORT QByteArray createShaderCode(const QStringList &enabledLayers = QStringList()) const;
+
+ QShaderGraph graph;
+ QShaderFormat format;
+};
+
+}
+Q_DECLARE_TYPEINFO(Qt3DRender::QShaderGenerator, Q_MOVABLE_TYPE);
+QT_END_NAMESPACE
+
+Q_DECLARE_METATYPE(Qt3DRender::QShaderGenerator)
+
+#endif // QT3DRENDER_QSHADERGENERATOR_P_H
diff --git a/src/render/shadergraph/qshadergraph.cpp b/src/render/shadergraph/qshadergraph.cpp
new file mode 100644
index 000000000..c2f3c343e
--- /dev/null
+++ b/src/render/shadergraph/qshadergraph.cpp
@@ -0,0 +1,317 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtGui 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 "qshadergraph_p.h"
+
+QT_BEGIN_NAMESPACE
+namespace Qt3DRender
+{
+
+namespace
+{
+ QVector<QShaderNode> copyOutputNodes(const QVector<QShaderNode> &nodes, const QVector<QShaderGraph::Edge> &edges)
+ {
+ auto res = QVector<QShaderNode>();
+ std::copy_if(nodes.cbegin(), nodes.cend(),
+ std::back_inserter(res),
+ [&edges] (const QShaderNode &node) {
+ return node.type() == QShaderNode::Output ||
+ (node.type() == QShaderNode::Function &&
+ !std::any_of(edges.cbegin(),
+ edges.cend(),
+ [&node] (const QShaderGraph::Edge &edge) {
+ return edge.sourceNodeUuid ==
+ node.uuid();
+ }));
+ });
+ return res;
+ }
+
+ QVector<QShaderGraph::Edge> incomingEdges(const QVector<QShaderGraph::Edge> &edges, const QUuid &uuid)
+ {
+ auto res = QVector<QShaderGraph::Edge>();
+ std::copy_if(edges.cbegin(), edges.cend(),
+ std::back_inserter(res),
+ [uuid] (const QShaderGraph::Edge &edge) {
+ return edge.sourceNodeUuid == uuid;
+ });
+ return res;
+ }
+
+ QVector<QShaderGraph::Edge> outgoingEdges(const QVector<QShaderGraph::Edge> &edges, const QUuid &uuid)
+ {
+ auto res = QVector<QShaderGraph::Edge>();
+ std::copy_if(edges.cbegin(), edges.cend(),
+ std::back_inserter(res),
+ [uuid] (const QShaderGraph::Edge &edge) {
+ return edge.targetNodeUuid == uuid;
+ });
+ return res;
+ }
+
+ QShaderGraph::Statement nodeToStatement(const QShaderNode &node, int &nextVarId)
+ {
+ auto statement = QShaderGraph::Statement();
+ statement.node = node;
+
+ const QVector<QShaderNodePort> ports = node.ports();
+ for (const QShaderNodePort &port : ports) {
+ if (port.direction == QShaderNodePort::Input) {
+ statement.inputs.append(-1);
+ } else {
+ statement.outputs.append(nextVarId);
+ nextVarId++;
+ }
+ }
+ return statement;
+ }
+
+ QShaderGraph::Statement completeStatement(const QHash<QUuid, QShaderGraph::Statement> &idHash,
+ const QVector<QShaderGraph::Edge> edges,
+ const QUuid &uuid)
+ {
+ auto targetStatement = idHash.value(uuid);
+ for (const QShaderGraph::Edge &edge : edges) {
+ if (edge.targetNodeUuid != uuid)
+ continue;
+
+ const QShaderGraph::Statement sourceStatement = idHash.value(edge.sourceNodeUuid);
+ const int sourcePortIndex = sourceStatement.portIndex(QShaderNodePort::Output, edge.sourcePortName);
+ const int targetPortIndex = targetStatement.portIndex(QShaderNodePort::Input, edge.targetPortName);
+
+ if (sourcePortIndex < 0 || targetPortIndex < 0)
+ continue;
+
+ const QVector<int> sourceOutputs = sourceStatement.outputs;
+ QVector<int> &targetInputs = targetStatement.inputs;
+ targetInputs[targetPortIndex] = sourceOutputs[sourcePortIndex];
+ }
+ return targetStatement;
+ }
+
+ void removeNodesWithUnboundInputs(QVector<QShaderGraph::Statement> &statements,
+ const QVector<QShaderGraph::Edge> &allEdges)
+ {
+ // A node is invalid if any of its input ports is disconected
+ // or connected to the output port of another invalid node.
+
+ // Keeps track of the edges from the nodes we know to be valid
+ // to unvisited nodes
+ auto currentEdges = QVector<QShaderGraph::Edge>();
+
+ statements.erase(std::remove_if(statements.begin(),
+ statements.end(),
+ [&currentEdges, &allEdges] (const QShaderGraph::Statement &statement) {
+ const QShaderNode &node = statement.node;
+ const QVector<QShaderGraph::Edge> outgoing = outgoingEdges(currentEdges, node.uuid());
+ const QVector<QShaderNodePort> ports = node.ports();
+
+ bool allInputsConnected = true;
+ for (const QShaderNodePort &port : node.ports()) {
+ if (port.direction == QShaderNodePort::Output)
+ continue;
+
+ const auto edgeIt = std::find_if(outgoing.cbegin(),
+ outgoing.cend(),
+ [&port] (const QShaderGraph::Edge &edge) {
+ return edge.targetPortName == port.name;
+ });
+
+ if (edgeIt != outgoing.cend())
+ currentEdges.removeAll(*edgeIt);
+ else
+ allInputsConnected = false;
+ }
+
+ if (allInputsConnected) {
+ const QVector<QShaderGraph::Edge> incoming = incomingEdges(allEdges, node.uuid());
+ currentEdges.append(incoming);
+ }
+
+ return !allInputsConnected;
+ }),
+ statements.end());
+ }
+}
+
+QUuid QShaderGraph::Statement::uuid() const noexcept
+{
+ return node.uuid();
+}
+
+int QShaderGraph::Statement::portIndex(QShaderNodePort::Direction direction, const QString &portName) const noexcept
+{
+ const QVector<QShaderNodePort> ports = node.ports();
+ int index = 0;
+ for (const QShaderNodePort &port : ports) {
+ if (port.name == portName && port.direction == direction)
+ return index;
+ else if (port.direction == direction)
+ index++;
+ }
+ return -1;
+}
+
+void QShaderGraph::addNode(const QShaderNode &node)
+{
+ removeNode(node);
+ m_nodes.append(node);
+}
+
+void QShaderGraph::removeNode(const QShaderNode &node)
+{
+ const auto it = std::find_if(m_nodes.begin(), m_nodes.end(),
+ [node] (const QShaderNode &n) { return n.uuid() == node.uuid(); });
+ if (it != m_nodes.end())
+ m_nodes.erase(it);
+}
+
+QVector<QShaderNode> QShaderGraph::nodes() const noexcept
+{
+ return m_nodes;
+}
+
+void QShaderGraph::addEdge(const QShaderGraph::Edge &edge)
+{
+ if (m_edges.contains(edge))
+ return;
+ m_edges.append(edge);
+}
+
+void QShaderGraph::removeEdge(const QShaderGraph::Edge &edge)
+{
+ m_edges.removeAll(edge);
+}
+
+QVector<QShaderGraph::Edge> QShaderGraph::edges() const noexcept
+{
+ return m_edges;
+}
+
+QVector<QShaderGraph::Statement> QShaderGraph::createStatements(const QStringList &enabledLayers) const
+{
+ const auto intersectsEnabledLayers = [enabledLayers] (const QStringList &layers) {
+ return layers.isEmpty()
+ || std::any_of(layers.cbegin(), layers.cend(),
+ [enabledLayers] (const QString &s) { return enabledLayers.contains(s); });
+ };
+
+ const QVector<QShaderNode> enabledNodes = [this, intersectsEnabledLayers] {
+ auto res = QVector<QShaderNode>();
+ std::copy_if(m_nodes.cbegin(), m_nodes.cend(),
+ std::back_inserter(res),
+ [intersectsEnabledLayers] (const QShaderNode &node) {
+ return intersectsEnabledLayers(node.layers());
+ });
+ return res;
+ }();
+
+ const QVector<Edge> enabledEdges = [this, intersectsEnabledLayers] {
+ auto res = QVector<Edge>();
+ std::copy_if(m_edges.cbegin(), m_edges.cend(),
+ std::back_inserter(res),
+ [intersectsEnabledLayers] (const Edge &edge) {
+ return intersectsEnabledLayers(edge.layers);
+ });
+ return res;
+ }();
+
+ const QHash<QUuid, Statement> idHash = [enabledNodes] {
+ auto nextVarId = 0;
+ auto res = QHash<QUuid, Statement>();
+ for (const QShaderNode &node : enabledNodes)
+ res.insert(node.uuid(), nodeToStatement(node, nextVarId));
+ return res;
+ }();
+
+ auto result = QVector<Statement>();
+ QVector<Edge> currentEdges = enabledEdges;
+ QVector<QUuid> currentUuids = [enabledNodes, enabledEdges] {
+ const QVector<QShaderNode> inputs = copyOutputNodes(enabledNodes, enabledEdges);
+ auto res = QVector<QUuid>();
+ std::transform(inputs.cbegin(), inputs.cend(),
+ std::back_inserter(res),
+ [](const QShaderNode &node) { return node.uuid(); });
+ return res;
+ }();
+
+ // Implements Kahn's algorithm to flatten the graph
+ // https://en.wikipedia.org/wiki/Topological_sorting#Kahn.27s_algorithm
+ //
+ // We implement it with a small twist though, we follow the edges backward
+ // because we want to track the dependencies from the output nodes and not the
+ // input nodes
+ while (!currentUuids.isEmpty()) {
+ const QUuid uuid = currentUuids.takeFirst();
+ result.append(completeStatement(idHash, enabledEdges, uuid));
+
+ const QVector<QShaderGraph::Edge> outgoing = outgoingEdges(currentEdges, uuid);
+ for (const QShaderGraph::Edge &outgoingEdge : outgoing) {
+ currentEdges.removeAll(outgoingEdge);
+ const QUuid nextUuid = outgoingEdge.sourceNodeUuid;
+ const QVector<QShaderGraph::Edge> incoming = incomingEdges(currentEdges, nextUuid);
+ if (incoming.isEmpty()) {
+ currentUuids.append(nextUuid);
+ }
+ }
+ }
+
+ std::reverse(result.begin(), result.end());
+
+ removeNodesWithUnboundInputs(result, enabledEdges);
+
+ return result;
+}
+
+bool operator==(const QShaderGraph::Edge &lhs, const QShaderGraph::Edge &rhs) noexcept
+{
+ return lhs.sourceNodeUuid == rhs.sourceNodeUuid
+ && lhs.sourcePortName == rhs.sourcePortName
+ && lhs.targetNodeUuid == rhs.targetNodeUuid
+ && lhs.targetPortName == rhs.targetPortName;
+}
+
+bool operator==(const QShaderGraph::Statement &lhs, const QShaderGraph::Statement &rhs) noexcept
+{
+ return lhs.inputs == rhs.inputs
+ && lhs.outputs == rhs.outputs
+ && lhs.node.uuid() == rhs.node.uuid();
+}
+}
+QT_END_NAMESPACE
diff --git a/src/render/shadergraph/qshadergraph_p.h b/src/render/shadergraph/qshadergraph_p.h
new file mode 100644
index 000000000..de746f7b2
--- /dev/null
+++ b/src/render/shadergraph/qshadergraph_p.h
@@ -0,0 +1,126 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtGui 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_QSHADERGRAPH_P_H
+#define QT3DRENDER_QSHADERGRAPH_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <Qt3DRender/private/qt3drender_global_p.h>
+
+#include <Qt3DRender/private/qshadernode_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender
+{
+class QShaderGraph
+{
+public:
+ class Edge
+ {
+ public:
+ QStringList layers;
+ QUuid sourceNodeUuid;
+ QString sourcePortName;
+ QUuid targetNodeUuid;
+ QString targetPortName;
+ };
+
+ class Statement
+ {
+ public:
+ Q_3DRENDERSHARED_PRIVATE_EXPORT QUuid uuid() const noexcept;
+ Q_3DRENDERSHARED_PRIVATE_EXPORT int portIndex(QShaderNodePort::Direction direction, const QString &portName) const noexcept;
+
+ QShaderNode node;
+ QVector<int> inputs;
+ QVector<int> outputs;
+ };
+
+ Q_3DRENDERSHARED_PRIVATE_EXPORT void addNode(const QShaderNode &node);
+ Q_3DRENDERSHARED_PRIVATE_EXPORT void removeNode(const QShaderNode &node);
+ Q_3DRENDERSHARED_PRIVATE_EXPORT QVector<QShaderNode> nodes() const noexcept;
+
+ Q_3DRENDERSHARED_PRIVATE_EXPORT void addEdge(const Edge &edge);
+ Q_3DRENDERSHARED_PRIVATE_EXPORT void removeEdge(const Edge &edge);
+ Q_3DRENDERSHARED_PRIVATE_EXPORT QVector<Edge> edges() const noexcept;
+
+ Q_3DRENDERSHARED_PRIVATE_EXPORT QVector<Statement> createStatements(const QStringList &enabledLayers = QStringList()) const;
+
+private:
+ QVector<QShaderNode> m_nodes;
+ QVector<Edge> m_edges;
+};
+
+Q_3DRENDERSHARED_PRIVATE_EXPORT bool operator==(const QShaderGraph::Edge &lhs, const QShaderGraph::Edge &rhs) noexcept;
+
+inline bool operator!=(const QShaderGraph::Edge &lhs, const QShaderGraph::Edge &rhs) noexcept
+{
+ return !(lhs == rhs);
+}
+
+Q_3DRENDERSHARED_PRIVATE_EXPORT bool operator==(const QShaderGraph::Statement &lhs, const QShaderGraph::Statement &rhs) noexcept;
+
+inline bool operator!=(const QShaderGraph::Statement &lhs, const QShaderGraph::Statement &rhs) noexcept
+{
+ return !(lhs == rhs);
+}
+
+}
+
+Q_DECLARE_TYPEINFO(Qt3DRender::QShaderGraph, Q_MOVABLE_TYPE);
+Q_DECLARE_TYPEINFO(Qt3DRender::QShaderGraph::Edge, Q_MOVABLE_TYPE);
+Q_DECLARE_TYPEINFO(Qt3DRender::QShaderGraph::Statement, Q_MOVABLE_TYPE);
+QT_END_NAMESPACE
+
+Q_DECLARE_METATYPE(Qt3DRender::QShaderGraph)
+Q_DECLARE_METATYPE(Qt3DRender::QShaderGraph::Edge)
+Q_DECLARE_METATYPE(Qt3DRender::QShaderGraph::Statement)
+
+#endif // QT3DRENDER_QSHADERGRAPH_P_H
diff --git a/src/render/shadergraph/qshadergraphloader.cpp b/src/render/shadergraph/qshadergraphloader.cpp
new file mode 100644
index 000000000..64f159e7b
--- /dev/null
+++ b/src/render/shadergraph/qshadergraphloader.cpp
@@ -0,0 +1,271 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtGui 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 "qshadergraphloader_p.h"
+
+#include "qshadernodesloader_p.h"
+
+#include <QtCore/qdebug.h>
+#include <QtCore/qiodevice.h>
+#include <QtCore/qjsonarray.h>
+#include <QtCore/qjsondocument.h>
+#include <QtCore/qjsonobject.h>
+#include <QtCore/qmetaobject.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender
+{
+void qt_register_ShaderLanguage_enums();
+
+QShaderGraphLoader::QShaderGraphLoader() noexcept
+ : m_status(Null),
+ m_device(nullptr)
+{
+ qt_register_ShaderLanguage_enums();
+}
+
+QShaderGraphLoader::Status QShaderGraphLoader::status() const noexcept
+{
+ return m_status;
+}
+
+QShaderGraph QShaderGraphLoader::graph() const noexcept
+{
+ return m_graph;
+}
+
+QIODevice *QShaderGraphLoader::device() const noexcept
+{
+ return m_device;
+}
+
+void QShaderGraphLoader::setDevice(QIODevice *device) noexcept
+{
+ m_device = device;
+ m_graph = QShaderGraph();
+ m_status = !m_device ? Null
+ : (m_device->openMode() & QIODevice::ReadOnly) ? Waiting
+ : Error;
+}
+
+QHash<QString, QShaderNode> QShaderGraphLoader::prototypes() const noexcept
+{
+ return m_prototypes;
+}
+
+void QShaderGraphLoader::setPrototypes(const QHash<QString, QShaderNode> &prototypes) noexcept
+{
+ m_prototypes = prototypes;
+}
+
+void QShaderGraphLoader::load()
+{
+ if (m_status == Error)
+ return;
+
+ auto error = QJsonParseError();
+ const QJsonDocument document = QJsonDocument::fromJson(m_device->readAll(), &error);
+
+ if (error.error != QJsonParseError::NoError) {
+ qWarning() << "Invalid JSON document:" << error.errorString();
+ m_status = Error;
+ return;
+ }
+
+ if (document.isEmpty() || !document.isObject()) {
+ qWarning() << "Invalid JSON document, root should be an object";
+ m_status = Error;
+ return;
+ }
+
+ const QJsonObject root = document.object();
+
+ const QJsonValue nodesValue = root.value(QStringLiteral("nodes"));
+ if (!nodesValue.isArray()) {
+ qWarning() << "Invalid nodes property, should be an array";
+ m_status = Error;
+ return;
+ }
+
+ const QJsonValue edgesValue = root.value(QStringLiteral("edges"));
+ if (!edgesValue.isArray()) {
+ qWarning() << "Invalid edges property, should be an array";
+ m_status = Error;
+ return;
+ }
+
+ bool hasError = false;
+
+ const QJsonValue prototypesValue = root.value(QStringLiteral("prototypes"));
+ if (!prototypesValue.isUndefined()) {
+ if (prototypesValue.isObject()) {
+ QShaderNodesLoader loader;
+ loader.load(prototypesValue.toObject());
+ m_prototypes.insert(loader.nodes());
+ } else {
+ qWarning() << "Invalid prototypes property, should be an object";
+ m_status = Error;
+ return;
+ }
+ }
+
+ const QJsonArray nodes = nodesValue.toArray();
+ for (const QJsonValue &nodeValue : nodes) {
+ if (!nodeValue.isObject()) {
+ qWarning() << "Invalid node found";
+ hasError = true;
+ continue;
+ }
+
+ const QJsonObject nodeObject = nodeValue.toObject();
+
+ const QString uuidString = nodeObject.value(QStringLiteral("uuid")).toString();
+ const QUuid uuid = QUuid(uuidString);
+ if (uuid.isNull()) {
+ qWarning() << "Invalid UUID found in node:" << uuidString;
+ hasError = true;
+ continue;
+ }
+
+ const QString type = nodeObject.value(QStringLiteral("type")).toString();
+ if (!m_prototypes.contains(type)) {
+ qWarning() << "Unsupported node type found:" << type;
+ hasError = true;
+ continue;
+ }
+
+ const QJsonArray layersArray = nodeObject.value(QStringLiteral("layers")).toArray();
+ auto layers = QStringList();
+ for (const QJsonValue &layerValue : layersArray) {
+ layers.append(layerValue.toString());
+ }
+
+ QShaderNode node = m_prototypes.value(type);
+ node.setUuid(uuid);
+ node.setLayers(layers);
+
+ const QJsonValue parametersValue = nodeObject.value(QStringLiteral("parameters"));
+ if (parametersValue.isObject()) {
+ const QJsonObject parametersObject = parametersValue.toObject();
+ for (const QString &parameterName : parametersObject.keys()) {
+ const QJsonValue parameterValue = parametersObject.value(parameterName);
+ if (parameterValue.isObject()) {
+ const QJsonObject parameterObject = parameterValue.toObject();
+ const QString type = parameterObject.value(QStringLiteral("type")).toString();
+ const int typeId = QMetaType::type(type.toUtf8());
+
+ const QString value = parameterObject.value(QStringLiteral("value")).toString();
+ auto variant = QVariant(value);
+
+ if (QMetaType::typeFlags(typeId) & QMetaType::IsEnumeration) {
+ const QMetaObject *metaObject = QMetaType::metaObjectForType(typeId);
+ const char *className = metaObject->className();
+ const QByteArray enumName = type.mid(static_cast<int>(qstrlen(className)) + 2).toUtf8();
+ const QMetaEnum metaEnum = metaObject->enumerator(metaObject->indexOfEnumerator(enumName));
+ const int enumValue = metaEnum.keyToValue(value.toUtf8());
+ variant = QVariant(enumValue);
+ variant.convert(typeId);
+ } else {
+ variant.convert(typeId);
+ }
+ node.setParameter(parameterName, variant);
+ } else {
+ node.setParameter(parameterName, parameterValue.toVariant());
+ }
+ }
+ }
+
+ m_graph.addNode(node);
+ }
+
+ const QJsonArray edges = edgesValue.toArray();
+ for (const QJsonValue &edgeValue : edges) {
+ if (!edgeValue.isObject()) {
+ qWarning() << "Invalid edge found";
+ hasError = true;
+ continue;
+ }
+
+ const QJsonObject edgeObject = edgeValue.toObject();
+
+ const QString sourceUuidString = edgeObject.value(QStringLiteral("sourceUuid")).toString();
+ const QUuid sourceUuid = QUuid(sourceUuidString);
+ if (sourceUuid.isNull()) {
+ qWarning() << "Invalid source UUID found in edge:" << sourceUuidString;
+ hasError = true;
+ continue;
+ }
+
+ const QString sourcePort = edgeObject.value(QStringLiteral("sourcePort")).toString();
+
+ const QString targetUuidString = edgeObject.value(QStringLiteral("targetUuid")).toString();
+ const QUuid targetUuid = QUuid(targetUuidString);
+ if (targetUuid.isNull()) {
+ qWarning() << "Invalid target UUID found in edge:" << targetUuidString;
+ hasError = true;
+ continue;
+ }
+
+ const QString targetPort = edgeObject.value(QStringLiteral("targetPort")).toString();
+
+ const QJsonArray layersArray = edgeObject.value(QStringLiteral("layers")).toArray();
+ auto layers = QStringList();
+ for (const QJsonValue &layerValue : layersArray) {
+ layers.append(layerValue.toString());
+ }
+
+ auto edge = QShaderGraph::Edge();
+ edge.sourceNodeUuid = sourceUuid;
+ edge.sourcePortName = sourcePort;
+ edge.targetNodeUuid = targetUuid;
+ edge.targetPortName = targetPort;
+ edge.layers = layers;
+ m_graph.addEdge(edge);
+ }
+
+ if (hasError) {
+ m_status = Error;
+ m_graph = QShaderGraph();
+ } else {
+ m_status = Ready;
+ }
+}
+}
+QT_END_NAMESPACE
diff --git a/src/render/shadergraph/qshadergraphloader_p.h b/src/render/shadergraph/qshadergraphloader_p.h
new file mode 100644
index 000000000..20589d724
--- /dev/null
+++ b/src/render/shadergraph/qshadergraphloader_p.h
@@ -0,0 +1,102 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtGui 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_QSHADERGRAPHLOADER_P_H
+#define QT3DRENDER_QSHADERGRAPHLOADER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <Qt3DRender/private/qt3drender_global_p.h>
+
+#include <Qt3DRender/private/qshadergraph_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QIODevice;
+namespace Qt3DRender
+{
+
+class QShaderGraphLoader
+{
+public:
+ enum Status : char {
+ Null,
+ Waiting,
+ Ready,
+ Error
+ };
+
+ Q_3DRENDERSHARED_PRIVATE_EXPORT QShaderGraphLoader() noexcept;
+
+ Q_3DRENDERSHARED_PRIVATE_EXPORT Status status() const noexcept;
+ Q_3DRENDERSHARED_PRIVATE_EXPORT QShaderGraph graph() const noexcept;
+
+ Q_3DRENDERSHARED_PRIVATE_EXPORT QIODevice *device() const noexcept;
+ Q_3DRENDERSHARED_PRIVATE_EXPORT void setDevice(QIODevice *device) noexcept;
+
+ Q_3DRENDERSHARED_PRIVATE_EXPORT QHash<QString, QShaderNode> prototypes() const noexcept;
+ Q_3DRENDERSHARED_PRIVATE_EXPORT void setPrototypes(const QHash<QString, QShaderNode> &prototypes) noexcept;
+
+ Q_3DRENDERSHARED_PRIVATE_EXPORT void load();
+
+private:
+ Status m_status;
+ QIODevice *m_device;
+ QHash<QString, QShaderNode> m_prototypes;
+ QShaderGraph m_graph;
+};
+
+
+}
+Q_DECLARE_TYPEINFO(Qt3DRender::QShaderGraphLoader, Q_MOVABLE_TYPE);
+QT_END_NAMESPACE
+
+Q_DECLARE_METATYPE(Qt3DRender::QShaderGraphLoader)
+Q_DECLARE_METATYPE(Qt3DRender::QShaderGraphLoader::Status)
+
+#endif // QT3DRENDER_QSHADERGRAPHLOADER_P_H
diff --git a/src/render/shadergraph/qshaderlanguage.cpp b/src/render/shadergraph/qshaderlanguage.cpp
new file mode 100644
index 000000000..1630e3de8
--- /dev/null
+++ b/src/render/shadergraph/qshaderlanguage.cpp
@@ -0,0 +1,57 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtGui 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 "qshaderlanguage_p.h"
+
+#include <QtCore/qcoreapplication.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender
+{
+// Note: to be invoked explicitly. Relying for example on
+// Q_COREAPP_STARTUP_FUNCTION would not be acceptable in static builds.
+void qt_register_ShaderLanguage_enums()
+{
+ qRegisterMetaType<QShaderLanguage::StorageQualifier>();
+ qRegisterMetaType<QShaderLanguage::VariableType>();
+}
+}
+
+QT_END_NAMESPACE
diff --git a/src/render/shadergraph/qshaderlanguage_p.h b/src/render/shadergraph/qshaderlanguage_p.h
new file mode 100644
index 000000000..1a56d7476
--- /dev/null
+++ b/src/render/shadergraph/qshaderlanguage_p.h
@@ -0,0 +1,164 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtGui 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_QSHADERLANGUAGE_P_H
+#define QT3DRENDER_QSHADERLANGUAGE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <Qt3DRender/private/qt3drender_global_p.h>
+
+#include <QtCore/qmetatype.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QShaderLanguage
+{
+ Q_NAMESPACE_EXPORT(Q_3DRENDERSHARED_PRIVATE_EXPORT)
+
+ enum StorageQualifier : char {
+ Const = 1,
+ Input,
+ BuiltIn,
+ Output,
+ Uniform
+ };
+ Q_ENUM_NS(StorageQualifier)
+
+ enum VariableType : int {
+ Bool = 1,
+ Int,
+ Uint,
+ Float,
+ Double,
+ Vec2,
+ Vec3,
+ Vec4,
+ DVec2,
+ DVec3,
+ DVec4,
+ BVec2,
+ BVec3,
+ BVec4,
+ IVec2,
+ IVec3,
+ IVec4,
+ UVec2,
+ UVec3,
+ UVec4,
+ Mat2,
+ Mat3,
+ Mat4,
+ Mat2x2,
+ Mat2x3,
+ Mat2x4,
+ Mat3x2,
+ Mat3x3,
+ Mat3x4,
+ Mat4x2,
+ Mat4x3,
+ Mat4x4,
+ DMat2,
+ DMat3,
+ DMat4,
+ DMat2x2,
+ DMat2x3,
+ DMat2x4,
+ DMat3x2,
+ DMat3x3,
+ DMat3x4,
+ DMat4x2,
+ DMat4x3,
+ DMat4x4,
+ Sampler1D,
+ Sampler2D,
+ Sampler3D,
+ SamplerCube,
+ Sampler2DRect,
+ Sampler2DMs,
+ SamplerBuffer,
+ Sampler1DArray,
+ Sampler2DArray,
+ Sampler2DMsArray,
+ SamplerCubeArray,
+ Sampler1DShadow,
+ Sampler2DShadow,
+ Sampler2DRectShadow,
+ Sampler1DArrayShadow,
+ Sampler2DArrayShadow,
+ SamplerCubeShadow,
+ SamplerCubeArrayShadow,
+ ISampler1D,
+ ISampler2D,
+ ISampler3D,
+ ISamplerCube,
+ ISampler2DRect,
+ ISampler2DMs,
+ ISamplerBuffer,
+ ISampler1DArray,
+ ISampler2DArray,
+ ISampler2DMsArray,
+ ISamplerCubeArray,
+ USampler1D,
+ USampler2D,
+ USampler3D,
+ USamplerCube,
+ USampler2DRect,
+ USampler2DMs,
+ USamplerBuffer,
+ USampler1DArray,
+ USampler2DArray,
+ USampler2DMsArray,
+ USamplerCubeArray
+ };
+ Q_ENUM_NS(VariableType)
+}
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_QSHADERLANGUAGE_P_H
diff --git a/src/render/shadergraph/qshadernode.cpp b/src/render/shadergraph/qshadernode.cpp
new file mode 100644
index 000000000..e0421e006
--- /dev/null
+++ b/src/render/shadergraph/qshadernode.cpp
@@ -0,0 +1,174 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtGui 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 "qshadernode_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender
+{
+QShaderNode::Type QShaderNode::type() const noexcept
+{
+ int inputCount = 0;
+ int outputCount = 0;
+ for (const auto &port : qAsConst(m_ports)) {
+ switch (port.direction) {
+ case QShaderNodePort::Input:
+ inputCount++;
+ break;
+ case QShaderNodePort::Output:
+ outputCount++;
+ break;
+ }
+ }
+
+ return (inputCount == 0 && outputCount == 0) ? Invalid
+ : (inputCount > 0 && outputCount == 0) ? Output
+ : (inputCount == 0 && outputCount > 0) ? Input
+ : Function;
+}
+
+QUuid QShaderNode::uuid() const noexcept
+{
+ return m_uuid;
+}
+
+void QShaderNode::setUuid(const QUuid &uuid) noexcept
+{
+ m_uuid = uuid;
+}
+
+QStringList QShaderNode::layers() const noexcept
+{
+ return m_layers;
+}
+
+void QShaderNode::setLayers(const QStringList &layers) noexcept
+{
+ m_layers = layers;
+}
+
+QVector<QShaderNodePort> QShaderNode::ports() const noexcept
+{
+ return m_ports;
+}
+
+void QShaderNode::addPort(const QShaderNodePort &port)
+{
+ removePort(port);
+ m_ports.append(port);
+}
+
+void QShaderNode::removePort(const QShaderNodePort &port)
+{
+ const auto it = std::find_if(m_ports.begin(), m_ports.end(),
+ [port](const QShaderNodePort &p) {
+ return p.name == port.name;
+ });
+ if (it != m_ports.end())
+ m_ports.erase(it);
+}
+
+QStringList QShaderNode::parameterNames() const
+{
+ return m_parameters.keys();
+}
+
+QVariant QShaderNode::parameter(const QString &name) const
+{
+ return m_parameters.value(name);
+}
+
+void QShaderNode::setParameter(const QString &name, const QVariant &value)
+{
+ m_parameters.insert(name, value);
+}
+
+void QShaderNode::clearParameter(const QString &name)
+{
+ m_parameters.remove(name);
+}
+
+void QShaderNode::addRule(const QShaderFormat &format, const QShaderNode::Rule &rule)
+{
+ removeRule(format);
+ m_rules << qMakePair(format, rule);
+}
+
+void QShaderNode::removeRule(const QShaderFormat &format)
+{
+ const auto it = std::find_if(m_rules.begin(), m_rules.end(),
+ [format](const QPair<QShaderFormat, Rule> &entry) {
+ return entry.first == format;
+ });
+ if (it != m_rules.end())
+ m_rules.erase(it);
+}
+
+QVector<QShaderFormat> QShaderNode::availableFormats() const
+{
+ auto res = QVector<QShaderFormat>();
+ std::transform(m_rules.cbegin(), m_rules.cend(),
+ std::back_inserter(res),
+ [](const QPair<QShaderFormat, Rule> &entry) { return entry.first; });
+ return res;
+}
+
+QShaderNode::Rule QShaderNode::rule(const QShaderFormat &format) const
+{
+ const auto it = std::find_if(m_rules.crbegin(), m_rules.crend(),
+ [format](const QPair<QShaderFormat, Rule> &entry) {
+ return format.supports(entry.first);
+ });
+ return it != m_rules.crend() ? it->second : Rule();
+}
+
+QShaderNode::Rule::Rule(const QByteArray &subs, const QByteArrayList &snippets) noexcept
+ : substitution(subs),
+ headerSnippets(snippets)
+{
+}
+
+bool operator==(const QShaderNode::Rule &lhs, const QShaderNode::Rule &rhs) noexcept
+{
+ return lhs.substitution == rhs.substitution
+ && lhs.headerSnippets == rhs.headerSnippets;
+}
+}
+QT_END_NAMESPACE
diff --git a/src/render/shadergraph/qshadernode_p.h b/src/render/shadergraph/qshadernode_p.h
new file mode 100644
index 000000000..92e0e8e14
--- /dev/null
+++ b/src/render/shadergraph/qshadernode_p.h
@@ -0,0 +1,131 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtGui 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_QSHADERNODE_P_H
+#define QT3DRENDER_QSHADERNODE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <Qt3DRender/private/qt3drender_global_p.h>
+
+#include <Qt3DRender/private/qshaderformat_p.h>
+#include <Qt3DRender/private/qshadernodeport_p.h>
+
+#include <QtCore/quuid.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender
+{
+class QShaderNode
+{
+public:
+ enum Type : char {
+ Invalid,
+ Input,
+ Output,
+ Function
+ };
+
+ class Rule
+ {
+ public:
+ Q_3DRENDERSHARED_PRIVATE_EXPORT Rule(const QByteArray &substitution = QByteArray(), const QByteArrayList &headerSnippets = QByteArrayList()) noexcept;
+
+ QByteArray substitution;
+ QByteArrayList headerSnippets;
+ };
+
+ Q_3DRENDERSHARED_PRIVATE_EXPORT Type type() const noexcept;
+
+ Q_3DRENDERSHARED_PRIVATE_EXPORT QUuid uuid() const noexcept;
+ Q_3DRENDERSHARED_PRIVATE_EXPORT void setUuid(const QUuid &uuid) noexcept;
+
+ Q_3DRENDERSHARED_PRIVATE_EXPORT QStringList layers() const noexcept;
+ Q_3DRENDERSHARED_PRIVATE_EXPORT void setLayers(const QStringList &layers) noexcept;
+
+ Q_3DRENDERSHARED_PRIVATE_EXPORT QVector<QShaderNodePort> ports() const noexcept;
+ Q_3DRENDERSHARED_PRIVATE_EXPORT void addPort(const QShaderNodePort &port);
+ Q_3DRENDERSHARED_PRIVATE_EXPORT void removePort(const QShaderNodePort &port);
+
+ Q_3DRENDERSHARED_PRIVATE_EXPORT QStringList parameterNames() const;
+ Q_3DRENDERSHARED_PRIVATE_EXPORT QVariant parameter(const QString &name) const;
+ Q_3DRENDERSHARED_PRIVATE_EXPORT void setParameter(const QString &name, const QVariant &value);
+ Q_3DRENDERSHARED_PRIVATE_EXPORT void clearParameter(const QString &name);
+
+ Q_3DRENDERSHARED_PRIVATE_EXPORT void addRule(const QShaderFormat &format, const Rule &rule);
+ Q_3DRENDERSHARED_PRIVATE_EXPORT void removeRule(const QShaderFormat &format);
+
+ Q_3DRENDERSHARED_PRIVATE_EXPORT QVector<QShaderFormat> availableFormats() const;
+ Q_3DRENDERSHARED_PRIVATE_EXPORT Rule rule(const QShaderFormat &format) const;
+
+private:
+ QUuid m_uuid;
+ QStringList m_layers;
+ QVector<QShaderNodePort> m_ports;
+ QHash<QString, QVariant> m_parameters;
+ QVector<QPair<QShaderFormat, QShaderNode::Rule>> m_rules;
+};
+
+Q_3DRENDERSHARED_PRIVATE_EXPORT bool operator==(const QShaderNode::Rule &lhs, const QShaderNode::Rule &rhs) noexcept;
+
+inline bool operator!=(const QShaderNode::Rule &lhs, const QShaderNode::Rule &rhs) noexcept
+{
+ return !(lhs == rhs);
+}
+
+}
+
+Q_DECLARE_TYPEINFO(Qt3DRender::QShaderNode, Q_MOVABLE_TYPE);
+Q_DECLARE_TYPEINFO(Qt3DRender::QShaderNode::Rule, Q_MOVABLE_TYPE);
+QT_END_NAMESPACE
+
+Q_DECLARE_METATYPE(Qt3DRender::QShaderNode)
+Q_DECLARE_METATYPE(Qt3DRender::QShaderNode::Rule)
+
+#endif // QT3DRENDER_QSHADERNODE_P_H
diff --git a/src/render/shadergraph/qshadernodeport.cpp b/src/render/shadergraph/qshadernodeport.cpp
new file mode 100644
index 000000000..a79c32234
--- /dev/null
+++ b/src/render/shadergraph/qshadernodeport.cpp
@@ -0,0 +1,58 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtGui 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 "qshadernodeport_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender
+{
+QShaderNodePort::QShaderNodePort() noexcept
+ : direction(Output)
+{
+}
+
+bool operator==(const QShaderNodePort &lhs, const QShaderNodePort &rhs) noexcept
+{
+ return lhs.direction == rhs.direction
+ && lhs.name == rhs.name;
+}
+}
+
+QT_END_NAMESPACE
diff --git a/src/render/shadergraph/qshadernodeport_p.h b/src/render/shadergraph/qshadernodeport_p.h
new file mode 100644
index 000000000..d83cdb8c4
--- /dev/null
+++ b/src/render/shadergraph/qshadernodeport_p.h
@@ -0,0 +1,91 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtGui 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_QSHADERNODEPORT_P_H
+#define QT3DRENDER_QSHADERNODEPORT_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <Qt3DRender/private/qt3drender_global_p.h>
+
+#include <QtCore/qstring.h>
+#include <QtCore/qvariant.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender
+{
+class QShaderNodePort
+{
+public:
+ enum Direction : char {
+ Input,
+ Output
+ };
+
+ Q_3DRENDERSHARED_PRIVATE_EXPORT QShaderNodePort() noexcept;
+
+ QShaderNodePort::Direction direction;
+ QString name;
+};
+
+Q_3DRENDERSHARED_PRIVATE_EXPORT bool operator==(const QShaderNodePort &lhs, const QShaderNodePort &rhs) noexcept;
+
+inline bool operator!=(const QShaderNodePort &lhs, const QShaderNodePort &rhs) noexcept
+{
+ return !(lhs == rhs);
+}
+
+
+}
+Q_DECLARE_TYPEINFO(Qt3DRender::QShaderNodePort, Q_MOVABLE_TYPE);
+QT_END_NAMESPACE
+
+Q_DECLARE_METATYPE(Qt3DRender::QShaderNodePort)
+
+#endif // QT3DRENDER_QSHADERNODEPORT_P_H
diff --git a/src/render/shadergraph/qshadernodesloader.cpp b/src/render/shadergraph/qshadernodesloader.cpp
new file mode 100644
index 000000000..df34fd1f7
--- /dev/null
+++ b/src/render/shadergraph/qshadernodesloader.cpp
@@ -0,0 +1,293 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtGui 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 "qshadernodesloader_p.h"
+
+#include <QtCore/qdebug.h>
+#include <QtCore/qiodevice.h>
+#include <QtCore/qjsonarray.h>
+#include <QtCore/qjsondocument.h>
+#include <QtCore/qjsonobject.h>
+#include <QtCore/qmetaobject.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender
+{
+QShaderNodesLoader::QShaderNodesLoader() noexcept
+ : m_status(Null),
+ m_device(nullptr)
+{
+}
+
+QShaderNodesLoader::Status QShaderNodesLoader::status() const noexcept
+{
+ return m_status;
+}
+
+QHash<QString, QShaderNode> QShaderNodesLoader::nodes() const noexcept
+{
+ return m_nodes;
+}
+
+QIODevice *QShaderNodesLoader::device() const noexcept
+{
+ return m_device;
+}
+
+void QShaderNodesLoader::setDevice(QIODevice *device) noexcept
+{
+ m_device = device;
+ m_nodes.clear();
+ m_status = !m_device ? Null
+ : (m_device->openMode() & QIODevice::ReadOnly) ? Waiting
+ : Error;
+}
+
+void QShaderNodesLoader::load()
+{
+ if (m_status == Error)
+ return;
+
+ auto error = QJsonParseError();
+ const QJsonDocument document = QJsonDocument::fromJson(m_device->readAll(), &error);
+
+ if (error.error != QJsonParseError::NoError) {
+ qWarning() << "Invalid JSON document:" << error.errorString();
+ m_status = Error;
+ return;
+ }
+
+ if (document.isEmpty() || !document.isObject()) {
+ qWarning() << "Invalid JSON document, root should be an object";
+ m_status = Error;
+ return;
+ }
+
+ const QJsonObject root = document.object();
+ load(root);
+}
+
+void QShaderNodesLoader::load(const QJsonObject &prototypesObject)
+{
+ bool hasError = false;
+
+ for (const QString &property : prototypesObject.keys()) {
+ const QJsonValue nodeValue = prototypesObject.value(property);
+ if (!nodeValue.isObject()) {
+ qWarning() << "Invalid node found";
+ hasError = true;
+ break;
+ }
+
+ const QJsonObject nodeObject = nodeValue.toObject();
+
+ auto node = QShaderNode();
+
+ const QJsonValue inputsValue = nodeObject.value(QStringLiteral("inputs"));
+ if (inputsValue.isArray()) {
+ const QJsonArray inputsArray = inputsValue.toArray();
+ for (const QJsonValue &inputValue : inputsArray) {
+ if (!inputValue.isString()) {
+ qWarning() << "Non-string value in inputs";
+ hasError = true;
+ break;
+ }
+
+ auto input = QShaderNodePort();
+ input.direction = QShaderNodePort::Input;
+ input.name = inputValue.toString();
+ node.addPort(input);
+ }
+ }
+
+ const QJsonValue outputsValue = nodeObject.value(QStringLiteral("outputs"));
+ if (outputsValue.isArray()) {
+ const QJsonArray outputsArray = outputsValue.toArray();
+ for (const QJsonValue &outputValue : outputsArray) {
+ if (!outputValue.isString()) {
+ qWarning() << "Non-string value in outputs";
+ hasError = true;
+ break;
+ }
+
+ auto output = QShaderNodePort();
+ output.direction = QShaderNodePort::Output;
+ output.name = outputValue.toString();
+ node.addPort(output);
+ }
+ }
+
+ const QJsonValue parametersValue = nodeObject.value(QStringLiteral("parameters"));
+ if (parametersValue.isObject()) {
+ const QJsonObject parametersObject = parametersValue.toObject();
+ for (const QString &parameterName : parametersObject.keys()) {
+ const QJsonValue parameterValue = parametersObject.value(parameterName);
+ if (parameterValue.isObject()) {
+ const QJsonObject parameterObject = parameterValue.toObject();
+ const QString type = parameterObject.value(QStringLiteral("type")).toString();
+ const int typeId = QMetaType::type(type.toUtf8());
+
+ const QString value = parameterObject.value(QStringLiteral("value")).toString();
+ auto variant = QVariant(value);
+
+ if (QMetaType::typeFlags(typeId) & QMetaType::IsEnumeration) {
+ const QMetaObject *metaObject = QMetaType::metaObjectForType(typeId);
+ const char *className = metaObject->className();
+ const QByteArray enumName = type.mid(static_cast<int>(qstrlen(className)) + 2).toUtf8();
+ const QMetaEnum metaEnum = metaObject->enumerator(metaObject->indexOfEnumerator(enumName));
+ const int enumValue = metaEnum.keyToValue(value.toUtf8());
+ variant = QVariant(enumValue);
+ variant.convert(typeId);
+ } else {
+ variant.convert(typeId);
+ }
+ node.setParameter(parameterName, variant);
+ } else {
+ node.setParameter(parameterName, parameterValue.toVariant());
+ }
+ }
+ }
+
+ const QJsonValue rulesValue = nodeObject.value(QStringLiteral("rules"));
+ if (rulesValue.isArray()) {
+ const QJsonArray rulesArray = rulesValue.toArray();
+ for (const QJsonValue &ruleValue : rulesArray) {
+ if (!ruleValue.isObject()) {
+ qWarning() << "Rules should be objects";
+ hasError = true;
+ break;
+ }
+
+ const QJsonObject ruleObject = ruleValue.toObject();
+
+ const QJsonValue formatValue = ruleObject.value(QStringLiteral("format"));
+ if (!formatValue.isObject()) {
+ qWarning() << "Format is mandatory in rules and should be an object";
+ hasError = true;
+ break;
+ }
+
+ const QJsonObject formatObject = formatValue.toObject();
+ auto format = QShaderFormat();
+
+ const QJsonValue apiValue = formatObject.value(QStringLiteral("api"));
+ if (!apiValue.isString()) {
+ qWarning() << "Format API must be a string";
+ hasError = true;
+ break;
+ }
+
+ const QString api = apiValue.toString();
+ format.setApi(api == QStringLiteral("OpenGLES") ? QShaderFormat::OpenGLES
+ : api == QStringLiteral("OpenGLNoProfile") ? QShaderFormat::OpenGLNoProfile
+ : api == QStringLiteral("OpenGLCoreProfile") ? QShaderFormat::OpenGLCoreProfile
+ : api == QStringLiteral("OpenGLCompatibilityProfile") ? QShaderFormat::OpenGLCompatibilityProfile
+ : api == QStringLiteral("VulkanFlavoredGLSL") ? QShaderFormat::VulkanFlavoredGLSL
+ : api == QStringLiteral("RHI") ? QShaderFormat::RHI
+ : QShaderFormat::NoApi);
+ if (format.api() == QShaderFormat::NoApi) {
+ qWarning() << "Format API must be one of: OpenGLES, OpenGLNoProfile, OpenGLCoreProfile OpenGLCompatibilityProfile, VulkanFlavoredGLSL or RHI";
+ hasError = true;
+ break;
+ }
+
+ const QJsonValue majorValue = formatObject.value(QStringLiteral("major"));
+ const QJsonValue minorValue = formatObject.value(QStringLiteral("minor"));
+ if (!majorValue.isDouble() || !minorValue.isDouble()) {
+ qWarning() << "Format major and minor version must be values";
+ hasError = true;
+ break;
+ }
+ format.setVersion(QVersionNumber(majorValue.toInt(), minorValue.toInt()));
+
+ const QJsonValue extensionsValue = formatObject.value(QStringLiteral("extensions"));
+ const QJsonArray extensionsArray = extensionsValue.toArray();
+ auto extensions = QStringList();
+ std::transform(extensionsArray.constBegin(), extensionsArray.constEnd(),
+ std::back_inserter(extensions),
+ [] (const QJsonValue &extensionValue) { return extensionValue.toString(); });
+ format.setExtensions(extensions);
+
+ const QString vendor = formatObject.value(QStringLiteral("vendor")).toString();
+ format.setVendor(vendor);
+
+ const QJsonValue substitutionValue = ruleObject.value(QStringLiteral("substitution"));
+ if (!substitutionValue.isString()) {
+ qWarning() << "Substitution needs to be a string";
+ hasError = true;
+ break;
+ }
+
+ // We default out to a Fragment ShaderType if nothing is specified
+ // as that was the initial behavior we introduced
+ const QString shaderType = formatObject.value(QStringLiteral("shaderType")).toString();
+ format.setShaderType(shaderType == QStringLiteral("Fragment") ? QShaderFormat::Fragment
+ : shaderType == QStringLiteral("Vertex") ? QShaderFormat::Vertex
+ : shaderType == QStringLiteral("TessellationControl") ? QShaderFormat::TessellationControl
+ : shaderType == QStringLiteral("TessellationEvaluation") ? QShaderFormat::TessellationEvaluation
+ : shaderType == QStringLiteral("Geometry") ? QShaderFormat::Geometry
+ : shaderType == QStringLiteral("Compute") ? QShaderFormat::Compute
+ : QShaderFormat::Fragment);
+
+ const QByteArray substitution = substitutionValue.toString().toUtf8();
+
+ const QJsonValue snippetsValue = ruleObject.value(QStringLiteral("headerSnippets"));
+ const QJsonArray snippetsArray = snippetsValue.toArray();
+ auto snippets = QByteArrayList();
+ std::transform(snippetsArray.constBegin(), snippetsArray.constEnd(),
+ std::back_inserter(snippets),
+ [] (const QJsonValue &snippetValue) { return snippetValue.toString().toUtf8(); });
+
+ node.addRule(format, QShaderNode::Rule(substitution, snippets));
+ }
+ }
+
+ m_nodes.insert(property, node);
+ }
+
+ if (hasError) {
+ m_status = Error;
+ m_nodes.clear();
+ } else {
+ m_status = Ready;
+ }
+}
+}
+QT_END_NAMESPACE
diff --git a/src/render/shadergraph/qshadernodesloader_p.h b/src/render/shadergraph/qshadernodesloader_p.h
new file mode 100644
index 000000000..80d75a0bc
--- /dev/null
+++ b/src/render/shadergraph/qshadernodesloader_p.h
@@ -0,0 +1,99 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtGui 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_QSHADERNODESLOADER_P_H
+#define QT3DRENDER_QSHADERNODESLOADER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <Qt3DRender/private/qt3drender_global_p.h>
+
+#include <Qt3DRender/private/qshadergraph_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QIODevice;
+
+namespace Qt3DRender
+{
+class QShaderNodesLoader
+{
+public:
+ enum Status : char {
+ Null,
+ Waiting,
+ Ready,
+ Error
+ };
+
+ Q_3DRENDERSHARED_PRIVATE_EXPORT QShaderNodesLoader() noexcept;
+
+ Q_3DRENDERSHARED_PRIVATE_EXPORT Status status() const noexcept;
+ Q_3DRENDERSHARED_PRIVATE_EXPORT QHash<QString, QShaderNode> nodes() const noexcept;
+
+ Q_3DRENDERSHARED_PRIVATE_EXPORT QIODevice *device() const noexcept;
+ Q_3DRENDERSHARED_PRIVATE_EXPORT void setDevice(QIODevice *device) noexcept;
+
+ Q_3DRENDERSHARED_PRIVATE_EXPORT void load();
+ Q_3DRENDERSHARED_PRIVATE_EXPORT void load(const QJsonObject &prototypesObject);
+
+private:
+ Status m_status;
+ QIODevice *m_device;
+ QHash<QString, QShaderNode> m_nodes;
+};
+
+
+}
+Q_DECLARE_TYPEINFO(Qt3DRender::QShaderNodesLoader, Q_MOVABLE_TYPE);
+QT_END_NAMESPACE
+
+Q_DECLARE_METATYPE(Qt3DRender::QShaderNodesLoader)
+Q_DECLARE_METATYPE(Qt3DRender::QShaderNodesLoader::Status)
+
+#endif // QT3DRENDER_QSHADERNODESLOADER_P_H
diff --git a/src/render/shadergraph/shadergraph.pri b/src/render/shadergraph/shadergraph.pri
new file mode 100644
index 000000000..27bb8c6e9
--- /dev/null
+++ b/src/render/shadergraph/shadergraph.pri
@@ -0,0 +1,21 @@
+INCLUDEPATH += $$PWD
+
+HEADERS += \
+ $$PWD/qshaderformat_p.h \
+ $$PWD/qshadergenerator_p.h \
+ $$PWD/qshadergraphloader_p.h \
+ $$PWD/qshadergraph_p.h \
+ $$PWD/qshaderlanguage_p.h \
+ $$PWD/qshadernode_p.h \
+ $$PWD/qshadernodeport_p.h \
+ $$PWD/qshadernodesloader_p.h
+
+SOURCES += \
+ $$PWD/qshaderformat.cpp \
+ $$PWD/qshadergenerator.cpp \
+ $$PWD/qshadergraph.cpp \
+ $$PWD/qshadergraphloader.cpp \
+ $$PWD/qshaderlanguage.cpp \
+ $$PWD/qshadernode.cpp \
+ $$PWD/qshadernodeport.cpp \
+ $$PWD/qshadernodesloader.cpp
diff --git a/src/render/surfaces/surfaces.pri b/src/render/surfaces/surfaces.pri
new file mode 100644
index 000000000..0d25a36c6
--- /dev/null
+++ b/src/render/surfaces/surfaces.pri
@@ -0,0 +1,9 @@
+INCLUDEPATH += $$PWD
+
+qtConfig(vulkan) {
+HEADERS += \
+ $$PWD/vulkaninstance_p.h \
+
+SOURCES += \
+ $$PWD/vulkaninstance.cpp
+}
diff --git a/src/render/surfaces/vulkaninstance.cpp b/src/render/surfaces/vulkaninstance.cpp
new file mode 100644
index 000000000..adbeb3074
--- /dev/null
+++ b/src/render/surfaces/vulkaninstance.cpp
@@ -0,0 +1,75 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtGui 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 "vulkaninstance_p.h"
+#include <QVulkanInstance>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+QVulkanInstance &staticVulkanInstance() noexcept
+{
+ static QVulkanInstance* vkInstance = []
+ {
+ QVulkanInstance* v = new QVulkanInstance;
+#ifndef Q_OS_ANDROID
+ v->setLayers({ "VK_LAYER_LUNARG_standard_validation" });
+#else
+ v->setLayers(QByteArrayList()
+ << "VK_LAYER_GOOGLE_threading"
+ << "VK_LAYER_LUNARG_parameter_validation"
+ << "VK_LAYER_LUNARG_object_tracker"
+ << "VK_LAYER_LUNARG_core_validation"
+ << "VK_LAYER_LUNARG_image"
+ << "VK_LAYER_LUNARG_swapchain"
+ << "VK_LAYER_GOOGLE_unique_objects");
+#endif
+
+ if (!v->create()) {
+ qWarning("Failed to create Vulkan instance");
+ }
+ return v;
+ }();
+ return *vkInstance;
+}
+
+} // Qt3DRender
+
+QT_END_NAMESPACE
diff --git a/src/render/surfaces/vulkaninstance_p.h b/src/render/surfaces/vulkaninstance_p.h
new file mode 100644
index 000000000..f46939ce6
--- /dev/null
+++ b/src/render/surfaces/vulkaninstance_p.h
@@ -0,0 +1,55 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtGui 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_VULKANINSTANCE_P_H
+#define QT3DRENDER_VULKANINSTANCE_P_H
+
+#include <QtGui/qtguiglobal.h>
+#include <Qt3DRender/private/qt3drender_global_p.h>
+QT_BEGIN_NAMESPACE
+#if QT_CONFIG(vulkan)
+class QVulkanInstance;
+namespace Qt3DRender {
+Q_3DRENDERSHARED_PRIVATE_EXPORT
+QVulkanInstance& staticVulkanInstance() noexcept;
+} // Qt3DRender
+#endif
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_VULKANINSTANCE_P_H
diff --git a/src/render/texture/qabstracttexture.cpp b/src/render/texture/qabstracttexture.cpp
index c8ed9186e..885f2d809 100644
--- a/src/render/texture/qabstracttexture.cpp
+++ b/src/render/texture/qabstracttexture.cpp
@@ -788,8 +788,9 @@ void QAbstractTexture::removeTextureImage(QAbstractTextureImage *textureImage)
{
Q_ASSERT(textureImage);
Q_D(QAbstractTexture);
+ if (!d->m_textureImages.removeOne(textureImage))
+ return;
d->update();
- d->m_textureImages.removeOne(textureImage);
// Remove bookkeeping connection
d->unregisterDestructionHelper(textureImage);
}