diff options
Diffstat (limited to 'src/render/backend')
43 files changed, 1604 insertions, 252 deletions
diff --git a/src/render/backend/abstractrenderer_p.h b/src/render/backend/abstractrenderer_p.h index 8b17cbf45..2f7545454 100644 --- a/src/render/backend/abstractrenderer_p.h +++ b/src/render/backend/abstractrenderer_p.h @@ -56,6 +56,8 @@ #include <Qt3DCore/qnodeid.h> #include <QtGui/qsurfaceformat.h> +#include <QtGui/qopenglcontext.h> + QT_BEGIN_NAMESPACE class QSurface; @@ -137,6 +139,7 @@ public: virtual QVector<Qt3DCore::QAspectJobPtr> renderBinJobs() = 0; virtual Qt3DCore::QAspectJobPtr pickBoundingVolumeJob() = 0; virtual Qt3DCore::QAspectJobPtr syncTextureLoadingJob() = 0; + virtual Qt3DCore::QAspectJobPtr expandBoundingVolumeJob() = 0; virtual void setSceneRoot(Qt3DCore::QBackendNodeFactory *factory, Entity *root) = 0; @@ -153,6 +156,7 @@ public: virtual void setOffscreenSurfaceHelper(OffscreenSurfaceHelper *helper) = 0; virtual QSurfaceFormat format() = 0; + virtual QOpenGLContext *shareContext() const = 0; }; Q_DECLARE_OPERATORS_FOR_FLAGS(AbstractRenderer::BackendNodeDirtySet) diff --git a/src/render/backend/attachmentpack.cpp b/src/render/backend/attachmentpack.cpp index d2bb5ee9a..6dee7587b 100644 --- a/src/render/backend/attachmentpack.cpp +++ b/src/render/backend/attachmentpack.cpp @@ -60,7 +60,7 @@ AttachmentPack::AttachmentPack(const RenderTargetSelector *selector, const Rende for (Qt3DCore::QNodeId outputId : outputIds) { const RenderTargetOutput *output = attachmentManager->lookupResource(outputId); if (output) - m_attachments.append(output->attachment()); + m_attachments.append(*output->attachment()); } // Create actual DrawBuffers list that is used for glDrawBuffers diff --git a/src/render/backend/backendnode.cpp b/src/render/backend/backendnode.cpp index 7054db6a4..0dc8da237 100644 --- a/src/render/backend/backendnode.cpp +++ b/src/render/backend/backendnode.cpp @@ -37,7 +37,10 @@ ** ****************************************************************************/ -#include "backendnode_p.h" +#include <private/backendnode_p.h> +#include <private/renderer_p.h> +#include <private/resourceaccessor_p.h> +#include <private/nodemanagers_p.h> QT_BEGIN_NAMESPACE @@ -61,12 +64,23 @@ void BackendNode::setRenderer(AbstractRenderer *renderer) m_renderer = renderer; } +AbstractRenderer *BackendNode::renderer() const +{ + return m_renderer; +} + void BackendNode::markDirty(AbstractRenderer::BackendNodeDirtySet changes) { Q_ASSERT(m_renderer); m_renderer->markDirty(changes, this); } +QSharedPointer<RenderBackendResourceAccessor> BackendNode::resourceAccessor() +{ + Render::Renderer *r = static_cast<Render::Renderer *>(renderer()); + return r->nodeManagers()->resourceAccessor(); +} + } // namespace Render } // namespace Qt3DRender diff --git a/src/render/backend/backendnode_p.h b/src/render/backend/backendnode_p.h index 5688c2412..8db68b11d 100644 --- a/src/render/backend/backendnode_p.h +++ b/src/render/backend/backendnode_p.h @@ -54,6 +54,7 @@ #include <Qt3DRender/qt3drender_global.h> #include <Qt3DCore/qbackendnode.h> #include <Qt3DRender/private/abstractrenderer_p.h> +#include <private/qt3drender_global_p.h> QT_BEGIN_NAMESPACE @@ -61,13 +62,18 @@ namespace Qt3DRender { namespace Render { -class Q_AUTOTEST_EXPORT BackendNode : public Qt3DCore::QBackendNode +class RenderBackendResourceAccessor; + +class QT3DRENDERSHARED_PRIVATE_EXPORT BackendNode : public Qt3DCore::QBackendNode { public: BackendNode(Qt3DCore::QBackendNode::Mode mode = ReadOnly); ~BackendNode(); void setRenderer(AbstractRenderer *renderer); + AbstractRenderer *renderer() const; + + QSharedPointer<RenderBackendResourceAccessor> resourceAccessor(); protected: void markDirty(AbstractRenderer::BackendNodeDirtySet changes); diff --git a/src/render/backend/bufferutils_p.h b/src/render/backend/bufferutils_p.h new file mode 100644 index 000000000..2bb35fac6 --- /dev/null +++ b/src/render/backend/bufferutils_p.h @@ -0,0 +1,115 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Paul Lemire paul.lemire350@gmail.com +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DRENDER_RENDER_BUFFERUTILS_P_H +#define QT3DRENDER_RENDER_BUFFERUTILS_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <Qt3DRender/QAttribute> +#include <QByteArray> + +QT_BEGIN_NAMESPACE + + +namespace Qt3DRender { + +namespace Render { + +class GeometryRenderer; +class NodeManagers; +class Attribute; +class Buffer; + +struct BufferInfo +{ + BufferInfo() + : type(QAttribute::VertexBaseType::Float) + , dataSize(0) + , count(0) + , byteStride(0) + , byteOffset(0) + {} + + QByteArray data; + QAttribute::VertexBaseType type; + uint dataSize; + uint count; + uint byteStride; + uint byteOffset; +}; + + +namespace BufferTypeInfo { + + template <QAttribute::VertexBaseType> struct EnumToType; + template <> struct EnumToType<QAttribute::Byte> { typedef const char type; }; + template <> struct EnumToType<QAttribute::UnsignedByte> { typedef const uchar type; }; + template <> struct EnumToType<QAttribute::Short> { typedef const short type; }; + template <> struct EnumToType<QAttribute::UnsignedShort> { typedef const ushort type; }; + template <> struct EnumToType<QAttribute::Int> { typedef const int type; }; + template <> struct EnumToType<QAttribute::UnsignedInt> { typedef const uint type; }; + template <> struct EnumToType<QAttribute::Float> { typedef const float type; }; + template <> struct EnumToType<QAttribute::Double> { typedef const double type; }; + + template<QAttribute::VertexBaseType v> + typename EnumToType<v>::type *castToType(const QByteArray &u, uint byteOffset) + { + return reinterpret_cast< typename EnumToType<v>::type *>(u.constData() + byteOffset); + } + +} // namespace BufferTypeInfo + +} // namespace Render + +} // namespace Qt3DRender + +QT_END_NAMESPACE + + +#endif // QT3DRENDER_RENDER_BUFFERUTILS_P_H diff --git a/src/render/backend/buffervisitor_p.h b/src/render/backend/buffervisitor_p.h new file mode 100644 index 000000000..7149e21ae --- /dev/null +++ b/src/render/backend/buffervisitor_p.h @@ -0,0 +1,212 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DRENDER_RENDER_BUFFERVISITOR_P_H +#define QT3DRENDER_RENDER_BUFFERVISITOR_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <Qt3DCore/qnodeid.h> +#include <Qt3DRender/QAttribute> +#include <Qt3DRender/private/trianglesvisitor_p.h> +#include <Qt3DRender/private/attribute_p.h> +#include <Qt3DRender/private/buffer_p.h> +#include <Qt3DRender/private/bufferutils_p.h> +#include <Qt3DRender/private/geometryrenderer_p.h> +#include <Qt3DRender/private/geometry_p.h> +#include <Qt3DRender/private/managers_p.h> +#include <Qt3DRender/private/nodemanagers_p.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3DCore { +class QEntity; +} + +namespace Qt3DRender { +namespace Render { + + +template <typename ValueType, QAttribute::VertexBaseType VertexBaseType, uint dataSize> +class Q_AUTOTEST_EXPORT BufferVisitor +{ +public: + explicit BufferVisitor(NodeManagers *manager) + : m_manager(manager) + { + } + virtual ~BufferVisitor() { } + + virtual void visit(uint ndx, ValueType x) { + Q_UNUSED(ndx); Q_UNUSED(x); + } + virtual void visit(uint ndx, ValueType x, ValueType y) { + Q_UNUSED(ndx); Q_UNUSED(x); Q_UNUSED(y); + } + virtual void visit(uint ndx, ValueType x, ValueType y, ValueType z) { + Q_UNUSED(ndx); Q_UNUSED(x); Q_UNUSED(y); Q_UNUSED(z); + } + virtual void visit(uint ndx, ValueType x, ValueType y, ValueType z, ValueType w) { + Q_UNUSED(ndx); Q_UNUSED(x); Q_UNUSED(y); Q_UNUSED(z); Q_UNUSED(w); + } + + bool apply(const GeometryRenderer *renderer, const QString &attributeName) + { + if (renderer == nullptr || renderer->instanceCount() != 1) { + return false; + } + + Geometry *geom = m_manager->lookupResource<Geometry, GeometryManager>(renderer->geometryId()); + + if (!geom) + return false; + + Attribute *attribute = nullptr; + + const auto attrIds = geom->attributes(); + for (const Qt3DCore::QNodeId attrId : attrIds) { + attribute = m_manager->lookupResource<Attribute, AttributeManager>(attrId); + if (attribute){ + if (attribute->name() == attributeName + || (attributeName == QStringLiteral("default") + && attribute->name() == QAttribute::defaultTextureCoordinateAttributeName())) { + break; + } + } + attribute = nullptr; + } + + if (!attribute) + return false; + + return apply(attribute); + } + + bool apply(Qt3DRender::Render::Attribute *attribute) + { + if (attribute->vertexBaseType() != VertexBaseType) + return false; + if (attribute->vertexSize() < dataSize) + return false; + + auto data = m_manager->lookupResource<Buffer, BufferManager>(attribute->bufferId())->data(); + auto buffer = BufferTypeInfo::castToType<VertexBaseType>(data, attribute->byteOffset()); + switch (dataSize) { + case 1: traverseCoordinates1(buffer, attribute->byteStride(), attribute->count()); break; + case 2: traverseCoordinates2(buffer, attribute->byteStride(), attribute->count()); break; + case 3: traverseCoordinates3(buffer, attribute->byteStride(), attribute->count()); break; + case 4: traverseCoordinates4(buffer, attribute->byteStride(), attribute->count()); break; + default: Q_UNREACHABLE(); + } + + return true; + } + +protected: + + template <typename Coordinate> + void traverseCoordinates1(Coordinate *coordinates, + const uint byteStride, + const uint count) + { + const uint stride = byteStride / sizeof(Coordinate); + for (uint ndx = 0; ndx < count; ++ndx) { + visit(ndx, coordinates[0]); + coordinates += stride; + } + } + + template <typename Coordinate> + void traverseCoordinates2(Coordinate *coordinates, + const uint byteStride, + const uint count) + { + const uint stride = byteStride / sizeof(Coordinate); + for (uint ndx = 0; ndx < count; ++ndx) { + visit(ndx, coordinates[0], coordinates[1]); + coordinates += stride; + } + } + + template <typename Coordinate> + void traverseCoordinates3(Coordinate *coordinates, + const uint byteStride, + const uint count) + { + const uint stride = byteStride / sizeof(Coordinate); + for (uint ndx = 0; ndx < count; ++ndx) { + visit(ndx, coordinates[0], coordinates[1], coordinates[2]); + coordinates += stride; + } + } + + template <typename Coordinate> + void traverseCoordinates4(Coordinate *coordinates, + const uint byteStride, + const uint count) + { + const uint stride = byteStride / sizeof(Coordinate); + for (uint ndx = 0; ndx < count; ++ndx) { + visit(ndx, coordinates[0], coordinates[1], coordinates[2], coordinates[3]); + coordinates += stride; + } + } + + NodeManagers *m_manager; +}; + +typedef BufferVisitor<float, QAttribute::Float, 3> Buffer3fVisitor; + +} // namespace Render + +} // namespace Qt3DRender + +QT_END_NAMESPACE + + +#endif // QT3DRENDER_RENDER_BUFFERVISITOR_P_H diff --git a/src/render/backend/cameralens.cpp b/src/render/backend/cameralens.cpp index e60a32422..ef71507eb 100644 --- a/src/render/backend/cameralens.cpp +++ b/src/render/backend/cameralens.cpp @@ -39,8 +39,14 @@ #include "cameralens_p.h" #include <Qt3DRender/qcameralens.h> +#include <Qt3DRender/private/nodemanagers_p.h> +#include <Qt3DRender/private/managers_p.h> #include <Qt3DRender/private/qcameralens_p.h> #include <Qt3DRender/private/renderlogging_p.h> +#include <Qt3DRender/private/renderer_p.h> +#include <Qt3DRender/private/entity_p.h> +#include <Qt3DRender/private/sphere_p.h> +#include <Qt3DRender/private/computefilteredboundingvolumejob_p.h> #include <Qt3DCore/qentity.h> #include <Qt3DCore/qpropertyupdatedchange.h> #include <Qt3DCore/qtransform.h> @@ -52,8 +58,35 @@ using namespace Qt3DCore; namespace Qt3DRender { namespace Render { + +namespace { + +class GetBoundingVolumeWithoutCameraJob : public ComputeFilteredBoundingVolumeJob +{ +public: + GetBoundingVolumeWithoutCameraJob(CameraLens *lens, + QNodeCommand::CommandId commandId) + : m_lens(lens), m_commandId(commandId) + { + } + +protected: + void finished(const Sphere &sphere) override + { + m_lens->notifySceneBoundingVolume(sphere, m_commandId); + } + +private: + CameraLens *m_lens; + QNodeCommand::CommandId m_commandId; +}; + +} // namespace + CameraLens::CameraLens() - : BackendNode() + : BackendNode(QBackendNode::ReadWrite) + , m_renderAspect(nullptr) + , m_exposure(0.0f) { } @@ -67,11 +100,52 @@ void CameraLens::cleanup() QBackendNode::setEnabled(false); } +void CameraLens::setRenderAspect(QRenderAspect *renderAspect) +{ + m_renderAspect = renderAspect; +} + void CameraLens::initializeFromPeer(const Qt3DCore::QNodeCreatedChangeBasePtr &change) { const auto typedChange = qSharedPointerCast<Qt3DCore::QNodeCreatedChange<QCameraLensData>>(change); const auto &data = typedChange->data; m_projection = data.projectionMatrix; + m_exposure = data.exposure; +} + +void CameraLens::computeSceneBoundingVolume(QNodeId entityId, + QNodeId cameraId, + QNodeCommand::CommandId commandId) +{ + if (!m_renderer || !m_renderAspect) + return; + NodeManagers *nodeManagers = m_renderer->nodeManagers(); + + Entity *root = m_renderer->sceneRoot(); + if (!entityId.isNull()) + root = nodeManagers->renderNodesManager()->lookupResource(entityId); + if (!root) + return; + + Entity *camNode = nodeManagers->renderNodesManager()->lookupResource(cameraId); + ComputeFilteredBoundingVolumeJobPtr job(new GetBoundingVolumeWithoutCameraJob(this, commandId)); + job->addDependency(m_renderer->expandBoundingVolumeJob()); + job->setRoot(root); + job->ignoreSubTree(camNode); + m_renderAspect->scheduleSingleShotJob(job); +} + +void CameraLens::notifySceneBoundingVolume(const Sphere &sphere, QNodeCommand::CommandId commandId) +{ + if (m_pendingViewAllCommand != commandId) + return; + if (sphere.radius() > 0.f) { + QVector<float> data = { sphere.center().x(), sphere.center().y(), sphere.center().z(), + sphere.radius() }; + QVariant v; + v.setValue(data); + sendCommand(QLatin1Literal("ViewAll"), v, m_pendingViewAllCommand); + } } void CameraLens::setProjection(const QMatrix4x4 &projection) @@ -79,6 +153,11 @@ void CameraLens::setProjection(const QMatrix4x4 &projection) m_projection = projection; } +void CameraLens::setExposure(float exposure) +{ + m_exposure = exposure; +} + void CameraLens::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e) { switch (e->type()) { @@ -88,18 +167,63 @@ void CameraLens::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e) if (propertyChange->propertyName() == QByteArrayLiteral("projectionMatrix")) { QMatrix4x4 projectionMatrix = propertyChange->value().value<QMatrix4x4>(); m_projection = projectionMatrix; + } else if (propertyChange->propertyName() == QByteArrayLiteral("exposure")) { + setExposure(propertyChange->value().toFloat()); } markDirty(AbstractRenderer::AllDirty); } break; + case CommandRequested: { + QNodeCommandPtr command = qSharedPointerCast<QNodeCommand>(e); + + if (command->name() == QLatin1Literal("QueryRootBoundingVolume")) { + m_pendingViewAllCommand = command->commandId(); + QVariant v = command->data(); + QNodeId id = v.value<QNodeId>(); + computeSceneBoundingVolume({}, id, command->commandId()); + } else if (command->name() == QLatin1Literal("QueryEntityBoundingVolume")) { + m_pendingViewAllCommand = command->commandId(); + QVariant v = command->data(); + QVector<QNodeId> ids = v.value<QVector<QNodeId>>(); + if (ids.size() == 2) + computeSceneBoundingVolume(ids[0], ids[1], command->commandId()); + } + } + break; + default: break; } BackendNode::sceneChangeEvent(e); } +CameraLensFunctor::CameraLensFunctor(AbstractRenderer *renderer, QRenderAspect *renderAspect) + : m_manager(renderer->nodeManagers()->manager<CameraLens, CameraManager>()) + , m_renderer(renderer) + , m_renderAspect(renderAspect) +{ +} + +QBackendNode *CameraLensFunctor::create(const QNodeCreatedChangeBasePtr &change) const +{ + CameraLens *backend = m_manager->getOrCreateResource(change->subjectId()); + backend->setRenderer(m_renderer); + backend->setRenderAspect(m_renderAspect); + return backend; +} + +QBackendNode *CameraLensFunctor::get(QNodeId id) const +{ + return m_manager->lookupResource(id); +} + +void CameraLensFunctor::destroy(QNodeId id) const +{ + m_manager->releaseResource(id); +} + } // namespace Render } // namespace Qt3DRender diff --git a/src/render/backend/cameralens_p.h b/src/render/backend/cameralens_p.h index e5268da53..4bf147f48 100644 --- a/src/render/backend/cameralens_p.h +++ b/src/render/backend/cameralens_p.h @@ -52,6 +52,7 @@ // #include <Qt3DRender/private/backendnode_p.h> +#include <Qt3DCore/private/qnodecommand_p.h> #include <QMatrix4x4> #include <QRectF> @@ -62,6 +63,21 @@ namespace Qt3DRender { namespace Render { class CameraManager; +class Sphere; + +class CameraLensFunctor : public Qt3DCore::QBackendNodeMapper +{ +public: + explicit CameraLensFunctor(AbstractRenderer *renderer, QRenderAspect *renderAspect); + Qt3DCore::QBackendNode *create(const Qt3DCore::QNodeCreatedChangeBasePtr &change) const Q_DECL_OVERRIDE; + Qt3DCore::QBackendNode *get(Qt3DCore::QNodeId id) const Q_DECL_OVERRIDE; + void destroy(Qt3DCore::QNodeId id) const Q_DECL_OVERRIDE; + +private: + CameraManager *m_manager; + AbstractRenderer *m_renderer; + QRenderAspect *m_renderAspect; +}; class CameraLens : public BackendNode { @@ -70,15 +86,27 @@ public: ~CameraLens(); void cleanup(); + void setRenderAspect(QRenderAspect* renderAspect); + void setProjection(const QMatrix4x4 &projection); inline QMatrix4x4 projection() const { return m_projection; } + void setExposure(float exposure); + inline float exposure() const { return m_exposure; } + void sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e) Q_DECL_OVERRIDE; + void notifySceneBoundingVolume(const Sphere &sphere, Qt3DCore::QNodeCommand::CommandId commandId); private: void initializeFromPeer(const Qt3DCore::QNodeCreatedChangeBasePtr &change) Q_DECL_FINAL; + void computeSceneBoundingVolume(Qt3DCore::QNodeId entityId, + Qt3DCore::QNodeId cameraId, + Qt3DCore::QNodeCommand::CommandId commandId); + QRenderAspect *m_renderAspect; QMatrix4x4 m_projection; + Qt3DCore::QNodeCommand::CommandId m_pendingViewAllCommand; + float m_exposure; }; } // namespace Render diff --git a/src/render/backend/commandexecuter.cpp b/src/render/backend/commandexecuter.cpp index 2f13b27ea..84bf7455a 100644 --- a/src/render/backend/commandexecuter.cpp +++ b/src/render/backend/commandexecuter.cpp @@ -34,9 +34,8 @@ ** ****************************************************************************/ -#ifdef QT3D_JOBS_RUN_STATS - #include "commandexecuter_p.h" + #include <Qt3DRender/private/renderer_p.h> #include <Qt3DCore/private/qabstractaspect_p.h> #include <Qt3DCore/qbackendnode.h> @@ -388,5 +387,3 @@ QVariant CommandExecuter::executeCommand(const QStringList &args) } // Qt3DRenderer QT_END_NAMESPACE - -#endif diff --git a/src/render/backend/commandexecuter_p.h b/src/render/backend/commandexecuter_p.h index 8135d97d9..896164543 100644 --- a/src/render/backend/commandexecuter_p.h +++ b/src/render/backend/commandexecuter_p.h @@ -48,8 +48,6 @@ // We mean it. // -#ifdef QT3D_JOBS_RUN_STATS - #include <QVector> #include <QVariant> @@ -92,6 +90,4 @@ private: QT_END_NAMESPACE -#endif // QT3D_JOBS_RUN_STATS - #endif // QT3DRENDER_DEBUG_COMMANDEXECUTER_H diff --git a/src/render/backend/commandthread.cpp b/src/render/backend/commandthread.cpp new file mode 100644 index 000000000..f91cff017 --- /dev/null +++ b/src/render/backend/commandthread.cpp @@ -0,0 +1,129 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "commandthread_p.h" +#include <QOpenGLContext> + +QT_BEGIN_NAMESPACE + +namespace Qt3DRender { + +namespace Render { + +CommandThread::CommandThread(Renderer *renderer) + : QThread() + , m_renderer(renderer) + , m_waitForStartSemaphore(0) + , m_initializedSemaphore(0) +{ +} + +CommandThread::~CommandThread() +{ +} + +// Called by RenderThread or MainThread (Scene3d) +void CommandThread::initialize(QOpenGLContext *mainContext) +{ + // Start the thread + start(); + + // Wait for thread to be started + m_waitForStartSemaphore.acquire(); + + m_mainContext = mainContext; + Q_ASSERT(m_mainContext); + + // Allow thread to proceed + m_initializedSemaphore.release(); +} + +// Called by RenderThread of MainThread (Scene3D) +void CommandThread::shutdown() +{ + // Tell thread to exit event loop + QThread::quit(); + + // Wait for thread to exit + wait(); + + // Reset semaphores (in case we ever want to restart) + m_waitForStartSemaphore.release(m_waitForStartSemaphore.available()); + m_initializedSemaphore.release(m_initializedSemaphore.available()); + m_localContext.reset(); +} + +// Any thread can call this, this is a blocking command +void CommandThread::executeCommand(Command *command) +{ + if (!isRunning()) + return; + QMetaObject::invokeMethod(this, + "executeCommandInternal", + Qt::BlockingQueuedConnection, + Q_ARG(Command *, command)); +} + +void CommandThread::run() +{ + // Allow initialize to proceed + m_waitForStartSemaphore.release(); + + // Wait for initialize to be completed + m_initializedSemaphore.acquire(); + + // Initialize shared context and resources for the thread + m_localContext.reset(new QOpenGLContext()); + m_localContext->setShareContext(m_mainContext); + + // Launch exec loop + QThread::exec(); +} + +// Executed in the Command Thread +void CommandThread::executeCommandInternal(Command *command) +{ + command->execute(m_renderer, m_localContext.data()); +} + +} // Render + +} // Qt3DRender + +QT_END_NAMESPACE diff --git a/src/render/backend/commandthread_p.h b/src/render/backend/commandthread_p.h new file mode 100644 index 000000000..1955d13ac --- /dev/null +++ b/src/render/backend/commandthread_p.h @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DRENDER_RENDER_COMMANDTHREAD_P_H +#define QT3DRENDER_RENDER_COMMANDTHREAD_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/QThread> +#include <QtCore/QSemaphore> + +QT_BEGIN_NAMESPACE + +class QOpenGLContext; + +namespace Qt3DRender { + +namespace Render { + +class Renderer; + +class Command +{ +public: + virtual void execute(Renderer *renderer, QOpenGLContext *localContext) = 0; +}; + +class CommandThread : public QThread +{ + Q_OBJECT +public: + explicit CommandThread(Renderer *renderer); + ~CommandThread(); + + Render::Renderer* renderer() const { return m_renderer; } + + void initialize(QOpenGLContext *mainContext); + void shutdown(); + + void executeCommand(Command *command); + +private: + void run() Q_DECL_OVERRIDE; + Q_INVOKABLE void executeCommandInternal(Command *command); + +private: + Renderer* m_renderer; + QSemaphore m_waitForStartSemaphore; + QSemaphore m_initializedSemaphore; + QOpenGLContext *m_mainContext; + QScopedPointer<QOpenGLContext> m_localContext; +}; + +} // Render + +} // Qt3DRender + +QT_END_NAMESPACE + +#endif // QT3DRENDER_RENDER_COMMANDTHREAD_P_H diff --git a/src/render/backend/entity.cpp b/src/render/backend/entity.cpp index 42233c85a..898c1e36e 100644 --- a/src/render/backend/entity.cpp +++ b/src/render/backend/entity.cpp @@ -41,6 +41,7 @@ #include <Qt3DRender/private/managers_p.h> #include <Qt3DRender/private/nodemanagers_p.h> #include <Qt3DRender/qabstractlight.h> +#include <Qt3DRender/qenvironmentlight.h> #include <Qt3DRender/qlayer.h> #include <Qt3DRender/qlevelofdetail.h> #include <Qt3DRender/qmaterial.h> @@ -118,6 +119,7 @@ void Entity::cleanup() m_levelOfDetailComponents.clear(); m_shaderDataComponents.clear(); m_lightComponents.clear(); + m_environmentLightComponents.clear(); m_localBoundingVolume.reset(); m_worldBoundingVolume.reset(); m_worldBoundingVolumeWithChildren.reset(); @@ -174,6 +176,7 @@ void Entity::initializeFromPeer(const QNodeCreatedChangeBasePtr &change) m_levelOfDetailComponents.clear(); m_shaderDataComponents.clear(); m_lightComponents.clear(); + m_environmentLightComponents.clear(); m_localBoundingVolume.reset(new Sphere(peerId())); m_worldBoundingVolume.reset(new Sphere(peerId())); m_worldBoundingVolumeWithChildren.reset(new Sphere(peerId())); @@ -293,6 +296,8 @@ void Entity::addComponent(Qt3DCore::QComponent *component) m_materialComponent = component->id(); } else if (qobject_cast<QAbstractLight *>(component) != nullptr) { m_lightComponents.append(component->id()); + } else if (qobject_cast<QEnvironmentLight *>(component) != nullptr) { + m_environmentLightComponents.append(component->id()); } else if (qobject_cast<QShaderData *>(component) != nullptr) { m_shaderDataComponents.append(component->id()); } else if (qobject_cast<QGeometryRenderer *>(component) != nullptr) { @@ -326,6 +331,8 @@ void Entity::addComponent(Qt3DCore::QNodeIdTypePair idAndType) m_materialComponent = id; } else if (type->inherits(&QAbstractLight::staticMetaObject)) { // QAbstractLight subclasses QShaderData m_lightComponents.append(id); + } else if (type->inherits(&QEnvironmentLight::staticMetaObject)) { + m_environmentLightComponents.append(id); } else if (type->inherits(&QShaderData::staticMetaObject)) { m_shaderDataComponents.append(id); } else if (type->inherits(&QGeometryRenderer::staticMetaObject)) { @@ -363,6 +370,8 @@ void Entity::removeComponent(Qt3DCore::QNodeId nodeId) // m_boundingVolumeDebugComponent = QNodeId(); } else if (m_lightComponents.contains(nodeId)) { m_lightComponents.removeAll(nodeId); + } else if (m_environmentLightComponents.contains(nodeId)) { + m_environmentLightComponents.removeAll(nodeId); } else if (m_computeComponent == nodeId) { m_computeComponent = QNodeId(); } @@ -457,6 +466,16 @@ QVector<HLight> Entity::componentsHandle<Light>() const } template<> +QVector<HEnvironmentLight> Entity::componentsHandle<EnvironmentLight>() const +{ + QVector<HEnvironmentLight> lightHandles; + lightHandles.reserve(m_environmentLightComponents.size()); + for (QNodeId id : m_environmentLightComponents) + lightHandles.append(m_nodeManagers->environmentLightManager()->lookupHandle(id)); + return lightHandles; +} + +template<> HComputeCommand Entity::componentHandle<ComputeCommand>() const { return m_nodeManagers->computeJobManager()->lookupHandle(m_computeComponent); @@ -534,6 +553,16 @@ QVector<Light *> Entity::renderComponents<Light>() const return lights; } +template<> +QVector<EnvironmentLight *> Entity::renderComponents<EnvironmentLight>() const +{ + QVector<EnvironmentLight *> lights; + lights.reserve(m_environmentLightComponents.size()); + for (QNodeId id : m_environmentLightComponents) + lights.append(m_nodeManagers->environmentLightManager()->lookupResource(id)); + return lights; +} + //template<> //BoundingVolumeDebug *Entity::renderComponent<BoundingVolumeDebug>() const //{ @@ -581,6 +610,9 @@ QNodeId Entity::componentUuid<ComputeCommand>() const { return m_computeComponen template<> QVector<Qt3DCore::QNodeId> Entity::componentsUuid<Light>() const { return m_lightComponents; } +template<> +QVector<Qt3DCore::QNodeId> Entity::componentsUuid<EnvironmentLight>() const { return m_environmentLightComponents; } + RenderEntityFunctor::RenderEntityFunctor(AbstractRenderer *renderer, NodeManagers *manager) : m_nodeManagers(manager) , m_renderer(renderer) diff --git a/src/render/backend/entity_p.h b/src/render/backend/entity_p.h index c86019444..4619314ad 100644 --- a/src/render/backend/entity_p.h +++ b/src/render/backend/entity_p.h @@ -190,6 +190,7 @@ private: QVector<Qt3DCore::QNodeId> m_levelOfDetailComponents; QVector<Qt3DCore::QNodeId> m_shaderDataComponents; QVector<Qt3DCore::QNodeId> m_lightComponents; + QVector<Qt3DCore::QNodeId> m_environmentLightComponents; Qt3DCore::QNodeId m_geometryRendererComponent; Qt3DCore::QNodeId m_objectPickerComponent; Qt3DCore::QNodeId m_boundingVolumeDebugComponent; @@ -233,6 +234,9 @@ template<> QVector<HLight> Entity::componentsHandle<Light>() const; template<> +QVector<HEnvironmentLight> Entity::componentsHandle<EnvironmentLight>() const; + +template<> Q_AUTOTEST_EXPORT HComputeCommand Entity::componentHandle<ComputeCommand>() const; // Render components @@ -246,10 +250,10 @@ template<> Transform *Entity::renderComponent<Transform>() const; template<> -Q_AUTOTEST_EXPORT GeometryRenderer *Entity::renderComponent<GeometryRenderer>() const; +QT3DRENDERSHARED_PRIVATE_EXPORT GeometryRenderer *Entity::renderComponent<GeometryRenderer>() const; template<> -Q_AUTOTEST_EXPORT ObjectPicker *Entity::renderComponent<ObjectPicker>() const; +QT3DRENDERSHARED_PRIVATE_EXPORT ObjectPicker *Entity::renderComponent<ObjectPicker>() const; template<> QVector<Layer *> Entity::renderComponents<Layer>() const; @@ -267,6 +271,9 @@ template<> QVector<Light *> Entity::renderComponents<Light>() const; template<> +QVector<EnvironmentLight *> Entity::renderComponents<EnvironmentLight>() const; + +template<> Q_AUTOTEST_EXPORT ComputeCommand *Entity::renderComponent<ComputeCommand>() const; // UUid @@ -289,10 +296,10 @@ template<> Q_AUTOTEST_EXPORT QVector<Qt3DCore::QNodeId> Entity::componentsUuid<ShaderData>() const; template<> -Q_AUTOTEST_EXPORT Qt3DCore::QNodeId Entity::componentUuid<GeometryRenderer>() const; +QT3DRENDERSHARED_PRIVATE_EXPORT Qt3DCore::QNodeId Entity::componentUuid<GeometryRenderer>() const; template<> -Q_AUTOTEST_EXPORT Qt3DCore::QNodeId Entity::componentUuid<ObjectPicker>() const; +QT3DRENDERSHARED_PRIVATE_EXPORT Qt3DCore::QNodeId Entity::componentUuid<ObjectPicker>() const; //template<> //Q_AUTOTEST_EXPORT Qt3DCore::QNodeId Entity::componentUuid<BoundingVolumeDebug>() const; @@ -303,6 +310,9 @@ Q_AUTOTEST_EXPORT Qt3DCore::QNodeId Entity::componentUuid<ComputeCommand>() cons template<> Q_AUTOTEST_EXPORT QVector<Qt3DCore::QNodeId> Entity::componentsUuid<Light>() const; +template<> +Q_AUTOTEST_EXPORT QVector<Qt3DCore::QNodeId> Entity::componentsUuid<EnvironmentLight>() const; + class RenderEntityFunctor : public Qt3DCore::QBackendNodeMapper { public: diff --git a/src/render/backend/frameprofiler_p.h b/src/render/backend/frameprofiler_p.h index 2412a8053..9e7bcd038 100644 --- a/src/render/backend/frameprofiler_p.h +++ b/src/render/backend/frameprofiler_p.h @@ -69,7 +69,7 @@ enum RecordingType RenderTargetUpdate }; -#ifdef QT3D_JOBS_RUN_STATS +#ifdef QT3D_OPENGL_RUN_STATS class FrameTimeRecorder { @@ -218,33 +218,35 @@ public: explicit GLTimeRecorder(RecordingType type) : m_type(type) { -#ifdef QT3D_JOBS_RUN_STATS +#ifdef QT3D_OPENGL_RUN_STATS frameProfiler.startRecordEvent(); #endif } ~GLTimeRecorder() { -#ifdef QT3D_JOBS_RUN_STATS +#ifdef QT3D_OPENGL_RUN_STATS frameProfiler.recordEvent(m_type); +#else + Q_UNUSED(m_type); #endif } static void writeResults() { -#ifdef QT3D_JOBS_RUN_STATS +#ifdef QT3D_OPENGL_RUN_STATS frameProfiler.writeResults(); #endif } private: -#ifdef QT3D_JOBS_RUN_STATS +#ifdef QT3D_OPENGL_RUN_STATS static FrameProfiler frameProfiler; #endif RecordingType m_type; }; -#ifdef QT3D_JOBS_RUN_STATS +#ifdef QT3D_OPENGL_RUN_STATS FrameProfiler GLTimeRecorder::frameProfiler; #endif diff --git a/src/render/backend/handle_types_p.h b/src/render/backend/handle_types_p.h index c8c73b749..352519e75 100644 --- a/src/render/backend/handle_types_p.h +++ b/src/render/backend/handle_types_p.h @@ -90,6 +90,7 @@ class ObjectPicker; class BoundingVolumeDebug; class OpenGLVertexArrayObject; class Light; +class EnvironmentLight; class ComputeCommand; class GLBuffer; class RenderStateNode; @@ -122,6 +123,7 @@ typedef Qt3DCore::QHandle<GeometryRenderer, 16> HGeometryRenderer; typedef Qt3DCore::QHandle<ObjectPicker, 16> HObjectPicker; typedef Qt3DCore::QHandle<BoundingVolumeDebug, 16> HBoundingVolumeDebug; typedef Qt3DCore::QHandle<Light, 16> HLight; +typedef Qt3DCore::QHandle<EnvironmentLight, 16> HEnvironmentLight; typedef Qt3DCore::QHandle<ComputeCommand, 16> HComputeCommand; typedef Qt3DCore::QHandle<GLBuffer, 16> HGLBuffer; typedef Qt3DCore::QHandle<RenderStateNode, 16> HRenderState; diff --git a/src/render/backend/levelofdetail.cpp b/src/render/backend/levelofdetail.cpp index 474133d1c..157bfc004 100644 --- a/src/render/backend/levelofdetail.cpp +++ b/src/render/backend/levelofdetail.cpp @@ -39,11 +39,9 @@ #include "levelofdetail_p.h" #include <Qt3DRender/QLevelOfDetail> -#include <Qt3DRender/QBoundingSphere> #include <Qt3DRender/private/qlevelofdetail_p.h> #include <Qt3DRender/private/stringtoint_p.h> #include <Qt3DCore/qpropertyupdatedchange.h> -#include <Qt3DCore/private/qpropertyupdatedchangebase_p.h> #include <QVariant> QT_BEGIN_NAMESPACE @@ -56,8 +54,8 @@ namespace Render { LevelOfDetail::LevelOfDetail() : BackendNode(BackendNode::ReadWrite) , m_currentIndex(0) - , m_thresholdType(QLevelOfDetail::DistanceToCamera) - , m_radius(1.f) + , m_thresholdType(QLevelOfDetail::DistanceToCameraThreshold) + , m_volumeOverride() { } @@ -74,8 +72,7 @@ void LevelOfDetail::initializeFromPeer(const QNodeCreatedChangeBasePtr &change) m_currentIndex = data.currentIndex; m_thresholdType = data.thresholdType; m_thresholds = data.thresholds; - m_radius = data.radius; - m_center = data.center; + m_volumeOverride = data.volumeOverride; } void LevelOfDetail::cleanup() @@ -87,23 +84,16 @@ void LevelOfDetail::sceneChangeEvent(const QSceneChangePtr &e) { if (e->type() == PropertyUpdated) { const QPropertyUpdatedChangePtr &propertyChange = qSharedPointerCast<QPropertyUpdatedChange>(e); - if (propertyChange->propertyName() == QByteArrayLiteral("currentIndex")) { + if (propertyChange->propertyName() == QByteArrayLiteral("currentIndex")) m_currentIndex = propertyChange->value().value<int>(); - } else if (propertyChange->propertyName() == QByteArrayLiteral("camera")) { + else if (propertyChange->propertyName() == QByteArrayLiteral("camera")) m_camera = propertyChange->value().value<Qt3DCore::QNodeId>(); - } else if (propertyChange->propertyName() == QByteArrayLiteral("thresholdType")) { + else if (propertyChange->propertyName() == QByteArrayLiteral("thresholdType")) m_thresholdType = propertyChange->value().value<QLevelOfDetail::ThresholdType>(); - } else if (propertyChange->propertyName() == QByteArrayLiteral("thresholds")) { + else if (propertyChange->propertyName() == QByteArrayLiteral("thresholds")) m_thresholds = propertyChange->value().value<QVector<qreal>>(); - } else if (propertyChange->propertyName() == QByteArrayLiteral("center")) { - m_center = propertyChange->value().value<QVector3D>(); - } else if (propertyChange->propertyName() == QByteArrayLiteral("radius")) { - m_radius = propertyChange->value().value<float>(); - } else if (propertyChange->propertyName() == QByteArrayLiteral("volumeOverride")) { - auto volumeOverride = propertyChange->value().value<Qt3DRender::QBoundingSphere*>(); - m_center = volumeOverride ? volumeOverride->center() : QVector3D(); - m_radius = volumeOverride ? volumeOverride->radius() : -1.f; - } + else if (propertyChange->propertyName() == QByteArrayLiteral("volumeOverride")) + m_volumeOverride = propertyChange->value().value<Qt3DRender::QLevelOfDetailBoundingSphere>(); } markDirty(AbstractRenderer::GeometryDirty); @@ -119,7 +109,6 @@ void LevelOfDetail::setCurrentIndex(int currentIndex) e->setDeliveryFlags(Qt3DCore::QSceneChange::DeliverToAll); e->setPropertyName("currentIndex"); e->setValue(m_currentIndex); - Qt3DCore::QPropertyUpdatedChangeBasePrivate::get(e.data())->m_isFinal = true; notifyObservers(e); } } diff --git a/src/render/backend/levelofdetail_p.h b/src/render/backend/levelofdetail_p.h index 83809a631..05f5686bb 100644 --- a/src/render/backend/levelofdetail_p.h +++ b/src/render/backend/levelofdetail_p.h @@ -79,8 +79,9 @@ public: int currentIndex() const { return m_currentIndex; } QLevelOfDetail::ThresholdType thresholdType() const { return m_thresholdType; } QVector<qreal> thresholds() const { return m_thresholds; } - float radius() const { return m_radius; } - QVector3D center() const { return m_center; } + float radius() const { return m_volumeOverride.radius(); } + QVector3D center() const { return m_volumeOverride.center(); } + bool hasBoundingVolumeOverride() const { return !m_volumeOverride.isEmpty(); } void setCurrentIndex(int currentIndex); @@ -90,8 +91,7 @@ private: int m_currentIndex; QLevelOfDetail::ThresholdType m_thresholdType; QVector<qreal> m_thresholds; - float m_radius; - QVector3D m_center; + QLevelOfDetailBoundingSphere m_volumeOverride; }; } // namespace Render diff --git a/src/render/backend/managers_p.h b/src/render/backend/managers_p.h index ce6767958..1c1a34b83 100644 --- a/src/render/backend/managers_p.h +++ b/src/render/backend/managers_p.h @@ -77,6 +77,7 @@ #include <Qt3DRender/private/boundingvolumedebug_p.h> #include <Qt3DRender/private/openglvertexarrayobject_p.h> #include <Qt3DRender/private/light_p.h> +#include <Qt3DRender/private/environmentlight_p.h> #include <Qt3DRender/private/computecommand_p.h> QT_BEGIN_NAMESPACE @@ -260,7 +261,7 @@ public: class VAOManager : public Qt3DCore::QResourceManager< OpenGLVertexArrayObject, - QPair<HGeometry, HShader>, + VAOIdentifier, 16, Qt3DCore::ArrayAllocatingPolicy, Qt3DCore::NonLockingPolicy> @@ -381,6 +382,17 @@ public: LightManager() {} }; +class EnvironmentLightManager : public Qt3DCore::QResourceManager< + EnvironmentLight, + Qt3DCore::QNodeId, + 16, + Qt3DCore::ArrayAllocatingPolicy, + Qt3DCore::NonLockingPolicy> +{ +public: + EnvironmentLightManager() {} +}; + class ComputeCommandManager : public Qt3DCore::QResourceManager< ComputeCommand, Qt3DCore::QNodeId, @@ -422,6 +434,7 @@ Q_DECLARE_RESOURCE_INFO(Qt3DRender::Render::BoundingVolumeDebug, Q_REQUIRES_CLEA Q_DECLARE_RESOURCE_INFO(Qt3DRender::Render::ComputeCommand, Q_REQUIRES_CLEANUP) Q_DECLARE_RESOURCE_INFO(Qt3DRender::Render::Parameter, Q_REQUIRES_CLEANUP) Q_DECLARE_RESOURCE_INFO(Qt3DRender::Render::Transform, Q_REQUIRES_CLEANUP) +Q_DECLARE_RESOURCE_INFO(Qt3DRender::Render::OpenGLVertexArrayObject, Q_REQUIRES_CLEANUP) QT_END_NAMESPACE diff --git a/src/render/backend/nodemanagers.cpp b/src/render/backend/nodemanagers.cpp index d83623ce2..2647bbb1a 100644 --- a/src/render/backend/nodemanagers.cpp +++ b/src/render/backend/nodemanagers.cpp @@ -46,6 +46,7 @@ #include <Qt3DRender/private/texturedatamanager_p.h> #include <Qt3DRender/private/geometryrenderermanager_p.h> #include <Qt3DRender/private/techniquemanager_p.h> +#include <private/resourceaccessor_p.h> #include <QOpenGLVertexArrayObject> @@ -88,8 +89,10 @@ NodeManagers::NodeManagers() , m_objectPickerManager(new ObjectPickerManager()) // , m_boundingVolumeDebugManager(new BoundingVolumeDebugManager()) , m_lightManager(new LightManager()) + , m_environmentLightManager(new EnvironmentLightManager()) , m_computeJobManager(new ComputeCommandManager()) , m_renderStateManager(new RenderStateManager()) + , m_resourceAccessor(new ResourceAccessor(this)) { } @@ -126,11 +129,17 @@ NodeManagers::~NodeManagers() delete m_objectPickerManager; // delete m_boundingVolumeDebugManager; delete m_lightManager; + delete m_environmentLightManager; delete m_computeJobManager; delete m_renderStateManager; delete m_renderNodesManager; } +QSharedPointer<ResourceAccessor> NodeManagers::resourceAccessor() +{ + return m_resourceAccessor; +} + template<> CameraManager *NodeManagers::manager<CameraLens>() const Q_DECL_NOTHROW { @@ -312,6 +321,12 @@ LightManager *NodeManagers::manager<Light>() const Q_DECL_NOTHROW } template<> +EnvironmentLightManager *NodeManagers::manager<EnvironmentLight>() const Q_DECL_NOTHROW +{ + return m_environmentLightManager; +} + +template<> ComputeCommandManager *NodeManagers::manager<ComputeCommand>() const Q_DECL_NOTHROW { return m_computeJobManager; diff --git a/src/render/backend/nodemanagers_p.h b/src/render/backend/nodemanagers_p.h index b1ea2a0de..9e913eec4 100644 --- a/src/render/backend/nodemanagers_p.h +++ b/src/render/backend/nodemanagers_p.h @@ -96,6 +96,7 @@ class TextureImageDataManager; class LayerManager; class LevelOfDetailManager; class LightManager; +class EnvironmentLightManager; class ComputeCommandManager; class RenderStateManager; @@ -127,10 +128,13 @@ class GeometryRenderer; class ObjectPicker; //class BoundingVolumeDebug; class Light; +class EnvironmentLight; class ComputeCommand; class RenderStateNode; class OpenGLVertexArrayObject; +class ResourceAccessor; + class QT3DRENDERSHARED_PRIVATE_EXPORT NodeManagers { public: @@ -203,9 +207,12 @@ public: inline ObjectPickerManager *objectPickerManager() const Q_DECL_NOEXCEPT { return m_objectPickerManager; } // inline BoundingVolumeDebugManager *boundingVolumeDebugManager() const Q_DECL_NOEXCEPT { return m_boundingVolumeDebugManager; } inline LightManager *lightManager() const Q_DECL_NOEXCEPT { return m_lightManager; } + inline EnvironmentLightManager *environmentLightManager() const Q_DECL_NOEXCEPT { return m_environmentLightManager; } inline ComputeCommandManager *computeJobManager() const Q_DECL_NOEXCEPT { return m_computeJobManager; } inline RenderStateManager *renderStateManager() const Q_DECL_NOEXCEPT { return m_renderStateManager; } + QSharedPointer<ResourceAccessor> resourceAccessor(); + private: CameraManager *m_cameraManager; EntityManager *m_renderNodesManager; @@ -239,8 +246,11 @@ private: ObjectPickerManager *m_objectPickerManager; // BoundingVolumeDebugManager *m_boundingVolumeDebugManager; LightManager *m_lightManager; + EnvironmentLightManager *m_environmentLightManager; ComputeCommandManager *m_computeJobManager; RenderStateManager *m_renderStateManager; + + QSharedPointer<ResourceAccessor> m_resourceAccessor; }; // Specializations @@ -336,6 +346,9 @@ template<> LightManager *NodeManagers::manager<Light>() const Q_DECL_NOEXCEPT; template<> +EnvironmentLightManager *NodeManagers::manager<EnvironmentLight>() const Q_DECL_NOEXCEPT; + +template<> QT3DRENDERSHARED_PRIVATE_EXPORT ComputeCommandManager *NodeManagers::manager<ComputeCommand>() const Q_DECL_NOEXCEPT; template<> diff --git a/src/render/backend/openglvertexarrayobject.cpp b/src/render/backend/openglvertexarrayobject.cpp index 6120c7233..eefc208d5 100644 --- a/src/render/backend/openglvertexarrayobject.cpp +++ b/src/render/backend/openglvertexarrayobject.cpp @@ -52,15 +52,8 @@ OpenGLVertexArrayObject::OpenGLVertexArrayObject() : m_ctx(nullptr) , m_specified(false) , m_supportsVao(false) - , m_createdEmulatedVAO(false) {} -void OpenGLVertexArrayObject::setGraphicsContext(GraphicsContext *ctx) -{ - m_ctx = ctx; - m_supportsVao = m_ctx->supportsVAO(); -} - void OpenGLVertexArrayObject::bind() { Q_ASSERT(m_ctx); @@ -75,7 +68,7 @@ void OpenGLVertexArrayObject::bind() m_ctx->m_currentVAO = this; // We need to specify array and vertex attributes - for (const GraphicsContext::VAOVertexAttribute &attr : m_vertexAttributes) + for (const GraphicsContext::VAOVertexAttribute &attr : qAsConst(m_vertexAttributes)) m_ctx->enableAttribute(attr); if (!m_indexAttribute.isNull()) m_ctx->bindGLBuffer(m_ctx->m_renderer->nodeManagers()->glBufferManager()->data(m_indexAttribute), @@ -92,44 +85,58 @@ void OpenGLVertexArrayObject::release() m_vao->release(); } else { if (m_ctx->m_currentVAO == this) { - for (const GraphicsContext::VAOVertexAttribute &attr : m_vertexAttributes) + for (const GraphicsContext::VAOVertexAttribute &attr : qAsConst(m_vertexAttributes)) m_ctx->disableAttribute(attr); m_ctx->m_currentVAO = nullptr; } } } -void OpenGLVertexArrayObject::create() +// called from Render thread +void OpenGLVertexArrayObject::create(GraphicsContext *ctx, const VAOIdentifier &key) { - Q_ASSERT(m_ctx); - if (m_supportsVao) { - Q_ASSERT(!m_vao.isNull()); - m_vao->create(); - } else { - m_createdEmulatedVAO = true; - } + QMutexLocker lock(&m_mutex); + + Q_ASSERT(!m_ctx && !m_vao); + + m_ctx = ctx; + m_supportsVao = m_ctx->supportsVAO(); + m_vao.reset(m_supportsVao ? new QOpenGLVertexArrayObject() : nullptr); + m_vao->create(); + m_owners = key; } +// called from Render thread void OpenGLVertexArrayObject::destroy() { + QMutexLocker locker(&m_mutex); + Q_ASSERT(m_ctx); - if (m_supportsVao) { - Q_ASSERT(!m_vao.isNull()); - m_vao->destroy(); - } + cleanup(); +} + +void OpenGLVertexArrayObject::cleanup() +{ + m_vao.reset(); + m_ctx = nullptr; m_specified = false; + m_supportsVao = false; m_indexAttribute = GraphicsContext::VAOIndexAttribute(); m_vertexAttributes.clear(); } -bool OpenGLVertexArrayObject::isCreated() const +// called from job +bool OpenGLVertexArrayObject::isAbandoned(GeometryManager *geomMgr, ShaderManager *shaderMgr) { - if (m_supportsVao) { - Q_ASSERT(!m_vao.isNull()); - return m_vao->isCreated(); - } else { - return m_createdEmulatedVAO; - } + QMutexLocker lock(&m_mutex); + + if (!m_ctx) + return false; + + const bool geometryExists = (geomMgr->data(m_owners.first) != nullptr); + const bool shaderExists = (shaderMgr->data(m_owners.second) != nullptr); + + return !geometryExists || !shaderExists; } void OpenGLVertexArrayObject::saveVertexAttribute(const GraphicsContext::VAOVertexAttribute &attr) diff --git a/src/render/backend/openglvertexarrayobject_p.h b/src/render/backend/openglvertexarrayobject_p.h index 4f21497f0..a564175f6 100644 --- a/src/render/backend/openglvertexarrayobject_p.h +++ b/src/render/backend/openglvertexarrayobject_p.h @@ -59,31 +59,39 @@ QT_BEGIN_NAMESPACE namespace Qt3DRender { namespace Render { +class GeometryManager; +class ShaderManager; + +typedef QPair<HGeometry, HShader> VAOIdentifier; + class OpenGLVertexArrayObject { public: OpenGLVertexArrayObject(); - void setGraphicsContext(GraphicsContext *ctx); void bind(); void release(); - void create(); + + void create(GraphicsContext *ctx, const VAOIdentifier &key); void destroy(); - bool isCreated() const; + void cleanup(); + + bool isAbandoned(GeometryManager *geomMgr, ShaderManager *shaderMgr); QOpenGLVertexArrayObject *vao() { return m_vao.data(); } const QOpenGLVertexArrayObject *vao() const { return m_vao.data(); } - void setVao(QOpenGLVertexArrayObject *vao) { m_vao.reset(vao); } void setSpecified(bool b) { m_specified = b; } bool isSpecified() const { return m_specified; } + private: + QMutex m_mutex; GraphicsContext *m_ctx; QScopedPointer<QOpenGLVertexArrayObject> m_vao; bool m_specified; bool m_supportsVao; - bool m_createdEmulatedVAO; + VAOIdentifier m_owners; friend class GraphicsContext; diff --git a/src/render/backend/platformsurfacefilter.cpp b/src/render/backend/platformsurfacefilter.cpp index 891e30c44..7458f607d 100644 --- a/src/render/backend/platformsurfacefilter.cpp +++ b/src/render/backend/platformsurfacefilter.cpp @@ -107,6 +107,10 @@ bool PlatformSurfaceFilter::eventFilter(QObject *obj, QEvent *e) // If we remove it, the call to isSurfaceValid will // implicitely return false PlatformSurfaceFilter::m_surfacesValidity.remove(m_surface); + if (m_obj) { + m_obj->removeEventFilter(this); + m_obj = nullptr; + } break; } diff --git a/src/render/backend/platformsurfacefilter_p.h b/src/render/backend/platformsurfacefilter_p.h index ec10327fe..dbdc07b01 100644 --- a/src/render/backend/platformsurfacefilter_p.h +++ b/src/render/backend/platformsurfacefilter_p.h @@ -51,6 +51,8 @@ // We mean it. // +#include <private/qt3drender_global_p.h> + #include <QtCore/qobject.h> #include <QtGui/qsurface.h> #include <QSemaphore> @@ -106,7 +108,7 @@ private: void markSurfaceAsValid(); }; -class SurfaceLocker +class QT3DRENDERSHARED_PRIVATE_EXPORT SurfaceLocker { public: explicit SurfaceLocker(QSurface *surface); diff --git a/src/render/backend/render-backend.pri b/src/render/backend/render-backend.pri index 93e323caa..5d515a173 100644 --- a/src/render/backend/render-backend.pri +++ b/src/render/backend/render-backend.pri @@ -1,6 +1,3 @@ -#DEFINES += QT3D_RENDER_VIEW_JOB_TIMING -#DEFINES += QT3D_RENDER_DUMP_BACKEND_NODES - INCLUDEPATH += $$PWD HEADERS += \ @@ -29,6 +26,8 @@ HEADERS += \ $$PWD/triangleboundingvolume_p.h \ $$PWD/openglvertexarrayobject_p.h \ $$PWD/trianglesextractor_p.h \ + $$PWD/buffervisitor_p.h \ + $$PWD/bufferutils_p.h \ $$PWD/trianglesvisitor_p.h \ $$PWD/abstractrenderer_p.h \ $$PWD/computecommand_p.h \ @@ -36,12 +35,13 @@ HEADERS += \ $$PWD/stringtoint_p.h \ $$PWD/backendnode_p.h \ $$PWD/rendertargetoutput_p.h \ - $$PWD/commandexecuter_p.h \ $$PWD/uniform_p.h \ $$PWD/shaderparameterpack_p.h \ $$PWD/renderviewbuilder_p.h \ $$PWD/frameprofiler_p.h \ - $$PWD/offscreensurfacehelper_p.h + $$PWD/offscreensurfacehelper_p.h \ + $$PWD/resourceaccessor_p.h \ + $$PWD/commandthread_p.h SOURCES += \ $$PWD/renderthread.cpp \ @@ -70,10 +70,18 @@ SOURCES += \ $$PWD/backendnode.cpp \ $$PWD/rendertargetoutput.cpp \ $$PWD/attachmentpack.cpp \ - $$PWD/commandexecuter.cpp \ $$PWD/openglvertexarrayobject.cpp \ $$PWD/uniform.cpp \ $$PWD/shaderparameterpack.cpp \ $$PWD/renderviewbuilder.cpp \ - $$PWD/offscreensurfacehelper.cpp + $$PWD/offscreensurfacehelper.cpp \ + $$PWD/resourceaccessor.cpp \ + $$PWD/commandthread.cpp + +include($$OUT_PWD/../core/qt3dcore-config.pri) +QT_FOR_CONFIG += 3dcore-private +qtConfig(qt3d-profile-jobs): { + HEADERS += $$PWD/commandexecuter_p.h + SOURCES += $$PWD/commandexecuter.cpp +} diff --git a/src/render/backend/renderer.cpp b/src/render/backend/renderer.cpp index af73b1ae5..b89f21522 100644 --- a/src/render/backend/renderer.cpp +++ b/src/render/backend/renderer.cpp @@ -68,6 +68,7 @@ #include <Qt3DRender/private/technique_p.h> #include <Qt3DRender/private/renderthread_p.h> #include <Qt3DRender/private/renderview_p.h> +#include <Qt3DRender/private/scenemanager_p.h> #include <Qt3DRender/private/techniquefilternode_p.h> #include <Qt3DRender/private/viewportnode_p.h> #include <Qt3DRender/private/vsyncframeadvanceservice_p.h> @@ -84,13 +85,19 @@ #include <Qt3DRender/private/loadbufferjob_p.h> #include <Qt3DRender/private/rendercapture_p.h> #include <Qt3DRender/private/updatelevelofdetailjob_p.h> +#include <Qt3DRender/private/buffercapture_p.h> #include <Qt3DRender/private/offscreensurfacehelper_p.h> +#include <Qt3DRender/private/renderviewbuilder_p.h> #include <Qt3DRender/qcameralens.h> +#include <Qt3DCore/qt3dcore-config.h> #include <Qt3DCore/private/qeventfilterservice_p.h> #include <Qt3DCore/private/qabstractaspectjobmanager_p.h> #include <Qt3DCore/private/qnodecreatedchangegenerator_p.h> + +#if defined(QT3D_JOBS_RUN_STATS) #include <Qt3DCore/private/aspectcommanddebugger_p.h> +#endif #include <QStack> #include <QOffscreenSurface> @@ -146,6 +153,7 @@ namespace Render { Renderer::Renderer(QRenderAspect::RenderType type) : m_services(nullptr) , m_nodesManager(nullptr) + , m_renderSceneRoot(nullptr) , m_defaultRenderStateSet(nullptr) , m_graphicsContext(nullptr) , m_renderQueue(new RenderQueue()) @@ -157,6 +165,7 @@ Renderer::Renderer(QRenderAspect::RenderType type) , m_changeSet(0) , m_lastFrameCorrect(0) , m_glContext(nullptr) + , m_shareContext(nullptr) , m_pickBoundingVolumeJob(PickBoundingVolumeJobPtr::create()) , m_time(0) , m_settings(nullptr) @@ -168,10 +177,12 @@ Renderer::Renderer(QRenderAspect::RenderType type) , m_updateWorldBoundingVolumeJob(Render::UpdateWorldBoundingVolumeJobPtr::create()) , m_updateTreeEnabledJob(Render::UpdateTreeEnabledJobPtr::create()) , m_sendRenderCaptureJob(Render::SendRenderCaptureJobPtr::create(this)) + , m_sendBufferCaptureJob(Render::SendBufferCaptureJobPtr::create()) , m_updateLevelOfDetailJob(Render::UpdateLevelOfDetailJobPtr::create()) , m_updateMeshTriangleListJob(Render::UpdateMeshTriangleListJobPtr::create()) , m_filterCompatibleTechniqueJob(Render::FilterCompatibleTechniqueJobPtr::create()) , m_bufferGathererJob(Render::GenericLambdaJobPtr<std::function<void ()>>::create([this] { lookForDirtyBuffers(); }, JobTypes::DirtyBufferGathering)) + , m_vaoGathererJob(Render::GenericLambdaJobPtr<std::function<void ()>>::create([this] { lookForAbandonedVaos(); }, JobTypes::DirtyVaoGathering)) , m_textureGathererJob(Render::GenericLambdaJobPtr<std::function<void ()>>::create([this] { lookForDirtyTextures(); }, JobTypes::DirtyTextureGathering)) , m_shaderGathererJob(Render::GenericLambdaJobPtr<std::function<void ()>>::create([this] { lookForDirtyShaders(); }, JobTypes::DirtyShaderGathering)) , m_syncTextureLoadingJob(Render::GenericLambdaJobPtr<std::function<void ()>>::create([] {}, JobTypes::SyncTextureLoading)) @@ -263,16 +274,35 @@ void Renderer::setNodeManagers(NodeManagers *managers) m_pickBoundingVolumeJob->setManagers(m_nodesManager); m_updateWorldBoundingVolumeJob->setManager(m_nodesManager->renderNodesManager()); m_sendRenderCaptureJob->setManagers(m_nodesManager); + m_sendBufferCaptureJob->setManagers(m_nodesManager); m_updateLevelOfDetailJob->setManagers(m_nodesManager); m_updateMeshTriangleListJob->setManagers(m_nodesManager); m_filterCompatibleTechniqueJob->setManager(m_nodesManager->techniqueManager()); } +void Renderer::setServices(QServiceLocator *services) +{ + m_services = services; + + m_nodesManager->sceneManager()->setDownloadService(m_services->downloadHelperService()); +} + NodeManagers *Renderer::nodeManagers() const { return m_nodesManager; } +/*! + \internal + + Return context which can be used to share resources safely + with qt3d main render context. +*/ +QOpenGLContext *Renderer::shareContext() const +{ + return m_shareContext ? m_shareContext : m_graphicsContext->openGLContext(); +} + void Renderer::setOpenGLContext(QOpenGLContext *context) { m_glContext = context; @@ -314,6 +344,10 @@ void Renderer::initialize() // Context is not owned by us, so we need to know if it gets destroyed m_contextConnection = QObject::connect(m_glContext, &QOpenGLContext::aboutToBeDestroyed, [this] { releaseGraphicsResources(); }); + m_shareContext = new QOpenGLContext; + m_shareContext->setFormat(m_glContext->format()); + m_shareContext->setShareContext(m_glContext); + m_shareContext->create(); } // Note: we don't have a surface at this point @@ -358,6 +392,7 @@ void Renderer::shutdown() // to be ready. The isReadyToSubmit() function checks for a shutdown // having been requested. m_submitRenderViewsSemaphore.release(1); + m_renderThread->wait(); } } @@ -405,6 +440,13 @@ void Renderer::releaseGraphicsResources() buffer->destroy(m_graphicsContext.data()); } + // Do the same thing with VAOs + const QVector<HVao> activeVaos = m_nodesManager->vaoManager()->activeHandles(); + for (const HVao &vaoHandle : activeVaos) { + OpenGLVertexArrayObject *vao = m_nodesManager->vaoManager()->data(vaoHandle); + vao->destroy(); + } + context->doneCurrent(); } else { qWarning() << "Failed to make context current: OpenGL resources will not be destroyed"; @@ -412,6 +454,8 @@ void Renderer::releaseGraphicsResources() if (m_ownedContext) delete context; + if (m_shareContext) + delete m_shareContext; m_graphicsContext.reset(nullptr); qCDebug(Backend) << Q_FUNC_INFO << "Renderer properly shutdown"; @@ -500,17 +544,26 @@ void Renderer::render() void Renderer::doRender() { - bool submissionSucceeded = false; - bool hasCleanedQueueAndProceeded = false; Renderer::ViewSubmissionResultData submissionData; + bool hasCleanedQueueAndProceeded = false; bool preprocessingComplete = false; - - if (isReadyToSubmit()) { - - // Lock the mutex to protect access to m_surface and check if we are still set - // to the running state and that we have a valid surface on which to draw - // TO DO: Is that still needed given the surface changes - QMutexLocker locker(&m_mutex); + bool beganDrawing = false; + const bool canSubmit = isReadyToSubmit(); + + // Lock the mutex to protect access to the renderQueue while we look for its state + QMutexLocker locker(&m_renderQueueMutex); + const bool queueIsComplete = m_renderQueue->isFrameQueueComplete(); + const bool queueIsEmpty = m_renderQueue->targetRenderViewCount() == 0; + + // When using synchronous rendering (QtQuick) + // We are not sure that the frame queue is actually complete + // Since a call to render may not be synched with the completions + // of the RenderViewJobs + // In such a case we return early, waiting for a next call with + // the frame queue complete at this point + + // RenderQueue is complete (but that means it may be of size 0) + if (canSubmit && (queueIsComplete && !queueIsEmpty)) { const QVector<Render::RenderView *> renderViews = m_renderQueue->nextFrameQueue(); #ifdef QT3D_JOBS_RUN_STATS @@ -525,8 +578,7 @@ void Renderer::doRender() submissionStatsPart2.jobId.typeAndInstance[1] = 0; submissionStatsPart2.threadId = reinterpret_cast<quint64>(QThread::currentThreadId()); #endif - - if (canRender() && (submissionSucceeded = renderViews.size() > 0) == true) { + if (canRender()) { // Clear all dirty flags but Compute so that // we still render every frame when a compute shader is used in a scene BackendNodeDirtySet changesToUnset = m_changeSet; @@ -548,7 +600,8 @@ void Renderer::doRender() // Reset state for each draw if we don't have complete control of the context if (!m_ownedContext) m_graphicsContext->setCurrentStateSet(nullptr); - if (m_graphicsContext->beginDrawing(surface)) { + beganDrawing = m_graphicsContext->beginDrawing(surface); + if (beganDrawing) { // 1) Execute commands for buffer uploads, texture updates, shader loading first updateGLResources(); // 2) Update VAO and copy data into commands to allow concurrent submission @@ -559,6 +612,7 @@ void Renderer::doRender() } // 2) Proceed to next frame and start preparing frame n + 1 m_renderQueue->reset(); + locker.unlock(); // Done protecting RenderQueue m_vsyncFrameAdvanceService->proceedToNextFrame(); hasCleanedQueueAndProceeded = true; @@ -602,61 +656,57 @@ void Renderer::doRender() #endif } - // Note: submissionSucceeded is false when - // * we cannot render because a shutdown has been scheduled - // * the renderqueue is incomplete (only when rendering with a Scene3D) - // Otherwise returns true even for cases like - // * No render view - // * No surface set - // * OpenGLContext failed to be set current - // This behavior is important as we need to - // call proceedToNextFrame despite rendering errors that aren't fatal - // Only reset renderQueue and proceed to next frame if the submission - // succeeded or it we are using a render thread and that is wasn't performed + // succeeded or if we are using a render thread and that is wasn't performed // already - // If submissionSucceeded isn't true this implies that something went wrong + // If hasCleanedQueueAndProceeded isn't true this implies that something went wrong // with the rendering and/or the renderqueue is incomplete from some reason // (in the case of scene3d the render jobs may be taking too long ....) - if (m_renderThread || submissionSucceeded) { - - if (!hasCleanedQueueAndProceeded) { - // Reset the m_renderQueue so that we won't try to render - // with a queue used by a previous frame with corrupted content - // if the current queue was correctly submitted - m_renderQueue->reset(); - - // We allow the RenderTickClock service to proceed to the next frame - // In turn this will allow the aspect manager to request a new set of jobs - // to be performed for each aspect - m_vsyncFrameAdvanceService->proceedToNextFrame(); - } + // or alternatively it could be complete but empty (RenderQueue of size 0) + if (!hasCleanedQueueAndProceeded && + (m_renderThread || queueIsComplete || queueIsEmpty)) { + // RenderQueue was full but something bad happened when + // trying to render it and therefore proceedToNextFrame was not called + // Note: in this case the renderQueue mutex is still locked + + // Reset the m_renderQueue so that we won't try to render + // with a queue used by a previous frame with corrupted content + // if the current queue was correctly submitted + m_renderQueue->reset(); + + // We allow the RenderTickClock service to proceed to the next frame + // In turn this will allow the aspect manager to request a new set of jobs + // to be performed for each aspect + m_vsyncFrameAdvanceService->proceedToNextFrame(); + } - // Perform the last swapBuffers calls after the proceedToNextFrame - // as this allows us to gain a bit of time for the preparation of the - // next frame + // Perform the last swapBuffers calls after the proceedToNextFrame + // as this allows us to gain a bit of time for the preparation of the + // next frame + // Finish up with last surface used in the list of RenderViews + if (beganDrawing) { + SurfaceLocker surfaceLock(submissionData.surface); // Finish up with last surface used in the list of RenderViews - if (submissionSucceeded) { - SurfaceLocker surfaceLock(submissionData.surface); - // Finish up with last surface used in the list of RenderViews - m_graphicsContext->endDrawing(submissionData.lastBoundFBOId == m_graphicsContext->defaultFBO() && surfaceLock.isSurfaceValid()); - } + m_graphicsContext->endDrawing(submissionData.lastBoundFBOId == m_graphicsContext->defaultFBO() && surfaceLock.isSurfaceValid()); } } // Called by RenderViewJobs +// When the frameQueue is complete and we are using a renderThread +// we allow the render thread to proceed void Renderer::enqueueRenderView(Render::RenderView *renderView, int submitOrder) { - QMutexLocker locker(&m_mutex); // Prevent out of order execution + QMutexLocker locker(&m_renderQueueMutex); // Prevent out of order execution // We cannot use a lock free primitive here because: // - QVector is not thread safe // - Even if the insert is made correctly, the isFrameComplete call // could be invalid since depending on the order of execution // the counter could be complete but the renderview not yet added to the // buffer depending on whichever order the cpu decides to process this - - if (m_renderQueue->queueRenderView(renderView, submitOrder)) { + const bool isQueueComplete = m_renderQueue->queueRenderView(renderView, submitOrder); + locker.unlock(); // We're done protecting the queue at this point + if (isQueueComplete) { if (m_renderThread && m_running.load()) Q_ASSERT(m_submitRenderViewsSemaphore.available() == 0); m_submitRenderViewsSemaphore.release(1); @@ -695,16 +745,6 @@ bool Renderer::isReadyToSubmit() // something to render // The case of shutdown should have been handled just before Q_ASSERT(m_renderQueue->isFrameQueueComplete()); - } else { - // When using synchronous rendering (QtQuick) - // We are not sure that the frame queue is actually complete - // Since a call to render may not be synched with the completions - // of the RenderViewJobs - // In such a case we return early, waiting for a next call with - // the frame queue complete at this point - QMutexLocker locker(&m_mutex); - if (!m_renderQueue->isFrameQueueComplete()) - return false; } return true; } @@ -898,6 +938,23 @@ void Renderer::prepareCommandsSubmission(const QVector<RenderView *> &renderView } // Executed in a job +void Renderer::lookForAbandonedVaos() +{ + const QVector<HVao> activeVaos = m_nodesManager->vaoManager()->activeHandles(); + for (HVao handle : activeVaos) { + OpenGLVertexArrayObject *vao = m_nodesManager->vaoManager()->data(handle); + + // Make sure to only mark VAOs for deletion that were already created + // (ignore those that might be currently under construction in the render thread) + if (vao && vao->isAbandoned(m_nodesManager->geometryManager(), m_nodesManager->shaderManager())) { + m_abandonedVaosMutex.lock(); + m_abandonedVaos.push_back(handle); + m_abandonedVaosMutex.unlock(); + } + } +} + +// Executed in a job void Renderer::lookForDirtyBuffers() { const QVector<HBuffer> activeBufferHandles = m_nodesManager->bufferManager()->activeHandles(); @@ -908,6 +965,17 @@ void Renderer::lookForDirtyBuffers() } } +void Renderer::lookForDownloadableBuffers() +{ + m_downloadableBuffers.clear(); + const QVector<HBuffer> activeBufferHandles = m_nodesManager->bufferManager()->activeHandles(); + for (HBuffer handle : activeBufferHandles) { + Buffer *buffer = m_nodesManager->bufferManager()->data(handle); + if (buffer->access() & QBuffer::Read) + m_downloadableBuffers.push_back(handle); + } +} + // Executed in a job void Renderer::lookForDirtyTextures() { @@ -935,6 +1003,8 @@ void Renderer::lookForDirtyShaders() RenderPass *renderPass = m_nodesManager->renderPassManager()->lookupResource(passId); HShader shaderHandle = m_nodesManager->shaderManager()->lookupHandle(renderPass->shaderProgram()); Shader *shader = m_nodesManager->shaderManager()->data(shaderHandle); + if (Q_UNLIKELY(shader->hasPendingNotifications())) + shader->submitPendingNotifications(); if (shader != nullptr && !shader->isLoaded()) m_dirtyShaders.push_back(shaderHandle); } @@ -951,12 +1021,11 @@ void Renderer::updateGLResources() const QVector<HBuffer> dirtyBufferHandles = std::move(m_dirtyBuffers); for (HBuffer handle: dirtyBufferHandles) { Buffer *buffer = m_nodesManager->bufferManager()->data(handle); - // Perform data upload // Forces creation if it doesn't exit if (!m_graphicsContext->hasGLBufferForBuffer(buffer)) m_graphicsContext->glBufferForRenderBuffer(buffer); - else if (buffer->isDirty()) // Otherwise update the glBuffer - m_graphicsContext->updateBuffer(buffer); + // Update the glBuffer data + m_graphicsContext->updateBuffer(buffer); buffer->unsetDirty(); } } @@ -964,10 +1033,11 @@ void Renderer::updateGLResources() { Profiling::GLTimeRecorder recorder(Profiling::ShaderUpload); const QVector<HShader> dirtyShaderHandles = std::move(m_dirtyShaders); + ShaderManager *shaderManager = m_nodesManager->shaderManager(); for (HShader handle: dirtyShaderHandles) { - Shader *shader = m_nodesManager->shaderManager()->data(handle); + Shader *shader = shaderManager->data(handle); // Compile shader - m_graphicsContext->loadShader(shader); + m_graphicsContext->loadShader(shader, shaderManager); } } @@ -990,13 +1060,16 @@ void Renderer::updateGLResources() // We can really release the texture at this point m_nodesManager->textureManager()->releaseResource(textureCleanedUpId); } - - } // Render Thread void Renderer::updateTexture(Texture *texture) { + // Check that the current texture images are still in place, if not, do not update + const bool isValid = texture->isValid(); + if (!isValid) + return; + // For implementing unique, non-shared, non-cached textures. // for now, every texture is shared by default @@ -1065,10 +1138,14 @@ void Renderer::updateTexture(Texture *texture) !glTextureManager->setParameters(glTexture, texture->parameters())) qWarning() << "[Qt3DRender::TextureNode] updateTexture: TextureImpl.setParameters failed, should be non-shared"; - if (dirtyFlags.testFlag(Texture::DirtyGenerators) && + if (dirtyFlags.testFlag(Texture::DirtyImageGenerators) && !glTextureManager->setImages(glTexture, texture->textureImages())) qWarning() << "[Qt3DRender::TextureNode] updateTexture: TextureImpl.setGenerators failed, should be non-shared"; + if (dirtyFlags.testFlag(Texture::DirtyDataGenerator) && + !glTextureManager->setGenerator(glTexture, texture->dataGenerator())) + qWarning() << "[Qt3DRender::TextureNode] updateTexture: TextureImpl.setGenerator failed, should be non-shared"; + // Unset the dirty flag on the texture texture->unsetDirty(); } @@ -1083,6 +1160,17 @@ void Renderer::cleanupTexture(const Texture *texture) glTextureManager->abandon(glTexture, texture); } +void Renderer::downloadGLBuffers() +{ + lookForDownloadableBuffers(); + const QVector<HBuffer> downloadableHandles = std::move(m_downloadableBuffers); + for (HBuffer handle : downloadableHandles) { + Buffer *buffer = m_nodesManager->bufferManager()->data(handle); + QByteArray content = m_graphicsContext->downloadBufferContent(buffer); + m_sendBufferCaptureJob->addRequest(QPair<Buffer*, QByteArray>(buffer, content)); + } +} + // Happens in RenderThread context when all RenderViewJobs are done // Returns the id of the last bound FBO @@ -1221,6 +1309,9 @@ Renderer::ViewSubmissionResultData Renderer::submitRenderViews(const QVector<Ren addRenderCaptureSendRequest(renderView->renderCaptureNodeId()); } + if (renderView->isDownloadBuffersEnable()) + downloadGLBuffers(); + frameElapsed = timer.elapsed() - frameElapsed; qCDebug(Rendering) << Q_FUNC_INFO << "Submitted Renderview " << i + 1 << "/" << renderViewsCount << "in " << frameElapsed << "ms"; frameElapsed = timer.elapsed(); @@ -1311,6 +1402,7 @@ QVector<Qt3DCore::QAspectJobPtr> Renderer::renderBinJobs() m_pickBoundingVolumeJob->setFrameGraphRoot(frameGraphRoot()); m_pickBoundingVolumeJob->setRenderSettings(settings()); m_pickBoundingVolumeJob->setMouseEvents(pendingPickingEvents()); + m_pickBoundingVolumeJob->setKeyEvents(pendingKeyEvents()); m_updateLevelOfDetailJob->setFrameGraphRoot(frameGraphRoot()); // Set dependencies of resource gatherer @@ -1331,25 +1423,36 @@ QVector<Qt3DCore::QAspectJobPtr> Renderer::renderBinJobs() renderBinJobs.push_back(m_worldTransformJob); renderBinJobs.push_back(m_cleanupJob); renderBinJobs.push_back(m_sendRenderCaptureJob); + renderBinJobs.push_back(m_sendBufferCaptureJob); renderBinJobs.push_back(m_filterCompatibleTechniqueJob); renderBinJobs.append(bufferJobs); // Jobs to prepare GL Resource upload renderBinJobs.push_back(m_syncTextureLoadingJob); + renderBinJobs.push_back(m_vaoGathererJob); renderBinJobs.push_back(m_bufferGathererJob); renderBinJobs.push_back(m_textureGathererJob); renderBinJobs.push_back(m_shaderGathererJob); - // Traverse the current framegraph. For each leaf node create a - // RenderView and set its configuration then create a job to - // populate the RenderView with a set of RenderCommands that get - // their details from the RenderNodes that are visible to the - // Camera selected by the framegraph configuration - FrameGraphVisitor visitor(this, m_nodesManager->frameGraphManager()); - visitor.traverse(frameGraphRoot(), &renderBinJobs); + QMutexLocker lock(&m_renderQueueMutex); + if (m_renderQueue->wasReset()) { // Have we rendered yet? (Scene3D case) + // Traverse the current framegraph. For each leaf node create a + // RenderView and set its configuration then create a job to + // populate the RenderView with a set of RenderCommands that get + // their details from the RenderNodes that are visible to the + // Camera selected by the framegraph configuration + FrameGraphVisitor visitor(m_nodesManager->frameGraphManager()); + const QVector<FrameGraphNode *> fgLeaves = visitor.traverse(frameGraphRoot()); + + const int fgBranchCount = fgLeaves.size(); + for (int i = 0; i < fgBranchCount; ++i) { + RenderViewBuilder builder(fgLeaves.at(i), i, this); + renderBinJobs.append(builder.buildJobHierachy()); + } - // Set target number of RenderViews - m_renderQueue->setTargetRenderViewCount(visitor.leafNodeCount()); + // Set target number of RenderViews + m_renderQueue->setTargetRenderViewCount(fgBranchCount); + } return renderBinJobs; } @@ -1364,6 +1467,11 @@ QAspectJobPtr Renderer::syncTextureLoadingJob() return m_syncTextureLoadingJob; } +QAspectJobPtr Renderer::expandBoundingVolumeJob() +{ + return m_expandBoundingVolumeJob; +} + QAbstractFrameAdvanceService *Renderer::frameAdvanceService() const { return static_cast<Qt3DCore::QAbstractFrameAdvanceService *>(m_vsyncFrameAdvanceService.data()); @@ -1474,16 +1582,15 @@ void Renderer::createOrUpdateVAO(RenderCommand *command, HVao *previousVaoHandle, OpenGLVertexArrayObject **vao) { + const VAOIdentifier vaoKey(command->m_geometry, command->m_shader); + VAOManager *vaoManager = m_nodesManager->vaoManager(); - command->m_vao = vaoManager->lookupHandle(QPair<HGeometry, HShader>(command->m_geometry, command->m_shader)); + command->m_vao = vaoManager->lookupHandle(vaoKey); if (command->m_vao.isNull()) { qCDebug(Rendering) << Q_FUNC_INFO << "Allocating new VAO"; - command->m_vao = vaoManager->getOrAcquireHandle(QPair<HGeometry, HShader>(command->m_geometry, command->m_shader)); - vaoManager->data(command->m_vao)->setGraphicsContext(m_graphicsContext.data()); - if (m_graphicsContext->supportsVAO()) - vaoManager->data(command->m_vao)->setVao(new QOpenGLVertexArrayObject()); - vaoManager->data(command->m_vao)->create(); + command->m_vao = vaoManager->getOrAcquireHandle(vaoKey); + vaoManager->data(command->m_vao)->create(m_graphicsContext.data(), vaoKey); } if (*previousVaoHandle != command->m_vao) { @@ -1679,11 +1786,30 @@ void Renderer::cleanGraphicsResources() tex->destroyGLTexture(); delete tex; } + + // Delete abandoned VAOs + m_abandonedVaosMutex.lock(); + const QVector<HVao> abandonedVaos = std::move(m_abandonedVaos); + m_abandonedVaosMutex.unlock(); + for (HVao vaoHandle : abandonedVaos) { + // might have already been destroyed last frame, but added by the cleanup job before, so + // check if the VAO is really still existent + OpenGLVertexArrayObject *vao = m_nodesManager->vaoManager()->data(vaoHandle); + if (vao) { + vao->destroy(); + m_nodesManager->vaoManager()->release(vaoHandle); + } + } } QList<QMouseEvent> Renderer::pendingPickingEvents() const { - return m_pickEventFilter->pendingEvents(); + return m_pickEventFilter->pendingMouseEvents(); +} + +QList<QKeyEvent> Renderer::pendingKeyEvents() const +{ + return m_pickEventFilter->pendingKeyEvents(); } const GraphicsApiFilterData *Renderer::contextInfo() const diff --git a/src/render/backend/renderer_p.h b/src/render/backend/renderer_p.h index 9b5c35ed1..e311261e0 100644 --- a/src/render/backend/renderer_p.h +++ b/src/render/backend/renderer_p.h @@ -71,6 +71,7 @@ #include <Qt3DRender/private/updatetreeenabledjob_p.h> #include <Qt3DRender/private/platformsurfacefilter_p.h> #include <Qt3DRender/private/sendrendercapturejob_p.h> +#include <Qt3DRender/private/sendbuffercapturejob_p.h> #include <Qt3DRender/private/genericlambdajob_p.h> #include <Qt3DRender/private/updatemeshtrianglelistjob_p.h> #include <Qt3DRender/private/filtercompatibletechniquejob_p.h> @@ -156,7 +157,7 @@ public: void setTime(qint64 time) Q_DECL_OVERRIDE; void setNodeManagers(NodeManagers *managers) Q_DECL_OVERRIDE; - void setServices(Qt3DCore::QServiceLocator *services) Q_DECL_OVERRIDE { m_services = services; } + void setServices(Qt3DCore::QServiceLocator *services) Q_DECL_OVERRIDE; void setSurfaceExposed(bool exposed) Q_DECL_OVERRIDE; NodeManagers *nodeManagers() const Q_DECL_OVERRIDE; @@ -188,11 +189,11 @@ public: QVector<Qt3DCore::QAspectJobPtr> renderBinJobs() Q_DECL_OVERRIDE; Qt3DCore::QAspectJobPtr pickBoundingVolumeJob() Q_DECL_OVERRIDE; Qt3DCore::QAspectJobPtr syncTextureLoadingJob() Q_DECL_OVERRIDE; + Qt3DCore::QAspectJobPtr expandBoundingVolumeJob() Q_DECL_OVERRIDE; QVector<Qt3DCore::QAspectJobPtr> createRenderBufferJobs() const; inline FrameCleanupJobPtr frameCleanupJob() const { return m_cleanupJob; } - inline ExpandBoundingVolumeJobPtr expandBoundingVolumeJob() const { return m_expandBoundingVolumeJob; } inline UpdateShaderDataTransformJobPtr updateShaderDataTransformJob() const { return m_updateShaderDataTransformJob; } inline CalculateBoundingVolumeJobPtr calculateBoundingVolumeJob() const { return m_calculateBoundingVolumeJob; } inline UpdateTreeEnabledJobPtr updateTreeEnabledJob() const { return m_updateTreeEnabledJob; } @@ -209,10 +210,12 @@ public: virtual void setSettings(RenderSettings *settings) Q_DECL_OVERRIDE; virtual RenderSettings *settings() const Q_DECL_OVERRIDE; + QOpenGLContext *shareContext() const Q_DECL_OVERRIDE; void updateGLResources(); void updateTexture(Texture *texture); void cleanupTexture(const Texture *texture); + void downloadGLBuffers(); void prepareCommandsSubmission(const QVector<RenderView *> &renderViews); bool executeCommandsSubmission(const RenderView *rv); @@ -230,8 +233,8 @@ public: inline RenderStateSet *defaultRenderState() const { return m_defaultRenderStateSet; } - QList<QMouseEvent> pendingPickingEvents() const; + QList<QKeyEvent> pendingKeyEvents() const; void addRenderCaptureSendRequest(Qt3DCore::QNodeId nodeId); const QVector<Qt3DCore::QNodeId> takePendingRenderCaptureSendRequests(); @@ -256,12 +259,13 @@ public: ViewSubmissionResultData submitRenderViews(const QVector<Render::RenderView *> &renderViews); - QMutex* mutex() { return &m_mutex; } + QMutex* mutex() { return &m_renderQueueMutex; } #ifdef QT3D_RENDER_UNIT_TESTS public: #else + private: #endif bool canRender() const; @@ -286,7 +290,7 @@ private: QScopedPointer<RenderThread> m_renderThread; QScopedPointer<VSyncFrameAdvanceService> m_vsyncFrameAdvanceService; - QMutex m_mutex; + QMutex m_renderQueueMutex; QSemaphore m_submitRenderViewsSemaphore; QSemaphore m_waitForInitializationToBeCompleted; @@ -300,6 +304,7 @@ private: BackendNodeDirtySet m_changeSet; QAtomicInt m_lastFrameCorrect; QOpenGLContext *m_glContext; + QOpenGLContext *m_shareContext; PickBoundingVolumeJobPtr m_pickBoundingVolumeJob; qint64 m_time; @@ -314,6 +319,7 @@ private: UpdateWorldBoundingVolumeJobPtr m_updateWorldBoundingVolumeJob; UpdateTreeEnabledJobPtr m_updateTreeEnabledJob; SendRenderCaptureJobPtr m_sendRenderCaptureJob; + SendBufferCaptureJobPtr m_sendBufferCaptureJob; UpdateLevelOfDetailJobPtr m_updateLevelOfDetailJob; UpdateMeshTriangleListJobPtr m_updateMeshTriangleListJob; FilterCompatibleTechniqueJobPtr m_filterCompatibleTechniqueJob; @@ -327,16 +333,23 @@ private: OpenGLVertexArrayObject **vao); GenericLambdaJobPtr<std::function<void ()>> m_bufferGathererJob; + GenericLambdaJobPtr<std::function<void ()>> m_vaoGathererJob; GenericLambdaJobPtr<std::function<void ()>> m_textureGathererJob; GenericLambdaJobPtr<std::function<void ()>> m_shaderGathererJob; SynchronizerJobPtr m_syncTextureLoadingJob; + void lookForAbandonedVaos(); void lookForDirtyBuffers(); + void lookForDownloadableBuffers(); void lookForDirtyTextures(); void lookForDirtyShaders(); + QMutex m_abandonedVaosMutex; + QVector<HVao> m_abandonedVaos; + QVector<HBuffer> m_dirtyBuffers; + QVector<HBuffer> m_downloadableBuffers; QVector<HShader> m_dirtyShaders; QVector<HTexture> m_dirtyTextures; diff --git a/src/render/backend/renderqueue.cpp b/src/render/backend/renderqueue.cpp index 6ec7da464..2fa1cb7d2 100644 --- a/src/render/backend/renderqueue.cpp +++ b/src/render/backend/renderqueue.cpp @@ -49,6 +49,7 @@ namespace Render { RenderQueue::RenderQueue() : m_noRender(false) + , m_wasReset(true) , m_targetRenderViewCount(0) , m_currentRenderViewCount(0) , m_currentWorkQueue(1) @@ -70,6 +71,7 @@ void RenderQueue::reset() m_targetRenderViewCount = 0; m_currentWorkQueue.clear(); m_noRender = false; + m_wasReset = true; } void RenderQueue::setNoRender() @@ -88,6 +90,7 @@ bool RenderQueue::queueRenderView(RenderView *renderView, uint submissionOrderIn Q_ASSERT(!m_noRender); m_currentWorkQueue[submissionOrderIndex] = renderView; ++m_currentRenderViewCount; + Q_ASSERT(m_currentRenderViewCount <= m_targetRenderViewCount); return isFrameQueueComplete(); } @@ -109,6 +112,7 @@ void RenderQueue::setTargetRenderViewCount(int targetRenderViewCount) Q_ASSERT(!m_noRender); m_targetRenderViewCount = targetRenderViewCount; m_currentWorkQueue.resize(targetRenderViewCount); + m_wasReset = false; } /*! @@ -119,7 +123,7 @@ void RenderQueue::setTargetRenderViewCount(int targetRenderViewCount) bool RenderQueue::isFrameQueueComplete() const { return (m_noRender - || (m_targetRenderViewCount && m_targetRenderViewCount == currentRenderViewCount())); + || (m_targetRenderViewCount > 0 && m_targetRenderViewCount == m_currentRenderViewCount)); } } // namespace Render diff --git a/src/render/backend/renderqueue_p.h b/src/render/backend/renderqueue_p.h index 49316049b..611f5849a 100644 --- a/src/render/backend/renderqueue_p.h +++ b/src/render/backend/renderqueue_p.h @@ -77,9 +77,13 @@ public: void reset(); void setNoRender(); + inline bool isNoRender() const { return m_noRender; } + + inline bool wasReset() const { return m_wasReset; } private: bool m_noRender; + bool m_wasReset; int m_targetRenderViewCount; int m_currentRenderViewCount; QVector<RenderView *> m_currentWorkQueue; diff --git a/src/render/backend/rendertargetoutput.cpp b/src/render/backend/rendertargetoutput.cpp index faebfacb6..cba92596d 100644 --- a/src/render/backend/rendertargetoutput.cpp +++ b/src/render/backend/rendertargetoutput.cpp @@ -121,9 +121,14 @@ void RenderTargetOutput::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e) BackendNode::sceneChangeEvent(e); } -Attachment RenderTargetOutput::attachment() const +Qt3DRender::Render::Attachment *RenderTargetOutput::attachment() { - return m_attachmentData; + return &m_attachmentData; +} + +const Attachment *RenderTargetOutput::attachment() const +{ + return &m_attachmentData; } } // namespace Render diff --git a/src/render/backend/rendertargetoutput_p.h b/src/render/backend/rendertargetoutput_p.h index 14daa84e9..b7867c2cb 100644 --- a/src/render/backend/rendertargetoutput_p.h +++ b/src/render/backend/rendertargetoutput_p.h @@ -77,7 +77,8 @@ public: QAbstractTexture::CubeMapFace face() const; QRenderTargetOutput::AttachmentPoint point() const; void sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e) Q_DECL_OVERRIDE; - Attachment attachment() const; + Attachment *attachment(); + const Attachment *attachment() const; private: void initializeFromPeer(const Qt3DCore::QNodeCreatedChangeBasePtr &change) Q_DECL_FINAL; diff --git a/src/render/backend/renderthread_p.h b/src/render/backend/renderthread_p.h index 026a98609..8d7c083ed 100644 --- a/src/render/backend/renderthread_p.h +++ b/src/render/backend/renderthread_p.h @@ -37,8 +37,8 @@ ** ****************************************************************************/ -#ifndef RENDERTHREAD_H -#define RENDERTHREAD_H +#ifndef QT3DRENDER_RENDER_RENDERTHREAD_H +#define QT3DRENDER_RENDER_RENDERTHREAD_H // // W A R N I N G @@ -75,7 +75,7 @@ public: Render::Renderer* renderer() const { return m_renderer; } protected: - void run(); + void run() Q_DECL_OVERRIDE; private: Renderer* m_renderer; @@ -88,4 +88,4 @@ private: QT_END_NAMESPACE -#endif // RENDERTHREAD_H +#endif // QT3DRENDER_RENDER_RENDERTHREAD_H diff --git a/src/render/backend/renderview.cpp b/src/render/backend/renderview.cpp index bfdb68eb1..03f61b2d9 100644 --- a/src/render/backend/renderview.cpp +++ b/src/render/backend/renderview.cpp @@ -66,6 +66,7 @@ #include <Qt3DRender/private/buffermanager_p.h> #include <Qt3DRender/private/geometryrenderermanager_p.h> #include <Qt3DRender/private/rendercapture_p.h> +#include <Qt3DRender/private/buffercapture_p.h> #include <Qt3DRender/private/stringtoint_p.h> #include <Qt3DCore/qentity.h> #include <QtGui/qsurface.h> @@ -128,6 +129,8 @@ RenderView::StandardUniformsNameToTypeHash RenderView::initializeStandardUniform setters.insert(StringToInt::lookupId(QLatin1String("modelViewNormal")), ModelViewNormalMatrix); setters.insert(StringToInt::lookupId(QLatin1String("viewportMatrix")), ViewportMatrix); setters.insert(StringToInt::lookupId(QLatin1String("inverseViewportMatrix")), InverseViewportMatrix); + setters.insert(StringToInt::lookupId(QLatin1String("exposure")), Exposure); + setters.insert(StringToInt::lookupId(QLatin1String("gamma")), Gamma); setters.insert(StringToInt::lookupId(QLatin1String("time")), Time); setters.insert(StringToInt::lookupId(QLatin1String("eyePosition")), EyePosition); @@ -190,6 +193,10 @@ UniformValue RenderView::standardUniformValue(RenderView::StandardUniform standa viewportMatrix.viewport(resolveViewport(m_viewport, m_surfaceSize)); return UniformValue(viewportMatrix.inverted()); } + case Exposure: + return UniformValue(m_data.m_renderCameraLens->exposure()); + case Gamma: + return UniformValue(m_gamma); case Time: return UniformValue(float(m_renderer->time() / 1000000000.0f)); case EyePosition: @@ -201,9 +208,11 @@ UniformValue RenderView::standardUniformValue(RenderView::StandardUniform standa } RenderView::RenderView() - : m_renderer(nullptr) + : m_isDownloadBuffersEnable(false) + , m_renderer(nullptr) , m_devicePixelRatio(1.) , m_viewport(QRectF(0.0f, 0.0f, 1.0f, 1.0f)) + , m_gamma(2.2f) , m_surface(nullptr) , m_clearBuffer(QClearBuffers::None) , m_stateSet(nullptr) @@ -211,6 +220,7 @@ RenderView::RenderView() , m_compute(false) , m_frustumCulling(false) , m_memoryBarrier(QMemoryBarrier::None) + , m_environmentLight(nullptr) { m_workGroups[0] = 1; m_workGroups[1] = 1; @@ -352,6 +362,7 @@ QVector<RenderCommand *> RenderView::buildDrawRenderCommands(const QVector<Entit // is only accessed from the same thread UniformBlockValueBuilder *builder = new UniformBlockValueBuilder(); builder->shaderDataManager = m_manager->shaderDataManager(); + builder->textureManager = m_manager->textureManager(); m_localData.setLocalData(builder); QVector<RenderCommand *> commands; @@ -404,7 +415,12 @@ QVector<RenderCommand *> RenderView::buildDrawRenderCommands(const QVector<Entit ParameterInfoList globalParameters = passData.parameterInfo; // setShaderAndUniforms can initialize a localData // make sure this is cleared before we leave this function - setShaderAndUniforms(command, pass, globalParameters, *(node->worldTransform()), lightSources.mid(0, std::max(lightSources.size(), MAX_LIGHTS))); + setShaderAndUniforms(command, + pass, + globalParameters, + *(node->worldTransform()), + lightSources.mid(0, std::max(lightSources.size(), MAX_LIGHTS)), + m_environmentLight); // Store all necessary information for actual drawing if command is valid command->m_isValid = !command->m_attributes.empty(); @@ -470,6 +486,7 @@ QVector<RenderCommand *> RenderView::buildComputeRenderCommands(const QVector<En // is only accessed from the same thread UniformBlockValueBuilder *builder = new UniformBlockValueBuilder(); builder->shaderDataManager = m_manager->shaderDataManager(); + builder->textureManager = m_manager->textureManager(); m_localData.setLocalData(builder); // If the RenderView contains only a ComputeDispatch then it cares about @@ -503,7 +520,8 @@ QVector<RenderCommand *> RenderView::buildComputeRenderCommands(const QVector<En pass, globalParameters, *(node->worldTransform()), - QVector<LightSource>()); + QVector<LightSource>(), + nullptr); commands.append(command); } } @@ -579,7 +597,7 @@ void RenderView::setUniformBlockValue(ShaderParameterPack &uniformPack, // If two shaders define the same block with the exact same layout, in that case the UBO could be shared // but how do we know that ? We'll need to compare ShaderUniformBlocks - // Note: we assume that if a buffer is shared accross multiple shaders + // Note: we assume that if a buffer is shared across multiple shaders // then it implies that they share the same layout // Temporarly disabled @@ -691,7 +709,7 @@ void RenderView::buildSortingKey(RenderCommand *command) const } void RenderView::setShaderAndUniforms(RenderCommand *command, RenderPass *rPass, ParameterInfoList ¶meters, const QMatrix4x4 &worldTransform, - const QVector<LightSource> &activeLightSources) const + const QVector<LightSource> &activeLightSources, EnvironmentLight *environmentLight) const { // The VAO Handle is set directly in the renderer thread so as to avoid having to use a mutex here // Set shader, technique, and effect by basically doing : @@ -812,13 +830,25 @@ void RenderView::setShaderAndUniforms(RenderCommand *command, RenderPass *rPass, if (uniformNamesIds.contains(LIGHT_COUNT_NAME_ID)) setUniformValue(command->m_parameterPack, LIGHT_COUNT_NAME_ID, UniformValue(qMax(1, lightIdx))); - if (activeLightSources.isEmpty()) { + // If no active light sources and no environment light, add a default light + if (activeLightSources.isEmpty() && !environmentLight) { // Note: implicit conversion of values to UniformValue setUniformValue(command->m_parameterPack, LIGHT_POSITION_NAMES[0], QVector3D(10.0f, 10.0f, 0.0f)); setUniformValue(command->m_parameterPack, LIGHT_TYPE_NAMES[0], int(QAbstractLight::PointLight)); setUniformValue(command->m_parameterPack, LIGHT_COLOR_NAMES[0], QVector3D(1.0f, 1.0f, 1.0f)); setUniformValue(command->m_parameterPack, LIGHT_INTENSITY_NAMES[0], 0.5f); } + + // Environment Light + int envLightCount = 0; + if (environmentLight && environmentLight->isEnabled()) { + ShaderData *shaderData = m_manager->shaderDataManager()->lookupResource(environmentLight->shaderData()); + if (shaderData) { + setDefaultUniformBlockShaderDataValue(command->m_parameterPack, shader, shaderData, QStringLiteral("envLight")); + envLightCount = 1; + } + } + setUniformValue(command->m_parameterPack, StringToInt::lookupId(QStringLiteral("envLightCount")), envLightCount); } // Set frag outputs in the shaders if hash not empty if (!fragOutputs.isEmpty()) @@ -830,6 +860,16 @@ void RenderView::setShaderAndUniforms(RenderCommand *command, RenderPass *rPass, } } +bool RenderView::isDownloadBuffersEnable() const +{ + return m_isDownloadBuffersEnable; +} + +void RenderView::setIsDownloadBuffersEnable(bool isDownloadBuffersEnable) +{ + m_isDownloadBuffersEnable = isDownloadBuffersEnable; +} + } // namespace Render } // namespace Qt3DRender diff --git a/src/render/backend/renderview_p.h b/src/render/backend/renderview_p.h index 1f00e2aae..440d51638 100644 --- a/src/render/backend/renderview_p.h +++ b/src/render/backend/renderview_p.h @@ -181,6 +181,9 @@ public: inline void setViewport(const QRectF &vp) Q_DECL_NOTHROW { m_viewport = vp; } inline QRectF viewport() const Q_DECL_NOTHROW { return m_viewport; } + inline float gamma() const Q_DECL_NOTHROW { return m_gamma; } + inline void setGamma(float gamma) Q_DECL_NOTHROW { m_gamma = gamma; } + // depth and stencil ClearBuffers are cached locally // color ClearBuffers are collected, as there may be multiple // color buffers to be cleared. we need to apply all these at rendering @@ -212,14 +215,15 @@ public: QSurface *surface() const { return m_surface; } void setLightSources(const QVector<LightSource> &lightSources) Q_DECL_NOTHROW { m_lightSources = lightSources; } + void setEnvironmentLight(EnvironmentLight *environmentLight) Q_DECL_NOTHROW { m_environmentLight = environmentLight; } void updateMatrices(); inline void setRenderCaptureNodeId(const Qt3DCore::QNodeId nodeId) Q_DECL_NOTHROW { m_renderCaptureNodeId = nodeId; } inline const Qt3DCore::QNodeId renderCaptureNodeId() const Q_DECL_NOTHROW { return m_renderCaptureNodeId; } - void setMemoryBarrier(QMemoryBarrier::BarrierTypes barrier) Q_DECL_NOTHROW { m_memoryBarrier = barrier; } - QMemoryBarrier::BarrierTypes memoryBarrier() const Q_DECL_NOTHROW { return m_memoryBarrier; } + void setMemoryBarrier(QMemoryBarrier::Operations barrier) Q_DECL_NOTHROW { m_memoryBarrier = barrier; } + QMemoryBarrier::Operations memoryBarrier() const Q_DECL_NOTHROW { return m_memoryBarrier; } // Helps making the size of RenderView smaller // Contains all the data needed for the actual building of the RenderView @@ -245,13 +249,17 @@ public: QVector3D m_eyePos; }; + bool isDownloadBuffersEnable() const; + void setIsDownloadBuffersEnable(bool isDownloadBuffersEnable); + private: void setShaderAndUniforms(RenderCommand *command, RenderPass *pass, ParameterInfoList ¶meters, const QMatrix4x4 &worldTransform, - const QVector<LightSource> &activeLightSources) const; + const QVector<LightSource> &activeLightSources, EnvironmentLight *environmentLight) const; mutable QThreadStorage<UniformBlockValueBuilder*> m_localData; Qt3DCore::QNodeId m_renderCaptureNodeId; + bool m_isDownloadBuffersEnable; Renderer *m_renderer; NodeManagers *m_manager; @@ -261,6 +269,7 @@ private: InnerData m_data; QRectF m_viewport; + float m_gamma; Qt3DCore::QNodeId m_renderTarget; QSurface *m_surface; AttachmentPack m_attachmentPack; @@ -274,13 +283,14 @@ private: bool m_compute:1; bool m_frustumCulling:1; int m_workGroups[3]; - QMemoryBarrier::BarrierTypes m_memoryBarrier; + QMemoryBarrier::Operations m_memoryBarrier; // We do not use pointers to RenderNodes or Drawable's here so that the // render aspect is free to change the drawables on the next frame whilst // the render thread is submitting these commands. QVector<RenderCommand *> m_commands; mutable QVector<LightSource> m_lightSources; + EnvironmentLight *m_environmentLight; QHash<Qt3DCore::QNodeId, QVector<RenderPassParameterData>> m_parameters; @@ -303,6 +313,8 @@ private: ViewportMatrix, InverseViewportMatrix, Time, + Exposure, + Gamma, EyePosition }; diff --git a/src/render/backend/renderviewbuilder.cpp b/src/render/backend/renderviewbuilder.cpp index 55d8ba462..f47c6f419 100644 --- a/src/render/backend/renderviewbuilder.cpp +++ b/src/render/backend/renderviewbuilder.cpp @@ -66,14 +66,14 @@ public: RenderView *rv = m_renderViewJob->renderView(); int totalCommandCount = 0; - for (const auto renderViewCommandBuilder : qAsConst(m_renderViewBuilderJobs)) + for (const auto &renderViewCommandBuilder : qAsConst(m_renderViewBuilderJobs)) totalCommandCount += renderViewCommandBuilder->commands().size(); QVector<RenderCommand *> commands; commands.reserve(totalCommandCount); // Reduction - for (const auto renderViewCommandBuilder : qAsConst(m_renderViewBuilderJobs)) + for (const auto &renderViewCommandBuilder : qAsConst(m_renderViewBuilderJobs)) commands += std::move(renderViewCommandBuilder->commands()); rv->setCommands(commands); @@ -139,13 +139,13 @@ public: m_filterEntityByLayerJob->setLayers(rv->layerFilter()); // Material Parameter building - for (const auto materialGatherer : qAsConst(m_materialGathererJobs)) { + for (const auto &materialGatherer : qAsConst(m_materialGathererJobs)) { materialGatherer->setRenderPassFilter(const_cast<RenderPassFilter *>(rv->renderPassFilter())); materialGatherer->setTechniqueFilter(const_cast<TechniqueFilter *>(rv->techniqueFilter())); } // Command builders - for (const auto renderViewCommandBuilder : qAsConst(m_renderViewBuilderJobs)) + for (const auto &renderViewCommandBuilder : qAsConst(m_renderViewBuilderJobs)) renderViewCommandBuilder->setRenderView(rv); // Set whether frustum culling is enabled or not @@ -188,8 +188,7 @@ public: RenderView *rv = m_renderViewJob->renderView(); if (!rv->noDraw()) { - // Set the light sources - rv->setLightSources(std::move(m_lightGathererJob->lights())); + rv->setEnvironmentLight(m_lightGathererJob->takeEnvironmentLight()); // We sort the vector so that the removal can then be performed linearly @@ -207,6 +206,14 @@ public: QVector<Entity *> filteredEntities = m_filterEntityByLayerJob->filteredEntities(); RenderViewBuilder::removeEntitiesNotInSubset(renderableEntities, filteredEntities); + // Set the light sources, with layer filters applied. + QVector<LightSource> lightSources = m_lightGathererJob->lights(); + for (int i = 0; i < lightSources.count(); ++i) { + if (!filteredEntities.contains(lightSources[i].entity)) + lightSources.removeAt(i--); + } + rv->setLightSources(lightSources); + // Filter out frustum culled entity for drawable entities if (isDraw && rv->frustumCulling()) RenderViewBuilder::removeEntitiesNotInSubset(renderableEntities, m_frustumCullingJob->visibleEntities()); @@ -224,7 +231,7 @@ public: // Reduction QHash<Qt3DCore::QNodeId, QVector<RenderPassParameterData>> params; - for (const auto materialGatherer : qAsConst(m_materialGathererJobs)) + for (const auto &materialGatherer : qAsConst(m_materialGathererJobs)) params.unite(materialGatherer->materialToPassAndParameter()); // Set all required data on the RenderView for final processing rv->setMaterialParameterTable(std::move(params)); @@ -424,7 +431,7 @@ QVector<Qt3DCore::QAspectJobPtr> RenderViewBuilder::buildJobHierachy() const m_filterEntityByLayerJob->addDependency(m_renderer->updateTreeEnabledJob()); m_syncRenderCommandBuildingJob->addDependency(m_syncRenderViewInitializationJob); - for (const auto materialGatherer : qAsConst(m_materialGathererJobs)) { + for (const auto &materialGatherer : qAsConst(m_materialGathererJobs)) { materialGatherer->addDependency(m_syncRenderViewInitializationJob); materialGatherer->addDependency(m_renderer->filterCompatibleTechniqueJob()); m_syncRenderCommandBuildingJob->addDependency(materialGatherer); @@ -435,7 +442,7 @@ QVector<Qt3DCore::QAspectJobPtr> RenderViewBuilder::buildJobHierachy() const m_syncRenderCommandBuildingJob->addDependency(m_lightGathererJob); m_syncRenderCommandBuildingJob->addDependency(m_frustumCullingJob); - for (const auto renderViewCommandBuilder : qAsConst(m_renderViewBuilderJobs)) { + for (const auto &renderViewCommandBuilder : qAsConst(m_renderViewBuilderJobs)) { renderViewCommandBuilder->addDependency(m_syncRenderCommandBuildingJob); m_syncRenderViewCommandBuildersJob->addDependency(renderViewCommandBuilder); } @@ -457,13 +464,13 @@ QVector<Qt3DCore::QAspectJobPtr> RenderViewBuilder::buildJobHierachy() const jobs.push_back(m_filterEntityByLayerJob); // Step 3 jobs.push_back(m_setClearDrawBufferIndexJob); // Step 3 - for (const auto materialGatherer : qAsConst(m_materialGathererJobs)) // Step3 + for (const auto &materialGatherer : qAsConst(m_materialGathererJobs)) // Step3 jobs.push_back(materialGatherer); jobs.push_back(m_frustumCullingJob); // Step 4 jobs.push_back(m_syncRenderCommandBuildingJob); // Step 4 - for (const auto renderViewCommandBuilder : qAsConst(m_renderViewBuilderJobs)) // Step 5 + for (const auto &renderViewCommandBuilder : qAsConst(m_renderViewBuilderJobs)) // Step 5 jobs.push_back(renderViewCommandBuilder); jobs.push_back(m_syncRenderViewCommandBuildersJob); // Step 6 diff --git a/src/render/backend/resourceaccessor.cpp b/src/render/backend/resourceaccessor.cpp new file mode 100644 index 000000000..7558eb4ad --- /dev/null +++ b/src/render/backend/resourceaccessor.cpp @@ -0,0 +1,123 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "resourceaccessor_p.h" + +#include <Qt3DRender/qrendertargetoutput.h> + +#include <private/qrendertargetoutput_p.h> +#include <private/nodemanagers_p.h> +#include <private/texture_p.h> +#include <private/rendertargetoutput_p.h> +#include <private/texturedatamanager_p.h> +#include <private/gltexturemanager_p.h> +#include <private/managers_p.h> +#include <private/gltexture_p.h> + +#include <QtCore/qmutex.h> + +QT_BEGIN_NAMESPACE + +namespace Qt3DRender { +namespace Render { + +RenderBackendResourceAccessor::~RenderBackendResourceAccessor() +{ + +} + +ResourceAccessor::ResourceAccessor(NodeManagers *mgr) + : m_glTextureManager(mgr->glTextureManager()) + , m_textureManager(mgr->textureManager()) + , m_attachmentManager(mgr->attachmentManager()) + , m_entityManager(mgr->renderNodesManager()) +{ + +} + +// called by render plugins from arbitrary thread +bool ResourceAccessor::accessResource(ResourceType type, Qt3DCore::QNodeId nodeId, void **handle, QMutex **lock) +{ + switch (type) { + + case RenderBackendResourceAccessor::OGLTexture: { + Texture *tex = m_textureManager->lookupResource(nodeId); + if (!tex) + return false; + + GLTexture *glTex = m_glTextureManager->lookupResource(tex->peerId()); + if (!glTex) + return false; + + if (glTex->isDirty()) + return false; + + QOpenGLTexture **glTextureHandle = reinterpret_cast<QOpenGLTexture **>(handle); + *glTextureHandle = glTex->getOrCreateGLTexture(); + *lock = glTex->textureLock(); + return true; + } + + case RenderBackendResourceAccessor::OutputAttachment: { + RenderTargetOutput *output = m_attachmentManager->lookupResource(nodeId); + if (output) { + Attachment **attachmentData = reinterpret_cast<Attachment **>(handle); + *attachmentData = output->attachment(); + return true; + } + break; + } + + case RenderBackendResourceAccessor::EntityHandle: { + Entity *entity = m_entityManager->lookupResource(nodeId); + if (entity) { + Entity **pEntity = reinterpret_cast<Entity **>(handle); + *pEntity = entity; + return true; + } + break; + } + + default: + break; + } + return false; +} + +} // namespace Render +} // namespace Qt3DRender + +QT_END_NAMESPACE diff --git a/src/render/backend/resourceaccessor_p.h b/src/render/backend/resourceaccessor_p.h new file mode 100644 index 000000000..b4ed2a3eb --- /dev/null +++ b/src/render/backend/resourceaccessor_p.h @@ -0,0 +1,102 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd and/or its subsidiary(-ies). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DRENDER_RENDER_RESOURCEACCESSOR_P_H +#define QT3DRENDER_RENDER_RESOURCEACCESSOR_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <Qt3DCore/qnodeid.h> + +#include <private/qt3drender_global_p.h> + +QT_BEGIN_NAMESPACE + +class QMutex; + +namespace Qt3DRender +{ +namespace Render { + +class TextureManager; +class AttachmentManager; +class GLTextureManager; +class EntityManager; +class NodeManagers; + +class RenderBackendResourceAccessor +{ +public: + enum ResourceType { + OGLTexture, + OutputAttachment, + EntityHandle, + }; + + virtual ~RenderBackendResourceAccessor(); + virtual bool accessResource(ResourceType type, Qt3DCore::QNodeId nodeId, void **handle, QMutex **lock) = 0; +}; + +class QT3DRENDERSHARED_PRIVATE_EXPORT ResourceAccessor : public RenderBackendResourceAccessor +{ +public: + ResourceAccessor(NodeManagers *mgr); + bool accessResource(ResourceType type, Qt3DCore::QNodeId nodeId, void **handle, QMutex **lock) Q_DECL_FINAL; +private: + GLTextureManager *m_glTextureManager; + TextureManager *m_textureManager; + AttachmentManager *m_attachmentManager; + EntityManager *m_entityManager; +}; + +} // namespace Render +} // namespace Qt3DRender + +QT_END_NAMESPACE + +#endif // QT3DRENDER_RENDER_RESOURCEACCESSOR_P_H diff --git a/src/render/backend/shadervariables_p.h b/src/render/backend/shadervariables_p.h index 2177abe0e..e0fa07dff 100644 --- a/src/render/backend/shadervariables_p.h +++ b/src/render/backend/shadervariables_p.h @@ -81,6 +81,7 @@ struct ShaderUniform { ShaderUniform() : m_nameId(-1) + , m_type(GL_NONE) , m_size(0) , m_offset(-1) , m_location(-1) diff --git a/src/render/backend/triangleboundingvolume.cpp b/src/render/backend/triangleboundingvolume.cpp index 80a23572c..ca2d26897 100644 --- a/src/render/backend/triangleboundingvolume.cpp +++ b/src/render/backend/triangleboundingvolume.cpp @@ -112,15 +112,18 @@ Qt3DCore::QNodeId TriangleBoundingVolume::id() const return m_id; } -bool TriangleBoundingVolume::intersects(const RayCasting::QRay3D &ray, QVector3D *q) const +bool TriangleBoundingVolume::intersects(const RayCasting::QRay3D &ray, QVector3D *q, QVector3D *uvw) const { float t = 0.0f; - QVector3D uvw; - const bool intersected = intersectsSegmentTriangle(ray, m_c, m_b, m_a, uvw, t); - - if (intersected && q != nullptr) - *q = ray.point(t * ray.distance()); - + QVector3D uvwr; + const float intersected = intersectsSegmentTriangle(ray, m_c, m_b, m_a, uvwr, t); + + if (intersected) { + if (q != nullptr) + *q = ray.point(t); + if (uvw != nullptr) + *uvw = uvwr; + } return intersected; } diff --git a/src/render/backend/triangleboundingvolume_p.h b/src/render/backend/triangleboundingvolume_p.h index 1163efc46..3192ad456 100644 --- a/src/render/backend/triangleboundingvolume_p.h +++ b/src/render/backend/triangleboundingvolume_p.h @@ -78,7 +78,7 @@ public: const QVector3D &c); Qt3DCore::QNodeId id() const Q_DECL_FINAL; - bool intersects(const RayCasting::QRay3D &ray, QVector3D *q) const Q_DECL_FINAL; + bool intersects(const RayCasting::QRay3D &ray, QVector3D *q, QVector3D *uvw) const Q_DECL_FINAL; Type type() const Q_DECL_FINAL; QVector3D a() const; diff --git a/src/render/backend/trianglesvisitor.cpp b/src/render/backend/trianglesvisitor.cpp index 35cdecc29..036f43fa0 100644 --- a/src/render/backend/trianglesvisitor.cpp +++ b/src/render/backend/trianglesvisitor.cpp @@ -48,6 +48,7 @@ #include <Qt3DRender/private/geometry_p.h> #include <Qt3DRender/private/attribute_p.h> #include <Qt3DRender/private/buffer_p.h> +#include <Qt3DRender/private/bufferutils_p.h> QT_BEGIN_NAMESPACE @@ -71,24 +72,6 @@ bool isTriangleBased(Qt3DRender::QGeometryRenderer::PrimitiveType type) Q_DECL_N } } -struct BufferInfo -{ - BufferInfo() - : type(QAttribute::VertexBaseType::Float) - , dataSize(0) - , count(0) - , byteStride(0) - , byteOffset(0) - {} - - QByteArray data; - QAttribute::VertexBaseType type; - uint dataSize; - uint count; - uint byteStride; - uint byteOffset; -}; - // TO DO: Add methods for triangle strip adjacency // What about primitive restart ? @@ -328,48 +311,67 @@ void traverseTriangleAdjacency(Vertex *vertices, } } - -template <QAttribute::VertexBaseType> struct EnumToType; -template <> struct EnumToType<QAttribute::Byte> { typedef const char type; }; -template <> struct EnumToType<QAttribute::UnsignedByte> { typedef const uchar type; }; -template <> struct EnumToType<QAttribute::Short> { typedef const short type; }; -template <> struct EnumToType<QAttribute::UnsignedShort> { typedef const ushort type; }; -template <> struct EnumToType<QAttribute::Int> { typedef const int type; }; -template <> struct EnumToType<QAttribute::UnsignedInt> { typedef const uint type; }; -template <> struct EnumToType<QAttribute::Float> { typedef const float type; }; -template <> struct EnumToType<QAttribute::Double> { typedef const double type; }; - -template<QAttribute::VertexBaseType v> -typename EnumToType<v>::type *castToType(const QByteArray &u, uint byteOffset) +template<typename Coordinate> +QVector4D readCoordinate(const BufferInfo &info, Coordinate *coordinates, uint index) { - return reinterpret_cast< typename EnumToType<v>::type *>(u.constData() + byteOffset); + const uint stride = info.byteStride / sizeof(Coordinate); + QVector4D ret(0, 0, 0, 1.0f); + coordinates += stride * index; + for (uint e = 0; e < info.dataSize; ++e) + ret[e] = coordinates[e]; + return ret; } template<typename Func> void processBuffer(const BufferInfo &info, Func &f) { switch (info.type) { - case QAttribute::Byte: f(info, castToType<QAttribute::Byte>(info.data, info.byteOffset)); + case QAttribute::Byte: f(info, BufferTypeInfo::castToType<QAttribute::Byte>(info.data, info.byteOffset)); return; - case QAttribute::UnsignedByte: f(info, castToType<QAttribute::UnsignedByte>(info.data, info.byteOffset)); + case QAttribute::UnsignedByte: f(info, BufferTypeInfo::castToType<QAttribute::UnsignedByte>(info.data, info.byteOffset)); return; - case QAttribute::Short: f(info, castToType<QAttribute::Short>(info.data, info.byteOffset)); + case QAttribute::Short: f(info, BufferTypeInfo::castToType<QAttribute::Short>(info.data, info.byteOffset)); return; - case QAttribute::UnsignedShort: f(info, castToType<QAttribute::UnsignedShort>(info.data, info.byteOffset)); + case QAttribute::UnsignedShort: f(info, BufferTypeInfo::castToType<QAttribute::UnsignedShort>(info.data, info.byteOffset)); return; - case QAttribute::Int: f(info, castToType<QAttribute::Int>(info.data, info.byteOffset)); + case QAttribute::Int: f(info, BufferTypeInfo::castToType<QAttribute::Int>(info.data, info.byteOffset)); return; - case QAttribute::UnsignedInt: f(info, castToType<QAttribute::UnsignedInt>(info.data, info.byteOffset)); + case QAttribute::UnsignedInt: f(info, BufferTypeInfo::castToType<QAttribute::UnsignedInt>(info.data, info.byteOffset)); return; - case QAttribute::Float: f(info, castToType<QAttribute::Float>(info.data, info.byteOffset)); + case QAttribute::Float: f(info, BufferTypeInfo::castToType<QAttribute::Float>(info.data, info.byteOffset)); return; - case QAttribute::Double: f(info, castToType<QAttribute::Double>(info.data, info.byteOffset)); + case QAttribute::Double: f(info, BufferTypeInfo::castToType<QAttribute::Double>(info.data, info.byteOffset)); return; default: return; } } +QVector4D readBuffer(const BufferInfo &info, uint index) +{ + switch (info.type) { + case QAttribute::Byte: + return readCoordinate(info, BufferTypeInfo::castToType<QAttribute::Byte>(info.data, info.byteOffset), index); + case QAttribute::UnsignedByte: + return readCoordinate(info, BufferTypeInfo::castToType<QAttribute::UnsignedByte>(info.data, info.byteOffset), index); + case QAttribute::Short: + return readCoordinate(info, BufferTypeInfo::castToType<QAttribute::Short>(info.data, info.byteOffset), index); + case QAttribute::UnsignedShort: + return readCoordinate(info, BufferTypeInfo::castToType<QAttribute::UnsignedShort>(info.data, info.byteOffset), index); + case QAttribute::Int: + return readCoordinate(info, BufferTypeInfo::castToType<QAttribute::Int>(info.data, info.byteOffset), index); + case QAttribute::UnsignedInt: + return readCoordinate(info, BufferTypeInfo::castToType<QAttribute::UnsignedInt>(info.data, info.byteOffset), index); + case QAttribute::Float: + return readCoordinate(info, BufferTypeInfo::castToType<QAttribute::Float>(info.data, info.byteOffset), index); + case QAttribute::Double: + return readCoordinate(info, BufferTypeInfo::castToType<QAttribute::Double>(info.data, info.byteOffset), index); + default: + break; + } + return QVector4D(); +} + template<typename Index> struct IndexedVertexExecutor { @@ -530,6 +532,53 @@ void TrianglesVisitor::apply(const GeometryRenderer *renderer, const Qt3DCore::Q } } +bool CoordinateReader::setGeometry(const GeometryRenderer *renderer, const QString &attributeName) +{ + if (renderer == nullptr || renderer->instanceCount() != 1 + || !isTriangleBased(renderer->primitiveType())) { + return false; + } + + Geometry *geom = m_manager->lookupResource<Geometry, GeometryManager>(renderer->geometryId()); + + if (!geom) + return false; + + Attribute *attribute = nullptr; + + const auto attrIds = geom->attributes(); + for (const Qt3DCore::QNodeId attrId : attrIds) { + attribute = m_manager->lookupResource<Attribute, AttributeManager>(attrId); + if (attribute){ + if (attribute->name() == attributeName + || (attributeName == QStringLiteral("default") + && attribute->name() == QAttribute::defaultTextureCoordinateAttributeName())) { + break; + } + } + attribute = nullptr; + } + + if (!attribute) + return false; + + m_attribute = attribute; + m_buffer = m_manager->lookupResource<Buffer, BufferManager>(attribute->bufferId()); + + m_bufferInfo.data = m_buffer->data(); + m_bufferInfo.type = m_attribute->vertexBaseType(); + m_bufferInfo.byteOffset = m_attribute->byteOffset(); + m_bufferInfo.byteStride = m_attribute->byteStride(); + m_bufferInfo.dataSize = m_attribute->vertexSize(); + m_bufferInfo.count = m_attribute->count(); + return true; +} + +QVector4D CoordinateReader::getCoordinate(uint vertexIndex) +{ + return readBuffer(m_bufferInfo, vertexIndex); +} + } // namespace Render } // namespace Qt3DRender diff --git a/src/render/backend/trianglesvisitor_p.h b/src/render/backend/trianglesvisitor_p.h index 5f7cff9c0..9428857ac 100644 --- a/src/render/backend/trianglesvisitor_p.h +++ b/src/render/backend/trianglesvisitor_p.h @@ -52,6 +52,10 @@ // #include <Qt3DCore/qnodeid.h> +#include <Qt3DRender/QAttribute> +#include <Qt3DRender/private/bufferutils_p.h> + +#include <private/qt3drender_global_p.h> QT_BEGIN_NAMESPACE @@ -65,6 +69,8 @@ namespace Render { class GeometryRenderer; class NodeManagers; +class Attribute; +class Buffer; class Q_AUTOTEST_EXPORT TrianglesVisitor { @@ -84,6 +90,27 @@ protected: Qt3DCore::QNodeId m_nodeId; }; +class QT3DRENDERSHARED_PRIVATE_EXPORT CoordinateReader +{ +public: + explicit CoordinateReader(NodeManagers *manager) + : m_manager(manager) + , m_attribute(nullptr) + , m_buffer(nullptr) + { + } + + bool setGeometry(const GeometryRenderer *renderer, const QString &attributeName); + + QVector4D getCoordinate(uint vertexIndex); + +protected: + NodeManagers *m_manager; + Attribute *m_attribute; + Buffer *m_buffer; + BufferInfo m_bufferInfo; +}; + } // namespace Render } // namespace Qt3DRender |