diff options
author | Sean Harmer <sean.harmer@kdab.com> | 2018-02-15 12:44:27 +0000 |
---|---|---|
committer | Sean Harmer <sean.harmer@kdab.com> | 2018-02-15 20:59:03 +0000 |
commit | 6e82860f19bb28744c0d7f6ccc88ca89b187e3ce (patch) | |
tree | 1c8682d5bf06a0a03cefc3da5e3918056de78a04 /src/render/backend | |
parent | 0bbccd492532786adc6816b992b85b4fdf162842 (diff) | |
parent | e1d1a59eb04f8b17582571275073a6cfa10e9e32 (diff) |
Merge remote-tracking branch 'origin/5.10' into 5.11
Conflicts:
src/animation/doc/src/qt3danimation-module.qdoc
src/render/backend/abstractrenderer_p.h
src/render/backend/buffervisitor_p.h
src/render/backend/renderer.cpp
src/render/backend/renderer_p.h
src/render/backend/triangleboundingvolume_p.h
src/render/backend/trianglesextractor_p.h
src/render/frontend/sphere_p.h
src/render/jobs/calcboundingvolumejob.cpp
src/render/jobs/job_common_p.h
src/render/jobs/pickboundingvolumejob.cpp
src/render/jobs/pickboundingvolumejob_p.h
src/render/jobs/pickboundingvolumeutils.cpp
src/render/jobs/renderviewjobutils_p.h
tests/auto/render/boundingsphere/tst_boundingsphere.cpp
tests/auto/render/commons/testrenderer.h
tests/auto/render/raycasting/tst_raycasting.cpp
tests/auto/render/render.pro
tests/auto/render/renderer/tst_renderer.cpp
Change-Id: I76633bc5a5a065e5f9ea62cc16563377e5c693a3
Diffstat (limited to 'src/render/backend')
34 files changed, 851 insertions, 508 deletions
diff --git a/src/render/backend/abstractrenderer_p.h b/src/render/backend/abstractrenderer_p.h index 54f37ea21..bf05bdec8 100644 --- a/src/render/backend/abstractrenderer_p.h +++ b/src/render/backend/abstractrenderer_p.h @@ -134,7 +134,7 @@ public: // Threaded renderer virtual void render() = 0; // Synchronous renderer - virtual void doRender(bool scene3dBlocking = false) = 0; + virtual void doRender() = 0; virtual void cleanGraphicsResources() = 0; @@ -151,7 +151,7 @@ public: virtual QVector<Qt3DCore::QAspectJobPtr> renderBinJobs() = 0; virtual Qt3DCore::QAspectJobPtr pickBoundingVolumeJob() = 0; virtual Qt3DCore::QAspectJobPtr rayCastingJob() = 0; - virtual Qt3DCore::QAspectJobPtr syncTextureLoadingJob() = 0; + virtual Qt3DCore::QAspectJobPtr syncSkeletonLoadingJob() = 0; virtual Qt3DCore::QAspectJobPtr expandBoundingVolumeJob() = 0; virtual void setSceneRoot(Qt3DCore::QBackendNodeFactory *factory, Entity *root) = 0; @@ -170,6 +170,12 @@ public: virtual void setOffscreenSurfaceHelper(OffscreenSurfaceHelper *helper) = 0; virtual QSurfaceFormat format() = 0; virtual QOpenGLContext *shareContext() const = 0; + + virtual void lockSurfaceAndRender() = 0; + virtual bool releaseRendererAndRequestPromiseToRender() = 0; + virtual bool waitForRenderJobs() = 0; + virtual bool tryWaitForRenderJobs(int timeout) = 0; + virtual void abortRenderJobs() = 0; }; Q_DECLARE_OPERATORS_FOR_FLAGS(AbstractRenderer::BackendNodeDirtySet) diff --git a/src/render/backend/buffervisitor_p.h b/src/render/backend/buffervisitor_p.h index 4870171b1..98749b62c 100644 --- a/src/render/backend/buffervisitor_p.h +++ b/src/render/backend/buffervisitor_p.h @@ -195,7 +195,7 @@ protected: const uint byteStride, const uint count) { - const uint stride = byteStride / sizeof(Coordinate); + const uint stride = byteStride ? byteStride / sizeof(Coordinate) : 2; for (uint ndx = 0; ndx < count; ++ndx) { visit(ndx, coordinates[0], coordinates[1]); coordinates += stride; @@ -211,7 +211,7 @@ protected: bool primitiveRestartEnabled, int primitiveRestartIndex) { - const uint stride = byteStride / sizeof(Coordinate); + const uint stride = byteStride ? byteStride / sizeof(Coordinate) : 2; for (uint i = 0; i < count; ++i) { if (!primitiveRestartEnabled || indices[i] != primitiveRestartIndex) { const uint n = stride * indices[i]; @@ -225,7 +225,7 @@ protected: const uint byteStride, const uint count) { - const uint stride = byteStride / sizeof(Coordinate); + const uint stride = byteStride ? byteStride / sizeof(Coordinate) : 3; for (uint ndx = 0; ndx < count; ++ndx) { visit(ndx, coordinates[0], coordinates[1], coordinates[2]); coordinates += stride; @@ -240,7 +240,7 @@ protected: bool primitiveRestartEnabled, int primitiveRestartIndex) { - const uint stride = byteStride / sizeof(Coordinate); + const uint stride = byteStride ? byteStride / sizeof(Coordinate) : 3; for (uint i = 0; i < count; ++i) { if (!primitiveRestartEnabled || indices[i] != primitiveRestartIndex) { const uint n = stride * indices[i]; @@ -254,7 +254,7 @@ protected: const uint byteStride, const uint count) { - const uint stride = byteStride / sizeof(Coordinate); + const uint stride = byteStride ? byteStride / sizeof(Coordinate) : 4; for (uint ndx = 0; ndx < count; ++ndx) { visit(ndx, coordinates[0], coordinates[1], coordinates[2], coordinates[3]); coordinates += stride; @@ -269,7 +269,7 @@ protected: bool primitiveRestartEnabled, int primitiveRestartIndex) { - const uint stride = byteStride / sizeof(Coordinate); + const uint stride = byteStride ? byteStride / sizeof(Coordinate) : 4; for (uint i = 0; i < count; ++i) { if (!primitiveRestartEnabled || indices[i] != primitiveRestartIndex) { const uint n = stride * indices[i]; diff --git a/src/render/backend/cameralens.cpp b/src/render/backend/cameralens.cpp index e127b5885..b540b24c8 100644 --- a/src/render/backend/cameralens.cpp +++ b/src/render/backend/cameralens.cpp @@ -105,23 +105,25 @@ void CameraLens::setRenderAspect(QRenderAspect *renderAspect) m_renderAspect = renderAspect; } -QMatrix4x4 CameraLens::viewMatrix(const QMatrix4x4 &worldTransform) +Matrix4x4 CameraLens::viewMatrix(const Matrix4x4 &worldTransform) { - const QVector4D position = worldTransform * QVector4D(0.0f, 0.0f, 0.0f, 1.0f); + const Vector4D position = worldTransform * Vector4D(0.0f, 0.0f, 0.0f, 1.0f); // OpenGL convention is looking down -Z - const QVector4D viewDirection = worldTransform * QVector4D(0.0f, 0.0f, -1.0f, 0.0f); - const QVector4D upVector = worldTransform * QVector4D(0.0f, 1.0f, 0.0f, 0.0f); + const Vector4D viewDirection = worldTransform * Vector4D(0.0f, 0.0f, -1.0f, 0.0f); + const Vector4D upVector = worldTransform * Vector4D(0.0f, 1.0f, 0.0f, 0.0f); QMatrix4x4 m; - m.lookAt(position.toVector3D(), (position + viewDirection).toVector3D(), upVector.toVector3D()); - return m; + m.lookAt(convertToQVector3D(Vector3D(position)), + convertToQVector3D(Vector3D(position + viewDirection)), + convertToQVector3D(Vector3D(upVector))); + return Matrix4x4(m); } 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_projection = Matrix4x4(data.projectionMatrix); m_exposure = data.exposure; } @@ -160,7 +162,7 @@ void CameraLens::notifySceneBoundingVolume(const Sphere &sphere, QNodeCommand::C } } -void CameraLens::setProjection(const QMatrix4x4 &projection) +void CameraLens::setProjection(const Matrix4x4 &projection) { m_projection = projection; } @@ -178,7 +180,7 @@ void CameraLens::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e) if (propertyChange->propertyName() == QByteArrayLiteral("projectionMatrix")) { QMatrix4x4 projectionMatrix = propertyChange->value().value<QMatrix4x4>(); - m_projection = projectionMatrix; + m_projection = Matrix4x4(projectionMatrix); } else if (propertyChange->propertyName() == QByteArrayLiteral("exposure")) { setExposure(propertyChange->value().toFloat()); } @@ -212,7 +214,7 @@ void CameraLens::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e) } bool CameraLens::viewMatrixForCamera(EntityManager* manager, Qt3DCore::QNodeId cameraId, - QMatrix4x4 &viewMatrix, QMatrix4x4 &projectionMatrix) + Matrix4x4 &viewMatrix, Matrix4x4 &projectionMatrix) { Entity *camNode = manager->lookupResource(cameraId); if (!camNode) diff --git a/src/render/backend/cameralens_p.h b/src/render/backend/cameralens_p.h index 9e237cfda..d00358fb8 100644 --- a/src/render/backend/cameralens_p.h +++ b/src/render/backend/cameralens_p.h @@ -53,7 +53,7 @@ #include <Qt3DRender/private/backendnode_p.h> #include <Qt3DCore/private/qnodecommand_p.h> -#include <QMatrix4x4> +#include <Qt3DCore/private/matrix4x4_p.h> #include <QRectF> QT_BEGIN_NAMESPACE @@ -89,10 +89,10 @@ public: void setRenderAspect(QRenderAspect* renderAspect); - QMatrix4x4 viewMatrix(const QMatrix4x4 &worldTransform); + Matrix4x4 viewMatrix(const Matrix4x4 &worldTransform); - void setProjection(const QMatrix4x4 &projection); - inline QMatrix4x4 projection() const { return m_projection; } + void setProjection(const Matrix4x4 &projection); + inline Matrix4x4 projection() const { return m_projection; } void setExposure(float exposure); inline float exposure() const { return m_exposure; } @@ -101,7 +101,7 @@ public: void notifySceneBoundingVolume(const Sphere &sphere, Qt3DCore::QNodeCommand::CommandId commandId); static bool viewMatrixForCamera(EntityManager *manager, Qt3DCore::QNodeId cameraId, - QMatrix4x4 &viewMatrix, QMatrix4x4 &projectionMatrix); + Matrix4x4 &viewMatrix, Matrix4x4 &projectionMatrix); private: void initializeFromPeer(const Qt3DCore::QNodeCreatedChangeBasePtr &change) final; @@ -110,8 +110,8 @@ private: Qt3DCore::QNodeCommand::CommandId commandId); QRenderAspect *m_renderAspect; - QMatrix4x4 m_projection; Qt3DCore::QNodeCommand::CommandId m_pendingViewAllCommand; + Matrix4x4 m_projection; float m_exposure; }; diff --git a/src/render/backend/entity.cpp b/src/render/backend/entity.cpp index 1a0e877d6..85334d1d3 100644 --- a/src/render/backend/entity.cpp +++ b/src/render/backend/entity.cpp @@ -291,12 +291,12 @@ QVector<Entity *> Entity::children() const return childrenVector; } -QMatrix4x4 *Entity::worldTransform() +Matrix4x4 *Entity::worldTransform() { return m_nodeManagers->worldMatrixManager()->data(m_worldTransform); } -const QMatrix4x4 *Entity::worldTransform() const +const Matrix4x4 *Entity::worldTransform() const { return m_nodeManagers->worldMatrixManager()->data(m_worldTransform); } diff --git a/src/render/backend/entity_p.h b/src/render/backend/entity_p.h index 5448b5297..075871d85 100644 --- a/src/render/backend/entity_p.h +++ b/src/render/backend/entity_p.h @@ -104,8 +104,8 @@ public: QVector<Entity *> children() const; bool hasChildren() const { return !m_childrenHandles.empty(); } - QMatrix4x4 *worldTransform(); - const QMatrix4x4 *worldTransform() const; + Matrix4x4 *worldTransform(); + const Matrix4x4 *worldTransform() const; Sphere *localBoundingVolume() const { return m_localBoundingVolume.data(); } Sphere *worldBoundingVolume() const { return m_worldBoundingVolume.data(); } Sphere *worldBoundingVolumeWithChildren() const { return m_worldBoundingVolumeWithChildren.data(); } diff --git a/src/render/backend/handle_types_p.h b/src/render/backend/handle_types_p.h index bec2e3a9a..035bbfc91 100644 --- a/src/render/backend/handle_types_p.h +++ b/src/render/backend/handle_types_p.h @@ -53,11 +53,10 @@ #include <Qt3DRender/qt3drender_global.h> #include <Qt3DCore/private/qhandle_p.h> +#include <Qt3DCore/private/matrix4x4_p.h> QT_BEGIN_NAMESPACE -class QMatrix4x4; - namespace Qt3DRender { class QTextureImageData; @@ -109,7 +108,7 @@ typedef Qt3DCore::QHandle<FrameGraphNode *> HFrameGraphNode; typedef Qt3DCore::QHandle<Layer> HLayer; typedef Qt3DCore::QHandle<LevelOfDetail> HLevelOfDetail; typedef Qt3DCore::QHandle<Material> HMaterial; -typedef Qt3DCore::QHandle<QMatrix4x4> HMatrix; +typedef Qt3DCore::QHandle<Matrix4x4> HMatrix; typedef Qt3DCore::QHandle<OpenGLVertexArrayObject> HVao; typedef Qt3DCore::QHandle<Shader> HShader; typedef Qt3DCore::QHandle<ShaderBuilder> HShaderBuilder; diff --git a/src/render/backend/managers_p.h b/src/render/backend/managers_p.h index b3e7b6f27..c9bcb799e 100644 --- a/src/render/backend/managers_p.h +++ b/src/render/backend/managers_p.h @@ -52,6 +52,7 @@ // #include <Qt3DCore/private/qresourcemanager_p.h> +#include <Qt3DCore/private/matrix4x4_p.h> #include <Qt3DRender/private/rendertargetoutput_p.h> #include <Qt3DRender/private/cameralens_p.h> #include <Qt3DRender/private/filterkey_p.h> @@ -189,7 +190,7 @@ public: }; class MatrixManager : public Qt3DCore::QResourceManager< - QMatrix4x4, + Matrix4x4, Qt3DCore::QNodeId, Qt3DCore::NonLockingPolicy> { diff --git a/src/render/backend/nodemanagers.cpp b/src/render/backend/nodemanagers.cpp index 035d4d8d4..5db35082d 100644 --- a/src/render/backend/nodemanagers.cpp +++ b/src/render/backend/nodemanagers.cpp @@ -174,7 +174,7 @@ MaterialManager *NodeManagers::manager<Material>() const Q_DECL_NOTHROW } template<> -MatrixManager *NodeManagers::manager<QMatrix4x4*>() const Q_DECL_NOTHROW +MatrixManager *NodeManagers::manager<Matrix4x4>() const Q_DECL_NOTHROW { return m_worldMatrixManager; } diff --git a/src/render/backend/pointsvisitor.cpp b/src/render/backend/pointsvisitor.cpp index 663488357..750182b19 100644 --- a/src/render/backend/pointsvisitor.cpp +++ b/src/render/backend/pointsvisitor.cpp @@ -72,7 +72,7 @@ void traverseCoordinatesIndexed(Index *indices, const uint maxVerticesDataSize = qMin(vertexInfo.dataSize, 3U); uint ndx; - QVector3D abc; + Vector3D abc; while (i < indexInfo.count) { ndx = indices[i]; const uint idx = ndx * verticesStride; @@ -94,7 +94,7 @@ void traverseCoordinates(Vertex *vertices, const uint maxVerticesDataSize = qMin(vertexInfo.dataSize, 3U); uint ndx = 0; - QVector3D abc; + Vector3D abc; while (ndx < vertexInfo.count) { const uint idx = ndx * verticesStride; for (uint j = 0; j < maxVerticesDataSize; ++j) diff --git a/src/render/backend/pointsvisitor_p.h b/src/render/backend/pointsvisitor_p.h index 9d44ffec5..1614f185b 100644 --- a/src/render/backend/pointsvisitor_p.h +++ b/src/render/backend/pointsvisitor_p.h @@ -52,6 +52,7 @@ // #include <Qt3DCore/qnodeid.h> +#include <Qt3DCore/private/vector3d_p.h> QT_BEGIN_NAMESPACE @@ -75,7 +76,7 @@ public: void apply(const Qt3DCore::QEntity *entity); void apply(const GeometryRenderer *renderer, const Qt3DCore::QNodeId id); - virtual void visit(uint ndx, const QVector3D &c) = 0; + virtual void visit(uint ndx, const Vector3D &c) = 0; protected: NodeManagers *m_manager; diff --git a/src/render/backend/render-backend.pri b/src/render/backend/render-backend.pri index f7f30b5c9..a3df90f8c 100644 --- a/src/render/backend/render-backend.pri +++ b/src/render/backend/render-backend.pri @@ -45,7 +45,8 @@ HEADERS += \ $$PWD/visitorutils_p.h \ $$PWD/segmentsvisitor_p.h \ $$PWD/pointsvisitor_p.h \ - $$PWD/renderercache_p.h + $$PWD/renderercache_p.h \ + $$PWD/renderbarrierjob_p.h SOURCES += \ $$PWD/renderthread.cpp \ @@ -82,7 +83,8 @@ SOURCES += \ $$PWD/resourceaccessor.cpp \ $$PWD/segmentsvisitor.cpp \ $$PWD/commandthread.cpp \ - $$PWD/pointsvisitor.cpp + $$PWD/pointsvisitor.cpp \ + $$PWD/renderbarrierjob.cpp include($$QT3D_BUILD_ROOT/src/core/qt3dcore-config.pri) QT_FOR_CONFIG += 3dcore-private diff --git a/src/render/backend/renderbarrierjob.cpp b/src/render/backend/renderbarrierjob.cpp new file mode 100644 index 000000000..c60c9f368 --- /dev/null +++ b/src/render/backend/renderbarrierjob.cpp @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore 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 "renderbarrierjob_p.h" + +#include <Qt3DRender/private/renderlogging_p.h> + +#include <QtCore/QThread> + +QT_BEGIN_NAMESPACE + +namespace Qt3DRender { +namespace Render { + +QDebug operator<<(QDebug debug, JobTypes::JobType type) +{ + switch (type) { + case JobTypes::ReadRenderQueueSizeBarrier: + debug << "ReadRenderQueueSize"; + break; + case JobTypes::BeginDrawingBarrier: + debug << "BeginDrawing"; + break; + case JobTypes::UpdateGLResourcesBarrier: + debug << "UpdateGLResources"; + break; + case JobTypes::PrepareCommandSubmissionBarrier: + debug << "PrepareCommandSubmission"; + break; + case JobTypes::EndDrawingBarrier: + debug << "EndDrawing"; + break; + default: + break; + } + return debug; +} + +RenderBarrierJob::RenderBarrierJob(JobTypes::JobType type) + : QAspectJob() + , m_type(type) + , m_begin(0) + , m_end(0) +{ + SET_JOB_RUN_STAT_TYPE(this, type, 0); +} + +void RenderBarrierJob::waitForDependencies() +{ + qCDebug(Jobs) << Q_FUNC_INFO << m_type << "waiting for job on" << QThread::currentThread(); + m_begin.acquire(); + qCDebug(Jobs) << Q_FUNC_INFO << m_type << "done waiting for job on" << QThread::currentThread(); + Q_ASSERT(m_begin.available() == 0); +} + +void RenderBarrierJob::allowToProceed() +{ + qCDebug(Jobs) << Q_FUNC_INFO << m_type << "releasing job on" << QThread::currentThread(); + m_end.release(); +} + +void RenderBarrierJob::run() +{ + qCDebug(Jobs) << Q_FUNC_INFO << m_type << "job releasing render thread on" << QThread::currentThread(); + m_begin.release(); + qCDebug(Jobs) << Q_FUNC_INFO << m_type << "job waiting for render thread on" << QThread::currentThread(); + m_end.acquire(); + qCDebug(Jobs) << Q_FUNC_INFO << m_type << "job done on" << QThread::currentThread(); + Q_ASSERT(m_end.available() == 0); +} + +} // namespace Render +} // namespace Qt3DRender + +QT_END_NAMESPACE diff --git a/src/render/backend/renderbarrierjob_p.h b/src/render/backend/renderbarrierjob_p.h new file mode 100644 index 000000000..a39b2aa83 --- /dev/null +++ b/src/render/backend/renderbarrierjob_p.h @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore 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_RENDERBARRIERJOB_P_H +#define QT3DRENDER_RENDER_RENDERBARRIERJOB_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/QAspectJob> + +#include <Qt3DRender/private/qt3drender_global_p.h> +#include <Qt3DRender/private/job_common_p.h> + +#include <QtCore/qsemaphore.h> + +#include <functional> + +QT_BEGIN_NAMESPACE + +namespace Qt3DRender { +namespace Render { + +class QT3DRENDERSHARED_PRIVATE_EXPORT RenderBarrierJob : public Qt3DCore::QAspectJob +{ +public: + RenderBarrierJob(JobTypes::JobType type); + // Called from render thread + void waitForDependencies(); + // Called from render thread + void allowToProceed(); + + void run() final; +private: + JobTypes::JobType m_type; + QSemaphore m_begin; + QSemaphore m_end; +}; + +using RenderBarrierJobPtr = QSharedPointer<RenderBarrierJob>; + +} // namespace Render +} // namespace Qt3DRender + +QT_END_NAMESPACE + +#endif // QT3DRENDER_RENDER_RENDERBARRIERJOB_P_H diff --git a/src/render/backend/renderer.cpp b/src/render/backend/renderer.cpp index bb0585e6f..d89813f57 100644 --- a/src/render/backend/renderer.cpp +++ b/src/render/backend/renderer.cpp @@ -89,11 +89,14 @@ #include <Qt3DRender/private/buffercapture_p.h> #include <Qt3DRender/private/offscreensurfacehelper_p.h> #include <Qt3DRender/private/renderviewbuilder_p.h> +#include <Qt3DRender/private/loadtexturedatajob_p.h> +#include <Qt3DRender/private/renderbarrierjob_p.h> #include <Qt3DRender/qcameralens.h> #include <Qt3DCore/private/qeventfilterservice_p.h> #include <Qt3DCore/private/qabstractaspectjobmanager_p.h> #include <Qt3DCore/private/qnodecreatedchangegenerator_p.h> +#include <private/rendersurfaceselector_p.h> #if QT_CONFIG(qt3d_profile_jobs) #include <Qt3DCore/private/aspectcommanddebugger_p.h> @@ -185,10 +188,16 @@ Renderer::Renderer(QRenderAspect::RenderType type) , 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_loadTextureJob(Render::LoadTextureDataJobPtr::create()) , m_shaderGathererJob(Render::GenericLambdaJobPtr<std::function<void ()>>::create([this] { lookForDirtyShaders(); }, JobTypes::DirtyShaderGathering)) - , m_syncTextureLoadingJob(Render::GenericLambdaJobPtr<std::function<void ()>>::create([] {}, JobTypes::SyncTextureLoading)) + , m_syncSkeletonLoadingJob(Render::GenericLambdaJobPtr<std::function<void ()>>::create([] {}, JobTypes::SyncSkeletonLoading)) , m_ownedContext(false) , m_offscreenHelper(nullptr) + , m_readRenderQueueSizeBarrierJob(RenderBarrierJobPtr::create(JobTypes::ReadRenderQueueSizeBarrier)) + , m_beginDrawingBarrierJob(RenderBarrierJobPtr::create(JobTypes::BeginDrawingBarrier)) + , m_updateGLResourcesBarrierJob(RenderBarrierJobPtr::create(JobTypes::UpdateGLResourcesBarrier)) + , m_prepareCommandSubmissionBarrierJob(RenderBarrierJobPtr::create(JobTypes::PrepareCommandSubmissionBarrier)) + , m_endDrawingBarrierJob(RenderBarrierJobPtr::create(JobTypes::EndDrawingBarrier)) #if QT_CONFIG(qt3d_profile_jobs) , m_commandExecuter(new Qt3DRender::Debug::CommandExecuter(this)) #endif @@ -199,27 +208,49 @@ Renderer::Renderer(QRenderAspect::RenderType type) if (m_renderThread) m_renderThread->waitForStart(); + // Render thread barrier interdependencies + m_beginDrawingBarrierJob->addDependency(m_readRenderQueueSizeBarrierJob); + m_updateGLResourcesBarrierJob->addDependency(m_beginDrawingBarrierJob); + m_prepareCommandSubmissionBarrierJob->addDependency(m_updateGLResourcesBarrierJob); + m_endDrawingBarrierJob->addDependency(m_prepareCommandSubmissionBarrierJob); + // Create jobs to update transforms and bounding volumes // We can only update bounding volumes once all world transforms are known - m_calculateBoundingVolumeJob->addDependency(m_updateTreeEnabledJob); m_updateWorldBoundingVolumeJob->addDependency(m_worldTransformJob); m_updateWorldBoundingVolumeJob->addDependency(m_calculateBoundingVolumeJob); m_expandBoundingVolumeJob->addDependency(m_updateWorldBoundingVolumeJob); m_updateShaderDataTransformJob->addDependency(m_worldTransformJob); m_pickBoundingVolumeJob->addDependency(m_expandBoundingVolumeJob); - - // Dirty texture gathering depends on m_syncTextureLoadingJob - // m_syncTextureLoadingJob will depend on the texture loading jobs - m_textureGathererJob->addDependency(m_syncTextureLoadingJob); + // m_calculateBoundingVolumeJob's dependency on m_updateTreeEnabledJob is set in renderBinJobs // Ensures all skeletons are loaded before we try to update them - m_updateSkinningPaletteJob->addDependency(m_syncTextureLoadingJob); + m_updateSkinningPaletteJob->addDependency(m_syncSkeletonLoadingJob); // All world stuff depends on the RenderEntity's localBoundingVolume m_updateLevelOfDetailJob->addDependency(m_updateMeshTriangleListJob); m_pickBoundingVolumeJob->addDependency(m_updateMeshTriangleListJob); m_rayCastingJob->addDependency(m_updateMeshTriangleListJob); + // Independent job dependencies + m_beginDrawingBarrierJob->addDependency(m_vaoGathererJob); + + // Make sure we do not try to filter techniques before we have proper context + m_filterCompatibleTechniqueJob->addDependency(m_beginDrawingBarrierJob); + + // Make sure we have gathered dirty shaders, textures and buffers before calling updateGLResources + m_updateGLResourcesBarrierJob->addDependency(m_bufferGathererJob); + m_updateGLResourcesBarrierJob->addDependency(m_shaderGathererJob); + m_updateGLResourcesBarrierJob->addDependency(m_textureGathererJob); + + // Make sure we load textures after gathering + m_loadTextureJob->addDependency(m_updateGLResourcesBarrierJob); + m_loadTextureJob->addDependency(m_textureGathererJob); + + // Make sure we prepare command submission after textures are loaded + m_prepareCommandSubmissionBarrierJob->addDependency(m_loadTextureJob); + + // Make sure we have found compatible techniques before gathering shaders + m_updateGLResourcesBarrierJob->addDependency(m_filterCompatibleTechniqueJob); m_shaderGathererJob->addDependency(m_filterCompatibleTechniqueJob); m_filterCompatibleTechniqueJob->setRenderer(this); @@ -288,6 +319,7 @@ void Renderer::setNodeManagers(NodeManagers *managers) m_updateSkinningPaletteJob->setManagers(m_nodesManager); m_updateMeshTriangleListJob->setManagers(m_nodesManager); m_filterCompatibleTechniqueJob->setManager(m_nodesManager->techniqueManager()); + m_loadTextureJob->setNodeManagers(m_nodesManager); } void Renderer::setServices(QServiceLocator *services) @@ -406,12 +438,17 @@ void Renderer::shutdown() m_renderQueue->reset(); if (!m_renderThread) { + // Make sure the aspect thread is not left behind + m_willRenderPromise.store(0); + m_rendererReadySemaphore.release(); releaseGraphicsResources(); } else { // Wake up the render thread in case it is waiting for some renderviews // to be ready. The isReadyToSubmit() function checks for a shutdown // having been requested. - m_submitRenderViewsSemaphore.release(1); + m_willRenderPromise.store(0); + m_rendererReadySemaphore.release(1); + m_renderJobsReadySemaphore.release(1); m_renderThread->wait(); } } @@ -527,7 +564,7 @@ void Renderer::setSceneRoot(QBackendNodeFactory *factory, Entity *sgRoot) m_updateTreeEnabledJob->setRoot(m_renderSceneRoot); // Set all flags to dirty - m_dirtyBits.marked |= AbstractRenderer::AllDirty; + m_dirtyBits |= AbstractRenderer::AllDirty; } void Renderer::registerEventFilter(QEventFilterService *service) @@ -546,6 +583,123 @@ RenderSettings *Renderer::settings() const return m_settings; } +bool Renderer::createSurfaceLockAndMakeCurrent() +{ + FrameGraphVisitor visitor(nodeManagers()->frameGraphManager()); + const QVector<FrameGraphNode *> fgLeaves = visitor.traverse(frameGraphRoot()); + for (FrameGraphNode *leafNode : fgLeaves) { + FrameGraphNode *node = leafNode; + while (node) { + if (!node->isEnabled() || node->nodeType() != FrameGraphNode::Surface) { + node = node->parent(); + continue; + } + const auto *surfaceSelector = static_cast<const RenderSurfaceSelector *>(node); + auto locker = QSharedPointer<SurfaceLocker>::create(surfaceSelector->surface()); + if (!locker->isSurfaceValid()) + return false; + + m_surfaceLockers.append(locker); + node = node->parent(); + + if (!m_graphicsContext->makeCurrent(surfaceSelector->surface())) + return false; + + // TODO: The same surface may appear twice here, so we return early to avoid a deadlock. + // We may want to fix this at some point and make sure we properly lock multiple + // surfaces. + return true; + } + } + // No surface found + return false; +} + +// Called by render() or Scene3D on render thread +void Renderer::lockSurfaceAndRender() +{ + // Make sure we can lock the surface before rendering. This cannot be done in a job, + // because we would otherwise risk rendering into a surface that could get destroyed. + // We could lock/unlock while rendering, but that requires a fair amount of extra + // logic to be able to recover gracefully from a partially completed render. + if (!createSurfaceLockAndMakeCurrent()) { + m_surfaceLockers.clear(); + abortRenderJobs(); + // We cannot render because we could not lock the surface or the surface is a nullptr. + // However, the surface might change in the next frontend/backend sync. + // We therefore need to make sure the aspect thread does not get stuck waiting for + // the next frame. + m_vsyncFrameAdvanceService->proceedToNextFrame(); + return; + } + // Let the aspect thread know that we promise to render + m_willRenderPromise.store(1); + // Release the aspect thread + m_rendererReadySemaphore.release(); + doRender(); + // Release the surface lockers + m_surfaceLockers.clear(); +} + +/*! + * \internal + * Called by RenderAspect on aspect thread, returns false if + * the render thread has not promised to render the next frame. + * + * If this function returns false, the aspect thread + * bails out and does not issue the renderBinJobs. + * + * This keeps us from ending up in a deadlock where the barriers are + * waiting for doRender to release them. +*/ +bool Renderer::releaseRendererAndRequestPromiseToRender() +{ + // Release the render thread, which could be waiting in waitForRenderJobs + // or is about to call tryWaitForRenderJobs + m_renderJobsReadySemaphore.release(); + // Wait for the render thread to allow us to continue + m_rendererReadySemaphore.acquire(); + // Check if the render thread has promised to call doRender + return m_willRenderPromise.testAndSetOrdered(1, 0); +} + +/*! + * \internal + * Called by render(), returns false if we are no longer running +*/ +bool Renderer::waitForRenderJobs() +{ + // Wait for the aspect thread to arrive at releaseRendererAndRequestPromiseToRender, + // which is right before it will create the renderBinJobs + m_renderJobsReadySemaphore.acquire(); + // Check if we have not shut down in the meantime and rather want to bail out + return m_running.load(); +} + +/*! + * \internal + * Called by Scene3D, returns true if we could acquire +*/ +bool Renderer::tryWaitForRenderJobs(int timeout) +{ + // See if the aspect thread has arrived at releaseRendererAndRequestPromiseToRender. + // If not, Scene3D needs to continue to avoid locking up the render thread. + return m_renderJobsReadySemaphore.tryAcquire(1, timeout) && m_running.load(); +} + +/*! + * \internal + * Called by Scene3D, in shutdown +*/ +void Renderer::abortRenderJobs() +{ + // Make sure the aspect does not start render jobs + m_willRenderPromise.store(0); + // Release the aspect so it gets out of the job creation + m_rendererReadySemaphore.release(); +} + + void Renderer::render() { // Traversing the framegraph tree from root to lead node @@ -562,168 +716,102 @@ void Renderer::render() // One scene description // One framegraph description - while (m_running.load() > 0) { - doRender(); + while (waitForRenderJobs()) { + lockSurfaceAndRender(); // TO DO: Restore windows exposed detection // Probably needs to happens some place else though } } -void Renderer::doRender(bool scene3dBlocking) +/*! + * \internal + * The doRender function is called either by Renderer::render or + * QRenderAspect::tryRenderSynchronous (Scene3D). + * Jobs that it depends on are guaranteed to have been spawned by the aspect thread. + * It will run once for each set of renderBinJobs. + * Throughout the function call, the jobs are kept in sync using barriers. + * This guarantees the state of the GL context for the jobs. + * It is therefore important that any job that needs this function to be in at a specific point + * in the executio depends on the right barrier. +*/ +void Renderer::doRender() { - Renderer::ViewSubmissionResultData submissionData; - bool hasCleanedQueueAndProceeded = false; - bool preprocessingComplete = false; - 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_renderQueue->mutex()); - bool queueIsComplete = m_renderQueue->isFrameQueueComplete(); + m_readRenderQueueSizeBarrierJob->waitForDependencies(); + const bool queueIsComplete = m_renderQueue->isFrameQueueComplete(); const bool queueIsEmpty = m_renderQueue->targetRenderViewCount() == 0; + Q_ASSERT(queueIsComplete && !queueIsEmpty); + m_readRenderQueueSizeBarrierJob->allowToProceed(); - // Scene3D Blocking Mode - if (scene3dBlocking && !queueIsComplete && !queueIsEmpty) { - int i = 0; - // We wait at most 10ms to avoid a case we could never recover from - while (!queueIsComplete && i++ < 10) { - QThread::msleep(1); - qCDebug(Backend) << Q_FUNC_INFO << "Waiting for ready queue (try:" << i << "/ 10)"; - locker.unlock(); - queueIsComplete = m_renderQueue->isFrameQueueComplete(); - locker.relock(); - } - } - - // 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(); - + const QVector<Render::RenderView *> renderViews = m_renderQueue->nextFrameQueue(); #if QT_CONFIG(qt3d_profile_jobs) - // Save start of frame - JobRunStats submissionStatsPart1; - JobRunStats submissionStatsPart2; - submissionStatsPart1.jobId.typeAndInstance[0] = JobTypes::FrameSubmissionPart1; - submissionStatsPart1.jobId.typeAndInstance[1] = 0; - submissionStatsPart1.threadId = reinterpret_cast<quint64>(QThread::currentThreadId()); - submissionStatsPart1.startTime = QThreadPooler::m_jobsStatTimer.nsecsElapsed(); - submissionStatsPart2.jobId.typeAndInstance[0] = JobTypes::FrameSubmissionPart2; - submissionStatsPart2.jobId.typeAndInstance[1] = 0; - submissionStatsPart2.threadId = reinterpret_cast<quint64>(QThread::currentThreadId()); + // Save start of frame + JobRunStats submissionStatsPart1; + JobRunStats submissionStatsPart2; + submissionStatsPart1.jobId.typeAndInstance[0] = JobTypes::FrameSubmissionPart1; + submissionStatsPart1.jobId.typeAndInstance[1] = 0; + submissionStatsPart1.threadId = reinterpret_cast<quint64>(QThread::currentThreadId()); + submissionStatsPart1.startTime = QThreadPooler::m_jobsStatTimer.nsecsElapsed(); + submissionStatsPart2.jobId.typeAndInstance[0] = JobTypes::FrameSubmissionPart2; + submissionStatsPart2.jobId.typeAndInstance[1] = 0; + submissionStatsPart2.threadId = reinterpret_cast<quint64>(QThread::currentThreadId()); #endif - if (canRender()) { - { // Scoped to destroy surfaceLock - QSurface *surface = nullptr; - for (const Render::RenderView *rv: renderViews) { - surface = rv->surface(); - if (surface) - break; - } - - SurfaceLocker surfaceLock(surface); - const bool surfaceIsValid = (surface && surfaceLock.isSurfaceValid()); - if (surfaceIsValid) { - // Reset state for each draw if we don't have complete control of the context - if (!m_ownedContext) - m_graphicsContext->setCurrentStateSet(nullptr); - 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 - prepareCommandsSubmission(renderViews); - preprocessingComplete = true; - } - } - } - // 2) Proceed to next frame and start preparing frame n + 1 - m_renderQueue->reset(); - locker.unlock(); // Done protecting RenderQueue - m_vsyncFrameAdvanceService->proceedToNextFrame(); - hasCleanedQueueAndProceeded = true; + // Reset state for each draw if we don't have complete control of the context + if (!m_ownedContext) + m_graphicsContext->setCurrentStateSet(nullptr); + // Initialize context + m_beginDrawingBarrierJob->waitForDependencies(); + m_graphicsContext->beginDrawing(); + m_beginDrawingBarrierJob->allowToProceed(); + // Execute commands for buffer uploads, texture updates, shader loading first + m_updateGLResourcesBarrierJob->waitForDependencies(); + updateGLResources(); + m_updateGLResourcesBarrierJob->allowToProceed(); + // Update VAO and copy data into commands to allow concurrent submission + m_prepareCommandSubmissionBarrierJob->waitForDependencies(); + prepareCommandsSubmission(renderViews); + m_prepareCommandSubmissionBarrierJob->allowToProceed(); + + // Proceed to next frame and start preparing frame n + 1 + m_renderQueue->reset(); + m_vsyncFrameAdvanceService->proceedToNextFrame(); #if QT_CONFIG(qt3d_profile_jobs) - if (preprocessingComplete) { - submissionStatsPart2.startTime = QThreadPooler::m_jobsStatTimer.nsecsElapsed(); - submissionStatsPart1.endTime = submissionStatsPart2.startTime; - } + submissionStatsPart2.startTime = QThreadPooler::m_jobsStatTimer.nsecsElapsed(); + submissionStatsPart1.endTime = submissionStatsPart2.startTime; #endif - // Only try to submit the RenderViews if the preprocessing was successful - // This part of the submission is happening in parallel to the RV building for the next frame - if (preprocessingComplete) { - // 3) Submit the render commands for frame n (making sure we never reference something that could be changing) - // Render using current device state and renderer configuration - submissionData = submitRenderViews(renderViews); - - // Perform any required cleanup of the Graphics resources (Buffers deleted, Shader deleted...) - cleanGraphicsResources(); - } - } + // Submit the render commands for frame n (making sure we never reference something that could be changing) + // Render using current device state and renderer configuration + Renderer::ViewSubmissionResultData submissionData = submitRenderViews(renderViews); + // Perform any required cleanup of the Graphics resources (Buffers deleted, Shader deleted...) + cleanGraphicsResources(); #if QT_CONFIG(qt3d_profile_jobs) - // Execute the pending shell commands - m_commandExecuter->performAsynchronousCommandExecution(renderViews); + // Execute the pending shell commands + m_commandExecuter->performAsynchronousCommandExecution(renderViews); #endif - // Delete all the RenderViews which will clear the allocators - // that were used for their allocation - qDeleteAll(renderViews); + // Delete all the RenderViews which will clear the allocators + // that were used for their allocation + qDeleteAll(renderViews); #if QT_CONFIG(qt3d_profile_jobs) - if (preprocessingComplete) { - // Save submission elapsed time - submissionStatsPart2.endTime = QThreadPooler::m_jobsStatTimer.nsecsElapsed(); - // Note this is safe since proceedToNextFrame is the one going to trigger - // the write to the file, and this is performed after this step - Qt3DCore::QThreadPooler::addSubmissionLogStatsEntry(submissionStatsPart1); - Qt3DCore::QThreadPooler::addSubmissionLogStatsEntry(submissionStatsPart2); - Profiling::GLTimeRecorder::writeResults(); - } + // Save submission elapsed time + submissionStatsPart2.endTime = QThreadPooler::m_jobsStatTimer.nsecsElapsed(); + // Note this is safe since proceedToNextFrame is the one going to trigger + // the write to the file, and this is performed after this step + Qt3DCore::QThreadPooler::addSubmissionLogStatsEntry(submissionStatsPart1); + Qt3DCore::QThreadPooler::addSubmissionLogStatsEntry(submissionStatsPart2); + Profiling::GLTimeRecorder::writeResults(); #endif - } - - // Only reset renderQueue and proceed to next frame if the submission - // succeeded or if we are using a render thread and that is wasn't performed - // already - - // 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 ....) - // 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 // 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 - m_graphicsContext->endDrawing(submissionData.lastBoundFBOId == m_graphicsContext->defaultFBO() && surfaceLock.isSurfaceValid()); - } + m_endDrawingBarrierJob->waitForDependencies(); + m_graphicsContext->endDrawing(submissionData.lastBoundFBOId == m_graphicsContext->defaultFBO()); + m_endDrawingBarrierJob->allowToProceed(); } // Called by RenderViewJobs @@ -738,48 +826,8 @@ void Renderer::enqueueRenderView(Render::RenderView *renderView, int submitOrder // 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 - const bool isQueueComplete = m_renderQueue->queueRenderView(renderView, submitOrder); + 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); - } -} - -bool Renderer::canRender() const -{ - // Make sure that we've not been told to terminate - if (m_renderThread && !m_running.load()) { - qCDebug(Rendering) << "RenderThread termination requested whilst waiting"; - return false; - } - - // TO DO: Check if all surfaces have been destroyed... - // It may be better if the last window to be closed trigger a call to shutdown - // Rather than having checks for the surface everywhere - - return true; -} - -bool Renderer::isReadyToSubmit() -{ - // If we are using a render thread, make sure that - // we've been told to render before rendering - if (m_renderThread) { // Prevent ouf of order execution - m_submitRenderViewsSemaphore.acquire(1); - - // Check if shutdown has been requested - if (m_running.load() == 0) - return false; - - // When using Thread rendering, the semaphore should only - // be released when the frame queue is complete and there's - // something to render - // The case of shutdown should have been handled just before - Q_ASSERT(m_renderQueue->isFrameQueueComplete()); - } - return true; } // Main thread @@ -890,6 +938,7 @@ void Renderer::prepareCommandsSubmission(const QVector<RenderView *> &renderView const QVector<Qt3DCore::QNodeId> attributeIds = rGeometry->attributes(); for (Qt3DCore::QNodeId attributeId : attributeIds) { Attribute *attribute = m_nodesManager->attributeManager()->lookupResource(attributeId); + Q_ASSERT(attribute); switch (attribute->attributeType()) { case QAttribute::IndexAttribute: indexAttribute = attribute; @@ -922,6 +971,7 @@ void Renderer::prepareCommandsSubmission(const QVector<RenderView *> &renderView if (command->m_drawIndirect) { command->m_indirectAttributeByteOffset = indirectAttribute->byteOffset(); command->m_indirectDrawBuffer = m_nodesManager->bufferManager()->lookupHandle(indirectAttribute->bufferId()); + Q_ASSERT(!command->m_indirectDrawBuffer.isNull()); } else { // Use the count specified by the GeometryRender // If not specify use the indexAttribute count if present @@ -1037,6 +1087,7 @@ 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); + Q_ASSERT(renderPass && shader); ShaderBuilder *shaderBuilder = nullptr; for (const HShaderBuilder &builderHandle : activeBuilders) { @@ -1103,6 +1154,7 @@ void Renderer::updateGLResources() const QVector<HBuffer> dirtyBufferHandles = std::move(m_dirtyBuffers); for (const HBuffer &handle: dirtyBufferHandles) { Buffer *buffer = m_nodesManager->bufferManager()->data(handle); + Q_ASSERT(buffer); // Forces creation if it doesn't exit // Also note the binding point doesn't really matter here, we just upload data if (!m_graphicsContext->hasGLBufferForBuffer(buffer)) @@ -1119,6 +1171,7 @@ void Renderer::updateGLResources() ShaderManager *shaderManager = m_nodesManager->shaderManager(); for (const HShader &handle: dirtyShaderHandles) { Shader *shader = shaderManager->data(handle); + Q_ASSERT(shader); // Compile shader m_graphicsContext->loadShader(shader, shaderManager); } @@ -1129,6 +1182,7 @@ void Renderer::updateGLResources() const QVector<HTexture> activeTextureHandles = std::move(m_dirtyTextures); for (const HTexture &handle: activeTextureHandles) { Texture *texture = m_nodesManager->textureManager()->data(handle); + Q_ASSERT(texture); // Upload/Update texture updateTexture(texture); } @@ -1236,6 +1290,7 @@ void Renderer::updateTexture(Texture *texture) // Render Thread void Renderer::cleanupTexture(const Texture *texture) { + Q_ASSERT(texture); GLTextureManager *glTextureManager = m_nodesManager->glTextureManager(); GLTexture *glTexture = glTextureManager->lookupResource(texture->peerId()); @@ -1288,7 +1343,6 @@ Renderer::ViewSubmissionResultData Renderer::submitRenderViews(const QVector<Ren // If not, we have to free up the context from the previous surface // and make the context current on the new surface surface = renderView->surface(); - SurfaceLocker surfaceLock(surface); // TO DO: Make sure that the surface we are rendering too has not been unset @@ -1296,7 +1350,7 @@ Renderer::ViewSubmissionResultData Renderer::submitRenderViews(const QVector<Ren // TODO: Investigate if it's worth providing a fallback offscreen surface // to use when surface is null. Or if we should instead expose an // offscreensurface to Qt3D. - if (!surface || !surfaceLock.isSurfaceValid()) { + if (!surface) { m_lastFrameCorrect.store(0); continue; } @@ -1313,12 +1367,14 @@ Renderer::ViewSubmissionResultData Renderer::submitRenderViews(const QVector<Ren if (surfaceHasChanged) { // If we can't make the context current on the surface, skip to the // next RenderView. We won't get the full frame but we may get something - if (!m_graphicsContext->beginDrawing(surface)) { + if (!m_graphicsContext->makeCurrent(surface)) { qWarning() << "Failed to make OpenGL context current on surface"; m_lastFrameCorrect.store(0); continue; } + m_graphicsContext->beginDrawing(); + previousSurface = surface; lastBoundFBOId = m_graphicsContext->boundFrameBufferObject(); } @@ -1457,39 +1513,35 @@ Renderer::ViewSubmissionResultData Renderer::submitRenderViews(const QVector<Ren void Renderer::markDirty(BackendNodeDirtySet changes, BackendNode *node) { Q_UNUSED(node); - m_dirtyBits.marked |= changes; + m_dirtyBits|= changes; } Renderer::BackendNodeDirtySet Renderer::dirtyBits() { - return m_dirtyBits.marked; + return m_dirtyBits; } #if defined(QT_BUILD_INTERNAL) void Renderer::clearDirtyBits(BackendNodeDirtySet changes) { - m_dirtyBits.remaining &= ~changes; - m_dirtyBits.marked &= ~changes; + m_dirtyBits &= ~changes; } #endif bool Renderer::shouldRender() { + if (!m_settings) + return false; // Only render if something changed during the last frame, or the last frame // was not rendered successfully (or render-on-demand is disabled) return (m_settings->renderPolicy() == QRenderSettings::Always - || m_dirtyBits.marked != 0 - || m_dirtyBits.remaining != 0 + || m_dirtyBits != 0 || !m_lastFrameCorrect.load()); } void Renderer::skipNextFrame() { - Q_ASSERT(m_settings->renderPolicy() != QRenderSettings::Always); - - // make submitRenderViews() actually run - m_renderQueue->setNoRender(); - m_submitRenderViewsSemaphore.release(1); + m_vsyncFrameAdvanceService->proceedToNextFrame(); } // Waits to be told to create jobs for the next frame @@ -1500,28 +1552,46 @@ QVector<Qt3DCore::QAspectJobPtr> Renderer::renderBinJobs() { QVector<QAspectJobPtr> renderBinJobs; - // Create the jobs to build the frame - const QVector<QAspectJobPtr> bufferJobs = createRenderBufferJobs(); - // Remove previous dependencies m_calculateBoundingVolumeJob->removeDependency(QWeakPointer<QAspectJob>()); m_cleanupJob->removeDependency(QWeakPointer<QAspectJob>()); - // Set dependencies - for (const QAspectJobPtr &bufferJob : bufferJobs) - m_calculateBoundingVolumeJob->addDependency(bufferJob); + // Render thread barrier jobs + renderBinJobs.append(m_readRenderQueueSizeBarrierJob); + renderBinJobs.append(m_beginDrawingBarrierJob); + renderBinJobs.append(m_updateGLResourcesBarrierJob); + renderBinJobs.append(m_prepareCommandSubmissionBarrierJob); + renderBinJobs.append(m_endDrawingBarrierJob); + // Jobs independent of dirty bits + renderBinJobs.push_back(m_updateSkinningPaletteJob); + renderBinJobs.push_back(m_updateLevelOfDetailJob); + renderBinJobs.push_back(m_cleanupJob); + renderBinJobs.push_back(m_sendRenderCaptureJob); + renderBinJobs.push_back(m_sendBufferCaptureJob); + renderBinJobs.push_back(m_vaoGathererJob); + + // Independent job properties + m_updateSkinningPaletteJob->setDirtyJoints(m_nodesManager->jointManager()->dirtyJoints()); m_updateLevelOfDetailJob->setFrameGraphRoot(frameGraphRoot()); - const BackendNodeDirtySet dirtyBitsForFrame = m_dirtyBits.marked | m_dirtyBits.remaining; - m_dirtyBits.marked = 0; - m_dirtyBits.remaining = 0; - BackendNodeDirtySet notCleared = 0; + // Buffer jobs, depends on dirty buffers + const QVector<QAspectJobPtr> bufferJobs = createRenderBufferJobs(); + for (const QAspectJobPtr &bufferJob : bufferJobs) { + m_calculateBoundingVolumeJob->addDependency(bufferJob); + m_beginDrawingBarrierJob->addDependency(bufferJob); + } + renderBinJobs.append(bufferJobs); + + // Jobs dependent on dirty bits + const BackendNodeDirtySet dirtyBitsForFrame = m_dirtyBits; + m_dirtyBits = 0; - // Add jobs const bool entitiesEnabledDirty = dirtyBitsForFrame & AbstractRenderer::EntityEnabledDirty; if (entitiesEnabledDirty) { renderBinJobs.push_back(m_updateTreeEnabledJob); + // This dependency is added here because we clear all dependencies + // at the start of this function. m_calculateBoundingVolumeJob->addDependency(m_updateTreeEnabledJob); } @@ -1541,80 +1611,55 @@ QVector<Qt3DCore::QAspectJobPtr> Renderer::renderBinJobs() renderBinJobs.push_back(m_expandBoundingVolumeJob); } - m_updateSkinningPaletteJob->setDirtyJoints(m_nodesManager->jointManager()->dirtyJoints()); - renderBinJobs.push_back(m_updateSkinningPaletteJob); - renderBinJobs.push_back(m_updateLevelOfDetailJob); - renderBinJobs.push_back(m_cleanupJob); - renderBinJobs.push_back(m_sendRenderCaptureJob); - renderBinJobs.push_back(m_sendBufferCaptureJob); - renderBinJobs.append(bufferJobs); - - // Jobs to prepare GL Resource upload - renderBinJobs.push_back(m_vaoGathererJob); - if (dirtyBitsForFrame & AbstractRenderer::BuffersDirty) renderBinJobs.push_back(m_bufferGathererJob); if (dirtyBitsForFrame & AbstractRenderer::TexturesDirty) { - renderBinJobs.push_back(m_syncTextureLoadingJob); + renderBinJobs.push_back(m_loadTextureJob); renderBinJobs.push_back(m_textureGathererJob); } + if (dirtyBitsForFrame & AbstractRenderer::SkeletonDataDirty) + renderBinJobs.push_back(m_syncSkeletonLoadingJob); + + if (dirtyBitsForFrame & AbstractRenderer::TechniquesDirty ) + renderBinJobs.push_back(m_filterCompatibleTechniqueJob); + + if (dirtyBitsForFrame & AbstractRenderer::ShadersDirty) + renderBinJobs.push_back(m_shaderGathererJob); // Layer cache is dependent on layers, layer filters and the enabled flag // on entities const bool layersDirty = dirtyBitsForFrame & AbstractRenderer::LayersDirty; const bool layersCacheNeedsToBeRebuilt = layersDirty || entitiesEnabledDirty; - const bool materialDirty = dirtyBitsForFrame & AbstractRenderer::MaterialDirty; - QMutexLocker lock(m_renderQueue->mutex()); - 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()); - - // Remove leaf nodes that no longer exist from cache - const QList<FrameGraphNode *> keys = m_cache.leafNodeCache.keys(); - for (FrameGraphNode *leafNode : keys) { - if (!fgLeaves.contains(leafNode)) - m_cache.leafNodeCache.remove(leafNode); - } - - const int fgBranchCount = fgLeaves.size(); - for (int i = 0; i < fgBranchCount; ++i) { - RenderViewBuilder builder(fgLeaves.at(i), i, this); - builder.setLayerCacheNeedsToBeRebuilt(layersCacheNeedsToBeRebuilt); - builder.setMaterialGathererCacheNeedsToBeRebuilt(materialDirty); - builder.prepareJobs(); - renderBinJobs.append(builder.buildJobHierachy()); - } - - // Set target number of RenderViews - m_renderQueue->setTargetRenderViewCount(fgBranchCount); - } else { - // FilterLayerEntityJob is part of the RenderViewBuilder jobs and must be run later - // if none of those jobs are started this frame - notCleared |= AbstractRenderer::EntityEnabledDirty; - notCleared |= AbstractRenderer::LayersDirty; + // 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()); + + // Remove leaf nodes that no longer exist from cache + const QList<FrameGraphNode *> keys = m_cache.leafNodeCache.keys(); + for (FrameGraphNode *leafNode : keys) { + if (!fgLeaves.contains(leafNode)) + m_cache.leafNodeCache.remove(leafNode); } - if (isRunning() && m_graphicsContext->isInitialized()) { - if (dirtyBitsForFrame & AbstractRenderer::TechniquesDirty ) - renderBinJobs.push_back(m_filterCompatibleTechniqueJob); - if (dirtyBitsForFrame & AbstractRenderer::ShadersDirty) - renderBinJobs.push_back(m_shaderGathererJob); - } else { - notCleared |= AbstractRenderer::TechniquesDirty; - notCleared |= AbstractRenderer::ShadersDirty; - notCleared |= AbstractRenderer::MaterialDirty; + const int fgBranchCount = fgLeaves.size(); + for (int i = 0; i < fgBranchCount; ++i) { + RenderViewBuilder builder(fgLeaves.at(i), i, this); + builder.setLayerCacheNeedsToBeRebuilt(layersCacheNeedsToBeRebuilt); + builder.setMaterialGathererCacheNeedsToBeRebuilt(materialDirty); + builder.prepareJobs(); + renderBinJobs.append(builder.buildJobHierachy()); } - m_dirtyBits.remaining = dirtyBitsForFrame & notCleared; + // Set target number of RenderViews + m_renderQueue->setTargetRenderViewCount(fgBranchCount); return renderBinJobs; } @@ -1645,9 +1690,9 @@ QAspectJobPtr Renderer::rayCastingJob() return m_rayCastingJob; } -QAspectJobPtr Renderer::syncTextureLoadingJob() +QAspectJobPtr Renderer::syncSkeletonLoadingJob() { - return m_syncTextureLoadingJob; + return m_syncSkeletonLoadingJob; } QAspectJobPtr Renderer::expandBoundingVolumeJob() @@ -1752,7 +1797,7 @@ void Renderer::performCompute(const RenderView *, RenderCommand *command) command->m_workGroups[2]); } // HACK: Reset the compute flag to dirty - m_dirtyBits.marked |= AbstractRenderer::ComputeDirty; + m_dirtyBits |= AbstractRenderer::ComputeDirty; #if defined(QT3D_RENDER_ASPECT_OPENGL_DEBUG) int err = m_graphicsContext->openGLContext()->functions()->glGetError(); @@ -1888,11 +1933,13 @@ bool Renderer::updateVAOWithAttributes(Geometry *geometry, for (QNodeId attributeId : attributeIds) { // TO DO: Improvement we could store handles and use the non locking policy on the attributeManager Attribute *attribute = m_nodesManager->attributeManager()->lookupResource(attributeId); + Q_ASSERT(attribute); if (attribute == nullptr) return false; Buffer *buffer = m_nodesManager->bufferManager()->lookupResource(attribute->bufferId()); + Q_ASSERT(buffer); // Buffer update was already performed at this point // Just make sure the attribute reference a valid buffer @@ -1945,6 +1992,7 @@ bool Renderer::requiresVAOAttributeUpdate(Geometry *geometry, for (QNodeId attributeId : attributeIds) { // TO DO: Improvement we could store handles and use the non locking policy on the attributeManager Attribute *attribute = m_nodesManager->attributeManager()->lookupResource(attributeId); + Q_ASSERT(attribute); if (attribute == nullptr) continue; @@ -1986,7 +2034,7 @@ void Renderer::cleanGraphicsResources() } } -QList<QMouseEvent> Renderer::pendingPickingEvents() const +QList<QPair<QObject *, QMouseEvent>> Renderer::pendingPickingEvents() const { return m_pickEventFilter->pendingMouseEvents(); } diff --git a/src/render/backend/renderer_p.h b/src/render/backend/renderer_p.h index 521d17e80..fbb29dc7c 100644 --- a/src/render/backend/renderer_p.h +++ b/src/render/backend/renderer_p.h @@ -144,6 +144,10 @@ class NodeManagers; class UpdateLevelOfDetailJob; typedef QSharedPointer<UpdateLevelOfDetailJob> UpdateLevelOfDetailJobPtr; +class LoadTextureDataJob; +using LoadTextureDataJobPtr = QSharedPointer<LoadTextureDataJob>; +class RenderBarrierJob; +using RenderBarrierJobPtr = QSharedPointer<RenderBarrierJob>; using SynchronizerJobPtr = GenericLambdaJobPtr<std::function<void()>>; @@ -171,7 +175,7 @@ public: void releaseGraphicsResources() override; void render() override; - void doRender(bool scene3dBlocking = false) override; + void doRender() override; void cleanGraphicsResources() override; bool isRunning() const override { return m_running.load(); } @@ -188,17 +192,29 @@ public: void clearDirtyBits(BackendNodeDirtySet changes) override; #endif + void lockSurfaceAndRender() override; + bool releaseRendererAndRequestPromiseToRender() override; + bool waitForRenderJobs() override; + bool tryWaitForRenderJobs(int timeout) override; + void abortRenderJobs() override; + bool shouldRender() override; void skipNextFrame() override; QVector<Qt3DCore::QAspectJobPtr> renderBinJobs() override; Qt3DCore::QAspectJobPtr pickBoundingVolumeJob() override; Qt3DCore::QAspectJobPtr rayCastingJob() override; - Qt3DCore::QAspectJobPtr syncTextureLoadingJob() override; + Qt3DCore::QAspectJobPtr syncSkeletonLoadingJob() override; Qt3DCore::QAspectJobPtr expandBoundingVolumeJob() override; QVector<Qt3DCore::QAspectJobPtr> createRenderBufferJobs() const; + inline RenderBarrierJobPtr readRenderQueueSizeBarrierJob() const { return m_readRenderQueueSizeBarrierJob; } + inline RenderBarrierJobPtr beginDrawingBarrierJob() const { return m_beginDrawingBarrierJob; } + inline RenderBarrierJobPtr updateGLResourcesBarrierJob() const { return m_updateGLResourcesBarrierJob; } + inline RenderBarrierJobPtr prepareCommandSubmissionBarrierJob() const { return m_prepareCommandSubmissionBarrierJob; } + inline RenderBarrierJobPtr endDrawingBarrierJob() const { return m_endDrawingBarrierJob; } + inline FrameCleanupJobPtr frameCleanupJob() const { return m_cleanupJob; } inline UpdateShaderDataTransformJobPtr updateShaderDataTransformJob() const { return m_updateShaderDataTransformJob; } inline CalculateBoundingVolumeJobPtr calculateBoundingVolumeJob() const { return m_calculateBoundingVolumeJob; } @@ -208,8 +224,9 @@ public: inline UpdateLevelOfDetailJobPtr updateLevelOfDetailJob() const { return m_updateLevelOfDetailJob; } inline UpdateMeshTriangleListJobPtr updateMeshTriangleListJob() const { return m_updateMeshTriangleListJob; } inline FilterCompatibleTechniqueJobPtr filterCompatibleTechniqueJob() const { return m_filterCompatibleTechniqueJob; } - inline SynchronizerJobPtr textureLoadSyncJob() const { return m_syncTextureLoadingJob; } + inline SynchronizerJobPtr textureLoadSyncJob() const { return m_syncSkeletonLoadingJob; } inline UpdateSkinningPaletteJobPtr updateSkinningPaletteJob() const { return m_updateSkinningPaletteJob; } + inline LoadTextureDataJobPtr loadTextureJob() const {return m_loadTextureJob; } Qt3DCore::QAbstractFrameAdvanceService *frameAdvanceService() const override; @@ -245,14 +262,13 @@ public: inline RenderStateSet *defaultRenderState() const { return m_defaultRenderStateSet; } - QList<QMouseEvent> pendingPickingEvents() const; + QList<QPair<QObject*, QMouseEvent>> pendingPickingEvents() const; QList<QKeyEvent> pendingKeyEvents() const; void addRenderCaptureSendRequest(Qt3DCore::QNodeId nodeId); const QVector<Qt3DCore::QNodeId> takePendingRenderCaptureSendRequests(); void enqueueRenderView(RenderView *renderView, int submitOrder); - bool isReadyToSubmit(); QVariant executeCommand(const QStringList &args) override; void setOffscreenSurfaceHelper(OffscreenSurfaceHelper *helper) override; @@ -279,7 +295,7 @@ public: private: #endif - bool canRender() const; + bool hasBeenAskedToTerminate() const; Qt3DCore::QServiceLocator *m_services; NodeManagers *m_nodesManager; @@ -301,7 +317,6 @@ private: QScopedPointer<RenderThread> m_renderThread; QScopedPointer<VSyncFrameAdvanceService> m_vsyncFrameAdvanceService; - QSemaphore m_submitRenderViewsSemaphore; QSemaphore m_waitForInitializationToBeCompleted; QAtomicInt m_running; @@ -312,11 +327,7 @@ private: QVector<Geometry *> m_dirtyGeometry; QAtomicInt m_exposed; - struct DirtyBits { - BackendNodeDirtySet marked = 0; // marked dirty since last job build - BackendNodeDirtySet remaining = 0; // remaining dirty after jobs have finished - }; - DirtyBits m_dirtyBits; + BackendNodeDirtySet m_dirtyBits; QAtomicInt m_lastFrameCorrect; QOpenGLContext *m_glContext; @@ -354,15 +365,18 @@ private: GenericLambdaJobPtr<std::function<void ()>> m_bufferGathererJob; GenericLambdaJobPtr<std::function<void ()>> m_vaoGathererJob; GenericLambdaJobPtr<std::function<void ()>> m_textureGathererJob; + LoadTextureDataJobPtr m_loadTextureJob; + GenericLambdaJobPtr<std::function<void ()>> m_shaderGathererJob; - SynchronizerJobPtr m_syncTextureLoadingJob; + SynchronizerJobPtr m_syncSkeletonLoadingJob; void lookForAbandonedVaos(); void lookForDirtyBuffers(); void lookForDownloadableBuffers(); void lookForDirtyTextures(); void lookForDirtyShaders(); + bool createSurfaceLockAndMakeCurrent(); QMutex m_abandonedVaosMutex; QVector<HVao> m_abandonedVaos; @@ -384,6 +398,18 @@ private: QMetaObject::Connection m_contextConnection; RendererCache m_cache; + + RenderBarrierJobPtr m_readRenderQueueSizeBarrierJob; + RenderBarrierJobPtr m_beginDrawingBarrierJob; + RenderBarrierJobPtr m_updateGLResourcesBarrierJob; + RenderBarrierJobPtr m_prepareCommandSubmissionBarrierJob; + RenderBarrierJobPtr m_endDrawingBarrierJob; + + QAtomicInt m_willRenderPromise; + QSemaphore m_rendererReadySemaphore; + QSemaphore m_renderJobsReadySemaphore; + + QVector<QSharedPointer<SurfaceLocker>> m_surfaceLockers; }; } // namespace Render diff --git a/src/render/backend/renderqueue.cpp b/src/render/backend/renderqueue.cpp index 2fa1cb7d2..db75558a4 100644 --- a/src/render/backend/renderqueue.cpp +++ b/src/render/backend/renderqueue.cpp @@ -90,7 +90,6 @@ 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(); } diff --git a/src/render/backend/renderview.cpp b/src/render/backend/renderview.cpp index 055c14143..84de728d4 100644 --- a/src/render/backend/renderview.cpp +++ b/src/render/backend/renderview.cpp @@ -148,16 +148,16 @@ static QRectF resolveViewport(const QRectF &fractionalViewport, const QSize &sur fractionalViewport.height() * surfaceSize.height()); } -static QMatrix4x4 getProjectionMatrix(const CameraLens *lens) +static Matrix4x4 getProjectionMatrix(const CameraLens *lens) { if (!lens) qWarning() << "[Qt3D Renderer] No Camera Lens found. Add a CameraSelector to your Frame Graph or make sure that no entities will be rendered."; - return lens ? lens->projection() : QMatrix4x4(); + return lens ? lens->projection() : Matrix4x4(); } UniformValue RenderView::standardUniformValue(RenderView::StandardUniform standardUniformType, Entity *entity, - const QMatrix4x4 &model) const + const Matrix4x4 &model) const { switch (standardUniformType) { case ModelMatrix: @@ -182,24 +182,26 @@ UniformValue RenderView::standardUniformValue(RenderView::StandardUniform standa case InverseModelViewMatrix: return UniformValue((m_data.m_viewMatrix * model).inverted()); case InverseViewProjectionMatrix: { - const QMatrix4x4 viewProjectionMatrix = getProjectionMatrix(m_data.m_renderCameraLens) * m_data.m_viewMatrix; + const Matrix4x4 viewProjectionMatrix = getProjectionMatrix(m_data.m_renderCameraLens) * m_data.m_viewMatrix; return UniformValue(viewProjectionMatrix.inverted()); } case InverseModelViewProjectionMatrix: - return UniformValue((m_data.m_viewProjectionMatrix * model).inverted(0)); + return UniformValue((m_data.m_viewProjectionMatrix * model).inverted()); case ModelNormalMatrix: - return UniformValue(model.normalMatrix()); + return UniformValue(convertToQMatrix4x4(model).normalMatrix()); case ModelViewNormalMatrix: - return UniformValue((m_data.m_viewMatrix * model).normalMatrix()); + return UniformValue(convertToQMatrix4x4(m_data.m_viewMatrix * model).normalMatrix()); case ViewportMatrix: { QMatrix4x4 viewportMatrix; + // TO DO: Implement on Matrix4x4 viewportMatrix.viewport(resolveViewport(m_viewport, m_surfaceSize)); - return UniformValue(viewportMatrix); + return UniformValue(Matrix4x4(viewportMatrix)); } case InverseViewportMatrix: { QMatrix4x4 viewportMatrix; + // TO DO: Implement on Matrix4x4 viewportMatrix.viewport(resolveViewport(m_viewport, m_surfaceSize)); - return UniformValue(viewportMatrix.inverted()); + return UniformValue(Matrix4x4(viewportMatrix.inverted())); } case AspectRatio: return float(m_surfaceSize.width()) / float(m_surfaceSize.height()); @@ -518,20 +520,6 @@ void RenderView::setRenderer(Renderer *renderer) m_manager = renderer->nodeManagers(); } -class LightSourceCompare -{ -public: - LightSourceCompare(Entity *node) { p = node->worldBoundingVolume()->center(); } - bool operator()(const LightSource &a, const LightSource &b) const { - const float distA = p.distanceToPoint(a.entity->worldBoundingVolume()->center()); - const float distB = p.distanceToPoint(b.entity->worldBoundingVolume()->center()); - return distA < distB; - } - -private: - QVector3D p; -}; - void RenderView::addClearBuffers(const ClearBuffers *cb) { QClearBuffers::BufferTypeFlags type = cb->type(); @@ -603,7 +591,7 @@ QVector<RenderCommand *> RenderView::buildDrawRenderCommands(const QVector<Entit // Project the camera-to-object-center vector onto the camera // view vector. This gives a depth value suitable as the key // for BackToFront sorting. - command->m_depth = QVector3D::dotProduct(entity->worldBoundingVolume()->center() - m_data.m_eyePos, m_data.m_eyeViewDir); + command->m_depth = Vector3D::dotProduct(entity->worldBoundingVolume()->center() - m_data.m_eyePos, m_data.m_eyeViewDir); command->m_geometry = geometryHandle; command->m_geometryRenderer = geometryRendererHandle; @@ -629,8 +617,15 @@ QVector<RenderCommand *> RenderView::buildDrawRenderCommands(const QVector<Entit // Replace with more sophisticated mechanisms later. // Copy vector so that we can sort it concurrently and we only want to sort the one for the current command QVector<LightSource> lightSources = m_lightSources; - if (lightSources.size() > 1) - std::sort(lightSources.begin(), lightSources.end(), LightSourceCompare(entity)); + if (lightSources.size() > 1) { + const Vector3D entityCenter = entity->worldBoundingVolume()->center(); + std::sort(lightSources.begin(), lightSources.end(), + [&] (const LightSource &a, const LightSource &b) { + const float distA = entityCenter.distanceToPoint(a.entity->worldBoundingVolume()->center()); + const float distB = entityCenter.distanceToPoint(b.entity->worldBoundingVolume()->center()); + return distA < distB; + }); + } ParameterInfoList globalParameters = passData.parameterInfo; // setShaderAndUniforms can initialize a localData @@ -653,6 +648,7 @@ QVector<RenderCommand *> RenderView::buildDrawRenderCommands(const QVector<Entit const QVector<Qt3DCore::QNodeId> attributeIds = geometry->attributes(); for (Qt3DCore::QNodeId attributeId : attributeIds) { Attribute *attribute = m_manager->attributeManager()->lookupResource(attributeId); + Q_ASSERT(attribute != nullptr); if (attribute->attributeType() == QAttribute::IndexAttribute) indexAttribute = attribute; else if (command->m_attributes.contains(attribute->nameId())) @@ -755,21 +751,21 @@ QVector<RenderCommand *> RenderView::buildComputeRenderCommands(const QVector<En void RenderView::updateMatrices() { if (m_data.m_renderCameraNode && m_data.m_renderCameraLens && m_data.m_renderCameraLens->isEnabled()) { - const QMatrix4x4 cameraWorld = *(m_data.m_renderCameraNode->worldTransform()); + const Matrix4x4 cameraWorld = *(m_data.m_renderCameraNode->worldTransform()); setViewMatrix(m_data.m_renderCameraLens->viewMatrix(cameraWorld)); setViewProjectionMatrix(m_data.m_renderCameraLens->projection() * viewMatrix()); //To get the eyePosition of the camera, we need to use the inverse of the //camera's worldTransform matrix. - const QMatrix4x4 inverseWorldTransform = viewMatrix().inverted(); - const QVector3D eyePosition(inverseWorldTransform.column(3)); + const Matrix4x4 inverseWorldTransform = viewMatrix().inverted(); + const Vector3D eyePosition(inverseWorldTransform.column(3)); setEyePosition(eyePosition); // Get the viewing direction of the camera. Use the normal matrix to // ensure non-uniform scale works too. - QMatrix3x3 normalMat = m_data.m_viewMatrix.normalMatrix(); + const QMatrix3x3 normalMat = convertToQMatrix4x4(m_data.m_viewMatrix).normalMatrix(); // dir = normalize(QVector3D(0, 0, -1) * normalMat) - setEyeViewDirection(QVector3D(-normalMat(2, 0), -normalMat(2, 1), -normalMat(2, 2)).normalized()); + setEyeViewDirection(Vector3D(-normalMat(2, 0), -normalMat(2, 1), -normalMat(2, 2)).normalized()); } } @@ -780,14 +776,19 @@ void RenderView::setUniformValue(ShaderParameterPack &uniformPack, int nameId, c // ShaderData/Buffers would be handled as UBO/SSBO and would therefore // not be in the default uniform block if (value.valueType() == UniformValue::NodeId) { - const Qt3DCore::QNodeId texId = *value.constData<Qt3DCore::QNodeId>(); - const Texture *tex = m_manager->textureManager()->lookupResource(texId); - if (tex != nullptr) { - uniformPack.setTexture(nameId, texId); - UniformValue::Texture textureValue; - textureValue.nodeId = texId; - uniformPack.setUniform(nameId, UniformValue(textureValue)); + const Qt3DCore::QNodeId *nodeIds = value.constData<Qt3DCore::QNodeId>(); + + const int uniformArraySize = value.byteSize() / sizeof(Qt3DCore::QNodeId); + for (int i = 0; i < uniformArraySize; ++i) { + const Qt3DCore::QNodeId texId = nodeIds[i]; + const Texture *tex = m_manager->textureManager()->lookupResource(texId); + if (tex != nullptr) + uniformPack.setTexture(nameId, i, texId); } + + UniformValue textureValue(uniformArraySize * sizeof(int), UniformValue::TextureValue); + std::fill(textureValue.data<int>(), textureValue.data<int>() + uniformArraySize, -1); + uniformPack.setUniform(nameId, textureValue); } else { uniformPack.setUniform(nameId, value); } @@ -797,7 +798,7 @@ void RenderView::setStandardUniformValue(ShaderParameterPack &uniformPack, int glslNameId, int nameId, Entity *entity, - const QMatrix4x4 &worldTransform) const + const Matrix4x4 &worldTransform) const { uniformPack.setUniform(glslNameId, standardUniformValue(ms_standardUniformSetters[nameId], entity, worldTransform)); } @@ -963,7 +964,7 @@ void RenderView::setShaderAndUniforms(RenderCommand *command, !shaderStorageBlockNamesIds.isEmpty() || !attributeNamesIds.isEmpty()) { // Set default standard uniforms without bindings - QMatrix4x4 worldTransform = *(entity->worldTransform()); + const Matrix4x4 worldTransform = *(entity->worldTransform()); for (const int uniformNameId : uniformNamesIds) { if (ms_standardUniformSetters.contains(uniformNameId)) setStandardUniformValue(command->m_parameterPack, uniformNameId, uniformNameId, entity, worldTransform); @@ -1010,7 +1011,7 @@ void RenderView::setShaderAndUniforms(RenderCommand *command, if (lightIdx == MAX_LIGHTS) break; Entity *lightEntity = lightSource.entity; - const QVector3D worldPos = lightEntity->worldBoundingVolume()->center(); + const Vector3D worldPos = lightEntity->worldBoundingVolume()->center(); for (Light *light : lightSource.lights) { if (!light->isEnabled()) continue; @@ -1025,13 +1026,13 @@ void RenderView::setShaderAndUniforms(RenderCommand *command, // Note: implicit conversion of values to UniformValue setUniformValue(command->m_parameterPack, LIGHT_POSITION_NAMES[lightIdx], worldPos); setUniformValue(command->m_parameterPack, LIGHT_TYPE_NAMES[lightIdx], int(QAbstractLight::PointLight)); - setUniformValue(command->m_parameterPack, LIGHT_COLOR_NAMES[lightIdx], QVector3D(1.0f, 1.0f, 1.0f)); + setUniformValue(command->m_parameterPack, LIGHT_COLOR_NAMES[lightIdx], Vector3D(1.0f, 1.0f, 1.0f)); setUniformValue(command->m_parameterPack, LIGHT_INTENSITY_NAMES[lightIdx], 0.5f); // There is no risk in doing that even if multithreaded // since we are sure that a shaderData is unique for a given light // and won't ever be referenced as a Component either - QMatrix4x4 *worldTransform = lightEntity->worldTransform(); + Matrix4x4 *worldTransform = lightEntity->worldTransform(); if (worldTransform) shaderData->updateWorldTransform(*worldTransform); @@ -1046,9 +1047,9 @@ void RenderView::setShaderAndUniforms(RenderCommand *command, // 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_POSITION_NAMES[0], Vector3D(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_COLOR_NAMES[0], Vector3D(1.0f, 1.0f, 1.0f)); setUniformValue(command->m_parameterPack, LIGHT_INTENSITY_NAMES[0], 0.5f); } diff --git a/src/render/backend/renderview_p.h b/src/render/backend/renderview_p.h index b684cbb85..cb3c74917 100644 --- a/src/render/backend/renderview_p.h +++ b/src/render/backend/renderview_p.h @@ -67,6 +67,7 @@ #include <Qt3DRender/private/qblitframebuffer_p.h> #include <Qt3DCore/private/qframeallocator_p.h> +#include <Qt3DRender/private/aligned_malloc_p.h> // TODO: Move out once this is all refactored #include <Qt3DRender/private/renderviewjobutils_p.h> @@ -98,14 +99,14 @@ typedef QPair<QString, ActivePropertyContent > ActiveProperty; struct Q_AUTOTEST_EXPORT Plane { - explicit Plane(const QVector4D &planeEquation) + explicit Plane(const Vector4D &planeEquation) : planeEquation(planeEquation) - , normal(planeEquation.toVector3D().normalized()) - , d(planeEquation.w() / planeEquation.toVector3D().length()) + , normal(Vector3D(planeEquation).normalized()) + , d(planeEquation.w() / Vector3D(planeEquation).length()) {} - const QVector4D planeEquation; - const QVector3D normal; + const Vector4D planeEquation; + const Vector3D normal; const float d; }; @@ -136,6 +137,8 @@ public: RenderView(); ~RenderView(); + QT3D_ALIGNED_MALLOC_AND_FREE() + // TODO: Add a way to specify a sort predicate for the RenderCommands void sort(); @@ -153,17 +156,17 @@ public: inline void setRenderCameraEntity(Entity *renderCameraNode) Q_DECL_NOTHROW { m_data.m_renderCameraNode = renderCameraNode; } inline Entity *renderCameraEntity() const Q_DECL_NOTHROW { return m_data.m_renderCameraNode; } - inline void setViewMatrix(const QMatrix4x4 &viewMatrix) Q_DECL_NOTHROW { m_data.m_viewMatrix = viewMatrix; } - inline QMatrix4x4 viewMatrix() const Q_DECL_NOTHROW { return m_data.m_viewMatrix; } + inline void setViewMatrix(const Matrix4x4 &viewMatrix) Q_DECL_NOTHROW { m_data.m_viewMatrix = viewMatrix; } + inline Matrix4x4 viewMatrix() const Q_DECL_NOTHROW { return m_data.m_viewMatrix; } - inline void setViewProjectionMatrix(const QMatrix4x4 &viewProjectionMatrix) Q_DECL_NOTHROW { m_data.m_viewProjectionMatrix = viewProjectionMatrix; } - inline QMatrix4x4 viewProjectionMatrix() const Q_DECL_NOTHROW { return m_data.m_viewProjectionMatrix; } + inline void setViewProjectionMatrix(const Matrix4x4 &viewProjectionMatrix) Q_DECL_NOTHROW { m_data.m_viewProjectionMatrix = viewProjectionMatrix; } + inline Matrix4x4 viewProjectionMatrix() const Q_DECL_NOTHROW { return m_data.m_viewProjectionMatrix; } - inline void setEyePosition(const QVector3D &eyePos) Q_DECL_NOTHROW { m_data.m_eyePos = eyePos; } - inline QVector3D eyePosition() const Q_DECL_NOTHROW { return m_data.m_eyePos; } + inline void setEyePosition(const Vector3D &eyePos) Q_DECL_NOTHROW { m_data.m_eyePos = eyePos; } + inline Vector3D eyePosition() const Q_DECL_NOTHROW { return m_data.m_eyePos; } - inline void setEyeViewDirection(const QVector3D &dir) Q_DECL_NOTHROW { m_data.m_eyeViewDir = dir; } - inline QVector3D eyeViewDirection() const Q_DECL_NOTHROW { return m_data.m_eyeViewDir; } + inline void setEyeViewDirection(const Vector3D &dir) Q_DECL_NOTHROW { m_data.m_eyeViewDir = dir; } + inline Vector3D eyeViewDirection() const Q_DECL_NOTHROW { return m_data.m_eyeViewDir; } inline void appendLayerFilter(const Qt3DCore::QNodeId layerFilterId) Q_DECL_NOTHROW { m_data.m_layerFilterIds.push_back(layerFilterId); } inline Qt3DCore::QNodeIdVector layerFilters() const Q_DECL_NOTHROW { return m_data.m_layerFilterIds; } @@ -260,12 +263,12 @@ public: Entity *m_renderCameraNode; const TechniqueFilter *m_techniqueFilter; const RenderPassFilter *m_passFilter; - QMatrix4x4 m_viewMatrix; - QMatrix4x4 m_viewProjectionMatrix; + Matrix4x4 m_viewMatrix; + Matrix4x4 m_viewProjectionMatrix; Qt3DCore::QNodeIdVector m_layerFilterIds; QVector<Qt3DRender::QSortPolicy::SortType> m_sortingTypes; - QVector3D m_eyePos; - QVector3D m_eyeViewDir; + Vector3D m_eyePos; + Vector3D m_eyeViewDir; Qt3DCore::QNodeIdVector m_proximityFilterIds; }; @@ -285,7 +288,6 @@ private: Entity *entity, const QVector<LightSource> &activeLightSources, EnvironmentLight *environmentLight) const; - mutable QThreadStorage<UniformBlockValueBuilder*> m_localData; Qt3DCore::QNodeId m_renderCaptureNodeId; @@ -360,14 +362,14 @@ private: UniformValue standardUniformValue(StandardUniform standardUniformType, Entity *entity, - const QMatrix4x4 &model) const; + const Matrix4x4 &model) const; void setUniformValue(ShaderParameterPack &uniformPack, int nameId, const UniformValue &value) const; void setStandardUniformValue(ShaderParameterPack &uniformPack, int glslNameId, int nameId, Entity *entity, - const QMatrix4x4 &worldTransform) const; + const Matrix4x4 &worldTransform) const; void setUniformBlockValue(ShaderParameterPack &uniformPack, Shader *shader, const ShaderUniformBlock &block, diff --git a/src/render/backend/renderviewbuilder.cpp b/src/render/backend/renderviewbuilder.cpp index 0682018fc..495b666f5 100644 --- a/src/render/backend/renderviewbuilder.cpp +++ b/src/render/backend/renderviewbuilder.cpp @@ -39,6 +39,8 @@ #include "renderviewbuilder_p.h" +#include <Qt3DRender/private/renderbarrierjob_p.h> + #include <QThread> QT_BEGIN_NAMESPACE @@ -81,9 +83,6 @@ public: // Sort the commands rv->sort(); - - // Enqueue our fully populated RenderView with the RenderThread - m_renderer->enqueueRenderView(rv, m_renderViewJob->submitOrderIndex()); } private: @@ -125,13 +124,15 @@ public: const FilterLayerEntityJobPtr &filterEntityByLayerJob, const FilterProximityDistanceJobPtr &filterProximityJob, const QVector<MaterialParameterGathererJobPtr> &materialGathererJobs, - const QVector<RenderViewBuilderJobPtr> &renderViewBuilderJobs) + const QVector<RenderViewBuilderJobPtr> &renderViewBuilderJobs, + Renderer *renderer) : m_renderViewJob(renderViewJob) , m_frustumCullingJob(frustumCullingJob) , m_filterEntityByLayerJob(filterEntityByLayerJob) , m_filterProximityJob(filterProximityJob) , m_materialGathererJobs(materialGathererJobs) , m_renderViewBuilderJobs(renderViewBuilderJobs) + , m_renderer(renderer) {} void operator()() @@ -157,6 +158,9 @@ public: // Set whether frustum culling is enabled or not m_frustumCullingJob->setActive(rv->frustumCulling()); + + // Enqueue RenderView with the renderer + m_renderer->enqueueRenderView(rv, m_renderViewJob->submitOrderIndex()); } private: @@ -166,6 +170,7 @@ private: FilterProximityDistanceJobPtr m_filterProximityJob; QVector<MaterialParameterGathererJobPtr> m_materialGathererJobs; QVector<RenderViewBuilderJobPtr> m_renderViewBuilderJobs; + Renderer *m_renderer; }; class SyncRenderCommandBuilding @@ -355,7 +360,7 @@ RenderViewBuilder::RenderViewBuilder(Render::FrameGraphNode *leafNode, int rende , m_lightGathererJob(Render::LightGathererPtr::create()) , m_renderableEntityFilterJob(RenderableEntityFilterPtr::create()) , m_computableEntityFilterJob(ComputableEntityFilterPtr::create()) - , m_frustumCullingJob(Render::FrustumCullingJobPtr::create()) + , m_frustumCullingJob(new Render::FrustumCullingJob()) , m_syncFrustumCullingJob(SynchronizerJobPtr::create(SyncFrustumCulling(m_renderViewJob, m_frustumCullingJob), JobTypes::SyncFrustumCulling)) , m_setClearDrawBufferIndexJob(SynchronizerJobPtr::create(SetClearDrawBufferIndex(m_renderViewJob), JobTypes::ClearBufferDrawIndex)) , m_syncFilterEntityByLayerJob() @@ -519,7 +524,8 @@ void RenderViewBuilder::prepareJobs() m_filterEntityByLayerJob, m_filterProximityJob, m_materialGathererJobs, - m_renderViewBuilderJobs), + m_renderViewBuilderJobs, + m_renderer), JobTypes::SyncRenderViewInitialization); } @@ -545,11 +551,13 @@ QVector<Qt3DCore::QAspectJobPtr> RenderViewBuilder::buildJobHierachy() const m_setClearDrawBufferIndexJob->addDependency(m_syncRenderViewInitializationJob); + m_renderer->readRenderQueueSizeBarrierJob()->addDependency(m_syncRenderViewInitializationJob); m_syncRenderViewInitializationJob->addDependency(m_renderViewJob); m_filterProximityJob->addDependency(m_renderer->expandBoundingVolumeJob()); m_filterProximityJob->addDependency(m_syncRenderViewInitializationJob); + m_syncRenderCommandBuildingJob->addDependency(m_renderer->updateGLResourcesBarrierJob()); m_syncRenderCommandBuildingJob->addDependency(m_syncRenderViewInitializationJob); m_syncRenderCommandBuildingJob->addDependency(m_renderableEntityFilterJob); m_syncRenderCommandBuildingJob->addDependency(m_computableEntityFilterJob); @@ -558,10 +566,15 @@ QVector<Qt3DCore::QAspectJobPtr> RenderViewBuilder::buildJobHierachy() const m_syncRenderCommandBuildingJob->addDependency(m_frustumCullingJob); for (const auto &renderViewCommandBuilder : qAsConst(m_renderViewBuilderJobs)) { + renderViewCommandBuilder->addDependency(m_renderer->updateSkinningPaletteJob()); + renderViewCommandBuilder->addDependency(m_renderer->updateWorldBoundingVolumeJob()); + renderViewCommandBuilder->addDependency(m_renderer->updateShaderDataTransformJob()); renderViewCommandBuilder->addDependency(m_syncRenderCommandBuildingJob); + m_syncRenderViewCommandBuildersJob->addDependency(renderViewCommandBuilder); } + m_renderer->prepareCommandSubmissionBarrierJob()->addDependency(m_syncRenderViewCommandBuildersJob); m_renderer->frameCleanupJob()->addDependency(m_syncRenderViewCommandBuildersJob); m_renderer->frameCleanupJob()->addDependency(m_setClearDrawBufferIndexJob); diff --git a/src/render/backend/segmentsvisitor.cpp b/src/render/backend/segmentsvisitor.cpp index 96e2b3b6c..a3a5d059c 100644 --- a/src/render/backend/segmentsvisitor.cpp +++ b/src/render/backend/segmentsvisitor.cpp @@ -86,7 +86,7 @@ void traverseSegmentsIndexed(Index *indices, const uint maxVerticesDataSize = qMin(vertexInfo.dataSize, 3U); uint ndx[2]; - QVector3D abc[2]; + Vector3D abc[2]; while (i < indexInfo.count) { for (uint u = 0; u < 2; ++u) { ndx[u] = indices[i + u]; @@ -112,7 +112,7 @@ void traverseSegments(Vertex *vertices, const uint maxVerticesDataSize = qMin(vertexInfo.dataSize, 3U); uint ndx[2]; - QVector3D abc[2]; + Vector3D abc[2]; while (i < vertexInfo.count) { for (uint u = 0; u < 2; ++u) { ndx[u] = (i + u); @@ -139,7 +139,7 @@ void traverseSegmentStripIndexed(Index *indices, const uint maxVerticesDataSize = qMin(vertexInfo.dataSize, 3U); uint ndx[2]; - QVector3D abc[2]; + Vector3D abc[2]; ndx[0] = indices[0]; uint idx = ndx[0] * verticesStride; for (uint j = 0; j < maxVerticesDataSize; ++j) @@ -180,7 +180,7 @@ void traverseSegmentStrip(Vertex *vertices, const uint maxVerticesDataSize = qMin(vertexInfo.dataSize, 3U); uint ndx[2]; - QVector3D abc[2]; + Vector3D abc[2]; ndx[0] = i; uint idx = ndx[0] * verticesStride; for (uint j = 0; j < maxVerticesDataSize; ++j) @@ -218,7 +218,7 @@ void traverseSegmentAdjacencyIndexed(Index *indices, const uint maxVerticesDataSize = qMin(vertexInfo.dataSize, 3U); uint ndx[2]; - QVector3D abc[2]; + Vector3D abc[2]; while (i < n) { for (uint u = 0; u < 2; ++u) { ndx[u] = indices[i + u]; @@ -245,7 +245,7 @@ void traverseSegmentAdjacency(Vertex *vertices, const uint maxVerticesDataSize = qMin(vertexInfo.dataSize, 3U); uint ndx[2]; - QVector3D abc[2]; + Vector3D abc[2]; while (i < n) { for (uint u = 0; u < 2; ++u) { ndx[u] = (i + u); diff --git a/src/render/backend/segmentsvisitor_p.h b/src/render/backend/segmentsvisitor_p.h index 21867b0d5..cd5df1268 100644 --- a/src/render/backend/segmentsvisitor_p.h +++ b/src/render/backend/segmentsvisitor_p.h @@ -52,6 +52,7 @@ // #include <Qt3DCore/qnodeid.h> +#include <Qt3DCore/private/vector3d_p.h> QT_BEGIN_NAMESPACE @@ -75,8 +76,8 @@ public: void apply(const Qt3DCore::QEntity *entity); void apply(const GeometryRenderer *renderer, const Qt3DCore::QNodeId id); - virtual void visit(uint andx, const QVector3D &a, - uint bndx, const QVector3D &b) = 0; + virtual void visit(uint andx, const Vector3D &a, + uint bndx, const Vector3D &b) = 0; protected: NodeManagers *m_manager; diff --git a/src/render/backend/shaderparameterpack.cpp b/src/render/backend/shaderparameterpack.cpp index 01a977aee..f78e45a5e 100644 --- a/src/render/backend/shaderparameterpack.cpp +++ b/src/render/backend/shaderparameterpack.cpp @@ -65,17 +65,17 @@ void ShaderParameterPack::setUniform(const int glslNameId, const UniformValue &v m_uniforms.insert(glslNameId, val); } -void ShaderParameterPack::setTexture(const int glslNameId, Qt3DCore::QNodeId texId) +void ShaderParameterPack::setTexture(const int glslNameId, int uniformArrayIndex, Qt3DCore::QNodeId texId) { for (int t=0; t<m_textures.size(); ++t) { - if (m_textures[t].glslNameId != glslNameId) + if (m_textures[t].glslNameId != glslNameId || m_textures[t].uniformArrayIndex != uniformArrayIndex) continue; m_textures[t].texId = texId; return; } - m_textures.append(NamedTexture(glslNameId, texId)); + m_textures.append(NamedTexture(glslNameId, texId, uniformArrayIndex)); } // Contains Uniform Block Index and QNodeId of the ShaderData (UBO) diff --git a/src/render/backend/shaderparameterpack_p.h b/src/render/backend/shaderparameterpack_p.h index c0ab05e57..abd63a187 100644 --- a/src/render/backend/shaderparameterpack_p.h +++ b/src/render/backend/shaderparameterpack_p.h @@ -96,7 +96,7 @@ public: ~ShaderParameterPack(); void setUniform(const int glslNameId, const UniformValue &val); - void setTexture(const int glslNameId, Qt3DCore::QNodeId id); + void setTexture(const int glslNameId, int uniformArrayIndex, Qt3DCore::QNodeId id); void setUniformBuffer(BlockToUBO blockToUBO); void setShaderStorageBuffer(BlockToSSBO blockToSSBO); void setSubmissionUniform(const ShaderUniform &uniform); @@ -108,13 +108,15 @@ public: struct NamedTexture { NamedTexture() {} - NamedTexture(const int nm, Qt3DCore::QNodeId t) - : glslNameId(nm) - , texId(t) + NamedTexture(const int glslNameId, Qt3DCore::QNodeId texId, int uniformArrayIndex) + : glslNameId(glslNameId) + , texId(texId) + , uniformArrayIndex(uniformArrayIndex) { } int glslNameId; Qt3DCore::QNodeId texId; + int uniformArrayIndex; }; inline QVector<NamedTexture> textures() const { return m_textures; } diff --git a/src/render/backend/transform.cpp b/src/render/backend/transform.cpp index 636c2d103..20574d6f7 100644 --- a/src/render/backend/transform.cpp +++ b/src/render/backend/transform.cpp @@ -64,7 +64,7 @@ void Transform::cleanup() m_rotation = QQuaternion(); m_scale = QVector3D(); m_translation = QVector3D(); - m_transformMatrix = QMatrix4x4(); + m_transformMatrix = Matrix4x4(); QBackendNode::setEnabled(false); } @@ -78,7 +78,7 @@ void Transform::initializeFromPeer(const Qt3DCore::QNodeCreatedChangeBasePtr &ch updateMatrix(); } -QMatrix4x4 Transform::transformMatrix() const +Matrix4x4 Transform::transformMatrix() const { return m_transformMatrix; } @@ -125,7 +125,7 @@ void Transform::updateMatrix() m.translate(m_translation); m.rotate(m_rotation); m.scale(m_scale); - m_transformMatrix = m; + m_transformMatrix = Matrix4x4(m); } } // namespace Render diff --git a/src/render/backend/transform_p.h b/src/render/backend/transform_p.h index ce026da9d..8e3e9d639 100644 --- a/src/render/backend/transform_p.h +++ b/src/render/backend/transform_p.h @@ -54,7 +54,7 @@ #include <Qt3DRender/private/backendnode_p.h> #include <QtGui/qquaternion.h> #include <QtGui/qvector3d.h> -#include <QMatrix4x4> +#include <Qt3DCore/private/matrix4x4_p.h> QT_BEGIN_NAMESPACE @@ -71,7 +71,7 @@ public: Transform(); void cleanup(); - QMatrix4x4 transformMatrix() const; + Matrix4x4 transformMatrix() const; QVector3D scale() const; QQuaternion rotation() const; QVector3D translation() const; @@ -83,7 +83,7 @@ public: private: void initializeFromPeer(const Qt3DCore::QNodeCreatedChangeBasePtr &change) final; - QMatrix4x4 m_transformMatrix; + Matrix4x4 m_transformMatrix; QQuaternion m_rotation; QVector3D m_scale; QVector3D m_translation; diff --git a/src/render/backend/triangleboundingvolume.cpp b/src/render/backend/triangleboundingvolume.cpp index ca2d26897..be5206657 100644 --- a/src/render/backend/triangleboundingvolume.cpp +++ b/src/render/backend/triangleboundingvolume.cpp @@ -49,35 +49,35 @@ namespace Render { // Note: a, b, c in clockwise order // RealTime Collision Detection page 192 bool intersectsSegmentTriangle(const RayCasting::QRay3D &ray, - const QVector3D &a, - const QVector3D &b, - const QVector3D &c, - QVector3D &uvw, + const Vector3D &a, + const Vector3D &b, + const Vector3D &c, + Vector3D &uvw, float &t) { - const QVector3D ab = b - a; - const QVector3D ac = c - a; - const QVector3D qp = (ray.origin() - ray.point(ray.distance())); + const Vector3D ab = b - a; + const Vector3D ac = c - a; + const Vector3D qp = (ray.origin() - ray.point(ray.distance())); - const QVector3D n = QVector3D::crossProduct(ab, ac); - const float d = QVector3D::dotProduct(qp, n); + const Vector3D n = Vector3D::crossProduct(ab, ac); + const float d = Vector3D::dotProduct(qp, n); if (d <= 0.0f) return false; - const QVector3D ap = ray.origin() - a; - t = QVector3D::dotProduct(ap, n); + const Vector3D ap = ray.origin() - a; + t = Vector3D::dotProduct(ap, n); if (t < 0.0f || t > d) return false; - const QVector3D e = QVector3D::crossProduct(qp, ap); - uvw.setY(QVector3D::dotProduct(ac, e)); + const Vector3D e = Vector3D::crossProduct(qp, ap); + uvw.setY(Vector3D::dotProduct(ac, e)); if (uvw.y() < 0.0f || uvw.y() > d) return false; - uvw.setZ(-QVector3D::dotProduct(ab, e)); + uvw.setZ(-Vector3D::dotProduct(ab, e)); if (uvw.z() < 0.0f || uvw.y() + uvw.z() > d) return false; @@ -99,7 +99,7 @@ TriangleBoundingVolume::TriangleBoundingVolume() /*! The vertices a, b, c are assumed to be in counter clockwise order. */ -TriangleBoundingVolume::TriangleBoundingVolume(Qt3DCore::QNodeId id, const QVector3D &a, const QVector3D &b, const QVector3D &c) +TriangleBoundingVolume::TriangleBoundingVolume(Qt3DCore::QNodeId id, const Vector3D &a, const Vector3D &b, const Vector3D &c) : QBoundingVolume() , m_id(id) , m_a(a) @@ -112,10 +112,10 @@ Qt3DCore::QNodeId TriangleBoundingVolume::id() const return m_id; } -bool TriangleBoundingVolume::intersects(const RayCasting::QRay3D &ray, QVector3D *q, QVector3D *uvw) const +bool TriangleBoundingVolume::intersects(const RayCasting::QRay3D &ray, Vector3D *q, Vector3D *uvw) const { float t = 0.0f; - QVector3D uvwr; + Vector3D uvwr; const float intersected = intersectsSegmentTriangle(ray, m_c, m_b, m_a, uvwr, t); if (intersected) { @@ -132,41 +132,41 @@ TriangleBoundingVolume::Type TriangleBoundingVolume::type() const return RayCasting::QBoundingVolume::Triangle; } -QVector3D TriangleBoundingVolume::a() const +Vector3D TriangleBoundingVolume::a() const { return m_a; } -QVector3D TriangleBoundingVolume::b() const +Vector3D TriangleBoundingVolume::b() const { return m_b; } -QVector3D TriangleBoundingVolume::c() const +Vector3D TriangleBoundingVolume::c() const { return m_c; } -void TriangleBoundingVolume::setA(const QVector3D &a) +void TriangleBoundingVolume::setA(const Vector3D &a) { m_a = a; } -void TriangleBoundingVolume::setB(const QVector3D &b) +void TriangleBoundingVolume::setB(const Vector3D &b) { m_b = b; } -void TriangleBoundingVolume::setC(const QVector3D &c) +void TriangleBoundingVolume::setC(const Vector3D &c) { m_c = c; } -TriangleBoundingVolume TriangleBoundingVolume::transformed(const QMatrix4x4 &mat) const +TriangleBoundingVolume TriangleBoundingVolume::transformed(const Matrix4x4 &mat) const { - const QVector3D tA = mat * m_a; - const QVector3D tB = mat * m_b; - const QVector3D tC = mat * m_c; + const Vector3D tA = mat * m_a; + const Vector3D tB = mat * m_b; + const Vector3D tC = mat * m_c; return TriangleBoundingVolume(id(), tA, tB, tC); } diff --git a/src/render/backend/triangleboundingvolume_p.h b/src/render/backend/triangleboundingvolume_p.h index 4735a1522..a45a5ed7c 100644 --- a/src/render/backend/triangleboundingvolume_p.h +++ b/src/render/backend/triangleboundingvolume_p.h @@ -53,6 +53,7 @@ #include <Qt3DRender/private/qboundingvolume_p.h> #include <Qt3DCore/qnodeid.h> +#include <Qt3DCore/private/matrix4x4_p.h> #include <QVector3D> QT_BEGIN_NAMESPACE @@ -62,10 +63,10 @@ namespace Qt3DRender { namespace Render { Q_AUTOTEST_EXPORT bool intersectsSegmentTriangle(const RayCasting::QRay3D &ray, - const QVector3D &a, - const QVector3D &b, - const QVector3D &c, - QVector3D &uvw, + const Vector3D &a, + const Vector3D &b, + const Vector3D &c, + Vector3D &uvw, float &t); class Q_AUTOTEST_EXPORT TriangleBoundingVolume : public RayCasting::QBoundingVolume @@ -73,25 +74,25 @@ class Q_AUTOTEST_EXPORT TriangleBoundingVolume : public RayCasting::QBoundingVol public: TriangleBoundingVolume(); explicit TriangleBoundingVolume(Qt3DCore::QNodeId id, - const QVector3D &a, - const QVector3D &b, - const QVector3D &c); + const Vector3D &a, + const Vector3D &b, + const Vector3D &c); Qt3DCore::QNodeId id() const final; - bool intersects(const RayCasting::QRay3D &ray, QVector3D *q, QVector3D *uvw) const final; - Type type() const final; + bool intersects(const RayCasting::QRay3D &ray, Vector3D *q, Vector3D *uvw) const final; + Type type() const final; - QVector3D a() const; - QVector3D b() const; - QVector3D c() const; + Vector3D a() const; + Vector3D b() const; + Vector3D c() const; - void setA(const QVector3D &a); - void setB(const QVector3D &b); - void setC(const QVector3D &c); + void setA(const Vector3D &a); + void setB(const Vector3D &b); + void setC(const Vector3D &c); - TriangleBoundingVolume transformed(const QMatrix4x4 &mat) const; + TriangleBoundingVolume transformed(const Matrix4x4 &mat) const; - inline TriangleBoundingVolume &transform(const QMatrix4x4 &mat) + inline TriangleBoundingVolume &transform(const Matrix4x4 &mat) { *this = transformed(mat); return *this; @@ -99,7 +100,7 @@ public: private: Qt3DCore::QNodeId m_id; - QVector3D m_a, m_b, m_c; + Vector3D m_a, m_b, m_c; }; } // namespace Render diff --git a/src/render/backend/trianglesextractor.cpp b/src/render/backend/trianglesextractor.cpp index e7a36dab3..2ddb425a0 100644 --- a/src/render/backend/trianglesextractor.cpp +++ b/src/render/backend/trianglesextractor.cpp @@ -68,7 +68,7 @@ QVector<RayCasting::QBoundingVolume *> TrianglesExtractor::extract(const Qt3DCor return m_volumes; } -void TrianglesExtractor::visit(uint andx, const QVector3D &a, uint bndx, const QVector3D &b, uint cndx, const QVector3D &c) +void TrianglesExtractor::visit(uint andx, const Vector3D &a, uint bndx, const Vector3D &b, uint cndx, const Vector3D &c) { Q_UNUSED(andx); Q_UNUSED(bndx); Q_UNUSED(cndx); m_volumes.push_back(new TriangleBoundingVolume(m_nodeId, a, b, c)); diff --git a/src/render/backend/trianglesextractor_p.h b/src/render/backend/trianglesextractor_p.h index 857c4c638..df4c20466 100644 --- a/src/render/backend/trianglesextractor_p.h +++ b/src/render/backend/trianglesextractor_p.h @@ -71,9 +71,9 @@ public: QVector<RayCasting::QBoundingVolume *> extract(const Qt3DCore::QNodeId id); private: - void visit(uint andx, const QVector3D &a, - uint bndx, const QVector3D &b, - uint cndx, const QVector3D &c) override; + void visit(uint andx, const Vector3D &a, + uint bndx, const Vector3D &b, + uint cndx, const Vector3D &c) override; GeometryRenderer *m_renderer; QVector<RayCasting::QBoundingVolume *> m_volumes; diff --git a/src/render/backend/trianglesvisitor.cpp b/src/render/backend/trianglesvisitor.cpp index 57538c8d7..87ba7bde9 100644 --- a/src/render/backend/trianglesvisitor.cpp +++ b/src/render/backend/trianglesvisitor.cpp @@ -85,11 +85,11 @@ void traverseTrianglesIndexed(index *indices, TrianglesVisitor* visitor) { uint i = 0; - const uint verticesStride = vertexInfo.byteStride / sizeof(vertex); const uint maxVerticesDataSize = qMin(vertexInfo.dataSize, 3U); + const uint verticesStride = vertexInfo.byteStride ? vertexInfo.byteStride / sizeof(vertex) : maxVerticesDataSize; uint ndx[3]; - QVector3D abc[3]; + Vector3D abc[3]; while (i < indexInfo.count) { for (uint u = 0; u < 3; ++u) { ndx[u] = indices[i + u]; @@ -111,11 +111,11 @@ void traverseTriangles(vertex *vertices, { uint i = 0; - const uint verticesStride = vertexInfo.byteStride / sizeof(vertex); const uint maxVerticesDataSize = qMin(vertexInfo.dataSize, 3U); + const uint verticesStride = vertexInfo.byteStride ? vertexInfo.byteStride / sizeof(vertex) : maxVerticesDataSize; uint ndx[3]; - QVector3D abc[3]; + Vector3D abc[3]; while (i < vertexInfo.count) { for (uint u = 0; u < 3; ++u) { ndx[u] = (i + u); @@ -147,11 +147,11 @@ void traverseTriangleStripIndexed(index *indices, TrianglesVisitor* visitor) { uint i = 0; - const uint verticesStride = vertexInfo.byteStride / sizeof(vertex); const uint maxVerticesDataSize = qMin(vertexInfo.dataSize, 3U); + const uint verticesStride = vertexInfo.byteStride ? vertexInfo.byteStride / sizeof(vertex) : maxVerticesDataSize; uint ndx[3]; - QVector3D abc[3]; + Vector3D abc[3]; while (i < indexInfo.count - 2) { bool degenerate = false; for (uint u = 0; u < 3; ++u) { @@ -178,11 +178,11 @@ void traverseTriangleStrip(vertex *vertices, { uint i = 0; - const uint verticesStride = vertexInfo.byteStride / sizeof(vertex); const uint maxVerticesDataSize = qMin(vertexInfo.dataSize, 3U); + const uint verticesStride = vertexInfo.byteStride ? vertexInfo.byteStride / sizeof(vertex) : maxVerticesDataSize; uint ndx[3]; - QVector3D abc[3]; + Vector3D abc[3]; while (i < vertexInfo.count - 2) { for (uint u = 0; u < 3; ++u) { ndx[u] = (i + u); @@ -204,11 +204,11 @@ void traverseTriangleFanIndexed(index *indices, const BufferInfo &vertexInfo, TrianglesVisitor* visitor) { - const uint verticesStride = vertexInfo.byteStride / sizeof(vertex); const uint maxVerticesDataSize = qMin(vertexInfo.dataSize, 3U); + const uint verticesStride = vertexInfo.byteStride ? vertexInfo.byteStride / sizeof(vertex) : maxVerticesDataSize; uint ndx[3]; - QVector3D abc[3]; + Vector3D abc[3]; for (uint j = 0; j < maxVerticesDataSize; ++j) { abc[0][j] = vertices[static_cast<int>(indices[0]) * verticesStride + j]; @@ -234,11 +234,11 @@ void traverseTriangleFan(vertex *vertices, const BufferInfo &vertexInfo, TrianglesVisitor* visitor) { - const uint verticesStride = vertexInfo.byteStride / sizeof(vertex); const uint maxVerticesDataSize = qMin(vertexInfo.dataSize, 3U); + const uint verticesStride = vertexInfo.byteStride ? vertexInfo.byteStride / sizeof(vertex) : maxVerticesDataSize; uint ndx[3]; - QVector3D abc[3]; + Vector3D abc[3]; for (uint j = 0; j < maxVerticesDataSize; ++j) { abc[0][j] = vertices[j]; @@ -268,11 +268,11 @@ void traverseTriangleAdjacencyIndexed(index *indices, TrianglesVisitor* visitor) { uint i = 0; - const uint verticesStride = vertexInfo.byteStride / sizeof(vertex); const uint maxVerticesDataSize = qMin(vertexInfo.dataSize, 3U); + const uint verticesStride = vertexInfo.byteStride ? vertexInfo.byteStride / sizeof(vertex) : maxVerticesDataSize; uint ndx[3]; - QVector3D abc[3]; + Vector3D abc[3]; while (i < indexInfo.count) { for (uint u = 0; u < 6; u += 2) { ndx[u / 2] = indices[i + u]; @@ -294,11 +294,11 @@ void traverseTriangleAdjacency(Vertex *vertices, { uint i = 0; - const uint verticesStride = vertexInfo.byteStride / sizeof(Vertex); const uint maxVerticesDataSize = qMin(vertexInfo.dataSize, 3U); + const uint verticesStride = vertexInfo.byteStride ? vertexInfo.byteStride / sizeof(Vertex) : maxVerticesDataSize; uint ndx[3]; - QVector3D abc[3]; + Vector3D abc[3]; while (i < vertexInfo.count) { for (uint u = 0; u < 6; u += 2) { ndx[u / 2] = (i + u); @@ -313,10 +313,10 @@ void traverseTriangleAdjacency(Vertex *vertices, } template<typename Coordinate> -QVector4D readCoordinate(const BufferInfo &info, Coordinate *coordinates, uint index) +Vector4D readCoordinate(const BufferInfo &info, Coordinate *coordinates, uint index) { - const uint stride = info.byteStride / sizeof(Coordinate); - QVector4D ret(0, 0, 0, 1.0f); + const uint stride = info.byteStride ? info.byteStride / sizeof(Coordinate) : info.dataSize; + Vector4D ret(0, 0, 0, 1.0f); coordinates += stride * index; for (uint e = 0; e < info.dataSize; ++e) ret[e] = coordinates[e]; @@ -340,7 +340,7 @@ typename EnumToType<v>::type *castToType(const QByteArray &u, uint byteOffset) return reinterpret_cast< typename EnumToType<v>::type *>(u.constData() + byteOffset); } -QVector4D readBuffer(const BufferInfo &info, uint index) +Vector4D readBuffer(const BufferInfo &info, uint index) { switch (info.type) { case QAttribute::Byte: @@ -362,7 +362,7 @@ QVector4D readBuffer(const BufferInfo &info, uint index) default: break; } - return QVector4D(); + return Vector4D(); } template<typename Index, typename Visitor> @@ -509,7 +509,7 @@ bool CoordinateReader::setGeometry(const GeometryRenderer *renderer, const QStri return true; } -QVector4D CoordinateReader::getCoordinate(uint vertexIndex) +Vector4D CoordinateReader::getCoordinate(uint vertexIndex) { return readBuffer(m_bufferInfo, vertexIndex); } diff --git a/src/render/backend/trianglesvisitor_p.h b/src/render/backend/trianglesvisitor_p.h index 9428857ac..ee206f1c9 100644 --- a/src/render/backend/trianglesvisitor_p.h +++ b/src/render/backend/trianglesvisitor_p.h @@ -54,6 +54,8 @@ #include <Qt3DCore/qnodeid.h> #include <Qt3DRender/QAttribute> #include <Qt3DRender/private/bufferutils_p.h> +#include <Qt3DCore/private/vector3d_p.h> +#include <Qt3DCore/private/vector4d_p.h> #include <private/qt3drender_global_p.h> @@ -81,9 +83,9 @@ public: void apply(const Qt3DCore::QEntity *entity); void apply(const GeometryRenderer *renderer, const Qt3DCore::QNodeId id); - virtual void visit(uint andx, const QVector3D &a, - uint bndx, const QVector3D &b, - uint cndx, const QVector3D &c) = 0; + virtual void visit(uint andx, const Vector3D &a, + uint bndx, const Vector3D &b, + uint cndx, const Vector3D &c) = 0; protected: NodeManagers *m_manager; @@ -102,7 +104,7 @@ public: bool setGeometry(const GeometryRenderer *renderer, const QString &attributeName); - QVector4D getCoordinate(uint vertexIndex); + Vector4D getCoordinate(uint vertexIndex); protected: NodeManagers *m_manager; diff --git a/src/render/backend/uniform.cpp b/src/render/backend/uniform.cpp index 03220a219..41ee24967 100644 --- a/src/render/backend/uniform.cpp +++ b/src/render/backend/uniform.cpp @@ -47,10 +47,22 @@ namespace Render { namespace { const int qNodeIdTypeId = qMetaTypeId<Qt3DCore::QNodeId>(); +const int qVector3DTypeId = qMetaTypeId<Vector3D>(); +const int qVector4DTypeId = qMetaTypeId<Vector4D>(); +const int qMatrix4x4TypeId = qMetaTypeId<Matrix4x4>(); // glUniform*fv/glUniform*iv/glUniform*uiv -> only handles sizeof(float)/sizeof(int) int byteSizeForMetaType(int type) { + if (type == qMatrix4x4TypeId) + return sizeof(Matrix4x4); + if (type == qVector3DTypeId) + return sizeof(Vector3D); + if (type == qVector4DTypeId) + return sizeof(Vector4D); + if (type == qNodeIdTypeId) + return sizeof(Qt3DCore::QNodeId); + switch (type) { case QMetaType::Bool: case QMetaType::Int: @@ -88,9 +100,9 @@ int byteSizeForMetaType(int type) case QMetaType::QVector4D: case QMetaType::QColor: return 4 * sizeof(float); - case QMetaType::QMatrix4x4: return 16 * sizeof(float); + default: Q_UNREACHABLE(); return -1; @@ -102,11 +114,22 @@ int byteSizeForMetaType(int type) UniformValue UniformValue::fromVariant(const QVariant &variant) { // Texture/Buffer case - if (variant.userType() == qNodeIdTypeId) + const int type = variant.userType(); + + if (type == qNodeIdTypeId) return UniformValue(variant.value<Qt3DCore::QNodeId>()); + if (type == qMatrix4x4TypeId) + return UniformValue(variant.value<Matrix4x4>()); + + if (type == qVector3DTypeId) + return UniformValue(variant.value<Vector3D>()); + + if (type == qVector4DTypeId) + return UniformValue(variant.value<Vector4D>()); + UniformValue v; - switch (variant.userType()) { + switch (type) { case QMetaType::Bool: v.data<bool>()[0] = variant.toBool(); break; @@ -209,6 +232,11 @@ UniformValue UniformValue::fromVariant(const QVariant &variant) break; const int listEntryType = variants.first().userType(); + + // array of textures + if (listEntryType == qNodeIdTypeId) + v.m_valueType = NodeId; + const int stride = byteSizeForMetaType(listEntryType) / sizeof(float); // Resize v.m_data v.m_data.resize(stride * variants.size()); diff --git a/src/render/backend/uniform_p.h b/src/render/backend/uniform_p.h index e0d8fedeb..e31aaa609 100644 --- a/src/render/backend/uniform_p.h +++ b/src/render/backend/uniform_p.h @@ -53,6 +53,9 @@ #include <qt3drender_global.h> #include <Qt3DCore/qnodeid.h> +#include <Qt3DCore/private/matrix4x4_p.h> +#include <Qt3DCore/private/vector3d_p.h> +#include <Qt3DCore/private/vector4d_p.h> #include <QMatrix4x4> #include <QVector2D> @@ -111,11 +114,6 @@ public: BufferValue }; - struct Texture { - int textureId = 0; // Set first so that glUniform1iv will work - Qt3DCore::QNodeId nodeId; - }; - // UniformValue implicitely converts doubles to floats to ensure // correct rendering behavior for the cases where Qt3D parameters created from // a double or QVariant(double) are used to fill uniform values that in reality @@ -135,8 +133,8 @@ public: UniformValue(double d) : UniformValue() { data<float>()[0] = d; } // Double to float conversion UniformValue(bool b) : UniformValue() { data<bool>()[0] = b; } UniformValue(const QVector2D &vec2) : UniformValue() { memcpy(m_data.data(), &vec2, sizeof(QVector2D)); } - UniformValue(const QVector3D &vec3) : UniformValue() { memcpy(m_data.data(), &vec3, sizeof(QVector3D)); } - UniformValue(const QVector4D &vec4) : m_data(4) { memcpy(m_data.data(), &vec4, sizeof(QVector4D)); } + UniformValue(const Vector3D &vec3) : UniformValue() { memcpy(m_data.data(), &vec3, sizeof(Vector3D)); } + UniformValue(const Vector4D &vec4) : m_data(sizeof(Vector4D) / sizeof(float)) { memcpy(m_data.data(), &vec4, sizeof(Vector4D)); } UniformValue(const QMatrix3x3 &mat33) : m_data(9) @@ -145,12 +143,22 @@ public: memcpy(m_data.data(), mat33.constData(), 9 * sizeof(float)); } + // We don t want the QMatrix4x4 builder to use sizeof since QMatrix4x4 contains a type flag +#if defined(__SSE2__) || defined(__AVX2__) + UniformValue(const Matrix4x4 &mat44) + : m_data(sizeof(Matrix4x4) / sizeof(float)) + { + // Use constData because we want column-major layout + memcpy(m_data.data(), &mat44, sizeof(Matrix4x4)); + } +#else UniformValue(const QMatrix4x4 &mat44) : m_data(16) { // Use constData because we want column-major layout memcpy(m_data.data(), mat44.constData(), 16 * sizeof(float)); } +#endif UniformValue(const QVector<QMatrix4x4> &v) : m_data(16 * v.size()) @@ -164,6 +172,13 @@ public: } } + // Reserve data to be filled in later + UniformValue(int byteSize, ValueType valueType) + : m_data(byteSize / sizeof(float)) + , m_valueType(valueType) + { + } + // For nodes which will later be replaced by a Texture or Buffer UniformValue(Qt3DCore::QNodeId id) : UniformValue() @@ -172,14 +187,6 @@ public: memcpy(m_data.data(), &id, sizeof(Qt3DCore::QNodeId)); } - // For textures - UniformValue(UniformValue::Texture t) - : UniformValue() - { - m_valueType = TextureValue; - memcpy(m_data.data(), &t, sizeof(Texture)); - } - ValueType valueType() const { return m_valueType; } UniformType storedType() const { return m_storedType; } |