diff options
Diffstat (limited to 'src/render')
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 ¶meterName : 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 ®exp, + int &variable, + const QString &replacement) noexcept { + int matchStart = 0; + do { + matchStart = deincluded.indexOf(regexp, matchStart); + if (matchStart != -1) { + const auto match = regexp.match(deincluded.midRef(matchStart)); + const auto length = match.capturedLength(0); + + deincluded.replace(matchStart, length, replacement.arg(variable++)); + } + } while (matchStart != -1); + }; + + // 1. Handle uniforms + { + thread_local const QRegularExpression bindings( + QStringLiteral("binding\\s?+=\\s?+auto")); + + replaceAndIncrement(bindings, currentBinding, QStringLiteral("binding = %1")); + } + + // 2. Handle inputs + { + thread_local const QRegularExpression inLocations( + QStringLiteral("location\\s?+=\\s?+auto\\s?+\\)\\s?+in\\s+")); + + replaceAndIncrement(inLocations, currentInputLocation, + QStringLiteral("location = %1) in ")); + } + + // 3. Handle outputs + { + thread_local const QRegularExpression outLocations( + QStringLiteral("location\\s?+=\\s?+auto\\s?+\\)\\s?+out\\s+")); + + replaceAndIncrement(outLocations, currentOutputLocation, + QStringLiteral("location = %1) out ")); + } + + code << deincluded.toUtf8(); + } + + int currentInputLocation { 0 }; + int currentOutputLocation { 0 }; + int currentBinding { 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(), + [¤tEdges, &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 ¶meterName : 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 ¶meterName : 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); } |