summaryrefslogtreecommitdiffstats
path: root/src/plugins/renderers/opengl/renderer
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/renderers/opengl/renderer')
-rw-r--r--src/plugins/renderers/opengl/renderer/commandexecuter.cpp391
-rw-r--r--src/plugins/renderers/opengl/renderer/commandexecuter_p.h95
-rw-r--r--src/plugins/renderers/opengl/renderer/glfence_p.h73
-rw-r--r--src/plugins/renderers/opengl/renderer/glshader.cpp316
-rw-r--r--src/plugins/renderers/opengl/renderer/glshader_p.h160
-rw-r--r--src/plugins/renderers/opengl/renderer/logging.cpp65
-rw-r--r--src/plugins/renderers/opengl/renderer/logging_p.h81
-rw-r--r--src/plugins/renderers/opengl/renderer/openglvertexarrayobject.cpp165
-rw-r--r--src/plugins/renderers/opengl/renderer/openglvertexarrayobject_p.h111
-rw-r--r--src/plugins/renderers/opengl/renderer/rendercommand.cpp91
-rw-r--r--src/plugins/renderers/opengl/renderer/rendercommand_p.h182
-rw-r--r--src/plugins/renderers/opengl/renderer/renderer.cpp2414
-rw-r--r--src/plugins/renderers/opengl/renderer/renderer.pri28
-rw-r--r--src/plugins/renderers/opengl/renderer/renderer_p.h464
-rw-r--r--src/plugins/renderers/opengl/renderer/renderercache_p.h97
-rw-r--r--src/plugins/renderers/opengl/renderer/renderqueue.cpp133
-rw-r--r--src/plugins/renderers/opengl/renderer/renderqueue_p.h102
-rw-r--r--src/plugins/renderers/opengl/renderer/renderview.cpp1229
-rw-r--r--src/plugins/renderers/opengl/renderer/renderview_p.h394
-rw-r--r--src/plugins/renderers/opengl/renderer/renderviewbuilder.cpp803
-rw-r--r--src/plugins/renderers/opengl/renderer/renderviewbuilder_p.h148
-rw-r--r--src/plugins/renderers/opengl/renderer/shaderparameterpack.cpp112
-rw-r--r--src/plugins/renderers/opengl/renderer/shaderparameterpack_p.h221
-rw-r--r--src/plugins/renderers/opengl/renderer/shadervariables_p.h152
24 files changed, 8027 insertions, 0 deletions
diff --git a/src/plugins/renderers/opengl/renderer/commandexecuter.cpp b/src/plugins/renderers/opengl/renderer/commandexecuter.cpp
new file mode 100644
index 000000000..6498b44b8
--- /dev/null
+++ b/src/plugins/renderers/opengl/renderer/commandexecuter.cpp
@@ -0,0 +1,391 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Paul Lemire <paul.lemire350@gmail.com>
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "commandexecuter_p.h"
+
+#include <Qt3DCore/private/qabstractaspect_p.h>
+#include <Qt3DCore/qbackendnode.h>
+#include <Qt3DRender/private/nodemanagers_p.h>
+#include <Qt3DRender/private/geometryrenderermanager_p.h>
+#include <Qt3DRender/private/stringtoint_p.h>
+#include <QJsonObject>
+#include <QJsonDocument>
+#include <QJsonArray>
+#include <graphicscontext_p.h>
+#include <renderview_p.h>
+#include <rendercommand_p.h>
+#include <renderer_p.h>
+#include <submissioncontext_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+namespace Debug {
+
+namespace {
+
+template<typename Type>
+QJsonObject typeToJsonObj(const Type &)
+{
+ return QJsonObject();
+}
+
+template<typename Type>
+QJsonValue typeToJsonValue(const Type &t)
+{
+ Q_UNUSED(t);
+ return QJsonValue();
+}
+
+template<>
+QJsonObject typeToJsonObj<QRectF>(const QRectF &rect)
+{
+ QJsonObject obj;
+
+ obj.insert(QLatin1String("x"), rect.x());
+ obj.insert(QLatin1String("y"), rect.y());
+ obj.insert(QLatin1String("width"), rect.width());
+ obj.insert(QLatin1String("height"), rect.height());
+
+ return obj;
+}
+
+template<>
+QJsonValue typeToJsonValue<QRectF>(const QRectF &rect)
+{
+ QJsonArray value;
+
+ value.push_back(rect.x());
+ value.push_back(rect.y());
+ value.push_back(rect.width());
+ value.push_back(rect.height());
+
+ return value;
+}
+
+template<>
+QJsonObject typeToJsonObj<QSize>(const QSize &s)
+{
+ QJsonObject obj;
+
+ obj.insert(QLatin1String("width"), s.width());
+ obj.insert(QLatin1String("height"), s.height());
+
+ return obj;
+}
+
+template<>
+QJsonValue typeToJsonValue<QSize>(const QSize &s)
+{
+ QJsonArray value;
+
+ value.push_back(s.width());
+ value.push_back(s.height());
+
+ return value;
+}
+
+template<>
+QJsonObject typeToJsonObj<QVector3D>(const QVector3D &v)
+{
+ QJsonObject obj;
+
+ obj.insert(QLatin1String("x"), v.x());
+ obj.insert(QLatin1String("y"), v.y());
+ obj.insert(QLatin1String("z"), v.z());
+
+ return obj;
+}
+
+template<>
+QJsonValue typeToJsonValue<QVector3D>(const QVector3D &v)
+{
+ QJsonArray value;
+
+ value.push_back(v.x());
+ value.push_back(v.y());
+ value.push_back(v.z());
+
+ return value;
+}
+
+template<>
+QJsonObject typeToJsonObj<Qt3DCore::QNodeId>(const Qt3DCore::QNodeId &v)
+{
+ QJsonObject obj;
+ obj.insert(QLatin1String("id"), qint64(v.id()));
+ return obj;
+}
+
+template<>
+QJsonValue typeToJsonValue<Qt3DCore::QNodeId>(const Qt3DCore::QNodeId &v)
+{
+ QJsonValue value(qint64(v.id()));
+ return value;
+}
+
+template<>
+QJsonObject typeToJsonObj<QVector4D>(const QVector4D &v)
+{
+ QJsonObject obj;
+
+ obj.insert(QLatin1String("x"), v.x());
+ obj.insert(QLatin1String("y"), v.y());
+ obj.insert(QLatin1String("z"), v.z());
+ obj.insert(QLatin1String("w"), v.w());
+
+ return obj;
+}
+
+template<>
+QJsonValue typeToJsonValue<QVector4D>(const QVector4D &v)
+{
+ QJsonArray value;
+
+ value.push_back(v.x());
+ value.push_back(v.y());
+ value.push_back(v.z());
+ value.push_back(v.w());
+
+ return value;
+}
+
+template<>
+QJsonObject typeToJsonObj<QMatrix4x4>(const QMatrix4x4 &v)
+{
+ QJsonObject obj;
+
+ obj.insert(QLatin1String("row 0"), typeToJsonObj(v.row(0)));
+ obj.insert(QLatin1String("row 1"), typeToJsonObj(v.row(0)));
+ obj.insert(QLatin1String("row 2"), typeToJsonObj(v.row(0)));
+ obj.insert(QLatin1String("row 3"), typeToJsonObj(v.row(0)));
+
+ return obj;
+}
+
+template<>
+QJsonValue typeToJsonValue<QMatrix4x4>(const QMatrix4x4 &v)
+{
+ QJsonArray value;
+
+ value.push_back(typeToJsonValue(v.row(0)));
+ value.push_back(typeToJsonValue(v.row(1)));
+ value.push_back(typeToJsonValue(v.row(2)));
+ value.push_back(typeToJsonValue(v.row(3)));
+
+ return value;
+}
+
+template<>
+QJsonValue typeToJsonValue<QVariant>(const QVariant &v)
+{
+ const int nodeTypeId = qMetaTypeId<Qt3DCore::QNodeId>();
+
+ if (v.userType() == nodeTypeId)
+ return typeToJsonValue(v.value<Qt3DCore::QNodeId>());
+
+ switch (v.userType()) {
+ case QMetaType::QVector3D:
+ return typeToJsonValue(v.value<QVector3D>());
+ case QMetaType::QVector4D:
+ return typeToJsonValue(v.value<QVector4D>());
+ case QMetaType::QMatrix4x4:
+ return typeToJsonValue(v.value<QMatrix4x4>());
+ default:
+ return QJsonValue::fromVariant(v);
+ }
+}
+
+template<typename Handle, typename Manager>
+QJsonObject backendNodeToJSon(Handle handle, Manager *manager)
+{
+ Qt3DCore::QBackendNode *node = manager->data(handle);
+ QJsonObject obj;
+ Qt3DCore::QNodeId id;
+ if (node != nullptr)
+ id = node->peerId();
+ obj.insert(QLatin1String("id"), qint64(id.id()));
+ return obj;
+}
+
+QJsonObject parameterPackToJson(const Render::ShaderParameterPack &pack)
+{
+ QJsonObject obj;
+
+ const Render::PackUniformHash &uniforms = pack.uniforms();
+ QJsonArray uniformsArray;
+ for (int i = 0, m = uniforms.keys.size(); i < m; ++i) {
+ QJsonObject uniformObj;
+ uniformObj.insert(QLatin1String("name"), Render::StringToInt::lookupString(uniforms.keys.at(i)));
+ const Render::UniformValue::ValueType type = uniforms.values.at(i).valueType();
+ uniformObj.insert(QLatin1String("type"),
+ type == Render::UniformValue::ScalarValue
+ ? QLatin1String("value")
+ : QLatin1String("texture"));
+ uniformsArray.push_back(uniformObj);
+ }
+ obj.insert(QLatin1String("uniforms"), uniformsArray);
+
+ QJsonArray texturesArray;
+ const QVector<Render::ShaderParameterPack::NamedResource> &textures = pack.textures();
+ for (const auto & texture : textures) {
+ QJsonObject textureObj;
+ textureObj.insert(QLatin1String("name"), Render::StringToInt::lookupString(texture.glslNameId));
+ textureObj.insert(QLatin1String("id"), qint64(texture.nodeId.id()));
+ texturesArray.push_back(textureObj);
+ }
+ obj.insert(QLatin1String("textures"), texturesArray);
+
+ const QVector<Render::BlockToUBO> &ubos = pack.uniformBuffers();
+ QJsonArray ubosArray;
+ for (const auto &ubo : ubos) {
+ QJsonObject uboObj;
+ uboObj.insert(QLatin1String("index"), ubo.m_blockIndex);
+ uboObj.insert(QLatin1String("bufferId"), qint64(ubo.m_bufferID.id()));
+ ubosArray.push_back(uboObj);
+
+ }
+ obj.insert(QLatin1String("ubos"), ubosArray);
+
+ const QVector<Render::BlockToSSBO> &ssbos = pack.shaderStorageBuffers();
+ QJsonArray ssbosArray;
+ for (const auto &ssbo : ssbos) {
+ QJsonObject ssboObj;
+ ssboObj.insert(QLatin1String("index"), ssbo.m_blockIndex);
+ ssboObj.insert(QLatin1String("bufferId"), qint64(ssbo.m_bufferID.id()));
+ ssbosArray.push_back(ssboObj);
+ }
+ obj.insert(QLatin1String("ssbos"), ssbosArray);
+
+ return obj;
+}
+
+} // anonymous
+
+CommandExecuter::CommandExecuter(Render::Renderer *renderer)
+ : m_renderer(renderer)
+{
+}
+
+// Render thread
+void CommandExecuter::performAsynchronousCommandExecution(const QVector<Render::RenderView *> &views)
+{
+ QMutexLocker lock(&m_pendingCommandsMutex);
+ const QVector<Qt3DCore::Debug::AsynchronousCommandReply *> shellCommands = std::move(m_pendingCommands);
+ lock.unlock();
+
+ for (auto *reply : shellCommands) {
+ if (reply->commandName() == QLatin1String("glinfo")) {
+ QJsonObject replyObj;
+ const GraphicsApiFilterData *contextInfo = m_renderer->submissionContext()->contextInfo();
+ if (contextInfo != nullptr) {
+ replyObj.insert(QLatin1String("api"),
+ contextInfo->m_api == QGraphicsApiFilter::OpenGL
+ ? QLatin1String("OpenGL")
+ : QLatin1String("OpenGLES"));
+ const QString versionString =
+ QString::number(contextInfo->m_major)
+ + QStringLiteral(".")
+ + QString::number(contextInfo->m_minor);
+ replyObj.insert(QLatin1String("version"), versionString);
+ replyObj.insert(QLatin1String("profile"),
+ contextInfo->m_profile == QGraphicsApiFilter::CoreProfile
+ ? QLatin1String("Core")
+ : contextInfo->m_profile == QGraphicsApiFilter::CompatibilityProfile
+ ? QLatin1String("Compatibility")
+ : QLatin1String("None"));
+ }
+ reply->setData(QJsonDocument(replyObj).toJson());
+ } else if (reply->commandName() == QLatin1String("rendercommands")) {
+ QJsonObject replyObj;
+
+ QJsonArray viewArray;
+ for (Render::RenderView *v : views) {
+ QJsonObject viewObj;
+ viewObj.insert(QLatin1String("viewport"), typeToJsonValue(v->viewport()));
+ viewObj.insert(QLatin1String("surfaceSize"), typeToJsonValue(v->surfaceSize()));
+ viewObj.insert(QLatin1String("devicePixelRatio"), v->devicePixelRatio());
+ viewObj.insert(QLatin1String("noDraw"), v->noDraw());
+ viewObj.insert(QLatin1String("frustumCulling"), v->frustumCulling());
+ viewObj.insert(QLatin1String("compute"), v->isCompute());
+ viewObj.insert(QLatin1String("clearDepthValue"), v->clearDepthValue());
+ viewObj.insert(QLatin1String("clearStencilValue"), v->clearStencilValue());
+
+ QJsonArray renderCommandsArray;
+ for (const Render::RenderCommand &c : v->commands()) {
+ QJsonObject commandObj;
+ Render::NodeManagers *nodeManagers = m_renderer->nodeManagers();
+ commandObj.insert(QLatin1String("shader"), typeToJsonValue(QVariant::fromValue(c.m_shaderId)));
+ commandObj.insert(QLatin1String("vao"), double(c.m_vao.handle()));
+ commandObj.insert(QLatin1String("instanceCount"), c.m_instanceCount);
+ commandObj.insert(QLatin1String("geometry"), backendNodeToJSon(c.m_geometry, nodeManagers->geometryManager()));
+ commandObj.insert(QLatin1String("geometryRenderer"), backendNodeToJSon(c.m_geometryRenderer, nodeManagers->geometryRendererManager()));
+ commandObj.insert(QLatin1String("shaderParameterPack"), parameterPackToJson(c.m_parameterPack));
+
+ renderCommandsArray.push_back(commandObj);
+ }
+ viewObj.insert(QLatin1String("commands"), renderCommandsArray);
+ viewArray.push_back(viewObj);
+ }
+
+ replyObj.insert(QLatin1String("renderViews"), viewArray);
+ reply->setData(QJsonDocument(replyObj).toJson());
+ }
+ reply->setFinished(true);
+ }
+}
+
+// Main thread
+QVariant CommandExecuter::executeCommand(const QStringList &args)
+{
+ // Note: The replies will be deleted by the AspectCommandDebugger
+ if (args.length() > 0 &&
+ (args.first() == QLatin1String("glinfo") ||
+ args.first() == QLatin1String("rendercommands"))) {
+ auto reply = new Qt3DCore::Debug::AsynchronousCommandReply(args.first());
+ QMutexLocker lock(&m_pendingCommandsMutex);
+ m_pendingCommands.push_back(reply);
+ return QVariant::fromValue(reply);
+ }
+ return QVariant();
+}
+
+} // Debug
+
+} // Qt3DRenderer
+
+QT_END_NAMESPACE
diff --git a/src/plugins/renderers/opengl/renderer/commandexecuter_p.h b/src/plugins/renderers/opengl/renderer/commandexecuter_p.h
new file mode 100644
index 000000000..2d90bf4d6
--- /dev/null
+++ b/src/plugins/renderers/opengl/renderer/commandexecuter_p.h
@@ -0,0 +1,95 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Paul Lemire <paul.lemire350@gmail.com>
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DRENDER_DEBUG_COMMANDEXECUTER_H
+#define QT3DRENDER_DEBUG_COMMANDEXECUTER_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 <QVector>
+#include <QVariant>
+#include <QMutex>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DCore {
+
+namespace Debug {
+class AsynchronousCommandReply;
+} // Debug
+
+} // Qt3DCore
+
+namespace Qt3DRender {
+
+namespace Render {
+class Renderer;
+class RenderView;
+} // Render
+
+namespace Debug {
+
+class CommandExecuter
+{
+public:
+ explicit CommandExecuter(Render::Renderer *renderer);
+
+ void performAsynchronousCommandExecution(const QVector<Render::RenderView *> &views);
+
+ QVariant executeCommand(const QStringList &args);
+
+private:
+ Render::Renderer *m_renderer;
+ QVector<Qt3DCore::Debug::AsynchronousCommandReply *> m_pendingCommands;
+ QMutex m_pendingCommandsMutex;
+};
+
+} // Debug
+
+} // Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_DEBUG_COMMANDEXECUTER_H
diff --git a/src/plugins/renderers/opengl/renderer/glfence_p.h b/src/plugins/renderers/opengl/renderer/glfence_p.h
new file mode 100644
index 000000000..366065048
--- /dev/null
+++ b/src/plugins/renderers/opengl/renderer/glfence_p.h
@@ -0,0 +1,73 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef GLFENCE_P_H
+#define GLFENCE_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 <QtGlobal>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+namespace Render {
+
+// GLsync is a pointer to a struct (unlike the rest of GL which used int ids)
+// We cannot reference GLsync as it's only available since 3.2 We use FenceId
+// to wrap that around and trust the GLHelpers will convert them accordingly.
+using GLFence = void *;
+
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+
+#endif // GLFENCE_P_H
diff --git a/src/plugins/renderers/opengl/renderer/glshader.cpp b/src/plugins/renderers/opengl/renderer/glshader.cpp
new file mode 100644
index 000000000..02fa2de6d
--- /dev/null
+++ b/src/plugins/renderers/opengl/renderer/glshader.cpp
@@ -0,0 +1,316 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "glshader_p.h"
+#include <QMutexLocker>
+#include <Qt3DRender/private/stringtoint_p.h>
+#include <graphicscontext_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+namespace Render {
+
+GLShader::GLShader()
+ : m_isLoaded(false)
+ , m_graphicsContext(nullptr)
+{
+ m_shaderCode.resize(static_cast<int>(QShaderProgram::Compute) + 1);
+}
+
+void GLShader::setGraphicsContext(GraphicsContext *context)
+{
+ QMutexLocker lock(&m_mutex);
+ m_graphicsContext = context;
+ if (m_graphicsContext) {
+ m_contextConnection = QObject::connect(m_graphicsContext->openGLContext(),
+ &QOpenGLContext::aboutToBeDestroyed,
+ [this] { setGraphicsContext(nullptr); });
+ }
+}
+
+GraphicsContext *GLShader::graphicsContext()
+{
+ QMutexLocker lock(&m_mutex);
+ return m_graphicsContext;
+}
+
+
+QVector<QString> GLShader::uniformsNames() const
+{
+ return m_uniformsNames;
+}
+
+QVector<QString> GLShader::attributesNames() const
+{
+ return m_attributesNames;
+}
+
+QVector<QString> GLShader::uniformBlockNames() const
+{
+ return m_uniformBlockNames;
+}
+
+QVector<QString> GLShader::storageBlockNames() const
+{
+ return m_shaderStorageBlockNames;
+}
+
+QVector<QByteArray> GLShader::shaderCode() const
+{
+ return m_shaderCode;
+}
+
+QHash<QString, ShaderUniform> GLShader::activeUniformsForUniformBlock(int blockIndex) const
+{
+ return m_uniformBlockIndexToShaderUniforms.value(blockIndex);
+}
+
+ShaderUniformBlock GLShader::uniformBlockForBlockIndex(int blockIndex)
+{
+ for (int i = 0, m = m_uniformBlocks.size(); i < m; ++i) {
+ if (m_uniformBlocks[i].m_index == blockIndex) {
+ return m_uniformBlocks[i];
+ }
+ }
+ return ShaderUniformBlock();
+}
+
+ShaderUniformBlock GLShader::uniformBlockForBlockNameId(int blockNameId)
+{
+ for (int i = 0, m = m_uniformBlocks.size(); i < m; ++i) {
+ if (m_uniformBlocks[i].m_nameId == blockNameId) {
+ return m_uniformBlocks[i];
+ }
+ }
+ return ShaderUniformBlock();
+}
+
+ShaderUniformBlock GLShader::uniformBlockForBlockName(const QString &blockName)
+{
+ for (int i = 0, m = m_uniformBlocks.size(); i < m; ++i) {
+ if (m_uniformBlocks[i].m_name == blockName) {
+ return m_uniformBlocks[i];
+ }
+ }
+ return ShaderUniformBlock();
+}
+
+ShaderStorageBlock GLShader::storageBlockForBlockIndex(int blockIndex)
+{
+ for (int i = 0, m = m_shaderStorageBlockNames.size(); i < m; ++i) {
+ if (m_shaderStorageBlocks[i].m_index == blockIndex)
+ return m_shaderStorageBlocks[i];
+ }
+ return ShaderStorageBlock();
+}
+
+ShaderStorageBlock GLShader::storageBlockForBlockNameId(int blockNameId)
+{
+ for (int i = 0, m = m_shaderStorageBlockNames.size(); i < m; ++i) {
+ if (m_shaderStorageBlocks[i].m_nameId == blockNameId)
+ return m_shaderStorageBlocks[i];
+ }
+ return ShaderStorageBlock();
+}
+
+ShaderStorageBlock GLShader::storageBlockForBlockName(const QString &blockName)
+{
+ for (int i = 0, m = m_shaderStorageBlockNames.size(); i < m; ++i) {
+ if (m_shaderStorageBlocks[i].m_name == blockName)
+ return m_shaderStorageBlocks[i];
+ }
+ return ShaderStorageBlock();
+}
+
+void GLShader::prepareUniforms(ShaderParameterPack &pack)
+{
+ const PackUniformHash &values = pack.uniforms();
+
+ auto it = values.keys.cbegin();
+ const auto end = values.keys.cend();
+
+ while (it != end) {
+ // Find if there's a uniform with the same name id
+ for (const ShaderUniform &uniform : qAsConst(m_uniforms)) {
+ if (uniform.m_nameId == *it) {
+ pack.setSubmissionUniform(uniform);
+ break;
+ }
+ }
+ ++it;
+ }
+}
+
+void GLShader::setFragOutputs(const QHash<QString, int> &fragOutputs)
+{
+ {
+ QMutexLocker lock(&m_mutex);
+ m_fragOutputs = fragOutputs;
+ }
+// updateDNA();
+}
+
+const QHash<QString, int> GLShader::fragOutputs() const
+{
+ QMutexLocker lock(&m_mutex);
+ return m_fragOutputs;
+}
+
+void GLShader::initializeUniforms(const QVector<ShaderUniform> &uniformsDescription)
+{
+ m_uniforms = uniformsDescription;
+ m_uniformsNames.resize(uniformsDescription.size());
+ m_uniformsNamesIds.reserve(uniformsDescription.size());
+ m_standardUniformNamesIds.reserve(5);
+ QHash<QString, ShaderUniform> activeUniformsInDefaultBlock;
+
+ static const QVector<int> standardUniformNameIds = {
+ Shader::modelMatrixNameId,
+ Shader::viewMatrixNameId,
+ Shader::projectionMatrixNameId,
+ Shader::modelViewMatrixNameId,
+ Shader::viewProjectionMatrixNameId,
+ Shader::modelViewProjectionNameId,
+ Shader::mvpNameId,
+ Shader::inverseModelMatrixNameId,
+ Shader::inverseViewMatrixNameId,
+ Shader::inverseProjectionMatrixNameId,
+ Shader::inverseModelViewNameId,
+ Shader::inverseViewProjectionMatrixNameId,
+ Shader::inverseModelViewProjectionNameId,
+ Shader::modelNormalMatrixNameId,
+ Shader::modelViewNormalNameId,
+ Shader::viewportMatrixNameId,
+ Shader::inverseViewportMatrixNameId,
+ Shader::aspectRatioNameId,
+ Shader::exposureNameId,
+ Shader::gammaNameId,
+ Shader::timeNameId,
+ Shader::eyePositionNameId,
+ Shader::skinningPaletteNameId,
+ };
+
+ for (int i = 0, m = uniformsDescription.size(); i < m; i++) {
+ m_uniformsNames[i] = m_uniforms[i].m_name;
+ const int nameId = StringToInt::lookupId(m_uniformsNames[i]);
+ m_uniforms[i].m_nameId = nameId;
+
+ // Is the uniform a Qt3D "Standard" uniform or a user defined one?
+ if (standardUniformNameIds.contains(nameId))
+ m_standardUniformNamesIds.push_back(nameId);
+ else
+ m_uniformsNamesIds.push_back(nameId);
+
+ if (uniformsDescription[i].m_blockIndex == -1) { // Uniform is in default block
+ qCDebug(Shaders) << "Active Uniform in Default Block " << uniformsDescription[i].m_name << uniformsDescription[i].m_blockIndex;
+ activeUniformsInDefaultBlock.insert(uniformsDescription[i].m_name, uniformsDescription[i]);
+ }
+ }
+ m_uniformBlockIndexToShaderUniforms.insert(-1, activeUniformsInDefaultBlock);
+}
+
+void GLShader::initializeAttributes(const QVector<ShaderAttribute> &attributesDescription)
+{
+ m_attributes = attributesDescription;
+ m_attributesNames.resize(attributesDescription.size());
+ m_attributeNamesIds.resize(attributesDescription.size());
+ for (int i = 0, m = attributesDescription.size(); i < m; i++) {
+ m_attributesNames[i] = attributesDescription[i].m_name;
+ m_attributes[i].m_nameId = StringToInt::lookupId(m_attributesNames[i]);
+ m_attributeNamesIds[i] = m_attributes[i].m_nameId;
+ qCDebug(Shaders) << "Active Attribute " << attributesDescription[i].m_name;
+ }
+}
+
+void GLShader::initializeUniformBlocks(const QVector<ShaderUniformBlock> &uniformBlockDescription)
+{
+ m_uniformBlocks = uniformBlockDescription;
+ m_uniformBlockNames.resize(uniformBlockDescription.size());
+ m_uniformBlockNamesIds.resize(uniformBlockDescription.size());
+ for (int i = 0, m = uniformBlockDescription.size(); i < m; ++i) {
+ m_uniformBlockNames[i] = m_uniformBlocks[i].m_name;
+ m_uniformBlockNamesIds[i] = StringToInt::lookupId(m_uniformBlockNames[i]);
+ m_uniformBlocks[i].m_nameId = m_uniformBlockNamesIds[i];
+ qCDebug(Shaders) << "Initializing Uniform Block {" << m_uniformBlockNames[i] << "}";
+
+ // Find all active uniforms for the shader block
+ QVector<ShaderUniform>::const_iterator uniformsIt = m_uniforms.cbegin();
+ const QVector<ShaderUniform>::const_iterator uniformsEnd = m_uniforms.cend();
+
+ QVector<QString>::const_iterator uniformNamesIt = m_uniformsNames.cbegin();
+ const QVector<QString>::const_iterator uniformNamesEnd = m_attributesNames.cend();
+
+ QHash<QString, ShaderUniform> activeUniformsInBlock;
+
+ while (uniformsIt != uniformsEnd && uniformNamesIt != uniformNamesEnd) {
+ if (uniformsIt->m_blockIndex == uniformBlockDescription[i].m_index) {
+ QString uniformName = *uniformNamesIt;
+ if (!m_uniformBlockNames[i].isEmpty() && !uniformName.startsWith(m_uniformBlockNames[i]))
+ uniformName = m_uniformBlockNames[i] + QLatin1Char('.') + *uniformNamesIt;
+ activeUniformsInBlock.insert(uniformName, *uniformsIt);
+ qCDebug(Shaders) << "Active Uniform Block " << uniformName << " in block " << m_uniformBlockNames[i] << " at index " << uniformsIt->m_blockIndex;
+ }
+ ++uniformsIt;
+ ++uniformNamesIt;
+ }
+ m_uniformBlockIndexToShaderUniforms.insert(uniformBlockDescription[i].m_index, activeUniformsInBlock);
+ }
+}
+
+void GLShader::initializeShaderStorageBlocks(const QVector<ShaderStorageBlock> &shaderStorageBlockDescription)
+{
+ m_shaderStorageBlocks = shaderStorageBlockDescription;
+ m_shaderStorageBlockNames.resize(shaderStorageBlockDescription.size());
+ m_shaderStorageBlockNamesIds.resize(shaderStorageBlockDescription.size());
+
+ for (int i = 0, m = shaderStorageBlockDescription.size(); i < m; ++i) {
+ m_shaderStorageBlockNames[i] = m_shaderStorageBlocks[i].m_name;
+ m_shaderStorageBlockNamesIds[i] = StringToInt::lookupId(m_shaderStorageBlockNames[i]);
+ m_shaderStorageBlocks[i].m_nameId =m_shaderStorageBlockNamesIds[i];
+ qCDebug(Shaders) << "Initializing Shader Storage Block {" << m_shaderStorageBlockNames[i] << "}";
+ }
+}
+
+} // Render
+
+} // Qt3DRender
+
+QT_END_NAMESPACE
diff --git a/src/plugins/renderers/opengl/renderer/glshader_p.h b/src/plugins/renderers/opengl/renderer/glshader_p.h
new file mode 100644
index 000000000..18293ac54
--- /dev/null
+++ b/src/plugins/renderers/opengl/renderer/glshader_p.h
@@ -0,0 +1,160 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DRENDER_RENDER_GLSHADER_P_H
+#define QT3DRENDER_RENDER_GLSHADER_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 <shadervariables_p.h>
+#include <shaderparameterpack_p.h>
+#include <Qt3DRender/qshaderprogram.h>
+#include <QMutex>
+
+
+QT_BEGIN_NAMESPACE
+
+class QOpenGLShaderProgram;
+
+namespace Qt3DRender {
+
+namespace Render {
+
+class Q_AUTOTEST_EXPORT GLShader
+{
+public:
+ GLShader();
+
+ void setGraphicsContext(GraphicsContext *context);
+ GraphicsContext *graphicsContext();
+
+ bool isLoaded() const { return m_isLoaded; }
+ void setLoaded(bool loaded) { m_isLoaded = loaded; }
+
+ void prepareUniforms(ShaderParameterPack &pack);
+
+ void setFragOutputs(const QHash<QString, int> &fragOutputs);
+ const QHash<QString, int> fragOutputs() const;
+
+ inline QVector<int> uniformsNamesIds() const { return m_uniformsNamesIds; }
+ inline QVector<int> standardUniformNameIds() const { return m_standardUniformNamesIds; }
+ inline QVector<int> uniformBlockNamesIds() const { return m_uniformBlockNamesIds; }
+ inline QVector<int> storageBlockNamesIds() const { return m_shaderStorageBlockNamesIds; }
+ inline QVector<int> attributeNamesIds() const { return m_attributeNamesIds; }
+
+ QVector<QString> uniformsNames() const;
+ QVector<QString> attributesNames() const;
+ QVector<QString> uniformBlockNames() const;
+ QVector<QString> storageBlockNames() const;
+
+ inline QVector<ShaderUniform> uniforms() const { return m_uniforms; }
+ inline QVector<ShaderAttribute> attributes() const { return m_attributes; }
+ inline QVector<ShaderUniformBlock> uniformBlocks() const { return m_uniformBlocks; }
+ inline QVector<ShaderStorageBlock> storageBlocks() const { return m_shaderStorageBlocks; }
+
+ QHash<QString, ShaderUniform> activeUniformsForUniformBlock(int blockIndex) const;
+
+ ShaderUniformBlock uniformBlockForBlockIndex(int blockNameId);
+ ShaderUniformBlock uniformBlockForBlockNameId(int blockIndex);
+ ShaderUniformBlock uniformBlockForBlockName(const QString &blockName);
+
+ ShaderStorageBlock storageBlockForBlockIndex(int blockIndex);
+ ShaderStorageBlock storageBlockForBlockNameId(int blockNameId);
+ ShaderStorageBlock storageBlockForBlockName(const QString &blockName);
+
+ QOpenGLShaderProgram *shaderProgram() { return &m_shader; }
+
+ void setShaderCode(const QVector<QByteArray> shaderCode) { m_shaderCode = shaderCode; }
+ QVector<QByteArray> shaderCode() const;
+
+private:
+ bool m_isLoaded;
+ QOpenGLShaderProgram m_shader;
+ GraphicsContext *m_graphicsContext;
+
+ QVector<QString> m_uniformsNames;
+ QVector<int> m_uniformsNamesIds;
+ QVector<int> m_standardUniformNamesIds;
+ QVector<ShaderUniform> m_uniforms;
+
+ QVector<QString> m_attributesNames;
+ QVector<int> m_attributeNamesIds;
+ QVector<ShaderAttribute> m_attributes;
+
+ QVector<QString> m_uniformBlockNames;
+ QVector<int> m_uniformBlockNamesIds;
+ QVector<ShaderUniformBlock> m_uniformBlocks;
+ QHash<int, QHash<QString, ShaderUniform> > m_uniformBlockIndexToShaderUniforms;
+
+ QVector<QString> m_shaderStorageBlockNames;
+ QVector<int> m_shaderStorageBlockNamesIds;
+ QVector<ShaderStorageBlock> m_shaderStorageBlocks;
+
+ QHash<QString, int> m_fragOutputs;
+ QVector<QByteArray> m_shaderCode;
+
+ // Private so that only GraphicContext can call it
+ void initializeUniforms(const QVector<ShaderUniform> &uniformsDescription);
+ void initializeAttributes(const QVector<ShaderAttribute> &attributesDescription);
+ void initializeUniformBlocks(const QVector<ShaderUniformBlock> &uniformBlockDescription);
+ void initializeShaderStorageBlocks(const QVector<ShaderStorageBlock> &shaderStorageBlockDescription);
+
+ friend class GraphicsContext;
+
+ mutable QMutex m_mutex;
+ QMetaObject::Connection m_contextConnection;
+};
+
+} // Render
+
+} // Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDER_GLSHADER_P_H
diff --git a/src/plugins/renderers/opengl/renderer/logging.cpp b/src/plugins/renderers/opengl/renderer/logging.cpp
new file mode 100644
index 000000000..ab7437eba
--- /dev/null
+++ b/src/plugins/renderers/opengl/renderer/logging.cpp
@@ -0,0 +1,65 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "logging_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+namespace Render {
+
+Q_LOGGING_CATEGORY(Backend, "Qt3D.Renderer.OpenGL.Backend", QtWarningMsg)
+Q_LOGGING_CATEGORY(Frontend, "Qt3D.Renderer.OpenGL.Frontend", QtWarningMsg)
+Q_LOGGING_CATEGORY(Io, "Qt3D.Renderer.OpenGL.IO", QtWarningMsg)
+Q_LOGGING_CATEGORY(Jobs, "Qt3D.Renderer.OpenGL.Jobs", QtWarningMsg)
+Q_LOGGING_CATEGORY(SceneLoaders, "Qt3D.Renderer.OpenGL.SceneLoaders", QtWarningMsg)
+Q_LOGGING_CATEGORY(Framegraph, "Qt3D.Renderer.OpenGL.Framegraph", QtWarningMsg)
+Q_LOGGING_CATEGORY(RenderNodes, "Qt3D.Renderer.OpenGL.RenderNodes", QtWarningMsg)
+Q_LOGGING_CATEGORY(Rendering, "Qt3D.Renderer.OpenGL.Rendering", QtWarningMsg)
+Q_LOGGING_CATEGORY(Memory, "Qt3D.Renderer.OpenGL.Memory", QtWarningMsg)
+Q_LOGGING_CATEGORY(Shaders, "Qt3D.Renderer.OpenGL.Shaders", QtWarningMsg)
+Q_LOGGING_CATEGORY(RenderStates, "Qt3D.Renderer.OpenGL.RenderStates", QtWarningMsg)
+Q_LOGGING_CATEGORY(VSyncAdvanceService, "Qt3D.Renderer.OpenGL.VsyncAdvanceService", QtWarningMsg)
+
+} // namespace Render
+
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
diff --git a/src/plugins/renderers/opengl/renderer/logging_p.h b/src/plugins/renderers/opengl/renderer/logging_p.h
new file mode 100644
index 000000000..8a550e22f
--- /dev/null
+++ b/src/plugins/renderers/opengl/renderer/logging_p.h
@@ -0,0 +1,81 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DRENDER_RENDER_OPENGL_RENDERLOGGING_P_H
+#define QT3DRENDER_RENDER_OPENGL_RENDERLOGGING_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 <QLoggingCategory>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+namespace Render {
+
+Q_DECLARE_LOGGING_CATEGORY(Backend)
+Q_DECLARE_LOGGING_CATEGORY(Frontend)
+Q_DECLARE_LOGGING_CATEGORY(Io)
+Q_DECLARE_LOGGING_CATEGORY(Jobs)
+Q_DECLARE_LOGGING_CATEGORY(SceneLoaders)
+Q_DECLARE_LOGGING_CATEGORY(Framegraph)
+Q_DECLARE_LOGGING_CATEGORY(RenderNodes)
+Q_DECLARE_LOGGING_CATEGORY(Rendering)
+Q_DECLARE_LOGGING_CATEGORY(Memory)
+Q_DECLARE_LOGGING_CATEGORY(Shaders)
+Q_DECLARE_LOGGING_CATEGORY(RenderStates)
+Q_DECLARE_LOGGING_CATEGORY(VSyncAdvanceService)
+
+} // namespace Render
+
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDER_OPENGL_RENDERLOGGING_P_H
diff --git a/src/plugins/renderers/opengl/renderer/openglvertexarrayobject.cpp b/src/plugins/renderers/opengl/renderer/openglvertexarrayobject.cpp
new file mode 100644
index 000000000..19ae4fa55
--- /dev/null
+++ b/src/plugins/renderers/opengl/renderer/openglvertexarrayobject.cpp
@@ -0,0 +1,165 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "openglvertexarrayobject_p.h"
+#include <submissioncontext_p.h>
+#include <renderer_p.h>
+#include <glresourcemanagers_p.h>
+#include <Qt3DRender/private/managers_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+namespace Render {
+
+OpenGLVertexArrayObject::OpenGLVertexArrayObject()
+ : m_ctx(nullptr)
+ , m_specified(false)
+ , m_supportsVao(false)
+{}
+
+void OpenGLVertexArrayObject::bind()
+{
+ Q_ASSERT(m_ctx);
+ if (m_supportsVao) {
+ Q_ASSERT(!m_vao.isNull());
+ Q_ASSERT(m_vao->isCreated());
+ m_vao->bind();
+ } else {
+ // Unbind any other VAO that may have been bound and not released correctly
+ if (m_ctx->m_currentVAO != nullptr && m_ctx->m_currentVAO != this)
+ m_ctx->m_currentVAO->release();
+
+ m_ctx->m_currentVAO = this;
+ // We need to specify array and vertex attributes
+ for (const SubmissionContext::VAOVertexAttribute &attr : qAsConst(m_vertexAttributes))
+ m_ctx->enableAttribute(attr);
+ if (!m_indexAttribute.isNull())
+ m_ctx->bindGLBuffer(m_ctx->m_renderer->glResourceManagers()->glBufferManager()->data(m_indexAttribute),
+ GLBuffer::IndexBuffer);
+ }
+}
+
+void OpenGLVertexArrayObject::release()
+{
+ Q_ASSERT(m_ctx);
+ if (m_supportsVao) {
+ Q_ASSERT(!m_vao.isNull());
+ Q_ASSERT(m_vao->isCreated());
+ m_vao->release();
+ } else {
+ if (m_ctx->m_currentVAO == this) {
+ for (const SubmissionContext::VAOVertexAttribute &attr : qAsConst(m_vertexAttributes))
+ m_ctx->disableAttribute(attr);
+ m_ctx->m_currentVAO = nullptr;
+ }
+ }
+}
+
+// called from Render thread
+void OpenGLVertexArrayObject::create(SubmissionContext *ctx, const VAOIdentifier &key)
+{
+ QMutexLocker lock(&m_mutex);
+
+ Q_ASSERT(!m_ctx && !m_vao);
+
+ m_ctx = ctx;
+ m_supportsVao = m_ctx->supportsVAO();
+ if (m_supportsVao) {
+ m_vao.reset(new QOpenGLVertexArrayObject());
+ m_vao->create();
+ }
+ m_owners = key;
+}
+
+VAOIdentifier OpenGLVertexArrayObject::key() const
+{
+ return m_owners;
+}
+
+// called from Render thread
+void OpenGLVertexArrayObject::destroy()
+{
+ QMutexLocker locker(&m_mutex);
+
+ Q_ASSERT(m_ctx);
+ cleanup();
+}
+
+void OpenGLVertexArrayObject::cleanup()
+{
+ m_vao.reset();
+ m_ctx = nullptr;
+ m_specified = false;
+ m_supportsVao = false;
+ m_indexAttribute = SubmissionContext::VAOIndexAttribute();
+ m_vertexAttributes.clear();
+}
+
+// called from job
+bool OpenGLVertexArrayObject::isAbandoned(GeometryManager *geomMgr, GLShaderManager *shaderMgr)
+{
+ QMutexLocker lock(&m_mutex);
+
+ if (!m_ctx)
+ return false;
+
+ const bool geometryExists = (geomMgr->data(m_owners.first) != nullptr);
+ const bool shaderExists = (shaderMgr->lookupResource(m_owners.second) != nullptr);
+
+ return !geometryExists || !shaderExists;
+}
+
+void OpenGLVertexArrayObject::saveVertexAttribute(const SubmissionContext::VAOVertexAttribute &attr)
+{
+ // Remove any vertexAttribute already at location
+ for (auto i = m_vertexAttributes.size() - 1; i >= 0; --i) {
+ if (m_vertexAttributes.at(i).location == attr.location) {
+ m_vertexAttributes.removeAt(i);
+ break;
+ }
+ }
+ m_vertexAttributes.push_back(attr);
+}
+
+
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
diff --git a/src/plugins/renderers/opengl/renderer/openglvertexarrayobject_p.h b/src/plugins/renderers/opengl/renderer/openglvertexarrayobject_p.h
new file mode 100644
index 000000000..d422b5b8b
--- /dev/null
+++ b/src/plugins/renderers/opengl/renderer/openglvertexarrayobject_p.h
@@ -0,0 +1,111 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef OPENGLVERTEXARRAYOBJECT_H
+#define OPENGLVERTEXARRAYOBJECT_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 <QtGui/qopenglvertexarrayobject.h>
+#include <submissioncontext_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+namespace Render {
+
+class GeometryManager;
+class GLShaderManager;
+
+typedef QPair<HGeometry, Qt3DCore::QNodeId> VAOIdentifier;
+
+class OpenGLVertexArrayObject
+{
+public:
+ OpenGLVertexArrayObject();
+
+ void bind();
+ void release();
+
+ void create(SubmissionContext *ctx, const VAOIdentifier &key);
+ VAOIdentifier key() const;
+ void destroy();
+ void cleanup();
+
+ bool isAbandoned(GeometryManager *geomMgr, GLShaderManager *shaderMgr);
+
+ QOpenGLVertexArrayObject *vao() { return m_vao.data(); }
+ const QOpenGLVertexArrayObject *vao() const { return m_vao.data(); }
+
+ void setSpecified(bool b) { m_specified = b; }
+ bool isSpecified() const { return m_specified; }
+
+
+private:
+ QMutex m_mutex;
+ SubmissionContext *m_ctx;
+ QScopedPointer<QOpenGLVertexArrayObject> m_vao;
+ bool m_specified;
+ bool m_supportsVao;
+ VAOIdentifier m_owners;
+
+ friend class SubmissionContext;
+
+ void saveVertexAttribute(const SubmissionContext::VAOVertexAttribute &attr);
+ inline void saveIndexAttribute(HGLBuffer glBufferHandle) { m_indexAttribute = glBufferHandle; }
+
+ QVector<SubmissionContext::VAOVertexAttribute> m_vertexAttributes;
+ SubmissionContext::VAOIndexAttribute m_indexAttribute;
+};
+
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // OPENGLVERTEXARRAYOBJECT_H
diff --git a/src/plugins/renderers/opengl/renderer/rendercommand.cpp b/src/plugins/renderers/opengl/renderer/rendercommand.cpp
new file mode 100644
index 000000000..c6d42fde1
--- /dev/null
+++ b/src/plugins/renderers/opengl/renderer/rendercommand.cpp
@@ -0,0 +1,91 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "rendercommand_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+namespace Render {
+
+RenderCommand::RenderCommand()
+ : m_glShader(nullptr)
+ , m_stateSet(nullptr)
+ , m_depth(0.0f)
+ , m_changeCost(0)
+ , m_type(RenderCommand::Draw)
+ , m_primitiveCount(0)
+ , m_primitiveType(QGeometryRenderer::Triangles)
+ , m_restartIndexValue(-1)
+ , m_firstInstance(0)
+ , m_firstVertex(0)
+ , m_verticesPerPatch(0)
+ , m_instanceCount(0)
+ , m_indexOffset(0)
+ , m_indexAttributeByteOffset(0)
+ , m_indexAttributeDataType(GL_UNSIGNED_SHORT)
+ , m_indirectAttributeByteOffset(0)
+ , m_drawIndexed(false)
+ , m_drawIndirect(false)
+ , m_primitiveRestartEnabled(false)
+ , m_isValid(false)
+{
+ m_workGroups[0] = 0;
+ m_workGroups[1] = 0;
+ m_workGroups[2] = 0;
+}
+
+bool operator==(const RenderCommand &a, const RenderCommand &b) noexcept
+{
+ return (a.m_vao == b.m_vao && a.m_glShader == b.m_glShader && a.m_material == b.m_material &&
+ a.m_stateSet == b.m_stateSet && a.m_geometry == b.m_geometry && a.m_geometryRenderer == b.m_geometryRenderer &&
+ a.m_indirectDrawBuffer == b.m_indirectDrawBuffer && a.m_activeAttributes == b.m_activeAttributes &&
+ a.m_depth == b.m_depth && a.m_changeCost == b.m_changeCost && a.m_shaderId == b.m_shaderId &&
+ a.m_workGroups[0] == b.m_workGroups[0] && a.m_workGroups[1] == b.m_workGroups[1] && a.m_workGroups[2] == b.m_workGroups[2] &&
+ a.m_primitiveCount == b.m_primitiveCount && a.m_primitiveType == b.m_primitiveType && a.m_restartIndexValue == b.m_restartIndexValue &&
+ a.m_firstInstance == b.m_firstInstance && a.m_firstVertex == b.m_firstVertex && a.m_verticesPerPatch == b.m_verticesPerPatch &&
+ a.m_instanceCount == b.m_instanceCount && a.m_indexOffset == b.m_indexOffset && a.m_indexAttributeByteOffset == b.m_indexAttributeByteOffset &&
+ a.m_drawIndexed == b.m_drawIndexed && a.m_drawIndirect == b.m_drawIndirect && a.m_primitiveRestartEnabled == b.m_primitiveRestartEnabled &&
+ a.m_isValid == b.m_isValid && a.m_computeCommand == b.m_computeCommand);
+}
+
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
diff --git a/src/plugins/renderers/opengl/renderer/rendercommand_p.h b/src/plugins/renderers/opengl/renderer/rendercommand_p.h
new file mode 100644
index 000000000..600c52959
--- /dev/null
+++ b/src/plugins/renderers/opengl/renderer/rendercommand_p.h
@@ -0,0 +1,182 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB).
+** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DRENDER_RENDER_RENDERCOMMAND_H
+#define QT3DRENDER_RENDER_RENDERCOMMAND_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qglobal.h>
+#include <shaderparameterpack_p.h>
+#include <gl_handle_types_p.h>
+#include <renderviewjobutils_p.h>
+#include <Qt3DRender/private/handle_types_p.h>
+#include <Qt3DRender/qgeometryrenderer.h>
+#include <QOpenGLShaderProgram>
+#include <QOpenGLTexture>
+#include <QMatrix4x4>
+
+QT_BEGIN_NAMESPACE
+
+class QOpenGLVertexArrayObject;
+
+namespace Qt3DRender {
+
+namespace Render {
+
+class RenderStateSet;
+using RenderStateSetPtr = QSharedPointer<RenderStateSet>;
+class GLShader;
+
+class Q_AUTOTEST_EXPORT RenderCommand
+{
+public:
+ RenderCommand();
+
+ HVao m_vao; // VAO used during the submission step to store all states and VBOs
+ HMaterial m_material; // Purely used to ease sorting (minimize stage changes, binding changes ....)
+ GLShader *m_glShader; // GL Shader to be used at render time
+ Qt3DCore::QNodeId m_shaderId; // Shader for given pass and mesh
+ ShaderParameterPack m_parameterPack; // Might need to be reworked so as to be able to destroy the
+ // Texture while submission is happening.
+ RenderStateSetPtr m_stateSet;
+
+ HGeometry m_geometry;
+ HGeometryRenderer m_geometryRenderer;
+
+ HBuffer m_indirectDrawBuffer; // Reference to indirect draw buffer (valid only m_drawIndirect == true)
+ HComputeCommand m_computeCommand;
+
+ // A QAttribute pack might be interesting
+ // This is a temporary fix in the meantime, to remove the hacked methods in Technique
+ QVector<int> m_activeAttributes;
+
+ float m_depth;
+ int m_changeCost;
+
+ enum CommandType {
+ Draw,
+ Compute
+ };
+
+ CommandType m_type;
+ int m_workGroups[3];
+
+ // Values filled for draw calls by Renderer (in prepare Submission)
+ GLsizei m_primitiveCount;
+ QGeometryRenderer::PrimitiveType m_primitiveType;
+ int m_restartIndexValue;
+ int m_firstInstance;
+ int m_firstVertex;
+ int m_verticesPerPatch;
+ int m_instanceCount;
+ int m_indexOffset;
+ uint m_indexAttributeByteOffset;
+ GLint m_indexAttributeDataType;
+ uint m_indirectAttributeByteOffset;
+ bool m_drawIndexed;
+ bool m_drawIndirect;
+ bool m_primitiveRestartEnabled;
+ bool m_isValid;
+};
+
+Q_AUTOTEST_EXPORT bool operator==(const RenderCommand &a, const RenderCommand &b) noexcept;
+
+inline bool operator!=(const RenderCommand &lhs, const RenderCommand &rhs) noexcept
+{ return !operator==(lhs, rhs); }
+
+struct EntityRenderCommandData
+{
+ QVector<Entity *> entities;
+ QVector<RenderCommand> commands;
+ QVector<RenderPassParameterData> passesData;
+
+ void reserve(int size)
+ {
+ entities.reserve(size);
+ commands.reserve(size);
+ passesData.reserve(size);
+ }
+
+ inline int size() const { return entities.size(); }
+
+ inline void push_back(Entity *e, const RenderCommand &c, const RenderPassParameterData &p)
+ {
+ entities.push_back(e);
+ commands.push_back(c);
+ passesData.push_back(p);
+ }
+
+ inline void push_back(Entity *e, RenderCommand &&c, RenderPassParameterData &&p)
+ {
+ entities.push_back(e);
+ commands.push_back(std::move(c));
+ passesData.push_back(std::move(p));
+ }
+
+ EntityRenderCommandData &operator+=(EntityRenderCommandData &&t)
+ {
+ entities += std::move(t.entities);
+ commands += std::move(t.commands);
+ passesData += std::move(t.passesData);
+ return *this;
+ }
+
+};
+
+using EntityRenderCommandDataPtr = QSharedPointer<EntityRenderCommandData>;
+
+
+} // namespace Render
+
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDER_RENDERCOMMAND_H
diff --git a/src/plugins/renderers/opengl/renderer/renderer.cpp b/src/plugins/renderers/opengl/renderer/renderer.cpp
new file mode 100644
index 000000000..a090f7abb
--- /dev/null
+++ b/src/plugins/renderers/opengl/renderer/renderer.cpp
@@ -0,0 +1,2414 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB).
+** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "renderer_p.h"
+
+#include <Qt3DCore/qentity.h>
+
+#include <Qt3DRender/qmaterial.h>
+#include <Qt3DRender/qmesh.h>
+#include <Qt3DRender/qrenderpass.h>
+#include <Qt3DRender/qshaderprogram.h>
+#include <Qt3DRender/qtechnique.h>
+#include <Qt3DRender/qrenderaspect.h>
+#include <Qt3DRender/qeffect.h>
+
+#include <Qt3DRender/private/qsceneimporter_p.h>
+#include <Qt3DRender/private/renderstates_p.h>
+#include <Qt3DRender/private/cameraselectornode_p.h>
+#include <Qt3DRender/private/framegraphvisitor_p.h>
+#include <Qt3DRender/private/cameralens_p.h>
+#include <Qt3DRender/private/entity_p.h>
+#include <Qt3DRender/private/renderlogging_p.h>
+#include <Qt3DRender/private/material_p.h>
+#include <Qt3DRender/private/renderpassfilternode_p.h>
+#include <Qt3DRender/private/shader_p.h>
+#include <Qt3DRender/private/buffer_p.h>
+#include <Qt3DRender/private/technique_p.h>
+#include <Qt3DRender/private/renderthread_p.h>
+#include <Qt3DRender/private/scenemanager_p.h>
+#include <Qt3DRender/private/techniquefilternode_p.h>
+#include <Qt3DRender/private/viewportnode_p.h>
+#include <Qt3DRender/private/vsyncframeadvanceservice_p.h>
+#include <Qt3DRender/private/pickeventfilter_p.h>
+#include <Qt3DRender/private/managers_p.h>
+#include <Qt3DRender/private/buffermanager_p.h>
+#include <Qt3DRender/private/nodemanagers_p.h>
+#include <Qt3DRender/private/geometryrenderermanager_p.h>
+#include <Qt3DRender/private/techniquemanager_p.h>
+#include <Qt3DRender/private/platformsurfacefilter_p.h>
+#include <Qt3DRender/private/loadbufferjob_p.h>
+#include <Qt3DRender/private/rendercapture_p.h>
+#include <Qt3DRender/private/updatelevelofdetailjob_p.h>
+#include <Qt3DRender/private/buffercapture_p.h>
+#include <Qt3DRender/private/offscreensurfacehelper_p.h>
+#include <Qt3DRender/private/subtreeenabler_p.h>
+#include <Qt3DRender/private/qshaderprogrambuilder_p.h>
+#include <Qt3DRender/private/qshaderprogram_p.h>
+#include <Qt3DRender/private/filterentitybycomponentjob_p.h>
+
+#include <Qt3DRender/qcameralens.h>
+#include <Qt3DCore/private/qeventfilterservice_p.h>
+#include <Qt3DCore/private/qabstractaspectjobmanager_p.h>
+#include <Qt3DCore/private/qaspectmanager_p.h>
+#include <Qt3DCore/private/qsysteminformationservice_p.h>
+#include <Qt3DCore/private/qsysteminformationservice_p_p.h>
+#include <Qt3DRender/private/resourceaccessor_p.h>
+#include <Qt3DRender/private/renderlogging_p.h>
+#include <Qt3DRender/private/renderstateset_p.h>
+#include <Qt3DRender/private/setfence_p.h>
+
+#include <glbuffer_p.h>
+#include <graphicscontext_p.h>
+#include <rendercommand_p.h>
+#include <renderqueue_p.h>
+#include <renderview_p.h>
+#include <gltexture_p.h>
+#include <openglvertexarrayobject_p.h>
+#include <renderviewbuilder_p.h>
+#include <glresourcemanagers_p.h>
+#include <commandexecuter_p.h>
+
+#include <QStack>
+#include <QOffscreenSurface>
+#include <QSurface>
+#include <QElapsedTimer>
+#include <QLibraryInfo>
+#include <QMutexLocker>
+#include <QPluginLoader>
+#include <QDir>
+#include <QUrl>
+#include <QOffscreenSurface>
+#include <QWindow>
+#include <QThread>
+
+#include <QtGui/private/qopenglcontext_p.h>
+#include <Qt3DRender/private/frameprofiler_p.h>
+
+QT_BEGIN_NAMESPACE
+
+// Crashes on AMD Radeon drivers on Windows. Disable for now.
+//#define SHADER_LOADING_IN_COMMAND_THREAD
+
+using namespace Qt3DCore;
+
+namespace Qt3DRender {
+namespace Render {
+
+namespace {
+
+class SyncLightsGatherer
+{
+public:
+ explicit SyncLightsGatherer(LightGathererPtr gatherJob,
+ RendererCache *cache)
+ : m_gatherJob(gatherJob)
+ , m_cache(cache)
+ {
+ }
+
+ void operator()()
+ {
+ QMutexLocker lock(m_cache->mutex());
+ m_cache->gatheredLights = m_gatherJob->lights();
+ m_cache->environmentLight = m_gatherJob->takeEnvironmentLight();
+ }
+
+private:
+ LightGathererPtr m_gatherJob;
+ RendererCache *m_cache;
+};
+
+class SyncRenderableEntities
+{
+public:
+ explicit SyncRenderableEntities(RenderableEntityFilterPtr gatherJob,
+ RendererCache *cache)
+ : m_gatherJob(gatherJob)
+ , m_cache(cache)
+ {
+ }
+
+ void operator()()
+ {
+ QVector<Entity *> selectedEntities = m_gatherJob->filteredEntities();
+ std::sort(selectedEntities.begin(), selectedEntities.end());
+
+ QMutexLocker lock(m_cache->mutex());
+ m_cache->renderableEntities = selectedEntities;
+ }
+
+private:
+ RenderableEntityFilterPtr m_gatherJob;
+ RendererCache *m_cache;
+};
+
+class SyncComputableEntities
+{
+public:
+ explicit SyncComputableEntities(ComputableEntityFilterPtr gatherJob,
+ RendererCache *cache)
+ : m_gatherJob(gatherJob)
+ , m_cache(cache)
+ {
+ }
+
+ void operator()()
+ {
+ QVector<Entity *> selectedEntities = m_gatherJob->filteredEntities();
+ std::sort(selectedEntities.begin(), selectedEntities.end());
+
+ QMutexLocker lock(m_cache->mutex());
+ m_cache->computeEntities = selectedEntities;
+ }
+
+private:
+ ComputableEntityFilterPtr m_gatherJob;
+ RendererCache *m_cache;
+};
+
+} // anonymous
+
+
+/*!
+ \internal
+
+ Renderer shutdown procedure:
+
+ Since the renderer relies on the surface and OpenGLContext to perform its cleanup,
+ it is shutdown when the surface is set to nullptr
+
+ When the surface is set to nullptr this will request the RenderThread to terminate
+ and will prevent createRenderBinJobs from returning a set of jobs as there is nothing
+ more to be rendered.
+
+ In turn, this will call shutdown which will make the OpenGL context current one last time
+ to allow cleanups requiring a call to QOpenGLContext::currentContext to execute properly.
+ At the end of that function, the GraphicsContext is set to null.
+
+ At this point though, the QAspectThread is still running its event loop and will only stop
+ a short while after.
+ */
+
+Renderer::Renderer(QRenderAspect::RenderType type)
+ : m_services(nullptr)
+ , m_nodesManager(nullptr)
+ , m_renderSceneRoot(nullptr)
+ , m_defaultRenderStateSet(nullptr)
+ , m_submissionContext(nullptr)
+ , m_renderQueue(new RenderQueue())
+ , m_renderThread(type == QRenderAspect::Threaded ? new RenderThread(this) : nullptr)
+ , m_vsyncFrameAdvanceService(new VSyncFrameAdvanceService(m_renderThread != nullptr))
+ , m_waitForInitializationToBeCompleted(0)
+ , m_hasBeenInitializedMutex()
+ , m_pickEventFilter(new PickEventFilter())
+ , m_exposed(0)
+ , m_lastFrameCorrect(0)
+ , m_glContext(nullptr)
+ , m_shareContext(nullptr)
+ , m_pickBoundingVolumeJob(PickBoundingVolumeJobPtr::create())
+ , m_rayCastingJob(RayCastingJobPtr::create())
+ , m_time(0)
+ , m_settings(nullptr)
+ , m_updateShaderDataTransformJob(Render::UpdateShaderDataTransformJobPtr::create())
+ , m_cleanupJob(Render::FrameCleanupJobPtr::create())
+ , m_worldTransformJob(Render::UpdateWorldTransformJobPtr::create())
+ , m_expandBoundingVolumeJob(Render::ExpandBoundingVolumeJobPtr::create())
+ , m_calculateBoundingVolumeJob(Render::CalculateBoundingVolumeJobPtr::create())
+ , m_updateWorldBoundingVolumeJob(Render::UpdateWorldBoundingVolumeJobPtr::create())
+ , m_updateTreeEnabledJob(Render::UpdateTreeEnabledJobPtr::create())
+ , m_sendBufferCaptureJob(Render::SendBufferCaptureJobPtr::create())
+ , m_updateSkinningPaletteJob(Render::UpdateSkinningPaletteJobPtr::create())
+ , m_updateLevelOfDetailJob(Render::UpdateLevelOfDetailJobPtr::create())
+ , m_updateMeshTriangleListJob(Render::UpdateMeshTriangleListJobPtr::create())
+ , m_filterCompatibleTechniqueJob(Render::FilterCompatibleTechniqueJobPtr::create())
+ , m_updateEntityLayersJob(Render::UpdateEntityLayersJobPtr::create())
+ , m_lightGathererJob(Render::LightGathererPtr::create())
+ , m_renderableEntityFilterJob(Render::RenderableEntityFilterPtr::create())
+ , m_computableEntityFilterJob(Render::ComputableEntityFilterPtr::create())
+ , m_bufferGathererJob(SynchronizerJobPtr::create([this] { lookForDirtyBuffers(); }, JobTypes::DirtyBufferGathering))
+ , m_vaoGathererJob(SynchronizerJobPtr::create([this] { lookForAbandonedVaos(); }, JobTypes::DirtyVaoGathering))
+ , m_textureGathererJob(SynchronizerJobPtr::create([this] { lookForDirtyTextures(); }, JobTypes::DirtyTextureGathering))
+ , m_sendSetFenceHandlesToFrontendJob(SynchronizerJobPtr::create([this] { sendSetFenceHandlesToFrontend(); }, JobTypes::SendSetFenceHandlesToFrontend))
+ , m_introspectShaderJob(SynchronizerPostFramePtr::create([this] { reloadDirtyShaders(); },
+ [this] (Qt3DCore::QAspectManager *m) { sendShaderChangesToFrontend(m); },
+ JobTypes::DirtyShaderGathering))
+ , m_syncLoadingJobs(SynchronizerJobPtr::create([] {}, JobTypes::SyncLoadingJobs))
+ , m_cacheRenderableEntitiesJob(SynchronizerJobPtr::create(SyncRenderableEntities(m_renderableEntityFilterJob, &m_cache),
+ JobTypes::EntityComponentTypeFiltering))
+ , m_cacheComputableEntitiesJob(SynchronizerJobPtr::create(SyncComputableEntities(m_computableEntityFilterJob, &m_cache),
+ JobTypes::EntityComponentTypeFiltering))
+ , m_cacheLightsJob(SynchronizerJobPtr::create(SyncLightsGatherer(m_lightGathererJob, &m_cache),
+ JobTypes::EntityComponentTypeFiltering))
+ , m_ownedContext(false)
+ , m_offscreenHelper(nullptr)
+ , m_glResourceManagers(nullptr)
+ , m_commandExecuter(new Qt3DRender::Debug::CommandExecuter(this))
+ , m_shouldSwapBuffers(true)
+{
+ // Set renderer as running - it will wait in the context of the
+ // RenderThread for RenderViews to be submitted
+ m_running.fetchAndStoreOrdered(1);
+ if (m_renderThread)
+ m_renderThread->waitForStart();
+
+ // Create jobs to update transforms and bounding volumes
+ // We can only update bounding volumes once all world transforms are known
+ 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);
+ m_rayCastingJob->addDependency(m_expandBoundingVolumeJob);
+ // 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_syncLoadingJobs);
+
+ // All world stuff depends on the RenderEntity's localBoundingVolume
+ m_updateLevelOfDetailJob->addDependency(m_updateMeshTriangleListJob);
+ m_pickBoundingVolumeJob->addDependency(m_updateMeshTriangleListJob);
+ m_rayCastingJob->addDependency(m_updateMeshTriangleListJob);
+
+ m_introspectShaderJob->addDependency(m_filterCompatibleTechniqueJob);
+
+ m_cacheLightsJob->addDependency(m_lightGathererJob);
+ m_cacheRenderableEntitiesJob->addDependency(m_renderableEntityFilterJob);
+ m_cacheComputableEntitiesJob->addDependency(m_computableEntityFilterJob);
+
+ m_filterCompatibleTechniqueJob->setRenderer(this);
+
+ m_defaultRenderStateSet = new RenderStateSet;
+ m_defaultRenderStateSet->addState(StateVariant::createState<DepthTest>(GL_LESS));
+ m_defaultRenderStateSet->addState(StateVariant::createState<CullFace>(GL_BACK));
+ m_defaultRenderStateSet->addState(StateVariant::createState<ColorMask>(true, true, true, true));
+}
+
+Renderer::~Renderer()
+{
+ Q_ASSERT(m_running.fetchAndStoreOrdered(0) == 0);
+ if (m_renderThread)
+ Q_ASSERT(m_renderThread->isFinished());
+
+ delete m_renderQueue;
+ delete m_defaultRenderStateSet;
+ delete m_glResourceManagers;
+
+ if (!m_ownedContext)
+ QObject::disconnect(m_contextConnection);
+}
+
+void Renderer::dumpInfo() const
+{
+ qDebug() << Q_FUNC_INFO << "t =" << m_time;
+
+ const ShaderManager *shaderManager = m_nodesManager->shaderManager();
+ qDebug() << "=== Shader Manager ===";
+ qDebug() << *shaderManager;
+
+ const TextureManager *textureManager = m_nodesManager->textureManager();
+ qDebug() << "=== Texture Manager ===";
+ qDebug() << *textureManager;
+
+ const TextureImageManager *textureImageManager = m_nodesManager->textureImageManager();
+ qDebug() << "=== Texture Image Manager ===";
+ qDebug() << *textureImageManager;
+}
+
+qint64 Renderer::time() const
+{
+ return m_time;
+}
+
+void Renderer::setTime(qint64 time)
+{
+ m_time = time;
+}
+
+void Renderer::setNodeManagers(NodeManagers *managers)
+{
+ m_nodesManager = managers;
+ m_glResourceManagers = new GLResourceManagers();
+ m_scene2DResourceAccessor.reset(new ResourceAccessor(this, m_nodesManager));
+
+ m_updateShaderDataTransformJob->setManagers(m_nodesManager);
+ m_cleanupJob->setManagers(m_nodesManager);
+ m_calculateBoundingVolumeJob->setManagers(m_nodesManager);
+ m_expandBoundingVolumeJob->setManagers(m_nodesManager);
+ m_worldTransformJob->setManagers(m_nodesManager);
+ m_pickBoundingVolumeJob->setManagers(m_nodesManager);
+ m_rayCastingJob->setManagers(m_nodesManager);
+ m_updateWorldBoundingVolumeJob->setManager(m_nodesManager->renderNodesManager());
+ m_updateLevelOfDetailJob->setManagers(m_nodesManager);
+ m_updateSkinningPaletteJob->setManagers(m_nodesManager);
+ m_updateMeshTriangleListJob->setManagers(m_nodesManager);
+ m_filterCompatibleTechniqueJob->setManager(m_nodesManager->techniqueManager());
+ m_updateEntityLayersJob->setManager(m_nodesManager);
+ m_updateTreeEnabledJob->setManagers(m_nodesManager);
+ m_sendBufferCaptureJob->setManagers(m_nodesManager);
+ m_lightGathererJob->setManager(m_nodesManager->renderNodesManager());
+ m_renderableEntityFilterJob->setManager(m_nodesManager->renderNodesManager());
+ m_computableEntityFilterJob->setManager(m_nodesManager->renderNodesManager());
+}
+
+void Renderer::setServices(QServiceLocator *services)
+{
+ m_services = services;
+
+ m_nodesManager->sceneManager()->setDownloadService(m_services->downloadHelperService());
+}
+
+NodeManagers *Renderer::nodeManagers() const
+{
+ return m_nodesManager;
+}
+
+/*!
+ \internal
+
+ Return context which can be used to share resources safely
+ with qt3d main render context.
+*/
+QOpenGLContext *Renderer::shareContext() const
+{
+ QMutexLocker lock(&m_shareContextMutex);
+ return m_shareContext ? m_shareContext
+ : (m_submissionContext->openGLContext()
+ ? m_submissionContext->openGLContext()->shareContext()
+ : nullptr);
+}
+
+// Executed in the reloadDirtyShader job
+void Renderer::loadShader(Shader *shader, HShader shaderHandle)
+{
+ Q_UNUSED(shader);
+ if (!m_dirtyShaders.contains(shaderHandle))
+ m_dirtyShaders.push_back(shaderHandle);
+}
+
+void Renderer::setOpenGLContext(QOpenGLContext *context)
+{
+ m_glContext = context;
+}
+
+void Renderer::setScreen(QScreen *scr)
+{
+ m_screen = scr;
+}
+
+QScreen *Renderer::screen() const
+{
+ return m_screen;
+}
+
+bool Renderer::accessOpenGLTexture(Qt3DCore::QNodeId nodeId,
+ QOpenGLTexture **texture,
+ QMutex **lock,
+ bool readonly)
+{
+ Texture *tex = m_nodesManager->textureManager()->lookupResource(nodeId);
+ if (!tex)
+ return false;
+
+ GLTexture *glTex = m_glResourceManagers->glTextureManager()->lookupResource(tex->peerId());
+ if (!glTex)
+ return false;
+
+ if (glTex->isDirty())
+ return false;
+
+ if (!readonly)
+ glTex->setExternalRenderingEnabled(true);
+
+ GLTexture::TextureUpdateInfo texInfo = glTex->createOrUpdateGLTexture();
+ *texture = texInfo.texture;
+
+ if (!readonly)
+ *lock = glTex->externalRenderingLock();
+
+ return true;
+}
+
+QSharedPointer<RenderBackendResourceAccessor> Renderer::resourceAccessor() const
+{
+ return m_scene2DResourceAccessor;
+}
+
+// Called in RenderThread context by the run method of RenderThread
+// RenderThread has locked the mutex already and unlocks it when this
+// method termintates
+void Renderer::initialize()
+{
+ QMutexLocker lock(&m_hasBeenInitializedMutex);
+ m_submissionContext.reset(new SubmissionContext);
+ m_submissionContext->setRenderer(this);
+
+ QOpenGLContext* ctx = m_glContext;
+
+ {
+ QMutexLocker lock(&m_shareContextMutex);
+ // If we are using our own context (not provided by QtQuick),
+ // we need to create it
+ if (!m_glContext) {
+ ctx = new QOpenGLContext;
+ if (m_screen)
+ ctx->setScreen(m_screen);
+ ctx->setShareContext(qt_gl_global_share_context());
+
+ // TO DO: Shouldn't we use the highest context available and trust
+ // QOpenGLContext to fall back on the best lowest supported ?
+ const QByteArray debugLoggingMode = qgetenv("QT3DRENDER_DEBUG_LOGGING");
+
+ if (!debugLoggingMode.isEmpty()) {
+ QSurfaceFormat sf = ctx->format();
+ sf.setOption(QSurfaceFormat::DebugContext);
+ ctx->setFormat(sf);
+ }
+
+ // Create OpenGL context
+ if (ctx->create())
+ qCDebug(Backend) << "OpenGL context created with actual format" << ctx->format();
+ else
+ qCWarning(Backend) << Q_FUNC_INFO << "OpenGL context creation failed";
+ m_ownedContext = true;
+ } else {
+ // Context is not owned by us, so we need to know if it gets destroyed
+ m_contextConnection = QObject::connect(m_glContext, &QOpenGLContext::aboutToBeDestroyed,
+ [this] { releaseGraphicsResources(); });
+ }
+
+ qCDebug(Backend) << "Qt3D shared context:" << ctx->shareContext();
+ qCDebug(Backend) << "Qt global shared context:" << qt_gl_global_share_context();
+
+ if (!ctx->shareContext()) {
+ m_shareContext = new QOpenGLContext;
+ if (ctx->screen())
+ m_shareContext->setScreen(ctx->screen());
+ m_shareContext->setFormat(ctx->format());
+ m_shareContext->setShareContext(ctx);
+ m_shareContext->create();
+ }
+
+ // Note: we don't have a surface at this point
+ // The context will be made current later on (at render time)
+ m_submissionContext->setOpenGLContext(ctx);
+
+ // Store the format used by the context and queue up creating an
+ // offscreen surface in the main thread so that it is available
+ // for use when we want to shutdown the renderer. We need to create
+ // the offscreen surface on the main thread because on some platforms
+ // (MS Windows), an offscreen surface is just a hidden QWindow.
+ m_format = ctx->format();
+ QMetaObject::invokeMethod(m_offscreenHelper, "createOffscreenSurface");
+ }
+
+ // Awake setScenegraphRoot in case it was waiting
+ m_waitForInitializationToBeCompleted.release(1);
+ // Allow the aspect manager to proceed
+ m_vsyncFrameAdvanceService->proceedToNextFrame();
+
+ // Force initial refresh
+ markDirty(AllDirty, nullptr);
+}
+
+/*!
+ * \internal
+ *
+ * Signals for the renderer to stop rendering. If a threaded renderer is in use,
+ * the render thread will call releaseGraphicsResources() just before the thread exits.
+ * If rendering synchronously, this function will call releaseGraphicsResources().
+ */
+void Renderer::shutdown()
+{
+ // Ensure we have waited to be fully initialized before trying to shut down
+ // (in case initialization is taking place at the same time)
+ QMutexLocker lock(&m_hasBeenInitializedMutex);
+
+ qCDebug(Backend) << Q_FUNC_INFO << "Requesting renderer shutdown";
+ m_running.storeRelaxed(0);
+
+ // We delete any renderqueue that we may not have had time to render
+ // before the surface was destroyed
+ QMutexLocker lockRenderQueue(m_renderQueue->mutex());
+ qDeleteAll(m_renderQueue->nextFrameQueue());
+ m_renderQueue->reset();
+ lockRenderQueue.unlock();
+
+ if (!m_renderThread) {
+ 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_renderThread->wait();
+ }
+
+ // Destroy internal managers
+ // This needs to be done before the nodeManager is destroy
+ // as the internal resources might somehow rely on nodeManager resources
+ delete m_glResourceManagers;
+ m_glResourceManagers = nullptr;
+}
+
+/*!
+ \internal
+
+ When using a threaded renderer this function is called in the context of the
+ RenderThread to do any shutdown and cleanup that needs to be performed in the
+ thread where the OpenGL context lives.
+
+ When using Scene3D or anything that provides a custom QOpenGLContext (not
+ owned by Qt3D) this function is called whenever the signal
+ QOpenGLContext::aboutToBeDestroyed is emitted. In that case this function
+ is called in the context of the emitter's thread.
+*/
+void Renderer::releaseGraphicsResources()
+{
+ // We may get called twice when running inside of a Scene3D. Once when Qt Quick
+ // wants to shutdown, and again when the render aspect gets unregistered. So
+ // check that we haven't already cleaned up before going any further.
+ if (!m_submissionContext)
+ return;
+
+ // Try to temporarily make the context current so we can free up any resources
+ QMutexLocker locker(&m_offscreenSurfaceMutex);
+ QOffscreenSurface *offscreenSurface = m_offscreenHelper->offscreenSurface();
+ if (!offscreenSurface) {
+ qWarning() << "Failed to make context current: OpenGL resources will not be destroyed";
+ // We still need to delete the submission context
+ m_submissionContext.reset(nullptr);
+ return;
+ }
+
+ QOpenGLContext *context = m_submissionContext->openGLContext();
+ Q_ASSERT(context);
+
+ if (context->thread() == QThread::currentThread() && context->makeCurrent(offscreenSurface)) {
+
+ // Clean up the graphics context and any resources
+ const QVector<HGLTexture> activeTexturesHandles = m_glResourceManagers->glTextureManager()->activeHandles();
+ for (const HGLTexture &textureHandle : activeTexturesHandles) {
+ GLTexture *tex = m_glResourceManagers->glTextureManager()->data(textureHandle);
+ tex->destroy();
+ }
+
+ // Do the same thing with buffers
+ const QVector<HGLBuffer> activeBuffers = m_glResourceManagers->glBufferManager()->activeHandles();
+ for (const HGLBuffer &bufferHandle : activeBuffers) {
+ GLBuffer *buffer = m_glResourceManagers->glBufferManager()->data(bufferHandle);
+ buffer->destroy(m_submissionContext.data());
+ }
+
+ // Do the same thing with shaders
+ const QVector<GLShader *> shaders = m_glResourceManagers->glShaderManager()->takeActiveResources();
+ qDeleteAll(shaders);
+
+ // Do the same thing with VAOs
+ const QVector<HVao> activeVaos = m_glResourceManagers->vaoManager()->activeHandles();
+ for (const HVao &vaoHandle : activeVaos) {
+ OpenGLVertexArrayObject *vao = m_glResourceManagers->vaoManager()->data(vaoHandle);
+ vao->destroy();
+ }
+
+ context->doneCurrent();
+ } else {
+ qWarning() << "Failed to make context current: OpenGL resources will not be destroyed";
+ }
+
+ if (m_ownedContext)
+ delete context;
+ if (m_shareContext)
+ delete m_shareContext;
+
+ m_submissionContext.reset(nullptr);
+ qCDebug(Backend) << Q_FUNC_INFO << "Renderer properly shutdown";
+}
+
+void Renderer::setSurfaceExposed(bool exposed)
+{
+ qCDebug(Backend) << "Window exposed: " << exposed;
+ m_exposed.fetchAndStoreOrdered(exposed);
+}
+
+Render::FrameGraphNode *Renderer::frameGraphRoot() const
+{
+ Q_ASSERT(m_settings);
+ if (m_nodesManager && m_nodesManager->frameGraphManager() && m_settings)
+ return m_nodesManager->frameGraphManager()->lookupNode(m_settings->activeFrameGraphID());
+ return nullptr;
+}
+
+// QAspectThread context
+// Order of execution :
+// 1) RenderThread is created -> release 1 of m_waitForInitializationToBeCompleted when started
+// 2) setSceneRoot waits to acquire initialization
+// 3) submitRenderView -> check for surface
+// -> make surface current + create proper glHelper if needed
+void Renderer::setSceneRoot(Entity *sgRoot)
+{
+ Q_ASSERT(sgRoot);
+
+ // If initialization hasn't been completed we must wait
+ m_waitForInitializationToBeCompleted.acquire();
+
+ m_renderSceneRoot = sgRoot;
+ if (!m_renderSceneRoot)
+ qCWarning(Backend) << "Failed to build render scene";
+ m_renderSceneRoot->dump();
+ qCDebug(Backend) << Q_FUNC_INFO << "DUMPING SCENE";
+
+ // Set the scene root on the jobs
+ m_worldTransformJob->setRoot(m_renderSceneRoot);
+ m_expandBoundingVolumeJob->setRoot(m_renderSceneRoot);
+ m_calculateBoundingVolumeJob->setRoot(m_renderSceneRoot);
+ m_cleanupJob->setRoot(m_renderSceneRoot);
+ m_pickBoundingVolumeJob->setRoot(m_renderSceneRoot);
+ m_rayCastingJob->setRoot(m_renderSceneRoot);
+ m_updateLevelOfDetailJob->setRoot(m_renderSceneRoot);
+ m_updateSkinningPaletteJob->setRoot(m_renderSceneRoot);
+ m_updateTreeEnabledJob->setRoot(m_renderSceneRoot);
+
+ // Set all flags to dirty
+ m_dirtyBits.marked |= AbstractRenderer::AllDirty;
+}
+
+void Renderer::registerEventFilter(QEventFilterService *service)
+{
+ qCDebug(Backend) << Q_FUNC_INFO << QThread::currentThread();
+ service->registerEventFilter(m_pickEventFilter.data(), 1024);
+}
+
+void Renderer::setSettings(RenderSettings *settings)
+{
+ m_settings = settings;
+}
+
+RenderSettings *Renderer::settings() const
+{
+ return m_settings;
+}
+
+void Renderer::render()
+{
+ // Traversing the framegraph tree from root to lead node
+ // Allows us to define the rendering set up
+ // Camera, RenderTarget ...
+
+ // Utimately the renderer should be a framework
+ // For the processing of the list of renderviews
+
+ // Matrice update, bounding volumes computation ...
+ // Should be jobs
+
+ // namespace Qt3DCore has 2 distincts node trees
+ // One scene description
+ // One framegraph description
+
+ while (m_running.loadRelaxed() > 0) {
+ doRender();
+ // TO DO: Restore windows exposed detection
+ // Probably needs to happens some place else though
+ }
+}
+
+// Either called by render if Qt3D is in charge of the RenderThread
+// or by QRenderAspectPrivate::renderSynchronous (for Scene3D)
+void Renderer::doRender(bool swapBuffers)
+{
+ Renderer::ViewSubmissionResultData submissionData;
+ bool hasCleanedQueueAndProceeded = false;
+ bool preprocessingComplete = false;
+ bool beganDrawing = false;
+
+ // Blocking until RenderQueue is full
+ const bool canSubmit = isReadyToSubmit();
+ m_shouldSwapBuffers = swapBuffers;
+
+ // Lock the mutex to protect access to the renderQueue while we look for its state
+ QMutexLocker locker(m_renderQueue->mutex());
+ const bool queueIsComplete = m_renderQueue->isFrameQueueComplete();
+ const bool queueIsEmpty = m_renderQueue->targetRenderViewCount() == 0;
+
+ // When using synchronous rendering (QtQuick)
+ // We are not sure that the frame queue is actually complete
+ // Since a call to render may not be synched with the completions
+ // of the RenderViewJobs
+ // In such a case we return early, waiting for a next call with
+ // the frame queue complete at this point
+
+ // RenderQueue is complete (but that means it may be of size 0)
+ if (canSubmit && (queueIsComplete && !queueIsEmpty)) {
+ const QVector<Render::RenderView *> renderViews = m_renderQueue->nextFrameQueue();
+ QTaskLogger submissionStatsPart1(m_services->systemInformation(),
+ {JobTypes::FrameSubmissionPart1, 0},
+ QTaskLogger::Submission);
+ QTaskLogger submissionStatsPart2(m_services->systemInformation(),
+ {JobTypes::FrameSubmissionPart2, 0},
+ QTaskLogger::Submission);
+
+ 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_submissionContext->setCurrentStateSet(nullptr);
+ beganDrawing = m_submissionContext->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;
+
+ // Purge shader which aren't used any longer
+ static int callCount = 0;
+ ++callCount;
+ const int shaderPurgePeriod = 600;
+ if (callCount % shaderPurgePeriod == 0)
+ m_glResourceManagers->glShaderManager()->purge();
+ }
+ }
+ }
+ // 2) Proceed to next frame and start preparing frame n + 1
+ m_renderQueue->reset();
+ locker.unlock(); // Done protecting RenderQueue
+ m_vsyncFrameAdvanceService->proceedToNextFrame();
+ hasCleanedQueueAndProceeded = true;
+
+ // 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) {
+ submissionStatsPart1.end(submissionStatsPart2.restart());
+
+ // 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();
+ }
+ }
+
+ // Execute the pending shell commands
+ m_commandExecuter->performAsynchronousCommandExecution(renderViews);
+
+ // Delete all the RenderViews which will clear the allocators
+ // that were used for their allocation
+ qDeleteAll(renderViews);
+ }
+
+ // If hasCleanedQueueAndProceeded isn't true this implies that something went wrong
+ // with the rendering and/or the renderqueue is incomplete from some reason
+ // or alternatively it could be complete but empty (RenderQueue of size 0)
+
+ if (!hasCleanedQueueAndProceeded) {
+ // 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
+ const bool swapBuffers = submissionData.lastBoundFBOId == m_submissionContext->defaultFBO()
+ && surfaceLock.isSurfaceValid()
+ && m_shouldSwapBuffers;
+ m_submissionContext->endDrawing(swapBuffers);
+ }
+}
+
+// Called by RenderViewJobs
+// When the frameQueue is complete and we are using a renderThread
+// we allow the render thread to proceed
+void Renderer::enqueueRenderView(Render::RenderView *renderView, int submitOrder)
+{
+ QMutexLocker locker(m_renderQueue->mutex()); // Prevent out of order execution
+ // We cannot use a lock free primitive here because:
+ // - QVector is not thread safe
+ // - Even if the insert is made correctly, the isFrameComplete call
+ // could be invalid since depending on the order of execution
+ // the counter could be complete but the renderview not yet added to the
+ // buffer depending on whichever order the cpu decides to process this
+ const bool isQueueComplete = m_renderQueue->queueRenderView(renderView, submitOrder);
+ locker.unlock(); // We're done protecting the queue at this point
+ if (isQueueComplete) {
+ if (m_renderThread && m_running.loadRelaxed())
+ 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.loadRelaxed()) {
+ 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()
+{
+ // Make sure that we've been told to render before rendering
+ // Prevent ouf of order execution
+ m_submitRenderViewsSemaphore.acquire(1);
+
+ // Check if shutdown has been requested
+ if (m_running.loadRelaxed() == 0)
+ return false;
+
+ // 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
+QVariant Renderer::executeCommand(const QStringList &args)
+{
+ return m_commandExecuter->executeCommand(args);
+}
+
+/*!
+ \internal
+ Called in the context of the aspect thread from QRenderAspect::onRegistered
+*/
+void Renderer::setOffscreenSurfaceHelper(OffscreenSurfaceHelper *helper)
+{
+ QMutexLocker locker(&m_offscreenSurfaceMutex);
+ m_offscreenHelper = helper;
+}
+
+QSurfaceFormat Renderer::format()
+{
+ return m_format;
+}
+
+// When this function is called, we must not be processing the commands for frame n+1
+void Renderer::prepareCommandsSubmission(const QVector<RenderView *> &renderViews)
+{
+ OpenGLVertexArrayObject *vao = nullptr;
+ QHash<HVao, bool> updatedTable;
+
+ for (RenderView *rv: renderViews) {
+ QVector<RenderCommand> &commands = rv->commands();
+ for (RenderCommand &command : commands) {
+ // Update/Create VAO
+ if (command.m_type == RenderCommand::Draw) {
+ Geometry *rGeometry = m_nodesManager->data<Geometry, GeometryManager>(command.m_geometry);
+ GeometryRenderer *rGeometryRenderer = m_nodesManager->data<GeometryRenderer, GeometryRendererManager>(command.m_geometryRenderer);
+ GLShader *shader = command.m_glShader;
+
+ // We should never have inserted a command for which these are null
+ // in the first place
+ Q_ASSERT(rGeometry && rGeometryRenderer && shader);
+
+ // The VAO should be created only once for a QGeometry and a ShaderProgram
+ // Manager should have a VAO Manager that are indexed by QMeshData and Shader
+ // RenderCommand should have a handle to the corresponding VAO for the Mesh and Shader
+ HVao vaoHandle;
+
+ // Create VAO or return already created instance associated with command shader/geometry
+ // (VAO is emulated if not supported)
+ createOrUpdateVAO(&command, &vaoHandle, &vao);
+ command.m_vao = vaoHandle;
+
+ // Avoids redoing the same thing for the same VAO
+ if (!updatedTable.contains(vaoHandle)) {
+ updatedTable.insert(vaoHandle, true);
+
+ // Do we have any attributes that are dirty ?
+ const bool requiresPartialVAOUpdate = requiresVAOAttributeUpdate(rGeometry, &command);
+
+ // If true, we need to reupload all attributes to set the VAO
+ // Otherwise only dirty attributes will be updates
+ const bool requiresFullVAOUpdate = (!vao->isSpecified()) || (rGeometry->isDirty() || rGeometryRenderer->isDirty());
+
+ // Append dirty Geometry to temporary vector
+ // so that its dirtiness can be unset later
+ if (rGeometry->isDirty())
+ m_dirtyGeometry.push_back(rGeometry);
+
+ if (!command.m_activeAttributes.isEmpty() && (requiresFullVAOUpdate || requiresPartialVAOUpdate)) {
+ Profiling::GLTimeRecorder recorder(Profiling::VAOUpload);
+ // Activate shader
+ m_submissionContext->activateShader(shader);
+ // Bind VAO
+ vao->bind();
+ // Update or set Attributes and Buffers for the given rGeometry and Command
+ // Note: this fills m_dirtyAttributes as well
+ if (updateVAOWithAttributes(rGeometry, &command, shader, requiresFullVAOUpdate))
+ vao->setSpecified(true);
+ }
+ }
+
+ // Unset dirtiness on rGeometryRenderer only
+ // The rGeometry may be shared by several rGeometryRenderer
+ // so we cannot unset its dirtiness at this point
+ if (rGeometryRenderer->isDirty())
+ rGeometryRenderer->unsetDirty();
+
+ // Prepare the ShaderParameterPack based on the active uniforms of the shader
+ shader->prepareUniforms(command.m_parameterPack);
+
+ } else if (command.m_type == RenderCommand::Compute) {
+ GLShader *shader = command.m_glShader;
+ Q_ASSERT(shader);
+
+ // Prepare the ShaderParameterPack based on the active uniforms of the shader
+ shader->prepareUniforms(command.m_parameterPack);
+ }
+ }
+ }
+
+ // Make sure we leave nothing bound
+ if (vao)
+ vao->release();
+
+ // Unset dirtiness on Geometry and Attributes
+ // Note: we cannot do it in the loop above as we want to be sure that all
+ // the VAO which reference the geometry/attributes are properly updated
+ for (Attribute *attribute : qAsConst(m_dirtyAttributes))
+ attribute->unsetDirty();
+ m_dirtyAttributes.clear();
+
+ for (Geometry *geometry : qAsConst(m_dirtyGeometry))
+ geometry->unsetDirty();
+ m_dirtyGeometry.clear();
+}
+
+// Executed in a job
+void Renderer::lookForAbandonedVaos()
+{
+ const QVector<HVao> activeVaos = m_glResourceManagers->vaoManager()->activeHandles();
+ for (HVao handle : activeVaos) {
+ OpenGLVertexArrayObject *vao = m_glResourceManagers->vaoManager()->data(handle);
+
+ // Make sure to only mark VAOs for deletion that were already created
+ // (ignore those that might be currently under construction in the render thread)
+ if (vao && vao->isAbandoned(m_nodesManager->geometryManager(), m_glResourceManagers->glShaderManager())) {
+ m_abandonedVaosMutex.lock();
+ m_abandonedVaos.push_back(handle);
+ m_abandonedVaosMutex.unlock();
+ }
+ }
+}
+
+// Executed in a job
+void Renderer::lookForDirtyBuffers()
+{
+ const QVector<HBuffer> activeBufferHandles = m_nodesManager->bufferManager()->activeHandles();
+ for (const HBuffer &handle: activeBufferHandles) {
+ Buffer *buffer = m_nodesManager->bufferManager()->data(handle);
+ if (buffer->isDirty())
+ m_dirtyBuffers.push_back(handle);
+ }
+}
+
+// Called in prepareSubmission
+void Renderer::lookForDownloadableBuffers()
+{
+ m_downloadableBuffers.clear();
+ const QVector<HBuffer> activeBufferHandles = m_nodesManager->bufferManager()->activeHandles();
+ for (const HBuffer &handle : activeBufferHandles) {
+ Buffer *buffer = m_nodesManager->bufferManager()->data(handle);
+ if (buffer->access() & QBuffer::Read)
+ m_downloadableBuffers.push_back(buffer->peerId());
+ }
+}
+
+// Executed in a job
+void Renderer::lookForDirtyTextures()
+{
+ // To avoid having Texture or TextureImage maintain relationships between
+ // one another, we instead perform a lookup here to check if a texture
+ // image has been updated to then notify textures referencing the image
+ // that they need to be updated
+ TextureImageManager *imageManager = m_nodesManager->textureImageManager();
+ const QVector<HTextureImage> activeTextureImageHandles = imageManager->activeHandles();
+ Qt3DCore::QNodeIdVector dirtyImageIds;
+ for (const HTextureImage &handle: activeTextureImageHandles) {
+ TextureImage *image = imageManager->data(handle);
+ if (image->isDirty()) {
+ dirtyImageIds.push_back(image->peerId());
+ image->unsetDirty();
+ }
+ }
+
+ TextureManager *textureManager = m_nodesManager->textureManager();
+ const QVector<HTexture> activeTextureHandles = textureManager->activeHandles();
+ for (const HTexture &handle: activeTextureHandles) {
+ Texture *texture = textureManager->data(handle);
+ const QNodeIdVector imageIds = texture->textureImageIds();
+
+ // Does the texture reference any of the dirty texture images?
+ for (const QNodeId imageId: imageIds) {
+ if (dirtyImageIds.contains(imageId)) {
+ texture->addDirtyFlag(Texture::DirtyImageGenerators);
+ break;
+ }
+ }
+
+ // Dirty meaning that something has changed on the texture
+ // either properties, parameters, shared texture id, generator or a texture image
+ if (texture->dirtyFlags() != Texture::NotDirty)
+ m_dirtyTextures.push_back(handle);
+ // Note: texture dirty flags are reset when actually updating the
+ // textures in updateGLResources() as resetting flags here would make
+ // us lose information about what was dirty exactly.
+ }
+}
+
+// Executed in a job
+void Renderer::reloadDirtyShaders()
+{
+ Q_ASSERT(isRunning());
+ const QVector<HTechnique> activeTechniques = m_nodesManager->techniqueManager()->activeHandles();
+ const QVector<HShaderBuilder> activeBuilders = m_nodesManager->shaderBuilderManager()->activeHandles();
+ for (const HTechnique &techniqueHandle : activeTechniques) {
+ Technique *technique = m_nodesManager->techniqueManager()->data(techniqueHandle);
+ // If api of the renderer matches the one from the technique
+ if (technique->isCompatibleWithRenderer()) {
+ const auto passIds = technique->renderPasses();
+ for (const QNodeId &passId : passIds) {
+ RenderPass *renderPass = m_nodesManager->renderPassManager()->lookupResource(passId);
+ HShader shaderHandle = m_nodesManager->shaderManager()->lookupHandle(renderPass->shaderProgram());
+ Shader *shader = m_nodesManager->shaderManager()->data(shaderHandle);
+
+ ShaderBuilder *shaderBuilder = nullptr;
+ for (const HShaderBuilder &builderHandle : activeBuilders) {
+ ShaderBuilder *builder = m_nodesManager->shaderBuilderManager()->data(builderHandle);
+ if (builder->shaderProgramId() == shader->peerId()) {
+ shaderBuilder = builder;
+ break;
+ }
+ }
+
+ if (shaderBuilder) {
+ shaderBuilder->setGraphicsApi(*technique->graphicsApiFilter());
+
+ for (int i = 0; i <= QShaderProgram::Compute; i++) {
+ const auto shaderType = static_cast<QShaderProgram::ShaderType>(i);
+ if (!shaderBuilder->shaderGraph(shaderType).isValid())
+ continue;
+
+ if (shaderBuilder->isShaderCodeDirty(shaderType)) {
+ shaderBuilder->generateCode(shaderType);
+ m_shaderBuilderUpdates.append(shaderBuilder->takePendingUpdates());
+ }
+
+ const auto code = shaderBuilder->shaderCode(shaderType);
+ shader->setShaderCode(shaderType, code);
+ }
+ }
+
+ if (shader != nullptr && shader->isDirty())
+ loadShader(shader, shaderHandle);
+ }
+ }
+ }
+}
+
+// Executed in job (in main thread when jobs are done)
+void Renderer::sendShaderChangesToFrontend(Qt3DCore::QAspectManager *manager)
+{
+ Q_ASSERT(isRunning());
+
+ // Sync Shader
+ const QVector<HShader> activeShaders = m_nodesManager->shaderManager()->activeHandles();
+ for (const HShader &handle :activeShaders) {
+ Shader *s = m_nodesManager->shaderManager()->data(handle);
+ if (s->requiresFrontendSync()) {
+ QShaderProgram *frontend = static_cast<decltype(frontend)>(manager->lookupNode(s->peerId()));
+ QShaderProgramPrivate *dFrontend = static_cast<decltype(dFrontend)>(QNodePrivate::get(frontend));
+ s->unsetRequiresFrontendSync();
+ dFrontend->setStatus(s->status());
+ dFrontend->setLog(s->log());
+ }
+ }
+
+ // Sync ShaderBuilder
+ const QVector<ShaderBuilderUpdate> shaderBuilderUpdates = std::move(m_shaderBuilderUpdates);
+ for (const ShaderBuilderUpdate &update : shaderBuilderUpdates) {
+ QShaderProgramBuilder *builder = static_cast<decltype(builder)>(manager->lookupNode(update.builderId));
+ QShaderProgramBuilderPrivate *dBuilder = static_cast<decltype(dBuilder)>(QNodePrivate::get(builder));
+ dBuilder->setShaderCode(update.shaderCode, update.shaderType);
+ }
+}
+
+// Executed in a job (in main thread when jobs are done)
+void Renderer::sendTextureChangesToFrontend(Qt3DCore::QAspectManager *manager)
+{
+ const QVector<QPair<Texture::TextureUpdateInfo, Qt3DCore::QNodeIdVector>> updateTextureProperties = std::move(m_updatedTextureProperties);
+ for (const auto &pair : updateTextureProperties) {
+ const Qt3DCore::QNodeIdVector targetIds = pair.second;
+ for (const Qt3DCore::QNodeId &targetId: targetIds) {
+ // Lookup texture
+ Texture *t = m_nodesManager->textureManager()->lookupResource(targetId);
+ // If backend texture is Dirty, some property has changed and the properties we are
+ // about to send are already outdate
+ if (t == nullptr || t->dirtyFlags() != Texture::NotDirty)
+ continue;
+
+ QAbstractTexture *texture = static_cast<QAbstractTexture *>(manager->lookupNode(targetId));
+ if (!texture)
+ continue;
+ const TextureProperties &properties = pair.first.properties;
+
+ const bool blocked = texture->blockNotifications(true);
+ texture->setWidth(properties.width);
+ texture->setHeight(properties.height);
+ texture->setDepth(properties.depth);
+ texture->setLayers(properties.layers);
+ texture->setFormat(properties.format);
+ texture->blockNotifications(blocked);
+
+ QAbstractTexturePrivate *dTexture = static_cast<QAbstractTexturePrivate *>(QNodePrivate::get(texture));
+
+ dTexture->setStatus(properties.status);
+ dTexture->setHandleType(pair.first.handleType);
+ dTexture->setHandle(pair.first.handle);
+ }
+ }
+}
+
+// Executed in a job
+void Renderer::sendSetFenceHandlesToFrontend()
+{
+ const QVector<QPair<Qt3DCore::QNodeId, GLFence>> updatedSetFence = std::move(m_updatedSetFences);
+ FrameGraphManager *fgManager = m_nodesManager->frameGraphManager();
+ for (const auto &pair : updatedSetFence) {
+ FrameGraphNode *fgNode = fgManager->lookupNode(pair.first);
+ if (fgNode != nullptr) { // Node could have been deleted before we got a chance to notify it
+ Q_ASSERT(fgNode->nodeType() == FrameGraphNode::SetFence);
+ SetFence *setFenceNode = static_cast<SetFence *>(fgNode);
+ setFenceNode->setHandleType(QSetFence::OpenGLFenceId);
+ setFenceNode->setHandle(QVariant::fromValue(pair.second));
+ }
+ }
+}
+
+// Executed in a job (in main thread when jobs done)
+void Renderer::sendDisablesToFrontend(Qt3DCore::QAspectManager *manager)
+{
+ // SubtreeEnabled
+ const auto updatedDisables = std::move(m_updatedDisableSubtreeEnablers);
+ for (const auto &nodeId : updatedDisables) {
+ QSubtreeEnabler *frontend = static_cast<decltype(frontend)>(manager->lookupNode(nodeId));
+ frontend->setEnabled(false);
+ }
+
+ // Compute Commands
+ const QVector<HComputeCommand> activeCommands = m_nodesManager->computeJobManager()->activeHandles();
+ for (const HComputeCommand &handle :activeCommands) {
+ ComputeCommand *c = m_nodesManager->computeJobManager()->data(handle);
+ if (c->hasReachedFrameCount()) {
+ QComputeCommand *frontend = static_cast<decltype(frontend)>(manager->lookupNode(c->peerId()));
+ frontend->setEnabled(false);
+ c->resetHasReachedFrameCount();
+ }
+ }
+}
+
+// Render Thread (or QtQuick RenderThread when using Scene3D)
+// Scene3D: When using Scene3D rendering, we can't assume that when
+// updateGLResources is called, the resource handles points to still existing
+// objects. This is because Scene3D calls doRender independently of whether all
+// jobs have completed or not which in turn calls proceedToNextFrame under some
+// conditions. Such conditions are usually met on startup to avoid deadlocks.
+// proceedToNextFrame triggers the syncChanges calls for the next frame, which
+// may contain destruction changes targeting resources. When the above
+// happens, this can result in the dirtyResource vectors containing handles of
+// objects that may already have been destroyed
+void Renderer::updateGLResources()
+{
+ {
+ // Update active fence objects:
+ // - Destroy fences that have reached their signaled state
+ GLFenceManager *fenceManager = m_glResourceManagers->glFenceManager();
+ const auto end = fenceManager->end();
+ auto it = fenceManager->begin();
+ while (it != end) {
+ const GLFence fence = it.value();
+ if (m_submissionContext->wasSyncSignaled(fence)) {
+ // Fence was signaled, we delete it
+ // before removing the entry from the manager
+ m_submissionContext->deleteSync(fence);
+ it = fenceManager->erase(it);
+ } else {
+ ++it;
+ }
+ }
+ }
+
+ {
+ Profiling::GLTimeRecorder recorder(Profiling::BufferUpload);
+ const QVector<HBuffer> dirtyBufferHandles = std::move(m_dirtyBuffers);
+ for (const HBuffer &handle: dirtyBufferHandles) {
+ Buffer *buffer = m_nodesManager->bufferManager()->data(handle);
+
+ // Can be null when using Scene3D rendering
+ if (buffer == nullptr)
+ continue;
+
+ // Forces creation if it doesn't exit
+ // Also note the binding point doesn't really matter here, we just upload data
+ if (!m_submissionContext->hasGLBufferForBuffer(buffer))
+ m_submissionContext->glBufferForRenderBuffer(buffer);
+ // Update the glBuffer data
+ m_submissionContext->updateBuffer(buffer);
+ buffer->unsetDirty();
+ }
+ }
+
+#ifndef SHADER_LOADING_IN_COMMAND_THREAD
+ {
+ Profiling::GLTimeRecorder recorder(Profiling::ShaderUpload);
+ const QVector<HShader> dirtyShaderHandles = std::move(m_dirtyShaders);
+ ShaderManager *shaderManager = m_nodesManager->shaderManager();
+ for (const HShader &handle: dirtyShaderHandles) {
+ Shader *shader = shaderManager->data(handle);
+
+ // Can be null when using Scene3D rendering
+ if (shader == nullptr)
+ continue;
+
+ // Compile shader
+ m_submissionContext->loadShader(shader, shaderManager, m_glResourceManagers->glShaderManager());
+ }
+ }
+#endif
+
+ {
+ Profiling::GLTimeRecorder recorder(Profiling::TextureUpload);
+ const QVector<HTexture> activeTextureHandles = std::move(m_dirtyTextures);
+ for (const HTexture &handle: activeTextureHandles) {
+ Texture *texture = m_nodesManager->textureManager()->data(handle);
+
+ // Can be null when using Scene3D rendering
+ if (texture == nullptr)
+ continue;
+
+ // Create or Update GLTexture (the GLTexture instance is created if required
+ // and all things that can take place without a GL context are done here)
+ updateTexture(texture);
+ }
+ // We want to upload textures data at this point as the SubmissionThread and
+ // AspectThread are locked ensuring no races between Texture/TextureImage and
+ // GLTexture
+ if (m_submissionContext != nullptr) {
+ GLTextureManager *glTextureManager = m_glResourceManagers->glTextureManager();
+ const QVector<HGLTexture> glTextureHandles = glTextureManager->activeHandles();
+ // Upload texture data
+ for (const HGLTexture &glTextureHandle : glTextureHandles) {
+ GLTexture *glTexture = glTextureManager->data(glTextureHandle);
+
+ // We create/update the actual GL texture using the GL context at this point
+ const GLTexture::TextureUpdateInfo info = glTexture->createOrUpdateGLTexture();
+
+ // GLTexture creation provides us width/height/format ... information
+ // for textures which had not initially specified these information (TargetAutomatic...)
+ // Gather these information and store them to be distributed by a change next frame
+ const QNodeIdVector referenceTextureIds = { glTextureManager->texNodeIdForGLTexture.value(glTexture) };
+ // Store properties and referenceTextureIds
+ if (info.wasUpdated) {
+ Texture::TextureUpdateInfo updateInfo;
+ updateInfo.properties = info.properties;
+ updateInfo.handleType = QAbstractTexture::OpenGLTextureId;
+ updateInfo.handle = info.texture ? QVariant(info.texture->textureId()) : QVariant();
+ m_updatedTextureProperties.push_back({updateInfo, referenceTextureIds});
+ }
+ }
+ }
+
+ // Record ids of texture to cleanup while we are still blocking the aspect thread
+ m_textureIdsToCleanup += m_nodesManager->textureManager()->takeTexturesIdsToCleanup();
+ }
+
+ // Record list of buffer that might need uploading
+ lookForDownloadableBuffers();
+}
+
+// Render Thread
+void Renderer::updateTexture(Texture *texture)
+{
+ // Check that the current texture images are still in place, if not, do not update
+ const bool isValid = texture->isValid(m_nodesManager->textureImageManager());
+ if (!isValid) {
+ qWarning() << Q_FUNC_INFO << "QTexture referencing invalid QTextureImages";
+ return;
+ }
+
+ // All textures are unique, if you instanciate twice the exact same texture
+ // this will create 2 identical GLTextures, no sharing will take place
+
+ // Try to find the associated GLTexture for the backend Texture
+ GLTextureManager *glTextureManager = m_glResourceManagers->glTextureManager();
+ GLTexture *glTexture = glTextureManager->lookupResource(texture->peerId());
+
+ // No GLTexture associated yet -> create it
+ if (glTexture == nullptr) {
+ glTexture = glTextureManager->getOrCreateResource(texture->peerId());
+ glTextureManager->texNodeIdForGLTexture.insert(glTexture, texture->peerId());
+ }
+
+ // Update GLTexture to match Texture instance
+ const Texture::DirtyFlags dirtyFlags = texture->dirtyFlags();
+ if (dirtyFlags.testFlag(Texture::DirtySharedTextureId))
+ glTexture->setSharedTextureId(texture->sharedTextureId());
+
+ if (dirtyFlags.testFlag(Texture::DirtyProperties))
+ glTexture->setProperties(texture->properties());
+
+ if (dirtyFlags.testFlag(Texture::DirtyParameters))
+ glTexture->setParameters(texture->parameters());
+
+ // Will make the texture requestUpload
+ if (dirtyFlags.testFlag(Texture::DirtyImageGenerators)) {
+ const QNodeIdVector textureImageIds = texture->textureImageIds();
+ QVector<GLTexture::Image> images;
+ images.reserve(textureImageIds.size());
+ // TODO: Move this into GLTexture directly
+ for (const QNodeId textureImageId : textureImageIds) {
+ const TextureImage *img = m_nodesManager->textureImageManager()->lookupResource(textureImageId);
+ if (img == nullptr) {
+ qWarning() << Q_FUNC_INFO << "invalid TextureImage handle";
+ } else {
+ GLTexture::Image glImg {img->dataGenerator(), img->layer(), img->mipLevel(), img->face()};
+ images.push_back(glImg);
+ }
+ }
+ glTexture->setImages(images);
+ }
+
+ // Will make the texture requestUpload
+ if (dirtyFlags.testFlag(Texture::DirtyDataGenerator))
+ glTexture->setGenerator(texture->dataGenerator());
+
+ // Will make the texture requestUpload
+ if (dirtyFlags.testFlag(Texture::DirtyPendingDataUpdates))
+ glTexture->addTextureDataUpdates(texture->takePendingTextureDataUpdates());
+
+ // Unset the dirty flag on the texture
+ texture->unsetDirty();
+}
+
+// Render Thread
+void Renderer::cleanupTexture(Qt3DCore::QNodeId cleanedUpTextureId)
+{
+ GLTextureManager *glTextureManager = m_glResourceManagers->glTextureManager();
+ GLTexture *glTexture = glTextureManager->lookupResource(cleanedUpTextureId);
+
+ // Destroying the GLTexture implicitely also destroy the GL resources
+ if (glTexture != nullptr) {
+ glTextureManager->releaseResource(cleanedUpTextureId);
+ glTextureManager->texNodeIdForGLTexture.remove(glTexture);
+ }
+}
+
+// Render Thread
+void Renderer::cleanupShader(const Shader *shader)
+{
+ GLShaderManager *glShaderManager = m_glResourceManagers->glShaderManager();
+ GLShader *glShader = glShaderManager->lookupResource(shader->peerId());
+
+ if (glShader != nullptr)
+ glShaderManager->abandon(glShader, shader);
+}
+
+// Called by SubmitRenderView
+void Renderer::downloadGLBuffers()
+{
+ const QVector<Qt3DCore::QNodeId> downloadableHandles = std::move(m_downloadableBuffers);
+ for (const Qt3DCore::QNodeId &bufferId : downloadableHandles) {
+ BufferManager *bufferManager = m_nodesManager->bufferManager();
+ BufferManager::ReadLocker locker(const_cast<const BufferManager *>(bufferManager));
+ Buffer *buffer = bufferManager->lookupResource(bufferId);
+ // Buffer could have been destroyed at this point
+ if (!buffer)
+ continue;
+ // locker is protecting us from the buffer being destroy while we're looking
+ // up its content
+ const QByteArray content = m_submissionContext->downloadBufferContent(buffer);
+ m_sendBufferCaptureJob->addRequest(QPair<Qt3DCore::QNodeId, QByteArray>(bufferId, content));
+ }
+}
+
+// Happens in RenderThread context when all RenderViewJobs are done
+// Returns the id of the last bound FBO
+Renderer::ViewSubmissionResultData Renderer::submitRenderViews(const QVector<Render::RenderView *> &renderViews)
+{
+ QElapsedTimer timer;
+ quint64 queueElapsed = 0;
+ timer.start();
+
+ const int renderViewsCount = renderViews.size();
+ quint64 frameElapsed = queueElapsed;
+ m_lastFrameCorrect.storeRelaxed(1); // everything fine until now.....
+
+ qCDebug(Memory) << Q_FUNC_INFO << "rendering frame ";
+
+ // We might not want to render on the default FBO
+ uint lastBoundFBOId = m_submissionContext->boundFrameBufferObject();
+ QSurface *surface = nullptr;
+ QSurface *previousSurface = nullptr;
+ for (const Render::RenderView *rv: renderViews) {
+ previousSurface = rv->surface();
+ if (previousSurface)
+ break;
+ }
+ QSurface *lastUsedSurface = nullptr;
+
+ for (int i = 0; i < renderViewsCount; ++i) {
+ // Initialize GraphicsContext for drawing
+ // If the RenderView has a RenderStateSet defined
+ const RenderView *renderView = renderViews.at(i);
+
+ // Check if using the same surface as the previous RenderView.
+ // 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
+
+ // For now, if we do not have a surface, skip this renderview
+ // 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()) {
+ m_lastFrameCorrect.storeRelaxed(0);
+ continue;
+ }
+
+ lastUsedSurface = surface;
+ const bool surfaceHasChanged = surface != previousSurface;
+
+ if (surfaceHasChanged && previousSurface) {
+ const bool swapBuffers = lastBoundFBOId == m_submissionContext->defaultFBO()
+ && surfaceLock.isSurfaceValid()
+ && m_shouldSwapBuffers;
+ // We only call swap buffer if we are sure the previous surface is still valid
+ m_submissionContext->endDrawing(swapBuffers);
+ }
+
+ 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_submissionContext->beginDrawing(surface)) {
+ qWarning() << "Failed to make OpenGL context current on surface";
+ m_lastFrameCorrect.storeRelaxed(0);
+ continue;
+ }
+
+ previousSurface = surface;
+ lastBoundFBOId = m_submissionContext->boundFrameBufferObject();
+ }
+
+ // Apply Memory Barrier if needed
+ if (renderView->memoryBarrier() != QMemoryBarrier::None)
+ m_submissionContext->memoryBarrier(renderView->memoryBarrier());
+
+
+ // Insert Fence into command stream if needed
+ const Qt3DCore::QNodeIdVector insertFenceIds = renderView->insertFenceIds();
+ GLFenceManager *fenceManager = m_glResourceManagers->glFenceManager();
+ for (const Qt3DCore::QNodeId insertFenceId : insertFenceIds) {
+ // If the fence is not in the manager, then it hasn't been inserted
+ // into the command stream yet.
+ if (fenceManager->find(insertFenceId) == fenceManager->end()) {
+ // Insert fence into command stream
+ GLFence glFence = m_submissionContext->fenceSync();
+ // Record glFence
+ fenceManager->insert(insertFenceId, glFence);
+ // Add entry for notification changes to be sent
+ m_updatedSetFences.push_back({insertFenceId, glFence});
+ }
+ // If it is in the manager, then it hasn't been signaled yet,
+ // nothing we can do but try at the next frame
+ }
+
+ // Wait for fences if needed
+ const QVector<QWaitFenceData> waitFences = renderView->waitFences();
+ for (const QWaitFenceData &waitFence : waitFences) {
+ // TO DO
+ if (waitFence.handleType != QWaitFence::OpenGLFenceId) {
+ qWarning() << "WaitFence handleType should be OpenGLFenceId when using the Qt 3D OpenGL renderer";
+ continue;
+ }
+ GLFence fence = reinterpret_cast<GLFence>(waitFence.handle.value<qintptr>());
+ if (fence == nullptr)
+ continue;
+
+ if (waitFence.waitOnCPU) {
+ m_submissionContext->clientWaitSync(fence,
+ waitFence.timeout);
+ } else {
+ m_submissionContext->waitSync(fence);
+ }
+ }
+
+ // Note: the RenderStateSet is allocated once per RV if needed
+ // and it contains a list of StateVariant value types
+ RenderStateSet *renderViewStateSet = renderView->stateSet();
+
+ {
+ Profiling::GLTimeRecorder recorder(Profiling::StateUpdate);
+ // Set the RV state if not null,
+ if (renderViewStateSet != nullptr)
+ m_submissionContext->setCurrentStateSet(renderViewStateSet);
+ else
+ m_submissionContext->setCurrentStateSet(m_defaultRenderStateSet);
+ }
+
+ // Set RenderTarget ...
+ // Activate RenderTarget
+ {
+ Profiling::GLTimeRecorder recorder(Profiling::RenderTargetUpdate);
+ m_submissionContext->activateRenderTarget(renderView->renderTargetId(),
+ renderView->attachmentPack(),
+ lastBoundFBOId);
+ }
+
+ {
+ Profiling::GLTimeRecorder recorder(Profiling::ClearBuffer);
+ // set color, depth, stencil clear values (only if needed)
+ auto clearBufferTypes = renderView->clearTypes();
+ if (clearBufferTypes & QClearBuffers::ColorBuffer) {
+ const QVector4D vCol = renderView->globalClearColorBufferInfo().clearColor;
+ m_submissionContext->clearColor(QColor::fromRgbF(vCol.x(), vCol.y(), vCol.z(), vCol.w()));
+ }
+ if (clearBufferTypes & QClearBuffers::DepthBuffer)
+ m_submissionContext->clearDepthValue(renderView->clearDepthValue());
+ if (clearBufferTypes & QClearBuffers::StencilBuffer)
+ m_submissionContext->clearStencilValue(renderView->clearStencilValue());
+
+ // Clear BackBuffer
+ m_submissionContext->clearBackBuffer(clearBufferTypes);
+
+ // if there are ClearColors set for different draw buffers,
+ // clear each of these draw buffers individually now
+ const QVector<ClearBufferInfo> clearDrawBuffers = renderView->specificClearColorBufferInfo();
+ for (const ClearBufferInfo &clearBuffer : clearDrawBuffers)
+ m_submissionContext->clearBufferf(clearBuffer.drawBufferIndex, clearBuffer.clearColor);
+ }
+
+ // Set the Viewport
+ m_submissionContext->setViewport(renderView->viewport(), renderView->surfaceSize());
+
+ // Execute the render commands
+ if (!executeCommandsSubmission(renderView))
+ m_lastFrameCorrect.storeRelaxed(0); // something went wrong; make sure to render the next frame!
+
+ // executeCommandsSubmission takes care of restoring the stateset to the value
+ // of gc->currentContext() at the moment it was called (either
+ // renderViewStateSet or m_defaultRenderStateSet)
+ if (!renderView->renderCaptureNodeId().isNull()) {
+ const QRenderCaptureRequest request = renderView->renderCaptureRequest();
+ const QSize size = m_submissionContext->renderTargetSize(renderView->surfaceSize());
+ QRect rect(QPoint(0, 0), size);
+ if (!request.rect.isEmpty())
+ rect = rect.intersected(request.rect);
+ QImage image;
+ if (!rect.isEmpty()) {
+ // Bind fbo as read framebuffer
+ m_submissionContext->bindFramebuffer(m_submissionContext->activeFBO(), GraphicsHelperInterface::FBORead);
+ image = m_submissionContext->readFramebuffer(rect);
+ } else {
+ qWarning() << "Requested capture rectangle is outside framebuffer";
+ }
+ Render::RenderCapture *renderCapture =
+ static_cast<Render::RenderCapture*>(m_nodesManager->frameGraphManager()->lookupNode(renderView->renderCaptureNodeId()));
+ renderCapture->addRenderCapture(request.captureId, image);
+ if (!m_pendingRenderCaptureSendRequests.contains(renderView->renderCaptureNodeId()))
+ m_pendingRenderCaptureSendRequests.push_back(renderView->renderCaptureNodeId());
+ }
+
+ if (renderView->isDownloadBuffersEnable())
+ downloadGLBuffers();
+
+ // Perform BlitFramebuffer operations
+ if (renderView->hasBlitFramebufferInfo()) {
+ const auto &blitFramebufferInfo = renderView->blitFrameBufferInfo();
+ const QNodeId inputTargetId = blitFramebufferInfo.sourceRenderTargetId;
+ const QNodeId outputTargetId = blitFramebufferInfo.destinationRenderTargetId;
+ const QRect inputRect = blitFramebufferInfo.sourceRect;
+ const QRect outputRect = blitFramebufferInfo.destinationRect;
+ const QRenderTargetOutput::AttachmentPoint inputAttachmentPoint = blitFramebufferInfo.sourceAttachmentPoint;
+ const QRenderTargetOutput::AttachmentPoint outputAttachmentPoint = blitFramebufferInfo.destinationAttachmentPoint;
+ const QBlitFramebuffer::InterpolationMethod interpolationMethod = blitFramebufferInfo.interpolationMethod;
+ m_submissionContext->blitFramebuffer(inputTargetId, outputTargetId, inputRect, outputRect, lastBoundFBOId,
+ inputAttachmentPoint, outputAttachmentPoint,
+ interpolationMethod);
+ }
+
+
+ frameElapsed = timer.elapsed() - frameElapsed;
+ qCDebug(Rendering) << Q_FUNC_INFO << "Submitted Renderview " << i + 1 << "/" << renderViewsCount << "in " << frameElapsed << "ms";
+ frameElapsed = timer.elapsed();
+ }
+
+ // Bind lastBoundFBOId back. Needed also in threaded mode.
+ // lastBoundFBOId != m_graphicsContext->activeFBO() when the last FrameGraph leaf node/renderView
+ // contains RenderTargetSelector/RenderTarget
+ if (lastBoundFBOId != m_submissionContext->activeFBO())
+ m_submissionContext->bindFramebuffer(lastBoundFBOId, GraphicsHelperInterface::FBOReadAndDraw);
+
+ // Reset state and call doneCurrent if the surface
+ // is valid and was actually activated
+ if (lastUsedSurface && m_submissionContext->hasValidGLHelper()) {
+ // Reset state to the default state if the last stateset is not the
+ // defaultRenderStateSet
+ if (m_submissionContext->currentStateSet() != m_defaultRenderStateSet)
+ m_submissionContext->setCurrentStateSet(m_defaultRenderStateSet);
+ }
+
+ queueElapsed = timer.elapsed() - queueElapsed;
+ qCDebug(Rendering) << Q_FUNC_INFO << "Submission of Queue in " << queueElapsed << "ms <=> " << queueElapsed / renderViewsCount << "ms per RenderView <=> Avg " << 1000.0f / (queueElapsed * 1.0f/ renderViewsCount * 1.0f) << " RenderView/s";
+ qCDebug(Rendering) << Q_FUNC_INFO << "Submission Completed in " << timer.elapsed() << "ms";
+
+ // Stores the necessary information to safely perform
+ // the last swap buffer call
+ ViewSubmissionResultData resultData;
+ resultData.lastBoundFBOId = lastBoundFBOId;
+ resultData.surface = lastUsedSurface;
+ return resultData;
+}
+
+void Renderer::markDirty(BackendNodeDirtySet changes, BackendNode *node)
+{
+ Q_UNUSED(node)
+ m_dirtyBits.marked |= changes;
+}
+
+Renderer::BackendNodeDirtySet Renderer::dirtyBits()
+{
+ return m_dirtyBits.marked;
+}
+
+#if defined(QT_BUILD_INTERNAL)
+void Renderer::clearDirtyBits(BackendNodeDirtySet changes)
+{
+ m_dirtyBits.remaining &= ~changes;
+ m_dirtyBits.marked &= ~changes;
+}
+#endif
+
+bool Renderer::shouldRender() const
+{
+ // 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_lastFrameCorrect.loadRelaxed());
+}
+
+void Renderer::skipNextFrame()
+{
+ Q_ASSERT(m_settings->renderPolicy() != QRenderSettings::Always);
+
+ // make submitRenderViews() actually run
+ m_renderQueue->setNoRender();
+ m_submitRenderViewsSemaphore.release(1);
+}
+
+void Renderer::jobsDone(Qt3DCore::QAspectManager *manager)
+{
+ // called in main thread once all jobs are done running
+
+ // sync captured renders to frontend
+ const QVector<Qt3DCore::QNodeId> pendingCaptureIds = std::move(m_pendingRenderCaptureSendRequests);
+ for (const Qt3DCore::QNodeId &id : qAsConst(pendingCaptureIds)) {
+ auto *backend = static_cast<Qt3DRender::Render::RenderCapture *>
+ (m_nodesManager->frameGraphManager()->lookupNode(id));
+ backend->syncRenderCapturesToFrontend(manager);
+ }
+
+ // Do we need to notify any texture about property changes?
+ if (m_updatedTextureProperties.size() > 0)
+ sendTextureChangesToFrontend(manager);
+
+ sendDisablesToFrontend(manager);
+}
+
+// Jobs we may have to run even if no rendering will happen
+QVector<QAspectJobPtr> Renderer::preRenderingJobs()
+{
+ QVector<QAspectJobPtr> jobs;
+
+ // Do we need to notify frontend about fence change?
+ if (m_updatedSetFences.size() > 0)
+ jobs.push_back(m_sendSetFenceHandlesToFrontendJob);
+
+ if (m_sendBufferCaptureJob->hasRequests())
+ jobs.push_back(m_sendBufferCaptureJob);
+
+ jobs.append(pickBoundingVolumeJob());
+ jobs.append(rayCastingJob());
+
+ return jobs;
+}
+
+// Waits to be told to create jobs for the next frame
+// Called by QRenderAspect jobsToExecute context of QAspectThread
+// Returns all the jobs (and with proper dependency chain) required
+// for the rendering of the scene
+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);
+
+ m_updateLevelOfDetailJob->setFrameGraphRoot(frameGraphRoot());
+
+ const BackendNodeDirtySet dirtyBitsForFrame = m_dirtyBits.marked | m_dirtyBits.remaining;
+ m_dirtyBits.marked = {};
+ m_dirtyBits.remaining = {};
+ BackendNodeDirtySet notCleared = {};
+
+ // 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);
+ }
+
+ if (dirtyBitsForFrame & AbstractRenderer::TransformDirty) {
+ renderBinJobs.push_back(m_worldTransformJob);
+ renderBinJobs.push_back(m_updateWorldBoundingVolumeJob);
+ renderBinJobs.push_back(m_updateShaderDataTransformJob);
+ }
+
+ if (dirtyBitsForFrame & AbstractRenderer::GeometryDirty ||
+ dirtyBitsForFrame & AbstractRenderer::BuffersDirty) {
+ renderBinJobs.push_back(m_calculateBoundingVolumeJob);
+ renderBinJobs.push_back(m_updateMeshTriangleListJob);
+ }
+
+ if (dirtyBitsForFrame & AbstractRenderer::GeometryDirty ||
+ dirtyBitsForFrame & AbstractRenderer::TransformDirty) {
+ renderBinJobs.push_back(m_expandBoundingVolumeJob);
+ }
+
+ // TO DO: Conditionally add if skeletons dirty
+ renderBinJobs.push_back(m_syncLoadingJobs);
+ m_updateSkinningPaletteJob->setDirtyJoints(m_nodesManager->jointManager()->dirtyJoints());
+ renderBinJobs.push_back(m_updateSkinningPaletteJob);
+ renderBinJobs.push_back(m_updateLevelOfDetailJob);
+ renderBinJobs.push_back(m_cleanupJob);
+ 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_textureGathererJob);
+
+
+ // Layer cache is dependent on layers, layer filters (hence FG structure
+ // changes) and the enabled flag on entities
+ const bool frameGraphDirty = dirtyBitsForFrame & AbstractRenderer::FrameGraphDirty;
+ const bool layersDirty = dirtyBitsForFrame & AbstractRenderer::LayersDirty;
+ const bool layersCacheNeedsToBeRebuilt = layersDirty || entitiesEnabledDirty || frameGraphDirty;
+ const bool shadersDirty = dirtyBitsForFrame & AbstractRenderer::ShadersDirty;
+ const bool materialDirty = dirtyBitsForFrame & AbstractRenderer::MaterialDirty;
+ const bool lightsDirty = dirtyBitsForFrame & AbstractRenderer::LightsDirty;
+ const bool computeableDirty = dirtyBitsForFrame & AbstractRenderer::ComputeDirty;
+ const bool renderableDirty = dirtyBitsForFrame & AbstractRenderer::GeometryDirty;
+ const bool materialCacheNeedsToBeRebuilt = shadersDirty || materialDirty || frameGraphDirty;
+ const bool renderCommandsDirty = materialCacheNeedsToBeRebuilt || renderableDirty || computeableDirty;
+
+ // Rebuild Entity Layers list if layers are dirty
+ if (layersDirty)
+ renderBinJobs.push_back(m_updateEntityLayersJob);
+
+ if (renderableDirty) {
+ renderBinJobs.push_back(m_renderableEntityFilterJob);
+ renderBinJobs.push_back(m_cacheRenderableEntitiesJob);
+ }
+
+ if (computeableDirty) {
+ renderBinJobs.push_back(m_computableEntityFilterJob);
+ renderBinJobs.push_back(m_cacheComputableEntitiesJob);
+ }
+
+ if (lightsDirty) {
+ renderBinJobs.push_back(m_lightGathererJob);
+ renderBinJobs.push_back(m_cacheLightsJob);
+ }
+
+ 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
+ if (frameGraphDirty) {
+ FrameGraphVisitor visitor(m_nodesManager->frameGraphManager());
+ m_frameGraphLeaves = 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 (!m_frameGraphLeaves.contains(leafNode))
+ m_cache.leafNodeCache.remove(leafNode);
+ }
+
+ // Handle single shot subtree enablers
+ const auto subtreeEnablers = visitor.takeEnablersToDisable();
+ for (auto *node : subtreeEnablers)
+ m_updatedDisableSubtreeEnablers.push_back(node->peerId());
+ }
+
+ const int fgBranchCount = m_frameGraphLeaves.size();
+ for (int i = 0; i < fgBranchCount; ++i) {
+ FrameGraphNode *leaf = m_frameGraphLeaves.at(i);
+ RenderViewBuilder builder(leaf, i, this);
+ // If we have a new RV (wasn't in the cache before, then it contains no cached data)
+ const bool isNewRV = !m_cache.leafNodeCache.contains(leaf);
+ builder.setLayerCacheNeedsToBeRebuilt(layersCacheNeedsToBeRebuilt || isNewRV);
+ builder.setMaterialGathererCacheNeedsToBeRebuilt(materialCacheNeedsToBeRebuilt || isNewRV);
+ builder.setRenderCommandCacheNeedsToBeRebuilt(renderCommandsDirty || isNewRV);
+
+ 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::FrameGraphDirty;
+ notCleared |= AbstractRenderer::LayersDirty;
+ }
+
+ if (isRunning() && m_submissionContext->isInitialized()) {
+ if (dirtyBitsForFrame & AbstractRenderer::TechniquesDirty )
+ renderBinJobs.push_back(m_filterCompatibleTechniqueJob);
+ if (dirtyBitsForFrame & AbstractRenderer::ShadersDirty)
+ renderBinJobs.push_back(m_introspectShaderJob);
+ } else {
+ notCleared |= AbstractRenderer::TechniquesDirty;
+ notCleared |= AbstractRenderer::ShadersDirty;
+ }
+
+ m_dirtyBits.remaining = dirtyBitsForFrame & notCleared;
+
+ return renderBinJobs;
+}
+
+QAspectJobPtr Renderer::pickBoundingVolumeJob()
+{
+ // Set values on pickBoundingVolumeJob
+ RenderSettings *renderSetting = settings();
+ if (renderSetting != nullptr) {
+ m_pickBoundingVolumeJob->setRenderSettings(renderSetting);
+ m_pickBoundingVolumeJob->setFrameGraphRoot(frameGraphRoot());
+ m_pickBoundingVolumeJob->setMouseEvents(pendingPickingEvents());
+ m_pickBoundingVolumeJob->setKeyEvents(pendingKeyEvents());
+ }
+
+ return m_pickBoundingVolumeJob;
+}
+
+QAspectJobPtr Renderer::rayCastingJob()
+{
+ // Set values on rayCastingJob
+ RenderSettings *renderSetting = settings();
+ if (renderSetting != nullptr) {
+ m_rayCastingJob->setRenderSettings(renderSetting);
+ m_rayCastingJob->setFrameGraphRoot(frameGraphRoot());
+ }
+
+ return m_rayCastingJob;
+}
+
+QAspectJobPtr Renderer::syncLoadingJobs()
+{
+ return m_syncLoadingJobs;
+}
+
+QAspectJobPtr Renderer::expandBoundingVolumeJob()
+{
+ return m_expandBoundingVolumeJob;
+}
+
+QAbstractFrameAdvanceService *Renderer::frameAdvanceService() const
+{
+ return static_cast<Qt3DCore::QAbstractFrameAdvanceService *>(m_vsyncFrameAdvanceService.data());
+}
+
+// Called by executeCommands
+void Renderer::performDraw(RenderCommand *command)
+{
+ // Indirect Draw Calls
+ if (command->m_drawIndirect) {
+
+ // Bind the indirect draw buffer
+ Buffer *indirectDrawBuffer = m_nodesManager->bufferManager()->data(command->m_indirectDrawBuffer);
+ if (Q_UNLIKELY(indirectDrawBuffer == nullptr)) {
+ qWarning() << "Invalid Indirect Draw Buffer - failed to retrieve Buffer";
+ return;
+ }
+
+ // Get GLBuffer from Buffer;
+ GLBuffer *indirectDrawGLBuffer = m_submissionContext->glBufferForRenderBuffer(indirectDrawBuffer);
+ if (Q_UNLIKELY(indirectDrawGLBuffer == nullptr)) {
+ qWarning() << "Invalid Indirect Draw Buffer - failed to retrieve GLBuffer";
+ return;
+ }
+
+ // Bind GLBuffer
+ const bool successfullyBound = indirectDrawGLBuffer->bind(m_submissionContext.data(), GLBuffer::DrawIndirectBuffer);
+
+ if (Q_LIKELY(successfullyBound)) {
+ // TO DO: Handle multi draw variants if attribute count > 1
+ if (command->m_drawIndexed) {
+ m_submissionContext->drawElementsIndirect(command->m_primitiveType,
+ command->m_indexAttributeDataType,
+ reinterpret_cast<void*>(quintptr(command->m_indirectAttributeByteOffset)));
+ } else {
+ m_submissionContext->drawArraysIndirect(command->m_primitiveType,
+ reinterpret_cast<void*>(quintptr(command->m_indirectAttributeByteOffset)));
+ }
+ } else {
+ qWarning() << "Failed to bind IndirectDrawBuffer";
+ }
+
+ } else { // Direct Draw Calls
+
+ // TO DO: Add glMulti Draw variants
+ if (command->m_primitiveType == QGeometryRenderer::Patches)
+ m_submissionContext->setVerticesPerPatch(command->m_verticesPerPatch);
+
+ if (command->m_primitiveRestartEnabled)
+ m_submissionContext->enablePrimitiveRestart(command->m_restartIndexValue);
+
+ // TO DO: Add glMulti Draw variants
+ if (command->m_drawIndexed) {
+ Profiling::GLTimeRecorder recorder(Profiling::DrawElement);
+ m_submissionContext->drawElementsInstancedBaseVertexBaseInstance(command->m_primitiveType,
+ command->m_primitiveCount,
+ command->m_indexAttributeDataType,
+ reinterpret_cast<void*>(quintptr(command->m_indexAttributeByteOffset)),
+ command->m_instanceCount,
+ command->m_indexOffset,
+ command->m_firstInstance);
+ } else {
+ Profiling::GLTimeRecorder recorder(Profiling::DrawArray);
+ m_submissionContext->drawArraysInstancedBaseInstance(command->m_primitiveType,
+ command->m_firstVertex,
+ command->m_primitiveCount,
+ command->m_instanceCount,
+ command->m_firstInstance);
+ }
+ }
+
+#if defined(QT3D_RENDER_ASPECT_OPENGL_DEBUG)
+ int err = m_submissionContext->openGLContext()->functions()->glGetError();
+ if (err)
+ qCWarning(Rendering) << "GL error after drawing mesh:" << QString::number(err, 16);
+#endif
+
+ if (command->m_primitiveRestartEnabled)
+ m_submissionContext->disablePrimitiveRestart();
+}
+
+void Renderer::performCompute(const RenderView *, RenderCommand *command)
+{
+ {
+ Profiling::GLTimeRecorder recorder(Profiling::ShaderUpdate);
+ GLShader *shader = m_glResourceManagers->glShaderManager()->lookupResource(command->m_shaderId);
+ m_submissionContext->activateShader(shader);
+ }
+ {
+ Profiling::GLTimeRecorder recorder(Profiling::UniformUpdate);
+ m_submissionContext->setParameters(command->m_parameterPack);
+ }
+ {
+ Profiling::GLTimeRecorder recorder(Profiling::DispatchCompute);
+ m_submissionContext->dispatchCompute(command->m_workGroups[0],
+ command->m_workGroups[1],
+ command->m_workGroups[2]);
+ }
+ // HACK: Reset the compute flag to dirty
+ m_dirtyBits.marked |= AbstractRenderer::ComputeDirty;
+
+#if defined(QT3D_RENDER_ASPECT_OPENGL_DEBUG)
+ int err = m_submissionContext->openGLContext()->functions()->glGetError();
+ if (err)
+ qCWarning(Rendering) << "GL error after drawing mesh:" << QString::number(err, 16);
+#endif
+}
+
+void Renderer::createOrUpdateVAO(RenderCommand *command,
+ HVao *previousVaoHandle,
+ OpenGLVertexArrayObject **vao)
+{
+ const VAOIdentifier vaoKey(command->m_geometry, command->m_shaderId);
+
+ VAOManager *vaoManager = m_glResourceManagers->vaoManager();
+ command->m_vao = vaoManager->lookupHandle(vaoKey);
+
+ if (command->m_vao.isNull()) {
+ qCDebug(Rendering) << Q_FUNC_INFO << "Allocating new VAO";
+ command->m_vao = vaoManager->getOrAcquireHandle(vaoKey);
+ vaoManager->data(command->m_vao)->create(m_submissionContext.data(), vaoKey);
+ }
+
+ if (*previousVaoHandle != command->m_vao) {
+ *previousVaoHandle = command->m_vao;
+ *vao = vaoManager->data(command->m_vao);
+ }
+ Q_ASSERT(*vao);
+}
+
+// Called by RenderView->submit() in RenderThread context
+// Returns true, if all RenderCommands were sent to the GPU
+bool Renderer::executeCommandsSubmission(const RenderView *rv)
+{
+ bool allCommandsIssued = true;
+
+ // Render drawing commands
+ QVector<RenderCommand> commands = rv->commands();
+
+ // Use the graphicscontext to submit the commands to the underlying
+ // graphics API (OpenGL)
+
+ // Save the RenderView base stateset
+ RenderStateSet *globalState = m_submissionContext->currentStateSet();
+ OpenGLVertexArrayObject *vao = nullptr;
+
+ for (RenderCommand &command : commands) {
+
+ if (command.m_type == RenderCommand::Compute) { // Compute Call
+ performCompute(rv, &command);
+ } else { // Draw Command
+ // Check if we have a valid command that can be drawn
+ if (!command.m_isValid) {
+ allCommandsIssued = false;
+ continue;
+ }
+
+ vao = m_glResourceManagers->vaoManager()->data(command.m_vao);
+
+ // something may have went wrong when initializing the VAO
+ if (!vao->isSpecified()) {
+ allCommandsIssued = false;
+ continue;
+ }
+
+ {
+ Profiling::GLTimeRecorder recorder(Profiling::ShaderUpdate);
+ //// We activate the shader here
+ GLShader *shader = command.m_glShader;
+ if (!m_submissionContext->activateShader(shader)) {
+ allCommandsIssued = false;
+ continue;
+ }
+ }
+
+ {
+ Profiling::GLTimeRecorder recorder(Profiling::VAOUpdate);
+ // Bind VAO
+ vao->bind();
+ }
+
+ {
+ Profiling::GLTimeRecorder recorder(Profiling::UniformUpdate);
+ //// Update program uniforms
+ if (!m_submissionContext->setParameters(command.m_parameterPack)) {
+ allCommandsIssued = false;
+ // If we have failed to set uniform (e.g unable to bind a texture)
+ // we won't perform the draw call which could show invalid content
+ continue;
+ }
+ }
+
+ //// OpenGL State
+ // TO DO: Make states not dependendent on their backend node for this step
+ // Set state
+ RenderStateSet *localState = command.m_stateSet.data();
+
+
+ {
+ Profiling::GLTimeRecorder recorder(Profiling::StateUpdate);
+ // Merge the RenderCommand state with the globalState of the RenderView
+ // Or restore the globalState if no stateSet for the RenderCommand
+ if (localState != nullptr) {
+ command.m_stateSet->merge(globalState);
+ m_submissionContext->setCurrentStateSet(localState);
+ } else {
+ m_submissionContext->setCurrentStateSet(globalState);
+ }
+ }
+ // All Uniforms for a pass are stored in the QUniformPack of the command
+ // Uniforms for Effect, Material and Technique should already have been correctly resolved
+ // at that point
+
+ //// Draw Calls
+ performDraw(&command);
+ }
+ } // end of RenderCommands loop
+
+ // We cache the VAO and release it only at the end of the exectute frame
+ // We try to minimize VAO binding between RenderCommands
+ if (vao)
+ vao->release();
+
+ // Reset to the state we were in before executing the render commands
+ m_submissionContext->setCurrentStateSet(globalState);
+
+ return allCommandsIssued;
+}
+
+bool Renderer::updateVAOWithAttributes(Geometry *geometry,
+ const RenderCommand *command,
+ GLShader *shader,
+ bool forceUpdate)
+{
+ m_dirtyAttributes.reserve(m_dirtyAttributes.size() + geometry->attributes().size());
+ const auto attributeIds = geometry->attributes();
+
+ 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);
+
+ if (attribute == nullptr)
+ return false;
+
+ Buffer *buffer = m_nodesManager->bufferManager()->lookupResource(attribute->bufferId());
+
+ // Buffer update was already performed at this point
+ // Just make sure the attribute reference a valid buffer
+ if (buffer == nullptr)
+ return false;
+
+ // Index Attribute
+ bool attributeWasDirty = false;
+ if (attribute->attributeType() == QAttribute::IndexAttribute) {
+ if ((attributeWasDirty = attribute->isDirty()) == true || forceUpdate)
+ m_submissionContext->specifyIndices(buffer);
+ // Vertex Attribute
+ } else if (command->m_activeAttributes.contains(attribute->nameId())) {
+ if ((attributeWasDirty = attribute->isDirty()) == true || forceUpdate) {
+ // Find the location for the attribute
+ const QVector<ShaderAttribute> shaderAttributes = shader->attributes();
+ const ShaderAttribute *attributeDescription = nullptr;
+ for (const ShaderAttribute &shaderAttribute : shaderAttributes) {
+ if (shaderAttribute.m_nameId == attribute->nameId()) {
+ attributeDescription = &shaderAttribute;
+ break;
+ }
+ }
+ if (!attributeDescription || attributeDescription->m_location < 0)
+ return false;
+ m_submissionContext->specifyAttribute(attribute, buffer, attributeDescription);
+ }
+ }
+
+ // Append attribute to temporary vector so that its dirtiness
+ // can be cleared at the end of the frame
+ if (attributeWasDirty)
+ m_dirtyAttributes.push_back(attribute);
+
+ // Note: We cannot call unsertDirty on the Attribute at this
+ // point as we don't know if the attributes are being shared
+ // with other geometry / geometryRenderer in which case they still
+ // should remain dirty so that VAO for these commands are properly
+ // updated
+ }
+
+ return true;
+}
+
+bool Renderer::requiresVAOAttributeUpdate(Geometry *geometry,
+ const RenderCommand *command) const
+{
+ const auto attributeIds = geometry->attributes();
+
+ 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);
+
+ if (attribute == nullptr)
+ continue;
+
+ if ((attribute->attributeType() == QAttribute::IndexAttribute && attribute->isDirty()) ||
+ (command->m_activeAttributes.contains(attribute->nameId()) && attribute->isDirty()))
+ return true;
+ }
+ return false;
+}
+
+// Erase graphics related resources that may become unused after a frame
+void Renderer::cleanGraphicsResources()
+{
+ // Clean buffers
+ const QVector<Qt3DCore::QNodeId> buffersToRelease = m_nodesManager->bufferManager()->takeBuffersToRelease();
+ for (Qt3DCore::QNodeId bufferId : buffersToRelease)
+ m_submissionContext->releaseBuffer(bufferId);
+
+ // When Textures are cleaned up, their id is saved so that they can be
+ // cleaned up in the render thread
+ const QVector<Qt3DCore::QNodeId> cleanedUpTextureIds = std::move(m_textureIdsToCleanup);
+ for (const Qt3DCore::QNodeId textureCleanedUpId: cleanedUpTextureIds)
+ cleanupTexture(textureCleanedUpId);
+
+ // Delete abandoned VAOs
+ m_abandonedVaosMutex.lock();
+ const QVector<HVao> abandonedVaos = std::move(m_abandonedVaos);
+ m_abandonedVaosMutex.unlock();
+ for (const HVao &vaoHandle : abandonedVaos) {
+ // might have already been destroyed last frame, but added by the cleanup job before, so
+ // check if the VAO is really still existent
+ OpenGLVertexArrayObject *vao = m_glResourceManagers->vaoManager()->data(vaoHandle);
+ if (vao) {
+ vao->destroy();
+ // We remove VAO from manager using its VAOIdentifier
+ m_glResourceManagers->vaoManager()->release(vaoHandle);
+ }
+ }
+
+ // Abandon GL shaders when a Shader node is destroyed Note: We are sure
+ // that when this gets executed, all scene changes have been received and
+ // shader nodes updated
+ const QVector<Qt3DCore::QNodeId> cleanedUpShaderIds = m_nodesManager->shaderManager()->takeShaderIdsToCleanup();
+ for (const Qt3DCore::QNodeId shaderCleanedUpId: cleanedUpShaderIds) {
+ cleanupShader(m_nodesManager->shaderManager()->lookupResource(shaderCleanedUpId));
+ // We can really release the texture at this point
+ m_nodesManager->shaderManager()->releaseResource(shaderCleanedUpId);
+ }
+}
+
+QList<QPair<QObject *, QMouseEvent>> Renderer::pendingPickingEvents() const
+{
+ return m_pickEventFilter->pendingMouseEvents();
+}
+
+QList<QKeyEvent> Renderer::pendingKeyEvents() const
+{
+ return m_pickEventFilter->pendingKeyEvents();
+}
+
+const GraphicsApiFilterData *Renderer::contextInfo() const
+{
+ return m_submissionContext->contextInfo();
+}
+
+SubmissionContext *Renderer::submissionContext() const
+{
+ return m_submissionContext.data();
+}
+
+// Returns a vector of jobs to be performed for dirty buffers
+// 1 dirty buffer == 1 job, all job can be performed in parallel
+QVector<Qt3DCore::QAspectJobPtr> Renderer::createRenderBufferJobs() const
+{
+ const QVector<QNodeId> dirtyBuffers = m_nodesManager->bufferManager()->takeDirtyBuffers();
+ QVector<QAspectJobPtr> dirtyBuffersJobs;
+ dirtyBuffersJobs.reserve(dirtyBuffers.size());
+
+ for (const QNodeId bufId : dirtyBuffers) {
+ Render::HBuffer bufferHandle = m_nodesManager->lookupHandle<Render::Buffer, Render::BufferManager, Render::HBuffer>(bufId);
+ if (!bufferHandle.isNull()) {
+ // Create new buffer job
+ auto job = Render::LoadBufferJobPtr::create(bufferHandle);
+ job->setNodeManager(m_nodesManager);
+ dirtyBuffersJobs.push_back(job);
+ }
+ }
+
+ return dirtyBuffersJobs;
+}
+
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
diff --git a/src/plugins/renderers/opengl/renderer/renderer.pri b/src/plugins/renderers/opengl/renderer/renderer.pri
new file mode 100644
index 000000000..0f68aa481
--- /dev/null
+++ b/src/plugins/renderers/opengl/renderer/renderer.pri
@@ -0,0 +1,28 @@
+INCLUDEPATH += $$PWD
+
+SOURCES += \
+ $$PWD/openglvertexarrayobject.cpp \
+ $$PWD/rendercommand.cpp \
+ $$PWD/renderer.cpp \
+ $$PWD/renderqueue.cpp \
+ $$PWD/renderview.cpp \
+ $$PWD/renderviewbuilder.cpp \
+ $$PWD/shaderparameterpack.cpp \
+ $$PWD/glshader.cpp \
+ $$PWD/logging.cpp \
+ $$PWD/commandexecuter.cpp
+
+HEADERS += \
+ $$PWD/openglvertexarrayobject_p.h \
+ $$PWD/renderercache_p.h \
+ $$PWD/rendercommand_p.h \
+ $$PWD/renderer_p.h \
+ $$PWD/renderqueue_p.h \
+ $$PWD/renderview_p.h \
+ $$PWD/renderviewbuilder_p.h \
+ $$PWD/shaderparameterpack_p.h \
+ $$PWD/shadervariables_p.h \
+ $$PWD/glshader_p.h \
+ $$PWD/glfence_p.h \
+ $$PWD/logging_p.h \
+ $$PWD/commandexecuter_p.h
diff --git a/src/plugins/renderers/opengl/renderer/renderer_p.h b/src/plugins/renderers/opengl/renderer/renderer_p.h
new file mode 100644
index 000000000..eac622192
--- /dev/null
+++ b/src/plugins/renderers/opengl/renderer/renderer_p.h
@@ -0,0 +1,464 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB).
+** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DRENDER_RENDER_RENDERER_H
+#define QT3DRENDER_RENDER_RENDERER_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <Qt3DRender/qrenderaspect.h>
+#include <Qt3DRender/qtechnique.h>
+#include <Qt3DRender/private/handle_types_p.h>
+#include <Qt3DRender/private/abstractrenderer_p.h>
+#include <Qt3DCore/qaspectjob.h>
+#include <Qt3DRender/private/qt3drender_global_p.h>
+#include <Qt3DRender/private/pickboundingvolumejob_p.h>
+#include <Qt3DRender/private/raycastingjob_p.h>
+#include <Qt3DRender/private/rendersettings_p.h>
+#include <Qt3DRender/private/expandboundingvolumejob_p.h>
+#include <Qt3DRender/private/updateworldtransformjob_p.h>
+#include <Qt3DRender/private/calcboundingvolumejob_p.h>
+#include <Qt3DRender/private/updateshaderdatatransformjob_p.h>
+#include <Qt3DRender/private/framecleanupjob_p.h>
+#include <Qt3DRender/private/updateworldboundingvolumejob_p.h>
+#include <Qt3DRender/private/updatetreeenabledjob_p.h>
+#include <Qt3DRender/private/platformsurfacefilter_p.h>
+#include <Qt3DRender/private/sendbuffercapturejob_p.h>
+#include <Qt3DRender/private/genericlambdajob_p.h>
+#include <Qt3DRender/private/updatemeshtrianglelistjob_p.h>
+#include <Qt3DRender/private/updateskinningpalettejob_p.h>
+#include <Qt3DRender/private/updateentitylayersjob_p.h>
+#include <Qt3DRender/private/shaderbuilder_p.h>
+#include <Qt3DRender/private/lightgatherer_p.h>
+#include <Qt3DRender/private/texture_p.h>
+#include <shaderparameterpack_p.h>
+#include <renderviewinitializerjob_p.h>
+#include <filtercompatibletechniquejob_p.h>
+#include <renderercache_p.h>
+#include <logging_p.h>
+#include <gl_handle_types_p.h>
+#include <glfence_p.h>
+#include <renderercache_p.h>
+
+#include <QHash>
+#include <QMatrix4x4>
+#include <QObject>
+
+#include <QOpenGLShaderProgram>
+#include <QOpenGLVertexArrayObject>
+#include <QOpenGLBuffer>
+#include <QMutex>
+#include <QWaitCondition>
+#include <QAtomicInt>
+#include <QScopedPointer>
+#include <QSemaphore>
+
+#include <functional>
+
+#if defined(QT_BUILD_INTERNAL)
+class tst_Renderer;
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class QSurface;
+class QMouseEvent;
+class QScreen;
+
+namespace Qt3DCore {
+class QEntity;
+class QFrameAllocator;
+class QEventFilterService;
+}
+
+namespace Qt3DRender {
+
+class QCamera;
+class QMaterial;
+class QShaderProgram;
+class QMesh;
+class QRenderPass;
+class QAbstractShapeMesh;
+struct GraphicsApiFilterData;
+class QSceneImporter;
+
+namespace Debug {
+class CommandExecuter;
+}
+
+namespace Render {
+
+class CameraLens;
+class SubmissionContext;
+class FrameGraphNode;
+class Material;
+class Technique;
+class Shader;
+class Entity;
+class RenderCommand;
+class RenderQueue;
+class RenderView;
+class Effect;
+class RenderPass;
+class RenderThread;
+class RenderStateSet;
+class VSyncFrameAdvanceService;
+class PickEventFilter;
+class NodeManagers;
+class GLResourceManagers;
+class GLShader;
+class ResourceAccessor;
+
+class UpdateLevelOfDetailJob;
+typedef QSharedPointer<UpdateLevelOfDetailJob> UpdateLevelOfDetailJobPtr;
+
+using SynchronizerJobPtr = GenericLambdaJobPtr<std::function<void()>>;
+using SynchronizerPostFramePtr = GenericLambdaJobAndPostFramePtr<std::function<void ()>, std::function<void (Qt3DCore::QAspectManager *)>>;
+
+template<typename T, typename ... Ts>
+class FilterEntityByComponentJob;
+template<typename T, typename ... Ts>
+using FilterEntityByComponentJobPtr = QSharedPointer<FilterEntityByComponentJob<T, Ts...>>;
+using ComputableEntityFilterPtr = FilterEntityByComponentJobPtr<Render::ComputeCommand, Render::Material>;
+using RenderableEntityFilterPtr = FilterEntityByComponentJobPtr<Render::GeometryRenderer, Render::Material>;
+
+class Q_3DRENDERSHARED_PRIVATE_EXPORT Renderer : public AbstractRenderer
+{
+public:
+ explicit Renderer(QRenderAspect::RenderType type);
+ ~Renderer();
+
+ void dumpInfo() const override;
+ API api() const override { return AbstractRenderer::OpenGL; }
+
+ qint64 time() const override;
+ void setTime(qint64 time) override;
+
+ void setNodeManagers(NodeManagers *managers) override;
+ void setServices(Qt3DCore::QServiceLocator *services) override;
+ void setSurfaceExposed(bool exposed) override;
+
+ NodeManagers *nodeManagers() const override;
+ Qt3DCore::QServiceLocator *services() const override { return m_services; }
+
+ void initialize() override;
+ void shutdown() override;
+ void releaseGraphicsResources() override;
+
+ void render() override;
+ void doRender(bool swapBuffers = true) override;
+ void cleanGraphicsResources() override;
+
+ bool isRunning() const override { return m_running.loadRelaxed(); }
+
+ void setSceneRoot(Entity *sgRoot) override;
+ Entity *sceneRoot() const override { return m_renderSceneRoot; }
+
+ FrameGraphNode *frameGraphRoot() const override;
+ RenderQueue *renderQueue() const { return m_renderQueue; }
+
+ void markDirty(BackendNodeDirtySet changes, BackendNode *node) override;
+ BackendNodeDirtySet dirtyBits() override;
+
+#if defined(QT_BUILD_INTERNAL)
+ void clearDirtyBits(BackendNodeDirtySet changes) override;
+#endif
+ bool shouldRender() const override;
+ void skipNextFrame() override;
+ void jobsDone(Qt3DCore::QAspectManager *manager) override;
+
+ QVector<Qt3DCore::QAspectJobPtr> preRenderingJobs() override;
+ QVector<Qt3DCore::QAspectJobPtr> renderBinJobs() override;
+ Qt3DCore::QAspectJobPtr pickBoundingVolumeJob() override;
+ Qt3DCore::QAspectJobPtr rayCastingJob() override;
+ Qt3DCore::QAspectJobPtr syncLoadingJobs() override;
+ Qt3DCore::QAspectJobPtr expandBoundingVolumeJob() override;
+
+ QVector<Qt3DCore::QAspectJobPtr> createRenderBufferJobs() const;
+
+ inline FrameCleanupJobPtr frameCleanupJob() const { return m_cleanupJob; }
+ inline UpdateShaderDataTransformJobPtr updateShaderDataTransformJob() const { return m_updateShaderDataTransformJob; }
+ inline CalculateBoundingVolumeJobPtr calculateBoundingVolumeJob() const { return m_calculateBoundingVolumeJob; }
+ inline UpdateTreeEnabledJobPtr updateTreeEnabledJob() const { return m_updateTreeEnabledJob; }
+ inline UpdateWorldTransformJobPtr updateWorldTransformJob() const { return m_worldTransformJob; }
+ inline UpdateWorldBoundingVolumeJobPtr updateWorldBoundingVolumeJob() const { return m_updateWorldBoundingVolumeJob; }
+ inline UpdateLevelOfDetailJobPtr updateLevelOfDetailJob() const { return m_updateLevelOfDetailJob; }
+ inline UpdateMeshTriangleListJobPtr updateMeshTriangleListJob() const { return m_updateMeshTriangleListJob; }
+ inline FilterCompatibleTechniqueJobPtr filterCompatibleTechniqueJob() const { return m_filterCompatibleTechniqueJob; }
+ inline SynchronizerJobPtr syncLoadingJobs() const { return m_syncLoadingJobs; }
+ inline UpdateSkinningPaletteJobPtr updateSkinningPaletteJob() const { return m_updateSkinningPaletteJob; }
+ inline SynchronizerPostFramePtr introspectShadersJob() const { return m_introspectShaderJob; }
+ inline Qt3DCore::QAspectJobPtr bufferGathererJob() const { return m_bufferGathererJob; }
+ inline Qt3DCore::QAspectJobPtr textureGathererJob() const { return m_textureGathererJob; }
+ inline UpdateEntityLayersJobPtr updateEntityLayersJob() const { return m_updateEntityLayersJob; }
+ inline LightGathererPtr lightGathererJob() const { return m_lightGathererJob; }
+ inline RenderableEntityFilterPtr renderableEntityFilterJob() const { return m_renderableEntityFilterJob; }
+ inline ComputableEntityFilterPtr computableEntityFilterJob() const { return m_computableEntityFilterJob; }
+ inline SynchronizerJobPtr cacheLightJob() const { return m_cacheLightsJob; }
+ inline SynchronizerJobPtr cacheRenderableEntitiesJob() const { return m_cacheRenderableEntitiesJob; }
+ inline SynchronizerJobPtr cacheComputableEntitiesJob() const { return m_cacheComputableEntitiesJob; }
+
+ Qt3DCore::QAbstractFrameAdvanceService *frameAdvanceService() const override;
+
+ void registerEventFilter(Qt3DCore::QEventFilterService *service) override;
+
+ void setSettings(RenderSettings *settings) override;
+ RenderSettings *settings() const override;
+ QOpenGLContext *shareContext() const override;
+
+ inline GLResourceManagers *glResourceManagers() const { return m_glResourceManagers; }
+
+ // Executed in secondary GL thread
+ void loadShader(Shader *shader, Qt3DRender::Render::HShader shaderHandle) override;
+
+
+ void updateGLResources();
+ void updateTexture(Texture *texture);
+ void cleanupTexture(Qt3DCore::QNodeId cleanedUpTextureId);
+ void cleanupShader(const Shader *shader);
+ void downloadGLBuffers();
+ void blitFramebuffer(Qt3DCore::QNodeId inputRenderTargetId,
+ Qt3DCore::QNodeId outputRenderTargetId,
+ QRect inputRect,
+ QRect outputRect,
+ GLuint defaultFramebuffer);
+
+ void prepareCommandsSubmission(const QVector<RenderView *> &renderViews);
+ bool executeCommandsSubmission(const RenderView *rv);
+ bool updateVAOWithAttributes(Geometry *geometry,
+ const RenderCommand *command,
+ GLShader *shader,
+ bool forceUpdate);
+
+ bool requiresVAOAttributeUpdate(Geometry *geometry,
+ const RenderCommand *command) const;
+
+ // For Scene2D rendering
+ void setOpenGLContext(QOpenGLContext *context) override;
+ bool accessOpenGLTexture(Qt3DCore::QNodeId nodeId,
+ QOpenGLTexture **texture,
+ QMutex **lock,
+ bool readonly) override;
+ QSharedPointer<RenderBackendResourceAccessor> resourceAccessor() const override;
+
+
+ const GraphicsApiFilterData *contextInfo() const;
+ SubmissionContext *submissionContext() const;
+
+ inline RenderStateSet *defaultRenderState() const { return m_defaultRenderStateSet; }
+
+ QList<QPair<QObject*, QMouseEvent>> pendingPickingEvents() const;
+ QList<QKeyEvent> pendingKeyEvents() const;
+
+ void enqueueRenderView(RenderView *renderView, int submitOrder);
+ bool isReadyToSubmit();
+
+ QVariant executeCommand(const QStringList &args) override;
+ void setOffscreenSurfaceHelper(OffscreenSurfaceHelper *helper) override;
+ QSurfaceFormat format() override;
+
+ struct ViewSubmissionResultData
+ {
+ ViewSubmissionResultData()
+ : lastBoundFBOId(0)
+ , surface(nullptr)
+ {}
+
+ uint lastBoundFBOId;
+ QSurface *surface;
+ };
+
+ ViewSubmissionResultData submitRenderViews(const QVector<Render::RenderView *> &renderViews);
+
+ RendererCache *cache() { return &m_cache; }
+ void setScreen(QScreen *scr) override;
+ QScreen *screen() const override;
+
+#ifdef QT3D_RENDER_UNIT_TESTS
+public:
+#else
+
+private:
+#endif
+ bool canRender() const;
+
+ Qt3DCore::QServiceLocator *m_services;
+ NodeManagers *m_nodesManager;
+
+ // Frame graph root
+ Qt3DCore::QNodeId m_frameGraphRootUuid;
+
+ Entity *m_renderSceneRoot;
+
+ // Fail safe values that we can use if a RenderCommand
+ // is missing a shader
+ RenderStateSet *m_defaultRenderStateSet;
+ ShaderParameterPack m_defaultUniformPack;
+
+ QScopedPointer<SubmissionContext> m_submissionContext;
+ QSurfaceFormat m_format;
+
+ RenderQueue *m_renderQueue;
+ QScopedPointer<RenderThread> m_renderThread;
+ QScopedPointer<VSyncFrameAdvanceService> m_vsyncFrameAdvanceService;
+
+ QSemaphore m_submitRenderViewsSemaphore;
+ QSemaphore m_waitForInitializationToBeCompleted;
+ QMutex m_hasBeenInitializedMutex;
+
+ QAtomicInt m_running;
+
+ QScopedPointer<PickEventFilter> m_pickEventFilter;
+
+ QVector<Attribute *> m_dirtyAttributes;
+ QVector<Geometry *> m_dirtyGeometry;
+ QAtomicInt m_exposed;
+
+ struct DirtyBits {
+ BackendNodeDirtySet marked; // marked dirty since last job build
+ BackendNodeDirtySet remaining; // remaining dirty after jobs have finished
+ };
+ DirtyBits m_dirtyBits;
+
+ QAtomicInt m_lastFrameCorrect;
+ QOpenGLContext *m_glContext;
+ QOpenGLContext *m_shareContext;
+ mutable QMutex m_shareContextMutex;
+ PickBoundingVolumeJobPtr m_pickBoundingVolumeJob;
+ RayCastingJobPtr m_rayCastingJob;
+
+ qint64 m_time;
+
+ RenderSettings *m_settings;
+
+ UpdateShaderDataTransformJobPtr m_updateShaderDataTransformJob;
+ FrameCleanupJobPtr m_cleanupJob;
+ UpdateWorldTransformJobPtr m_worldTransformJob;
+ ExpandBoundingVolumeJobPtr m_expandBoundingVolumeJob;
+ CalculateBoundingVolumeJobPtr m_calculateBoundingVolumeJob;
+ UpdateWorldBoundingVolumeJobPtr m_updateWorldBoundingVolumeJob;
+ UpdateTreeEnabledJobPtr m_updateTreeEnabledJob;
+ SendBufferCaptureJobPtr m_sendBufferCaptureJob;
+ UpdateSkinningPaletteJobPtr m_updateSkinningPaletteJob;
+ UpdateLevelOfDetailJobPtr m_updateLevelOfDetailJob;
+ UpdateMeshTriangleListJobPtr m_updateMeshTriangleListJob;
+ FilterCompatibleTechniqueJobPtr m_filterCompatibleTechniqueJob;
+ UpdateEntityLayersJobPtr m_updateEntityLayersJob;
+ LightGathererPtr m_lightGathererJob;
+ RenderableEntityFilterPtr m_renderableEntityFilterJob;
+ ComputableEntityFilterPtr m_computableEntityFilterJob;
+
+ QVector<Qt3DCore::QNodeId> m_pendingRenderCaptureSendRequests;
+
+ void performDraw(RenderCommand *command);
+ void performCompute(const RenderView *rv, RenderCommand *command);
+ void createOrUpdateVAO(RenderCommand *command,
+ HVao *previousVAOHandle,
+ OpenGLVertexArrayObject **vao);
+
+ SynchronizerJobPtr m_bufferGathererJob;
+ SynchronizerJobPtr m_vaoGathererJob;
+ SynchronizerJobPtr m_textureGathererJob;
+ SynchronizerJobPtr m_sendSetFenceHandlesToFrontendJob;
+ SynchronizerPostFramePtr m_introspectShaderJob;
+ SynchronizerJobPtr m_syncLoadingJobs;
+ SynchronizerJobPtr m_cacheRenderableEntitiesJob;
+ SynchronizerJobPtr m_cacheComputableEntitiesJob;
+ SynchronizerJobPtr m_cacheLightsJob;
+
+ void lookForAbandonedVaos();
+ void lookForDirtyBuffers();
+ void lookForDownloadableBuffers();
+ void lookForDirtyTextures();
+ void reloadDirtyShaders();
+ void sendShaderChangesToFrontend(Qt3DCore::QAspectManager *manager);
+ void sendTextureChangesToFrontend(Qt3DCore::QAspectManager *manager);
+ void sendSetFenceHandlesToFrontend();
+ void sendDisablesToFrontend(Qt3DCore::QAspectManager *manager);
+
+ QMutex m_abandonedVaosMutex;
+ QVector<HVao> m_abandonedVaos;
+
+ QVector<HBuffer> m_dirtyBuffers;
+ QVector<Qt3DCore::QNodeId> m_downloadableBuffers;
+ QVector<HShader> m_dirtyShaders;
+ QVector<HTexture> m_dirtyTextures;
+ QVector<QPair<Texture::TextureUpdateInfo, Qt3DCore::QNodeIdVector>> m_updatedTextureProperties;
+ QVector<QPair<Qt3DCore::QNodeId, GLFence>> m_updatedSetFences;
+ QVector<Qt3DCore::QNodeId> m_updatedDisableSubtreeEnablers;
+ Qt3DCore::QNodeIdVector m_textureIdsToCleanup;
+ QVector<ShaderBuilderUpdate> m_shaderBuilderUpdates;
+
+ bool m_ownedContext;
+
+ OffscreenSurfaceHelper *m_offscreenHelper;
+ GLResourceManagers *m_glResourceManagers;
+ QMutex m_offscreenSurfaceMutex;
+
+ QScopedPointer<Qt3DRender::Debug::CommandExecuter> m_commandExecuter;
+
+#ifdef QT_BUILD_INTERNAL
+ friend class ::tst_Renderer;
+#endif
+
+ QMetaObject::Connection m_contextConnection;
+ RendererCache m_cache;
+ bool m_shouldSwapBuffers;
+
+ QVector<FrameGraphNode *> m_frameGraphLeaves;
+ QScreen *m_screen = nullptr;
+ QSharedPointer<ResourceAccessor> m_scene2DResourceAccessor;
+};
+
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDER_RENDERER_H
diff --git a/src/plugins/renderers/opengl/renderer/renderercache_p.h b/src/plugins/renderers/opengl/renderer/renderercache_p.h
new file mode 100644
index 000000000..70a5ef43c
--- /dev/null
+++ b/src/plugins/renderers/opengl/renderer/renderercache_p.h
@@ -0,0 +1,97 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DRENDER_RENDER_RENDERERCACHE_P_H
+#define QT3DRENDER_RENDER_RENDERERCACHE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <Qt3DRender/QFrameGraphNode>
+
+#include <Qt3DRender/private/entity_p.h>
+#include <renderviewjobutils_p.h>
+#include <Qt3DRender/private/lightsource_p.h>
+#include <rendercommand_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+namespace Render {
+
+struct RendererCache
+{
+ struct LeafNodeData
+ {
+ QVector<Entity *> filterEntitiesByLayer;
+ MaterialParameterGathererData materialParameterGatherer;
+ EntityRenderCommandData renderCommandData;
+ };
+
+ // Shared amongst all RV cache
+ QVector<Entity *> renderableEntities;
+ QVector<Entity *> computeEntities;
+ QVector<LightSource> gatheredLights;
+ EnvironmentLight* environmentLight;
+
+ // Per RV cache
+ QHash<FrameGraphNode *, LeafNodeData> leafNodeCache;
+
+ QMutex *mutex() { return &m_mutex; }
+
+private:
+ QMutex m_mutex;
+};
+
+} // namespace Render
+
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDER_RENDERERCACHE_P_H
diff --git a/src/plugins/renderers/opengl/renderer/renderqueue.cpp b/src/plugins/renderers/opengl/renderer/renderqueue.cpp
new file mode 100644
index 000000000..9ba66952f
--- /dev/null
+++ b/src/plugins/renderers/opengl/renderer/renderqueue.cpp
@@ -0,0 +1,133 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "renderqueue_p.h"
+#include <renderview_p.h>
+#include <QThread>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+namespace Render {
+
+RenderQueue::RenderQueue()
+ : m_noRender(false)
+ , m_wasReset(true)
+ , m_targetRenderViewCount(0)
+ , m_currentRenderViewCount(0)
+ , m_currentWorkQueue(1)
+{
+}
+
+int RenderQueue::currentRenderViewCount() const
+{
+ return m_currentRenderViewCount;
+}
+
+/*
+ * In case the framegraph changed or when the current number of render queue
+ * needs to be reset.
+ */
+void RenderQueue::reset()
+{
+ m_currentRenderViewCount = 0;
+ m_targetRenderViewCount = 0;
+ m_currentWorkQueue.clear();
+ m_noRender = false;
+ m_wasReset = true;
+}
+
+void RenderQueue::setNoRender()
+{
+ Q_ASSERT(m_targetRenderViewCount == 0);
+ m_noRender = true;
+}
+
+/*
+ * Queue up a RenderView for the frame being built.
+ * Thread safe as this is called from the renderer which is locked.
+ * Returns true if the renderView is complete
+ */
+bool RenderQueue::queueRenderView(RenderView *renderView, uint submissionOrderIndex)
+{
+ Q_ASSERT(!m_noRender);
+ m_currentWorkQueue[submissionOrderIndex] = renderView;
+ ++m_currentRenderViewCount;
+ Q_ASSERT(m_currentRenderViewCount <= m_targetRenderViewCount);
+ return isFrameQueueComplete();
+}
+
+/*
+ * Called by the Rendering Thread to retrieve the a frame queue to render.
+ * A call to reset is required after rendering of the frame. Otherwise under some
+ * conditions the current but then invalidated frame queue could be reused.
+ */
+QVector<RenderView *> RenderQueue::nextFrameQueue()
+{
+ return m_currentWorkQueue;
+}
+
+/*
+ * Sets the number \a targetRenderViewCount of RenderView objects that make up a frame.
+ */
+void RenderQueue::setTargetRenderViewCount(int targetRenderViewCount)
+{
+ Q_ASSERT(!m_noRender);
+ m_targetRenderViewCount = targetRenderViewCount;
+ m_currentWorkQueue.resize(targetRenderViewCount);
+ m_wasReset = false;
+}
+
+/*
+ * Returns true if all the RenderView objects making up the current frame have been queued.
+ * Returns false otherwise.
+ * \note a frameQueue or size 0 is considered incomplete.
+ */
+bool RenderQueue::isFrameQueueComplete() const
+{
+ return (m_noRender
+ || (m_targetRenderViewCount > 0 && m_targetRenderViewCount == m_currentRenderViewCount));
+}
+
+} // namespace Render
+
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
diff --git a/src/plugins/renderers/opengl/renderer/renderqueue_p.h b/src/plugins/renderers/opengl/renderer/renderqueue_p.h
new file mode 100644
index 000000000..e565115f2
--- /dev/null
+++ b/src/plugins/renderers/opengl/renderer/renderqueue_p.h
@@ -0,0 +1,102 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DRENDER_RENDER_RENDERQUEUE_H
+#define QT3DRENDER_RENDER_RENDERQUEUE_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 <QVector>
+#include <QtGlobal>
+#include <QMutex>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+namespace Render {
+
+class RenderView;
+
+class Q_AUTOTEST_EXPORT RenderQueue
+{
+public:
+ RenderQueue();
+
+ void setTargetRenderViewCount(int targetRenderViewCount);
+ int targetRenderViewCount() const { return m_targetRenderViewCount; }
+ int currentRenderViewCount() const;
+ bool isFrameQueueComplete() const;
+
+ bool queueRenderView(RenderView *renderView, uint submissionOrderIndex);
+ QVector<RenderView *> nextFrameQueue();
+ void reset();
+
+ void setNoRender();
+ inline bool isNoRender() const { return m_noRender; }
+
+ inline bool wasReset() const { return m_wasReset; }
+
+ inline QMutex *mutex() { return &m_mutex; }
+
+private:
+ bool m_noRender;
+ bool m_wasReset;
+ int m_targetRenderViewCount;
+ int m_currentRenderViewCount;
+ QVector<RenderView *> m_currentWorkQueue;
+ QMutex m_mutex;
+};
+
+} // namespace Render
+
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDER_RENDERQUEUE_H
diff --git a/src/plugins/renderers/opengl/renderer/renderview.cpp b/src/plugins/renderers/opengl/renderer/renderview.cpp
new file mode 100644
index 000000000..bd4c98a01
--- /dev/null
+++ b/src/plugins/renderers/opengl/renderer/renderview.cpp
@@ -0,0 +1,1229 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB).
+** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "renderview_p.h"
+#include <Qt3DRender/qmaterial.h>
+#include <Qt3DRender/qrenderaspect.h>
+#include <Qt3DRender/qrendertarget.h>
+#include <Qt3DRender/qabstractlight.h>
+#include <Qt3DRender/private/sphere_p.h>
+
+#include <Qt3DRender/private/cameraselectornode_p.h>
+#include <Qt3DRender/private/framegraphnode_p.h>
+#include <Qt3DRender/private/layerfilternode_p.h>
+#include <Qt3DRender/private/qparameter_p.h>
+#include <Qt3DRender/private/cameralens_p.h>
+#include <Qt3DRender/private/effect_p.h>
+#include <Qt3DRender/private/entity_p.h>
+#include <Qt3DRender/private/nodemanagers_p.h>
+#include <Qt3DRender/private/layer_p.h>
+#include <Qt3DRender/private/renderlogging_p.h>
+#include <Qt3DRender/private/renderpassfilternode_p.h>
+#include <Qt3DRender/private/renderpass_p.h>
+#include <Qt3DRender/private/geometryrenderer_p.h>
+#include <Qt3DRender/private/techniquefilternode_p.h>
+#include <Qt3DRender/private/viewportnode_p.h>
+#include <Qt3DRender/private/buffermanager_p.h>
+#include <Qt3DRender/private/geometryrenderermanager_p.h>
+#include <Qt3DRender/private/rendercapture_p.h>
+#include <Qt3DRender/private/buffercapture_p.h>
+#include <Qt3DRender/private/stringtoint_p.h>
+#include <Qt3DRender/private/renderlogging_p.h>
+#include <Qt3DRender/private/renderstateset_p.h>
+#include <rendercommand_p.h>
+#include <renderer_p.h>
+#include <graphicscontext_p.h>
+#include <submissioncontext_p.h>
+#include <glresourcemanagers_p.h>
+#include <Qt3DCore/qentity.h>
+#include <QtGui/qsurface.h>
+#include <algorithm>
+
+#include <QDebug>
+#if defined(QT3D_RENDER_VIEW_JOB_TIMINGS)
+#include <QElapsedTimer>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+namespace Render {
+
+
+namespace {
+
+// register our QNodeId's as a metatype during program loading
+const int Q_DECL_UNUSED qNodeIdTypeId = qMetaTypeId<Qt3DCore::QNodeId>();
+
+const int MAX_LIGHTS = 8;
+
+#define LIGHT_POSITION_NAME QLatin1String(".position")
+#define LIGHT_TYPE_NAME QLatin1String(".type")
+#define LIGHT_COLOR_NAME QLatin1String(".color")
+#define LIGHT_INTENSITY_NAME QLatin1String(".intensity")
+
+int LIGHT_COUNT_NAME_ID = 0;
+int LIGHT_POSITION_NAMES[MAX_LIGHTS];
+int LIGHT_TYPE_NAMES[MAX_LIGHTS];
+int LIGHT_COLOR_NAMES[MAX_LIGHTS];
+int LIGHT_INTENSITY_NAMES[MAX_LIGHTS];
+QString LIGHT_STRUCT_NAMES[MAX_LIGHTS];
+
+int LIGHT_POSITION_UNROLL_NAMES[MAX_LIGHTS];
+int LIGHT_TYPE_UNROLL_NAMES[MAX_LIGHTS];
+int LIGHT_COLOR_UNROLL_NAMES[MAX_LIGHTS];
+int LIGHT_INTENSITY_UNROLL_NAMES[MAX_LIGHTS];
+QString LIGHT_STRUCT_UNROLL_NAMES[MAX_LIGHTS];
+
+bool wasInitialized = false;
+
+} // anonymous namespace
+
+RenderView::StandardUniformsNameToTypeHash RenderView::ms_standardUniformSetters;
+
+
+RenderView::StandardUniformsNameToTypeHash RenderView::initializeStandardUniformSetters()
+{
+ RenderView::StandardUniformsNameToTypeHash setters;
+
+ setters.insert(Shader::modelMatrixNameId, ModelMatrix);
+ setters.insert(Shader::viewMatrixNameId, ViewMatrix);
+ setters.insert(Shader::projectionMatrixNameId, ProjectionMatrix);
+ setters.insert(Shader::modelViewMatrixNameId, ModelViewMatrix);
+ setters.insert(Shader::viewProjectionMatrixNameId, ViewProjectionMatrix);
+ setters.insert(Shader::modelViewProjectionNameId, ModelViewProjectionMatrix);
+ setters.insert(Shader::mvpNameId, ModelViewProjectionMatrix);
+ setters.insert(Shader::inverseModelMatrixNameId, InverseModelMatrix);
+ setters.insert(Shader::inverseViewMatrixNameId, InverseViewMatrix);
+ setters.insert(Shader::inverseProjectionMatrixNameId, InverseProjectionMatrix);
+ setters.insert(Shader::inverseModelViewNameId, InverseModelViewMatrix);
+ setters.insert(Shader::inverseViewProjectionMatrixNameId, InverseViewProjectionMatrix);
+ setters.insert(Shader::inverseModelViewProjectionNameId, InverseModelViewProjectionMatrix);
+ setters.insert(Shader::modelNormalMatrixNameId, ModelNormalMatrix);
+ setters.insert(Shader::modelViewNormalNameId, ModelViewNormalMatrix);
+ setters.insert(Shader::viewportMatrixNameId, ViewportMatrix);
+ setters.insert(Shader::inverseViewportMatrixNameId, InverseViewportMatrix);
+ setters.insert(Shader::aspectRatioNameId, AspectRatio);
+ setters.insert(Shader::exposureNameId, Exposure);
+ setters.insert(Shader::gammaNameId, Gamma);
+ setters.insert(Shader::timeNameId, Time);
+ setters.insert(Shader::eyePositionNameId, EyePosition);
+ setters.insert(Shader::skinningPaletteNameId, SkinningPalette);
+
+ return setters;
+}
+
+// TODO: Move this somewhere global where GraphicsContext::setViewport() can use it too
+static QRectF resolveViewport(const QRectF &fractionalViewport, const QSize &surfaceSize)
+{
+ return QRectF(fractionalViewport.x() * surfaceSize.width(),
+ (1.0 - fractionalViewport.y() - fractionalViewport.height()) * surfaceSize.height(),
+ fractionalViewport.width() * surfaceSize.width(),
+ fractionalViewport.height() * surfaceSize.height());
+}
+
+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() : Matrix4x4();
+}
+
+UniformValue RenderView::standardUniformValue(RenderView::StandardUniform standardUniformType,
+ Entity *entity,
+ const Matrix4x4 &model) const
+{
+ switch (standardUniformType) {
+ case ModelMatrix:
+ return UniformValue(model);
+ case ViewMatrix:
+ return UniformValue(m_data.m_viewMatrix);
+ case ProjectionMatrix:
+ return UniformValue(getProjectionMatrix(m_data.m_renderCameraLens));
+ case ModelViewMatrix:
+ return UniformValue(m_data.m_viewMatrix * model);
+ case ViewProjectionMatrix:
+ return UniformValue(getProjectionMatrix(m_data.m_renderCameraLens) * m_data.m_viewMatrix);
+ case ModelViewProjectionMatrix:
+ return UniformValue(m_data.m_viewProjectionMatrix * model);
+ case InverseModelMatrix:
+ return UniformValue(model.inverted());
+ case InverseViewMatrix:
+ return UniformValue(m_data.m_viewMatrix.inverted());
+ case InverseProjectionMatrix: {
+ return UniformValue(getProjectionMatrix(m_data.m_renderCameraLens).inverted());
+ }
+ case InverseModelViewMatrix:
+ return UniformValue((m_data.m_viewMatrix * model).inverted());
+ case InverseViewProjectionMatrix: {
+ 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());
+ case ModelNormalMatrix:
+ return UniformValue(convertToQMatrix4x4(model).normalMatrix());
+ case ModelViewNormalMatrix:
+ 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(Matrix4x4(viewportMatrix));
+ }
+ case InverseViewportMatrix: {
+ QMatrix4x4 viewportMatrix;
+ // TO DO: Implement on Matrix4x4
+ viewportMatrix.viewport(resolveViewport(m_viewport, m_surfaceSize));
+ return UniformValue(Matrix4x4(viewportMatrix.inverted()));
+ }
+ case AspectRatio:
+ return float(m_surfaceSize.width()) / std::max(1.f, float(m_surfaceSize.height()));
+ case Exposure:
+ return UniformValue(m_data.m_renderCameraLens ? m_data.m_renderCameraLens->exposure() : 0.0f);
+ case Gamma:
+ return UniformValue(m_gamma);
+ case Time:
+ return UniformValue(float(m_renderer->time() / 1000000000.0f));
+ case EyePosition:
+ return UniformValue(m_data.m_eyePos);
+ case SkinningPalette: {
+ const Armature *armature = entity->renderComponent<Armature>();
+ if (!armature) {
+ qCWarning(Jobs, "Requesting skinningPalette uniform but no armature set on entity");
+ return UniformValue();
+ }
+ return armature->skinningPaletteUniform();
+ }
+ default:
+ Q_UNREACHABLE();
+ return UniformValue();
+ }
+}
+
+RenderView::RenderView()
+ : m_isDownloadBuffersEnable(false)
+ , m_hasBlitFramebufferInfo(false)
+ , m_renderer(nullptr)
+ , m_manager(nullptr)
+ , m_devicePixelRatio(1.)
+ , m_viewport(QRectF(0., 0., 1., 1.))
+ , m_gamma(2.2f)
+ , m_surface(nullptr)
+ , m_clearBuffer(QClearBuffers::None)
+ , m_clearDepthValue(1.f)
+ , m_clearStencilValue(0)
+ , m_stateSet(nullptr)
+ , m_noDraw(false)
+ , m_compute(false)
+ , m_frustumCulling(false)
+ , m_memoryBarrier(QMemoryBarrier::None)
+ , m_environmentLight(nullptr)
+{
+ m_workGroups[0] = 1;
+ m_workGroups[1] = 1;
+ m_workGroups[2] = 1;
+
+ if (Q_UNLIKELY(!wasInitialized)) {
+ // Needed as we can control the init order of static/global variables across compile units
+ // and this hash relies on the static StringToInt class
+ wasInitialized = true;
+ RenderView::ms_standardUniformSetters = RenderView::initializeStandardUniformSetters();
+ LIGHT_COUNT_NAME_ID = StringToInt::lookupId(QLatin1String("lightCount"));
+ for (int i = 0; i < MAX_LIGHTS; ++i) {
+ Q_STATIC_ASSERT_X(MAX_LIGHTS < 10, "can't use the QChar trick anymore");
+ LIGHT_STRUCT_NAMES[i] = QLatin1String("lights[") + QLatin1Char(char('0' + i)) + QLatin1Char(']');
+ LIGHT_POSITION_NAMES[i] = StringToInt::lookupId(LIGHT_STRUCT_NAMES[i] + LIGHT_POSITION_NAME);
+ LIGHT_TYPE_NAMES[i] = StringToInt::lookupId(LIGHT_STRUCT_NAMES[i] + LIGHT_TYPE_NAME);
+ LIGHT_COLOR_NAMES[i] = StringToInt::lookupId(LIGHT_STRUCT_NAMES[i] + LIGHT_COLOR_NAME);
+ LIGHT_INTENSITY_NAMES[i] = StringToInt::lookupId(LIGHT_STRUCT_NAMES[i] + LIGHT_INTENSITY_NAME);
+
+ LIGHT_STRUCT_UNROLL_NAMES[i] = QLatin1String("light_") + QLatin1Char(char('0' + i));
+ LIGHT_POSITION_UNROLL_NAMES[i] = StringToInt::lookupId(LIGHT_STRUCT_UNROLL_NAMES[i] + LIGHT_POSITION_NAME);
+ LIGHT_TYPE_UNROLL_NAMES[i] = StringToInt::lookupId(LIGHT_STRUCT_UNROLL_NAMES[i] + LIGHT_TYPE_NAME);
+ LIGHT_COLOR_UNROLL_NAMES[i] = StringToInt::lookupId(LIGHT_STRUCT_UNROLL_NAMES[i] + LIGHT_COLOR_NAME);
+ LIGHT_INTENSITY_UNROLL_NAMES[i] = StringToInt::lookupId(LIGHT_STRUCT_UNROLL_NAMES[i] + LIGHT_INTENSITY_NAME);
+ }
+ }
+}
+
+RenderView::~RenderView()
+{
+ delete m_stateSet;
+}
+
+namespace {
+
+template<int SortType>
+struct AdjacentSubRangeFinder
+{
+ static bool adjacentSubRange(const RenderCommand &, const RenderCommand &)
+ {
+ Q_UNREACHABLE();
+ return false;
+ }
+};
+
+template<>
+struct AdjacentSubRangeFinder<QSortPolicy::StateChangeCost>
+{
+ static bool adjacentSubRange(const RenderCommand &a, const RenderCommand &b)
+ {
+ return a.m_changeCost == b.m_changeCost;
+ }
+};
+
+template<>
+struct AdjacentSubRangeFinder<QSortPolicy::BackToFront>
+{
+ static bool adjacentSubRange(const RenderCommand &a, const RenderCommand &b)
+ {
+ return a.m_depth == b.m_depth;
+ }
+};
+
+template<>
+struct AdjacentSubRangeFinder<QSortPolicy::Material>
+{
+ static bool adjacentSubRange(const RenderCommand &a, const RenderCommand &b)
+ {
+ return a.m_glShader == b.m_glShader;
+ }
+};
+
+template<>
+struct AdjacentSubRangeFinder<QSortPolicy::FrontToBack>
+{
+ static bool adjacentSubRange(const RenderCommand &a, const RenderCommand &b)
+ {
+ return a.m_depth == b.m_depth;
+ }
+};
+
+template<>
+struct AdjacentSubRangeFinder<QSortPolicy::Texture>
+{
+ static bool adjacentSubRange(const RenderCommand &a, const RenderCommand &b)
+ {
+ // Two renderCommands are adjacent if one contains all the other command's textures
+ QVector<ShaderParameterPack::NamedResource> texturesA = a.m_parameterPack.textures();
+ QVector<ShaderParameterPack::NamedResource> texturesB = b.m_parameterPack.textures();
+
+ if (texturesB.size() > texturesA.size())
+ qSwap(texturesA, texturesB);
+
+ // textureB.size() is always <= textureA.size()
+ for (const ShaderParameterPack::NamedResource &texB : qAsConst(texturesB)) {
+ if (!texturesA.contains(texB))
+ return false;
+ }
+ return true;
+ }
+};
+
+template<typename Predicate>
+int advanceUntilNonAdjacent(const QVector<RenderCommand> &commands,
+ const int beg, const int end, Predicate pred)
+{
+ int i = beg + 1;
+ while (i < end) {
+ if (!pred(*(commands.begin() + beg), *(commands.begin() + i)))
+ break;
+ ++i;
+ }
+ return i;
+}
+
+
+using CommandIt = QVector<RenderCommand>::iterator;
+
+template<int SortType>
+struct SubRangeSorter
+{
+ static void sortSubRange(CommandIt begin, const CommandIt end)
+ {
+ Q_UNUSED(begin);
+ Q_UNUSED(end);
+ Q_UNREACHABLE();
+ }
+};
+
+template<>
+struct SubRangeSorter<QSortPolicy::StateChangeCost>
+{
+ static void sortSubRange(CommandIt begin, const CommandIt end)
+ {
+ std::stable_sort(begin, end, [] (const RenderCommand &a, const RenderCommand &b) {
+ return a.m_changeCost > b.m_changeCost;
+ });
+ }
+};
+
+template<>
+struct SubRangeSorter<QSortPolicy::BackToFront>
+{
+ static void sortSubRange(CommandIt begin, const CommandIt end)
+ {
+ std::stable_sort(begin, end, [] (const RenderCommand &a, const RenderCommand &b) {
+ return a.m_depth > b.m_depth;
+ });
+ }
+};
+
+template<>
+struct SubRangeSorter<QSortPolicy::Material>
+{
+ static void sortSubRange(CommandIt begin, const CommandIt end)
+ {
+ // First we sort by shader
+ std::stable_sort(begin, end, [] (const RenderCommand &a, const RenderCommand &b) {
+ return a.m_glShader > b.m_glShader;
+ });
+ }
+};
+
+template<>
+struct SubRangeSorter<QSortPolicy::FrontToBack>
+{
+ static void sortSubRange(CommandIt begin, const CommandIt end)
+ {
+ std::stable_sort(begin, end, [] (const RenderCommand &a, const RenderCommand &b) {
+ return a.m_depth < b.m_depth;
+ });
+ }
+};
+
+template<>
+struct SubRangeSorter<QSortPolicy::Texture>
+{
+ static void sortSubRange(CommandIt begin, const CommandIt end)
+ {
+ std::stable_sort(begin, end, [] (const RenderCommand &a, const RenderCommand &b) {
+ QVector<ShaderParameterPack::NamedResource> texturesA = a.m_parameterPack.textures();
+ QVector<ShaderParameterPack::NamedResource> texturesB = b.m_parameterPack.textures();
+
+ const int originalTextureASize = texturesA.size();
+
+ if (texturesB.size() > texturesA.size())
+ qSwap(texturesA, texturesB);
+
+ int identicalTextureCount = 0;
+
+ for (const ShaderParameterPack::NamedResource &texB : qAsConst(texturesB)) {
+ if (texturesA.contains(texB))
+ ++identicalTextureCount;
+ }
+
+ return identicalTextureCount < originalTextureASize;
+ });
+ }
+};
+
+int findSubRange(const QVector<RenderCommand> &commands,
+ const int begin, const int end,
+ const QSortPolicy::SortType sortType)
+{
+ switch (sortType) {
+ case QSortPolicy::StateChangeCost:
+ return advanceUntilNonAdjacent(commands, begin, end, AdjacentSubRangeFinder<QSortPolicy::StateChangeCost>::adjacentSubRange);
+ case QSortPolicy::BackToFront:
+ return advanceUntilNonAdjacent(commands, begin, end, AdjacentSubRangeFinder<QSortPolicy::BackToFront>::adjacentSubRange);
+ case QSortPolicy::Material:
+ return advanceUntilNonAdjacent(commands, begin, end, AdjacentSubRangeFinder<QSortPolicy::Material>::adjacentSubRange);
+ case QSortPolicy::FrontToBack:
+ return advanceUntilNonAdjacent(commands, begin, end, AdjacentSubRangeFinder<QSortPolicy::FrontToBack>::adjacentSubRange);
+ case QSortPolicy::Texture:
+ return advanceUntilNonAdjacent(commands, begin, end, AdjacentSubRangeFinder<QSortPolicy::Texture>::adjacentSubRange);
+ default:
+ Q_UNREACHABLE();
+ return end;
+ }
+}
+
+void sortByMaterial(QVector<RenderCommand> &commands, int begin, const int end)
+{
+ // We try to arrange elements so that their rendering cost is minimized for a given shader
+ int rangeEnd = advanceUntilNonAdjacent(commands, begin, end, AdjacentSubRangeFinder<QSortPolicy::Material>::adjacentSubRange);
+ while (begin != end) {
+ if (begin + 1 < rangeEnd) {
+ std::stable_sort(commands.begin() + begin + 1, commands.begin() + rangeEnd, [] (const RenderCommand &a, const RenderCommand &b){
+ return a.m_material.handle() < b.m_material.handle();
+ });
+ }
+ begin = rangeEnd;
+ rangeEnd = advanceUntilNonAdjacent(commands, begin, end, AdjacentSubRangeFinder<QSortPolicy::Material>::adjacentSubRange);
+ }
+}
+
+void sortCommandRange(QVector<RenderCommand> &commands, int begin, const int end, const int level,
+ const QVector<Qt3DRender::QSortPolicy::SortType> &sortingTypes)
+{
+ if (level >= sortingTypes.size())
+ return;
+
+ switch (sortingTypes.at(level)) {
+ case QSortPolicy::StateChangeCost:
+ SubRangeSorter<QSortPolicy::StateChangeCost>::sortSubRange(commands.begin() + begin, commands.begin() + end);
+ break;
+ case QSortPolicy::BackToFront:
+ SubRangeSorter<QSortPolicy::BackToFront>::sortSubRange(commands.begin() + begin, commands.begin() + end);
+ break;
+ case QSortPolicy::Material:
+ // Groups all same shader DNA together
+ SubRangeSorter<QSortPolicy::Material>::sortSubRange(commands.begin() + begin, commands.begin() + end);
+ // Group all same material together (same parameters most likely)
+ sortByMaterial(commands, begin, end);
+ break;
+ case QSortPolicy::FrontToBack:
+ SubRangeSorter<QSortPolicy::FrontToBack>::sortSubRange(commands.begin() + begin, commands.begin() + end);
+ break;
+ case QSortPolicy::Texture:
+ SubRangeSorter<QSortPolicy::Texture>::sortSubRange(commands.begin() + begin, commands.begin() + end);
+ break;
+ default:
+ Q_UNREACHABLE();
+ }
+
+ // For all sub ranges of adjacent item for sortType[i]
+ // Perform filtering with sortType[i + 1]
+ int rangeEnd = findSubRange(commands, begin, end, sortingTypes.at(level));
+ while (begin != end) {
+ sortCommandRange(commands, begin, rangeEnd, level + 1, sortingTypes);
+ begin = rangeEnd;
+ rangeEnd = findSubRange(commands, begin, end, sortingTypes.at(level));
+ }
+}
+
+} // anonymous
+
+void RenderView::sort()
+{
+ // Compares the bitsetKey of the RenderCommands
+ // Key[Depth | StateCost | Shader]
+ sortCommandRange(m_commands, 0, m_commands.size(), 0, m_data.m_sortingTypes);
+
+ // For RenderCommand with the same shader
+ // We compute the adjacent change cost
+
+ // Minimize uniform changes
+ int i = 0;
+ const int commandSize = m_commands.size();
+ while (i < commandSize) {
+ int j = i;
+
+ // Advance while commands share the same shader
+ while (i < commandSize &&
+ m_commands[j].m_glShader == m_commands[i].m_glShader)
+ ++i;
+
+ if (i - j > 0) { // Several commands have the same shader, so we minimize uniform changes
+ PackUniformHash cachedUniforms = m_commands[j++].m_parameterPack.uniforms();
+
+ while (j < i) {
+ // We need the reference here as we are modifying the original container
+ // not the copy
+ PackUniformHash &uniforms = m_commands[j].m_parameterPack.m_uniforms;
+
+ for (int u = 0; u < uniforms.keys.size();) {
+ // We are comparing the values:
+ // - raw uniform values
+ // - the texture Node id if the uniform represents a texture
+ // since all textures are assigned texture units before the RenderCommands
+ // sharing the same material (shader) are rendered, we can't have the case
+ // where two uniforms, referencing the same texture eventually have 2 different
+ // texture unit values
+ const int uniformNameId = uniforms.keys.at(u);
+ const UniformValue &refValue = cachedUniforms.value(uniformNameId);
+ const UniformValue &newValue = uniforms.values.at(u);
+ if (newValue == refValue) {
+ uniforms.erase(u);
+ } else {
+ // Record updated value so that subsequent comparison
+ // for the next command will be made againts latest
+ // uniform value
+ cachedUniforms.insert(uniformNameId, newValue);
+ ++u;
+ }
+ }
+ ++j;
+ }
+ }
+ }
+}
+
+void RenderView::setRenderer(Renderer *renderer)
+{
+ m_renderer = renderer;
+ m_manager = renderer->nodeManagers();
+}
+
+void RenderView::addClearBuffers(const ClearBuffers *cb) {
+ QClearBuffers::BufferTypeFlags type = cb->type();
+
+ if (type & QClearBuffers::StencilBuffer) {
+ m_clearStencilValue = cb->clearStencilValue();
+ m_clearBuffer |= QClearBuffers::StencilBuffer;
+ }
+ if (type & QClearBuffers::DepthBuffer) {
+ m_clearDepthValue = cb->clearDepthValue();
+ m_clearBuffer |= QClearBuffers::DepthBuffer;
+ }
+ // keep track of global ClearColor (if set) and collect all DrawBuffer-specific
+ // ClearColors
+ if (type & QClearBuffers::ColorBuffer) {
+ ClearBufferInfo clearBufferInfo;
+ clearBufferInfo.clearColor = cb->clearColor();
+
+ if (cb->clearsAllColorBuffers()) {
+ m_globalClearColorBuffer = clearBufferInfo;
+ m_clearBuffer |= QClearBuffers::ColorBuffer;
+ } else {
+ if (cb->bufferId()) {
+ const RenderTargetOutput *targetOutput = m_manager->attachmentManager()->lookupResource(cb->bufferId());
+ if (targetOutput) {
+ clearBufferInfo.attchmentPoint = targetOutput->point();
+ // Note: a job is later performed to find the drawIndex from the buffer attachment point
+ // using the AttachmentPack
+ m_specificClearColorBuffers.push_back(clearBufferInfo);
+ }
+ }
+ }
+ }
+}
+
+// If we are there, we know that entity had a GeometryRenderer + Material
+EntityRenderCommandData RenderView::buildDrawRenderCommands(const QVector<Entity *> &entities,
+ int offset, int count) const
+{
+ GLShaderManager *glShaderManager = m_renderer->glResourceManagers()->glShaderManager();
+ EntityRenderCommandData commands;
+
+ commands.reserve(count);
+
+ for (int i = 0; i < count; ++i) {
+ const int idx = offset + i;
+ Entity *entity = entities.at(idx);
+ GeometryRenderer *geometryRenderer = nullptr;
+ HGeometryRenderer geometryRendererHandle = entity->componentHandle<GeometryRenderer>();
+
+ // There is a geometry renderer with geometry
+ if ((geometryRenderer = m_manager->geometryRendererManager()->data(geometryRendererHandle)) != nullptr
+ && geometryRenderer->isEnabled()
+ && !geometryRenderer->geometryId().isNull()) {
+
+ const Qt3DCore::QNodeId materialComponentId = entity->componentUuid<Material>();
+ const HMaterial materialHandle = entity->componentHandle<Material>();
+ const QVector<RenderPassParameterData> renderPassData = m_parameters.value(materialComponentId);
+
+ HGeometry geometryHandle = m_manager->geometryManager()->lookupHandle(geometryRenderer->geometryId());
+ Geometry *geometry = m_manager->geometryManager()->data(geometryHandle);
+
+ // 1 RenderCommand per RenderPass pass on an Entity with a Mesh
+ for (const RenderPassParameterData &passData : renderPassData) {
+ // Add the RenderPass Parameters
+ RenderCommand command = {};
+ command.m_geometryRenderer = geometryRendererHandle;
+ command.m_geometry = geometryHandle;
+
+ command.m_material = materialHandle;
+ // For RenderPass based states we use the globally set RenderState
+ // if no renderstates are defined as part of the pass. That means:
+ // RenderPass { renderStates: [] } will use the states defined by
+ // StateSet in the FrameGraph
+ RenderPass *pass = passData.pass;
+ if (pass->hasRenderStates()) {
+ command.m_stateSet = RenderStateSetPtr::create();
+ addStatesToRenderStateSet(command.m_stateSet.data(), pass->renderStates(), m_manager->renderStateManager());
+ if (m_stateSet != nullptr)
+ command.m_stateSet->merge(m_stateSet);
+ command.m_changeCost = m_renderer->defaultRenderState()->changeCost(command.m_stateSet.data());
+ }
+ command.m_shaderId = pass->shaderProgram();
+ command.m_glShader = glShaderManager->lookupResource(command.m_shaderId);
+
+ // It takes two frames to have a valid command as we can only
+ // reference a glShader at frame n if it has been loaded at frame n - 1
+ if (!command.m_glShader)
+ continue;
+
+ { // Scoped to show extent
+
+ // Update the draw command with what's going to be needed for the drawing
+ int primitiveCount = geometryRenderer->vertexCount();
+ int estimatedCount = 0;
+ Attribute *indexAttribute = nullptr;
+ Attribute *indirectAttribute = nullptr;
+
+ const QVector<Qt3DCore::QNodeId> attributeIds = geometry->attributes();
+ for (Qt3DCore::QNodeId attributeId : attributeIds) {
+ Attribute *attribute = m_manager->attributeManager()->lookupResource(attributeId);
+ switch (attribute->attributeType()) {
+ case QAttribute::IndexAttribute:
+ indexAttribute = attribute;
+ break;
+ case QAttribute::DrawIndirectAttribute:
+ indirectAttribute = attribute;
+ break;
+ case QAttribute::VertexAttribute:
+ estimatedCount = std::max(int(attribute->count()), estimatedCount);
+ break;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+ }
+
+ command.m_drawIndexed = (indexAttribute != nullptr);
+ command.m_drawIndirect = (indirectAttribute != nullptr);
+
+ // Update the draw command with all the information required for the drawing
+ if (command.m_drawIndexed) {
+ command.m_indexAttributeDataType = GraphicsContext::glDataTypeFromAttributeDataType(indexAttribute->vertexBaseType());
+ command.m_indexAttributeByteOffset = indexAttribute->byteOffset() + geometryRenderer->indexBufferByteOffset();
+ }
+
+ // Note: we only care about the primitiveCount when using direct draw calls
+ // For indirect draw calls it is assumed the buffer was properly set already
+ if (command.m_drawIndirect) {
+ command.m_indirectAttributeByteOffset = indirectAttribute->byteOffset();
+ command.m_indirectDrawBuffer = m_manager->bufferManager()->lookupHandle(indirectAttribute->bufferId());
+ } else {
+ // Use the count specified by the GeometryRender
+ // If not specify use the indexAttribute count if present
+ // Otherwise tries to use the count from the attribute with the highest count
+ if (primitiveCount == 0) {
+ if (indexAttribute)
+ primitiveCount = indexAttribute->count();
+ else
+ primitiveCount = estimatedCount;
+ }
+ }
+
+ command.m_primitiveCount = primitiveCount;
+ command.m_primitiveType = geometryRenderer->primitiveType();
+ command.m_primitiveRestartEnabled = geometryRenderer->primitiveRestartEnabled();
+ command.m_restartIndexValue = geometryRenderer->restartIndexValue();
+ command.m_firstInstance = geometryRenderer->firstInstance();
+ command.m_instanceCount = geometryRenderer->instanceCount();
+ command.m_firstVertex = geometryRenderer->firstVertex();
+ command.m_indexOffset = geometryRenderer->indexOffset();
+ command.m_verticesPerPatch = geometryRenderer->verticesPerPatch();
+ } // scope
+
+
+ commands.push_back(entity,
+ std::move(command),
+ std::move(passData));
+ }
+ }
+ }
+
+ return commands;
+}
+
+EntityRenderCommandData RenderView::buildComputeRenderCommands(const QVector<Entity *> &entities,
+ int offset, int count) const
+{
+ // If the RenderView contains only a ComputeDispatch then it cares about
+ // A ComputeDispatch is also implicitely a NoDraw operation
+ // enabled flag
+ // layer component
+ // material/effect/technique/parameters/filters/
+ EntityRenderCommandData commands;
+ GLShaderManager *glShaderManager = m_renderer->glResourceManagers()->glShaderManager();
+
+ commands.reserve(count);
+
+ for (int i = 0; i < count; ++i) {
+ const int idx = offset + i;
+ Entity *entity = entities.at(idx);
+ ComputeCommand *computeJob = nullptr;
+ HComputeCommand computeCommandHandle = entity->componentHandle<ComputeCommand>();
+ if ((computeJob = nodeManagers()->computeJobManager()->data(computeCommandHandle)) != nullptr
+ && computeJob->isEnabled()) {
+
+ const Qt3DCore::QNodeId materialComponentId = entity->componentUuid<Material>();
+ const QVector<RenderPassParameterData> renderPassData = m_parameters.value(materialComponentId);
+
+ // 1 RenderCommand per RenderPass pass on an Entity with a Mesh
+ for (const RenderPassParameterData &passData : renderPassData) {
+ // Add the RenderPass Parameters
+ RenderCommand command = {};
+ RenderPass *pass = passData.pass;
+
+ if (pass->hasRenderStates()) {
+ command.m_stateSet = RenderStateSetPtr::create();
+ addStatesToRenderStateSet(command.m_stateSet.data(), pass->renderStates(), m_manager->renderStateManager());
+
+ // Merge per pass stateset with global stateset
+ // so that the local stateset only overrides
+ if (m_stateSet != nullptr)
+ command.m_stateSet->merge(m_stateSet);
+ command.m_changeCost = m_renderer->defaultRenderState()->changeCost(command.m_stateSet.data());
+ }
+ command.m_shaderId = pass->shaderProgram();
+ command.m_glShader = glShaderManager->lookupResource(command.m_shaderId);
+
+ // It takes two frames to have a valid command as we can only
+ // reference a glShader at frame n if it has been loaded at frame n - 1
+ if (!command.m_glShader)
+ continue;
+
+ command.m_computeCommand = computeCommandHandle;
+ command.m_type = RenderCommand::Compute;
+ command.m_workGroups[0] = std::max(m_workGroups[0], computeJob->x());
+ command.m_workGroups[1] = std::max(m_workGroups[1], computeJob->y());
+ command.m_workGroups[2] = std::max(m_workGroups[2], computeJob->z());
+
+ commands.push_back(entity,
+ std::move(command),
+ std::move(passData));
+ }
+ }
+ }
+
+ return commands;
+}
+
+void RenderView::updateRenderCommand(EntityRenderCommandData *renderCommandData,
+ int offset,
+ int count)
+{
+ // Note: since many threads can be building render commands
+ // we need to ensure that the UniformBlockValueBuilder they are using
+ // is only accessed from the same thread
+ UniformBlockValueBuilder *builder = new UniformBlockValueBuilder();
+ builder->shaderDataManager = m_manager->shaderDataManager();
+ builder->textureManager = m_manager->textureManager();
+ m_localData.setLocalData(builder);
+
+ for (int i = 0, m = count; i < m; ++i) {
+ const int idx = offset + i;
+ Entity *entity = renderCommandData->entities.at(idx);
+ const RenderPassParameterData passData = renderCommandData->passesData.at(idx);
+ RenderCommand &command = renderCommandData->commands[idx];
+
+ // Pick which lights to take in to account.
+ // For now decide based on the distance by taking the MAX_LIGHTS closest lights.
+ // 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;
+ EnvironmentLight *environmentLight = nullptr;
+
+ if (command.m_type == RenderCommand::Draw) {
+ // 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 = Vector3D::dotProduct(entity->worldBoundingVolume()->center() - m_data.m_eyePos, m_data.m_eyeViewDir);
+
+ environmentLight = m_environmentLight;
+ lightSources = m_lightSources;
+
+ 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;
+ });
+ }
+ lightSources = lightSources.mid(0, std::max(lightSources.size(), MAX_LIGHTS));
+ } else { // Compute
+ // Note: if frameCount has reached 0 in the previous frame, isEnabled
+ // would be false
+ ComputeCommand *computeJob = m_manager->computeJobManager()->data(command.m_computeCommand);
+ if (computeJob->runType() == QComputeCommand::Manual)
+ computeJob->updateFrameCount();
+ }
+
+ ParameterInfoList globalParameters = passData.parameterInfo;
+ // setShaderAndUniforms can initialize a localData
+ // make sure this is cleared before we leave this function
+
+ setShaderAndUniforms(&command,
+ globalParameters,
+ entity,
+ lightSources,
+ environmentLight);
+ }
+
+ // We reset the local data once we are done with it
+ m_localData.setLocalData(nullptr);
+}
+
+void RenderView::updateMatrices()
+{
+ if (m_data.m_renderCameraNode && m_data.m_renderCameraLens && m_data.m_renderCameraLens->isEnabled()) {
+ 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 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.
+ const QMatrix3x3 normalMat = convertToQMatrix4x4(m_data.m_viewMatrix).normalMatrix();
+ // dir = normalize(QVector3D(0, 0, -1) * normalMat)
+ setEyeViewDirection(Vector3D(-normalMat(2, 0), -normalMat(2, 1), -normalMat(2, 2)).normalized());
+ }
+}
+
+void RenderView::setUniformValue(ShaderParameterPack &uniformPack, int nameId, const UniformValue &value) const
+{
+ // At this point a uniform value can only be a scalar type
+ // or a Qt3DCore::QNodeId corresponding to a Texture or Image
+ // 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 *nodeIds = value.constData<Qt3DCore::QNodeId>();
+
+ const int uniformArraySize = value.byteSize() / sizeof(Qt3DCore::QNodeId);
+ UniformValue::ValueType resourceType = UniformValue::TextureValue;
+
+ for (int i = 0; i < uniformArraySize; ++i) {
+ const Qt3DCore::QNodeId resourceId = nodeIds[i];
+
+ const Texture *tex = m_manager->textureManager()->lookupResource(resourceId);
+ if (tex != nullptr) {
+ uniformPack.setTexture(nameId, i, resourceId);
+ } else {
+ const ShaderImage *img = m_manager->shaderImageManager()->lookupResource(resourceId);
+ if (img != nullptr) {
+ resourceType = UniformValue::ShaderImageValue;
+ uniformPack.setImage(nameId, i, resourceId);
+ }
+ }
+ }
+
+ // This uniform will be overridden in SubmissionContext::setParameters
+ // and -1 values will be replaced by valid Texture or Image units
+ UniformValue uniformValue(uniformArraySize * sizeof(int), resourceType);
+ std::fill(uniformValue.data<int>(), uniformValue.data<int>() + uniformArraySize, -1);
+ uniformPack.setUniform(nameId, uniformValue);
+ } else {
+ uniformPack.setUniform(nameId, value);
+ }
+}
+
+void RenderView::setStandardUniformValue(ShaderParameterPack &uniformPack,
+ int glslNameId,
+ int nameId,
+ Entity *entity,
+ const Matrix4x4 &worldTransform) const
+{
+ uniformPack.setUniform(glslNameId, standardUniformValue(ms_standardUniformSetters[nameId], entity, worldTransform));
+}
+
+void RenderView::setUniformBlockValue(ShaderParameterPack &uniformPack,
+ GLShader *shader,
+ const ShaderUniformBlock &block,
+ const UniformValue &value) const
+{
+ Q_UNUSED(shader)
+
+ if (value.valueType() == UniformValue::NodeId) {
+
+ Buffer *buffer = nullptr;
+ if ((buffer = m_manager->bufferManager()->lookupResource(*value.constData<Qt3DCore::QNodeId>())) != nullptr) {
+ BlockToUBO uniformBlockUBO;
+ uniformBlockUBO.m_blockIndex = block.m_index;
+ uniformBlockUBO.m_bufferID = buffer->peerId();
+ uniformBlockUBO.m_needsUpdate = false;
+ uniformPack.setUniformBuffer(std::move(uniformBlockUBO));
+ // Buffer update to GL buffer will be done at render time
+ }
+ }
+}
+
+void RenderView::setShaderStorageValue(ShaderParameterPack &uniformPack,
+ GLShader *shader,
+ const ShaderStorageBlock &block,
+ const UniformValue &value) const
+{
+ Q_UNUSED(shader)
+ if (value.valueType() == UniformValue::NodeId) {
+ Buffer *buffer = nullptr;
+ if ((buffer = m_manager->bufferManager()->lookupResource(*value.constData<Qt3DCore::QNodeId>())) != nullptr) {
+ BlockToSSBO shaderStorageBlock;
+ shaderStorageBlock.m_blockIndex = block.m_index;
+ shaderStorageBlock.m_bufferID = buffer->peerId();
+ shaderStorageBlock.m_bindingIndex = block.m_binding;
+ uniformPack.setShaderStorageBuffer(shaderStorageBlock);
+ // Buffer update to GL buffer will be done at render time
+ }
+ }
+}
+
+void RenderView::setDefaultUniformBlockShaderDataValue(ShaderParameterPack &uniformPack, GLShader *shader, ShaderData *shaderData, const QString &structName) const
+{
+ UniformBlockValueBuilder *builder = m_localData.localData();
+ builder->activeUniformNamesToValue.clear();
+
+ // Set the view matrix to be used to transform "Transformed" properties in the ShaderData
+ builder->viewMatrix = m_data.m_viewMatrix;
+ // Force to update the whole block
+ builder->updatedPropertiesOnly = false;
+ // Retrieve names and description of each active uniforms in the uniform block
+ builder->uniforms = shader->activeUniformsForUniformBlock(-1);
+ // Build name-value map for the block
+ builder->buildActiveUniformNameValueMapStructHelper(shaderData, structName);
+ // Set uniform values for each entrie of the block name-value map
+ QHash<int, QVariant>::const_iterator activeValuesIt = builder->activeUniformNamesToValue.constBegin();
+ const QHash<int, QVariant>::const_iterator activeValuesEnd = builder->activeUniformNamesToValue.constEnd();
+
+ // TO DO: Make the ShaderData store UniformValue
+ while (activeValuesIt != activeValuesEnd) {
+ setUniformValue(uniformPack, activeValuesIt.key(), UniformValue::fromVariant(activeValuesIt.value()));
+ ++activeValuesIt;
+ }
+}
+
+void RenderView::setShaderAndUniforms(RenderCommand *command,
+ ParameterInfoList &parameters,
+ Entity *entity,
+ const QVector<LightSource> &activeLightSources,
+ EnvironmentLight *environmentLight) const
+{
+ // The VAO Handle is set directly in the renderer thread so as to avoid having to use a mutex here
+ // Set shader, technique, and effect by basically doing :
+ // ShaderProgramManager[MaterialManager[frontentEntity->id()]->Effect->Techniques[TechniqueFilter->name]->RenderPasses[RenderPassFilter->name]];
+ // The Renderer knows that if one of those is null, a default material / technique / effect as to be used
+
+ // Find all RenderPasses (in order) matching values set in the RenderPassFilter
+ // Get list of parameters for the Material, Effect, and Technique
+ // For each ParameterBinder in the RenderPass -> create a QUniformPack
+ // Once that works, improve that to try and minimize QUniformPack updates
+
+ GLShader *shader = command->m_glShader;
+ if (shader != nullptr && shader->isLoaded()) {
+
+ // Builds the QUniformPack, sets shader standard uniforms and store attributes name / glname bindings
+ // If a parameter is defined and not found in the bindings it is assumed to be a binding of Uniform type with the glsl name
+ // equals to the parameter name
+ const QVector<int> uniformNamesIds = shader->uniformsNamesIds();
+ const QVector<int> standardUniformNamesIds = shader->standardUniformNameIds();
+ const QVector<int> uniformBlockNamesIds = shader->uniformBlockNamesIds();
+ const QVector<int> shaderStorageBlockNamesIds = shader->storageBlockNamesIds();
+ const QVector<int> attributeNamesIds = shader->attributeNamesIds();
+
+ // Set fragData Name and index
+ // Later on we might want to relink the shader if attachments have changed
+ // But for now we set them once and for all
+ if (!m_renderTarget.isNull() && !shader->isLoaded()) {
+ QHash<QString, int> fragOutputs;
+ const auto atts = m_attachmentPack.attachments();
+ for (const Attachment &att : atts) {
+ if (att.m_point <= QRenderTargetOutput::Color15)
+ fragOutputs.insert(att.m_name, att.m_point);
+ }
+ // Set frag outputs in the shaders if hash not empty
+ if (!fragOutputs.isEmpty())
+ shader->setFragOutputs(fragOutputs);
+ }
+
+ if (!uniformNamesIds.isEmpty() || !standardUniformNamesIds.isEmpty() ||
+ !attributeNamesIds.isEmpty() ||
+ !shaderStorageBlockNamesIds.isEmpty() || !attributeNamesIds.isEmpty()) {
+
+ // Set default standard uniforms without bindings
+ const Matrix4x4 worldTransform = *(entity->worldTransform());
+
+ for (const int uniformNameId : standardUniformNamesIds)
+ setStandardUniformValue(command->m_parameterPack, uniformNameId, uniformNameId, entity, worldTransform);
+
+ // Set default attributes
+ command->m_activeAttributes = attributeNamesIds;
+
+ // At this point we know whether the command is a valid draw command or not
+ // We still need to process the uniforms as the command could be a compute command
+ command->m_isValid = !command->m_activeAttributes.empty();
+
+ // Parameters remaining could be
+ // -> uniform scalar / vector
+ // -> uniform struct / arrays
+ // -> uniform block / array (4.3)
+ // -> ssbo block / array (4.3)
+
+ ParameterInfoList::const_iterator it = parameters.cbegin();
+ const ParameterInfoList::const_iterator parametersEnd = parameters.cend();
+
+ while (it != parametersEnd) {
+ Parameter *param = m_manager->data<Parameter, ParameterManager>(it->handle);
+ const UniformValue &uniformValue = param->uniformValue();
+ if (uniformNamesIds.contains(it->nameId)) { // Parameter is a regular uniform
+ setUniformValue(command->m_parameterPack, it->nameId, uniformValue);
+ } else if (uniformBlockNamesIds.indexOf(it->nameId) != -1) { // Parameter is a uniform block
+ setUniformBlockValue(command->m_parameterPack, shader, shader->uniformBlockForBlockNameId(it->nameId), uniformValue);
+ } else if (shaderStorageBlockNamesIds.indexOf(it->nameId) != -1) { // Parameters is a SSBO
+ setShaderStorageValue(command->m_parameterPack, shader, shader->storageBlockForBlockNameId(it->nameId), uniformValue);
+ } else { // Parameter is a struct
+ ShaderData *shaderData = nullptr;
+ if (uniformValue.valueType() == UniformValue::NodeId &&
+ (shaderData = m_manager->shaderDataManager()->lookupResource(*uniformValue.constData<Qt3DCore::QNodeId>())) != nullptr) {
+ // Try to check if we have a struct or array matching a QShaderData parameter
+ setDefaultUniformBlockShaderDataValue(command->m_parameterPack, shader, shaderData, StringToInt::lookupString(it->nameId));
+ }
+ // Otherwise: param unused by current shader
+ }
+ ++it;
+ }
+
+ // Lights
+
+ int lightIdx = 0;
+ for (const LightSource &lightSource : activeLightSources) {
+ if (lightIdx == MAX_LIGHTS)
+ break;
+ Entity *lightEntity = lightSource.entity;
+ const Matrix4x4 lightWorldTransform = *(lightEntity->worldTransform());
+ const Vector3D worldPos = lightWorldTransform * Vector3D(0.0f, 0.0f, 0.0f);
+ for (Light *light : lightSource.lights) {
+ if (!light->isEnabled())
+ continue;
+
+ ShaderData *shaderData = m_manager->shaderDataManager()->lookupResource(light->shaderData());
+ if (!shaderData)
+ continue;
+
+ if (lightIdx == MAX_LIGHTS)
+ break;
+
+ // 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], Vector3D(1.0f, 1.0f, 1.0f));
+ setUniformValue(command->m_parameterPack, LIGHT_INTENSITY_NAMES[lightIdx], 0.5f);
+
+ setUniformValue(command->m_parameterPack, LIGHT_POSITION_UNROLL_NAMES[lightIdx], worldPos);
+ setUniformValue(command->m_parameterPack, LIGHT_TYPE_UNROLL_NAMES[lightIdx], int(QAbstractLight::PointLight));
+ setUniformValue(command->m_parameterPack, LIGHT_COLOR_UNROLL_NAMES[lightIdx], Vector3D(1.0f, 1.0f, 1.0f));
+ setUniformValue(command->m_parameterPack, LIGHT_INTENSITY_UNROLL_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
+ Matrix4x4 *worldTransform = lightEntity->worldTransform();
+ if (worldTransform)
+ shaderData->updateWorldTransform(*worldTransform);
+
+ setDefaultUniformBlockShaderDataValue(command->m_parameterPack, shader, shaderData, LIGHT_STRUCT_NAMES[lightIdx]);
+ setDefaultUniformBlockShaderDataValue(command->m_parameterPack, shader, shaderData, LIGHT_STRUCT_UNROLL_NAMES[lightIdx]);
+ ++lightIdx;
+ }
+ }
+
+ if (uniformNamesIds.contains(LIGHT_COUNT_NAME_ID))
+ setUniformValue(command->m_parameterPack, LIGHT_COUNT_NAME_ID, UniformValue(qMax((environmentLight ? 0 : 1), lightIdx)));
+
+ // 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], 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], Vector3D(1.0f, 1.0f, 1.0f));
+ setUniformValue(command->m_parameterPack, LIGHT_INTENSITY_NAMES[0], 0.5f);
+
+ setUniformValue(command->m_parameterPack, LIGHT_POSITION_UNROLL_NAMES[0], Vector3D(10.0f, 10.0f, 0.0f));
+ setUniformValue(command->m_parameterPack, LIGHT_TYPE_UNROLL_NAMES[0], int(QAbstractLight::PointLight));
+ setUniformValue(command->m_parameterPack, LIGHT_COLOR_UNROLL_NAMES[0], Vector3D(1.0f, 1.0f, 1.0f));
+ setUniformValue(command->m_parameterPack, LIGHT_INTENSITY_UNROLL_NAMES[0], 0.5f);
+ }
+
+ // Environment Light
+ int envLightCount = 0;
+ if (environmentLight && environmentLight->isEnabled()) {
+ ShaderData *shaderData = m_manager->shaderDataManager()->lookupResource(environmentLight->shaderData());
+ if (shaderData) {
+ setDefaultUniformBlockShaderDataValue(command->m_parameterPack, shader, shaderData, QStringLiteral("envLight"));
+ envLightCount = 1;
+ }
+ } else {
+ // with some drivers, samplers (like the envbox sampler) need to be bound even though
+ // they may not be actually used, otherwise draw calls can fail
+ static const int irradianceId = StringToInt::lookupId(QLatin1String("envLight.irradiance"));
+ static const int specularId = StringToInt::lookupId(QLatin1String("envLight.specular"));
+ setUniformValue(command->m_parameterPack, irradianceId, m_renderer->submissionContext()->maxTextureUnitsCount());
+ setUniformValue(command->m_parameterPack, specularId, m_renderer->submissionContext()->maxTextureUnitsCount());
+ }
+ setUniformValue(command->m_parameterPack, StringToInt::lookupId(QStringLiteral("envLightCount")), envLightCount);
+ }
+ }
+}
+
+bool RenderView::hasBlitFramebufferInfo() const
+{
+ return m_hasBlitFramebufferInfo;
+}
+
+void RenderView::setHasBlitFramebufferInfo(bool hasBlitFramebufferInfo)
+{
+ m_hasBlitFramebufferInfo = hasBlitFramebufferInfo;
+}
+
+BlitFramebufferInfo RenderView::blitFrameBufferInfo() const
+{
+ return m_blitFrameBufferInfo;
+}
+
+void RenderView::setBlitFrameBufferInfo(const BlitFramebufferInfo &blitFrameBufferInfo)
+{
+ m_blitFrameBufferInfo = blitFrameBufferInfo;
+}
+
+bool RenderView::isDownloadBuffersEnable() const
+{
+ return m_isDownloadBuffersEnable;
+}
+
+void RenderView::setIsDownloadBuffersEnable(bool isDownloadBuffersEnable)
+{
+ m_isDownloadBuffersEnable = isDownloadBuffersEnable;
+}
+
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
diff --git a/src/plugins/renderers/opengl/renderer/renderview_p.h b/src/plugins/renderers/opengl/renderer/renderview_p.h
new file mode 100644
index 000000000..c12ee627a
--- /dev/null
+++ b/src/plugins/renderers/opengl/renderer/renderview_p.h
@@ -0,0 +1,394 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB).
+** Copyright (C) 2016 The Qt Company Ltd and/or its subsidiary(-ies).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DRENDER_RENDER_RENDERVIEW_H
+#define QT3DRENDER_RENDER_RENDERVIEW_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <Qt3DRender/qparameter.h>
+#include <Qt3DRender/qclearbuffers.h>
+#include <Qt3DRender/qlayerfilter.h>
+#include <Qt3DRender/private/clearbuffers_p.h>
+#include <Qt3DRender/private/cameralens_p.h>
+#include <Qt3DRender/private/attachmentpack_p.h>
+#include <Qt3DRender/private/handle_types_p.h>
+#include <Qt3DRender/private/qsortpolicy_p.h>
+#include <Qt3DRender/private/lightsource_p.h>
+#include <Qt3DRender/private/qmemorybarrier_p.h>
+#include <Qt3DRender/private/qrendercapture_p.h>
+#include <Qt3DRender/private/qblitframebuffer_p.h>
+#include <Qt3DRender/private/qwaitfence_p.h>
+
+#include <Qt3DCore/private/qframeallocator_p.h>
+#include <Qt3DRender/private/aligned_malloc_p.h>
+
+#include <renderer_p.h>
+// TODO: Move out once this is all refactored
+#include <renderviewjobutils_p.h>
+
+#include <QVector>
+#include <QSurface>
+#include <QMutex>
+#include <QColor>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+class QRenderPass;
+
+namespace Render {
+
+class Renderer;
+class NodeManagers;
+class RenderCommand;
+class RenderPassFilter;
+class TechniqueFilter;
+class ViewportNode;
+class Effect;
+class RenderPass;
+
+typedef QPair<ShaderUniform, QVariant> ActivePropertyContent;
+typedef QPair<QString, ActivePropertyContent > ActiveProperty;
+
+struct Q_AUTOTEST_EXPORT ClearBufferInfo
+{
+ int drawBufferIndex = 0;
+ QRenderTargetOutput::AttachmentPoint attchmentPoint = QRenderTargetOutput::Color0;
+ QVector4D clearColor;
+};
+
+struct Q_AUTOTEST_EXPORT BlitFramebufferInfo
+{
+ Qt3DCore::QNodeId sourceRenderTargetId;
+ Qt3DCore::QNodeId destinationRenderTargetId;
+ QRect sourceRect;
+ QRect destinationRect;
+ Qt3DRender::QRenderTargetOutput::AttachmentPoint sourceAttachmentPoint;
+ Qt3DRender::QRenderTargetOutput::AttachmentPoint destinationAttachmentPoint;
+ QBlitFramebuffer::InterpolationMethod interpolationMethod;
+};
+
+// This class is kind of analogous to RenderBin but I want to avoid trampling
+// on that until we get this working
+
+class Q_AUTOTEST_EXPORT RenderView
+{
+public:
+ RenderView();
+ ~RenderView();
+
+ QT3D_ALIGNED_MALLOC_AND_FREE()
+
+ // TODO: Add a way to specify a sort predicate for the RenderCommands
+ void sort();
+
+ void setRenderer(Renderer *renderer);
+ inline void setSurfaceSize(const QSize &size) Q_DECL_NOTHROW { m_surfaceSize = size; }
+ inline Renderer *renderer() const Q_DECL_NOTHROW { return m_renderer; }
+ inline NodeManagers *nodeManagers() const Q_DECL_NOTHROW { return m_manager; }
+ inline const QSize &surfaceSize() const Q_DECL_NOTHROW { return m_surfaceSize; }
+ inline void setDevicePixelRatio(qreal r) Q_DECL_NOTHROW { m_devicePixelRatio = r; }
+ inline qreal devicePixelRatio() const Q_DECL_NOTHROW { return m_devicePixelRatio; }
+
+ inline void setRenderCameraLens(CameraLens *renderCameraLens) Q_DECL_NOTHROW { m_data.m_renderCameraLens = renderCameraLens; }
+ inline CameraLens *renderCameraLens() const Q_DECL_NOTHROW { return m_data.m_renderCameraLens; }
+
+ 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 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 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 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 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; }
+
+ inline void appendProximityFilterId(const Qt3DCore::QNodeId proximityFilterId) { m_data.m_proximityFilterIds.push_back(proximityFilterId); }
+ inline Qt3DCore::QNodeIdVector proximityFilterIds() const { return m_data.m_proximityFilterIds; }
+
+ inline void appendInsertFenceId(const Qt3DCore::QNodeId setFenceId) { m_insertFenceIds.push_back(setFenceId); }
+ // We prefix with get to avoid confusion when it is called
+ inline Qt3DCore::QNodeIdVector insertFenceIds() const { return m_insertFenceIds; }
+
+ inline void appendWaitFence(const QWaitFenceData &data) { m_waitFences.push_back(data); }
+ inline QVector<QWaitFenceData> waitFences() const { return m_waitFences; }
+
+ inline void setRenderPassFilter(const RenderPassFilter *rpFilter) Q_DECL_NOTHROW { m_data.m_passFilter = rpFilter; }
+ inline const RenderPassFilter *renderPassFilter() const Q_DECL_NOTHROW { return m_data.m_passFilter; }
+
+ inline void setTechniqueFilter(const TechniqueFilter *filter) Q_DECL_NOTHROW { m_data.m_techniqueFilter = filter; }
+ inline const TechniqueFilter *techniqueFilter() const Q_DECL_NOTHROW { return m_data.m_techniqueFilter; }
+
+ inline RenderStateSet *stateSet() const Q_DECL_NOTHROW { return m_stateSet; }
+ void setStateSet(RenderStateSet *stateSet) Q_DECL_NOTHROW { m_stateSet = stateSet; }
+
+ inline bool noDraw() const Q_DECL_NOTHROW { return m_noDraw; }
+ void setNoDraw(bool noDraw) Q_DECL_NOTHROW { m_noDraw = noDraw; }
+
+ inline bool isCompute() const Q_DECL_NOTHROW { return m_compute; }
+ void setCompute(bool compute) Q_DECL_NOTHROW { m_compute = compute; }
+
+ void setComputeWorkgroups(int x, int y, int z) Q_DECL_NOTHROW { m_workGroups[0] = x; m_workGroups[1] = y; m_workGroups[2] = z; }
+ const int *computeWorkGroups() const Q_DECL_NOTHROW { return m_workGroups; }
+ inline bool frustumCulling() const Q_DECL_NOTHROW { return m_frustumCulling; }
+ void setFrustumCulling(bool frustumCulling) Q_DECL_NOTHROW { m_frustumCulling = frustumCulling; }
+
+ inline void setMaterialParameterTable(const MaterialParameterGathererData &parameters) Q_DECL_NOTHROW { m_parameters = parameters; }
+
+ // TODO: Get rid of this overly complex memory management by splitting out the
+ // InnerData as a RenderViewConfig struct. This can be created by setRenderViewConfigFromFrameGraphLeafNode
+ // and passed along with the RenderView to the functions that populate the renderview
+ inline void setViewport(const QRectF &vp) Q_DECL_NOTHROW { m_viewport = vp; }
+ inline QRectF viewport() const Q_DECL_NOTHROW { return m_viewport; }
+
+ inline float gamma() const Q_DECL_NOTHROW { return m_gamma; }
+ inline void setGamma(float gamma) Q_DECL_NOTHROW { m_gamma = gamma; }
+
+ // depth and stencil ClearBuffers are cached locally
+ // color ClearBuffers are collected, as there may be multiple
+ // color buffers to be cleared. we need to apply all these at rendering
+ void addClearBuffers(const ClearBuffers *cb);
+ inline QVector<ClearBufferInfo> specificClearColorBufferInfo() const { return m_specificClearColorBuffers; }
+ inline QVector<ClearBufferInfo> &specificClearColorBufferInfo() { return m_specificClearColorBuffers; }
+ inline ClearBufferInfo globalClearColorBufferInfo() const { return m_globalClearColorBuffer; }
+
+ inline QClearBuffers::BufferTypeFlags clearTypes() const { return m_clearBuffer; }
+ inline float clearDepthValue() const { return m_clearDepthValue; }
+ inline int clearStencilValue() const { return m_clearStencilValue; }
+
+ RenderPassList passesAndParameters(ParameterInfoList *parameter, Entity *node, bool useDefaultMaterials = true);
+
+ EntityRenderCommandData buildDrawRenderCommands(const QVector<Entity *> &entities,
+ int offset, int count) const;
+ EntityRenderCommandData buildComputeRenderCommands(const QVector<Entity *> &entities,
+ int offset, int count) const;
+
+
+ void updateRenderCommand(EntityRenderCommandData *renderCommandData,
+ int offset, int count);
+
+
+ void setCommands(const QVector<RenderCommand> &commands) Q_DECL_NOTHROW { m_commands = commands; }
+ QVector<RenderCommand> &commands() { return m_commands; }
+ QVector<RenderCommand> commands() const { return m_commands; }
+
+ void setAttachmentPack(const AttachmentPack &pack) { m_attachmentPack = pack; }
+ const AttachmentPack &attachmentPack() const { return m_attachmentPack; }
+
+ void setRenderTargetId(Qt3DCore::QNodeId renderTargetId) Q_DECL_NOTHROW { m_renderTarget = renderTargetId; }
+ Qt3DCore::QNodeId renderTargetId() const Q_DECL_NOTHROW { return m_renderTarget; }
+
+ void addSortType(const QVector<Qt3DRender::QSortPolicy::SortType> &sortTypes) { m_data.m_sortingTypes.append(sortTypes); }
+
+ void setSurface(QSurface *surface) { m_surface = surface; }
+ QSurface *surface() const { return m_surface; }
+
+ void setLightSources(const QVector<LightSource> &lightSources) Q_DECL_NOTHROW { m_lightSources = lightSources; }
+ void setEnvironmentLight(EnvironmentLight *environmentLight) Q_DECL_NOTHROW { m_environmentLight = environmentLight; }
+
+ void updateMatrices();
+
+ inline void setRenderCaptureNodeId(const Qt3DCore::QNodeId nodeId) Q_DECL_NOTHROW { m_renderCaptureNodeId = nodeId; }
+ inline const Qt3DCore::QNodeId renderCaptureNodeId() const Q_DECL_NOTHROW { return m_renderCaptureNodeId; }
+ inline void setRenderCaptureRequest(const QRenderCaptureRequest& request) Q_DECL_NOTHROW { m_renderCaptureRequest = request; }
+ inline const QRenderCaptureRequest renderCaptureRequest() const Q_DECL_NOTHROW { return m_renderCaptureRequest; }
+
+ void setMemoryBarrier(QMemoryBarrier::Operations barrier) Q_DECL_NOTHROW { m_memoryBarrier = barrier; }
+ QMemoryBarrier::Operations memoryBarrier() const Q_DECL_NOTHROW { return m_memoryBarrier; }
+
+ // Helps making the size of RenderView smaller
+ // Contains all the data needed for the actual building of the RenderView
+ // But that aren't used later by the Renderer
+ struct InnerData {
+ InnerData()
+ : m_renderCameraLens(nullptr)
+ , m_renderCameraNode(nullptr)
+ , m_techniqueFilter(nullptr)
+ , m_passFilter(nullptr)
+ {
+ }
+ CameraLens *m_renderCameraLens;
+ Entity *m_renderCameraNode;
+ const TechniqueFilter *m_techniqueFilter;
+ const RenderPassFilter *m_passFilter;
+ Matrix4x4 m_viewMatrix;
+ Matrix4x4 m_viewProjectionMatrix;
+ Qt3DCore::QNodeIdVector m_layerFilterIds;
+ QVector<Qt3DRender::QSortPolicy::SortType> m_sortingTypes;
+ Vector3D m_eyePos;
+ Vector3D m_eyeViewDir;
+ Qt3DCore::QNodeIdVector m_proximityFilterIds;
+ };
+
+ bool isDownloadBuffersEnable() const;
+ void setIsDownloadBuffersEnable(bool isDownloadBuffersEnable);
+
+ BlitFramebufferInfo blitFrameBufferInfo() const;
+ void setBlitFrameBufferInfo(const BlitFramebufferInfo &blitFrameBufferInfo);
+
+ bool hasBlitFramebufferInfo() const;
+ void setHasBlitFramebufferInfo(bool hasBlitFramebufferInfo);
+
+private:
+ void setShaderAndUniforms(RenderCommand *command,
+ ParameterInfoList &parameters,
+ Entity *entity,
+ const QVector<LightSource> &activeLightSources,
+ EnvironmentLight *environmentLight) const;
+ mutable QThreadStorage<UniformBlockValueBuilder*> m_localData;
+
+ Qt3DCore::QNodeId m_renderCaptureNodeId;
+ QRenderCaptureRequest m_renderCaptureRequest;
+ bool m_isDownloadBuffersEnable;
+
+ bool m_hasBlitFramebufferInfo;
+ BlitFramebufferInfo m_blitFrameBufferInfo;
+
+ Renderer *m_renderer;
+ NodeManagers *m_manager;
+ QSize m_surfaceSize;
+ qreal m_devicePixelRatio;
+
+ InnerData m_data;
+
+ QRectF m_viewport;
+ float m_gamma;
+ Qt3DCore::QNodeId m_renderTarget;
+ QSurface *m_surface;
+ AttachmentPack m_attachmentPack;
+ QClearBuffers::BufferTypeFlags m_clearBuffer;
+ float m_clearDepthValue;
+ int m_clearStencilValue;
+ ClearBufferInfo m_globalClearColorBuffer; // global ClearColor
+ QVector<ClearBufferInfo> m_specificClearColorBuffers; // different draw buffers with distinct colors
+ RenderStateSet *m_stateSet;
+ bool m_noDraw:1;
+ bool m_compute:1;
+ bool m_frustumCulling:1;
+ int m_workGroups[3];
+ QMemoryBarrier::Operations m_memoryBarrier;
+ QVector<Qt3DCore::QNodeId> m_insertFenceIds;
+ QVector<QWaitFenceData> m_waitFences;
+
+ QVector<RenderCommand> m_commands;
+ mutable QVector<LightSource> m_lightSources;
+ EnvironmentLight *m_environmentLight;
+
+ MaterialParameterGathererData m_parameters;
+
+ enum StandardUniform
+ {
+ ModelMatrix,
+ ViewMatrix,
+ ProjectionMatrix,
+ ModelViewMatrix,
+ ViewProjectionMatrix,
+ ModelViewProjectionMatrix,
+ InverseModelMatrix,
+ InverseViewMatrix,
+ InverseProjectionMatrix,
+ InverseModelViewMatrix,
+ InverseViewProjectionMatrix,
+ InverseModelViewProjectionMatrix,
+ ModelNormalMatrix,
+ ModelViewNormalMatrix,
+ ViewportMatrix,
+ InverseViewportMatrix,
+ AspectRatio,
+ Time,
+ Exposure,
+ Gamma,
+ EyePosition,
+ SkinningPalette
+ };
+
+ typedef QHash<int, StandardUniform> StandardUniformsNameToTypeHash;
+ static StandardUniformsNameToTypeHash ms_standardUniformSetters;
+ static StandardUniformsNameToTypeHash initializeStandardUniformSetters();
+
+ UniformValue standardUniformValue(StandardUniform standardUniformType,
+ Entity *entity,
+ 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 Matrix4x4 &worldTransform) const;
+ void setUniformBlockValue(ShaderParameterPack &uniformPack,
+ GLShader *shader,
+ const ShaderUniformBlock &block,
+ const UniformValue &value) const;
+ void setShaderStorageValue(ShaderParameterPack &uniformPack,
+ GLShader *shader,
+ const ShaderStorageBlock &block,
+ const UniformValue &value) const;
+ void setDefaultUniformBlockShaderDataValue(ShaderParameterPack &uniformPack,
+ GLShader *shader,
+ ShaderData *shaderData,
+ const QString &structName) const;
+};
+
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDER_RENDERVIEW_H
diff --git a/src/plugins/renderers/opengl/renderer/renderviewbuilder.cpp b/src/plugins/renderers/opengl/renderer/renderviewbuilder.cpp
new file mode 100644
index 000000000..8f1b17119
--- /dev/null
+++ b/src/plugins/renderers/opengl/renderer/renderviewbuilder.cpp
@@ -0,0 +1,803 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "renderviewbuilder_p.h"
+
+#include <QThread>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+namespace Render {
+
+// In some cases having less jobs is better (especially on fast cpus where
+// splitting just adds more overhead). Ideally, we should try to set the value
+// depending on the platform/CPU/nbr of cores
+const int RenderViewBuilder::m_optimalParallelJobCount = QThread::idealThreadCount();
+
+namespace {
+
+int findIdealNumberOfWorkers(int elementCount, int packetSize = 100)
+{
+ if (elementCount == 0 || packetSize == 0)
+ return 0;
+ return std::min(std::max(elementCount / packetSize, 1), RenderViewBuilder::optimalJobCount());
+}
+
+
+class SyncPreCommandBuilding
+{
+public:
+ explicit SyncPreCommandBuilding(RenderViewInitializerJobPtr renderViewInitializerJob,
+ const QVector<RenderViewCommandBuilderJobPtr> &renderViewCommandBuilderJobs,
+ Renderer *renderer,
+ FrameGraphNode *leafNode)
+ : m_renderViewInitializer(renderViewInitializerJob)
+ , m_renderViewCommandBuilderJobs(renderViewCommandBuilderJobs)
+ , m_renderer(renderer)
+ , m_leafNode(leafNode)
+ {
+ }
+
+ void operator()()
+ {
+ // Split commands to build among jobs
+ QMutexLocker lock(m_renderer->cache()->mutex());
+ // Rebuild RenderCommands for all entities in RV (ignoring filtering)
+ RendererCache *cache = m_renderer->cache();
+ const RendererCache::LeafNodeData &dataCacheForLeaf = cache->leafNodeCache[m_leafNode];
+ RenderView *rv = m_renderViewInitializer->renderView();
+ const auto entities = !rv->isCompute() ? cache->renderableEntities : cache->computeEntities;
+
+ rv->setMaterialParameterTable(dataCacheForLeaf.materialParameterGatherer);
+
+ lock.unlock();
+
+ // Split among the ideal number of command builders
+ const int idealPacketSize = std::min(std::max(100, entities.size() / RenderViewBuilder::optimalJobCount()), entities.size());
+ // Try to split work into an ideal number of workers
+ const int m = findIdealNumberOfWorkers(entities.size(), idealPacketSize);
+
+ for (int i = 0; i < m; ++i) {
+ const RenderViewCommandBuilderJobPtr renderViewCommandBuilder = m_renderViewCommandBuilderJobs.at(i);
+ const int count = (i == m - 1) ? entities.size() - (i * idealPacketSize) : idealPacketSize;
+ renderViewCommandBuilder->setEntities(entities, i * idealPacketSize, count);
+ }
+ }
+
+private:
+ RenderViewInitializerJobPtr m_renderViewInitializer;
+ QVector<RenderViewCommandBuilderJobPtr> m_renderViewCommandBuilderJobs;
+ Renderer *m_renderer;
+ FrameGraphNode *m_leafNode;
+};
+
+class SyncRenderViewPostCommandUpdate
+{
+public:
+ explicit SyncRenderViewPostCommandUpdate(const RenderViewInitializerJobPtr &renderViewJob,
+ const QVector<RenderViewCommandUpdaterJobPtr> &renderViewCommandUpdateJobs,
+ Renderer *renderer)
+ : m_renderViewJob(renderViewJob)
+ , m_renderViewCommandUpdaterJobs(renderViewCommandUpdateJobs)
+ , m_renderer(renderer)
+ {}
+
+ void operator()()
+ {
+ // Append all the commands and sort them
+ RenderView *rv = m_renderViewJob->renderView();
+
+ const EntityRenderCommandDataPtr commandData = m_renderViewCommandUpdaterJobs.first()->renderables();
+
+ if (commandData) {
+ const QVector<RenderCommand> commands = std::move(commandData->commands);
+ rv->setCommands(commands);
+
+ // TO DO: Find way to store commands once or at least only when required
+ // Sort the commands
+ rv->sort();
+ }
+
+ // Enqueue our fully populated RenderView with the RenderThread
+ m_renderer->enqueueRenderView(rv, m_renderViewJob->submitOrderIndex());
+ }
+
+private:
+ RenderViewInitializerJobPtr m_renderViewJob;
+ QVector<RenderViewCommandUpdaterJobPtr> m_renderViewCommandUpdaterJobs;
+ Renderer *m_renderer;
+};
+
+class SyncPreFrustumCulling
+{
+public:
+ explicit SyncPreFrustumCulling(const RenderViewInitializerJobPtr &renderViewJob,
+ const FrustumCullingJobPtr &frustumCulling)
+ : m_renderViewJob(renderViewJob)
+ , m_frustumCullingJob(frustumCulling)
+ {}
+
+ void operator()()
+ {
+ RenderView *rv = m_renderViewJob->renderView();
+
+ // Update matrices now that all transforms have been updated
+ rv->updateMatrices();
+
+ // Frustum culling
+ m_frustumCullingJob->setViewProjection(rv->viewProjectionMatrix());
+ }
+
+private:
+ RenderViewInitializerJobPtr m_renderViewJob;
+ FrustumCullingJobPtr m_frustumCullingJob;
+};
+
+class SyncRenderViewPostInitialization
+{
+public:
+ explicit SyncRenderViewPostInitialization(const RenderViewInitializerJobPtr &renderViewJob,
+ const FrustumCullingJobPtr &frustumCullingJob,
+ const FilterLayerEntityJobPtr &filterEntityByLayerJob,
+ const FilterProximityDistanceJobPtr &filterProximityJob,
+ const QVector<MaterialParameterGathererJobPtr> &materialGathererJobs,
+ const QVector<RenderViewCommandUpdaterJobPtr> &renderViewCommandUpdaterJobs,
+ const QVector<RenderViewCommandBuilderJobPtr> &renderViewCommandBuilderJobs)
+ : m_renderViewJob(renderViewJob)
+ , m_frustumCullingJob(frustumCullingJob)
+ , m_filterEntityByLayerJob(filterEntityByLayerJob)
+ , m_filterProximityJob(filterProximityJob)
+ , m_materialGathererJobs(materialGathererJobs)
+ , m_renderViewCommandUpdaterJobs(renderViewCommandUpdaterJobs)
+ , m_renderViewCommandBuilderJobs(renderViewCommandBuilderJobs)
+ {}
+
+ void operator()()
+ {
+ RenderView *rv = m_renderViewJob->renderView();
+
+ // Layer filtering
+ if (!m_filterEntityByLayerJob.isNull())
+ m_filterEntityByLayerJob->setLayerFilters(rv->layerFilters());
+
+ // Proximity filtering
+ m_filterProximityJob->setProximityFilterIds(rv->proximityFilterIds());
+
+ // Material Parameter building
+ for (const auto &materialGatherer : qAsConst(m_materialGathererJobs)) {
+ materialGatherer->setRenderPassFilter(const_cast<RenderPassFilter *>(rv->renderPassFilter()));
+ materialGatherer->setTechniqueFilter(const_cast<TechniqueFilter *>(rv->techniqueFilter()));
+ }
+
+ // Command builders and updates
+ for (const auto &renderViewCommandUpdater : qAsConst(m_renderViewCommandUpdaterJobs))
+ renderViewCommandUpdater->setRenderView(rv);
+ for (const auto &renderViewCommandBuilder : qAsConst(m_renderViewCommandBuilderJobs))
+ renderViewCommandBuilder->setRenderView(rv);
+
+ // Set whether frustum culling is enabled or not
+ m_frustumCullingJob->setActive(rv->frustumCulling());
+ }
+
+private:
+ RenderViewInitializerJobPtr m_renderViewJob;
+ FrustumCullingJobPtr m_frustumCullingJob;
+ FilterLayerEntityJobPtr m_filterEntityByLayerJob;
+ FilterProximityDistanceJobPtr m_filterProximityJob;
+ QVector<MaterialParameterGathererJobPtr> m_materialGathererJobs;
+ QVector<RenderViewCommandUpdaterJobPtr> m_renderViewCommandUpdaterJobs;
+ QVector<RenderViewCommandBuilderJobPtr> m_renderViewCommandBuilderJobs;
+};
+
+class SyncRenderViewPreCommandUpdate
+{
+public:
+ explicit SyncRenderViewPreCommandUpdate(const RenderViewInitializerJobPtr &renderViewJob,
+ const FrustumCullingJobPtr &frustumCullingJob,
+ const FilterProximityDistanceJobPtr &filterProximityJob,
+ const QVector<MaterialParameterGathererJobPtr> &materialGathererJobs,
+ const QVector<RenderViewCommandUpdaterJobPtr> &renderViewCommandUpdaterJobs,
+ const QVector<RenderViewCommandBuilderJobPtr> &renderViewCommandBuilderJobs,
+ Renderer *renderer,
+ FrameGraphNode *leafNode,
+ bool fullCommandRebuild)
+ : m_renderViewJob(renderViewJob)
+ , m_frustumCullingJob(frustumCullingJob)
+ , m_filterProximityJob(filterProximityJob)
+ , m_materialGathererJobs(materialGathererJobs)
+ , m_renderViewCommandUpdaterJobs(renderViewCommandUpdaterJobs)
+ , m_renderViewCommandBuilderJobs(renderViewCommandBuilderJobs)
+ , m_renderer(renderer)
+ , m_leafNode(leafNode)
+ , m_fullRebuild(fullCommandRebuild)
+ {}
+
+ void operator()()
+ {
+ // Set the result of previous job computations
+ // for final RenderCommand building
+ RenderView *rv = m_renderViewJob->renderView();
+
+ if (!rv->noDraw()) {
+ ///////// CACHE LOCKED ////////////
+ // Retrieve Data from Cache
+ RendererCache *cache = m_renderer->cache();
+ QMutexLocker lock(cache->mutex());
+ Q_ASSERT(cache->leafNodeCache.contains(m_leafNode));
+
+ const bool isDraw = !rv->isCompute();
+ const RendererCache::LeafNodeData &dataCacheForLeaf = cache->leafNodeCache[m_leafNode];
+
+ // Rebuild RenderCommands if required
+ // This should happen fairly infrequently (FrameGraph Change, Geometry/Material change)
+ // and allow to skip that step most of the time
+ if (m_fullRebuild) {
+ EntityRenderCommandData commandData;
+ // Reduction
+ {
+ int totalCommandCount = 0;
+ for (const RenderViewCommandBuilderJobPtr &renderViewCommandBuilder : qAsConst(m_renderViewCommandBuilderJobs))
+ totalCommandCount += renderViewCommandBuilder->commandData().size();
+ commandData.reserve(totalCommandCount);
+ for (const RenderViewCommandBuilderJobPtr &renderViewCommandBuilder : qAsConst(m_renderViewCommandBuilderJobs))
+ commandData += std::move(renderViewCommandBuilder->commandData());
+ }
+
+
+ // Store new cache
+ RendererCache::LeafNodeData &writableCacheForLeaf = cache->leafNodeCache[m_leafNode];
+ writableCacheForLeaf.renderCommandData = std::move(commandData);
+ }
+ const EntityRenderCommandData commandData = dataCacheForLeaf.renderCommandData;
+ const QVector<Entity *> filteredEntities = dataCacheForLeaf.filterEntitiesByLayer;
+ QVector<Entity *> renderableEntities = isDraw ? cache->renderableEntities : cache->computeEntities;
+ QVector<LightSource> lightSources = cache->gatheredLights;
+
+ rv->setMaterialParameterTable(dataCacheForLeaf.materialParameterGatherer);
+ rv->setEnvironmentLight(cache->environmentLight);
+ lock.unlock();
+ ///////// END OF CACHE LOCKED ////////////
+
+ // Filter out entities that weren't selected by the layer filters
+ // Remove all entities from the compute and renderable vectors that aren't in the filtered layer vector
+ renderableEntities = RenderViewBuilder::entitiesInSubset(renderableEntities, filteredEntities);
+
+ // Set the light sources, with layer filters applied.
+ for (int i = 0; i < lightSources.count(); ++i) {
+ if (!filteredEntities.contains(lightSources[i].entity))
+ lightSources.removeAt(i--);
+ }
+ rv->setLightSources(lightSources);
+
+ if (isDraw) {
+ // Filter out frustum culled entity for drawable entities
+ if (rv->frustumCulling())
+ renderableEntities = RenderViewBuilder::entitiesInSubset(renderableEntities, m_frustumCullingJob->visibleEntities());
+ // Filter out entities which didn't satisfy proximity filtering
+ if (!rv->proximityFilterIds().empty())
+ renderableEntities = RenderViewBuilder::entitiesInSubset(renderableEntities, m_filterProximityJob->filteredEntities());
+ }
+
+ // Early return in case we have nothing to filter
+ if (renderableEntities.size() == 0)
+ return;
+
+ // Filter out Render commands for which the Entity wasn't selected because
+ // of frustum, proximity or layer filtering
+ EntityRenderCommandDataPtr filteredCommandData = EntityRenderCommandDataPtr::create();
+ filteredCommandData->reserve(renderableEntities.size());
+ // Because dataCacheForLeaf.renderableEntities or computeEntities are sorted
+ // What we get out of EntityRenderCommandData is also sorted by Entity
+ auto eIt = std::cbegin(renderableEntities);
+ const auto eEnd = std::cend(renderableEntities);
+ int cIt = 0;
+ const int cEnd = commandData.size();
+
+ while (eIt != eEnd) {
+ const Entity *targetEntity = *eIt;
+ // Advance until we have commands whose Entity has a lower address
+ // than the selected filtered entity
+ while (cIt != cEnd && commandData.entities.at(cIt) < targetEntity)
+ ++cIt;
+
+ // Push pointers to command data for all commands that match the
+ // entity
+ while (cIt != cEnd && commandData.entities.at(cIt) == targetEntity) {
+ filteredCommandData->push_back(commandData.entities.at(cIt),
+ commandData.commands.at(cIt),
+ commandData.passesData.at(cIt));
+ ++cIt;
+ }
+ ++eIt;
+ }
+
+ // Split among the number of command builders
+ // The idealPacketSize is at least 100 entities per worker
+ const int idealPacketSize = std::min(std::max(100, filteredCommandData->size() / RenderViewBuilder::optimalJobCount()), filteredCommandData->size());
+ const int m = findIdealNumberOfWorkers(filteredCommandData->size(), idealPacketSize);
+
+ for (int i = 0; i < m; ++i) {
+ const RenderViewCommandUpdaterJobPtr renderViewCommandBuilder = m_renderViewCommandUpdaterJobs.at(i);
+ const int count = (i == m - 1) ? filteredCommandData->size() - (i * idealPacketSize) : idealPacketSize;
+ renderViewCommandBuilder->setRenderables(filteredCommandData, i * idealPacketSize, count);
+ }
+ }
+ }
+
+private:
+ RenderViewInitializerJobPtr m_renderViewJob;
+ FrustumCullingJobPtr m_frustumCullingJob;
+ FilterProximityDistanceJobPtr m_filterProximityJob;
+ QVector<MaterialParameterGathererJobPtr> m_materialGathererJobs;
+ QVector<RenderViewCommandUpdaterJobPtr> m_renderViewCommandUpdaterJobs;
+ QVector<RenderViewCommandBuilderJobPtr> m_renderViewCommandBuilderJobs;
+ Renderer *m_renderer;
+ FrameGraphNode *m_leafNode;
+ bool m_fullRebuild;
+};
+
+class SetClearDrawBufferIndex
+{
+public:
+ explicit SetClearDrawBufferIndex(const RenderViewInitializerJobPtr &renderViewJob)
+ : m_renderViewJob(renderViewJob)
+ {}
+
+ void operator()()
+ {
+ RenderView *rv = m_renderViewJob->renderView();
+ QVector<ClearBufferInfo> &clearBuffersInfo = rv->specificClearColorBufferInfo();
+ const AttachmentPack &attachmentPack = rv->attachmentPack();
+ for (ClearBufferInfo &clearBufferInfo : clearBuffersInfo)
+ clearBufferInfo.drawBufferIndex = attachmentPack.getDrawBufferIndex(clearBufferInfo.attchmentPoint);
+
+ }
+
+private:
+ RenderViewInitializerJobPtr m_renderViewJob;
+};
+
+class SyncFilterEntityByLayer
+{
+public:
+ explicit SyncFilterEntityByLayer(const FilterLayerEntityJobPtr &filterEntityByLayerJob,
+ Renderer *renderer,
+ FrameGraphNode *leafNode)
+ : m_filterEntityByLayerJob(filterEntityByLayerJob)
+ , m_renderer(renderer)
+ , m_leafNode(leafNode)
+ {
+ }
+
+ void operator()()
+ {
+ QMutexLocker lock(m_renderer->cache()->mutex());
+ // Save the filtered by layer subset into the cache
+ const QVector<Entity *> filteredEntities = m_filterEntityByLayerJob->filteredEntities();
+ RendererCache::LeafNodeData &dataCacheForLeaf = m_renderer->cache()->leafNodeCache[m_leafNode];
+ dataCacheForLeaf.filterEntitiesByLayer = filteredEntities;
+ }
+
+private:
+ FilterLayerEntityJobPtr m_filterEntityByLayerJob;
+ Renderer *m_renderer;
+ FrameGraphNode *m_leafNode;
+};
+
+class SyncMaterialParameterGatherer
+{
+public:
+ explicit SyncMaterialParameterGatherer(const QVector<MaterialParameterGathererJobPtr> &materialParameterGathererJobs,
+ Renderer *renderer,
+ FrameGraphNode *leafNode)
+ : m_materialParameterGathererJobs(materialParameterGathererJobs)
+ , m_renderer(renderer)
+ , m_leafNode(leafNode)
+ {
+ }
+
+ void operator()()
+ {
+ QMutexLocker lock(m_renderer->cache()->mutex());
+ RendererCache::LeafNodeData &dataCacheForLeaf = m_renderer->cache()->leafNodeCache[m_leafNode];
+ dataCacheForLeaf.materialParameterGatherer.clear();
+
+ for (const auto &materialGatherer : qAsConst(m_materialParameterGathererJobs))
+ dataCacheForLeaf.materialParameterGatherer.unite(materialGatherer->materialToPassAndParameter());
+ }
+
+private:
+ QVector<MaterialParameterGathererJobPtr> m_materialParameterGathererJobs;
+ Renderer *m_renderer;
+ FrameGraphNode *m_leafNode;
+};
+
+} // anonymous
+
+RenderViewBuilder::RenderViewBuilder(Render::FrameGraphNode *leafNode, int renderViewIndex, Renderer *renderer)
+ : m_leafNode(leafNode)
+ , m_renderViewIndex(renderViewIndex)
+ , m_renderer(renderer)
+ , m_layerCacheNeedsToBeRebuilt(false)
+ , m_materialGathererCacheNeedsToBeRebuilt(false)
+ , m_renderCommandCacheNeedsToBeRebuilt(false)
+ , m_renderViewJob(RenderViewInitializerJobPtr::create())
+ , m_filterEntityByLayerJob()
+ , m_frustumCullingJob(new Render::FrustumCullingJob())
+ , m_syncPreFrustumCullingJob(SynchronizerJobPtr::create(SyncPreFrustumCulling(m_renderViewJob, m_frustumCullingJob), JobTypes::SyncFrustumCulling))
+ , m_setClearDrawBufferIndexJob(SynchronizerJobPtr::create(SetClearDrawBufferIndex(m_renderViewJob), JobTypes::ClearBufferDrawIndex))
+ , m_syncFilterEntityByLayerJob()
+ , m_filterProximityJob(Render::FilterProximityDistanceJobPtr::create())
+{
+}
+
+RenderViewInitializerJobPtr RenderViewBuilder::renderViewJob() const
+{
+ return m_renderViewJob;
+}
+
+FilterLayerEntityJobPtr RenderViewBuilder::filterEntityByLayerJob() const
+{
+ return m_filterEntityByLayerJob;
+}
+
+FrustumCullingJobPtr RenderViewBuilder::frustumCullingJob() const
+{
+ return m_frustumCullingJob;
+}
+
+QVector<RenderViewCommandUpdaterJobPtr> RenderViewBuilder::renderViewCommandUpdaterJobs() const
+{
+ return m_renderViewCommandUpdaterJobs;
+}
+
+QVector<RenderViewCommandBuilderJobPtr> RenderViewBuilder::renderViewCommandBuilderJobs() const
+{
+ return m_renderViewCommandBuilderJobs;
+}
+
+QVector<MaterialParameterGathererJobPtr> RenderViewBuilder::materialGathererJobs() const
+{
+ return m_materialGathererJobs;
+}
+
+SynchronizerJobPtr RenderViewBuilder::syncRenderViewPostInitializationJob() const
+{
+ return m_syncRenderViewPostInitializationJob;
+}
+
+SynchronizerJobPtr RenderViewBuilder::syncPreFrustumCullingJob() const
+{
+ return m_syncPreFrustumCullingJob;
+}
+
+SynchronizerJobPtr RenderViewBuilder::syncRenderViewPreCommandBuildingJob() const
+{
+ return m_syncRenderViewPreCommandBuildingJob;
+}
+
+SynchronizerJobPtr RenderViewBuilder::syncRenderViewPreCommandUpdateJob() const
+{
+ return m_syncRenderViewPreCommandUpdateJob;
+}
+
+SynchronizerJobPtr RenderViewBuilder::syncRenderViewPostCommandUpdateJob() const
+{
+ return m_syncRenderViewPostCommandUpdateJob;
+}
+
+SynchronizerJobPtr RenderViewBuilder::setClearDrawBufferIndexJob() const
+{
+ return m_setClearDrawBufferIndexJob;
+}
+
+SynchronizerJobPtr RenderViewBuilder::syncFilterEntityByLayerJob() const
+{
+ return m_syncFilterEntityByLayerJob;
+}
+
+SynchronizerJobPtr RenderViewBuilder::syncMaterialGathererJob() const
+{
+ return m_syncMaterialGathererJob;
+}
+
+FilterProximityDistanceJobPtr RenderViewBuilder::filterProximityJob() const
+{
+ return m_filterProximityJob;
+}
+
+void RenderViewBuilder::prepareJobs()
+{
+ // Init what we can here
+ m_filterProximityJob->setManager(m_renderer->nodeManagers());
+ m_frustumCullingJob->setRoot(m_renderer->sceneRoot());
+
+ if (m_renderCommandCacheNeedsToBeRebuilt) {
+
+ m_renderViewCommandBuilderJobs.reserve(RenderViewBuilder::m_optimalParallelJobCount);
+ for (auto i = 0; i < RenderViewBuilder::m_optimalParallelJobCount; ++i) {
+ auto renderViewCommandBuilder = Render::RenderViewCommandBuilderJobPtr::create();
+ m_renderViewCommandBuilderJobs.push_back(renderViewCommandBuilder);
+ }
+ m_syncRenderViewPreCommandBuildingJob = SynchronizerJobPtr::create(SyncPreCommandBuilding(m_renderViewJob,
+ m_renderViewCommandBuilderJobs,
+ m_renderer,
+ m_leafNode),
+ JobTypes::SyncRenderViewPreCommandBuilding);
+ }
+
+ m_renderViewJob->setRenderer(m_renderer);
+ m_renderViewJob->setFrameGraphLeafNode(m_leafNode);
+ m_renderViewJob->setSubmitOrderIndex(m_renderViewIndex);
+
+ // RenderCommand building is the most consuming task -> split it
+ // Estimate the number of jobs to create based on the number of entities
+ m_renderViewCommandUpdaterJobs.reserve(RenderViewBuilder::m_optimalParallelJobCount);
+ for (auto i = 0; i < RenderViewBuilder::m_optimalParallelJobCount; ++i) {
+ auto renderViewCommandUpdater = Render::RenderViewCommandUpdaterJobPtr::create();
+ renderViewCommandUpdater->setRenderer(m_renderer);
+ m_renderViewCommandUpdaterJobs.push_back(renderViewCommandUpdater);
+ }
+
+ if (m_materialGathererCacheNeedsToBeRebuilt) {
+ // Since Material gathering is an heavy task, we split it
+ const QVector<HMaterial> materialHandles = m_renderer->nodeManagers()->materialManager()->activeHandles();
+ const int elementsPerJob = materialHandles.size() / RenderViewBuilder::m_optimalParallelJobCount;
+ const int lastRemaingElements = materialHandles.size() % RenderViewBuilder::m_optimalParallelJobCount;
+ m_materialGathererJobs.reserve(RenderViewBuilder::m_optimalParallelJobCount);
+ for (auto i = 0; i < RenderViewBuilder::m_optimalParallelJobCount; ++i) {
+ auto materialGatherer = Render::MaterialParameterGathererJobPtr::create();
+ materialGatherer->setNodeManagers(m_renderer->nodeManagers());
+ if (i == RenderViewBuilder::m_optimalParallelJobCount - 1)
+ materialGatherer->setHandles(materialHandles.mid(i * elementsPerJob, elementsPerJob + lastRemaingElements));
+ else
+ materialGatherer->setHandles(materialHandles.mid(i * elementsPerJob, elementsPerJob));
+ m_materialGathererJobs.push_back(materialGatherer);
+ }
+ m_syncMaterialGathererJob = SynchronizerJobPtr::create(SyncMaterialParameterGatherer(m_materialGathererJobs,
+ m_renderer,
+ m_leafNode),
+ JobTypes::SyncMaterialGatherer);
+ }
+
+ if (m_layerCacheNeedsToBeRebuilt) {
+ m_filterEntityByLayerJob = Render::FilterLayerEntityJobPtr::create();
+ m_filterEntityByLayerJob->setManager(m_renderer->nodeManagers());
+ m_syncFilterEntityByLayerJob = SynchronizerJobPtr::create(SyncFilterEntityByLayer(m_filterEntityByLayerJob,
+ m_renderer,
+ m_leafNode),
+ JobTypes::SyncFilterEntityByLayer);
+ }
+
+ m_syncRenderViewPreCommandUpdateJob = SynchronizerJobPtr::create(SyncRenderViewPreCommandUpdate(m_renderViewJob,
+ m_frustumCullingJob,
+ m_filterProximityJob,
+ m_materialGathererJobs,
+ m_renderViewCommandUpdaterJobs,
+ m_renderViewCommandBuilderJobs,
+ m_renderer,
+ m_leafNode,
+ m_renderCommandCacheNeedsToBeRebuilt),
+ JobTypes::SyncRenderViewPreCommandUpdate);
+
+ m_syncRenderViewPostCommandUpdateJob = SynchronizerJobPtr::create(SyncRenderViewPostCommandUpdate(m_renderViewJob,
+ m_renderViewCommandUpdaterJobs,
+ m_renderer),
+ JobTypes::SyncRenderViewPostCommandUpdate);
+
+ m_syncRenderViewPostInitializationJob = SynchronizerJobPtr::create(SyncRenderViewPostInitialization(m_renderViewJob,
+ m_frustumCullingJob,
+ m_filterEntityByLayerJob,
+ m_filterProximityJob,
+ m_materialGathererJobs,
+ m_renderViewCommandUpdaterJobs,
+ m_renderViewCommandBuilderJobs),
+ JobTypes::SyncRenderViewInitialization);
+}
+
+QVector<Qt3DCore::QAspectJobPtr> RenderViewBuilder::buildJobHierachy() const
+{
+ QVector<Qt3DCore::QAspectJobPtr> jobs;
+
+ jobs.reserve(m_materialGathererJobs.size() + m_renderViewCommandUpdaterJobs.size() + 11);
+
+ // Set dependencies
+
+ // Finish the skinning palette job before processing renderviews
+ // TODO: Maybe only update skinning palettes for non-culled entities
+ m_renderViewJob->addDependency(m_renderer->updateSkinningPaletteJob());
+
+ m_syncPreFrustumCullingJob->addDependency(m_renderer->updateWorldTransformJob());
+ m_syncPreFrustumCullingJob->addDependency(m_renderer->updateShaderDataTransformJob());
+ m_syncPreFrustumCullingJob->addDependency(m_syncRenderViewPostInitializationJob);
+
+ m_frustumCullingJob->addDependency(m_renderer->expandBoundingVolumeJob());
+ m_frustumCullingJob->addDependency(m_syncPreFrustumCullingJob);
+
+ m_setClearDrawBufferIndexJob->addDependency(m_syncRenderViewPostInitializationJob);
+
+ m_syncRenderViewPostInitializationJob->addDependency(m_renderViewJob);
+
+ m_filterProximityJob->addDependency(m_renderer->expandBoundingVolumeJob());
+ m_filterProximityJob->addDependency(m_syncRenderViewPostInitializationJob);
+
+ m_syncRenderViewPreCommandUpdateJob->addDependency(m_syncRenderViewPostInitializationJob);
+ m_syncRenderViewPreCommandUpdateJob->addDependency(m_filterProximityJob);
+ m_syncRenderViewPreCommandUpdateJob->addDependency(m_frustumCullingJob);
+
+ // Ensure the RenderThread won't be able to process dirtyResources
+ // before they have been completely gathered
+ m_syncRenderViewPreCommandUpdateJob->addDependency(m_renderer->introspectShadersJob());
+ m_syncRenderViewPreCommandUpdateJob->addDependency(m_renderer->bufferGathererJob());
+ m_syncRenderViewPreCommandUpdateJob->addDependency(m_renderer->textureGathererJob());
+ m_syncRenderViewPreCommandUpdateJob->addDependency(m_renderer->cacheLightJob());
+
+ for (const auto &renderViewCommandUpdater : qAsConst(m_renderViewCommandUpdaterJobs)) {
+ renderViewCommandUpdater->addDependency(m_syncRenderViewPreCommandUpdateJob);
+ m_syncRenderViewPostCommandUpdateJob->addDependency(renderViewCommandUpdater);
+ }
+
+ m_renderer->frameCleanupJob()->addDependency(m_syncRenderViewPostCommandUpdateJob);
+ m_renderer->frameCleanupJob()->addDependency(m_setClearDrawBufferIndexJob);
+
+ // Add jobs
+ jobs.push_back(m_renderViewJob); // Step 1
+
+ jobs.push_back(m_syncRenderViewPostInitializationJob); // Step 2
+
+ if (m_renderCommandCacheNeedsToBeRebuilt) { // Step 3
+ m_syncRenderViewPreCommandBuildingJob->addDependency(m_renderer->cacheComputableEntitiesJob());
+ m_syncRenderViewPreCommandBuildingJob->addDependency(m_renderer->cacheRenderableEntitiesJob());
+ m_syncRenderViewPreCommandBuildingJob->addDependency(m_syncRenderViewPostInitializationJob);
+
+ if (m_materialGathererCacheNeedsToBeRebuilt)
+ m_syncRenderViewPreCommandBuildingJob->addDependency(m_syncMaterialGathererJob);
+
+ jobs.push_back(m_syncRenderViewPreCommandBuildingJob);
+
+ for (const auto &renderViewCommandBuilder : qAsConst(m_renderViewCommandBuilderJobs)) {
+ renderViewCommandBuilder->addDependency(m_syncRenderViewPreCommandBuildingJob);
+ m_syncRenderViewPreCommandUpdateJob->addDependency(renderViewCommandBuilder);
+ jobs.push_back(renderViewCommandBuilder);
+ }
+ }
+
+ if (m_layerCacheNeedsToBeRebuilt) {
+ m_filterEntityByLayerJob->addDependency(m_renderer->updateEntityLayersJob());
+ m_filterEntityByLayerJob->addDependency(m_syncRenderViewPostInitializationJob);
+ m_filterEntityByLayerJob->addDependency(m_renderer->updateTreeEnabledJob());
+
+ m_syncFilterEntityByLayerJob->addDependency(m_filterEntityByLayerJob);
+ m_syncRenderViewPreCommandUpdateJob->addDependency(m_syncFilterEntityByLayerJob);
+
+ jobs.push_back(m_filterEntityByLayerJob); // Step 3
+ jobs.push_back(m_syncFilterEntityByLayerJob); // Step 4
+ }
+ jobs.push_back(m_syncPreFrustumCullingJob); // Step 3
+ jobs.push_back(m_filterProximityJob); // Step 3
+ jobs.push_back(m_setClearDrawBufferIndexJob); // Step 3
+
+ if (m_materialGathererCacheNeedsToBeRebuilt) {
+ for (const auto &materialGatherer : qAsConst(m_materialGathererJobs)) {
+ materialGatherer->addDependency(m_syncRenderViewPostInitializationJob);
+ materialGatherer->addDependency(m_renderer->introspectShadersJob());
+ materialGatherer->addDependency(m_renderer->filterCompatibleTechniqueJob());
+ jobs.push_back(materialGatherer); // Step3
+ m_syncMaterialGathererJob->addDependency(materialGatherer);
+ }
+ m_syncRenderViewPreCommandUpdateJob->addDependency(m_syncMaterialGathererJob);
+
+ jobs.push_back(m_syncMaterialGathererJob); // Step 3
+ }
+
+ jobs.push_back(m_frustumCullingJob); // Step 4
+ jobs.push_back(m_syncRenderViewPreCommandUpdateJob); // Step 5
+
+ // Build RenderCommands or Update RenderCommand Uniforms
+ for (const auto &renderViewCommandBuilder : qAsConst(m_renderViewCommandUpdaterJobs)) // Step 6
+ jobs.push_back(renderViewCommandBuilder);
+
+ jobs.push_back(m_syncRenderViewPostCommandUpdateJob); // Step 7
+
+ return jobs;
+}
+
+Renderer *RenderViewBuilder::renderer() const
+{
+ return m_renderer;
+}
+
+int RenderViewBuilder::renderViewIndex() const
+{
+ return m_renderViewIndex;
+}
+
+void RenderViewBuilder::setLayerCacheNeedsToBeRebuilt(bool needsToBeRebuilt)
+{
+ m_layerCacheNeedsToBeRebuilt = needsToBeRebuilt;
+}
+
+bool RenderViewBuilder::layerCacheNeedsToBeRebuilt() const
+{
+ return m_layerCacheNeedsToBeRebuilt;
+}
+
+void RenderViewBuilder::setMaterialGathererCacheNeedsToBeRebuilt(bool needsToBeRebuilt)
+{
+ m_materialGathererCacheNeedsToBeRebuilt = needsToBeRebuilt;
+}
+
+bool RenderViewBuilder::materialGathererCacheNeedsToBeRebuilt() const
+{
+ return m_materialGathererCacheNeedsToBeRebuilt;
+}
+
+void RenderViewBuilder::setRenderCommandCacheNeedsToBeRebuilt(bool needsToBeRebuilt)
+{
+ m_renderCommandCacheNeedsToBeRebuilt = needsToBeRebuilt;
+}
+
+bool RenderViewBuilder::renderCommandCacheNeedsToBeRebuilt() const
+{
+ return m_renderCommandCacheNeedsToBeRebuilt;
+}
+
+int RenderViewBuilder::optimalJobCount()
+{
+ return RenderViewBuilder::m_optimalParallelJobCount;
+}
+
+QVector<Entity *> RenderViewBuilder::entitiesInSubset(const QVector<Entity *> &entities, const QVector<Entity *> &subset)
+{
+ QVector<Entity *> intersection;
+ intersection.reserve(qMin(entities.size(), subset.size()));
+ std::set_intersection(entities.begin(), entities.end(),
+ subset.begin(), subset.end(),
+ std::back_inserter(intersection));
+
+ return intersection;
+}
+
+} // Render
+
+} // Qt3DRender
+
+QT_END_NAMESPACE
diff --git a/src/plugins/renderers/opengl/renderer/renderviewbuilder_p.h b/src/plugins/renderers/opengl/renderer/renderviewbuilder_p.h
new file mode 100644
index 000000000..71719abee
--- /dev/null
+++ b/src/plugins/renderers/opengl/renderer/renderviewbuilder_p.h
@@ -0,0 +1,148 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DRENDER_RENDER_RENDERVIEWBUILDER_H
+#define QT3DRENDER_RENDER_RENDERVIEWBUILDER_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 <functional>
+#include <Qt3DCore/qaspectjob.h>
+#include <Qt3DRender/private/filterlayerentityjob_p.h>
+#include <Qt3DRender/private/genericlambdajob_p.h>
+#include <Qt3DRender/private/nodemanagers_p.h>
+#include <Qt3DRender/private/frustumcullingjob_p.h>
+#include <Qt3DRender/private/filterproximitydistancejob_p.h>
+#include <renderviewcommandbuilderjob_p.h>
+#include <renderviewcommandupdaterjob_p.h>
+#include <materialparametergathererjob_p.h>
+#include <renderviewbuilderjob_p.h>
+#include <renderview_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+namespace Render {
+
+class Renderer;
+
+using SynchronizerJobPtr = GenericLambdaJobPtr<std::function<void()>>;
+
+class Q_AUTOTEST_EXPORT RenderViewBuilder
+{
+public:
+ explicit RenderViewBuilder(Render::FrameGraphNode *leafNode, int renderViewIndex, Renderer *renderer);
+
+ RenderViewInitializerJobPtr renderViewJob() const;
+ FilterLayerEntityJobPtr filterEntityByLayerJob() const;
+ FrustumCullingJobPtr frustumCullingJob() const;
+ QVector<RenderViewCommandBuilderJobPtr> renderViewCommandBuilderJobs() const;
+ QVector<RenderViewCommandUpdaterJobPtr> renderViewCommandUpdaterJobs() const;
+ QVector<MaterialParameterGathererJobPtr> materialGathererJobs() const;
+ SynchronizerJobPtr syncRenderViewPostInitializationJob() const;
+ SynchronizerJobPtr syncPreFrustumCullingJob() const;
+ SynchronizerJobPtr syncRenderViewPreCommandBuildingJob() const;
+ SynchronizerJobPtr syncRenderViewPreCommandUpdateJob() const;
+ SynchronizerJobPtr syncRenderViewPostCommandUpdateJob() const;
+ SynchronizerJobPtr setClearDrawBufferIndexJob() const;
+ SynchronizerJobPtr syncFilterEntityByLayerJob() const;
+ FilterProximityDistanceJobPtr filterProximityJob() const;
+ SynchronizerJobPtr syncMaterialGathererJob() const;
+
+ void prepareJobs();
+ QVector<Qt3DCore::QAspectJobPtr> buildJobHierachy() const;
+
+ Renderer *renderer() const;
+ int renderViewIndex() const;
+
+ void setLayerCacheNeedsToBeRebuilt(bool needsToBeRebuilt);
+ bool layerCacheNeedsToBeRebuilt() const;
+ void setMaterialGathererCacheNeedsToBeRebuilt(bool needsToBeRebuilt);
+ bool materialGathererCacheNeedsToBeRebuilt() const;
+ void setRenderCommandCacheNeedsToBeRebuilt(bool needsToBeRebuilt);
+ bool renderCommandCacheNeedsToBeRebuilt() const;
+
+ static int optimalJobCount();
+ static QVector<Entity *> entitiesInSubset(const QVector<Entity *> &entities, const QVector<Entity *> &subset);
+
+private:
+ Render::FrameGraphNode *m_leafNode;
+ const int m_renderViewIndex;
+ Renderer *m_renderer;
+ bool m_layerCacheNeedsToBeRebuilt;
+ bool m_materialGathererCacheNeedsToBeRebuilt;
+ bool m_renderCommandCacheNeedsToBeRebuilt;
+
+ RenderViewInitializerJobPtr m_renderViewJob;
+ FilterLayerEntityJobPtr m_filterEntityByLayerJob;
+ FrustumCullingJobPtr m_frustumCullingJob;
+ QVector<RenderViewCommandBuilderJobPtr> m_renderViewCommandBuilderJobs;
+ QVector<RenderViewCommandUpdaterJobPtr> m_renderViewCommandUpdaterJobs;
+ QVector<MaterialParameterGathererJobPtr> m_materialGathererJobs;
+
+ SynchronizerJobPtr m_syncRenderViewPostInitializationJob;
+ SynchronizerJobPtr m_syncPreFrustumCullingJob;
+ SynchronizerJobPtr m_syncRenderViewPreCommandBuildingJob;
+ SynchronizerJobPtr m_syncRenderViewPreCommandUpdateJob;
+ SynchronizerJobPtr m_syncRenderViewPostCommandUpdateJob;
+ SynchronizerJobPtr m_setClearDrawBufferIndexJob;
+ SynchronizerJobPtr m_syncFilterEntityByLayerJob;
+ SynchronizerJobPtr m_syncMaterialGathererJob;
+ FilterProximityDistanceJobPtr m_filterProximityJob;
+
+ static const int m_optimalParallelJobCount;
+};
+
+} // Render
+
+} // Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDER_RENDERVIEWBUILDER_H
diff --git a/src/plugins/renderers/opengl/renderer/shaderparameterpack.cpp b/src/plugins/renderers/opengl/renderer/shaderparameterpack.cpp
new file mode 100644
index 000000000..e3cc7baf7
--- /dev/null
+++ b/src/plugins/renderers/opengl/renderer/shaderparameterpack.cpp
@@ -0,0 +1,112 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "shaderparameterpack_p.h"
+
+#include <graphicscontext_p.h>
+#include <Qt3DRender/private/texture_p.h>
+
+#include <Qt3DCore/private/qframeallocator_p.h>
+
+#include <QOpenGLShaderProgram>
+#include <QDebug>
+#include <QColor>
+#include <QQuaternion>
+#include <Qt3DRender/private/renderlogging_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+namespace Render {
+
+ShaderParameterPack::~ShaderParameterPack()
+{
+}
+
+void ShaderParameterPack::setUniform(const int glslNameId, const UniformValue &val)
+{
+ m_uniforms.insert(glslNameId, val);
+}
+
+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 || m_textures[t].uniformArrayIndex != uniformArrayIndex)
+ continue;
+
+ m_textures[t].nodeId = texId;
+ return;
+ }
+
+ m_textures.append(NamedResource(glslNameId, texId, uniformArrayIndex, NamedResource::Texture));
+}
+
+void ShaderParameterPack::setImage(const int glslNameId, int uniformArrayIndex, Qt3DCore::QNodeId id)
+{
+ for (int i=0, m = m_images.size(); i < m; ++i) {
+ if (m_images[i].glslNameId != glslNameId || m_images[i].uniformArrayIndex != uniformArrayIndex)
+ continue;
+
+ m_images[i].nodeId = id;
+ return;
+ }
+
+ m_images.append(NamedResource(glslNameId, id, uniformArrayIndex, NamedResource::Image));
+}
+
+// Contains Uniform Block Index and QNodeId of the ShaderData (UBO)
+void ShaderParameterPack::setUniformBuffer(BlockToUBO blockToUBO)
+{
+ m_uniformBuffers.append(std::move(blockToUBO));
+}
+
+void ShaderParameterPack::setShaderStorageBuffer(BlockToSSBO blockToSSBO)
+{
+ m_shaderStorageBuffers.push_back(std::move(blockToSSBO));
+}
+
+void ShaderParameterPack::setSubmissionUniform(const ShaderUniform &uniform)
+{
+ m_submissionUniforms.push_back(uniform);
+}
+
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
diff --git a/src/plugins/renderers/opengl/renderer/shaderparameterpack_p.h b/src/plugins/renderers/opengl/renderer/shaderparameterpack_p.h
new file mode 100644
index 000000000..045081afd
--- /dev/null
+++ b/src/plugins/renderers/opengl/renderer/shaderparameterpack_p.h
@@ -0,0 +1,221 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DRENDER_RENDER_SHADERPARAMETERPACK_P_H
+#define QT3DRENDER_RENDER_SHADERPARAMETERPACK_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 <QVariant>
+#include <QByteArray>
+#include <QVector>
+#include <QOpenGLShaderProgram>
+#include <Qt3DCore/qnodeid.h>
+#include <Qt3DRender/private/renderlogging_p.h>
+#include <Qt3DRender/private/uniform_p.h>
+#include <shadervariables_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QOpenGLShaderProgram;
+
+namespace Qt3DCore {
+class QFrameAllocator;
+}
+
+namespace Qt3DRender {
+namespace Render {
+
+class GraphicsContext;
+
+struct BlockToUBO {
+ int m_blockIndex;
+ Qt3DCore::QNodeId m_bufferID;
+ bool m_needsUpdate;
+ QHash<QString, QVariant> m_updatedProperties;
+};
+QT3D_DECLARE_TYPEINFO_2(Qt3DRender, Render, BlockToUBO, Q_MOVABLE_TYPE)
+
+struct BlockToSSBO {
+ int m_blockIndex;
+ int m_bindingIndex;
+ Qt3DCore::QNodeId m_bufferID;
+};
+QT3D_DECLARE_TYPEINFO_2(Qt3DRender, Render, BlockToSSBO, Q_PRIMITIVE_TYPE)
+
+
+struct PackUniformHash
+{
+ QVector<int> keys;
+ QVector<UniformValue> values;
+
+ PackUniformHash()
+ {
+ keys.reserve(10);
+ values.reserve(10);
+ }
+
+ void insert(int key, const UniformValue &value)
+ {
+ const int idx = keys.indexOf(key);
+ if (idx != -1) {
+ values[idx] = value;
+ } else {
+ keys.push_back(key);
+ values.push_back(value);
+ }
+ }
+
+ UniformValue value(int key) const
+ {
+ const int idx = keys.indexOf(key);
+ if (idx != -1)
+ return values.at(idx);
+ return UniformValue();
+ }
+
+ UniformValue& value(int key)
+ {
+ const int idx = keys.indexOf(key);
+ if (idx != -1)
+ return values[idx];
+ insert(key, UniformValue());
+ return value(key);
+ }
+
+ void erase(int idx)
+ {
+ keys.removeAt(idx);
+ values.removeAt(idx);
+ }
+
+ bool contains(int key) const
+ {
+ return keys.contains(key);
+ }
+};
+
+class Q_AUTOTEST_EXPORT ShaderParameterPack
+{
+public:
+ ~ShaderParameterPack();
+
+ void setUniform(const int glslNameId, const UniformValue &val);
+ void setTexture(const int glslNameId, int uniformArrayIndex, Qt3DCore::QNodeId id);
+ void setImage(const int glslNameId, int uniformArrayIndex, Qt3DCore::QNodeId id);
+
+ void setUniformBuffer(BlockToUBO blockToUBO);
+ void setShaderStorageBuffer(BlockToSSBO blockToSSBO);
+ void setSubmissionUniform(const ShaderUniform &uniform);
+
+ inline PackUniformHash &uniforms() { return m_uniforms; }
+ inline const PackUniformHash &uniforms() const { return m_uniforms; }
+ UniformValue uniform(const int glslNameId) const { return m_uniforms.value(glslNameId); }
+
+
+ struct NamedResource
+ {
+ enum Type {
+ Texture = 0,
+ Image
+ };
+
+ NamedResource() {}
+ NamedResource(const int glslNameId, Qt3DCore::QNodeId texId,
+ int uniformArrayIndex, Type type)
+ : glslNameId(glslNameId)
+ , nodeId(texId)
+ , uniformArrayIndex(uniformArrayIndex)
+ , type(type)
+ { }
+
+ int glslNameId;
+ Qt3DCore::QNodeId nodeId;
+ int uniformArrayIndex;
+ Type type;
+
+ bool operator==(const NamedResource &other) const
+ {
+ return glslNameId == other.glslNameId &&
+ nodeId == other.nodeId &&
+ uniformArrayIndex == other.uniformArrayIndex &&
+ type == other.type;
+ }
+
+ bool operator!=(const NamedResource &other) const
+ {
+ return !(*this == other);
+ }
+ };
+
+ inline QVector<NamedResource> textures() const { return m_textures; }
+ inline QVector<NamedResource> images() const { return m_images; }
+ inline QVector<BlockToUBO> uniformBuffers() const { return m_uniformBuffers; }
+ inline QVector<BlockToSSBO> shaderStorageBuffers() const { return m_shaderStorageBuffers; }
+ inline QVector<ShaderUniform> submissionUniforms() const { return m_submissionUniforms; }
+private:
+ PackUniformHash m_uniforms;
+
+ QVector<NamedResource> m_textures;
+ QVector<NamedResource> m_images;
+ QVector<BlockToUBO> m_uniformBuffers;
+ QVector<BlockToSSBO> m_shaderStorageBuffers;
+ QVector<ShaderUniform> m_submissionUniforms;
+
+ friend class RenderView;
+};
+QT3D_DECLARE_TYPEINFO_2(Qt3DRender, Render, ShaderParameterPack::NamedResource, Q_PRIMITIVE_TYPE)
+
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+Q_DECLARE_METATYPE(Qt3DRender::Render::ShaderParameterPack)
+
+#endif // QT3DRENDER_RENDER_SHADERPARAMETERPACK_P_H
diff --git a/src/plugins/renderers/opengl/renderer/shadervariables_p.h b/src/plugins/renderers/opengl/renderer/shadervariables_p.h
new file mode 100644
index 000000000..e0fa07dff
--- /dev/null
+++ b/src/plugins/renderers/opengl/renderer/shadervariables_p.h
@@ -0,0 +1,152 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DRENDER_RENDER_SHADERVARIABLES_P_H
+#define QT3DRENDER_RENDER_SHADERVARIABLES_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <Qt3DRender/qt3drender_global.h>
+#include <QOpenGLContext>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+namespace Render {
+
+struct ShaderAttribute
+{
+ ShaderAttribute()
+ : m_nameId(-1)
+ , m_type(0)
+ , m_size(0)
+ , m_location(-1)
+ {}
+
+ QString m_name;
+ int m_nameId;
+ GLenum m_type;
+ int m_size;
+ int m_location;
+};
+QT3D_DECLARE_TYPEINFO_2(Qt3DRender, Render, ShaderAttribute, Q_MOVABLE_TYPE)
+
+struct ShaderUniform
+{
+ ShaderUniform()
+ : m_nameId(-1)
+ , m_type(GL_NONE)
+ , m_size(0)
+ , m_offset(-1)
+ , m_location(-1)
+ , m_blockIndex(-1)
+ , m_arrayStride(-1)
+ , m_matrixStride(-1)
+ , m_rawByteSize(0)
+ {}
+
+ QString m_name;
+ int m_nameId;
+ GLenum m_type;
+ int m_size;
+ int m_offset; // -1 default, >= 0 if uniform defined in uniform block
+ int m_location; // -1 if uniform defined in a uniform block
+ int m_blockIndex; // -1 is the default, >= 0 if uniform defined in uniform block
+ int m_arrayStride; // -1 is the default, >= 0 if uniform defined in uniform block and if it's an array
+ int m_matrixStride; // -1 is the default, >= 0 uniform defined in uniform block and is a matrix
+ uint m_rawByteSize; // contains byte size (size / type / strides)
+ // size, offset and strides are in bytes
+};
+QT3D_DECLARE_TYPEINFO_2(Qt3DRender, Render, ShaderUniform, Q_MOVABLE_TYPE)
+
+struct ShaderUniformBlock
+{
+ ShaderUniformBlock()
+ : m_nameId(-1)
+ , m_index(-1)
+ , m_binding(-1)
+ , m_activeUniformsCount(0)
+ , m_size(0)
+ {}
+
+ QString m_name;
+ int m_nameId;
+ int m_index;
+ int m_binding;
+ int m_activeUniformsCount;
+ int m_size;
+};
+QT3D_DECLARE_TYPEINFO_2(Qt3DRender, Render, ShaderUniformBlock, Q_MOVABLE_TYPE)
+
+struct ShaderStorageBlock
+{
+ ShaderStorageBlock()
+ : m_nameId(-1)
+ , m_index(-1)
+ , m_binding(-1)
+ , m_size(0)
+ , m_activeVariablesCount(0)
+ {}
+
+ QString m_name;
+ int m_nameId;
+ int m_index;
+ int m_binding;
+ int m_size;
+ int m_activeVariablesCount;
+};
+QT3D_DECLARE_TYPEINFO_2(Qt3DRender, Render, ShaderStorageBlock, Q_MOVABLE_TYPE)
+
+} // namespace Render
+
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDER_SHADERVARIABLES_P_H