summaryrefslogtreecommitdiffstats
path: root/src/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins')
-rw-r--r--src/plugins/renderers/opengl/debug/imguirenderer.cpp10
-rw-r--r--src/plugins/renderers/opengl/graphicshelpers/submissioncontext.cpp16
-rw-r--r--src/plugins/renderers/opengl/graphicshelpers/submissioncontext_p.h2
-rw-r--r--src/plugins/renderers/opengl/jobs/materialparametergathererjob.cpp26
-rw-r--r--src/plugins/renderers/opengl/jobs/materialparametergathererjob_p.h3
-rw-r--r--src/plugins/renderers/opengl/jobs/renderviewcommandbuilderjob.cpp42
-rw-r--r--src/plugins/renderers/opengl/jobs/renderviewcommandbuilderjob_p.h4
-rw-r--r--src/plugins/renderers/opengl/jobs/renderviewcommandupdaterjob.cpp7
-rw-r--r--src/plugins/renderers/opengl/jobs/renderviewjobutils.cpp9
-rw-r--r--src/plugins/renderers/opengl/jobs/renderviewjobutils_p.h4
-rw-r--r--src/plugins/renderers/opengl/opengl.pro3
-rw-r--r--src/plugins/renderers/opengl/renderer/gllights.cpp345
-rw-r--r--src/plugins/renderers/opengl/renderer/gllights_p.h88
-rw-r--r--src/plugins/renderers/opengl/renderer/glshader.cpp122
-rw-r--r--src/plugins/renderers/opengl/renderer/glshader_p.h53
-rw-r--r--src/plugins/renderers/opengl/renderer/renderer.cpp36
-rw-r--r--src/plugins/renderers/opengl/renderer/renderer.pri2
-rw-r--r--src/plugins/renderers/opengl/renderer/renderer_p.h4
-rw-r--r--src/plugins/renderers/opengl/renderer/renderview.cpp275
-rw-r--r--src/plugins/renderers/opengl/renderer/renderview_p.h16
-rw-r--r--src/plugins/renderers/opengl/renderer/renderviewbuilder.cpp137
-rw-r--r--src/plugins/renderers/opengl/renderer/renderviewbuilder_p.h8
-rw-r--r--src/plugins/renderers/opengl/renderer/shaderparameterpack.cpp10
-rw-r--r--src/plugins/renderers/opengl/renderer/shaderparameterpack_p.h15
-rw-r--r--src/plugins/renderers/renderers.pro4
-rw-r--r--src/plugins/renderers/rhi/graphicshelpers/graphicshelpers.pri9
-rw-r--r--src/plugins/renderers/rhi/graphicshelpers/submissioncontext.cpp1871
-rw-r--r--src/plugins/renderers/rhi/graphicshelpers/submissioncontext_p.h266
-rw-r--r--src/plugins/renderers/rhi/io/io.pri8
-rw-r--r--src/plugins/renderers/rhi/io/rhibuffer.cpp199
-rw-r--r--src/plugins/renderers/rhi/io/rhibuffer_p.h116
-rw-r--r--src/plugins/renderers/rhi/jobs/filtercompatibletechniquejob.cpp97
-rw-r--r--src/plugins/renderers/rhi/jobs/filtercompatibletechniquejob_p.h96
-rw-r--r--src/plugins/renderers/rhi/jobs/jobs.pri17
-rw-r--r--src/plugins/renderers/rhi/jobs/materialparametergathererjob.cpp140
-rw-r--r--src/plugins/renderers/rhi/jobs/materialparametergathererjob_p.h124
-rw-r--r--src/plugins/renderers/rhi/jobs/renderviewcommandbuilderjob.cpp82
-rw-r--r--src/plugins/renderers/rhi/jobs/renderviewcommandbuilderjob_p.h (renamed from src/plugins/renderers/opengl/jobs/renderviewbuilderjob_p.h)14
-rw-r--r--src/plugins/renderers/rhi/jobs/renderviewcommandupdaterjob.cpp (renamed from src/plugins/renderers/opengl/jobs/renderviewbuilderjob.cpp)21
-rw-r--r--src/plugins/renderers/rhi/jobs/renderviewcommandupdaterjob_p.h109
-rw-r--r--src/plugins/renderers/rhi/jobs/renderviewinitializerjob.cpp104
-rw-r--r--src/plugins/renderers/rhi/jobs/renderviewinitializerjob_p.h108
-rw-r--r--src/plugins/renderers/rhi/jobs/renderviewjobutils.cpp583
-rw-r--r--src/plugins/renderers/rhi/jobs/renderviewjobutils_p.h189
-rw-r--r--src/plugins/renderers/rhi/main.cpp60
-rw-r--r--src/plugins/renderers/rhi/managers/managers.pri8
-rw-r--r--src/plugins/renderers/rhi/managers/rhihandle_types_p.h85
-rw-r--r--src/plugins/renderers/rhi/managers/rhiresourcemanagers.cpp87
-rw-r--r--src/plugins/renderers/rhi/managers/rhiresourcemanagers_p.h153
-rw-r--r--src/plugins/renderers/rhi/renderer/commandexecuter.cpp400
-rw-r--r--src/plugins/renderers/rhi/renderer/commandexecuter_p.h98
-rw-r--r--src/plugins/renderers/rhi/renderer/logging.cpp69
-rw-r--r--src/plugins/renderers/rhi/renderer/logging_p.h85
-rw-r--r--src/plugins/renderers/rhi/renderer/rendercommand.cpp112
-rw-r--r--src/plugins/renderers/rhi/renderer/rendercommand_p.h211
-rw-r--r--src/plugins/renderers/rhi/renderer/renderer.cpp2537
-rw-r--r--src/plugins/renderers/rhi/renderer/renderer.pri27
-rw-r--r--src/plugins/renderers/rhi/renderer/renderer_p.h443
-rw-r--r--src/plugins/renderers/rhi/renderer/renderercache_p.h101
-rw-r--r--src/plugins/renderers/rhi/renderer/renderqueue.cpp138
-rw-r--r--src/plugins/renderers/rhi/renderer/renderqueue_p.h106
-rw-r--r--src/plugins/renderers/rhi/renderer/renderview.cpp1253
-rw-r--r--src/plugins/renderers/rhi/renderer/renderview_p.h455
-rw-r--r--src/plugins/renderers/rhi/renderer/renderviewbuilder.cpp841
-rw-r--r--src/plugins/renderers/rhi/renderer/renderviewbuilder_p.h153
-rw-r--r--src/plugins/renderers/rhi/renderer/rhigraphicspipeline.cpp81
-rw-r--r--src/plugins/renderers/rhi/renderer/rhigraphicspipeline_p.h127
-rw-r--r--src/plugins/renderers/rhi/renderer/rhishader.cpp681
-rw-r--r--src/plugins/renderers/rhi/renderer/rhishader_p.h198
-rw-r--r--src/plugins/renderers/rhi/renderer/shaderparameterpack.cpp114
-rw-r--r--src/plugins/renderers/rhi/renderer/shaderparameterpack_p.h215
-rw-r--r--src/plugins/renderers/rhi/renderer/shadervariables_p.h126
-rw-r--r--src/plugins/renderers/rhi/rhi.pri17
-rw-r--r--src/plugins/renderers/rhi/rhi.pro32
-rw-r--r--src/plugins/renderers/rhi/rhirenderer.json3
-rw-r--r--src/plugins/renderers/rhi/textures/renderbuffer.cpp111
-rw-r--r--src/plugins/renderers/rhi/textures/renderbuffer_p.h92
-rw-r--r--src/plugins/renderers/rhi/textures/texture.cpp918
-rw-r--r--src/plugins/renderers/rhi/textures/texture_p.h249
-rw-r--r--src/plugins/renderers/rhi/textures/textures.pri9
80 files changed, 15480 insertions, 309 deletions
diff --git a/src/plugins/renderers/opengl/debug/imguirenderer.cpp b/src/plugins/renderers/opengl/debug/imguirenderer.cpp
index 8dd9b98fe..0acee945a 100644
--- a/src/plugins/renderers/opengl/debug/imguirenderer.cpp
+++ b/src/plugins/renderers/opengl/debug/imguirenderer.cpp
@@ -283,13 +283,21 @@ void ImGuiRenderer::renderDebugOverlay(const QVector<RenderView *> &renderViews,
QMetaObject::invokeMethod(m_renderer->services()->systemInformation(), "dumpCommand",
Qt::QueuedConnection, Q_ARG(QString, QLatin1String("render framegraph")));
ImGui::SameLine();
- if (ImGui::Button("FrameGraph Paths##1"))
+ if (ImGui::Button("Render Views##1"))
QMetaObject::invokeMethod(m_renderer->services()->systemInformation(), "dumpCommand",
Qt::QueuedConnection, Q_ARG(QString, QLatin1String("render framepaths")));
+
+ ImGui::AlignTextToFramePadding();
+ ImGui::Text(" ");
+ ImGui::SameLine();
+ if (ImGui::Button("Filter State##1"))
+ QMetaObject::invokeMethod(m_renderer->services()->systemInformation(), "dumpCommand",
+ Qt::QueuedConnection, Q_ARG(QString, QLatin1String("render filterstates")));
ImGui::SameLine();
if (ImGui::Button("JobsGraph##1"))
QMetaObject::invokeMethod(m_renderer->services()->systemInformation(), "dumpCommand",
Qt::QueuedConnection, Q_ARG(QString, QLatin1String("dump jobs")));
+
ImGui::End();
if (m_showGLInfoWindow)
diff --git a/src/plugins/renderers/opengl/graphicshelpers/submissioncontext.cpp b/src/plugins/renderers/opengl/graphicshelpers/submissioncontext.cpp
index 519f2ae98..d4d3457ab 100644
--- a/src/plugins/renderers/opengl/graphicshelpers/submissioncontext.cpp
+++ b/src/plugins/renderers/opengl/graphicshelpers/submissioncontext.cpp
@@ -1166,7 +1166,7 @@ void SubmissionContext::setUpdatedTexture(const Qt3DCore::QNodeIdVector &updated
// It will be easier if the QGraphicContext applies the QUniformPack
// than the other way around
-bool SubmissionContext::setParameters(ShaderParameterPack &parameterPack)
+bool SubmissionContext::setParameters(ShaderParameterPack &parameterPack, GLShader *shader)
{
static const int irradianceId = StringToInt::lookupId(QLatin1String("envLight.irradiance"));
static const int specularId = StringToInt::lookupId(QLatin1String("envLight.specular"));
@@ -1231,7 +1231,7 @@ bool SubmissionContext::setParameters(ShaderParameterPack &parameterPack)
}
}
- QOpenGLShaderProgram *shader = activeShader();
+ QOpenGLShaderProgram *glShader = activeShader();
// TO DO: We could cache the binding points somehow and only do the binding when necessary
// for SSBO and UBO
@@ -1245,7 +1245,7 @@ bool SubmissionContext::setParameters(ShaderParameterPack &parameterPack)
// This is currently not required as we are introspecting the bindingIndex
// value from the shaders and not replacing them, making such a call useless
// bindShaderStorageBlock(shader->programId(), b.m_blockIndex, b.m_bindingIndex);
- bindShaderStorageBlock(shader->programId(), b.m_blockIndex, b.m_bindingIndex);
+ bindShaderStorageBlock(glShader->programId(), b.m_blockIndex, b.m_bindingIndex);
// Needed to avoid conflict where the buffer would already
// be bound as a VertexArray
bindGLBuffer(ssbo, GLBuffer::ShaderStorageBuffer);
@@ -1260,7 +1260,7 @@ bool SubmissionContext::setParameters(ShaderParameterPack &parameterPack)
for (const BlockToUBO &b : blockToUBOs) {
Buffer *cpuBuffer = m_renderer->nodeManagers()->bufferManager()->lookupResource(b.m_bufferID);
GLBuffer *ubo = glBufferForRenderBuffer(cpuBuffer);
- bindUniformBlock(shader->programId(), b.m_blockIndex, uboIndex);
+ bindUniformBlock(glShader->programId(), b.m_blockIndex, uboIndex);
// Needed to avoid conflict where the buffer would already
// be bound as a VertexArray
bindGLBuffer(ubo, GLBuffer::UniformBuffer);
@@ -1270,11 +1270,11 @@ bool SubmissionContext::setParameters(ShaderParameterPack &parameterPack)
// Update uniforms in the Default Uniform Block
const PackUniformHash values = parameterPack.uniforms();
- const QVector<ShaderUniform> activeUniforms = parameterPack.submissionUniforms();
+ const QVector<int> &activeUniformsIndices = parameterPack.submissionUniformIndices();
+ const QVector<ShaderUniform> &shaderUniforms = shader->uniforms();
- for (const ShaderUniform &uniform : activeUniforms) {
- // We can use [] as we are sure the the uniform wouldn't
- // be un activeUniforms if there wasn't a matching value
+ for (const int shaderUniformIndex : activeUniformsIndices) {
+ const ShaderUniform &uniform = shaderUniforms[shaderUniformIndex];
const UniformValue &v = values.value(uniform.m_nameId);
// skip invalid textures/images
diff --git a/src/plugins/renderers/opengl/graphicshelpers/submissioncontext_p.h b/src/plugins/renderers/opengl/graphicshelpers/submissioncontext_p.h
index 59b2b78f3..4c895013c 100644
--- a/src/plugins/renderers/opengl/graphicshelpers/submissioncontext_p.h
+++ b/src/plugins/renderers/opengl/graphicshelpers/submissioncontext_p.h
@@ -137,7 +137,7 @@ public:
GLBuffer *glBufferForRenderBuffer(Buffer *buf);
// Parameters
- bool setParameters(ShaderParameterPack &parameterPack);
+ bool setParameters(ShaderParameterPack &parameterPack, GLShader *shader);
// RenderState
void setCurrentStateSet(RenderStateSet* ss);
diff --git a/src/plugins/renderers/opengl/jobs/materialparametergathererjob.cpp b/src/plugins/renderers/opengl/jobs/materialparametergathererjob.cpp
index 1f51ceba3..1dd26b847 100644
--- a/src/plugins/renderers/opengl/jobs/materialparametergathererjob.cpp
+++ b/src/plugins/renderers/opengl/jobs/materialparametergathererjob.cpp
@@ -59,8 +59,32 @@ const int likelyNumberOfParameters = 24;
} // anonymous
+class MaterialParameterGathererJobPrivate : public Qt3DCore::QAspectJobPrivate
+{
+public:
+ MaterialParameterGathererJobPrivate(MaterialParameterGathererJob *q) : q_ptr(q) { }
+ ~MaterialParameterGathererJobPrivate() override = default;
+
+ bool isRequired() const override;
+ void postFrame(Qt3DCore::QAspectManager *manager) override;
+
+ MaterialParameterGathererJob *q_ptr;
+ Q_DECLARE_PUBLIC(MaterialParameterGathererJob)
+};
+
+bool MaterialParameterGathererJobPrivate::isRequired() const
+{
+ return !q_ptr->m_handles.isEmpty();
+}
+
+void MaterialParameterGathererJobPrivate::postFrame(Qt3DCore::QAspectManager *manager)
+{
+ Q_UNUSED(manager)
+ materialParameterGathererCounter = 0;
+}
+
MaterialParameterGathererJob::MaterialParameterGathererJob()
- : Qt3DCore::QAspectJob()
+ : Qt3DCore::QAspectJob(*new MaterialParameterGathererJobPrivate(this))
, m_manager(nullptr)
, m_techniqueFilter(nullptr)
, m_renderPassFilter(nullptr)
diff --git a/src/plugins/renderers/opengl/jobs/materialparametergathererjob_p.h b/src/plugins/renderers/opengl/jobs/materialparametergathererjob_p.h
index cd5af8124..8c9997827 100644
--- a/src/plugins/renderers/opengl/jobs/materialparametergathererjob_p.h
+++ b/src/plugins/renderers/opengl/jobs/materialparametergathererjob_p.h
@@ -71,6 +71,7 @@ namespace OpenGL {
class Renderer;
// TO be executed for each FrameGraph branch with a given RenderPassFilter/TechniqueFilter
+class MaterialParameterGathererJobPrivate;
class Q_AUTOTEST_EXPORT MaterialParameterGathererJob : public Qt3DCore::QAspectJob
{
@@ -96,6 +97,8 @@ private:
// Material id to array of RenderPasse with parameters
MaterialParameterGathererData m_parameters;
QVector<HMaterial> m_handles;
+
+ Q_DECLARE_PRIVATE(MaterialParameterGathererJob)
};
typedef QSharedPointer<MaterialParameterGathererJob> MaterialParameterGathererJobPtr;
diff --git a/src/plugins/renderers/opengl/jobs/renderviewcommandbuilderjob.cpp b/src/plugins/renderers/opengl/jobs/renderviewcommandbuilderjob.cpp
index d8a33b693..d5e17e0bf 100644
--- a/src/plugins/renderers/opengl/jobs/renderviewcommandbuilderjob.cpp
+++ b/src/plugins/renderers/opengl/jobs/renderviewcommandbuilderjob.cpp
@@ -53,8 +53,32 @@ namespace {
int renderViewInstanceCounter = 0;
} // anonymous
+class RenderViewCommandBuilderJobPrivate : public Qt3DCore::QAspectJobPrivate
+{
+public:
+ RenderViewCommandBuilderJobPrivate(RenderViewCommandBuilderJob *q) : q_ptr(q) { }
+ ~RenderViewCommandBuilderJobPrivate() override = default;
+
+ bool isRequired() const override;
+ void postFrame(Qt3DCore::QAspectManager *manager) override;
+
+ RenderViewCommandBuilderJob *q_ptr;
+ Q_DECLARE_PUBLIC(RenderViewCommandBuilderJob)
+};
+
+bool RenderViewCommandBuilderJobPrivate::isRequired() const
+{
+ return q_ptr->m_renderView && !q_ptr->m_renderView->noDraw() && q_ptr->m_count > 0;
+}
+
+void RenderViewCommandBuilderJobPrivate::postFrame(Qt3DCore::QAspectManager *manager)
+{
+ Q_UNUSED(manager)
+ renderViewInstanceCounter = 0;
+}
+
RenderViewCommandBuilderJob::RenderViewCommandBuilderJob()
- : Qt3DCore::QAspectJob()
+ : Qt3DCore::QAspectJob(*new RenderViewCommandBuilderJobPrivate(this))
, m_offset(0)
, m_count(0)
, m_renderView(nullptr)
@@ -64,18 +88,14 @@ RenderViewCommandBuilderJob::RenderViewCommandBuilderJob()
void RenderViewCommandBuilderJob::run()
{
- if (!m_renderView->noDraw()) {
- if (m_count == 0)
- return;
-
- const bool isDraw = !m_renderView->isCompute();
- if (isDraw)
- m_commandData = m_renderView->buildDrawRenderCommands(m_entities, m_offset, m_count);
- else
- m_commandData = m_renderView->buildComputeRenderCommands(m_entities, m_offset, m_count);
- }
+ const bool isDraw = !m_renderView->isCompute();
+ if (isDraw)
+ m_commandData = m_renderView->buildDrawRenderCommands(m_entities, m_offset, m_count);
+ else
+ m_commandData = m_renderView->buildComputeRenderCommands(m_entities, m_offset, m_count);
}
+
} // OpenGL
} // Render
diff --git a/src/plugins/renderers/opengl/jobs/renderviewcommandbuilderjob_p.h b/src/plugins/renderers/opengl/jobs/renderviewcommandbuilderjob_p.h
index e9f8bb10a..52c055285 100644
--- a/src/plugins/renderers/opengl/jobs/renderviewcommandbuilderjob_p.h
+++ b/src/plugins/renderers/opengl/jobs/renderviewcommandbuilderjob_p.h
@@ -63,6 +63,8 @@ namespace Render {
namespace OpenGL {
+class RenderViewCommandBuilderJobPrivate;
+
class Q_AUTOTEST_EXPORT RenderViewCommandBuilderJob : public Qt3DCore::QAspectJob
{
public:
@@ -85,6 +87,8 @@ private:
RenderView *m_renderView;
QVector<Entity *> m_entities;
EntityRenderCommandData m_commandData;
+
+ Q_DECLARE_PRIVATE(RenderViewCommandBuilderJob)
};
typedef QSharedPointer<RenderViewCommandBuilderJob> RenderViewCommandBuilderJobPtr;
diff --git a/src/plugins/renderers/opengl/jobs/renderviewcommandupdaterjob.cpp b/src/plugins/renderers/opengl/jobs/renderviewcommandupdaterjob.cpp
index ed0854ecf..9bbdf50fd 100644
--- a/src/plugins/renderers/opengl/jobs/renderviewcommandupdaterjob.cpp
+++ b/src/plugins/renderers/opengl/jobs/renderviewcommandupdaterjob.cpp
@@ -60,17 +60,16 @@ public:
RenderViewCommandUpdaterJobPrivate(RenderViewCommandUpdaterJob *q) : q_ptr(q) { }
~RenderViewCommandUpdaterJobPrivate() override = default;
- bool isRequired() override;
+ bool isRequired() const override;
void postFrame(Qt3DCore::QAspectManager *manager) override;
RenderViewCommandUpdaterJob *q_ptr;
Q_DECLARE_PUBLIC(RenderViewCommandUpdaterJob)
};
-bool RenderViewCommandUpdaterJobPrivate::isRequired()
+bool RenderViewCommandUpdaterJobPrivate::isRequired() const
{
- Q_Q(RenderViewCommandUpdaterJob);
-
+ Q_Q(const RenderViewCommandUpdaterJob);
return q->m_renderView && !q->m_renderView->noDraw() && q->m_count > 0;
}
diff --git a/src/plugins/renderers/opengl/jobs/renderviewjobutils.cpp b/src/plugins/renderers/opengl/jobs/renderviewjobutils.cpp
index d4835054b..8774ac368 100644
--- a/src/plugins/renderers/opengl/jobs/renderviewjobutils.cpp
+++ b/src/plugins/renderers/opengl/jobs/renderviewjobutils.cpp
@@ -473,7 +473,10 @@ UniformBlockValueBuilder::~UniformBlockValueBuilder()
{
}
-void UniformBlockValueBuilder::buildActiveUniformNameValueMapHelper(ShaderData *currentShaderData, const QString &blockName, const QString &qmlPropertyName, const QVariant &value)
+void UniformBlockValueBuilder::buildActiveUniformNameValueMapHelper(const ShaderData *currentShaderData,
+ const QString &blockName,
+ const QString &qmlPropertyName,
+ const QVariant &value)
{
// In the end, values are either scalar or a scalar array
// Composed elements (structs, structs array) are simplified into simple scalars
@@ -534,7 +537,9 @@ void UniformBlockValueBuilder::buildActiveUniformNameValueMapHelper(ShaderData *
}
}
-void UniformBlockValueBuilder::buildActiveUniformNameValueMapStructHelper(ShaderData *rShaderData, const QString &blockName, const QString &qmlPropertyName)
+void UniformBlockValueBuilder::buildActiveUniformNameValueMapStructHelper(const ShaderData *rShaderData,
+ const QString &blockName,
+ const QString &qmlPropertyName)
{
const QHash<QString, ShaderData::PropertyValue> &properties = rShaderData->properties();
auto it = properties.begin();
diff --git a/src/plugins/renderers/opengl/jobs/renderviewjobutils_p.h b/src/plugins/renderers/opengl/jobs/renderviewjobutils_p.h
index 7b5ba2bfd..bc5bfd8aa 100644
--- a/src/plugins/renderers/opengl/jobs/renderviewjobutils_p.h
+++ b/src/plugins/renderers/opengl/jobs/renderviewjobutils_p.h
@@ -162,11 +162,11 @@ struct Q_AUTOTEST_EXPORT UniformBlockValueBuilder
QT3D_ALIGNED_MALLOC_AND_FREE()
- void buildActiveUniformNameValueMapHelper(ShaderData *currentShaderData,
+ void buildActiveUniformNameValueMapHelper(const ShaderData *currentShaderData,
const QString &blockName,
const QString &qmlPropertyName,
const QVariant &value);
- void buildActiveUniformNameValueMapStructHelper(ShaderData *rShaderData,
+ void buildActiveUniformNameValueMapStructHelper(const ShaderData *rShaderData,
const QString &blockName,
const QString &qmlPropertyName = QString());
diff --git a/src/plugins/renderers/opengl/opengl.pro b/src/plugins/renderers/opengl/opengl.pro
index ebd4ff5d1..f098513e8 100644
--- a/src/plugins/renderers/opengl/opengl.pro
+++ b/src/plugins/renderers/opengl/opengl.pro
@@ -1,5 +1,8 @@
TARGET = openglrenderer
+# We use QT_AUTOTEST_EXPORT to test the plug-ins, which needs QT_BUILDING_QT
+DEFINES += QT_BUILDING_QT
+
include(opengl.pri)
SOURCES += \
diff --git a/src/plugins/renderers/opengl/renderer/gllights.cpp b/src/plugins/renderers/opengl/renderer/gllights.cpp
new file mode 100644
index 000000000..c87fd766b
--- /dev/null
+++ b/src/plugins/renderers/opengl/renderer/gllights.cpp
@@ -0,0 +1,345 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <Qt3DRender/private/stringtoint_p.h>
+#include "gllights_p.h"
+
+#define LIGHT_POSITION_NAME QLatin1String(".position")
+#define LIGHT_TYPE_NAME QLatin1String(".type")
+#define LIGHT_COLOR_NAME QLatin1String(".color")
+#define LIGHT_INTENSITY_NAME QLatin1String(".intensity")
+#define LIGHT_DIRECTION_NAME QLatin1String(".direction")
+#define LIGHT_LINEAR_ATTENUATION_NAME QLatin1String(".linearAttenuation")
+#define LIGHT_QUADRATIC_ATTENUATION_NAME QLatin1String(".quadraticAttenuation")
+#define LIGHT_CONSTANT_ATTENUATION_NAME QLatin1String(".constantAttenuation")
+#define LIGHT_CUT_OFF_ANGLE_NAME QLatin1String(".cutOffAngle")
+
+#define DECLARE_LIGHT_STRUCT_NAME(idx)\
+ QLatin1String("lights[") + QLatin1Char(char('0' + idx)) + QLatin1Char(']')
+
+#define DECLARE_LIGHT_POSITION_NAME(idx)\
+ StringToInt::lookupId(LIGHT_STRUCT_NAMES[idx] + LIGHT_POSITION_NAME)
+
+#define DECLARE_LIGHT_TYPE_NAME(idx)\
+ StringToInt::lookupId(LIGHT_STRUCT_NAMES[idx] + LIGHT_TYPE_NAME)
+
+#define DECLARE_LIGHT_COLOR_NAME(idx)\
+ StringToInt::lookupId(LIGHT_STRUCT_NAMES[idx] + LIGHT_COLOR_NAME)
+
+#define DECLARE_LIGHT_INTENSITY_NAME(idx)\
+ StringToInt::lookupId(LIGHT_STRUCT_NAMES[idx] + LIGHT_INTENSITY_NAME)
+
+#define DECLARE_LIGHT_DIRECTION_NAME(idx)\
+ StringToInt::lookupId(LIGHT_STRUCT_NAMES[idx] + LIGHT_DIRECTION_NAME)
+
+#define DECLARE_LIGHT_LINEAR_ATTENUATION_NAME(idx)\
+ StringToInt::lookupId(LIGHT_STRUCT_NAMES[idx] + LIGHT_LINEAR_ATTENUATION_NAME)
+
+#define DECLARE_LIGHT_QUADRATIC_ATTENUATION_NAME(idx)\
+ StringToInt::lookupId(LIGHT_STRUCT_NAMES[idx] + LIGHT_QUADRATIC_ATTENUATION_NAME)
+
+#define DECLARE_LIGHT_CONSTANT_ATTENUATION_NAME(idx)\
+ StringToInt::lookupId(LIGHT_STRUCT_NAMES[idx] + LIGHT_CONSTANT_ATTENUATION_NAME)
+
+#define DECLARE_LIGHT_CUT_OFF_ANGLE_NAME(idx)\
+ StringToInt::lookupId(LIGHT_STRUCT_NAMES[idx] + LIGHT_CUT_OFF_ANGLE_NAME)
+
+#define DECLARE_LIGHT_STRUCT_UNROLL_NAME(idx)\
+ QLatin1String("light_") + QLatin1Char(char('0' + idx))
+
+#define DECLARE_LIGHT_POSITION_UNROLL_NAME(idx)\
+ StringToInt::lookupId(LIGHT_STRUCT_UNROLL_NAMES[idx] + LIGHT_POSITION_NAME)
+
+#define DECLARE_LIGHT_TYPE_UNROLL_NAME(idx)\
+ StringToInt::lookupId(LIGHT_STRUCT_UNROLL_NAMES[idx] + LIGHT_TYPE_NAME)
+
+#define DECLARE_LIGHT_COLOR_UNROLL_NAME(idx)\
+ StringToInt::lookupId(LIGHT_STRUCT_UNROLL_NAMES[idx] + LIGHT_COLOR_NAME)
+
+#define DECLARE_LIGHT_INTENSITY_UNROLL_NAME(idx)\
+ StringToInt::lookupId(LIGHT_STRUCT_UNROLL_NAMES[idx] + LIGHT_INTENSITY_NAME)
+
+#define DECLARE_LIGHT_DIRECTION_UNROLL_NAME(idx)\
+ StringToInt::lookupId(LIGHT_STRUCT_UNROLL_NAMES[idx] + LIGHT_DIRECTION_NAME)
+
+#define DECLARE_LIGHT_LINEAR_ATTENUATION_UNROLL_NAME(idx)\
+ StringToInt::lookupId(LIGHT_STRUCT_UNROLL_NAMES[idx] + LIGHT_LINEAR_ATTENUATION_NAME)
+
+#define DECLARE_LIGHT_QUADRATIC_ATTENUATION_UNROLL_NAME(idx)\
+ StringToInt::lookupId(LIGHT_STRUCT_UNROLL_NAMES[idx] + LIGHT_QUADRATIC_ATTENUATION_NAME)
+
+#define DECLARE_LIGHT_CONSTANT_ATTENUATION_UNROLL_NAME(idx)\
+ StringToInt::lookupId(LIGHT_STRUCT_UNROLL_NAMES[idx] + LIGHT_CONSTANT_ATTENUATION_NAME)
+
+#define DECLARE_LIGHT_CUT_OFF_ANGLE_UNROLL_NAME(idx)\
+ StringToInt::lookupId(LIGHT_STRUCT_UNROLL_NAMES[idx] + LIGHT_CUT_OFF_ANGLE_NAME)
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+namespace Render {
+namespace OpenGL {
+
+int GLLights::LIGHT_COUNT_NAME_ID = StringToInt::lookupId(QLatin1String("lightCount"));
+
+QString GLLights::LIGHT_STRUCT_NAMES[MAX_LIGHTS] = {
+ DECLARE_LIGHT_STRUCT_NAME(0),
+ DECLARE_LIGHT_STRUCT_NAME(1),
+ DECLARE_LIGHT_STRUCT_NAME(2),
+ DECLARE_LIGHT_STRUCT_NAME(3),
+ DECLARE_LIGHT_STRUCT_NAME(4),
+ DECLARE_LIGHT_STRUCT_NAME(5),
+ DECLARE_LIGHT_STRUCT_NAME(6),
+ DECLARE_LIGHT_STRUCT_NAME(7)
+};
+
+int GLLights::LIGHT_POSITION_NAMES[MAX_LIGHTS] = {
+ DECLARE_LIGHT_POSITION_NAME(0),
+ DECLARE_LIGHT_POSITION_NAME(1),
+ DECLARE_LIGHT_POSITION_NAME(2),
+ DECLARE_LIGHT_POSITION_NAME(3),
+ DECLARE_LIGHT_POSITION_NAME(4),
+ DECLARE_LIGHT_POSITION_NAME(5),
+ DECLARE_LIGHT_POSITION_NAME(6),
+ DECLARE_LIGHT_POSITION_NAME(7)
+};
+
+int GLLights::LIGHT_TYPE_NAMES[MAX_LIGHTS] = {
+ DECLARE_LIGHT_TYPE_NAME(0),
+ DECLARE_LIGHT_TYPE_NAME(1),
+ DECLARE_LIGHT_TYPE_NAME(2),
+ DECLARE_LIGHT_TYPE_NAME(3),
+ DECLARE_LIGHT_TYPE_NAME(4),
+ DECLARE_LIGHT_TYPE_NAME(5),
+ DECLARE_LIGHT_TYPE_NAME(6),
+ DECLARE_LIGHT_TYPE_NAME(7)
+};
+
+int GLLights::LIGHT_COLOR_NAMES[MAX_LIGHTS] = {
+ DECLARE_LIGHT_COLOR_NAME(0),
+ DECLARE_LIGHT_COLOR_NAME(1),
+ DECLARE_LIGHT_COLOR_NAME(2),
+ DECLARE_LIGHT_COLOR_NAME(3),
+ DECLARE_LIGHT_COLOR_NAME(4),
+ DECLARE_LIGHT_COLOR_NAME(5),
+ DECLARE_LIGHT_COLOR_NAME(6),
+ DECLARE_LIGHT_COLOR_NAME(7)
+};
+
+int GLLights::LIGHT_INTENSITY_NAMES[MAX_LIGHTS] = {
+ DECLARE_LIGHT_INTENSITY_NAME(0),
+ DECLARE_LIGHT_INTENSITY_NAME(1),
+ DECLARE_LIGHT_INTENSITY_NAME(2),
+ DECLARE_LIGHT_INTENSITY_NAME(3),
+ DECLARE_LIGHT_INTENSITY_NAME(4),
+ DECLARE_LIGHT_INTENSITY_NAME(5),
+ DECLARE_LIGHT_INTENSITY_NAME(6),
+ DECLARE_LIGHT_INTENSITY_NAME(7)
+};
+
+int GLLights::LIGHT_DIRECTION_NAMES[MAX_LIGHTS] = {
+ DECLARE_LIGHT_DIRECTION_NAME(0),
+ DECLARE_LIGHT_DIRECTION_NAME(1),
+ DECLARE_LIGHT_DIRECTION_NAME(2),
+ DECLARE_LIGHT_DIRECTION_NAME(3),
+ DECLARE_LIGHT_DIRECTION_NAME(4),
+ DECLARE_LIGHT_DIRECTION_NAME(5),
+ DECLARE_LIGHT_DIRECTION_NAME(6),
+ DECLARE_LIGHT_DIRECTION_NAME(7)
+};
+
+int GLLights::LIGHT_LINEAR_ATTENUATION_NAMES[MAX_LIGHTS] = {
+ DECLARE_LIGHT_LINEAR_ATTENUATION_NAME(0),
+ DECLARE_LIGHT_LINEAR_ATTENUATION_NAME(1),
+ DECLARE_LIGHT_LINEAR_ATTENUATION_NAME(2),
+ DECLARE_LIGHT_LINEAR_ATTENUATION_NAME(3),
+ DECLARE_LIGHT_LINEAR_ATTENUATION_NAME(4),
+ DECLARE_LIGHT_LINEAR_ATTENUATION_NAME(5),
+ DECLARE_LIGHT_LINEAR_ATTENUATION_NAME(6),
+ DECLARE_LIGHT_LINEAR_ATTENUATION_NAME(7)
+};
+
+int GLLights::LIGHT_QUADRATIC_ATTENUATION_NAMES[MAX_LIGHTS] = {
+ DECLARE_LIGHT_QUADRATIC_ATTENUATION_NAME(0),
+ DECLARE_LIGHT_QUADRATIC_ATTENUATION_NAME(1),
+ DECLARE_LIGHT_QUADRATIC_ATTENUATION_NAME(2),
+ DECLARE_LIGHT_QUADRATIC_ATTENUATION_NAME(3),
+ DECLARE_LIGHT_QUADRATIC_ATTENUATION_NAME(4),
+ DECLARE_LIGHT_QUADRATIC_ATTENUATION_NAME(5),
+ DECLARE_LIGHT_QUADRATIC_ATTENUATION_NAME(6),
+ DECLARE_LIGHT_QUADRATIC_ATTENUATION_NAME(7)
+};
+
+int GLLights::LIGHT_CONSTANT_ATTENUATION_NAMES[MAX_LIGHTS] = {
+ DECLARE_LIGHT_CONSTANT_ATTENUATION_NAME(0),
+ DECLARE_LIGHT_CONSTANT_ATTENUATION_NAME(1),
+ DECLARE_LIGHT_CONSTANT_ATTENUATION_NAME(2),
+ DECLARE_LIGHT_CONSTANT_ATTENUATION_NAME(3),
+ DECLARE_LIGHT_CONSTANT_ATTENUATION_NAME(4),
+ DECLARE_LIGHT_CONSTANT_ATTENUATION_NAME(5),
+ DECLARE_LIGHT_CONSTANT_ATTENUATION_NAME(6),
+ DECLARE_LIGHT_CONSTANT_ATTENUATION_NAME(7)
+};
+
+int GLLights::LIGHT_CUT_OFF_ANGLE_NAMES[MAX_LIGHTS] = {
+ DECLARE_LIGHT_CUT_OFF_ANGLE_NAME(0),
+ DECLARE_LIGHT_CUT_OFF_ANGLE_NAME(1),
+ DECLARE_LIGHT_CUT_OFF_ANGLE_NAME(2),
+ DECLARE_LIGHT_CUT_OFF_ANGLE_NAME(3),
+ DECLARE_LIGHT_CUT_OFF_ANGLE_NAME(4),
+ DECLARE_LIGHT_CUT_OFF_ANGLE_NAME(5),
+ DECLARE_LIGHT_CUT_OFF_ANGLE_NAME(6),
+ DECLARE_LIGHT_CUT_OFF_ANGLE_NAME(7)
+};
+
+QString GLLights::LIGHT_STRUCT_UNROLL_NAMES[MAX_LIGHTS] = {
+ DECLARE_LIGHT_STRUCT_UNROLL_NAME(0),
+ DECLARE_LIGHT_STRUCT_UNROLL_NAME(1),
+ DECLARE_LIGHT_STRUCT_UNROLL_NAME(2),
+ DECLARE_LIGHT_STRUCT_UNROLL_NAME(3),
+ DECLARE_LIGHT_STRUCT_UNROLL_NAME(4),
+ DECLARE_LIGHT_STRUCT_UNROLL_NAME(5),
+ DECLARE_LIGHT_STRUCT_UNROLL_NAME(6),
+ DECLARE_LIGHT_STRUCT_UNROLL_NAME(7)
+};
+
+int GLLights::LIGHT_POSITION_UNROLL_NAMES[MAX_LIGHTS] = {
+ DECLARE_LIGHT_POSITION_UNROLL_NAME(0),
+ DECLARE_LIGHT_POSITION_UNROLL_NAME(1),
+ DECLARE_LIGHT_POSITION_UNROLL_NAME(2),
+ DECLARE_LIGHT_POSITION_UNROLL_NAME(3),
+ DECLARE_LIGHT_POSITION_UNROLL_NAME(4),
+ DECLARE_LIGHT_POSITION_UNROLL_NAME(5),
+ DECLARE_LIGHT_POSITION_UNROLL_NAME(6),
+ DECLARE_LIGHT_POSITION_UNROLL_NAME(7)
+};
+
+int GLLights::LIGHT_TYPE_UNROLL_NAMES[MAX_LIGHTS] = {
+ DECLARE_LIGHT_TYPE_UNROLL_NAME(0),
+ DECLARE_LIGHT_TYPE_UNROLL_NAME(1),
+ DECLARE_LIGHT_TYPE_UNROLL_NAME(2),
+ DECLARE_LIGHT_TYPE_UNROLL_NAME(3),
+ DECLARE_LIGHT_TYPE_UNROLL_NAME(4),
+ DECLARE_LIGHT_TYPE_UNROLL_NAME(5),
+ DECLARE_LIGHT_TYPE_UNROLL_NAME(6),
+ DECLARE_LIGHT_TYPE_UNROLL_NAME(7)
+};
+
+int GLLights::LIGHT_COLOR_UNROLL_NAMES[MAX_LIGHTS] = {
+ DECLARE_LIGHT_COLOR_UNROLL_NAME(0),
+ DECLARE_LIGHT_COLOR_UNROLL_NAME(1),
+ DECLARE_LIGHT_COLOR_UNROLL_NAME(2),
+ DECLARE_LIGHT_COLOR_UNROLL_NAME(3),
+ DECLARE_LIGHT_COLOR_UNROLL_NAME(4),
+ DECLARE_LIGHT_COLOR_UNROLL_NAME(5),
+ DECLARE_LIGHT_COLOR_UNROLL_NAME(6),
+ DECLARE_LIGHT_COLOR_UNROLL_NAME(7)
+};
+
+int GLLights::LIGHT_INTENSITY_UNROLL_NAMES[MAX_LIGHTS] = {
+ DECLARE_LIGHT_INTENSITY_UNROLL_NAME(0),
+ DECLARE_LIGHT_INTENSITY_UNROLL_NAME(1),
+ DECLARE_LIGHT_INTENSITY_UNROLL_NAME(2),
+ DECLARE_LIGHT_INTENSITY_UNROLL_NAME(3),
+ DECLARE_LIGHT_INTENSITY_UNROLL_NAME(4),
+ DECLARE_LIGHT_INTENSITY_UNROLL_NAME(5),
+ DECLARE_LIGHT_INTENSITY_UNROLL_NAME(6),
+ DECLARE_LIGHT_INTENSITY_UNROLL_NAME(7)
+};
+
+int GLLights::LIGHT_DIRECTION_UNROLL_NAMES[MAX_LIGHTS] = {
+ DECLARE_LIGHT_DIRECTION_UNROLL_NAME(0),
+ DECLARE_LIGHT_DIRECTION_UNROLL_NAME(1),
+ DECLARE_LIGHT_DIRECTION_UNROLL_NAME(2),
+ DECLARE_LIGHT_DIRECTION_UNROLL_NAME(3),
+ DECLARE_LIGHT_DIRECTION_UNROLL_NAME(4),
+ DECLARE_LIGHT_DIRECTION_UNROLL_NAME(5),
+ DECLARE_LIGHT_DIRECTION_UNROLL_NAME(6),
+ DECLARE_LIGHT_DIRECTION_UNROLL_NAME(7)
+};
+
+int GLLights::LIGHT_LINEAR_ATTENUATION_UNROLL_NAMES[MAX_LIGHTS] = {
+ DECLARE_LIGHT_LINEAR_ATTENUATION_UNROLL_NAME(0),
+ DECLARE_LIGHT_LINEAR_ATTENUATION_UNROLL_NAME(1),
+ DECLARE_LIGHT_LINEAR_ATTENUATION_UNROLL_NAME(2),
+ DECLARE_LIGHT_LINEAR_ATTENUATION_UNROLL_NAME(3),
+ DECLARE_LIGHT_LINEAR_ATTENUATION_UNROLL_NAME(4),
+ DECLARE_LIGHT_LINEAR_ATTENUATION_UNROLL_NAME(5),
+ DECLARE_LIGHT_LINEAR_ATTENUATION_UNROLL_NAME(6),
+ DECLARE_LIGHT_LINEAR_ATTENUATION_UNROLL_NAME(7)
+};
+
+int GLLights::LIGHT_QUADRATIC_ATTENUATION_UNROLL_NAMES[MAX_LIGHTS] = {
+ DECLARE_LIGHT_QUADRATIC_ATTENUATION_UNROLL_NAME(0),
+ DECLARE_LIGHT_QUADRATIC_ATTENUATION_UNROLL_NAME(1),
+ DECLARE_LIGHT_QUADRATIC_ATTENUATION_UNROLL_NAME(2),
+ DECLARE_LIGHT_QUADRATIC_ATTENUATION_UNROLL_NAME(3),
+ DECLARE_LIGHT_QUADRATIC_ATTENUATION_UNROLL_NAME(4),
+ DECLARE_LIGHT_QUADRATIC_ATTENUATION_UNROLL_NAME(5),
+ DECLARE_LIGHT_QUADRATIC_ATTENUATION_UNROLL_NAME(6),
+ DECLARE_LIGHT_QUADRATIC_ATTENUATION_UNROLL_NAME(7)
+};
+
+int GLLights::LIGHT_CONSTANT_ATTENUATION_UNROLL_NAMES[MAX_LIGHTS] = {
+ DECLARE_LIGHT_CONSTANT_ATTENUATION_UNROLL_NAME(0),
+ DECLARE_LIGHT_CONSTANT_ATTENUATION_UNROLL_NAME(1),
+ DECLARE_LIGHT_CONSTANT_ATTENUATION_UNROLL_NAME(2),
+ DECLARE_LIGHT_CONSTANT_ATTENUATION_UNROLL_NAME(3),
+ DECLARE_LIGHT_CONSTANT_ATTENUATION_UNROLL_NAME(4),
+ DECLARE_LIGHT_CONSTANT_ATTENUATION_UNROLL_NAME(5),
+ DECLARE_LIGHT_CONSTANT_ATTENUATION_UNROLL_NAME(6),
+ DECLARE_LIGHT_CONSTANT_ATTENUATION_UNROLL_NAME(7)
+};
+
+int GLLights::LIGHT_CUT_OFF_ANGLE_UNROLL_NAMES[MAX_LIGHTS] = {
+ DECLARE_LIGHT_CUT_OFF_ANGLE_UNROLL_NAME(0),
+ DECLARE_LIGHT_CUT_OFF_ANGLE_UNROLL_NAME(1),
+ DECLARE_LIGHT_CUT_OFF_ANGLE_UNROLL_NAME(2),
+ DECLARE_LIGHT_CUT_OFF_ANGLE_UNROLL_NAME(3),
+ DECLARE_LIGHT_CUT_OFF_ANGLE_UNROLL_NAME(4),
+ DECLARE_LIGHT_CUT_OFF_ANGLE_UNROLL_NAME(5),
+ DECLARE_LIGHT_CUT_OFF_ANGLE_UNROLL_NAME(6),
+ DECLARE_LIGHT_CUT_OFF_ANGLE_UNROLL_NAME(7)
+};
+
+} // namespace OpenGL
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
diff --git a/src/plugins/renderers/opengl/renderer/gllights_p.h b/src/plugins/renderers/opengl/renderer/gllights_p.h
new file mode 100644
index 000000000..90fa91588
--- /dev/null
+++ b/src/plugins/renderers/opengl/renderer/gllights_p.h
@@ -0,0 +1,88 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DRENDER_RENDER_OPENGL_GLLIGHTS_P_H
+#define QT3DRENDER_RENDER_OPENGL_GLLIGHTS_P_H
+
+#include <QString>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+namespace Render {
+namespace OpenGL {
+
+#define MAX_LIGHTS 8
+
+static_assert (MAX_LIGHTS < 10, "GL_Lights can't use the QChar trick anymore");
+
+struct GLLights
+{
+ static int LIGHT_COUNT_NAME_ID;
+
+ static QString LIGHT_STRUCT_NAMES[MAX_LIGHTS];
+ static int LIGHT_POSITION_NAMES[MAX_LIGHTS];
+ static int LIGHT_TYPE_NAMES[MAX_LIGHTS];
+ static int LIGHT_COLOR_NAMES[MAX_LIGHTS];
+ static int LIGHT_INTENSITY_NAMES[MAX_LIGHTS];
+ static int LIGHT_DIRECTION_NAMES[MAX_LIGHTS];
+ static int LIGHT_LINEAR_ATTENUATION_NAMES[MAX_LIGHTS];
+ static int LIGHT_QUADRATIC_ATTENUATION_NAMES[MAX_LIGHTS];
+ static int LIGHT_CONSTANT_ATTENUATION_NAMES[MAX_LIGHTS];
+ static int LIGHT_CUT_OFF_ANGLE_NAMES[MAX_LIGHTS];
+
+ static QString LIGHT_STRUCT_UNROLL_NAMES[MAX_LIGHTS];
+ static int LIGHT_POSITION_UNROLL_NAMES[MAX_LIGHTS];
+ static int LIGHT_TYPE_UNROLL_NAMES[MAX_LIGHTS];
+ static int LIGHT_COLOR_UNROLL_NAMES[MAX_LIGHTS];
+ static int LIGHT_INTENSITY_UNROLL_NAMES[MAX_LIGHTS];
+ static int LIGHT_DIRECTION_UNROLL_NAMES[MAX_LIGHTS];
+ static int LIGHT_LINEAR_ATTENUATION_UNROLL_NAMES[MAX_LIGHTS];
+ static int LIGHT_QUADRATIC_ATTENUATION_UNROLL_NAMES[MAX_LIGHTS];
+ static int LIGHT_CONSTANT_ATTENUATION_UNROLL_NAMES[MAX_LIGHTS];
+ static int LIGHT_CUT_OFF_ANGLE_UNROLL_NAMES[MAX_LIGHTS];
+};
+
+} // namespace OpenGL
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDER_OPENGL_GLLIGHTS_P_H
diff --git a/src/plugins/renderers/opengl/renderer/glshader.cpp b/src/plugins/renderers/opengl/renderer/glshader.cpp
index 5e92d84c2..564e78a8e 100644
--- a/src/plugins/renderers/opengl/renderer/glshader.cpp
+++ b/src/plugins/renderers/opengl/renderer/glshader.cpp
@@ -42,6 +42,7 @@
#include <Qt3DRender/private/stringtoint_p.h>
#include <graphicscontext_p.h>
#include <logging_p.h>
+#include <gllights_p.h>
QT_BEGIN_NAMESPACE
@@ -51,9 +52,51 @@ namespace Render {
namespace OpenGL {
+namespace {
+
+QVector<int> getLightUniformNameIds()
+{
+ QVector<int> names;
+ names.reserve(MAX_LIGHTS * 18 + 1);
+
+ names << GLLights::LIGHT_COUNT_NAME_ID;
+ for (int i = 0; i < MAX_LIGHTS; ++i) {
+ names << GLLights::LIGHT_TYPE_NAMES[i]
+ << GLLights::LIGHT_COLOR_NAMES[i]
+ << GLLights::LIGHT_POSITION_NAMES[i]
+ << GLLights::LIGHT_INTENSITY_NAMES[i]
+ << GLLights::LIGHT_DIRECTION_NAMES[i]
+ << GLLights::LIGHT_LINEAR_ATTENUATION_NAMES[i]
+ << GLLights::LIGHT_QUADRATIC_ATTENUATION_NAMES[i]
+ << GLLights::LIGHT_CONSTANT_ATTENUATION_NAMES[i]
+ << GLLights::LIGHT_CUT_OFF_ANGLE_NAMES[i]
+ << GLLights::LIGHT_TYPE_UNROLL_NAMES[i]
+ << GLLights::LIGHT_COLOR_UNROLL_NAMES[i]
+ << GLLights::LIGHT_POSITION_UNROLL_NAMES[i]
+ << GLLights::LIGHT_INTENSITY_UNROLL_NAMES[i]
+ << GLLights::LIGHT_DIRECTION_UNROLL_NAMES[i]
+ << GLLights::LIGHT_LINEAR_ATTENUATION_UNROLL_NAMES[i]
+ << GLLights::LIGHT_QUADRATIC_ATTENUATION_UNROLL_NAMES[i]
+ << GLLights::LIGHT_CONSTANT_ATTENUATION_UNROLL_NAMES[i]
+ << GLLights::LIGHT_CUT_OFF_ANGLE_UNROLL_NAMES[i];
+ }
+
+ return names;
+}
+
+template<typename Vector>
+bool fastContains(const Vector &v, int value)
+{
+ return std::binary_search(v.cbegin(), v.cend(), value);
+}
+
+}
+
GLShader::GLShader()
: m_isLoaded(false)
, m_graphicsContext(nullptr)
+ , m_parameterPackSize(0)
+ , m_hasActiveVariables(false)
{
m_shaderCode.resize(static_cast<int>(QShaderProgram::Compute) + 1);
}
@@ -106,7 +149,7 @@ QHash<QString, ShaderUniform> GLShader::activeUniformsForUniformBlock(int blockI
return m_uniformBlockIndexToShaderUniforms.value(blockIndex);
}
-ShaderUniformBlock GLShader::uniformBlockForBlockIndex(int blockIndex)
+ShaderUniformBlock GLShader::uniformBlockForBlockIndex(int blockIndex) const noexcept
{
for (int i = 0, m = m_uniformBlocks.size(); i < m; ++i) {
if (m_uniformBlocks[i].m_index == blockIndex) {
@@ -116,7 +159,7 @@ ShaderUniformBlock GLShader::uniformBlockForBlockIndex(int blockIndex)
return ShaderUniformBlock();
}
-ShaderUniformBlock GLShader::uniformBlockForBlockNameId(int blockNameId)
+ShaderUniformBlock GLShader::uniformBlockForBlockNameId(int blockNameId) const noexcept
{
for (int i = 0, m = m_uniformBlocks.size(); i < m; ++i) {
if (m_uniformBlocks[i].m_nameId == blockNameId) {
@@ -126,7 +169,7 @@ ShaderUniformBlock GLShader::uniformBlockForBlockNameId(int blockNameId)
return ShaderUniformBlock();
}
-ShaderUniformBlock GLShader::uniformBlockForBlockName(const QString &blockName)
+ShaderUniformBlock GLShader::uniformBlockForBlockName(const QString &blockName) const noexcept
{
for (int i = 0, m = m_uniformBlocks.size(); i < m; ++i) {
if (m_uniformBlocks[i].m_name == blockName) {
@@ -136,7 +179,7 @@ ShaderUniformBlock GLShader::uniformBlockForBlockName(const QString &blockName)
return ShaderUniformBlock();
}
-ShaderStorageBlock GLShader::storageBlockForBlockIndex(int blockIndex)
+ShaderStorageBlock GLShader::storageBlockForBlockIndex(int blockIndex) const noexcept
{
for (int i = 0, m = m_shaderStorageBlockNames.size(); i < m; ++i) {
if (m_shaderStorageBlocks[i].m_index == blockIndex)
@@ -145,7 +188,7 @@ ShaderStorageBlock GLShader::storageBlockForBlockIndex(int blockIndex)
return ShaderStorageBlock();
}
-ShaderStorageBlock GLShader::storageBlockForBlockNameId(int blockNameId)
+ShaderStorageBlock GLShader::storageBlockForBlockNameId(int blockNameId) const noexcept
{
for (int i = 0, m = m_shaderStorageBlockNames.size(); i < m; ++i) {
if (m_shaderStorageBlocks[i].m_nameId == blockNameId)
@@ -154,7 +197,7 @@ ShaderStorageBlock GLShader::storageBlockForBlockNameId(int blockNameId)
return ShaderStorageBlock();
}
-ShaderStorageBlock GLShader::storageBlockForBlockName(const QString &blockName)
+ShaderStorageBlock GLShader::storageBlockForBlockName(const QString &blockName) const noexcept
{
for (int i = 0, m = m_shaderStorageBlockNames.size(); i < m; ++i) {
if (m_shaderStorageBlocks[i].m_name == blockName)
@@ -163,6 +206,22 @@ ShaderStorageBlock GLShader::storageBlockForBlockName(const QString &blockName)
return ShaderStorageBlock();
}
+GLShader::ParameterKind GLShader::categorizeVariable(int nameId) const noexcept
+{
+ if (fastContains(m_uniformsNamesIds, nameId))
+ return ParameterKind::Uniform;
+ if (fastContains(m_uniformBlockNamesIds, nameId))
+ return ParameterKind::UBO;
+ if (fastContains(m_shaderStorageBlockNamesIds, nameId))
+ return ParameterKind::SSBO;
+ return ParameterKind::Struct;
+}
+
+bool GLShader::hasUniform(int nameId) const noexcept
+{
+ return m_uniformsNamesIds.contains(nameId);
+}
+
void GLShader::prepareUniforms(ShaderParameterPack &pack)
{
const PackUniformHash &values = pack.uniforms();
@@ -170,14 +229,20 @@ void GLShader::prepareUniforms(ShaderParameterPack &pack)
auto it = values.keys.cbegin();
const auto end = values.keys.cend();
+ const int shaderUniformsCount = m_uniforms.size();
+ const auto uIt = m_uniforms.cbegin();
+
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;
- }
- }
+
+ int i = 0;
+ const int targetNameId = *it;
+ while (i < shaderUniformsCount && (uIt + i)->m_nameId < targetNameId)
+ ++i;
+
+ if (i < shaderUniformsCount && (uIt + i)->m_nameId == targetNameId)
+ pack.setSubmissionUniformIndex(i);
+
++it;
}
}
@@ -188,7 +253,6 @@ void GLShader::setFragOutputs(const QHash<QString, int> &fragOutputs)
QMutexLocker lock(&m_mutex);
m_fragOutputs = fragOutputs;
}
-// updateDNA();
}
const QHash<QString, int> GLShader::fragOutputs() const
@@ -203,6 +267,7 @@ void GLShader::initializeUniforms(const QVector<ShaderUniform> &uniformsDescript
m_uniformsNames.resize(uniformsDescription.size());
m_uniformsNamesIds.reserve(uniformsDescription.size());
m_standardUniformNamesIds.reserve(5);
+ m_lightUniformsNamesIds.reserve(MAX_LIGHTS * 8 + 1);
QHash<QString, ShaderUniform> activeUniformsInDefaultBlock;
static const QVector<int> standardUniformNameIds = {
@@ -231,14 +296,18 @@ void GLShader::initializeUniforms(const QVector<ShaderUniform> &uniformsDescript
Shader::skinningPaletteNameId,
};
+ static const QVector<int> lightUniformNameIds = getLightUniformNameIds();
+
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?
+ // Is the uniform a Qt3D "Standard" uniform, a light uniform or a user defined one?
if (standardUniformNameIds.contains(nameId))
m_standardUniformNamesIds.push_back(nameId);
+ else if (lightUniformNameIds.contains(nameId))
+ m_lightUniformsNamesIds.push_back(nameId);
else
m_uniformsNamesIds.push_back(nameId);
@@ -248,6 +317,18 @@ void GLShader::initializeUniforms(const QVector<ShaderUniform> &uniformsDescript
}
}
m_uniformBlockIndexToShaderUniforms.insert(-1, activeUniformsInDefaultBlock);
+
+ m_parameterPackSize += m_standardUniformNamesIds.size() + m_lightUniformsNamesIds.size() + m_uniformsNamesIds.size();
+ m_hasActiveVariables |= (m_parameterPackSize > 0);
+
+ // Sort by ascending order to make contains check faster
+ std::sort(m_uniformsNamesIds.begin(), m_uniformsNamesIds.end());
+ std::sort(m_lightUniformsNamesIds.begin(), m_lightUniformsNamesIds.end());
+ std::sort(m_standardUniformNamesIds.begin(), m_standardUniformNamesIds.end());
+ std::sort(m_uniforms.begin(), m_uniforms.end(),
+ [] (const ShaderUniform &a, const ShaderUniform &b) {
+ return a.m_nameId < b.m_nameId;
+ });
}
void GLShader::initializeAttributes(const QVector<ShaderAttribute> &attributesDescription)
@@ -261,6 +342,7 @@ void GLShader::initializeAttributes(const QVector<ShaderAttribute> &attributesDe
m_attributeNamesIds[i] = m_attributes[i].m_nameId;
qCDebug(Shaders) << "Active Attribute " << attributesDescription[i].m_name;
}
+ m_hasActiveVariables |= (m_attributeNamesIds.size() > 0);
}
void GLShader::initializeUniformBlocks(const QVector<ShaderUniformBlock> &uniformBlockDescription)
@@ -296,6 +378,12 @@ void GLShader::initializeUniformBlocks(const QVector<ShaderUniformBlock> &unifor
}
m_uniformBlockIndexToShaderUniforms.insert(uniformBlockDescription[i].m_index, activeUniformsInBlock);
}
+
+ m_parameterPackSize += m_uniformsNamesIds.size();
+ m_hasActiveVariables |= (m_parameterPackSize > 0);
+
+ // Sort by ascending order to make contains check faster
+ std::sort(m_uniformBlockNamesIds.begin(), m_uniformBlockNamesIds.end());
}
void GLShader::initializeShaderStorageBlocks(const QVector<ShaderStorageBlock> &shaderStorageBlockDescription)
@@ -310,6 +398,12 @@ void GLShader::initializeShaderStorageBlocks(const QVector<ShaderStorageBlock> &
m_shaderStorageBlocks[i].m_nameId =m_shaderStorageBlockNamesIds[i];
qCDebug(Shaders) << "Initializing Shader Storage Block {" << m_shaderStorageBlockNames[i] << "}";
}
+
+ m_parameterPackSize += m_shaderStorageBlockNamesIds.size();
+ m_hasActiveVariables |= (m_parameterPackSize > 0);
+
+ // Sort by ascending order to make contains check faster
+ std::sort(m_shaderStorageBlockNamesIds.begin(), m_shaderStorageBlockNamesIds.end());
}
} // OpenGL
diff --git a/src/plugins/renderers/opengl/renderer/glshader_p.h b/src/plugins/renderers/opengl/renderer/glshader_p.h
index 6bd5400af..ae447cd18 100644
--- a/src/plugins/renderers/opengl/renderer/glshader_p.h
+++ b/src/plugins/renderers/opengl/renderer/glshader_p.h
@@ -57,6 +57,9 @@
#include <Qt3DRender/qshaderprogram.h>
#include <QMutex>
+#ifdef QT_BUILD_INTERNAL
+ class tst_BenchShaderParameterPack;
+#endif
QT_BEGIN_NAMESPACE
@@ -84,31 +87,44 @@ public:
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; }
+ inline const QVector<int> &uniformsNamesIds() const { return m_uniformsNamesIds; }
+ inline const QVector<int> &lightUniformsNamesIds() const { return m_lightUniformsNamesIds; }
+ inline const QVector<int> &standardUniformNameIds() const { return m_standardUniformNamesIds; }
+ inline const QVector<int> &uniformBlockNamesIds() const { return m_uniformBlockNamesIds; }
+ inline const QVector<int> &storageBlockNamesIds() const { return m_shaderStorageBlockNamesIds; }
+ inline const 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; }
+ inline const QVector<ShaderUniform> &uniforms() const { return m_uniforms; }
+ inline const QVector<ShaderAttribute> &attributes() const { return m_attributes; }
+ inline const QVector<ShaderUniformBlock> &uniformBlocks() const { return m_uniformBlocks; }
+ inline const 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);
+ ShaderUniformBlock uniformBlockForBlockIndex(int blockNameId) const noexcept;
+ ShaderUniformBlock uniformBlockForBlockNameId(int blockIndex) const noexcept;
+ ShaderUniformBlock uniformBlockForBlockName(const QString &blockName) const noexcept;
- ShaderStorageBlock storageBlockForBlockIndex(int blockIndex);
- ShaderStorageBlock storageBlockForBlockNameId(int blockNameId);
- ShaderStorageBlock storageBlockForBlockName(const QString &blockName);
+ ShaderStorageBlock storageBlockForBlockIndex(int blockIndex) const noexcept;
+ ShaderStorageBlock storageBlockForBlockNameId(int blockNameId) const noexcept;
+ ShaderStorageBlock storageBlockForBlockName(const QString &blockName) const noexcept;
+
+ enum ParameterKind {
+ Uniform,
+ UBO,
+ SSBO,
+ Struct
+ };
+ ParameterKind categorizeVariable(int nameId) const noexcept;
+
+ bool hasUniform(int nameId) const noexcept;
+ inline bool hasActiveVariables() const noexcept { return m_hasActiveVariables; }
+ inline int parameterPackSize() const noexcept { return m_parameterPackSize; }
QOpenGLShaderProgram *shaderProgram() { return &m_shader; }
@@ -122,6 +138,7 @@ private:
QVector<QString> m_uniformsNames;
QVector<int> m_uniformsNamesIds;
+ QVector<int> m_lightUniformsNamesIds;
QVector<int> m_standardUniformNamesIds;
QVector<ShaderUniform> m_uniforms;
@@ -141,6 +158,9 @@ private:
QHash<QString, int> m_fragOutputs;
QVector<QByteArray> m_shaderCode;
+ int m_parameterPackSize;
+ int m_hasActiveVariables;
+
// Private so that only GraphicContext can call it
void initializeUniforms(const QVector<ShaderUniform> &uniformsDescription);
void initializeAttributes(const QVector<ShaderAttribute> &attributesDescription);
@@ -148,6 +168,9 @@ private:
void initializeShaderStorageBlocks(const QVector<ShaderStorageBlock> &shaderStorageBlockDescription);
friend class GraphicsContext;
+#ifdef QT_BUILD_INTERNAL
+ friend class ::tst_BenchShaderParameterPack;
+#endif
mutable QMutex m_mutex;
QMetaObject::Connection m_contextConnection;
diff --git a/src/plugins/renderers/opengl/renderer/renderer.cpp b/src/plugins/renderers/opengl/renderer/renderer.cpp
index 3ad9c2818..57ee5ec88 100644
--- a/src/plugins/renderers/opengl/renderer/renderer.cpp
+++ b/src/plugins/renderers/opengl/renderer/renderer.cpp
@@ -995,16 +995,9 @@ void Renderer::prepareCommandsSubmission(const QVector<RenderView *> &renderView
// 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);
}
}
}
@@ -1172,6 +1165,10 @@ void Renderer::sendShaderChangesToFrontend(Qt3DCore::QAspectManager *manager)
Shader *s = m_nodesManager->shaderManager()->data(handle);
if (s->requiresFrontendSync()) {
QShaderProgram *frontend = static_cast<decltype(frontend)>(manager->lookupNode(s->peerId()));
+ // Could happen as a backend shader might live beyong the frontend
+ // the time needed to destroy the GLShader assoicated with it.
+ if (!frontend)
+ continue;
QShaderProgramPrivate *dFrontend = static_cast<decltype(dFrontend)>(QNodePrivate::get(frontend));
s->unsetRequiresFrontendSync();
dFrontend->setStatus(s->status());
@@ -1917,10 +1914,31 @@ QVector<Qt3DCore::QAspectJobPtr> Renderer::renderBinJobs()
m_updatedDisableSubtreeEnablers.push_back(node->peerId());
}
+ int idealThreadCount = QThread::idealThreadCount();
+ const QByteArray maxThreadCount = qgetenv("QT3D_MAX_THREAD_COUNT");
+ if (!maxThreadCount.isEmpty()) {
+ bool conversionOK = false;
+ const int maxThreadCountValue = maxThreadCount.toInt(&conversionOK);
+ if (conversionOK)
+ idealThreadCount = maxThreadCountValue;
+ }
+
const int fgBranchCount = m_frameGraphLeaves.size();
+ if (fgBranchCount > 1) {
+ int workBranches = fgBranchCount;
+ for (auto leaf: qAsConst(m_frameGraphLeaves))
+ if (leaf->nodeType() == FrameGraphNode::NoDraw)
+ --workBranches;
+
+ if (idealThreadCount > 4 && workBranches && maxThreadCount.isEmpty())
+ idealThreadCount = qMax(4, idealThreadCount / workBranches);
+ }
+
for (int i = 0; i < fgBranchCount; ++i) {
FrameGraphNode *leaf = m_frameGraphLeaves.at(i);
RenderViewBuilder builder(leaf, i, this);
+ builder.setOptimalJobCount(leaf->nodeType() == FrameGraphNode::NoDraw ? 1 : idealThreadCount);
+
// 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);
@@ -2046,7 +2064,7 @@ void Renderer::performCompute(const RenderView *, RenderCommand *command)
}
{
Profiling::GLTimeRecorder recorder(Profiling::UniformUpdate, activeProfiler());
- m_submissionContext->setParameters(command->m_parameterPack);
+ m_submissionContext->setParameters(command->m_parameterPack, command->m_glShader);
}
{
Profiling::GLTimeRecorder recorder(Profiling::DispatchCompute, activeProfiler());
@@ -2140,7 +2158,7 @@ bool Renderer::executeCommandsSubmission(const RenderView *rv)
{
Profiling::GLTimeRecorder recorder(Profiling::UniformUpdate, activeProfiler());
//// Update program uniforms
- if (!m_submissionContext->setParameters(command.m_parameterPack)) {
+ if (!m_submissionContext->setParameters(command.m_parameterPack, command.m_glShader)) {
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
diff --git a/src/plugins/renderers/opengl/renderer/renderer.pri b/src/plugins/renderers/opengl/renderer/renderer.pri
index 3e3f83c86..1a0240e77 100644
--- a/src/plugins/renderers/opengl/renderer/renderer.pri
+++ b/src/plugins/renderers/opengl/renderer/renderer.pri
@@ -1,6 +1,7 @@
INCLUDEPATH += $$PWD
SOURCES += \
+ $$PWD/gllights.cpp \
$$PWD/openglvertexarrayobject.cpp \
$$PWD/rendercommand.cpp \
$$PWD/renderer.cpp \
@@ -13,6 +14,7 @@ SOURCES += \
$$PWD/commandexecuter.cpp
HEADERS += \
+ $$PWD/gllights_p.h \
$$PWD/openglvertexarrayobject_p.h \
$$PWD/renderercache_p.h \
$$PWD/rendercommand_p.h \
diff --git a/src/plugins/renderers/opengl/renderer/renderer_p.h b/src/plugins/renderers/opengl/renderer/renderer_p.h
index 8cf610efb..d4980464a 100644
--- a/src/plugins/renderers/opengl/renderer/renderer_p.h
+++ b/src/plugins/renderers/opengl/renderer/renderer_p.h
@@ -173,7 +173,7 @@ public:
~Renderer();
void dumpInfo() const override;
- API api() const override { return AbstractRenderer::OpenGL; }
+ API api() const override { return Qt3DRender::API::OpenGL; }
qint64 time() const override;
void setTime(qint64 time) override;
@@ -270,7 +270,7 @@ public:
QSharedPointer<RenderBackendResourceAccessor> resourceAccessor() const override;
- const GraphicsApiFilterData *contextInfo() const;
+ const GraphicsApiFilterData *contextInfo() const override;
SubmissionContext *submissionContext() const;
inline RenderStateSet *defaultRenderState() const { return m_defaultRenderStateSet; }
diff --git a/src/plugins/renderers/opengl/renderer/renderview.cpp b/src/plugins/renderers/opengl/renderer/renderview.cpp
index 99ac88a94..a3a00782e 100644
--- a/src/plugins/renderers/opengl/renderer/renderview.cpp
+++ b/src/plugins/renderers/opengl/renderer/renderview.cpp
@@ -75,7 +75,8 @@
#include <Qt3DCore/qentity.h>
#include <QtGui/qsurface.h>
#include <algorithm>
-
+#include <atomic>
+#include <gllights_p.h>
#include <QDebug>
#if defined(QT3D_RENDER_VIEW_JOB_TIMINGS)
#include <QElapsedTimer>
@@ -87,33 +88,12 @@ namespace Qt3DRender {
namespace Render {
namespace OpenGL {
-
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;
+std::atomic_bool wasInitialized{};
} // anonymous namespace
@@ -168,9 +148,10 @@ static Matrix4x4 getProjectionMatrix(const CameraLens *lens)
}
UniformValue RenderView::standardUniformValue(RenderView::StandardUniform standardUniformType,
- Entity *entity,
- const Matrix4x4 &model) const
+ const Entity *entity) const
{
+ const Matrix4x4 &model = *(entity->worldTransform());
+
switch (standardUniformType) {
case ModelMatrix:
return UniformValue(model);
@@ -263,26 +244,11 @@ RenderView::RenderView()
m_workGroups[1] = 1;
m_workGroups[2] = 1;
- if (Q_UNLIKELY(!wasInitialized)) {
+ if (Q_UNLIKELY(!wasInitialized.exchange(true))) {
// 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);
- }
}
}
@@ -437,6 +403,7 @@ struct SubRangeSorter<QSortPolicy::Texture>
{
static void sortSubRange(CommandIt begin, const CommandIt end)
{
+#ifndef Q_OS_WIN
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();
@@ -455,6 +422,7 @@ struct SubRangeSorter<QSortPolicy::Texture>
return identicalTextureCount < originalTextureASize;
});
+#endif
}
};
@@ -958,21 +926,16 @@ void RenderView::setUniformValue(ShaderParameterPack &uniformPack, int nameId, c
}
void RenderView::setStandardUniformValue(ShaderParameterPack &uniformPack,
- int glslNameId,
int nameId,
- Entity *entity,
- const Matrix4x4 &worldTransform) const
+ const Entity *entity) const
{
- uniformPack.setUniform(glslNameId, standardUniformValue(ms_standardUniformSetters[nameId], entity, worldTransform));
+ uniformPack.setUniform(nameId, standardUniformValue(ms_standardUniformSetters[nameId], entity));
}
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;
@@ -988,11 +951,9 @@ void RenderView::setUniformBlockValue(ShaderParameterPack &uniformPack,
}
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) {
@@ -1006,7 +967,10 @@ void RenderView::setShaderStorageValue(ShaderParameterPack &uniformPack,
}
}
-void RenderView::setDefaultUniformBlockShaderDataValue(ShaderParameterPack &uniformPack, GLShader *shader, ShaderData *shaderData, const QString &structName) const
+void RenderView::setDefaultUniformBlockShaderDataValue(ShaderParameterPack &uniformPack,
+ const GLShader *shader,
+ const ShaderData *shaderData,
+ const QString &structName) const
{
UniformBlockValueBuilder *builder = m_localData.localData();
builder->activeUniformNamesToValue.clear();
@@ -1030,6 +994,42 @@ void RenderView::setDefaultUniformBlockShaderDataValue(ShaderParameterPack &unif
}
}
+void RenderView::applyParameter(const Parameter *param,
+ RenderCommand *command,
+ const GLShader *shader) const noexcept
+{
+ const int nameId = param->nameId();
+ const UniformValue &uniformValue = param->uniformValue();
+ const GLShader::ParameterKind kind = shader->categorizeVariable(nameId);
+
+ switch (kind) {
+ case GLShader::Uniform: {
+ setUniformValue(command->m_parameterPack, nameId, uniformValue);
+ break;
+ }
+ case GLShader::UBO: {
+ setUniformBlockValue(command->m_parameterPack, shader->uniformBlockForBlockNameId(nameId), uniformValue);
+ break;
+ }
+ case GLShader::SSBO: {
+ setShaderStorageValue(command->m_parameterPack, shader->storageBlockForBlockNameId(nameId), uniformValue);
+ break;
+ }
+ case GLShader::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(nameId));
+ }
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+
void RenderView::setShaderAndUniforms(RenderCommand *command,
ParameterInfoList &parameters,
Entity *entity,
@@ -1052,11 +1052,6 @@ void RenderView::setShaderAndUniforms(RenderCommand *command,
// 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
@@ -1073,113 +1068,95 @@ void RenderView::setShaderAndUniforms(RenderCommand *command,
shader->setFragOutputs(fragOutputs);
}
- if (!uniformNamesIds.isEmpty() || !standardUniformNamesIds.isEmpty() ||
- !attributeNamesIds.isEmpty() ||
- !shaderStorageBlockNamesIds.isEmpty() || !attributeNamesIds.isEmpty()) {
+ // Set default attributes
+ command->m_activeAttributes = shader->attributeNamesIds();
- // Set default standard uniforms without bindings
- const Matrix4x4 worldTransform = *(entity->worldTransform());
+ // 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();
- for (const int uniformNameId : standardUniformNamesIds)
- setStandardUniformValue(command->m_parameterPack, uniformNameId, uniformNameId, entity, worldTransform);
-
- // Set default attributes
- command->m_activeAttributes = attributeNamesIds;
+ if (shader->hasActiveVariables()) {
- // 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();
+ // Reserve amount of uniforms we are going to need
+ command->m_parameterPack.reserve(shader->parameterPackSize());
- // Parameters remaining could be
- // -> uniform scalar / vector
- // -> uniform struct / arrays
- // -> uniform block / array (4.3)
- // -> ssbo block / array (4.3)
+ const QVector<int> &standardUniformNamesIds = shader->standardUniformNameIds();
+ for (const int uniformNameId : standardUniformNamesIds)
+ setStandardUniformValue(command->m_parameterPack, uniformNameId, entity);
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
- }
+ const Parameter *param = m_manager->data<Parameter, ParameterManager>(it->handle);
+ applyParameter(param, command, 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;
-
+ const QVector<int> &lightUniformNamesIds = shader->lightUniformsNamesIds();
+ if (!lightUniformNamesIds.empty()) {
+ 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;
+ // Note: implicit conversion of values to UniformValue
+ if (lightUniformNamesIds.contains(GLLights::LIGHT_TYPE_NAMES[lightIdx])) {
+ setUniformValue(command->m_parameterPack, GLLights::LIGHT_POSITION_NAMES[lightIdx], worldPos);
+ setUniformValue(command->m_parameterPack, GLLights::LIGHT_TYPE_NAMES[lightIdx], int(QAbstractLight::PointLight));
+ setUniformValue(command->m_parameterPack, GLLights::LIGHT_COLOR_NAMES[lightIdx], Vector3D(1.0f, 1.0f, 1.0f));
+ setUniformValue(command->m_parameterPack, GLLights::LIGHT_INTENSITY_NAMES[lightIdx], 0.5f);
+ } else if (lightUniformNamesIds.contains(GLLights::LIGHT_TYPE_UNROLL_NAMES[lightIdx])) {
+ setUniformValue(command->m_parameterPack, GLLights::LIGHT_POSITION_UNROLL_NAMES[lightIdx], worldPos);
+ setUniformValue(command->m_parameterPack, GLLights::LIGHT_TYPE_UNROLL_NAMES[lightIdx], int(QAbstractLight::PointLight));
+ setUniformValue(command->m_parameterPack, GLLights::LIGHT_COLOR_UNROLL_NAMES[lightIdx], Vector3D(1.0f, 1.0f, 1.0f));
+ setUniformValue(command->m_parameterPack, GLLights::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, GLLights::LIGHT_STRUCT_NAMES[lightIdx]);
+ setDefaultUniformBlockShaderDataValue(command->m_parameterPack, shader, shaderData, GLLights::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);
+ setUniformValue(command->m_parameterPack, GLLights::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
+ if (lightUniformNamesIds.contains(GLLights::LIGHT_TYPE_NAMES[0])) {
+ setUniformValue(command->m_parameterPack, GLLights::LIGHT_POSITION_NAMES[0], Vector3D(10.0f, 10.0f, 0.0f));
+ setUniformValue(command->m_parameterPack, GLLights::LIGHT_TYPE_NAMES[0], int(QAbstractLight::PointLight));
+ setUniformValue(command->m_parameterPack, GLLights::LIGHT_COLOR_NAMES[0], Vector3D(1.0f, 1.0f, 1.0f));
+ setUniformValue(command->m_parameterPack, GLLights::LIGHT_INTENSITY_NAMES[0], 0.5f);
+ } else if (lightUniformNamesIds.contains(GLLights::LIGHT_TYPE_UNROLL_NAMES[lightIdx])) {
+ setUniformValue(command->m_parameterPack, GLLights::LIGHT_POSITION_UNROLL_NAMES[0], Vector3D(10.0f, 10.0f, 0.0f));
+ setUniformValue(command->m_parameterPack, GLLights::LIGHT_TYPE_UNROLL_NAMES[0], int(QAbstractLight::PointLight));
+ setUniformValue(command->m_parameterPack, GLLights::LIGHT_COLOR_UNROLL_NAMES[0], Vector3D(1.0f, 1.0f, 1.0f));
+ setUniformValue(command->m_parameterPack, GLLights::LIGHT_INTENSITY_UNROLL_NAMES[0], 0.5f);
+ }
+ }
}
// Environment Light
@@ -1200,6 +1177,9 @@ void RenderView::setShaderAndUniforms(RenderCommand *command,
}
setUniformValue(command->m_parameterPack, StringToInt::lookupId(QStringLiteral("envLightCount")), envLightCount);
}
+
+ // Prepare the ShaderParameterPack based on the active uniforms of the shader
+ shader->prepareUniforms(command->m_parameterPack);
}
}
@@ -1233,6 +1213,9 @@ bool RenderView::shouldSkipSubmission() const
if (m_clearBuffer != QClearBuffers::None)
return false;
+ if (!m_renderCaptureNodeId.isNull())
+ return false;
+
return true;
}
diff --git a/src/plugins/renderers/opengl/renderer/renderview_p.h b/src/plugins/renderers/opengl/renderer/renderview_p.h
index adab30f4e..6c41ce500 100644
--- a/src/plugins/renderers/opengl/renderer/renderview_p.h
+++ b/src/plugins/renderers/opengl/renderer/renderview_p.h
@@ -370,27 +370,25 @@ private:
static StandardUniformsNameToTypeHash initializeStandardUniformSetters();
UniformValue standardUniformValue(StandardUniform standardUniformType,
- Entity *entity,
- const Matrix4x4 &model) const;
+ const Entity *entity) 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;
+ const Entity *entity) 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 GLShader *shader,
+ const ShaderData *shaderData,
const QString &structName) const;
+ void applyParameter(const Parameter *param,
+ RenderCommand *command,
+ const GLShader *shader) const noexcept;
};
} // namespace OpenGL
diff --git a/src/plugins/renderers/opengl/renderer/renderviewbuilder.cpp b/src/plugins/renderers/opengl/renderer/renderviewbuilder.cpp
index 8ed32ff10..b0ac76199 100644
--- a/src/plugins/renderers/opengl/renderer/renderviewbuilder.cpp
+++ b/src/plugins/renderers/opengl/renderer/renderviewbuilder.cpp
@@ -49,18 +49,13 @@ namespace Qt3DRender {
namespace Render {
namespace OpenGL {
-// 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)
+int findIdealNumberOfWorkers(int elementCount, int packetSize = 100, int maxJobCount = 1)
{
if (elementCount == 0 || packetSize == 0)
return 0;
- return std::min(std::max(elementCount / packetSize, 1), RenderViewBuilder::optimalJobCount());
+ return std::min(std::max(elementCount / packetSize, 1), maxJobCount);
}
@@ -93,9 +88,10 @@ public:
lock.unlock();
// Split among the ideal number of command builders
- const int idealPacketSize = std::min(std::max(100, entities.size() / RenderViewBuilder::optimalJobCount()), entities.size());
+ const int jobCount = m_renderViewCommandBuilderJobs.size();
+ const int idealPacketSize = std::min(std::max(10, entities.size() / jobCount), entities.size());
// Try to split work into an ideal number of workers
- const int m = findIdealNumberOfWorkers(entities.size(), idealPacketSize);
+ const int m = findIdealNumberOfWorkers(entities.size(), idealPacketSize, jobCount);
for (int i = 0; i < m; ++i) {
const RenderViewCommandBuilderJobPtr renderViewCommandBuilder = m_renderViewCommandBuilderJobs.at(i);
@@ -352,9 +348,9 @@ public:
}
// 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);
+ const int jobCount = m_renderViewCommandUpdaterJobs.size();
+ const int idealPacketSize = std::min(std::max(10, filteredCommandData->size() / jobCount), filteredCommandData->size());
+ const int m = findIdealNumberOfWorkers(filteredCommandData->size(), idealPacketSize, jobCount);
for (int i = 0; i < m; ++i) {
const RenderViewCommandUpdaterJobPtr renderViewCommandBuilder = m_renderViewCommandUpdaterJobs.at(i);
@@ -474,6 +470,10 @@ RenderViewBuilder::RenderViewBuilder(Render::FrameGraphNode *leafNode, int rende
, m_syncFilterEntityByLayerJob()
, m_filterProximityJob(Render::FilterProximityDistanceJobPtr::create())
{
+ // 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
+ m_optimalParallelJobCount = QThread::idealThreadCount();
}
RenderViewInitializerJobPtr RenderViewBuilder::renderViewJob() const
@@ -558,17 +558,16 @@ void RenderViewBuilder::prepareJobs()
m_frustumCullingJob->setRoot(m_renderer->sceneRoot());
if (m_renderCommandCacheNeedsToBeRebuilt) {
-
- m_renderViewCommandBuilderJobs.reserve(RenderViewBuilder::m_optimalParallelJobCount);
- for (auto i = 0; i < RenderViewBuilder::m_optimalParallelJobCount; ++i) {
+ m_renderViewCommandBuilderJobs.reserve(m_optimalParallelJobCount);
+ for (auto i = 0; i < m_optimalParallelJobCount; ++i) {
auto renderViewCommandBuilder = Render::OpenGL::RenderViewCommandBuilderJobPtr::create();
m_renderViewCommandBuilderJobs.push_back(renderViewCommandBuilder);
}
m_syncRenderViewPreCommandBuildingJob = CreateSynchronizerJobPtr(SyncPreCommandBuilding(m_renderViewJob,
- m_renderViewCommandBuilderJobs,
- m_renderer,
- m_leafNode),
- JobTypes::SyncRenderViewPreCommandBuilding);
+ m_renderViewCommandBuilderJobs,
+ m_renderer,
+ m_leafNode),
+ JobTypes::SyncRenderViewPreCommandBuilding);
}
m_renderViewJob->setRenderer(m_renderer);
@@ -577,8 +576,8 @@ void RenderViewBuilder::prepareJobs()
// 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) {
+ m_renderViewCommandUpdaterJobs.reserve(m_optimalParallelJobCount);
+ for (auto i = 0; i < m_optimalParallelJobCount; ++i) {
auto renderViewCommandUpdater = Render::OpenGL::RenderViewCommandUpdaterJobPtr::create();
renderViewCommandUpdater->setRenderer(m_renderer);
m_renderViewCommandUpdaterJobs.push_back(renderViewCommandUpdater);
@@ -587,22 +586,23 @@ void RenderViewBuilder::prepareJobs()
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 = 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);
+ if (materialHandles.count()) {
+ const int elementsPerJob = qMax(materialHandles.size() / m_optimalParallelJobCount, 1);
+ m_materialGathererJobs.reserve(m_optimalParallelJobCount);
+ int elementCount = 0;
+ while (elementCount < materialHandles.size()) {
+ auto materialGatherer = MaterialParameterGathererJobPtr::create();
+ materialGatherer->setNodeManagers(m_renderer->nodeManagers());
+ materialGatherer->setHandles(materialHandles.mid(elementCount, elementsPerJob));
+ m_materialGathererJobs.push_back(materialGatherer);
+
+ elementCount += elementsPerJob;
+ }
}
m_syncMaterialGathererJob = CreateSynchronizerJobPtr(SyncMaterialParameterGatherer(m_materialGathererJobs,
- m_renderer,
- m_leafNode),
- JobTypes::SyncMaterialGatherer);
+ m_renderer,
+ m_leafNode),
+ JobTypes::SyncMaterialGatherer);
}
if (m_layerCacheNeedsToBeRebuilt) {
@@ -615,29 +615,29 @@ void RenderViewBuilder::prepareJobs()
}
m_syncRenderViewPreCommandUpdateJob = CreateSynchronizerJobPtr(SyncRenderViewPreCommandUpdate(m_renderViewJob,
- m_frustumCullingJob,
- m_filterProximityJob,
- m_materialGathererJobs,
- m_renderViewCommandUpdaterJobs,
- m_renderViewCommandBuilderJobs,
- m_renderer,
- m_leafNode,
- m_renderCommandCacheNeedsToBeRebuilt),
- JobTypes::SyncRenderViewPreCommandUpdate);
+ m_frustumCullingJob,
+ m_filterProximityJob,
+ m_materialGathererJobs,
+ m_renderViewCommandUpdaterJobs,
+ m_renderViewCommandBuilderJobs,
+ m_renderer,
+ m_leafNode,
+ m_renderCommandCacheNeedsToBeRebuilt),
+ JobTypes::SyncRenderViewPreCommandUpdate);
m_syncRenderViewPostCommandUpdateJob = CreateSynchronizerJobPtr(SyncRenderViewPostCommandUpdate(m_renderViewJob,
- m_renderViewCommandUpdaterJobs,
- m_renderer),
- JobTypes::SyncRenderViewPostCommandUpdate);
+ m_renderViewCommandUpdaterJobs,
+ m_renderer),
+ JobTypes::SyncRenderViewPostCommandUpdate);
m_syncRenderViewPostInitializationJob = CreateSynchronizerJobPtr(SyncRenderViewPostInitialization(m_renderViewJob,
- m_frustumCullingJob,
- m_filterEntityByLayerJob,
- m_filterProximityJob,
- m_materialGathererJobs,
- m_renderViewCommandUpdaterJobs,
- m_renderViewCommandBuilderJobs),
- JobTypes::SyncRenderViewInitialization);
+ m_frustumCullingJob,
+ m_filterEntityByLayerJob,
+ m_filterProximityJob,
+ m_materialGathererJobs,
+ m_renderViewCommandUpdaterJobs,
+ m_renderViewCommandBuilderJobs),
+ JobTypes::SyncRenderViewInitialization);
}
QVector<Qt3DCore::QAspectJobPtr> RenderViewBuilder::buildJobHierachy() const
@@ -793,9 +793,34 @@ bool RenderViewBuilder::renderCommandCacheNeedsToBeRebuilt() const
return m_renderCommandCacheNeedsToBeRebuilt;
}
-int RenderViewBuilder::optimalJobCount()
+int RenderViewBuilder::defaultJobCount()
+{
+ static int jobCount = 0;
+ if (jobCount)
+ return jobCount;
+
+ const QByteArray maxThreadCount = qgetenv("QT3D_MAX_THREAD_COUNT");
+ if (!maxThreadCount.isEmpty()) {
+ bool conversionOK = false;
+ const int maxThreadCountValue = maxThreadCount.toInt(&conversionOK);
+ if (conversionOK) {
+ jobCount = maxThreadCountValue;
+ return jobCount;
+ }
+ }
+
+ jobCount = QThread::idealThreadCount();
+ return jobCount;
+}
+
+int RenderViewBuilder::optimalJobCount() const
+{
+ return m_optimalParallelJobCount;
+}
+
+void RenderViewBuilder::setOptimalJobCount(int v)
{
- return RenderViewBuilder::m_optimalParallelJobCount;
+ m_optimalParallelJobCount = v;
}
QVector<Entity *> RenderViewBuilder::entitiesInSubset(const QVector<Entity *> &entities, const QVector<Entity *> &subset)
diff --git a/src/plugins/renderers/opengl/renderer/renderviewbuilder_p.h b/src/plugins/renderers/opengl/renderer/renderviewbuilder_p.h
index 54fc98352..17e5fa744 100644
--- a/src/plugins/renderers/opengl/renderer/renderviewbuilder_p.h
+++ b/src/plugins/renderers/opengl/renderer/renderviewbuilder_p.h
@@ -61,7 +61,6 @@
#include <renderviewcommandbuilderjob_p.h>
#include <renderviewcommandupdaterjob_p.h>
#include <materialparametergathererjob_p.h>
-#include <renderviewbuilderjob_p.h>
#include <renderview_p.h>
QT_BEGIN_NAMESPACE
@@ -112,7 +111,10 @@ public:
void setRenderCommandCacheNeedsToBeRebuilt(bool needsToBeRebuilt);
bool renderCommandCacheNeedsToBeRebuilt() const;
- static int optimalJobCount();
+ static int defaultJobCount();
+ int optimalJobCount() const;
+ void setOptimalJobCount(int v);
+
static QVector<Entity *> entitiesInSubset(const QVector<Entity *> &entities, const QVector<Entity *> &subset);
private:
@@ -140,7 +142,7 @@ private:
SynchronizerJobPtr m_syncMaterialGathererJob;
FilterProximityDistanceJobPtr m_filterProximityJob;
- static const int m_optimalParallelJobCount;
+ int m_optimalParallelJobCount;
};
} // OpenGL
diff --git a/src/plugins/renderers/opengl/renderer/shaderparameterpack.cpp b/src/plugins/renderers/opengl/renderer/shaderparameterpack.cpp
index c51595bb7..bc9e9434b 100644
--- a/src/plugins/renderers/opengl/renderer/shaderparameterpack.cpp
+++ b/src/plugins/renderers/opengl/renderer/shaderparameterpack.cpp
@@ -58,6 +58,12 @@ ShaderParameterPack::~ShaderParameterPack()
{
}
+void ShaderParameterPack::reserve(int uniformCount)
+{
+ m_uniforms.reserve(uniformCount);
+ m_submissionUniformIndices.reserve(uniformCount);
+}
+
void ShaderParameterPack::setUniform(const int glslNameId, const UniformValue &val)
{
m_uniforms.insert(glslNameId, val);
@@ -100,9 +106,9 @@ void ShaderParameterPack::setShaderStorageBuffer(BlockToSSBO blockToSSBO)
m_shaderStorageBuffers.push_back(std::move(blockToSSBO));
}
-void ShaderParameterPack::setSubmissionUniform(const ShaderUniform &uniform)
+void ShaderParameterPack::setSubmissionUniformIndex(const int uniformIdx)
{
- m_submissionUniforms.push_back(uniform);
+ m_submissionUniformIndices.push_back(uniformIdx);
}
} // namespace OpenGL
diff --git a/src/plugins/renderers/opengl/renderer/shaderparameterpack_p.h b/src/plugins/renderers/opengl/renderer/shaderparameterpack_p.h
index 31ef4f7ea..bb6bb0dc6 100644
--- a/src/plugins/renderers/opengl/renderer/shaderparameterpack_p.h
+++ b/src/plugins/renderers/opengl/renderer/shaderparameterpack_p.h
@@ -93,8 +93,12 @@ struct PackUniformHash
PackUniformHash()
{
- keys.reserve(10);
- values.reserve(10);
+ }
+
+ void reserve(int count)
+ {
+ keys.reserve(count);
+ values.reserve(count);
}
void insert(int key, const UniformValue &value)
@@ -142,13 +146,14 @@ class Q_AUTOTEST_EXPORT ShaderParameterPack
public:
~ShaderParameterPack();
+ void reserve(int uniformCount);
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);
+ void setSubmissionUniformIndex(const int shaderUniformIndex);
inline PackUniformHash &uniforms() { return m_uniforms; }
inline const PackUniformHash &uniforms() const { return m_uniforms; }
@@ -194,7 +199,7 @@ public:
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; }
+ inline QVector<int> submissionUniformIndices() const { return m_submissionUniformIndices; }
private:
PackUniformHash m_uniforms;
@@ -202,7 +207,7 @@ private:
QVector<NamedResource> m_images;
QVector<BlockToUBO> m_uniformBuffers;
QVector<BlockToSSBO> m_shaderStorageBuffers;
- QVector<ShaderUniform> m_submissionUniforms;
+ QVector<int> m_submissionUniformIndices;
friend class RenderView;
};
diff --git a/src/plugins/renderers/renderers.pro b/src/plugins/renderers/renderers.pro
index dc58bf7fc..f5399ce84 100644
--- a/src/plugins/renderers/renderers.pro
+++ b/src/plugins/renderers/renderers.pro
@@ -7,3 +7,7 @@ QT_FOR_CONFIG += 3drender-private
#SUBDIRS += dummy
qtConfig(qt3d-opengl-renderer): SUBDIRS += opengl
+
+qtConfig(qt3d-rhi-renderer): {
+ qtHaveModule(shadertools): SUBDIRS += rhi
+}
diff --git a/src/plugins/renderers/rhi/graphicshelpers/graphicshelpers.pri b/src/plugins/renderers/rhi/graphicshelpers/graphicshelpers.pri
new file mode 100644
index 000000000..e156d3ce5
--- /dev/null
+++ b/src/plugins/renderers/rhi/graphicshelpers/graphicshelpers.pri
@@ -0,0 +1,9 @@
+#DEFINES += QT3D_RENDER_ASPECT_OPENGL_DEBUG
+
+INCLUDEPATH += $$PWD
+
+HEADERS += \
+ $$PWD/submissioncontext_p.h
+
+SOURCES += \
+ $$PWD/submissioncontext.cpp
diff --git a/src/plugins/renderers/rhi/graphicshelpers/submissioncontext.cpp b/src/plugins/renderers/rhi/graphicshelpers/submissioncontext.cpp
new file mode 100644
index 000000000..5b217929c
--- /dev/null
+++ b/src/plugins/renderers/rhi/graphicshelpers/submissioncontext.cpp
@@ -0,0 +1,1871 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "submissioncontext_p.h"
+
+#include <Qt3DRender/qgraphicsapifilter.h>
+#include <Qt3DRender/qparameter.h>
+#include <Qt3DRender/qcullface.h>
+#include <Qt3DRender/qfrontface.h>
+#include <Qt3DRender/qdepthtest.h>
+#include <Qt3DRender/qblendequation.h>
+#include <Qt3DRender/qblendequationarguments.h>
+#include <Qt3DRender/qstenciloperationarguments.h>
+#include <Qt3DRender/qstenciltestarguments.h>
+#include <Qt3DRender/private/renderlogging_p.h>
+#include <Qt3DRender/private/shader_p.h>
+#include <Qt3DRender/private/material_p.h>
+#include <Qt3DRender/private/buffer_p.h>
+#include <Qt3DRender/private/attribute_p.h>
+#include <Qt3DRender/private/renderstates_p.h>
+#include <Qt3DRender/private/renderstateset_p.h>
+#include <Qt3DRender/private/rendertarget_p.h>
+#include <Qt3DRender/private/nodemanagers_p.h>
+#include <Qt3DRender/private/buffermanager_p.h>
+#include <Qt3DRender/private/managers_p.h>
+#include <Qt3DRender/private/attachmentpack_p.h>
+#include <Qt3DRender/private/qbuffer_p.h>
+#include <Qt3DRender/private/stringtoint_p.h>
+#include <Qt3DRender/private/vulkaninstance_p.h>
+#include <QGuiApplication>
+#include <texture_p.h>
+#include <rendercommand_p.h>
+#include <renderer_p.h>
+#include <rhiresourcemanagers_p.h>
+#include <renderbuffer_p.h>
+#include <rhishader_p.h>
+#include <QOpenGLShaderProgram>
+
+#include <private/qdebug_p.h>
+#include <QSurface>
+#include <QWindow>
+#include <QShaderBaker>
+
+#ifdef Q_OS_WIN
+#include <QtGui/private/qrhid3d11_p.h>
+#endif
+
+#if defined(Q_OS_MACOS) || defined(Q_OS_IOS)
+#include <QtGui/private/qrhimetal_p.h>
+#endif
+
+#ifndef QT_NO_OPENGL
+#include <QtGui/private/qrhigles2_p.h>
+#endif
+
+#if QT_CONFIG(vulkan)
+#include <QtGui/private/qrhivulkan_p.h>
+#endif
+#include <bitset>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+namespace Render {
+namespace Rhi {
+
+static QHash<unsigned int, SubmissionContext *> static_contexts;
+
+unsigned int nextFreeContextId() noexcept
+{
+ for (unsigned int i = 0; i < 0xffff; ++i) {
+ if (!static_contexts.contains(i))
+ return i;
+ }
+
+ qFatal("Couldn't find free context ID");
+ return 0;
+}
+
+namespace {
+
+RHIBuffer::Type attributeTypeToGLBufferType(QAttribute::AttributeType type) noexcept
+{
+ switch (type) {
+ case QAttribute::VertexAttribute:
+ return RHIBuffer::ArrayBuffer;
+ case QAttribute::IndexAttribute:
+ return RHIBuffer::IndexBuffer;
+ case QAttribute::DrawIndirectAttribute:
+ return RHIBuffer::DrawIndirectBuffer;
+ default:
+ Q_UNREACHABLE();
+ }
+}
+
+void copyGLFramebufferDataToImage(QImage &img, const uchar *srcData, uint stride, uint width,
+ uint height, QAbstractTexture::TextureFormat format) noexcept
+{
+ switch (format) {
+ case QAbstractTexture::RGBA32F: {
+ uchar *srcScanline = const_cast<uchar *>(srcData) + stride * (height - 1);
+ for (uint i = 0; i < height; ++i) {
+ uchar *dstScanline = img.scanLine(i);
+ float *pSrc = reinterpret_cast<float *>(srcScanline);
+ for (uint j = 0; j < width; j++) {
+ *dstScanline++ = (uchar)(255.0f * qBound(0.0f, pSrc[4 * j + 2], 1.0f));
+ *dstScanline++ = (uchar)(255.0f * qBound(0.0f, pSrc[4 * j + 1], 1.0f));
+ *dstScanline++ = (uchar)(255.0f * qBound(0.0f, pSrc[4 * j + 0], 1.0f));
+ *dstScanline++ = (uchar)(255.0f * qBound(0.0f, pSrc[4 * j + 3], 1.0f));
+ }
+ srcScanline -= stride;
+ }
+ } break;
+ default: {
+ uchar *srcScanline = (uchar *)srcData + stride * (height - 1);
+ for (uint i = 0; i < height; ++i) {
+ memcpy(img.scanLine(i), srcScanline, stride);
+ srcScanline -= stride;
+ }
+ } break;
+ }
+}
+
+// Render States Helpers
+
+template<typename GenericState>
+void applyStateHelper(const GenericState *state, QRhiGraphicsPipeline *gp) noexcept
+{
+ Q_UNUSED(state);
+ Q_UNUSED(gp);
+ qWarning() << "RHI Unhandled render state" << typeid(GenericState).name();
+}
+
+void applyStateHelper(const BlendEquationArguments *state, QRhiGraphicsPipeline *gp) noexcept
+{
+ const auto values = state->values();
+
+ // We assume a single color attachment
+ QRhiGraphicsPipeline::TargetBlend targetBlend {};
+
+ const bool hasTargetBlend = gp->cbeginTargetBlends() != gp->cendTargetBlends();
+ if (hasTargetBlend)
+ targetBlend = *(gp->cbeginTargetBlends());
+
+ auto getRHIBlendFactor = [](int arg) {
+ switch (arg) {
+ case QBlendEquationArguments::Zero:
+ return QRhiGraphicsPipeline::Zero;
+ case QBlendEquationArguments::One:
+ return QRhiGraphicsPipeline::One;
+ case QBlendEquationArguments::SourceColor:
+ return QRhiGraphicsPipeline::SrcColor;
+ case QBlendEquationArguments::SourceAlpha:
+ return QRhiGraphicsPipeline::SrcAlpha;
+ // ### Qt 6 Fix values
+ // case QBlendEquationArguments::Source1Alpha:
+ // return QRhiGraphicsPipeline::Src1Alpha;
+ // case QBlendEquationArguments::Source1Color:
+ // return QRhiGraphicsPipeline::Src1Color;
+ case QBlendEquationArguments::DestinationColor:
+ return QRhiGraphicsPipeline::DstColor;
+ case QBlendEquationArguments::DestinationAlpha:
+ return QRhiGraphicsPipeline::DstAlpha;
+ case QBlendEquationArguments::SourceAlphaSaturate:
+ return QRhiGraphicsPipeline::SrcAlphaSaturate;
+ case QBlendEquationArguments::ConstantColor:
+ return QRhiGraphicsPipeline::ConstantColor;
+ case QBlendEquationArguments::ConstantAlpha:
+ return QRhiGraphicsPipeline::ConstantAlpha;
+ case QBlendEquationArguments::OneMinusSourceColor:
+ return QRhiGraphicsPipeline::OneMinusSrcColor;
+ case QBlendEquationArguments::OneMinusSourceAlpha:
+ return QRhiGraphicsPipeline::OneMinusSrcAlpha;
+ case QBlendEquationArguments::OneMinusDestinationAlpha:
+ return QRhiGraphicsPipeline::OneMinusDstAlpha;
+ case QBlendEquationArguments::OneMinusDestinationColor:
+ return QRhiGraphicsPipeline::OneMinusDstColor;
+ case QBlendEquationArguments::OneMinusConstantColor:
+ return QRhiGraphicsPipeline::OneMinusConstantColor;
+ case QBlendEquationArguments::OneMinusConstantAlpha:
+ return QRhiGraphicsPipeline::OneMinusConstantAlpha;
+ case QBlendEquationArguments::OneMinusSource1Alpha:
+ return QRhiGraphicsPipeline::OneMinusSrc1Alpha;
+ case QBlendEquationArguments::OneMinusSource1Color:
+ return QRhiGraphicsPipeline::OneMinusSrc1Color;
+ default:
+ qDebug() << "Unhandled blend equation argument" << arg;
+ return QRhiGraphicsPipeline::Zero;
+ }
+ };
+
+ targetBlend.srcAlpha = getRHIBlendFactor(std::get<2>(values));
+ targetBlend.dstAlpha = getRHIBlendFactor(std::get<3>(values));
+ targetBlend.srcColor = getRHIBlendFactor(std::get<0>(values));
+ targetBlend.dstColor = getRHIBlendFactor(std::get<1>(values));
+ gp->setTargetBlends({ targetBlend });
+}
+
+void applyStateHelper(const BlendEquation *state, QRhiGraphicsPipeline *gp) noexcept
+{
+ const auto values = state->values();
+ const QBlendEquation::BlendFunction equation =
+ static_cast<QBlendEquation::BlendFunction>(std::get<0>(values));
+
+ // We assume a single color attachment
+ QRhiGraphicsPipeline::TargetBlend targetBlend;
+
+ const bool hasTargetBlend = gp->cbeginTargetBlends() != gp->cendTargetBlends();
+ if (hasTargetBlend)
+ targetBlend = *(gp->cbeginTargetBlends());
+
+ auto getRHIBlendOp = [](QBlendEquation::BlendFunction equation) {
+ switch (equation) {
+ case QBlendEquation::Add:
+ return QRhiGraphicsPipeline::Add;
+ case QBlendEquation::Subtract:
+ return QRhiGraphicsPipeline::Subtract;
+ case QBlendEquation::ReverseSubtract:
+ return QRhiGraphicsPipeline::ReverseSubtract;
+ case QBlendEquation::Min:
+ return QRhiGraphicsPipeline::Min;
+ case QBlendEquation::Max:
+ return QRhiGraphicsPipeline::Max;
+ }
+ };
+
+ targetBlend.enable = true;
+ targetBlend.opAlpha = getRHIBlendOp(equation);
+ gp->setTargetBlends({ targetBlend });
+}
+
+void applyStateHelper(const MSAAEnabled *state, QRhiGraphicsPipeline *gp,
+ const QSurfaceFormat &format) noexcept
+{
+ gp->setSampleCount(format.samples());
+}
+
+void applyStateHelper(const DepthTest *state, QRhiGraphicsPipeline *gp) noexcept
+{
+ gp->setDepthTest(true);
+ const QDepthTest::DepthFunction depthFunc =
+ static_cast<QDepthTest::DepthFunction>(std::get<0>(state->values()));
+ switch (depthFunc) {
+ case QDepthTest::Never:
+ gp->setDepthOp(QRhiGraphicsPipeline::Never);
+ break;
+ case QDepthTest::Always:
+ gp->setDepthOp(QRhiGraphicsPipeline::Always);
+ break;
+ case QDepthTest::Less:
+ gp->setDepthOp(QRhiGraphicsPipeline::Less);
+ break;
+ case QDepthTest::LessOrEqual:
+ gp->setDepthOp(QRhiGraphicsPipeline::LessOrEqual);
+ break;
+ case QDepthTest::Equal:
+ gp->setDepthOp(QRhiGraphicsPipeline::Equal);
+ break;
+ case QDepthTest::GreaterOrEqual:
+ gp->setDepthOp(QRhiGraphicsPipeline::GreaterOrEqual);
+ break;
+ case QDepthTest::Greater:
+ gp->setDepthOp(QRhiGraphicsPipeline::Greater);
+ break;
+ case QDepthTest::NotEqual:
+ gp->setDepthOp(QRhiGraphicsPipeline::NotEqual);
+ break;
+ }
+}
+
+void applyStateHelper(const NoDepthMask *state, QRhiGraphicsPipeline *gp) noexcept
+{
+ const auto values = state->values();
+ gp->setDepthWrite(std::get<0>(values));
+}
+
+void applyStateHelper(const CullFace *state, QRhiGraphicsPipeline *gp) noexcept
+{
+ const auto values = state->values();
+ const QCullFace::CullingMode cullingMode =
+ static_cast<QCullFace::CullingMode>(std::get<0>(values));
+ switch (cullingMode) {
+ case QCullFace::NoCulling:
+ gp->setCullMode(QRhiGraphicsPipeline::None);
+ break;
+ case QCullFace::Front:
+ gp->setCullMode(QRhiGraphicsPipeline::Front);
+ break;
+ case QCullFace::Back:
+ gp->setCullMode(QRhiGraphicsPipeline::Back);
+ break;
+ case QCullFace::FrontAndBack:
+ qWarning() << "RHI doesn't handle FrontAndBack CullFace";
+ break;
+ }
+}
+
+void applyStateHelper(const FrontFace *state, QRhiGraphicsPipeline *gp) noexcept
+{
+ const auto values = state->values();
+ const QFrontFace::WindingDirection cullingMode =
+ static_cast<QFrontFace::WindingDirection>(std::get<0>(values));
+
+ switch (cullingMode) {
+ case QFrontFace::ClockWise:
+ gp->setFrontFace(QRhiGraphicsPipeline::CW);
+ break;
+ case QFrontFace::CounterClockWise:
+ gp->setFrontFace(QRhiGraphicsPipeline::CCW);
+ break;
+ }
+}
+
+void applyStateHelper(const StencilTest *state, QRhiGraphicsPipeline *gp) noexcept
+{
+ const auto values = state->values();
+ gp->setStencilTest(true);
+
+ auto getCompareOp = [](int compareOp) {
+ switch (compareOp) {
+ case QStencilTestArguments::Never:
+ return QRhiGraphicsPipeline::Never;
+ case QStencilTestArguments::Always:
+ return QRhiGraphicsPipeline::Always;
+ case QStencilTestArguments::Less:
+ return QRhiGraphicsPipeline::Less;
+ case QStencilTestArguments::LessOrEqual:
+ return QRhiGraphicsPipeline::LessOrEqual;
+ case QStencilTestArguments::Equal:
+ return QRhiGraphicsPipeline::Equal;
+ case QStencilTestArguments::GreaterOrEqual:
+ return QRhiGraphicsPipeline::GreaterOrEqual;
+ case QStencilTestArguments::Greater:
+ return QRhiGraphicsPipeline::Greater;
+ case QStencilTestArguments::NotEqual:
+ return QRhiGraphicsPipeline::NotEqual;
+ default:
+ qDebug() << "Unhandled stencil test argument";
+ return QRhiGraphicsPipeline::Never;
+ }
+ };
+
+ QRhiGraphicsPipeline::StencilOpState frontCompare = gp->stencilFront();
+ frontCompare.compareOp = getCompareOp(std::get<0>(values));
+ gp->setStencilFront(frontCompare);
+
+ QRhiGraphicsPipeline::StencilOpState backCompare = gp->stencilBack();
+ backCompare.compareOp = getCompareOp(std::get<3>(values));
+ gp->setStencilBack(backCompare);
+}
+
+void applyStateHelper(const ColorMask *state, QRhiGraphicsPipeline *gp) noexcept
+{
+ const auto values = state->values();
+
+ // We assume a single color attachment
+ QRhiGraphicsPipeline::TargetBlend targetBlend;
+
+ const bool hasTargetBlend = gp->cbeginTargetBlends() != gp->cendTargetBlends();
+ if (hasTargetBlend)
+ targetBlend = *(gp->cbeginTargetBlends());
+
+ const bool redEnabled = std::get<0>(values);
+ const bool greenEnabled = std::get<1>(values);
+ const bool blueEnabled = std::get<2>(values);
+ const bool alphaEnabled = std::get<3>(values);
+
+ QRhiGraphicsPipeline::ColorMask colorMask;
+ if (redEnabled)
+ colorMask |= QRhiGraphicsPipeline::R;
+ if (greenEnabled)
+ colorMask |= QRhiGraphicsPipeline::G;
+ if (blueEnabled)
+ colorMask |= QRhiGraphicsPipeline::B;
+ if (alphaEnabled)
+ colorMask |= QRhiGraphicsPipeline::A;
+
+ targetBlend.colorWrite = colorMask;
+ gp->setTargetBlends({ targetBlend });
+}
+
+void applyStateHelper(const StencilOp *state, QRhiGraphicsPipeline *gp) noexcept
+{
+ const auto values = state->values();
+ auto getRHIStencilOp = [](int op) {
+ switch (op) {
+ case QStencilOperationArguments::Zero:
+ return QRhiGraphicsPipeline::StencilZero;
+ case QStencilOperationArguments::Keep:
+ return QRhiGraphicsPipeline::Keep;
+ case QStencilOperationArguments::Replace:
+ return QRhiGraphicsPipeline::Replace;
+ case QStencilOperationArguments::Increment:
+ return QRhiGraphicsPipeline::IncrementAndClamp;
+ case QStencilOperationArguments::Decrement:
+ return QRhiGraphicsPipeline::DecrementAndClamp;
+ case QStencilOperationArguments::IncrementWrap:
+ return QRhiGraphicsPipeline::IncrementAndWrap;
+ case QStencilOperationArguments::DecrementWrap:
+ return QRhiGraphicsPipeline::DecrementAndWrap;
+ case QStencilOperationArguments::Invert:
+ return QRhiGraphicsPipeline::Invert;
+ default:
+ qDebug() << "Unhandled stencil operation argument";
+ return QRhiGraphicsPipeline::StencilZero;
+ }
+ };
+
+ QRhiGraphicsPipeline::StencilOpState frontCompare = gp->stencilFront();
+ frontCompare.depthFailOp = getRHIStencilOp(std::get<1>(values));
+ frontCompare.failOp = getRHIStencilOp(std::get<0>(values));
+ frontCompare.passOp = getRHIStencilOp(std::get<2>(values));
+ gp->setStencilFront(frontCompare);
+
+ QRhiGraphicsPipeline::StencilOpState backCompare = gp->stencilBack();
+ backCompare.depthFailOp = getRHIStencilOp(std::get<4>(values));
+ backCompare.failOp = getRHIStencilOp(std::get<3>(values));
+ backCompare.passOp = getRHIStencilOp(std::get<5>(values));
+ gp->setStencilBack(backCompare);
+}
+
+void applyStateHelper(const StencilMask *state, QRhiGraphicsPipeline *gp) noexcept
+{
+ const auto values = state->values();
+ gp->setStencilWriteMask(std::get<0>(values));
+ gp->setStencilReadMask(std::get<1>(values));
+}
+
+static QShader::Stage rhiShaderStage(QShaderProgram::ShaderType type) noexcept
+{
+ switch (type) {
+ case QShaderProgram::Vertex:
+ return QShader::VertexStage;
+ case QShaderProgram::Fragment:
+ return QShader::FragmentStage;
+ case QShaderProgram::TessellationControl:
+ return QShader::TessellationControlStage;
+ case QShaderProgram::TessellationEvaluation:
+ return QShader::TessellationEvaluationStage;
+ case QShaderProgram::Geometry:
+ return QShader::GeometryStage;
+ case QShaderProgram::Compute:
+ return QShader::ComputeStage;
+ default:
+ std::abort();
+ }
+}
+
+} // anonymous
+
+SubmissionContext::SubmissionContext()
+ : m_ownCurrent(true),
+ m_id(nextFreeContextId()),
+ m_surface(nullptr),
+ m_activeShader(nullptr),
+ m_renderTargetFormat(QAbstractTexture::NoFormat),
+ m_material(nullptr),
+ m_activeFBO(0),
+ m_renderer(nullptr),
+ m_uboTempArray(QByteArray(1024, 0)),
+ m_initialized(false),
+ m_maxTextureUnits(0),
+ m_defaultFBO(0),
+ m_rhi(nullptr),
+ m_currentSwapChain(nullptr),
+ m_currentRenderPassDescriptor(nullptr)
+#ifndef QT_NO_OPENGL
+ ,
+ m_fallbackSurface(nullptr)
+#endif
+{
+ static_contexts[m_id] = this;
+ m_contextInfo.m_api = QGraphicsApiFilter::RHI;
+
+ // We set those version numbers because QShaderGenerator wants major > 0
+ m_contextInfo.m_major = 1;
+ m_contextInfo.m_minor = 0;
+}
+
+SubmissionContext::~SubmissionContext()
+{
+ releaseResources();
+
+ Q_ASSERT(static_contexts[m_id] == this);
+ static_contexts.remove(m_id);
+}
+
+void SubmissionContext::initialize()
+{
+ m_initialized = true;
+ // m_textureContext.initialize(this);
+
+ Qt3DRender::API requestedApi = Qt3DRender::API::OpenGL;
+ const auto userRequestedApi = qgetenv("QT3D_RHI_DEFAULT_API").toLower();
+ if (!userRequestedApi.isEmpty()) {
+ if (userRequestedApi == QByteArrayLiteral("opengl")) {
+ requestedApi = Qt3DRender::API::OpenGL;
+ } else if (userRequestedApi == QByteArrayLiteral("vulkan")) {
+ requestedApi = Qt3DRender::API::Vulkan;
+ } else if (userRequestedApi == QByteArrayLiteral("metal")) {
+ requestedApi = Qt3DRender::API::Metal;
+ } else if (userRequestedApi == QByteArrayLiteral("d3d11")) {
+ requestedApi = Qt3DRender::API::DirectX;
+ } else if (userRequestedApi == QByteArrayLiteral("null")) {
+ requestedApi = Qt3DRender::API::Null;
+ }
+ }
+
+ QRhi::Flags rhiFlags = QRhi::EnableDebugMarkers;
+
+#if QT_CONFIG(vulkan)
+ if (requestedApi == Qt3DRender::API::Vulkan) {
+ QRhiVulkanInitParams params;
+ params.inst = &Qt3DRender::staticVulkanInstance();
+ m_rhi = QRhi::create(QRhi::Vulkan, &params, rhiFlags);
+ }
+#endif
+
+#ifdef Q_OS_WIN
+ if (requestedApi == Qt3DRender::API::DirectX) {
+ QRhiD3D11InitParams params;
+ params.enableDebugLayer = true;
+ m_rhi = QRhi::create(QRhi::D3D11, &params, rhiFlags);
+ }
+#endif
+
+#if defined(Q_OS_MACOS) || defined(Q_OS_IOS)
+ if (requestedApi == Qt3DRender::API::Metal) {
+ QRhiMetalInitParams params;
+ m_rhi = QRhi::create(QRhi::Metal, &params, rhiFlags);
+ }
+#endif
+ if (requestedApi == Qt3DRender::API::Null) {
+ QRhiInitParams params;
+ m_rhi = QRhi::create(QRhi::Null, &params, rhiFlags);
+ }
+
+ if (requestedApi != Qt3DRender::API::OpenGL && m_rhi == nullptr) {
+ qWarning() << "RHI: Unable to use requested RHI Api, trying to fall back on OpenGL";
+ requestedApi = Qt3DRender::API::OpenGL;
+ }
+
+ if (requestedApi == Qt3DRender::API::OpenGL) {
+#ifndef QT_NO_OPENGL
+ m_fallbackSurface = QRhiGles2InitParams::newFallbackSurface();
+ QRhiGles2InitParams params;
+ params.format = QSurfaceFormat::defaultFormat();
+ params.fallbackSurface = m_fallbackSurface;
+ m_rhi = QRhi::create(QRhi::OpenGLES2, &params, rhiFlags);
+#else
+ qWarning() << "RHI: OpenGL not supported";
+#endif
+ }
+
+ Q_ASSERT(m_rhi != nullptr);
+}
+
+void SubmissionContext::resolveRenderTargetFormat()
+{
+ RHI_UNIMPLEMENTED;
+
+ //* const QSurfaceFormat format = m_gl->format();
+ //* const uint a = (format.alphaBufferSize() == -1) ? 0 : format.alphaBufferSize();
+ //* const uint r = format.redBufferSize();
+ //* const uint g = format.greenBufferSize();
+ //* const uint b = format.blueBufferSize();
+ //*
+ //* #define RGBA_BITS(r,g,b,a) (r | (g << 6) | (b << 12) | (a << 18))
+ //*
+ //* const uint bits = RGBA_BITS(r,g,b,a);
+ //* switch (bits) {
+ //* case RGBA_BITS(8,8,8,8):
+ //* m_renderTargetFormat = QAbstractTexture::RGBA8_UNorm;
+ //* break;
+ //* case RGBA_BITS(8,8,8,0):
+ //* m_renderTargetFormat = QAbstractTexture::RGB8_UNorm;
+ //* break;
+ //* case RGBA_BITS(5,6,5,0):
+ //* m_renderTargetFormat = QAbstractTexture::R5G6B5;
+ //* break;
+ //* }
+ //* #undef RGBA_BITS
+}
+
+bool SubmissionContext::beginDrawing(QSurface *surface)
+{
+ Q_ASSERT(surface);
+
+ m_surface = surface;
+
+ // TO DO: Find a way to make to pause work if the window is not exposed
+ // if (m_surface && m_surface->surfaceClass() == QSurface::Window) {
+ // qDebug() << Q_FUNC_INFO << 1;
+ // if (!static_cast<QWindow *>(m_surface)->isExposed())
+ // return false;
+ // qDebug() << Q_FUNC_INFO << 2;
+ // }
+
+ // Makes the surface current on the OpenGLContext
+ // and sets the right glHelper
+ // m_ownCurrent = !(m_gl->surface() == m_surface);
+ // if (m_ownCurrent && !makeCurrent(m_surface))
+ // return false;
+
+ // TODO: cache surface format somewhere rather than doing this every time render surface changes
+ resolveRenderTargetFormat();
+
+#if defined(QT3D_RENDER_ASPECT_RHI_DEBUG)
+ GLint err = m_gl->functions()->glGetError();
+ if (err != 0) {
+ qCWarning(Backend) << Q_FUNC_INFO << "glGetError:" << err;
+ }
+#endif
+
+ Q_ASSERT(isInitialized());
+
+ // need to reset these values every frame, may get overwritten elsewhere
+ RHI_UNIMPLEMENTED;
+
+ if (m_activeShader) {
+ m_activeShader = nullptr;
+ }
+
+ // Check if we have a swapchain for the Window, if not create one
+ SwapChainInfo *swapChainInfo = swapChainForSurface(surface);
+ QRhiSwapChain *swapChain = swapChainInfo->swapChain;
+
+ // TO DO: Check if that's required all the time
+ {
+ // Rebuild RenderPassDescriptor
+ // TODO -> this is not necessary, swapChain->buildOrResize already does it
+ // swapChainInfo->renderBuffer->setPixelSize(surface->size());
+ // swapChainInfo->renderBuffer->build();
+
+ // Resize swapchain if needed
+ if (m_surface->size() != swapChain->surfacePixelSize()) {
+ bool couldRebuild = swapChain->buildOrResize();
+ if (!couldRebuild)
+ return false;
+ }
+ }
+
+ m_currentSwapChain = swapChain;
+ m_currentRenderPassDescriptor = swapChainInfo->renderPassDescriptor;
+
+ // Begin Frame
+ const auto success = m_rhi->beginFrame(m_currentSwapChain);
+
+ return success == QRhi::FrameOpSuccess;
+}
+
+void SubmissionContext::endDrawing(bool swapBuffers)
+{
+ m_rhi->endFrame(m_currentSwapChain, {});
+
+ RHI_UNIMPLEMENTED;
+ //* if (swapBuffers)
+ //* m_gl->swapBuffers(m_surface);
+ //* if (m_ownCurrent)
+ //* m_gl->doneCurrent();
+ // m_textureContext.endDrawing();
+ //* static int i = 0;
+ //* if (i++ == 10)
+ //* std::exit(0);
+}
+
+void SubmissionContext::activateRenderTarget(Qt3DCore::QNodeId renderTargetNodeId,
+ const AttachmentPack &attachments, GLuint defaultFboId)
+{
+ RHI_UNIMPLEMENTED;
+ GLuint fboId = defaultFboId; // Default FBO
+ if (renderTargetNodeId) {
+ // New RenderTarget
+ if (!m_renderTargets.contains(renderTargetNodeId)) {
+ if (m_defaultFBO && fboId == m_defaultFBO) {
+ // this is the default fbo that some platforms create (iOS), we just register it
+ // Insert FBO into hash
+ m_renderTargets.insert(renderTargetNodeId, fboId);
+ } else {
+ RHI_UNIMPLEMENTED;
+ fboId = createRenderTarget(renderTargetNodeId, attachments);
+ }
+ } else {
+ RHI_UNIMPLEMENTED;
+ fboId = updateRenderTarget(renderTargetNodeId, attachments, true);
+ }
+ }
+ m_activeFBO = fboId;
+ //* m_glHelper->bindFrameBufferObject(m_activeFBO, GraphicsHelperInterface::FBODraw);
+ // Set active drawBuffers
+ activateDrawBuffers(attachments);
+}
+
+GLuint SubmissionContext::createRenderTarget(Qt3DCore::QNodeId renderTargetNodeId,
+ const AttachmentPack &attachments)
+{
+ RHI_UNIMPLEMENTED;
+ return 0;
+ //* const GLuint fboId = m_glHelper->createFrameBufferObject();
+ //* if (fboId) {
+ //* // The FBO is created and its attachments are set once
+ //* // Insert FBO into hash
+ //* m_renderTargets.insert(renderTargetNodeId, fboId);
+ //* // Bind FBO
+ //* m_glHelper->bindFrameBufferObject(fboId, GraphicsHelperInterface::FBODraw);
+ //* bindFrameBufferAttachmentHelper(fboId, attachments);
+ //* } else {
+ //* qCritical("Failed to create FBO");
+ //* }
+ //* return fboId;
+}
+
+GLuint SubmissionContext::updateRenderTarget(Qt3DCore::QNodeId renderTargetNodeId,
+ const AttachmentPack &attachments,
+ bool isActiveRenderTarget)
+{
+ RHI_UNIMPLEMENTED;
+ return 0;
+ //* const GLuint fboId = m_renderTargets.value(renderTargetNodeId);
+ //*
+ //* // We need to check if one of the attachment was resized
+ //* bool needsResize = !m_renderTargetsSize.contains(fboId); // not even initialized yet?
+ //* if (!needsResize) {
+ //* // render target exists, has attachment been resized?
+ //* RHITextureManager *rhiTextureManager =
+ //m_renderer->rhiResourceManagers()->rhiTextureManager();
+ //* const QSize s = m_renderTargetsSize[fboId];
+ //* const auto attachments_ = attachments.attachments();
+ //* for (const Attachment &attachment : attachments_) {
+ //* RHITexture *rTex = rhiTextureManager->lookupResource(attachment.m_textureUuid);
+ //* // ### TODO QTBUG-64757 this check is insufficient since the
+ //* // texture may have changed to another one with the same size. That
+ //* // case is not handled atm.
+ //* if (rTex) {
+ //* needsResize |= rTex->size() != s;
+ //* if (isActiveRenderTarget && attachment.m_point ==
+ //QRenderTargetOutput::Color0)
+ //* m_renderTargetFormat = rTex->properties().format;
+ //* }
+ //* }
+ //* }
+ //*
+ //* if (needsResize) {
+ //* m_glHelper->bindFrameBufferObject(fboId, GraphicsHelperInterface::FBODraw);
+ //* bindFrameBufferAttachmentHelper(fboId, attachments);
+ //* }
+ //*
+ //* return fboId;
+}
+
+QSize SubmissionContext::renderTargetSize(const QSize &surfaceSize) const
+{
+ RHI_UNIMPLEMENTED;
+ return surfaceSize;
+ //* QSize renderTargetSize{};
+ //* if (m_activeFBO != m_defaultFBO) {
+ //* // For external FBOs we may not have a m_renderTargets entry.
+ //* if (m_renderTargetsSize.contains(m_activeFBO)) {
+ //* renderTargetSize = m_renderTargetsSize[m_activeFBO];
+ //* } else if (surfaceSize.isValid()) {
+ //* renderTargetSize = surfaceSize;
+ //* } else {
+ //* // External FBO (when used with QtQuick2 Scene3D)
+ //*
+ //* // Query FBO color attachment 0 size
+ //* GLint attachmentObjectType = GL_NONE;
+ //* GLint attachment0Name = 0;
+ //* m_gl->functions()->glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER,
+ //* GL_COLOR_ATTACHMENT0,
+ //* GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE,
+ //* &attachmentObjectType);
+ //* m_gl->functions()->glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER,
+ //* GL_COLOR_ATTACHMENT0,
+ //* GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME,
+ //* &attachment0Name);
+ //*
+ //* if (attachmentObjectType == GL_RENDERBUFFER &&
+ //m_glHelper->supportsFeature(GraphicsHelperInterface::RenderBufferDimensionRetrieval))
+ //* renderTargetSize = m_glHelper->getRenderBufferDimensions(attachment0Name);
+ //* else if (attachmentObjectType == GL_TEXTURE &&
+ //m_glHelper->supportsFeature(GraphicsHelperInterface::TextureDimensionRetrieval))
+ //* // Assumes texture level 0 and GL_TEXTURE_2D target
+ //* renderTargetSize = m_glHelper->getTextureDimensions(attachment0Name,
+ //GL_TEXTURE_2D);
+ //* else
+ //* return renderTargetSize;
+ //* }
+ //* } else {
+ //* renderTargetSize = m_surface->size();
+ //* if (m_surface->surfaceClass() == QSurface::Window) {
+ //* const float dpr = static_cast<QWindow *>(m_surface)->devicePixelRatio();
+ //* renderTargetSize *= dpr;
+ //* }
+ //* }
+ //* return renderTargetSize;
+}
+
+QImage SubmissionContext::readFramebuffer(const QRect &rect)
+{
+ RHI_UNIMPLEMENTED;
+ return {};
+ //* QImage img;
+ //* const unsigned int area = rect.width() * rect.height();
+ //* unsigned int bytes;
+ //* GLenum format, type;
+ //* QImage::Format imageFormat;
+ //* uint stride;
+ //*
+ //* /* format value should match GL internalFormat */
+ //* GLenum internalFormat = m_renderTargetFormat;
+ //*
+ //* switch (m_renderTargetFormat) {
+ //* case QAbstractTexture::RGBAFormat:
+ //* case QAbstractTexture::RGBA8_SNorm:
+ //* case QAbstractTexture::RGBA8_UNorm:
+ //* case QAbstractTexture::RGBA8U:
+ //* case QAbstractTexture::SRGB8_Alpha8:
+ //*#ifdef QT_OPENGL_ES_2
+ //* format = GL_RGBA;
+ //* imageFormat = QImage::Format_RGBA8888_Premultiplied;
+ //*#else
+ //* format = GL_BGRA;
+ //* imageFormat = QImage::Format_ARGB32_Premultiplied;
+ //* internalFormat = GL_RGBA8;
+ //*#endif
+ //* type = GL_UNSIGNED_BYTE;
+ //* bytes = area * 4;
+ //* stride = rect.width() * 4;
+ //* break;
+ //* case QAbstractTexture::SRGB8:
+ //* case QAbstractTexture::RGBFormat:
+ //* case QAbstractTexture::RGB8U:
+ //* case QAbstractTexture::RGB8_UNorm:
+ //*#ifdef QT_OPENGL_ES_2
+ //* format = GL_RGBA;
+ //* imageFormat = QImage::Format_RGBX8888;
+ //*#else
+ //* format = GL_BGRA;
+ //* imageFormat = QImage::Format_RGB32;
+ //* internalFormat = GL_RGB8;
+ //*#endif
+ //* type = GL_UNSIGNED_BYTE;
+ //* bytes = area * 4;
+ //* stride = rect.width() * 4;
+ //* break;
+ //*#ifndef QT_OPENGL_ES_2
+ //* case QAbstractTexture::RG11B10F:
+ //* bytes = area * 4;
+ //* format = GL_RGB;
+ //* type = GL_UNSIGNED_INT_10F_11F_11F_REV;
+ //* imageFormat = QImage::Format_RGB30;
+ //* stride = rect.width() * 4;
+ //* break;
+ //* case QAbstractTexture::RGB10A2:
+ //* bytes = area * 4;
+ //* format = GL_RGBA;
+ //* type = GL_UNSIGNED_INT_2_10_10_10_REV;
+ //* imageFormat = QImage::Format_A2BGR30_Premultiplied;
+ //* stride = rect.width() * 4;
+ //* break;
+ //* case QAbstractTexture::R5G6B5:
+ //* bytes = area * 2;
+ //* format = GL_RGB;
+ //* type = GL_UNSIGNED_SHORT;
+ //* internalFormat = GL_UNSIGNED_SHORT_5_6_5_REV;
+ //* imageFormat = QImage::Format_RGB16;
+ //* stride = rect.width() * 2;
+ //* break;
+ //* case QAbstractTexture::RGBA16F:
+ //* case QAbstractTexture::RGBA16U:
+ //* case QAbstractTexture::RGBA32F:
+ //* case QAbstractTexture::RGBA32U:
+ //* bytes = area * 16;
+ //* format = GL_RGBA;
+ //* type = GL_FLOAT;
+ //* imageFormat = QImage::Format_ARGB32_Premultiplied;
+ //* stride = rect.width() * 16;
+ //* break;
+ //*#endif
+ //* default:
+ //* auto warning = qWarning();
+ //* warning << "Unable to convert";
+ //* QtDebugUtils::formatQEnum(warning, m_renderTargetFormat);
+ //* warning << "render target texture format to QImage.";
+ //* return img;
+ //* }
+ //*
+ //* GLint samples = 0;
+ //* m_gl->functions()->glGetIntegerv(GL_SAMPLES, &samples);
+ //* if (samples > 0 &&
+ //!m_glHelper->supportsFeature(GraphicsHelperInterface::BlitFramebuffer)) {
+ //* qCWarning(Backend) << Q_FUNC_INFO << "Unable to capture multisampled framebuffer; "
+ //* "Required feature BlitFramebuffer is missing.";
+ //* return img;
+ //* }
+ //*
+ //* img = QImage(rect.width(), rect.height(), imageFormat);
+ //*
+ //* QScopedArrayPointer<uchar> data(new uchar [bytes]);
+ //*
+ //* if (samples > 0) {
+ //* // resolve multisample-framebuffer to renderbuffer and read pixels from it
+ //* GLuint fbo, rb;
+ //* QOpenGLFunctions *gl = m_gl->functions();
+ //* gl->glGenFramebuffers(1, &fbo);
+ //* gl->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
+ //* gl->glGenRenderbuffers(1, &rb);
+ //* gl->glBindRenderbuffer(GL_RENDERBUFFER, rb);
+ //* gl->glRenderbufferStorage(GL_RENDERBUFFER, internalFormat, rect.width(),
+ //rect.height());
+ //* gl->glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ //GL_RENDERBUFFER, rb);
+ //*
+ //* const GLenum status = gl->glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER);
+ //* if (status != GL_FRAMEBUFFER_COMPLETE) {
+ //* gl->glDeleteRenderbuffers(1, &rb);
+ //* gl->glDeleteFramebuffers(1, &fbo);
+ //* qCWarning(Backend) << Q_FUNC_INFO << "Copy-framebuffer not complete: " << status;
+ //* return img;
+ //* }
+ //*
+ //* m_glHelper->blitFramebuffer(rect.x(), rect.y(), rect.x() + rect.width(), rect.y() +
+ //rect.height(),
+ //* 0, 0, rect.width(), rect.height(),
+ //* GL_COLOR_BUFFER_BIT, GL_NEAREST);
+ //* gl->glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
+ //* gl->glReadPixels(0,0,rect.width(), rect.height(), format, type, data.data());
+ //*
+ //* copyGLFramebufferDataToImage(img, data.data(), stride, rect.width(), rect.height(),
+ //m_renderTargetFormat);
+ //*
+ //* gl->glBindRenderbuffer(GL_RENDERBUFFER, rb);
+ //* gl->glDeleteRenderbuffers(1, &rb);
+ //* gl->glBindFramebuffer(GL_FRAMEBUFFER, m_activeFBO);
+ //* gl->glDeleteFramebuffers(1, &fbo);
+ //* } else {
+ //* // read pixels directly from framebuffer
+ //* m_gl->functions()->glReadPixels(rect.x(), rect.y(), rect.width(), rect.height(),
+ //format, type, data.data());
+ //* copyGLFramebufferDataToImage(img, data.data(), stride, rect.width(), rect.height(),
+ //m_renderTargetFormat);
+ //* }
+ //*
+ //* return img;
+}
+
+void SubmissionContext::releaseResources()
+{
+ m_renderBufferHash.clear();
+ RHI_UNIMPLEMENTED;
+
+ // Free RHI resources
+ {
+ qCDebug(Backend) << Q_FUNC_INFO;
+
+ // We must ensure no remaining resource before deleting m_rhi.
+ m_renderer->rhiResourceManagers()->releaseAllResources();
+
+ auto it = m_swapChains.begin();
+ while (it != m_swapChains.end()) {
+ SwapChainInfo &swapChainInfo = it.value();
+ delete swapChainInfo.renderPassDescriptor;
+ delete swapChainInfo.renderBuffer;
+ delete swapChainInfo.swapChain;
+ it = m_swapChains.erase(it);
+ }
+
+ delete m_rhi;
+ m_rhi = nullptr;
+
+#ifndef QT_NO_OPENGL
+ delete m_fallbackSurface;
+ m_fallbackSurface = nullptr;
+#endif
+ }
+
+ //* // Stop and destroy the OpenGL logger
+ //* if (m_debugLogger) {
+ //* m_debugLogger->stopLogging();
+ //* m_debugLogger.reset(nullptr);
+ //* }
+}
+
+// Called only from RenderThread
+bool SubmissionContext::activateShader(RHIShader *shader)
+{
+ RHI_UNIMPLEMENTED;
+ //* if (shader->shaderProgram() != m_activeShader) {
+ //* // Ensure material uniforms are re-applied
+ //* m_material = nullptr;
+ //*
+ //* m_activeShader = shader->shaderProgram();
+ //* if (Q_LIKELY(m_activeShader != nullptr)) {
+ //* m_activeShader->bind();
+ //* } else {
+ //* m_glHelper->useProgram(0);
+ //* qWarning() << "No shader program found";
+ //* return false;
+ //* }
+ //* }
+ return true;
+}
+
+void SubmissionContext::bindFrameBufferAttachmentHelper(GLuint fboId,
+ const AttachmentPack &attachments)
+{
+ RHI_UNIMPLEMENTED;
+ // Set FBO attachments. These are normally textures, except that on Open GL
+ // ES <= 3.1 we must use a renderbuffer if a combined depth+stencil is
+ // desired since this cannot be achieved neither with a single texture (not
+ // before GLES 3.2) nor with separate textures (no suitable format for
+ // stencil before 3.1 with the appropriate extension).
+
+ //* QSize fboSize;
+ //* RHITextureManager *rhiTextureManager =
+ //m_renderer->rhiResourceManagers()->rhiTextureManager();
+ //* const auto attachments_ = attachments.attachments();
+ //* for (const Attachment &attachment : attachments_) {
+ //* RHITexture *rTex = rhiTextureManager->lookupResource(attachment.m_textureUuid);
+ //* if (!m_glHelper->frameBufferNeedsRenderBuffer(attachment)) {
+ //* QOpenGLTexture *glTex = rTex ? rTex->getGLTexture() : nullptr;
+ //* if (glTex != nullptr) {
+ //* // The texture can not be rendered simultaniously by another renderer
+ //* Q_ASSERT(!rTex->isExternalRenderingEnabled());
+ //* if (fboSize.isEmpty())
+ //* fboSize = QSize(glTex->width(), glTex->height());
+ //* else
+ //* fboSize = QSize(qMin(fboSize.width(), glTex->width()),
+ //qMin(fboSize.height(), glTex->height()));
+ //* m_glHelper->bindFrameBufferAttachment(glTex, attachment);
+ //* }
+ //* } else {
+ //* RenderBuffer *renderBuffer = rTex ? rTex->getOrCreateRenderBuffer() : nullptr;
+ //* if (renderBuffer) {
+ //* if (fboSize.isEmpty())
+ //* fboSize = QSize(renderBuffer->width(), renderBuffer->height());
+ //* else
+ //* fboSize = QSize(qMin(fboSize.width(), renderBuffer->width()),
+ //qMin(fboSize.height(), renderBuffer->height()));
+ //* m_glHelper->bindFrameBufferAttachment(renderBuffer, attachment);
+ //* }
+ //* }
+ //* }
+ //* m_renderTargetsSize.insert(fboId, fboSize);
+}
+
+void SubmissionContext::activateDrawBuffers(const AttachmentPack &attachments)
+{
+ RHI_UNIMPLEMENTED;
+ //* const QVector<int> activeDrawBuffers = attachments.getGlDrawBuffers();
+ //*
+ //* if (m_glHelper->checkFrameBufferComplete()) {
+ //* if (activeDrawBuffers.size() > 1) {// We need MRT
+ //* if (m_glHelper->supportsFeature(GraphicsHelperInterface::MRT)) {
+ //* // Set up MRT, glDrawBuffers...
+ //* m_glHelper->drawBuffers(activeDrawBuffers.size(), activeDrawBuffers.data());
+ //* }
+ //* }
+ //* } else {
+ //* qCWarning(Backend) << "FBO incomplete";
+ //* }
+}
+
+void SubmissionContext::setActiveMaterial(Material *rmat)
+{
+ if (m_material == rmat)
+ return;
+
+ // m_textureContext.deactivateTexturesWithScope(TextureSubmissionContext::TextureScopeMaterial);
+ m_material = rmat;
+}
+
+void SubmissionContext::applyState(const StateVariant &stateVariant,
+ QRhiGraphicsPipeline *graphicsPipeline)
+{
+ switch (stateVariant.type) {
+
+ case AlphaCoverageStateMask: {
+ applyStateHelper(static_cast<const AlphaCoverage *>(stateVariant.constState()),
+ graphicsPipeline);
+ break;
+ }
+ case AlphaTestMask: {
+ applyStateHelper(static_cast<const AlphaFunc *>(stateVariant.constState()),
+ graphicsPipeline);
+ break;
+ }
+ case BlendStateMask: {
+ applyStateHelper(static_cast<const BlendEquation *>(stateVariant.constState()),
+ graphicsPipeline);
+ break;
+ }
+ case BlendEquationArgumentsMask: {
+ applyStateHelper(static_cast<const BlendEquationArguments *>(stateVariant.constState()),
+ graphicsPipeline);
+ break;
+ }
+ case MSAAEnabledStateMask: {
+ applyStateHelper(static_cast<const MSAAEnabled *>(stateVariant.constState()),
+ graphicsPipeline, m_renderer->format());
+ break;
+ }
+
+ case CullFaceStateMask: {
+ applyStateHelper(static_cast<const CullFace *>(stateVariant.constState()),
+ graphicsPipeline);
+ break;
+ }
+
+ case DepthWriteStateMask: {
+ applyStateHelper(static_cast<const NoDepthMask *>(stateVariant.constState()),
+ graphicsPipeline);
+ break;
+ }
+
+ case DepthTestStateMask: {
+ applyStateHelper(static_cast<const DepthTest *>(stateVariant.constState()),
+ graphicsPipeline);
+ break;
+ }
+
+ case DepthRangeMask: {
+ applyStateHelper(static_cast<const DepthRange *>(stateVariant.constState()),
+ graphicsPipeline);
+ break;
+ }
+
+ case RasterModeMask: {
+ applyStateHelper(static_cast<const RasterMode *>(stateVariant.constState()),
+ graphicsPipeline);
+ break;
+ }
+
+ case FrontFaceStateMask: {
+ applyStateHelper(static_cast<const FrontFace *>(stateVariant.constState()),
+ graphicsPipeline);
+ break;
+ }
+
+ case ScissorStateMask: {
+ applyStateHelper(static_cast<const ScissorTest *>(stateVariant.constState()),
+ graphicsPipeline);
+ break;
+ }
+
+ case StencilTestStateMask: {
+ applyStateHelper(static_cast<const StencilTest *>(stateVariant.constState()),
+ graphicsPipeline);
+ break;
+ }
+
+ case PointSizeMask: {
+ applyStateHelper(static_cast<const PointSize *>(stateVariant.constState()),
+ graphicsPipeline);
+ break;
+ }
+
+ case PolygonOffsetStateMask: {
+ applyStateHelper(static_cast<const PolygonOffset *>(stateVariant.constState()),
+ graphicsPipeline);
+ break;
+ }
+
+ case ColorStateMask: {
+ applyStateHelper(static_cast<const ColorMask *>(stateVariant.constState()),
+ graphicsPipeline);
+ break;
+ }
+
+ case ClipPlaneMask: {
+ applyStateHelper(static_cast<const ClipPlane *>(stateVariant.constState()),
+ graphicsPipeline);
+ break;
+ }
+
+ case SeamlessCubemapMask: {
+ applyStateHelper(static_cast<const SeamlessCubemap *>(stateVariant.constState()),
+ graphicsPipeline);
+ break;
+ }
+
+ case StencilOpMask: {
+ applyStateHelper(static_cast<const StencilOp *>(stateVariant.constState()),
+ graphicsPipeline);
+ break;
+ }
+
+ case StencilWriteStateMask: {
+ applyStateHelper(static_cast<const StencilMask *>(stateVariant.constState()),
+ graphicsPipeline);
+ break;
+ }
+
+ case DitheringStateMask: {
+ applyStateHelper(static_cast<const Dithering *>(stateVariant.constState()),
+ graphicsPipeline);
+ break;
+ }
+
+ case LineWidthMask: {
+ applyStateHelper(static_cast<const LineWidth *>(stateVariant.constState()),
+ graphicsPipeline);
+ break;
+ }
+ default:
+ Q_UNREACHABLE();
+ }
+}
+
+void SubmissionContext::applyStateSet(const RenderStateSet *ss,
+ QRhiGraphicsPipeline *graphicsPipeline)
+{
+ // Set default state values on graphicsPipeline
+ graphicsPipeline->setDepthWrite(true);
+ graphicsPipeline->setDepthTest(false);
+ graphicsPipeline->setSampleCount(format().samples());
+
+ const QVector<StateVariant> statesToSet = ss->states();
+ for (const StateVariant &ds : statesToSet)
+ applyState(ds, graphicsPipeline);
+}
+
+StateVariant *SubmissionContext::getState(RenderStateSet *ss, StateMask type) const
+{
+ const QVector<StateVariant> &statesToSet = ss->states();
+ for (int i = 0, m = statesToSet.size(); i < m; ++i) {
+ const StateVariant &ds = statesToSet.at(i);
+ if (ds.type == type)
+ return ss->states().begin() + i;
+ }
+ return nullptr;
+}
+
+SubmissionContext::SwapChainInfo *SubmissionContext::swapChainForSurface(QSurface *surface) noexcept
+{
+ SwapChainInfo &swapChainInfo = m_swapChains[surface];
+ auto &swapChain = swapChainInfo.swapChain;
+
+ if (swapChain == nullptr) {
+ swapChain = m_rhi->newSwapChain();
+ Q_ASSERT(surface->surfaceClass() == QSurface::Window);
+ QWindow *window = static_cast<QWindow *>(surface);
+ Q_ASSERT(window != nullptr);
+ const int samples = format().samples();
+
+ swapChain->setWindow(window);
+ swapChain->setFlags(QRhiSwapChain::Flags {});
+ swapChain->setSampleCount(samples);
+
+ QRhiRenderBuffer *renderBuffer =
+ m_rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil, QSize(), samples,
+ QRhiRenderBuffer::UsedWithSwapChainOnly);
+ swapChain->setDepthStencil(renderBuffer);
+
+ QRhiRenderPassDescriptor *renderPassDescriptor =
+ swapChain->newCompatibleRenderPassDescriptor();
+ swapChain->setRenderPassDescriptor(renderPassDescriptor);
+
+ // Build swapChain the first time
+ if (swapChain->buildOrResize()) {
+ swapChainInfo.swapChain = swapChain;
+ swapChainInfo.renderBuffer = renderBuffer;
+ swapChainInfo.renderPassDescriptor = renderPassDescriptor;
+ } else {
+ swapChain->releaseAndDestroyLater();
+ m_swapChains.remove(surface);
+ return nullptr;
+ }
+ }
+ return &swapChainInfo;
+}
+
+QRhiCommandBuffer *SubmissionContext::currentFrameCommandBuffer() const
+{
+ return m_currentSwapChain->currentFrameCommandBuffer();
+}
+
+QRhiRenderTarget *SubmissionContext::currentFrameRenderTarget() const
+{
+ return m_currentSwapChain->currentFrameRenderTarget();
+}
+
+QRhiSwapChain *SubmissionContext::currentSwapChain() const
+{
+ return m_currentSwapChain;
+}
+
+QRhiRenderPassDescriptor *SubmissionContext::currentRenderPassDescriptor() const
+{
+ return m_currentRenderPassDescriptor;
+}
+
+QSurfaceFormat SubmissionContext::format() const noexcept
+{
+ if (this->m_rhi && this->m_rhi->backend() == QRhi::OpenGLES2) {
+ auto rhi_gl = static_cast<const QRhiGles2NativeHandles *>(this->m_rhi->nativeHandles());
+ return rhi_gl->context->format();
+ }
+ return QSurfaceFormat::defaultFormat();
+}
+
+// It will be easier if the QGraphicContext applies the QUniformPack
+// than the other way around
+bool SubmissionContext::setParameters(ShaderParameterPack &parameterPack)
+{
+ static const int irradianceId = StringToInt::lookupId(QLatin1String("envLight_irradiance"));
+ static const int specularId = StringToInt::lookupId(QLatin1String("envLight_specular"));
+ // Activate textures and update TextureUniform in the pack
+ // with the correct textureUnit
+
+ // Set the pinned texture of the previous material texture
+ // to pinable so that we should easily find an available texture unit
+ // m_textureContext.deactivateTexturesWithScope(TextureSubmissionContext::TextureScopeMaterial);
+ // Update the uniforms with the correct texture unit id's
+ PackUniformHash &uniformValues = parameterPack.uniforms();
+
+ // Fill Texture Uniform Value with proper texture units
+ // so that they can be applied as regular uniforms in a second step
+ for (int i = 0; i < parameterPack.textures().size(); ++i) {
+ RHI_UNIMPLEMENTED;
+ //* const ShaderParameterPack::NamedResource &namedTex = parameterPack.textures().at(i);
+ //* // Given a Texture QNodeId, we retrieve the associated shared RHITexture
+ //* if (uniformValues.contains(namedTex.glslNameId)) {
+ //* RHITexture *t =
+ //m_renderer->rhiResourceManagers()->rhiTextureManager()->lookupResource(namedTex.nodeId);
+ //* if (t != nullptr) {
+ //* UniformValue &texUniform = uniformValues.value(namedTex.glslNameId);
+ //* if (texUniform.valueType() == UniformValue::TextureValue) {
+ //* const int texUnit =
+ //m_textureContext.activateTexture(TextureSubmissionContext::TextureScopeMaterial, m_gl, t);
+ //* texUniform.data<int>()[namedTex.uniformArrayIndex] = texUnit;
+ //* if (texUnit == -1) {
+ //* if (namedTex.glslNameId != irradianceId &&
+ //* namedTex.glslNameId != specularId) {
+ //* // Only return false if we are not dealing with env light textures
+ //* qCWarning(Backend) << "Unable to find suitable Texture Unit";
+ //* return false;
+ //* }
+ //* }
+ //* }
+ //* }
+ //* }
+ }
+
+ RHIShader *shader = activeShader();
+
+ // TO DO: We could cache the binding points somehow and only do the binding when necessary
+ // for SSBO and UBO
+
+ // Bind Shader Storage block to SSBO and update SSBO
+ const QVector<BlockToSSBO> &blockToSSBOs = parameterPack.shaderStorageBuffers();
+ for (const BlockToSSBO &b : blockToSSBOs) {
+ RHI_UNIMPLEMENTED;
+ Buffer *cpuBuffer =
+ m_renderer->nodeManagers()->bufferManager()->lookupResource(b.m_bufferID);
+ RHIBuffer *ssbo = rhiBufferForRenderBuffer(cpuBuffer);
+ // bindShaderStorageBlock
+ // This is currently not required as we are introspecting the bindingIndex
+ // value from the shaders and not replacing them, making such a call useless
+ // bindShaderStorageBlock(shader->programId(), b.m_blockIndex, b.m_bindingIndex);
+ // bindShaderStorageBlock(shader->programId(), b.m_blockIndex, b.m_bindingIndex);
+ // Needed to avoid conflict where the buffer would already
+ // be bound as a VertexArray
+ bindRHIBuffer(ssbo, RHIBuffer::ShaderStorageBuffer);
+ ssbo->bindBufferBase(this, b.m_bindingIndex, RHIBuffer::ShaderStorageBuffer);
+ // TO DO: Make sure that there's enough binding points
+ }
+
+ // Bind UniformBlocks to UBO and update UBO from Buffer
+ // TO DO: Convert ShaderData to Buffer so that we can use that generic process
+ const QVector<BlockToUBO> blockToUBOs = parameterPack.uniformBuffers();
+ int uboIndex = 0;
+ for (const BlockToUBO &b : blockToUBOs) {
+ RHI_UNIMPLEMENTED;
+ Buffer *cpuBuffer =
+ m_renderer->nodeManagers()->bufferManager()->lookupResource(b.m_bufferID);
+ RHIBuffer *ubo = rhiBufferForRenderBuffer(cpuBuffer);
+ // bindUniformBlock(shader->programId(), b.m_blockIndex, uboIndex);
+ // Needed to avoid conflict where the buffer would already
+ // be bound as a VertexArray
+ bindRHIBuffer(ubo, RHIBuffer::UniformBuffer);
+ ubo->bindBufferBase(this, uboIndex++, RHIBuffer::UniformBuffer);
+ // TO DO: Make sure that there's enough binding points
+ }
+
+ // Update uniforms in the Default Uniform Block
+ const PackUniformHash values = parameterPack.uniforms();
+ const QVector<ShaderUniform> activeUniforms = parameterPack.submissionUniforms();
+
+ for (const ShaderUniform &uniform : activeUniforms) {
+ RHI_UNIMPLEMENTED;
+ // We can use [] as we are sure the the uniform wouldn't
+ // be un activeUniforms if there wasn't a matching value
+ const UniformValue &v = values.value(uniform.m_nameId);
+
+ // skip invalid textures/images
+ if ((v.valueType() == UniformValue::TextureValue
+ || v.valueType() == UniformValue::ShaderImageValue)
+ && *v.constData<int>() == -1)
+ continue;
+
+ // applyUniform(uniform, v);
+ }
+ // if not all data is valid, the next frame will be rendered immediately
+ return true;
+}
+
+void SubmissionContext::updateBuffer(Buffer *buffer)
+{
+ const QHash<Qt3DCore::QNodeId, HRHIBuffer>::iterator it =
+ m_renderBufferHash.find(buffer->peerId());
+ if (it != m_renderBufferHash.end())
+ uploadDataToRHIBuffer(
+ buffer, m_renderer->rhiResourceManagers()->rhiBufferManager()->data(it.value()));
+}
+
+QByteArray SubmissionContext::downloadBufferContent(Buffer *buffer)
+{
+ const QHash<Qt3DCore::QNodeId, HRHIBuffer>::iterator it =
+ m_renderBufferHash.find(buffer->peerId());
+ if (it != m_renderBufferHash.end())
+ return downloadDataFromRHIBuffer(
+ buffer, m_renderer->rhiResourceManagers()->rhiBufferManager()->data(it.value()));
+ return QByteArray();
+}
+
+void SubmissionContext::releaseBuffer(Qt3DCore::QNodeId bufferId)
+{
+ auto it = m_renderBufferHash.find(bufferId);
+ if (it != m_renderBufferHash.end()) {
+ HRHIBuffer glBuffHandle = it.value();
+ RHIBuffer *glBuff =
+ m_renderer->rhiResourceManagers()->rhiBufferManager()->data(glBuffHandle);
+
+ Q_ASSERT(glBuff);
+ // Destroy the GPU resource
+ glBuff->destroy(this);
+ // Destroy the RHIBuffer instance
+ m_renderer->rhiResourceManagers()->rhiBufferManager()->releaseResource(bufferId);
+ // Remove Id - HRHIBuffer entry
+ m_renderBufferHash.erase(it);
+ }
+}
+
+bool SubmissionContext::hasRHIBufferForBuffer(Buffer *buffer)
+{
+ const QHash<Qt3DCore::QNodeId, HRHIBuffer>::iterator it =
+ m_renderBufferHash.find(buffer->peerId());
+ return (it != m_renderBufferHash.end());
+}
+
+RHIBuffer *SubmissionContext::rhiBufferForRenderBuffer(Buffer *buf)
+{
+ if (!m_renderBufferHash.contains(buf->peerId()))
+ m_renderBufferHash.insert(buf->peerId(), createRHIBufferFor(buf));
+ return m_renderer->rhiResourceManagers()->rhiBufferManager()->data(
+ m_renderBufferHash.value(buf->peerId()));
+}
+
+HRHIBuffer SubmissionContext::createRHIBufferFor(Buffer *buffer)
+{
+ m_renderer->rhiResourceManagers()->rhiBufferManager()->getOrCreateResource(buffer->peerId());
+ return m_renderer->rhiResourceManagers()->rhiBufferManager()->lookupHandle(buffer->peerId());
+}
+
+bool SubmissionContext::bindRHIBuffer(RHIBuffer *buffer, RHIBuffer::Type type)
+{
+ return buffer->bind(this, type);
+}
+
+void SubmissionContext::uploadDataToRHIBuffer(Buffer *buffer, RHIBuffer *b, bool releaseBuffer)
+{
+ // If the buffer is dirty (hence being called here)
+ // there are two possible cases
+ // * setData was called changing the whole data or functor (or the usage pattern)
+ // * partial buffer updates where received
+
+ // Note: we are only storing the updates data CPU side at this point
+ // actually upload will be performed when the buffer will be bound
+ // as we would otherwise need to know the usage type of the buffer
+ QVector<Qt3DRender::QBufferUpdate> updates = std::move(buffer->pendingBufferUpdates());
+ for (auto it = updates.begin(); it != updates.end(); ++it) {
+ auto update = it;
+ // We have a partial update
+ if (update->offset >= 0) {
+ // accumulate sequential updates as single one
+ int bufferSize = update->data.size();
+ auto it2 = it + 1;
+ while ((it2 != updates.end()) && (it2->offset - update->offset == bufferSize)) {
+ bufferSize += it2->data.size();
+ ++it2;
+ }
+ update->data.resize(bufferSize);
+ while (it + 1 != it2) {
+ ++it;
+ update->data.replace(it->offset - update->offset, it->data.size(), it->data);
+ it->data.clear();
+ }
+ // TO DO: based on the number of updates .., it might make sense to
+ // sometime use glMapBuffer rather than glBufferSubData
+ b->update(this, update->data, update->offset);
+ } else {
+ // We have an update that was done by calling QBuffer::setData
+ // which is used to resize or entirely clear the buffer
+ // Note: we use the buffer data directly in that case
+ b->orphan(this); // orphan the buffer
+ b->allocate(this, buffer->data(), false);
+ }
+ }
+
+ if (releaseBuffer) {
+ b->release(this);
+ }
+ qCDebug(Io) << "uploaded buffer size=" << buffer->data().size();
+}
+
+QByteArray SubmissionContext::downloadDataFromRHIBuffer(Buffer *buffer, RHIBuffer *b)
+{
+ if (!bindRHIBuffer(b,
+ RHIBuffer::ArrayBuffer)) // We're downloading, the type doesn't matter here
+ qCWarning(Io) << Q_FUNC_INFO << "buffer bind failed";
+
+ return b->download(this, buffer->data().size());
+}
+
+void SubmissionContext::blitFramebuffer(Qt3DCore::QNodeId inputRenderTargetId,
+ Qt3DCore::QNodeId outputRenderTargetId, QRect inputRect,
+ QRect outputRect, uint defaultFboId,
+ QRenderTargetOutput::AttachmentPoint inputAttachmentPoint,
+ QRenderTargetOutput::AttachmentPoint outputAttachmentPoint,
+ QBlitFramebuffer::InterpolationMethod interpolationMethod)
+{
+ RHI_UNIMPLEMENTED;
+ //* GLuint inputFboId = defaultFboId;
+ //* bool inputBufferIsDefault = true;
+ //* if (!inputRenderTargetId.isNull()) {
+ //* RenderTarget *renderTarget =
+ //m_renderer->nodeManagers()->renderTargetManager()->lookupResource(inputRenderTargetId);
+ //* if (renderTarget) {
+ //* AttachmentPack attachments(renderTarget,
+ //m_renderer->nodeManagers()->attachmentManager());
+ //* if (m_renderTargets.contains(inputRenderTargetId))
+ //* inputFboId = updateRenderTarget(inputRenderTargetId, attachments, false);
+ //* else
+ //* inputFboId = createRenderTarget(inputRenderTargetId, attachments);
+ //* }
+ //* inputBufferIsDefault = false;
+ //* }
+ //*
+ //* GLuint outputFboId = defaultFboId;
+ //* bool outputBufferIsDefault = true;
+ //* if (!outputRenderTargetId.isNull()) {
+ //* RenderTarget *renderTarget =
+ //m_renderer->nodeManagers()->renderTargetManager()->lookupResource(outputRenderTargetId);
+ //* if (renderTarget) {
+ //* AttachmentPack attachments(renderTarget,
+ //m_renderer->nodeManagers()->attachmentManager());
+ //* if (m_renderTargets.contains(outputRenderTargetId))
+ //* outputFboId = updateRenderTarget(outputRenderTargetId, attachments, false);
+ //* else
+ //* outputFboId = createRenderTarget(outputRenderTargetId, attachments);
+ //* }
+ //* outputBufferIsDefault = false;
+ //* }
+ //*
+ //* // Up until this point the input and output rects are normal Qt rectangles.
+ //* // Convert them to GL rectangles (Y at bottom).
+ //* const int inputFboHeight = inputFboId == defaultFboId ? m_surfaceSize.height() :
+ //m_renderTargetsSize[inputFboId].height();
+ //* const GLint srcX0 = inputRect.left();
+ //* const GLint srcY0 = inputFboHeight - (inputRect.top() + inputRect.height());
+ //* const GLint srcX1 = srcX0 + inputRect.width();
+ //* const GLint srcY1 = srcY0 + inputRect.height();
+ //*
+ //* const int outputFboHeight = outputFboId == defaultFboId ? m_surfaceSize.height() :
+ //m_renderTargetsSize[outputFboId].height();
+ //* const GLint dstX0 = outputRect.left();
+ //* const GLint dstY0 = outputFboHeight - (outputRect.top() + outputRect.height());
+ //* const GLint dstX1 = dstX0 + outputRect.width();
+ //* const GLint dstY1 = dstY0 + outputRect.height();
+ //*
+ //* //Get the last bounded framebuffers
+ //* const GLuint lastDrawFboId = boundFrameBufferObject();
+ //*
+ //* // Activate input framebuffer for reading
+ //* bindFramebuffer(inputFboId, GraphicsHelperInterface::FBORead);
+ //*
+ //* // Activate output framebuffer for writing
+ //* bindFramebuffer(outputFboId, GraphicsHelperInterface::FBODraw);
+ //*
+ //* //Bind texture
+ //* if (!inputBufferIsDefault)
+ //* readBuffer(GL_COLOR_ATTACHMENT0 + inputAttachmentPoint);
+ //*
+ //* if (!outputBufferIsDefault) {
+ //* // Note that we use glDrawBuffers, not glDrawBuffer. The
+ //* // latter is not available with GLES.
+ //* const int buf = outputAttachmentPoint;
+ //* drawBuffers(1, &buf);
+ //* }
+ //*
+ //* // Blit framebuffer
+ //* const GLenum mode = interpolationMethod ? GL_NEAREST : GL_LINEAR;
+ //* m_glHelper->blitFramebuffer(srcX0, srcY0, srcX1, srcY1,
+ //* dstX0, dstY0, dstX1, dstY1,
+ //* GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT,
+ //* mode);
+ //*
+ //* // Reset draw buffer
+ //* bindFramebuffer(lastDrawFboId, GraphicsHelperInterface::FBOReadAndDraw);
+ //* if (outputAttachmentPoint != QRenderTargetOutput::Color0) {
+ //* const int buf = QRenderTargetOutput::Color0;
+ //* drawBuffers(1, &buf);
+ //* }
+}
+
+namespace {
+template<std::size_t N>
+constexpr int getFirstAvailableBit(const std::bitset<N> &bits)
+{
+ for (std::size_t i = 0; i < N; i++) {
+ if (!bits.test(i))
+ return i;
+ }
+ return -1;
+}
+// This function ensures that the shader stages all have the same bindings
+void preprocessRHIShader(QVector<QByteArray> &shaderCodes)
+{
+ // Map the variable names to bindings
+ std::map<QByteArray, int> bindings;
+ bindings["qt3d_render_view_uniforms"] = 0;
+ bindings["qt3d_command_uniforms"] = 1;
+ std::bitset<512> assignedBindings;
+ assignedBindings.set(0);
+ assignedBindings.set(1);
+
+ thread_local const QRegularExpression samplerRegex(
+ QStringLiteral("binding\\s*=\\s*([0-9]+).*\\)\\s*uniform\\s*[ui]?sampler[a-zA-Z0-9]+"
+ "\\s*([a-zA-Z0-9_]+)\\s*;"));
+ thread_local const QRegularExpression uboRegex(
+ QStringLiteral("(?:std140\\s*,\\s*binding\\s*=\\s*([0-9]+).*|binding\\s*=\\s*([0-9]+)"
+ "\\s*,\\s*std140.*)\\)\\s*uniform\\s*([a-zA-Z0-9_]+)"));
+
+ auto replaceBinding = [&bindings, &assignedBindings](
+ int &offset, QRegularExpressionMatch &match, QByteArray &code,
+ int indexCapture, int variableCapture) noexcept {
+ int index = match.captured(indexCapture).toInt();
+ QByteArray variable = match.captured(variableCapture).toUtf8();
+
+ auto it = bindings.find(variable);
+ if (it == bindings.end()) {
+ // 1. Check if the index is already used
+ if (assignedBindings.test(index)) {
+ index = getFirstAvailableBit(assignedBindings);
+ if (index == -1) {
+ return;
+ }
+
+ const int indexStartOffset = match.capturedStart(indexCapture);
+ const int indexEndOffset = match.capturedEnd(indexCapture);
+ const int indexLength = indexEndOffset - indexStartOffset;
+ code.replace(indexStartOffset, indexLength, QByteArray::number(index));
+ }
+
+ assignedBindings.set(index);
+ bindings.emplace(std::move(variable), index);
+ } else {
+ int indexToUse = it->second;
+ const int indexStartOffset = match.capturedStart(indexCapture);
+ const int indexEndOffset = match.capturedEnd(indexCapture);
+ const int indexLength = indexEndOffset - indexStartOffset;
+ code.replace(indexStartOffset, indexLength, QByteArray::number(indexToUse));
+ }
+ // This may fail in the case where the replaced offset is an incredibly long number,
+ // which seems quite unlikely
+ offset = match.capturedEnd(0);
+ };
+
+ for (QByteArray &shaderCode : shaderCodes) {
+ // Regex for the sampler variables
+ int offset = 0;
+ auto match = samplerRegex.match(shaderCode, offset);
+ while (match.hasMatch()) {
+ const int indexCapture = 1;
+ const int variableCapture = 2;
+ replaceBinding(offset, match, shaderCode, indexCapture, variableCapture);
+
+ match = samplerRegex.match(shaderCode, offset);
+ }
+
+ // Regex for the UBOs
+ offset = 0;
+ match = uboRegex.match(shaderCode, offset);
+ while (match.hasMatch()) {
+ const int indexCapture = !match.capturedView(1).isEmpty() ? 1 : 2;
+ const int variableCapture = 3;
+ replaceBinding(offset, match, shaderCode, indexCapture, variableCapture);
+
+ match = uboRegex.match(shaderCode, offset);
+ }
+ }
+}
+
+int glslVersionForFormat(const QSurfaceFormat &format) noexcept
+{
+ const int major = format.majorVersion();
+ const int minor = format.minorVersion();
+
+ static const QHash<std::pair<int, int>, int> glVersionToGLSLVersion = {
+ { { 4, 6 }, 460 }, { { 4, 5 }, 450 }, { { 4, 4 }, 440 }, { { 4, 3 }, 430 },
+ { { 4, 2 }, 420 }, { { 4, 1 }, 410 }, { { 4, 0 }, 400 }, { { 3, 3 }, 330 },
+ { { 3, 2 }, 150 }, { { 3, 2 }, 120 }, { { 3, 1 }, 120 },
+ };
+
+ const auto it = glVersionToGLSLVersion.find({ major, minor });
+ if (it == glVersionToGLSLVersion.end()) {
+ if (major < 3) {
+ return 120;
+ } else {
+ return major * 100 + minor * 10;
+ }
+ } else {
+ return *it;
+ }
+}
+}
+
+// Called by GL Command Thread
+SubmissionContext::ShaderCreationInfo SubmissionContext::createShaderProgram(RHIShader *shader)
+{
+ // Compile shaders
+ const auto &shaderCode = shader->shaderCode();
+ QShaderBaker b;
+ b.setGeneratedShaders({
+ { QShader::SpirvShader, 100 },
+#ifndef QT_NO_OPENGL
+ { QShader::GlslShader, glslVersionForFormat(format()) },
+#endif
+ { QShader::HlslShader, QShaderVersion(50) },
+ { QShader::MslShader, QShaderVersion(12) },
+ });
+
+ b.setGeneratedShaderVariants({ QShader::Variant {},
+#ifndef QT_NO_OPENGL
+ QShader::Variant {},
+#endif
+ QShader::Variant {}, QShader::Variant {} });
+
+ // TODO handle caching as QShader does not have a built-in mechanism for that
+ QString logs;
+ bool success = true;
+ for (int i = QShaderProgram::Vertex; i <= QShaderProgram::Compute; ++i) {
+ const QShaderProgram::ShaderType type = static_cast<QShaderProgram::ShaderType>(i);
+ if (!shaderCode.at(i).isEmpty()) {
+ // Note: logs only return the error but not all the shader code
+ // we could append it
+
+ const auto rhiStage = rhiShaderStage(type);
+ b.setSourceString(shaderCode.at(i), rhiStage);
+ QShader bakedShader = b.bake();
+ if (b.errorMessage() != QString() || !bakedShader.isValid()) {
+ qDebug() << "Shader Error: " << b.errorMessage() << shaderCode.at(i).data()
+ << rhiStage;
+ logs += b.errorMessage();
+ success = false;
+ }
+ shader->m_stages[rhiStage] = std::move(bakedShader);
+ }
+ }
+
+ // Perform shader introspection
+ if (success)
+ shader->introspect();
+
+ return { success, logs };
+}
+
+// Called by Renderer::updateResources
+void SubmissionContext::loadShader(Shader *shaderNode, ShaderManager *shaderManager,
+ RHIShaderManager *rhiShaderManager)
+{
+ const Qt3DCore::QNodeId shaderId = shaderNode->peerId();
+ RHIShader *rhiShader = rhiShaderManager->lookupResource(shaderId);
+
+ // We already have a shader associated with the node
+ if (rhiShader != nullptr) {
+ // We need to abandon it
+ rhiShaderManager->abandon(rhiShader, shaderNode);
+ }
+
+ // We create or adopt an already created rhiShader
+ rhiShader = rhiShaderManager->createOrAdoptExisting(shaderNode);
+
+ const QVector<Qt3DCore::QNodeId> sharedShaderIds =
+ rhiShaderManager->shaderIdsForProgram(rhiShader);
+ if (sharedShaderIds.size() == 1) {
+ // Shader in the cache hasn't been loaded yet
+ QVector<QByteArray> shaderCodes = shaderNode->shaderCode();
+ preprocessRHIShader(shaderCodes);
+ rhiShader->setShaderCode(shaderCodes);
+
+ const ShaderCreationInfo loadResult = createShaderProgram(rhiShader);
+ shaderNode->setStatus(loadResult.linkSucceeded ? QShaderProgram::Ready
+ : QShaderProgram::Error);
+ shaderNode->setLog(loadResult.logs);
+ // Loaded in the sense we tried to load it (and maybe it failed)
+ rhiShader->setLoaded(true);
+ } else {
+ // Find an already loaded shader that shares the same QShaderProgram
+ for (const Qt3DCore::QNodeId &sharedShaderId : sharedShaderIds) {
+ if (sharedShaderId != shaderNode->peerId()) {
+ Shader *refShader = shaderManager->lookupResource(sharedShaderId);
+ // We only introspect once per actual OpenGL shader program
+ // rather than once per ShaderNode.
+ shaderNode->initializeFromReference(*refShader);
+ break;
+ }
+ }
+ }
+ shaderNode->unsetDirty();
+ // Ensure we will rebuilt material caches
+ shaderNode->requestCacheRebuild();
+}
+
+const GraphicsApiFilterData *SubmissionContext::contextInfo() const
+{
+ return &m_contextInfo;
+}
+
+} // namespace Rhi
+} // namespace Render
+} // namespace Qt3DRender of namespace
+
+QT_END_NAMESPACE
diff --git a/src/plugins/renderers/rhi/graphicshelpers/submissioncontext_p.h b/src/plugins/renderers/rhi/graphicshelpers/submissioncontext_p.h
new file mode 100644
index 000000000..7578639e6
--- /dev/null
+++ b/src/plugins/renderers/rhi/graphicshelpers/submissioncontext_p.h
@@ -0,0 +1,266 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 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_RHI_SUBMISSIONCONTEXT_H
+#define QT3DRENDER_RENDER_RHI_SUBMISSIONCONTEXT_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 <rhibuffer_p.h>
+#include <Qt3DRender/qclearbuffers.h>
+#include <Qt3DRender/qattribute.h>
+#include <Qt3DRender/private/handle_types_p.h>
+#include <Qt3DRender/qblitframebuffer.h>
+#include <Qt3DRender/private/shader_p.h>
+#include <Qt3DRender/private/statemask_p.h>
+#include <Qt3DRender/private/qgraphicsapifilter_p.h>
+#include <shaderparameterpack_p.h>
+#include <shadervariables_p.h>
+#include <rhihandle_types_p.h>
+#include <QSurface>
+#include <QtGui/private/qrhi_p.h>
+#include <QOffscreenSurface>
+
+QT_BEGIN_NAMESPACE
+
+class QAbstractOpenGLFunctions;
+
+namespace Qt3DRender {
+
+namespace Render {
+
+class Material;
+class AttachmentPack;
+class Attribute;
+class Buffer;
+class ShaderManager;
+struct StateVariant;
+class RenderTarget;
+class RenderStateSet;
+
+namespace Rhi {
+
+class Renderer;
+class GraphicsHelperInterface;
+class RHITexture;
+class RHIShader;
+class RHIShaderManager;
+class RenderCommand;
+
+typedef QPair<QString, int> NamedUniformLocation;
+
+class Q_AUTOTEST_EXPORT SubmissionContext
+{
+public:
+ enum Feature {
+ MRT = 0,
+ Tessellation,
+ UniformBufferObject,
+ BindableFragmentOutputs,
+ PrimitiveRestart,
+ RenderBufferDimensionRetrieval,
+ TextureDimensionRetrieval,
+ ShaderStorageObject,
+ Compute,
+ DrawBuffersBlend,
+ BlitFramebuffer,
+ IndirectDrawing,
+ MapBuffer,
+ Fences,
+ ShaderImage
+ };
+
+ enum FBOBindMode { FBODraw, FBORead, FBOReadAndDraw };
+
+ SubmissionContext();
+ ~SubmissionContext();
+
+ void initialize();
+ int id() const; // unique, small integer ID of this context
+ void setRenderer(Renderer *renderer) { m_renderer = renderer; }
+
+ bool beginDrawing(QSurface *surface);
+ void endDrawing(bool swapBuffers);
+ void releaseResources();
+ void setOpenGLContext(QOpenGLContext *ctx);
+ bool isInitialized() const { return m_initialized; }
+ const GraphicsApiFilterData *contextInfo() const;
+
+ // Shaders
+ struct ShaderCreationInfo
+ {
+ bool linkSucceeded = false;
+ QString logs;
+ };
+
+ ShaderCreationInfo createShaderProgram(RHIShader *shaderNode);
+ void loadShader(Shader *shader, ShaderManager *shaderManager,
+ RHIShaderManager *rhiShaderManager);
+
+ GLuint defaultFBO() const { return m_defaultFBO; }
+
+ // Shaders
+ bool activateShader(RHIShader *shader);
+ RHIShader *activeShader() const { return m_activeShader; }
+
+ // FBO
+ GLuint activeFBO() const { return m_activeFBO; }
+ void activateRenderTarget(const Qt3DCore::QNodeId id, const AttachmentPack &attachments,
+ GLuint defaultFboId);
+ QSize renderTargetSize(const QSize &surfaceSize) const;
+ QImage readFramebuffer(const QRect &rect);
+ void blitFramebuffer(Qt3DCore::QNodeId outputRenderTargetId,
+ Qt3DCore::QNodeId inputRenderTargetId, QRect inputRect, QRect outputRect,
+ uint defaultFboId,
+ QRenderTargetOutput::AttachmentPoint inputAttachmentPoint,
+ QRenderTargetOutput::AttachmentPoint outputAttachmentPoint,
+ QBlitFramebuffer::InterpolationMethod interpolationMethod);
+
+ // Attributes
+ void specifyAttribute(const Attribute *attribute, Buffer *buffer,
+ const ShaderAttribute *attributeDescription);
+ void specifyIndices(Buffer *buffer);
+
+ // Buffer
+ void updateBuffer(Buffer *buffer);
+ QByteArray downloadBufferContent(Buffer *buffer);
+ void releaseBuffer(Qt3DCore::QNodeId bufferId);
+ bool hasRHIBufferForBuffer(Buffer *buffer);
+ RHIBuffer *rhiBufferForRenderBuffer(Buffer *buf);
+
+ // Parameters
+ bool setParameters(ShaderParameterPack &parameterPack);
+
+ // RenderState
+ void applyStateSet(const RenderStateSet *ss, QRhiGraphicsPipeline *graphicsPipeline);
+ StateVariant *getState(RenderStateSet *ss, StateMask type) const;
+
+ // Swap chain
+
+ struct SwapChainInfo
+ {
+ QRhiSwapChain *swapChain = nullptr;
+ QRhiRenderBuffer *renderBuffer = nullptr;
+ QRhiRenderPassDescriptor *renderPassDescriptor = nullptr;
+ };
+ SwapChainInfo *swapChainForSurface(QSurface *surface) noexcept;
+
+ QRhiResourceUpdateBatch *m_currentUpdates {};
+
+ QRhi *rhi() const { return m_rhi; }
+ QRhiCommandBuffer *currentFrameCommandBuffer() const;
+ QRhiRenderTarget *currentFrameRenderTarget() const;
+ QRhiRenderPassDescriptor *currentRenderPassDescriptor() const;
+ QRhiSwapChain *currentSwapChain() const;
+ QSurfaceFormat format() const noexcept;
+
+private:
+ // Material
+ Material *activeMaterial() const { return m_material; }
+ void setActiveMaterial(Material *rmat);
+
+ // FBO
+ void bindFrameBufferAttachmentHelper(GLuint fboId, const AttachmentPack &attachments);
+ void activateDrawBuffers(const AttachmentPack &attachments);
+ void resolveRenderTargetFormat();
+ GLuint createRenderTarget(Qt3DCore::QNodeId renderTargetNodeId,
+ const AttachmentPack &attachments);
+ GLuint updateRenderTarget(Qt3DCore::QNodeId renderTargetNodeId,
+ const AttachmentPack &attachments, bool isActiveRenderTarget);
+
+ // Buffers
+ HRHIBuffer createRHIBufferFor(Buffer *buffer);
+ void uploadDataToRHIBuffer(Buffer *buffer, RHIBuffer *b, bool releaseBuffer = false);
+ QByteArray downloadDataFromRHIBuffer(Buffer *buffer, RHIBuffer *b);
+ bool bindRHIBuffer(RHIBuffer *buffer, RHIBuffer::Type type);
+
+ // States
+ void applyState(const StateVariant &state, QRhiGraphicsPipeline *graphicsPipeline);
+
+ bool m_ownCurrent;
+ const unsigned int m_id;
+ QSurface *m_surface;
+ QSize m_surfaceSize;
+
+ RHIShader *m_activeShader;
+
+ QHash<Qt3DCore::QNodeId, HRHIBuffer> m_renderBufferHash;
+ QHash<Qt3DCore::QNodeId, GLuint> m_renderTargets;
+ QHash<GLuint, QSize> m_renderTargetsSize;
+ QAbstractTexture::TextureFormat m_renderTargetFormat;
+
+ Material *m_material;
+ GLuint m_activeFBO;
+
+ Renderer *m_renderer;
+ QByteArray m_uboTempArray;
+
+ bool m_initialized;
+
+ GLint m_maxTextureUnits;
+ GLuint m_defaultFBO;
+ GraphicsApiFilterData m_contextInfo;
+
+ QRhi *m_rhi;
+ QHash<QSurface *, SwapChainInfo> m_swapChains;
+ QRhiSwapChain *m_currentSwapChain;
+ QRhiRenderPassDescriptor *m_currentRenderPassDescriptor;
+
+#ifndef QT_NO_OPENGL
+ QOffscreenSurface *m_fallbackSurface;
+#endif
+};
+
+} // namespace Rhi
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDER_RHI_SUBMISSIONCONTEXT_H
diff --git a/src/plugins/renderers/rhi/io/io.pri b/src/plugins/renderers/rhi/io/io.pri
new file mode 100644
index 000000000..bac06ba95
--- /dev/null
+++ b/src/plugins/renderers/rhi/io/io.pri
@@ -0,0 +1,8 @@
+INCLUDEPATH += $$PWD
+
+SOURCES += \
+ $$PWD/rhibuffer.cpp
+
+HEADERS += \
+ $$PWD/rhibuffer_p.h
+
diff --git a/src/plugins/renderers/rhi/io/rhibuffer.cpp b/src/plugins/renderers/rhi/io/rhibuffer.cpp
new file mode 100644
index 000000000..7b63b01a2
--- /dev/null
+++ b/src/plugins/renderers/rhi/io/rhibuffer.cpp
@@ -0,0 +1,199 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "rhibuffer_p.h"
+#include <submissioncontext_p.h>
+#include <QtGui/private/qrhi_p.h>
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+namespace Render {
+
+namespace Rhi {
+
+namespace {
+QRhiBuffer::UsageFlag bufferTypeToRhi(RHIBuffer::Type t)
+{
+ switch (t) {
+ case RHIBuffer::Type::ArrayBuffer:
+ return QRhiBuffer::VertexBuffer;
+ case RHIBuffer::Type::IndexBuffer:
+ return QRhiBuffer::IndexBuffer;
+ case RHIBuffer::Type::UniformBuffer:
+ return QRhiBuffer::UniformBuffer;
+ default:
+ RHI_UNIMPLEMENTED;
+ return QRhiBuffer::StorageBuffer;
+ }
+}
+}
+
+// A UBO is created for each ShaderData Shader Pair
+// That means a UBO is unique to a shader/shaderdata
+
+RHIBuffer::RHIBuffer() : m_bufferId(0), m_dynamic(true), m_lastTarget(GL_ARRAY_BUFFER) { }
+
+bool RHIBuffer::bind(SubmissionContext *ctx, Type t)
+{
+ assert(ctx->m_currentUpdates);
+ if (this->m_datasToUpload.empty())
+ return bool(m_rhiBuffer);
+
+ const auto uploadMethod = m_dynamic ? &QRhiResourceUpdateBatch::updateDynamicBuffer
+ : qOverload<QRhiBuffer *, int, int, const void *>(
+ &QRhiResourceUpdateBatch::uploadStaticBuffer);
+ if (!m_rhiBuffer) {
+ if (m_allocSize <= 0)
+ return false;
+
+ const auto kind = m_dynamic ? QRhiBuffer::Dynamic : QRhiBuffer::Static;
+ const auto usage = bufferTypeToRhi(t);
+
+ // RHI does not seem to support using the same buffer with different types
+ if (m_rhiBuffer)
+ assert(m_rhiBuffer->usage() == usage);
+
+ if (!m_rhiBuffer)
+ m_rhiBuffer = ctx->rhi()->newBuffer(kind, usage, m_allocSize);
+ assert(m_rhiBuffer);
+
+ m_rhiBuffer->build();
+#if defined(QT_DEBUG)
+ {
+ // for debug: we set the buffer to zero
+ auto ptr = new char[m_allocSize] {};
+ (ctx->m_currentUpdates->*uploadMethod)(m_rhiBuffer, 0, m_allocSize, ptr);
+ delete[] ptr;
+ }
+#endif
+ }
+
+ for (const std::pair<QByteArray, int> &pair : this->m_datasToUpload) {
+ const QByteArray &data = pair.first;
+ int offset = pair.second;
+ (ctx->m_currentUpdates->*uploadMethod)(m_rhiBuffer, offset, data.size(), data.constData());
+ }
+
+ m_datasToUpload.clear();
+ return true;
+}
+
+bool RHIBuffer::release(SubmissionContext *ctx)
+{
+ if (m_rhiBuffer)
+ m_rhiBuffer->release();
+ return true;
+}
+
+bool RHIBuffer::create(SubmissionContext *ctx)
+{
+ return true;
+}
+
+void RHIBuffer::destroy(SubmissionContext *ctx)
+{
+ if (m_rhiBuffer) {
+ m_rhiBuffer->releaseAndDestroyLater();
+ m_rhiBuffer = nullptr;
+ }
+ m_allocSize = 0;
+}
+
+void RHIBuffer::orphan(SubmissionContext *)
+{
+ m_datasToUpload.clear();
+ if (m_rhiBuffer) {
+ m_rhiBuffer->releaseAndDestroyLater();
+ m_rhiBuffer = nullptr;
+ }
+ m_allocSize = 0;
+}
+
+void RHIBuffer::allocate(SubmissionContext *ctx, const QByteArray &data, bool dynamic)
+{
+ m_datasToUpload.clear();
+ m_datasToUpload.push_back({ data, 0 });
+ m_allocSize = data.size();
+ m_dynamic = dynamic;
+}
+
+void RHIBuffer::update(SubmissionContext *ctx, const QByteArray &data, int offset)
+{
+ m_datasToUpload.push_back({ data, offset });
+}
+
+QByteArray RHIBuffer::download(SubmissionContext *ctx, uint size)
+{
+ RHI_UNIMPLEMENTED;
+ // char *gpu_ptr = ctx->mapBuffer(m_lastTarget, size);
+ // QByteArray data;
+ // if (gpu_ptr != nullptr) {
+ // data.resize(size);
+ // std::copy(gpu_ptr, gpu_ptr+size, data.data());
+ // }
+ // ctx->unmapBuffer(m_lastTarget);
+ // return data;
+ return {};
+}
+
+void RHIBuffer::bindBufferBase(SubmissionContext *ctx, int bindingPoint, RHIBuffer::Type t)
+{
+ RHI_UNIMPLEMENTED;
+ // ctx->bindBufferBase(glBufferTypes[t], bindingPoint, m_bufferId);
+}
+
+void RHIBuffer::bindBufferBase(SubmissionContext *ctx, int bindingPoint)
+{
+ RHI_UNIMPLEMENTED;
+ // ctx->bindBufferBase(m_lastTarget, bindingPoint, m_bufferId);
+}
+
+void RHIBuffer::cleanup()
+{
+ destroy(nullptr);
+}
+
+} // namespace Rhi
+
+} // namespace Render
+
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
diff --git a/src/plugins/renderers/rhi/io/rhibuffer_p.h b/src/plugins/renderers/rhi/io/rhibuffer_p.h
new file mode 100644
index 000000000..4616c4111
--- /dev/null
+++ b/src/plugins/renderers/rhi/io/rhibuffer_p.h
@@ -0,0 +1,116 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DRENDER_RENDER_RHI_RHIBUFFER_P_H
+#define QT3DRENDER_RENDER_RHI_RHIBUFFER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <Qt3DCore/qnodeid.h>
+#include <qbytearray.h>
+
+QT_BEGIN_NAMESPACE
+class QRhiBuffer;
+namespace Qt3DRender {
+
+namespace Render {
+
+namespace Rhi {
+
+class SubmissionContext;
+
+class RHIBuffer
+{
+public:
+ RHIBuffer();
+
+ enum Type {
+ ArrayBuffer = 0,
+ UniformBuffer,
+ IndexBuffer,
+ ShaderStorageBuffer,
+ PixelPackBuffer,
+ PixelUnpackBuffer,
+ DrawIndirectBuffer
+ };
+
+ bool bind(SubmissionContext *ctx, Type t);
+ bool release(SubmissionContext *ctx);
+ bool create(SubmissionContext *ctx);
+ void destroy(SubmissionContext *ctx);
+ void orphan(SubmissionContext *ctx);
+ void allocate(SubmissionContext *ctx, const QByteArray &data, bool dynamic = true);
+ void update(SubmissionContext *ctx, const QByteArray &data, int offset = 0);
+ QByteArray download(SubmissionContext *ctx, uint size);
+ void bindBufferBase(SubmissionContext *ctx, int bindingPoint, Type t);
+ void bindBufferBase(SubmissionContext *ctx, int bindingPoint);
+
+ void cleanup();
+
+ QRhiBuffer *rhiBuffer() const noexcept { return m_rhiBuffer; }
+
+private:
+ uint m_bufferId;
+ bool m_dynamic;
+ int m_allocSize {};
+ int m_lastTarget;
+
+ QRhiBuffer *m_rhiBuffer {};
+
+ std::vector<std::pair<QByteArray /*data*/, int /*offset*/>> m_datasToUpload;
+};
+
+} // namespace Rhi
+
+} // namespace Render
+
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDER_RHI_RHIBUFFER_P_H
diff --git a/src/plugins/renderers/rhi/jobs/filtercompatibletechniquejob.cpp b/src/plugins/renderers/rhi/jobs/filtercompatibletechniquejob.cpp
new file mode 100644
index 000000000..657e5eea7
--- /dev/null
+++ b/src/plugins/renderers/rhi/jobs/filtercompatibletechniquejob.cpp
@@ -0,0 +1,97 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "filtercompatibletechniquejob_p.h"
+#include <Qt3DRender/private/techniquemanager_p.h>
+#include <Qt3DRender/private/nodemanagers_p.h>
+#include <Qt3DRender/private/job_common_p.h>
+#include <renderer_p.h>
+#include <submissioncontext_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+namespace Render {
+namespace Rhi {
+
+FilterCompatibleTechniqueJob::FilterCompatibleTechniqueJob()
+ : m_manager(nullptr), m_renderer(nullptr)
+{
+ SET_JOB_RUN_STAT_TYPE(this, JobTypes::FilterCompatibleTechniques, 0)
+}
+
+void FilterCompatibleTechniqueJob::setManager(TechniqueManager *manager)
+{
+ m_manager = manager;
+}
+
+TechniqueManager *FilterCompatibleTechniqueJob::manager() const
+{
+ return m_manager;
+}
+
+void FilterCompatibleTechniqueJob::setRenderer(Renderer *renderer)
+{
+ m_renderer = renderer;
+}
+
+Renderer *FilterCompatibleTechniqueJob::renderer() const
+{
+ return m_renderer;
+}
+
+void FilterCompatibleTechniqueJob::run()
+{
+ Q_ASSERT(m_manager != nullptr && m_renderer != nullptr);
+ Q_ASSERT(m_renderer->isRunning() && m_renderer->submissionContext()->isInitialized());
+
+ const QVector<Qt3DCore::QNodeId> dirtyTechniqueIds = m_manager->takeDirtyTechniques();
+ for (const Qt3DCore::QNodeId techniqueId : dirtyTechniqueIds) {
+ Technique *technique = m_manager->lookupResource(techniqueId);
+ if (Q_LIKELY(technique != nullptr))
+ technique->setCompatibleWithRenderer(
+ (*m_renderer->contextInfo() == *technique->graphicsApiFilter()));
+ }
+}
+
+} // namespace Rhi
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
diff --git a/src/plugins/renderers/rhi/jobs/filtercompatibletechniquejob_p.h b/src/plugins/renderers/rhi/jobs/filtercompatibletechniquejob_p.h
new file mode 100644
index 000000000..726ed9faa
--- /dev/null
+++ b/src/plugins/renderers/rhi/jobs/filtercompatibletechniquejob_p.h
@@ -0,0 +1,96 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DRENDER_RENDER_RHI_FILTERCOMPATIBLETECHNIQUEJOB_H
+#define QT3DRENDER_RENDER_RHI_FILTERCOMPATIBLETECHNIQUEJOB_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <Qt3DCore/qaspectjob.h>
+#include <Qt3DRender/private/qt3drender_global_p.h>
+
+#include <QSharedPointer>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+namespace Render {
+
+class TechniqueManager;
+
+namespace Rhi {
+
+class Renderer;
+
+class Q_AUTOTEST_EXPORT FilterCompatibleTechniqueJob : public Qt3DCore::QAspectJob
+{
+public:
+ FilterCompatibleTechniqueJob();
+
+ void setManager(TechniqueManager *managers);
+ TechniqueManager *manager() const;
+
+ void setRenderer(Renderer *renderer);
+ Renderer *renderer() const;
+
+ void run() override;
+
+private:
+ TechniqueManager *m_manager;
+ Renderer *m_renderer;
+};
+
+typedef QSharedPointer<FilterCompatibleTechniqueJob> FilterCompatibleTechniqueJobPtr;
+
+} // namespace Rhi
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDER_RHI_FILTERCOMPATIBLETECHNIQUEJOB_H
diff --git a/src/plugins/renderers/rhi/jobs/jobs.pri b/src/plugins/renderers/rhi/jobs/jobs.pri
new file mode 100644
index 000000000..d80b8bfd9
--- /dev/null
+++ b/src/plugins/renderers/rhi/jobs/jobs.pri
@@ -0,0 +1,17 @@
+INCLUDEPATH += $$PWD
+
+SOURCES += \
+ $$PWD/filtercompatibletechniquejob.cpp \
+ $$PWD/materialparametergathererjob.cpp \
+ $$PWD/renderviewcommandbuilderjob.cpp \
+ $$PWD/renderviewcommandupdaterjob.cpp \
+ $$PWD/renderviewinitializerjob.cpp \
+ $$PWD/renderviewjobutils.cpp
+
+HEADERS += \
+ $$PWD/filtercompatibletechniquejob_p.h \
+ $$PWD/materialparametergathererjob_p.h \
+ $$PWD/renderviewcommandbuilderjob_p.h \
+ $$PWD/renderviewcommandupdaterjob_p.h \
+ $$PWD/renderviewinitializerjob_p.h \
+ $$PWD/renderviewjobutils_p.h
diff --git a/src/plugins/renderers/rhi/jobs/materialparametergathererjob.cpp b/src/plugins/renderers/rhi/jobs/materialparametergathererjob.cpp
new file mode 100644
index 000000000..c791d30f8
--- /dev/null
+++ b/src/plugins/renderers/rhi/jobs/materialparametergathererjob.cpp
@@ -0,0 +1,140 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Paul Lemire
+** 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 "materialparametergathererjob_p.h"
+#include <Qt3DRender/private/nodemanagers_p.h>
+#include <Qt3DRender/private/managers_p.h>
+#include <Qt3DRender/private/renderpassfilternode_p.h>
+#include <Qt3DRender/private/techniquefilternode_p.h>
+#include <Qt3DRender/private/job_common_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+namespace Render {
+
+namespace Rhi {
+
+namespace {
+
+int materialParameterGathererCounter = 0;
+const int likelyNumberOfParameters = 24;
+
+} // anonymous
+
+MaterialParameterGathererJob::MaterialParameterGathererJob()
+ : Qt3DCore::QAspectJob(),
+ m_manager(nullptr),
+ m_techniqueFilter(nullptr),
+ m_renderPassFilter(nullptr)
+{
+ SET_JOB_RUN_STAT_TYPE(this, JobTypes::MaterialParameterGathering,
+ materialParameterGathererCounter++)
+}
+
+// TechniqueFilter / RenderPassFilter
+
+// Parameters from Material/Effect/Technique
+
+// Note: we could maybe improve that by having a smart update when we detect
+// that a parameter value has changed. That might require way more book keeping
+// which might make this solution a bit too complex
+
+// The fact that this can now be performed in parallel should already provide a big
+// improvement
+void MaterialParameterGathererJob::run()
+{
+ for (const HMaterial &materialHandle : qAsConst(m_handles)) {
+ Material *material = m_manager->materialManager()->data(materialHandle);
+
+ if (Q_UNLIKELY(!material->isEnabled()))
+ continue;
+
+ Effect *effect = m_manager->effectManager()->lookupResource(material->effect());
+ Technique *technique = findTechniqueForEffect(m_manager, m_techniqueFilter, effect);
+
+ if (Q_LIKELY(technique != nullptr)) {
+ RenderPassList passes =
+ findRenderPassesForTechnique(m_manager, m_renderPassFilter, technique);
+ if (Q_LIKELY(passes.size() > 0)) {
+ // Order set:
+ // 1 Pass Filter
+ // 2 Technique Filter
+ // 3 Material
+ // 4 Effect
+ // 5 Technique
+ // 6 RenderPass
+
+ // Add Parameters define in techniqueFilter and passFilter
+ // passFilter have priority over techniqueFilter
+
+ ParameterInfoList parameters;
+ // Doing the reserve allows a gain of 0.5ms on some of the demo examples
+ parameters.reserve(likelyNumberOfParameters);
+
+ if (m_renderPassFilter)
+ parametersFromParametersProvider(&parameters, m_manager->parameterManager(),
+ m_renderPassFilter);
+ if (m_techniqueFilter)
+ parametersFromParametersProvider(&parameters, m_manager->parameterManager(),
+ m_techniqueFilter);
+ // Get the parameters for our selected rendering setup (override what was defined in
+ // the technique/pass filter)
+ parametersFromMaterialEffectTechnique(&parameters, m_manager->parameterManager(),
+ material, effect, technique);
+
+ for (RenderPass *renderPass : passes) {
+ ParameterInfoList globalParameters = parameters;
+ parametersFromParametersProvider(&globalParameters,
+ m_manager->parameterManager(), renderPass);
+ m_parameters[material->peerId()].push_back({ renderPass, globalParameters });
+ }
+ }
+ }
+ }
+}
+
+} // Rhi
+
+} // Render
+
+} // Qt3DRender
+
+QT_END_NAMESPACE
diff --git a/src/plugins/renderers/rhi/jobs/materialparametergathererjob_p.h b/src/plugins/renderers/rhi/jobs/materialparametergathererjob_p.h
new file mode 100644
index 000000000..f635755d7
--- /dev/null
+++ b/src/plugins/renderers/rhi/jobs/materialparametergathererjob_p.h
@@ -0,0 +1,124 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Paul Lemire
+** 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_RHI_MATERIALPARAMETERGATHERERJOB_P_H
+#define QT3DRENDER_RENDER_RHI_MATERIALPARAMETERGATHERERJOB_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <Qt3DCore/qaspectjob.h>
+#include <Qt3DCore/qnodeid.h>
+#include <Qt3DRender/private/handle_types_p.h>
+#include <Qt3DRender/private/qt3drender_global_p.h>
+#include <renderviewjobutils_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+namespace Render {
+
+class NodeManagers;
+class TechniqueFilter;
+class RenderPassFilter;
+
+namespace Rhi {
+class Renderer;
+
+// TO be executed for each FrameGraph branch with a given RenderPassFilter/TechniqueFilter
+
+class Q_AUTOTEST_EXPORT MaterialParameterGathererJob : public Qt3DCore::QAspectJob
+{
+public:
+ MaterialParameterGathererJob();
+
+ inline void setNodeManagers(NodeManagers *manager) Q_DECL_NOTHROW { m_manager = manager; }
+ inline void setTechniqueFilter(TechniqueFilter *techniqueFilter) Q_DECL_NOTHROW
+ {
+ m_techniqueFilter = techniqueFilter;
+ }
+ inline void setRenderPassFilter(RenderPassFilter *renderPassFilter) Q_DECL_NOTHROW
+ {
+ m_renderPassFilter = renderPassFilter;
+ }
+ inline const QHash<Qt3DCore::QNodeId, QVector<RenderPassParameterData>> &
+ materialToPassAndParameter() Q_DECL_NOTHROW
+ {
+ return m_parameters;
+ }
+ inline void setHandles(const QVector<HMaterial> &handles) Q_DECL_NOTHROW
+ {
+ m_handles = handles;
+ }
+
+ inline TechniqueFilter *techniqueFilter() const Q_DECL_NOTHROW { return m_techniqueFilter; }
+ inline RenderPassFilter *renderPassFilter() const Q_DECL_NOTHROW { return m_renderPassFilter; }
+
+ void run() final;
+
+private:
+ NodeManagers *m_manager;
+ TechniqueFilter *m_techniqueFilter;
+ RenderPassFilter *m_renderPassFilter;
+
+ // Material id to array of RenderPasse with parameters
+ MaterialParameterGathererData m_parameters;
+ QVector<HMaterial> m_handles;
+};
+
+typedef QSharedPointer<MaterialParameterGathererJob> MaterialParameterGathererJobPtr;
+
+} // Rhi
+
+} // Render
+
+} // Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDER_RHI_MATERIALPARAMETERGATHERERJOB_P_H
diff --git a/src/plugins/renderers/rhi/jobs/renderviewcommandbuilderjob.cpp b/src/plugins/renderers/rhi/jobs/renderviewcommandbuilderjob.cpp
new file mode 100644
index 000000000..7a7520b60
--- /dev/null
+++ b/src/plugins/renderers/rhi/jobs/renderviewcommandbuilderjob.cpp
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "renderviewcommandbuilderjob_p.h"
+#include <Qt3DRender/private/job_common_p.h>
+#include <renderview_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+namespace Render {
+
+namespace Rhi {
+
+namespace {
+int renderViewInstanceCounter = 0;
+} // anonymous
+
+RenderViewCommandBuilderJob::RenderViewCommandBuilderJob()
+ : Qt3DCore::QAspectJob(), m_offset(0), m_count(0), m_renderView(nullptr)
+{
+ SET_JOB_RUN_STAT_TYPE(this, JobTypes::RenderViewCommandBuilder, renderViewInstanceCounter++)
+}
+
+void RenderViewCommandBuilderJob::run()
+{
+ if (!m_renderView->noDraw()) {
+ if (m_count == 0)
+ return;
+
+ const bool isDraw = !m_renderView->isCompute();
+ if (isDraw)
+ m_commandData = m_renderView->buildDrawRenderCommands(m_entities, m_offset, m_count);
+ else
+ m_commandData = m_renderView->buildComputeRenderCommands(m_entities, m_offset, m_count);
+ }
+}
+
+} // Rhi
+
+} // Render
+
+} // Qt3DRender
+
+QT_END_NAMESPACE
diff --git a/src/plugins/renderers/opengl/jobs/renderviewbuilderjob_p.h b/src/plugins/renderers/rhi/jobs/renderviewcommandbuilderjob_p.h
index 13e19daf7..0832772d3 100644
--- a/src/plugins/renderers/opengl/jobs/renderviewbuilderjob_p.h
+++ b/src/plugins/renderers/rhi/jobs/renderviewcommandbuilderjob_p.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2019 Klaralvdalens Datakonsult AB (KDAB).
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt3D module of the Qt Toolkit.
@@ -37,8 +37,8 @@
**
****************************************************************************/
-#ifndef QT3DRENDER_RENDER_OPENGL_RENDERVIEWCOMMANDBUILDERJOB_P_H
-#define QT3DRENDER_RENDER_OPENGL_RENDERVIEWCOMMANDBUILDERJOB_P_H
+#ifndef QT3DRENDER_RENDER_RHI_RENDERVIEWCOMMANDBUILDERJOB_P_H
+#define QT3DRENDER_RENDER_RHI_RENDERVIEWCOMMANDBUILDERJOB_P_H
//
// W A R N I N G
@@ -61,9 +61,7 @@ namespace Qt3DRender {
namespace Render {
-namespace OpenGL {
-
-class RenderView;
+namespace Rhi {
class Q_AUTOTEST_EXPORT RenderViewCommandBuilderJob : public Qt3DCore::QAspectJob
{
@@ -91,7 +89,7 @@ private:
typedef QSharedPointer<RenderViewCommandBuilderJob> RenderViewCommandBuilderJobPtr;
-} // OpenGL
+} // Rhi
} // Render
@@ -99,4 +97,4 @@ typedef QSharedPointer<RenderViewCommandBuilderJob> RenderViewCommandBuilderJobP
QT_END_NAMESPACE
-#endif // QT3DRENDER_RENDER_OPENGL_RENDERVIEWCOMMANDBUILDERJOB_P_H
+#endif // QT3DRENDER_RENDER_RHI_RENDERVIEWCOMMANDBUILDERJOB_P_H
diff --git a/src/plugins/renderers/opengl/jobs/renderviewbuilderjob.cpp b/src/plugins/renderers/rhi/jobs/renderviewcommandupdaterjob.cpp
index 468ab9342..442f9a7d3 100644
--- a/src/plugins/renderers/opengl/jobs/renderviewbuilderjob.cpp
+++ b/src/plugins/renderers/rhi/jobs/renderviewcommandupdaterjob.cpp
@@ -1,5 +1,6 @@
/****************************************************************************
**
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
** Copyright (C) 2016 Paul Lemire
** Contact: https://www.qt.io/licensing/
**
@@ -48,21 +49,21 @@ namespace Qt3DRender {
namespace Render {
-namespace OpenGL {
+namespace Rhi {
namespace {
int renderViewInstanceCounter = 0;
} // anonymous
RenderViewCommandUpdaterJob::RenderViewCommandUpdaterJob()
- : Qt3DCore::QAspectJob()
- , m_offset(0)
- , m_count(0)
- , m_renderView(nullptr)
- , m_renderer(nullptr)
- , m_renderables(nullptr)
+ : Qt3DCore::QAspectJob(),
+ m_offset(0),
+ m_count(0),
+ m_renderView(nullptr),
+ m_renderer(nullptr),
+ m_renderables()
{
- SET_JOB_RUN_STAT_TYPE(this, JobTypes::RenderCommandUpdater, renderViewInstanceCounter++);
+ SET_JOB_RUN_STAT_TYPE(this, JobTypes::RenderCommandUpdater, renderViewInstanceCounter++)
}
void RenderViewCommandUpdaterJob::run()
@@ -73,11 +74,11 @@ void RenderViewCommandUpdaterJob::run()
if (m_count == 0)
return;
// Update Render Commands (Uniform Change, Depth Change)
- m_renderView->updateRenderCommand(m_renderables, m_offset, m_count);
+ m_renderView->updateRenderCommand(m_renderables.data(), m_offset, m_count);
}
}
-} // OpenGL
+} // Rhi
} // Render
diff --git a/src/plugins/renderers/rhi/jobs/renderviewcommandupdaterjob_p.h b/src/plugins/renderers/rhi/jobs/renderviewcommandupdaterjob_p.h
new file mode 100644
index 000000000..3b40124ad
--- /dev/null
+++ b/src/plugins/renderers/rhi/jobs/renderviewcommandupdaterjob_p.h
@@ -0,0 +1,109 @@
+ļ»æ/****************************************************************************
+**
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
+** Copyright (C) 2016 Paul Lemire
+** 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_RHI_RENDERVIEWCOMMANDUPDATEJOB_P_H
+#define QT3DRENDER_RENDER_RHI_RENDERVIEWCOMMANDUPDATEJOB_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <Qt3DCore/qaspectjob.h>
+#include <Qt3DRender/private/handle_types_p.h>
+#include <rendercommand_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+namespace Render {
+
+namespace Rhi {
+
+class RenderView;
+class Renderer;
+
+class Q_AUTOTEST_EXPORT RenderViewCommandUpdaterJob : public Qt3DCore::QAspectJob
+{
+public:
+ RenderViewCommandUpdaterJob();
+
+ inline void setRenderView(RenderView *rv) Q_DECL_NOTHROW { m_renderView = rv; }
+ inline void setRenderer(Renderer *renderer) Q_DECL_NOTHROW { m_renderer = renderer; }
+ inline void setRenderables(const EntityRenderCommandDataPtr &renderables, int offset,
+ int count) Q_DECL_NOTHROW
+ {
+ m_offset = offset;
+ m_count = count;
+ m_renderables = renderables;
+ }
+ EntityRenderCommandDataPtr renderables() const { return m_renderables; }
+
+ QVector<RenderCommand> &commands() Q_DECL_NOTHROW { return m_commands; }
+
+ void run() final;
+
+private:
+ int m_offset;
+ int m_count;
+ RenderView *m_renderView;
+ Renderer *m_renderer;
+ EntityRenderCommandDataPtr m_renderables;
+ QVector<RenderCommand> m_commands;
+};
+
+typedef QSharedPointer<RenderViewCommandUpdaterJob> RenderViewCommandUpdaterJobPtr;
+
+} // Rhi
+
+} // Render
+
+} // Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDER_RHI_RENDERVIEWCOMMANDUPDATEJOB_P_H
diff --git a/src/plugins/renderers/rhi/jobs/renderviewinitializerjob.cpp b/src/plugins/renderers/rhi/jobs/renderviewinitializerjob.cpp
new file mode 100644
index 000000000..2a95cecaa
--- /dev/null
+++ b/src/plugins/renderers/rhi/jobs/renderviewinitializerjob.cpp
@@ -0,0 +1,104 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
+** Copyright (C) 2016 Paul Lemire
+** 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 "renderviewinitializerjob_p.h"
+
+#include <renderview_p.h>
+#include <renderer_p.h>
+#include <renderviewjobutils_p.h>
+#include <Qt3DRender/private/renderlogging_p.h>
+#include <Qt3DRender/private/job_common_p.h>
+
+#include <QElapsedTimer>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+namespace Render {
+namespace Rhi {
+
+namespace {
+// only accessed in ctor and dtor of RenderViewJob
+// which are always being called in a non concurrent manner
+int renderViewInstanceCounter = 0;
+} // anonymous
+
+RenderViewInitializerJob::RenderViewInitializerJob()
+ : m_renderer(nullptr),
+ m_fgLeaf(nullptr),
+ m_index(0),
+ m_renderView(nullptr) { SET_JOB_RUN_STAT_TYPE(this, JobTypes::RenderView,
+ renderViewInstanceCounter++) }
+
+ RenderViewInitializerJob::~RenderViewInitializerJob()
+{
+ renderViewInstanceCounter--;
+}
+
+void RenderViewInitializerJob::run()
+{
+ qCDebug(Jobs) << Q_FUNC_INFO << m_index;
+#if defined(QT3D_RENDER_VIEW_JOB_TIMINGS)
+ QElapsedTimer timer;
+ timer.start();
+ qint64 gatherLightsTime;
+ qint64 buildCommandsTime;
+#endif
+
+ // Create a RenderView object
+ // The RenderView are created from a QFrameAllocator stored in the current Thread local storage
+ m_renderView = new RenderView;
+
+ // RenderView should allocate heap resources using only the currentFrameAllocator
+ m_renderView->setRenderer(m_renderer);
+
+ // Populate the renderview's configuration from the framegraph
+ setRenderViewConfigFromFrameGraphLeafNode(m_renderView, m_fgLeaf);
+#if defined(QT3D_RENDER_VIEW_JOB_TIMINGS)
+ qint64 gatherStateTime = timer.nsecsElapsed();
+ timer.restart();
+#endif
+}
+
+} // namespace Rhi
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
diff --git a/src/plugins/renderers/rhi/jobs/renderviewinitializerjob_p.h b/src/plugins/renderers/rhi/jobs/renderviewinitializerjob_p.h
new file mode 100644
index 000000000..fb38c8716
--- /dev/null
+++ b/src/plugins/renderers/rhi/jobs/renderviewinitializerjob_p.h
@@ -0,0 +1,108 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Paul Lemire
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DRENDER_RENDER_RHI_RENDERVIEWINITIALIZERJOB_H
+#define QT3DRENDER_RENDER_RHI_RENDERVIEWINITIALIZERJOB_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <Qt3DCore/qaspectjob.h>
+#include <Qt3DCore/private/qframeallocator_p.h>
+#include <QSize>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+namespace Render {
+
+class FrameGraphNode;
+
+namespace Rhi {
+
+class Renderer;
+class RenderView;
+
+class Q_AUTOTEST_EXPORT RenderViewInitializerJob : public Qt3DCore::QAspectJob
+{
+public:
+ RenderViewInitializerJob();
+ ~RenderViewInitializerJob();
+
+ inline void setRenderer(Renderer *renderer) { m_renderer = renderer; }
+ inline RenderView *renderView() const Q_DECL_NOTHROW { return m_renderView; }
+
+ inline void setFrameGraphLeafNode(FrameGraphNode *fgLeaf) { m_fgLeaf = fgLeaf; }
+
+ // Sets the position in the queue of RenderViews that the
+ // RenderView generated by this job should be inserted. This is
+ // used to ensure that for example a RenderView for creating
+ // a shadow map texture is submitted before the RenderView that
+ // contains commands making use of the shadow map
+ inline void setSubmitOrderIndex(int index) { m_index = index; }
+ inline int submitOrderIndex() const { return m_index; }
+
+ void run() override;
+
+private:
+ Renderer *m_renderer;
+ FrameGraphNode *m_fgLeaf;
+ int m_index;
+ RenderView *m_renderView;
+};
+
+typedef QSharedPointer<RenderViewInitializerJob> RenderViewInitializerJobPtr;
+
+} // namespace Rhi
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDER_RHI_RENDERVIEWINITIALIZERJOB_H
diff --git a/src/plugins/renderers/rhi/jobs/renderviewjobutils.cpp b/src/plugins/renderers/rhi/jobs/renderviewjobutils.cpp
new file mode 100644
index 000000000..c9a37c5e1
--- /dev/null
+++ b/src/plugins/renderers/rhi/jobs/renderviewjobutils.cpp
@@ -0,0 +1,583 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "renderviewjobutils_p.h"
+#include <Qt3DRender/private/renderlogging_p.h>
+
+#include <Qt3DRender/qgraphicsapifilter.h>
+#include <Qt3DRender/private/sphere_p.h>
+#include <Qt3DRender/qshaderdata.h>
+
+#include <Qt3DRender/private/cameraselectornode_p.h>
+#include <Qt3DRender/private/clearbuffers_p.h>
+#include <Qt3DRender/private/layerfilternode_p.h>
+#include <Qt3DRender/private/nodemanagers_p.h>
+#include <Qt3DRender/private/effect_p.h>
+#include <Qt3DRender/private/renderpassfilternode_p.h>
+#include <Qt3DRender/private/rendertargetselectornode_p.h>
+#include <Qt3DRender/private/sortpolicy_p.h>
+#include <Qt3DRender/private/techniquefilternode_p.h>
+#include <Qt3DRender/private/viewportnode_p.h>
+#include <Qt3DRender/private/managers_p.h>
+#include <Qt3DRender/private/shaderdata_p.h>
+#include <Qt3DRender/private/statesetnode_p.h>
+#include <Qt3DRender/private/dispatchcompute_p.h>
+#include <Qt3DRender/private/rendersurfaceselector_p.h>
+#include <Qt3DRender/private/rendercapture_p.h>
+#include <Qt3DRender/private/buffercapture_p.h>
+#include <Qt3DRender/private/stringtoint_p.h>
+#include <Qt3DRender/private/techniquemanager_p.h>
+#include <Qt3DRender/private/memorybarrier_p.h>
+#include <Qt3DRender/private/blitframebuffer_p.h>
+#include <Qt3DRender/private/waitfence_p.h>
+#include <Qt3DRender/private/renderstateset_p.h>
+#include <renderview_p.h>
+#include <shadervariables_p.h>
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt3DCore;
+
+namespace Qt3DRender {
+namespace Render {
+namespace Rhi {
+
+/*!
+ \internal
+ Walks up the framegraph tree from \a fgLeaf and builds up as much state
+ as possible and populates \a rv. For cases where we can't get the specific state
+ (e.g. because it depends upon more than just the framegraph) we store the data from
+ the framegraph that will be needed to later when the rest of the data becomes available
+*/
+void setRenderViewConfigFromFrameGraphLeafNode(RenderView *rv, const FrameGraphNode *fgLeaf)
+{
+ // The specific RenderPass to be used is also dependent upon the Effect and TechniqueFilter
+ // which is referenced by the Material which is referenced by the RenderMesh. So we can
+ // only store the filter info in the RenderView structure and use it to do the resolving
+ // when we build the RenderCommand list.
+ const NodeManagers *manager = rv->nodeManagers();
+ const FrameGraphNode *node = fgLeaf;
+
+ while (node) {
+ FrameGraphNode::FrameGraphNodeType type = node->nodeType();
+ if (node->isEnabled())
+ switch (type) {
+ case FrameGraphNode::InvalidNodeType:
+ // A base FrameGraphNode, can be used for grouping purposes
+ break;
+ case FrameGraphNode::CameraSelector:
+ // Can be set only once and we take camera nearest to the leaf node
+ if (!rv->renderCameraLens()) {
+ const CameraSelector *cameraSelector =
+ static_cast<const CameraSelector *>(node);
+ Entity *camNode = manager->renderNodesManager()->lookupResource(
+ cameraSelector->cameraUuid());
+ if (camNode) {
+ CameraLens *lens = camNode->renderComponent<CameraLens>();
+ rv->setRenderCameraEntity(camNode);
+ if (lens && lens->isEnabled()) {
+ rv->setRenderCameraLens(lens);
+ // ViewMatrix and ProjectionMatrix are computed
+ // later in updateMatrices()
+ // since at this point the transformation matrices
+ // may not yet have been updated
+ }
+ }
+ }
+ break;
+
+ case FrameGraphNode::LayerFilter: // Can be set multiple times in the tree
+ rv->appendLayerFilter(static_cast<const LayerFilterNode *>(node)->peerId());
+ break;
+
+ case FrameGraphNode::ProximityFilter: // Can be set multiple times in the tree
+ rv->appendProximityFilterId(node->peerId());
+ break;
+
+ case FrameGraphNode::RenderPassFilter:
+ // Can be set once
+ // TODO: Amalgamate all render pass filters from leaf to root
+ if (!rv->renderPassFilter())
+ rv->setRenderPassFilter(static_cast<const RenderPassFilter *>(node));
+ break;
+
+ case FrameGraphNode::RenderTarget: {
+ // Can be set once and we take render target nearest to the leaf node
+ const RenderTargetSelector *targetSelector =
+ static_cast<const RenderTargetSelector *>(node);
+ QNodeId renderTargetUid = targetSelector->renderTargetUuid();
+ HTarget renderTargetHandle =
+ manager->renderTargetManager()->lookupHandle(renderTargetUid);
+
+ // Add renderTarget Handle and build renderCommand AttachmentPack
+ if (!rv->renderTargetId()) {
+ rv->setRenderTargetId(renderTargetUid);
+
+ RenderTarget *renderTarget =
+ manager->renderTargetManager()->data(renderTargetHandle);
+ if (renderTarget)
+ rv->setAttachmentPack(AttachmentPack(renderTarget,
+ manager->attachmentManager(),
+ targetSelector->outputs()));
+ }
+ break;
+ }
+
+ case FrameGraphNode::ClearBuffers: {
+ const ClearBuffers *cbNode = static_cast<const ClearBuffers *>(node);
+ rv->addClearBuffers(cbNode);
+ break;
+ }
+
+ case FrameGraphNode::TechniqueFilter:
+ // Can be set once
+ // TODO Amalgamate all technique filters from leaf to root
+ if (!rv->techniqueFilter())
+ rv->setTechniqueFilter(static_cast<const TechniqueFilter *>(node));
+ break;
+
+ case FrameGraphNode::Viewport: {
+ // If the Viewport has already been set in a lower node
+ // Make it so that the new viewport is actually
+ // a subregion relative to that of the parent viewport
+ const ViewportNode *vpNode = static_cast<const ViewportNode *>(node);
+ rv->setViewport(ViewportNode::computeViewport(rv->viewport(), vpNode));
+ rv->setGamma(vpNode->gamma());
+ break;
+ }
+
+ case FrameGraphNode::SortMethod: {
+ const Render::SortPolicy *sortPolicy =
+ static_cast<const Render::SortPolicy *>(node);
+ rv->addSortType(sortPolicy->sortTypes());
+ break;
+ }
+
+ case FrameGraphNode::SubtreeEnabler:
+ // Has no meaning here. SubtreeEnabler was used
+ // in a prior step to filter the list of RenderViewJobs
+ break;
+
+ case FrameGraphNode::StateSet: {
+ const Render::StateSetNode *rStateSet =
+ static_cast<const Render::StateSetNode *>(node);
+ // Create global RenderStateSet for renderView if no stateSet was set before
+ RenderStateSet *stateSet = rv->stateSet();
+ if (stateSet == nullptr && rStateSet->hasRenderStates()) {
+ stateSet = new RenderStateSet();
+ rv->setStateSet(stateSet);
+ }
+
+ // Add states from new stateSet we might be missing
+ // but don' t override existing states (lower StateSetNode always has priority)
+ if (rStateSet->hasRenderStates())
+ addStatesToRenderStateSet(stateSet, rStateSet->renderStates(),
+ manager->renderStateManager());
+ break;
+ }
+
+ case FrameGraphNode::NoDraw: {
+ rv->setNoDraw(true);
+ break;
+ }
+
+ case FrameGraphNode::FrustumCulling: {
+ rv->setFrustumCulling(true);
+ break;
+ }
+
+ case FrameGraphNode::ComputeDispatch: {
+ const Render::DispatchCompute *dispatchCompute =
+ static_cast<const Render::DispatchCompute *>(node);
+ rv->setCompute(true);
+ rv->setComputeWorkgroups(dispatchCompute->x(), dispatchCompute->y(),
+ dispatchCompute->z());
+ break;
+ }
+
+ case FrameGraphNode::Lighting: {
+ // TODO
+ break;
+ }
+
+ case FrameGraphNode::Surface: {
+ // Use the surface closest to leaf node
+ if (rv->surface() == nullptr) {
+ const Render::RenderSurfaceSelector *surfaceSelector =
+ static_cast<const Render::RenderSurfaceSelector *>(node);
+ rv->setSurface(surfaceSelector->surface());
+ rv->setSurfaceSize(surfaceSelector->renderTargetSize()
+ * surfaceSelector->devicePixelRatio());
+ rv->setDevicePixelRatio(surfaceSelector->devicePixelRatio());
+ }
+ break;
+ }
+ case FrameGraphNode::RenderCapture: {
+ auto *renderCapture = const_cast<Render::RenderCapture *>(
+ static_cast<const Render::RenderCapture *>(node));
+ if (rv->renderCaptureNodeId().isNull() && renderCapture->wasCaptureRequested()) {
+ rv->setRenderCaptureNodeId(renderCapture->peerId());
+ rv->setRenderCaptureRequest(renderCapture->takeCaptureRequest());
+ }
+ break;
+ }
+
+ case FrameGraphNode::MemoryBarrier: {
+ // Not available in rhi
+ break;
+ }
+
+ case FrameGraphNode::BufferCapture: {
+ auto *bufferCapture = const_cast<Render::BufferCapture *>(
+ static_cast<const Render::BufferCapture *>(node));
+ if (bufferCapture != nullptr)
+ rv->setIsDownloadBuffersEnable(bufferCapture->isEnabled());
+ break;
+ }
+
+ case FrameGraphNode::BlitFramebuffer: {
+ const Render::BlitFramebuffer *blitFramebufferNode =
+ static_cast<const Render::BlitFramebuffer *>(node);
+ rv->setHasBlitFramebufferInfo(true);
+ BlitFramebufferInfo bfbInfo;
+ bfbInfo.sourceRenderTargetId = blitFramebufferNode->sourceRenderTargetId();
+ bfbInfo.destinationRenderTargetId =
+ blitFramebufferNode->destinationRenderTargetId();
+ bfbInfo.sourceRect = blitFramebufferNode->sourceRect();
+ bfbInfo.destinationRect = blitFramebufferNode->destinationRect();
+ bfbInfo.sourceAttachmentPoint = blitFramebufferNode->sourceAttachmentPoint();
+ bfbInfo.destinationAttachmentPoint =
+ blitFramebufferNode->destinationAttachmentPoint();
+ bfbInfo.interpolationMethod = blitFramebufferNode->interpolationMethod();
+ rv->setBlitFrameBufferInfo(bfbInfo);
+ break;
+ }
+
+ case FrameGraphNode::WaitFence: {
+ // Not available in rhi
+ break;
+ }
+
+ case FrameGraphNode::SetFence: {
+ // Not available in rhi
+ break;
+ }
+
+ case FrameGraphNode::NoPicking:
+ // Nothing to do RenderView wise for NoPicking
+ break;
+
+ default:
+ // Should never get here
+ qCWarning(Backend) << "Unhandled FrameGraphNode type";
+ }
+
+ node = node->parent();
+ }
+}
+
+/*!
+ \internal
+ Searches the best matching Technique from \a effect specified.
+*/
+Technique *findTechniqueForEffect(NodeManagers *manager, const TechniqueFilter *techniqueFilter,
+ Effect *effect)
+{
+ if (!effect)
+ return nullptr;
+
+ QVector<Technique *> matchingTechniques;
+ const bool hasInvalidTechniqueFilter =
+ (techniqueFilter == nullptr || techniqueFilter->filters().isEmpty());
+
+ // Iterate through the techniques in the effect
+ const auto techniqueIds = effect->techniques();
+ for (const QNodeId techniqueId : techniqueIds) {
+ Technique *technique = manager->techniqueManager()->lookupResource(techniqueId);
+
+ // Should be valid, if not there likely a problem with node addition/destruction changes
+ Q_ASSERT(technique);
+
+ // Check if the technique is compatible with the rendering API
+ // If no techniqueFilter is present, we return the technique as it satisfies OpenGL version
+ if (technique->isCompatibleWithRenderer()
+ && (hasInvalidTechniqueFilter
+ || technique->isCompatibleWithFilters(techniqueFilter->filters())))
+ matchingTechniques.append(technique);
+ }
+
+ if (matchingTechniques.size() == 0) // We failed to find a suitable technique to use :(
+ return nullptr;
+
+ if (matchingTechniques.size() == 1)
+ return matchingTechniques.first();
+
+ // Several compatible techniques, return technique with highest major and minor version
+ Technique *highest = matchingTechniques.first();
+ GraphicsApiFilterData filter = *highest->graphicsApiFilter();
+ for (auto it = matchingTechniques.cbegin() + 1; it < matchingTechniques.cend(); ++it) {
+ if (filter < *(*it)->graphicsApiFilter()) {
+ filter = *(*it)->graphicsApiFilter();
+ highest = *it;
+ }
+ }
+ return highest;
+}
+
+RenderPassList findRenderPassesForTechnique(NodeManagers *manager,
+ const RenderPassFilter *passFilter,
+ Technique *technique)
+{
+ Q_ASSERT(manager);
+ Q_ASSERT(technique);
+
+ RenderPassList passes;
+ const auto passIds = technique->renderPasses();
+ for (const QNodeId passId : passIds) {
+ RenderPass *renderPass = manager->renderPassManager()->lookupResource(passId);
+
+ if (renderPass && renderPass->isEnabled()) {
+ bool foundMatch = (!passFilter || passFilter->filters().size() == 0);
+
+ // A pass filter is present so we need to check for matching criteria
+ if (!foundMatch && renderPass->filterKeys().size() >= passFilter->filters().size()) {
+
+ // Iterate through the filter criteria and look for render passes with criteria that
+ // satisfy them
+ const auto filterKeyIds = passFilter->filters();
+ for (const QNodeId filterKeyId : filterKeyIds) {
+ foundMatch = false;
+ FilterKey *filterFilterKey =
+ manager->filterKeyManager()->lookupResource(filterKeyId);
+
+ const auto passFilterKeyIds = renderPass->filterKeys();
+ for (const QNodeId passFilterKeyId : passFilterKeyIds) {
+ FilterKey *passFilterKey =
+ manager->filterKeyManager()->lookupResource(passFilterKeyId);
+ if ((foundMatch = (*passFilterKey == *filterFilterKey)))
+ break;
+ }
+
+ if (!foundMatch) {
+ // No match for criterion in any of the render pass' criteria
+ break;
+ }
+ }
+ }
+
+ if (foundMatch) {
+ // Found a renderpass that satisfies our needs. Add it in order
+ passes << renderPass;
+ }
+ }
+ }
+
+ return passes;
+}
+
+ParameterInfoList::const_iterator findParamInfo(ParameterInfoList *params, const int nameId)
+{
+ const ParameterInfoList::const_iterator end = params->cend();
+ ParameterInfoList::const_iterator it = std::lower_bound(params->cbegin(), end, nameId);
+ if (it != end && it->nameId != nameId)
+ return end;
+ return it;
+}
+
+void addParametersForIds(ParameterInfoList *params, ParameterManager *manager,
+ const Qt3DCore::QNodeIdVector &parameterIds)
+{
+ for (const QNodeId paramId : parameterIds) {
+ const HParameter parameterHandle = manager->lookupHandle(paramId);
+ const Parameter *param = manager->data(parameterHandle);
+ ParameterInfoList::iterator it =
+ std::lower_bound(params->begin(), params->end(), param->nameId());
+ if (it == params->end() || it->nameId != param->nameId())
+ params->insert(it, ParameterInfo(param->nameId(), parameterHandle));
+ }
+}
+
+void parametersFromMaterialEffectTechnique(ParameterInfoList *infoList, ParameterManager *manager,
+ Material *material, Effect *effect, Technique *technique)
+{
+ // The parameters are taken in the following priority order:
+ //
+ // 1) Material
+ // 2) Effect
+ // 3) Technique
+ //
+ // That way a user can override defaults in Effect's and Techniques on a
+ // object manner and a Technique can override global defaults from the Effect.
+ parametersFromParametersProvider(infoList, manager, material);
+ parametersFromParametersProvider(infoList, manager, effect);
+ parametersFromParametersProvider(infoList, manager, technique);
+}
+
+// Only add states with types we don't already have
+void addStatesToRenderStateSet(RenderStateSet *stateSet, const QVector<Qt3DCore::QNodeId> &stateIds,
+ RenderStateManager *manager)
+{
+ for (const Qt3DCore::QNodeId &stateId : stateIds) {
+ RenderStateNode *node = manager->lookupResource(stateId);
+ if (node->isEnabled() && stateSet->canAddStateOfType(node->type())) {
+ stateSet->addState(node->impl());
+ }
+ }
+}
+
+namespace {
+
+const QString blockArray = QStringLiteral("[%1]");
+const int qNodeIdTypeId = qMetaTypeId<QNodeId>();
+
+}
+
+UniformBlockValueBuilder::UniformBlockValueBuilder()
+ : updatedPropertiesOnly(false), shaderDataManager(nullptr), textureManager(nullptr)
+{
+}
+
+UniformBlockValueBuilder::~UniformBlockValueBuilder() { }
+
+void UniformBlockValueBuilder::buildActiveUniformNameValueMapHelper(
+ const ShaderData *currentShaderData, const QString &blockName,
+ const QString &qmlPropertyName, const QVariant &value)
+{
+ // In the end, values are either scalar or a scalar array
+ // Composed elements (structs, structs array) are simplified into simple scalars
+ if (value.userType() == QMetaType::QVariantList) { // Array
+ QVariantList list = value.value<QVariantList>();
+ if (list.at(0).userType()
+ == qNodeIdTypeId) { // Array of struct qmlPropertyName[i].structMember
+ for (int i = 0; i < list.size(); ++i) {
+ const QVariant &variantElement = list.at(i);
+ if (list.at(i).userType() == qNodeIdTypeId) {
+ const auto nodeId = variantElement.value<QNodeId>();
+ ShaderData *subShaderData = shaderDataManager->lookupResource(nodeId);
+ if (subShaderData) {
+ buildActiveUniformNameValueMapStructHelper(
+ subShaderData,
+ blockName + QLatin1Char('.') + qmlPropertyName + blockArray.arg(i),
+ QLatin1String(""));
+ }
+ // Note: we only handle ShaderData as nested container nodes here
+ }
+ }
+ } else { // Array of scalar/vec qmlPropertyName[0]
+ QString varName;
+ varName.reserve(blockName.length() + 1 + qmlPropertyName.length() + 3);
+ varName.append(blockName);
+ varName.append(QLatin1String("."));
+ varName.append(qmlPropertyName);
+ varName.append(QLatin1String("[0]"));
+ if (uniforms.contains(varName)) {
+ qCDebug(Shaders) << "UBO array member " << varName << " set for update";
+ activeUniformNamesToValue.insert(StringToInt::lookupId(varName), value);
+ }
+ }
+ } else if (value.userType() == qNodeIdTypeId) { // Struct qmlPropertyName.structMember
+ const auto nodeId = value.value<QNodeId>();
+ ShaderData *rSubShaderData = shaderDataManager->lookupResource(nodeId);
+ if (rSubShaderData) {
+ buildActiveUniformNameValueMapStructHelper(rSubShaderData, blockName, qmlPropertyName);
+ } else if (textureManager->contains(nodeId)) {
+ const auto varId =
+ StringToInt::lookupId(blockName + QLatin1Char('.') + qmlPropertyName);
+ activeUniformNamesToValue.insert(varId, value);
+ }
+ } else { // Scalar / Vec
+ QString varName;
+ varName.reserve(blockName.length() + 1 + qmlPropertyName.length());
+ varName.append(blockName);
+ varName.append(QLatin1String("."));
+ varName.append(qmlPropertyName);
+ if (uniforms.contains(varName)) {
+ qCDebug(Shaders) << "UBO scalar member " << varName << " set for update";
+
+ // If the property needs to be transformed, we transform it here as
+ // the shaderdata cannot hold transformed properties for multiple
+ // thread contexts at once
+ activeUniformNamesToValue.insert(
+ StringToInt::lookupId(varName),
+ currentShaderData->getTransformedProperty(qmlPropertyName, viewMatrix));
+ }
+ }
+}
+
+void UniformBlockValueBuilder::buildActiveUniformNameValueMapStructHelper(
+ const ShaderData *rShaderData, const QString &blockName, const QString &qmlPropertyName)
+{
+ const QHash<QString, ShaderData::PropertyValue> &properties = rShaderData->properties();
+ auto it = properties.begin();
+ const auto end = properties.end();
+
+ while (it != end) {
+ QString fullBlockName;
+ fullBlockName.reserve(blockName.length() + 1 + qmlPropertyName.length());
+ fullBlockName.append(blockName);
+ if (!qmlPropertyName.isEmpty()) {
+ fullBlockName.append(QLatin1String("."));
+ fullBlockName.append(qmlPropertyName);
+ }
+ buildActiveUniformNameValueMapHelper(rShaderData, fullBlockName, it.key(),
+ it.value().value);
+ ++it;
+ }
+}
+
+ParameterInfo::ParameterInfo(const int nameId, const HParameter &handle)
+ : nameId(nameId), handle(handle)
+{
+}
+
+bool ParameterInfo::operator<(const ParameterInfo &other) const Q_DECL_NOEXCEPT
+{
+ return nameId < other.nameId;
+}
+
+bool ParameterInfo::operator<(const int otherNameId) const Q_DECL_NOEXCEPT
+{
+ return nameId < otherNameId;
+}
+
+} // namespace Rhi
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
diff --git a/src/plugins/renderers/rhi/jobs/renderviewjobutils_p.h b/src/plugins/renderers/rhi/jobs/renderviewjobutils_p.h
new file mode 100644
index 000000000..31bc29b8d
--- /dev/null
+++ b/src/plugins/renderers/rhi/jobs/renderviewjobutils_p.h
@@ -0,0 +1,189 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DRENDER_RENDER_RHI_RENDERVIEWJOBUTILS_P_H
+#define QT3DRENDER_RENDER_RHI_RENDERVIEWJOBUTILS_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 <Qt3DCore/qnodeid.h>
+#include <QtCore/qhash.h>
+#include <QtCore/qvariant.h>
+#include <QMatrix4x4>
+#include <Qt3DRender/private/uniform_p.h>
+#include <Qt3DRender/private/handle_types_p.h>
+#include <Qt3DRender/private/aligned_malloc_p.h>
+#include <shadervariables_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DCore {
+class QFrameAllocator;
+}
+
+namespace Qt3DRender {
+namespace Render {
+
+class FrameGraphNode;
+class ParameterManager;
+class Effect;
+class Entity;
+class Material;
+class RenderPass;
+class Technique;
+class TechniqueFilter;
+class RenderPassFilter;
+class NodeManagers;
+class ShaderDataManager;
+class ShaderData;
+class TextureManager;
+class RenderStateManager;
+class RenderStateCollection;
+class RenderStateSet;
+
+namespace Rhi {
+class Renderer;
+class RenderView;
+struct ShaderUniform;
+
+Q_AUTOTEST_EXPORT void setRenderViewConfigFromFrameGraphLeafNode(RenderView *rv,
+ const FrameGraphNode *fgLeaf);
+
+Q_AUTOTEST_EXPORT Technique *findTechniqueForEffect(NodeManagers *manager,
+ const TechniqueFilter *techniqueFilter,
+ Effect *effect);
+
+typedef QVarLengthArray<RenderPass *, 4> RenderPassList;
+Q_AUTOTEST_EXPORT RenderPassList findRenderPassesForTechnique(NodeManagers *manager,
+ const RenderPassFilter *passFilter,
+ Technique *technique);
+
+// Extracts the type T from a QVariant v without using QVariant::value which is slow
+// Note: Assumes you are 100% sure about the type you requested
+template<typename T>
+inline T variant_value(const QVariant &v)
+{
+ return *reinterpret_cast<const T *>(v.data());
+}
+
+struct ParameterInfo
+{
+ explicit ParameterInfo(const int nameId = -1, const HParameter &handle = HParameter());
+
+ int nameId;
+ HParameter handle;
+
+ bool operator<(const int otherNameId) const Q_DECL_NOEXCEPT;
+ bool operator<(const ParameterInfo &other) const Q_DECL_NOEXCEPT;
+};
+typedef QVector<ParameterInfo> ParameterInfoList;
+
+struct RenderPassParameterData
+{
+ RenderPass *pass;
+ ParameterInfoList parameterInfo;
+};
+QT3D_DECLARE_TYPEINFO_3(Qt3DRender, Render, Rhi, RenderPassParameterData, Q_MOVABLE_TYPE)
+
+using MaterialParameterGathererData = QHash<Qt3DCore::QNodeId, QVector<RenderPassParameterData>>;
+
+Q_AUTOTEST_EXPORT void parametersFromMaterialEffectTechnique(ParameterInfoList *infoList,
+ ParameterManager *manager,
+ Material *material, Effect *effect,
+ Technique *technique);
+
+Q_AUTOTEST_EXPORT void addParametersForIds(ParameterInfoList *params, ParameterManager *manager,
+ const QVector<Qt3DCore::QNodeId> &parameterIds);
+
+template<class T>
+void parametersFromParametersProvider(ParameterInfoList *infoList, ParameterManager *manager,
+ T *provider)
+{
+ addParametersForIds(infoList, manager, provider->parameters());
+}
+
+Q_AUTOTEST_EXPORT ParameterInfoList::const_iterator findParamInfo(ParameterInfoList *infoList,
+ const int nameId);
+
+Q_AUTOTEST_EXPORT void addStatesToRenderStateSet(RenderStateSet *stateSet,
+ const QVector<Qt3DCore::QNodeId> &stateIds,
+ RenderStateManager *manager);
+
+typedef QHash<int, QVariant> UniformBlockValueBuilderHash;
+
+struct Q_AUTOTEST_EXPORT UniformBlockValueBuilder
+{
+ UniformBlockValueBuilder();
+ ~UniformBlockValueBuilder();
+
+ QT3D_ALIGNED_MALLOC_AND_FREE()
+
+ void buildActiveUniformNameValueMapHelper(const ShaderData *currentShaderData,
+ const QString &blockName,
+ const QString &qmlPropertyName,
+ const QVariant &value);
+ void buildActiveUniformNameValueMapStructHelper(const ShaderData *rShaderData,
+ const QString &blockName,
+ const QString &qmlPropertyName = QString());
+
+ bool updatedPropertiesOnly;
+ QSet<QString> uniforms;
+ UniformBlockValueBuilderHash activeUniformNamesToValue;
+ ShaderDataManager *shaderDataManager;
+ TextureManager *textureManager;
+ Matrix4x4 viewMatrix;
+};
+
+} // namespace Rhi
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDER_RHI_RENDERVIEWJOBUTILS_P_H
diff --git a/src/plugins/renderers/rhi/main.cpp b/src/plugins/renderers/rhi/main.cpp
new file mode 100644
index 000000000..47efc7530
--- /dev/null
+++ b/src/plugins/renderers/rhi/main.cpp
@@ -0,0 +1,60 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <Qt3DRender/private/qrendererplugin_p.h>
+#include <renderer_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class RhiRendererPlugin : public Qt3DRender::Render::QRendererPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID QRendererPluginFactoryInterface_iid FILE "rhirenderer.json")
+
+ Qt3DRender::Render::AbstractRenderer *
+ create(const QString &key, Qt3DRender::QRenderAspect::RenderType renderMode) override
+ {
+ Q_UNUSED(key)
+ return new Qt3DRender::Render::Rhi::Renderer(renderMode);
+ }
+};
+
+QT_END_NAMESPACE
+
+#include "main.moc"
diff --git a/src/plugins/renderers/rhi/managers/managers.pri b/src/plugins/renderers/rhi/managers/managers.pri
new file mode 100644
index 000000000..de3f29b30
--- /dev/null
+++ b/src/plugins/renderers/rhi/managers/managers.pri
@@ -0,0 +1,8 @@
+INCLUDEPATH += $$PWD
+
+HEADERS += \
+ $$PWD/rhihandle_types_p.h \
+ $$PWD/rhiresourcemanagers_p.h
+
+SOURCES += \
+ $$PWD/rhiresourcemanagers.cpp
diff --git a/src/plugins/renderers/rhi/managers/rhihandle_types_p.h b/src/plugins/renderers/rhi/managers/rhihandle_types_p.h
new file mode 100644
index 000000000..2e74131e1
--- /dev/null
+++ b/src/plugins/renderers/rhi/managers/rhihandle_types_p.h
@@ -0,0 +1,85 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DRENDER_RENDER_RHI_RHIHANDLE_TYPES_P_H
+#define QT3DRENDER_RENDER_RHI_RHIHANDLE_TYPES_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <Qt3DCore/private/qhandle_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+namespace Render {
+
+namespace Rhi {
+
+class RHIBuffer;
+class RHITexture;
+class RHIGraphicsPipeline;
+
+typedef Qt3DCore::QHandle<RHIBuffer> HRHIBuffer;
+typedef Qt3DCore::QHandle<RHITexture> HRHITexture;
+typedef Qt3DCore::QHandle<RHIGraphicsPipeline> HRHIGraphicsPipeline;
+
+} // namespace Rhi
+
+} // namespace Render
+
+} // namespace Qt3DRender
+
+#if defined(_MSC_VER)
+#define RHI_UNIMPLEMENTED // do { qDebug() << "Unimplemented: " << __FUNCSIG__; } while (0)
+#else
+#define RHI_UNIMPLEMENTED // do { qDebug() << "Unimplemented: " << __PRETTY_FUNCTION__; } while (0)
+#endif
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDER_RHI_RHIHANDLE_TYPES_P_H
diff --git a/src/plugins/renderers/rhi/managers/rhiresourcemanagers.cpp b/src/plugins/renderers/rhi/managers/rhiresourcemanagers.cpp
new file mode 100644
index 000000000..3e813250b
--- /dev/null
+++ b/src/plugins/renderers/rhi/managers/rhiresourcemanagers.cpp
@@ -0,0 +1,87 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "rhiresourcemanagers_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+namespace Render {
+
+namespace Rhi {
+
+RHIResourceManagers::RHIResourceManagers()
+ : m_rhiBufferManager(new RHIBufferManager()),
+ m_rhiShaderManager(new RHIShaderManager()),
+ m_rhiTextureManager(new RHITextureManager()),
+ m_rhiGraphicsPipelineManager(new RHIGraphicsPipelineManager())
+{
+}
+
+RHIResourceManagers::~RHIResourceManagers()
+{
+ delete m_rhiTextureManager;
+ delete m_rhiShaderManager;
+ delete m_rhiBufferManager;
+ delete m_rhiGraphicsPipelineManager;
+}
+
+void RHIResourceManagers::releaseAllResources()
+{
+ auto releaseAll = [](auto *manager) noexcept {
+ const auto handles = manager->activeHandles();
+ for (const auto &handle : handles) {
+ manager->release(handle);
+ }
+ };
+
+ releaseAll(m_rhiTextureManager);
+ releaseAll(m_rhiBufferManager);
+ // releaseAll(m_rhiShaderManager);
+ releaseAll(m_rhiGraphicsPipelineManager);
+}
+
+} // Rhi
+
+} // Render
+
+} // Qt3DRender
+
+QT_END_NAMESPACE
diff --git a/src/plugins/renderers/rhi/managers/rhiresourcemanagers_p.h b/src/plugins/renderers/rhi/managers/rhiresourcemanagers_p.h
new file mode 100644
index 000000000..34758530d
--- /dev/null
+++ b/src/plugins/renderers/rhi/managers/rhiresourcemanagers_p.h
@@ -0,0 +1,153 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DRENDER_RENDER_RHI_RHIRESOURCEMANAGERS_P_H
+#define QT3DRENDER_RENDER_RHI_RHIRESOURCEMANAGERS_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/private/qt3drender_global_p.h>
+#include <Qt3DCore/private/qresourcemanager_p.h>
+#include <texture_p.h>
+#include <rhibuffer_p.h>
+#include <rhishader_p.h>
+#include <rhigraphicspipeline_p.h>
+#include <Qt3DRender/private/apishadermanager_p.h>
+#include <Qt3DRender/private/renderstateset_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+namespace Render {
+
+namespace Rhi {
+
+class Q_AUTOTEST_EXPORT RHIBufferManager
+ : public Qt3DCore::QResourceManager<RHIBuffer, Qt3DCore::QNodeId, Qt3DCore::NonLockingPolicy>
+{
+};
+
+class Q_AUTOTEST_EXPORT RHITextureManager
+ : public Qt3DCore::QResourceManager<RHITexture, Qt3DCore::QNodeId, Qt3DCore::NonLockingPolicy>
+{
+public:
+ QHash<RHITexture *, Qt3DCore::QNodeId> texNodeIdForRHITexture;
+};
+
+class Q_AUTOTEST_EXPORT RHIShaderManager : public APIShaderManager<RHIShader>
+{
+public:
+ explicit RHIShaderManager() : APIShaderManager<RHIShader>() { }
+};
+
+// Geometry | Shader | RenderStateMask
+struct GraphicsPipelineIdentifier
+{
+ HGeometry geometry;
+ Qt3DCore::QNodeId shader;
+ int renderViewIndex;
+};
+
+class Q_AUTOTEST_EXPORT RHIGraphicsPipelineManager
+ : public Qt3DCore::QResourceManager<RHIGraphicsPipeline, GraphicsPipelineIdentifier,
+ Qt3DCore::NonLockingPolicy>
+{
+public:
+ RHIGraphicsPipelineManager() { }
+};
+
+class Q_AUTOTEST_EXPORT RHIResourceManagers
+{
+public:
+ RHIResourceManagers();
+ ~RHIResourceManagers();
+
+ inline RHIShaderManager *rhiShaderManager() const noexcept { return m_rhiShaderManager; }
+ inline RHITextureManager *rhiTextureManager() const noexcept { return m_rhiTextureManager; }
+ inline RHIBufferManager *rhiBufferManager() const noexcept { return m_rhiBufferManager; }
+ inline RHIGraphicsPipelineManager *rhiGraphicsPipelineManager() const noexcept
+ {
+ return m_rhiGraphicsPipelineManager;
+ }
+
+ void releaseAllResources();
+
+private:
+ RHIBufferManager *m_rhiBufferManager;
+ RHIShaderManager *m_rhiShaderManager;
+ RHITextureManager *m_rhiTextureManager;
+ RHIGraphicsPipelineManager *m_rhiGraphicsPipelineManager;
+};
+
+inline uint qHash(const GraphicsPipelineIdentifier &key, uint seed)
+{
+ const QPair<HGeometry, Qt3DCore::QNodeId> p = { key.geometry, key.shader };
+ using QT_PREPEND_NAMESPACE(qHash);
+ return qHash(p, seed) + qHash(key.renderViewIndex, seed);
+}
+
+inline bool operator==(const GraphicsPipelineIdentifier &a, const GraphicsPipelineIdentifier &b)
+{
+ return a.geometry == b.geometry && a.shader == b.shader
+ && a.renderViewIndex == b.renderViewIndex;
+}
+
+} // Rhi
+
+} // Render
+
+} // Qt3DRender
+
+Q_DECLARE_RESOURCE_INFO(Qt3DRender::Render::Rhi::RHIGraphicsPipeline, Q_REQUIRES_CLEANUP)
+Q_DECLARE_RESOURCE_INFO(Qt3DRender::Render::Rhi::RHITexture, Q_REQUIRES_CLEANUP)
+Q_DECLARE_RESOURCE_INFO(Qt3DRender::Render::Rhi::RHIBuffer, Q_REQUIRES_CLEANUP)
+Q_DECLARE_RESOURCE_INFO(Qt3DRender::Render::Rhi::RHIShader, Q_REQUIRES_CLEANUP)
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDER_RHI_RHIRESOURCEMANAGERS_P_H
diff --git a/src/plugins/renderers/rhi/renderer/commandexecuter.cpp b/src/plugins/renderers/rhi/renderer/commandexecuter.cpp
new file mode 100644
index 000000000..cfa6531be
--- /dev/null
+++ b/src/plugins/renderers/rhi/renderer/commandexecuter.cpp
@@ -0,0 +1,400 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
+** 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 <submissioncontext_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 &) noexcept
+{
+ return QJsonObject();
+}
+
+template<typename Type>
+QJsonValue typeToJsonValue(const Type &t) noexcept
+{
+ Q_UNUSED(t);
+ return QJsonValue();
+}
+
+template<>
+QJsonObject typeToJsonObj<QRectF>(const QRectF &rect) noexcept
+{
+ 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) noexcept
+{
+ 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) noexcept
+{
+ QJsonObject obj;
+
+ obj.insert(QLatin1String("width"), s.width());
+ obj.insert(QLatin1String("height"), s.height());
+
+ return obj;
+}
+
+template<>
+QJsonValue typeToJsonValue<QSize>(const QSize &s) noexcept
+{
+ QJsonArray value;
+
+ value.push_back(s.width());
+ value.push_back(s.height());
+
+ return value;
+}
+
+template<>
+QJsonObject typeToJsonObj<QVector3D>(const QVector3D &v) noexcept
+{
+ 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) noexcept
+{
+ 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) noexcept
+{
+ QJsonObject obj;
+ obj.insert(QLatin1String("id"), qint64(v.id()));
+ return obj;
+}
+
+template<>
+QJsonValue typeToJsonValue<Qt3DCore::QNodeId>(const Qt3DCore::QNodeId &v) noexcept
+{
+ QJsonValue value(qint64(v.id()));
+ return value;
+}
+
+template<>
+QJsonObject typeToJsonObj<QVector4D>(const QVector4D &v) noexcept
+{
+ 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) noexcept
+{
+ 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) noexcept
+{
+ 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) noexcept
+{
+ 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) noexcept
+{
+ 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) noexcept
+{
+ 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::Rhi::ShaderParameterPack &pack) noexcept
+{
+ QJsonObject obj;
+
+ const Render::Rhi::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::Rhi::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::Rhi::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::Rhi::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::Rhi::Renderer *renderer) : m_renderer(renderer) { }
+
+// Render thread
+void CommandExecuter::performAsynchronousCommandExecution(
+ const QVector<Render::Rhi::RenderView *> &views)
+{
+ RHI_UNIMPLEMENTED;
+ //* 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::Rhi::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 (Render::Rhi::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)
+{
+ RHI_UNIMPLEMENTED;
+ //* // 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/rhi/renderer/commandexecuter_p.h b/src/plugins/renderers/rhi/renderer/commandexecuter_p.h
new file mode 100644
index 000000000..924930a49
--- /dev/null
+++ b/src/plugins/renderers/rhi/renderer/commandexecuter_p.h
@@ -0,0 +1,98 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
+** 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 {
+namespace Rhi {
+class Renderer;
+class RenderView;
+} // Rhi
+} // Render
+
+namespace Debug {
+
+class CommandExecuter
+{
+public:
+ explicit CommandExecuter(Render::Rhi::Renderer *renderer);
+
+ void performAsynchronousCommandExecution(const QVector<Render::Rhi::RenderView *> &views);
+
+ QVariant executeCommand(const QStringList &args);
+
+private:
+ Render::Rhi::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/rhi/renderer/logging.cpp b/src/plugins/renderers/rhi/renderer/logging.cpp
new file mode 100644
index 000000000..29bdb2145
--- /dev/null
+++ b/src/plugins/renderers/rhi/renderer/logging.cpp
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "logging_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+namespace Render {
+
+namespace Rhi {
+
+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 Rhi
+
+} // namespace Render
+
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
diff --git a/src/plugins/renderers/rhi/renderer/logging_p.h b/src/plugins/renderers/rhi/renderer/logging_p.h
new file mode 100644
index 000000000..45d63978d
--- /dev/null
+++ b/src/plugins/renderers/rhi/renderer/logging_p.h
@@ -0,0 +1,85 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DRENDER_RENDER_RHI_RENDERLOGGING_P_H
+#define QT3DRENDER_RENDER_RHI_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 {
+
+namespace Rhi {
+
+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 Rhi
+
+} // namespace Render
+
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDER_RHI_RENDERLOGGING_P_H
diff --git a/src/plugins/renderers/rhi/renderer/rendercommand.cpp b/src/plugins/renderers/rhi/renderer/rendercommand.cpp
new file mode 100644
index 000000000..8320abac1
--- /dev/null
+++ b/src/plugins/renderers/rhi/renderer/rendercommand.cpp
@@ -0,0 +1,112 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "rendercommand_p.h"
+#include "renderer/rhigraphicspipeline_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+namespace Render {
+namespace Rhi {
+
+RenderCommand::RenderCommand()
+ : m_rhiShader(nullptr),
+ m_stateSet(nullptr),
+ m_depth(0.0f),
+ m_changeCost(0),
+ m_type(RenderCommand::Draw),
+ m_workGroups(),
+ 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(Qt3DRender::QAttribute::UnsignedShort),
+ m_indirectAttributeByteOffset(0),
+ m_drawIndexed(false),
+ m_drawIndirect(false),
+ m_primitiveRestartEnabled(false),
+ m_isValid(false),
+ indexAttribute(nullptr),
+ indexBuffer(nullptr),
+ m_commandUBO(),
+ pipeline(nullptr)
+
+{
+ m_workGroups[0] = 0;
+ m_workGroups[1] = 0;
+ m_workGroups[2] = 0;
+}
+
+bool RenderCommand::isValid() const noexcept
+{
+ return m_rhiShader && pipeline && pipeline->pipeline();
+}
+
+bool operator==(const RenderCommand &a, const RenderCommand &b) noexcept
+{
+ return (a.m_rhiShader == b.m_rhiShader && 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 Rhi
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
diff --git a/src/plugins/renderers/rhi/renderer/rendercommand_p.h b/src/plugins/renderers/rhi/renderer/rendercommand_p.h
new file mode 100644
index 000000000..e6924a60c
--- /dev/null
+++ b/src/plugins/renderers/rhi/renderer/rendercommand_p.h
@@ -0,0 +1,211 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 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_RHI_RENDERCOMMAND_H
+#define QT3DRENDER_RENDER_RHI_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 <rhihandle_types_p.h>
+#include <renderviewjobutils_p.h>
+#include <Qt3DRender/private/handle_types_p.h>
+#include <Qt3DRender/qgeometryrenderer.h>
+#include <QOpenGLShaderProgram>
+#include <QOpenGLTexture>
+#include <QMatrix4x4>
+#include <QtGui/private/qrhi_p.h>
+#include <Qt3DRender/qattribute.h>
+
+QT_BEGIN_NAMESPACE
+class QRhiGraphicsPipeline;
+class QRhiShaderResourceBindings;
+
+namespace Qt3DRender {
+
+namespace Render {
+
+class RenderStateSet;
+using RenderStateSetPtr = QSharedPointer<RenderStateSet>;
+
+namespace Rhi {
+
+class RHIShader;
+class RHIGraphicsPipeline;
+
+struct CommandUBO
+{
+ float modelMatrix[16];
+ float inverseModelMatrix[16];
+ float modelViewMatrix[16];
+ float modelNormalMatrix[12];
+ float inverseModelViewMatrix[16];
+ float mvp[16];
+ float inverseModelViewProjectionMatrix[16];
+};
+static_assert(sizeof(CommandUBO) == 6 * (16 * sizeof(float)) + 1 * (12 * sizeof(float)),
+ "UBO doesn't match std140");
+
+class Q_AUTOTEST_EXPORT RenderCommand
+{
+public:
+ RenderCommand();
+
+ bool isValid() const noexcept;
+
+ HMaterial m_material; // Purely used to ease sorting (minimize stage changes, binding changes
+ // ....)
+ RHIShader *m_rhiShader; // 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;
+ Qt3DRender::QAttribute::VertexBaseType m_indexAttributeDataType;
+ uint m_indirectAttributeByteOffset;
+ bool m_drawIndexed;
+ bool m_drawIndirect;
+ bool m_primitiveRestartEnabled;
+ bool m_isValid;
+
+ QVarLengthArray<QRhiCommandBuffer::VertexInput, 8> vertex_input;
+
+ const Attribute *indexAttribute {};
+ QRhiBuffer *indexBuffer {};
+
+ CommandUBO m_commandUBO;
+ RHIGraphicsPipeline *pipeline {};
+};
+
+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 Rhi
+
+} // namespace Render
+
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDER_RHI_RENDERCOMMAND_H
diff --git a/src/plugins/renderers/rhi/renderer/renderer.cpp b/src/plugins/renderers/rhi/renderer/renderer.cpp
new file mode 100644
index 000000000..ee9a26a98
--- /dev/null
+++ b/src/plugins/renderers/rhi/renderer/renderer.cpp
@@ -0,0 +1,2537 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 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/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/qcameralens.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 <Qt3DRender/private/stringtoint_p.h>
+#include <Qt3DRender/private/qrenderaspect_p.h>
+
+#include <rhibuffer_p.h>
+#include <rhigraphicspipeline_p.h>
+
+#include <rendercommand_p.h>
+#include <renderqueue_p.h>
+#include <renderview_p.h>
+#include <texture_p.h>
+#include <renderviewbuilder_p.h>
+#include <rhiresourcemanagers_p.h>
+#include <commandexecuter_p.h>
+#include <submissioncontext_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 <QKeyEvent>
+#include <QMouseEvent>
+
+#include <QtGui/private/qopenglcontext_p.h>
+#include <QGuiApplication>
+
+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 Rhi {
+
+namespace {
+
+class CachingLightGatherer : public LightGatherer
+{
+public:
+ CachingLightGatherer(RendererCache *cache) : LightGatherer(), m_cache(cache) { }
+
+ void run() override
+ {
+ LightGatherer::run();
+
+ QMutexLocker lock(m_cache->mutex());
+ m_cache->gatheredLights = lights();
+ m_cache->environmentLight = environmentLight();
+ }
+
+private:
+ RendererCache *m_cache;
+};
+
+class CachingRenderableEntityFilter : public RenderableEntityFilter
+{
+public:
+ CachingRenderableEntityFilter(RendererCache *cache) : RenderableEntityFilter(), m_cache(cache)
+ {
+ }
+
+ void run() override
+ {
+ RenderableEntityFilter::run();
+
+ QVector<Entity *> selectedEntities = filteredEntities();
+ std::sort(selectedEntities.begin(), selectedEntities.end());
+
+ QMutexLocker lock(m_cache->mutex());
+ m_cache->renderableEntities = selectedEntities;
+ }
+
+private:
+ RendererCache *m_cache;
+};
+
+class CachingComputableEntityFilter : public ComputableEntityFilter
+{
+public:
+ CachingComputableEntityFilter(RendererCache *cache) : ComputableEntityFilter(), m_cache(cache)
+ {
+ }
+
+ void run() override
+ {
+ ComputableEntityFilter::run();
+
+ QVector<Entity *> selectedEntities = filteredEntities();
+ std::sort(selectedEntities.begin(), selectedEntities.end());
+
+ QMutexLocker lock(m_cache->mutex());
+ m_cache->computeEntities = selectedEntities;
+ }
+
+private:
+ RendererCache *m_cache;
+};
+
+int locationForAttribute(Attribute *attr, RHIShader *shader) noexcept
+{
+ const QVector<ShaderAttribute> attribInfo = shader->attributes();
+ const auto it = std::find_if(
+ attribInfo.begin(), attribInfo.end(),
+ [attr](const ShaderAttribute &sAttr) { return attr->nameId() == sAttr.m_nameId; });
+ if (it != attribInfo.end())
+ return it->m_location;
+ return -1;
+}
+
+} // 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_aspect(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_exposed(0),
+ m_lastFrameCorrect(0),
+ m_glContext(nullptr),
+ m_time(0),
+ m_settings(nullptr),
+ m_updateShaderDataTransformJob(Render::UpdateShaderDataTransformJobPtr::create()),
+ m_cleanupJob(Render::FrameCleanupJobPtr::create()),
+ m_sendBufferCaptureJob(Render::SendBufferCaptureJobPtr::create()),
+ m_filterCompatibleTechniqueJob(FilterCompatibleTechniqueJobPtr::create()),
+ m_lightGathererJob(new CachingLightGatherer(&m_cache)),
+ m_renderableEntityFilterJob(new CachingRenderableEntityFilter(&m_cache)),
+ m_computableEntityFilterJob(new CachingComputableEntityFilter(&m_cache)),
+ m_bufferGathererJob(SynchronizerJobPtr::create([this] { lookForDirtyBuffers(); },
+ JobTypes::DirtyBufferGathering)),
+ m_textureGathererJob(SynchronizerJobPtr::create([this] { lookForDirtyTextures(); },
+ JobTypes::DirtyTextureGathering)),
+ m_introspectShaderJob(SynchronizerPostFramePtr::create(
+ [this] { reloadDirtyShaders(); },
+ [this](Qt3DCore::QAspectManager *m) { sendShaderChangesToFrontend(m); },
+ JobTypes::DirtyShaderGathering)),
+ m_ownedContext(false),
+ m_offscreenHelper(nullptr),
+ m_RHIResourceManagers(nullptr),
+ m_commandExecuter(new Qt3DRender::Debug::CommandExecuter(this)),
+ m_shouldSwapBuffers(true)
+{
+ std::fill_n(m_textureTransform, 4, 0.f);
+
+ // 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();
+
+ m_introspectShaderJob->addDependency(m_filterCompatibleTechniqueJob);
+
+ 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_RHIResourceManagers;
+
+ 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;
+}
+
+API Renderer::api() const
+{
+ return API::OpenGL;
+}
+
+qint64 Renderer::time() const
+{
+ return m_time;
+}
+
+void Renderer::setTime(qint64 time)
+{
+ m_time = time;
+}
+
+void Renderer::setJobsInLastFrame(int jobsInLastFrame)
+{
+ m_jobsInLastFrame = jobsInLastFrame;
+}
+
+void Renderer::setAspect(QRenderAspect *aspect)
+{
+ m_aspect = aspect;
+ m_updateShaderDataTransformJob->addDependency(
+ QRenderAspectPrivate::get(aspect)->m_worldTransformJob);
+}
+
+void Renderer::setNodeManagers(NodeManagers *managers)
+{
+ m_nodesManager = managers;
+ m_RHIResourceManagers = new RHIResourceManagers();
+ m_scene2DResourceAccessor.reset(new ResourceAccessor(this, m_nodesManager));
+
+ m_updateShaderDataTransformJob->setManagers(m_nodesManager);
+ m_cleanupJob->setManagers(m_nodesManager);
+ m_filterCompatibleTechniqueJob->setManager(m_nodesManager->techniqueManager());
+ 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());
+}
+
+QRenderAspect *Renderer::aspect() const
+{
+ return m_aspect;
+}
+
+NodeManagers *Renderer::nodeManagers() const
+{
+ return m_nodesManager;
+}
+
+/*!
+ \internal
+
+ Return context which can be used to share resources safely
+ with qt3d main render context.
+*/
+QOpenGLContext *Renderer::shareContext() const
+{
+ return 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)
+{
+ RHI_UNIMPLEMENTED;
+
+ Texture *tex = m_nodesManager->textureManager()->lookupResource(nodeId);
+ if (!tex)
+ return false;
+
+ RHITexture *glTex = m_RHIResourceManagers->rhiTextureManager()->lookupResource(tex->peerId());
+ if (!glTex)
+ return false;
+
+ if (glTex->isDirty())
+ return false;
+
+ if (!readonly)
+ glTex->setExternalRenderingEnabled(true);
+
+ // RHITexture::TextureUpdateInfo texInfo =
+ // glTex->createOrUpdateRhiTexture(m_submissionContext.data()); *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);
+
+ // RHI initialization
+ {
+ qCDebug(Backend) << Q_FUNC_INFO << "Requesting renderer initialize";
+ m_submissionContext->initialize();
+
+ // We need to adapt texture coordinates
+ // m_textureTransform is (a;b) in texCoord = a * texCoord + b
+ if (m_submissionContext->rhi()->isYUpInFramebuffer()) {
+ // OpenGL case - that is what we assume to be the default so we do not change
+ // anything
+ m_textureTransform[0] = 1.f;
+ m_textureTransform[1] = 1.f;
+ m_textureTransform[2] = 0.f;
+ m_textureTransform[3] = 0.f;
+ } else {
+ // Other cases : y = 1 - y
+ m_textureTransform[0] = 1.f;
+ m_textureTransform[1] = -1.f;
+ m_textureTransform[2] = 0.f;
+ m_textureTransform[3] = 1.f;
+ }
+
+ // 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);
+ return;
+ }
+}
+
+/*!
+ * \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_RHIResourceManagers;
+ m_RHIResourceManagers = 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<HRHITexture> activeTexturesHandles =
+ //m_RHIResourceManagers->rhiTextureManager()->activeHandles();
+ //* for (const HRHITexture &textureHandle : activeTexturesHandles) {
+ //* RHITexture *tex = m_RHIResourceManagers->rhiTextureManager()->data(textureHandle);
+ //* tex->destroy();
+ //* }
+ //*
+ //* // Do the same thing with buffers
+ //* const QVector<HRHIBuffer> activeBuffers =
+ //m_RHIResourceManagers->rhiBufferManager()->activeHandles();
+ //* for (const HRHIBuffer &bufferHandle : activeBuffers) {
+ //* RHIBuffer *buffer = m_RHIResourceManagers->rhiBufferManager()->data(bufferHandle);
+ //* buffer->destroy(m_submissionContext.data());
+ //* }
+ //*
+ //* // Do the same thing with shaders
+ //* const QVector<RHIShader *> shaders =
+ //m_RHIResourceManagers->rhiShaderManager()->takeActiveResources();
+ //* qDeleteAll(shaders);
+ //*
+ //*
+ //* context->doneCurrent();
+ //* } else {
+ //* qWarning() << "Failed to make context current: OpenGL resources will not be destroyed";
+ //* }
+ //*
+ //* if (m_ownedContext)
+ //* delete context;
+
+ 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_cleanupJob->setRoot(m_renderSceneRoot);
+
+ // Set all flags to dirty
+ m_dirtyBits.marked |= AbstractRenderer::AllDirty;
+}
+
+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;
+
+ bool mustCleanResources = false;
+
+ // 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::Rhi::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);
+
+ QVector<RHIPassInfo> rhiPassesInfo;
+
+ if (canRender()) {
+ QSurface *surface = nullptr;
+ for (const RenderView *rv : renderViews) {
+ surface = rv->surface();
+ if (surface)
+ break;
+ }
+
+ // In case we did not draw because e.g. there wase no swapchain,
+ // we keep the resource updates from the previous frame.
+ if (!m_submissionContext->m_currentUpdates) {
+ m_submissionContext->m_currentUpdates =
+ m_submissionContext->rhi()->nextResourceUpdateBatch();
+ }
+
+ // 1) Execute commands for buffer uploads, texture updates, shader loading first
+ updateResources();
+
+ rhiPassesInfo = prepareCommandsSubmission(renderViews);
+ // 2) Update Pipelines and copy data into commands to allow concurrent submission
+ preprocessingComplete = true;
+
+ bool hasCommands = false;
+ for (const RenderView *rv : renderViews) {
+ const auto &commands = rv->commands();
+ hasCommands = std::any_of(commands.begin(), commands.end(),
+ [](const RenderCommand &cmd) { return cmd.isValid(); });
+ if (hasCommands)
+ break;
+ }
+
+ if (hasCommands) {
+ // Scoped to destroy surfaceLock
+ SurfaceLocker surfaceLock(surface);
+ const bool surfaceIsValid = (surface && surfaceLock.isSurfaceValid());
+ if (surfaceIsValid) {
+ beganDrawing = m_submissionContext->beginDrawing(surface);
+ if (beganDrawing) {
+ // Purge shader which aren't used any longer
+ static int callCount = 0;
+ ++callCount;
+ const int shaderPurgePeriod = 600;
+ if (callCount % shaderPurgePeriod == 0)
+ m_RHIResourceManagers->rhiShaderManager()->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 (beganDrawing) {
+ 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(rhiPassesInfo);
+
+ // Perform any required cleanup of the Graphics resources (Buffers deleted, Shader
+ // deleted...)
+ mustCleanResources = true;
+ }
+ }
+
+ // 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);
+
+ if (mustCleanResources)
+ cleanGraphicsResources();
+ }
+}
+
+// Called by RenderViewJobs
+// When the frameQueue is complete and we are using a renderThread
+// we allow the render thread to proceed
+void Renderer::enqueueRenderView(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_submissionContext->format();
+}
+
+void Renderer::updateGraphicsPipeline(RenderCommand &cmd, RenderView *rv, int renderViewIndex)
+{
+ if (!cmd.m_rhiShader) {
+ qDebug() << "Warning: command has no shader";
+ return;
+ }
+
+ // The Graphics Pipeline defines
+ // - Render State (Depth, Culling, Stencil, Blending)
+ // - Shader Resources Binding
+ // - Shader Vertex Attribute Layout
+
+ // This means we need to have one GraphicsPipeline per
+ // - geometry
+ // - material
+ // - state (RV + RC)
+
+ RenderStateSet *renderState = nullptr;
+ {
+ RenderStateSet *globalState =
+ (rv->stateSet() != nullptr) ? rv->stateSet() : m_defaultRenderStateSet;
+
+ // Merge global state into local state
+ RenderStateSet *localState = cmd.m_stateSet.data();
+ if (localState != nullptr) {
+ localState->merge(globalState);
+ renderState = localState;
+ } else {
+ renderState = globalState;
+ }
+ }
+
+ // Try to retrieve existing pipeline
+ auto &pipelineManager = *m_RHIResourceManagers->rhiGraphicsPipelineManager();
+ const GraphicsPipelineIdentifier pipelineKey { cmd.m_geometry, cmd.m_shaderId,
+ renderViewIndex };
+ RHIGraphicsPipeline *graphicsPipeline = pipelineManager.getOrCreateResource(pipelineKey);
+ // TO DO: Ensure we find a way to know when the state is dirty to trigger a rebuild
+
+ if (!graphicsPipeline) {
+ qDebug() << "Warning : could not create a graphics pipeline";
+ return;
+ }
+
+ // Increase score so that we know the pipeline was used for this frame and shouldn't be
+ // destroyed
+ graphicsPipeline->increaseScore();
+
+ // TO DO: Set to true if geometry, shader or render state dirty
+ bool requiredRebuild = false;
+
+ // Note: we can rebuild add/remove things from the QRhiShaderResourceBindings after having
+ // created the pipeline and rebuild it. Changes should be picked up automatically
+
+ // Create pipeline if it doesn't exist or needs to be updated
+ if (graphicsPipeline->pipeline() == nullptr || requiredRebuild) {
+ bool ok = true;
+
+ const SubmissionContext::SwapChainInfo *swapchain =
+ m_submissionContext->swapChainForSurface(rv->surface());
+ if (!swapchain || !swapchain->swapChain || !swapchain->renderPassDescriptor)
+ return;
+
+ // TO DO: Find a way to recycle those
+ // Create Per Command UBO
+ QRhiBuffer *commandUBO = m_submissionContext->rhi()->newBuffer(
+ QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, sizeof(CommandUBO));
+ QRhiBuffer *rvUBO = m_submissionContext->rhi()->newBuffer(
+ QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, sizeof(RenderViewUBO));
+ commandUBO->build();
+ rvUBO->build();
+ graphicsPipeline->setCommandUBO(commandUBO);
+ graphicsPipeline->setRenderViewUBO(rvUBO);
+
+ QVector<QRhiShaderResourceBinding> uboBindings;
+ uboBindings << QRhiShaderResourceBinding::uniformBuffer(
+ 0,
+ QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage,
+ rvUBO)
+ << QRhiShaderResourceBinding::uniformBuffer(
+ 1,
+ QRhiShaderResourceBinding::VertexStage
+ | QRhiShaderResourceBinding::FragmentStage,
+ commandUBO);
+
+ // Create additional empty UBO Buffer for UBO with binding point > 1 (since we assume 0 and
+ // 1 and for Qt3D standard values)
+ const QVector<ShaderUniformBlock> uniformBlocks = cmd.m_rhiShader->uniformBlocks();
+ QHash<int, RHIGraphicsPipeline::UBOBuffer> uboBuffers;
+ for (const ShaderUniformBlock &block : uniformBlocks) {
+ if (block.m_binding > 1) {
+ auto handle = m_RHIResourceManagers->rhiBufferManager()->allocateResource();
+ RHIBuffer *ubo = m_RHIResourceManagers->rhiBufferManager()->data(handle);
+ Q_ASSERT(ubo);
+ const QByteArray rawData(block.m_size, '\0');
+ ubo->allocate(m_submissionContext.data(), rawData, true);
+ ok = ubo->bind(m_submissionContext.data(), RHIBuffer::UniformBuffer);
+ uboBuffers[block.m_binding] = { handle, ubo };
+ uboBindings << QRhiShaderResourceBinding::uniformBuffer(
+ block.m_binding,
+ QRhiShaderResourceBinding::VertexStage
+ | QRhiShaderResourceBinding::FragmentStage,
+ ubo->rhiBuffer());
+ }
+ }
+ graphicsPipeline->setUBOs(uboBuffers);
+
+ // Samplers
+ for (const auto &textureParameter : cmd.m_parameterPack.textures()) {
+ const auto handle = m_RHIResourceManagers->rhiTextureManager()->getOrAcquireHandle(
+ textureParameter.nodeId);
+ const auto textureData = m_RHIResourceManagers->rhiTextureManager()->data(handle);
+
+ for (const ShaderAttribute &samplerAttribute : cmd.m_rhiShader->samplers()) {
+ if (samplerAttribute.m_nameId == textureParameter.glslNameId) {
+ const auto rhiTexture = textureData->getRhiTexture();
+ const auto rhiSampler = textureData->getRhiSampler();
+ if (rhiTexture && rhiSampler) {
+ uboBindings.push_back(QRhiShaderResourceBinding::sampledTexture(
+ samplerAttribute.m_location,
+ QRhiShaderResourceBinding::FragmentStage, rhiTexture, rhiSampler));
+ }
+ }
+ }
+ }
+
+ QRhiShaderResourceBindings *shaderResourceBindings =
+ m_submissionContext->rhi()->newShaderResourceBindings();
+ assert(shaderResourceBindings);
+
+ shaderResourceBindings->setBindings(uboBindings.cbegin(), uboBindings.cend());
+ ok = shaderResourceBindings->build();
+ assert(ok);
+
+ // Create pipeline
+ QRhiGraphicsPipeline *pipeline = m_submissionContext->rhi()->newGraphicsPipeline();
+ graphicsPipeline->setShaderResourceBindings(shaderResourceBindings);
+ graphicsPipeline->setPipeline(pipeline);
+ assert(pipeline);
+
+ const QShader vertexShader = cmd.m_rhiShader->shaderStage(QShader::VertexStage);
+ const QShader fragmentShader = cmd.m_rhiShader->shaderStage(QShader::FragmentStage);
+
+ assert(vertexShader.isValid());
+ assert(fragmentShader.isValid());
+
+ pipeline->setShaderStages({ { QRhiShaderStage::Vertex, vertexShader },
+ { QRhiShaderStage::Fragment, fragmentShader } });
+
+ QVarLengthArray<QRhiVertexInputBinding, 8> inputBindings;
+ QVarLengthArray<QRhiVertexInputAttribute, 8> rhiAttributes;
+
+ const auto geom = cmd.m_geometry;
+ const auto &attributes = geom->attributes();
+
+ struct BufferBinding
+ {
+ Qt3DCore::QNodeId bufferId;
+ uint stride;
+ QRhiVertexInputBinding::Classification classification;
+ uint attributeDivisor;
+ };
+ QVector<BufferBinding> uniqueBindings;
+
+ auto rhiAttributeType = [](Attribute *attr) {
+ switch (attr->vertexBaseType()) {
+ case QAttribute::Byte:
+ case QAttribute::UnsignedByte: {
+ if (attr->vertexSize() == 1)
+ return QRhiVertexInputAttribute::UNormByte;
+ if (attr->vertexSize() == 2)
+ return QRhiVertexInputAttribute::UNormByte2;
+ if (attr->vertexSize() == 4)
+ return QRhiVertexInputAttribute::UNormByte4;
+ Q_FALLTHROUGH();
+ }
+ case QAttribute::Float: {
+ if (attr->vertexSize() == 1)
+ return QRhiVertexInputAttribute::Float;
+ if (attr->vertexSize() == 2)
+ return QRhiVertexInputAttribute::Float2;
+ if (attr->vertexSize() == 3)
+ return QRhiVertexInputAttribute::Float3;
+ if (attr->vertexSize() == 4)
+ return QRhiVertexInputAttribute::Float4;
+ Q_FALLTHROUGH();
+ }
+ default:
+ qWarning() << "Attribute type not handles by RHI";
+ Q_UNREACHABLE();
+ }
+ };
+
+ // QRhiVertexInputBinding -> specifies the stride of an attribute, whether it's per vertex
+ // or per instance and the instance divisor QRhiVertexInputAttribute -> specifies the format
+ // of the attribute (offset, type), the shader location and the index of the binding
+ // QRhiCommandBuffer::VertexInput -> binds a buffer to a binding
+
+ QHash<int, int> attributeNameToBinding;
+
+ for (Qt3DCore::QNodeId attribute_id : attributes) {
+ Attribute *attrib = m_nodesManager->attributeManager()->lookupResource(attribute_id);
+ if (attrib->attributeType() == QAttribute::VertexAttribute) {
+ const bool isPerInstanceAttr = attrib->divisor() != 0;
+ const QRhiVertexInputBinding::Classification classification = isPerInstanceAttr
+ ? QRhiVertexInputBinding::PerInstance
+ : QRhiVertexInputBinding::PerVertex;
+ const BufferBinding binding { attrib->bufferId(), attrib->byteStride(),
+ classification,
+ isPerInstanceAttr ? attrib->divisor() : 1U };
+
+ const auto it =
+ std::find_if(uniqueBindings.begin(), uniqueBindings.end(),
+ [binding](const BufferBinding &a) {
+ return binding.bufferId == a.bufferId
+ && binding.stride == a.stride
+ && binding.classification == a.classification
+ && binding.attributeDivisor == a.attributeDivisor;
+ });
+
+ int bindingIndex = uniqueBindings.size();
+ if (it == uniqueBindings.end())
+ uniqueBindings.push_back(binding);
+ else
+ bindingIndex = std::distance(uniqueBindings.begin(), it);
+
+ rhiAttributes.push_back({ bindingIndex,
+ locationForAttribute(attrib, cmd.m_rhiShader),
+ rhiAttributeType(attrib), attrib->byteOffset() });
+
+ attributeNameToBinding.insert(attrib->nameId(), bindingIndex);
+ }
+ }
+
+ inputBindings.resize(uniqueBindings.size());
+ for (int i = 0, m = uniqueBindings.size(); i < m; ++i) {
+ const BufferBinding binding = uniqueBindings.at(i);
+ /*
+ qDebug() << binding.bufferId
+ << binding.stride
+ << binding.classification
+ << binding.attributeDivisor;
+ */
+ inputBindings[i] = QRhiVertexInputBinding { binding.stride, binding.classification,
+ int(binding.attributeDivisor) };
+ }
+
+ QRhiVertexInputLayout inputLayout;
+ inputLayout.setBindings(inputBindings.begin(), inputBindings.end());
+ inputLayout.setAttributes(rhiAttributes.begin(), rhiAttributes.end());
+
+ pipeline->setVertexInputLayout(inputLayout);
+ pipeline->setShaderResourceBindings(shaderResourceBindings);
+
+ pipeline->setRenderPassDescriptor(swapchain->renderPassDescriptor);
+
+ graphicsPipeline->setAttributesToBindingHash(attributeNameToBinding);
+
+ // Render States
+ m_submissionContext->applyStateSet(renderState, pipeline);
+
+ ok = pipeline->build();
+ assert(ok);
+ }
+
+ // Record RHIGraphicsPipeline into command for later use
+ if (graphicsPipeline && graphicsPipeline->pipeline())
+ cmd.pipeline = graphicsPipeline;
+}
+
+// When this function is called, we must not be processing the commands for frame n+1
+QVector<Renderer::RHIPassInfo>
+Renderer::prepareCommandsSubmission(const QVector<RenderView *> &renderViews)
+{
+ // TO DO: Find a central place to initialize RHI resources
+ const int renderViewCount = renderViews.size();
+
+ // We need to have a single RHI RenderPass per RenderTarget
+ // as creating the pass clears the buffers
+ // 1) We need to find all adjacents RenderViews that have the same renderTarget
+ // and submit all of these as part of the same RHI pass
+ QVector<RHIPassInfo> rhiPassesInfo;
+
+ for (int i = 0; i < renderViewCount;) {
+ QVector<RenderView *> sameRenderTargetRVs;
+ QVector<QRhiBuffer *> rvUbos;
+ RenderView *refRV = renderViews.at(i);
+ sameRenderTargetRVs.push_back(refRV);
+
+ for (i = i + 1; i < renderViewCount; ++i) {
+ RenderView *curRV = renderViews.at(i);
+ if (refRV->renderTargetId() == curRV->renderTargetId()) {
+ sameRenderTargetRVs.push_back(curRV);
+ } else
+ break;
+ }
+
+ RHIPassInfo bucket;
+ bucket.rvs = std::move(sameRenderTargetRVs);
+ bucket.surface = refRV->surface();
+ bucket.renderTargetId = refRV->renderTargetId();
+ bucket.attachmentPack = refRV->attachmentPack();
+ rhiPassesInfo.push_back(bucket);
+ }
+
+ for (int i = 0; i < renderViewCount; ++i) {
+ RenderView *rv = renderViews.at(i);
+ QVector<RenderCommand> &commands = rv->commands();
+ for (RenderCommand &command : commands) {
+ // Update/Create GraphicsPipelines
+ 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);
+ // By this time shaders should have been loaded
+ RHIShader *shader = m_RHIResourceManagers->rhiShaderManager()->lookupResource(
+ command.m_shaderId);
+ if (!shader) {
+ qDebug() << "Warning: could not find shader";
+ continue;
+ }
+
+ // We should never have inserted a command for which these are null
+ // in the first place
+ Q_ASSERT(rGeometry && rGeometryRenderer && shader);
+
+ // 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);
+
+ updateGraphicsPipeline(command, rv, i);
+
+ } else if (command.m_type == RenderCommand::Compute) {
+ RHI_UNIMPLEMENTED;
+ // By this time shaders have been loaded
+ RHIShader *shader = m_RHIResourceManagers->rhiShaderManager()->lookupResource(
+ command.m_shaderId);
+ command.m_rhiShader = shader;
+ Q_ASSERT(shader);
+
+ // Prepare the ShaderParameterPack based on the active uniforms of the shader
+ // shader->prepareUniforms(command.m_parameterPack);
+ }
+ }
+ }
+
+ // 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
+ RHI_UNIMPLEMENTED;
+ for (Attribute *attribute : qAsConst(m_dirtyAttributes))
+ attribute->unsetDirty();
+ m_dirtyAttributes.clear();
+
+ for (Geometry *geometry : qAsConst(m_dirtyGeometry))
+ geometry->unsetDirty();
+ m_dirtyGeometry.clear();
+
+ return rhiPassesInfo;
+}
+
+// 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 (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::updateResources()
+{
+ {
+ 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->hasRHIBufferForBuffer(buffer))
+ m_submissionContext->rhiBufferForRenderBuffer(buffer);
+ // Update the RHIBuffer data
+ m_submissionContext->updateBuffer(buffer);
+ buffer->unsetDirty();
+ }
+ }
+
+#ifndef SHADER_LOADING_IN_COMMAND_THREAD
+ {
+ 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_RHIResourceManagers->rhiShaderManager());
+ }
+ }
+#endif
+
+ {
+ 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 RHITexture (the RHITexture 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
+ // RHITexture
+ if (m_submissionContext != nullptr) {
+ RHITextureManager *rhiTextureManager = m_RHIResourceManagers->rhiTextureManager();
+ const QVector<HRHITexture> glTextureHandles = rhiTextureManager->activeHandles();
+ // Upload texture data
+ for (const HRHITexture &glTextureHandle : glTextureHandles) {
+ RHI_UNIMPLEMENTED;
+ RHITexture *glTexture = rhiTextureManager->data(glTextureHandle);
+
+ // We create/update the actual GL texture using the GL context at this point
+ const RHITexture::TextureUpdateInfo info =
+ glTexture->createOrUpdateRhiTexture(m_submissionContext.data());
+
+ // RHITexture 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 = {
+ rhiTextureManager->texNodeIdForRHITexture.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)
+{
+ RHI_UNIMPLEMENTED;
+ // 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 RHITexture for the backend Texture
+ RHITextureManager *rhiTextureManager = m_RHIResourceManagers->rhiTextureManager();
+ RHITexture *rhiTexture = rhiTextureManager->lookupResource(texture->peerId());
+
+ // No RHITexture associated yet -> create it
+ if (rhiTexture == nullptr) {
+ rhiTexture = rhiTextureManager->getOrCreateResource(texture->peerId());
+ rhiTextureManager->texNodeIdForRHITexture.insert(rhiTexture, texture->peerId());
+ }
+
+ // Update RHITexture to match Texture instance
+ const Texture::DirtyFlags dirtyFlags = texture->dirtyFlags();
+ if (dirtyFlags.testFlag(Texture::DirtySharedTextureId))
+ rhiTexture->setSharedTextureId(texture->sharedTextureId());
+
+ if (dirtyFlags.testFlag(Texture::DirtyProperties))
+ rhiTexture->setProperties(texture->properties());
+
+ if (dirtyFlags.testFlag(Texture::DirtyParameters))
+ rhiTexture->setParameters(texture->parameters());
+
+ // Will make the texture requestUpload
+ if (dirtyFlags.testFlag(Texture::DirtyImageGenerators)) {
+ const QNodeIdVector textureImageIds = texture->textureImageIds();
+ QVector<RHITexture::Image> images;
+ images.reserve(textureImageIds.size());
+ // TODO: Move this into RHITexture 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 {
+ RHITexture::Image glImg { img->dataGenerator(), img->layer(), img->mipLevel(),
+ img->face() };
+ images.push_back(glImg);
+ }
+ }
+ rhiTexture->setImages(images);
+ }
+
+ // Will make the texture requestUpload
+ if (dirtyFlags.testFlag(Texture::DirtyDataGenerator))
+ rhiTexture->setGenerator(texture->dataGenerator());
+
+ // Will make the texture requestUpload
+ if (dirtyFlags.testFlag(Texture::DirtyPendingDataUpdates))
+ rhiTexture->addTextureDataUpdates(texture->takePendingTextureDataUpdates());
+
+ // Unset the dirty flag on the texture
+ texture->unsetDirty();
+}
+
+// Render Thread
+void Renderer::cleanupTexture(Qt3DCore::QNodeId cleanedUpTextureId)
+{
+ RHITextureManager *rhiTextureManager = m_RHIResourceManagers->rhiTextureManager();
+ RHITexture *glTexture = rhiTextureManager->lookupResource(cleanedUpTextureId);
+
+ // Destroying the RHITexture implicitely also destroy the GL resources
+ if (glTexture != nullptr) {
+ rhiTextureManager->releaseResource(cleanedUpTextureId);
+ rhiTextureManager->texNodeIdForRHITexture.remove(glTexture);
+ }
+}
+
+// Render Thread
+void Renderer::cleanupShader(const Shader *shader)
+{
+ RHIShaderManager *rhiShaderManager = m_RHIResourceManagers->rhiShaderManager();
+ RHIShader *glShader = rhiShaderManager->lookupResource(shader->peerId());
+
+ if (glShader != nullptr)
+ rhiShaderManager->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<RHIPassInfo> &rhiPassesInfo)
+{
+ QElapsedTimer timer;
+ quint64 queueElapsed = 0;
+ timer.start();
+
+ 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 = 0; // m_submissionContext->boundFrameBufferObject();
+ QSurface *surface = nullptr;
+ QSurface *previousSurface = nullptr;
+ QSurface *lastUsedSurface = nullptr;
+
+ const int rhiPassesCount = rhiPassesInfo.size();
+
+ for (int i = 0; i < rhiPassesCount; ++i) {
+ // Initialize GraphicsContext for drawing
+ const RHIPassInfo &rhiPassInfo = rhiPassesInfo.at(i);
+
+ // Initialize Previous surface the first time we enter this loop
+ if (i == 0) {
+ for (const RenderView *rv : rhiPassInfo.rvs) {
+ previousSurface = rv->surface();
+ if (previousSurface)
+ break;
+ }
+ }
+
+ // Check if using the same surface as the previous RHIPassInfo.
+ // If not, we have to free up the context from the previous surface
+ // and make the context current on the new surface
+ surface = rhiPassInfo.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 rhipassinfo
+ // 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)
+ // qWarning() << "RHI Doesn't support MemoryBarrier";
+
+ // Set RenderTarget ...
+ // Activate RenderTarget
+ {
+ m_submissionContext->activateRenderTarget(rhiPassInfo.renderTargetId,
+ rhiPassInfo.attachmentPack, lastBoundFBOId);
+ }
+
+ // Execute the render commands
+ if (!executeCommandsSubmission(rhiPassInfo))
+ 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()) {
+ // RHI_UNIMPLEMENTED;
+ //* 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())
+ // {
+ // RHI_UNIMPLEMENTED;
+ ////* downloadGLBuffers();
+ // }
+
+ // // Perform BlitFramebuffer operations
+ // if (renderView->hasBlitFramebufferInfo()) {
+ // RHI_UNIMPLEMENTED;
+ ////* 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 RHI Passes " << i + 1 << "/"
+ << rhiPassesCount << "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()) {
+ RHI_UNIMPLEMENTED;
+ // m_submissionContext->bindFramebuffer(lastBoundFBOId,
+ // GraphicsHelperInterface::FBOReadAndDraw);
+ }
+
+ // Reset state and call doneCurrent if the surface
+ // is valid and was actually activated
+ if (lastUsedSurface) {
+ RHI_UNIMPLEMENTED;
+ // 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 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);
+}
+
+void Renderer::setPendingEvents(const QList<QPair<QObject *, QMouseEvent>> &mouseEvents,
+ const QList<QKeyEvent> &keyEvents)
+{
+ QMutexLocker l(&m_frameEventsMutex);
+ m_frameMouseEvents = mouseEvents;
+ m_frameKeyEvents = keyEvents;
+}
+
+// Jobs we may have to run even if no rendering will happen
+QVector<QAspectJobPtr> Renderer::preRenderingJobs()
+{
+ if (m_sendBufferCaptureJob->hasRequests())
+ return { m_sendBufferCaptureJob };
+ else
+ return {};
+}
+
+// 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;
+
+ // Remove previous dependencies
+ m_cleanupJob->removeDependency(QWeakPointer<QAspectJob>());
+
+ const BackendNodeDirtySet dirtyBitsForFrame = m_dirtyBits.marked | m_dirtyBits.remaining;
+ m_dirtyBits.marked = {};
+ m_dirtyBits.remaining = {};
+ BackendNodeDirtySet notCleared = {};
+
+ // Add jobs
+ if (dirtyBitsForFrame & AbstractRenderer::TransformDirty) {
+ renderBinJobs.push_back(m_updateShaderDataTransformJob);
+ }
+
+ // TO DO: Conditionally add if skeletons dirty
+ renderBinJobs.push_back(m_cleanupJob);
+
+ // Jobs to prepare RHI Resource upload
+ 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 entitiesEnabledDirty = dirtyBitsForFrame & AbstractRenderer::EntityEnabledDirty;
+ 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 (renderableDirty)
+ renderBinJobs.push_back(m_renderableEntityFilterJob);
+
+ if (computeableDirty)
+ renderBinJobs.push_back(m_computableEntityFilterJob);
+
+ if (lightsDirty)
+ renderBinJobs.push_back(m_lightGathererJob);
+
+ 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;
+}
+
+QAbstractFrameAdvanceService *Renderer::frameAdvanceService() const
+{
+ return static_cast<Qt3DCore::QAbstractFrameAdvanceService *>(m_vsyncFrameAdvanceService.data());
+}
+
+// Called by executeCommands
+void Renderer::performDraw(RenderCommand *command)
+{
+ QRhiCommandBuffer *cb = m_submissionContext->currentFrameCommandBuffer();
+ // Indirect Draw Calls
+ if (command->m_drawIndirect) {
+ RHI_UNIMPLEMENTED;
+ } else { // Direct Draw Calls
+
+ // TO DO: Add glMulti Draw variants
+ if (command->m_primitiveType == QGeometryRenderer::Patches) {
+ RHI_UNIMPLEMENTED;
+ //* m_submissionContext->setVerticesPerPatch(command->m_verticesPerPatch);
+ }
+
+ if (command->m_primitiveRestartEnabled) {
+ RHI_UNIMPLEMENTED;
+ //* m_submissionContext->enablePrimitiveRestart(command->m_restartIndexValue);
+ }
+
+ // TO DO: Add glMulti Draw variants
+ if (command->m_drawIndexed) {
+ cb->drawIndexed(command->m_primitiveCount, command->m_instanceCount,
+ command->m_indexOffset, command->m_indexAttributeByteOffset,
+ command->m_firstInstance);
+ } else {
+ cb->draw(command->m_primitiveCount, command->m_instanceCount, command->m_firstVertex,
+ command->m_firstInstance);
+ }
+ }
+
+#if defined(QT3D_RENDER_ASPECT_RHI_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)
+{
+ RHI_UNIMPLEMENTED;
+ //* {
+ //* RHIShader *shader =
+ //m_RHIResourceManagers->rhiShaderManager()->lookupResource(command->m_shaderId);
+ //* m_submissionContext->activateShader(shader);
+ //* }
+ //* {
+ //* m_submissionContext->setParameters(command->m_parameterPack);
+ //* }
+ //* {
+ //* 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_RHI_DEBUG)
+ //* int err = m_submissionContext->openGLContext()->functions()->glGetError();
+ //* if (err)
+ //* qCWarning(Rendering) << "GL error after drawing mesh:" << QString::number(err, 16);
+ //* #endif
+}
+
+static auto rhiIndexFormat(QAttribute::VertexBaseType type)
+{
+ switch (type) {
+ case QAttribute::VertexBaseType ::UnsignedShort:
+ return QRhiCommandBuffer::IndexUInt16;
+ case QAttribute::VertexBaseType ::UnsignedInt:
+ return QRhiCommandBuffer::IndexUInt32;
+ default:
+ std::abort();
+ }
+}
+
+bool Renderer::uploadBuffersForCommand(QRhiCommandBuffer *cb, const RenderView *rv,
+ RenderCommand &command)
+{
+ RHIGraphicsPipeline *graphicsPipeline = command.pipeline;
+ if (!graphicsPipeline)
+ return true;
+
+ // Create the vertex input description
+
+ // Note: we have to bind the buffers here -> which will trigger the actual
+ // upload, as this is the only place where we know about the usage type of the buffers
+
+ const auto geom = command.m_geometry;
+ const auto &attributes = geom->attributes();
+ const QRhiVertexInputLayout layout = graphicsPipeline->pipeline()->vertexInputLayout();
+ const int bindingAttributeCount = std::distance(layout.cbeginBindings(), layout.cendBindings());
+ command.vertex_input.resize(bindingAttributeCount);
+
+ for (Qt3DCore::QNodeId attribute_id : attributes) {
+ // TODO isn't there a more efficient way than doing three hash lookups ?
+ Attribute *attrib = m_nodesManager->attributeManager()->lookupResource(attribute_id);
+ Buffer *buffer = m_nodesManager->bufferManager()->lookupResource(attrib->bufferId());
+ RHIBuffer *hbuf =
+ m_RHIResourceManagers->rhiBufferManager()->lookupResource(buffer->peerId());
+ switch (attrib->attributeType()) {
+ case QAttribute::VertexAttribute: {
+ hbuf->bind(&*m_submissionContext, RHIBuffer::Type::ArrayBuffer);
+ assert(hbuf->rhiBuffer());
+ // Find Binding for Attribute
+ const int bindingIndex = graphicsPipeline->bindingIndexForAttribute(attrib->nameId());
+ // We need to reference a binding, a buffer and an offset which is always = 0
+ // as Qt3D only assumes interleaved or continuous data but not
+ // buffer where first half of it is attribute1 and second half attribute2
+ command.vertex_input[bindingIndex] = { hbuf->rhiBuffer(), 0 };
+ break;
+ }
+ case QAttribute::IndexAttribute: {
+ hbuf->bind(&*m_submissionContext, RHIBuffer::Type::IndexBuffer);
+ assert(hbuf->rhiBuffer());
+ assert(command.indexBuffer == nullptr);
+
+ command.indexBuffer = hbuf->rhiBuffer();
+ command.indexAttribute = attrib;
+ break;
+ }
+ case QAttribute::DrawIndirectAttribute:
+ RHI_UNIMPLEMENTED;
+ break;
+ }
+ }
+
+ for (const BlockToUBO &pack : command.m_parameterPack.uniformBuffers()) {
+ Buffer *cpuBuffer = nodeManagers()->bufferManager()->lookupResource(pack.m_bufferID);
+ RHIBuffer *ubo = m_submissionContext->rhiBufferForRenderBuffer(cpuBuffer);
+ ubo->bind(&*m_submissionContext, RHIBuffer::UniformBuffer);
+ }
+
+ return true;
+}
+
+namespace {
+void printUpload(const UniformValue &value, const QShaderDescription::BlockVariable &member)
+{
+ switch (member.type) {
+ case QShaderDescription::VariableType::Int:
+ qDebug() << "Updating" << member.name << "with int data: " << *value.constData<int>()
+ << " (offset: " << member.offset << ", size: " << member.size << ")";
+ break;
+ case QShaderDescription::VariableType::Float:
+ qDebug() << "Updating" << member.name << "with float data: " << *value.constData<float>()
+ << " (offset: " << member.offset << ", size: " << member.size << ")";
+ break;
+ case QShaderDescription::VariableType::Vec2:
+ qDebug() << "Updating" << member.name << "with vec2 data: " << value.constData<float>()[0]
+ << ", " << value.constData<float>()[1] << " (offset: " << member.offset
+ << ", size: " << member.size << ")";
+ ;
+ break;
+ case QShaderDescription::VariableType::Vec3:
+ qDebug() << "Updating" << member.name << "with vec3 data: " << value.constData<float>()[0]
+ << ", " << value.constData<float>()[1] << ", " << value.constData<float>()[2]
+ << " (offset: " << member.offset << ", size: " << member.size << ")";
+ ;
+ break;
+ case QShaderDescription::VariableType::Vec4:
+ qDebug() << "Updating" << member.name << "with vec4 data: " << value.constData<float>()[0]
+ << ", " << value.constData<float>()[1] << ", " << value.constData<float>()[2]
+ << ", " << value.constData<float>()[3] << " (offset: " << member.offset
+ << ", size: " << member.size << ")";
+ ;
+ break;
+ default:
+ qDebug() << "Updating" << member.name << "with data: " << value.constData<char>();
+ break;
+ }
+}
+
+void uploadUniform(SubmissionContext &submissionContext, const PackUniformHash &uniforms,
+ const RHIShader::UBO_Member &uboMember,
+ const QHash<int, RHIGraphicsPipeline::UBOBuffer> &uboBuffers,
+ const QString &uniformName, const QShaderDescription::BlockVariable &member,
+ int arrayOffset = 0)
+{
+ const int uniformNameId = StringToInt::lookupId(uniformName);
+
+ if (!uniforms.contains(uniformNameId))
+ return;
+
+ const UniformValue value = uniforms.value(uniformNameId);
+ const ShaderUniformBlock block = uboMember.block;
+
+ // Update UBO with uniform value
+ Q_ASSERT(uboBuffers.contains(block.m_binding));
+ const RHIGraphicsPipeline::UBOBuffer &ubo = uboBuffers[block.m_binding];
+ RHIBuffer *buffer = ubo.buffer;
+
+ // TODO we should maybe have this thread_local to not reallocate memory every time
+ QByteArray rawData;
+ rawData.resize(member.size);
+ memcpy(rawData.data(), value.constData<char>(), std::min(value.byteSize(), member.size));
+ buffer->update(&submissionContext, rawData, member.offset + arrayOffset);
+
+ // printUpload(value, member);
+}
+}
+
+bool Renderer::uploadUBOsForCommand(QRhiCommandBuffer *cb, const RenderView *rv,
+ const RenderCommand &command)
+{
+ RHIGraphicsPipeline *pipeline = command.pipeline;
+ if (!pipeline)
+ return true;
+
+ // Upload UBO data for the Command
+ QRhiBuffer *commandUBO = pipeline->commandUBO();
+ m_submissionContext->m_currentUpdates->updateDynamicBuffer(commandUBO, 0, sizeof(CommandUBO),
+ &command.m_commandUBO);
+
+ // We have to update the RV UBO once per graphics pipeline
+ QRhiBuffer *rvUBO = pipeline->renderViewUBO();
+ m_submissionContext->m_currentUpdates->updateDynamicBuffer(rvUBO, 0, sizeof(RenderViewUBO),
+ rv->renderViewUBO());
+
+ // Upload UBO for custom parameters
+ {
+ RHIShader *shader =
+ m_RHIResourceManagers->rhiShaderManager()->lookupResource(command.m_shaderId);
+ if (!shader)
+ return true;
+
+ const QVector<RHIShader::UBO_Member> &uboMembers = shader->uboMembers();
+ const QHash<int, RHIGraphicsPipeline::UBOBuffer> &uboBuffers = pipeline->ubos();
+ const ShaderParameterPack &parameterPack = command.m_parameterPack;
+ const PackUniformHash &uniforms = parameterPack.uniforms();
+
+ // Update Buffer CPU side data based on uniforms being set
+ for (const RHIShader::UBO_Member &uboMember : uboMembers) {
+ for (const QShaderDescription::BlockVariable &member : qAsConst(uboMember.members)) {
+
+ if (!member.arrayDims.empty()) {
+ if (!member.structMembers.empty()) {
+ const int arr0 = member.arrayDims[0];
+ for (int i = 0; i < arr0; i++) {
+ for (const QShaderDescription::BlockVariable &structMember :
+ member.structMembers) {
+ const QString processedName = member.name + "[" + QString::number(i)
+ + "]." + structMember.name;
+ uploadUniform(*m_submissionContext, uniforms, uboMember, uboBuffers,
+ processedName, structMember, i * member.size / arr0);
+ }
+ }
+ } else {
+ uploadUniform(*m_submissionContext, uniforms, uboMember, uboBuffers,
+ member.name, member);
+ }
+ } else {
+ uploadUniform(*m_submissionContext, uniforms, uboMember, uboBuffers,
+ member.name, member);
+ }
+ }
+ }
+ // Upload changes to GPU Buffer
+ for (const RHIGraphicsPipeline::UBOBuffer &ubo : uboBuffers) {
+ // Binding triggers the upload
+ ubo.buffer->bind(m_submissionContext.data(), RHIBuffer::UniformBuffer);
+ }
+ }
+ return true;
+}
+
+bool Renderer::performDraw(QRhiCommandBuffer *cb, const QRhiViewport &vp,
+ const QRhiScissor *scissor, const RenderCommand &command)
+{
+ RHIGraphicsPipeline *pipeline = command.pipeline;
+ if (!pipeline)
+ return true;
+
+ // Setup the rendering pass
+ cb->setGraphicsPipeline(pipeline->pipeline());
+ cb->setViewport(vp);
+ if (scissor)
+ cb->setScissor(*scissor);
+ cb->setShaderResources(pipeline->pipeline()->shaderResourceBindings());
+
+ // Send the draw command
+ if (Q_UNLIKELY(!command.indexBuffer)) {
+ cb->setVertexInput(0, command.vertex_input.size(), command.vertex_input.data());
+ cb->draw(command.m_primitiveCount, command.m_instanceCount, command.m_firstVertex,
+ command.m_firstInstance);
+ } else {
+ auto indexFormat = rhiIndexFormat(command.indexAttribute->vertexBaseType());
+ auto indexOffset = command.indexAttribute->byteOffset();
+ cb->setVertexInput(0, command.vertex_input.size(), command.vertex_input.data(),
+ command.indexBuffer, indexOffset, indexFormat);
+ cb->drawIndexed(command.m_primitiveCount, command.m_instanceCount, command.m_indexOffset,
+ command.m_indexAttributeByteOffset, command.m_firstInstance);
+ }
+ return true;
+}
+
+// Called by RenderView->submit() in RenderThread context
+// Returns true, if all RenderCommands were sent to the GPU
+bool Renderer::executeCommandsSubmission(const RHIPassInfo &passInfo)
+{
+ bool allCommandsIssued = true;
+
+ const QVector<RenderView *> &renderViews = passInfo.rvs;
+ QColor clearColor;
+ QRhiDepthStencilClearValue clearDepthStencil;
+
+ // Submit the commands to the underlying graphics API (RHI)
+ QRhiCommandBuffer *cb = m_submissionContext->currentFrameCommandBuffer();
+
+ // Upload data for all RenderCommands
+ for (RenderView *rv : renderViews) {
+ // Render drawing commands
+
+ QVector<RenderCommand> &commands = rv->commands();
+
+ // Upload all the required data to rhi...
+ for (RenderCommand &command : commands) {
+ if (command.m_type == RenderCommand::Draw) {
+ uploadBuffersForCommand(cb, rv, command);
+ uploadUBOsForCommand(cb, rv, command);
+ }
+ }
+
+ // Record clear information
+ if (rv->clearTypes() != QClearBuffers::None) {
+ clearColor = [=] {
+ auto col = rv->globalClearColorBufferInfo().clearColor;
+ return QColor::fromRgbF(col.x(), col.y(), col.z(), col.w());
+ }();
+ clearDepthStencil = { rv->clearDepthValue(), (quint32)rv->clearStencilValue() };
+ }
+ }
+ // TO DO: should be moved elsewhere
+ // Perform compute actions
+ // cb->beginComputePass(m_submissionContext->m_currentUpdates);
+ // for (RenderCommand &command : commands) {
+ // if (command.m_type == RenderCommand::Compute) {
+ // performCompute(rv, &command);
+ // }
+ // }
+ // cb->endComputePass();
+ // m_submissionContext->m_currentUpdates =
+ // m_submissionContext->rhi()->nextResourceUpdateBatch();
+
+ // Draw the commands
+
+ // TO DO: Retrieve real renderTarget for RHIPassInfo
+ QRhiRenderTarget *renderTarget = m_submissionContext->currentFrameRenderTarget();
+
+ // Begin pass
+ cb->beginPass(renderTarget, clearColor, clearDepthStencil,
+ m_submissionContext->m_currentUpdates);
+
+ // Per Pass Global States
+ for (RenderView *rv : renderViews) {
+ // Viewport
+ QRhiViewport vp;
+ QRhiScissor scissor;
+ bool hasScissor = false;
+ {
+ const float x = rv->viewport().x() * rv->surfaceSize().width();
+ const float y = (1. - rv->viewport().y() - rv->viewport().height())
+ * rv->surfaceSize().height();
+ const float w = rv->viewport().width() * rv->surfaceSize().width();
+ const float h = rv->viewport().height() * rv->surfaceSize().height();
+ // qDebug() << x << y << w << h;
+ vp = { x, y, w, h };
+ }
+ // Scissoring
+ {
+ RenderStateSet *ss = rv->stateSet();
+ if (ss == nullptr)
+ ss = m_defaultRenderStateSet;
+ StateVariant *scissorTestSVariant =
+ m_submissionContext->getState(ss, StateMask::ScissorStateMask);
+ if (scissorTestSVariant) {
+ const ScissorTest *scissorTest =
+ static_cast<const ScissorTest *>(scissorTestSVariant->constState());
+ const auto &scissorValues = scissorTest->values();
+ scissor = { std::get<0>(scissorValues), std::get<1>(scissorValues),
+ std::get<2>(scissorValues), std::get<3>(scissorValues) };
+ hasScissor = true;
+ }
+ }
+
+ // Render drawing commands
+ const QVector<RenderCommand> &commands = rv->commands();
+
+ for (const RenderCommand &command : commands) {
+ if (command.m_type == RenderCommand::Draw) {
+ performDraw(cb, vp, hasScissor ? &scissor : nullptr, command);
+ }
+ }
+ }
+
+ cb->endPass();
+ m_submissionContext->m_currentUpdates = m_submissionContext->rhi()->nextResourceUpdateBatch();
+
+ return allCommandsIssued;
+}
+
+// Erase graphics related resources that may become unused after a frame
+void Renderer::cleanGraphicsResources()
+{
+ // Remove unused GraphicsPipeline
+ RHIGraphicsPipelineManager *pipelineManager =
+ m_RHIResourceManagers->rhiGraphicsPipelineManager();
+ const QVector<HRHIGraphicsPipeline> graphicsPipelinesHandles = pipelineManager->activeHandles();
+ for (HRHIGraphicsPipeline pipelineHandle : graphicsPipelinesHandles) {
+ RHIGraphicsPipeline *pipeline = pipelineManager->data(pipelineHandle);
+ pipeline->decreaseScore();
+ // Pipeline wasn't used recently, let's destroy it
+ if (pipeline->score() < 0) {
+ pipeline->cleanup();
+ }
+ }
+
+ // 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);
+
+ // 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);
+ }
+}
+
+const GraphicsApiFilterData *Renderer::contextInfo() const
+{
+ return m_submissionContext->contextInfo();
+}
+
+SubmissionContext *Renderer::submissionContext() const
+{
+ return m_submissionContext.data();
+}
+
+} // namespace Rhi
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
diff --git a/src/plugins/renderers/rhi/renderer/renderer.pri b/src/plugins/renderers/rhi/renderer/renderer.pri
new file mode 100644
index 000000000..4ec8cca9b
--- /dev/null
+++ b/src/plugins/renderers/rhi/renderer/renderer.pri
@@ -0,0 +1,27 @@
+INCLUDEPATH += $$PWD
+
+SOURCES += \
+ $$PWD/rendercommand.cpp \
+ $$PWD/renderer.cpp \
+ $$PWD/renderqueue.cpp \
+ $$PWD/renderview.cpp \
+ $$PWD/renderviewbuilder.cpp \
+ $$PWD/rhigraphicspipeline.cpp \
+ $$PWD/rhishader.cpp \
+ $$PWD/shaderparameterpack.cpp \
+ $$PWD/logging.cpp \
+ $$PWD/commandexecuter.cpp
+
+HEADERS += \
+ $$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/rhigraphicspipeline_p.h \
+ $$PWD/rhishader_p.h \
+ $$PWD/shaderparameterpack_p.h \
+ $$PWD/shadervariables_p.h \
+ $$PWD/logging_p.h \
+ $$PWD/commandexecuter_p.h
diff --git a/src/plugins/renderers/rhi/renderer/renderer_p.h b/src/plugins/renderers/rhi/renderer/renderer_p.h
new file mode 100644
index 000000000..8cceca801
--- /dev/null
+++ b/src/plugins/renderers/rhi/renderer/renderer_p.h
@@ -0,0 +1,443 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 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_RHI_RENDERER_H
+#define QT3DRENDER_RENDER_RHI_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/rendersettings_p.h>
+#include <Qt3DRender/private/updateshaderdatatransformjob_p.h>
+#include <Qt3DRender/private/framecleanupjob_p.h>
+#include <Qt3DRender/private/platformsurfacefilter_p.h>
+#include <Qt3DRender/private/sendbuffercapturejob_p.h>
+#include <Qt3DRender/private/genericlambdajob_p.h>
+#include <Qt3DRender/private/shaderbuilder_p.h>
+#include <Qt3DRender/private/lightgatherer_p.h>
+#include <Qt3DRender/private/texture_p.h>
+#include <Qt3DRender/private/attachmentpack_p.h>
+#include <Qt3DRender/private/filterentitybycomponentjob_p.h>
+
+#include <QtGui/private/qrhi_p.h>
+
+#include <shaderparameterpack_p.h>
+#include <renderviewinitializerjob_p.h>
+#include <filtercompatibletechniquejob_p.h>
+#include <renderercache_p.h>
+#include <logging_p.h>
+#include <rhihandle_types_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 FrameGraphNode;
+class Material;
+class Technique;
+class Shader;
+class Entity;
+class Effect;
+class RenderPass;
+class RenderThread;
+class RenderStateSet;
+class VSyncFrameAdvanceService;
+class NodeManagers;
+class ResourceAccessor;
+
+using ComputableEntityFilter = FilterEntityByComponentJob<Render::ComputeCommand, Render::Material>;
+using ComputableEntityFilterPtr = QSharedPointer<ComputableEntityFilter>;
+using RenderableEntityFilter =
+ FilterEntityByComponentJob<Render::GeometryRenderer, Render::Material>;
+using RenderableEntityFilterPtr = QSharedPointer<RenderableEntityFilter>;
+
+using SynchronizerJobPtr = GenericLambdaJobPtr<std::function<void()>>;
+using SynchronizerPostFramePtr =
+ GenericLambdaJobAndPostFramePtr<std::function<void()>,
+ std::function<void(Qt3DCore::QAspectManager *)>>;
+
+namespace Rhi {
+
+class CommandThread;
+class SubmissionContext;
+class RenderCommand;
+class RenderQueue;
+class RenderView;
+class RHIShader;
+class RHIResourceManagers;
+
+class Q_AUTOTEST_EXPORT Renderer : public AbstractRenderer
+{
+public:
+ explicit Renderer(QRenderAspect::RenderType type);
+ ~Renderer();
+
+ void dumpInfo() const override;
+ API api() const override;
+
+ qint64 time() const override;
+ void setTime(qint64 time) override;
+ void setJobsInLastFrame(int jobsInLastFrame) override;
+
+ void setAspect(QRenderAspect *aspect) override;
+ void setNodeManagers(NodeManagers *managers) override;
+ void setServices(Qt3DCore::QServiceLocator *services) override;
+ void setSurfaceExposed(bool exposed) override;
+
+ QRenderAspect *aspect() const 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;
+
+ void setPendingEvents(const QList<QPair<QObject *, QMouseEvent>> &mouseEvents,
+ const QList<QKeyEvent> &keyEvents) override;
+
+ QVector<Qt3DCore::QAspectJobPtr> preRenderingJobs() override;
+ QVector<Qt3DCore::QAspectJobPtr> renderBinJobs() override;
+
+ inline FrameCleanupJobPtr frameCleanupJob() const { return m_cleanupJob; }
+ inline UpdateShaderDataTransformJobPtr updateShaderDataTransformJob() const
+ {
+ return m_updateShaderDataTransformJob;
+ }
+ inline FilterCompatibleTechniqueJobPtr filterCompatibleTechniqueJob() const
+ {
+ return m_filterCompatibleTechniqueJob;
+ }
+ inline SynchronizerPostFramePtr introspectShadersJob() const { return m_introspectShaderJob; }
+ inline Qt3DCore::QAspectJobPtr bufferGathererJob() const { return m_bufferGathererJob; }
+ inline Qt3DCore::QAspectJobPtr textureGathererJob() const { return m_textureGathererJob; }
+ inline LightGathererPtr lightGathererJob() const { return m_lightGathererJob; }
+ inline RenderableEntityFilterPtr renderableEntityFilterJob() const
+ {
+ return m_renderableEntityFilterJob;
+ }
+ inline ComputableEntityFilterPtr computableEntityFilterJob() const
+ {
+ return m_computableEntityFilterJob;
+ }
+
+ Qt3DCore::QAbstractFrameAdvanceService *frameAdvanceService() const override;
+
+ void setSettings(RenderSettings *settings) override;
+ RenderSettings *settings() const override;
+ QOpenGLContext *shareContext() const override;
+
+ inline RHIResourceManagers *rhiResourceManagers() const { return m_RHIResourceManagers; }
+
+ // Executed in secondary GL thread
+ void loadShader(Shader *shader, Qt3DRender::Render::HShader shaderHandle) override;
+
+ void updateResources();
+ 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);
+
+ struct RHIPassInfo
+ {
+ QVector<RenderView *> rvs;
+ QSurface *surface = nullptr;
+ Qt3DCore::QNodeId renderTargetId;
+ AttachmentPack attachmentPack;
+ };
+
+ QVector<RHIPassInfo> prepareCommandsSubmission(const QVector<RenderView *> &renderViews);
+ bool executeCommandsSubmission(const RHIPassInfo &passInfo);
+
+ // 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<RHIPassInfo> &rhiPassesInfo);
+
+ RendererCache *cache() { return &m_cache; }
+ void setScreen(QScreen *scr) override;
+ QScreen *screen() const override;
+
+ float *textureTransform() noexcept { return m_textureTransform; }
+ const float *textureTransform() const noexcept { return m_textureTransform; }
+#ifdef QT3D_RENDER_UNIT_TESTS
+public:
+#else
+
+private:
+#endif
+ bool canRender() const;
+
+ Qt3DCore::QServiceLocator *m_services;
+ QRenderAspect *m_aspect;
+ 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;
+
+ RenderQueue *m_renderQueue;
+ QScopedPointer<RenderThread> m_renderThread;
+ QScopedPointer<VSyncFrameAdvanceService> m_vsyncFrameAdvanceService;
+
+ QSemaphore m_submitRenderViewsSemaphore;
+ QSemaphore m_waitForInitializationToBeCompleted;
+ QMutex m_hasBeenInitializedMutex;
+
+ QAtomicInt m_running;
+
+ 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;
+
+ qint64 m_time;
+
+ RenderSettings *m_settings;
+
+ UpdateShaderDataTransformJobPtr m_updateShaderDataTransformJob;
+ FrameCleanupJobPtr m_cleanupJob;
+ SendBufferCaptureJobPtr m_sendBufferCaptureJob;
+ FilterCompatibleTechniqueJobPtr m_filterCompatibleTechniqueJob;
+ 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);
+
+ SynchronizerJobPtr m_bufferGathererJob;
+ SynchronizerJobPtr m_textureGathererJob;
+ SynchronizerPostFramePtr m_introspectShaderJob;
+
+ 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);
+
+ 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<Qt3DCore::QNodeId> m_updatedDisableSubtreeEnablers;
+ Qt3DCore::QNodeIdVector m_textureIdsToCleanup;
+ QVector<ShaderBuilderUpdate> m_shaderBuilderUpdates;
+
+ bool m_ownedContext;
+
+ OffscreenSurfaceHelper *m_offscreenHelper;
+ RHIResourceManagers *m_RHIResourceManagers;
+ 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;
+
+ QOffscreenSurface *m_fallbackSurface {};
+
+ bool m_hasSwapChain = false;
+
+ QList<QPair<QObject *, QMouseEvent>> m_frameMouseEvents;
+ QList<QKeyEvent> m_frameKeyEvents;
+ QMutex m_frameEventsMutex;
+ int m_jobsInLastFrame = 0;
+
+ float m_textureTransform[4];
+
+ void updateGraphicsPipeline(RenderCommand &command, RenderView *rv, int renderViewIndex);
+ bool uploadBuffersForCommand(QRhiCommandBuffer *cb, const RenderView *rv,
+ RenderCommand &command);
+ bool uploadUBOsForCommand(QRhiCommandBuffer *cb, const RenderView *rv,
+ const RenderCommand &command);
+ bool performDraw(QRhiCommandBuffer *cb, const QRhiViewport &vp, const QRhiScissor *scissor,
+ const RenderCommand &command);
+};
+
+} // namespace Rhi
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDER_RHI_RENDERER_H
diff --git a/src/plugins/renderers/rhi/renderer/renderercache_p.h b/src/plugins/renderers/rhi/renderer/renderercache_p.h
new file mode 100644
index 000000000..ec3223dd2
--- /dev/null
+++ b/src/plugins/renderers/rhi/renderer/renderercache_p.h
@@ -0,0 +1,101 @@
+/****************************************************************************
+**
+** 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_RHI_RENDERERCACHE_P_H
+#define QT3DRENDER_RENDER_RHI_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 {
+
+namespace Rhi {
+
+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 Rhi
+
+} // namespace Render
+
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDER_RHI_RENDERERCACHE_P_H
diff --git a/src/plugins/renderers/rhi/renderer/renderqueue.cpp b/src/plugins/renderers/rhi/renderer/renderqueue.cpp
new file mode 100644
index 000000000..39eaddc9a
--- /dev/null
+++ b/src/plugins/renderers/rhi/renderer/renderqueue.cpp
@@ -0,0 +1,138 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "renderqueue_p.h"
+#include <renderview_p.h>
+#include <QThread>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+namespace Render {
+
+namespace Rhi {
+
+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 Rhi
+
+} // namespace Render
+
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
diff --git a/src/plugins/renderers/rhi/renderer/renderqueue_p.h b/src/plugins/renderers/rhi/renderer/renderqueue_p.h
new file mode 100644
index 000000000..3b6eec13d
--- /dev/null
+++ b/src/plugins/renderers/rhi/renderer/renderqueue_p.h
@@ -0,0 +1,106 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DRENDER_RENDER_RHI_RENDERQUEUE_H
+#define QT3DRENDER_RENDER_RHI_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 {
+
+namespace Rhi {
+
+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 Rhi
+
+} // namespace Render
+
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDER_RHI_RENDERQUEUE_H
diff --git a/src/plugins/renderers/rhi/renderer/renderview.cpp b/src/plugins/renderers/rhi/renderer/renderview.cpp
new file mode 100644
index 000000000..54295ef5f
--- /dev/null
+++ b/src/plugins/renderers/rhi/renderer/renderview.cpp
@@ -0,0 +1,1253 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 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 <submissioncontext_p.h>
+#include <rhiresourcemanagers_p.h>
+#include <Qt3DCore/qentity.h>
+#include <QtGui/qsurface.h>
+#include <algorithm>
+#include <atomic>
+#include <cstdlib>
+#include <QDebug>
+#if defined(QT3D_RENDER_VIEW_JOB_TIMINGS)
+#include <QElapsedTimer>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+namespace Render {
+namespace Rhi {
+
+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];
+
+std::atomic_bool wasInitialized {};
+
+} // anonymous namespace
+
+// 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, bool yIsUp)
+{
+ if (lens) {
+ if (yIsUp) {
+ // OpenGL
+ return lens->projection();
+ } else {
+ // Others. Note : this could likely be optimized...
+ auto p = lens->projection();
+ Matrix4x4 rev { 0, 0, 0, 0, 0, -2 * p.m22(), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ p += rev;
+ return p;
+ }
+ } else {
+ qWarning() << "[Qt3D Renderer] No Camera Lens found. Add a CameraSelector to your Frame "
+ "Graph or make sure that no entities will be rendered.";
+ return Matrix4x4();
+ }
+}
+
+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_environmentLight(nullptr)
+{
+ m_workGroups[0] = 1;
+ m_workGroups[1] = 1;
+ m_workGroups[2] = 1;
+
+ if (Q_UNLIKELY(!wasInitialized.exchange(true))) {
+ // Needed as we can control the init order of static/global variables across compile units
+ // and this hash relies on the static StringToInt class
+
+ 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);
+ }
+ }
+}
+
+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_rhiShader == b.m_rhiShader;
+ }
+};
+
+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_rhiShader > b.m_rhiShader;
+ });
+ }
+};
+
+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)
+ {
+#ifndef Q_OS_WIN
+ 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;
+ });
+#endif
+ }
+};
+
+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_rhiShader == m_commands[i].m_rhiShader)
+ ++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
+{
+ EntityRenderCommandData commands;
+ RHIShaderManager *rhiShaderManager = m_renderer->rhiResourceManagers()->rhiShaderManager();
+
+ 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);
+
+ if (geometry == nullptr)
+ continue;
+
+ // 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_rhiShader = rhiShaderManager->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_shaderId)
+ 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 = 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), 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;
+ RHIShaderManager *rhiShaderManager = m_renderer->rhiResourceManagers()->rhiShaderManager();
+
+ 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_rhiShader = rhiShaderManager->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
+ assert(command.m_rhiShader);
+ if (!command.m_rhiShader)
+ 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), 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);
+
+ // Update RenderViewUBO (Qt3D standard uniforms)
+ const bool yIsUp = m_renderer->submissionContext()->rhi()->isYUpInFramebuffer();
+ const Matrix4x4 projectionMatrix = getProjectionMatrix(m_data.m_renderCameraLens, yIsUp);
+ const Matrix4x4 inverseViewMatrix = m_data.m_viewMatrix.inverted();
+ const Matrix4x4 inversedProjectionMatrix = projectionMatrix.inverted();
+ const Matrix4x4 viewProjectionMatrix = (projectionMatrix * m_data.m_viewMatrix);
+ const Matrix4x4 inversedViewProjectionMatrix = viewProjectionMatrix.inverted();
+ {
+ memcpy(&m_renderViewUBO.viewMatrix, &m_data.m_viewMatrix, sizeof(Matrix4x4));
+ memcpy(&m_renderViewUBO.projectionMatrix, &projectionMatrix, sizeof(Matrix4x4));
+ memcpy(&m_renderViewUBO.viewProjectionMatrix, &viewProjectionMatrix, sizeof(Matrix4x4));
+ memcpy(&m_renderViewUBO.inverseViewMatrix, &inverseViewMatrix, sizeof(Matrix4x4));
+ memcpy(&m_renderViewUBO.inverseProjectionMatrix, &inversedProjectionMatrix,
+ sizeof(Matrix4x4));
+ memcpy(&m_renderViewUBO.inverseViewProjectionMatrix, &inversedViewProjectionMatrix,
+ sizeof(Matrix4x4));
+ {
+ QMatrix4x4 viewportMatrix;
+ // TO DO: Implement on Matrix4x4
+ viewportMatrix.viewport(resolveViewport(m_viewport, m_surfaceSize));
+ Matrix4x4 vpMatrix(viewportMatrix);
+ Matrix4x4 invVpMatrix = vpMatrix.inverted();
+ memcpy(&m_renderViewUBO.viewportMatrix, &vpMatrix, sizeof(Matrix4x4));
+ memcpy(&m_renderViewUBO.inverseViewportMatrix, &invVpMatrix, sizeof(Matrix4x4));
+ }
+ memcpy(&m_renderViewUBO.textureTransformMatrix, m_renderer->textureTransform(),
+ sizeof(float) * 4);
+
+ memcpy(&m_renderViewUBO.eyePosition, &m_data.m_eyePos, sizeof(float) * 3);
+ const float ratio =
+ float(m_surfaceSize.width()) / std::max(1.f, float(m_surfaceSize.height()));
+ memcpy(&m_renderViewUBO.aspectRatio, &ratio, sizeof(float));
+ memcpy(&m_renderViewUBO.gamma, &m_gamma, sizeof(float));
+ const float exposure =
+ m_data.m_renderCameraLens ? m_data.m_renderCameraLens->exposure() : 0.0f;
+ memcpy(&m_renderViewUBO.exposure, &exposure, sizeof(float));
+ const float timeValue = float(m_renderer->time() / 1000000000.0f);
+ memcpy(&m_renderViewUBO.time, &timeValue, sizeof(float));
+ }
+
+ 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);
+
+ // Update CommandUBO (Qt3D standard uniforms)
+ const Matrix4x4 worldTransform = *(entity->worldTransform());
+ const Matrix4x4 inverseWorldTransform = worldTransform.inverted();
+ const QMatrix3x3 modelNormalMatrix = convertToQMatrix4x4(worldTransform).normalMatrix();
+ const Matrix4x4 modelViewMatrix = m_data.m_viewMatrix * worldTransform;
+ const Matrix4x4 inverseModelViewMatrix = modelViewMatrix.inverted();
+ const Matrix4x4 mvp = projectionMatrix * modelViewMatrix;
+ const Matrix4x4 inverseModelViewProjection = mvp.inverted();
+ {
+ memcpy(&command.m_commandUBO.modelMatrix, &worldTransform, sizeof(Matrix4x4));
+ memcpy(&command.m_commandUBO.inverseModelMatrix, &inverseWorldTransform,
+ sizeof(Matrix4x4));
+ memcpy(&command.m_commandUBO.modelViewMatrix, &modelViewMatrix, sizeof(Matrix4x4));
+ {
+ float(&normal)[12] = command.m_commandUBO.modelNormalMatrix;
+ normal[0] = modelNormalMatrix.constData()[0 * 3 + 0];
+ normal[1] = modelNormalMatrix.constData()[0 * 3 + 1];
+ normal[2] = modelNormalMatrix.constData()[0 * 3 + 2];
+
+ normal[4] = modelNormalMatrix.constData()[1 * 3 + 0];
+ normal[5] = modelNormalMatrix.constData()[1 * 3 + 1];
+ normal[6] = modelNormalMatrix.constData()[1 * 3 + 2];
+
+ normal[8] = modelNormalMatrix.constData()[2 * 3 + 0];
+ normal[9] = modelNormalMatrix.constData()[2 * 3 + 1];
+ normal[10] = modelNormalMatrix.constData()[2 * 3 + 2];
+ }
+ memcpy(&command.m_commandUBO.inverseModelViewMatrix, &inverseModelViewMatrix,
+ sizeof(Matrix4x4));
+ memcpy(&command.m_commandUBO.mvp, &mvp, sizeof(Matrix4x4));
+ memcpy(&command.m_commandUBO.inverseModelViewProjectionMatrix,
+ &inverseModelViewProjection, sizeof(Matrix4x4));
+ }
+ }
+
+ // 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::setUniformBlockValue(ShaderParameterPack &uniformPack, const RHIShader *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, const RHIShader *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,
+ const RHIShader *shader,
+ const 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->unqualifiedUniformNames();
+ // 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::applyParameter(const Parameter *param, RenderCommand *command,
+ const RHIShader *shader) const noexcept
+{
+ const int nameId = param->nameId();
+ const UniformValue &uniformValue = param->uniformValue();
+ switch (shader->categorizeVariable(nameId)) {
+ case RHIShader::Uniform: {
+ setUniformValue(command->m_parameterPack, nameId, uniformValue);
+ break;
+ }
+ case RHIShader::UBO: {
+ setUniformBlockValue(command->m_parameterPack, shader,
+ shader->uniformBlockForBlockNameId(nameId), uniformValue);
+ break;
+ }
+ case RHIShader::SSBO: {
+ setShaderStorageValue(command->m_parameterPack, shader,
+ shader->storageBlockForBlockNameId(nameId), uniformValue);
+ break;
+ }
+ case RHIShader::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(nameId));
+ }
+ break;
+ }
+ }
+}
+
+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
+
+ RHIShader *shader = command->m_rhiShader;
+ 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
+
+ // 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 (shader->hasActiveVariables()) {
+
+ // Unlike the GL engine, the standard uniforms are set a bit before this function,
+ // in RenderView::updateRenderCommand
+
+ // Set default attributes
+ command->m_activeAttributes = shader->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) {
+ const Parameter *param = m_manager->data<Parameter, ParameterManager>(it->handle);
+ applyParameter(param, command, 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);
+
+ // 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]);
+ ++lightIdx;
+ }
+ }
+
+ if (shader->hasUniform(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);
+ }
+
+ // Environment Light
+ int envLightCount = 0;
+ if (environmentLight && environmentLight->isEnabled()) {
+ static const int irradianceId =
+ StringToInt::lookupId(QLatin1String("envLight_irradiance"));
+ static const int specularId =
+ StringToInt::lookupId(QLatin1String("envLight_specular"));
+ ShaderData *shaderData = m_manager->shaderDataManager()->lookupResource(
+ environmentLight->shaderData());
+ if (shaderData) {
+ envLightCount = 1;
+
+ // ("specularSize", "irradiance", "irradianceSize", "specular")
+ auto irr =
+ shaderData->properties()["irradiance"].value.value<Qt3DCore::QNodeId>();
+ auto spec =
+ shaderData->properties()["specular"].value.value<Qt3DCore::QNodeId>();
+
+ setUniformValue(command->m_parameterPack, irradianceId, irr);
+ setUniformValue(command->m_parameterPack, specularId, spec);
+ }
+ }
+ 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 Rhi
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
diff --git a/src/plugins/renderers/rhi/renderer/renderview_p.h b/src/plugins/renderers/rhi/renderer/renderview_p.h
new file mode 100644
index 000000000..3206b6e66
--- /dev/null
+++ b/src/plugins/renderers/rhi/renderer/renderview_p.h
@@ -0,0 +1,455 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 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_RHI_RENDERVIEW_H
+#define QT3DRENDER_RENDER_RHI_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 NodeManagers;
+class RenderPassFilter;
+class TechniqueFilter;
+class ViewportNode;
+class Effect;
+class RenderPass;
+
+namespace Rhi {
+
+class Renderer;
+class RenderCommand;
+
+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
+
+struct RenderViewUBO
+{
+ float viewMatrix[16];
+ float projectionMatrix[16];
+ float viewProjectionMatrix[16];
+ float inverseViewMatrix[16];
+ float inverseProjectionMatrix[16];
+ float inverseViewProjectionMatrix[16];
+ float viewportMatrix[16];
+ float inverseViewportMatrix[16];
+ float textureTransformMatrix[4];
+ float eyePosition[3];
+ float aspectRatio;
+ float gamma;
+ float exposure;
+ float time;
+};
+static_assert(sizeof(RenderViewUBO) == sizeof(float) * (8 * 16 + 1 * 4 + 1 * 3 + 4 * 1),
+ "UBO doesn't match std140");
+
+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 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; }
+
+ inline const RenderViewUBO *renderViewUBO() const { return &m_renderViewUBO; }
+
+ 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;
+ }
+
+ // 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];
+
+ RenderViewUBO m_renderViewUBO;
+
+ QVector<RenderCommand> m_commands;
+ mutable QVector<LightSource> m_lightSources;
+ EnvironmentLight *m_environmentLight;
+
+ MaterialParameterGathererData m_parameters;
+
+ void setUniformValue(ShaderParameterPack &uniformPack, int nameId,
+ const UniformValue &value) const;
+ void setUniformBlockValue(ShaderParameterPack &uniformPack, const RHIShader *shader,
+ const ShaderUniformBlock &block, const UniformValue &value) const;
+ void setShaderStorageValue(ShaderParameterPack &uniformPack, const RHIShader *shader,
+ const ShaderStorageBlock &block, const UniformValue &value) const;
+ void setDefaultUniformBlockShaderDataValue(ShaderParameterPack &uniformPack,
+ const RHIShader *shader,
+ const ShaderData *shaderData,
+ const QString &structName) const;
+ void applyParameter(const Parameter *param, RenderCommand *command,
+ const RHIShader *shader) const noexcept;
+};
+
+} // namespace Rhi
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDER_RHI_ENDERVIEW_H
diff --git a/src/plugins/renderers/rhi/renderer/renderviewbuilder.cpp b/src/plugins/renderers/rhi/renderer/renderviewbuilder.cpp
new file mode 100644
index 000000000..6455d2e10
--- /dev/null
+++ b/src/plugins/renderers/rhi/renderer/renderviewbuilder.cpp
@@ -0,0 +1,841 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "renderviewbuilder_p.h"
+#include <Qt3DRender/private/qrenderaspect_p.h>
+
+#include <QThread>
+
+namespace Qt3DRender {
+
+namespace Render {
+namespace Rhi {
+
+// 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(std::move(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);
+ // assert(totalCommandCount != 0);
+ 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::Rhi::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::Rhi::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 = 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;
+ auto daspect = QRenderAspectPrivate::get(m_renderer->aspect());
+ auto expandBVJob = daspect->m_expandBoundingVolumeJob;
+ auto worldTransformJob = daspect->m_worldTransformJob;
+ auto updateTreeEnabledJob = daspect->m_updateTreeEnabledJob;
+ auto updateSkinningPaletteJob = daspect->m_updateSkinningPaletteJob;
+ auto updateEntityLayersJob = daspect->m_updateEntityLayersJob;
+
+ 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(updateSkinningPaletteJob);
+
+ m_syncPreFrustumCullingJob->addDependency(worldTransformJob);
+ m_syncPreFrustumCullingJob->addDependency(m_renderer->updateShaderDataTransformJob());
+ m_syncPreFrustumCullingJob->addDependency(m_syncRenderViewPostInitializationJob);
+
+ m_frustumCullingJob->addDependency(expandBVJob);
+ m_frustumCullingJob->addDependency(m_syncPreFrustumCullingJob);
+
+ m_setClearDrawBufferIndexJob->addDependency(m_syncRenderViewPostInitializationJob);
+
+ m_syncRenderViewPostInitializationJob->addDependency(m_renderViewJob);
+
+ m_filterProximityJob->addDependency(expandBVJob);
+ 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->lightGathererJob());
+
+ 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->computableEntityFilterJob());
+ m_syncRenderViewPreCommandBuildingJob->addDependency(
+ m_renderer->renderableEntityFilterJob());
+ 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(updateEntityLayersJob);
+ m_filterEntityByLayerJob->addDependency(m_syncRenderViewPostInitializationJob);
+ m_filterEntityByLayerJob->addDependency(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;
+}
+
+} // Rhi
+
+} // Render
+
+} // Qt3DRender
+
+QT_END_NAMESPACE
diff --git a/src/plugins/renderers/rhi/renderer/renderviewbuilder_p.h b/src/plugins/renderers/rhi/renderer/renderviewbuilder_p.h
new file mode 100644
index 000000000..4df57b139
--- /dev/null
+++ b/src/plugins/renderers/rhi/renderer/renderviewbuilder_p.h
@@ -0,0 +1,153 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DRENDER_RENDER_RHI_RENDERVIEWBUILDER_H
+#define QT3DRENDER_RENDER_RHI_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 <renderview_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+namespace Render {
+
+namespace Rhi {
+
+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;
+};
+
+} // Rhi
+
+} // Render
+
+} // Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDER_RHI_RENDERVIEWBUILDER_H
diff --git a/src/plugins/renderers/rhi/renderer/rhigraphicspipeline.cpp b/src/plugins/renderers/rhi/renderer/rhigraphicspipeline.cpp
new file mode 100644
index 000000000..2a76f9c10
--- /dev/null
+++ b/src/plugins/renderers/rhi/renderer/rhigraphicspipeline.cpp
@@ -0,0 +1,81 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "rhigraphicspipeline_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+namespace Render {
+
+namespace Rhi {
+
+RHIGraphicsPipeline::RHIGraphicsPipeline()
+ : m_rvUbo(nullptr),
+ m_cmdUbo(nullptr),
+ m_pipeline(nullptr),
+ m_shaderResourceBindings(nullptr),
+ m_score(0)
+{
+}
+
+RHIGraphicsPipeline::~RHIGraphicsPipeline() { }
+
+void RHIGraphicsPipeline::cleanup()
+{
+ delete m_shaderResourceBindings;
+ delete m_rvUbo;
+ delete m_cmdUbo;
+ delete m_pipeline;
+ m_rvUbo = nullptr;
+ m_cmdUbo = nullptr;
+ m_pipeline = nullptr;
+ m_shaderResourceBindings = nullptr;
+ m_ubos.clear();
+ m_attributeNameIdToBindingIndex.clear();
+}
+
+} // Rhi
+
+} // Render
+
+} // Qt3DRender
+
+QT_END_NAMESPACE
diff --git a/src/plugins/renderers/rhi/renderer/rhigraphicspipeline_p.h b/src/plugins/renderers/rhi/renderer/rhigraphicspipeline_p.h
new file mode 100644
index 000000000..a13bf50d3
--- /dev/null
+++ b/src/plugins/renderers/rhi/renderer/rhigraphicspipeline_p.h
@@ -0,0 +1,127 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DRENDER_RENDER_RHI_RHIGRAPHICSPIPELINE_H
+#define QT3DRENDER_RENDER_RHI_RHIGRAPHICSPIPELINE_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 <private/qrhi_p.h>
+#include <rhihandle_types_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+namespace Render {
+
+namespace Rhi {
+
+class RHIBuffer;
+
+class RHIGraphicsPipeline
+{
+public:
+ struct UBOBuffer
+ {
+ HRHIBuffer handle {};
+ RHIBuffer *buffer {};
+ };
+
+ RHIGraphicsPipeline();
+ ~RHIGraphicsPipeline();
+
+ QRhiBuffer *commandUBO() const { return m_cmdUbo; }
+ QRhiBuffer *renderViewUBO() const { return m_rvUbo; }
+ QRhiGraphicsPipeline *pipeline() const { return m_pipeline; }
+ QRhiShaderResourceBindings *shaderResourceBindings() const { return m_shaderResourceBindings; }
+ QHash<int, UBOBuffer> ubos() const { return m_ubos; }
+ int score() const { return m_score; }
+
+ void setPipeline(QRhiGraphicsPipeline *pipeline) { m_pipeline = pipeline; }
+ void setCommandUBO(QRhiBuffer *commandUBO) { m_cmdUbo = commandUBO; }
+ void setRenderViewUBO(QRhiBuffer *rvUBO) { m_rvUbo = rvUBO; }
+ void setShaderResourceBindings(QRhiShaderResourceBindings *shaderResourceBindings)
+ {
+ m_shaderResourceBindings = shaderResourceBindings;
+ }
+ void setUBOs(const QHash<int, UBOBuffer> ubos) { m_ubos = ubos; }
+
+ void setAttributesToBindingHash(const QHash<int, int> &attributeNameToBinding)
+ {
+ m_attributeNameIdToBindingIndex = attributeNameToBinding;
+ }
+ int bindingIndexForAttribute(int attributeNameId) const
+ {
+ return m_attributeNameIdToBindingIndex.value(attributeNameId, -1);
+ }
+ void increaseScore() { ++m_score; }
+ void decreaseScore() { --m_score; }
+
+ void cleanup();
+
+private:
+ QRhiBuffer *m_rvUbo;
+ QRhiBuffer *m_cmdUbo;
+ QRhiGraphicsPipeline *m_pipeline;
+ QRhiShaderResourceBindings *m_shaderResourceBindings;
+ // For user defined uniforms
+ QHash<int, UBOBuffer> m_ubos;
+ QHash<int, int> m_attributeNameIdToBindingIndex;
+ int m_score;
+};
+
+} // Rhi
+
+} // Render
+
+} // Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDER_RHI_RHIGRAPHICSPIPELINE_H
diff --git a/src/plugins/renderers/rhi/renderer/rhishader.cpp b/src/plugins/renderers/rhi/renderer/rhishader.cpp
new file mode 100644
index 000000000..20da46347
--- /dev/null
+++ b/src/plugins/renderers/rhi/renderer/rhishader.cpp
@@ -0,0 +1,681 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "rhishader_p.h"
+#include <QMutexLocker>
+#include <Qt3DRender/private/stringtoint_p.h>
+#include <submissioncontext_p.h>
+#include <logging_p.h>
+#include <QRegularExpression>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+namespace Render {
+
+namespace Rhi {
+
+RHIShader::RHIShader() : m_isLoaded(false)
+{
+ m_shaderCode.resize(static_cast<int>(QShaderProgram::Compute) + 1);
+}
+
+QVector<QString> RHIShader::uniformsNames() const
+{
+ return m_uniformsNames;
+}
+
+QVector<QString> RHIShader::attributesNames() const
+{
+ return m_attributesNames;
+}
+
+QVector<QString> RHIShader::uniformBlockNames() const
+{
+ return m_uniformBlockNames;
+}
+
+QVector<QString> RHIShader::storageBlockNames() const
+{
+ return m_shaderStorageBlockNames;
+}
+
+QVector<QString> RHIShader::samplerNames() const
+{
+ return m_samplerNames;
+}
+
+QVector<QString> RHIShader::imagesNames() const
+{
+ return m_imageNames;
+}
+
+QVector<QByteArray> RHIShader::shaderCode() const
+{
+ return m_shaderCode;
+}
+
+namespace {
+static constexpr QRhiVertexInputAttribute::Format
+rhiInputType(QShaderDescription::VariableType type)
+{
+ switch (type) {
+ case QShaderDescription::Vec4:
+ return QRhiVertexInputAttribute::Float4;
+ case QShaderDescription::Vec3:
+ return QRhiVertexInputAttribute::Float3;
+ case QShaderDescription::Vec2:
+ return QRhiVertexInputAttribute::Float2;
+ case QShaderDescription::Float:
+ return QRhiVertexInputAttribute::Float;
+ default:
+ // TODO UNormByte4, UNormByte2, UNormByte
+ RHI_UNIMPLEMENTED;
+ return QRhiVertexInputAttribute::UNormByte;
+ break;
+ }
+}
+
+static constexpr int rhiTypeSize(QShaderDescription::VariableType type)
+{
+ switch (type) {
+ case QShaderDescription::Unknown:
+ return 0;
+
+ case QShaderDescription::Float:
+ return 1;
+ case QShaderDescription::Vec2:
+ return 2;
+ case QShaderDescription::Vec3:
+ return 3;
+ case QShaderDescription::Vec4:
+ return 4;
+ case QShaderDescription::Mat2:
+ return 2 * 2;
+ case QShaderDescription::Mat2x3:
+ return 2 * 3;
+ case QShaderDescription::Mat2x4:
+ return 2 * 4;
+ case QShaderDescription::Mat3:
+ return 3 * 3;
+ case QShaderDescription::Mat3x2:
+ return 3 * 2;
+ case QShaderDescription::Mat3x4:
+ return 3 * 4;
+ case QShaderDescription::Mat4:
+ return 4 * 4;
+ case QShaderDescription::Mat4x2:
+ return 4 * 2;
+ case QShaderDescription::Mat4x3:
+ return 4 * 3;
+
+ case QShaderDescription::Int:
+ return 1;
+ case QShaderDescription::Int2:
+ return 2;
+ case QShaderDescription::Int3:
+ return 3;
+ case QShaderDescription::Int4:
+ return 4;
+
+ case QShaderDescription::Uint:
+ return 1;
+ case QShaderDescription::Uint2:
+ return 2;
+ case QShaderDescription::Uint3:
+ return 3;
+ case QShaderDescription::Uint4:
+ return 4;
+
+ case QShaderDescription::Bool:
+ return 1;
+ case QShaderDescription::Bool2:
+ return 2;
+ case QShaderDescription::Bool3:
+ return 3;
+ case QShaderDescription::Bool4:
+ return 4;
+
+ case QShaderDescription::Double:
+ return 1;
+ case QShaderDescription::Double2:
+ return 2;
+ case QShaderDescription::Double3:
+ return 3;
+ case QShaderDescription::Double4:
+ return 4;
+ case QShaderDescription::DMat2:
+ return 2 * 2;
+ case QShaderDescription::DMat2x3:
+ return 2 * 3;
+ case QShaderDescription::DMat2x4:
+ return 2 * 4;
+ case QShaderDescription::DMat3:
+ return 3 * 3;
+ case QShaderDescription::DMat3x2:
+ return 3 * 2;
+ case QShaderDescription::DMat3x4:
+ return 3 * 4;
+ case QShaderDescription::DMat4:
+ return 4 * 4;
+ case QShaderDescription::DMat4x2:
+ return 4 * 2;
+ case QShaderDescription::DMat4x3:
+ return 4 * 3;
+
+ case QShaderDescription::Sampler1D:
+ return 0;
+ case QShaderDescription::Sampler2D:
+ return 0;
+ case QShaderDescription::Sampler2DMS:
+ return 0;
+ case QShaderDescription::Sampler3D:
+ return 0;
+ case QShaderDescription::SamplerCube:
+ return 0;
+ case QShaderDescription::Sampler1DArray:
+ return 0;
+ case QShaderDescription::Sampler2DArray:
+ return 0;
+ case QShaderDescription::Sampler2DMSArray:
+ return 0;
+ case QShaderDescription::Sampler3DArray:
+ return 0;
+ case QShaderDescription::SamplerCubeArray:
+ return 0;
+ case QShaderDescription::SamplerRect:
+ return 0;
+ case QShaderDescription::SamplerBuffer:
+ return 0;
+
+ case QShaderDescription::Image1D:
+ return 0;
+ case QShaderDescription::Image2D:
+ return 0;
+ case QShaderDescription::Image2DMS:
+ return 0;
+ case QShaderDescription::Image3D:
+ return 0;
+ case QShaderDescription::ImageCube:
+ return 0;
+ case QShaderDescription::Image1DArray:
+ return 0;
+ case QShaderDescription::Image2DArray:
+ return 0;
+ case QShaderDescription::Image2DMSArray:
+ return 0;
+ case QShaderDescription::Image3DArray:
+ return 0;
+ case QShaderDescription::ImageCubeArray:
+ return 0;
+ case QShaderDescription::ImageRect:
+ return 0;
+ case QShaderDescription::ImageBuffer:
+ return 0;
+
+ case QShaderDescription::Struct:
+ return 0;
+ default:
+ return 0;
+ }
+}
+
+template<typename T, typename Pred>
+QVector<T> stableRemoveDuplicates(QVector<T> in, Pred predicate)
+{
+ QVector<T> out;
+ for (const auto &element : in) {
+ if (std::none_of(out.begin(), out.end(),
+ [&](T &other) { return predicate(element, other); }))
+ out.push_back(element);
+ }
+ return out;
+}
+
+// Utility function to enumerate an array of dimensions
+// Given dims == [0, 3, 2] and maxs == [4, 4, 4]
+// changes dims into [0, 3, 3]
+// Given dims == [0, 3, 3] and maxs == [4, 4, 4]
+// changes dims into [1, 0, 0]
+bool incrementArray(QVarLengthArray<int> &dims, const QVector<int> &maxs)
+{
+ const int n = dims.size();
+ int i = n;
+ for (; i-- > 0;) {
+ if (dims[i] == maxs[i] - 1) {
+ if (i == 0) {
+ // we're done
+ return false;
+ }
+ continue;
+
+ } else {
+ dims[i]++;
+ for (int j = i + 1; j < n; j++) {
+ dims[j] = 0;
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+// Call a function with a string such as [0][3][2]
+// for all valable array values, given an array of dimension sizes.
+// Dimensions must all be >= 1
+template<typename F>
+void forEachArrayAccessor(const QVector<int> &maxs, F f)
+{
+ if (std::any_of(maxs.begin(), maxs.end(), [](int v) { return v <= 0; }))
+ return;
+
+ QVarLengthArray<int> dims;
+ dims.resize(maxs.size());
+
+ // QVarLengthArray does not initialize ints
+ std::fill(dims.begin(), dims.end(), 0);
+
+ QString str;
+
+ do {
+ str.resize(0);
+ for (int k : dims) {
+ str += QStringLiteral("[%1]").arg(k);
+ }
+ f(str);
+ } while (incrementArray(dims, maxs));
+}
+}
+
+void RHIShader::recordAllUniforms(const QShaderDescription::BlockVariable &member,
+ QString parentName)
+{
+ const bool isStruct = !member.structMembers.empty();
+ const bool isArray = !member.arrayDims.empty();
+
+ // "foo.bar"
+ const QString fullMemberName = parentName + member.name;
+ m_unqualifiedUniformNames << fullMemberName;
+
+ if (isStruct && !isArray) {
+ m_structNames << fullMemberName;
+ m_structNamesIds << StringToInt::lookupId(fullMemberName);
+
+ for (const QShaderDescription::BlockVariable& bv : member.structMembers) {
+ // recordAllUniforms("baz", "foo.bar.")
+ recordAllUniforms(bv, fullMemberName + QLatin1Char('.'));
+ }
+ } else if (!isStruct && isArray) {
+ // We iterate through all the [l][n][m] by building [0][0][0] and incrementing
+ forEachArrayAccessor(member.arrayDims, [&](const QString &str) {
+ // "foo.bar[1][2]"
+ m_unqualifiedUniformNames << (fullMemberName + str);
+ // Question : does it make sense to also record foo[0], foo[0][0], etc...
+ // if there are e.g. 3 dimensions ?
+ });
+ }
+ else if (isStruct && isArray) {
+ // Record the struct names
+ forEachArrayAccessor(member.arrayDims, [&] (const QString& str) {
+ m_structNames << (fullMemberName + str);
+ m_structNamesIds << StringToInt::lookupId(m_structNames.back());
+ });
+
+ // Record the struct members
+ for (const QShaderDescription::BlockVariable& bv : member.structMembers) {
+ forEachArrayAccessor(member.arrayDims, [&] (const QString& str) {
+ //recordAllUniforms("baz", "foo.bar[1][2].")
+ recordAllUniforms(bv, fullMemberName + str + QLatin1Char('.'));
+ });
+ }
+ }
+}
+
+void RHIShader::introspect()
+{
+ const thread_local QRegularExpression generatedUBOName { "_[0-9]+" };
+ QVector<QShaderDescription::UniformBlock> rhiUBO;
+ QVector<QShaderDescription::StorageBlock> rhiSSBO;
+
+ QVector<ShaderUniformBlock> uniformBlocks;
+ QVector<ShaderStorageBlock> storageBlocks;
+ QVector<ShaderAttribute> attributes;
+ QVector<ShaderAttribute> samplers;
+ QVector<ShaderAttribute> images;
+
+ // Introspect shader vertex input
+ if (m_stages[QShader::VertexStage].isValid()) {
+ const QShaderDescription &vtx = m_stages[QShader::VertexStage].description();
+
+ for (const QShaderDescription::InOutVariable &input : vtx.inputVariables()) {
+ attributes.push_back(ShaderAttribute { input.name, StringToInt::lookupId(input.name),
+ input.type, rhiTypeSize(input.type),
+ input.location });
+ }
+
+ rhiUBO += vtx.uniformBlocks();
+ rhiSSBO += vtx.storageBlocks();
+ }
+
+ // Introspect shader uniforms
+
+ if (m_stages[QShader::FragmentStage].isValid()) {
+ const QShaderDescription &frag = m_stages[QShader::FragmentStage].description();
+ for (const QShaderDescription::InOutVariable &sampler : frag.combinedImageSamplers()) {
+ samplers.push_back(ShaderAttribute { sampler.name, StringToInt::lookupId(sampler.name),
+ sampler.type, rhiTypeSize(sampler.type),
+ sampler.binding });
+ }
+ for (const QShaderDescription::InOutVariable &image : frag.storageImages()) {
+ images.push_back(ShaderAttribute { image.name, StringToInt::lookupId(image.name),
+ image.type, rhiTypeSize(image.type),
+ image.binding });
+ }
+
+ rhiUBO += frag.uniformBlocks();
+ rhiSSBO += frag.storageBlocks();
+ }
+
+ rhiUBO = stableRemoveDuplicates(rhiUBO,
+ [](const QShaderDescription::UniformBlock &lhs,
+ const QShaderDescription::UniformBlock &rhs) {
+ return lhs.blockName == rhs.blockName;
+ });
+ rhiSSBO = stableRemoveDuplicates(rhiSSBO,
+ [](const QShaderDescription::StorageBlock &lhs,
+ const QShaderDescription::StorageBlock &rhs) {
+ return lhs.blockName == rhs.blockName;
+ });
+
+ for (const QShaderDescription::UniformBlock &ubo : rhiUBO) {
+ uniformBlocks.push_back(ShaderUniformBlock { ubo.blockName,
+ StringToInt::lookupId(ubo.blockName), -1,
+ ubo.binding, ubo.members.size(), ubo.size });
+ const bool addUnqualifiedUniforms = ubo.structName.contains(generatedUBOName);
+
+ // Parse Uniform Block members so that we can later on map a Parameter name to an actual
+ // member
+ const QVector<QShaderDescription::BlockVariable> members = ubo.members;
+
+ QVector<int> namesIds;
+ namesIds.reserve(members.size());
+
+ for (const QShaderDescription::BlockVariable &member : members) {
+ namesIds << StringToInt::lookupId(member.name);
+ if (addUnqualifiedUniforms) {
+ recordAllUniforms(member, QStringLiteral(""));
+ }
+ }
+ m_uniformsNamesIds += namesIds;
+ m_uboMembers.push_back({ uniformBlocks.last(), members });
+ }
+
+ for (const QShaderDescription::StorageBlock &ssbo : rhiSSBO) {
+ storageBlocks.push_back(ShaderStorageBlock { ssbo.blockName, -1, -1, ssbo.binding, 0, 0 });
+ }
+
+ initializeAttributes(attributes);
+ initializeUniformBlocks(uniformBlocks);
+ initializeShaderStorageBlocks(storageBlocks);
+ initializeSamplers(samplers);
+ initializeImages(images);
+}
+
+QHash<QString, ShaderUniform> RHIShader::activeUniformsForUniformBlock(int blockIndex) const
+{
+ return m_uniformBlockIndexToShaderUniforms.value(blockIndex);
+}
+
+ShaderUniformBlock RHIShader::uniformBlockForBlockIndex(int blockIndex) const noexcept
+{
+ 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 RHIShader::uniformBlockForBlockNameId(int blockNameId) const noexcept
+{
+ 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 RHIShader::uniformBlockForBlockName(const QString &blockName) const noexcept
+{
+ 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 RHIShader::storageBlockForBlockIndex(int blockIndex) const noexcept
+{
+ 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 RHIShader::storageBlockForBlockNameId(int blockNameId) const noexcept
+{
+ 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 RHIShader::storageBlockForBlockName(const QString &blockName) const noexcept
+{
+ for (int i = 0, m = m_shaderStorageBlockNames.size(); i < m; ++i) {
+ if (m_shaderStorageBlocks[i].m_name == blockName)
+ return m_shaderStorageBlocks[i];
+ }
+ return ShaderStorageBlock();
+}
+
+RHIShader::ParameterKind RHIShader::categorizeVariable(int nameId) const noexcept
+{
+ if (m_uniformsNamesIds.contains(nameId))
+ return ParameterKind::Uniform;
+ else if (m_uniformBlockNamesIds.contains(nameId))
+ return ParameterKind::UBO;
+ else if (m_shaderStorageBlockNamesIds.contains(nameId))
+ return ParameterKind::SSBO;
+ else if (m_structNamesIds.contains(nameId))
+ return ParameterKind::Struct;
+ return ParameterKind::Uniform;
+}
+
+bool RHIShader::hasUniform(int nameId) const noexcept
+{
+ return m_uniformsNamesIds.contains(nameId);
+}
+
+bool RHIShader::hasActiveVariables() const noexcept
+{
+ return !m_attributeNamesIds.empty() || !m_uniformsNamesIds.empty()
+ || !m_uniformBlockNamesIds.empty() || !m_shaderStorageBlockNamesIds.empty();
+}
+
+void RHIShader::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 RHIShader::setFragOutputs(const QHash<QString, int> &fragOutputs)
+{
+ {
+ QMutexLocker lock(&m_mutex);
+ m_fragOutputs = fragOutputs;
+ }
+ // updateDNA();
+}
+
+const QHash<QString, int> RHIShader::fragOutputs() const
+{
+ QMutexLocker lock(&m_mutex);
+ return m_fragOutputs;
+}
+
+void RHIShader::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 RHIShader::initializeSamplers(const QVector<ShaderAttribute> &samplersDescription)
+{
+ m_samplers = samplersDescription;
+ m_samplerNames.resize(samplersDescription.size());
+ m_samplerIds.resize(samplersDescription.size());
+ for (int i = 0, m = samplersDescription.size(); i < m; i++) {
+ m_samplerNames[i] = samplersDescription[i].m_name;
+ m_samplers[i].m_nameId = StringToInt::lookupId(m_samplerNames[i]);
+ m_samplerIds[i] = m_samplers[i].m_nameId;
+ qCDebug(Shaders) << "Active sampler " << samplersDescription[i].m_name;
+ }
+}
+
+void RHIShader::initializeImages(const QVector<ShaderAttribute> &imagesDescription)
+{
+ m_images = imagesDescription;
+ m_imageNames.resize(imagesDescription.size());
+ m_imageIds.resize(imagesDescription.size());
+ for (int i = 0, m = imagesDescription.size(); i < m; i++) {
+ m_imageNames[i] = imagesDescription[i].m_name;
+ m_images[i].m_nameId = StringToInt::lookupId(m_imageNames[i]);
+ m_imageIds[i] = m_images[i].m_nameId;
+ qCDebug(Shaders) << "Active image " << imagesDescription[i].m_name;
+ }
+}
+
+void RHIShader::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 RHIShader::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]
+ << "}";
+ }
+}
+
+} // Rhi
+
+} // Render
+
+} // Qt3DRender
+
+QT_END_NAMESPACE
diff --git a/src/plugins/renderers/rhi/renderer/rhishader_p.h b/src/plugins/renderers/rhi/renderer/rhishader_p.h
new file mode 100644
index 000000000..774dcbdb0
--- /dev/null
+++ b/src/plugins/renderers/rhi/renderer/rhishader_p.h
@@ -0,0 +1,198 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DRENDER_RENDER_RHI_GLSHADER_P_H
+#define QT3DRENDER_RENDER_RHI_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>
+#include <QtGui/private/qshader_p.h>
+#include <QtGui/private/qrhi_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+namespace Render {
+
+namespace Rhi {
+
+class Q_AUTOTEST_EXPORT RHIShader
+{
+public:
+ struct UBO_Member
+ {
+ ShaderUniformBlock block;
+ QVector<QShaderDescription::BlockVariable> members;
+ };
+
+ RHIShader();
+
+ 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;
+ QVector<QString> samplerNames() const;
+ QVector<QString> imagesNames() 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; }
+ inline QVector<ShaderAttribute> samplers() const { return m_samplers; }
+ inline QVector<ShaderAttribute> images() const { return m_images; }
+
+ QHash<QString, ShaderUniform> activeUniformsForUniformBlock(int blockIndex) const;
+
+ ShaderUniformBlock uniformBlockForBlockIndex(int blockNameId) const noexcept;
+ ShaderUniformBlock uniformBlockForBlockNameId(int blockIndex) const noexcept;
+ ShaderUniformBlock uniformBlockForBlockName(const QString &blockName) const noexcept;
+
+ ShaderStorageBlock storageBlockForBlockIndex(int blockIndex) const noexcept;
+ ShaderStorageBlock storageBlockForBlockNameId(int blockNameId) const noexcept;
+ ShaderStorageBlock storageBlockForBlockName(const QString &blockName) const noexcept;
+
+ enum ParameterKind { Uniform, UBO, SSBO, Struct };
+ ParameterKind categorizeVariable(int nameId) const noexcept;
+
+ bool hasUniform(int nameId) const noexcept;
+ bool hasActiveVariables() const noexcept;
+
+ void setShaderCode(const QVector<QByteArray> shaderCode) { m_shaderCode = shaderCode; }
+ QVector<QByteArray> shaderCode() const;
+
+ const QShader &shaderStage(QShader::Stage stage) const noexcept { return m_stages[stage]; }
+ QVector<UBO_Member> uboMembers() const { return m_uboMembers; }
+
+ const QSet<QString> &unqualifiedUniformNames() const noexcept
+ {
+ return m_unqualifiedUniformNames;
+ }
+
+ void introspect();
+
+private:
+ bool m_isLoaded;
+ QShader m_stages[6];
+
+ 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;
+ QSet<QString> m_unqualifiedUniformNames;
+
+ QVector<QString> m_shaderStorageBlockNames;
+ QVector<int> m_shaderStorageBlockNamesIds;
+ QVector<ShaderStorageBlock> m_shaderStorageBlocks;
+
+ QVector<QString> m_samplerNames;
+ QVector<int> m_samplerIds;
+ QVector<ShaderAttribute> m_samplers;
+
+ QVector<QString> m_imageNames;
+ QVector<int> m_imageIds;
+ QVector<ShaderAttribute> m_images;
+
+ QVector<QString> m_structNames;
+ QVector<int> m_structNamesIds;
+
+ QHash<QString, int> m_fragOutputs;
+ QVector<QByteArray> m_shaderCode;
+
+ // Private so that only SubmissionContext can call it
+ friend class SubmissionContext;
+ void initializeAttributes(const QVector<ShaderAttribute> &attributesDescription);
+ void initializeUniformBlocks(const QVector<ShaderUniformBlock> &uniformBlockDescription);
+ void
+ initializeShaderStorageBlocks(const QVector<ShaderStorageBlock> &shaderStorageBlockDescription);
+ void initializeSamplers(const QVector<ShaderAttribute> &samplerDescription);
+ void initializeImages(const QVector<ShaderAttribute> &imageDescription);
+ void recordAllUniforms(const QShaderDescription::BlockVariable &ubo, QString parentName);
+
+ QVector<UBO_Member> m_uboMembers;
+
+ mutable QMutex m_mutex;
+ QMetaObject::Connection m_contextConnection;
+};
+
+} // Rhi
+
+} // Render
+
+} // Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDER_RHI_GLSHADER_P_H
diff --git a/src/plugins/renderers/rhi/renderer/shaderparameterpack.cpp b/src/plugins/renderers/rhi/renderer/shaderparameterpack.cpp
new file mode 100644
index 000000000..5a28d28d4
--- /dev/null
+++ b/src/plugins/renderers/rhi/renderer/shaderparameterpack.cpp
@@ -0,0 +1,114 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "shaderparameterpack_p.h"
+
+#include <submissioncontext_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 {
+namespace Rhi {
+
+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 (NamedResource &texture : m_textures) {
+ if (texture.glslNameId != glslNameId || texture.uniformArrayIndex != uniformArrayIndex)
+ continue;
+
+ texture.nodeId = texId;
+ return;
+ }
+
+ m_textures.append(NamedResource(glslNameId, texId, uniformArrayIndex, NamedResource::Texture));
+}
+
+void ShaderParameterPack::setImage(const int glslNameId, int uniformArrayIndex,
+ Qt3DCore::QNodeId id)
+{
+ for (NamedResource &image : m_images) {
+ if (image.glslNameId != glslNameId || image.uniformArrayIndex != uniformArrayIndex)
+ continue;
+
+ image.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(blockToSSBO);
+}
+
+void ShaderParameterPack::setSubmissionUniform(const ShaderUniform &uniform)
+{
+ m_submissionUniforms.push_back(uniform);
+}
+
+} // namespace Rhi
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
diff --git a/src/plugins/renderers/rhi/renderer/shaderparameterpack_p.h b/src/plugins/renderers/rhi/renderer/shaderparameterpack_p.h
new file mode 100644
index 000000000..94c485a94
--- /dev/null
+++ b/src/plugins/renderers/rhi/renderer/shaderparameterpack_p.h
@@ -0,0 +1,215 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DRENDER_RENDER_RHI_SHADERPARAMETERPACK_P_H
+#define QT3DRENDER_RENDER_RHI_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 {
+namespace Rhi {
+
+class GraphicsContext;
+
+struct BlockToUBO
+{
+ int m_blockIndex;
+ Qt3DCore::QNodeId m_bufferID;
+ bool m_needsUpdate;
+ QHash<QString, QVariant> m_updatedProperties;
+};
+QT3D_DECLARE_TYPEINFO_3(Qt3DRender, Render, Rhi, BlockToUBO, Q_MOVABLE_TYPE)
+
+struct BlockToSSBO
+{
+ int m_blockIndex;
+ int m_bindingIndex;
+ Qt3DCore::QNodeId m_bufferID;
+};
+QT3D_DECLARE_TYPEINFO_3(Qt3DRender, Render, Rhi, 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_3(Qt3DRender, Render, Rhi, ShaderParameterPack::NamedResource,
+ Q_PRIMITIVE_TYPE)
+
+} // namespace Rhi
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+Q_DECLARE_METATYPE(Qt3DRender::Render::Rhi::ShaderParameterPack)
+
+#endif // QT3DRENDER_RENDER_RHI_SHADERPARAMETERPACK_P_H
diff --git a/src/plugins/renderers/rhi/renderer/shadervariables_p.h b/src/plugins/renderers/rhi/renderer/shadervariables_p.h
new file mode 100644
index 000000000..4e710a92c
--- /dev/null
+++ b/src/plugins/renderers/rhi/renderer/shadervariables_p.h
@@ -0,0 +1,126 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DRENDER_RENDER_RHI_SHADERVARIABLES_P_H
+#define QT3DRENDER_RENDER_RHI_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 <QtGui/private/qshaderdescription_p.h>
+#include <QOpenGLContext>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+namespace Render {
+
+namespace Rhi {
+
+struct ShaderAttribute
+{
+ QString m_name;
+ int m_nameId { -1 };
+ QShaderDescription::VariableType m_type {};
+ int m_size {};
+ int m_location { -1 };
+};
+QT3D_DECLARE_TYPEINFO_3(Qt3DRender, Render, Rhi, ShaderAttribute, Q_MOVABLE_TYPE)
+
+struct ShaderUniform
+{
+ QString m_name;
+ int m_nameId { -1 };
+ QShaderDescription::VariableType m_type { QShaderDescription::Unknown };
+ int m_size { 0 };
+ int m_offset { -1 }; // -1 default, >= 0 if uniform defined in uniform block
+ int m_location { -1 }; // -1 if uniform defined in a uniform block
+ int m_blockIndex { -1 }; // -1 is the default, >= 0 if uniform defined in uniform block
+ int m_arrayStride {
+ -1
+ }; // -1 is the default, >= 0 if uniform defined in uniform block and if it's an array
+ int m_matrixStride {
+ -1
+ }; // -1 is the default, >= 0 uniform defined in uniform block and is a matrix
+ uint m_rawByteSize { 0 }; // contains byte size (size / type / strides)
+ // size, offset and strides are in bytes
+};
+QT3D_DECLARE_TYPEINFO_3(Qt3DRender, Render, Rhi, ShaderUniform, Q_MOVABLE_TYPE)
+
+struct ShaderUniformBlock
+{
+ QString m_name;
+ int m_nameId { -1 };
+ int m_index { -1 };
+ int m_binding { -1 };
+ int m_activeUniformsCount { 0 };
+ int m_size { 0 };
+};
+QT3D_DECLARE_TYPEINFO_3(Qt3DRender, Render, Rhi, ShaderUniformBlock, Q_MOVABLE_TYPE)
+
+struct ShaderStorageBlock
+{
+ QString m_name;
+ int m_nameId { -1 };
+ int m_index { -1 };
+ int m_binding { -1 };
+ int m_size { 0 };
+ int m_activeVariablesCount { 0 };
+};
+QT3D_DECLARE_TYPEINFO_3(Qt3DRender, Render, Rhi, ShaderStorageBlock, Q_MOVABLE_TYPE)
+
+} // namespace Rhi
+
+} // namespace Render
+
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDER_RHI_SHADERVARIABLES_P_H
diff --git a/src/plugins/renderers/rhi/rhi.pri b/src/plugins/renderers/rhi/rhi.pri
new file mode 100644
index 000000000..8ae4a11af
--- /dev/null
+++ b/src/plugins/renderers/rhi/rhi.pri
@@ -0,0 +1,17 @@
+
+include (renderer/renderer.pri)
+include (jobs/jobs.pri)
+include (io/io.pri)
+include (textures/textures.pri)
+include (graphicshelpers/graphicshelpers.pri)
+include (managers/managers.pri)
+
+INCLUDEPATH += $$PWD
+
+# Qt3D is free of Q_FOREACH - make sure it stays that way:
+DEFINES += QT_NO_FOREACH
+
+gcov {
+ QMAKE_CXXFLAGS += -fprofile-arcs -ftest-coverage
+ QMAKE_LFLAGS += -fprofile-arcs -ftest-coverage
+}
diff --git a/src/plugins/renderers/rhi/rhi.pro b/src/plugins/renderers/rhi/rhi.pro
new file mode 100644
index 000000000..786749a71
--- /dev/null
+++ b/src/plugins/renderers/rhi/rhi.pro
@@ -0,0 +1,32 @@
+TARGET = rhirenderer
+
+PLUGIN_TYPE = renderers
+PLUGIN_CLASS_NAME = RhiRendererPlugin
+load(qt_plugin)
+
+QT += core-private gui-private 3dcore 3dcore-private 3drender 3drender-private shadertools shadertools-private
+
+# Qt3D is free of Q_FOREACH - make sure it stays that way:
+DEFINES += QT_NO_FOREACH
+
+# We use QT_AUTOTEST_EXPORT to test the plug-ins, which needs QT_BUILDING_QT
+DEFINES += QT_BUILDING_QT
+
+SOURCES += \
+ main.cpp
+
+DISTFILES += \
+ rhirenderer.json
+
+include(rhi.pri)
+
+qtConfig(qt3d-simd-avx2) {
+ CONFIG += simd
+ QMAKE_CXXFLAGS += $$QMAKE_CFLAGS_AVX2
+}
+
+qtConfig(qt3d-simd-sse2):!qtConfig(qt3d-simd-avx2) {
+ CONFIG += simd
+ QMAKE_CXXFLAGS += $$QMAKE_CFLAGS_SSE2
+}
+
diff --git a/src/plugins/renderers/rhi/rhirenderer.json b/src/plugins/renderers/rhi/rhirenderer.json
new file mode 100644
index 000000000..145b20636
--- /dev/null
+++ b/src/plugins/renderers/rhi/rhirenderer.json
@@ -0,0 +1,3 @@
+{
+ "Keys": ["rhi"]
+}
diff --git a/src/plugins/renderers/rhi/textures/renderbuffer.cpp b/src/plugins/renderers/rhi/textures/renderbuffer.cpp
new file mode 100644
index 000000000..8df256ad1
--- /dev/null
+++ b/src/plugins/renderers/rhi/textures/renderbuffer.cpp
@@ -0,0 +1,111 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE: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 "renderbuffer_p.h"
+#include <QtGui/QOpenGLContext>
+#include <QtGui/QOpenGLFunctions>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+namespace Render {
+namespace Rhi {
+
+RenderBuffer::RenderBuffer(int width, int height, QAbstractTexture::TextureFormat format)
+ : m_size(width, height), m_format(format), m_renderBuffer(0), m_context(nullptr)
+{
+ QOpenGLContext *ctx = QOpenGLContext::currentContext();
+ if (!ctx) {
+ qWarning("Renderbuffer requires an OpenGL context");
+ return;
+ }
+
+ m_context = ctx;
+ QOpenGLFunctions *f = ctx->functions();
+ f->glGenRenderbuffers(1, &m_renderBuffer);
+ if (!m_renderBuffer)
+ return;
+
+ f->glBindRenderbuffer(GL_RENDERBUFFER, m_renderBuffer);
+ while (f->glGetError() != GL_NO_ERROR) { }
+ f->glRenderbufferStorage(GL_RENDERBUFFER, format, width, height);
+ GLint err = f->glGetError();
+ if (err != GL_NO_ERROR)
+ qWarning("Failed to set renderbuffer storage: error 0x%x", err);
+ f->glBindRenderbuffer(GL_RENDERBUFFER, 0);
+}
+
+RenderBuffer::~RenderBuffer()
+{
+ if (m_renderBuffer) {
+ QOpenGLContext *ctx = QOpenGLContext::currentContext();
+
+ // Ignore the fact that renderbuffers are sharable resources and let's
+ // just expect that the context is the same as when the resource was
+ // created. QOpenGLTexture suffers from the same limitation anyway, and
+ // this is unlikely to become an issue within Qt 3D.
+ if (ctx == m_context) {
+ ctx->functions()->glDeleteRenderbuffers(1, &m_renderBuffer);
+ } else {
+ qWarning("Wrong current context; renderbuffer not destroyed");
+ }
+ }
+}
+
+void RenderBuffer::bind()
+{
+ if (!m_renderBuffer)
+ return;
+
+ m_context->functions()->glBindRenderbuffer(GL_RENDERBUFFER, m_renderBuffer);
+}
+
+void RenderBuffer::release()
+{
+ if (!m_context)
+ return;
+
+ m_context->functions()->glBindRenderbuffer(GL_RENDERBUFFER, 0);
+}
+
+} // namespace Rhi
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
diff --git a/src/plugins/renderers/rhi/textures/renderbuffer_p.h b/src/plugins/renderers/rhi/textures/renderbuffer_p.h
new file mode 100644
index 000000000..ed043366e
--- /dev/null
+++ b/src/plugins/renderers/rhi/textures/renderbuffer_p.h
@@ -0,0 +1,92 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE: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_RHI_RENDERBUFFER_P_H
+#define QT3DRENDER_RENDER_RHI_RENDERBUFFER_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/qabstracttexture.h>
+
+QT_BEGIN_NAMESPACE
+
+class QOpenGLContext;
+
+namespace Qt3DRender {
+namespace Render {
+namespace Rhi {
+
+class Q_AUTOTEST_EXPORT RenderBuffer
+{
+public:
+ RenderBuffer(int width, int height, QAbstractTexture::TextureFormat format);
+ ~RenderBuffer();
+
+ int width() const { return m_size.width(); }
+ int height() const { return m_size.height(); }
+ QSize size() const { return m_size; }
+ QAbstractTexture::TextureFormat format() const { return m_format; }
+ GLuint renderBufferId() const { return m_renderBuffer; }
+
+ void bind();
+ void release();
+
+private:
+ QSize m_size;
+ QAbstractTexture::TextureFormat m_format;
+ GLuint m_renderBuffer;
+ QOpenGLContext *m_context;
+};
+
+} // namespace Rhi
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDER_RHI_RENDERBUFFER_P_H
diff --git a/src/plugins/renderers/rhi/textures/texture.cpp b/src/plugins/renderers/rhi/textures/texture.cpp
new file mode 100644
index 000000000..53f9e62ca
--- /dev/null
+++ b/src/plugins/renderers/rhi/textures/texture.cpp
@@ -0,0 +1,918 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtCore/qhash.h>
+#include "texture_p.h"
+
+#include <private/qdebug_p.h>
+#include <private/qrhi_p.h>
+#include <QDebug>
+#include <Qt3DRender/qtexture.h>
+#include <Qt3DRender/qtexturedata.h>
+#include <Qt3DRender/qtextureimagedata.h>
+#include <Qt3DRender/private/managers_p.h>
+#include <Qt3DRender/private/qabstracttexture_p.h>
+#include <Qt3DRender/private/qtextureimagedata_p.h>
+#include <renderbuffer_p.h>
+#include <submissioncontext_p.h>
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt3DCore;
+
+namespace Qt3DRender {
+namespace Render {
+namespace Rhi {
+
+namespace {
+
+bool issRGBFormat(QAbstractTexture::TextureFormat format) noexcept
+{
+ switch (format) {
+ case QAbstractTexture::SRGB8:
+ case QAbstractTexture::SRGB8_ETC2:
+ case QAbstractTexture::SRGB8_PunchThrough_Alpha1_ETC2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+QRhiTexture::Format rhiFormatFromTextureFormat(QAbstractTexture::TextureFormat format) noexcept
+{
+ switch (format) {
+ case QAbstractTexture::RGBA8_UNorm:
+ case QAbstractTexture::SRGB8:
+ return QRhiTexture::RGBA8;
+ case QAbstractTexture::R8_UNorm:
+ return QRhiTexture::R8;
+ case QAbstractTexture::R16_UNorm:
+ return QRhiTexture::R16;
+ case QAbstractTexture::RGBA16F:
+ return QRhiTexture::RGBA16F;
+ case QAbstractTexture::RGBA32F:
+ return QRhiTexture::RGBA32F;
+ case QAbstractTexture::R16F:
+ return QRhiTexture::R16F;
+ case QAbstractTexture::R32F:
+ return QRhiTexture::R32F;
+ case QAbstractTexture::D16:
+ return QRhiTexture::D16;
+ case QAbstractTexture::D32F:
+ return QRhiTexture::D32F;
+ case QAbstractTexture::RGB_DXT1:
+ case QAbstractTexture::RGBA_DXT1:
+ return QRhiTexture::BC1;
+ case QAbstractTexture::RGBA_DXT3:
+ return QRhiTexture::BC2;
+ case QAbstractTexture::RGBA_DXT5:
+ return QRhiTexture::BC3;
+ case QAbstractTexture::RGB8_ETC2:
+ case QAbstractTexture::SRGB8_ETC2:
+ return QRhiTexture::ETC2_RGB8;
+ case QAbstractTexture::RGB8_PunchThrough_Alpha1_ETC2:
+ case QAbstractTexture::SRGB8_PunchThrough_Alpha1_ETC2:
+ return QRhiTexture::ETC2_RGB8A1;
+ case QAbstractTexture::RGBA8_ETC2_EAC:
+ return QRhiTexture::ETC2_RGBA8;
+ default:
+ Q_UNREACHABLE();
+ return QRhiTexture::UnknownFormat;
+ }
+}
+
+QRhiSampler::Filter rhiFilterFromTextureFilter(QAbstractTexture::Filter filter) noexcept
+{
+ switch (filter) {
+ case QAbstractTexture::Nearest:
+ case QAbstractTexture::NearestMipMapNearest:
+ case QAbstractTexture::NearestMipMapLinear:
+ return QRhiSampler::Nearest;
+ case QAbstractTexture::Linear:
+ case QAbstractTexture::LinearMipMapNearest:
+ case QAbstractTexture::LinearMipMapLinear:
+ return QRhiSampler::Linear;
+ default:
+ Q_UNREACHABLE();
+ return QRhiSampler::Nearest;
+ }
+}
+
+QRhiSampler::Filter rhiMipMapFilterFromTextureFilter(QAbstractTexture::Filter filter) noexcept
+{
+ switch (filter) {
+ case QAbstractTexture::NearestMipMapNearest:
+ case QAbstractTexture::LinearMipMapNearest:
+ return QRhiSampler::Nearest;
+ case QAbstractTexture::Linear:
+ case QAbstractTexture::NearestMipMapLinear:
+ case QAbstractTexture::LinearMipMapLinear:
+ return QRhiSampler::Linear;
+ default:
+ Q_UNREACHABLE();
+ return QRhiSampler::None;
+ }
+}
+
+std::tuple<QRhiSampler::AddressMode, QRhiSampler::AddressMode, QRhiSampler::AddressMode>
+rhiWrapModeFromTextureWrapMode(QTextureWrapMode::WrapMode x, QTextureWrapMode::WrapMode y,
+ QTextureWrapMode::WrapMode z) noexcept
+{
+ auto toRhiAddress = [](QTextureWrapMode::WrapMode mode) noexcept {
+ switch (mode) {
+ case Qt3DRender::QTextureWrapMode::Repeat:
+ return QRhiSampler::Repeat;
+ case Qt3DRender::QTextureWrapMode::ClampToEdge:
+ case Qt3DRender::QTextureWrapMode::ClampToBorder:
+ return QRhiSampler::ClampToEdge;
+ case Qt3DRender::QTextureWrapMode::MirroredRepeat:
+ return QRhiSampler::Mirror;
+ default:
+ Q_UNREACHABLE();
+ }
+ };
+
+ return { toRhiAddress(x), toRhiAddress(y), toRhiAddress(z) };
+}
+
+QRhiSampler::CompareOp
+rhiCompareOpFromTextureCompareOp(QAbstractTexture::ComparisonFunction mode) noexcept
+{
+ switch (mode) {
+ case QAbstractTexture::CompareLessEqual:
+ return QRhiSampler::LessOrEqual;
+ case QAbstractTexture::CompareGreaterEqual:
+ return QRhiSampler::GreaterOrEqual;
+ case QAbstractTexture::CompareLess:
+ return QRhiSampler::Less;
+ case QAbstractTexture::CompareGreater:
+ return QRhiSampler::Greater;
+ case QAbstractTexture::CompareEqual:
+ return QRhiSampler::Equal;
+ case QAbstractTexture::CommpareNotEqual:
+ return QRhiSampler::NotEqual;
+ case QAbstractTexture::CompareAlways:
+ return QRhiSampler::Always;
+ case QAbstractTexture::CompareNever:
+ return QRhiSampler::Never;
+ default:
+ return QRhiSampler::Always;
+ }
+}
+
+// This uploadGLData where the data is a fullsize subimage
+// as QOpenGLTexture doesn't allow partial subimage uploads
+QRhiTextureUploadEntry createUploadEntry(int level, int layer, const QByteArray &bytes) noexcept
+{
+ QRhiTextureSubresourceUploadDescription description;
+ description.setData(bytes);
+ return QRhiTextureUploadEntry(layer, level, description);
+}
+
+template<typename F>
+void filterLayersAndFaces(const QTextureImageData &data, F f)
+{
+ const int layers = data.layers();
+ const int faces = data.faces();
+ const int miplevels = data.mipLevels();
+
+ if (layers == 1 && faces == 1) {
+ for (int level = 0; level < miplevels; level++) {
+ f(createUploadEntry(level, 0, data.data(0, 0, level)));
+ }
+ } else if (layers > 1 && faces == 1) {
+ qWarning() << Q_FUNC_INFO << "Unsupported case, see QTBUG-83343";
+ /*
+ for (int layer = 0; layer < data.layers(); layer++) {
+ for (int level = 0; level < mipLevels; level++) {
+ f(createUploadEntry(level, layer, data.data(layer, 0, level)));
+ }
+ }
+ */
+ } else if (faces > 1 && layers == 1) {
+ // Mip levels do not seem to be supported by cubemaps...
+ for (int face = 0; face < data.faces(); face++) {
+ f(createUploadEntry(0, face, data.data(0, face, 0)));
+ }
+ } else {
+ qWarning() << Q_FUNC_INFO << "Unsupported case";
+ }
+}
+
+template<typename F>
+void filterLayerAndFace(int layer, int face, F f)
+{
+ if (layer == 0 && face == 0) {
+ f(0);
+ } else if (layer > 0 && face == 0) {
+ qWarning() << Q_FUNC_INFO << "Unsupported case, see QTBUG-83343";
+ // f(layer);
+ } else if (layer == 0 && face > 0) {
+ f(face);
+ } else {
+ qWarning() << Q_FUNC_INFO << "Unsupported case";
+ }
+}
+
+// For partial sub image uploads
+QRhiTextureUploadEntry createUploadEntry(int mipLevel, int layer, int xOffset, int yOffset,
+ int zOffset, const QByteArray &bytes,
+ const QTextureImageDataPtr &data) noexcept
+{
+ QRhiTextureSubresourceUploadDescription description;
+ description.setData(bytes);
+ description.setSourceTopLeft(QPoint(xOffset, yOffset));
+ return QRhiTextureUploadEntry(layer, mipLevel, description);
+}
+
+} // anonymous
+
+RHITexture::RHITexture()
+ : m_dirtyFlags(None),
+ m_rhi(nullptr),
+ m_rhiSampler(nullptr),
+ m_renderBuffer(nullptr),
+ m_dataFunctor(),
+ m_pendingDataFunctor(nullptr),
+ m_sharedTextureId(-1),
+ m_externalRendering(false),
+ m_wasTextureRecreated(false)
+{
+}
+
+RHITexture::~RHITexture() { }
+
+// Must be called from RenderThread with active GL context
+void RHITexture::destroy()
+{
+ delete m_rhi;
+ m_rhi = nullptr;
+ delete m_rhiSampler;
+ m_rhiSampler = nullptr;
+ delete m_renderBuffer;
+ m_renderBuffer = nullptr;
+
+ m_dirtyFlags = None;
+ m_sharedTextureId = -1;
+ m_externalRendering = false;
+ m_wasTextureRecreated = false;
+ m_dataFunctor.reset();
+ m_pendingDataFunctor = nullptr;
+
+ m_properties = {};
+ m_parameters = {};
+ m_textureData.reset();
+ m_images.clear();
+ m_imageData.clear();
+ m_pendingTextureDataUpdates.clear();
+}
+
+bool RHITexture::loadTextureDataFromGenerator()
+{
+ m_textureData = m_dataFunctor->operator()();
+ // if there is a texture generator, most properties will be defined by it
+ if (m_textureData) {
+ const QAbstractTexture::Target target = m_textureData->target();
+
+ // If both target and functor return Automatic we are still
+ // probably loading the texture, return false
+ if (m_properties.target == QAbstractTexture::TargetAutomatic
+ && target == QAbstractTexture::TargetAutomatic) {
+ m_textureData.reset();
+ return false;
+ }
+
+ if (m_properties.target != QAbstractTexture::TargetAutomatic
+ && target != QAbstractTexture::TargetAutomatic && m_properties.target != target) {
+ qWarning() << Q_FUNC_INFO
+ << "Generator and Properties not requesting the same texture target";
+ m_textureData.reset();
+ return false;
+ }
+
+ // We take target type from generator if it wasn't explicitly set by the user
+ if (m_properties.target == QAbstractTexture::TargetAutomatic)
+ m_properties.target = target;
+ m_properties.width = m_textureData->width();
+ m_properties.height = m_textureData->height();
+ m_properties.depth = m_textureData->depth();
+ m_properties.layers = m_textureData->layers();
+ m_properties.format = m_textureData->format();
+
+ const QVector<QTextureImageDataPtr> imageData = m_textureData->imageData();
+
+ if (!imageData.empty()) {
+ // Set the mips level based on the first image if autoMipMapGeneration is disabled
+ if (!m_properties.generateMipMaps)
+ m_properties.mipLevels = imageData.first()->mipLevels();
+ }
+ }
+ return !m_textureData.isNull();
+}
+
+void RHITexture::loadTextureDataFromImages()
+{
+ int maxMipLevel = 0;
+ for (const Image &img : qAsConst(m_images)) {
+ const QTextureImageDataPtr imgData = img.generator->operator()();
+ // imgData may be null in the following cases:
+ // - Texture is created with TextureImages which have yet to be
+ // loaded (skybox where you don't yet know the path, source set by
+ // a property binding, queued connection ...)
+ // - TextureImage whose generator failed to return a valid data
+ // (invalid url, error opening file...)
+ if (imgData.isNull())
+ continue;
+
+ m_imageData.push_back(imgData);
+ maxMipLevel = qMax(maxMipLevel, img.mipLevel);
+
+ // If the texture doesn't have a texture generator, we will
+ // derive some properties from the first TextureImage (layer=0, miplvl=0, face=0)
+ if (!m_textureData && img.layer == 0 && img.mipLevel == 0
+ && img.face == QAbstractTexture::CubeMapPositiveX) {
+ if (imgData->width() != -1 && imgData->height() != -1 && imgData->depth() != -1) {
+ m_properties.width = imgData->width();
+ m_properties.height = imgData->height();
+ m_properties.depth = imgData->depth();
+ }
+ // Set the format of the texture if the texture format is set to Automatic
+ if (m_properties.format == QAbstractTexture::Automatic) {
+ m_properties.format =
+ static_cast<QAbstractTexture::TextureFormat>(imgData->format());
+ }
+ setDirtyFlag(Properties, true);
+ }
+ }
+
+ // make sure the number of mip levels is set when there is no texture data generator
+ if (!m_dataFunctor) {
+ m_properties.mipLevels = maxMipLevel + 1;
+ setDirtyFlag(Properties, true);
+ }
+}
+
+// Called from RenderThread
+RHITexture::TextureUpdateInfo RHITexture::createOrUpdateRhiTexture(SubmissionContext *ctx)
+{
+ TextureUpdateInfo textureInfo;
+ m_wasTextureRecreated = false;
+
+ const bool hasSharedTextureId = m_sharedTextureId > 0;
+ // Only load texture data if we are not using a sharedTextureId
+ // Check if dataFunctor or images have changed
+ if (!hasSharedTextureId) {
+ // If dataFunctor exists and we have no data and it hasnĀ“t run yet
+ if (m_dataFunctor && !m_textureData && m_dataFunctor.get() != m_pendingDataFunctor) {
+ const bool successfullyLoadedTextureData = loadTextureDataFromGenerator();
+ // If successful, m_textureData has content
+ if (successfullyLoadedTextureData) {
+ setDirtyFlag(Properties, true);
+ setDirtyFlag(TextureData, true);
+ } else {
+ if (m_pendingDataFunctor != m_dataFunctor.get()) {
+ qWarning() << "[Qt3DRender::RHITexture] No QTextureData generated from Texture "
+ "Generator yet. Texture will be invalid for this frame";
+ m_pendingDataFunctor = m_dataFunctor.get();
+ }
+ textureInfo.properties.status = QAbstractTexture::Loading;
+ return textureInfo;
+ }
+ }
+
+ // If images have changed, clear previous images data
+ // and regenerate m_imageData for the images
+ if (testDirtyFlag(TextureImageData)) {
+ m_imageData.clear();
+ loadTextureDataFromImages();
+ // Mark for upload if we actually have something to upload
+ if (!m_imageData.empty()) {
+ setDirtyFlag(TextureData, true);
+ }
+ // Reset image flag
+ setDirtyFlag(TextureImageData, false);
+ }
+
+ // Don't try to create the texture if the target or format was still not set
+ // Format should either be set by user or if Automatic
+ // by either the dataGenerator of the texture or the first Image
+ // Target should explicitly be set by the user or the dataGenerator
+ if (m_properties.target == QAbstractTexture::TargetAutomatic
+ || m_properties.format == QAbstractTexture::Automatic
+ || m_properties.format == QAbstractTexture::NoFormat) {
+ textureInfo.properties.status = QAbstractTexture::Error;
+ return textureInfo;
+ }
+ }
+
+ // If the properties changed or texture has become a shared texture from a
+ // 3rd party engine, we need to destroy and maybe re-allocate the texture
+ if (testDirtyFlag(Properties) || testDirtyFlag(SharedTextureId)) {
+ delete m_rhi;
+ m_rhi = nullptr;
+ textureInfo.wasUpdated = true;
+ // If we are destroyed because of some property change but still have (some) of
+ // our content data make sure we are marked for upload
+ // TO DO: We should actually check if the textureData is still correct
+ // in regard to the size, target and format of the texture though.
+ if (!testDirtyFlag(SharedTextureId)
+ && (m_textureData || !m_imageData.empty() || !m_pendingTextureDataUpdates.empty()))
+ setDirtyFlag(TextureData, true);
+ }
+
+ m_properties.status = QAbstractTexture::Ready;
+
+ if (testDirtyFlag(SharedTextureId) || hasSharedTextureId) {
+ // Update m_properties by doing introspection on the texture
+ if (hasSharedTextureId)
+ introspectPropertiesFromSharedTextureId();
+ setDirtyFlag(SharedTextureId, false);
+ } else {
+ // We only build a QOpenGLTexture if we have no shared textureId set
+ if (!m_rhi) {
+ m_rhi = buildRhiTexture(ctx);
+ if (!m_rhi) {
+ qWarning() << "[Qt3DRender::RHITexture] failed to create texture";
+ textureInfo.properties.status = QAbstractTexture::Error;
+ return textureInfo;
+ }
+ m_wasTextureRecreated = true;
+ }
+
+ textureInfo.texture = m_rhi;
+
+ // need to (re-)upload texture data?
+ const bool needsUpload = testDirtyFlag(TextureData);
+ if (needsUpload) {
+ uploadRhiTextureData(ctx);
+ setDirtyFlag(TextureData, false);
+ }
+
+ // need to set texture parameters?
+ if (testDirtyFlag(Properties) || testDirtyFlag(Parameters)) {
+ updateRhiTextureParameters(ctx);
+ setDirtyFlag(Properties, false);
+ setDirtyFlag(Parameters, false);
+ }
+ }
+
+ textureInfo.properties = m_properties;
+
+ return textureInfo;
+}
+
+RenderBuffer *RHITexture::getOrCreateRenderBuffer()
+{
+ if (m_dataFunctor && !m_textureData) {
+ m_textureData = m_dataFunctor->operator()();
+ if (m_textureData) {
+ if (m_properties.target != QAbstractTexture::TargetAutomatic)
+ qWarning() << "[Qt3DRender::RHITexture] [renderbuffer] When a texture provides a "
+ "generator, it's target is expected to be TargetAutomatic";
+
+ m_properties.width = m_textureData->width();
+ m_properties.height = m_textureData->height();
+ m_properties.format = m_textureData->format();
+
+ setDirtyFlag(Properties);
+ } else {
+ if (m_pendingDataFunctor != m_dataFunctor.get()) {
+ qWarning() << "[Qt3DRender::RHITexture] [renderbuffer] No QTextureData generated "
+ "from Texture Generator yet. Texture will be invalid for this frame";
+ m_pendingDataFunctor = m_dataFunctor.get();
+ }
+ return nullptr;
+ }
+ }
+
+ if (testDirtyFlag(Properties)) {
+ delete m_renderBuffer;
+ m_renderBuffer = nullptr;
+ }
+
+ if (!m_renderBuffer)
+ m_renderBuffer =
+ new RenderBuffer(m_properties.width, m_properties.height, m_properties.format);
+
+ setDirtyFlag(Properties, false);
+ setDirtyFlag(Parameters, false);
+
+ return m_renderBuffer;
+}
+
+// This must be called from the RenderThread
+// So RHITexture release from the manager can only be done from that thread
+void RHITexture::cleanup()
+{
+ destroy();
+}
+
+void RHITexture::setParameters(const TextureParameters &params)
+{
+ if (m_parameters != params) {
+ m_parameters = params;
+ setDirtyFlag(Parameters);
+ }
+}
+
+void RHITexture::setProperties(const TextureProperties &props)
+{
+ if (m_properties != props) {
+ m_properties = props;
+ setDirtyFlag(Properties);
+ }
+}
+
+void RHITexture::setImages(const QVector<Image> &images)
+{
+ // check if something has changed at all
+ bool same = (images.size() == m_images.size());
+ if (same) {
+ for (int i = 0; i < images.size(); i++) {
+ if (images[i] != m_images[i]) {
+ same = false;
+ break;
+ }
+ }
+ }
+
+ if (!same) {
+ m_images = images;
+ requestImageUpload();
+ }
+}
+
+void RHITexture::setGenerator(const QTextureGeneratorPtr &generator)
+{
+ m_textureData.reset();
+ m_dataFunctor = generator;
+ m_pendingDataFunctor = nullptr;
+ requestUpload();
+}
+
+void RHITexture::setSharedTextureId(int textureId)
+{
+ if (m_sharedTextureId != textureId) {
+ m_sharedTextureId = textureId;
+ setDirtyFlag(SharedTextureId);
+ }
+}
+
+void RHITexture::addTextureDataUpdates(const QVector<QTextureDataUpdate> &updates)
+{
+ m_pendingTextureDataUpdates += updates;
+ requestUpload();
+}
+
+// Return nullptr if
+// - context cannot be obtained
+// - texture hasn't yet been loaded
+QRhiTexture *RHITexture::buildRhiTexture(SubmissionContext *ctx)
+{
+ const QAbstractTexture::Target actualTarget = m_properties.target;
+ if (actualTarget == QAbstractTexture::TargetAutomatic) {
+ // If the target is automatic at this point, it means that the texture
+ // hasn't been loaded yet (case of remote urls) and that loading failed
+ // and that target format couldn't be deduced
+ return nullptr;
+ }
+
+ const QRhiTexture::Format rhiFormat = rhiFormatFromTextureFormat(m_properties.format);
+ const QSize pixelSize(m_properties.width, m_properties.height);
+ QRhiTexture::Flags rhiFlags {};
+ int sampleCount = 1;
+
+ const bool issRGB8Format = issRGBFormat(m_properties.format);
+ if (issRGB8Format)
+ rhiFlags |= QRhiTexture::sRGB;
+
+ if (actualTarget == QAbstractTexture::Target2DMultisample
+ || actualTarget == QAbstractTexture::Target2DMultisampleArray) {
+ // Set samples count if multisampled texture
+ // (multisampled textures don't have mipmaps)
+ sampleCount = m_properties.samples;
+ }
+
+ switch (actualTarget) {
+ case QAbstractTexture::TargetCubeMap:
+ case QAbstractTexture::TargetCubeMapArray: {
+ rhiFlags |= QRhiTexture::CubeMap;
+ break;
+ }
+ default: {
+ // Mipmaps don't see to work with cubemaps at the moment
+ if (m_properties.generateMipMaps) {
+ rhiFlags |= QRhiTexture::UsedWithGenerateMips;
+ rhiFlags |= QRhiTexture::MipMapped;
+ } else if (m_properties.mipLevels > 1) {
+ rhiFlags |= QRhiTexture::MipMapped;
+ }
+ break;
+ }
+ }
+
+ QRhiTexture *rhiTexture = ctx->rhi()->newTexture(rhiFormat, pixelSize, sampleCount, rhiFlags);
+
+ if (!rhiTexture->build()) {
+ qWarning() << Q_FUNC_INFO << "creating QRhiTexture failed";
+ delete rhiTexture;
+ return nullptr;
+ }
+ return rhiTexture;
+}
+
+void RHITexture::uploadRhiTextureData(SubmissionContext *ctx)
+{
+ QVarLengthArray<QRhiTextureUploadEntry> uploadEntries;
+
+ // Upload all QTexImageData set by the QTextureGenerator
+ if (m_textureData) {
+ const QVector<QTextureImageDataPtr> &imgData = m_textureData->imageData();
+
+ for (const QTextureImageDataPtr &data : imgData) {
+ const int mipLevels = data->mipLevels();
+ Q_ASSERT(mipLevels <= ctx->rhi()->mipLevelsForSize({ data->width(), data->height() }));
+
+ filterLayersAndFaces(*data, [&](QRhiTextureUploadEntry &&entry) {
+ uploadEntries.push_back(std::move(entry));
+ });
+ }
+ }
+
+ // Upload all QTexImageData references by the TextureImages
+ for (int i = 0; i < std::min(m_images.size(), m_imageData.size()); i++) {
+ const QTextureImageDataPtr &imgData = m_imageData.at(i);
+ // Here the bytes in the QTextureImageData contain data for a single
+ // layer, face or mip level, unlike the QTextureGenerator case where
+ // they are in a single blob. Hence QTextureImageData::data() is not suitable.
+ const QByteArray bytes(QTextureImageDataPrivate::get(imgData.get())->m_data);
+ const int layer = m_images[i].layer;
+ const int face = m_images[i].face;
+ filterLayerAndFace(layer, face, [&](int rhiLayer) {
+ uploadEntries.push_back(createUploadEntry(m_images[i].mipLevel, rhiLayer, bytes));
+ });
+ }
+
+ // Free up image data once content has been uploaded
+ // Note: if data functor stores the data, this won't really free anything though
+ m_imageData.clear();
+
+ // Update data from TextureUpdates
+ const QVector<QTextureDataUpdate> textureDataUpdates = std::move(m_pendingTextureDataUpdates);
+ for (const QTextureDataUpdate &update : textureDataUpdates) {
+ const QTextureImageDataPtr imgData = update.data();
+
+ if (!imgData) {
+ qWarning() << Q_FUNC_INFO << "QTextureDataUpdate no QTextureImageData set";
+ continue;
+ }
+
+ // TO DO: There's currently no way to check the depth of an existing QRhiTexture
+ const int xOffset = update.x();
+ const int yOffset = update.y();
+ const int xExtent = xOffset + imgData->width();
+ const int yExtent = yOffset + imgData->height();
+
+ // Check update is compatible with our texture
+ if (xOffset >= m_rhi->pixelSize().width() || yOffset >= m_rhi->pixelSize().height()
+ || xExtent > m_rhi->pixelSize().width() || yExtent > m_rhi->pixelSize().height()) {
+ qWarning() << Q_FUNC_INFO << "QTextureDataUpdate incompatible with texture";
+ continue;
+ }
+
+ const QByteArray bytes = (QTextureImageDataPrivate::get(imgData.get())->m_data);
+ // Here the bytes in the QTextureImageData contain data for a single
+ // layer, face or mip level, unlike the QTextureGenerator case where
+ // they are in a single blob. Hence QTextureImageData::data() is not suitable.
+
+ const int layer = update.layer();
+ const int face = update.face();
+ filterLayerAndFace(layer, face, [&](int rhiLayer) {
+ const QRhiTextureUploadEntry entry = createUploadEntry(
+ update.mipLevel(), rhiLayer, xOffset, yOffset, 0, bytes, imgData);
+ uploadEntries.push_back(entry);
+ });
+ }
+
+ QRhiTextureUploadDescription uploadDescription;
+ uploadDescription.setEntries(uploadEntries.begin(), uploadEntries.end());
+
+ ctx->m_currentUpdates->uploadTexture(m_rhi, uploadDescription);
+ if (m_properties.generateMipMaps)
+ ctx->m_currentUpdates->generateMips(m_rhi);
+}
+
+void RHITexture::updateRhiTextureParameters(SubmissionContext *ctx)
+{
+ const QAbstractTexture::Target actualTarget = m_properties.target;
+ const bool isMultisampledTexture =
+ (actualTarget == QAbstractTexture::Target2DMultisample
+ || actualTarget == QAbstractTexture::Target2DMultisampleArray);
+ // Multisampled textures can only be accessed by texelFetch in shaders
+ // and don't support wrap modes and mig/mag filtes
+ if (isMultisampledTexture)
+ return;
+
+ // TO DO:
+ if (m_rhiSampler) {
+ delete m_rhiSampler;
+ m_rhiSampler = nullptr;
+ }
+
+ const QRhiSampler::Filter magFilter =
+ rhiFilterFromTextureFilter(m_parameters.magnificationFilter);
+ const QRhiSampler::Filter minFilter =
+ rhiFilterFromTextureFilter(m_parameters.minificationFilter);
+ const QRhiSampler::Filter mipMapFilter =
+ rhiMipMapFilterFromTextureFilter(m_parameters.magnificationFilter);
+ const auto wrapMode = rhiWrapModeFromTextureWrapMode(
+ m_parameters.wrapModeX, m_parameters.wrapModeY, m_parameters.wrapModeZ);
+ const QRhiSampler::CompareOp compareOp =
+ rhiCompareOpFromTextureCompareOp(m_parameters.comparisonFunction);
+ m_rhiSampler = ctx->rhi()->newSampler(magFilter, minFilter, mipMapFilter, std::get<0>(wrapMode),
+ std::get<1>(wrapMode), std::get<2>(wrapMode));
+
+ m_rhiSampler->setTextureCompareOp(compareOp);
+
+ if (!m_rhiSampler->build()) {
+ qDebug("Could not build RHI texture sampler");
+ }
+}
+
+void RHITexture::introspectPropertiesFromSharedTextureId()
+{
+ // // We know that the context is active when this function is called
+ // QOpenGLContext *ctx = QOpenGLContext::currentContext();
+ // if (!ctx) {
+ // qWarning() << Q_FUNC_INFO << "requires an OpenGL context";
+ // return;
+ // }
+ // QOpenGLFunctions *gl = ctx->functions();
+
+ // // If the user has set the target format himself, we won't try to deduce it
+ // if (m_properties.target != QAbstractTexture::TargetAutomatic)
+ // return;
+
+ // const QAbstractTexture::Target targets[] = {
+ // QAbstractTexture::Target2D,
+ // QAbstractTexture::TargetCubeMap,
+ //#ifndef QT_OPENGL_ES_2
+ // QAbstractTexture::Target1D,
+ // QAbstractTexture::Target1DArray,
+ // QAbstractTexture::Target3D,
+ // QAbstractTexture::Target2DArray,
+ // QAbstractTexture::TargetCubeMapArray,
+ // QAbstractTexture::Target2DMultisample,
+ // QAbstractTexture::Target2DMultisampleArray,
+ // QAbstractTexture::TargetRectangle,
+ // QAbstractTexture::TargetBuffer,
+ //#endif
+ // };
+
+ //#ifndef QT_OPENGL_ES_2
+ // // Try to find texture target with GL 4.5 functions
+ // const QPair<int, int> ctxGLVersion = ctx->format().version();
+ // if (ctxGLVersion.first > 4 || (ctxGLVersion.first == 4 && ctxGLVersion.second >= 5)) {
+ // // Only for GL 4.5+
+ //#ifdef GL_TEXTURE_TARGET
+ // QOpenGLFunctions_4_5_Core *gl5 = ctx->versionFunctions<QOpenGLFunctions_4_5_Core>();
+ // if (gl5 != nullptr)
+ // gl5->glGetTextureParameteriv(m_sharedTextureId, GL_TEXTURE_TARGET,
+ // reinterpret_cast<int *>(&m_properties.target));
+ //#endif
+ // }
+ //#endif
+
+ // // If GL 4.5 function unavailable or not working, try a slower way
+ // if (m_properties.target == QAbstractTexture::TargetAutomatic) {
+ // // // OpenGL offers no proper way of querying for the target of a texture given its
+ // id gl->glActiveTexture(GL_TEXTURE0);
+
+ // const GLenum targetBindings[] = {
+ // GL_TEXTURE_BINDING_2D,
+ // GL_TEXTURE_BINDING_CUBE_MAP,
+ //#ifndef QT_OPENGL_ES_2
+ // GL_TEXTURE_BINDING_1D,
+ // GL_TEXTURE_BINDING_1D_ARRAY,
+ // GL_TEXTURE_BINDING_3D,
+ // GL_TEXTURE_BINDING_2D_ARRAY,
+ // GL_TEXTURE_BINDING_CUBE_MAP_ARRAY,
+ // GL_TEXTURE_BINDING_2D_MULTISAMPLE,
+ // GL_TEXTURE_BINDING_2D_MULTISAMPLE_ARRAY,
+ // GL_TEXTURE_BINDING_RECTANGLE,
+ // GL_TEXTURE_BINDING_BUFFER
+ //#endif
+ // };
+
+ // Q_ASSERT(sizeof(targetBindings) / sizeof(targetBindings[0] == sizeof(targets) /
+ // sizeof(targets[0])));
+
+ // for (uint i = 0; i < sizeof(targetBindings) / sizeof(targetBindings[0]); ++i) {
+ // const int target = targets[i];
+ // gl->glBindTexture(target, m_sharedTextureId);
+ // int boundId = 0;
+ // gl->glGetIntegerv(targetBindings[i], &boundId);
+ // gl->glBindTexture(target, 0);
+ // if (boundId == m_sharedTextureId) {
+ // m_properties.target = static_cast<QAbstractTexture::Target>(target);
+ // break;
+ // }
+ // }
+ // }
+
+ // // Return early if we weren't able to find texture target
+ // if (std::find(std::begin(targets), std::end(targets), m_properties.target) ==
+ // std::end(targets)) {
+ // qWarning() << "Unable to determine texture target for shared GL texture";
+ // return;
+ // }
+
+ // // Bind texture once we know its target
+ // gl->glBindTexture(m_properties.target, m_sharedTextureId);
+
+ // // TO DO: Improve by using glGetTextureParameters when available which
+ // // support direct state access
+ //#ifndef GL_TEXTURE_MAX_LEVEL
+ //#define GL_TEXTURE_MAX_LEVEL 0x813D
+ //#endif
+
+ //#ifndef GL_TEXTURE_WRAP_R
+ //#define GL_TEXTURE_WRAP_R 0x8072
+ //#endif
+
+ // gl->glGetTexParameteriv(int(m_properties.target), GL_TEXTURE_MAX_LEVEL,
+ // reinterpret_cast<int *>(&m_properties.mipLevels));
+ // gl->glGetTexParameteriv(int(m_properties.target), GL_TEXTURE_MIN_FILTER,
+ // reinterpret_cast<int *>(&m_parameters.minificationFilter));
+ // gl->glGetTexParameteriv(int(m_properties.target), GL_TEXTURE_MAG_FILTER,
+ // reinterpret_cast<int *>(&m_parameters.magnificationFilter));
+ // gl->glGetTexParameteriv(int(m_properties.target), GL_TEXTURE_WRAP_R, reinterpret_cast<int
+ // *>(&m_parameters.wrapModeX)); gl->glGetTexParameteriv(int(m_properties.target),
+ // GL_TEXTURE_WRAP_S, reinterpret_cast<int *>(&m_parameters.wrapModeY));
+ // gl->glGetTexParameteriv(int(m_properties.target), GL_TEXTURE_WRAP_T, reinterpret_cast<int
+ // *>(&m_parameters.wrapModeZ));
+
+ //#ifndef QT_OPENGL_ES_2
+ // // Try to retrieve dimensions (not available on ES 2.0)
+ // if (!ctx->isOpenGLES()) {
+ // QOpenGLFunctions_3_1 *gl3 = ctx->versionFunctions<QOpenGLFunctions_3_1>();
+ // if (!gl3) {
+ // qWarning() << "Failed to retrieve shared texture dimensions";
+ // return;
+ // }
+
+ // gl3->glGetTexLevelParameteriv(int(m_properties.target), 0, GL_TEXTURE_WIDTH,
+ // reinterpret_cast<int *>(&m_properties.width));
+ // gl3->glGetTexLevelParameteriv(int(m_properties.target), 0, GL_TEXTURE_HEIGHT,
+ // reinterpret_cast<int *>(&m_properties.height));
+ // gl3->glGetTexLevelParameteriv(int(m_properties.target), 0, GL_TEXTURE_DEPTH,
+ // reinterpret_cast<int *>(&m_properties.depth));
+ // gl3->glGetTexLevelParameteriv(int(m_properties.target), 0, GL_TEXTURE_INTERNAL_FORMAT,
+ // reinterpret_cast<int *>(&m_properties.format));
+ // }
+ //#endif
+
+ // gl->glBindTexture(m_properties.target, 0);
+}
+
+} // namespace Rhi
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
diff --git a/src/plugins/renderers/rhi/textures/texture_p.h b/src/plugins/renderers/rhi/textures/texture_p.h
new file mode 100644
index 000000000..536cb962e
--- /dev/null
+++ b/src/plugins/renderers/rhi/textures/texture_p.h
@@ -0,0 +1,249 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DRENDER_RENDER_RHI_GLTEXTURE_H
+#define QT3DRENDER_RENDER_RHI_GLTEXTURE_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/qtexture.h>
+#include <Qt3DRender/qtextureimagedata.h>
+#include <Qt3DRender/qtexturegenerator.h>
+#include <Qt3DRender/private/backendnode_p.h>
+#include <Qt3DRender/private/handle_types_p.h>
+#include <Qt3DRender/private/texture_p.h>
+#include <QFlags>
+#include <QMutex>
+#include <QSize>
+
+QT_BEGIN_NAMESPACE
+
+class QRhiTexture;
+class QRhiSampler;
+
+namespace Qt3DRender {
+namespace Render {
+
+template<class APITexture, class APITextureImage>
+class APITextureManager;
+
+class TextureImageManager;
+class TextureDataManager;
+class TextureImageDataManager;
+
+namespace Rhi {
+class RenderBuffer;
+class SubmissionContext;
+
+/**
+ * @brief
+ * Actual implementation of the OpenGL texture object. Makes sure the
+ * QOpenGLTexture is up-to-date with the generators, properties and parameters
+ * that were set for this RHITexture.
+ *
+ * Can be shared among multiple QTexture backend nodes through the
+ * RHITextureManager, which will make sure that there are no two GLTextures
+ * sharing the same texture data.
+ *
+ * A RHITexture can be unique though. In that case, it will not be shared
+ * between QTextures, but private to one QTexture only.
+ *
+ * A RHITexture can also represent an OpenGL renderbuffer object. This is used
+ * only in certain special cases, mainly to provide a packed depth-stencil
+ * renderbuffer suitable as an FBO attachment with OpenGL ES 3.1 and earlier.
+ * Such a RHITexture will have no texture object under the hood, and therefore
+ * the only valid operation is getOrCreateRenderBuffer().
+ */
+class Q_AUTOTEST_EXPORT RHITexture
+{
+public:
+ RHITexture();
+ ~RHITexture();
+
+ enum DirtyFlag {
+ None = 0,
+ TextureData = (1 << 0), // texture data needs uploading to GPU
+ Properties = (1 << 1), // texture needs to be (re-)created
+ Parameters = (1 << 2), // texture parameters need to be (re-)set
+ SharedTextureId = (1 << 3), // texture id from shared context
+ TextureImageData = (1 << 4) // texture image data needs uploading
+ };
+
+ /**
+ * Helper class to hold the defining properties of TextureImages
+ */
+ struct Image
+ {
+ QTextureImageDataGeneratorPtr generator;
+ int layer;
+ int mipLevel;
+ QAbstractTexture::CubeMapFace face;
+
+ inline bool operator==(const Image &o) const
+ {
+ bool sameGenerators = (generator == o.generator)
+ || (!generator.isNull() && !o.generator.isNull() && *generator == *o.generator);
+ return sameGenerators && layer == o.layer && mipLevel == o.mipLevel && face == o.face;
+ }
+ inline bool operator!=(const Image &o) const { return !(*this == o); }
+ };
+
+ inline TextureProperties properties() const { return m_properties; }
+ inline TextureParameters parameters() const { return m_parameters; }
+ inline QTextureGeneratorPtr textureGenerator() const { return m_dataFunctor; }
+ inline int sharedTextureId() const { return m_sharedTextureId; }
+ inline QVector<Image> images() const { return m_images; }
+
+ inline QSize size() const { return QSize(m_properties.width, m_properties.height); }
+ inline QRhiTexture *getRhiTexture() const { return m_rhi; }
+ inline QRhiSampler *getRhiSampler() const { return m_rhiSampler; }
+
+ /**
+ * @brief
+ * Returns the QOpenGLTexture for this RHITexture. If necessary,
+ * the GL texture will be created from the TextureImageDatas associated
+ * with the texture and image functors. If no functors are provided,
+ * the texture will be created without images.
+ *
+ * If the texture properties or parameters have changed, these changes
+ * will be applied to the resulting OpenGL texture.
+ */
+ struct TextureUpdateInfo
+ {
+ QRhiTexture *texture = nullptr;
+ bool wasUpdated = false;
+ TextureProperties properties;
+ };
+
+ TextureUpdateInfo createOrUpdateRhiTexture(SubmissionContext *ctx);
+
+ /**
+ * @brief
+ * Returns the RenderBuffer for this RHITexture. If this is the first
+ * call, the OpenGL renderbuffer object will be created.
+ */
+ RenderBuffer *getOrCreateRenderBuffer();
+
+ void destroy();
+
+ void cleanup();
+
+ bool isDirty() const { return m_dirtyFlags != None; }
+
+ bool hasTextureData() const { return !m_textureData.isNull(); }
+ bool hasImagesData() const { return !m_imageData.isEmpty(); }
+
+ QFlags<DirtyFlag> dirtyFlags() const { return m_dirtyFlags; }
+
+ QMutex *externalRenderingLock() { return &m_externalRenderingMutex; }
+
+ void setExternalRenderingEnabled(bool enable) { m_externalRendering = enable; }
+
+ bool isExternalRenderingEnabled() const { return m_externalRendering; }
+
+ // Purely for unit testing purposes
+ bool wasTextureRecreated() const { return m_wasTextureRecreated; }
+
+ void setParameters(const TextureParameters &params);
+ void setProperties(const TextureProperties &props);
+ void setImages(const QVector<Image> &images);
+ void setGenerator(const QTextureGeneratorPtr &generator);
+ void setSharedTextureId(int textureId);
+ void addTextureDataUpdates(const QVector<QTextureDataUpdate> &updates);
+
+ QVector<QTextureDataUpdate> textureDataUpdates() const { return m_pendingTextureDataUpdates; }
+ QTextureGeneratorPtr dataGenerator() const { return m_dataFunctor; }
+
+private:
+ void requestImageUpload() { m_dirtyFlags |= TextureImageData; }
+
+ void requestUpload() { m_dirtyFlags |= TextureData; }
+
+ bool testDirtyFlag(DirtyFlag flag) { return m_dirtyFlags.testFlag(flag); }
+
+ void setDirtyFlag(DirtyFlag flag, bool value = true) { m_dirtyFlags.setFlag(flag, value); }
+
+ QRhiTexture *buildRhiTexture(SubmissionContext *ctx);
+ bool loadTextureDataFromGenerator();
+ void loadTextureDataFromImages();
+ void uploadRhiTextureData(SubmissionContext *ctx);
+ void updateRhiTextureParameters(SubmissionContext *ctx);
+ void introspectPropertiesFromSharedTextureId();
+ void destroyResources();
+
+ QFlags<DirtyFlag> m_dirtyFlags;
+ QMutex m_externalRenderingMutex;
+ QRhiTexture *m_rhi;
+ QRhiSampler *m_rhiSampler;
+ RenderBuffer *m_renderBuffer;
+
+ // target which is actually used for GL texture
+ TextureProperties m_properties;
+ TextureParameters m_parameters;
+
+ QTextureGeneratorPtr m_dataFunctor;
+ QTextureGenerator *m_pendingDataFunctor;
+ QVector<Image> m_images;
+
+ // cache actual image data generated by the functors
+ QTextureDataPtr m_textureData;
+ QVector<QTextureImageDataPtr> m_imageData;
+ QVector<QTextureDataUpdate> m_pendingTextureDataUpdates;
+
+ int m_sharedTextureId;
+ bool m_externalRendering;
+ bool m_wasTextureRecreated;
+};
+
+} // namespace Rhi
+} // namespace Render
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+#endif // QT3DRENDER_RENDER_RHI_GLTEXTURE_H
diff --git a/src/plugins/renderers/rhi/textures/textures.pri b/src/plugins/renderers/rhi/textures/textures.pri
new file mode 100644
index 000000000..dd9e6404f
--- /dev/null
+++ b/src/plugins/renderers/rhi/textures/textures.pri
@@ -0,0 +1,9 @@
+INCLUDEPATH += $$PWD
+
+SOURCES += \
+ $$PWD/renderbuffer.cpp \
+ $$PWD/texture.cpp
+
+HEADERS += \
+ $$PWD/renderbuffer_p.h \
+ $$PWD/texture_p.h